use core::ptr;
use volatile_register::RW;
#[cfg(not(armv6m))]
use super::cpuid::CsselrCacheType;
#[cfg(not(armv6m))]
use super::CBP;
#[cfg(not(armv6m))]
use super::CPUID;
use super::SCB;
#[repr(C)]
pub struct RegisterBlock {
pub icsr: RW<u32>,
pub vtor: RW<u32>,
pub aircr: RW<u32>,
pub scr: RW<u32>,
pub ccr: RW<u32>,
#[cfg(not(armv6m))]
pub shpr: [RW<u8>; 12],
#[cfg(armv6m)]
_reserved1: u32,
#[cfg(armv6m)]
pub shpr: [RW<u32>; 2],
pub shcsr: RW<u32>,
#[cfg(not(armv6m))]
pub cfsr: RW<u32>,
#[cfg(armv6m)]
_reserved2: u32,
#[cfg(not(armv6m))]
pub hfsr: RW<u32>,
#[cfg(armv6m)]
_reserved3: u32,
#[cfg(not(armv6m))]
pub dfsr: RW<u32>,
#[cfg(armv6m)]
_reserved4: u32,
#[cfg(not(armv6m))]
pub mmfar: RW<u32>,
#[cfg(armv6m)]
_reserved5: u32,
#[cfg(not(armv6m))]
pub bfar: RW<u32>,
#[cfg(armv6m)]
_reserved6: u32,
#[cfg(not(armv6m))]
pub afsr: RW<u32>,
#[cfg(armv6m)]
_reserved7: u32,
_reserved8: [u32; 18],
#[cfg(not(armv6m))]
pub cpacr: RW<u32>,
#[cfg(armv6m)]
_reserved9: u32,
}
#[cfg(has_fpu)]
#[allow(clippy::missing_inline_in_public_items)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum FpuAccessMode {
Disabled,
Enabled,
Privileged,
}
#[cfg(has_fpu)]
mod fpu_consts {
pub const SCB_CPACR_FPU_MASK: u32 = 0b11_11 << 20;
pub const SCB_CPACR_FPU_ENABLE: u32 = 0b01_01 << 20;
pub const SCB_CPACR_FPU_USER: u32 = 0b10_10 << 20;
}
#[cfg(has_fpu)]
use self::fpu_consts::*;
#[cfg(has_fpu)]
impl SCB {
#[inline]
pub fn disable_fpu(&mut self) {
self.set_fpu_access_mode(FpuAccessMode::Disabled)
}
#[inline]
pub fn enable_fpu(&mut self) {
self.set_fpu_access_mode(FpuAccessMode::Enabled)
}
#[inline]
pub fn fpu_access_mode() -> FpuAccessMode {
let cpacr = unsafe { (*Self::ptr()).cpacr.read() };
if cpacr & SCB_CPACR_FPU_MASK == SCB_CPACR_FPU_ENABLE | SCB_CPACR_FPU_USER {
FpuAccessMode::Enabled
} else if cpacr & SCB_CPACR_FPU_MASK == SCB_CPACR_FPU_ENABLE {
FpuAccessMode::Privileged
} else {
FpuAccessMode::Disabled
}
}
#[inline]
pub fn set_fpu_access_mode(&mut self, mode: FpuAccessMode) {
let mut cpacr = self.cpacr.read() & !SCB_CPACR_FPU_MASK;
match mode {
FpuAccessMode::Disabled => (),
FpuAccessMode::Privileged => cpacr |= SCB_CPACR_FPU_ENABLE,
FpuAccessMode::Enabled => cpacr |= SCB_CPACR_FPU_ENABLE | SCB_CPACR_FPU_USER,
}
unsafe { self.cpacr.write(cpacr) }
}
}
impl SCB {
#[inline]
pub fn vect_active() -> VectActive {
let icsr = unsafe { ptr::read(&(*SCB::ptr()).icsr as *const _ as *const u32) };
match icsr as u8 {
0 => VectActive::ThreadMode,
2 => VectActive::Exception(Exception::NonMaskableInt),
3 => VectActive::Exception(Exception::HardFault),
#[cfg(not(armv6m))]
4 => VectActive::Exception(Exception::MemoryManagement),
#[cfg(not(armv6m))]
5 => VectActive::Exception(Exception::BusFault),
#[cfg(not(armv6m))]
6 => VectActive::Exception(Exception::UsageFault),
#[cfg(any(armv8m, target_arch = "x86_64"))]
7 => VectActive::Exception(Exception::SecureFault),
11 => VectActive::Exception(Exception::SVCall),
#[cfg(not(armv6m))]
12 => VectActive::Exception(Exception::DebugMonitor),
14 => VectActive::Exception(Exception::PendSV),
15 => VectActive::Exception(Exception::SysTick),
irqn => VectActive::Interrupt { irqn: irqn - 16 },
}
}
}
#[allow(clippy::missing_inline_in_public_items)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Exception {
NonMaskableInt,
HardFault,
#[cfg(not(armv6m))]
MemoryManagement,
#[cfg(not(armv6m))]
BusFault,
#[cfg(not(armv6m))]
UsageFault,
#[cfg(any(armv8m, target_arch = "x86_64"))]
SecureFault,
SVCall,
#[cfg(not(armv6m))]
DebugMonitor,
PendSV,
SysTick,
}
impl Exception {
#[inline]
pub fn irqn(self) -> i8 {
match self {
Exception::NonMaskableInt => -14,
Exception::HardFault => -13,
#[cfg(not(armv6m))]
Exception::MemoryManagement => -12,
#[cfg(not(armv6m))]
Exception::BusFault => -11,
#[cfg(not(armv6m))]
Exception::UsageFault => -10,
#[cfg(any(armv8m, target_arch = "x86_64"))]
Exception::SecureFault => -9,
Exception::SVCall => -5,
#[cfg(not(armv6m))]
Exception::DebugMonitor => -4,
Exception::PendSV => -2,
Exception::SysTick => -1,
}
}
}
#[allow(clippy::missing_inline_in_public_items)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum VectActive {
ThreadMode,
Exception(Exception),
Interrupt {
irqn: u8,
},
}
impl VectActive {
#[inline]
pub fn from(vect_active: u8) -> Option<Self> {
Some(match vect_active {
0 => VectActive::ThreadMode,
2 => VectActive::Exception(Exception::NonMaskableInt),
3 => VectActive::Exception(Exception::HardFault),
#[cfg(not(armv6m))]
4 => VectActive::Exception(Exception::MemoryManagement),
#[cfg(not(armv6m))]
5 => VectActive::Exception(Exception::BusFault),
#[cfg(not(armv6m))]
6 => VectActive::Exception(Exception::UsageFault),
#[cfg(any(armv8m, target_arch = "x86_64"))]
7 => VectActive::Exception(Exception::SecureFault),
11 => VectActive::Exception(Exception::SVCall),
#[cfg(not(armv6m))]
12 => VectActive::Exception(Exception::DebugMonitor),
14 => VectActive::Exception(Exception::PendSV),
15 => VectActive::Exception(Exception::SysTick),
irqn if irqn >= 16 => VectActive::Interrupt { irqn },
_ => return None,
})
}
}
#[cfg(not(armv6m))]
mod scb_consts {
pub const SCB_CCR_IC_MASK: u32 = (1 << 17);
pub const SCB_CCR_DC_MASK: u32 = (1 << 16);
}
#[cfg(not(armv6m))]
use self::scb_consts::*;
#[cfg(not(armv6m))]
impl SCB {
#[inline]
pub fn enable_icache(&mut self) {
if Self::icache_enabled() {
return;
}
let mut cbp = unsafe { CBP::new() };
cbp.iciallu();
unsafe { self.ccr.modify(|r| r | SCB_CCR_IC_MASK) };
crate::asm::dsb();
crate::asm::isb();
}
#[inline]
pub fn disable_icache(&mut self) {
if !Self::icache_enabled() {
return;
}
let mut cbp = unsafe { CBP::new() };
unsafe { self.ccr.modify(|r| r & !SCB_CCR_IC_MASK) };
cbp.iciallu();
crate::asm::dsb();
crate::asm::isb();
}
#[inline]
pub fn icache_enabled() -> bool {
crate::asm::dsb();
crate::asm::isb();
unsafe { (*Self::ptr()).ccr.read() & SCB_CCR_IC_MASK == SCB_CCR_IC_MASK }
}
#[inline]
pub fn invalidate_icache(&mut self) {
let mut cbp = unsafe { CBP::new() };
cbp.iciallu();
crate::asm::dsb();
crate::asm::isb();
}
#[inline]
pub fn enable_dcache(&mut self, cpuid: &mut CPUID) {
if Self::dcache_enabled() {
return;
}
self.invalidate_dcache(cpuid);
unsafe { self.ccr.modify(|r| r | SCB_CCR_DC_MASK) };
crate::asm::dsb();
crate::asm::isb();
}
#[inline]
pub fn disable_dcache(&mut self, cpuid: &mut CPUID) {
if !Self::dcache_enabled() {
return;
}
unsafe { self.ccr.modify(|r| r & !SCB_CCR_DC_MASK) };
self.clean_invalidate_dcache(cpuid);
}
#[inline]
pub fn dcache_enabled() -> bool {
crate::asm::dsb();
crate::asm::isb();
unsafe { (*Self::ptr()).ccr.read() & SCB_CCR_DC_MASK == SCB_CCR_DC_MASK }
}
#[inline]
fn invalidate_dcache(&mut self, cpuid: &mut CPUID) {
let mut cbp = unsafe { CBP::new() };
let (sets, ways) = cpuid.cache_num_sets_ways(0, CsselrCacheType::DataOrUnified);
for set in 0..sets {
for way in 0..ways {
cbp.dcisw(set, way);
}
}
crate::asm::dsb();
crate::asm::isb();
}
#[inline]
pub fn clean_dcache(&mut self, cpuid: &mut CPUID) {
let mut cbp = unsafe { CBP::new() };
let (sets, ways) = cpuid.cache_num_sets_ways(0, CsselrCacheType::DataOrUnified);
for set in 0..sets {
for way in 0..ways {
cbp.dccsw(set, way);
}
}
crate::asm::dsb();
crate::asm::isb();
}
#[inline]
pub fn clean_invalidate_dcache(&mut self, cpuid: &mut CPUID) {
let mut cbp = unsafe { CBP::new() };
let (sets, ways) = cpuid.cache_num_sets_ways(0, CsselrCacheType::DataOrUnified);
for set in 0..sets {
for way in 0..ways {
cbp.dccisw(set, way);
}
}
crate::asm::dsb();
crate::asm::isb();
}
#[inline]
pub fn invalidate_dcache_by_address(&mut self, addr: usize, size: usize) {
if size == 0 {
return;
}
let mut cbp = unsafe { CBP::new() };
crate::asm::dsb();
const LINESIZE: usize = 32;
let num_lines = ((size - 1) / LINESIZE) + 1;
let mut addr = addr & 0xFFFF_FFE0;
for _ in 0..num_lines {
cbp.dcimvac(addr as u32);
addr += LINESIZE;
}
crate::asm::dsb();
crate::asm::isb();
}
#[inline]
pub fn clean_dcache_by_address(&mut self, addr: usize, size: usize) {
if size == 0 {
return;
}
let mut cbp = unsafe { CBP::new() };
crate::asm::dsb();
const LINESIZE: usize = 32;
let num_lines = ((size - 1) / LINESIZE) + 1;
let mut addr = addr & 0xFFFF_FFE0;
for _ in 0..num_lines {
cbp.dccmvac(addr as u32);
addr += LINESIZE;
}
crate::asm::dsb();
crate::asm::isb();
}
#[inline]
pub fn clean_invalidate_dcache_by_address(&mut self, addr: usize, size: usize) {
if size == 0 {
return;
}
let mut cbp = unsafe { CBP::new() };
crate::asm::dsb();
const LINESIZE: usize = 32;
let num_lines = ((size - 1) / LINESIZE) + 1;
let mut addr = addr & 0xFFFF_FFE0;
for _ in 0..num_lines {
cbp.dccimvac(addr as u32);
addr += LINESIZE;
}
crate::asm::dsb();
crate::asm::isb();
}
}
const SCB_SCR_SLEEPDEEP: u32 = 0x1 << 2;
impl SCB {
#[inline]
pub fn set_sleepdeep(&mut self) {
unsafe {
self.scr.modify(|scr| scr | SCB_SCR_SLEEPDEEP);
}
}
#[inline]
pub fn clear_sleepdeep(&mut self) {
unsafe {
self.scr.modify(|scr| scr & !SCB_SCR_SLEEPDEEP);
}
}
}
const SCB_SCR_SLEEPONEXIT: u32 = 0x1 << 1;
impl SCB {
#[inline]
pub fn set_sleeponexit(&mut self) {
unsafe {
self.scr.modify(|scr| scr | SCB_SCR_SLEEPONEXIT);
}
}
#[inline]
pub fn clear_sleeponexit(&mut self) {
unsafe {
self.scr.modify(|scr| scr & !SCB_SCR_SLEEPONEXIT);
}
}
}
const SCB_AIRCR_VECTKEY: u32 = 0x05FA << 16;
const SCB_AIRCR_PRIGROUP_MASK: u32 = 0x5 << 8;
const SCB_AIRCR_SYSRESETREQ: u32 = 1 << 2;
impl SCB {
#[deprecated(since = "0.6.1", note = "Use `SCB::sys_reset`")]
#[inline]
pub fn system_reset(&mut self) -> ! {
crate::asm::dsb();
unsafe {
self.aircr.modify(
|r| {
SCB_AIRCR_VECTKEY |
r & SCB_AIRCR_PRIGROUP_MASK |
SCB_AIRCR_SYSRESETREQ
},
)
};
crate::asm::dsb();
loop {
crate::asm::nop();
}
}
#[inline]
pub fn sys_reset() -> ! {
crate::asm::dsb();
unsafe {
(*Self::ptr()).aircr.modify(
|r| {
SCB_AIRCR_VECTKEY |
r & SCB_AIRCR_PRIGROUP_MASK |
SCB_AIRCR_SYSRESETREQ
},
)
};
crate::asm::dsb();
loop {
crate::asm::nop();
}
}
}
const SCB_ICSR_PENDSVSET: u32 = 1 << 28;
const SCB_ICSR_PENDSVCLR: u32 = 1 << 27;
const SCB_ICSR_PENDSTSET: u32 = 1 << 26;
const SCB_ICSR_PENDSTCLR: u32 = 1 << 25;
impl SCB {
#[inline]
pub fn set_pendsv() {
unsafe {
(*Self::ptr()).icsr.write(SCB_ICSR_PENDSVSET);
}
}
#[inline]
pub fn is_pendsv_pending() -> bool {
unsafe { (*Self::ptr()).icsr.read() & SCB_ICSR_PENDSVSET == SCB_ICSR_PENDSVSET }
}
#[inline]
pub fn clear_pendsv() {
unsafe {
(*Self::ptr()).icsr.write(SCB_ICSR_PENDSVCLR);
}
}
#[inline]
pub fn set_pendst() {
unsafe {
(*Self::ptr()).icsr.write(SCB_ICSR_PENDSTSET);
}
}
#[inline]
pub fn is_pendst_pending() -> bool {
unsafe { (*Self::ptr()).icsr.read() & SCB_ICSR_PENDSTSET == SCB_ICSR_PENDSTSET }
}
#[inline]
pub fn clear_pendst() {
unsafe {
(*Self::ptr()).icsr.write(SCB_ICSR_PENDSTCLR);
}
}
}
#[allow(clippy::missing_inline_in_public_items)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum SystemHandler {
#[cfg(not(armv6m))]
MemoryManagement,
#[cfg(not(armv6m))]
BusFault,
#[cfg(not(armv6m))]
UsageFault,
#[cfg(any(armv8m, target_arch = "x86_64"))]
SecureFault,
SVCall,
#[cfg(not(armv6m))]
DebugMonitor,
PendSV,
SysTick,
}
impl SystemHandler {
fn index(self) -> u8 {
match self {
#[cfg(not(armv6m))]
SystemHandler::MemoryManagement => 4,
#[cfg(not(armv6m))]
SystemHandler::BusFault => 5,
#[cfg(not(armv6m))]
SystemHandler::UsageFault => 6,
#[cfg(any(armv8m, target_arch = "x86_64"))]
SystemHandler::SecureFault => 7,
SystemHandler::SVCall => 11,
#[cfg(not(armv6m))]
SystemHandler::DebugMonitor => 12,
SystemHandler::PendSV => 14,
SystemHandler::SysTick => 15,
}
}
}
impl SCB {
#[inline]
pub fn get_priority(system_handler: SystemHandler) -> u8 {
let index = system_handler.index();
#[cfg(not(armv6m))]
{
unsafe { (*Self::ptr()).shpr[usize::from(index - 4)].read() }
}
#[cfg(armv6m)]
{
let shpr = unsafe { (*Self::ptr()).shpr[usize::from((index - 8) / 4)].read() };
let prio = (shpr >> (8 * (index % 4))) & 0x0000_00ff;
prio as u8
}
}
#[inline]
pub unsafe fn set_priority(&mut self, system_handler: SystemHandler, prio: u8) {
let index = system_handler.index();
#[cfg(not(armv6m))]
{
self.shpr[usize::from(index - 4)].write(prio)
}
#[cfg(armv6m)]
{
self.shpr[usize::from((index - 8) / 4)].modify(|value| {
let shift = 8 * (index % 4);
let mask = 0x0000_00ff << shift;
let prio = u32::from(prio) << shift;
(value & !mask) | prio
});
}
}
}