Skip to content

Commit a8d5db7

Browse files
bors[bot]gkelly
andauthored
Merge #120
120: Enforce register value constraints r=burrbull a=gkelly 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. --- I saw that there are some tests from CMSIS that are disabled because of constraint violations due to a check like this. There are also a couple of ATSAMD51 tests (at least) that will fail because of this that are currently enabled. Co-authored-by: Garret Kelly <[email protected]>
2 parents 1e8b49a + ba7b156 commit a8d5db7

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
@@ -74,6 +74,16 @@ pub enum NameError {
7474
Invalid(String, String),
7575
}
7676

77+
#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
78+
pub enum ResetValueError {
79+
#[error("Reset value 0x{0:x} doesn't fit in {1} bits")]
80+
ValueTooLarge(u32, u32),
81+
#[error("Reset value 0x{0:x} conflicts with mask '{1}'")]
82+
MaskConflict(u32, u32),
83+
#[error("Mask value 0x{0:x} doesn't fit in {1} bits")]
84+
MaskTooLarge(u32, u32),
85+
}
86+
7787
pub(crate) fn check_name(name: &str, tag: &str) -> Result<()> {
7888
static PATTERN: Lazy<Regex> = Lazy::new(|| Regex::new("^[_A-Za-z0-9]*$").unwrap());
7989
if PATTERN.is_match(name) {
@@ -93,3 +103,56 @@ pub(crate) fn check_dimable_name(name: &str, tag: &str) -> Result<()> {
93103
Err(NameError::Invalid(name.to_string(), tag.to_string()).into())
94104
}
95105
}
106+
107+
pub(crate) fn check_reset_value(
108+
size: Option<u32>,
109+
value: Option<u32>,
110+
mask: Option<u32>,
111+
) -> Result<()> {
112+
const MAX_BITS: u32 = u32::MAX.count_ones();
113+
114+
if let (Some(size), Some(value)) = (size, value) {
115+
if MAX_BITS - value.leading_zeros() > size {
116+
return Err(ResetValueError::ValueTooLarge(value, size).into());
117+
}
118+
}
119+
if let (Some(size), Some(mask)) = (size, mask) {
120+
if MAX_BITS - mask.leading_zeros() > size {
121+
return Err(ResetValueError::MaskTooLarge(mask, size).into());
122+
}
123+
}
124+
if let (Some(value), Some(mask)) = (value, mask) {
125+
if value & mask != value {
126+
return Err(ResetValueError::MaskConflict(value, mask).into());
127+
}
128+
}
129+
130+
Ok(())
131+
}
132+
133+
#[cfg(test)]
134+
mod tests {
135+
use crate::error::check_reset_value;
136+
137+
#[test]
138+
fn test_check_reset_value() {
139+
check_reset_value(None, None, None).unwrap();
140+
check_reset_value(Some(8), None, None).unwrap();
141+
check_reset_value(Some(8), None, Some(0xff)).unwrap();
142+
check_reset_value(Some(32), Some(0xfaceface), None).unwrap();
143+
check_reset_value(Some(32), Some(0xfaceface), Some(0xffffffff)).unwrap();
144+
145+
assert!(
146+
check_reset_value(Some(8), None, Some(0x100)).is_err(),
147+
"mask shouldn't fit in size"
148+
);
149+
assert!(
150+
check_reset_value(Some(1), Some(0x02), None).is_err(),
151+
"reset value shouldn't fit in field"
152+
);
153+
assert!(
154+
check_reset_value(Some(8), Some(0x80), Some(0x01)).is_err(),
155+
"value should conflict with mask"
156+
);
157+
}
158+
}

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)