commit 9b414e2408605e1fd7ae8d5fdf0c076f7f9e0442 Author: Astro Date: Sun May 5 14:56:23 2019 +0200 PoC: boot, uart output in qemu diff --git a/.cargo/config b/.cargo/config new file mode 100644 index 0000000..70a37e8 --- /dev/null +++ b/.cargo/config @@ -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" diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..180f3ea --- /dev/null +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..840fa0f --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "zc706" +version = "0.0.0" +authors = ["Astro "] +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" diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..517830e --- /dev/null +++ b/build.rs @@ -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"); +} diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..c40a9ba --- /dev/null +++ b/default.nix @@ -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 { 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; +} diff --git a/link.x b/link.x new file mode 100644 index 0000000..9dcbe80 --- /dev/null +++ b/link.x @@ -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.*); + } +} diff --git a/qemu.gdb b/qemu.gdb new file mode 100644 index 0000000..8ebd754 --- /dev/null +++ b/qemu.gdb @@ -0,0 +1,6 @@ +target remote :1234 + +# print demangled symbols by default +set print asm-demangle on + +load diff --git a/runner.sh b/runner.sh new file mode 100755 index 0000000..b0bedfe --- /dev/null +++ b/runner.sh @@ -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 diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..d1e0636 --- /dev/null +++ b/shell.nix @@ -0,0 +1,24 @@ +let + mozillaOverlay = import (builtins.fetchTarball https://github.com/mozilla/nixpkgs-mozilla/archive/master.tar.gz); + pkgs = import { 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 = '' + ''; +} diff --git a/src/cortex_a9/asm.rs b/src/cortex_a9/asm.rs new file mode 100644 index 0000000..f69b197 --- /dev/null +++ b/src/cortex_a9/asm.rs @@ -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") } +} diff --git a/src/cortex_a9/mod.rs b/src/cortex_a9/mod.rs new file mode 100644 index 0000000..79d3436 --- /dev/null +++ b/src/cortex_a9/mod.rs @@ -0,0 +1,2 @@ +pub mod asm; +pub mod regs; diff --git a/src/cortex_a9/regs.rs b/src/cortex_a9/regs.rs new file mode 100644 index 0000000..498e087 --- /dev/null +++ b/src/cortex_a9/regs.rs @@ -0,0 +1,38 @@ +pub trait ReadableRegister { + 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 { + 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"); diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..1ebd35c --- /dev/null +++ b/src/main.rs @@ -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); + } +} diff --git a/src/slcr.rs b/src/slcr.rs new file mode 100644 index 0000000..b95c35c --- /dev/null +++ b/src/slcr.rs @@ -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, +} + +impl UartClkCtrl { + const ADDR: *mut Self = 0xF8000154 as *mut _; + + pub fn new() -> &'static mut Self { + unsafe { &mut *Self::ADDR } + } + + const DIVISOR: RangeInclusive = 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, +} + +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(reg: &RW, 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 + }); +} diff --git a/src/uart/mod.rs b/src/uart/mod.rs new file mode 100644 index 0000000..f8ba328 --- /dev/null +++ b/src/uart/mod.rs @@ -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); + } + } +} diff --git a/src/uart/regs.rs b/src/uart/regs.rs new file mode 100644 index 0000000..0b1e6fb --- /dev/null +++ b/src/uart/regs.rs @@ -0,0 +1,125 @@ +use volatile_register::{RO, WO, RW}; +use bit_field::BitField; + +#[repr(packed)] +pub struct RegisterBlock { + pub control: RW, + pub mode: RW, + pub intrpt_en: RW, + pub intrpt_dis: RW, + pub intrpt_mask: RO, + pub chnl_int_sts: WO, + pub baud_rate_gen: RW, + pub rcvr_timeout: RW, + pub rcvr_fifo_trigger_level: RW, + pub modem_ctrl: RW, + pub modem_sts: RW, + pub channel_sts: RO, + pub tx_rx_fifo: RW, + pub baud_rate_divider: RW, + pub flow_delay: RW, + pub reserved0: RO, + pub reserved1: RO, + pub tx_fifo_trigger_level: RW, +} + +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) + } +}