From 9b414e2408605e1fd7ae8d5fdf0c076f7f9e0442 Mon Sep 17 00:00:00 2001 From: Astro Date: Sun, 5 May 2019 14:56:23 +0200 Subject: [PATCH] PoC: boot, uart output in qemu --- .cargo/config | 11 ++++ Cargo.lock | 46 ++++++++++++++++ Cargo.toml | 19 +++++++ build.rs | 18 ++++++ default.nix | 22 ++++++++ link.x | 62 +++++++++++++++++++++ qemu.gdb | 6 ++ runner.sh | 11 ++++ shell.nix | 24 ++++++++ src/cortex_a9/asm.rs | 11 ++++ src/cortex_a9/mod.rs | 2 + src/cortex_a9/regs.rs | 38 +++++++++++++ src/main.rs | 50 +++++++++++++++++ src/slcr.rs | 70 +++++++++++++++++++++++ src/uart/mod.rs | 38 +++++++++++++ src/uart/regs.rs | 125 ++++++++++++++++++++++++++++++++++++++++++ 16 files changed, 553 insertions(+) create mode 100644 .cargo/config create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 build.rs create mode 100644 default.nix create mode 100644 link.x create mode 100644 qemu.gdb create mode 100755 runner.sh create mode 100644 shell.nix create mode 100644 src/cortex_a9/asm.rs create mode 100644 src/cortex_a9/mod.rs create mode 100644 src/cortex_a9/regs.rs create mode 100644 src/main.rs create mode 100644 src/slcr.rs create mode 100644 src/uart/mod.rs create mode 100644 src/uart/regs.rs 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) + } +}