use super::{Access, Protection, SvdError, ValidateLevel};
#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
pub enum Error {
#[error("Reset value 0x{0:x} doesn't fit in {1} bits")]
ValueTooLarge(u64, u32),
#[error("Reset value 0x{0:x} conflicts with mask '0x{1:x}'")]
MaskConflict(u64, u64),
#[error("Mask value 0x{0:x} doesn't fit in {1} bits")]
MaskTooLarge(u64, u32),
}
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
serde(rename_all = "camelCase")
)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[non_exhaustive]
pub struct RegisterProperties {
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub size: Option<u32>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub access: Option<Access>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub protection: Option<Protection>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub reset_value: Option<u64>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub reset_mask: Option<u64>,
}
impl RegisterProperties {
pub fn new() -> Self {
Self::default()
}
pub fn modify_from(
&mut self,
builder: RegisterProperties,
lvl: ValidateLevel,
) -> Result<(), SvdError> {
if builder.size.is_some() {
self.size = builder.size;
}
if builder.access.is_some() {
self.access = builder.access;
}
if builder.protection.is_some() {
self.protection = builder.protection;
}
if builder.reset_value.is_some() {
self.reset_value = builder.reset_value;
}
if builder.reset_mask.is_some() {
self.reset_mask = builder.reset_mask;
}
self.validate(lvl)
}
pub fn validate(&self, lvl: ValidateLevel) -> Result<(), SvdError> {
if !lvl.is_disabled() {
check_reset_value(self.size, self.reset_value, self.reset_mask, lvl)?;
}
Ok(())
}
pub fn size(mut self, value: Option<u32>) -> Self {
self.size = value;
self
}
pub fn access(mut self, value: Option<Access>) -> Self {
self.access = value;
self
}
pub fn protection(mut self, value: Option<Protection>) -> Self {
self.protection = value;
self
}
pub fn reset_value(mut self, value: Option<u64>) -> Self {
self.reset_value = value;
self
}
pub fn reset_mask(mut self, value: Option<u64>) -> Self {
self.reset_mask = value;
self
}
pub fn build(self, lvl: ValidateLevel) -> Result<RegisterProperties, SvdError> {
self.validate(lvl)?;
Ok(self)
}
}
pub(crate) fn check_reset_value(
size: Option<u32>,
value: Option<u64>,
mask: Option<u64>,
lvl: ValidateLevel,
) -> Result<(), Error> {
const MAX_BITS: u32 = u64::MAX.count_ones();
if let (Some(size), Some(value)) = (size, value) {
if MAX_BITS - value.leading_zeros() > size {
return Err(Error::ValueTooLarge(value, size));
}
}
if lvl.is_strict() {
if let (Some(size), Some(mask)) = (size, mask) {
if MAX_BITS - mask.leading_zeros() > size {
return Err(Error::MaskTooLarge(mask, size));
}
}
if let (Some(value), Some(mask)) = (value, mask) {
if value & mask != value {
return Err(Error::MaskConflict(value, mask));
}
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::{check_reset_value, ValidateLevel};
#[test]
fn test_check_reset_value() {
let lvl = ValidateLevel::Strict;
check_reset_value(None, None, None, lvl).unwrap();
check_reset_value(Some(8), None, None, lvl).unwrap();
check_reset_value(Some(8), None, Some(0xff), lvl).unwrap();
check_reset_value(Some(32), Some(0xfaceface), None, lvl).unwrap();
check_reset_value(Some(32), Some(0xfaceface), Some(0xffffffff), lvl).unwrap();
assert!(
check_reset_value(Some(8), None, Some(0x100), lvl).is_err(),
"mask shouldn't fit in size"
);
assert!(
check_reset_value(Some(1), Some(0x02), None, lvl).is_err(),
"reset value shouldn't fit in field"
);
assert!(
check_reset_value(Some(8), Some(0x80), Some(0x01), lvl).is_err(),
"value should conflict with mask"
);
}
}