Skip to content

Commit 3405452

Browse files
committed
Generate bool accessors for single-bit fields.
Instead of `bits()`, single-bit fields now have the `bit()` accessor, returning `bool`. Writable fields also have the `set()` and `clear()` mutators. Single-bit fields are also marked as safe even if they are not covered by an explicit writeConstraint. This makes using such fields significantly more ergonomic, as previously they would have been u8s, leading to unnecessary conversion and silent truncation in downstream code.
1 parent 9f1c0d3 commit 3405452

File tree

3 files changed

+93
-25
lines changed

3 files changed

+93
-25
lines changed

src/generate.rs

Lines changed: 51 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,12 @@ fn unsafety(write_constraint: Option<&WriteConstraint>, width: u32) -> Option<Id
402402
// any value that can fit in the field
403403
None
404404
}
405+
None if width == 1 => {
406+
// the field is one bit wide, so we assume it's legal to write
407+
// either value into it or it wouldn't exist; despite that
408+
// if a writeConstraint exists then respect it
409+
None
410+
}
405411
_ => Some(Ident::new("unsafe"))
406412
}
407413
}
@@ -630,6 +636,7 @@ pub fn fields(
630636
pc_r: Ident,
631637
pc_w: Ident,
632638
sc: Ident,
639+
bits: Ident,
633640
ty: Ident,
634641
width: u32,
635642
write_constraint: Option<&'a WriteConstraint>,
@@ -644,6 +651,11 @@ pub fn fields(
644651
let pc_w = Ident::new(&*format!("{}W", pc));
645652
let _pc_w = Ident::new(&*format!("_{}W", pc));
646653
let _sc = Ident::new(&*format!("_{}", sc));
654+
let bits = if width == 1 {
655+
Ident::new("bit")
656+
} else {
657+
Ident::new("bits")
658+
};
647659
let mut description = if width == 1 {
648660
format!("Bit {}", offset)
649661
} else {
@@ -660,11 +672,12 @@ pub fn fields(
660672
description: description,
661673
pc_r: pc_r,
662674
pc_w: pc_w,
675+
bits: bits,
663676
width: width,
664677
access: f.access,
665678
evs: &f.enumerated_values,
666679
sc: Ident::new(&*sc),
667-
mask: util::unsuffixed((1 << width) - 1),
680+
mask: util::unsuffixed_or_bool((1 << width) - 1, width),
668681
name: &f.name,
669682
offset: util::unsuffixed(u64(f.bit_range.offset)),
670683
ty: width.to_ty()?,
@@ -683,14 +696,20 @@ pub fn fields(
683696
continue;
684697
}
685698

699+
let bits = &f.bits;
686700
let mask = &f.mask;
687701
let offset = &f.offset;
688702
let fty = &f.ty;
689-
let bits = quote! {
703+
let cast = if f.width == 1 {
704+
quote! { != 0 }
705+
} else {
706+
quote! { as #fty }
707+
};
708+
let value = quote! {
690709
const MASK: #fty = #mask;
691710
const OFFSET: u8 = #offset;
692711

693-
((self.bits >> OFFSET) & MASK as #rty) as #fty
712+
((self.bits >> OFFSET) & MASK as #rty) #cast
694713
};
695714

696715
if let Some((evs, base)) =
@@ -768,7 +787,7 @@ pub fn fields(
768787
#[doc = #description]
769788
#[inline(always)]
770789
pub fn #sc(&self) -> #pc_r {
771-
#pc_r::_from({ #bits })
790+
#pc_r::_from({ #value })
772791
}
773792
},
774793
);
@@ -814,7 +833,7 @@ pub fn fields(
814833
.iter()
815834
.map(
816835
|v| {
817-
let value = util::unsuffixed(v.value);
836+
let value = util::unsuffixed_or_bool(v.value, f.width);
818837
let pc = &v.pc;
819838

820839
quote! {
@@ -834,7 +853,7 @@ pub fn fields(
834853
quote! {
835854
/// Value of the field as raw bits
836855
#[inline(always)]
837-
pub fn bits(&self) -> #fty {
856+
pub fn #bits(&self) -> #fty {
838857
match *self {
839858
#(#arms),*
840859
}
@@ -846,7 +865,7 @@ pub fn fields(
846865
.iter()
847866
.map(
848867
|v| {
849-
let i = util::unsuffixed(v.value);
868+
let i = util::unsuffixed_or_bool(v.value, f.width);
850869
let pc = &v.pc;
851870

852871
quote! {
@@ -862,7 +881,7 @@ pub fn fields(
862881
i => #pc_r::_Reserved(i)
863882
},
864883
);
865-
} else {
884+
} else if 1 << f.width.to_ty_width()? != variants.len() {
866885
arms.push(
867886
quote! {
868887
_ => unreachable!()
@@ -875,8 +894,8 @@ pub fn fields(
875894
#[allow(missing_docs)]
876895
#[doc(hidden)]
877896
#[inline(always)]
878-
pub fn _from(bits: #fty) -> #pc_r {
879-
match bits {
897+
pub fn _from(value: #fty) -> #pc_r {
898+
match value {
880899
#(#arms),*,
881900
}
882901
}
@@ -925,7 +944,7 @@ pub fn fields(
925944
#[doc = #description]
926945
#[inline(always)]
927946
pub fn #sc(&self) -> #pc_r {
928-
let bits = { # bits };
947+
let bits = { #value };
929948
#pc_r { bits }
930949
}
931950
},
@@ -941,7 +960,7 @@ pub fn fields(
941960
impl #pc_r {
942961
/// Value of the field as raw bits
943962
#[inline(always)]
944-
pub fn bits(&self) -> #fty {
963+
pub fn #bits(&self) -> #fty {
945964
self.bits
946965
}
947966
}
@@ -961,9 +980,11 @@ pub fn fields(
961980
let mut proxy_items = vec![];
962981

963982
let mut unsafety = unsafety(f.write_constraint, f.width);
983+
let bits = &f.bits;
964984
let fty = &f.ty;
965985
let offset = &f.offset;
966986
let mask = &f.mask;
987+
let width = f.width;
967988

968989
if let Some((evs, base)) =
969990
util::lookup(
@@ -1069,7 +1090,7 @@ pub fn fields(
10691090
.map(
10701091
|v| {
10711092
let pc = &v.pc;
1072-
let value = util::unsuffixed(v.value);
1093+
let value = util::unsuffixed_or_bool(v.value, f.width);
10731094

10741095
quote! {
10751096
#pc_w::#pc => #value
@@ -1100,7 +1121,7 @@ pub fn fields(
11001121
#[inline(always)]
11011122
pub fn variant(self, variant: #pc_w) -> &'a mut W {
11021123
#unsafety {
1103-
self.bits(variant._bits())
1124+
self.#bits(variant._bits())
11041125
}
11051126
}
11061127
},
@@ -1133,18 +1154,32 @@ pub fn fields(
11331154
);
11341155
}
11351156
}
1157+
} else if width == 1 {
1158+
proxy_items.push(
1159+
quote! {
1160+
/// Sets the field bit
1161+
pub fn set(self) -> &'a mut W {
1162+
self.bit(true)
1163+
}
1164+
1165+
/// Clears the field bit
1166+
pub fn clear(self) -> &'a mut W {
1167+
self.bit(false)
1168+
}
1169+
}
1170+
);
11361171
}
11371172

11381173
proxy_items.push(
11391174
quote! {
11401175
/// Writes raw bits to the field
11411176
#[inline(always)]
1142-
pub #unsafety fn bits(self, bits: #fty) -> &'a mut W {
1177+
pub #unsafety fn #bits(self, value: #fty) -> &'a mut W {
11431178
const MASK: #fty = #mask;
11441179
const OFFSET: u8 = #offset;
11451180

11461181
self.w.bits &= !((MASK as #rty) << OFFSET);
1147-
self.w.bits |= ((bits & MASK) as #rty) << OFFSET;
1182+
self.w.bits |= ((value & MASK) as #rty) << OFFSET;
11481183
self.w
11491184
}
11501185
},

src/lib.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@
183183
//!
184184
//! ``` rust
185185
//! // is the SADD0 bit of the CR2 register set?
186-
//! if i2c1.c2r.read().sadd0().bits() == 1 {
186+
//! if i2c1.c2r.read().sadd0().bit() {
187187
//! // yes
188188
//! } else {
189189
//! // no
@@ -225,7 +225,7 @@
225225
//! // Starting from the reset value, `0x0000_0000`, change the bitfields SADD0
226226
//! // and SADD1 to `1` and `0b0011110` respectively and write that to the
227227
//! // register CR2.
228-
//! i2c1.cr2.write(|w| unsafe { w.sadd0().bits(1).sadd1().bits(0b0011110) });
228+
//! i2c1.cr2.write(|w| unsafe { w.sadd0().bit(true).sadd1().bits(0b0011110) });
229229
//! // NOTE ^ unsafe because you could be writing a reserved bit pattern into
230230
//! // the register. In this case, the SVD doesn't provide enough information to
231231
//! // check whether that's the case.
@@ -246,10 +246,10 @@
246246
//!
247247
//! ``` rust
248248
//! // Set the START bit to 1 while KEEPING the state of the other bits intact
249-
//! i2c1.cr2.modify(|_, w| unsafe { w.start().bits(1) });
249+
//! i2c1.cr2.modify(|_, w| unsafe { w.start().bit(true) });
250250
//!
251251
//! // TOGGLE the STOP bit, all the other bits will remain untouched
252-
//! i2c1.cr2.modify(|r, w| w.stop().bits(r.stop().bits() ^ 1));
252+
//! i2c1.cr2.modify(|r, w| w.stop().bit(!r.stop().bit()));
253253
//! ```
254254
//!
255255
//! # enumeratedValues
@@ -314,12 +314,12 @@
314314
//! gpioa.dir.write(|w| w.pin0().output());
315315
//! ```
316316
//!
317-
//! The `bits` method is still available but will become safe if it's impossible
318-
//! to write a reserved bit pattern into the register
317+
//! The `bits` (or `bit`) method is still available but will become safe if it's
318+
//! impossible to write a reserved bit pattern into the register:
319319
//!
320320
//! ```
321321
//! // safe because there are only two options: `0` or `1`
322-
//! gpioa.dir.write(|w| w.pin0().bits(1));
322+
//! gpioa.dir.write(|w| w.pin0().bit(true));
323323
//! ```
324324
//!
325325
//! # Interrupt API

src/util.rs

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,18 @@ pub fn unsuffixed(n: u64) -> Lit {
235235
Lit::Int(n, IntTy::Unsuffixed)
236236
}
237237

238+
pub fn unsuffixed_or_bool(n: u64, width: u32) -> Lit {
239+
if width == 1 {
240+
if n == 0 {
241+
Lit::Bool(false)
242+
} else {
243+
Lit::Bool(true)
244+
}
245+
} else {
246+
unsuffixed(n)
247+
}
248+
}
249+
238250
#[derive(Clone, Debug)]
239251
pub struct Base<'a> {
240252
pub register: Option<&'a str>,
@@ -427,24 +439,45 @@ fn lookup_in_register<'r>
427439

428440
pub trait U32Ext {
429441
fn to_ty(&self) -> Result<Ident>;
442+
fn to_ty_width(&self) -> Result<u32>;
430443
}
431444

432445
impl U32Ext for u32 {
433446
fn to_ty(&self) -> Result<Ident> {
434447
Ok(
435448
match *self {
436-
1...8 => Ident::new("u8"),
449+
1 => Ident::new("bool"),
450+
2...8 => Ident::new("u8"),
437451
9...16 => Ident::new("u16"),
438452
17...32 => Ident::new("u32"),
439453
_ => {
440454
Err(
441455
format!(
442-
"can't convert {} bits into a Rust integer type",
456+
"can't convert {} bits into a Rust integral type",
443457
*self
444458
),
445459
)?
446460
}
447461
},
448462
)
449463
}
464+
465+
fn to_ty_width(&self) -> Result<u32> {
466+
Ok(
467+
match *self {
468+
1 => 1,
469+
2...8 => 8,
470+
9...16 => 16,
471+
17...32 => 32,
472+
_ => {
473+
Err(
474+
format!(
475+
"can't convert {} bits into a Rust integral type width",
476+
*self
477+
),
478+
)?
479+
}
480+
}
481+
)
482+
}
450483
}

0 commit comments

Comments
 (0)