PoC: boot, uart output in qemu

smoltcp
Astro 2019-05-05 14:56:23 +02:00
commit 9b414e2408
16 changed files with 553 additions and 0 deletions

11
.cargo/config Normal file
View File

@ -0,0 +1,11 @@
[target.armv7-unknown-linux-gnueabihf]
runner = "./runner.sh"
linker = "arm-none-eabihf-gcc"
rustflags = [
"-C", "link-arg=-Wl,-Tlink.x",
"-C", "target-feature=a9,armv7-a,neon",
"-C", "target-cpu=cortex-a9",
]
[build]
target = "armv7-unknown-linux-gnueabihf"

46
Cargo.lock generated Normal file
View File

@ -0,0 +1,46 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "bit_field"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "panic-abort"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "r0"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "vcell"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "volatile-register"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "zc706"
version = "0.0.0"
dependencies = [
"bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"panic-abort 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[metadata]
"checksum bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a165d606cf084741d4ac3a28fb6e9b1eb0bd31f6cd999098cfddb0b2ab381dc0"
"checksum panic-abort 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2c14a66511ed17b6a8b4256b868d7fd207836d891db15eea5195dbcaf87e630f"
"checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f"
"checksum vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "45c297f0afb6928cd08ab1ff9d95e99392595ea25ae1b5ecf822ff8764e57a0d"
"checksum volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286"

19
Cargo.toml Normal file
View File

@ -0,0 +1,19 @@
[package]
name = "zc706"
version = "0.0.0"
authors = ["Astro <astro@spaceboyz.net>"]
edition = "2018"
[profile.dev]
panic = "abort"
lto = false
[profile.release]
panic = "abort"
lto = false
debug = true
[dependencies]
panic-abort = "0.3"
r0 = "0.2"
volatile-register = "0.2"
bit_field = "0.10"

18
build.rs Normal file
View File

@ -0,0 +1,18 @@
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
fn main() {
// Put the linker script somewhere the linker can find it
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
File::create(out.join("link.x"))
.unwrap()
.write_all(include_bytes!("link.x"))
.unwrap();
println!("cargo:rustc-link-search={}", out.display());
// Only re-run the build script when memory.x is changed,
// instead of when any part of the source code changes.
println!("cargo:rerun-if-changed=link.x");
}

22
default.nix Normal file
View File

@ -0,0 +1,22 @@
{ # Use master branch of the overlay by default
mozillaOverlay ? import (builtins.fetchTarball https://github.com/mozilla/nixpkgs-mozilla/archive/master.tar.gz),
rustSrc ? https://github.com/rustlang/rust/archive/master.tar.gz,
}:
let
pkgs = import <nixpkgs> { overlays = [ mozillaOverlay ]; };
in
with pkgs;
let
targets = [
"armv7-unknown-linux-gnueabihf"
];
rust =
rustChannelOfTargets "nightly" null targets;
rustPlatform = recurseIntoAttrs (makeRustPlatform {
rustc = rust // { src = rustSrc; };
cargo = rust;
});
in {
inherit pkgs rustPlatform;
}

62
link.x Normal file
View File

@ -0,0 +1,62 @@
ENTRY(_boot_cores);
/* SECTIONS */
/* { */
/* . = 0x8000; */
/* .text : */
/* { */
/* KEEP(*(.text.boot)) *(.text .text.*) */
/* } */
/* /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } */
/* } */
SECTIONS
{
/* Starts at LOADER_ADDR. */
. = 0x8000;
__start = .;
__text_start = .;
.text :
{
KEEP(*(.text.boot))
*(.text)
}
. = ALIGN(4096); /* align to page size */
__text_end = .;
__rodata_start = .;
.rodata :
{
*(.rodata)
}
. = ALIGN(4096); /* align to page size */
__rodata_end = .;
__data_start = .;
.data :
{
*(.data)
}
. = ALIGN(4096); /* align to page size */
__data_end = .;
__bss_start = .;
.bss :
{
bss = .;
*(.bss)
}
. = ALIGN(4096); /* align to page size */
__bss_end = .;
__end = .;
/DISCARD/ :
{
/* Unused exception related info that only wastes space */
*(.ARM.exidx.*);
*(.ARM.extab.*);
}
}

6
qemu.gdb Normal file
View File

@ -0,0 +1,6 @@
target remote :1234
# print demangled symbols by default
set print asm-demangle on
load

11
runner.sh Executable file
View File

@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -e -m
ELF=$1
IMAGE=$ELF.bin
arm-none-eabihf-objcopy -O binary $ELF $IMAGE
qemu-system-arm -M xilinx-zynq-a9 -s -kernel $IMAGE -chardev file,id=uart0,path=/tmp/qemu.serial &
sleep 1
gdb -x qemu.gdb $ELF
kill -KILL %1

24
shell.nix Normal file
View File

@ -0,0 +1,24 @@
let
mozillaOverlay = import (builtins.fetchTarball https://github.com/mozilla/nixpkgs-mozilla/archive/master.tar.gz);
pkgs = import <nixpkgs> { overlays = [ mozillaOverlay ]; };
in
with pkgs;
let
project = callPackage ./default.nix {};
in
with project;
stdenv.mkDerivation {
name = "adc2tcp-env";
buildInputs = with rustPlatform.rust; [
rustc cargo
pkgsCross.armhf-embedded.buildPackages.gcc
#pkgsCross.armv7l-hf-multiplatform.buildPackages.gcc
#pkgsCross.armhf-embedded.buildPackages.binutils
];
# Set Environment Variables
RUST_BACKTRACE = 1;
shellHook = ''
'';
}

11
src/cortex_a9/asm.rs Normal file
View File

@ -0,0 +1,11 @@
/// The classic no-op
#[inline]
pub fn nop() {
unsafe { asm!("nop" :::: "volatile") }
}
/// Wait For Event
#[inline]
pub fn wfe() {
unsafe { asm!("wfe" :::: "volatile") }
}

2
src/cortex_a9/mod.rs Normal file
View File

@ -0,0 +1,2 @@
pub mod asm;
pub mod regs;

38
src/cortex_a9/regs.rs Normal file
View File

@ -0,0 +1,38 @@
pub trait ReadableRegister<T> {
fn get(&self) -> T;
}
macro_rules! def_reg_get {
($name:ty, $type:ty, $asm_instr:tt) => {
impl ReadableRegister<$type> for $name {
#[inline(always)]
fn get(&self) -> $type {
let mut value;
unsafe { asm!($asm_instr : "=r" (value) ::: "volatile") }
value
}
}
}
}
pub trait WritableRegister<T> {
fn set(&self, value: T);
}
macro_rules! def_reg_set {
($name:ty, $type:ty, $asm_instr:tt) => {
impl WritableRegister<$type> for $name {
#[inline(always)]
fn set(&self, value: $type) {
unsafe { asm!($asm_instr :: "r" (value) :: "volatile") }
}
}
}
}
pub struct SP;
def_reg_get!(SP, u32, "mov $0, sp");
def_reg_set!(SP, u32, "mov sp, $0");
pub struct MPIDR;
def_reg_get!(MPIDR, u32, "mrc p15, 0, $0, c0, c0, 5");

50
src/main.rs Normal file
View File

@ -0,0 +1,50 @@
#![no_std]
#![no_main]
#![feature(asm)]
//[feature(global_asm)]
#![feature(naked_functions)]
use panic_abort as _;
use r0::zero_bss;
mod cortex_a9;
mod slcr;
mod uart;
use uart::Uart;
extern "C" {
static mut __bss_start: u32;
static mut __bss_end: u32;
static mut __end: u32;
}
#[link_section = ".text.boot"]
#[no_mangle]
#[naked]
pub unsafe extern "C" fn _boot_cores() -> ! {
use cortex_a9::{asm, regs::*};
const CORE_MASK: u32 = 0x3;
// End of OCM RAM
const STACK_START: u32 = 256 << 10;
match MPIDR.get() & CORE_MASK {
0 => {
SP.set(STACK_START);
zero_bss(&mut __bss_start, &mut __bss_end);
main();
panic!("return from main");
}
_ => loop {
// if not core0, infinitely wait for events
asm::wfe();
},
}
}
fn main() {
let uart = Uart::uart0();
for b in "Hello World\r\n".bytes() {
uart.write_byte(b);
}
}

70
src/slcr.rs Normal file
View File

@ -0,0 +1,70 @@
use core::ops::RangeInclusive;
use volatile_register::{RO, WO, RW};
use bit_field::BitField;
#[repr(packed)]
pub struct UartClkCtrl {
pub reg: RW<u32>,
}
impl UartClkCtrl {
const ADDR: *mut Self = 0xF8000154 as *mut _;
pub fn new() -> &'static mut Self {
unsafe { &mut *Self::ADDR }
}
const DIVISOR: RangeInclusive<usize> = 8..=13;
const CLKACT1: usize = 1;
const CLKACT0: usize = 0;
pub fn enable_uart0(&self) {
unsafe {
self.reg.modify(|mut x| {
x.set_bits(Self::DIVISOR, 0x14);
x.set_bit(Self::CLKACT0, true);
x
})
}
}
}
#[repr(packed)]
pub struct UartRstCtrl {
pub reg: RW<u32>,
}
impl UartRstCtrl {
const ADDR: *mut Self = 0xF8000228 as *mut _;
pub fn new() -> &'static mut Self {
unsafe { &mut *Self::ADDR }
}
const UART1_REF_RST: usize = 3;
const UART0_REF_RST: usize = 2;
const UART1_CPU1X_RST: usize = 1;
const UART0_CPU1X_RST: usize = 0;
pub fn reset_uart0(&self) {
unsafe { toggle(&self.reg, Self::UART0_REF_RST); }
}
pub fn reset_uart1(&self) {
unsafe { toggle(&self.reg, Self::UART1_REF_RST); }
}
}
unsafe fn toggle<T: BitField + Copy>(reg: &RW<T>, bit: usize) {
reg.modify(|x| {
let mut x = x.clone();
x.set_bit(bit, true);
x
});
reg.modify(|x| {
let mut x = x.clone();
x.set_bit(bit, false);
x
});
}

38
src/uart/mod.rs Normal file
View File

@ -0,0 +1,38 @@
mod regs;
pub use regs::RegisterBlock;
pub struct Uart {
regs: &'static mut RegisterBlock,
}
impl Uart {
pub fn uart0() -> Self {
let uart_rst_ctrl = super::slcr::UartRstCtrl::new();
uart_rst_ctrl.reset_uart0();
// TODO: Route UART 0 RxD/TxD Signals to MIO Pins
// a. Clock divisor, slcr.UART_CLK_CTRL[DIVISOR] = 0x14.
// b. Select the IO PLL, slcr.UART_CLK_CTRL[SRCSEL] = 0.
// c. Enable the UART 0 Reference clock, slcr.UART_CLK_CTRL [CLKACT0] = 1.
// d. Disable UART 1 Reference clock, slcr.UART_CLK_CTRL [CLKACT1] bit = 0.
let uart_clk_ctrl = super::slcr::UartClkCtrl::new();
uart_clk_ctrl.enable_uart0();
Uart {
regs: RegisterBlock::uart0(),
}.init()
}
fn init(self) -> Self {
self.regs.configure();
self
}
pub fn write_byte(&self, v: u8) {
unsafe {
while self.regs.tx_fifo_full() {}
self.regs.write_byte(v);
}
}
}

125
src/uart/regs.rs Normal file
View File

@ -0,0 +1,125 @@
use volatile_register::{RO, WO, RW};
use bit_field::BitField;
#[repr(packed)]
pub struct RegisterBlock {
pub control: RW<u32>,
pub mode: RW<u32>,
pub intrpt_en: RW<u32>,
pub intrpt_dis: RW<u32>,
pub intrpt_mask: RO<u32>,
pub chnl_int_sts: WO<u32>,
pub baud_rate_gen: RW<u32>,
pub rcvr_timeout: RW<u32>,
pub rcvr_fifo_trigger_level: RW<u32>,
pub modem_ctrl: RW<u32>,
pub modem_sts: RW<u32>,
pub channel_sts: RO<u32>,
pub tx_rx_fifo: RW<u32>,
pub baud_rate_divider: RW<u32>,
pub flow_delay: RW<u32>,
pub reserved0: RO<u32>,
pub reserved1: RO<u32>,
pub tx_fifo_trigger_level: RW<u32>,
}
impl RegisterBlock {
const UART0: *mut Self = 0xE0000000 as *mut _;
const UART1: *mut Self = 0xE0001000 as *mut _;
pub fn uart0() -> &'static mut Self {
unsafe { &mut *Self::UART0 }
}
pub fn uart1() -> &'static mut Self {
unsafe { &mut *Self::UART1 }
}
pub fn configure(&self) {
unsafe {
// Confiugre UART character frame
// * Disable clock-divider
// * 8-bit
// * no parity
// * 1 stop bit
// * Normal channel mode
self.mode.write(0x20);
// Configure the Buad Rate
self.disable_rx();
self.disable_tx();
// 9,600 baud
self.baud_rate_gen.write(651);
self.baud_rate_divider.write(7);
self.reset_rx();
self.reset_tx();
self.enable_rx();
self.enable_tx();
}
}
pub unsafe fn write_byte(&self, value: u8) {
self.tx_rx_fifo.write(value.into());
}
const CONTROL_RXEN: usize = 2;
const CONTROL_RXDIS: usize = 3;
const CONTROL_TXEN: usize = 4;
const CONTROL_TXDIS: usize = 5;
const CONTROL_TXRST: usize = 1;
const CONTROL_RXRST: usize = 0;
unsafe fn disable_rx(&self) {
self.control.modify(|mut x| {
x.set_bit(Self::CONTROL_RXEN, false);
x.set_bit(Self::CONTROL_RXDIS, true);
x
})
}
unsafe fn disable_tx(&self) {
self.control.modify(|mut x| {
x.set_bit(Self::CONTROL_TXEN, false);
x.set_bit(Self::CONTROL_TXDIS, true);
x
})
}
unsafe fn enable_rx(&self) {
self.control.modify(|mut x| {
x.set_bit(Self::CONTROL_RXEN, true);
x.set_bit(Self::CONTROL_RXDIS, false);
x
})
}
unsafe fn enable_tx(&self) {
self.control.modify(|mut x| {
x.set_bit(Self::CONTROL_TXEN, true);
x.set_bit(Self::CONTROL_TXDIS, false);
x
})
}
unsafe fn reset_rx(&self) {
self.control.modify(|mut x| {
x.set_bit(Self::CONTROL_RXRST, true);
x
})
}
unsafe fn reset_tx(&self) {
self.control.modify(|mut x| {
x.set_bit(Self::CONTROL_TXRST, true);
x
})
}
const CHANNEL_STS_TXFULL: usize = 4;
pub fn tx_fifo_full(&self) -> bool {
self.channel_sts.read().get_bit(Self::CHANNEL_STS_TXFULL)
}
}