From 69e54fcac98d0996e2d0a7c9fecf10981c98989c Mon Sep 17 00:00:00 2001 From: Andrey Zgarbul Date: Wed, 24 Nov 2021 14:43:18 +0300 Subject: [PATCH] make Device match specification --- svd-encoder/src/device.rs | 56 +++++---- svd-parser/src/device.rs | 29 +++-- svd-rs/CHANGELOG.md | 2 + svd-rs/src/derive_from.rs | 4 +- svd-rs/src/device.rs | 236 ++++++++++++++++++++++++++++++-------- 5 files changed, 253 insertions(+), 74 deletions(-) diff --git a/svd-encoder/src/device.rs b/svd-encoder/src/device.rs index 616bf237..0b78be9e 100644 --- a/svd-encoder/src/device.rs +++ b/svd-encoder/src/device.rs @@ -6,29 +6,51 @@ impl Encode for Device { fn encode(&self) -> Result { let mut elem = Element::new("device"); + if let Some(v) = &self.vendor { + elem.children.push(new_node("vendor", v.clone())); + } + if let Some(v) = &self.vendor_id { + elem.children.push(new_node("vendorID", v.clone())); + } + elem.children.push(new_node("name", self.name.clone())); - if let Some(v) = &self.version { - elem.children.push(new_node("version", v.clone())); + if let Some(v) = &self.series { + elem.children.push(new_node("series", v.clone())); } - if let Some(v) = &self.description { - elem.children.push(new_node("description", v.clone())); + elem.children + .push(new_node("version", self.version.clone())); + + elem.children + .push(new_node("description", self.description.clone())); + + if let Some(v) = &self.license_text { + elem.children.push(new_node("licenseText", v.clone())); } if let Some(v) = &self.cpu { elem.children.push(XMLNode::Element(v.encode()?)); } - if let Some(v) = &self.address_unit_bits { + if let Some(v) = &self.header_system_filename { elem.children - .push(new_node("addressUnitBits", format!("{}", v))); + .push(new_node("headerSystemFilename", v.clone())); } - if let Some(v) = &self.width { - elem.children.push(new_node("width", format!("{}", v))); + if let Some(v) = &self.header_definitions_prefix { + elem.children + .push(new_node("header_definitions_prefix", v.clone())); } + elem.children.push(new_node( + "addressUnitBits", + format!("{}", self.address_unit_bits), + )); + + elem.children + .push(new_node("width", format!("{}", self.width))); + elem.children .extend(self.default_register_properties.encode()?); @@ -43,20 +65,14 @@ impl Encode for Device { XMLNode::Element(e) }); + elem.attributes + .insert(String::from("schemaVersion"), self.schema_version.clone()); + elem.attributes + .insert(String::from("xmlns:xs"), self.xmlns_xs.clone()); elem.attributes.insert( - String::from("xmlns:xs"), - String::from("http://www.w3.org/2001/XMLSchema-instance"), + String::from("xs:noNamespaceSchemaLocation"), + self.no_namespace_schema_location.clone(), ); - if let Some(schema_version) = &self.schema_version { - elem.attributes - .insert(String::from("schemaVersion"), schema_version.to_string()); - } - if let Some(schema_version) = &self.schema_version { - elem.attributes.insert( - String::from("xs:noNamespaceSchemaLocation"), - format!("CMSIS-SVD_Schema_{}.xsd", schema_version.replace(".", "_")), - ); - } Ok(elem) } diff --git a/svd-parser/src/device.rs b/svd-parser/src/device.rs index 595f6032..3ff1d38c 100644 --- a/svd-parser/src/device.rs +++ b/svd-parser/src/device.rs @@ -14,13 +14,19 @@ impl Parse for Device { return Err(SVDError::NotExpectedTag("device".to_string()).at(tree.id())); } - Device::builder() + let mut device = Device::builder() + .vendor(tree.get_child_text_opt("vendor")?) + .vendor_id(tree.get_child_text_opt("vendorID")?) .name(tree.get_child_text("name")?) - .version(tree.get_child_text_opt("version")?) - .description(tree.get_child_text_opt("description")?) + .series(tree.get_child_text_opt("series")?) + .version(tree.get_child_text("version")?) + .description(tree.get_child_text("description")?) + .license_text(tree.get_child_text_opt("licenseText")?) .cpu(optional::("cpu", tree, config)?) - .address_unit_bits(optional::("addressUnitBits", tree, &())?) - .width(optional::("width", tree, &())?) + .header_system_filename(tree.get_child_text_opt("headerSystemFilename")?) + .header_definitions_prefix(tree.get_child_text_opt("headerDefinitionsPrefix")?) + .address_unit_bits(tree.get_child_u32("addressUnitBits")?) + .width(tree.get_child_u32("width")?) .default_register_properties(RegisterProperties::parse(tree, config)?) .peripherals({ let ps: Result, _> = tree @@ -30,8 +36,17 @@ impl Parse for Device { .map(|t| Peripheral::parse(&t, config)) .collect(); ps? - }) - .schema_version(tree.attribute("schemaVersion").map(|s| s.to_string())) + }); + if let Some(xmlns_xs) = tree.attribute("xmlns:xs") { + device = device.xmlns_xs(xmlns_xs.to_string()); + } + if let Some(location) = tree.attribute("xs:noNamespaceSchemaLocation") { + device = device.no_namespace_schema_location(location.to_string()); + } + if let Some(schema_version) = tree.attribute("schemaVersion") { + device = device.schema_version(schema_version.to_string()); + } + device .build(config.validate_level) .map_err(|e| SVDError::from(e).at(tree.id())) } diff --git a/svd-rs/CHANGELOG.md b/svd-rs/CHANGELOG.md index 1d4eb61e..6bf35f6a 100644 --- a/svd-rs/CHANGELOG.md +++ b/svd-rs/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## Unreleased +- add missing fields in `Device`, require `version`, `description`, `address_unit_bits` and `width`, + also `schema_version` is required, but skipped during (de)serialization - merge `register` with `registerinfo` modules same as other `info`s - `EnumeratedValues.usage()` now return `None` if it is derived, fix bug in usage check - Use generic `SingleArray` enum for types which can be either collected into SVD arrays or have only one instance diff --git a/svd-rs/src/derive_from.rs b/svd-rs/src/derive_from.rs index 0a0f86c4..0e8ec769 100644 --- a/svd-rs/src/derive_from.rs +++ b/svd-rs/src/derive_from.rs @@ -29,7 +29,7 @@ impl DeriveFrom for ClusterInfo { impl DeriveFrom for EnumeratedValues { fn derive_from(&self, other: &Self) -> Self { let mut derived = self.clone(); - derived.usage = derived.usage.or_else(|| other.usage.clone()); + derived.usage = derived.usage.or(other.usage); if derived.values.is_empty() { derived.values = other.values.clone(); } @@ -80,7 +80,7 @@ impl DeriveFrom for RegisterInfo { impl DeriveFrom for RegisterProperties { fn derive_from(&self, other: &Self) -> Self { - let mut derived = self.clone(); + let mut derived = *self; derived.size = derived.size.or(other.size); derived.access = derived.access.or(other.access); derived.protection = derived.protection.or(other.protection); diff --git a/svd-rs/src/device.rs b/svd-rs/src/device.rs index db8348f9..fac50092 100644 --- a/svd-rs/src/device.rs +++ b/svd-rs/src/device.rs @@ -19,28 +19,43 @@ pub enum Error { #[derive(Clone, Debug, PartialEq)] #[non_exhaustive] pub struct Device { - // vendor + /// Specify the vendor of the device using the full name + #[cfg_attr( + feature = "serde", + serde(default, skip_serializing_if = "Option::is_none") + )] + pub vendor: Option, + + /// Specify the vendor abbreviation without spaces or special characters + #[cfg_attr( + feature = "serde", + serde(default, skip_serializing_if = "Option::is_none", rename = "vendorID") + )] + pub vendor_id: Option, - // vendorID /// The string identifies the device or device series. Device names are required to be unique pub name: String, - // series - /// Define the version of the SVD file + /// Specify the name of the device series #[cfg_attr( feature = "serde", serde(default, skip_serializing_if = "Option::is_none") )] - pub version: Option, + pub series: Option, + + /// Define the version of the SVD file + pub version: String, /// Describe the main features of the device (for example CPU, clock frequency, peripheral overview) + pub description: String, + + /// The text will be copied into the header section of the generated device header file and shall contain the legal disclaimer #[cfg_attr( feature = "serde", serde(default, skip_serializing_if = "Option::is_none") )] - pub description: Option, + pub license_text: Option, - // licenseText /// Describe the processor included in the device #[cfg_attr( feature = "serde", @@ -48,19 +63,25 @@ pub struct Device { )] pub cpu: Option, - /// Define the number of data bits uniquely selected by each address + /// Specify the file name (without extension) of the device-specific system include file #[cfg_attr( feature = "serde", serde(default, skip_serializing_if = "Option::is_none") )] - pub address_unit_bits: Option, + pub header_system_filename: Option, - /// Define the number of data bit-width of the maximum single data transfer supported by the bus infrastructure + /// This string is prepended to all type definition names generated in the CMSIS-Core device header file #[cfg_attr( feature = "serde", serde(default, skip_serializing_if = "Option::is_none") )] - pub width: Option, + pub header_definitions_prefix: Option, + + /// Define the number of data bits uniquely selected by each address + pub address_unit_bits: u32, + + /// Define the number of data bit-width of the maximum single data transfer supported by the bus infrastructure + pub width: u32, /// Default properties for all registers #[cfg_attr(feature = "serde", serde(flatten))] @@ -69,58 +90,99 @@ pub struct Device { /// Group to define peripherals pub peripherals: Vec, + /// Specify the underlying XML schema to which the CMSIS-SVD schema is compliant. + #[cfg_attr(feature = "serde", serde(skip))] + pub xmlns_xs: String, + + /// Specify the file path and file name of the CMSIS-SVD Schema + #[cfg_attr(feature = "serde", serde(skip))] + pub no_namespace_schema_location: String, + /// Specify the compliant CMSIS-SVD schema version - #[cfg_attr( - feature = "serde", - serde(default, skip_serializing_if = "Option::is_none") - )] - pub schema_version: Option, + #[cfg_attr(feature = "serde", serde(skip))] + pub schema_version: String, } /// Builder for [`Device`] #[derive(Clone, Debug, Default)] pub struct DeviceBuilder { + vendor: Option, + vendor_id: Option, name: Option, + series: Option, version: Option, description: Option, + license_text: Option, cpu: Option, + header_system_filename: Option, + header_definitions_prefix: Option, address_unit_bits: Option, width: Option, default_register_properties: RegisterProperties, peripherals: Option>, + xmlns_xs: Option, + no_namespace_schema_location: Option, schema_version: Option, } impl From for DeviceBuilder { fn from(d: Device) -> Self { Self { + vendor: d.vendor, + vendor_id: d.vendor_id, name: Some(d.name), - version: d.version, - description: d.description, - cpu: d.cpu.map(Into::into), - address_unit_bits: d.address_unit_bits, - width: d.width, + series: d.series, + version: Some(d.version), + description: Some(d.description), + license_text: d.license_text, + cpu: d.cpu, + header_system_filename: d.header_system_filename, + header_definitions_prefix: d.header_definitions_prefix, + address_unit_bits: Some(d.address_unit_bits), + width: Some(d.width), default_register_properties: d.default_register_properties, peripherals: Some(d.peripherals), - schema_version: d.schema_version, + xmlns_xs: Some(d.xmlns_xs), + no_namespace_schema_location: Some(d.no_namespace_schema_location), + schema_version: Some(d.schema_version), } } } impl DeviceBuilder { + /// Set the vendor of the device. + pub fn vendor(mut self, value: Option) -> Self { + self.vendor = value; + self + } + /// Set the vendor_id of the device. + pub fn vendor_id(mut self, value: Option) -> Self { + self.vendor_id = value; + self + } /// Set the name of the device. pub fn name(mut self, value: String) -> Self { self.name = Some(value); self } + /// Set the series of the device. + pub fn series(mut self, value: Option) -> Self { + self.series = value; + self + } /// Set the version of the device. - pub fn version(mut self, value: Option) -> Self { - self.version = value; + pub fn version(mut self, value: String) -> Self { + self.version = Some(value); self } /// Set the description of the device. - pub fn description(mut self, value: Option) -> Self { - self.description = value; + pub fn description(mut self, value: String) -> Self { + self.description = Some(value); + self + } + /// Set the license_text of the device. + pub fn license_text(mut self, value: Option) -> Self { + self.license_text = value; self } /// Set the cpu of the device. @@ -128,14 +190,24 @@ impl DeviceBuilder { self.cpu = value; self } + /// Set the header_system_filename of the device. + pub fn header_system_filename(mut self, value: Option) -> Self { + self.header_system_filename = value; + self + } + /// Set the header_definitions_prefix of the device. + pub fn header_definitions_prefix(mut self, value: Option) -> Self { + self.header_definitions_prefix = value; + self + } /// Set the address unit bits of the device. - pub fn address_unit_bits(mut self, value: Option) -> Self { - self.address_unit_bits = value; + pub fn address_unit_bits(mut self, value: u32) -> Self { + self.address_unit_bits = Some(value); self } /// Set the width of the device. - pub fn width(mut self, value: Option) -> Self { - self.width = value; + pub fn width(mut self, value: u32) -> Self { + self.width = Some(value); self } /// Set the default register properties of the device. @@ -148,27 +220,74 @@ impl DeviceBuilder { self.peripherals = Some(value); self } + /// Set the xmlns_xs version of the device. + pub fn xmlns_xs(mut self, value: String) -> Self { + self.xmlns_xs = Some(value); + self + } + /// Set the no_namespace_schema_location version of the device. + pub fn no_namespace_schema_location(mut self, value: String) -> Self { + self.no_namespace_schema_location = Some(value); + self + } /// Set the schema version of the device. - pub fn schema_version(mut self, value: Option) -> Self { - self.schema_version = value; + pub fn schema_version(mut self, value: String) -> Self { + self.schema_version = Some(value); self } /// Validate and build a [`Device`]. pub fn build(self, lvl: ValidateLevel) -> Result { + let schema_version = self.schema_version.unwrap_or_else(|| "1.1".into()); let mut device = Device { + vendor: self.vendor, + vendor_id: self.vendor_id, name: self .name .ok_or_else(|| BuildError::Uninitialized("name".to_string()))?, - version: self.version, - description: self.description, + series: self.series, + version: self + .version + .or_else(|| { + if !lvl.is_strict() { + Some("1.0".into()) + } else { + None + } + }) + .ok_or_else(|| BuildError::Uninitialized("version".to_string()))?, + description: self + .description + .or_else(|| { + if !lvl.is_strict() { + Some("".into()) + } else { + None + } + }) + .ok_or_else(|| BuildError::Uninitialized("description".to_string()))?, + license_text: self.license_text, cpu: self.cpu, - address_unit_bits: self.address_unit_bits, - width: self.width, + header_system_filename: self.header_system_filename, + header_definitions_prefix: self.header_definitions_prefix, + address_unit_bits: self + .address_unit_bits + .or_else(|| if !lvl.is_strict() { Some(8) } else { None }) + .ok_or_else(|| BuildError::Uninitialized("addressUnitBits".to_string()))?, + width: self + .width + .or_else(|| if !lvl.is_strict() { Some(32) } else { None }) + .ok_or_else(|| BuildError::Uninitialized("width".to_string()))?, default_register_properties: self.default_register_properties.build(lvl)?, peripherals: self .peripherals .ok_or_else(|| BuildError::Uninitialized("peripherals".to_string()))?, - schema_version: self.schema_version, + xmlns_xs: self + .xmlns_xs + .unwrap_or_else(|| "http://www.w3.org/2001/XMLSchema-instance".into()), + no_namespace_schema_location: self.no_namespace_schema_location.unwrap_or_else(|| { + format!("CMSIS-SVD_Schema_{}.xsd", schema_version.replace(".", "_")) + }), + schema_version, }; if !lvl.is_disabled() { device.validate(lvl)?; @@ -188,29 +307,56 @@ impl Device { builder: DeviceBuilder, lvl: ValidateLevel, ) -> Result<(), SvdError> { + if builder.vendor.is_some() { + self.vendor = builder.vendor.empty_to_none(); + } + if builder.vendor_id.is_some() { + self.vendor_id = builder.vendor_id.empty_to_none(); + } if let Some(name) = builder.name { self.name = name; } - if builder.version.is_some() { - self.version = builder.version.empty_to_none(); + if builder.series.is_some() { + self.series = builder.series.empty_to_none(); + } + if let Some(version) = builder.version { + self.version = version; } - if builder.description.is_some() { - self.description = builder.description.empty_to_none(); + if let Some(description) = builder.description { + self.description = description; + } + if builder.license_text.is_some() { + self.license_text = builder.license_text.empty_to_none(); } if builder.cpu.is_some() { self.cpu = builder.cpu; } - if builder.address_unit_bits.is_some() { - self.address_unit_bits = builder.address_unit_bits; + if builder.header_system_filename.is_some() { + self.header_system_filename = builder.header_system_filename.empty_to_none(); + } + if builder.header_definitions_prefix.is_some() { + self.header_definitions_prefix = builder.header_definitions_prefix.empty_to_none(); + } + if let Some(address_unit_bits) = builder.address_unit_bits { + self.address_unit_bits = address_unit_bits; } - if builder.width.is_some() { - self.width = builder.width; + if let Some(width) = builder.width { + self.width = width; } self.default_register_properties .modify_from(builder.default_register_properties, lvl)?; if let Some(peripherals) = builder.peripherals { self.peripherals = peripherals; } + if let Some(xmlns_xs) = builder.xmlns_xs { + self.xmlns_xs = xmlns_xs; + } + if let Some(no_namespace_schema_location) = builder.no_namespace_schema_location { + self.no_namespace_schema_location = no_namespace_schema_location; + } + if let Some(schema_version) = builder.schema_version { + self.schema_version = schema_version; + } if !lvl.is_disabled() { self.validate(lvl) } else {