You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I have two scenarios that I'm unable to solve with svd2rust. I'm lumping them into one ticket because I think the solutions may be related.
Problem 1
I'm porting some driver code where the original in C does this:
voidwrite_special_reg(intvalue) {
// A write to this register will be ignored if the device is busy,// but we can't simply poll for the device to be not-busy// because it could become busy after we check its status. 🤦🏻♂️// The workaround is to write the register// and then check that the register has the desired value,// repeating until it succeeds.while (tries--) {
write_reg(SPECIAL_REG, value);
if (value==read_reg(SPECIAL_REG))
break;
}
}
Unfortunately, I don't see any way to implement this using svd2rust. Not only is w inaccessible outside of the closure, there's no way to read from w, so you can't compare w to r in any way!
for _ in0..tries {let w = device.special_reg().write(|w| w.parity().none());// ⚠️ Not possible: no way to save `w` from the closureif w.parity() == device.special_reg().read().parity(){// ⚠️ Not possible: no way to read fields from `w`break;}}
Nor can you do
for _ in0..tries {letmut parity;
device.special_reg().write(|w| {
w.parity().none();
parity = w.read().parity().bits();// ⚠️ Not possible: no way to read fields from `w`
w
});if parity == device.special_reg().read().parity().bits(){break;}}
Problem 2
The inability to save w and use it later means you can't generate an efficient sequence of writes. Suppose you need to write the same register multiple times, e.g. turning on power to sub-blocks sequentially. Ideally, you would do this using write(); it's wasteful to use modify() when you know the contents of the register didn't change since the last time you wrote it. However, svd2rust doesn't have a way to do this. You either have to use modify():
device.power_reg().write(|w| w.periph1().on());delay_usec(100);
device.power_reg().modify(|r, w| w.periph2().on());// ⚠️ Wasteful: has to read the register// even though we know its contents haven't changed
Or you have to use write() and repeat yourself:
device.power_reg().write(|w| w.periph1().on());delay_usec(100);
device.power_reg().write(|w| w.periph1().on().periph2().on());// ⚠️ Repetitive: you have to say `periph1().on()` again,// which leads to code maintenance problems// It also makes it very annoying to have a dynamic value (for example, an optional peripheral);// you would have to do this every time:
device.power_reg().write(|w| {// Set the fields that were previously set
w.periph1().on();if use_periph2 {
w.periph2().on();}// Now set the field we wanted to change this time
w.periph3().on()});
It would be better if there were a way to "preserve" the value of w across writes so that you could do something like this, using a write_again() function:
letmut last_w = device.power_reg().write(|w| w.periph1().on());delay_usec(100);
last_w = device.power_reg().write_again(last_w, |w| w.periph2().on());delay_usec(100);// Even an optional peripheral is easy:if use_periph3 {
last_w = device.power_reg().write_again(last_w, |w| w.periph3().on());}
device.power_reg().write_again(last_w, |w| w.periph4().on());// value will be correct whether use_periph3 was true or false
Or a more functional programming solution might look like this, using w.with(previous_w) overwriting all of w with the value from the previous call:
letmut last_w = device.power_reg().write(|w| w.periph1().on());delay_usec(100);
last_w = device.power_reg().write(|w| w.with(last_w).periph2().on());delay_usec(100);// Even an optional peripheral is easy:if use_periph3 {
last_w = device.power_reg().write(|w| w.with(last_w).periph3().on());}
device.power_reg().write(|w| w.with(last_w).periph4().on());// value will be correct whether use_periph3 was true or false
(Although at that point, you could probably ignore w and just use last_w directly, unless this causes a problem with lifetimes:
To be sure, this problem is really just a microoptimization, but would be nice to have in order to further svd2rust's goal of generating "the same efficient code as you would write in C" (however that goal is phrased).
Solutions
The solution for problem 2 seems somewhat straightforward. write() and modify() should return w, and there should be a way to use the saved w later for a subsequent write().
The solution for problem 1 may overlap. The first issue is that there's no way to read from w. That might be solved be implementing w.read() or w.as_reader(), or on a per-field basis by implementing w.<field>_read(). Depending on how that capability is implemented, it might be used in a couple of ways. One would be to return w from write() and modify(), as in the solution for problem 2, so that it can be used freely outside of the write() or modify():
let w = device.special_reg().write(|w| w.parity().none());if w.as_reader() == device.special_reg().read(){break;}
Another might be that you have to save the value from the closure, as in:
I have two scenarios that I'm unable to solve with svd2rust. I'm lumping them into one ticket because I think the solutions may be related.
Problem 1
I'm porting some driver code where the original in C does this:
Unfortunately, I don't see any way to implement this using svd2rust. Not only is
w
inaccessible outside of the closure, there's no way to read fromw
, so you can't comparew
tor
in any way!Nor can you do
Problem 2
The inability to save
w
and use it later means you can't generate an efficient sequence of writes. Suppose you need to write the same register multiple times, e.g. turning on power to sub-blocks sequentially. Ideally, you would do this usingwrite()
; it's wasteful to usemodify()
when you know the contents of the register didn't change since the last time you wrote it. However, svd2rust doesn't have a way to do this. You either have to usemodify()
:Or you have to use
write()
and repeat yourself:It would be better if there were a way to "preserve" the value of
w
across writes so that you could do something like this, using awrite_again()
function:Or a more functional programming solution might look like this, using
w.with(previous_w)
overwriting all ofw
with the value from the previous call:(Although at that point, you could probably ignore
w
and just uselast_w
directly, unless this causes a problem with lifetimes:)
To be sure, this problem is really just a microoptimization, but would be nice to have in order to further svd2rust's goal of generating "the same efficient code as you would write in C" (however that goal is phrased).
Solutions
The solution for problem 2 seems somewhat straightforward.
write()
andmodify()
should returnw
, and there should be a way to use the savedw
later for a subsequentwrite()
.The solution for problem 1 may overlap. The first issue is that there's no way to read from
w
. That might be solved be implementingw.read()
orw.as_reader()
, or on a per-field basis by implementingw.<field>_read()
. Depending on how that capability is implemented, it might be used in a couple of ways. One would be to returnw
fromwrite()
andmodify()
, as in the solution for problem 2, so that it can be used freely outside of thewrite()
ormodify()
:Another might be that you have to save the value from the closure, as in:
The text was updated successfully, but these errors were encountered: