#![no_std]
#[cfg(target_feature = "vfp2")]
compile_error!(
"rivet-arch-cortex-m assumes a soft-float ABI (build for e.g. \
thumbv7em-none-eabi, not -eabihf) — see this crate's own module \
docs for what a hardfloat port would additionally need"
);
pub mod dwt;
pub mod mpu;
#[cfg(feature = "nvic")]
pub mod nvic;
pub mod semihosting;
#[cfg(feature = "systick")]
pub mod systick;
pub const MIN_TASK_STACK: usize = 64 + 64;
#[no_mangle]
extern "Rust" fn __rivet_arch_init() {
unsafe extern "C" {
static __vector_table: u32;
}
unsafe {
(*cortex_m::peripheral::SCB::PTR)
.vtor
.write(core::ptr::addr_of!(__vector_table) as u32);
}
mpu::init();
dwt::init();
unsafe {
let scb = &*cortex_m::peripheral::SCB::PTR;
scb.shpr[10].write(0xFF); scb.shpr[11].write(0xFF); scb.shcsr.write(
(1 << 16) | (1 << 17) | (1 << 18), );
}
unsafe {
let nvic = &*cortex_m::peripheral::NVIC::PTR;
for ipr in nvic.ipr.iter() {
ipr.write(0xFF);
}
}
}
#[no_mangle]
extern "Rust" fn __rivet_arch_idle() {
cortex_m::asm::wfi();
}
#[no_mangle]
extern "Rust" fn __rivet_arch_min_task_stack() -> usize {
MIN_TASK_STACK
}
#[no_mangle]
extern "Rust" fn __rivet_arch_min_guard_size() -> usize {
64
}
#[no_mangle]
extern "Rust" fn __rivet_arch_cycle_count() -> u64 {
dwt::cycle_count()
}
#[no_mangle]
extern "Rust" fn __rivet_arch_irq_enable(_irq_num: u32) {
#[cfg(feature = "nvic")]
nvic::enable(_irq_num);
}
#[no_mangle]
extern "Rust" fn __rivet_arch_irq_disable(_irq_num: u32) {
#[cfg(feature = "nvic")]
nvic::disable(_irq_num);
}
#[no_mangle]
extern "Rust" fn __rivet_arch_irq_set_priority(_irq_num: u32, _priority: u8) {
#[cfg(feature = "nvic")]
nvic::set_priority(_irq_num, _priority);
}
#[no_mangle]
extern "Rust" fn __rivet_arch_hart_id() -> usize {
0
}
#[no_mangle]
extern "Rust" fn __rivet_arch_request_reschedule_on(hart: usize) {
debug_assert_eq!(hart, 0, "rivet-arch-cortex-m: single-core, hart must be 0");
__rivet_arch_request_reschedule();
}
#[cfg(feature = "latency-histograms")]
static RESCHEDULE_REQUESTED_AT: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0);
#[no_mangle]
extern "Rust" fn __rivet_arch_request_reschedule() {
#[cfg(feature = "latency-histograms")]
RESCHEDULE_REQUESTED_AT.store(dwt::cycle_count() as u32, core::sync::atomic::Ordering::Relaxed);
unsafe {
let scb = &*cortex_m::peripheral::SCB::PTR;
scb.icsr.write(1 << 28); }
}
#[no_mangle]
extern "Rust" fn __rivet_arch_irq_save() -> usize {
let was_enabled = cortex_m::register::primask::read().is_active();
cortex_m::interrupt::disable();
was_enabled as usize
}
#[no_mangle]
extern "Rust" fn __rivet_arch_irq_restore(token: usize) {
if token != 0 {
unsafe { cortex_m::interrupt::enable() };
}
}
#[no_mangle]
extern "Rust" fn __rivet_arch_on_switch_to(stack_base: usize, stack_size: usize) {
mpu::set_current_stack(stack_base, stack_size);
}
#[no_mangle]
extern "Rust" fn __rivet_arch_guard_register(_guard_base: usize, _slot: usize) {
}
#[no_mangle]
extern "Rust" fn __rivet_arch_scratch_open(base: usize, size: usize) {
mpu::allow_scratch(base, size);
}
#[no_mangle]
extern "Rust" fn __rivet_arch_scratch_close() {
mpu::clear_scratch();
}
#[no_mangle]
unsafe extern "C" fn rivet_pendsv_rust(interrupted_sp: usize) -> usize {
#[cfg(feature = "latency-histograms")]
{
let requested_at = RESCHEDULE_REQUESTED_AT.load(core::sync::atomic::Ordering::Relaxed);
let now = dwt::cycle_count() as u32;
rivet::latency::record(
rivet::latency::Kind::IrqEntry,
now.wrapping_sub(requested_at) as u64,
);
}
rivet::preempt::on_tick(interrupted_sp)
}
core::arch::global_asm!(
".section .text.rivet_task_exit",
".global rivet_task_exit",
".thumb_func",
"rivet_task_exit:",
" bl rivet_task_exit_core", "1:",
" b 1b",
);
core::arch::global_asm!(
".section .text.PendSV",
".global PendSV",
".thumb_func",
"PendSV:",
" push {{lr}}",
" sub sp, sp, #4",
" mrs r0, psp",
" subs r0, r0, #32",
" stmia r0, {{r4-r11}}",
" bl rivet_pendsv_rust",
" ldmia r0, {{r4-r11}}",
" adds r0, r0, #32",
" msr psp, r0",
" add sp, sp, #4",
" pop {{lr}}",
".global rivet_pendsv_resume",
"rivet_pendsv_resume:",
" bx lr",
);
#[no_mangle]
unsafe extern "Rust" fn __rivet_arch_start_first_task(sp: usize) -> ! {
let frame = sp as *const u32;
let arg = unsafe { core::ptr::read(frame.add(8)) };
let entry_fn = unsafe { core::ptr::read(frame.add(14)) };
unsafe {
core::arch::asm!(
"msr psp, {sp}",
"movs r2, #2",
"msr control, r2", "isb",
sp = in(reg) sp,
out("r2") _,
);
}
#[cfg(feature = "systick")]
systick::enable();
unsafe {
cortex_m::interrupt::enable();
}
unsafe {
core::arch::asm!(
"mov r0, {arg}",
"bx {entry}",
arg = in(reg) arg,
entry = in(reg) entry_fn,
options(noreturn)
);
}
}
unsafe fn init_task_stack_impl(stack: &mut [u8], entry_fn: usize, arg: usize) -> usize {
const FRAME_WORDS: usize = 16; const STACK_ALIGN: usize = 16;
unsafe {
let base = stack.as_mut_ptr() as usize;
let top = base + stack.len();
let frame_start = (top - FRAME_WORDS * 4) & !(STACK_ALIGN - 1);
let frame = frame_start as *mut u32;
for i in 0..FRAME_WORDS {
core::ptr::write(frame.add(i), 0);
}
core::ptr::write(frame.add(8), arg as u32); extern "C" {
fn rivet_task_exit();
}
core::ptr::write(frame.add(13), rivet_task_exit as *const () as usize as u32); core::ptr::write(frame.add(14), entry_fn as u32); core::ptr::write(frame.add(15), 0x0100_0000);
frame_start
}
}
#[unsafe(naked)]
#[no_mangle]
unsafe extern "C" fn rivet_svc_handler() {
core::arch::naked_asm!(
"uxtb r1, lr", "cmp r1, #0xfd", "bne 1f", "mrs r0, psp", "b 2f",
"1:",
"mov r0, sp", "2:",
"push {{r1, lr}}", "bl rivet_svc_core",
"pop {{r1, lr}}",
"bx lr", );
}
#[no_mangle]
fn rivet_svc_core(frame: *mut u32) {
let (stack_ptr, stack_len, entry, arg) = unsafe {
(
*frame.add(0) as *mut u8,
*frame.add(1) as usize,
*frame.add(2) as usize,
*frame.add(3) as usize,
)
};
let sp = unsafe {
let saved = mpu::disable_for_scope();
let sp = init_task_stack_impl(
core::slice::from_raw_parts_mut(stack_ptr, stack_len),
entry,
arg,
);
mpu::restore_after_scope(saved);
sp
};
unsafe {
core::ptr::write_volatile(frame, sp as u32);
}
}
#[no_mangle]
unsafe extern "Rust" fn __rivet_arch_init_task_stack(
stack_ptr: *mut u8,
stack_len: usize,
entry_fn: usize,
arg: usize,
) -> usize {
let ptr = stack_ptr as usize;
let mut sp = 0usize;
unsafe {
let mut primask: u32;
core::arch::asm!(
"mrs {0}, primask",
out(reg) primask,
options(nomem, nostack, preserves_flags),
);
if primask & 1 != 0 {
core::arch::asm!("cpsie i", options(nomem, nostack, preserves_flags));
}
core::arch::asm!(
"svc 0",
inout("r0") ptr => sp,
in("r1") stack_len,
in("r2") entry_fn,
in("r3") arg,
options(nomem, nostack, preserves_flags),
);
if primask & 1 != 0 {
core::arch::asm!("cpsid i", options(nomem, nostack, preserves_flags));
}
}
sp
}
pub fn system_reset() -> ! {
unsafe {
core::ptr::write_volatile(0xE000_ED0C as *mut u32, 0x05FA_0004);
}
loop {
core::hint::spin_loop();
}
}