use core::fmt;
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Exception(pub u16);
impl Exception {
pub const THREAD: Exception = Exception(0);
pub const RESET: Exception = Exception(1);
pub const NMI: Exception = Exception(2);
pub const HARD_FAULT: Exception = Exception(3);
pub const MEM_MANAGE: Exception = Exception(4);
pub const BUS_FAULT: Exception = Exception(5);
pub const USAGE_FAULT: Exception = Exception(6);
pub const SVCALL: Exception = Exception(11);
pub const DEBUG_MONITOR: Exception = Exception(12);
pub const PEND_SV: Exception = Exception(14);
pub const SYSTICK: Exception = Exception(15);
pub const IRQ0: Exception = Exception(16);
pub const COUNT: usize = 256;
#[must_use]
pub const fn vector_offset(self) -> u32 {
(self.0 as u32) * 4
}
#[must_use]
pub const fn is_configurable_fault(self) -> bool {
matches!(self.0, 4..=6)
}
#[must_use]
pub fn name(self) -> &'static str {
match self.0 {
0 => "thread",
1 => "reset",
2 => "nmi",
3 => "hardfault",
4 => "memmanage",
5 => "busfault",
6 => "usagefault",
11 => "svcall",
12 => "debugmon",
14 => "pendsv",
15 => "systick",
16.. => "irq",
_ => "reserved",
}
}
}
impl fmt::Display for Exception {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.0 >= 16 {
write!(f, "irq{}", self.0 - 16)
} else {
f.write_str(self.name())
}
}
}
pub mod fsr {
pub const MM_IACCVIOL: u32 = 1 << 0;
pub const MM_DACCVIOL: u32 = 1 << 1;
pub const MM_MUNSTKERR: u32 = 1 << 3;
pub const MM_MSTKERR: u32 = 1 << 4;
pub const MM_MMARVALID: u32 = 1 << 7;
pub const BF_IBUSERR: u32 = 1 << 8;
pub const BF_PRECISERR: u32 = 1 << 9;
pub const BF_IMPRECISERR: u32 = 1 << 10;
pub const BF_UNSTKERR: u32 = 1 << 11;
pub const BF_STKERR: u32 = 1 << 12;
pub const BF_BFARVALID: u32 = 1 << 15;
pub const UF_UNDEFINSTR: u32 = 1 << 16;
pub const UF_INVSTATE: u32 = 1 << 17;
pub const UF_INVPC: u32 = 1 << 18;
pub const UF_NOCP: u32 = 1 << 19;
pub const UF_UNALIGNED: u32 = 1 << 24;
pub const UF_DIVBYZERO: u32 = 1 << 25;
pub const HF_VECTTBL: u32 = 1 << 1;
pub const HF_FORCED: u32 = 1 << 30;
pub const HF_DEBUGEVT: u32 = 1 << 31;
}
pub mod ccr {
pub const NONBASETHRDENA: u32 = 1 << 0;
pub const USERSETMPEND: u32 = 1 << 1;
pub const UNALIGN_TRP: u32 = 1 << 3;
pub const DIV_0_TRP: u32 = 1 << 4;
pub const BFHFNMIGN: u32 = 1 << 8;
pub const STKALIGN: u32 = 1 << 9;
}
pub mod shcsr {
pub const MEMFAULTENA: u32 = 1 << 16;
pub const BUSFAULTENA: u32 = 1 << 17;
pub const USGFAULTENA: u32 = 1 << 18;
}
pub mod control {
pub const NPRIV: u32 = 1 << 0;
pub const SPSEL: u32 = 1 << 1;
pub const FPCA: u32 = 1 << 2;
}
pub mod exc_return {
pub const HANDLER_MSP: u32 = 0xffff_fff1;
pub const THREAD_MSP: u32 = 0xffff_fff9;
pub const THREAD_PSP: u32 = 0xffff_fffd;
#[must_use]
pub const fn is_magic(value: u32) -> bool {
value & 0xf000_0000 == 0xf000_0000
}
}
const WORDS: usize = Exception::COUNT / 32;
pub const CPUID_CORTEX_M4: u32 = 0x410f_c241;
pub const CPUID_CORTEX_M7: u32 = 0x411f_c271;
#[derive(Debug, Clone)]
pub struct Sys {
pub enable: [u32; WORDS],
pub pending: [u32; WORDS],
pub active: [u32; WORDS],
pub priority: [u8; Exception::COUNT],
pub priority_bits: u8,
pub vtor: u32,
pub prigroup: u8,
pub scr: u32,
pub ccr: u32,
pub shcsr: u32,
pub cfsr: u32,
pub hfsr: u32,
pub mmfar: u32,
pub bfar: u32,
pub afsr: u32,
pub cpacr: u32,
pub cpuid: u32,
pub reset_requested: bool,
pub syst_csr: u32,
pub syst_rvr: u32,
pub syst_cvr: u32,
pub syst_calib: u32,
pub mpu_ctrl: u32,
pub mpu_rnr: u32,
pub mpu_rbar: [u32; MPU_REGIONS],
pub mpu_rasr: [u32; MPU_REGIONS],
pub mpu_regions: u8,
}
pub const MPU_REGIONS: usize = 8;
impl Default for Sys {
fn default() -> Sys {
Sys::new(CPUID_CORTEX_M4, 8, MPU_REGIONS as u8)
}
}
impl Sys {
#[must_use]
pub fn new(cpuid: u32, priority_bits: u8, mpu_regions: u8) -> Sys {
let mut sys = Sys {
enable: [0; WORDS],
pending: [0; WORDS],
active: [0; WORDS],
priority: [0; Exception::COUNT],
priority_bits: priority_bits.clamp(1, 8),
vtor: 0,
prigroup: 0,
scr: 0,
ccr: ccr::STKALIGN,
shcsr: 0,
cfsr: 0,
hfsr: 0,
mmfar: 0,
bfar: 0,
afsr: 0,
cpacr: 0,
cpuid,
reset_requested: false,
syst_csr: 0,
syst_rvr: 0,
syst_cvr: 0,
syst_calib: (1 << 31) | (1 << 30),
mpu_ctrl: 0,
mpu_rnr: 0,
mpu_rbar: [0; MPU_REGIONS],
mpu_rasr: [0; MPU_REGIONS],
mpu_regions: mpu_regions.min(MPU_REGIONS as u8),
};
for n in [1u16, 2, 3, 11, 14, 15] {
sys.set_enable(Exception(n), true);
}
sys
}
#[inline]
fn get(map: &[u32; WORDS], e: Exception) -> bool {
let n = e.0 as usize;
n < Exception::COUNT && map[n / 32] & (1 << (n % 32)) != 0
}
#[inline]
fn set(map: &mut [u32; WORDS], e: Exception, on: bool) {
let n = e.0 as usize;
if n >= Exception::COUNT {
return;
}
if on {
map[n / 32] |= 1 << (n % 32);
} else {
map[n / 32] &= !(1 << (n % 32));
}
}
#[must_use]
pub fn is_enabled(&self, e: Exception) -> bool {
match e.0 {
4 => self.shcsr & shcsr::MEMFAULTENA != 0,
5 => self.shcsr & shcsr::BUSFAULTENA != 0,
6 => self.shcsr & shcsr::USGFAULTENA != 0,
_ => Sys::get(&self.enable, e),
}
}
pub fn set_enable(&mut self, e: Exception, on: bool) {
Sys::set(&mut self.enable, e, on);
}
#[must_use]
pub fn is_pending(&self, e: Exception) -> bool {
Sys::get(&self.pending, e)
}
pub fn set_pending(&mut self, e: Exception, on: bool) {
Sys::set(&mut self.pending, e, on);
}
#[must_use]
pub fn is_active(&self, e: Exception) -> bool {
Sys::get(&self.active, e)
}
pub fn set_active(&mut self, e: Exception, on: bool) {
Sys::set(&mut self.active, e, on);
}
#[must_use]
pub fn any_active(&self) -> bool {
self.active.iter().any(|w| *w != 0)
}
#[must_use]
pub fn active_count(&self) -> u32 {
self.active.iter().map(|w| w.count_ones()).sum()
}
#[must_use]
pub const fn group_mask(&self) -> u8 {
let sub_bits = (self.prigroup as u32) + 1;
if sub_bits >= 8 { 0 } else { (!0u8) << sub_bits }
}
#[must_use]
pub fn priority_of(&self, e: Exception) -> i32 {
match e.0 {
1 => -3,
2 => -2,
3 => -1,
_ => i32::from(
self.priority[(e.0 as usize) & (Exception::COUNT - 1)] & self.group_mask(),
),
}
}
#[must_use]
pub const fn quantize_priority(&self, value: u8) -> u8 {
let drop = 8 - self.priority_bits;
if drop >= 8 {
0
} else {
(value >> drop) << drop
}
}
#[must_use]
pub fn highest_pending(&self) -> Option<(Exception, i32)> {
let mut best: Option<(Exception, i32)> = None;
for (word_index, word) in self.pending.iter().enumerate() {
let mut bits = *word;
while bits != 0 {
let bit = bits.trailing_zeros();
bits &= bits - 1;
let e = Exception((word_index * 32 + bit as usize) as u16);
if !self.is_enabled(e) {
continue;
}
let p = self.priority_of(e);
match best {
Some((_, bp)) if bp <= p => {}
_ => best = Some((e, p)),
}
}
}
best
}
#[must_use]
pub fn active_priority(&self) -> i32 {
let mut prio = 256;
for (word_index, word) in self.active.iter().enumerate() {
let mut bits = *word;
while bits != 0 {
let bit = bits.trailing_zeros();
bits &= bits - 1;
let e = Exception((word_index * 32 + bit as usize) as u16);
let p = self.priority_of(e);
if p < prio {
prio = p;
}
}
}
prio
}
#[must_use]
pub fn vect_pending(&self) -> u32 {
self.highest_pending().map_or(0, |(e, _)| u32::from(e.0))
}
}
pub const PPB_BASE: u32 = 0xe000_0000;
pub const PPB_END: u32 = 0xe010_0000;
#[inline]
#[must_use]
pub const fn in_ppb(addr: u32) -> bool {
addr >= PPB_BASE && addr < PPB_END
}
impl Sys {
#[must_use]
#[allow(clippy::too_many_lines)] pub fn read_word(&mut self, addr: u32, debug: bool) -> Option<u32> {
let word = addr & !3;
Some(match word {
0xe000_e010 => {
let v = self.syst_csr;
if !debug {
self.syst_csr &= !(1 << 16);
}
v
}
0xe000_e014 => self.syst_rvr,
0xe000_e018 => self.syst_cvr,
0xe000_e01c => self.syst_calib,
0xe000_e100..=0xe000_e11c => self.irq_word(&self.enable, word - 0xe000_e100),
0xe000_e180..=0xe000_e19c => self.irq_word(&self.enable, word - 0xe000_e180),
0xe000_e200..=0xe000_e21c => self.irq_word(&self.pending, word - 0xe000_e200),
0xe000_e280..=0xe000_e29c => self.irq_word(&self.pending, word - 0xe000_e280),
0xe000_e300..=0xe000_e31c => self.irq_word(&self.active, word - 0xe000_e300),
0xe000_e400..=0xe000_e4ec => {
let first = 16 + (word - 0xe000_e400);
let mut v = 0;
for k in 0..4 {
let n = (first + k) as usize;
if n < Exception::COUNT {
v |= u32::from(self.priority[n]) << (8 * k);
}
}
v
}
0xe000_ed00 => self.cpuid,
0xe000_ed04 => self.icsr(),
0xe000_ed08 => self.vtor,
0xe000_ed0c => 0xfa05_0000 | (u32::from(self.prigroup) << 8),
0xe000_ed10 => self.scr,
0xe000_ed14 => self.ccr,
0xe000_ed18..=0xe000_ed20 => {
let first = 4 + (word - 0xe000_ed18);
let mut v = 0;
for k in 0..4 {
v |= u32::from(self.priority[(first + k) as usize]) << (8 * k);
}
v
}
0xe000_ed24 => self.shcsr_read(),
0xe000_ed28 => self.cfsr,
0xe000_ed2c => self.hfsr,
0xe000_ed30 => 0,
0xe000_ed34 => self.mmfar,
0xe000_ed38 => self.bfar,
0xe000_ed3c => self.afsr,
0xe000_ed88 => self.cpacr,
0xe000_ed90 => u32::from(self.mpu_regions) << 8,
0xe000_ed94 => self.mpu_ctrl,
0xe000_ed98 => self.mpu_rnr,
0xe000_ed9c | 0xe000_eda4 | 0xe000_edac | 0xe000_edb4 => {
let n = (self.mpu_rnr as usize) & (MPU_REGIONS - 1);
self.mpu_rbar[n]
}
0xe000_eda0 | 0xe000_eda8 | 0xe000_edb0 | 0xe000_edb8 => {
let n = (self.mpu_rnr as usize) & (MPU_REGIONS - 1);
self.mpu_rasr[n]
}
0xe000_ef00 => 0,
_ if in_ppb(word) => 0,
_ => return None,
})
}
#[allow(clippy::too_many_lines)] pub fn write_word(&mut self, addr: u32, value: u32) -> bool {
let word = addr & !3;
match word {
0xe000_e010 => {
self.syst_csr = (self.syst_csr & (1 << 16)) | (value & 0x7);
}
0xe000_e014 => self.syst_rvr = value & 0x00ff_ffff,
0xe000_e018 => {
self.syst_cvr = 0;
self.syst_csr &= !(1 << 16);
}
0xe000_e01c => {}
0xe000_e100..=0xe000_e11c => self.irq_set(Bitmap::Enable, word - 0xe000_e100, value),
0xe000_e180..=0xe000_e19c => self.irq_clear(Bitmap::Enable, word - 0xe000_e180, value),
0xe000_e200..=0xe000_e21c => self.irq_set(Bitmap::Pending, word - 0xe000_e200, value),
0xe000_e280..=0xe000_e29c => self.irq_clear(Bitmap::Pending, word - 0xe000_e280, value),
0xe000_e300..=0xe000_e31c => {}
0xe000_e400..=0xe000_e4ec => {
let first = 16 + (word - 0xe000_e400);
for k in 0..4 {
let n = (first + k) as usize;
if n < Exception::COUNT {
self.priority[n] = self.quantize_priority((value >> (8 * k)) as u8);
}
}
}
0xe000_ed00 => {}
0xe000_ed04 => {
if value & (1 << 31) != 0 {
self.set_pending(Exception::NMI, true);
}
if value & (1 << 28) != 0 {
self.set_pending(Exception::PEND_SV, true);
}
if value & (1 << 27) != 0 {
self.set_pending(Exception::PEND_SV, false);
}
if value & (1 << 26) != 0 {
self.set_pending(Exception::SYSTICK, true);
}
if value & (1 << 25) != 0 {
self.set_pending(Exception::SYSTICK, false);
}
}
0xe000_ed08 => self.vtor = value & 0xffff_ff80,
0xe000_ed0c => {
if value >> 16 != 0x05fa {
return true;
}
self.prigroup = ((value >> 8) & 7) as u8;
if value & (1 << 2) != 0 {
self.reset_requested = true;
}
if value & (1 << 1) != 0 {
self.active = [0; WORDS];
}
}
0xe000_ed10 => self.scr = value & 0x1e,
0xe000_ed14 => self.ccr = (value & 0x0000_031b) | ccr::STKALIGN,
0xe000_ed18..=0xe000_ed20 => {
let first = 4 + (word - 0xe000_ed18);
for k in 0..4 {
let n = (first + k) as usize;
self.priority[n] = self.quantize_priority((value >> (8 * k)) as u8);
}
}
0xe000_ed24 => {
self.shcsr = value & (shcsr::MEMFAULTENA | shcsr::BUSFAULTENA | shcsr::USGFAULTENA);
self.set_pending(Exception::USAGE_FAULT, value & (1 << 12) != 0);
self.set_pending(Exception::MEM_MANAGE, value & (1 << 13) != 0);
self.set_pending(Exception::BUS_FAULT, value & (1 << 14) != 0);
self.set_pending(Exception::SVCALL, value & (1 << 15) != 0);
}
0xe000_ed28 => self.cfsr &= !value,
0xe000_ed2c => self.hfsr &= !value,
0xe000_ed30 => {}
0xe000_ed34 => self.mmfar = value,
0xe000_ed38 => self.bfar = value,
0xe000_ed3c => self.afsr = value,
0xe000_ed88 => self.cpacr = value & 0x00f0_0000,
0xe000_ed90 => {}
0xe000_ed94..=0xe000_edb8 if self.mpu_regions == 0 => {}
0xe000_ed94 => self.mpu_ctrl = value & 0x7,
0xe000_ed98 => self.mpu_rnr = value & 0xff,
0xe000_ed9c | 0xe000_eda4 | 0xe000_edac | 0xe000_edb4 => {
let n = if value & (1 << 4) != 0 {
let n = (value & 0xf) as usize & (MPU_REGIONS - 1);
self.mpu_rnr = n as u32;
n
} else {
(self.mpu_rnr as usize) & (MPU_REGIONS - 1)
};
self.mpu_rbar[n] = (value & !0x1f) | (n as u32);
}
0xe000_eda0 | 0xe000_eda8 | 0xe000_edb0 | 0xe000_edb8 => {
let n = (self.mpu_rnr as usize) & (MPU_REGIONS - 1);
self.mpu_rasr[n] = value;
}
0xe000_ef00 => {
let n = (value & 0x1ff) as u16;
if usize::from(n) + 16 < Exception::COUNT {
self.set_pending(Exception(n + 16), true);
}
}
_ if in_ppb(word) => {}
_ => return false,
}
true
}
fn irq_word(&self, map: &[u32; WORDS], offset: u32) -> u32 {
let first = 16 + (offset / 4) * 32;
let mut v = 0u32;
for k in 0..32u32 {
let n = (first + k) as usize;
if n < Exception::COUNT && map[n / 32] & (1 << (n % 32)) != 0 {
v |= 1 << k;
}
}
v
}
fn irq_set(&mut self, which: Bitmap, offset: u32, value: u32) {
self.irq_rmw(which, offset, value, true);
}
fn irq_clear(&mut self, which: Bitmap, offset: u32, value: u32) {
self.irq_rmw(which, offset, value, false);
}
fn irq_rmw(&mut self, which: Bitmap, offset: u32, value: u32, on: bool) {
let first = 16 + (offset / 4) * 32;
for k in 0..32u32 {
if value & (1 << k) == 0 {
continue;
}
let e = Exception((first + k) as u16);
if usize::from(e.0) >= Exception::COUNT {
continue;
}
match which {
Bitmap::Enable => Sys::set(&mut self.enable, e, on),
Bitmap::Pending => Sys::set(&mut self.pending, e, on),
}
}
}
fn icsr(&self) -> u32 {
let mut v = 0u32;
v |= self.vect_pending() << 12;
if self.pending.iter().enumerate().any(|(i, w)| {
if i == 0 { *w & !0xffff != 0 } else { *w != 0 }
}) {
v |= 1 << 22;
}
if self.active_count() <= 1 {
v |= 1 << 11;
}
if self.is_pending(Exception::PEND_SV) {
v |= 1 << 28;
}
if self.is_pending(Exception::SYSTICK) {
v |= 1 << 26;
}
if self.is_pending(Exception::NMI) {
v |= 1 << 31;
}
v
}
fn shcsr_read(&self) -> u32 {
let mut v = self.shcsr;
let act = |e: u16, bit: u32, v: &mut u32| {
if self.is_active(Exception(e)) {
*v |= 1 << bit;
}
};
act(4, 0, &mut v);
act(5, 1, &mut v);
act(6, 3, &mut v);
act(11, 7, &mut v);
act(12, 8, &mut v);
act(14, 10, &mut v);
act(15, 11, &mut v);
let pend = |e: u16, bit: u32, v: &mut u32| {
if self.is_pending(Exception(e)) {
*v |= 1 << bit;
}
};
pend(6, 12, &mut v);
pend(4, 13, &mut v);
pend(5, 14, &mut v);
pend(11, 15, &mut v);
v
}
pub fn tick_systick(&mut self, ticks: u64) -> bool {
if self.syst_csr & 1 == 0 || self.syst_rvr == 0 {
return false;
}
let mut wrapped = false;
let mut left = ticks;
while left > 0 {
let cur = self.syst_cvr & 0x00ff_ffff;
if cur == 0 {
self.syst_cvr = self.syst_rvr & 0x00ff_ffff;
left -= 1;
continue;
}
let step = left.min(u64::from(cur));
let next = cur - (step as u32);
self.syst_cvr = next;
left -= step;
if next == 0 {
wrapped = true;
self.syst_csr |= 1 << 16;
}
}
wrapped
}
}
#[derive(Debug, Clone, Copy)]
enum Bitmap {
Enable,
Pending,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Access {
Fetch,
Read,
Write,
}
impl Sys {
#[must_use]
pub fn mpu_permits(&self, addr: u32, access: Access, privileged: bool, priority: i32) -> bool {
if self.mpu_regions == 0 || self.mpu_ctrl & 1 == 0 {
return true;
}
if priority < 0 && self.mpu_ctrl & 0b10 == 0 {
return true;
}
let mut decision = None;
for n in 0..MPU_REGIONS {
let rasr = self.mpu_rasr[n];
if rasr & 1 == 0 {
continue;
}
let size_field = (rasr >> 1) & 0x1f;
if size_field < 4 {
continue;
}
let bits = size_field + 1;
let size = if bits >= 32 { 0u32 } else { 1u32 << bits };
let mask = if bits >= 32 { 0u32 } else { !(size - 1) };
let base = self.mpu_rbar[n] & mask;
if bits < 32 && (addr & mask) != base {
continue;
}
if bits >= 8 {
let sub = if bits >= 32 {
(addr >> 29) & 7
} else {
((addr - base) >> (bits - 3)) & 7
};
if (rasr >> 8) & (1 << sub) != 0 {
continue;
}
}
decision = Some(rasr);
}
let Some(rasr) = decision else {
return privileged && self.mpu_ctrl & 0b100 != 0;
};
if access == Access::Fetch && rasr & (1 << 28) != 0 {
return false;
}
let ap = (rasr >> 24) & 7;
match (ap, privileged, access) {
(0b000, _, _) => false,
(0b001, true, _) => true,
(0b001, false, _) => false,
(0b010, true, _) => true,
(0b010, false, Access::Write) => false,
(0b010, false, _) => true,
(0b011, _, _) => true,
(0b101, true, Access::Write) => false,
(0b101, true, _) => true,
(0b101, false, _) => false,
(0b110 | 0b111, _, Access::Write) => false,
(0b110 | 0b111, _, _) => true,
_ => false,
}
}
}