riscv icon indicating copy to clipboard operation
riscv copied to clipboard

`riscv-rt`: Implement support for hardware interrupt dispatching

Open dvc94ch opened this issue 7 years ago • 5 comments

Currently there is a single interrupt entry point. RISCV CPU's also have a hardware dispatching mode.

dvc94ch avatar Feb 23 '18 17:02 dvc94ch

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.

Disasm avatar Mar 15 '19 13:03 Disasm

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.

allexoll avatar May 29 '21 01:05 allexoll

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?

hegza avatar Dec 20 '22 17:12 hegza

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, e310x would call the claim method 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, e310x would call the complete method 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?

romancardenas avatar Mar 13 '23 13:03 romancardenas

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

romancardenas avatar Mar 13 '23 16:03 romancardenas