Compare commits

..

7 Commits

25 changed files with 730 additions and 235 deletions

10
Cargo.lock generated
View File

@ -126,6 +126,16 @@ dependencies = [
"volatile-register",
]
[[package]]
name = "libcortex_r5"
version = "0.0.0"
dependencies = [
"bit_field",
"libcortex_a9",
"libregister",
"volatile-register",
]
[[package]]
name = "libregister"
version = "0.0.0"

View File

@ -2,6 +2,7 @@
members = [
"libregister",
"libcortex_a9",
"libcortex_r5",
"libboard_zynq",
"libsupport_zynq",
"libasync",

View File

@ -18,5 +18,5 @@ embedded-hal = "0.2"
libregister = { path = "../libregister" }
libcortex_a9 = { path = "../libcortex_a9" }
libboard_zynq = { path = "../libboard_zynq" }
libsupport_zynq = { path = "../libsupport_zynq", default-features = false, features = ["panic_handler", "dummy_fiq_handler"]}
libsupport_zynq = { path = "../libsupport_zynq", default-features = false, features = ["panic_handler"]}
libasync = { path = "../libasync" }

View File

@ -39,7 +39,7 @@ use libcortex_a9::{
};
use libregister::{RegisterR, RegisterW};
use libsupport_zynq::{
boot, exception_vectors, ram,
boot, ram,
};
use log::{info, warn};
use core::sync::atomic::{AtomicBool, Ordering};
@ -56,29 +56,19 @@ extern "C" {
static CORE1_RESTART: AtomicBool = AtomicBool::new(false);
interrupt_handler!(IRQ, irq, __irq_stack0_start, __irq_stack1_start, {
let mpcore = mpcore::RegisterBlock::mpcore();
let mut gic = gic::InterruptController::gic(mpcore);
let id = gic.get_interrupt_id();
match MPIDR.read().cpu_id(){
0 => {
if id.0 == 0 {
println!("Interrupting core0...");
gic.end_interrupt(id);
return;
}
},
1 => {
if id.0 == 0 {
gic.end_interrupt(id);
asm::exit_irq();
SP.write(&mut __stack1_start as *mut _ as u32);
asm::enable_irq();
CORE1_RESTART.store(false, Ordering::Relaxed);
notify_spin_lock();
main_core1();
}
},
_ => {}
if MPIDR.read().cpu_id() == 1{
let mpcore = mpcore::RegisterBlock::mpcore();
let mut gic = gic::InterruptController::gic(mpcore);
let id = gic.get_interrupt_id();
if id.0 == 0 {
gic.end_interrupt(id);
asm::exit_irq();
SP.write(&mut __stack1_start as *mut _ as u32);
asm::enable_irq();
CORE1_RESTART.store(false, Ordering::Relaxed);
notify_spin_lock();
main_core1();
}
}
stdio::drop_uart();
println!("IRQ");
@ -96,7 +86,6 @@ pub fn restart_core1() {
#[no_mangle]
pub fn main_core0() {
exception_vectors::set_vector_table(0x0);
// zynq::clocks::CpuClocks::enable_io(1_250_000_000);
enable_l2_cache(0x8);
println!("\nZynq experiments");
@ -145,10 +134,6 @@ pub fn main_core0() {
ddr.memtest();
ram::init_alloc_ddr(&mut ddr);
info!("Send software interrupt to core0");
interrupt_controller.send_sgi(gic::InterruptId(0), gic::CPUCore::Core0.into());
info!("Core0 returned from interrupt");
boot::Core1::start(false);
let core1_req = unsafe { &mut CORE1_REQ.0 };

View File

@ -3,11 +3,11 @@
"mozilla-overlay": {
"flake": false,
"locked": {
"lastModified": 1704373101,
"narHash": "sha256-+gi59LRWRQmwROrmE1E2b3mtocwueCQqZ60CwLG+gbg=",
"lastModified": 1650459918,
"narHash": "sha256-sroCK+QJTmoXtcRkwZyKOP9iAYOPID2Bwdxn4GkG16w=",
"owner": "mozilla",
"repo": "nixpkgs-mozilla",
"rev": "9b11a87c0cc54e308fa83aac5b4ee1816d5418a2",
"rev": "e1f7540fc0a8b989fb8cf701dc4fd7fc76bcf168",
"type": "github"
},
"original": {
@ -18,16 +18,16 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1715087517,
"narHash": "sha256-CLU5Tsg24Ke4+7sH8azHWXKd0CFd4mhLWfhYgUiDBpQ=",
"lastModified": 1653920503,
"narHash": "sha256-BBeCZwZImtjP3oYy4WogkQYy5OxNyfNciVSc1AfZgLQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b211b392b8486ee79df6cdfb1157ad2133427a29",
"rev": "a634c8f6c1fbf9b9730e01764999666f3436f10a",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"ref": "nixos-22.05",
"repo": "nixpkgs",
"type": "github"
}

203
flake.nix
View File

@ -1,12 +1,12 @@
{
description = "Bare-metal Rust on Zynq-7000";
inputs.nixpkgs.url = github:NixOS/nixpkgs/nixos-unstable;
inputs.nixpkgs.url = github:NixOS/nixpkgs/nixos-22.05;
inputs.mozilla-overlay = { url = github:mozilla/nixpkgs-mozilla; flake = false; };
outputs = { self, nixpkgs, mozilla-overlay }:
let
pkgs = import nixpkgs { system = "x86_64-linux"; overlays = [ (import mozilla-overlay) crosspkgs-overlay ]; };
pkgs = import nixpkgs { system = "x86_64-linux"; overlays = [ (import mozilla-overlay) ]; };
rustManifest = pkgs.fetchurl {
url = "https://static.rust-lang.org/dist/2021-01-29/channel-rust-nightly.toml";
@ -26,29 +26,148 @@
cargo = rust;
});
crosspkgs-overlay = (self: super: {
pkgsCross = super.pkgsCross // {
zynq-baremetal = import super.path {
system = "x86_64-linux";
crossSystem = {
config = "arm-none-eabihf";
libc = "newlib";
gcc.cpu = "cortex-a9";
gcc.fpu = "vfpv3";
};
};
gnu-platform = "arm-none-eabi";
binutils-pkg = { zlib, extraConfigureFlags ? [] }: pkgs.stdenv.mkDerivation rec {
basename = "binutils";
version = "2.30";
name = "${basename}-${gnu-platform}-${version}";
src = pkgs.fetchurl {
url = "https://ftp.gnu.org/gnu/binutils/binutils-${version}.tar.bz2";
sha256 = "028cklfqaab24glva1ks2aqa1zxa6w6xmc8q34zs1sb7h22dxspg";
};
});
configureFlags = [
"--enable-deterministic-archives"
"--target=${gnu-platform}"
"--with-cpu=cortex-a9"
"--with-fpu=vfpv3"
"--with-float=hard"
"--with-mode=thumb"
] ++ extraConfigureFlags;
outputs = [ "out" "info" "man" ];
depsBuildBuild = [ pkgs.buildPackages.stdenv.cc ];
buildInputs = [ zlib ];
enableParallelBuilding = true;
meta = {
description = "Tools for manipulating binaries (linker, assembler, etc.)";
longDescription = ''
The GNU Binutils are a collection of binary tools. The main
ones are `ld' (the GNU linker) and `as' (the GNU assembler).
They also include the BFD (Binary File Descriptor) library,
`gprof', `nm', `strip', etc.
'';
homepage = http://www.gnu.org/software/binutils/;
license = pkgs.lib.licenses.gpl3Plus;
/* Give binutils a lower priority than gcc-wrapper to prevent a
collision due to the ld/as wrappers/symlinks in the latter. */
priority = "10";
};
};
gcc-pkg = { gmp, mpfr, libmpc, platform-binutils, extraConfigureFlags ? [] }: pkgs.stdenv.mkDerivation rec {
basename = "gcc";
version = "9.1.0";
name = "${basename}-${gnu-platform}-${version}";
src = pkgs.fetchurl {
url = "https://ftp.gnu.org/gnu/gcc/gcc-${version}/gcc-${version}.tar.xz";
sha256 = "1817nc2bqdc251k0lpc51cimna7v68xjrnvqzvc50q3ax4s6i9kr";
};
preConfigure = ''
mkdir build
cd build
'';
configureScript = "../configure";
configureFlags = [
"--target=${gnu-platform}"
"--with-arch=armv7-a"
"--with-tune=cortex-a9"
"--with-fpu=vfpv3"
"--with-float=hard"
"--disable-libssp"
"--enable-languages=c"
"--with-as=${platform-binutils}/bin/${gnu-platform}-as"
"--with-ld=${platform-binutils}/bin/${gnu-platform}-ld" ] ++ extraConfigureFlags;
outputs = [ "out" "info" "man" ];
hardeningDisable = [ "format" "pie" ];
propagatedBuildInputs = [ gmp mpfr libmpc platform-binutils ];
enableParallelBuilding = true;
dontFixup = true;
};
newlib-pkg = { platform-binutils, platform-gcc }: pkgs.stdenv.mkDerivation rec {
pname = "newlib";
version = "3.1.0";
src = pkgs.fetchurl {
url = "ftp://sourceware.org/pub/newlib/newlib-${version}.tar.gz";
sha256 = "0ahh3n079zjp7d9wynggwrnrs27440aac04340chf1p9476a2kzv";
};
nativeBuildInputs = [ platform-binutils platform-gcc ];
configureFlags = [
"--target=${gnu-platform}"
"--with-cpu=cortex-a9"
"--with-fpu=vfpv3"
"--with-float=hard"
"--with-mode=thumb"
"--enable-interwork"
"--disable-multilib"
"--disable-newlib-supplied-syscalls"
"--with-gnu-ld"
"--with-gnu-as"
"--disable-newlib-io-float"
"--disable-werror"
];
dontFixup = true;
};
gnutoolchain = rec {
binutils-bootstrap = pkgs.callPackage binutils-pkg { };
gcc-bootstrap = pkgs.callPackage gcc-pkg {
platform-binutils = binutils-bootstrap;
extraConfigureFlags = [ "--disable-libgcc" ];
};
newlib = pkgs.callPackage newlib-pkg {
platform-binutils = binutils-bootstrap;
platform-gcc = gcc-bootstrap;
};
binutils = pkgs.callPackage binutils-pkg {
extraConfigureFlags = [ "--with-lib-path=${newlib}/arm-none-eabi/lib" ];
};
gcc = pkgs.callPackage gcc-pkg {
platform-binutils = binutils;
extraConfigureFlags = [ "--enable-newlib" "--with-headers=${newlib}/arm-none-eabi/include" ];
};
};
cargo-xbuild = rustPlatform.buildRustPackage rec {
pname = "cargo-xbuild";
version = "0.6.5";
src = pkgs.fetchFromGitHub {
owner = "rust-osdev";
repo = pname;
rev = "v${version}";
sha256 = "18djvygq9v8rmfchvi2hfj0i6fhn36m716vqndqnj56fiqviwxvf";
};
cargoSha256 = "13sj9j9kl6js75h9xq0yidxy63vixxm9q3f8jil6ymarml5wkhx8";
meta = with pkgs.lib; {
description = "Automatically cross-compiles the sysroot crates core, compiler_builtins, and alloc";
homepage = "https://github.com/rust-osdev/cargo-xbuild";
license = with licenses; [ mit asl20 ];
maintainers = with maintainers; [ johntitor xrelkd ];
};
};
mkbootimage = pkgs.stdenv.mkDerivation {
pname = "mkbootimage";
version = "2.3dev";
version = "2.2";
src = pkgs.fetchFromGitHub {
owner = "antmicro";
repo = "zynq-mkbootimage";
rev = "872363ce32c249f8278cf107bc6d3bdeb38d849f";
sha256 = "sha256-5FPyAhUWZDwHbqmp9J2ZXTmjaXPz+dzrJMolaNwADHs=";
rev = "4ee42d782a9ba65725ed165a4916853224a8edf7";
sha256 = "1k1mbsngqadqihzjgvwvsrkvryxy5ladpxd9yh9iqn2s7fxqwqa9";
};
propagatedBuildInputs = [ pkgs.libelf pkgs.pcre ];
@ -61,7 +180,6 @@
mkdir -p $out/bin
cp mkbootimage $out/bin
'';
hardeningDisable = [ "fortify" ];
};
fsbl = { board ? "zc706" }: pkgs.stdenv.mkDerivation {
@ -69,20 +187,19 @@
src = pkgs.fetchFromGitHub {
owner = "Xilinx";
repo = "embeddedsw";
rev = "xilinx_v2022.2";
sha256 = "sha256-UDz9KK/Hw3qM1BAeKif30rE8Bi6C2uvuZlvyvtJCMfw=";
rev = "65c849ed46c88c67457e1fc742744f96db968ff1";
sha256 = "1rvl06ha40dzd6s9aa4sylmksh4xb9dqaxq462lffv1fdk342pda";
};
patches = [ ./fsbl.patch ];
nativeBuildInputs = [
pkgs.pkgsCross.zynq-baremetal.buildPackages.binutils
pkgs.pkgsCross.zynq-baremetal.buildPackages.gcc
pkgs.gnumake
gnutoolchain.binutils
gnutoolchain.gcc
];
patchPhase = ''
patch -p1 -i ${./fsbl.patch}
patchShebangs lib/sw_apps/zynq_fsbl/misc/copy_bsp.sh
for x in lib/sw_apps/zynq_fsbl/src/Makefile lib/sw_apps/zynq_fsbl/misc/copy_bsp.sh lib/bsp/standalone/src/arm/cortexa9/gcc/Makefile; do
substituteInPlace $x \
--replace "arm-none-eabi-" "arm-none-eabihf-"
done
echo 'SEARCH_DIR("${gnutoolchain.newlib}/arm-none-eabi/lib");' >> lib/sw_apps/zynq_fsbl/src/lscript.ld
'';
buildPhase = ''
cd lib/sw_apps/zynq_fsbl/src
@ -96,10 +213,6 @@
dontFixup = true;
};
cargo-xbuild = pkgs.cargo-xbuild.overrideAttrs(oa: {
postPatch = "substituteInPlace src/sysroot.rs --replace 2021 2018";
});
build-crate = name: crate: features: rustPlatform.buildRustPackage rec {
name = "${crate}";
@ -108,9 +221,9 @@
) ./.;
cargoLock = { lockFile = ./Cargo.lock; };
nativeBuildInputs = [ cargo-xbuild pkgs.llvmPackages_14.clang-unwrapped ];
nativeBuildInputs = [ cargo-xbuild pkgs.llvmPackages_9.clang-unwrapped ];
buildPhase = ''
export XARGO_RUST_SRC="${rust}/lib/rustlib/src/rust/library"
export XARGO_RUST_SRC="${rustPlatform.rust.rustc}/lib/rustlib/src/rust/library"
export CARGO_HOME=$(mktemp -d cargo-home.XXX)
pushd ${crate}
cargo xbuild --release --frozen \
@ -127,7 +240,6 @@
doCheck = false;
dontFixup = true;
auditable = false;
};
targetCrates = target: {
@ -153,20 +265,21 @@
hydraJobs = packages.x86_64-linux;
inherit rust rustPlatform;
inherit rustPlatform;
devShell.x86_64-linux = pkgs.mkShell {
name = "zynq-rs-dev-shell";
buildInputs = [
rust
buildInputs = with pkgs; [
rustPlatform.rust.rustc
rustPlatform.rust.cargo
cacert
cargo-xbuild
mkbootimage
pkgs.openocd pkgs.gdb
pkgs.openssh pkgs.rsync
pkgs.llvmPackages_14.clang-unwrapped
(pkgs.python3.withPackages(ps: [ ps.pyftdi ]))
];
};
openocd gdb
openssh rsync
llvmPackages_9.clang-unwrapped
(python3.withPackages(ps: [ ps.pyftdi ]))
mkbootimage ];
};
};
}
}

31
fsbl.patch Normal file
View File

@ -0,0 +1,31 @@
diff --git a/lib/sw_apps/zynq_fsbl/src/Makefile b/lib/sw_apps/zynq_fsbl/src/Makefile
index 0e3ccdf1c5..a5b02f386e 100644
--- a/lib/sw_apps/zynq_fsbl/src/Makefile
+++ b/lib/sw_apps/zynq_fsbl/src/Makefile
@@ -71,11 +71,14 @@ endif
all: $(EXEC)
$(EXEC): $(LIBS) $(OBJS) $(INCLUDES)
- cp $(BSP_DIR)/$(BOARD)/ps7_init.* .
$(LINKER) $(LD1FLAGS) -o $@ $(OBJS) $(LDFLAGS)
rm -rf $(OBJS)
-
-
+
+.PHONY: ps7_init
+
+ps7_init:
+ cp $(BSP_DIR)/$(BOARD)/ps7_init.* .
+
$(LIBS):
echo "Copying BSP files"
$(BSP_DIR)/copy_bsp.sh $(BOARD) $(CC)
@@ -86,7 +89,7 @@ $(LIBS):
make -C $(BSP_DIR) -k all "CC=armcc" "AR=armar" "C_FLAGS= -O2 -c" "EC_FLAGS=--debug --wchar32"; \
fi;
-%.o:%.c
+%.o:%.c ps7_init
$(CC) $(CC_FLAGS) $(CFLAGS) $(ECFLAGS) -c $< -o $@ $(INCLUDEPATH)
%.o:%.S

View File

@ -18,6 +18,8 @@ impl ErrorLED {
.pullup(true)
.disable_rcvr(true)
);
// reset
slcr.gpio_rst_ctrl.reset_gpio();
});
Self::error_led_common(0xFFFF - 0x0080)

View File

@ -13,9 +13,6 @@ mod regs;
pub mod rx;
pub mod tx;
use super::time::Milliseconds;
use embedded_hal::timer::CountDown;
/// Size of all the buffers
pub const MTU: usize = 1536;
/// Maximum MDC clock
@ -303,18 +300,11 @@ impl<GEM: Gem> Eth<GEM, (), ()> {
fn gem_common(macaddr: [u8; 6]) -> Self {
GEM::setup_clock(TX_1000);
#[cfg(feature="target_kasli_soc")]
{
let mut eth_reset_pin = PhyRst::rst_pin();
eth_reset_pin.reset();
}
let mut inner = EthInner {
gem: PhantomData,
link: None,
};
inner.init();
inner.configure(macaddr);
let phy = Phy::find(&mut inner).expect("phy");
@ -492,69 +482,6 @@ impl<'a, GEM: Gem> smoltcp::phy::Device<'a> for &mut Eth<GEM, rx::DescList, tx::
}
}
pub struct PhyRst {
regs: regs::GpioRegisterBlock,
count_down: super::timer::global::CountDown<Milliseconds>,
}
impl PhyRst {
pub fn rst_pin() -> Self {
slcr::RegisterBlock::unlocked(|slcr| {
// Hardware Reset for PHY
slcr.mio_pin_47.write(
slcr::MioPin47::zeroed()
.l3_sel(0b000)
.io_type(slcr::IoBufferType::Lvcmos18)
.pullup(true)
.disable_rcvr(true)
);
});
Self::eth_reset_common(0xFFFF - 0x8000)
}
fn delay_ms(&mut self, ms: u64) {
self.count_down.start(Milliseconds(ms));
nb::block!(self.count_down.wait()).unwrap();
}
fn eth_reset_common(gpio_output_mask: u16) -> Self {
let self_ = Self {
regs: regs::GpioRegisterBlock::regs(),
count_down: unsafe { super::timer::GlobalTimer::get() }.countdown(),
};
// Setup GPIO output mask
self_.regs.gpio_output_mask.modify(|_, w| {
w.mask(gpio_output_mask)
});
self_.regs.gpio_direction.modify(|_, w| {
w.phy_rst(true)
});
self_
}
fn oe(&mut self, oe: bool) {
self.regs.gpio_output_enable.modify(|_, w| {
w.phy_rst(oe)
})
}
fn toggle(&mut self, o: bool) {
self.regs.gpio_output_mask.modify(|_, w| {
w.phy_rst(o)
})
}
pub fn reset(&mut self) {
self.toggle(false); // drive phy_rst (active LOW) pin low
self.oe(true); // enable pin's output
self.delay_ms(10);
self.toggle(true);
}
}
struct EthInner<GEM: Gem> {
gem: PhantomData<GEM>,

View File

@ -110,49 +110,6 @@ pub struct RegisterBlock {
pub design_cfg5: RO<u32>,
}
pub struct GpioRegisterBlock {
pub gpio_output_mask: &'static mut OutputMask,
pub gpio_direction: &'static mut Direction,
pub gpio_output_enable: &'static mut OutputEnable,
}
impl GpioRegisterBlock {
pub fn regs() -> Self {
Self {
gpio_output_mask: OutputMask::new(),
gpio_direction: Direction::new(),
gpio_output_enable: OutputEnable::new(),
}
}
}
register!(gpio_output_mask,
/// MASK_DATA_1_SW:
/// Maskable output data for MIO[47:32]
OutputMask, RW, u32);
register_at!(OutputMask, 0xE000A008, new);
register_bit!(gpio_output_mask,
/// Output for PHY_RST (MIO[47])
phy_rst, 15);
register_bits!(gpio_output_mask,
mask, u16, 16, 31);
register!(gpio_direction,
/// DIRM_1:
/// Direction mode for MIO[53:32]; 0/1 = in/out
Direction, RW, u32);
register_at!(Direction, 0xE000A244, new);
register_bit!(gpio_direction,
/// Direction for PHY_RST
phy_rst, 15);
register!(gpio_output_enable,
/// OEN_1:
/// Output enable for MIO[53:32]
OutputEnable, RW, u32);
register_at!(OutputEnable, 0xE000A248, new);
register_bit!(gpio_output_enable,
/// Output enable for PHY_RST
phy_rst, 15);
register_at!(RegisterBlock, 0xE000B000, gem0);
register_at!(RegisterBlock, 0xE000C000, gem1);

View File

@ -115,7 +115,7 @@ impl InterruptController {
let m = (id.0 >> 2) as usize;
let n = (8 * (id.0 & 3)) as usize;
unsafe {
self.mpcore.icdiptr[m].modify(|mut icdiptr| *icdiptr.set_bits(n..=n+1, target_cpu as u32));
self.mpcore.icdiptr[m].modify(|mut icdiptr| *icdiptr.set_bits(n..=n+1, target_cpu as u32 + 1));
}
// sensitivity

View File

@ -53,6 +53,8 @@ impl I2c {
.pullup(false)
.disable_rcvr(true)
);
// Reset
slcr.gpio_rst_ctrl.reset_gpio();
});
Self::i2c_common(0xFFFF - 0x000C, 0xFFFF - 0x0002)

View File

@ -34,12 +34,6 @@ pub fn isb() {
unsafe { llvm_asm!("isb" :::: "volatile") }
}
/// Enable FIQ
#[inline]
pub unsafe fn enable_fiq() {
llvm_asm!("cpsie f":::: "volatile");
}
/// Enable IRQ
#[inline]
pub unsafe fn enable_irq() {

View File

@ -125,8 +125,8 @@ pub fn dcciall() {
dsb();
}
const CACHE_LINE: usize = 0x20;
const CACHE_LINE_MASK: usize = CACHE_LINE - 1;
pub const CACHE_LINE: usize = 0x20;
pub const CACHE_LINE_MASK: usize = CACHE_LINE - 1;
#[inline]
fn cache_line_addrs(first_addr: usize, beyond_addr: usize) -> impl Iterator<Item = usize> {
@ -136,13 +136,13 @@ fn cache_line_addrs(first_addr: usize, beyond_addr: usize) -> impl Iterator<Item
(first_addr..beyond_addr).step_by(CACHE_LINE)
}
fn object_cache_line_addrs<T>(object: &T) -> impl Iterator<Item = usize> {
pub fn object_cache_line_addrs<T>(object: &T) -> impl Iterator<Item = usize> {
let first_addr = object as *const _ as usize;
let beyond_addr = (object as *const _ as usize) + core::mem::size_of_val(object);
cache_line_addrs(first_addr, beyond_addr)
}
fn slice_cache_line_addrs<T>(slice: &[T]) -> impl Iterator<Item = usize> {
pub fn slice_cache_line_addrs<T>(slice: &[T]) -> impl Iterator<Item = usize> {
let first_addr = &slice[0] as *const _ as usize;
let beyond_addr = (&slice[slice.len() - 1] as *const _ as usize) +
core::mem::size_of_val(&slice[slice.len() - 1]);

View File

@ -36,9 +36,7 @@ pub fn notify_spin_lock() {
}
#[macro_export]
/// Interrupt handler, which setup the stack and preserve registers before jumping to actual interrupt handler.
/// Registers r0-r12, PC, SP and CPSR are restored after the actual handler.
///
/// Interrupt handler, which setup the stack and jump to actual interrupt handler.
/// - `name` is the name of the interrupt, should be the same as the one defined in vector table.
/// - `name2` is the name for the actual handler, should be different from name.
/// - `stack0` is the stack for the interrupt handler when called from core0.
@ -46,7 +44,8 @@ pub fn notify_spin_lock() {
/// - `body` is the body of the actual interrupt handler, should be a normal unsafe rust function
/// body.
///
/// Note that the interrupt handler would use the same stack as normal programs by default.
/// Note that the interrupt handler would use the same stack as normal programs by default, so
/// interrupt handlers should not return to normal program or it may corrupt the stack.
macro_rules! interrupt_handler {
($name:ident, $name2:ident, $stack0:ident, $stack1:ident, $body:block) => {
#[link_section = ".text.boot"]
@ -55,27 +54,19 @@ macro_rules! interrupt_handler {
pub unsafe extern "C" fn $name() -> ! {
asm!(
// setup SP, depending on CPU 0 or 1
// and preserve registers
"sub lr, lr, #4",
"stmfd sp!, {{r0-r12, lr}}",
"mrc p15, #0, r0, c0, c0, #5",
concat!("movw r1, :lower16:", stringify!($stack0)),
concat!("movt r1, :upper16:", stringify!($stack0)),
"tst r0, #3",
concat!("movwne r1, :lower16:", stringify!($stack1)),
concat!("movtne r1, :upper16:", stringify!($stack1)),
"mov r0, sp",
"mov sp, r1",
"push {{r0, r1}}", // 2 registers are pushed to maintain 8 byte stack alignment
concat!("bl ", stringify!($name2)),
"pop {{r0, r1}}",
"mov sp, r0",
"ldmfd sp!, {{r0-r12, pc}}^", // caret ^ : copy SPSR to the CPSR
options(noreturn)
);
}
#[no_mangle]
pub unsafe extern "C" fn $name2() $body
pub unsafe extern "C" fn $name2() -> ! $body
};
}

View File

@ -3,6 +3,7 @@ use libregister::{
RegisterR, RegisterW, RegisterRW,
};
#[macro_export]
macro_rules! def_reg_r {
($name:tt, $type: ty, $asm_instr:tt) => {
impl RegisterR for $name {
@ -18,6 +19,7 @@ macro_rules! def_reg_r {
}
}
#[macro_export]
macro_rules! def_reg_w {
($name:ty, $type:ty, $asm_instr:tt) => {
impl RegisterW for $name {
@ -37,6 +39,7 @@ macro_rules! def_reg_w {
}
}
#[macro_export]
macro_rules! wrap_reg {
($mod_name: ident) => {
pub mod $mod_name {

15
libcortex_r5/Cargo.toml Normal file
View File

@ -0,0 +1,15 @@
[package]
name = "libcortex_r5"
version = "0.0.0"
authors = ["M-Labs"]
edition = "2018"
[features]
power_saving = []
default = []
[dependencies]
bit_field = "0.10"
volatile-register = "0.2"
libregister = { path = "../libregister" }
libcortex_a9 = { path = "../libcortex_a9" }

1
libcortex_r5/src/asm.rs Normal file
View File

@ -0,0 +1 @@
pub use libcortex_a9::asm::*;

87
libcortex_r5/src/cache.rs Normal file
View File

@ -0,0 +1,87 @@
/// Basically same as the Cortex A9 but with no L2
pub use libcortex_a9::cache::{
iciallu,
dcisw,
dccsw,
dccisw,
dccimvac,
dccmvac,
dcimvac,
object_cache_line_addrs,
slice_cache_line_addrs,
CACHE_LINE,
CACHE_LINE_MASK
};
use super::asm::{dmb, dsb};
#[inline(always)]
pub fn dciall() {
unsafe {
llvm_asm!("mcr p15, 0, $0, c15, c5, 0")
}
}
// D$ clean and invalidate
pub fn dcci<T>(object: &T) {
dmb();
for addr in object_cache_line_addrs(object) {
dccimvac(addr);
}
dsb();
}
pub fn dcci_slice<T>(slice: &[T]) {
dmb();
for addr in slice_cache_line_addrs(slice) {
dccimvac(addr);
}
dsb();
}
// D$ clean
pub fn dcc<T>(object: &T) {
dmb();
for addr in object_cache_line_addrs(object) {
dccmvac(addr);
}
dsb();
}
pub fn dcc_slice<T>(slice: &[T]) {
if slice.len() == 0 {
return;
}
dmb();
for addr in slice_cache_line_addrs(slice) {
dccmvac(addr);
}
dsb();
}
// D$ invalidate
pub unsafe fn dci<T>(object: &mut T) {
let first_addr = object as *const _ as usize;
let beyond_addr = (object as *const _ as usize) + core::mem::size_of_val(object);
assert_eq!(first_addr & CACHE_LINE_MASK, 0, "dci object first_addr must be aligned");
assert_eq!(beyond_addr & CACHE_LINE_MASK, 0, "dci object beyond_addr must be aligned");
dmb();
for addr in (first_addr..beyond_addr).step_by(CACHE_LINE) {
dcimvac(addr);
}
dsb();
}
pub unsafe fn dci_slice<T>(slice: &mut [T]) {
let first_addr = &slice[0] as *const _ as usize;
let beyond_addr = (&slice[slice.len() - 1] as *const _ as usize) +
core::mem::size_of_val(&slice[slice.len() - 1]);
assert_eq!(first_addr & CACHE_LINE_MASK, 0, "dci slice first_addr must be aligned");
assert_eq!(beyond_addr & CACHE_LINE_MASK, 0, "dci slice beyond_addr must be aligned");
dmb();
for addr in (first_addr..beyond_addr).step_by(CACHE_LINE) {
dcimvac(addr);
}
dsb();
}

10
libcortex_r5/src/lib.rs Normal file
View File

@ -0,0 +1,10 @@
#![no_std]
#![feature(llvm_asm, global_asm)]
#![feature(never_type)]
#![feature(const_fn)]
extern crate alloc;
pub mod asm;
pub mod regs;
pub mod cache;

374
libcortex_r5/src/regs.rs Normal file
View File

@ -0,0 +1,374 @@
use libregister::{
register_bit, register_bits,
RegisterR, RegisterW, RegisterRW,
};
use libcortex_a9::{def_reg_r, def_reg_w, wrap_reg};
pub use libcortex_a9::regs::{SP, LR};
/// Multiprocessor Affinity Register
pub struct MPIDR;
wrap_reg!(mpidr);
def_reg_r!(MPIDR, mpidr::Read, "mrc p15, 0, $0, c0, c0, 5");
// CPU ID
register_bits!(mpidr, cpu_id, u8, 0, 7);
// group ID
register_bits!(mpidr, group_id, u8, 8, 15);
// 0b11 if part of uniprocessor system
register_bits!(mpidr, u, u8, 30, 31);
/// System Control Register
pub struct SCTLR;
wrap_reg!(sctlr);
def_reg_r!(SCTLR, sctlr::Read, "mrc p15, 0, $0, c1, c0, 0");
def_reg_w!(SCTLR, sctlr::Write, "mrc p15, 0, $0, c1, c0, 0");
// MPU enable
register_bit!(sctlr, m, 0);
// strict alignment
register_bit!(sctlr, a, 1);
// L1 data cache enable
register_bit!(sctlr, c, 2);
// enable SWP and SWPB instructions
register_bit!(sctlr, sw, 10);
// enable branch prediction (SBO)
register_bit!(sctlr, z, 11);
// L1 instruction cache enable
register_bit!(sctlr, i, 12);
// Determines the location of exception vectors:
// 0 = normal exception vectors selected, address range = 0x00000000-0x0000001C
// 1 = high exception vectors (HIVECS) selected, address range = 0xFFFF0000-0xFFFF001C.
// The primary input VINITHIm defines the reset value.
register_bit!(sctlr, v, 13);
// Round-robin bit, controls replacement strategy for instruction and data caches:
// 0 = random replacement strategy
// 1 = round-robin replacement strategy.
// The reset value of this bit is 0. The processor always uses a random replacement strategy, regardless of the state
// of this bit.
register_bit!(sctlr, rr, 14);
// MPU background region enable
register_bit!(sctlr, br, 17);
// Divide by zero:
// 0 = do not generate an Undefined Instruction exception
// 1 = generate an Undefined Instruction exception.
// The reset value of this bit is 0
register_bit!(sctlr, dz, 19);
// Fast Interrupts enable.
// On the processor Fast Interrupts are always enabled. This bit is SBO
register_bit!(sctlr, fi, 21);
// Configures vectored interrupt:
// 0 = exception vector address for IRQ is 0x00000018 or 0xFFFF0018. See V bit.
// 1 = VIC controller provides handler address for IRQ.
// The reset value of this bit is 0.
register_bit!(sctlr, ve, 24);
// Determines how the E bit in the CPSR is set on an exception:
// 0 = CPSR E bit is set to 0 on an exception
// 1 = CPSR E bit is set to 1 on an exception.
// The primary input CFGEE defines the reset value.
register_bit!(sctlr, ee, 25);
// NMFI, non-maskable fast interrupt enable:
// 0 = Software can disable FIQs
// 1 = Software cannot disable FIQs.
// This bit is read-only. The configuration input CFGNMFIm defines its value.
register_bit!(sctlr, nmfi, 27);
// TEX Remap Enable. On the processor this bit is SBZ.
register_bit!(sctlr, tre, 28);
// Access Flag Enable. On the processor this bit is SBZ.
register_bit!(sctlr, afe, 29);
// Thumb exception enable:
// 0 = enable ARM exception generation
// 1 = enable Thumb exception generation.
// The primary input TEINIT defines the reset value
register_bit!(sctlr, te, 30);
// Identifies little or big instruction endianness in use:
// 0 = little-endianness
// 1 = big-endianness.
// The primary input CFGIE defines the value. This bit is read-only
register_bit!(sctlr, ie, 31);
/// Auxiliary Control Register
pub struct ACTLR;
wrap_reg!(actlr);
def_reg_r!(ACTLR, actlr::Read, "mrc p15, 0, $0, c1, c0, 1");
def_reg_w!(ACTLR, actlr::Write, "mcr p15, 0, $0, c1, c0, 1");
// A(B0/1)TCM external error enable:
// 0 = Disabled
// 1 = Enabled.
// The primary input ERRENRAMm[0] defines the reset value.
register_bit!(actlr, atcmecen, 0);
register_bit!(actlr, b0tcmecen, 1);
register_bit!(actlr, b1tcmecen, 2);
// Cache error control for cache parity and ECC errors
register_bits!(actlr, cec, u8, 3, 5);
// Disable low interrupt latency on all load/store instructions
register_bit!(actlr, dils, 6);
// sMOV of a divide does not complete out of order.
// No other instruction is issued until the divide is finished
register_bit!(actlr, smov, 7);
// Force D-side to not-shared when MPU is off:
// 0 = Normal operation. This is the reset value.
// 1 = D-side normal Non-cacheable forced to Non-shared when MPU is off.
register_bit!(actlr, fdsns, 8);
// Force write-through (WT) for write-back (WB) regions:
// 0 = No forcing of WT. This is the reset value.
// 1 = WT forced for WB regions
register_bit!(actlr, fwt, 9);
// Force outer read allocate (ORA) for outer write allocate (OWA) regions:
// 0 = No forcing of ORA. This is the reset value.
// 1 = ORA forced for OWA regions
register_bit!(actlr, fora, 10);
// Disable data forwarding for Non-cacheable accesses in the AXI master:
// 0 = Normal operation. This is the reset value.
// 1 = Disable data forwarding for Non-cacheable accesses
register_bit!(actlr, dnch, 11);
// Enable random parity error generation:
// 0 = Random parity error generation disabled. This is the reset value.
// 1 = Enable random parity error generation in the cache RAMs.
register_bit!(actlr, erpeg, 12);
// Disable linefill optimization in the AXI master:
// 0 = Normal operation. This is the reset value.
// 1 = Limits the number of outstanding data linefills to two
register_bit!(actlr, dlfo, 13);
// Disable write burst in the AXI master:
// 0 = Normal operation. This is the reset value.
// 1 = Disable write burst optimization
register_bit!(actlr, dbwr, 14);
// This field controls the branch prediction policy:
// b00 = Normal operation. This is the reset value.
// b01 = Branch always taken and history table updates disabled.
// b10 = Branch always not taken and history table updates disabled.
// b11 = Reserved. Behavior is Unpredictable if this field is set to b11
register_bits!(actlr, bp, u8, 15, 16);
// Return stack disable:
// 0 = Normal return stack operation. This is the reset value.
// 1 = Return stack disabled.
register_bit!(actlr, rsdis, 17);
// Fetch rate control disable:
// 0 = Normal fetch rate control operation. This is the reset value.
// 1 = Fetch rate control disabled.
register_bit!(actlr, frcdis, 19);
// Disable Branch History (BH) extension:
// 0 = Enable the extension. This is the reset value.
// 1 = Disable the extension
register_bit!(actlr, dbhe, 20);
// Disable end of loop prediction:
// 0 = Enable loop prediction. This is the reset value.
// 1 = Disable loop prediction.
register_bit!(actlr, deolp, 21);
// Disable Low Interrupt Latency (LIL) on load/store multiples:
// 0 = Enable LIL on load/store multiples. This is the reset value.
// 1 = Disable LIL on all load/store multiples.
register_bit!(actlr, dilsm, 22);
// AXI slave cache RAM non-privileged access enable:
// 0 = Disabled. This is the reset value.
// 1 = Enabled
register_bit!(actlr, axiscuen, 23);
// AXI slave cache RAM access enable:
// 0 = Disabled. This is the reset value.
// 1 = Enabled
register_bit!(actlr, axiscen, 24);
// A(B0/1)TCM ECC check enable:
// 0 = Disabled
// 1 = Enabled
// The primary input PARECCENRAMm[0] defines the reset value
register_bit!(actlr, atcmpcen, 25);
register_bit!(actlr, b0tcmpcen, 26);
register_bit!(actlr, b1tcmpcen, 27);
// Case A(B1, B2, C) dual issue control:
// 0 = Enabled. This is the reset value.
// 1 = Disabled.
register_bit!(actlr, diadi, 28);
register_bit!(actlr, dib1di, 29);
register_bit!(actlr, dib2di, 30);
register_bit!(actlr, dicdi, 31);
/// Secondary Auxiliary Control Register
pub struct SACTLR;
wrap_reg!(sactlr);
def_reg_r!(SACTLR, sactlr::Read, "mrc p15, 0, $0, c15, c0, 0");
def_reg_w!(SACTLR, sactlr::Write, "mcr p15, 0, $0, c15, c0, 0");
// Enables 64-bit stores for the ATCM. When enabled, the processor uses read-modify-write to ensure that all
// reads and writes presented on the ATCM port are 64 bits wide.
// 0 = Disabled
// 1 = Enabled.
// The primary input RMWENRAMm[0] defines the reset value.
register_bit!(sactlr, atcmrmw, 0);
register_bit!(sactlr, btcmrmw, 1);
// Correction for internal ECC logic on ATCM port.
// 0 = Enabled. This is the reset value.
// 1 = Disabled
register_bit!(sactlr, atcmecc, 2);
register_bit!(sactlr, btcmecc, 3);
// Floating-point input denormal exception output mask.
// 0 = Mask floating-point input denormal exception output. The output FPIDCm is forced to zero. This is
// the reset value.
// 1 = Propagate floating-point input denormal exception flag FPSCR.IDC to output FPIDCm
register_bit!(sactlr, idc, 8);
// Floating-point divide-by-zero exception output mask.
// 0 = Mask floating-point divide-by-zero exception output. The output FPDZCm is forced to zero. This is
// the reset value.
// 1 = Propagate floating-point divide-by-zero exception flag FPSCR.DZC to output FPDZCm
register_bit!(sactlr, dzc, 9);
// Floating-point invalid operation exception output mask.
// 0 = Mask floating-point invalid operation exception output. The output FPIOCm is forced to zero. This is
// the reset value.
// 1 = Propagate floating-point invalid operation exception flag FPSCR.IOC to output FPIOCm.
register_bit!(sactlr, ioc, 10);
// Floating-point underflow exception output mask.
// 0 = Mask floating-point underflow exception output. The output FPUFCm is forced to zero. This is the
// reset value.
// 1 = Propagate floating-point underflow exception flag FPSCR.UFC to output FPUFCm
register_bit!(sactlr, ufc, 11);
// Floating-point overflow exception output mask.
// 0 = Mask floating-point overflow exception output. The output FPOFCm is forced to zero. This is the reset
// value.
// 1 = Propagate floating-point overflow exception flag FPSCR.OFC to output FPOFCm
register_bit!(sactlr, ofc, 12);
// Floating-point inexact exception output mask.
// 0 = Mask floating-point inexact exception output. The output FPIXCm is forced to zero. This is the reset
// value.
// 1 = Propagate floating point inexact exception flag FPSCR.IXC to output FPIXCm
register_bit!(sactlr, ixc, 13);
// Out-of-order FMACS control.
// 0 = Enabled. This is the reset value.
// 1 = Disabled
register_bit!(sactlr, doofmacs, 16);
// Out-of-order double-precision floating point instruction control.
// 0 = Enabled. This is the reset value.
// 1 = Disabled.
register_bit!(sactlr, doodpfp, 17);
// F1/F3/F4dual issue control.
// 0 = Enabled. This is the reset value.
// 1 = Disabled.
register_bit!(sactlr, ddi, 18);
// F2_Id/F2_st/F2D dual issue control.
// 0 = Enabled. This is the reset value.
// 1 = Disabled
register_bit!(sactlr, df2di, 19);
// F6 dual issue control.
// 0 = Enabled. This is the reset value.
// 1 = Disabled
register_bit!(sactlr, df6di, 20);
// Enable random 2-bit error generation in cache RAMs. This bit has no effect unless ECC is configured, see
// Configurable options on page 1-6.
// 0 = Disabled. This is the reset value.
// 1 = Enabled.
register_bit!(sactlr, dr2b, 21);
// Disable hard-error support in the caches.
// 0 = Enabled. The cache logic recovers from some hard errors.
// 1 = Disabled. Most hard errors in the caches are fatal. This is the reset value
register_bit!(sactlr, dche, 22);
/// Data Fault Status Register
pub struct DFSR;
wrap_reg!(dfsr);
def_reg_r!(DFSR, dfsr::Read, "mrc p15, 0, $0, c5, c0, 0");
// Indicates the type of fault generated. To determine the data fault, you must use bit [12] and bit [10] in
// conjunction with bits [3:0].
register_bits!(dfsr, status, u8, 0, 3);
register_bit!(dfsr, s, 10);
// Indicates whether a read or write access caused an abort:
// 0 = read access caused the abort
// 1 = write access caused the abort.
register_bit!(dfsr, rw, 11);
// Distinguishes between an AXI Decode or Slave error on an external abort. This bit is only valid for external
// aborts. For all other aborts types of abort, this bit is set to zero:
// 0 = AXI Decode error (DECERR), or AHB error, caused the abort
// 1 = AXI Slave error (SLVERR), or unsupported exclusive access, for example exclusive access using the AHB
// peripheral port, caused the abort.
register_bit!(dfsr, sd, 12);
/// Data Fault Address Register
pub struct DFAR;
def_reg_r!(DFAR, u32, "mrc p15, 0, $0, c6, c0, 0");
/// MPU Type Register
pub struct MPUIR;
wrap_reg!(mpuir);
def_reg_r!(MPUIR, mpuir::Read, "mrc p15, 0, $0, c0, c0, 4");
// number of unified MPU regions (0, 12, or 16)
register_bits!(mpuir, d_region, u8, 8, 15);
/// MPU Region Number Register
pub struct RGNR;
wrap_reg!(rgnr);
def_reg_r!(RGNR, rgnr::Read, "mrc p15, 0, $0, c6, c2, 0");
def_reg_w!(RGNR, rgnr::Write, "mcr p15, 0, $0, c6, c2, 0");
register_bits!(rgnr, region, u8, 0, 3);
/// MPU Region Access Control Registers
pub struct RACTLR;
wrap_reg!(ractlr);
def_reg_r!(RACTLR, ractlr::Read, "mrc p15, 0, $0, c6, c1, 4");
def_reg_w!(RACTLR, ractlr::Write, "mcr p15, 0, $0, c6, c1, 4");
// bufferable
register_bit!(ractlr, b, 0);
// cacheable
register_bit!(ractlr, c, 1);
// shareable
register_bit!(ractlr, s, 2);
// type extension
register_bits!(ractlr, tex, u8, 3, 5);
// Access permission
register_bits!(ractlr, ap, u8, 8, 10);
// Execute Never. Determines if a region of memory is executable:
// 0 = all instruction fetches enabled
// 1 = no instruction fetches enabled.
register_bit!(ractlr, xn, 12);
/// MPU Region Size and Enable Registers
pub struct RSER;
wrap_reg!(rser);
def_reg_r!(RSER, rser::Read, "mrc p15, 0, $0, c6, c1, 2");
def_reg_w!(RSER, rser::Write, "mcr p15, 0, $0, c6, c1, 2");
// enable region
register_bit!(rser, enable, 0);
// Defines the region size:
// b00000 - b00011=Unpredictable
// b00100 = 32 bytes
// b00101 = 64 bytes
// b00110 = 128 bytes
// b00111 = 256 bytes
// b01000 = 512 bytes
// b01001 = 1KB
// b01010 = 2KB
// b01011 = 4KB
// b01100 = 8KB
// b01101 = 16KB
// b01110 = 32KB
// b01111 = 64KB
// b10000 = 128KB
// b10001 = 256KB
// b10010 = 512KB
// b10011 = 1MB
// b10100 = 2MB
// b10101 = 4MB
// b10110 = 8MB
// b10111 = 16MB
// b11000 = 32MB
// b11001 = 64MB
// b11010 = 128MB
// b11011 = 256MB
// b11100 = 512MB
// b11101 = 1GB
// b11110 = 2GB
// b11111 = 4GB
register_bits!(rser, region_size, u8, 1, 5);
// Each bit position represents a sub-region, 0-7.
// Bit [8] corresponds to sub-region 0
// ...
// Bit [15] corresponds to sub-region 7
// The meaning of each bit is:
// 0 = address range is part of this region
// 1 = address range is not part of this region
register_bits!(rser, subregion_disable, u8, 8, 15);
/// MPU Region Base Address Registers
pub struct RBAR;
wrap_reg!(rbar);
def_reg_r!(RBAR, rbar::Read, "mrc p15, 0, $0, c6, c1, 0");
def_reg_w!(RBAR, rbar::Write, "mcr p15, 0, $0, c6, c1, 0");
// base address
register_bits!(rbar, base_address, u32, 5, 31);
// TODO: TCM registers (R5 TRM sec 4.3.23-25)

View File

@ -12,10 +12,9 @@ target_redpitaya = ["libboard_zynq/target_redpitaya"]
target_kasli_soc = ["libboard_zynq/target_kasli_soc"]
panic_handler = []
dummy_irq_handler = []
dummy_fiq_handler = []
alloc_core = []
default = ["panic_handler", "dummy_irq_handler", "dummy_fiq_handler"]
default = ["panic_handler", "dummy_irq_handler"]
[dependencies]
r0 = "1"

View File

@ -1,10 +1,6 @@
use libregister::{RegisterR, RegisterW};
use libcortex_a9::{regs::{DFSR, MPIDR, VBAR}, interrupt_handler};
use libboard_zynq::{println, stdio};
pub fn set_vector_table(base_addr: u32){
VBAR.write(base_addr);
}
use libregister::RegisterR;
use libcortex_a9::{regs::{DFSR, MPIDR}, interrupt_handler};
use libboard_zynq::{println, stdio};
interrupt_handler!(UndefinedInstruction, undefined_instruction, __irq_stack0_start, __irq_stack1_start, {
stdio::drop_uart();
@ -46,7 +42,6 @@ interrupt_handler!(IRQ, irq, __irq_stack0_start, __irq_stack1_start, {
loop {}
});
#[cfg(feature = "dummy_fiq_handler")]
interrupt_handler!(FIQ, fiq, __irq_stack0_start, __irq_stack1_start, {
stdio::drop_uart();
println!("FIQ");

View File

@ -54,7 +54,6 @@ unsafe extern "C" fn boot_core0() -> ! {
asm::dmb();
asm::dsb();
asm::enable_fiq();
asm::enable_irq();
main_core0();
panic!("return from main");
@ -76,7 +75,6 @@ unsafe extern "C" fn boot_core1() -> ! {
asm::dmb();
asm::dsb();
asm::enable_fiq();
asm::enable_irq();
main_core1();
panic!("return from main_core1");

View File

@ -9,7 +9,7 @@ pub extern crate alloc;
pub extern crate compiler_builtins;
pub mod boot;
pub mod exception_vectors;
mod abort;
#[cfg(feature = "panic_handler")]
mod panic;
pub mod ram;