-
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
Add Mstatus helpers to allow setting fields in Mstatus #207
Conversation
f766976
to
a10e637
Compare
I find it confusing having methods that modify Would you consider adapting the builders approach for this PR? |
I think there's a few things to pick apart here:
The third one is what adds the complications, because of the special atomic bit set/clear operations in a single csr instruction. These are very useful for all the single-bit fields in mstatus so we wouldn't want to do without them. But in the general case - either updating a multi-bit field or updating multiple fields at once - requires a full read/modify/write. So as I see it, the current "operate directly on mstatus" methods are the special case exception, and the common case is:
I'm not sure how that would really be different? As I said, my use-case here is to read mstatus, make some changes, then put it back, whereas a builder would imply building new Mstatus values from scratch. But I agree with you about the potential for confusion. I should definitely add a clarifying line in the doc comment to say it updates the Mstatus value but not the csr, and maybe rename them to |
I added a commit to:
|
37581f1
to
79d85c6
Compare
Oh I just noticed there's no |
Hm, need to do more testing. |
Ugh, shift vs |
riscv/src/register/mstatus.rs
Outdated
/// Note this updates the [`Mstatus`] value, but does not affect the mstatus | ||
/// CSR itself. See [`set_sie`]/[`clear_sie`] to directly update the CSR. | ||
#[inline] | ||
pub fn update_sie(&self, sie: bool) -> Self { |
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.
Somewhat of a bikeshed, but methods that set a bit in a bitfield without clearing the rest of the bitfield typically use the modify_*
convention, e.g. in pac
crates generated with svd2rust
.
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 cortex-m
, they use the set_*
convention. I would align with them
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.
I started with set but the concern was it would be confusing vs the functions which act directly on the csr. Also there's a semantic difference as the csr functions use parameterleas set/clear for single bit fields whereas the update/modify/... on Mstatus takes a parameter.
Whenever I have doubts about new features, I take a look at how Let me take some time to review your changes, I'll try to provide a review ASAP. |
riscv/src/register/mstatus.rs
Outdated
@@ -72,6 +72,15 @@ impl From<bool> for Endianness { | |||
} | |||
|
|||
impl Mstatus { |
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.
It would be nice to have macros for structs like Mstatus
, as I expect a lot of duplicates with this
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.
Maybe we could have a pub(crate) bit
module somewhere with bitwise functions for usize
. In this way, we avoid having methods in every register, and all registers use the same function. I'm not sure if this would help us optimize the binary size or if the compiler is smart enough to collapse all these methods (don't think so).
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.
Yeah I saw a lot of opportunity to collapse some repeated code.
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.
Let's follow the set_*
approach as in cortex-m
. Also, maybe it is a good moment to add a utility module with bit-wise functions that can be shared among all the registers (it feels like we will need it in the future)
riscv/src/register/mstatus.rs
Outdated
/// Note this updates the [`Mstatus`] value, but does not affect the mstatus | ||
/// CSR itself. See [`set_sie`]/[`clear_sie`] to directly update the CSR. | ||
#[inline] | ||
pub fn update_sie(&self, sie: bool) -> Self { |
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 cortex-m
, they use the set_*
convention. I would align with them
riscv/src/register/mstatus.rs
Outdated
/// Machine Interrupt Enable | ||
#[inline] | ||
pub fn mie(&self) -> bool { | ||
self.bits & (1 << 3) != 0 | ||
} | ||
|
||
/// Update Machine Interrupt Enable | ||
/// | ||
/// Note this updates the [`Mstatus`] value, but does not affect the mstatus |
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.
/// Note this updates the [`Mstatus`] value, but does not affect the mstatus | |
/// Note this updates the [`Mstatus`] value read previously, but does not affect the mstatus |
riscv/src/register/mstatus.rs
Outdated
@@ -72,6 +72,15 @@ impl From<bool> for Endianness { | |||
} | |||
|
|||
impl Mstatus { |
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.
Maybe we could have a pub(crate) bit
module somewhere with bitwise functions for usize
. In this way, we avoid having methods in every register, and all registers use the same function. I'm not sure if this would help us optimize the binary size or if the compiler is smart enough to collapse all these methods (don't think so).
@romancardenas Yeah, I think a good general structure for any register is to define a type to abstract all the bitfields, read/write operations in terms of that type, and operations on the type to get/set each field. And as an optimization, specific atomic set operations directly on the register if it makes a difference (as it does for riscv single bit fields, and multi-bit in some situations). But it's not really worth having those direct-access functions for anything that's going to require a full read/modify/write pattern anyway.
For registers which only contain one logical field - eg the pmp_addr registers - you could say that it's probably not necessary to define a new type for it. But that 2 bit shift keeps biting me, so I'd probably still go for it... |
Without needing to touch the CSR. This allows multiple changes in a single register write.
And add a clarifying doc comment that this is an operation on Mstatus values, not on the CSR itself.
This adds a macro to generate `write(value:T)` function for writing a structured value to a CSR.
Allow `Mstatus` values to be directly written back to the CSR.
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.
Thanks for the hard work! Have a look to make comments, especially the one regarding the setter approach.
@@ -36,6 +36,7 @@ | |||
#![allow(clippy::missing_safety_doc)] | |||
|
|||
pub mod asm; | |||
pub(crate) mod bits; |
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.
we can leave it as this, but now I don't find any reason why we should hide it from dependents. Maybe PACs find them handy
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.
I'd hold off on that until we're confident it's a stable public api.
riscv/src/register/mstatus.rs
Outdated
/// Helper to insert a bitfield into Mstatus | ||
#[inline] | ||
fn bf_insert(&self, bit: usize, width: usize, val: usize) -> Self { | ||
Self { | ||
bits: bf_insert(self.bits, bit, width, val), | ||
} | ||
} | ||
|
||
/// Helper to extract a bitfield from Mstatus | ||
#[inline] | ||
fn bf_extract(&self, bit: usize, width: usize) -> usize { | ||
bf_extract(self.bits, bit, width) | ||
} | ||
|
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.
/// Helper to insert a bitfield into Mstatus | |
#[inline] | |
fn bf_insert(&self, bit: usize, width: usize, val: usize) -> Self { | |
Self { | |
bits: bf_insert(self.bits, bit, width, val), | |
} | |
} | |
/// Helper to extract a bitfield from Mstatus | |
#[inline] | |
fn bf_extract(&self, bit: usize, width: usize) -> usize { | |
bf_extract(self.bits, bit, width) | |
} |
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.
I personally prefer using the new utility functions instead of filling all the registers with new methods. But it is not a hard feeling, if you think this approach has advantages, please write them so we can choose the best option.
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.
It's mostly to save on repeating Self { bits: ... }
but if we're going with the &mut self
approach there's no benefit.
riscv/src/register/mstatus.rs
Outdated
@@ -81,46 +96,106 @@ impl Mstatus { | |||
/// Supervisor Interrupt Enable | |||
#[inline] | |||
pub fn sie(&self) -> bool { | |||
self.bits & (1 << 1) != 0 | |||
self.bf_extract(1, 1) != 0 |
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.
self.bf_extract(1, 1) != 0 | |
bf_extract(self.bits, 1, 1) != 0 |
riscv/src/register/mstatus.rs
Outdated
/// update the CSR. | ||
#[inline] | ||
pub fn set_sie(&self, sie: bool) -> Self { | ||
self.bf_insert(1, 1, sie as usize) |
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.
self.bf_insert(1, 1, sie as usize) | |
self.bits = bf_insert(self.bits, 1, 1, sie as usize); |
riscv/src/register/mstatus.rs
Outdated
/// affect the mstatus CSR itself. See [`set_sie`]/[`clear_sie`] to directly | ||
/// update the CSR. | ||
#[inline] | ||
pub fn set_sie(&self, sie: bool) -> Self { |
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.
pub fn set_sie(&self, sie: bool) -> Self { | |
pub fn set_sie(&mut self, sie: bool) { |
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.
From cortex-m
:
#[inline]
pub fn set_n(&mut self, n: bool) {
let mask = 1 << 31;
match n {
true => self.bits |= mask,
false => self.bits &= !mask,
}
}
So let's stick with the classic setter approach with mutable reference and no return value.
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.
What about returning &mut Self
so you can chain them?
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.
I like your idea!
BTW there's a lot of repetition here and in other modules - what do you think about a larger scale macro to generate all of this more automatically? It would make it easier to have all the different registers be consistent in form. (I'm not a huge fan of big macros, but this crate already has a fair amount of macro-driven codegen and I think it tips the cost/benefit scales the right way for this.) (In a separate PR of course.) |
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.
found a 🐛
riscv/src/register/mstatus.rs
Outdated
} | ||
|
||
/// Supervisor Previous Privilege Mode | ||
#[inline] | ||
pub fn spp(&self) -> SPP { | ||
match self.bits & (1 << 8) != 0 { | ||
match bf_extract(self.bits, 7, 1) != 0 { |
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.
match bf_extract(self.bits, 7, 1) != 0 { | |
match bf_extract(self.bits, 8, 1) != 0 { |
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.
Ugh oops. Definitely need a macro-based mechanism that can derive all this from a single source of truth.
I agree with you. We could explore using macros to "ease" the new changes to all the registers |
Also remove local bf_* helpers
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.
LGTM
Summary: Bring in: - rust-embedded/riscv#206 - Eq for Permission - rust-embedded/riscv#207 - Mstatus update operations Details of 207 are still under discussion so this will probably need another freshen (and go back to pulling from the riscv repo once its landed). Reviewed By: dtolnay Differential Revision: D57136236 fbshipit-source-id: 09e5365d3fd499c4dccae7d2ada333bf369fac47
Summary: Bring in: - rust-embedded/riscv#206 - Eq for Permission - rust-embedded/riscv#207 - Mstatus update operations Details of 207 are still under discussion so this will probably need another freshen (and go back to pulling from the riscv repo once its landed). Reviewed By: dtolnay Differential Revision: D57136236 fbshipit-source-id: 09e5365d3fd499c4dccae7d2ada333bf369fac47
Without needing to touch the CSR. This allows multiple changes in a single register write.