1use super::{Access, Protection, SvdError, ValidateLevel};
2
3#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
5pub enum Error {
6 #[error("Reset value 0x{0:x} doesn't fit in {1} bits")]
8 ValueTooLarge(u64, u32),
9 #[error("Reset value 0x{0:x} conflicts with mask '0x{1:x}'")]
11 MaskConflict(u64, u64),
12 #[error("Mask value 0x{0:x} doesn't fit in {1} bits")]
14 MaskTooLarge(u64, u32),
15}
16
17#[cfg_attr(
19 feature = "serde",
20 derive(serde::Deserialize, serde::Serialize),
21 serde(rename_all = "camelCase")
22)]
23#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
24#[non_exhaustive]
25pub struct RegisterProperties {
26 #[cfg_attr(
28 feature = "serde",
29 serde(default, skip_serializing_if = "Option::is_none")
30 )]
31 pub size: Option<u32>,
32
33 #[cfg_attr(
35 feature = "serde",
36 serde(default, skip_serializing_if = "Option::is_none")
37 )]
38 pub access: Option<Access>,
39
40 #[cfg_attr(
42 feature = "serde",
43 serde(default, skip_serializing_if = "Option::is_none")
44 )]
45 pub protection: Option<Protection>,
46
47 #[cfg_attr(
49 feature = "serde",
50 serde(default, skip_serializing_if = "Option::is_none")
51 )]
52 pub reset_value: Option<u64>,
53
54 #[cfg_attr(
56 feature = "serde",
57 serde(default, skip_serializing_if = "Option::is_none")
58 )]
59 pub reset_mask: Option<u64>,
60}
61
62impl RegisterProperties {
63 pub fn new() -> Self {
65 Self::default()
66 }
67 pub fn modify_from(
69 &mut self,
70 builder: RegisterProperties,
71 lvl: ValidateLevel,
72 ) -> Result<(), SvdError> {
73 if builder.size.is_some() {
74 self.size = builder.size;
75 }
76 if builder.access.is_some() {
77 self.access = builder.access;
78 }
79 if builder.protection.is_some() {
80 self.protection = builder.protection;
81 }
82 if builder.reset_value.is_some() {
83 self.reset_value = builder.reset_value;
84 }
85 if builder.reset_mask.is_some() {
86 self.reset_mask = builder.reset_mask;
87 }
88 self.validate(lvl)
89 }
90
91 pub fn validate(&self, lvl: ValidateLevel) -> Result<(), SvdError> {
93 if !lvl.is_disabled() {
94 check_reset_value(self.size, self.reset_value, self.reset_mask, lvl)?;
95 }
96 Ok(())
97 }
98 pub fn size(mut self, value: Option<u32>) -> Self {
100 self.size = value;
101 self
102 }
103 pub fn access(mut self, value: Option<Access>) -> Self {
105 self.access = value;
106 self
107 }
108 pub fn protection(mut self, value: Option<Protection>) -> Self {
110 self.protection = value;
111 self
112 }
113 pub fn reset_value(mut self, value: Option<u64>) -> Self {
115 self.reset_value = value;
116 self
117 }
118 pub fn reset_mask(mut self, value: Option<u64>) -> Self {
120 self.reset_mask = value;
121 self
122 }
123 pub fn build(self, lvl: ValidateLevel) -> Result<RegisterProperties, SvdError> {
125 self.validate(lvl)?;
126 Ok(self)
127 }
128}
129
130pub(crate) fn check_reset_value(
131 size: Option<u32>,
132 value: Option<u64>,
133 mask: Option<u64>,
134 lvl: ValidateLevel,
135) -> Result<(), Error> {
136 const MAX_BITS: u32 = u64::MAX.count_ones();
137
138 if let (Some(size), Some(value)) = (size, value) {
139 if MAX_BITS - value.leading_zeros() > size {
140 return Err(Error::ValueTooLarge(value, size));
141 }
142 }
143 if lvl.is_strict() {
144 if let (Some(size), Some(mask)) = (size, mask) {
145 if MAX_BITS - mask.leading_zeros() > size {
146 return Err(Error::MaskTooLarge(mask, size));
147 }
148 }
149 if let (Some(value), Some(mask)) = (value, mask) {
150 if value & mask != value {
151 return Err(Error::MaskConflict(value, mask));
152 }
153 }
154 }
155
156 Ok(())
157}
158
159#[cfg(test)]
160mod tests {
161 use super::{check_reset_value, ValidateLevel};
162
163 #[test]
164 fn test_check_reset_value() {
165 let lvl = ValidateLevel::Strict;
166 check_reset_value(None, None, None, lvl).unwrap();
167 check_reset_value(Some(8), None, None, lvl).unwrap();
168 check_reset_value(Some(8), None, Some(0xff), lvl).unwrap();
169 check_reset_value(Some(32), Some(0xfaceface), None, lvl).unwrap();
170 check_reset_value(Some(32), Some(0xfaceface), Some(0xffffffff), lvl).unwrap();
171
172 assert!(
173 check_reset_value(Some(8), None, Some(0x100), lvl).is_err(),
174 "mask shouldn't fit in size"
175 );
176 assert!(
177 check_reset_value(Some(1), Some(0x02), None, lvl).is_err(),
178 "reset value shouldn't fit in field"
179 );
180 assert!(
181 check_reset_value(Some(8), Some(0x80), Some(0x01), lvl).is_err(),
182 "value should conflict with mask"
183 );
184 }
185}