Skip to content
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

Support "non-standard" interrupts and exceptions #211

Open
wants to merge 13 commits into
base: master
Choose a base branch
from

Conversation

romancardenas
Copy link
Contributor

Currently, the riscv and riscv-rt crates assume that the interrupt and exception sources are the ones gathered in the RISCV ISA. However, we do not support target-specific sources. Moreover, some RISC-V targets do not follow the standard interrupt scheme (e.g., ESP32 C3). Another example is the E310x microcontroller, which does not support supervisor interrupt sources.

Proposal

Now that we have the riscv-pac trait to define interrupt and exception sources, we should be able to adapt the rest of the crates to rely on generic functions with constraints to these traits. For example, the mcause register can have something like this:

// in riscv::register::mcause 
pub use riscv_pac::{CoreInterruptNumber, ExceptionNumber};

pub enum Trap<I, E> {
    Interrupt(I),
    Exception(E),
}

impl Mcause {
    pub fn cause<I: CoreInterruptNumber, E: ExceptionNumber>(&self) -> Trap<I, E> {
        if self.is_interrupt() {
            Trap::Interrupt(I::from_number(self.code()).unwrap())
        } else {
            Trap::Exception(E::from_number(self.code()).unwrap())
        }
    }
}

By default, I and E should be the standard enumerations (as we are doing now). Still, PACs will be able to opt out of this default implementation and provide their custom interrupt/exception enumeration.

Next, we can enrich riscv-rt macros to automatically generate the necessary code for letting PACs inject their custom interrupt and exception tables. Also, in the case of vectored mode, we could generate the necessary vector table and so on.

NOTE THAT THIS IS JUST MY PROPOSAL, but I would love to hear your opinion as well as clever mechanisms to implement this neatly.

I will do a self-review to point out some to-dos that I would like to discuss with you.

Solves #146

Copy link

This PR is being prevented from merging because it presents one of the blocking labels: work in progress, do not merge.

riscv-pac/src/lib.rs Outdated Show resolved Hide resolved
riscv-pac/src/lib.rs Show resolved Hide resolved
riscv-pac/src/lib.rs Show resolved Hide resolved
riscv-pac/src/lib.rs Show resolved Hide resolved
@almindor
Copy link
Contributor

I think this is a good approach once it's clean and tested. I only had time to skim through it but don't see anything "wrong" with it.

Copy link
Contributor Author

@romancardenas romancardenas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes done. Please, take a look and let me know what you think. I personally think that we can improve it considerably. I will comment on the PR with my alternative idea.

@romancardenas
Copy link
Contributor Author

Different approach: do not use generics nor enums at all in mcause and scause

I think the current proposal might become hard to handle for developers, as they now need to always work with generic arguments. I propose the following changes:

In riscv::register::xcause

  • Remove Interrupt and Exception
  • Change Trap<I, E> to:
pub enum Trap {
    Interrupt(usize),
    Exception(usize),
}
  • Change Xcause::cause accordingly:
impl Mcause {
    pub cause(&self) -> Trap { ...}
}

In riscv::interrupt::{machine,supervisor}

  • Here will be the standard Interrupt and Exception enums with the implementation of the riscv-pac traits
  • Here we can add a generic cause function that will be the one used by users or frameworks such as RTIC:
pub use riscv_pac::{CoreInterruptNumber, InterruptNumber, ExceptionNumber};

/// Trap cause
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Cause<I, E> {
    Interrupt(I),
    Exception(E),
}

/// Trap cause error
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum CauseError {
    InvalidInterrupt(usize),
    InvalidException(usize),
}

pub mod machine {
    use crate::register::{mcause, Trap}

    pub fn cause<I: CoreInterruptNumber, E: ExceptionNumber>() -> Result<Cause<I, E>, CauseError> {
        match mcause::read().cause() {
            Trap::Interrupt(i) => match I::from_number(i) {
                Ok(interrupt) => Ok(interrupt),
                Err(code) => Err(CauseError::InvalidInterrupt(code)),
            },
            Trap::Exception(e) => match E::from_number(e) {
                Ok(exception) => Ok(exception),
                Err(code) => Err(CauseError::InvalidException(code)),
            }
        }
    }
}
...

Naming can be improved. For instance, Xcause could return a riscv::interrupt::Cause<usize, usize> to avoid having too many similar enums.

riscv/src/register/mcause.rs Outdated Show resolved Hide resolved
riscv/src/register/mcause.rs Outdated Show resolved Hide resolved
@romancardenas
Copy link
Contributor Author

Thanks for the feedback @rmsyn !

I implemented my alternative approach in the riscv-pac2 branch. Please take a look and let me know what you think. Personally, I feel like it is more user friendly, and somehow it makes sense to me that the Interrupt and Exception enums are in riscv::interrupt instead of riscv::register

@rmsyn
Copy link
Contributor

rmsyn commented Jun 6, 2024

it makes sense to me that the Interrupt and Exception enums are in riscv::interrupt instead of riscv::register

That makes sense to me, too.

Really either one can work, because the values are read from the mcause and scause registers (which makes sense for riscv::register).

riscv::interrupt makes obvious sense because Interrupt and Exception are explicitly about interrupts.

🤷

Copy link
Contributor

@rmsyn rmsyn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I probably need more time to review the proc-macro stuff, but it looks good to me.

@rmsyn
Copy link
Contributor

rmsyn commented Jun 6, 2024

I implemented my alternative approach in the riscv-pac2 branch.

After an initial review, I do like the code organization in riscv-pac2 more. Mostly aesthetic preferences, but it does feel like it clarifies things a bit.

The unification of one Trap type is also nice from an ergonomics perspective regarding the mcause/scause registers.

@romancardenas
Copy link
Contributor Author

So I think the new riscv version looks good (still needs some testing etc.) BUT... it's time for riscv-rt and the interrupt/exception handlers. We need to provide an out-of-the-box solution for dealing with custom interrupts and exceptions. I guess that it's time for thinking on macros that:

  • declare the interrupt and exception handlers as extern "C" fn
  • define __INTERRUPTS and __EXCEPTIONS arrays from the enums
  • In vectored mode, creates the vector table etc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants