use core::arch::global_asm;
use core::ffi::c_void;
use crate::config::*;
#[allow(unused_imports)]
use crate::kernel::tasks::{pxCurrentTCB, vTaskSwitchContext, xTaskIncrementTick};
use crate::types::*;
pub const portSTACK_GROWTH: BaseType_t = -1;
pub const portBYTE_ALIGNMENT: usize = 16;
pub const portBYTE_ALIGNMENT_MASK: usize = portBYTE_ALIGNMENT - 1;
pub const portARCH_NAME: &str = "RISC-V RV32";
pub const portTICK_PERIOD_MS: TickType_t = 1000 / configTICK_RATE_HZ;
const CLINT_BASE: usize = 0x0200_0000;
const MTIME_ADDR: usize = CLINT_BASE + 0xBFF8;
const MTIMECMP_ADDR: usize = CLINT_BASE + 0x4000;
const portCONTEXT_SIZE: usize = 31;
const portCONTEXT_SIZE_BYTES: usize = portCONTEXT_SIZE * 4;
const portCRITICAL_NESTING_OFFSET: usize = 30;
const portINITIAL_MSTATUS: u32 = 0x1880;
#[no_mangle]
pub static mut xCriticalNesting: usize = 0xAAAAAAAA;
#[inline(always)]
pub fn portDISABLE_INTERRUPTS() {
unsafe {
core::arch::asm!(
"csrc mstatus, {mie_bit}",
mie_bit = in(reg) 0x8,
options(nomem, nostack)
);
}
}
#[inline(always)]
pub fn portENABLE_INTERRUPTS() {
unsafe {
core::arch::asm!(
"csrs mstatus, {mie_bit}",
mie_bit = in(reg) 0x8,
options(nomem, nostack)
);
}
}
pub fn portENTER_CRITICAL() {
portDISABLE_INTERRUPTS();
unsafe {
xCriticalNesting += 1;
}
}
pub fn portEXIT_CRITICAL() {
unsafe {
xCriticalNesting -= 1;
if xCriticalNesting == 0 {
portENABLE_INTERRUPTS();
}
}
}
#[inline(always)]
pub fn portSET_INTERRUPT_MASK_FROM_ISR() -> UBaseType_t {
let mstatus: u32;
unsafe {
core::arch::asm!(
"csrr {mstatus}, mstatus",
"csrc mstatus, {mie_bit}",
mstatus = out(reg) mstatus,
mie_bit = in(reg) 0x8,
options(nomem, nostack)
);
}
mstatus as UBaseType_t
}
#[inline(always)]
pub fn portCLEAR_INTERRUPT_MASK_FROM_ISR(saved_mstatus: UBaseType_t) {
unsafe {
core::arch::asm!(
"csrw mstatus, {mstatus}",
mstatus = in(reg) saved_mstatus as u32,
options(nomem, nostack)
);
}
}
#[inline(always)]
pub fn portYIELD() {
unsafe {
core::arch::asm!("ecall", options(nomem, nostack));
}
}
#[inline(always)]
pub fn portYIELD_FROM_ISR(xSwitchRequired: BaseType_t) {
if xSwitchRequired != crate::types::pdFALSE {
unsafe {
YIELD_PENDING = true;
}
}
}
#[inline(always)]
pub fn portEND_SWITCHING_ISR(xSwitchRequired: BaseType_t) {
portYIELD_FROM_ISR(xSwitchRequired);
}
static mut YIELD_PENDING: bool = false;
#[no_mangle]
extern "C" fn prvTaskExitError() -> ! {
portDISABLE_INTERRUPTS();
loop {
unsafe {
core::arch::asm!("wfi", options(nomem, nostack));
}
}
}
pub fn pxPortInitialiseStack(
pxTopOfStack: *mut StackType_t,
pxCode: TaskFunction_t,
pvParameters: *mut c_void,
) -> *mut StackType_t {
unsafe {
let mut pxStack = pxTopOfStack;
pxStack = ((pxStack as usize) & !0xF) as *mut StackType_t;
pxStack = pxStack.sub(portCONTEXT_SIZE);
*pxStack.add(0) = pxCode as StackType_t;
*pxStack.add(1) = portINITIAL_MSTATUS;
*pxStack.add(2) = prvTaskExitError as StackType_t;
for i in 3..8 {
*pxStack.add(i) = 0;
}
*pxStack.add(8) = pvParameters as StackType_t;
for i in 9..30 {
*pxStack.add(i) = 0;
}
*pxStack.add(portCRITICAL_NESTING_OFFSET) = 0;
pxStack
}
}
static mut TIMER_INCREMENT: u64 = 0;
#[no_mangle]
pub static mut ullNextTime: u64 = 0;
#[no_mangle]
pub extern "C" fn vPortSetupTimerInterrupt() {
unsafe {
TIMER_INCREMENT = (configMTIME_HZ / configTICK_RATE_HZ as u32) as u64;
let mtime_lo = core::ptr::read_volatile(MTIME_ADDR as *const u32);
let mtime_hi = core::ptr::read_volatile((MTIME_ADDR + 4) as *const u32);
let mtime = ((mtime_hi as u64) << 32) | (mtime_lo as u64);
ullNextTime = mtime + TIMER_INCREMENT;
core::ptr::write_volatile((MTIMECMP_ADDR + 4) as *mut u32, 0xFFFFFFFF);
core::ptr::write_volatile(MTIMECMP_ADDR as *mut u32, ullNextTime as u32);
core::ptr::write_volatile((MTIMECMP_ADDR + 4) as *mut u32, (ullNextTime >> 32) as u32);
ullNextTime += TIMER_INCREMENT;
core::arch::asm!(
"csrs mie, {mtie}",
mtie = in(reg) 0x80,
options(nomem, nostack)
);
}
}
#[no_mangle]
pub extern "C" fn vPortUpdateTimerCompare() {
unsafe {
core::ptr::write_volatile((MTIMECMP_ADDR + 4) as *mut u32, 0xFFFFFFFF);
core::ptr::write_volatile(MTIMECMP_ADDR as *mut u32, ullNextTime as u32);
core::ptr::write_volatile((MTIMECMP_ADDR + 4) as *mut u32, (ullNextTime >> 32) as u32);
ullNextTime += TIMER_INCREMENT;
}
}
static mut ISR_STACK: [u8; 1024] = [0; 1024];
#[no_mangle]
pub static mut xISRStackTop: usize = 0;
pub fn xPortStartScheduler() -> BaseType_t {
unsafe {
xISRStackTop = ((&ISR_STACK as *const _ as usize) + ISR_STACK.len()) & !0xF;
xCriticalNesting = 0;
vPortSetupTimerInterrupt();
core::arch::asm!(
"la t0, freertos_risc_v_trap_handler",
"csrw mtvec, t0",
options(nomem, nostack)
);
vRestoreContextOfFirstTask();
}
#[allow(unreachable_code)]
0
}
pub fn vPortEndScheduler() {
}
#[no_mangle]
unsafe extern "C" fn vRestoreContextOfFirstTask() -> ! {
core::arch::asm!(
"la t1, pxCurrentTCB",
"lw sp, 0(t1)", "lw sp, 0(sp)", "lw t0, 0(sp)",
"csrw mepc, t0",
"lw t0, 4(sp)",
"ori t0, t0, 0x8",
"csrw mstatus, t0",
"lw t0, 120(sp)", "la t1, xCriticalNesting",
"sw t0, 0(t1)",
"lw x1, 8(sp)", "lw x5, 12(sp)", "lw x6, 16(sp)", "lw x7, 20(sp)", "lw x8, 24(sp)", "lw x9, 28(sp)", "lw x10, 32(sp)", "lw x11, 36(sp)", "lw x12, 40(sp)", "lw x13, 44(sp)", "lw x14, 48(sp)", "lw x15, 52(sp)", "lw x16, 56(sp)", "lw x17, 60(sp)", "lw x18, 64(sp)", "lw x19, 68(sp)", "lw x20, 72(sp)", "lw x21, 76(sp)", "lw x22, 80(sp)", "lw x23, 84(sp)", "lw x24, 88(sp)", "lw x25, 92(sp)", "lw x26, 96(sp)", "lw x27, 100(sp)", "lw x28, 104(sp)", "lw x29, 108(sp)", "lw x30, 112(sp)", "lw x31, 116(sp)", "addi sp, sp, 124", "mret",
options(noreturn)
);
}
global_asm!(
r#"
.section .text.freertos_risc_v_trap_handler
.global freertos_risc_v_trap_handler
.align 4
freertos_risc_v_trap_handler:
# Save context to current task's stack
addi sp, sp, -124 # portCONTEXT_SIZE_BYTES
# Save registers
sw x1, 8(sp) # ra
sw x5, 12(sp) # t0
sw x6, 16(sp) # t1
sw x7, 20(sp) # t2
sw x8, 24(sp) # s0/fp
sw x9, 28(sp) # s1
sw x10, 32(sp) # a0
sw x11, 36(sp) # a1
sw x12, 40(sp) # a2
sw x13, 44(sp) # a3
sw x14, 48(sp) # a4
sw x15, 52(sp) # a5
sw x16, 56(sp) # a6
sw x17, 60(sp) # a7
sw x18, 64(sp) # s2
sw x19, 68(sp) # s3
sw x20, 72(sp) # s4
sw x21, 76(sp) # s5
sw x22, 80(sp) # s6
sw x23, 84(sp) # s7
sw x24, 88(sp) # s8
sw x25, 92(sp) # s9
sw x26, 96(sp) # s10
sw x27, 100(sp) # s11
sw x28, 104(sp) # t3
sw x29, 108(sp) # t4
sw x30, 112(sp) # t5
sw x31, 116(sp) # t6
# Save xCriticalNesting
la t0, xCriticalNesting
lw t0, 0(t0)
sw t0, 120(sp) # [30] = xCriticalNesting
# Save mstatus
csrr t0, mstatus
sw t0, 4(sp) # [1] = mstatus
# Save sp to current TCB
la t0, pxCurrentTCB
lw t0, 0(t0)
sw sp, 0(t0)
# Check mcause
csrr a0, mcause
bltz a0, handle_interrupt # MSB set = interrupt
handle_exception:
# Save mepc + 4 (skip ecall instruction)
csrr t0, mepc
addi t0, t0, 4
sw t0, 0(sp) # [0] = mepc
# Check if ecall (mcause == 11)
li t1, 11
bne a0, t1, unhandled_exception
# ecall = context switch request
# Switch to ISR stack
la sp, xISRStackTop
lw sp, 0(sp)
call vTaskSwitchContext
j restore_context
handle_interrupt:
# Save mepc unchanged (async interrupt)
csrr t0, mepc
sw t0, 0(sp) # [0] = mepc
# Check if machine timer interrupt (mcause & 0x7FF == 7)
andi a0, a0, 0x7FF
li t1, 7
bne a0, t1, unhandled_interrupt
# Machine timer interrupt - switch to ISR stack
la sp, xISRStackTop
lw sp, 0(sp)
# Update timer for next tick
call vPortUpdateTimerCompare
# Process tick
call xTaskIncrementTick
beqz a0, restore_context # No context switch needed
# Context switch needed
call vTaskSwitchContext
j restore_context
unhandled_exception:
unhandled_interrupt:
# Loop forever on unhandled trap
j unhandled_exception
restore_context:
# Load pxCurrentTCB
la t1, pxCurrentTCB
lw t1, 0(t1)
lw sp, 0(t1) # sp = pxCurrentTCB->pxTopOfStack
# Restore mepc
lw t0, 0(sp)
csrw mepc, t0
# Restore mstatus
lw t0, 4(sp)
csrw mstatus, t0
# Restore xCriticalNesting
lw t0, 120(sp)
la t1, xCriticalNesting
sw t0, 0(t1)
# Restore registers
lw x1, 8(sp) # ra
lw x5, 12(sp) # t0
lw x6, 16(sp) # t1
lw x7, 20(sp) # t2
lw x8, 24(sp) # s0/fp
lw x9, 28(sp) # s1
lw x10, 32(sp) # a0
lw x11, 36(sp) # a1
lw x12, 40(sp) # a2
lw x13, 44(sp) # a3
lw x14, 48(sp) # a4
lw x15, 52(sp) # a5
lw x16, 56(sp) # a6
lw x17, 60(sp) # a7
lw x18, 64(sp) # s2
lw x19, 68(sp) # s3
lw x20, 72(sp) # s4
lw x21, 76(sp) # s5
lw x22, 80(sp) # s6
lw x23, 84(sp) # s7
lw x24, 88(sp) # s8
lw x25, 92(sp) # s9
lw x26, 96(sp) # s10
lw x27, 100(sp) # s11
lw x28, 104(sp) # t3
lw x29, 108(sp) # t4
lw x30, 112(sp) # t5
lw x31, 116(sp) # t6
# Restore sp
addi sp, sp, 124
# Return from trap
mret
"#
);
pub fn xPortIsInsideInterrupt() -> BaseType_t {
let mstatus: u32;
unsafe {
core::arch::asm!(
"csrr {mstatus}, mstatus",
mstatus = out(reg) mstatus,
options(nomem, nostack)
);
}
if (mstatus & 0x8) == 0 {
crate::types::pdTRUE
} else {
crate::types::pdFALSE
}
}
#[inline(always)]
pub fn portNOP() {
unsafe {
core::arch::asm!("nop", options(nomem, nostack, preserves_flags));
}
}
#[inline(always)]
pub fn portMEMORY_BARRIER() {
unsafe {
core::arch::asm!("fence iorw, iorw", options(nomem, nostack));
}
}
#[cfg(feature = "generate-run-time-stats")]
static mut RUN_TIME_COUNTER: u32 = 0;
#[cfg(feature = "generate-run-time-stats")]
pub fn portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() {
unsafe {
RUN_TIME_COUNTER = 0;
}
}
#[cfg(feature = "generate-run-time-stats")]
pub fn portGET_RUN_TIME_COUNTER_VALUE() -> u32 {
unsafe { core::ptr::read_volatile(MTIME_ADDR as *const u32) }
}
#[cfg(feature = "tickless-idle")]
pub fn vPortSuppressTicksAndSleep(_xExpectedIdleTime: TickType_t) {
unsafe {
core::arch::asm!("wfi", options(nomem, nostack));
}
}