use core::arch::global_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;
pub const portARCH_NAME: &str = "ARM Cortex-A9";
const SYS_MODE: u32 = 0x1F;
const SVC_MODE: u32 = 0x13;
const IRQ_MODE: u32 = 0x12;
const portINITIAL_SPSR: StackType_t = SYS_MODE as StackType_t;
const portTHUMB_MODE_BIT: StackType_t = 0x20;
const portTHUMB_MODE_ADDRESS: u32 = 0x01;
const portNO_CRITICAL_NESTING: u32 = 0;
const portNO_FLOATING_POINT_CONTEXT: StackType_t = 0;
const portAPSR_MODE_BITS_MASK: u32 = 0x1F;
const portAPSR_USER_MODE: u32 = 0x10;
const portUNMASK_VALUE: u32 = 0xFF;
const portICCPMR_PRIORITY_MASK_OFFSET: usize = 0x04;
const portICCIAR_INTERRUPT_ACKNOWLEDGE_OFFSET: usize = 0x0C;
const portICCEOIR_END_OF_INTERRUPT_OFFSET: usize = 0x10;
const portICCBPR_BINARY_POINT_OFFSET: usize = 0x08;
const portICCRPR_RUNNING_PRIORITY_OFFSET: usize = 0x14;
pub const configUNIQUE_INTERRUPT_PRIORITIES: u32 = 32;
const portPRIORITY_SHIFT: u32 = 3;
const portMAX_BINARY_POINT_VALUE: u32 = 2;
pub const portLOWEST_INTERRUPT_PRIORITY: u32 = configUNIQUE_INTERRUPT_PRIORITIES - 1;
pub const portLOWEST_USABLE_INTERRUPT_PRIORITY: u32 = portLOWEST_INTERRUPT_PRIORITY - 1;
pub const configINTERRUPT_CONTROLLER_BASE_ADDRESS: usize = 0x1E00_0000;
pub const configINTERRUPT_CONTROLLER_CPU_INTERFACE_OFFSET: usize = 0x100;
pub const configMAX_API_CALL_INTERRUPT_PRIORITY: u32 = 18;
const GIC_CPU_INTERFACE_ADDRESS: usize =
configINTERRUPT_CONTROLLER_BASE_ADDRESS + configINTERRUPT_CONTROLLER_CPU_INTERFACE_OFFSET;
const ICCPMR_ADDRESS: usize = GIC_CPU_INTERFACE_ADDRESS + portICCPMR_PRIORITY_MASK_OFFSET;
const ICCIAR_ADDRESS: usize = GIC_CPU_INTERFACE_ADDRESS + portICCIAR_INTERRUPT_ACKNOWLEDGE_OFFSET;
const ICCEOIR_ADDRESS: usize = GIC_CPU_INTERFACE_ADDRESS + portICCEOIR_END_OF_INTERRUPT_OFFSET;
const ICCBPR_ADDRESS: usize = GIC_CPU_INTERFACE_ADDRESS + portICCBPR_BINARY_POINT_OFFSET;
const ICCRPR_ADDRESS: usize = GIC_CPU_INTERFACE_ADDRESS + portICCRPR_RUNNING_PRIORITY_OFFSET;
const ulMaxAPIPriorityMask: u32 = configMAX_API_CALL_INTERRUPT_PRIORITY << portPRIORITY_SHIFT;
#[no_mangle]
pub static mut ulCriticalNesting: u32 = 9999;
#[no_mangle]
pub static mut ulPortTaskHasFPUContext: u32 = pdFALSE as u32;
#[no_mangle]
pub static mut ulPortYieldRequired: u32 = pdFALSE as u32;
#[no_mangle]
pub static mut ulPortInterruptNesting: u32 = 0;
#[no_mangle]
pub static ulICCIARAddress: u32 = ICCIAR_ADDRESS as u32;
#[no_mangle]
pub static ulICCEOIRAddress: u32 = ICCEOIR_ADDRESS as u32;
#[no_mangle]
pub static ulICCPMRAddress: u32 = ICCPMR_ADDRESS as u32;
#[no_mangle]
pub static ulMaxAPIPriorityMaskConst: u32 = ulMaxAPIPriorityMask;
#[inline(always)]
pub fn portENTER_CRITICAL() {
vPortEnterCritical();
}
#[inline(always)]
pub fn portEXIT_CRITICAL() {
vPortExitCritical();
}
#[no_mangle]
pub extern "C" fn vPortEnterCritical() {
ulPortSetInterruptMask();
unsafe {
ulCriticalNesting += 1;
if ulCriticalNesting == 1 {
configASSERT(ulPortInterruptNesting == 0);
}
}
}
#[no_mangle]
pub extern "C" fn vPortExitCritical() {
unsafe {
if ulCriticalNesting > portNO_CRITICAL_NESTING {
ulCriticalNesting -= 1;
if ulCriticalNesting == portNO_CRITICAL_NESTING {
portCLEAR_INTERRUPT_MASK();
}
}
}
}
#[inline(always)]
pub fn portDISABLE_INTERRUPTS() -> UBaseType_t {
ulPortSetInterruptMask()
}
#[inline(always)]
pub fn portENABLE_INTERRUPTS() {
vPortClearInterruptMask(0);
}
#[no_mangle]
pub extern "C" fn ulPortSetInterruptMask() -> u32 {
let ulReturn: u32;
unsafe {
core::arch::asm!("cpsid i", options(nomem, nostack));
let iccpmr = ICCPMR_ADDRESS as *mut u32;
let current = core::ptr::read_volatile(iccpmr);
if current == ulMaxAPIPriorityMask {
ulReturn = pdTRUE as u32;
} else {
ulReturn = pdFALSE as u32;
core::ptr::write_volatile(iccpmr, ulMaxAPIPriorityMask);
core::arch::asm!("dsb", "isb", options(nomem, nostack));
}
core::arch::asm!("cpsie i", options(nomem, nostack));
}
ulReturn
}
#[no_mangle]
pub extern "C" fn vPortClearInterruptMask(ulNewMaskValue: u32) {
if ulNewMaskValue == pdFALSE as u32 {
portCLEAR_INTERRUPT_MASK();
}
}
#[inline(always)]
fn portCLEAR_INTERRUPT_MASK() {
unsafe {
core::arch::asm!("cpsid i", options(nomem, nostack));
let iccpmr = ICCPMR_ADDRESS as *mut u32;
core::ptr::write_volatile(iccpmr, portUNMASK_VALUE);
core::arch::asm!("dsb", "isb", options(nomem, nostack));
core::arch::asm!("cpsie i", options(nomem, nostack));
}
}
#[inline(always)]
pub fn portSET_INTERRUPT_MASK_FROM_ISR() -> UBaseType_t {
ulPortSetInterruptMask()
}
#[inline(always)]
pub fn portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus: UBaseType_t) {
vPortClearInterruptMask(uxSavedInterruptStatus as u32);
}
#[inline(always)]
pub fn portYIELD() {
unsafe {
core::arch::asm!("svc 0", options(nomem, nostack));
}
}
#[inline(always)]
pub fn portYIELD_FROM_ISR(xSwitchRequired: BaseType_t) {
if xSwitchRequired != pdFALSE {
unsafe {
ulPortYieldRequired = pdTRUE as u32;
}
}
}
#[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 = 0;
pxStack = pxStack.sub(1);
*pxStack = 0;
pxStack = pxStack.sub(1);
*pxStack = 0;
pxStack = pxStack.sub(1);
let mut spsr = portINITIAL_SPSR;
if (pxCode as u32 & portTHUMB_MODE_ADDRESS) != 0 {
spsr |= portTHUMB_MODE_BIT;
}
*pxStack = spsr;
pxStack = pxStack.sub(1);
*pxStack = pxCode as StackType_t;
pxStack = pxStack.sub(1);
*pxStack = prvTaskExitError as StackType_t;
pxStack = pxStack.sub(1);
*pxStack = 0x12121212;
pxStack = pxStack.sub(1);
*pxStack = 0x11111111;
pxStack = pxStack.sub(1);
*pxStack = 0x10101010;
pxStack = pxStack.sub(1);
*pxStack = 0x09090909;
pxStack = pxStack.sub(1);
*pxStack = 0x08080808;
pxStack = pxStack.sub(1);
*pxStack = 0x07070707;
pxStack = pxStack.sub(1);
*pxStack = 0x06060606;
pxStack = pxStack.sub(1);
*pxStack = 0x05050505;
pxStack = pxStack.sub(1);
*pxStack = 0x04040404;
pxStack = pxStack.sub(1);
*pxStack = 0x03030303;
pxStack = pxStack.sub(1);
*pxStack = 0x02020202;
pxStack = pxStack.sub(1);
*pxStack = 0x01010101;
pxStack = pxStack.sub(1);
*pxStack = pvParameters as StackType_t;
pxStack = pxStack.sub(1);
*pxStack = portNO_CRITICAL_NESTING as StackType_t;
pxStack = pxStack.sub(1);
*pxStack = portNO_FLOATING_POINT_CONTEXT;
pxStack
}
}
fn prvTaskExitError() -> ! {
configASSERT(false);
portDISABLE_INTERRUPTS();
loop {
unsafe {
core::arch::asm!("wfi", options(nomem, nostack));
}
}
}
#[no_mangle]
pub extern "C" fn xPortStartScheduler() -> BaseType_t {
let ulAPSR: u32;
unsafe {
core::arch::asm!("mrs {}, cpsr", out(reg) ulAPSR);
}
let mode = ulAPSR & portAPSR_MODE_BITS_MASK;
configASSERT(mode != portAPSR_USER_MODE);
if mode != portAPSR_USER_MODE {
let bpr = unsafe { core::ptr::read_volatile(ICCBPR_ADDRESS as *const u32) };
configASSERT((bpr & 0x03) <= portMAX_BINARY_POINT_VALUE);
if (bpr & 0x03) <= portMAX_BINARY_POINT_VALUE {
unsafe {
core::arch::asm!("cpsid i", options(nomem, nostack));
}
unsafe {
vPortRestoreTaskContext();
}
}
}
0
}
pub fn vPortEndScheduler() {
configASSERT(false);
loop {
unsafe {
core::arch::asm!("wfi", options(nomem, nostack));
}
}
}
#[no_mangle]
pub extern "C" fn FreeRTOS_Tick_Handler() {
unsafe {
core::arch::asm!("cpsid i", options(nomem, nostack));
let iccpmr = ICCPMR_ADDRESS as *mut u32;
core::ptr::write_volatile(iccpmr, ulMaxAPIPriorityMask);
core::arch::asm!("dsb", "isb", options(nomem, nostack));
core::arch::asm!("cpsie i", options(nomem, nostack));
}
if unsafe { crate::kernel::tasks::xTaskIncrementTick() } != pdFALSE {
unsafe {
ulPortYieldRequired = pdTRUE as u32;
}
}
portCLEAR_INTERRUPT_MASK();
}
#[no_mangle]
pub extern "C" fn vPortTaskUsesFPU() {
unsafe {
ulPortTaskHasFPUContext = pdTRUE as u32;
let fpscr: u32 = 0;
core::arch::asm!("vmsr fpscr, {}", in(reg) fpscr, options(nomem, nostack));
}
}
#[inline(always)]
pub fn xPortIsInsideInterrupt() -> BaseType_t {
unsafe {
if ulPortInterruptNesting > 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));
}
}
#[cfg(feature = "generate-run-time-stats")]
const A9_GLOBAL_TIMER_COUNTER_LOW: usize = configINTERRUPT_CONTROLLER_BASE_ADDRESS + 0x200;
#[cfg(feature = "generate-run-time-stats")]
const A9_GLOBAL_TIMER_COUNTER_HIGH: usize = configINTERRUPT_CONTROLLER_BASE_ADDRESS + 0x204;
#[cfg(feature = "generate-run-time-stats")]
const A9_GLOBAL_TIMER_CONTROL: usize = configINTERRUPT_CONTROLLER_BASE_ADDRESS + 0x208;
#[cfg(feature = "generate-run-time-stats")]
const A9_GLOBAL_TIMER_ENABLE: u32 = 1;
#[cfg(feature = "generate-run-time-stats")]
static mut ullRunTimeCounterStart: u64 = 0;
#[cfg(feature = "generate-run-time-stats")]
#[inline(always)]
fn prvReadRunTimeCounter() -> u64 {
loop {
let high_before =
unsafe { core::ptr::read_volatile(A9_GLOBAL_TIMER_COUNTER_HIGH as *const u32) };
let low = unsafe { core::ptr::read_volatile(A9_GLOBAL_TIMER_COUNTER_LOW as *const u32) };
let high_after =
unsafe { core::ptr::read_volatile(A9_GLOBAL_TIMER_COUNTER_HIGH as *const u32) };
if high_before == high_after {
return ((high_before as u64) << 32) | low as u64;
}
}
}
#[cfg(feature = "generate-run-time-stats")]
#[inline(always)]
pub fn portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() {
unsafe {
let control = A9_GLOBAL_TIMER_CONTROL as *mut u32;
let current = core::ptr::read_volatile(control);
core::ptr::write_volatile(control, current | A9_GLOBAL_TIMER_ENABLE);
core::arch::asm!("dsb", "isb", options(nomem, nostack));
ullRunTimeCounterStart = prvReadRunTimeCounter();
}
}
#[cfg(feature = "generate-run-time-stats")]
#[inline(always)]
pub fn portGET_RUN_TIME_COUNTER_VALUE() -> crate::config::configRUN_TIME_COUNTER_TYPE {
prvReadRunTimeCounter().wrapping_sub(unsafe { ullRunTimeCounterStart })
as crate::config::configRUN_TIME_COUNTER_TYPE
}
#[cfg(feature = "generate-run-time-stats")]
#[inline(always)]
pub fn portINCREMENT_RUN_TIME_COUNTER() {
}
const SP804_TIMER_LOAD: usize = 0x00;
const SP804_TIMER_VALUE: usize = 0x04;
const SP804_TIMER_CONTROL: usize = 0x08;
const SP804_TIMER_INTCLR: usize = 0x0C;
const SP804_TIMER_RIS: usize = 0x10;
const SP804_TIMER_BGLOAD: usize = 0x18;
const SP804_CTRL_ENABLE: u32 = 1 << 7;
const SP804_CTRL_PERIODIC: u32 = 1 << 6;
const SP804_CTRL_INTENABLE: u32 = 1 << 5;
const SP804_CTRL_32BIT: u32 = 1 << 1;
#[no_mangle]
pub static mut ulTimerBaseAddress: usize = 0;
#[no_mangle]
pub static mut ulTimerCountsForOneTick: u32 = 1000;
#[no_mangle]
pub static mut ulMaximumPossibleSuppressedTicks: TickType_t = 0xFFFFFFFF / 1000;
#[no_mangle]
pub static mut ulStoppedTimerCompensation: u32 = 45;
#[no_mangle]
pub extern "C" fn vPortConfigureTimerForTickless(
timer_base: usize,
counts_per_tick: u32,
timer_max_count: u32,
) {
unsafe {
ulTimerBaseAddress = timer_base;
ulTimerCountsForOneTick = counts_per_tick;
ulMaximumPossibleSuppressedTicks = if counts_per_tick > 1 {
(timer_max_count / counts_per_tick) as TickType_t
} else {
0
};
}
}
#[cfg(feature = "tickless-idle")]
pub fn vPortSuppressTicksAndSleep(xExpectedIdleTime: TickType_t) {
use crate::types::eSleepModeStatus;
unsafe {
if ulTimerBaseAddress == 0
|| ulTimerCountsForOneTick < 2
|| ulMaximumPossibleSuppressedTicks < 2
|| xExpectedIdleTime < 2
{
return;
}
let timer_load = (ulTimerBaseAddress + SP804_TIMER_LOAD) as *mut u32;
let timer_value = (ulTimerBaseAddress + SP804_TIMER_VALUE) as *const u32;
let timer_control = (ulTimerBaseAddress + SP804_TIMER_CONTROL) as *mut u32;
let timer_intclr = (ulTimerBaseAddress + SP804_TIMER_INTCLR) as *mut u32;
let timer_ris = (ulTimerBaseAddress + SP804_TIMER_RIS) as *const u32;
let timer_bgload = (ulTimerBaseAddress + SP804_TIMER_BGLOAD) as *mut u32;
let mut xExpectedIdleTime = xExpectedIdleTime;
if xExpectedIdleTime > ulMaximumPossibleSuppressedTicks {
xExpectedIdleTime = ulMaximumPossibleSuppressedTicks;
}
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));
return;
}
let ctrl = core::ptr::read_volatile(timer_control);
core::ptr::write_volatile(timer_control, ctrl & !SP804_CTRL_ENABLE);
let ulTimerDecrementsLeft = core::cmp::min(
core::ptr::read_volatile(timer_value).saturating_add(1),
ulTimerCountsForOneTick,
);
let mut ullReloadCounts = ulTimerDecrementsLeft as u64
+ ulTimerCountsForOneTick as u64 * (xExpectedIdleTime as u64 - 1);
if ullReloadCounts > ulStoppedTimerCompensation as u64 {
ullReloadCounts -= ulStoppedTimerCompensation as u64;
}
let ulReloadValue = (ullReloadCounts.saturating_sub(1) as u32).max(1);
let ulPeriodicReloadValue = ulTimerCountsForOneTick.saturating_sub(1).max(1);
core::ptr::write_volatile(timer_load, ulReloadValue);
core::ptr::write_volatile(timer_intclr, 1); core::ptr::write_volatile(
timer_control,
SP804_CTRL_ENABLE | SP804_CTRL_PERIODIC | SP804_CTRL_INTENABLE | SP804_CTRL_32BIT,
);
core::ptr::write_volatile(timer_bgload, ulPeriodicReloadValue);
core::arch::asm!("dsb", options(nomem, nostack));
core::arch::asm!("wfi", options(nomem, nostack));
core::arch::asm!("isb", options(nomem, nostack));
let xTimerExpired = core::ptr::read_volatile(timer_ris) != 0;
let ulTimerValueAtWake = core::ptr::read_volatile(timer_value);
let ctrl = core::ptr::read_volatile(timer_control);
core::ptr::write_volatile(timer_control, ctrl & !SP804_CTRL_ENABLE);
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));
let ullCurrentCounts = ulTimerValueAtWake as u64 + 1;
let ullSleepElapsedCounts = if xTimerExpired {
let ullPeriodicCountsLeft =
core::cmp::min(ullCurrentCounts, ulTimerCountsForOneTick as u64);
ullReloadCounts + (ulTimerCountsForOneTick as u64).saturating_sub(ullPeriodicCountsLeft)
} else if ullReloadCounts >= ullCurrentCounts {
ullReloadCounts - ullCurrentCounts
} else {
0
};
let ullCountsSinceTickBoundary =
(ulTimerCountsForOneTick - ulTimerDecrementsLeft) as u64 + ullSleepElapsedCounts;
let mut ulCompletedTicks = ullCountsSinceTickBoundary / ulTimerCountsForOneTick as u64;
if xTimerExpired {
ulCompletedTicks =
core::cmp::min(ulCompletedTicks, xExpectedIdleTime.saturating_sub(1) as u64);
}
if ulCompletedTicks > 0 {
crate::kernel::tasks::vTaskStepTick(ulCompletedTicks as TickType_t);
}
let ulTickPhase = (ullCountsSinceTickBoundary % ulTimerCountsForOneTick as u64) as u32;
let ulRemainingCounts = if ulTickPhase == 0 {
ulTimerCountsForOneTick
} else {
ulTimerCountsForOneTick - ulTickPhase
};
let ulFirstReloadValue = ulRemainingCounts.saturating_sub(1).max(1);
core::ptr::write_volatile(timer_load, ulFirstReloadValue);
core::ptr::write_volatile(timer_intclr, 1);
core::ptr::write_volatile(
timer_control,
SP804_CTRL_ENABLE | SP804_CTRL_PERIODIC | SP804_CTRL_INTENABLE | SP804_CTRL_32BIT,
);
core::ptr::write_volatile(timer_bgload, ulPeriodicReloadValue);
core::arch::asm!("cpsie i", options(nomem, nostack));
}
}
#[cfg(not(feature = "tickless-idle"))]
pub fn vPortSuppressTicksAndSleep(_xExpectedIdleTime: TickType_t) {
}
extern "C" {
fn vPortRestoreTaskContext();
}
global_asm!(
".arm",
".align 4",
".set SYS_MODE, 0x1f",
".set SVC_MODE, 0x13",
".set IRQ_MODE, 0x12",
".macro portSAVE_CONTEXT",
"srsdb sp!, #SYS_MODE",
"cps #SYS_MODE",
"push {{r0-r12, r14}}",
"ldr r2, =ulCriticalNesting",
"ldr r1, [r2]",
"push {{r1}}",
"ldr r2, =ulPortTaskHasFPUContext",
"ldr r3, [r2]",
"cmp r3, #0",
"fmrxne r1, fpscr",
"pushne {{r1}}",
"vpushne {{d0-d15}}",
"vpushne {{d16-d31}}",
"push {{r3}}",
"ldr r0, =pxCurrentTCB",
"ldr r1, [r0]",
"str sp, [r1]",
".endm",
".macro portRESTORE_CONTEXT",
"ldr r0, =pxCurrentTCB",
"ldr r1, [r0]",
"ldr sp, [r1]",
"ldr r0, =ulPortTaskHasFPUContext",
"pop {{r1}}",
"str r1, [r0]",
"cmp r1, #0",
"vpopne {{d16-d31}}",
"vpopne {{d0-d15}}",
"popne {{r0}}",
"vmsrne fpscr, r0",
"ldr r0, =ulCriticalNesting",
"pop {{r1}}",
"str r1, [r0]",
"ldr r2, =ulICCPMRAddress",
"ldr r2, [r2]",
"cmp r1, #0",
"moveq r4, #255",
"ldrne r4, =ulMaxAPIPriorityMaskConst",
"ldrne r4, [r4]",
"str r4, [r2]",
"pop {{r0-r12, r14}}",
"rfeia sp!",
".endm",
".global FreeRTOS_SWI_Handler",
".type FreeRTOS_SWI_Handler, %function",
"FreeRTOS_SWI_Handler:",
"portSAVE_CONTEXT",
"mov r2, sp",
"and r2, r2, #4",
"sub sp, sp, r2",
"bl vTaskSwitchContext",
"portRESTORE_CONTEXT",
".global vPortRestoreTaskContext",
".type vPortRestoreTaskContext, %function",
"vPortRestoreTaskContext:",
"cps #SYS_MODE",
"portRESTORE_CONTEXT",
".global FreeRTOS_IRQ_Handler",
".type FreeRTOS_IRQ_Handler, %function",
"FreeRTOS_IRQ_Handler:",
"sub lr, lr, #4",
"push {{lr}}",
"mrs lr, spsr",
"push {{lr}}",
"cps #SVC_MODE",
"push {{r0-r4, r12}}",
"ldr r3, =ulPortInterruptNesting",
"ldr r1, [r3]",
"add r4, r1, #1",
"str r4, [r3]",
"ldr r2, =ulICCIARAddress",
"ldr r2, [r2]",
"ldr r0, [r2]",
"mov r2, sp",
"and r2, r2, #4",
"sub sp, sp, r2",
"push {{r0-r4, lr}}",
"bl vApplicationIRQHandler",
"pop {{r0-r4, lr}}",
"add sp, sp, r2",
"cpsid i",
"dsb",
"isb",
"ldr r4, =ulICCEOIRAddress",
"ldr r4, [r4]",
"str r0, [r4]",
"str r1, [r3]",
"cmp r1, #0",
"bne exit_without_switch",
"ldr r1, =ulPortYieldRequired",
"ldr r0, [r1]",
"cmp r0, #0",
"bne switch_before_exit",
"exit_without_switch:",
"pop {{r0-r4, r12}}",
"cps #IRQ_MODE",
"pop {{lr}}",
"msr spsr_cxsf, lr",
"pop {{lr}}",
"movs pc, lr",
"switch_before_exit:",
"mov r0, #0",
"str r0, [r1]",
"pop {{r0-r4, r12}}",
"cps #IRQ_MODE",
"pop {{lr}}",
"msr spsr_cxsf, lr",
"pop {{lr}}",
"portSAVE_CONTEXT",
"mov r2, sp",
"and r2, r2, #4",
"sub sp, sp, r2",
"bl vTaskSwitchContext",
"portRESTORE_CONTEXT",
".weak vApplicationIRQHandler",
".type vApplicationIRQHandler, %function",
"vApplicationIRQHandler:",
"push {{lr}}",
"fmrx r1, fpscr",
"vpush {{d0-d7}}",
"vpush {{d16-d31}}",
"push {{r1}}",
"bl vApplicationFPUSafeIRQHandler",
"pop {{r0}}",
"vpop {{d16-d31}}",
"vpop {{d0-d7}}",
"vmsr fpscr, r0",
"pop {{pc}}",
);