PoC: boot, uart output in qemu
This commit is contained in:
commit
9b414e2408
|
@ -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"
|
|
@ -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"
|
|
@ -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"
|
|
@ -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");
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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.*);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
target remote :1234
|
||||
|
||||
# print demangled symbols by default
|
||||
set print asm-demangle on
|
||||
|
||||
load
|
|
@ -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
|
|
@ -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 = ''
|
||||
'';
|
||||
}
|
|
@ -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") }
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
pub mod asm;
|
||||
pub mod regs;
|
|
@ -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");
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
});
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue