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

riscv: add fallible functions #222

Merged
merged 10 commits into from
Jul 17, 2024
Merged

Conversation

rmsyn
Copy link
Contributor

@rmsyn rmsyn commented Jun 28, 2024

Adds fallible variants to all functions that explicitly panic in the riscv crate.

Resolves: #212

@rmsyn rmsyn requested a review from a team as a code owner June 28, 2024 05:20
@rmsyn
Copy link
Contributor Author

rmsyn commented Jun 28, 2024

We probably also want to cover the riscv::register::satp and riscv::register::pmpcfgx modules, too.

Wanted to submit the initial work to get feedback on the approach.

Copy link
Contributor

@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.

Looks great! Thank you so much. We can keep working in this PR

riscv/src/register/macros.rs Outdated Show resolved Hide resolved
riscv/src/register/satp.rs Outdated Show resolved Hide resolved
Copy link
Contributor

@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.

It looks good to me. What do you think about more details in invalid values? Is it overcomplicating stuff maybe?

riscv/src/register/macros.rs Outdated Show resolved Hide resolved
riscv/src/register/macros.rs Outdated Show resolved Hide resolved
riscv/src/register/macros.rs Outdated Show resolved Hide resolved
riscv/src/register/macros.rs Outdated Show resolved Hide resolved
riscv/src/register/macros.rs Outdated Show resolved Hide resolved
riscv/src/register/pmpcfgx.rs Show resolved Hide resolved
riscv/src/register/pmpcfgx.rs Outdated Show resolved Hide resolved
riscv/src/result.rs Outdated Show resolved Hide resolved
riscv/src/result.rs Outdated Show resolved Hide resolved
riscv/src/result.rs Outdated Show resolved Hide resolved
@rmsyn
Copy link
Contributor Author

rmsyn commented Jun 30, 2024

I've added suggested fixes as fixup commits for easier review.

If you're happy with them, I'll squash them into their respective parent commits before a merge.

I've also left conversations open to continue discussion on some of the style-related suggestions.

Copy link
Contributor

@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.

It is looking great! The only comments I have are:

  • Adding an InvalidVariant error for those cases in which there are reserved values within a bitmask.
  • Updating "infallible" functions to use their "fallible" counterpart followed by an unwrap(). In this way, new panic messages will show a more comprehensive outcome.

riscv/src/result.rs Outdated Show resolved Hide resolved
riscv/src/register/mcounteren.rs Outdated Show resolved Hide resolved
riscv/src/register/mcounteren.rs Outdated Show resolved Hide resolved
riscv/src/register/pmpcfgx.rs Show resolved Hide resolved
riscv/src/result.rs Outdated Show resolved Hide resolved
@rmsyn rmsyn force-pushed the riscv/result branch 2 times, most recently from 7347b51 to 768f825 Compare July 2, 2024 02:31
@rmsyn
Copy link
Contributor Author

rmsyn commented Jul 2, 2024

Ok, I added the next round of fixup changes.

Copy link
Contributor

@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.

So far so good!

I was thinking about the correct location of the result module and maybe we should consider moving it to riscv-pac. Ideally, riscv-pac will have way less activity and act as a dependency for all the riscv ecosystem. Crates such as riscv or riscv-peripheral would re-export the result module. Moving it to riscv-pac will allow us to bump riscv versions without breaking other crates, as there will not be version issues.

riscv/src/register/mcounteren.rs Outdated Show resolved Hide resolved
riscv/src/register/mcounteren.rs Outdated Show resolved Hide resolved
riscv/src/register/satp.rs Show resolved Hide resolved
@rmsyn
Copy link
Contributor Author

rmsyn commented Jul 3, 2024

I was thinking about the correct location of the result module and maybe we should consider moving it to riscv-pac. Ideally, riscv-pac will have way less activity and act as a dependency for all the riscv ecosystem.

I'm not sure about the "correct" location for the result module. Putting it in riscv-pac would require adding riscv-pac as a riscv dependency, where keeping it in riscv requires the opposite.

I think the latter makes more sense, and follows with riscv-rt having a riscv dependency.

Moving it to riscv-pac will allow us to bump riscv versions without breaking other crates, as there will not be version issues.

We could still bump riscv versions, we would just need to do semver bumps for any rust-embedded/riscv workspace crates that have a riscv dependency.

Maybe I'm misunderstanding the architecture, but I thought riscv was the "core" crate that all the riscv-* crates would bring in as a dependency (if they need it).

Copy link
Contributor

@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.

LGTM

@romancardenas
Copy link
Contributor

romancardenas commented Jul 3, 2024

You can get more information about the motivation of the riscv-pac crate here.

You can think of riscv-pac as a "special" module of riscv that is completely isolated from the rest of the crate. In future implementations, I foresee riscv doing a public reexport of riscv-pac (in fact, it is already a dependency in this PR). riscv-pac will experience little to no breaking changes, while we can keep improving riscv without too big impacts in the ecosystem.

Let us assume that we have the ExternalInterruptNumber trait in riscv instead of riscv-pac. The riscv_peripheral::PLIC peripheral implementation relies on this trait to provide safe access to different registers. If we add breaking changes to riscv, we then should also release a new version of riscv-peripheral, even if no changes were made in the ExternalInterruptNumber trait. This scales to PACs, third-party peripheral drivers, and so on.

If we place the result module in riscv-pac, it will be available in riscv with the reexport. Additionally, we can improve the current riscv-pac traits by using your new Result enum.

@rmsyn
Copy link
Contributor Author

rmsyn commented Jul 3, 2024

If we place the result module in riscv-pac, it will be available in riscv with the reexport. Additionally, we can improve the current riscv-pac traits by using your new Result enum.

I think this makes sense. If you are planning to have riscv-pac be a dependency in riscv anyway, then moving result into riscv-pac doesn't cause any issues.

In fact, if that's the plan, then moving result into riscv-pac is a requirement to avoid a circular dependency.

A similar approach would still need to be taken for any additions to the Error enum, where breaking changes would require a semver bump in riscv-pac, and everything that re-exports riscv_pac::result.

I'll move it in a fixup commit, and if you like it, squash the commit before merge.

Copy link
Contributor

@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.

LGTM

riscv/src/lib.rs Outdated
@@ -40,6 +40,7 @@ pub(crate) mod bits;
pub mod delay;
pub mod interrupt;
pub mod register;
pub use riscv_pac::result;
Copy link
Contributor

Choose a reason for hiding this comment

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

It could be handy to reexport the riscv-pac crate

Suggested change
pub use riscv_pac::result;
pub use riscv_pac::{self as pac, result};

Copy link
Contributor

Choose a reason for hiding this comment

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

... or even pub use riscv_pac::*?

rmsyn added 7 commits July 4, 2024 18:11
Adds the `result` module to contain types for fallible functions.

For discussion, see:
<rust-embedded#212 (comment)>
Adds fallible `try_` function variants to most `register` module macros.
Adds fallible access functions for `Mcounteren` HPM fields.
Adds fallible access functions for `Scounteren` HPM fields.
Adds fallible access functions for `Mcountinhibit` HPM fields.
Adds fallible access functions for `Satp` ASID and PPN fields.
Adds fallible conversion functions for `Pmpcfgx` permission and range
fields.
Copy link
Contributor

@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.

LGTM

@romancardenas
Copy link
Contributor

I think this is done. The main new features are:

  • New result module in riscv-pac, in which we define the typical error cases for embedded RISC-V.
  • Now, in riscv, we do pub use riscv_pac::*. Thus, riscv-pac could be considered just an "extension" of riscv that isolates traits and error enums from other breaking changes. Also, in this way, PACs would not need to add an extra dependency to riscv-pac.

While there are other aspects to polish (e.g., modifying riscv-pac traits so they use the new Errors), I would like to wait for the next WG meeting (we are already on the agenda). The idea is to get some consensus so the cortex-m ecosystem could replicate this work. Also, there are very knowledgeable folks who have been working on embedded-hal and all sorts of interfaces that could help us improve the user experience and avoid potential issues in the future.

@romancardenas
Copy link
Contributor

@rmsyn could you please update the riscv-pac traits so they use riscv_pac::result::Error?

Uses the new `Result` type for library traits.
Copy link
Contributor

@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.

LGTM. Now we need to update riscv-peripheral 😁

@rmsyn
Copy link
Contributor Author

rmsyn commented Jul 12, 2024

LGTM. Now we need to update riscv-peripheral 😁

😅 I looked for current usage of the traits, and must have missed riscv-peripheral. Updating now.

Uses the `riscv_pac::result::*` types for unsafe trait implementations.
Copy link
Contributor

@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.

Let me know what you think about my comments

Comment on lines 15 to 22
/// Invalid field value.
InvalidValue {
field: &'static str,
value: usize,
bitmask: usize,
},
/// Invalid value of a register field that does not match any known variants.
InvalidVariant { field: &'static str, value: usize },
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we could differentiate errors regarding fields of a register and errors regarding the whole register/value. The idea is avoiding defining the field in the riscv-peripheral examples, as I think it is not necessary.

Suggested change
/// Invalid field value.
InvalidValue {
field: &'static str,
value: usize,
bitmask: usize,
},
/// Invalid value of a register field that does not match any known variants.
InvalidVariant { field: &'static str, value: usize },
/// Invalid value of a register field.
InvalidFieldValue {
field: &'static str,
value: usize,
bitmask: usize,
},
/// Invalid value of a register field that does not match any known variants.
InvalidFieldVariant { field: &'static str, value: usize },
/// Invalid value.
InvalidValue {
value: usize,
bitmask: usize,
},
/// Invalid value that does not match any known variants.
InvalidVariant(usize),

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Maybe we could differentiate errors regarding fields of a register and errors regarding the whole register/value.

This makes sense to me. The value covers the entire structure, so this separation could be useful in other cases without subfields.

if number > Self::MAX_HART_ID_NUMBER {
Err(number)
Err(Error::InvalidVariant {
field: "hart_id",
Copy link
Contributor

Choose a reason for hiding this comment

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

Adding field to these errors feels unnecessary to me.

Changes current `Invalid*` variants to `InvalidField*` variants to
indicate their use for subfields in an outer struct.

Adds the field-less counterparts for use in a context without subfields.
@rmsyn
Copy link
Contributor Author

rmsyn commented Jul 13, 2024

Let me know what you think about my comments

Added the recommended changes. Hopefully they cover the contexts you were concerned with.

Agree that for contexts where the value covers the entire structure, we can get the necessary context from the conversion function, e.g. in a backtrace / panic dump.

Copy link
Contributor

@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.

Let me know what you think of my review. It feels to me like the errors containing field names are not that necessary.

Comment on lines +31 to +34
_ => Err(Error::InvalidFieldValue {
field: "permission",
value: val as usize,
bitmask: 0b111,
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
_ => Err(Error::InvalidFieldValue {
field: "permission",
value: val as usize,
bitmask: 0b111,
_ => Err(Error::InvalidValue {
value: val as usize,
bitmask: 0b111,

Comment on lines +58 to +61
_ => Err(Error::InvalidFieldValue {
field: "range",
value: val as usize,
bitmask: 0b11,
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
_ => Err(Error::InvalidFieldValue {
field: "range",
value: val as usize,
bitmask: 0b11,
_ => Err(Error::InvalidValue {
value: val as usize,
bitmask: 0b11,

@rmsyn
Copy link
Contributor Author

rmsyn commented Jul 16, 2024

Let me know what you think of my review. It feels to me like the errors containing field names are not that necessary.

I can appreciate that reviews are incremental, but this could have been made clearer in the last set of changes.

The field entry is useful when viewing the error messages, e.g. if users have some form of logging enabled.

It also clarifies situations where multiple fields could return/bubble-up an error. For instance, in a function parsing the permission AND range field, how would you differentiate which field errors without attaching a debugger/panicing?

@romancardenas
Copy link
Contributor

romancardenas commented Jul 16, 2024

Hey @rmsyn sorry if it annoyed you. After the last changes, I thought that backtracking would be enough to find out which field changed, with no need to add static strings to the binary. In any case, if you think that variants that indicate the invalid field are necessary/valuable, we can definitely leave them as is, I have no strong feelings against them.

I will wait a couple of days in case anyone from @rust-embedded/riscv wants to leave their review and merge it to master.
Again, thank you so much for your contribution!

@rmsyn
Copy link
Contributor Author

rmsyn commented Jul 16, 2024

After the last changes, I thought that backtracking would be enough to find out which field changed, with no need to add static strings to the binary. In any case, if you think that variants that indicate the invalid field are necessary/valuable, we can definitely leave them as is, I have no strong feelings against them.

I think the field is helpful, but not exactly necessary. I can see the argument that including static strings in riscv could lead to an increase in static memory. An alternative that would take up less space would be an enum of all CSRs with fields. However, that seemed even more burdensome for maintenance, and downstream users.

If we did use an enum, or follow svd to use a module-specific error type, maybe we can use minimal formatting in riscv. Then users can decide how verbose they would like to get with error messages. The module-specific approach would require a reworking of the entire result module.

We could also just drop the field like you suggested, and have less directly helpful error messages. This may not be that large of a trade-off. My main concern is when working on a platform with only printf-style debugging available, e.g. I'm currently working on a platform without a working JTAG interface.

Copy link
Contributor

@almindor almindor left a comment

Choose a reason for hiding this comment

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

LGTM

@romancardenas
Copy link
Contributor

Let's leave the field thing for now and open a new issue to discuss this in more depth. I'll add this PR to the merge queue tonight, so we can also advance in #211

@romancardenas romancardenas added this pull request to the merge queue Jul 17, 2024
Merged via the queue into rust-embedded:master with commit 5612138 Jul 17, 2024
95 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

riscv: Consider strategy for exception safe code
3 participants