Compare commits

...

7 Commits

9 changed files with 505 additions and 4 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

@ -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

@ -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)