#![allow(missing_docs)]
#[cfg(cortex_m)]
use core::arch::asm;
#[cfg(cortex_m)]
use core::sync::atomic::{Ordering, compiler_fence};
use cortex_m_macros::asm_cfg;
#[inline(always)]
#[asm_cfg(cortex_m)]
pub fn bkpt() {
unsafe { asm!("bkpt", options(nomem, nostack, preserves_flags)) };
}
#[inline]
#[asm_cfg(cortex_m)]
pub fn delay(cycles: u32) {
let real_cyc = cycles.saturating_add(1);
unsafe {
asm!(
".p2align 3",
"2:", "subs {}, #1", "bne 2b", inout(reg) real_cyc => _,
options(nomem, nostack),
)
};
}
#[inline]
#[asm_cfg(cortex_m)]
pub fn nop() {
unsafe { asm!("nop", options(nomem, nostack, preserves_flags)) };
}
#[inline]
#[asm_cfg(cortex_m)]
pub fn udf() -> ! {
unsafe { asm!("udf #0", options(noreturn, nomem, nostack, preserves_flags)) };
}
#[inline]
#[asm_cfg(cortex_m)]
pub fn wfe() {
unsafe { asm!("wfe", options(nomem, nostack, preserves_flags)) };
}
#[inline]
#[asm_cfg(cortex_m)]
pub fn wfi() {
unsafe { asm!("wfi", options(nomem, nostack, preserves_flags)) };
}
#[inline]
#[asm_cfg(cortex_m)]
pub fn sev() {
unsafe { asm!("sev", options(nomem, nostack, preserves_flags)) };
}
#[inline]
#[asm_cfg(cortex_m)]
pub fn isb() {
compiler_fence(Ordering::SeqCst);
unsafe { asm!("isb", options(nostack, preserves_flags)) };
compiler_fence(Ordering::SeqCst);
}
#[inline]
#[asm_cfg(cortex_m)]
pub fn dsb() {
compiler_fence(Ordering::SeqCst);
unsafe { asm!("dsb", options(nostack, preserves_flags)) };
compiler_fence(Ordering::SeqCst);
}
#[inline]
#[asm_cfg(cortex_m)]
pub fn dmb() {
compiler_fence(Ordering::SeqCst);
unsafe { asm!("dmb", options(nostack, preserves_flags)) };
compiler_fence(Ordering::SeqCst);
}
#[inline]
#[asm_cfg(armv8m)]
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn tt(addr: *mut u32) -> u32 {
let mut addr = addr as u32;
unsafe {
asm!(
"tt {addr}, {addr}",
addr = inout(reg) addr,
options(nomem, nostack, preserves_flags),
)
};
addr
}
#[inline]
#[asm_cfg(armv8m)]
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn ttt(addr: *mut u32) -> u32 {
let mut addr = addr as u32;
unsafe {
asm!(
"ttt {addr}, {addr}",
addr = inout(reg)addr,
options(nomem, nostack, preserves_flags),
)
};
addr
}
#[inline]
#[asm_cfg(armv8m)]
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn tta(addr: *mut u32) -> u32 {
let mut addr = addr as u32;
unsafe {
asm!(
"tta {addr}, {addr}",
addr = inout(reg) addr,
options(nomem, nostack, preserves_flags),
)
};
addr
}
#[inline]
#[asm_cfg(armv8m)]
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn ttat(addr: *mut u32) -> u32 {
let mut addr = addr as u32;
unsafe {
asm!(
"ttat {addr}, {addr}",
addr = inout(reg) addr,
options(nomem, nostack, preserves_flags),
)
};
addr
}
#[inline]
#[asm_cfg(armv8m)]
pub unsafe fn bx_ns(addr: u32) {
unsafe { asm!("BXNS {}", in(reg) addr, options(nomem, nostack, preserves_flags)) };
}
#[inline]
#[asm_cfg(cortex_m)]
pub unsafe fn semihosting_syscall(mut nr: u32, arg: u32) -> u32 {
unsafe {
asm!("bkpt #0xab", inout("r0") nr, in("r1") arg, options(nomem, nostack, preserves_flags))
};
nr
}
#[inline(always)]
#[asm_cfg(cortex_m)]
pub unsafe fn enter_unprivileged_psp(psp: *const u32, entry: extern "C" fn() -> !) -> ! {
use crate::register::control::{Control, Npriv, Spsel};
const CONTROL_FLAGS: u32 = {
Control::from_bits(0)
.with_npriv(Npriv::Unprivileged)
.with_spsel(Spsel::Psp)
.bits()
};
unsafe {
core::arch::asm!(
"msr PSP, {psp}",
"mrs {tmp}, CONTROL",
"orrs {tmp}, {flags}",
"msr CONTROL, {tmp}",
"isb",
"bx {ent}",
tmp = in(reg) 0,
flags = in(reg) CONTROL_FLAGS,
psp = in(reg) psp,
ent = in(reg) entry,
options(noreturn, nostack)
);
}
}
#[inline(always)]
#[asm_cfg(cortex_m)]
pub unsafe fn enter_privileged_psp(psp: *const u32, entry: extern "C" fn() -> !) -> ! {
use crate::register::control::{Control, Npriv, Spsel};
const CONTROL_FLAGS: u32 = {
Control::from_bits(0)
.with_npriv(Npriv::Privileged)
.with_spsel(Spsel::Psp)
.bits()
};
unsafe {
core::arch::asm!(
"msr PSP, {psp}",
"mrs {tmp}, CONTROL",
"orrs {tmp}, {flags}",
"msr CONTROL, {tmp}",
"isb",
"bx {ent}",
tmp = in(reg) 0,
flags = in(reg) CONTROL_FLAGS,
psp = in(reg) psp,
ent = in(reg) entry,
options(noreturn, nostack)
);
}
}
#[inline]
#[asm_cfg(cortex_m)]
pub unsafe fn bootstrap(msp: *const u32, rv: *const u32) -> ! {
let rv = (rv as u32) | 1;
let msp = msp as u32;
unsafe {
asm!(
"mrs {tmp}, CONTROL",
"bics {tmp}, {spsel}",
"msr CONTROL, {tmp}",
"isb",
"msr MSP, {msp}",
"bx {rv}",
tmp = in(reg) 0,
spsel = in(reg) 2,
msp = in(reg) msp,
rv = in(reg) rv,
options(noreturn, nomem, nostack),
)
};
}
#[inline]
#[asm_cfg(cortex_m)]
pub unsafe fn bootload(vector_table: *const u32) -> ! {
unsafe {
let msp = core::ptr::read_volatile(vector_table);
let rv = core::ptr::read_volatile(vector_table.offset(1));
bootstrap(msp as *const u32, rv as *const u32);
}
}
#[cfg(all(armv8m, feature = "secure-mode"))]
pub unsafe fn bootload_ns(ns_vtor: *const u32, scb_ns: crate::peripheral::SCBNS) -> ! {
unsafe {
scb_ns.vtor.write(ns_vtor as usize as u32);
}
let ns_sp = unsafe { ns_vtor.read_volatile() };
unsafe {
crate::register::msp::write_ns(ns_sp);
}
let ns_reset = unsafe { ns_vtor.add(1).read_volatile() };
unsafe extern "C" {
fn _bx_ns_trampoline(boot: u32) -> !;
}
unsafe {
_bx_ns_trampoline(ns_reset & 0xFFFF_FFFE);
}
}
#[cfg(all(armv8m, feature = "secure-mode"))]
core::arch::global_asm!(
r#"
.type _bx_ns_trampoline,%function
.global _bx_ns_trampoline
_bx_ns_trampoline:
vlstm sp // Push secure FPU state to stack, and zero secure FPU registers (nop if no FPU present)
mov lr, r0 // Put target address in LR
mov r0, 0 // Zero all the other registers
mov r1, 0 // Except secure MSP, as nonsecure has its own MSP, which we set
mov r2, 0
mov r3, 0
mov r4, 0
mov r5, 0
mov r6, 0
mov r7, 0
mov r8, 0
mov r9, 0
mov r10, 0
mov r11, 0
mov r12, 0
msr apsr_nzcvq, r0 // Also clear processor flags
bxns lr // Branch to nonsecure mode
.size _bx_ns_trampoline, . - _bx_ns_trampoline
"#,
);
#[inline(always)]
#[asm_cfg(any(armv7m, armv8m))]
pub unsafe fn mcr<const CP: u32, const OP1: u32, const CRN: u32, const CRM: u32, const OP2: u32>(
value: u32,
) {
unsafe {
core::arch::asm!(
"MCR p{cp}, #{op1}, {0}, c{crn}, c{crm}, #{op2}",
in(reg) value,
cp = const CP,
op1 = const OP1,
crn = const CRN,
crm = const CRM,
op2 = const OP2,
options(nostack, nomem)
)
};
}
#[inline(always)]
#[asm_cfg(any(armv7m, armv8m))]
pub unsafe fn mrc<const CP: u32, const OP1: u32, const CRN: u32, const CRM: u32, const OP2: u32>()
-> u32 {
let a: u32;
unsafe {
core::arch::asm!(
"MRC p{cp}, #{op1}, {0}, c{crn}, c{crm}, #{op2}",
out(reg) a,
cp = const CP,
op1 = const OP1,
crn = const CRN,
crm = const CRM,
op2 = const OP2,
options(nostack, nomem)
)
};
a
}
#[inline(always)]
#[asm_cfg(any(armv7m, armv8m))]
pub unsafe fn mcrr<const CP: u32, const OP1: u32, const CRM: u32>(a: u32, b: u32) {
unsafe {
core::arch::asm!(
"MCRR p{cp}, #{op1}, {0}, {1}, c{crm}",
in(reg) a,
in(reg) b,
cp = const CP,
op1 = const OP1,
crm = const CRM,
options(nostack, nomem)
)
};
}
#[inline(always)]
#[asm_cfg(any(armv7m, armv8m))]
pub unsafe fn mrrc<const CP: u32, const OPC: u32, const CRM: u32>() -> (u32, u32) {
let a: u32;
let b: u32;
unsafe {
core::arch::asm!(
"MRRC p{cp}, #{opc}, {0}, {1}, c{crm}",
out(reg) a,
out(reg) b,
cp = const CP,
opc = const OPC,
crm = const CRM,
options(nostack, nomem)
)
};
(a, b)
}