[sw/ottf] implemented default ISRs
This commit adds the following to the OTTF:
- defualt (weak) ISRs
- FreeRTOS context saving (asm) subroutines to be called on ISR
entry/exit
Signed-off-by: Timothy Trippel <ttrippel@google.com>
diff --git a/sw/device/lib/base/macros.h b/sw/device/lib/base/macros.h
index 7f5ded0..2e55205 100644
--- a/sw/device/lib/base/macros.h
+++ b/sw/device/lib/base/macros.h
@@ -93,4 +93,9 @@
*/
#define OT_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+/**
+ * Attribute for weak functions that can be overridden, e.g., ISRs.
+ */
+#define OT_ATTR_WEAK __attribute__((weak))
+
#endif // OPENTITAN_SW_DEVICE_LIB_BASE_MACROS_H_
diff --git a/sw/device/lib/runtime/ibex.c b/sw/device/lib/runtime/ibex.c
index 776edee..7e3ff05 100644
--- a/sw/device/lib/runtime/ibex.c
+++ b/sw/device/lib/runtime/ibex.c
@@ -4,4 +4,18 @@
#include "sw/device/lib/runtime/ibex.h"
+#include "sw/device/lib/base/csr.h"
+
extern uint64_t ibex_mcycle_read(void);
+
+uint32_t ibex_mcause_read(void) {
+ uint32_t mtval;
+ CSR_READ(CSR_REG_MCAUSE, &mtval);
+ return mtval;
+}
+
+uint32_t ibex_mtval_read(void) {
+ uint32_t mtval;
+ CSR_READ(CSR_REG_MTVAL, &mtval);
+ return mtval;
+}
diff --git a/sw/device/lib/runtime/ibex.h b/sw/device/lib/runtime/ibex.h
index 5c389ba..d989f34 100644
--- a/sw/device/lib/runtime/ibex.h
+++ b/sw/device/lib/runtime/ibex.h
@@ -41,4 +41,38 @@
return (uint64_t)cycle_high << 32 | cycle_low;
}
+/**
+ * Reads the mcause register.
+ *
+ * When an exception is encountered, the corresponding exception code is stored
+ * in mcause register.
+ *
+ * A list of the exception codes can be found at:
+ * https://ibex-core.readthedocs.io/en/latest/03_reference/
+ * exception_interrupts.html#exceptions
+ */
+uint32_t ibex_mcause_read(void);
+
+/**
+ * Reads the mtval register.
+ *
+ * When an exception is encountered, the Machine Trap Value (mtval) register
+ * can holds exception-specific information to assist software in handling the
+ * trap.
+ *
+ * From the Ibex documentation (found at
+ * https://ibex-core.readthedocs.io/en/latest/03_reference/cs_registers.html)
+ * - In the case of errors in the load-store unit mtval holds the address of
+ * the transaction causing the error.
+ *
+ * - If a transaction is misaligned, mtval holds the address of the missing
+ * transaction part.
+ *
+ * - In the case of illegal instruction exceptions, mtval holds the actual
+ * faulting instruction.
+ *
+ * - For all other exceptions, mtval is 0.
+ */
+uint32_t ibex_mtval_read(void);
+
#endif // OPENTITAN_SW_DEVICE_LIB_RUNTIME_IBEX_H_
diff --git a/sw/device/lib/testing/test_framework/FreeRTOSConfig.h b/sw/device/lib/testing/test_framework/FreeRTOSConfig.h
index d8db2ba..501367c 100644
--- a/sw/device/lib/testing/test_framework/FreeRTOSConfig.h
+++ b/sw/device/lib/testing/test_framework/FreeRTOSConfig.h
@@ -1,7 +1,7 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
-//
+
#ifndef OPENTITAN_SW_DEVICE_LIB_TESTING_TEST_FRAMEWORK_FREERTOSCONFIG_H_
#define OPENTITAN_SW_DEVICE_LIB_TESTING_TEST_FRAMEWORK_FREERTOSCONFIG_H_
@@ -14,7 +14,7 @@
// guide, since they are specific to FreeRTOS.
// Main FreeRTOS configs.
-#define configUSE_PREEMPTION 0
+#define configUSE_PREEMPTION 1
#define configUSE_IDLE_HOOK 0
#define configUSE_TICK_HOOK 0
#define configTICK_RATE_HZ ((TickType_t)5)
@@ -48,7 +48,7 @@
// Task priorities. Allow these to be overridden.
#ifndef uartPRIMARY_PRIORITY
#define uartPRIMARY_PRIORITY (configMAX_PRIORITIES - 3)
-#endif
+#endif // uartPRIMARY_PRIORITY
// Set the following definitions to 1 to include the API function, or zero to
// exclude the API function.
diff --git a/sw/device/lib/testing/test_framework/freertos_port.S b/sw/device/lib/testing/test_framework/freertos_port.S
index a11b1f6..e0ce730 100644
--- a/sw/device/lib/testing/test_framework/freertos_port.S
+++ b/sw/device/lib/testing/test_framework/freertos_port.S
@@ -8,13 +8,327 @@
.extern pxCurrentTCB
.extern xISRStackTop
+.extern ottf_exception_handler
+.extern ottf_software_isr
+.extern ottf_timer_isr
+.extern ottf_external_isr
// -----------------------------------------------------------------------------
/**
+ * Exception handler.
+ */
+.balign 4
+.global handler_exception
+.type handler_exception, @function
+handler_exception:
+ // Save all registers to the stack.
+ addi sp, sp, -PORT_CONTEXT_SIZE
+ sw ra, 1 * PORT_WORD_SIZE(sp)
+ sw t0, 2 * PORT_WORD_SIZE(sp)
+ sw t1, 3 * PORT_WORD_SIZE(sp)
+ sw t2, 4 * PORT_WORD_SIZE(sp)
+ sw s0, 5 * PORT_WORD_SIZE(sp)
+ sw s1, 6 * PORT_WORD_SIZE(sp)
+ sw a0, 7 * PORT_WORD_SIZE(sp)
+ sw a1, 8 * PORT_WORD_SIZE(sp)
+ sw a2, 9 * PORT_WORD_SIZE(sp)
+ sw a3, 10 * PORT_WORD_SIZE(sp)
+ sw a4, 11 * PORT_WORD_SIZE(sp)
+ sw a5, 12 * PORT_WORD_SIZE(sp)
+ sw a6, 13 * PORT_WORD_SIZE(sp)
+ sw a7, 14 * PORT_WORD_SIZE(sp)
+ sw s2, 15 * PORT_WORD_SIZE(sp)
+ sw s3, 16 * PORT_WORD_SIZE(sp)
+ sw s4, 17 * PORT_WORD_SIZE(sp)
+ sw s5, 18 * PORT_WORD_SIZE(sp)
+ sw s6, 19 * PORT_WORD_SIZE(sp)
+ sw s7, 20 * PORT_WORD_SIZE(sp)
+ sw s8, 21 * PORT_WORD_SIZE(sp)
+ sw s9, 22 * PORT_WORD_SIZE(sp)
+ sw s10, 23 * PORT_WORD_SIZE(sp)
+ sw s11, 24 * PORT_WORD_SIZE(sp)
+ sw t3, 25 * PORT_WORD_SIZE(sp)
+ sw t4, 26 * PORT_WORD_SIZE(sp)
+ sw t5, 27 * PORT_WORD_SIZE(sp)
+ sw t6, 28 * PORT_WORD_SIZE(sp)
+
+ // Save MSTATUS for the MPIE bit.
+ csrr t0, mstatus
+ sw t0, 29 * PORT_WORD_SIZE(sp)
+
+ // Save MEPC to the stack.
+ // NOTE: this ISQ is synchronous, therefore, we must update the ISR resturn
+ // address to point to the instruction after the one that triggered this IRQ.
+ csrr t0, mepc
+ addi t0, t0, PORT_WORD_SIZE
+ sw t0, 0(sp)
+
+ // Store stack pointer to current TCB.
+ lw t0, pxCurrentTCB
+ sw sp, 0(t0)
+
+ // Jump to the exception handler.
+ jal ottf_exception_handler
+
+ // Return from ISR.
+ j freertosIrqExit
+
+ // Set size so this function can be disassembled.
+ .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, -PORT_CONTEXT_SIZE
+ sw ra, 1 * PORT_WORD_SIZE(sp)
+ sw t0, 2 * PORT_WORD_SIZE(sp)
+ sw t1, 3 * PORT_WORD_SIZE(sp)
+ sw t2, 4 * PORT_WORD_SIZE(sp)
+ sw s0, 5 * PORT_WORD_SIZE(sp)
+ sw s1, 6 * PORT_WORD_SIZE(sp)
+ sw a0, 7 * PORT_WORD_SIZE(sp)
+ sw a1, 8 * PORT_WORD_SIZE(sp)
+ sw a2, 9 * PORT_WORD_SIZE(sp)
+ sw a3, 10 * PORT_WORD_SIZE(sp)
+ sw a4, 11 * PORT_WORD_SIZE(sp)
+ sw a5, 12 * PORT_WORD_SIZE(sp)
+ sw a6, 13 * PORT_WORD_SIZE(sp)
+ sw a7, 14 * PORT_WORD_SIZE(sp)
+ sw s2, 15 * PORT_WORD_SIZE(sp)
+ sw s3, 16 * PORT_WORD_SIZE(sp)
+ sw s4, 17 * PORT_WORD_SIZE(sp)
+ sw s5, 18 * PORT_WORD_SIZE(sp)
+ sw s6, 19 * PORT_WORD_SIZE(sp)
+ sw s7, 20 * PORT_WORD_SIZE(sp)
+ sw s8, 21 * PORT_WORD_SIZE(sp)
+ sw s9, 22 * PORT_WORD_SIZE(sp)
+ sw s10, 23 * PORT_WORD_SIZE(sp)
+ sw s11, 24 * PORT_WORD_SIZE(sp)
+ sw t3, 25 * PORT_WORD_SIZE(sp)
+ sw t4, 26 * PORT_WORD_SIZE(sp)
+ sw t5, 27 * PORT_WORD_SIZE(sp)
+ sw t6, 28 * PORT_WORD_SIZE(sp)
+
+ // Save MSTATUS for the MPIE bit.
+ csrr t0, mstatus
+ sw t0, 29 * PORT_WORD_SIZE(sp)
+
+ // Save MEPC to the stack.
+ // NOTE: this ISQ is synchronous, therefore, we must update the ISR resturn
+ // address to point to the instruction after the one that triggered this IRQ.
+ csrr t0, mepc
+ addi t0, t0, PORT_WORD_SIZE
+ sw t0, 0(sp)
+
+ // Store stack pointer to current TCB.
+ lw t0, pxCurrentTCB
+ sw sp, 0(t0)
+
+ // Jump to the software ISR.
+ jal ottf_software_isr
+
+ // Return from ISR.
+ j freertosIrqExit
+
+ // Set size so this function can be disassembled.
+ .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, -PORT_CONTEXT_SIZE
+ sw ra, 1 * PORT_WORD_SIZE(sp)
+ sw t0, 2 * PORT_WORD_SIZE(sp)
+ sw t1, 3 * PORT_WORD_SIZE(sp)
+ sw t2, 4 * PORT_WORD_SIZE(sp)
+ sw s0, 5 * PORT_WORD_SIZE(sp)
+ sw s1, 6 * PORT_WORD_SIZE(sp)
+ sw a0, 7 * PORT_WORD_SIZE(sp)
+ sw a1, 8 * PORT_WORD_SIZE(sp)
+ sw a2, 9 * PORT_WORD_SIZE(sp)
+ sw a3, 10 * PORT_WORD_SIZE(sp)
+ sw a4, 11 * PORT_WORD_SIZE(sp)
+ sw a5, 12 * PORT_WORD_SIZE(sp)
+ sw a6, 13 * PORT_WORD_SIZE(sp)
+ sw a7, 14 * PORT_WORD_SIZE(sp)
+ sw s2, 15 * PORT_WORD_SIZE(sp)
+ sw s3, 16 * PORT_WORD_SIZE(sp)
+ sw s4, 17 * PORT_WORD_SIZE(sp)
+ sw s5, 18 * PORT_WORD_SIZE(sp)
+ sw s6, 19 * PORT_WORD_SIZE(sp)
+ sw s7, 20 * PORT_WORD_SIZE(sp)
+ sw s8, 21 * PORT_WORD_SIZE(sp)
+ sw s9, 22 * PORT_WORD_SIZE(sp)
+ sw s10, 23 * PORT_WORD_SIZE(sp)
+ sw s11, 24 * PORT_WORD_SIZE(sp)
+ sw t3, 25 * PORT_WORD_SIZE(sp)
+ sw t4, 26 * PORT_WORD_SIZE(sp)
+ sw t5, 27 * PORT_WORD_SIZE(sp)
+ sw t6, 28 * PORT_WORD_SIZE(sp)
+
+ // Save MSTATUS for the MPIE bit.
+ csrr t0, mstatus
+ sw t0, 29 * PORT_WORD_SIZE(sp)
+
+ // Save MEPC to the stack.
+ // NOTE: this ISQ is asynchronous, therefore, we do not need to modify MEPC.
+ csrr t0, mepc
+ sw t0, 0(sp)
+
+ // Store stack pointer to current TCB.
+ lw t0, pxCurrentTCB
+ sw sp, 0(t0)
+
+ // Jump to timer ISR.
+ jal ottf_timer_isr
+
+ // Return from ISR.
+ j freertosIrqExit
+
+ // Set size so this function can be disassembled.
+ .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, -PORT_CONTEXT_SIZE
+ sw ra, 1 * PORT_WORD_SIZE(sp)
+ sw t0, 2 * PORT_WORD_SIZE(sp)
+ sw t1, 3 * PORT_WORD_SIZE(sp)
+ sw t2, 4 * PORT_WORD_SIZE(sp)
+ sw s0, 5 * PORT_WORD_SIZE(sp)
+ sw s1, 6 * PORT_WORD_SIZE(sp)
+ sw a0, 7 * PORT_WORD_SIZE(sp)
+ sw a1, 8 * PORT_WORD_SIZE(sp)
+ sw a2, 9 * PORT_WORD_SIZE(sp)
+ sw a3, 10 * PORT_WORD_SIZE(sp)
+ sw a4, 11 * PORT_WORD_SIZE(sp)
+ sw a5, 12 * PORT_WORD_SIZE(sp)
+ sw a6, 13 * PORT_WORD_SIZE(sp)
+ sw a7, 14 * PORT_WORD_SIZE(sp)
+ sw s2, 15 * PORT_WORD_SIZE(sp)
+ sw s3, 16 * PORT_WORD_SIZE(sp)
+ sw s4, 17 * PORT_WORD_SIZE(sp)
+ sw s5, 18 * PORT_WORD_SIZE(sp)
+ sw s6, 19 * PORT_WORD_SIZE(sp)
+ sw s7, 20 * PORT_WORD_SIZE(sp)
+ sw s8, 21 * PORT_WORD_SIZE(sp)
+ sw s9, 22 * PORT_WORD_SIZE(sp)
+ sw s10, 23 * PORT_WORD_SIZE(sp)
+ sw s11, 24 * PORT_WORD_SIZE(sp)
+ sw t3, 25 * PORT_WORD_SIZE(sp)
+ sw t4, 26 * PORT_WORD_SIZE(sp)
+ sw t5, 27 * PORT_WORD_SIZE(sp)
+ sw t6, 28 * PORT_WORD_SIZE(sp)
+
+ // Save MSTATUS for the MPIE bit.
+ csrr t0, mstatus
+ sw t0, 29 * PORT_WORD_SIZE(sp)
+
+ // Save MEPC to the stack.
+ // NOTE: this ISQ is asynchronous, therefore, we do not need to modify MEPC.
+ csrr t0, mepc
+ sw t0, 0(sp)
+
+ // Store stack pointer to current TCB.
+ lw t0, pxCurrentTCB
+ sw sp, 0(t0)
+
+ // Jump to external ISR.
+ jal ottf_external_isr
+
+ // Return from ISR.
+ j freertosIrqExit
+
+ // Set size so this function can be disassembled.
+ .size handler_irq_external, .-handler_irq_external
+
+// -----------------------------------------------------------------------------
+
+/**
+ * ISR exit sub-routine restores registers from the stack.
+ */
+.balign 4
+.global freertosIrqExit
+.type freertosIrqExit, @function
+freertosIrqExit:
+ // Load the stack pointer for the current TCB.
+ lw t1, pxCurrentTCB
+ lw sp, 0(t1)
+
+ // 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 * PORT_WORD_SIZE(sp)
+ csrw mstatus, t0
+
+ // Restore all registers from the stack.
+ lw ra, 1 * PORT_WORD_SIZE(sp)
+ lw t0, 2 * PORT_WORD_SIZE(sp)
+ lw t1, 3 * PORT_WORD_SIZE(sp)
+ lw t2, 4 * PORT_WORD_SIZE(sp)
+ lw s0, 5 * PORT_WORD_SIZE(sp)
+ lw s1, 6 * PORT_WORD_SIZE(sp)
+ lw a0, 7 * PORT_WORD_SIZE(sp)
+ lw a1, 8 * PORT_WORD_SIZE(sp)
+ lw a2, 9 * PORT_WORD_SIZE(sp)
+ lw a3, 10 * PORT_WORD_SIZE(sp)
+ lw a4, 11 * PORT_WORD_SIZE(sp)
+ lw a5, 12 * PORT_WORD_SIZE(sp)
+ lw a6, 13 * PORT_WORD_SIZE(sp)
+ lw a7, 14 * PORT_WORD_SIZE(sp)
+ lw s2, 15 * PORT_WORD_SIZE(sp)
+ lw s3, 16 * PORT_WORD_SIZE(sp)
+ lw s4, 17 * PORT_WORD_SIZE(sp)
+ lw s5, 18 * PORT_WORD_SIZE(sp)
+ lw s6, 19 * PORT_WORD_SIZE(sp)
+ lw s7, 20 * PORT_WORD_SIZE(sp)
+ lw s8, 21 * PORT_WORD_SIZE(sp)
+ lw s9, 22 * PORT_WORD_SIZE(sp)
+ lw s10, 23 * PORT_WORD_SIZE(sp)
+ lw s11, 24 * PORT_WORD_SIZE(sp)
+ lw t3, 25 * PORT_WORD_SIZE(sp)
+ lw t4, 26 * PORT_WORD_SIZE(sp)
+ lw t5, 27 * PORT_WORD_SIZE(sp)
+ lw t6, 28 * PORT_WORD_SIZE(sp)
+ addi sp, sp, PORT_CONTEXT_SIZE
+
+ // This exits the ISR completely, and does not return control flow to the ISR
+ // that called this sub-routine.
+ mret
+
+ // Set size so this function can be disassembled.
+ .size freertosIrqExit, .-freertosIrqExit
+// -----------------------------------------------------------------------------
+
+/**
* FreeRTOS, expects this function to exist and uses it to start the first task.
*/
-.balign 8
+.balign 4
.global xPortStartFirstTask
.type xPortStartFirstTask, @function
xPortStartFirstTask:
@@ -133,7 +447,7 @@
* 0 - (pxCode)
* -----Stack Top----
*/
-.balign 8
+.balign 4
.global pxPortInitialiseStack
.type pxPortInitialiseStack, @function
pxPortInitialiseStack:
diff --git a/sw/device/lib/testing/test_framework/freertos_port.c b/sw/device/lib/testing/test_framework/freertos_port.c
index f5f155c..c30d346 100644
--- a/sw/device/lib/testing/test_framework/freertos_port.c
+++ b/sw/device/lib/testing/test_framework/freertos_port.c
@@ -2,13 +2,12 @@
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
-#include "sw/device/lib/base/csr.h"
-#include "sw/device/lib/base/memory.h"
#include "sw/device/lib/dif/dif_rv_timer.h"
-#include "sw/device/lib/handler.h"
#include "sw/device/lib/irq.h"
+#include "sw/device/lib/runtime/hart.h"
#include "sw/device/lib/runtime/log.h"
#include "sw/device/lib/testing/check.h"
+#include "sw/device/lib/testing/test_framework/FreeRTOSConfig.h"
#include "sw/vendor/freertos_freertos_kernel/include/FreeRTOS.h"
#include "sw/vendor/freertos_freertos_kernel/include/task.h"
#include "sw/vendor/freertos_freertos_kernel/portable/GCC/RISC-V/portmacro.h"
@@ -21,14 +20,29 @@
// functions.
// ----------------------------------------------------------------------------
-// Timer.
+// Timer Setup (for use when preemptive scheduling is enabled)
// ----------------------------------------------------------------------------
+#if configUSE_PREEMPTION
+
static dif_rv_timer_t timer;
static const uint32_t kTimerHartId = (uint32_t)kTopEarlgreyPlicTargetIbex0;
static const uint32_t kTimerComparatorId = 0;
static const uint64_t kTimerDeadline =
100; // Counter must reach 100 for an IRQ to be triggered.
+// Override the timer ISR to support preemptive context switching.
+void ottf_timer_isr(void) {
+ LOG_INFO("Handling timer IQR ...");
+ CHECK(dif_rv_timer_irq_disable(&timer, kTimerHartId, NULL) == kDifRvTimerOk);
+ CHECK(dif_rv_timer_counter_write(&timer, kTimerHartId, 0) == kDifRvTimerOk);
+ CHECK(dif_rv_timer_irq_clear(&timer, kTimerHartId, kTimerComparatorId) ==
+ kDifRvTimerOk);
+ // TODO: increment scheduler tick and switch context if necessary
+ CHECK(dif_rv_timer_irq_enable(&timer, kTimerHartId, kTimerComparatorId,
+ kDifRvTimerEnabled) == kDifRvTimerOk);
+ LOG_INFO("Done.");
+}
+
void vPortSetupTimerInterrupt(void) {
LOG_INFO("Configuring timer interrupt ...");
@@ -61,13 +75,17 @@
kDifRvTimerEnabled) == kDifRvTimerOk);
}
+#endif // configUSE_PREEMPTION
+
// ----------------------------------------------------------------------------
-// Scheduler.
+// Scheduler Setup
// ----------------------------------------------------------------------------
extern void xPortStartFirstTask(void);
BaseType_t xPortStartScheduler(void) {
+#if configUSE_PREEMPTION
vPortSetupTimerInterrupt();
+#endif // configUSE_PREEMPTION
irq_timer_ctrl(true);
irq_external_ctrl(true);
irq_software_ctrl(true);
@@ -85,18 +103,3 @@
wait_for_interrupt();
}
}
-
-// ----------------------------------------------------------------------------
-// ISRs.
-// TODO: add support for remaining ISRs.
-// ----------------------------------------------------------------------------
-void handler_irq_timer(void) {
- LOG_INFO("Handling timer IQR ...");
- CHECK(dif_rv_timer_irq_disable(&timer, kTimerHartId, NULL) == kDifRvTimerOk);
- CHECK(dif_rv_timer_counter_write(&timer, kTimerHartId, 0) == kDifRvTimerOk);
- CHECK(dif_rv_timer_irq_clear(&timer, kTimerHartId, kTimerComparatorId) ==
- kDifRvTimerOk);
- CHECK(dif_rv_timer_irq_enable(&timer, kTimerHartId, kTimerComparatorId,
- kDifRvTimerEnabled) == kDifRvTimerOk);
- LOG_INFO("Done.");
-}
diff --git a/sw/device/lib/testing/test_framework/meson.build b/sw/device/lib/testing/test_framework/meson.build
index b3b1092..ee1425a 100644
--- a/sw/device/lib/testing/test_framework/meson.build
+++ b/sw/device/lib/testing/test_framework/meson.build
@@ -78,6 +78,7 @@
# OpenTitan Test Framework (OTTF) sources & includes.
ottf_sources = [
'ottf.c',
+ 'ottf_isrs.c',
'example_earlgrey_test.c',
'freertos_hooks.c',
'freertos_port.S',
@@ -103,6 +104,7 @@
],
dependencies: [
sw_lib_mem,
+ sw_lib_runtime_ibex,
sw_lib_runtime_hart,
sw_lib_runtime_log,
sw_lib_runtime_print,
diff --git a/sw/device/lib/testing/test_framework/ottf_isrs.c b/sw/device/lib/testing/test_framework/ottf_isrs.c
new file mode 100644
index 0000000..3e302e2
--- /dev/null
+++ b/sw/device/lib/testing/test_framework/ottf_isrs.c
@@ -0,0 +1,101 @@
+// 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_isrs.h"
+
+#include "sw/device/lib/base/csr.h"
+#include "sw/device/lib/base/macros.h"
+#include "sw/device/lib/runtime/hart.h"
+#include "sw/device/lib/runtime/ibex.h"
+#include "sw/device/lib/runtime/log.h"
+#include "sw/device/lib/testing/test_framework/FreeRTOSConfig.h"
+
+OT_ATTR_WEAK void ottf_exception_handler(void) {
+ uint32_t mcause = ibex_mcause_read();
+
+ switch ((ottf_exc_id_t)(mcause & kIdMax)) {
+ case kInstrMisaligned:
+ ottf_instr_misaligned_fault();
+ break;
+ case kInstrAccessFault:
+ ottf_instr_access_fault();
+ break;
+ case kIllegalInstrFault:
+ ottf_illegal_instr_fault();
+ break;
+ case kBreakpoint:
+ ottf_breakpoint();
+ break;
+ case kLoadAccessFault:
+ ottf_load_store_fault();
+ break;
+ case kStoreAccessFault:
+ ottf_load_store_fault();
+ break;
+ case kMachineECall:
+ ottf_machine_ecall();
+ break;
+ case kUserECall:
+ ottf_user_ecall();
+ break;
+ default:
+ LOG_INFO("Unknown exception triggered!");
+ abort();
+ }
+}
+
+OT_ATTR_WEAK void ottf_instr_misaligned_fault(void) {
+ LOG_INFO("Misaligned instruction, mtval holds the fault address.");
+ LOG_INFO("MTVAL value is 0x%x", ibex_mtval_read());
+ abort();
+}
+
+OT_ATTR_WEAK void ottf_instr_access_fault(void) {
+ LOG_INFO("Instruction access fault, mtval holds the fault address.");
+ LOG_INFO("MTVAL value is 0x%x", ibex_mtval_read());
+ abort();
+}
+
+OT_ATTR_WEAK void ottf_illegal_instr_fault(void) {
+ LOG_INFO("Illegal instruction fault, mtval shows the faulting instruction.");
+ LOG_INFO("MTVAL value is 0x%x", ibex_mtval_read());
+ abort();
+}
+
+OT_ATTR_WEAK void ottf_breakpoint(void) {
+ LOG_INFO("Breakpoint triggered, mtval holds the breakpoint address.");
+ LOG_INFO("MTVAL value is 0x%x", ibex_mtval_read());
+ abort();
+}
+
+OT_ATTR_WEAK void ottf_load_store_fault(void) {
+ LOG_INFO("Load/Store fault, mtval holds the fault address.");
+ LOG_INFO("MTVAL value is 0x%x", ibex_mtval_read());
+ abort();
+}
+
+OT_ATTR_WEAK void ottf_machine_ecall(void) {
+ LOG_INFO("Machine-mode environment call (syscall).");
+ abort();
+}
+
+OT_ATTR_WEAK void ottf_user_ecall(void) {
+ LOG_INFO("User-mode environment call (syscall).");
+ abort();
+}
+
+OT_ATTR_WEAK void ottf_software_isr(void) {
+ LOG_INFO("Software IRQ triggered!");
+ abort();
+}
+
+OT_ATTR_WEAK void ottf_timer_isr(void) {
+ LOG_INFO("Timer IRQ triggered!");
+ abort();
+}
+
+OT_ATTR_WEAK void ottf_external_isr(void) {
+ LOG_INFO("External IRQ triggered!");
+ abort();
+}
diff --git a/sw/device/lib/testing/test_framework/ottf_isrs.h b/sw/device/lib/testing/test_framework/ottf_isrs.h
new file mode 100644
index 0000000..e76487f
--- /dev/null
+++ b/sw/device/lib/testing/test_framework/ottf_isrs.h
@@ -0,0 +1,134 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef OPENTITAN_SW_DEVICE_LIB_TESTING_TEST_FRAMEWORK_OTTF_ISRS_H_
+#define OPENTITAN_SW_DEVICE_LIB_TESTING_TEST_FRAMEWORK_OTTF_ISRS_H_
+
+/**
+ * An OTTF exception type.
+ *
+ * This enum is used to decode RISC-V exception causes generated by Ibex.
+ */
+typedef enum ottf_exc_id {
+ kInstrMisaligned = 0,
+ kInstrAccessFault = 1,
+ kIllegalInstrFault = 2,
+ kBreakpoint = 3,
+ kLoadAccessFault = 5,
+ kStoreAccessFault = 7,
+ kUserECall = 8,
+ kMachineECall = 11,
+ kIdMax = 31
+} ottf_exc_id_t;
+
+/**
+ * OTTF exception handler.
+ *
+ * `ottf_isrs.c` provides a weak definition of this symbol, which can be
+ * overriden at link-time by providing an additional non-weak definition.
+ */
+void ottf_exception_handler(void);
+
+/**
+ * OTTF instruction misaligned fault handler.
+ *
+ * Called by default implementation of `ottf_exception_handler`. If that
+ * function is overriden, this function may not be called.
+ *
+ * `ottf_isrs.c` provides a weak definition of this symbol, which can be
+ * overriden at link-time by providing an additional non-weak definition.
+ */
+void ottf_instr_misaligned_fault(void);
+
+/**
+ * OTTF instruction access fault handler.
+ *
+ * Called by default implementation of `ottf_exception_handler`. If that
+ * function is overriden, this function may not be called.
+ *
+ * `ottf_isrs.c` provides a weak definition of this symbol, which can be
+ * overriden at link-time by providing an additional non-weak definition.
+ */
+void ottf_instr_access_fault(void);
+
+/**
+ * OTTF illegal instruction fault handler.
+ *
+ * Called by default implementation of `ottf_exception_handler`. If that
+ * function is overriden, this function may not be called.
+ *
+ * `ottf_isrs.c` provides a weak definition of this symbol, which can be
+ * overriden at link-time by providing an additional non-weak definition.
+ */
+void ottf_illegal_instr_fault(void);
+
+/**
+ * OTTF breakpoint handler.
+ *
+ * Called by default implementation of `ottf_exception_handler`. If that
+ * function is overriden, this function may not be called.
+ *
+ * `ottf_isrs.c` provides a weak definition of this symbol, which can be
+ * overriden at link-time by providing an additional non-weak definition.
+ */
+void ottf_breakpoint(void);
+
+/**
+ * OTTF load/store fault handler.
+ *
+ * Called by default implementation of `ottf_exception_handler`. If that
+ * function is overriden, this function may not be called.
+ *
+ * `ottf_isrs.c` provides a weak definition of this symbol, which can be
+ * overriden at link-time by providing an additional non-weak definition.
+ */
+void ottf_load_store_fault(void);
+
+/**
+ * OTTF machine-mode evironment call handler.
+ *
+ * Called by default implementation of `ottf_exception_handler`. If that
+ * function is overriden, this function may not be called.
+ *
+ * `ottf_isrs.c` provides a weak definition of this symbol, which can be
+ * overriden at link-time by providing an additional non-weak definition.
+ */
+void ottf_machine_ecall(void);
+
+/**
+ * OTTF user-mode evironment call handler.
+ *
+ * Called by default implementation of `ottf_exception_handler`. If that
+ * function is overriden, this function may not be called.
+ *
+ * `ottf_isrs.c` provides a weak definition of this symbol, which can be
+ * overriden at link-time by providing an additional non-weak definition.
+ */
+void ottf_user_ecall(void);
+
+/**
+ * OTTF software IRQ handler.
+ *
+ * `ottf_isrs.c` provides a weak definition of this symbol, which can be
+ * overriden at link-time by providing an additional non-weak definition.
+ */
+void ottf_software_isr(void);
+
+/**
+ * OTTF timer IRQ handler.
+ *
+ * `ottf_isrs.c` provides a weak definition of this symbol, which can be
+ * overriden at link-time by providing an additional non-weak definition.
+ */
+void ottf_timer_isr(void);
+
+/**
+ * OTTF external IRQ handler.
+ *
+ * `ottf_isrs.c` provides a weak definition of this symbol, which can be
+ * overriden at link-time by providing an additional non-weak definition.
+ */
+void ottf_external_isr(void);
+
+#endif // OPENTITAN_SW_DEVICE_LIB_TESTING_TEST_FRAMEWORK_OTTF_ISRS_H_