use core::arch::naked_asm;
use core::ffi::c_void;
use core::sync::atomic::{AtomicUsize, Ordering};
use cortex_m::register::basepri;
use crate::config::*;
use crate::types::*;
pub const portSTACK_GROWTH: BaseType_t = -1;
pub const portBYTE_ALIGNMENT: usize = 8;
const portINITIAL_XPSR: StackType_t = 0x0100_0000;
const portINITIAL_EXC_RETURN: StackType_t = 0xFFFF_FFFD;
const portSTART_ADDRESS_MASK: StackType_t = 0xFFFF_FFFE;
const portMIN_INTERRUPT_PRIORITY: u8 = 255;
const portMAX_SYSCALL_INTERRUPT_PRIORITY: u8 = configMAX_SYSCALL_INTERRUPT_PRIORITY as u8;
const NVIC_ICSR: *mut u32 = 0xE000_ED04 as *mut u32;
const NVIC_PENDSVSET_BIT: u32 = 1 << 28;
const SYST_CSR: *mut u32 = 0xE000_E010 as *mut u32;
const SYST_RVR: *mut u32 = 0xE000_E014 as *mut u32;
const SYST_CVR: *mut u32 = 0xE000_E018 as *mut u32;
const SYST_CSR_ENABLE: u32 = 1 << 0;
const SYST_CSR_TICKINT: u32 = 1 << 1;
const SYST_CSR_CLKSOURCE: u32 = 1 << 2;
static CRITICAL_NESTING: AtomicUsize = AtomicUsize::new(0);
#[inline(always)]
pub fn portENTER_CRITICAL() {
portDISABLE_INTERRUPTS();
CRITICAL_NESTING.fetch_add(1, Ordering::SeqCst);
cortex_m::asm::dsb();
cortex_m::asm::isb();
}
#[inline(always)]
pub fn portEXIT_CRITICAL() {
let prev = CRITICAL_NESTING.fetch_sub(1, Ordering::SeqCst);
if prev == 1 {
portENABLE_INTERRUPTS();
}
}
#[inline(always)]
pub fn portDISABLE_INTERRUPTS() {
unsafe {
basepri::write(portMAX_SYSCALL_INTERRUPT_PRIORITY);
}
cortex_m::asm::dsb();
cortex_m::asm::isb();
}
#[inline(always)]
pub fn portENABLE_INTERRUPTS() {
unsafe {
basepri::write(0);
}
}
#[inline(always)]
pub fn portSET_INTERRUPT_MASK_FROM_ISR() -> UBaseType_t {
let saved = basepri::read() as UBaseType_t;
unsafe {
basepri::write(portMAX_SYSCALL_INTERRUPT_PRIORITY);
}
cortex_m::asm::dsb();
cortex_m::asm::isb();
saved
}
#[inline(always)]
pub fn portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus: UBaseType_t) {
unsafe {
basepri::write(uxSavedInterruptStatus as u8);
}
}
#[inline(always)]
pub fn portYIELD() {
unsafe {
core::ptr::write_volatile(NVIC_ICSR, NVIC_PENDSVSET_BIT);
}
cortex_m::asm::dsb();
cortex_m::asm::isb();
}
#[inline(always)]
pub fn portYIELD_FROM_ISR(xSwitchRequired: BaseType_t) {
if xSwitchRequired != pdFALSE {
portYIELD();
}
}
#[inline(always)]
pub fn portEND_SWITCHING_ISR(xSwitchRequired: BaseType_t) {
portYIELD_FROM_ISR(xSwitchRequired);
}
pub unsafe fn pxPortInitialiseStack(
pxTopOfStack: *mut StackType_t,
pxCode: TaskFunction_t,
pvParameters: *mut c_void,
) -> *mut StackType_t {
unsafe {
let mut pxStack = pxTopOfStack;
pxStack = pxStack.sub(1);
*pxStack = portINITIAL_XPSR;
pxStack = pxStack.sub(1);
*pxStack = (pxCode as StackType_t) & portSTART_ADDRESS_MASK;
pxStack = pxStack.sub(1);
*pxStack = prvTaskExitError as StackType_t;
pxStack = pxStack.sub(1);
pxStack = pxStack.sub(1);
pxStack = pxStack.sub(1);
pxStack = pxStack.sub(1);
pxStack = pxStack.sub(1);
*pxStack = pvParameters as StackType_t;
pxStack = pxStack.sub(1);
*pxStack = portINITIAL_EXC_RETURN;
pxStack = pxStack.sub(8);
pxStack
}
}
fn prvTaskExitError() -> ! {
portDISABLE_INTERRUPTS();
loop {
cortex_m::asm::wfi();
}
}
#[unsafe(naked)]
#[no_mangle]
pub unsafe extern "C" fn vPortSVCHandler() {
naked_asm!(
"ldr r3, =pxCurrentTCB",
"ldr r1, [r3]", "ldr r0, [r1]", "ldmia r0!, {{r4-r11, r14}}",
"msr psp, r0",
"isb",
"mov r0, #0",
"msr basepri, r0",
"bx r14",
);
}
#[unsafe(naked)]
#[no_mangle]
pub unsafe extern "C" fn xPortPendSVHandler() {
naked_asm!(
".fpu fpv4-sp-d16",
"mrs r0, psp",
"isb",
"ldr r3, =pxCurrentTCB",
"ldr r2, [r3]",
"tst r14, #0x10",
"it eq",
"vstmdbeq r0!, {{s16-s31}}",
"stmdb r0!, {{r4-r11, r14}}",
"str r0, [r2]",
"stmdb sp!, {{r0, r3}}",
"mov r0, #{max_syscall_pri}",
"msr basepri, r0",
"dsb",
"isb",
"bl vTaskSwitchContext",
"mov r0, #0",
"msr basepri, r0",
"ldmia sp!, {{r0, r3}}",
"ldr r1, [r3]", "ldr r0, [r1]",
"ldmia r0!, {{r4-r11, r14}}",
"tst r14, #0x10",
"it eq",
"vldmiaeq r0!, {{s16-s31}}",
"msr psp, r0",
"isb",
"bx r14",
max_syscall_pri = const portMAX_SYSCALL_INTERRUPT_PRIORITY,
);
}
#[no_mangle]
pub extern "C" fn xPortSysTickHandler() {
let saved = portSET_INTERRUPT_MASK_FROM_ISR();
#[cfg(feature = "generate-run-time-stats")]
portINCREMENT_RUN_TIME_COUNTER();
crate::trace::traceISR_ENTER();
if unsafe { crate::kernel::tasks::xTaskIncrementTick() } != pdFALSE {
crate::trace::traceISR_EXIT_TO_SCHEDULER();
unsafe {
core::ptr::write_volatile(NVIC_ICSR, NVIC_PENDSVSET_BIT);
}
} else {
crate::trace::traceISR_EXIT();
}
portCLEAR_INTERRUPT_MASK_FROM_ISR(saved);
}
#[unsafe(naked)]
unsafe extern "C" fn prvPortStartFirstTask() {
naked_asm!(
"ldr r0, 1f", "ldr r0, [r0]", "ldr r0, [r0]", "msr msp, r0", "mov r0, #0",
"msr control, r0",
"cpsie i",
"cpsie f",
"dsb",
"isb",
"svc 0",
"nop",
"b .",
".align 4",
"1: .word 0xE000ED08", );
}
pub fn vPortSetupTimerInterrupt() {
unsafe {
core::ptr::write_volatile(SYST_CSR, 0);
let reload = (configCPU_CLOCK_HZ / configTICK_RATE_HZ as u32) - 1;
core::ptr::write_volatile(SYST_RVR, reload);
core::ptr::write_volatile(SYST_CVR, 0);
#[cfg(feature = "tickless-idle")]
prvSetupTicklessIdle();
core::ptr::write_volatile(
SYST_CSR,
SYST_CSR_ENABLE | SYST_CSR_TICKINT | SYST_CSR_CLKSOURCE,
);
}
}
pub fn xPortStartScheduler() -> BaseType_t {
const SHPR3: *mut u32 = 0xE000_ED20 as *mut u32;
unsafe {
let mut shpr3 = core::ptr::read_volatile(SHPR3);
shpr3 |= (portMIN_INTERRUPT_PRIORITY as u32) << 16; shpr3 |= (portMIN_INTERRUPT_PRIORITY as u32) << 24; core::ptr::write_volatile(SHPR3, shpr3);
}
CRITICAL_NESTING.store(0, Ordering::SeqCst);
vPortSetupTimerInterrupt();
unsafe {
prvPortStartFirstTask();
}
0
}
pub fn vPortEndScheduler() {
loop {
cortex_m::asm::wfi();
}
}
#[inline(always)]
pub fn xPortIsInsideInterrupt() -> BaseType_t {
let ipsr: u32;
unsafe {
core::arch::asm!("mrs {}, ipsr", out(reg) ipsr);
}
if ipsr != 0 {
pdTRUE
} else {
pdFALSE
}
}
#[inline(always)]
pub fn portNOP() {
cortex_m::asm::nop();
}
#[inline(always)]
pub fn portMEMORY_BARRIER() {
cortex_m::asm::dmb();
}
#[cfg(all(feature = "port-cortex-m7", not(feature = "port-cortex-m4f")))]
pub const portARCH_NAME: &str = "ARM Cortex-M7";
#[cfg(not(all(feature = "port-cortex-m7", not(feature = "port-cortex-m4f"))))]
pub const portARCH_NAME: &str = "ARM Cortex-M4F";
#[cfg(feature = "generate-run-time-stats")]
static mut ulRunTimeCounterValue: crate::config::configRUN_TIME_COUNTER_TYPE = 0;
#[cfg(feature = "generate-run-time-stats")]
#[inline(always)]
pub fn portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() {
unsafe {
ulRunTimeCounterValue = 0;
}
}
#[cfg(feature = "generate-run-time-stats")]
#[inline(always)]
pub fn portGET_RUN_TIME_COUNTER_VALUE() -> crate::config::configRUN_TIME_COUNTER_TYPE {
unsafe { ulRunTimeCounterValue }
}
#[cfg(feature = "generate-run-time-stats")]
#[inline(always)]
pub fn portINCREMENT_RUN_TIME_COUNTER() {
unsafe {
ulRunTimeCounterValue = ulRunTimeCounterValue.wrapping_add(1);
}
}
#[cfg(feature = "tickless-idle")]
static mut ulTimerCountsForOneTick: u32 = 0;
#[cfg(feature = "tickless-idle")]
static mut xMaximumPossibleSuppressedTicks: TickType_t = 0;
#[cfg(feature = "tickless-idle")]
static mut ulStoppedTimerCompensation: u32 = 0;
#[cfg(feature = "tickless-idle")]
const portNVIC_SYSTICK_COUNT_FLAG_BIT: u32 = 1 << 16;
#[cfg(feature = "tickless-idle")]
const portNVIC_PEND_SYSTICK_CLEAR_BIT: u32 = 1 << 25;
#[cfg(feature = "tickless-idle")]
fn prvSetupTicklessIdle() {
unsafe {
ulTimerCountsForOneTick = configCPU_CLOCK_HZ / configTICK_RATE_HZ as u32;
xMaximumPossibleSuppressedTicks = (0x00FF_FFFF / ulTimerCountsForOneTick) as TickType_t;
ulStoppedTimerCompensation = 45;
}
}
#[cfg(feature = "tickless-idle")]
pub fn vPortSuppressTicksAndSleep(xExpectedIdleTime: TickType_t) {
use crate::types::eSleepModeStatus;
unsafe {
let mut xExpectedIdleTime = xExpectedIdleTime;
if xExpectedIdleTime > xMaximumPossibleSuppressedTicks {
xExpectedIdleTime = xMaximumPossibleSuppressedTicks;
}
core::arch::asm!("cpsid i", options(nomem, nostack));
core::arch::asm!("dsb", options(nomem, nostack));
core::arch::asm!("isb", options(nomem, nostack));
if crate::kernel::tasks::eTaskConfirmSleepModeStatus() == eSleepModeStatus::eAbortSleep {
core::arch::asm!("cpsie i", options(nomem, nostack));
} else {
let systick_ctrl = core::ptr::read_volatile(SYST_CSR);
core::ptr::write_volatile(SYST_CSR, systick_ctrl & !SYST_CSR_ENABLE);
let mut ulSysTickDecrementsLeft = core::ptr::read_volatile(SYST_CVR);
if ulSysTickDecrementsLeft == 0 {
ulSysTickDecrementsLeft = ulTimerCountsForOneTick;
}
let mut ulReloadValue = ulSysTickDecrementsLeft
+ (ulTimerCountsForOneTick * (xExpectedIdleTime as u32 - 1));
if (core::ptr::read_volatile(NVIC_ICSR) & (1 << 26)) != 0 {
core::ptr::write_volatile(NVIC_ICSR, portNVIC_PEND_SYSTICK_CLEAR_BIT);
ulReloadValue -= ulTimerCountsForOneTick;
}
if ulReloadValue > ulStoppedTimerCompensation {
ulReloadValue -= ulStoppedTimerCompensation;
}
core::ptr::write_volatile(SYST_RVR, ulReloadValue);
core::ptr::write_volatile(SYST_CVR, 0);
core::ptr::write_volatile(
SYST_CSR,
SYST_CSR_ENABLE | SYST_CSR_TICKINT | SYST_CSR_CLKSOURCE,
);
core::arch::asm!("dsb", options(nomem, nostack));
core::arch::asm!("wfi", options(nomem, nostack));
core::arch::asm!("isb", options(nomem, nostack));
core::arch::asm!("cpsie i", options(nomem, nostack));
core::arch::asm!("dsb", options(nomem, nostack));
core::arch::asm!("isb", options(nomem, nostack));
core::arch::asm!("cpsid i", options(nomem, nostack));
core::arch::asm!("dsb", options(nomem, nostack));
core::arch::asm!("isb", options(nomem, nostack));
core::ptr::write_volatile(SYST_CSR, SYST_CSR_TICKINT | SYST_CSR_CLKSOURCE);
let ulCompleteTickPeriods: u32;
let systick_ctrl = core::ptr::read_volatile(SYST_CSR);
if (systick_ctrl & portNVIC_SYSTICK_COUNT_FLAG_BIT) != 0 {
let ulCalculatedLoadValue: u32;
let current_value = core::ptr::read_volatile(SYST_CVR);
let calc = (ulTimerCountsForOneTick - 1)
.wrapping_sub(ulReloadValue.wrapping_sub(current_value));
if calc <= ulStoppedTimerCompensation || calc > ulTimerCountsForOneTick {
ulCalculatedLoadValue = ulTimerCountsForOneTick - 1;
} else {
ulCalculatedLoadValue = calc;
}
core::ptr::write_volatile(SYST_RVR, ulCalculatedLoadValue);
ulCompleteTickPeriods = xExpectedIdleTime as u32 - 1;
} else {
let mut ulSysTickDecrementsLeft = core::ptr::read_volatile(SYST_CVR);
if ulSysTickDecrementsLeft == 0 {
ulSysTickDecrementsLeft = ulReloadValue;
}
let ulCompletedSysTickDecrements =
(xExpectedIdleTime as u32 * ulTimerCountsForOneTick) - ulSysTickDecrementsLeft;
ulCompleteTickPeriods = ulCompletedSysTickDecrements / ulTimerCountsForOneTick;
let reload = ((ulCompleteTickPeriods + 1) * ulTimerCountsForOneTick)
- ulCompletedSysTickDecrements;
core::ptr::write_volatile(SYST_RVR, reload);
}
core::ptr::write_volatile(SYST_CVR, 0);
core::ptr::write_volatile(
SYST_CSR,
SYST_CSR_ENABLE | SYST_CSR_TICKINT | SYST_CSR_CLKSOURCE,
);
core::ptr::write_volatile(SYST_RVR, ulTimerCountsForOneTick - 1);
crate::kernel::tasks::vTaskStepTick(ulCompleteTickPeriods as TickType_t);
core::arch::asm!("cpsie i", options(nomem, nostack));
}
}
}
#[cfg(not(feature = "tickless-idle"))]
pub fn vPortSuppressTicksAndSleep(_xExpectedIdleTime: TickType_t) {
}
use core::arch::global_asm;
global_asm!(
".thumb_func",
".global SVCall",
".type SVCall, %function",
"SVCall:",
"b vPortSVCHandler",
".thumb_func",
".global PendSV",
".type PendSV, %function",
"PendSV:",
"b xPortPendSVHandler",
".thumb_func",
".global SysTick",
".type SysTick, %function",
"SysTick:",
"b xPortSysTickHandler",
);