-
Notifications
You must be signed in to change notification settings - Fork 162
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
riscv-rt
: Support for vectored mode interrupt handling
#200
Conversation
90e140e
to
245179f
Compare
riscv-rt
: Support for vectored mode interruption handlingriscv-rt
: Support for vectored mode interrupt handling
For sure I can at least test this on our HW & sims, compare it with mine and see how it fits into the current workflow. I'll be onto this. |
I successfully ran an example on QEMU using For each interrupt source, an independent trap handler is added in the final binary: 20010150 <_start_MachineSoft_trap>:
20010150: addi sp, sp, -64 // allocate space for trap frame
20010152: sw a0, 32(sp) // store trap partially (only register a0)
20010154: auipc a0, 1
20010158: addi a0, a0, -476 // load MachineSoft address into a0
2001015c: j 0x20010210 <_continue_interrupt_trap> // jump to common interrupt handler
20010160 <_start_MachineTimer_trap>:
20010160: addi sp, sp, -64 // allocate space for trap frame
20010162: sw a0, 32(sp) // store trap partially (only register a0)
20010164: auipc a0, 1
20010168: addi a0, a0, 1250 // load MachineTimer address into a0
2001016c: j 0x20010210 <_continue_interrupt_trap> // jump to common interrupt handler Additionally, a common interrupt trap does the rest of the work and jumps to the corresponding handler: 20010210 <_continue_interrupt_trap>:
// store all the registers of the trap excluding a0 (which points to interrupt handler)
20010210: sw ra, 0(sp)
20010214: sw t0, 4(sp)
20010218: sw t1, 8(sp)
2001021c: sw t2, 12(sp)
20010220: sw t3, 16(sp)
20010224: sw t4, 20(sp)
20010228: sw t5, 24(sp)
2001022c: sw t6, 28(sp)
20010230: sw a1, 36(sp) // skip a0, already stored
20010234: sw a2, 40(sp)
20010238: sw a3, 44(sp)
2001023c: sw a4, 48(sp)
20010240: sw a5, 52(sp)
20010244: sw a6, 56(sp)
20010248: sw a7, 60(sp)
2001024c: jalr a0 // jump to interrupt handler in a0
// load registers from trap
20010250: lw ra, 0(sp)
20010254: lw t0, 4(sp)
20010258: lw t1, 8(sp)
2001025c: lw t2, 12(sp)
20010260: lw t3, 16(sp)
20010264: lw t4, 20(sp)
20010268: lw t5, 24(sp)
2001026c: lw t6, 28(sp)
20010270: lw a0, 32(sp) // we now include a0
20010274: lw a1, 36(sp)
20010278: lw a2, 40(sp)
2001027c: lw a3, 44(sp)
20010280: lw a4, 48(sp)
20010284: lw a5, 52(sp)
20010288: lw a6, 56(sp)
2001028c: lw a7, 60(sp)
20010290: addi sp, sp, 64 // free stack and return from interrupt
20010294: mret We also preserve the original 20010200 <_start_trap>:
20010200: addi sp, sp, -64
20010204: sw ra, 0(sp)
20010208: sw t0, 4(sp)
2001020c: sw t1, 8(sp)
20010210: sw t2, 12(sp)
20010214: sw t3, 16(sp)
20010218: sw t4, 20(sp)
2001021c: sw t5, 24(sp)
20010220: sw t6, 28(sp)
20010224: sw a0, 32(sp)
20010228: sw a1, 36(sp)
2001022c: sw a2, 40(sp)
20010230: sw a3, 44(sp)
20010234: sw a4, 48(sp)
20010238: sw a5, 52(sp)
2001023c: sw a6, 56(sp)
20010240: sw a7, 60(sp)
20010244: add a0, sp, zero // store pointer to trap in a0
20010248: jal 0x2001042a <_start_trap_rust>
2001024c: lw ra, 0(sp)
20010250: lw t0, 4(sp)
20010254: lw t1, 8(sp)
20010258: lw t2, 12(sp)
2001025c: lw t3, 16(sp)
20010260: lw t4, 20(sp)
20010264: lw t5, 24(sp)
20010268: lw t6, 28(sp)
2001026c: lw a0, 32(sp)
20010270: lw a1, 36(sp)
20010274: lw a2, 40(sp)
20010278: lw a3, 44(sp)
2001027c: lw a4, 48(sp)
20010280: lw a5, 52(sp)
20010284: lw a6, 56(sp)
20010288: lw a7, 60(sp)
2001028c: addi sp, sp, 64
20010290: mret Next, the vector table is filled as expected: 20010300 <_vector_table>:
20010300: j 0x20010200 <_start_trap>
20010304: j 0x20010200 <_start_trap>
20010308: j 0x20010200 <_start_trap>
2001030c: j 0x20010150 <_start_MachineSoft_trap>
20010310: j 0x20010200 <_start_trap>
20010314: j 0x20010200 <_start_trap>
20010318: j 0x20010200 <_start_trap>
2001031c: j 0x200101a0 <_start_MachineTimer_trap>
20010320: j 0x20010200 <_start_trap>
20010324: j 0x20010200 <_start_trap>
20010328: j 0x20010200 <_start_trap>
2001032c: j 0x20010200 <_start_trap> I used GDB to monitor which trap handler is used and works as expected. The program does not use the |
riscv-rt/macros/src/lib.rs
Outdated
" | ||
core::arch::global_asm!( | ||
\".section .trap, \\\"ax\\\" | ||
.align {width} // TODO is this necessary? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my understanding, we only need _continue_interrupt_trap
to be a valid target for j
instruction. j
has LSB set to 0 so we'd need to make sure this is align(2)
even if there's multiple items placed in .trap
section. I can't see why it would ever need to be 4 or 8 byte aligned. I could be wrong.
The proc-macro trap handler is somewhat more intimidating than the previous Looks OK other than that. I'll try out the user experience on a sim. |
Works as expected on Renode simulated generic 64-bit RISC-V using a CLINT for interrupt dispatch. I tested I wonder how it meshes with the nested interrupts we use on the FPGA implementation of the real-time RVE. I'll try adapting that runtime to this one and see if it raises any issue. |
I tested this with my SLIC crate, which exploits nested |
TO DO LIST
Next, another challenge is supporting heterogeneous interrupt sources (e.g., target-specific additional interrupt sources or non-standard interrupt sources. In order to achieve this, I would do as follows:
Essentially, with these changes, we would start moving towards supporting target-specific interrupt sources (see #146) |
New changes to
|
The PR is good to go. @rust-embedded/riscv please take a look. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did an initial review, just some minor style nits.
I'll do a more in-depth review, and test against hardware soon.
.type _vector_table, @function | ||
|
||
.option push | ||
.balign 0x4 // TODO check if this is the correct alignment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks correct to me, from the RISC-V Privileged Spec entry for mtvec
:
The value in the BASE field must always be aligned on a 4-byte boundary,
and the MODE setting may impose additional alignment constraints on
the value in the BASE field.
MODE
field set to 1
for vectored
mode:
Asynchronous interrupts set pc to BASE+4×cause.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The CLIC specification may require _vector_table
to be 64-byte aligned so this will have to be overridden but I think there's not much we can or should do in riscv-rt
about this. CLIC based implementations might want to define a separate vector for CLIC interrupts anyway; I don't know for sure, there's not many implementations like this in the wild 🙂
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, if I remember well, CLIC interrupt table is not comprised of j
instructions, but the address of the trap itself. So, once we start integrating vectored CLIC mode, we might use a different methodology.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is correct. CLIC uses a vector table of addresses instead of jumps (at least our implementation 🙂).
Co-authored-by: rmsyn <[email protected]>
Sorry for not taking a look at this sooner. I will hopefully have some time to review at the weekend :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changes LGTM. I'll work on testing these changes on hardware soon.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This LGTM, I hope in the some day we can ditch esp-riscv-rt and go back to using riscv-rt and friends :)
core::arch::global_asm!( | ||
".section .trap, \"ax\" | ||
.align {width} | ||
.weak _start_trap |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One interesting data point, which might not be required here, is that in esp-riscv-rt, with high opt level + lto = 'fat' we found instances where weak assembly symbols were duplicated by the linker. To fix this we had to sprinkle some weird llvm specific attributes before the weak definitions:
probably not required here, but if you run into issue you'll know what to do :D.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you! This is my goal. If riscv-rt
is also suitable for ESP32 Cx, then it is flexible enough for any RISC-V target. The issue you point out is interesting, we will monitor it in case it appears in future versions
I am working on providing support for interrupt dispatching in vectored mode. This is a first draft, and I could not test yet if it works. However, I wanted to share it with you so I can start getting some feedback and suggestions about different design decisions. So, the main changes are:
v-trap
feature to enable vectored mode. If this feature is enabled, the assembly code will add a trap vector withj
instructions to a different trap per interrupt source. Some targets do not implement the standard interrupt convention (e.g., ESP32C3). That is why I left the table as weak. PACs will be able to replace this table accordingly.interrupt
procedural macro to implement interrupt handlers. Currently, in direct mode, this macro is quite dummy. However, I plan to add some parameters to the macro that will, eventually, allow us to "check" at compile time that the interrupt source name is an interrupt source of the target. Additionally, ifv-trap
feature is enabled, this macro will generate the trap handler to make sure that user registers are properly restored after executing the interrupt handler.Also, I've been careful regarding how traps are generated to ease shortly the compatibility with the E extension (see #178).
This PR is still WIP, and I appreciate the feedback. Especially from the esp-rs folks (@MabezDev , @jessebraham) and @hegza , who already implemented a solution for this.
Closes #158