Skip to content

Commit 6e626a7

Browse files
committed
Enforce register value constraints
Enforce register size, reset, and mask value agreement. If the provided reset value or mask extend past the register size, or the provided reset value would conflict with the mask, then fail parsing.
1 parent 6d61af9 commit 6e626a7

File tree

2 files changed

+64
-0
lines changed

2 files changed

+64
-0
lines changed

src/error.rs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,16 @@ pub enum NameError {
7272
Invalid(String, String),
7373
}
7474

75+
#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
76+
pub enum ResetValueError {
77+
#[error("Reset value 0x{0:x} doesn't fit in {1} bits")]
78+
ValueTooLarge(u32, u32),
79+
#[error("Reset value 0x{0:x} conflicts with mask '{1}'")]
80+
MaskConflict(u32, u32),
81+
#[error("Mask value 0x{0:x} doesn't fit in {1} bits")]
82+
MaskTooLarge(u32, u32),
83+
}
84+
7585
pub(crate) fn is_valid_name(name: &str) -> bool {
7686
let mut chars = name.chars();
7787
if let Some(first) = chars.next() {
@@ -96,3 +106,56 @@ pub(crate) fn check_name(name: &str, tag: &str) -> Result<()> {
96106
Err(NameError::Invalid(name.to_string(), tag.to_string()).into())
97107
}
98108
}
109+
110+
pub(crate) fn check_reset_value(
111+
size: &Option<u32>,
112+
value: &Option<u32>,
113+
mask: &Option<u32>,
114+
) -> Result<()> {
115+
const MAX_BITS: u32 = u32::MAX.count_ones();
116+
117+
if let (&Some(size), &Some(value)) = (size, value) {
118+
if MAX_BITS - value.leading_zeros() > size {
119+
return Err(ResetValueError::ValueTooLarge(value, size).into());
120+
}
121+
}
122+
if let (&Some(size), &Some(mask)) = (size, mask) {
123+
if MAX_BITS - mask.leading_zeros() > size {
124+
return Err(ResetValueError::MaskTooLarge(mask, size).into());
125+
}
126+
}
127+
if let (&Some(value), &Some(mask)) = (value, mask) {
128+
if value & mask != value {
129+
return Err(ResetValueError::MaskConflict(value, mask).into());
130+
}
131+
}
132+
133+
Ok(())
134+
}
135+
136+
#[cfg(test)]
137+
mod tests {
138+
use crate::error::check_reset_value;
139+
140+
#[test]
141+
fn test_check_reset_value() {
142+
check_reset_value(&None, &None, &None).unwrap();
143+
check_reset_value(&Some(8), &None, &None).unwrap();
144+
check_reset_value(&Some(8), &None, &Some(0xff)).unwrap();
145+
check_reset_value(&Some(32), &Some(0xfaceface), &None).unwrap();
146+
check_reset_value(&Some(32), &Some(0xfaceface), &Some(0xffffffff)).unwrap();
147+
148+
assert!(
149+
check_reset_value(&Some(8), &None, &Some(0x100)).is_err(),
150+
"mask shouldn't fit in size"
151+
);
152+
assert!(
153+
check_reset_value(&Some(1), &Some(0x02), &None).is_err(),
154+
"reset value shouldn't fit in field"
155+
);
156+
assert!(
157+
check_reset_value(&Some(8), &Some(0x80), &Some(0x01)).is_err(),
158+
"value should conflict with mask"
159+
);
160+
}
161+
}

src/svd/registerproperties.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ impl Parse for RegisterProperties {
5151
p.reset_value = parse::optional::<u32>("resetValue", tree)?;
5252
p.reset_mask = parse::optional::<u32>("resetMask", tree)?;
5353
p.access = parse::optional::<Access>("access", tree)?;
54+
check_reset_value(&p.size, &p.reset_value, &p.reset_mask)?;
5455
Ok(p)
5556
}
5657
}

0 commit comments

Comments
 (0)