[sw] Split irq control functions from handlers
When testing, it is useful to be able to use the irq control functions,
while also overriding the default handler implementations in handler.c.
This commit splits the libraries, so components can both control their
interrupts and override the default handlers (which depend on uart).
This commit also splits off the declarations of the handler function
names into the handler header, so that other libraries can produce
compatible definitions which override the default, weak-linkage,
definitions. In particular, only the default definitions should have
`__attribute__((weak))`, and only the hardware interrupt handlers should
be aligned and have `__attribute__((interrupt))`.
This commit also documents the requirements about interrupt handler and
mtvec address alignment.
Signed-off-by: Sam Elliott <selliott@lowrisc.org>
diff --git a/sw/device/examples/hello_usbdev/meson.build b/sw/device/examples/hello_usbdev/meson.build
index 970f780..9b50288 100644
--- a/sw/device/examples/hello_usbdev/meson.build
+++ b/sw/device/examples/hello_usbdev/meson.build
@@ -12,6 +12,7 @@
sw_lib_uart,
sw_lib_usb,
riscv_crt,
+ sw_lib_irq_handlers,
],
)
diff --git a/sw/device/examples/hello_world/meson.build b/sw/device/examples/hello_world/meson.build
index 4fc0065..6caee00 100644
--- a/sw/device/examples/hello_world/meson.build
+++ b/sw/device/examples/hello_world/meson.build
@@ -13,6 +13,7 @@
sw_lib_spi_device,
sw_lib_uart,
riscv_crt,
+ sw_lib_irq_handlers,
],
)
diff --git a/sw/device/exts/common/_crt.c b/sw/device/exts/common/_crt.c
index 5084ee8..af943f0 100644
--- a/sw/device/exts/common/_crt.c
+++ b/sw/device/exts/common/_crt.c
@@ -19,7 +19,7 @@
extern char _bss_start[];
extern char _bss_end[];
- update_mtvec(_svectors);
+ irq_set_vector_offset((uintptr_t)_svectors);
memcpy(_sdata, _idata, _edata - _sdata);
memset(_bss_start, 0, _bss_end - _bss_start);
diff --git a/sw/device/exts/common/meson.build b/sw/device/exts/common/meson.build
index 465a0a6..38ce046 100644
--- a/sw/device/exts/common/meson.build
+++ b/sw/device/exts/common/meson.build
@@ -23,7 +23,7 @@
# This is included as a source, not as a static library, so that the .crt
# section is picked up correctly.
sources: ['_crt.c'],
- dependencies: [sw_lib_irq],
+ dependencies: [sw_lib_irq, sw_lib_mem],
# This argument exists solely so that Meson realizes that riscv_linker_script
# is part of the dependency graph. This seems to be the only way to convince
# Meson to behave in this way, for the following reasons:
diff --git a/sw/device/lib/handler.c b/sw/device/lib/handler.c
index 12bfcdc..ed5728c 100644
--- a/sw/device/lib/handler.c
+++ b/sw/device/lib/handler.c
@@ -9,52 +9,6 @@
#include "sw/device/lib/uart.h"
/**
- * Default exception handler. Can be overidden.
- */
-void handler_exception(void) __attribute__((aligned(4), interrupt, weak));
-
-/**
- * SW IRQ handler. Can be overidden.
- */
-void handler_irq_software(void) __attribute__((aligned(4), interrupt, weak));
-
-/**
- * Timer IRQ handler. Can be overidden.
- */
-void handler_irq_timer(void) __attribute__((aligned(4), interrupt, weak));
-
-/**
- * external IRQ handler. Can be overidden.
- */
-void handler_irq_external(void) __attribute__((aligned(4), interrupt, weak));
-
-/**
- * Instruction access fault. Can be overriden.
- */
-void handler_instr_acc_fault(void) __attribute__((aligned(4), interrupt, weak));
-
-/**
- * Illegal Instruction fault. Can be overriden.
- */
-void handler_instr_ill_fault(void) __attribute__((aligned(4), interrupt, weak));
-;
-
-/**
- * Breakpoint handler. Can be overriden.
- */
-void handler_bkpt(void) __attribute__((aligned(4), interrupt, weak));
-
-/**
- * Load store unit fault. Can be overriden.
- */
-void handler_lsu_fault(void) __attribute__((aligned(4), interrupt, weak));
-
-/**
- * Exception call handler. Can be overriden.
- */
-void handler_ecall(void) __attribute__((aligned(4), interrupt, weak));
-
-/**
* Return value of mtval
*/
static uint32_t get_mtval(void) {
@@ -79,7 +33,7 @@
}
// Below functions are default weak exception handlers meant to be overriden
-void handler_exception(void) {
+__attribute__((weak)) void handler_exception(void) {
uint32_t mcause;
exc_id_t exc_cause;
@@ -114,48 +68,48 @@
}
}
-void handler_irq_software(void) {
+__attribute__((weak)) void handler_irq_software(void) {
uart_send_str("Software IRQ triggered!\n");
while (1) {
}
}
-void handler_irq_timer(void) {
+__attribute__((weak)) void handler_irq_timer(void) {
uart_send_str("Timer IRQ triggered!\n");
while (1) {
}
}
-void handler_irq_external(void) {
+__attribute__((weak)) void handler_irq_external(void) {
uart_send_str("External IRQ triggered!\n");
while (1) {
}
}
-void handler_instr_acc_fault(void) {
+__attribute__((weak)) void handler_instr_acc_fault(void) {
const char fault_msg[] =
"Instruction access fault, mtval shows fault address\n";
print_exc_msg(fault_msg);
}
-void handler_instr_ill_fault(void) {
+__attribute__((weak)) void handler_instr_ill_fault(void) {
const char fault_msg[] =
"Illegal Instruction fault, mtval shows instruction content\n";
print_exc_msg(fault_msg);
}
-void handler_bkpt(void) {
+__attribute__((weak)) void handler_bkpt(void) {
const char exc_msg[] =
"Breakpoint triggerd, mtval shows the breakpoint address\n";
print_exc_msg(exc_msg);
}
-void handler_lsu_fault(void) {
+__attribute__((weak)) void handler_lsu_fault(void) {
const char exc_msg[] = "Load/Store fault, mtval shows the fault address\n";
print_exc_msg(exc_msg);
}
-void handler_ecall(void) {
+__attribute__((weak)) void handler_ecall(void) {
uart_send_str("Environment call encountered\n");
while (1) {
}
diff --git a/sw/device/lib/handler.h b/sw/device/lib/handler.h
index 6af92d3..50dfadf 100644
--- a/sw/device/lib/handler.h
+++ b/sw/device/lib/handler.h
@@ -16,4 +16,114 @@
kIdMax = 31
} exc_id_t;
+// The RISC-V interrupt vector will not include the addresses of the handlers,
+// instead, it includes (uncompressed) instructions. Thus the interrupt vector
+// will include `j <interrupt handler name>` for each handler.
+//
+// The only requirement on the symbol in the jump is that it must be correctly
+// aligned. If the processor supports the C extension, this can be 2-byte
+// aligned, but 4-byte aligned is compatible with all RISC-V processors.
+//
+// If the processor is not using interrupt vectoring, then there will be a
+// single address where interrupts jump to, which will either contain a function
+// (which will need to be aligned), or will contain a jump to a function, again
+// which will need to be aligned.
+//
+// You only need to use this ABI for handlers that are the first function called
+// in an interrupt handler. Subsequent functions can just use the regular RISC-V
+// calling convention.
+#define INTERRUPT_HANDLER_ABI __attribute__((aligned(4), interrupt))
+
+// The following `handler_*` functions have weak definitions, provided by
+// `handler.c`. This weak definition can be overriden at link-time by providing
+// an additional non-weak definition of each function. Executables and libraries
+// must not contain more than one weak definition of the same symbol.
+
+/**
+ * Default exception handler.
+ *
+ * `handler.c` provides a weak definition of this symbol, which can be overriden
+ * at link-time by providing an additional non-weak definition.
+ */
+INTERRUPT_HANDLER_ABI void handler_exception(void);
+
+/**
+ * SW IRQ handler.
+ *
+ * `handler.c` provides a weak definition of this symbol, which can be overriden
+ * at link-time by providing an additional non-weak definition.
+ */
+INTERRUPT_HANDLER_ABI void handler_irq_software(void);
+
+/**
+ * Timer IRQ handler.
+ *
+ * `handler.c` provides a weak definition of this symbol, which can be overriden
+ * at link-time by providing an additional non-weak definition.
+ */
+INTERRUPT_HANDLER_ABI void handler_irq_timer(void);
+
+/**
+ * external IRQ handler.
+ *
+ * `handler.c` provides a weak definition of this symbol, which can be overriden
+ * at link-time by providing an additional non-weak definition.
+ */
+INTERRUPT_HANDLER_ABI void handler_irq_external(void);
+
+/**
+ * Instruction access fault.
+ *
+ * Called by default implementation of `handler_exception`. If that function is
+ * overriden, this function may not be called.
+ *
+ * `handler.c` provides a weak definition of this symbol, which can be overriden
+ * at link-time by providing an additional non-weak definition.
+ */
+void handler_instr_acc_fault(void);
+
+/**
+ * Illegal Instruction fault.
+ *
+ * Called by default implementation of `handler_exception`. If that function is
+ * overriden, this function may not be called.
+ *
+ * `handler.c` provides a weak definition of this symbol, which can be overriden
+ * at link-time by providing an additional non-weak definition.
+ */
+void handler_instr_ill_fault(void);
+
+/**
+ * Breakpoint handler.
+ *
+ * Called by default implementation of `handler_exception`. If that function is
+ * overriden, this function may not be called.
+ *
+ * `handler.c` provides a weak definition of this symbol, which can be overriden
+ * at link-time by providing an additional non-weak definition.
+ */
+void handler_bkpt(void);
+
+/**
+ * Load store unit fault.
+ *
+ * Called by default implementation of `handler_exception`. If that function is
+ * overriden, this function may not be called.
+ *
+ * `handler.c` provides a weak definition of this symbol, which can be overriden
+ * at link-time by providing an additional non-weak definition.
+ */
+void handler_lsu_fault(void);
+
+/**
+ * Exception call handler.
+ *
+ * Called by default implementation of `handler_exception`. If that function is
+ * overriden, this function may not be called.
+ *
+ * `handler.c` provides a weak definition of this symbol, which can be overriden
+ * at link-time by providing an additional non-weak definition.
+ */
+void handler_ecall(void);
+
#endif
diff --git a/sw/device/lib/irq.c b/sw/device/lib/irq.c
index 8955436..77831f2 100644
--- a/sw/device/lib/irq.c
+++ b/sw/device/lib/irq.c
@@ -10,6 +10,10 @@
static const uint32_t IRQ_TIMER_ENABLE_OFFSET = 7;
static const uint32_t IRQ_SW_ENABLE_OFFSET = 3;
+void irq_set_vector_offset(uintptr_t address) {
+ asm volatile("csrw mtvec, %0" ::"r"(address));
+}
+
static void irq_mie_set(uint32_t value) {
asm volatile("csrrs zero, mie, %0" : : "r"(value) :);
}
diff --git a/sw/device/lib/irq.h b/sw/device/lib/irq.h
index 316b5ed..00f311e 100644
--- a/sw/device/lib/irq.h
+++ b/sw/device/lib/irq.h
@@ -10,8 +10,10 @@
/**
* Update to the location of vectors as specificed in the linker file
+ *
+ * The address must be 256-byte aligned.
*/
-extern void update_mtvec(char *ptr);
+void irq_set_vector_offset(uintptr_t address);
/**
* Enable / disable ibex globlal interrupts
diff --git a/sw/device/lib/irq_vectors.S b/sw/device/lib/irq_vectors.S
index 456d2ab..70d72de 100644
--- a/sw/device/lib/irq_vectors.S
+++ b/sw/device/lib/irq_vectors.S
@@ -7,12 +7,6 @@
.extern handler_irq_timer
.extern handler_irq_external
-update_mtvec:
- .section .text
- .global update_mtvec
- csrw mtvec, a0
- ret
-
exception_handlers:
.section .vectors
.global vector_handlers
diff --git a/sw/device/lib/meson.build b/sw/device/lib/meson.build
index bd88345..dae41b9 100644
--- a/sw/device/lib/meson.build
+++ b/sw/device/lib/meson.build
@@ -82,20 +82,36 @@
# IRQ library (sw_lib_irq)
sw_lib_irq = declare_dependency(
+ link_with: static_library(
+ 'irq_ot',
+ sources: [
+ 'irq.c',
+ ],
+ )
+)
+
+# IRQ Handlers Library
+#
+# handler.c contains various definitions with weak linkage, for symbols
+# referenced in irq_vectors.S
+#
+# To override default handlers, use the definitions in handler.h, and either
+# link to this library to pick up the other default definitions, or include
+# irq_vectors.S as a source dependency.
+sw_lib_irq_handlers = declare_dependency(
# The IRQ handler definitions need to be included as a "source" rather than
# as part of a static library; this ensures that it is linked in as a .o
# file, so the .vectors section is picked up.
sources: ['irq_vectors.S'],
link_with: static_library(
- 'irq_ot',
+ 'irq_default_handlers_ot',
sources: [
'handler.c',
- 'irq.c'
],
dependencies: [
+ sw_lib_irq,
sw_lib_uart,
- sw_lib_mem,
- ]
+ ],
)
)
diff --git a/sw/device/tests/aes/meson.build b/sw/device/tests/aes/meson.build
index 17438f8..12668a4 100644
--- a/sw/device/tests/aes/meson.build
+++ b/sw/device/tests/aes/meson.build
@@ -9,6 +9,7 @@
dependencies: [
sw_lib_aes,
sw_lib_uart,
+ sw_lib_mem,
riscv_crt,
],
)
diff --git a/sw/device/tests/flash_ctrl/meson.build b/sw/device/tests/flash_ctrl/meson.build
index 5072db9..d1c60f8 100644
--- a/sw/device/tests/flash_ctrl/meson.build
+++ b/sw/device/tests/flash_ctrl/meson.build
@@ -12,6 +12,7 @@
sw_lib_irq,
sw_lib_uart,
riscv_crt,
+ sw_lib_irq_handlers,
],
)
diff --git a/sw/device/tests/hmac/meson.build b/sw/device/tests/hmac/meson.build
index fe289b2..90bb33f 100644
--- a/sw/device/tests/hmac/meson.build
+++ b/sw/device/tests/hmac/meson.build
@@ -12,6 +12,7 @@
sw_lib_irq,
sw_lib_uart,
riscv_crt,
+ sw_lib_irq_handlers,
],
)
diff --git a/sw/device/tests/rv_timer/meson.build b/sw/device/tests/rv_timer/meson.build
index 9a4d81d..a843a9c 100644
--- a/sw/device/tests/rv_timer/meson.build
+++ b/sw/device/tests/rv_timer/meson.build
@@ -11,6 +11,7 @@
sw_lib_rv_timer,
sw_lib_uart,
riscv_crt,
+ sw_lib_irq_handlers,
],
)