riscv
                                
                                 riscv copied to clipboard
                                
                                    riscv copied to clipboard
                            
                            
                            
                        `riscv-rt`: Implement support for hardware interrupt dispatching
Currently there is a single interrupt entry point. RISCV CPU's also have a hardware dispatching mode.
I see two cons for using hardware interrupt dispatching:
- For each vectored interrupt handler you have to declare register handling code like in asm.S/_start_trap, which will cause additional size overhead.
- Vectored interrupts are not supported in priv spec 1.9 since they arrived only in priv spec 1.10.
Nevertheless, it's easy to implement "fake" vectored interrupt dispatching with a single trap entry point and two arrays of handler pointers.
i've opened an issue over at svd2rust about producing a device.x that could be used for this.
Also, i think the vectored IRQ would break the current trap implementation since pc would jump to BASE+4*cause. I think currently the trap reads the cause and then jumps to the correct handler in the vector table.
the vectored IRQ would use non-vectortable addresses.
We stumbled into this in our project. We're using a version of lowRISC Ibex which doesn't support direct mode interrupts at all.
I patched an implementation of riscv-rt that defaults to vectored interrupts and that one seems to be working out for us.
@Disasm Do you think it would be useful work for me to see if some of it can be upstreamed?
If it happens that the implementation causes code bloat, it could be gated with a feature-flag such as "default-vector-traps".
Related: any advice for how to pass user-picked Rust feature-flags as preproc. defines for the assembly compilation unit? I guess the SMODE-feature/define uses something like that?
I propose a modification to this crate to ease vector-like handling of the external interrupts.
Right now, svd2rust will generate a __EXTERNAL_INTERRUPTS table with all the handlers for the external interrupts of a board. Thus, we could define a DefaultMachineExternal function that takes advantage of this. We would need the following symbols:
- extern "C" static mut _n_external_interrupts: u16: a symbol that tells the size of the external interrupt array. By default, it can be set to 0. This symbol would be generated by- svd2rust.
- __EXTERNAL_INTERRUPTS: [unsafe extern "C" fn(); _n_external_interrupts]: Array with the handlers of each external interrupt. This can also be generated by- svd2rust.
- fn _get_external_interrupt_n() -> u16: Returns the number of the pending external interrupt with the highest priority. By default, it just returns 0 (i.e., no interrupt). Each PAC should implement its own function. For instance,- e310xwould call the- claimmethod of the PLIC.
- fn _clear_external_interrupt(interrupt_n: u16): Helper function to clear the interrupt once it is attended. Each PAC should implement its own function. For instance,- e310xwould call the- completemethod of the PLIC.
Having this, we could do a default implementation that looks like this:
#[no_mangle]
fn DefaultMachineExternalHandler() {
    let interrupt_n = _get_external_interrupt_n();
    if interrupt_n < __EXTERNAL_INTERRUPTS.len() {
        __EXTERNAL_INTERRUPTS[interrupt_n]();
       _clear_external_interrupt(interrupt_n);
   } else {
       DefaultInterruptHandler();
   }
}
What do you think?
It turns out that my proposed approach wasn't that easy to implement. I developed a macro for doing the same. You can check it in rust-embedded/riscv-rt#113