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}