use core::arch::{global_asm, naked_asm};
use core::ffi::c_void;
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 portMIN_INTERRUPT_PRIORITY: u8 = 255;
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 mut CRITICAL_NESTING: u32 = 0;
#[inline(always)]
pub fn portENTER_CRITICAL() {
portDISABLE_INTERRUPTS();
unsafe {
CRITICAL_NESTING += 1;
core::arch::asm!("dsb", options(nomem, nostack));
core::arch::asm!("isb", options(nomem, nostack));
}
}
#[inline(always)]
pub fn portEXIT_CRITICAL() {
unsafe {
CRITICAL_NESTING -= 1;
if CRITICAL_NESTING == 0 {
portENABLE_INTERRUPTS();
}
}
}
#[inline(always)]
pub fn portDISABLE_INTERRUPTS() {
unsafe {
core::arch::asm!("cpsid i", options(nomem, nostack));
}
}
#[inline(always)]
pub fn portENABLE_INTERRUPTS() {
unsafe {
core::arch::asm!("cpsie i", options(nomem, nostack));
}
}
#[inline(always)]
pub fn portSET_INTERRUPT_MASK_FROM_ISR() -> UBaseType_t {
let primask: u32;
unsafe {
core::arch::asm!("mrs {}, PRIMASK", out(reg) primask, options(nomem, nostack));
core::arch::asm!("cpsid i", options(nomem, nostack));
}
primask as UBaseType_t
}
#[inline(always)]
pub fn portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus: UBaseType_t) {
unsafe {
core::arch::asm!("msr PRIMASK, {}", in(reg) uxSavedInterruptStatus as u32, options(nomem, nostack));
}
}
#[inline(always)]
pub fn portYIELD() {
unsafe {
core::ptr::write_volatile(NVIC_ICSR, NVIC_PENDSVSET_BIT);
core::arch::asm!("dsb", options(nomem, nostack));
core::arch::asm!("isb", options(nomem, nostack));
}
}
#[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;
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(9);
*pxStack = portINITIAL_EXC_RETURN;
pxStack
}
}
fn prvTaskExitError() -> ! {
portDISABLE_INTERRUPTS();
loop {
unsafe {
core::arch::asm!("wfi", options(nomem, nostack));
}
}
}
global_asm!(
".syntax unified",
".section .text.SVCall",
".global SVCall",
".thumb_func",
"SVCall:",
"movs r0, #4",
"mov r1, lr",
"tst r0, r1",
"beq 1f",
"mrs r0, psp",
"b 2f",
"1:",
"mrs r0, msp",
"2:",
"ldr r3, =vPortSVCHandler_C",
"bx r3",
);
#[no_mangle]
pub unsafe extern "C" fn vPortSVCHandler() {
extern "C" {
fn SVCall();
}
SVCall();
}
#[no_mangle]
pub unsafe extern "C" fn vPortSVCHandler_C(_pulCallerStackAddress: *mut u32) {
unsafe {
vRestoreContextOfFirstTask();
}
}
#[unsafe(naked)]
#[no_mangle]
pub unsafe extern "C" fn vRestoreContextOfFirstTask() {
naked_asm!(
".syntax unified",
"ldr r2, =pxCurrentTCB",
"ldr r1, [r2]", "ldr r0, [r1]", "ldm r0!, {{r2}}", "movs r1, #2",
"msr CONTROL, r1",
"adds r0, #32", "msr psp, r0",
"isb",
"bx r2",
);
}
global_asm!(
".syntax unified",
".section .text.PendSV",
".global PendSV",
".thumb_func",
"PendSV:",
"mrs r0, psp",
"ldr r2, =pxCurrentTCB",
"ldr r1, [r2]", "subs r0, r0, #36",
"str r0, [r1]",
"mov r3, lr", "stmia r0!, {{r3-r7}}", "mov r4, r8",
"mov r5, r9",
"mov r6, r10",
"mov r7, r11",
"stmia r0!, {{r4-r7}}", "cpsid i",
"bl vTaskSwitchContext",
"cpsie i",
"ldr r2, =pxCurrentTCB",
"ldr r1, [r2]", "ldr r0, [r1]", "adds r0, r0, #20", "ldmia r0!, {{r4-r7}}",
"mov r8, r4",
"mov r9, r5",
"mov r10, r6",
"mov r11, r7",
"msr psp, r0",
"subs r0, r0, #36",
"ldmia r0!, {{r3-r7}}", "bx r3",
);
#[no_mangle]
pub unsafe extern "C" fn xPortPendSVHandler() {
extern "C" {
fn PendSV();
}
PendSV();
}
global_asm!(
".syntax unified",
".section .text.SysTick",
".global SysTick",
".thumb_func",
"SysTick:",
"push {{lr}}",
"bl xPortSysTickHandler_impl",
"pop {{pc}}",
);
#[no_mangle]
extern "C" fn xPortSysTickHandler_impl() {
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);
}
#[no_mangle]
pub extern "C" fn xPortSysTickHandler() {
xPortSysTickHandler_impl();
}
#[unsafe(naked)]
unsafe extern "C" fn prvPortStartFirstTask() {
naked_asm!(
".syntax unified",
"cpsie i",
"dsb",
"isb",
"svc #0",
"nop",
);
}
pub fn vPortSetupTimerInterrupt() {
unsafe {
core::ptr::write_volatile(SYST_CSR, SYST_CSR_CLKSOURCE);
core::ptr::write_volatile(SYST_CVR, 0);
let reload = (configCPU_CLOCK_HZ / configTICK_RATE_HZ as u32) - 1;
core::ptr::write_volatile(SYST_RVR, reload);
#[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);
}
unsafe {
CRITICAL_NESTING = 0;
}
vPortSetupTimerInterrupt();
unsafe {
prvPortStartFirstTask();
}
0
}
pub fn vPortEndScheduler() {
loop {
unsafe {
core::arch::asm!("wfi", options(nomem, nostack));
}
}
}
#[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() {
unsafe {
core::arch::asm!("nop", options(nomem, nostack));
}
}
#[inline(always)]
pub fn portMEMORY_BARRIER() {
unsafe {
core::arch::asm!("dmb", options(nomem, nostack));
}
}
pub const portARCH_NAME: &str = "ARM Cortex-M0";
#[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 = 94; }
}
#[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 current_value = core::ptr::read_volatile(SYST_CVR);
let calc = (ulTimerCountsForOneTick - 1)
.wrapping_sub(ulReloadValue.wrapping_sub(current_value));
let ulCalculatedLoadValue =
if calc <= ulStoppedTimerCompensation || calc > ulTimerCountsForOneTick {
ulTimerCountsForOneTick - 1
} else {
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) {
}