blob: 4f6976797c8f07ad3b89aacda801ea129d9c0cea [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#include "sw/device/lib/testing/test_framework/ottf_macros.h"
// -----------------------------------------------------------------------------
/**
* Compute the MEPC to that will be saved to the stack by the associated ISR
* handler sub-routine below.
*
* This subroutine is only invoked for IRQs that are synchronous. Specifically,
* this subroutine updates the ISR return address to point to the instruction
* after the trapped instruction to prevent an endless interrupt cycle.
*
* Since we support the RISC-V compressed instructions extension, we need to
* check if the two least significant bits of the instruction are
* b11 (0x3), which means that the trapped instruction is not compressed,
* i.e., the trapped instruction is 32bits = 4bytes. Otherwise, the trapped
* instruction is 16bits = 2bytes.
*
* Before loading the instruction pointed to by the `mepc` we should check
* whether the `mepc` address is invalid by checking the `mcause` register for
* the `Instruction access fault` code (1u). If it is invalid, return the
* unmodified `mpec`, as attempting to load an instruction at an invalid address
* below will cause another exception.
*/
.balign 4
.type compute_mepc_on_synchronous_irq, @function
compute_mepc_on_synchronous_irq:
csrr t0, mepc
csrr t1, mcause
li t2, 0x01
bne t1, t2, .L_load_mepc
ret
.L_load_mepc:
lh t2, 0(t0)
li t1, 0x3
and t3, t2, t1
beq t3, t1, .L_32bit_trap_instr
addi t0, t0, OTTF_HALF_WORD_SIZE
ret
.L_32bit_trap_instr:
addi t0, t0, OTTF_WORD_SIZE
ret
.size compute_mepc_on_synchronous_irq, .-compute_mepc_on_synchronous_irq
// -----------------------------------------------------------------------------
/**
* Store stack pointer to current Task Control Block (TCB) ONLY if concurrency
* is enabled for a given test, i.e., the test that triggers an IRQ is running
* as a FreeRTOS task. This is because for bare-metal tests, the current
* FreeRTOS TCB pointer (pxCurrentTCB) will be NULL, which will result in an
* exception, if we attempt to perform a store to said address.
*/
.balign 4
.type save_current_sp_to_tcb, @function
save_current_sp_to_tcb:
la t0, kOttfTestConfig
lb t1, 0(t0)
beqz t1, .L_skip_sp_save
lw t2, pxCurrentTCB
sw sp, 0(t2)
.L_skip_sp_save:
ret
.size save_current_sp_to_tcb, .-save_current_sp_to_tcb
// -----------------------------------------------------------------------------
/**
* Exception handler.
*/
.balign 4
.global handler_exception
.type handler_exception, @function
handler_exception:
// Save all registers to the stack.
addi sp, sp, -OTTF_CONTEXT_SIZE
sw ra, 1 * OTTF_WORD_SIZE(sp)
sw t0, 2 * OTTF_WORD_SIZE(sp)
sw t1, 3 * OTTF_WORD_SIZE(sp)
sw t2, 4 * OTTF_WORD_SIZE(sp)
sw s0, 5 * OTTF_WORD_SIZE(sp)
sw s1, 6 * OTTF_WORD_SIZE(sp)
sw a0, 7 * OTTF_WORD_SIZE(sp)
sw a1, 8 * OTTF_WORD_SIZE(sp)
sw a2, 9 * OTTF_WORD_SIZE(sp)
sw a3, 10 * OTTF_WORD_SIZE(sp)
sw a4, 11 * OTTF_WORD_SIZE(sp)
sw a5, 12 * OTTF_WORD_SIZE(sp)
sw a6, 13 * OTTF_WORD_SIZE(sp)
sw a7, 14 * OTTF_WORD_SIZE(sp)
sw s2, 15 * OTTF_WORD_SIZE(sp)
sw s3, 16 * OTTF_WORD_SIZE(sp)
sw s4, 17 * OTTF_WORD_SIZE(sp)
sw s5, 18 * OTTF_WORD_SIZE(sp)
sw s6, 19 * OTTF_WORD_SIZE(sp)
sw s7, 20 * OTTF_WORD_SIZE(sp)
sw s8, 21 * OTTF_WORD_SIZE(sp)
sw s9, 22 * OTTF_WORD_SIZE(sp)
sw s10, 23 * OTTF_WORD_SIZE(sp)
sw s11, 24 * OTTF_WORD_SIZE(sp)
sw t3, 25 * OTTF_WORD_SIZE(sp)
sw t4, 26 * OTTF_WORD_SIZE(sp)
sw t5, 27 * OTTF_WORD_SIZE(sp)
sw t6, 28 * OTTF_WORD_SIZE(sp)
// Save MSTATUS for the MPIE bit.
csrr t0, mstatus
sw t0, 29 * OTTF_WORD_SIZE(sp)
// Save MEPC to the stack after updating it to the next instruction (since
// this is a synchronous IRQ).
jal compute_mepc_on_synchronous_irq
sw t0, 0(sp)
// Store stack pointer to current TCB (only if concurrency is enabled, i.e.,
// the test that triggers this is running as a FreeRTOS task).
jal save_current_sp_to_tcb
// Jump to the exception handler.
jal ottf_exception_handler
// Return from ISR.
j ottf_isr_exit
.size handler_exception, .-handler_exception
// -----------------------------------------------------------------------------
/**
* Software IRQ handler.
*/
.balign 4
.global handler_irq_software
.type handler_irq_software, @function
handler_irq_software:
// Save all registers to the stack.
addi sp, sp, -OTTF_CONTEXT_SIZE
sw ra, 1 * OTTF_WORD_SIZE(sp)
sw t0, 2 * OTTF_WORD_SIZE(sp)
sw t1, 3 * OTTF_WORD_SIZE(sp)
sw t2, 4 * OTTF_WORD_SIZE(sp)
sw s0, 5 * OTTF_WORD_SIZE(sp)
sw s1, 6 * OTTF_WORD_SIZE(sp)
sw a0, 7 * OTTF_WORD_SIZE(sp)
sw a1, 8 * OTTF_WORD_SIZE(sp)
sw a2, 9 * OTTF_WORD_SIZE(sp)
sw a3, 10 * OTTF_WORD_SIZE(sp)
sw a4, 11 * OTTF_WORD_SIZE(sp)
sw a5, 12 * OTTF_WORD_SIZE(sp)
sw a6, 13 * OTTF_WORD_SIZE(sp)
sw a7, 14 * OTTF_WORD_SIZE(sp)
sw s2, 15 * OTTF_WORD_SIZE(sp)
sw s3, 16 * OTTF_WORD_SIZE(sp)
sw s4, 17 * OTTF_WORD_SIZE(sp)
sw s5, 18 * OTTF_WORD_SIZE(sp)
sw s6, 19 * OTTF_WORD_SIZE(sp)
sw s7, 20 * OTTF_WORD_SIZE(sp)
sw s8, 21 * OTTF_WORD_SIZE(sp)
sw s9, 22 * OTTF_WORD_SIZE(sp)
sw s10, 23 * OTTF_WORD_SIZE(sp)
sw s11, 24 * OTTF_WORD_SIZE(sp)
sw t3, 25 * OTTF_WORD_SIZE(sp)
sw t4, 26 * OTTF_WORD_SIZE(sp)
sw t5, 27 * OTTF_WORD_SIZE(sp)
sw t6, 28 * OTTF_WORD_SIZE(sp)
// Save MSTATUS for the MPIE bit.
csrr t0, mstatus
sw t0, 29 * OTTF_WORD_SIZE(sp)
// Save MEPC to the stack.
// NOTE: this IRQ is asynchronous, therefore, we do not need to modify MEPC.
csrr t0, mepc
sw t0, 0(sp)
// Store stack pointer to current TCB (only if concurrency is enabled, i.e.,
// the test that triggers this is running as a FreeRTOS task).
jal save_current_sp_to_tcb
// Jump to the software ISR.
jal ottf_software_isr
// Return from ISR.
j ottf_isr_exit
.size handler_irq_software, .-handler_irq_software
// -----------------------------------------------------------------------------
/**
* Timer IRQ handler.
*/
.balign 4
.global handler_irq_timer
.type handler_irq_timer, @function
handler_irq_timer:
// Save all registers to the stack.
addi sp, sp, -OTTF_CONTEXT_SIZE
sw ra, 1 * OTTF_WORD_SIZE(sp)
sw t0, 2 * OTTF_WORD_SIZE(sp)
sw t1, 3 * OTTF_WORD_SIZE(sp)
sw t2, 4 * OTTF_WORD_SIZE(sp)
sw s0, 5 * OTTF_WORD_SIZE(sp)
sw s1, 6 * OTTF_WORD_SIZE(sp)
sw a0, 7 * OTTF_WORD_SIZE(sp)
sw a1, 8 * OTTF_WORD_SIZE(sp)
sw a2, 9 * OTTF_WORD_SIZE(sp)
sw a3, 10 * OTTF_WORD_SIZE(sp)
sw a4, 11 * OTTF_WORD_SIZE(sp)
sw a5, 12 * OTTF_WORD_SIZE(sp)
sw a6, 13 * OTTF_WORD_SIZE(sp)
sw a7, 14 * OTTF_WORD_SIZE(sp)
sw s2, 15 * OTTF_WORD_SIZE(sp)
sw s3, 16 * OTTF_WORD_SIZE(sp)
sw s4, 17 * OTTF_WORD_SIZE(sp)
sw s5, 18 * OTTF_WORD_SIZE(sp)
sw s6, 19 * OTTF_WORD_SIZE(sp)
sw s7, 20 * OTTF_WORD_SIZE(sp)
sw s8, 21 * OTTF_WORD_SIZE(sp)
sw s9, 22 * OTTF_WORD_SIZE(sp)
sw s10, 23 * OTTF_WORD_SIZE(sp)
sw s11, 24 * OTTF_WORD_SIZE(sp)
sw t3, 25 * OTTF_WORD_SIZE(sp)
sw t4, 26 * OTTF_WORD_SIZE(sp)
sw t5, 27 * OTTF_WORD_SIZE(sp)
sw t6, 28 * OTTF_WORD_SIZE(sp)
// Save MSTATUS for the MPIE bit.
csrr t0, mstatus
sw t0, 29 * OTTF_WORD_SIZE(sp)
// Save MEPC to the stack.
// NOTE: this IRQ is asynchronous, therefore, we do not need to modify MEPC.
csrr t0, mepc
sw t0, 0(sp)
// Store stack pointer to current TCB (only if concurrency is enabled, i.e.,
// the test that triggers this is running as a FreeRTOS task).
jal save_current_sp_to_tcb
// Jump to timer ISR.
jal ottf_timer_isr
// Return from ISR.
j ottf_isr_exit
.size handler_irq_timer, .-handler_irq_timer
// -----------------------------------------------------------------------------
/**
* External IRQ handler.
*/
.balign 4
.global handler_irq_external
.type handler_irq_external, @function
handler_irq_external:
// Save all registers to the stack.
addi sp, sp, -OTTF_CONTEXT_SIZE
sw ra, 1 * OTTF_WORD_SIZE(sp)
sw t0, 2 * OTTF_WORD_SIZE(sp)
sw t1, 3 * OTTF_WORD_SIZE(sp)
sw t2, 4 * OTTF_WORD_SIZE(sp)
sw s0, 5 * OTTF_WORD_SIZE(sp)
sw s1, 6 * OTTF_WORD_SIZE(sp)
sw a0, 7 * OTTF_WORD_SIZE(sp)
sw a1, 8 * OTTF_WORD_SIZE(sp)
sw a2, 9 * OTTF_WORD_SIZE(sp)
sw a3, 10 * OTTF_WORD_SIZE(sp)
sw a4, 11 * OTTF_WORD_SIZE(sp)
sw a5, 12 * OTTF_WORD_SIZE(sp)
sw a6, 13 * OTTF_WORD_SIZE(sp)
sw a7, 14 * OTTF_WORD_SIZE(sp)
sw s2, 15 * OTTF_WORD_SIZE(sp)
sw s3, 16 * OTTF_WORD_SIZE(sp)
sw s4, 17 * OTTF_WORD_SIZE(sp)
sw s5, 18 * OTTF_WORD_SIZE(sp)
sw s6, 19 * OTTF_WORD_SIZE(sp)
sw s7, 20 * OTTF_WORD_SIZE(sp)
sw s8, 21 * OTTF_WORD_SIZE(sp)
sw s9, 22 * OTTF_WORD_SIZE(sp)
sw s10, 23 * OTTF_WORD_SIZE(sp)
sw s11, 24 * OTTF_WORD_SIZE(sp)
sw t3, 25 * OTTF_WORD_SIZE(sp)
sw t4, 26 * OTTF_WORD_SIZE(sp)
sw t5, 27 * OTTF_WORD_SIZE(sp)
sw t6, 28 * OTTF_WORD_SIZE(sp)
// Save MSTATUS for the MPIE bit.
csrr t0, mstatus
sw t0, 29 * OTTF_WORD_SIZE(sp)
// Save MEPC to the stack.
// NOTE: this IRQ is asynchronous, therefore, we do not need to modify MEPC.
csrr t0, mepc
sw t0, 0(sp)
// Store stack pointer to current TCB (only if concurrency is enabled, i.e.,
// the test that triggers this is running as a FreeRTOS task).
jal save_current_sp_to_tcb
// Jump to external ISR.
jal ottf_external_isr
// Return from ISR.
j ottf_isr_exit
.size handler_irq_external, .-handler_irq_external
// -----------------------------------------------------------------------------
/**
* Internal IRQ handler.
*/
.balign 4
.global handler_irq_internal
.type handler_irq_internal, @function
handler_irq_internal:
// Save all registers to the stack.
addi sp, sp, -OTTF_CONTEXT_SIZE
sw ra, 1 * OTTF_WORD_SIZE(sp)
sw t0, 2 * OTTF_WORD_SIZE(sp)
sw t1, 3 * OTTF_WORD_SIZE(sp)
sw t2, 4 * OTTF_WORD_SIZE(sp)
sw s0, 5 * OTTF_WORD_SIZE(sp)
sw s1, 6 * OTTF_WORD_SIZE(sp)
sw a0, 7 * OTTF_WORD_SIZE(sp)
sw a1, 8 * OTTF_WORD_SIZE(sp)
sw a2, 9 * OTTF_WORD_SIZE(sp)
sw a3, 10 * OTTF_WORD_SIZE(sp)
sw a4, 11 * OTTF_WORD_SIZE(sp)
sw a5, 12 * OTTF_WORD_SIZE(sp)
sw a6, 13 * OTTF_WORD_SIZE(sp)
sw a7, 14 * OTTF_WORD_SIZE(sp)
sw s2, 15 * OTTF_WORD_SIZE(sp)
sw s3, 16 * OTTF_WORD_SIZE(sp)
sw s4, 17 * OTTF_WORD_SIZE(sp)
sw s5, 18 * OTTF_WORD_SIZE(sp)
sw s6, 19 * OTTF_WORD_SIZE(sp)
sw s7, 20 * OTTF_WORD_SIZE(sp)
sw s8, 21 * OTTF_WORD_SIZE(sp)
sw s9, 22 * OTTF_WORD_SIZE(sp)
sw s10, 23 * OTTF_WORD_SIZE(sp)
sw s11, 24 * OTTF_WORD_SIZE(sp)
sw t3, 25 * OTTF_WORD_SIZE(sp)
sw t4, 26 * OTTF_WORD_SIZE(sp)
sw t5, 27 * OTTF_WORD_SIZE(sp)
sw t6, 28 * OTTF_WORD_SIZE(sp)
// Save MSTATUS for the MPIE bit.
csrr t0, mstatus
sw t0, 29 * OTTF_WORD_SIZE(sp)
// Save MEPC to the stack.
// NOTE: this IRQ is asynchronous, therefore, we do not need to modify MEPC.
csrr t0, mepc
sw t0, 0(sp)
// Store stack pointer to current TCB (only if concurrency is enabled, i.e.,
// the test that triggers this is running as a FreeRTOS task).
jal save_current_sp_to_tcb
// Jump to the internal ISR.
jal ottf_internal_isr
// Return from ISR.
j ottf_isr_exit
.size handler_irq_internal, .-handler_irq_internal
// -----------------------------------------------------------------------------
/**
* ISR exit sub-routine restores registers from the stack.
*/
.balign 4
.global ottf_isr_exit
.type ottf_isr_exit, @function
ottf_isr_exit:
// Load the stack pointer for the current task control block (TCB), only if
// the `enable_concurrency` flag is set in the test configuration struct,
// meaning a test is run as a FreeRTOS task, where each task maintains its own
// stack. Otherwise, the test is run on bare-metal, and there is no TCB, and
// only a single stack/stack pointer.
la t0, kOttfTestConfig
lb t1, 0(t0)
beqz t1, .L_skip_sp_restore
lw t2, pxCurrentTCB
lw sp, 0(t2)
.L_skip_sp_restore:
// Load the correct MEPC for the next instruction in the current task.
lw t0, 0(sp)
csrw mepc, t0
// Load MSTATUS for the MPIE bit.
lw t0, 29 * OTTF_WORD_SIZE(sp)
csrw mstatus, t0
// Restore all registers from the stack.
lw ra, 1 * OTTF_WORD_SIZE(sp)
lw t0, 2 * OTTF_WORD_SIZE(sp)
lw t1, 3 * OTTF_WORD_SIZE(sp)
lw t2, 4 * OTTF_WORD_SIZE(sp)
lw s0, 5 * OTTF_WORD_SIZE(sp)
lw s1, 6 * OTTF_WORD_SIZE(sp)
lw a0, 7 * OTTF_WORD_SIZE(sp)
lw a1, 8 * OTTF_WORD_SIZE(sp)
lw a2, 9 * OTTF_WORD_SIZE(sp)
lw a3, 10 * OTTF_WORD_SIZE(sp)
lw a4, 11 * OTTF_WORD_SIZE(sp)
lw a5, 12 * OTTF_WORD_SIZE(sp)
lw a6, 13 * OTTF_WORD_SIZE(sp)
lw a7, 14 * OTTF_WORD_SIZE(sp)
lw s2, 15 * OTTF_WORD_SIZE(sp)
lw s3, 16 * OTTF_WORD_SIZE(sp)
lw s4, 17 * OTTF_WORD_SIZE(sp)
lw s5, 18 * OTTF_WORD_SIZE(sp)
lw s6, 19 * OTTF_WORD_SIZE(sp)
lw s7, 20 * OTTF_WORD_SIZE(sp)
lw s8, 21 * OTTF_WORD_SIZE(sp)
lw s9, 22 * OTTF_WORD_SIZE(sp)
lw s10, 23 * OTTF_WORD_SIZE(sp)
lw s11, 24 * OTTF_WORD_SIZE(sp)
lw t3, 25 * OTTF_WORD_SIZE(sp)
lw t4, 26 * OTTF_WORD_SIZE(sp)
lw t5, 27 * OTTF_WORD_SIZE(sp)
lw t6, 28 * OTTF_WORD_SIZE(sp)
addi sp, sp, OTTF_CONTEXT_SIZE
// This exits the ISR completely, and does not return control flow to the ISR
// that called this sub-routine.
mret
.size ottf_isr_exit, .-ottf_isr_exit