├── .gitmodules ├── triagebot.toml ├── .github ├── CODEOWNERS └── workflows │ ├── rustfmt.yml │ └── ci.yml ├── Cargo.toml ├── .gitignore ├── tests ├── src │ ├── bad_svd.rs │ ├── modifiedwritevalues.rs │ ├── usage.rs │ ├── endian.rs │ ├── access.rs │ ├── writeconstraint.rs │ ├── registerproperties.rs │ ├── addressblock.rs │ ├── cpu.rs │ ├── bitrange.rs │ ├── interrupt.rs │ ├── lib.rs │ ├── enumeratedvalue.rs │ ├── enumeratedvalues.rs │ ├── dimelement.rs │ └── field.rs └── Cargo.toml ├── svd-encoder ├── src │ ├── access.rs │ ├── endian.rs │ ├── usage.rs │ ├── datatype.rs │ ├── protection.rs │ ├── readaction.rs │ ├── modifiedwritevalues.rs │ ├── registercluster.rs │ ├── interrupt.rs │ ├── enumeratedvalue.rs │ ├── enumeratedvalues.rs │ ├── bitrange.rs │ ├── addressblock.rs │ ├── writeconstraint.rs │ ├── registerproperties.rs │ ├── dimelement.rs │ ├── cpu.rs │ ├── cluster.rs │ ├── field.rs │ ├── lib.rs │ ├── device.rs │ ├── register.rs │ └── peripheral.rs ├── Cargo.toml ├── README.md └── CHANGELOG.md ├── svd-parser ├── src │ ├── usage.rs │ ├── protection.rs │ ├── endian.rs │ ├── access.rs │ ├── datatype.rs │ ├── readaction.rs │ ├── modifiedwritevalues.rs │ ├── registercluster.rs │ ├── interrupt.rs │ ├── registerproperties.rs │ ├── enumeratedvalue.rs │ ├── array.rs │ ├── addressblock.rs │ ├── writeconstraint.rs │ ├── enumeratedvalues.rs │ ├── cluster.rs │ ├── cpu.rs │ ├── dimelement.rs │ ├── field.rs │ ├── register.rs │ ├── device.rs │ ├── types.rs │ ├── peripheral.rs │ ├── elementext.rs │ └── bitrange.rs ├── Cargo.toml ├── examples │ ├── svd2yaml.rs │ └── svd2json.rs ├── README.md └── CHANGELOG.md ├── svd-rs ├── Cargo.toml ├── src │ ├── usage.rs │ ├── endian.rs │ ├── readaction.rs │ ├── protection.rs │ ├── writeconstraint.rs │ ├── access.rs │ ├── modifiedwritevalues.rs │ ├── datatype.rs │ ├── interrupt.rs │ ├── array.rs │ ├── bitrange.rs │ ├── registercluster.rs │ ├── addressblock.rs │ ├── derive_from.rs │ ├── enumeratedvalue.rs │ ├── registerproperties.rs │ └── enumeratedvalues.rs ├── README.md └── CHANGELOG.md ├── LICENSE-MIT ├── README.md ├── generate-tests.sh └── CHANGELOG.md /.gitmodules: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /triagebot.toml: -------------------------------------------------------------------------------- 1 | [assign] 2 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @rust-embedded/tools -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "svd-rs", 4 | "svd-parser", 5 | "svd-encoder", 6 | "tests", 7 | ] 8 | resolver = "2" 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.rs.bk 2 | [._]*.sw[a-p] 3 | Cargo.lock 4 | target 5 | 6 | # generate-tests.sh related ignores 7 | cmsis-svd-data 8 | tests/cmsis.rs 9 | tests/cmsis_tests 10 | -------------------------------------------------------------------------------- /tests/src/bad_svd.rs: -------------------------------------------------------------------------------- 1 | use svd_parser as svd; 2 | 3 | #[test] 4 | fn arm_sample_faulty() { 5 | let xml = include_str!(concat!( 6 | env!("CARGO_MANIFEST_DIR"), 7 | "/data/ARM_Sample_faulty.svd" 8 | )); 9 | if let Err(e) = svd::parse(xml) { 10 | for e in e.chain() { 11 | println!("{}", e); 12 | } 13 | } else { 14 | panic!() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /svd-encoder/src/access.rs: -------------------------------------------------------------------------------- 1 | use super::{Config, Element, Encode, EncodeError, XMLNode}; 2 | 3 | impl Encode for crate::svd::Access { 4 | type Error = EncodeError; 5 | 6 | fn encode_with_config(&self, _config: &Config) -> Result { 7 | let mut elem = Element::new("access"); 8 | elem.children.push(XMLNode::Text(self.as_str().to_string())); 9 | Ok(elem) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /svd-encoder/src/endian.rs: -------------------------------------------------------------------------------- 1 | use super::{Config, Element, Encode, EncodeError, XMLNode}; 2 | 3 | impl Encode for crate::svd::Endian { 4 | type Error = EncodeError; 5 | 6 | fn encode_with_config(&self, _config: &Config) -> Result { 7 | let mut elem = Element::new("endian"); 8 | elem.children.push(XMLNode::Text(self.as_str().to_string())); 9 | Ok(elem) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /svd-encoder/src/usage.rs: -------------------------------------------------------------------------------- 1 | use super::{Config, Element, Encode, EncodeError, XMLNode}; 2 | 3 | impl Encode for crate::svd::Usage { 4 | type Error = EncodeError; 5 | 6 | fn encode_with_config(&self, _config: &Config) -> Result { 7 | let mut elem = Element::new("usage"); 8 | elem.children.push(XMLNode::Text(self.as_str().to_string())); 9 | Ok(elem) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /svd-encoder/src/datatype.rs: -------------------------------------------------------------------------------- 1 | use super::{Config, Element, Encode, EncodeError, XMLNode}; 2 | 3 | impl Encode for crate::svd::DataType { 4 | type Error = EncodeError; 5 | 6 | fn encode_with_config(&self, _config: &Config) -> Result { 7 | let mut elem = Element::new("dataType"); 8 | elem.children.push(XMLNode::Text(self.as_str().to_string())); 9 | Ok(elem) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /svd-encoder/src/protection.rs: -------------------------------------------------------------------------------- 1 | use super::{Config, Element, Encode, EncodeError, XMLNode}; 2 | 3 | impl Encode for crate::svd::Protection { 4 | type Error = EncodeError; 5 | 6 | fn encode_with_config(&self, _config: &Config) -> Result { 7 | let mut elem = Element::new("protection"); 8 | elem.children.push(XMLNode::Text(self.as_str().to_string())); 9 | Ok(elem) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /svd-encoder/src/readaction.rs: -------------------------------------------------------------------------------- 1 | use super::{Config, Element, Encode, EncodeError, XMLNode}; 2 | 3 | impl Encode for crate::svd::ReadAction { 4 | type Error = EncodeError; 5 | 6 | fn encode_with_config(&self, _config: &Config) -> Result { 7 | let mut elem = Element::new("readAction"); 8 | elem.children.push(XMLNode::Text(self.as_str().to_string())); 9 | Ok(elem) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /svd-parser/src/usage.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::svd::Usage; 3 | 4 | impl Parse for Usage { 5 | type Object = Self; 6 | type Error = SVDErrorAt; 7 | type Config = Config; 8 | 9 | fn parse(tree: &Node, _config: &Self::Config) -> Result { 10 | let text = tree.get_text()?; 11 | 12 | Self::parse_str(text).ok_or_else(|| SVDError::UnknownUsageVariant.at(tree.id())) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /svd-parser/src/protection.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | impl Parse for crate::svd::Protection { 4 | type Object = Self; 5 | type Error = SVDErrorAt; 6 | type Config = Config; 7 | 8 | fn parse(tree: &Node, _config: &Self::Config) -> Result { 9 | let text = tree.get_text()?; 10 | 11 | Self::parse_str(text).ok_or_else(|| SVDError::InvalidProtection(text.into()).at(tree.id())) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /svd-parser/src/endian.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::svd::Endian; 3 | 4 | impl Parse for Endian { 5 | type Object = Self; 6 | type Error = SVDErrorAt; 7 | type Config = Config; 8 | 9 | fn parse(tree: &Node, _config: &Self::Config) -> Result { 10 | let text = tree.get_text()?; 11 | 12 | Self::parse_str(text).ok_or_else(|| SVDError::UnknownEndian(text.into()).at(tree.id())) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /svd-parser/src/access.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | use crate::svd::Access; 4 | impl Parse for Access { 5 | type Object = Self; 6 | type Error = SVDErrorAt; 7 | type Config = Config; 8 | 9 | fn parse(tree: &Node, _config: &Self::Config) -> Result { 10 | let text = tree.get_text()?; 11 | 12 | Self::parse_str(text).ok_or_else(|| SVDError::UnknownAccessType(text.into()).at(tree.id())) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /svd-parser/src/datatype.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::svd::DataType; 3 | 4 | impl Parse for DataType { 5 | type Object = Self; 6 | type Error = SVDErrorAt; 7 | type Config = Config; 8 | 9 | fn parse(tree: &Node, _config: &Self::Config) -> Result { 10 | let text = tree.get_text()?; 11 | 12 | Self::parse_str(text).ok_or_else(|| SVDError::InvalidDatatype(text.into()).at(tree.id())) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | keywords = ["CMSIS", "SVD"] 3 | license = "MIT OR Apache-2.0" 4 | name = "svd-tests" 5 | repository = "https://github.com/rust-embedded/svd" 6 | edition = "2021" 7 | version = "0.12.0" 8 | publish = false 9 | 10 | [dependencies] 11 | svd-rs = { path = "../svd-rs"} 12 | svd-parser = { path = "../svd-parser"} 13 | svd-encoder = { path = "../svd-encoder"} 14 | roxmltree = "0.20" 15 | xmltree = "0.11.0" 16 | anyhow = "1.0.45" 17 | -------------------------------------------------------------------------------- /.github/workflows/rustfmt.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: master 4 | pull_request: 5 | merge_group: 6 | 7 | name: Code formatting check 8 | 9 | jobs: 10 | fmt: 11 | name: Rustfmt 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | - uses: dtolnay/rust-toolchain@master 16 | with: 17 | toolchain: stable 18 | components: rustfmt 19 | - run: cargo fmt --all -- --check 20 | -------------------------------------------------------------------------------- /svd-encoder/src/modifiedwritevalues.rs: -------------------------------------------------------------------------------- 1 | use super::{Config, Element, Encode, EncodeError, XMLNode}; 2 | 3 | impl Encode for crate::svd::ModifiedWriteValues { 4 | type Error = EncodeError; 5 | 6 | fn encode_with_config(&self, _config: &Config) -> Result { 7 | let mut elem = Element::new("modifiedWriteValues"); 8 | elem.children.push(XMLNode::Text(self.as_str().to_string())); 9 | Ok(elem) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /svd-parser/src/readaction.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | use crate::svd::ReadAction; 4 | impl Parse for ReadAction { 5 | type Object = Self; 6 | type Error = SVDErrorAt; 7 | type Config = Config; 8 | 9 | fn parse(tree: &Node, _config: &Self::Config) -> Result { 10 | let text = tree.get_text()?; 11 | 12 | Self::parse_str(text).ok_or_else(|| SVDError::InvalidReadAction(text.into()).at(tree.id())) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tests/src/modifiedwritevalues.rs: -------------------------------------------------------------------------------- 1 | use super::run_test; 2 | use crate::svd::ModifiedWriteValues; 3 | 4 | #[test] 5 | fn decode_encode() { 6 | // FIXME: Do we need a more extensive test? 7 | let tests = [( 8 | ModifiedWriteValues::OneToToggle, 9 | "oneToToggle", 10 | "oneToToggle", 11 | )]; 12 | 13 | run_test::(&tests[..], None, None); 14 | } 15 | -------------------------------------------------------------------------------- /svd-encoder/src/registercluster.rs: -------------------------------------------------------------------------------- 1 | use super::{Config, Element, Encode, EncodeError}; 2 | 3 | use crate::svd::RegisterCluster; 4 | 5 | impl Encode for RegisterCluster { 6 | type Error = EncodeError; 7 | 8 | fn encode_with_config(&self, config: &Config) -> Result { 9 | match self { 10 | RegisterCluster::Register(r) => r.encode_with_config(config), 11 | RegisterCluster::Cluster(c) => c.encode_with_config(config), 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tests/src/usage.rs: -------------------------------------------------------------------------------- 1 | use super::run_test; 2 | use crate::svd::Usage; 3 | 4 | #[test] 5 | fn decode_encode() { 6 | let tests = [ 7 | (Usage::Read, "read", "read"), 8 | (Usage::Write, "write", "write"), 9 | ( 10 | Usage::ReadWrite, 11 | "read-write", 12 | "read-write", 13 | ), 14 | ]; 15 | 16 | run_test::(&tests[..], None, None); 17 | } 18 | -------------------------------------------------------------------------------- /svd-parser/src/modifiedwritevalues.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | use crate::svd::ModifiedWriteValues; 4 | impl Parse for ModifiedWriteValues { 5 | type Object = Self; 6 | type Error = SVDErrorAt; 7 | type Config = Config; 8 | 9 | fn parse(tree: &Node, _config: &Self::Config) -> Result { 10 | let text = tree.get_text()?; 11 | 12 | Self::parse_str(text) 13 | .ok_or_else(|| SVDError::InvalidModifiedWriteValues(text.into()).at(tree.id())) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /svd-encoder/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["The Tools Team "] 3 | description = "A CMSIS-SVD file encoder" 4 | documentation = "https://docs.rs/svd-encoder" 5 | keywords = ["CMSIS", "SVD", "encoder"] 6 | license = "MIT OR Apache-2.0" 7 | name = "svd-encoder" 8 | repository = "https://github.com/rust-embedded/svd" 9 | edition = "2021" 10 | rust-version = "1.70.0" 11 | version = "0.14.7" 12 | readme = "README.md" 13 | 14 | [dependencies] 15 | convert_case = "0.6.0" 16 | svd-rs = { version = "0.14.12", path = "../svd-rs" } 17 | thiserror = "1.0.31" 18 | 19 | [dependencies.xmltree] 20 | version = "0.11.0" 21 | features = ["attribute-order"] 22 | -------------------------------------------------------------------------------- /svd-parser/src/registercluster.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::svd::{Cluster, Register}; 3 | 4 | use crate::svd::RegisterCluster; 5 | impl Parse for RegisterCluster { 6 | type Object = Self; 7 | type Error = SVDErrorAt; 8 | type Config = Config; 9 | 10 | fn parse(tree: &Node, config: &Self::Config) -> Result { 11 | match tree.tag_name().name() { 12 | "register" => Register::parse(tree, config).map(Into::into), 13 | "cluster" => Cluster::parse(tree, config).map(Into::into), 14 | _ => Err( 15 | SVDError::InvalidRegisterCluster(tree.tag_name().name().to_string()).at(tree.id()), 16 | ), 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /svd-rs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["The Tools Team "] 3 | description = "A CMSIS-SVD base structures" 4 | documentation = "https://docs.rs/svd-rs" 5 | keywords = ["CMSIS", "SVD"] 6 | license = "MIT OR Apache-2.0" 7 | name = "svd-rs" 8 | repository = "https://github.com/rust-embedded/svd" 9 | edition = "2021" 10 | rust-version = "1.70.0" 11 | version = "0.14.12" 12 | readme = "README.md" 13 | 14 | [features] 15 | derive-from = [] 16 | 17 | [dependencies] 18 | thiserror = "1.0.31" 19 | 20 | [dependencies.regex] 21 | version = "1.10" 22 | 23 | [dependencies.once_cell] 24 | version = "1.17.2" 25 | 26 | [dependencies.serde] 27 | version = "1.0" 28 | features = ["derive"] 29 | optional = true 30 | -------------------------------------------------------------------------------- /tests/src/endian.rs: -------------------------------------------------------------------------------- 1 | use super::run_test; 2 | use crate::svd::Endian; 3 | 4 | #[test] 5 | fn decode_encode() { 6 | let tests = [ 7 | ( 8 | Endian::Little, 9 | "little", 10 | "little", 11 | ), 12 | (Endian::Big, "big", "big"), 13 | ( 14 | Endian::Selectable, 15 | "selectable", 16 | "selectable", 17 | ), 18 | ( 19 | Endian::Other, 20 | "other", 21 | "other", 22 | ), 23 | ]; 24 | 25 | run_test::(&tests[..], None, None); 26 | } 27 | -------------------------------------------------------------------------------- /svd-encoder/src/interrupt.rs: -------------------------------------------------------------------------------- 1 | use super::{new_node, Config, Element, Encode, EncodeError}; 2 | 3 | use crate::{config::change_case, svd::Interrupt}; 4 | 5 | impl Encode for Interrupt { 6 | type Error = EncodeError; 7 | 8 | fn encode_with_config(&self, config: &Config) -> Result { 9 | let mut children = vec![new_node( 10 | "name", 11 | change_case(&self.name, config.interrupt_name), 12 | )]; 13 | if let Some(d) = self.description.clone() { 14 | children.push(new_node("description", d)); 15 | } 16 | children.push(new_node("value", format!("{}", self.value))); 17 | let mut elem = Element::new("interrupt"); 18 | elem.children = children; 19 | Ok(elem) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /svd-parser/src/interrupt.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::svd::Interrupt; 3 | 4 | impl Parse for Interrupt { 5 | type Object = Self; 6 | type Error = SVDErrorAt; 7 | type Config = Config; 8 | 9 | fn parse(tree: &Node, config: &Self::Config) -> Result { 10 | if !tree.has_tag_name("interrupt") { 11 | return Err(SVDError::NotExpectedTag("interrupt".to_string()).at(tree.id())); 12 | } 13 | let name = tree.get_child_text("name")?; 14 | 15 | Interrupt::builder() 16 | .name(name) 17 | .description(tree.get_child_text_opt("description")?) 18 | .value(tree.get_child_u32("value")?) 19 | .build(config.validate_level) 20 | .map_err(|e| SVDError::from(e).at(tree.id())) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /svd-parser/src/registerproperties.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::svd::{Access, Protection, RegisterProperties}; 3 | 4 | impl Parse for RegisterProperties { 5 | type Object = Self; 6 | type Error = SVDErrorAt; 7 | type Config = Config; 8 | 9 | fn parse(tree: &Node, config: &Self::Config) -> Result { 10 | RegisterProperties::new() 11 | .size(optional::("size", tree, &())?) 12 | .access(optional::("access", tree, config)?) 13 | .protection(optional::("protection", tree, config)?) 14 | .reset_value(optional::("resetValue", tree, &())?) 15 | .reset_mask(optional::("resetMask", tree, &())?) 16 | .build(config.validate_level) 17 | .map_err(|e| SVDError::from(e).at(tree.id())) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /svd-parser/src/enumeratedvalue.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::svd::EnumeratedValue; 3 | 4 | impl Parse for EnumeratedValue { 5 | type Object = Self; 6 | type Error = SVDErrorAt; 7 | type Config = Config; 8 | 9 | fn parse(tree: &Node, config: &Self::Config) -> Result { 10 | if !tree.has_tag_name("enumeratedValue") { 11 | return Err(SVDError::NotExpectedTag("enumeratedValue".to_string()).at(tree.id())); 12 | } 13 | 14 | EnumeratedValue::builder() 15 | .name(tree.get_child_text("name")?) 16 | .description(tree.get_child_text_opt("description")?) 17 | .value(optional::("value", tree, &())?) 18 | .is_default(tree.get_child_bool("isDefault").ok()) 19 | .build(config.validate_level) 20 | .map_err(|e| SVDError::from(e).at(tree.id())) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /svd-parser/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = [ 3 | "The Tools Team ", 4 | "Jorge Aparicio ", 5 | ] 6 | description = "A CMSIS-SVD file parser" 7 | documentation = "https://docs.rs/svd-parser" 8 | keywords = ["CMSIS", "SVD", "parser"] 9 | license = "MIT OR Apache-2.0" 10 | name = "svd-parser" 11 | repository = "https://github.com/rust-embedded/svd" 12 | edition = "2021" 13 | rust-version = "1.70.0" 14 | version = "0.14.9" 15 | readme = "README.md" 16 | 17 | [features] 18 | derive-from = ["svd-rs/derive-from"] 19 | expand = ["derive-from"] 20 | 21 | [dependencies] 22 | svd-rs = { version = "0.14.12", path = "../svd-rs" } 23 | roxmltree = "0.20" 24 | anyhow = "1.0.58" 25 | thiserror = "1.0.31" 26 | 27 | [dev-dependencies] 28 | serde_json = { version = "1.0", features = ["preserve_order"] } 29 | serde_yaml = "0.8.26" 30 | svd-rs = { version = "0.14.10", path = "../svd-rs", features = ["serde"] } 31 | 32 | [[example]] 33 | name = "svd2json" 34 | -------------------------------------------------------------------------------- /tests/src/access.rs: -------------------------------------------------------------------------------- 1 | use super::run_test; 2 | use crate::svd::Access; 3 | 4 | #[test] 5 | fn decode_encode() { 6 | let tests = [ 7 | ( 8 | Access::ReadOnly, 9 | "read-only", 10 | "read-only", 11 | ), 12 | ( 13 | Access::ReadWrite, 14 | "read-write", 15 | "read-write", 16 | ), 17 | ( 18 | Access::ReadWriteOnce, 19 | "read-writeOnce", 20 | "read-writeOnce", 21 | ), 22 | ( 23 | Access::WriteOnly, 24 | "write-only", 25 | "write-only", 26 | ), 27 | ( 28 | Access::WriteOnce, 29 | "writeOnce", 30 | "writeOnce", 31 | ), 32 | ]; 33 | 34 | run_test::(&tests[..], None, None); 35 | } 36 | -------------------------------------------------------------------------------- /tests/src/writeconstraint.rs: -------------------------------------------------------------------------------- 1 | use super::run_test; 2 | use crate::svd::{WriteConstraint, WriteConstraintRange}; 3 | 4 | #[test] 5 | fn decode_encode() { 6 | let tests = [( 7 | WriteConstraint::WriteAsRead(true), 8 | "true", 9 | "true" 10 | ), 11 | ( 12 | WriteConstraint::UseEnumeratedValues(true), 13 | "true", 14 | "true" 15 | ), 16 | ( 17 | WriteConstraint::Range(WriteConstraintRange{min: 1, max: 10}), 18 | "110", 19 | "110" 20 | )]; 21 | 22 | run_test::(&tests[..], None, None); 23 | } 24 | -------------------------------------------------------------------------------- /tests/src/registerproperties.rs: -------------------------------------------------------------------------------- 1 | use crate::EncodeChildren; 2 | use crate::parse::Parse; 3 | use crate::svd::{Access, RegisterProperties}; 4 | use xmltree::Element; 5 | 6 | #[test] 7 | fn decode_encode() { 8 | let example = String::from( 9 | " 10 | 11 | 64 12 | read-only 13 | 0x11223344 14 | 0xFFFFFFFF 15 | 16 | ", 17 | ); 18 | 19 | let mut expected = RegisterProperties::default(); 20 | expected.size = Some(64); 21 | expected.reset_value = Some(0x11223344); 22 | expected.reset_mask = Some(0xffffffff); 23 | expected.access = Some(Access::ReadOnly); 24 | 25 | let tree1 = Element::parse(example.as_bytes()).unwrap(); 26 | 27 | let parsed = RegisterProperties::parse(&tree1).unwrap(); 28 | assert_eq!(parsed, expected, "Parsing tree failed"); 29 | 30 | let mut tree2 = Element::new("mock"); 31 | tree2.children = parsed.encode().unwrap(); 32 | assert_eq!(tree1, tree2, "Encoding value failed"); 33 | } 34 | -------------------------------------------------------------------------------- /svd-encoder/src/enumeratedvalue.rs: -------------------------------------------------------------------------------- 1 | use super::{new_node, Config, Element, Encode, EncodeError}; 2 | 3 | use crate::{ 4 | config::{change_case, format_number}, 5 | svd::EnumeratedValue, 6 | }; 7 | 8 | impl Encode for EnumeratedValue { 9 | type Error = EncodeError; 10 | 11 | fn encode_with_config(&self, config: &Config) -> Result { 12 | let mut base = Element::new("enumeratedValue"); 13 | base.children.push(new_node( 14 | "name", 15 | change_case(&self.name, config.enumerated_value_name), 16 | )); 17 | 18 | if let Some(d) = &self.description { 19 | base.children.push(new_node("description", d.clone())); 20 | }; 21 | 22 | if let Some(v) = &self.value { 23 | base.children.push(new_node( 24 | "value", 25 | format_number(*v, config.enumerated_value_value), 26 | )); 27 | }; 28 | 29 | if let Some(v) = &self.is_default { 30 | base.children.push(new_node("isDefault", format!("{}", v))); 31 | }; 32 | 33 | Ok(base) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/src/addressblock.rs: -------------------------------------------------------------------------------- 1 | use super::run_test; 2 | use crate::svd::{AddressBlock, AddressBlockUsage, ValidateLevel}; 3 | 4 | #[test] 5 | fn decode_encode() { 6 | let parse_config = svd_parser::Config::default(); 7 | let mut encode_config = svd_encoder::Config::default(); 8 | encode_config.update("address_block_offset", "Dec"); 9 | encode_config.update("address_block_size", "UpperHex8"); 10 | 11 | let tests = [( 12 | AddressBlock::builder() 13 | .offset(0) 14 | .size(0x00000F00) 15 | .usage(AddressBlockUsage::Registers) 16 | .protection(None) 17 | .build(ValidateLevel::Strict) 18 | .unwrap(), 19 | " 20 | 0x0 21 | 0xF00 22 | registers 23 | ", 24 | " 25 | 0 26 | 0x00000F00 27 | registers 28 | ", 29 | )]; 30 | 31 | run_test::(&tests[..], Some(parse_config), Some(encode_config)); 32 | } 33 | -------------------------------------------------------------------------------- /svd-encoder/src/enumeratedvalues.rs: -------------------------------------------------------------------------------- 1 | use super::{new_node, Config, Element, Encode, EncodeError}; 2 | 3 | use crate::{config::change_case, svd::EnumeratedValues}; 4 | 5 | impl Encode for EnumeratedValues { 6 | type Error = EncodeError; 7 | 8 | fn encode_with_config(&self, config: &Config) -> Result { 9 | let mut base = Element::new("enumeratedValues"); 10 | 11 | if let Some(d) = &self.name { 12 | base.children.push(new_node( 13 | "name", 14 | change_case(d, config.enumerated_values_name), 15 | )); 16 | }; 17 | 18 | if let Some(v) = &self.usage { 19 | base.children.push(v.encode_node()?); 20 | }; 21 | 22 | if let Some(v) = &self.derived_from { 23 | base.attributes.insert( 24 | String::from("derivedFrom"), 25 | change_case(v, config.enumerated_values_name), 26 | ); 27 | } 28 | 29 | for v in &self.values { 30 | base.children.push(v.encode_node_with_config(config)?); 31 | } 32 | 33 | Ok(base) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /svd-parser/src/array.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use svd_rs::{DimElement, MaybeArray, Name}; 3 | 4 | pub fn parse_array(tag: &str, tree: &Node, config: &Config) -> Result, SVDErrorAt> 5 | where 6 | T: Parse + Name, 7 | { 8 | if !tree.has_tag_name(tag) { 9 | return Err(SVDError::NotExpectedTag(tag.into()).at(tree.id())); 10 | } 11 | 12 | let info = T::parse(tree, config)?; 13 | 14 | if tree.get_child("dimIncrement").is_some() { 15 | let array_info = DimElement::parse(tree, config)?; 16 | check_has_placeholder(info.name(), tag).map_err(|e| e.at(tree.id()))?; 17 | if let Some(indexes) = &array_info.dim_index { 18 | if array_info.dim as usize != indexes.len() { 19 | return Err(SVDError::IncorrectDimIndexesCount( 20 | array_info.dim as usize, 21 | indexes.len(), 22 | ) 23 | .at(tree.id())); 24 | } 25 | } 26 | Ok(MaybeArray::Array(info, array_info)) 27 | } else { 28 | Ok(MaybeArray::Single(info)) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Jorge Aparicio 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /svd-parser/src/addressblock.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::svd::{AddressBlock, AddressBlockUsage, Protection}; 3 | 4 | impl Parse for AddressBlock { 5 | type Object = Self; 6 | type Error = SVDErrorAt; 7 | type Config = Config; 8 | 9 | fn parse(tree: &Node, config: &Self::Config) -> Result { 10 | Self::builder() 11 | .offset(tree.get_child_u32("offset")?) 12 | .size(tree.get_child_u32("size")?) 13 | .usage(AddressBlockUsage::parse( 14 | &tree.get_child_elem("usage")?, 15 | config, 16 | )?) 17 | .protection(optional::("protection", tree, config)?) 18 | .build(config.validate_level) 19 | .map_err(|e| SVDError::from(e).at(tree.id())) 20 | } 21 | } 22 | 23 | impl Parse for AddressBlockUsage { 24 | type Object = Self; 25 | type Error = SVDErrorAt; 26 | type Config = Config; 27 | 28 | fn parse(tree: &Node, _config: &Self::Config) -> Result { 29 | let text = tree.get_text()?; 30 | 31 | Self::parse_str(text).ok_or_else(|| SVDError::UnknownAddressBlockUsageVariant.at(tree.id())) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /svd-encoder/src/bitrange.rs: -------------------------------------------------------------------------------- 1 | use super::{new_node, Config, EncodeError, XMLNode}; 2 | use crate::{ 3 | config::FieldBitRangeFormat, 4 | svd::{BitRange, BitRangeType}, 5 | }; 6 | 7 | // TODO: Encode method differs from Encode trait as it acts on a set of possible children, create an interface or decide how to better do this 8 | pub fn encode_bitrange(br: &BitRange, config: &Config) -> Result, EncodeError> { 9 | match (config.field_bit_range, br.range_type) { 10 | (Some(FieldBitRangeFormat(BitRangeType::BitRange)), _) | (None, BitRangeType::BitRange) => { 11 | Ok(vec![new_node("bitRange", br.bit_range())]) 12 | } 13 | (Some(FieldBitRangeFormat(BitRangeType::MsbLsb)), _) | (None, BitRangeType::MsbLsb) => { 14 | Ok(vec![ 15 | new_node("lsb", format!("{}", br.lsb())), 16 | new_node("msb", format!("{}", br.msb())), 17 | ]) 18 | } 19 | (Some(FieldBitRangeFormat(BitRangeType::OffsetWidth)), _) 20 | | (None, BitRangeType::OffsetWidth) => Ok(vec![ 21 | new_node("bitOffset", format!("{}", br.offset)), 22 | new_node("bitWidth", format!("{}", br.width)), 23 | ]), 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /svd-encoder/src/addressblock.rs: -------------------------------------------------------------------------------- 1 | use crate::config::format_number; 2 | 3 | use super::{new_node, Config, Element, Encode, EncodeError, XMLNode}; 4 | 5 | impl Encode for crate::svd::AddressBlock { 6 | type Error = EncodeError; 7 | 8 | fn encode_with_config(&self, config: &Config) -> Result { 9 | let mut children = vec![ 10 | new_node( 11 | "offset", 12 | format_number(self.offset, config.address_block_offset), 13 | ), 14 | new_node("size", format_number(self.size, config.address_block_size)), 15 | self.usage.encode_node()?, 16 | ]; 17 | if let Some(v) = &self.protection { 18 | children.push(v.encode_node()?); 19 | }; 20 | let mut elem = Element::new("addressBlock"); 21 | elem.children = children; 22 | Ok(elem) 23 | } 24 | } 25 | 26 | impl Encode for crate::svd::AddressBlockUsage { 27 | type Error = EncodeError; 28 | 29 | fn encode_with_config(&self, _config: &Config) -> Result { 30 | let mut elem = Element::new("usage"); 31 | elem.children.push(XMLNode::Text(self.as_str().to_string())); 32 | Ok(elem) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /svd-encoder/src/writeconstraint.rs: -------------------------------------------------------------------------------- 1 | use super::{new_node, Config, Element, Encode, EncodeError}; 2 | 3 | use crate::svd::{WriteConstraint, WriteConstraintRange}; 4 | 5 | impl Encode for WriteConstraint { 6 | type Error = EncodeError; 7 | 8 | fn encode_with_config(&self, _config: &Config) -> Result { 9 | let v = match *self { 10 | WriteConstraint::WriteAsRead(v) => new_node("writeAsRead", format!("{}", v)), 11 | WriteConstraint::UseEnumeratedValues(v) => { 12 | new_node("useEnumeratedValues", format!("{}", v)) 13 | } 14 | WriteConstraint::Range(v) => v.encode_node()?, 15 | }; 16 | 17 | let mut elem = Element::new("writeConstraint"); 18 | elem.children = vec![v]; 19 | Ok(elem) 20 | } 21 | } 22 | 23 | impl Encode for WriteConstraintRange { 24 | type Error = EncodeError; 25 | 26 | fn encode_with_config(&self, _config: &Config) -> Result { 27 | let mut elem = Element::new("range"); 28 | elem.children = vec![ 29 | new_node("minimum", format!("{}", self.min)), 30 | new_node("maximum", format!("{}", self.max)), 31 | ]; 32 | Ok(elem) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /svd-rs/src/usage.rs: -------------------------------------------------------------------------------- 1 | /// Allows specifying two different enumerated values 2 | /// depending whether it is to be used for a read or a write access. 3 | #[cfg_attr( 4 | feature = "serde", 5 | derive(serde::Deserialize, serde::Serialize), 6 | serde(rename_all = "kebab-case") 7 | )] 8 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 9 | pub enum Usage { 10 | /// Read 11 | Read, 12 | /// Write 13 | Write, 14 | /// Read & Write 15 | ReadWrite, 16 | } 17 | 18 | impl Default for Usage { 19 | fn default() -> Self { 20 | Self::ReadWrite 21 | } 22 | } 23 | 24 | impl Usage { 25 | /// Parse a string into an [`Usage`] value, returning [`Option::None`] if the string is not valid. 26 | pub fn parse_str(s: &str) -> Option { 27 | match s { 28 | "read" => Some(Self::Read), 29 | "write" => Some(Self::Write), 30 | "read-write" => Some(Self::ReadWrite), 31 | _ => None, 32 | } 33 | } 34 | 35 | /// Convert this [`Usage`] into a static string. 36 | pub const fn as_str(self) -> &'static str { 37 | match self { 38 | Self::Read => "read", 39 | Self::Write => "write", 40 | Self::ReadWrite => "read-write", 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /svd-encoder/src/registerproperties.rs: -------------------------------------------------------------------------------- 1 | use super::{new_node, Config, Encode, EncodeChildren, EncodeError, XMLNode}; 2 | 3 | use crate::{config::format_number, svd::RegisterProperties}; 4 | 5 | impl EncodeChildren for RegisterProperties { 6 | type Error = EncodeError; 7 | 8 | fn encode_with_config(&self, config: &Config) -> Result, EncodeError> { 9 | let mut children = Vec::new(); 10 | 11 | if let Some(v) = &self.size { 12 | children.push(new_node("size", format_number(*v, config.register_size))); 13 | }; 14 | 15 | if let Some(v) = &self.access { 16 | children.push(v.encode_node_with_config(config)?); 17 | }; 18 | 19 | if let Some(v) = &self.protection { 20 | children.push(v.encode_node_with_config(config)?); 21 | }; 22 | 23 | if let Some(v) = &self.reset_value { 24 | children.push(new_node( 25 | "resetValue", 26 | format_number(*v, config.register_reset_value), 27 | )); 28 | }; 29 | 30 | if let Some(v) = &self.reset_mask { 31 | children.push(new_node( 32 | "resetMask", 33 | format_number(*v, config.register_reset_mask), 34 | )); 35 | }; 36 | 37 | Ok(children) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /svd-rs/src/endian.rs: -------------------------------------------------------------------------------- 1 | /// Endianness of a [processor](crate::Cpu). 2 | #[cfg_attr( 3 | feature = "serde", 4 | derive(serde::Deserialize, serde::Serialize), 5 | serde(rename_all = "kebab-case") 6 | )] 7 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 8 | pub enum Endian { 9 | /// Little endian. 10 | Little, 11 | /// Big endian. 12 | Big, 13 | /// Mixed endian. 14 | Selectable, 15 | /// Other 16 | Other, 17 | } 18 | 19 | impl Default for Endian { 20 | fn default() -> Self { 21 | Self::Little 22 | } 23 | } 24 | 25 | impl Endian { 26 | /// Parse a string into an [Endian] value, returning [`Option::None`] if the string is not valid. 27 | pub fn parse_str(s: &str) -> Option { 28 | match s { 29 | "little" => Some(Self::Little), 30 | "big" => Some(Self::Big), 31 | "selectable" => Some(Self::Selectable), 32 | "other" => Some(Self::Other), 33 | _ => None, 34 | } 35 | } 36 | 37 | /// Convert this [`Endian`] into a static string. 38 | pub const fn as_str(self) -> &'static str { 39 | match self { 40 | Self::Little => "little", 41 | Self::Big => "big", 42 | Self::Selectable => "selectable", 43 | Self::Other => "other", 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /svd-parser/src/writeconstraint.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::svd::{WriteConstraint, WriteConstraintRange}; 3 | 4 | impl Parse for WriteConstraint { 5 | type Object = Self; 6 | type Error = SVDErrorAt; 7 | type Config = Config; 8 | 9 | fn parse(tree: &Node, _config: &Self::Config) -> Result { 10 | let child = tree.first_element_child().unwrap(); 11 | if child.next_sibling_element().is_some() { 12 | return Err(SVDError::MoreThanOneWriteConstraint.at(tree.id())); 13 | } 14 | let field = child.tag_name().name(); 15 | // Write constraint can only be one of the following 16 | match field { 17 | "writeAsRead" => tree.get_child_bool(field).map(WriteConstraint::WriteAsRead), 18 | "useEnumeratedValues" => tree 19 | .get_child_bool(field) 20 | .map(WriteConstraint::UseEnumeratedValues), 21 | "range" => WriteConstraintRange::parse(&tree.get_child_elem(field)?, &()) 22 | .map(WriteConstraint::Range), 23 | _ => Err(SVDError::UnknownWriteConstraint.at(tree.id())), 24 | } 25 | } 26 | } 27 | 28 | impl Parse for WriteConstraintRange { 29 | type Object = Self; 30 | type Error = SVDErrorAt; 31 | type Config = (); 32 | 33 | fn parse(tree: &Node, _config: &Self::Config) -> Result { 34 | Ok(Self { 35 | min: tree.get_child_u64("minimum")?, 36 | max: tree.get_child_u64("maximum")?, 37 | }) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rust SVD manipulating libraries 2 | 3 | This project is developed and maintained by the [Tools team][team]. 4 | 5 | It consists of: 6 | 7 | - [`svd-rs`](https://docs.rs/svd-rs). Basic structures and builders, also (de)serializers under `serde` feature. 8 | - [`svd-parser`](https://docs.rs/svd-parser). Library for parsing SVD XML source in Rust `Device` structure. 9 | - [`svd-encoder`](https://docs.rs/svd-encoder). Library for creating SVD XML. 10 | 11 | ## Minimum Supported Rust Version (MSRV) 12 | 13 | This crate is guaranteed to compile on stable Rust 1.70.0 and up. It *might* 14 | compile with older versions but that may change in any new patch release. 15 | 16 | ## License 17 | 18 | Licensed under either of 19 | 20 | - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or 21 | http://www.apache.org/licenses/LICENSE-2.0) 22 | - MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 23 | 24 | at your option. 25 | 26 | ### Contribution 27 | 28 | Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the 29 | work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any 30 | additional terms or conditions. 31 | 32 | ## Code of Conduct 33 | 34 | Contribution to this crate is organized under the terms of the [Rust Code of 35 | Conduct][CoC], the maintainer of this crate, the [Tools team][team], promises 36 | to intervene to uphold that code of conduct. 37 | 38 | [CoC]: CODE_OF_CONDUCT.md 39 | [team]: https://github.com/rust-embedded/wg#the-tools-team 40 | -------------------------------------------------------------------------------- /tests/src/cpu.rs: -------------------------------------------------------------------------------- 1 | use super::run_test; 2 | use crate::svd::{Cpu, Endian, ValidateLevel}; 3 | 4 | #[test] 5 | fn decode_encode() { 6 | let tests = [( 7 | Cpu::builder() 8 | .name("EFM32JG12B500F512GM48".to_string()) 9 | .revision("5.1.1".to_string()) 10 | .endian(Endian::Little) 11 | .mpu_present(true) 12 | .fpu_present(true) 13 | .nvic_priority_bits(8) 14 | .has_vendor_systick(false) 15 | .build(ValidateLevel::Strict) 16 | .unwrap(), 17 | " 18 | 19 | EFM32JG12B500F512GM48 20 | 5.1.1 21 | little 22 | true 23 | true 24 | 8 25 | false 26 | 27 | ", 28 | " 29 | 30 | EFM32JG12B500F512GM48 31 | 5.1.1 32 | little 33 | true 34 | true 35 | 8 36 | false 37 | 38 | ", 39 | )]; 40 | 41 | run_test::(&tests[..], None, None); 42 | } 43 | -------------------------------------------------------------------------------- /svd-parser/src/enumeratedvalues.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::svd::{EnumeratedValue, EnumeratedValues, Usage}; 3 | 4 | impl Parse for EnumeratedValues { 5 | type Object = Self; 6 | type Error = SVDErrorAt; 7 | type Config = Config; 8 | 9 | fn parse(tree: &Node, config: &Self::Config) -> Result { 10 | if !tree.has_tag_name("enumeratedValues") { 11 | return Err(SVDError::NotExpectedTag("enumeratedValues".to_string()).at(tree.id())); 12 | } 13 | EnumeratedValues::builder() 14 | .name(tree.get_child_text_opt("name")?) 15 | .usage(optional::("usage", tree, config)?) 16 | .derived_from(tree.attribute("derivedFrom").map(|s| s.to_owned())) 17 | .values({ 18 | let values: Result, _> = tree 19 | .children() 20 | .filter(|t| { 21 | t.is_element() 22 | && !matches!(t.tag_name().name(), "name" | "headerEnumName" | "usage") 23 | }) 24 | .map(|t| { 25 | if t.has_tag_name("enumeratedValue") { 26 | EnumeratedValue::parse(&t, config) 27 | } else { 28 | Err(SVDError::NotExpectedTag("enumeratedValue".to_string()).at(t.id())) 29 | } 30 | }) 31 | .collect(); 32 | values? 33 | }) 34 | .build(config.validate_level) 35 | .map_err(|e| SVDError::from(e).at(tree.id())) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/src/bitrange.rs: -------------------------------------------------------------------------------- 1 | use crate::parse::Parse; 2 | use crate::svd::{BitRange, BitRangeType}; 3 | use xmltree::Element; 4 | 5 | #[test] 6 | fn decode_encode() { 7 | let types = vec![ 8 | ( 9 | BitRange { 10 | offset: 16, 11 | width: 4, 12 | range_type: BitRangeType::BitRange, 13 | }, 14 | String::from( 15 | " 16 | [19:16] 17 | ", 18 | ), 19 | ), 20 | ( 21 | BitRange { 22 | offset: 16, 23 | width: 4, 24 | range_type: BitRangeType::OffsetWidth, 25 | }, 26 | String::from( 27 | " 28 | 164 29 | ", 30 | ), 31 | ), 32 | ( 33 | BitRange { 34 | offset: 16, 35 | width: 4, 36 | range_type: BitRangeType::MsbLsb, 37 | }, 38 | String::from( 39 | " 40 | 1619 41 | ", 42 | ), 43 | ), 44 | ]; 45 | 46 | for (a, s) in types { 47 | let tree1 = Element::parse(s.as_bytes()).unwrap(); 48 | let value = BitRange::parse(&tree1).unwrap(); 49 | assert_eq!(value, a, "Parsing `{}` expected `{:?}`", s, a); 50 | let mut tree2 = Element::new("fake"); 51 | tree2.children = value.encode().unwrap(); 52 | assert_eq!(tree1, tree2, "Encoding {:?} expected {}", a, s); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /svd-parser/examples/svd2yaml.rs: -------------------------------------------------------------------------------- 1 | use serde_yaml as yaml; 2 | use svd_parser as svd; 3 | 4 | use std::env::args; 5 | use std::fs::File; 6 | use std::io::{Read, Write}; 7 | use std::path::PathBuf; 8 | use yaml::Value; 9 | 10 | fn main() { 11 | // Collect command-line arguments. 12 | let mut args = args(); 13 | // Expect exactly one argument, with the name of the SVD file. 14 | // (Arg #0 is this program's name, Arg #1 is the actual argument) 15 | let svd_fn = if let (Some(_), Some(arg1), None) = (args.next(), args.next(), args.next()) { 16 | PathBuf::from(arg1) 17 | } else { 18 | println!("Usage: (svd2yaml) file.svd"); 19 | return; 20 | }; 21 | 22 | // Open the XML-formatted SVD file and read it into a String. 23 | let mut svd_xml = String::new(); 24 | File::open(&svd_fn) 25 | .expect("Failed to open SVD input file") 26 | .read_to_string(&mut svd_xml) 27 | .expect("Failed to read SVD input file to a String"); 28 | 29 | // Use the 'svd_parser' crate to parse the file. 30 | let device = svd::parse(&mut svd_xml).expect("Failed to parse the SVD file into Rust structs"); 31 | 32 | // Convert the parsed data into YAML format. 33 | let v: Value = 34 | yaml::to_value(device).expect("Failed to serialize Rust structs into YAML format"); 35 | 36 | // Write the YAML-formatted device description to a file. 37 | let mut yaml_fn = svd_fn.clone(); 38 | yaml_fn.set_extension("yaml"); 39 | File::create(&yaml_fn) 40 | .expect("Failed to open YAML output file") 41 | .write_all(yaml::to_string(&v).unwrap().as_bytes()) 42 | .expect("Failed to write to YAML output file"); 43 | } 44 | -------------------------------------------------------------------------------- /svd-rs/src/readaction.rs: -------------------------------------------------------------------------------- 1 | /// Specifies the side effect following a read operation 2 | #[cfg_attr( 3 | feature = "serde", 4 | derive(serde::Deserialize, serde::Serialize), 5 | serde(rename_all = "camelCase") 6 | )] 7 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 8 | pub enum ReadAction { 9 | /// The register/field is cleared (set to zero) following a read operation 10 | Clear, 11 | 12 | /// The register/field is set (set to ones) following a read operation 13 | Set, 14 | 15 | /// The register/field is modified in some way after a read operation 16 | Modify, 17 | 18 | /// One or more dependent resources other than the current register/field are immediately affected by a read operation 19 | ModifyExternal, 20 | } 21 | 22 | impl Default for ReadAction { 23 | fn default() -> Self { 24 | Self::Modify 25 | } 26 | } 27 | 28 | impl ReadAction { 29 | /// Parse a string into an [`ReadAction`] value, returning [`Option::None`] if the string is not valid. 30 | pub fn parse_str(s: &str) -> Option { 31 | use self::ReadAction::*; 32 | match s { 33 | "clear" => Some(Clear), 34 | "set" => Some(Set), 35 | "modify" => Some(Modify), 36 | "modifyExternal" => Some(ModifyExternal), 37 | _ => None, 38 | } 39 | } 40 | 41 | /// Convert this [`ReadAction`] into a static string. 42 | pub const fn as_str(self) -> &'static str { 43 | match self { 44 | Self::Clear => "clear", 45 | Self::Set => "set", 46 | Self::Modify => "modify", 47 | Self::ModifyExternal => "modifyExternal", 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /svd-parser/examples/svd2json.rs: -------------------------------------------------------------------------------- 1 | use serde_json as json; 2 | use svd_parser as svd; 3 | 4 | use json::Value; 5 | use std::env::args; 6 | use std::fs::File; 7 | use std::io::{Read, Write}; 8 | use std::path::PathBuf; 9 | 10 | fn main() { 11 | // Collect command-line arguments. 12 | let mut args = args(); 13 | // Expect exactly one argument, with the name of the SVD file. 14 | // (Arg #0 is this program's name, Arg #1 is the actual argument) 15 | let svd_fn = if let (Some(_), Some(arg1), None) = (args.next(), args.next(), args.next()) { 16 | PathBuf::from(arg1) 17 | } else { 18 | println!("Usage: (svd2json) file.svd"); 19 | return; 20 | }; 21 | 22 | // Open the XML-formatted SVD file and read it into a String. 23 | let mut svd_xml = String::new(); 24 | File::open(&svd_fn) 25 | .expect("Failed to open SVD input file") 26 | .read_to_string(&mut svd_xml) 27 | .expect("Failed to read SVD input file to a String"); 28 | 29 | // Use the 'svd_parser' crate to parse the file. 30 | let device = svd::parse(&mut svd_xml).expect("Failed to parse the SVD file into Rust structs"); 31 | 32 | // Convert the parsed data into JSON format. 33 | let v: Value = 34 | json::to_value(device).expect("Failed to serialize Rust structs into JSON format"); 35 | 36 | // Write the JSON-formatted device description to a file. 37 | let mut json_fn = svd_fn.to_path_buf(); 38 | json_fn.set_extension("json"); 39 | File::create(json_fn) 40 | .expect("Failed to open JSON output file") 41 | .write_all(json::to_string_pretty(&v).unwrap().as_bytes()) 42 | .expect("Failed to write to JSON output file"); 43 | } 44 | -------------------------------------------------------------------------------- /svd-rs/src/protection.rs: -------------------------------------------------------------------------------- 1 | /// Specify the security privilege to access an address region 2 | /// 3 | /// This information is relevant for the programmer as well as the debugger 4 | /// when no universal access permissions have been granted. 5 | /// If no specific information is provided, an address region is accessible in any mode 6 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 7 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 8 | pub enum Protection { 9 | /// Secure permission required for access 10 | #[cfg_attr(feature = "serde", serde(rename = "s"))] 11 | Secure, 12 | 13 | /// Non-secure or secure permission required for access 14 | #[cfg_attr(feature = "serde", serde(rename = "n"))] 15 | NonSecure, 16 | 17 | /// Privileged permission required for access 18 | #[cfg_attr(feature = "serde", serde(rename = "p"))] 19 | Privileged, 20 | } 21 | 22 | impl Default for Protection { 23 | fn default() -> Self { 24 | Self::NonSecure 25 | } 26 | } 27 | 28 | impl Protection { 29 | /// Parse a string into an [`Protection`] value, returning [`Option::None`] if the string is not valid. 30 | pub fn parse_str(s: &str) -> Option { 31 | match s { 32 | "s" => Some(Self::Secure), 33 | "n" => Some(Self::NonSecure), 34 | "p" => Some(Self::Privileged), 35 | _ => None, 36 | } 37 | } 38 | 39 | /// Convert this [`Protection`] into a static string. 40 | pub const fn as_str(self) -> &'static str { 41 | match self { 42 | Self::Secure => "s", 43 | Self::NonSecure => "n", 44 | Self::Privileged => "p", 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /svd-parser/src/cluster.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::svd::{Cluster, ClusterInfo, RegisterCluster, RegisterProperties}; 3 | 4 | impl Parse for Cluster { 5 | type Object = Self; 6 | type Error = SVDErrorAt; 7 | type Config = Config; 8 | 9 | fn parse(tree: &Node, config: &Self::Config) -> Result { 10 | parse_array("cluster", tree, config) 11 | } 12 | } 13 | 14 | impl Parse for ClusterInfo { 15 | type Object = Self; 16 | type Error = SVDErrorAt; 17 | type Config = Config; 18 | 19 | fn parse(tree: &Node, config: &Self::Config) -> Result { 20 | ClusterInfo::builder() 21 | .name(tree.get_child_text("name")?) 22 | .description(tree.get_child_text_opt("description")?) 23 | .alternate_cluster(tree.get_child_text_opt("alternateCluster")?) 24 | .header_struct_name(tree.get_child_text_opt("headerStructName")?) 25 | .address_offset(tree.get_child_u32("addressOffset")?) 26 | .default_register_properties(RegisterProperties::parse(tree, config)?) 27 | .children({ 28 | let children: Result, _> = tree 29 | .children() 30 | .filter(|t| { 31 | t.is_element() && (t.has_tag_name("register") || t.has_tag_name("cluster")) 32 | }) 33 | .map(|t| RegisterCluster::parse(&t, config)) 34 | .collect(); 35 | children? 36 | }) 37 | .derived_from(tree.attribute("derivedFrom").map(|s| s.to_owned())) 38 | .build(config.validate_level) 39 | .map_err(|e| SVDError::from(e).at(tree.id())) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /svd-rs/README.md: -------------------------------------------------------------------------------- 1 | [![crates.io](https://img.shields.io/crates/d/svd-rs.svg)](https://crates.io/crates/svd-rs) 2 | [![crates.io](https://img.shields.io/crates/v/svd-rs.svg)](https://crates.io/crates/svd-rs) 3 | [![Continuous Integration](https://github.com/rust-embedded/svd/workflows/Continuous%20Integration/badge.svg)](https://github.com/rust-embedded/svd/actions) 4 | [![Documentation](https://docs.rs/svd-rs/badge.svg)](https://docs.rs/svd-rs) 5 | 6 | # `svd-rs` 7 | 8 | > CMSIS-SVD like structures with `serde` support 9 | 10 | This project is developed and maintained by the [Tools team][team]. 11 | 12 | ## ["Documentation"](https://docs.rs/svd-rs) 13 | 14 | ## Minimum Supported Rust Version (MSRV) 15 | 16 | This crate is guaranteed to compile on stable Rust 1.70.0 and up. It *might* 17 | compile with older versions but that may change in any new patch release. 18 | 19 | ## License 20 | 21 | Licensed under either of 22 | 23 | - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or 24 | http://www.apache.org/licenses/LICENSE-2.0) 25 | - MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 26 | 27 | at your option. 28 | 29 | ### Contribution 30 | 31 | Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the 32 | work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any 33 | additional terms or conditions. 34 | 35 | ## Code of Conduct 36 | 37 | Contribution to this crate is organized under the terms of the [Rust Code of 38 | Conduct][CoC], the maintainer of this crate, the [Tools team][team], promises 39 | to intervene to uphold that code of conduct. 40 | 41 | [CoC]: CODE_OF_CONDUCT.md 42 | [team]: https://github.com/rust-embedded/wg#the-tools-team 43 | -------------------------------------------------------------------------------- /svd-parser/README.md: -------------------------------------------------------------------------------- 1 | [![crates.io](https://img.shields.io/crates/d/svd-parser.svg)](https://crates.io/crates/svd-parser) 2 | [![crates.io](https://img.shields.io/crates/v/svd-parser.svg)](https://crates.io/crates/svd-parser) 3 | [![Continuous Integration](https://github.com/rust-embedded/svd/workflows/Continuous%20Integration/badge.svg)](https://github.com/rust-embedded/svd/actions) 4 | [![Documentation](https://docs.rs/svd-parser/badge.svg)](https://docs.rs/svd-parser) 5 | 6 | # `svd-parser` 7 | 8 | > A CMSIS-SVD file parser 9 | 10 | This project is developed and maintained by the [Tools team][team]. 11 | 12 | ## ["Documentation"](https://docs.rs/svd-parser) 13 | 14 | ## Minimum Supported Rust Version (MSRV) 15 | 16 | This crate is guaranteed to compile on stable Rust 1.70.0 and up. It *might* 17 | compile with older versions but that may change in any new patch release. 18 | 19 | ## License 20 | 21 | Licensed under either of 22 | 23 | - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or 24 | http://www.apache.org/licenses/LICENSE-2.0) 25 | - MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 26 | 27 | at your option. 28 | 29 | ### Contribution 30 | 31 | Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the 32 | work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any 33 | additional terms or conditions. 34 | 35 | ## Code of Conduct 36 | 37 | Contribution to this crate is organized under the terms of the [Rust Code of 38 | Conduct][CoC], the maintainer of this crate, the [Tools team][team], promises 39 | to intervene to uphold that code of conduct. 40 | 41 | [CoC]: CODE_OF_CONDUCT.md 42 | [team]: https://github.com/rust-embedded/wg#the-tools-team 43 | -------------------------------------------------------------------------------- /svd-encoder/README.md: -------------------------------------------------------------------------------- 1 | [![crates.io](https://img.shields.io/crates/d/svd-encoder.svg)](https://crates.io/crates/svd-encoder) 2 | [![crates.io](https://img.shields.io/crates/v/svd-encoder.svg)](https://crates.io/crates/svd-encoder) 3 | [![Continuous Integration](https://github.com/rust-embedded/svd/workflows/Continuous%20Integration/badge.svg)](https://github.com/rust-embedded/svd/actions) 4 | [![Documentation](https://docs.rs/svd-encoder/badge.svg)](https://docs.rs/svd-encoder) 5 | 6 | # `svd-encoder` 7 | 8 | > A CMSIS-SVD file encoder 9 | 10 | This project is developed and maintained by the [Tools team][team]. 11 | 12 | ## ["Documentation"](https://docs.rs/svd-encoder) 13 | 14 | ## Minimum Supported Rust Version (MSRV) 15 | 16 | This crate is guaranteed to compile on stable Rust 1.70.0 and up. It *might* 17 | compile with older versions but that may change in any new patch release. 18 | 19 | ## License 20 | 21 | Licensed under either of 22 | 23 | - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or 24 | http://www.apache.org/licenses/LICENSE-2.0) 25 | - MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 26 | 27 | at your option. 28 | 29 | ### Contribution 30 | 31 | Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the 32 | work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any 33 | additional terms or conditions. 34 | 35 | ## Code of Conduct 36 | 37 | Contribution to this crate is organized under the terms of the [Rust Code of 38 | Conduct][CoC], the maintainer of this crate, the [Tools team][team], promises 39 | to intervene to uphold that code of conduct. 40 | 41 | [CoC]: CODE_OF_CONDUCT.md 42 | [team]: https://github.com/rust-embedded/wg#the-tools-team 43 | -------------------------------------------------------------------------------- /svd-parser/src/cpu.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::svd::{Cpu, Endian}; 3 | use crate::types::BoolParse; 4 | 5 | impl Parse for Cpu { 6 | type Object = Self; 7 | type Error = SVDErrorAt; 8 | type Config = Config; 9 | 10 | fn parse(tree: &Node, config: &Config) -> Result { 11 | if !tree.has_tag_name("cpu") { 12 | return Err(SVDError::NotExpectedTag("cpu".to_string()).at(tree.id())); 13 | } 14 | 15 | Cpu::builder() 16 | .name(tree.get_child_text("name")?) 17 | .revision(tree.get_child_text("revision")?) 18 | .endian(Endian::parse(&tree.get_child_elem("endian")?, config)?) 19 | .mpu_present(tree.get_child_bool("mpuPresent")?) 20 | .fpu_present(tree.get_child_bool("fpuPresent")?) 21 | .fpu_double_precision(optional::("fpuDP", tree, &())?) 22 | .dsp_present(optional::("dspPresent", tree, &())?) 23 | .icache_present(optional::("icachePresent", tree, &())?) 24 | .dcache_present(optional::("dcachePresent", tree, &())?) 25 | .itcm_present(optional::("itcmPresent", tree, &())?) 26 | .dtcm_present(optional::("dtcmPresent", tree, &())?) 27 | .vtor_present(optional::("vtorPresent", tree, &())?) 28 | .nvic_priority_bits(tree.get_child_u32("nvicPrioBits")?) 29 | .has_vendor_systick(tree.get_child_bool("vendorSystickConfig")?) 30 | .device_num_interrupts(optional::("deviceNumInterrupts", tree, &())?) 31 | .sau_num_regions(optional::("sauNumRegions", tree, &())?) 32 | .build(config.validate_level) 33 | .map_err(|e| SVDError::from(e).at(tree.id())) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/src/interrupt.rs: -------------------------------------------------------------------------------- 1 | use super::run_test; 2 | use crate::svd::{Interrupt, ValidateLevel}; 3 | 4 | #[test] 5 | fn decode_encode() { 6 | let tests = [( 7 | Interrupt::builder() 8 | .name("test".to_string()) 9 | .description(Some("description".to_string())) 10 | .value(14) 11 | .build(ValidateLevel::Strict) 12 | .unwrap(), 13 | " 14 | 15 | test 16 | description 17 | 14 18 | ", 19 | " 20 | 21 | test 22 | description 23 | 14 24 | ", 25 | )]; 26 | 27 | run_test::(&tests[..], None, None); 28 | 29 | let parse_config = svd_parser::Config::default(); 30 | let mut encode_config = svd_encoder::Config::default(); 31 | encode_config.update("interrupt_name", "Constant"); 32 | 33 | let tests = [( 34 | Interrupt::builder() 35 | .name("test".to_string()) 36 | .description(Some("description".to_string())) 37 | .value(14) 38 | .build(ValidateLevel::Strict) 39 | .unwrap(), 40 | " 41 | 42 | test 43 | description 44 | 14 45 | ", 46 | " 47 | 48 | TEST 49 | description 50 | 14 51 | ", 52 | )]; 53 | 54 | run_test::(&tests[..], Some(parse_config), Some(encode_config)); 55 | } 56 | -------------------------------------------------------------------------------- /generate-tests.sh: -------------------------------------------------------------------------------- 1 | set -e 2 | 3 | elementIn() { 4 | local e 5 | for e in "${@:2}"; do 6 | [[ "$e" == "$1" ]] && return 0 7 | done 8 | return 1 9 | } 10 | 11 | main() { 12 | local tests_dir=$(pwd)/tests 13 | local cmsis_dir=$tests_dir/cmsis_tests 14 | local blacklist=( 15 | # These SVD files have some registers with a `resetValue` bigger than the register itself 16 | Toshiba/M365 17 | Toshiba/M367 18 | Toshiba/M368 19 | Toshiba/M369 20 | Toshiba/M36B 21 | SiliconLabs/SIM3L1x8_SVD 22 | ) 23 | 24 | rm -rf tests/cmsis_tests 25 | mkdir -p tests/cmsis_tests 26 | 27 | local svd_source=cmsis-svd-data 28 | if [ ! -d $svd_source ] 29 | then 30 | git clone https://github.com/cmsis-svd/cmsis-svd-data.git 31 | fi 32 | 33 | local vendor_dir 34 | for vendor_dir in $(echo $svd_source/data/*); do 35 | local vendor=$(basename $vendor_dir) 36 | cat >"$cmsis_dir/$vendor.rs" <>"$cmsis_dir/$vendor.rs" <>"$cmsis_dir/mod.rs" <"$tests_dir/cmsis.rs"< 17 | + Encode 18 | + core::fmt::Debug 19 | + PartialEq, 20 | >( 21 | // tests: [(Object, input string, output string)], 22 | tests: &[(T, &str, &str)], 23 | parser_config: Option, 24 | encoder_config: Option, 25 | ) { 26 | for t in tests { 27 | let rotree = Document::parse(t.1).unwrap(); 28 | let elem = T::parse( 29 | &rotree.root().first_element_child().unwrap(), 30 | &parser_config.unwrap_or_default(), 31 | ) 32 | .unwrap(); 33 | assert_eq!( 34 | elem, t.0, 35 | "Error parsing xml` (mismatch between parsed and expected)" 36 | ); 37 | 38 | let tree1 = Element::parse(t.2.as_bytes()).unwrap(); 39 | let tree2 = elem 40 | .encode_with_config(&encoder_config.unwrap_or_default()) 41 | .unwrap(); 42 | assert_eq!( 43 | tree1, tree2, 44 | "Error encoding xml (mismatch between encoded and expected)" 45 | ); 46 | } 47 | } 48 | 49 | mod access; 50 | mod addressblock; 51 | //mod bitrange; 52 | mod cpu; 53 | mod dimelement; 54 | mod endian; 55 | mod enumeratedvalue; 56 | //mod enumeratedvalues; 57 | mod field; 58 | mod interrupt; 59 | mod modifiedwritevalues; 60 | mod register; 61 | //mod registerproperties; 62 | mod usage; 63 | mod writeconstraint; 64 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: master 4 | pull_request: 5 | merge_group: 6 | 7 | name: Continuous Integration 8 | 9 | jobs: 10 | ci: 11 | name: CI 12 | runs-on: ubuntu-latest 13 | needs: [build, test, test-strict] 14 | if: always() 15 | steps: 16 | - name: Done 17 | run: jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' 18 | 19 | # check if the project builds with MSRV, stable and nighly 20 | build: 21 | name: "Build (${{ matrix.name || matrix.rust }})" 22 | runs-on: ubuntu-latest 23 | continue-on-error: ${{ matrix.experimental || false }} 24 | strategy: 25 | matrix: 26 | rust: [stable] 27 | include: 28 | # Test nightly but don't fail the build. 29 | - rust: nightly 30 | experimental: true 31 | # MSRV 32 | - rust: 1.70.0 33 | name: "MSRV" 34 | steps: 35 | - name: Checkout repository 36 | uses: actions/checkout@v4 37 | - name: Install Rust 38 | uses: dtolnay/rust-toolchain@master 39 | with: 40 | toolchain: ${{ matrix.rust }} 41 | components: clippy 42 | - run: cargo check 43 | env: 44 | RUSTFLAGS: -D warnings 45 | - run: cargo clippy 46 | 47 | test: 48 | runs-on: ubuntu-latest 49 | steps: 50 | - name: Checkout repository 51 | uses: actions/checkout@v4 52 | - name: Install Rust 53 | uses: dtolnay/rust-toolchain@master 54 | with: 55 | toolchain: stable 56 | - run: cargo test 57 | 58 | test-strict: 59 | runs-on: ubuntu-latest 60 | steps: 61 | - name: Checkout repository 62 | uses: actions/checkout@v4 63 | - name: Install Rust 64 | uses: dtolnay/rust-toolchain@master 65 | with: 66 | toolchain: stable 67 | - run: cargo test --all-features 68 | -------------------------------------------------------------------------------- /svd-encoder/src/dimelement.rs: -------------------------------------------------------------------------------- 1 | use crate::config::{change_case, format_number}; 2 | 3 | use super::{new_node, Config, Element, Encode, EncodeError}; 4 | 5 | impl Encode for crate::svd::DimElement { 6 | type Error = EncodeError; 7 | 8 | fn encode_with_config(&self, config: &Config) -> Result { 9 | let mut e = Element::new("dimElement"); 10 | 11 | e.children 12 | .push(new_node("dim", format_number(self.dim, config.dim_dim))); 13 | e.children.push(new_node( 14 | "dimIncrement", 15 | format_number(self.dim_increment, config.dim_increment), 16 | )); 17 | 18 | if let Some(di) = &self.dim_index { 19 | e.children 20 | .push(if let Some(range) = self.indexes_as_range() { 21 | new_node("dimIndex", format!("{}-{}", range.start(), range.end())) 22 | } else { 23 | new_node("dimIndex", di.join(",")) 24 | }); 25 | } 26 | 27 | if let Some(dim_name) = &self.dim_name { 28 | e.children.push(new_node("dimName", dim_name.clone())) 29 | } 30 | 31 | if let Some(v) = &self.dim_array_index { 32 | e.children.push(v.encode_node_with_config(config)?); 33 | } 34 | 35 | Ok(e) 36 | } 37 | } 38 | 39 | impl Encode for crate::svd::DimArrayIndex { 40 | type Error = EncodeError; 41 | 42 | fn encode_with_config(&self, config: &Config) -> Result { 43 | let mut base = Element::new("dimArrayIndex"); 44 | 45 | if let Some(d) = &self.header_enum_name { 46 | base.children.push(new_node( 47 | "headerEnumName", 48 | change_case(d, config.dim_array_index_header_enum_name), 49 | )); 50 | } 51 | 52 | for v in &self.values { 53 | base.children.push(v.encode_node_with_config(config)?); 54 | } 55 | 56 | Ok(base) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /svd-rs/src/writeconstraint.rs: -------------------------------------------------------------------------------- 1 | use super::SvdError; 2 | 3 | /// Define constraints for writing values to a field 4 | #[cfg_attr( 5 | feature = "serde", 6 | derive(serde::Deserialize, serde::Serialize), 7 | serde(rename_all = "camelCase") 8 | )] 9 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 10 | pub enum WriteConstraint { 11 | /// If `true`, only the last read value can be written. 12 | WriteAsRead(bool), 13 | /// If `true`, only the values listed in the enumeratedValues list can be written. 14 | UseEnumeratedValues(bool), 15 | /// A range of numbers that can be written. 16 | Range(WriteConstraintRange), 17 | } 18 | 19 | /// The smallest and largest number that can be written. 20 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 21 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 22 | pub struct WriteConstraintRange { 23 | /// Specify the smallest number to be written to the field 24 | #[cfg_attr(feature = "serde", serde(rename = "minimum"))] 25 | pub min: u64, 26 | /// Specify the largest number to be written to the field. 27 | #[cfg_attr(feature = "serde", serde(rename = "maximum"))] 28 | pub max: u64, 29 | } 30 | 31 | /// Errors for [`WriteConstraintRange::check_range`] 32 | #[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)] 33 | pub enum Error { 34 | /// The value is not in range. 35 | #[error("Value {0} out of range {1:?}")] 36 | OutOfRange(u64, core::ops::Range), 37 | /// Minimum is greater than maximum. 38 | #[error("Range minimum {0} is greater than maximum {1}")] 39 | ReversedRange(u64, u64), 40 | } 41 | 42 | impl WriteConstraintRange { 43 | pub(crate) fn check_range(&self, range: core::ops::Range) -> Result<(), SvdError> { 44 | if self.min > self.max { 45 | return Err(Error::ReversedRange(self.min, self.max).into()); 46 | } 47 | for v in [&self.min, &self.max] { 48 | if !range.contains(v) { 49 | return Err(Error::OutOfRange(*v, range).into()); 50 | } 51 | } 52 | Ok(()) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /svd-parser/src/dimelement.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::svd::{DimArrayIndex, DimElement, EnumeratedValue}; 3 | 4 | impl Parse for DimElement { 5 | type Object = Self; 6 | type Error = SVDErrorAt; 7 | type Config = Config; 8 | 9 | fn parse(tree: &Node, config: &Self::Config) -> Result { 10 | DimElement::builder() 11 | .dim(tree.get_child_u32("dim")?) 12 | .dim_increment(tree.get_child_u32("dimIncrement")?) 13 | .dim_index(optional::("dimIndex", tree, config)?) 14 | .dim_name(tree.get_child_text_opt("dimName")?) 15 | .dim_array_index(optional::("dimArrayIndex", tree, config)?) 16 | .build(config.validate_level) 17 | .map_err(|e| SVDError::from(e).at(tree.id())) 18 | } 19 | } 20 | 21 | impl Parse for DimArrayIndex { 22 | type Object = Self; 23 | type Error = SVDErrorAt; 24 | type Config = Config; 25 | 26 | fn parse(tree: &Node, config: &Self::Config) -> Result { 27 | Ok(Self { 28 | header_enum_name: tree.get_child_text_opt("headerEnumName")?, 29 | values: { 30 | let values: Result, _> = tree 31 | .children() 32 | .filter(|t| t.is_element() && !matches!(t.tag_name().name(), "headerEnumName")) 33 | .map(|t| { 34 | if t.has_tag_name("enumeratedValue") { 35 | EnumeratedValue::parse(&t, config) 36 | } else { 37 | Err(SVDError::NotExpectedTag("enumeratedValue".to_string()).at(t.id())) 38 | } 39 | }) 40 | .collect(); 41 | values? 42 | }, 43 | }) 44 | } 45 | } 46 | 47 | struct DimIndex; 48 | 49 | impl Parse for DimIndex { 50 | type Object = Vec; 51 | type Error = SVDErrorAt; 52 | type Config = Config; 53 | 54 | fn parse(tree: &Node, _config: &Self::Config) -> Result, Self::Error> { 55 | DimElement::parse_indexes(tree.get_text()?) 56 | .ok_or_else(|| SVDError::DimIndexParse.at(tree.id())) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /svd-parser/src/field.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::svd::{ 3 | Access, BitRange, EnumeratedValues, Field, FieldInfo, ModifiedWriteValues, ReadAction, 4 | WriteConstraint, 5 | }; 6 | 7 | impl Parse for Field { 8 | type Object = Self; 9 | type Error = SVDErrorAt; 10 | type Config = Config; 11 | 12 | fn parse(tree: &Node, config: &Self::Config) -> Result { 13 | parse_array("field", tree, config) 14 | } 15 | } 16 | 17 | impl Parse for FieldInfo { 18 | type Object = Self; 19 | type Error = SVDErrorAt; 20 | type Config = Config; 21 | 22 | fn parse(tree: &Node, config: &Self::Config) -> Result { 23 | if !tree.has_tag_name("field") { 24 | return Err(SVDError::NotExpectedTag("field".to_string()).at(tree.id())); 25 | } 26 | 27 | let bit_range = BitRange::parse(tree, config)?; 28 | FieldInfo::builder() 29 | .name(tree.get_child_text("name")?) 30 | .description(tree.get_child_text_opt("description")?) 31 | .bit_range(bit_range) 32 | .access(optional::("access", tree, config)?) 33 | .modified_write_values(optional::( 34 | "modifiedWriteValues", 35 | tree, 36 | config, 37 | )?) 38 | .write_constraint(if !config.ignore_enums { 39 | optional::("writeConstraint", tree, config)? 40 | } else { 41 | None 42 | }) 43 | .read_action(optional::("readAction", tree, config)?) 44 | .enumerated_values(if !config.ignore_enums { 45 | let values: Result, _> = tree 46 | .children() 47 | .filter(|t| t.is_element() && t.has_tag_name("enumeratedValues")) 48 | .map(|t| EnumeratedValues::parse(&t, config)) 49 | .collect(); 50 | values? 51 | } else { 52 | Vec::new() 53 | }) 54 | .derived_from(tree.attribute("derivedFrom").map(|s| s.to_owned())) 55 | .build(config.validate_level) 56 | .map_err(|e| SVDError::from(e).at(tree.id())) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /svd-encoder/src/cpu.rs: -------------------------------------------------------------------------------- 1 | use super::{new_node, Config, Element, Encode, EncodeError}; 2 | 3 | use crate::svd::Cpu; 4 | impl Encode for Cpu { 5 | type Error = EncodeError; 6 | 7 | fn encode_with_config(&self, _config: &Config) -> Result { 8 | let mut children = vec![ 9 | new_node("name", self.name.clone()), 10 | new_node("revision", self.revision.clone()), 11 | self.endian.encode_node()?, 12 | new_node("mpuPresent", format!("{}", self.mpu_present)), 13 | new_node("fpuPresent", format!("{}", self.fpu_present)), 14 | ]; 15 | if let Some(v) = &self.fpu_double_precision { 16 | children.push(new_node("fpuDP", format!("{}", v))); 17 | } 18 | if let Some(v) = &self.dsp_present { 19 | children.push(new_node("dspPresent", format!("{}", v))); 20 | } 21 | if let Some(v) = &self.icache_present { 22 | children.push(new_node("icachePresent", format!("{}", v))); 23 | } 24 | if let Some(v) = &self.dcache_present { 25 | children.push(new_node("dcachePresent", format!("{}", v))); 26 | } 27 | if let Some(v) = &self.itcm_present { 28 | children.push(new_node("itcmPresent", format!("{}", v))); 29 | } 30 | if let Some(v) = &self.dtcm_present { 31 | children.push(new_node("dtcmPresent", format!("{}", v))); 32 | } 33 | if let Some(v) = &self.vtor_present { 34 | children.push(new_node("vtorPresent", format!("{}", v))); 35 | } 36 | children.push(new_node( 37 | "nvicPrioBits", 38 | format!("{}", self.nvic_priority_bits), 39 | )); 40 | children.push(new_node( 41 | "vendorSystickConfig", 42 | format!("{}", self.has_vendor_systick), 43 | )); 44 | 45 | if let Some(v) = &self.device_num_interrupts { 46 | children.push(new_node("deviceNumInterrupts", format!("{}", v))); 47 | } 48 | if let Some(v) = &self.sau_num_regions { 49 | children.push(new_node("sauNumRegions", format!("{}", v))); 50 | } 51 | 52 | let mut elem = Element::new("cpu"); 53 | elem.children = children; 54 | Ok(elem) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /tests/src/enumeratedvalue.rs: -------------------------------------------------------------------------------- 1 | use super::run_test; 2 | use crate::svd::{EnumeratedValue, ValidateLevel}; 3 | 4 | #[test] 5 | fn decode_encode() { 6 | let tests = [( 7 | EnumeratedValue::builder() 8 | .name("WS0".to_string()) 9 | .description(Some( 10 | "Zero wait-states inserted in fetch or read transfers".to_string(), 11 | )) 12 | .value(Some(0)) 13 | .build(ValidateLevel::Strict) 14 | .unwrap(), 15 | " 16 | 17 | WS0 18 | Zero wait-states inserted in fetch or read transfers 19 | 0 20 | 21 | ", 22 | " 23 | 24 | WS0 25 | Zero wait-states inserted in fetch or read transfers 26 | 0 27 | 28 | ", 29 | )]; 30 | 31 | run_test::(&tests[..], None, None); 32 | 33 | let parse_config = svd_parser::Config::default(); 34 | let mut encode_config = svd_encoder::Config::default(); 35 | encode_config.update("enumerated_value_name", "Pascal"); 36 | encode_config.update("enumerated_value_value", "Bin"); 37 | 38 | let tests = [( 39 | EnumeratedValue::builder() 40 | .name("WS0".to_string()) 41 | .description(Some( 42 | "Zero wait-states inserted in fetch or read transfers".to_string(), 43 | )) 44 | .value(Some(0)) 45 | .build(ValidateLevel::Strict) 46 | .unwrap(), 47 | " 48 | 49 | WS0 50 | Zero wait-states inserted in fetch or read transfers 51 | 0 52 | 53 | ", 54 | " 55 | 56 | Ws0 57 | Zero wait-states inserted in fetch or read transfers 58 | 0b0 59 | 60 | ", 61 | )]; 62 | 63 | run_test::(&tests[..], Some(parse_config), Some(encode_config)); 64 | } 65 | -------------------------------------------------------------------------------- /svd-parser/src/register.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::svd::{ 3 | DataType, Field, ModifiedWriteValues, ReadAction, Register, RegisterInfo, RegisterProperties, 4 | WriteConstraint, 5 | }; 6 | 7 | impl Parse for Register { 8 | type Object = Self; 9 | type Error = SVDErrorAt; 10 | type Config = Config; 11 | 12 | fn parse(tree: &Node, config: &Self::Config) -> Result { 13 | parse_array("register", tree, config) 14 | } 15 | } 16 | 17 | impl Parse for RegisterInfo { 18 | type Object = Self; 19 | type Error = SVDErrorAt; 20 | type Config = Config; 21 | 22 | fn parse(tree: &Node, config: &Self::Config) -> Result { 23 | RegisterInfo::builder() 24 | .name(tree.get_child_text("name")?) 25 | .display_name(tree.get_child_text_opt("displayName")?) 26 | .description(tree.get_child_text_opt("description")?) 27 | .alternate_group(tree.get_child_text_opt("alternateGroup")?) 28 | .alternate_register(tree.get_child_text_opt("alternateRegister")?) 29 | .address_offset(tree.get_child_u32("addressOffset")?) 30 | .properties(RegisterProperties::parse(tree, config)?) 31 | .datatype(optional::("dataType", tree, config)?) 32 | .modified_write_values(optional::( 33 | "modifiedWriteValues", 34 | tree, 35 | config, 36 | )?) 37 | .write_constraint(optional::( 38 | "writeConstraint", 39 | tree, 40 | config, 41 | )?) 42 | .read_action(optional::("readAction", tree, config)?) 43 | .fields({ 44 | if let Some(fields) = tree.get_child("fields") { 45 | let fs: Result, _> = fields 46 | .children() 47 | .filter(Node::is_element) 48 | .map(|t| Field::parse(&t, config)) 49 | .collect(); 50 | Some(fs?) 51 | } else { 52 | None 53 | } 54 | }) 55 | .derived_from(tree.attribute("derivedFrom").map(|s| s.to_owned())) 56 | .build(config.validate_level) 57 | .map_err(|e| SVDError::from(e).at(tree.id())) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /svd-encoder/src/cluster.rs: -------------------------------------------------------------------------------- 1 | use super::{ 2 | new_node, Config, Element, ElementMerge, Encode, EncodeChildren, EncodeError, XMLNode, 3 | }; 4 | 5 | use crate::{ 6 | config::{change_case, format_number}, 7 | svd::{Cluster, ClusterInfo}, 8 | }; 9 | 10 | impl Encode for Cluster { 11 | type Error = EncodeError; 12 | 13 | fn encode_with_config(&self, config: &Config) -> Result { 14 | match self { 15 | Self::Single(i) => i.encode_with_config(config), 16 | Self::Array(i, a) => { 17 | let mut e = Element::new("cluster"); 18 | e.merge(&a.encode_with_config(config)?); 19 | e.merge(&i.encode_with_config(config)?); 20 | Ok(e) 21 | } 22 | } 23 | } 24 | } 25 | 26 | impl Encode for ClusterInfo { 27 | type Error = EncodeError; 28 | 29 | fn encode_with_config(&self, config: &Config) -> Result { 30 | let mut e = Element::new("cluster"); 31 | 32 | e.children.push(new_node( 33 | "name", 34 | change_case(&self.name, config.cluster_name), 35 | )); 36 | 37 | if let Some(v) = &self.description { 38 | e.children.push(new_node("description", v.clone())); 39 | } 40 | 41 | if let Some(v) = &self.alternate_cluster { 42 | e.children.push(new_node( 43 | "alternateCluster", 44 | change_case(v, config.cluster_name), 45 | )); 46 | } 47 | 48 | if let Some(v) = &self.header_struct_name { 49 | e.children.push(new_node("headerStructName", v.clone())); 50 | } 51 | 52 | e.children.push(new_node( 53 | "addressOffset", 54 | format_number(self.address_offset, config.cluster_address_offset), 55 | )); 56 | 57 | e.children.extend( 58 | self.default_register_properties 59 | .encode_with_config(config)?, 60 | ); 61 | 62 | for c in &self.children { 63 | e.children 64 | .push(XMLNode::Element(c.encode_with_config(config)?)); 65 | } 66 | 67 | if let Some(v) = &self.derived_from { 68 | e.attributes.insert( 69 | String::from("derivedFrom"), 70 | change_case(v, config.cluster_name), 71 | ); 72 | } 73 | 74 | Ok(e) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /svd-encoder/src/field.rs: -------------------------------------------------------------------------------- 1 | use super::{new_node, Config, Element, ElementMerge, Encode, EncodeError, XMLNode}; 2 | use crate::bitrange::encode_bitrange; 3 | 4 | use crate::config::change_case; 5 | use crate::svd::{Field, FieldInfo}; 6 | 7 | impl Encode for Field { 8 | type Error = EncodeError; 9 | 10 | fn encode_with_config(&self, config: &Config) -> Result { 11 | match self { 12 | Self::Single(info) => info.encode_with_config(config), 13 | Self::Array(info, array_info) => { 14 | let mut base = Element::new("field"); 15 | base.merge(&array_info.encode_with_config(config)?); 16 | base.merge(&info.encode_with_config(config)?); 17 | Ok(base) 18 | } 19 | } 20 | } 21 | } 22 | 23 | impl Encode for FieldInfo { 24 | type Error = EncodeError; 25 | 26 | fn encode_with_config(&self, config: &Config) -> Result { 27 | let mut elem = Element::new("field"); 28 | elem.children 29 | .push(new_node("name", change_case(&self.name, config.field_name))); 30 | 31 | if let Some(description) = &self.description { 32 | elem.children 33 | .push(new_node("description", description.clone())) 34 | } 35 | 36 | // Add bit range 37 | elem.children 38 | .append(&mut encode_bitrange(&self.bit_range, config)?); 39 | 40 | if let Some(v) = &self.access { 41 | elem.children.push(v.encode_node()?); 42 | } 43 | 44 | if let Some(v) = &self.modified_write_values { 45 | elem.children.push(v.encode_node()?); 46 | } 47 | 48 | if let Some(v) = &self.write_constraint { 49 | elem.children.push(v.encode_node()?); 50 | } 51 | 52 | if let Some(v) = &self.read_action { 53 | elem.children.push(v.encode_node()?); 54 | } 55 | 56 | let enumerated_values: Result, EncodeError> = self 57 | .enumerated_values 58 | .iter() 59 | .map(|v| v.encode_node_with_config(config)) 60 | .collect(); 61 | elem.children.append(&mut enumerated_values?); 62 | 63 | if let Some(v) = &self.derived_from { 64 | elem.attributes.insert( 65 | String::from("derivedFrom"), 66 | change_case(v, config.field_name), 67 | ); 68 | } 69 | 70 | Ok(elem) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /svd-rs/src/access.rs: -------------------------------------------------------------------------------- 1 | /// Defines access rights for fields on the device, though it may be specified at a 2 | /// higher level than individual fields. 3 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 4 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 5 | pub enum Access { 6 | /// Read access is permitted. Write operations have an undefined effect. 7 | #[cfg_attr(feature = "serde", serde(rename = "read-only"))] 8 | ReadOnly, 9 | 10 | /// Read and write accesses are permitted. 11 | #[cfg_attr(feature = "serde", serde(rename = "read-write"))] 12 | ReadWrite, 13 | 14 | /// Read access is always permitted. 15 | /// Only the first write after a reset will affect the content. 16 | /// Following writes have an undefined effect. 17 | #[cfg_attr(feature = "serde", serde(rename = "read-writeOnce"))] 18 | ReadWriteOnce, 19 | 20 | /// Read operations have undefined results. 21 | /// Only the first write after a reset will affect the content. 22 | #[cfg_attr(feature = "serde", serde(rename = "writeOnce"))] 23 | WriteOnce, 24 | 25 | /// Read operations have an undefined result. Write access is permitted. 26 | #[cfg_attr(feature = "serde", serde(rename = "write-only"))] 27 | WriteOnly, 28 | } 29 | 30 | impl Access { 31 | /// Whether the register/field is readable at least once. 32 | pub fn can_read(self) -> bool { 33 | matches!(self, Self::ReadOnly | Self::ReadWrite | Self::ReadWriteOnce) 34 | } 35 | 36 | /// Whether the register/field is writable at least once. 37 | pub fn can_write(self) -> bool { 38 | !matches!(self, Self::ReadOnly) 39 | } 40 | } 41 | 42 | impl Default for Access { 43 | fn default() -> Self { 44 | Self::ReadWrite 45 | } 46 | } 47 | 48 | impl Access { 49 | /// Parse a string into an [`Access`] value, returning [`Option::None`] if the string is not valid. 50 | pub fn parse_str(s: &str) -> Option { 51 | match s { 52 | "read-only" => Some(Self::ReadOnly), 53 | "read-write" => Some(Self::ReadWrite), 54 | "read-writeOnce" => Some(Self::ReadWriteOnce), 55 | "write-only" => Some(Self::WriteOnly), 56 | "writeOnce" => Some(Self::WriteOnce), 57 | _ => None, 58 | } 59 | } 60 | 61 | /// Convert this [`Access`] into a static string. 62 | pub const fn as_str(self) -> &'static str { 63 | match self { 64 | Self::ReadOnly => "read-only", 65 | Self::ReadWrite => "read-write", 66 | Self::ReadWriteOnce => "read-writeOnce", 67 | Self::WriteOnly => "write-only", 68 | Self::WriteOnce => "writeOnce", 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /svd-rs/src/modifiedwritevalues.rs: -------------------------------------------------------------------------------- 1 | /// Describe the manipulation of data written to a register/field. 2 | /// If not specified, the value written to the field is the value stored in the field 3 | #[cfg_attr( 4 | feature = "serde", 5 | derive(serde::Deserialize, serde::Serialize), 6 | serde(rename_all = "camelCase") 7 | )] 8 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 9 | pub enum ModifiedWriteValues { 10 | /// Write data bit of one shall clear (set to zero) the corresponding bit in the field 11 | OneToClear, 12 | 13 | /// Write data bit of one shall set (set to one) the corresponding bit in the field 14 | OneToSet, 15 | 16 | /// Write data bit of one shall toggle (invert) the corresponding bit in the field 17 | OneToToggle, 18 | 19 | /// Write data bit of zero shall clear (set to zero) the corresponding bit in the field 20 | ZeroToClear, 21 | 22 | /// Write data bit of zero shall set (set to one) the corresponding bit in the field 23 | ZeroToSet, 24 | 25 | /// Write data bit of zero shall toggle (invert) the corresponding bit in the field 26 | ZeroToToggle, 27 | 28 | /// After a write operation all bits in the field are cleared (set to zero) 29 | Clear, 30 | 31 | /// After a write operation all bits in the field are set (set to one) 32 | Set, 33 | 34 | /// After a write operation all bit in the field may be modified (default) 35 | Modify, 36 | } 37 | 38 | impl Default for ModifiedWriteValues { 39 | fn default() -> Self { 40 | Self::Modify 41 | } 42 | } 43 | 44 | impl ModifiedWriteValues { 45 | /// Parse a string into an [`ModifiedWriteValues`] value, returning [`Option::None`] if the string is not valid. 46 | pub fn parse_str(s: &str) -> Option { 47 | use self::ModifiedWriteValues::*; 48 | match s { 49 | "oneToClear" => Some(OneToClear), 50 | "oneToSet" => Some(OneToSet), 51 | "oneToToggle" => Some(OneToToggle), 52 | "zeroToClear" => Some(ZeroToClear), 53 | "zeroToSet" => Some(ZeroToSet), 54 | "zeroToToggle" => Some(ZeroToToggle), 55 | "clear" => Some(Clear), 56 | "set" => Some(Set), 57 | "modify" => Some(Modify), 58 | _ => None, 59 | } 60 | } 61 | 62 | /// Convert this [`ModifiedWriteValues`] into a static string. 63 | pub const fn as_str(self) -> &'static str { 64 | match self { 65 | Self::OneToClear => "oneToClear", 66 | Self::OneToSet => "oneToSet", 67 | Self::OneToToggle => "oneToToggle", 68 | Self::ZeroToClear => "zeroToClear", 69 | Self::ZeroToSet => "zeroToSet", 70 | Self::ZeroToToggle => "zeroToToggle", 71 | Self::Clear => "clear", 72 | Self::Set => "set", 73 | Self::Modify => "modify", 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /svd-encoder/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 6 | and this project adheres to [Semantic Versioning](http://semver.org/). 7 | 8 | ## [Unreleased] 9 | 10 | ## [v0.14.7] - 2025-03-11 11 | 12 | - Bump MSRV to 1.70.0 13 | - Bump `xmltree` to "0.11" 14 | 15 | ## [v0.14.6] - 2025-02-08 16 | 17 | - Revert the `riscv` element, as well as the `unstable-riscv` feature. 18 | 19 | ## [v0.14.5] - 2024-08-20 20 | 21 | - Adapt the `riscv` element to handle `riscv::Exception`. 22 | - Add `riscv` element for configuration parameters related to RISC-V targets. 23 | You must use the `unstable-riscv` feature to enable this exeperimental element. 24 | - Bump MSRV to 1.65.0 25 | 26 | ## [v0.14.4] - 2023-11-15 27 | 28 | - Bump `svd-rs` to 0.14.4 29 | 30 | ## [v0.14.3] - 2023-04-04 31 | 32 | - Add `Sorting` options to `Config` 33 | 34 | ## [v0.14.2] - 2022-12-19 35 | 36 | - Fix typo in `headerDefinitionsPrefix` 37 | - Update `convert_case` dependency 38 | 39 | ## [v0.14.1] - 2022-07-23 40 | 41 | - Fix uppercased names of arrays 42 | 43 | ## [v0.14.0] - 2022-07-19 44 | 45 | - Bump MSRV to 1.56.0 (2021) 46 | - Add advanced encoder options `Config` 47 | - BREAKING: `Encode` trait needs to implement `encode_with_config` 48 | 49 | ## [v0.13.1] - 2022-02-12 50 | 51 | - Encode "dimIndex" ranges 52 | 53 | ## [v0.13.0] - 2022-01-04 54 | 55 | - Bump `svd-rs` 56 | 57 | ## [v0.12.0] - 2021-11-11 58 | 59 | - Bump `svd-rs` 60 | - Add `protection` encoding 61 | - Add `readAction` encoding 62 | - Add array support for peripherals 63 | 64 | ## [v0.11.0] - 2021-10-02 65 | - Splitted from `svd-parser` 66 | 67 | Previous versions in common [changelog](../CHANGELOG.md). 68 | 69 | [Unreleased]: https://github.com/rust-embedded/svd/compare/svd-rs-v0.14.12...HEAD 70 | [v0.14.7]: https://github.com/rust-embedded/svd/compare/svd-rs-v0.14.11...svd-rs-v0.14.12 71 | [v0.14.6]: https://github.com/rust-embedded/svd/compare/svd-rs-v0.14.9...svd-rs-v0.14.10 72 | [v0.14.5]: https://github.com/rust-embedded/svd/compare/svd-rs-v0.14.8...svd-rs-v0.14.9 73 | [v0.14.4]: https://github.com/rust-embedded/svd/compare/svd-rs-v0.14.3...svd-rs-v0.14.4 74 | [v0.14.3]: https://github.com/rust-embedded/svd/compare/svd-encoder-v0.14.2..svd-rs-v0.14.2 75 | [v0.14.2]: https://github.com/rust-embedded/svd/compare/svd-encoder-v0.14.1..svd-encoder-v0.14.2 76 | [v0.14.1]: https://github.com/rust-embedded/svd/compare/v0.14.0..svd-encoder-v0.14.1 77 | [v0.14.0]: https://github.com/rust-embedded/svd/compare/svd-rs-v0.13.1..v0.14.0 78 | [v0.13.1]: https://github.com/rust-embedded/svd/compare/svd-parser-v0.13.1...svd-rs-v0.13.1 79 | [v0.13.0]: https://github.com/rust-embedded/svd/compare/v0.12.0...v0.13.0 80 | [v0.12.0]: https://github.com/rust-embedded/svd/compare/v0.11.0...v0.12.0 81 | [v0.11.0]: https://github.com/rust-embedded/svd/compare/v0.10.2...v0.11.0 82 | -------------------------------------------------------------------------------- /svd-rs/src/datatype.rs: -------------------------------------------------------------------------------- 1 | /// Register data type 2 | #[cfg_attr( 3 | feature = "serde", 4 | derive(serde::Deserialize, serde::Serialize), 5 | serde(rename_all = "snake_case") 6 | )] 7 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 8 | pub enum DataType { 9 | /// unsigned byte 10 | U8, 11 | /// unsigned half word 12 | U16, 13 | /// unsigned word 14 | U32, 15 | /// unsigned double word 16 | U64, 17 | /// signed byte 18 | I8, 19 | /// signed half word 20 | I16, 21 | /// signed world 22 | I32, 23 | /// signed double word 24 | I64, 25 | /// pointer to unsigned byte 26 | U8Ptr, 27 | /// pointer to unsigned half word 28 | U16Ptr, 29 | /// pointer to unsigned word 30 | U32Ptr, 31 | /// pointer to unsigned double word 32 | U64Ptr, 33 | /// pointer to signed byte 34 | I8Ptr, 35 | /// pointer to signed half word 36 | I16Ptr, 37 | /// pointer to signed world 38 | I32Ptr, 39 | /// pointer to signed double word 40 | I64Ptr, 41 | } 42 | 43 | impl DataType { 44 | /// Parse a string into an [`DataType`] value, returning [`Option::None`] if the string is not valid. 45 | pub fn parse_str(s: &str) -> Option { 46 | match s { 47 | "uint8_t" => Some(Self::U8), 48 | "uint16_t" => Some(Self::U16), 49 | "uint32_t" => Some(Self::U32), 50 | "uint64_t" => Some(Self::U64), 51 | "int8_t" => Some(Self::I8), 52 | "int16_t" => Some(Self::I16), 53 | "int32_t" => Some(Self::I32), 54 | "int64_t" => Some(Self::I64), 55 | "uint8_t *" => Some(Self::U8Ptr), 56 | "uint16_t *" => Some(Self::U16Ptr), 57 | "uint32_t *" => Some(Self::U32Ptr), 58 | "uint64_t *" => Some(Self::U64Ptr), 59 | "int8_t *" => Some(Self::I8Ptr), 60 | "int16_t *" => Some(Self::I16Ptr), 61 | "int32_t *" => Some(Self::I32Ptr), 62 | "int64_t *" => Some(Self::I64Ptr), 63 | _ => None, 64 | } 65 | } 66 | 67 | /// Convert this [`DataType`] into a static string. 68 | pub const fn as_str(self) -> &'static str { 69 | match self { 70 | Self::U8 => "uint8_t", 71 | Self::U16 => "uint16_t", 72 | Self::U32 => "uint32_t", 73 | Self::U64 => "uint64_t", 74 | Self::I8 => "int8_t", 75 | Self::I16 => "int16_t", 76 | Self::I32 => "int32_t", 77 | Self::I64 => "int64_t", 78 | Self::U8Ptr => "uint8_t *", 79 | Self::U16Ptr => "uint16_t *", 80 | Self::U32Ptr => "uint32_t *", 81 | Self::U64Ptr => "uint64_t *", 82 | Self::I8Ptr => "int8_t *", 83 | Self::I16Ptr => "int16_t *", 84 | Self::I32Ptr => "int32_t *", 85 | Self::I64Ptr => "int64_t *", 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /svd-parser/src/device.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::svd::{cpu::Cpu, peripheral::Peripheral, registerproperties::RegisterProperties}; 3 | 4 | /// Parses a SVD file 5 | impl Parse for Device { 6 | type Object = Self; 7 | type Error = SVDErrorAt; 8 | type Config = Config; 9 | 10 | fn parse(tree: &Node, config: &Self::Config) -> Result { 11 | if !tree.has_tag_name("device") { 12 | return Err(SVDError::NotExpectedTag("device".to_string()).at(tree.id())); 13 | } 14 | 15 | let mut device = Device::builder() 16 | .vendor(tree.get_child_text_opt("vendor")?) 17 | .vendor_id(tree.get_child_text_opt("vendorID")?) 18 | .name(tree.get_child_text("name")?) 19 | .series(tree.get_child_text_opt("series")?) 20 | .license_text(tree.get_child_text_opt("licenseText")?) 21 | .cpu(optional::("cpu", tree, config)?) 22 | .header_system_filename(tree.get_child_text_opt("headerSystemFilename")?) 23 | .header_definitions_prefix(tree.get_child_text_opt("headerDefinitionsPrefix")?) 24 | .default_register_properties(RegisterProperties::parse(tree, config)?) 25 | .peripherals({ 26 | let ps: Result, _> = tree 27 | .get_child_elem("peripherals")? 28 | .children() 29 | .filter(Node::is_element) 30 | .map(|t| Peripheral::parse(&t, config)) 31 | .collect(); 32 | ps? 33 | }); 34 | if let Some(version) = tree.get_child_text_opt("version")? { 35 | device = device.version(version) 36 | } 37 | if let Some(description) = tree.get_child_text_opt("description")? { 38 | device = device.description(description) 39 | } 40 | if let Some(bits) = optional::("addressUnitBits", tree, &())? { 41 | device = device.address_unit_bits(bits) 42 | } 43 | if let Some(width) = optional::("width", tree, &())? { 44 | device = device.width(width) 45 | } 46 | // TODO: accept namespace other than `xs` 47 | // Now assert `xs` exists and `noNamespaceSchemaLocation` is under `xs` 48 | if let Some(xmlns_xs) = tree.lookup_namespace_uri(Some("xs")) { 49 | device = device.xmlns_xs(xmlns_xs.to_string()); 50 | if let Some(location) = tree.attribute((xmlns_xs, "noNamespaceSchemaLocation")) { 51 | device = device.no_namespace_schema_location(location.to_string()); 52 | } 53 | } 54 | if let Some(schema_version) = tree.attribute("schemaVersion") { 55 | device = device.schema_version(schema_version.to_string()); 56 | } 57 | device 58 | .build(config.validate_level) 59 | .map_err(|e| SVDError::from(e).at(tree.id())) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /svd-parser/src/types.rs: -------------------------------------------------------------------------------- 1 | //! Shared primitive types for use in SVD objects. 2 | #![allow(clippy::manual_strip)] 3 | 4 | use roxmltree::Node; 5 | 6 | use super::{ElementExt, Parse, SVDError, SVDErrorAt}; 7 | 8 | impl Parse for u32 { 9 | type Object = u32; 10 | type Error = SVDErrorAt; 11 | type Config = (); 12 | 13 | fn parse(tree: &Node, _config: &Self::Config) -> Result { 14 | let text = tree.get_text()?; 15 | 16 | (if text.starts_with("0x") || text.starts_with("0X") { 17 | u32::from_str_radix(&text["0x".len()..], 16) 18 | } else if text.starts_with('#') { 19 | // Handle strings in the binary form of: 20 | // #01101x1 21 | // along with don't care character x (replaced with 0) 22 | u32::from_str_radix( 23 | &str::replace(&text.to_lowercase()["#".len()..], "x", "0"), 24 | 2, 25 | ) 26 | } else if text.starts_with("0b") { 27 | // Handle strings in the binary form of: 28 | // 0b01101x1 29 | // along with don't care character x (replaced with 0) 30 | u32::from_str_radix(&str::replace(&text["0b".len()..], "x", "0"), 2) 31 | } else { 32 | text.parse::() 33 | }) 34 | .map_err(|e| SVDError::from(e).at(tree.id())) 35 | } 36 | } 37 | 38 | impl Parse for u64 { 39 | type Object = u64; 40 | type Error = SVDErrorAt; 41 | type Config = (); 42 | 43 | fn parse(tree: &Node, _config: &Self::Config) -> Result { 44 | let text = tree.get_text()?; 45 | 46 | (if text.starts_with("0x") || text.starts_with("0X") { 47 | u64::from_str_radix(&text["0x".len()..], 16) 48 | } else if text.starts_with('#') { 49 | // Handle strings in the binary form of: 50 | // #01101x1 51 | // along with don't care character x (replaced with 0) 52 | u64::from_str_radix( 53 | &str::replace(&text.to_lowercase()["#".len()..], "x", "0"), 54 | 2, 55 | ) 56 | } else if text.starts_with("0b") { 57 | // Handle strings in the binary form of: 58 | // 0b01101x1 59 | // along with don't care character x (replaced with 0) 60 | u64::from_str_radix(&str::replace(&text["0b".len()..], "x", "0"), 2) 61 | } else { 62 | text.parse::() 63 | }) 64 | .map_err(|e| SVDError::from(e).at(tree.id())) 65 | } 66 | } 67 | 68 | pub struct BoolParse; 69 | 70 | impl Parse for BoolParse { 71 | type Object = bool; 72 | type Error = SVDErrorAt; 73 | type Config = (); 74 | 75 | fn parse(tree: &Node, _config: &Self::Config) -> Result { 76 | let text = tree.get_text()?; 77 | match text { 78 | "0" => Ok(false), 79 | "1" => Ok(true), 80 | _ => match text.parse() { 81 | Ok(b) => Ok(b), 82 | Err(e) => Err(SVDError::InvalidBooleanValue(text.into(), e).at(tree.id())), 83 | }, 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /svd-parser/src/peripheral.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::svd::{ 3 | AddressBlock, Interrupt, Peripheral, PeripheralInfo, RegisterCluster, RegisterProperties, 4 | }; 5 | 6 | impl Parse for Peripheral { 7 | type Object = Self; 8 | type Error = SVDErrorAt; 9 | type Config = Config; 10 | 11 | fn parse(tree: &Node, config: &Self::Config) -> Result { 12 | parse_array("peripheral", tree, config) 13 | } 14 | } 15 | 16 | impl Parse for PeripheralInfo { 17 | type Object = Self; 18 | type Error = SVDErrorAt; 19 | type Config = Config; 20 | 21 | fn parse(tree: &Node, config: &Self::Config) -> Result { 22 | if !tree.has_tag_name("peripheral") { 23 | return Err(SVDError::NotExpectedTag("peripheral".to_string()).at(tree.id())); 24 | } 25 | 26 | PeripheralInfo::builder() 27 | .name(tree.get_child_text("name")?) 28 | .display_name(tree.get_child_text_opt("displayName")?) 29 | .version(tree.get_child_text_opt("version")?) 30 | .description(tree.get_child_text_opt("description")?) 31 | .alternate_peripheral(tree.get_child_text_opt("alternatePeripheral")?) 32 | .group_name(tree.get_child_text_opt("groupName")?) 33 | .prepend_to_name(tree.get_child_text_opt("prependToName")?) 34 | .append_to_name(tree.get_child_text_opt("appendToName")?) 35 | .header_struct_name(tree.get_child_text_opt("headerStructName")?) 36 | .base_address(tree.get_child_u64("baseAddress")?) 37 | .default_register_properties(RegisterProperties::parse(tree, config)?) 38 | .address_block({ 39 | let ab: Result, _> = tree 40 | .children() 41 | .filter(|t| t.is_element() && t.has_tag_name("addressBlock")) 42 | .map(|i| AddressBlock::parse(&i, config)) 43 | .collect(); 44 | let ab = ab?; 45 | if ab.is_empty() { 46 | None 47 | } else { 48 | Some(ab) 49 | } 50 | }) 51 | .interrupt({ 52 | let interrupt: Result, _> = tree 53 | .children() 54 | .filter(|t| t.is_element() && t.has_tag_name("interrupt")) 55 | .map(|i| Interrupt::parse(&i, config)) 56 | .collect(); 57 | Some(interrupt?) 58 | }) 59 | .registers(if let Some(registers) = tree.get_child("registers") { 60 | let rs: Result, _> = registers 61 | .children() 62 | .filter(Node::is_element) 63 | .map(|t| RegisterCluster::parse(&t, config)) 64 | .collect(); 65 | Some(rs?) 66 | } else { 67 | None 68 | }) 69 | .derived_from(tree.attribute("derivedFrom").map(|s| s.to_owned())) 70 | .build(config.validate_level) 71 | .map_err(|e| SVDError::from(e).at(tree.id())) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /tests/src/enumeratedvalues.rs: -------------------------------------------------------------------------------- 1 | use crate::encode::Encode; 2 | use crate::parse::Parse; 3 | use crate::svd::{EnumeratedValue, EnumeratedValues}; 4 | use anyhow::Result; 5 | use xmltree::Element; 6 | 7 | #[test] 8 | fn decode_encode() { 9 | let example = String::from( 10 | " 11 | 12 | 13 | WS0 14 | Zero wait-states inserted in fetch or read transfers 15 | true 16 | 17 | 18 | WS1 19 | One wait-state inserted for each fetch or read transfer. See Flash Wait-States table for details 20 | 1 21 | 22 | 23 | ", 24 | ); 25 | 26 | let expected = EnumeratedValues::builder() 27 | .derived_from(Some("fake_derivation".to_string())) 28 | .values(vec![ 29 | EnumeratedValue::builder() 30 | .name("WS0".to_string()) 31 | .description(Some( 32 | "Zero wait-states inserted in fetch or read transfers".to_string() 33 | )) 34 | .is_default(Some(true)) 35 | .build() 36 | .unwrap(), 37 | EnumeratedValue::builder() 38 | .name("WS1".to_string()) 39 | .description(Some( 40 | "One wait-state inserted for each fetch or read transfer. See Flash Wait-States table for details".to_string() 41 | )) 42 | .value(Some(1)) 43 | .build() 44 | .unwrap(), 45 | ]) 46 | .build() 47 | .unwrap(); 48 | 49 | // TODO: move to test! macro 50 | let tree1 = Element::parse(example.as_bytes()).unwrap(); 51 | 52 | let parsed = EnumeratedValues::parse(&tree1).unwrap(); 53 | assert_eq!(parsed, expected, "Parsing tree failed"); 54 | 55 | let tree2 = parsed.encode().unwrap(); 56 | assert_eq!(tree1, tree2, "Encoding value failed"); 57 | } 58 | 59 | #[test] 60 | fn valid_children() { 61 | fn parse(contents: String) -> Result { 62 | let example = String::from("") + &contents + ""; 63 | let tree = Element::parse(example.as_bytes()).unwrap(); 64 | EnumeratedValues::parse(&tree) 65 | } 66 | 67 | // `enumeratedValue` occurrence: 1..* 68 | parse("".into()).expect_err("must contain at least one "); 69 | 70 | let value = String::from( 71 | " 72 | 73 | WS0 74 | Zero wait-states inserted in fetch or read transfers 75 | 0 76 | ", 77 | ); 78 | 79 | // Valid tags 80 | parse(value.clone() + "foo").expect(" is valid"); 81 | parse(value.clone() + "foo") 82 | .expect(" is valid"); 83 | parse(value.clone() + "read").expect(" is valid"); 84 | 85 | // Invalid tags 86 | parse(value.clone() + "") 87 | .expect_err(" in invalid here"); 88 | parse(value.clone() + "") 89 | .expect_err(" in invalid here"); 90 | } 91 | -------------------------------------------------------------------------------- /svd-rs/src/interrupt.rs: -------------------------------------------------------------------------------- 1 | use super::{BuildError, Description, Name, SvdError, ValidateLevel}; 2 | 3 | /// Describes an interrupt in the device 4 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 5 | #[derive(Clone, Debug, PartialEq, Eq)] 6 | #[non_exhaustive] 7 | pub struct Interrupt { 8 | /// The string represents the interrupt name 9 | pub name: String, 10 | 11 | /// The string describes the interrupt 12 | #[cfg_attr( 13 | feature = "serde", 14 | serde(default, skip_serializing_if = "Option::is_none") 15 | )] 16 | pub description: Option, 17 | 18 | /// Represents the enumeration index value associated to the interrupt 19 | pub value: u32, 20 | } 21 | 22 | /// Builder for [`Interrupt`] 23 | #[derive(Clone, Debug, Default, PartialEq, Eq)] 24 | pub struct InterruptBuilder { 25 | name: Option, 26 | description: Option, 27 | value: Option, 28 | } 29 | 30 | impl From for InterruptBuilder { 31 | fn from(d: Interrupt) -> Self { 32 | Self { 33 | name: Some(d.name), 34 | description: d.description, 35 | value: Some(d.value), 36 | } 37 | } 38 | } 39 | 40 | impl InterruptBuilder { 41 | /// Set the name of the interrupt 42 | pub fn name(mut self, value: String) -> Self { 43 | self.name = Some(value); 44 | self 45 | } 46 | /// Set the description of the interrupt 47 | pub fn description(mut self, value: Option) -> Self { 48 | self.description = value; 49 | self 50 | } 51 | /// Set the value of the interrupt 52 | pub fn value(mut self, value: u32) -> Self { 53 | self.value = Some(value); 54 | self 55 | } 56 | /// Validate and build a [`Interrupt`]. 57 | pub fn build(self, lvl: ValidateLevel) -> Result { 58 | let de = Interrupt { 59 | name: self 60 | .name 61 | .ok_or_else(|| BuildError::Uninitialized("name".to_string()))?, 62 | description: self.description, 63 | value: self 64 | .value 65 | .ok_or_else(|| BuildError::Uninitialized("value".to_string()))?, 66 | }; 67 | de.validate(lvl)?; 68 | Ok(de) 69 | } 70 | } 71 | 72 | impl Interrupt { 73 | /// Make a builder for [`Interrupt`] 74 | pub fn builder() -> InterruptBuilder { 75 | InterruptBuilder::default() 76 | } 77 | /// Modify an existing [`Interrupt`] based on a [builder](InterruptBuilder). 78 | pub fn modify_from( 79 | &mut self, 80 | builder: InterruptBuilder, 81 | lvl: ValidateLevel, 82 | ) -> Result<(), SvdError> { 83 | if let Some(name) = builder.name { 84 | self.name = name; 85 | } 86 | if builder.description.is_some() { 87 | self.description = builder.description; 88 | } 89 | if let Some(value) = builder.value { 90 | self.value = value; 91 | } 92 | self.validate(lvl) 93 | } 94 | /// Validate the [`Interrupt`]. 95 | /// 96 | /// # Notes 97 | /// 98 | /// This doesn't do anything. 99 | pub fn validate(&self, _lvl: ValidateLevel) -> Result<(), SvdError> { 100 | Ok(()) 101 | } 102 | } 103 | 104 | impl Name for Interrupt { 105 | fn name(&self) -> &str { 106 | &self.name 107 | } 108 | } 109 | 110 | impl Description for Interrupt { 111 | fn description(&self) -> Option<&str> { 112 | self.description.as_deref() 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /svd-encoder/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Encode traits. 2 | //! These support encoding of SVD types to XML 3 | 4 | use svd_rs as svd; 5 | 6 | use crate::svd::Device; 7 | use xmltree::{Element, EmitterConfig, XMLNode}; 8 | 9 | pub use crate::config::{ 10 | Config, DerivableSorting, IdentifierFormat, NumberFormat, RcSorting, Sorting, 11 | }; 12 | 13 | #[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)] 14 | pub enum EncodeError {} 15 | 16 | /// Encode trait allows SVD objects to be encoded into XML elements. 17 | pub trait Encode { 18 | /// Encoding error 19 | type Error; 20 | /// Encode into an XML/SVD element 21 | fn encode(&self) -> Result { 22 | self.encode_with_config(&Config::default()) 23 | } 24 | /// Encode into an XML/SVD element with a custom configuration 25 | fn encode_with_config(&self, config: &Config) -> Result; 26 | fn encode_node(&self) -> Result { 27 | self.encode().map(XMLNode::Element) 28 | } 29 | fn encode_node_with_config(&self, config: &Config) -> Result { 30 | self.encode_with_config(config).map(XMLNode::Element) 31 | } 32 | } 33 | 34 | /// EncodeChildren allows SVD objects to be encoded as a list of XML nodes 35 | /// This is typically used to merge with an existing element. 36 | pub trait EncodeChildren { 37 | /// Encoding error 38 | type Error; 39 | /// Encode into XML/SVD children to merge with existing object 40 | fn encode(&self) -> Result, Self::Error> { 41 | self.encode_with_config(&Config::default()) 42 | } 43 | /// Encode into XML/SVD children to merge with existing object with a custom configuration 44 | fn encode_with_config(&self, config: &Config) -> Result, Self::Error>; 45 | } 46 | 47 | /// Encodes a device object to an SVD (XML) string 48 | pub fn encode(d: &Device) -> Result { 49 | encode_with_config(d, &Config::default()) 50 | } 51 | 52 | /// Encodes a device object to an SVD (XML) string 53 | pub fn encode_with_config(d: &Device, config: &Config) -> Result { 54 | let root = d.encode_with_config(config)?; 55 | let mut wr = Vec::new(); 56 | let mut cfg = EmitterConfig::new(); 57 | cfg.perform_indent = true; 58 | cfg.pad_self_closing = false; 59 | root.write_with_config(&mut wr, cfg).unwrap(); 60 | Ok(String::from_utf8(wr).unwrap()) 61 | } 62 | 63 | /// Defines extensions for implementation over xmltree::Element 64 | trait ElementMerge { 65 | fn merge(&mut self, n: &Self); 66 | } 67 | /// Implements extensions for xmltree::Element 68 | impl ElementMerge for Element { 69 | // Merges the children of two elements, maintaining the name and description of the first 70 | fn merge(&mut self, r: &Self) { 71 | self.children.extend(r.children.iter().cloned()); 72 | for (key, val) in &r.attributes { 73 | self.attributes.insert(key.clone(), val.clone()); 74 | } 75 | } 76 | } 77 | 78 | /// Helper to create new base xml nodes 79 | pub(crate) fn new_node(name: &str, text: String) -> XMLNode { 80 | let mut e = Element::new(name); 81 | e.children.push(XMLNode::Text(text)); 82 | XMLNode::Element(e) 83 | } 84 | 85 | mod access; 86 | mod addressblock; 87 | mod bitrange; 88 | mod cluster; 89 | mod config; 90 | mod cpu; 91 | mod datatype; 92 | mod device; 93 | mod dimelement; 94 | mod endian; 95 | mod enumeratedvalue; 96 | mod enumeratedvalues; 97 | mod field; 98 | mod interrupt; 99 | mod modifiedwritevalues; 100 | mod peripheral; 101 | mod protection; 102 | mod readaction; 103 | mod register; 104 | mod registercluster; 105 | mod registerproperties; 106 | mod usage; 107 | mod writeconstraint; 108 | -------------------------------------------------------------------------------- /svd-parser/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 6 | and this project adheres to [Semantic Versioning](http://semver.org/). 7 | 8 | ## [Unreleased] 9 | 10 | ## [v0.14.9] - 2025-03-11 11 | 12 | - Bump MSRV to 1.70.0 13 | 14 | ## [v0.14.8] - 2025-02-08 15 | 16 | - Revert the `riscv` element, as well as the `unstable-riscv` feature. 17 | 18 | ## [v0.14.7] - 2024-10-03 19 | 20 | - Bump svd-rs to 0.14.9 21 | 22 | ## [v0.14.6] - 2024-08-20 23 | 24 | - Adapt the `riscv` element to handle `riscv::Exception`. 25 | - Add `riscv` element for configuration parameters related to RISC-V targets. 26 | You must use the `unstable-riscv` feature to enable this exeperimental element. 27 | - Bump MSRV to 1.65.0 28 | - Bump roxmltree to 0.20 29 | 30 | ## [v0.14.5] - 2024-01-03 31 | 32 | - Bump MSRV to 1.61.0 33 | - Bump svd-rs to 0.14.7, roxmltree to 0.19 34 | 35 | ## [v0.14.4] - 2023-11-15 36 | 37 | - Bump svd-rs dependency to 0.14.4 or higher. 38 | 39 | ## [v0.14.3] - 2023-11-15 40 | 41 | - Correctly place `expand_properties` under `expand` feature 42 | 43 | ## [v0.14.2] - 2023-09-17 44 | 45 | - Bump MSRV to 1.58.0 46 | - Ignore whitespaces around tag contents 47 | 48 | ## [v0.14.1] - 2022-10-23 49 | 50 | - Update to `svd-rs` 0.14.1 51 | - Add `BlockPath::parent` 52 | 53 | ## [v0.14.0] - 2022-07-19 54 | 55 | - Make `expand::Index`, pathes & `derive_peripheral`, etc. public 56 | - Fix parsing `xs:noNamespaceSchemaLocation` 57 | - Bump MSRV to 1.56.0 (2021) 58 | 59 | ## [v0.13.4] - 2022-05-13 60 | 61 | - Support nested `derivedFrom` for `expand` 62 | 63 | ## [v0.13.3] - 2022-05-09 64 | 65 | - Add `expand_properties` (under `expand` feature) 66 | 67 | ## [v0.13.2] - 2022-04-23 68 | 69 | - Add `expand` (under `expand` feature) and `ignore_enums` options 70 | 71 | ## [v0.13.1] - 2022-01-04 72 | 73 | - Make `version`, `description`, `width` and `address_unit_bits` on `Device` optional again 74 | 75 | ## [v0.13.0] - 2022-01-04 76 | 77 | - Add `svd2yaml` example 78 | - Bump `svd-rs` 79 | 80 | ## [v0.12.0] - 2021-11-11 81 | 82 | - Bump `svd-rs` 83 | - Add `protection` parsing 84 | - Add `readAction` parsing 85 | - Add array support for peripherals 86 | 87 | ## [v0.11.0] - 2021-10-02 88 | 89 | Previous versions in common [changelog](../CHANGELOG.md). 90 | 91 | [Unreleased]: https://github.com/rust-embedded/svd/compare/svd-rs-v0.14.12...HEAD 92 | [v0.14.9]: https://github.com/rust-embedded/svd/compare/svd-rs-v0.14.11...svd-rs-v0.14.12 93 | [v0.14.8]: https://github.com/rust-embedded/svd/compare/svd-rs-v0.14.9...svd-rs-v0.14.10 94 | [v0.14.6]: https://github.com/rust-embedded/svd/compare/svd-rs-v0.14.8...svd-rs-v0.14.9 95 | [v0.14.5]: https://github.com/rust-embedded/svd/compare/svd-parser-v0.14.4...svd-rs-v0.14.7 96 | [v0.14.4]: https://github.com/rust-embedded/svd/compare/svd-parser-v0.14.3...svd-parser-v0.14.4 97 | [v0.14.3]: https://github.com/rust-embedded/svd/compare/svd-parser-v0.14.2...svd-parser-v0.14.3 98 | [v0.14.2]: https://github.com/rust-embedded/svd/compare/svd-rs-v0.14.2...svd-parser-v0.14.2 99 | [v0.14.1]: https://github.com/rust-embedded/svd/compare/v0.14.0...svd-rs-v0.14.1 100 | [v0.14.0]: https://github.com/rust-embedded/svd/compare/svd-parser-v0.13.4...v0.14.0 101 | [v0.13.4]: https://github.com/rust-embedded/svd/compare/svd-parser-v0.13.3...svd-parser-v0.13.4 102 | [v0.13.3]: https://github.com/rust-embedded/svd/compare/svd-parser-v0.13.2...svd-parser-v0.13.3 103 | [v0.13.2]: https://github.com/rust-embedded/svd/compare/svd-rs-v0.13.2...svd-parser-v0.13.2 104 | [v0.13.1]: https://github.com/rust-embedded/svd/compare/v0.13.0...svd-parser-v0.13.1 105 | [v0.13.0]: https://github.com/rust-embedded/svd/compare/v0.12.0...v0.13.0 106 | [v0.12.0]: https://github.com/rust-embedded/svd/compare/v0.11.0...v0.12.0 107 | [v0.11.0]: https://github.com/rust-embedded/svd/compare/v0.10.2...v0.11.0 108 | -------------------------------------------------------------------------------- /tests/src/dimelement.rs: -------------------------------------------------------------------------------- 1 | use super::run_test; 2 | use crate::svd::{DimElement, ValidateLevel}; 3 | 4 | #[test] 5 | fn decode_encode() { 6 | let tests = [( 7 | DimElement::builder() 8 | .dim(2) 9 | .dim_increment(4) 10 | .dim_index(Some(vec!["10".to_string(), "20".to_string()])) 11 | .build(ValidateLevel::Strict) 12 | .unwrap(), 13 | " 14 | 2 15 | 0x4 16 | 10,20 17 | 18 | ", 19 | " 20 | 2 21 | 0x4 22 | 10,20 23 | 24 | ", 25 | )]; 26 | run_test::(&tests[..], None, None); 27 | 28 | let tests = [( 29 | DimElement::builder() 30 | .dim(3) 31 | .dim_increment(4) 32 | .dim_index(Some(vec![ 33 | "3".to_string(), 34 | "4".to_string(), 35 | "5".to_string(), 36 | ])) 37 | .build(ValidateLevel::Strict) 38 | .unwrap(), 39 | " 40 | 3 41 | 0x4 42 | 3-5 43 | 44 | ", 45 | " 46 | 3 47 | 0x4 48 | 3-5 49 | 50 | ", 51 | )]; 52 | run_test::(&tests[..], None, None); 53 | 54 | let tests = [( 55 | DimElement::builder() 56 | .dim(3) 57 | .dim_increment(4) 58 | .dim_index(Some(vec![ 59 | "3".to_string(), 60 | "5".to_string(), 61 | "4".to_string(), 62 | ])) 63 | .build(ValidateLevel::Strict) 64 | .unwrap(), 65 | " 66 | 3 67 | 0x4 68 | 3,5,4 69 | 70 | ", 71 | " 72 | 3 73 | 0x4 74 | 3,5,4 75 | 76 | ", 77 | )]; 78 | run_test::(&tests[..], None, None); 79 | 80 | let tests = [( 81 | DimElement::builder() 82 | .dim(1) 83 | .dim_increment(0) 84 | .dim_index(Some(vec!["3".to_string()])) 85 | .build(ValidateLevel::Strict) 86 | .unwrap(), 87 | " 88 | 1 89 | 0x0 90 | 3-3 91 | 92 | ", 93 | " 94 | 1 95 | 0x0 96 | 3-3 97 | 98 | ", 99 | )]; 100 | run_test::(&tests[..], None, None); 101 | 102 | let parse_config = svd_parser::Config::default(); 103 | let mut encode_config = svd_encoder::Config::default(); 104 | encode_config.update("dim_dim", "UpperHex"); 105 | encode_config.update("dim_increment", "LowerHex"); 106 | 107 | let tests = [( 108 | DimElement::builder() 109 | .dim(14) 110 | .dim_increment(15) 111 | .build(ValidateLevel::Strict) 112 | .unwrap(), 113 | " 114 | 14 115 | 0xF 116 | 117 | ", 118 | " 119 | 0xE 120 | 0xf 121 | 122 | ", 123 | )]; 124 | 125 | run_test::(&tests[..], Some(parse_config), Some(encode_config)); 126 | } 127 | 128 | #[test] 129 | fn decode_encode_one_element() {} 130 | -------------------------------------------------------------------------------- /svd-parser/src/elementext.rs: -------------------------------------------------------------------------------- 1 | //! SVD Element Extensions. 2 | //! This module is extends roxmltree::Element objects with convenience methods 3 | 4 | use roxmltree::Node; 5 | 6 | use super::types::BoolParse; 7 | use super::{Parse, SVDError, SVDErrorAt}; 8 | 9 | /// Defines extensions for implementation over roxmltree::Node 10 | pub trait ElementExt { 11 | fn get_child(&self, k: K) -> Option> 12 | where 13 | K: AsRef; 14 | fn get_child_text_opt(&self, k: K) -> Result, SVDErrorAt> 15 | where 16 | K: AsRef; 17 | fn get_child_text(&self, k: K) -> Result 18 | where 19 | K: AsRef; 20 | 21 | fn get_text(&self) -> Result<&str, SVDErrorAt>; 22 | 23 | fn get_child_elem(&self, n: &str) -> Result, SVDErrorAt>; 24 | fn get_child_u32(&self, n: &str) -> Result; 25 | fn get_child_u64(&self, n: &str) -> Result; 26 | fn get_child_bool(&self, n: &str) -> Result; 27 | 28 | fn debug(&self); 29 | } 30 | 31 | /// Implements extensions for roxmltree::Node 32 | impl ElementExt for Node<'_, '_> { 33 | fn get_child(&self, k: K) -> Option> 34 | where 35 | K: AsRef, 36 | { 37 | self.children().find(|&c| c.has_tag_name(k.as_ref())) 38 | } 39 | fn get_child_text_opt(&self, k: K) -> Result, SVDErrorAt> 40 | where 41 | K: AsRef, 42 | { 43 | if let Some(child) = self.get_child(k) { 44 | match child.get_text() { 45 | Err(e) => { 46 | // if tag is empty just ignore it 47 | match e { 48 | SVDErrorAt { 49 | error: SVDError::EmptyTag(_), 50 | .. 51 | } => Ok(None), 52 | _ => Err(e), 53 | } 54 | } 55 | Ok(s) => Ok(Some(s.to_string())), 56 | } 57 | } else { 58 | Ok(None) 59 | } 60 | } 61 | fn get_child_text(&self, k: K) -> Result 62 | where 63 | K: AsRef, 64 | { 65 | let k = k.as_ref(); 66 | self.get_child_text_opt(k)? 67 | .ok_or_else(|| SVDError::MissingTag(k.to_string()).at(self.id())) 68 | } 69 | 70 | /// Get text contained by an XML Element 71 | fn get_text(&self) -> Result<&str, SVDErrorAt> { 72 | match self.text() { 73 | // TODO: return error on `strict` 74 | Some(s) => Ok(s.trim()), 75 | // FIXME: Doesn't look good because SVDError doesn't format by itself. We already 76 | // capture the element and this information can be used for getting the name 77 | // This would fix ParseError 78 | None => Err(SVDError::EmptyTag(self.tag_name().name().to_string()).at(self.id())), 79 | } 80 | } 81 | 82 | /// Get a named child element from an XML Element 83 | fn get_child_elem(&self, n: &str) -> Result, SVDErrorAt> { 84 | self.get_child(n) 85 | .ok_or_else(|| SVDError::MissingTag(n.to_string()).at(self.id())) 86 | } 87 | 88 | /// Get a u32 value from a named child element 89 | fn get_child_u32(&self, n: &str) -> Result { 90 | let s = self.get_child_elem(n)?; 91 | u32::parse(&s, &()) 92 | } 93 | 94 | /// Get a u64 value from a named child element 95 | fn get_child_u64(&self, n: &str) -> Result { 96 | let s = self.get_child_elem(n)?; 97 | u64::parse(&s, &()) 98 | } 99 | 100 | /// Get a bool value from a named child element 101 | fn get_child_bool(&self, n: &str) -> Result { 102 | let s = self.get_child_elem(n)?; 103 | BoolParse::parse(&s, &()) 104 | } 105 | 106 | fn debug(&self) { 107 | let name = self.tag_name().name(); 108 | println!("<{}>", name); 109 | for c in self.children() { 110 | println!("{}: {:?}", c.tag_name().name(), c.text()) 111 | } 112 | println!("", name); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /svd-parser/src/bitrange.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::svd::{BitRange, BitRangeType}; 3 | 4 | #[derive(Clone, Debug, PartialEq, Eq)] 5 | pub enum InvalidBitRange { 6 | Syntax, 7 | ParseError, 8 | MsbLsb, 9 | Empty, 10 | Size, 11 | } 12 | 13 | impl Parse for BitRange { 14 | type Object = Self; 15 | type Error = SVDErrorAt; 16 | type Config = Config; 17 | 18 | fn parse(tree: &Node, _config: &Self::Config) -> Result { 19 | let (end, start, range_type): (u32, u32, BitRangeType) = 20 | if let Some(range) = tree.get_child("bitRange") { 21 | let text = range.text().ok_or_else(|| { 22 | SVDError::InvalidBitRange(InvalidBitRange::Empty).at(tree.id()) 23 | })?; 24 | if !text.starts_with('[') { 25 | return Err(SVDError::InvalidBitRange(InvalidBitRange::Syntax).at(tree.id())); 26 | // TODO: Maybe have a MissingOpen/MissingClosing variant 27 | } 28 | if !text.ends_with(']') { 29 | return Err(SVDError::InvalidBitRange(InvalidBitRange::Syntax).at(tree.id())); 30 | // TODO: Maybe have a MissingOpen/MissingClosing variant 31 | } 32 | 33 | let mut parts = text[1..text.len() - 1].split(':'); 34 | ( 35 | parts 36 | .next() 37 | .ok_or_else(|| { 38 | SVDError::InvalidBitRange(InvalidBitRange::Syntax).at(tree.id()) 39 | })? 40 | .parse::() 41 | .map_err(|_| { 42 | SVDError::InvalidBitRange(InvalidBitRange::ParseError).at(tree.id()) 43 | })?, 44 | parts 45 | .next() 46 | .ok_or_else(|| { 47 | SVDError::InvalidBitRange(InvalidBitRange::Syntax).at(tree.id()) 48 | })? 49 | .parse::() 50 | .map_err(|_| { 51 | SVDError::InvalidBitRange(InvalidBitRange::ParseError).at(tree.id()) 52 | })?, 53 | BitRangeType::BitRange, 54 | ) 55 | // TODO: Consider matching instead so we can say which of these tags are missing 56 | } else if let (Some(lsb), Some(msb)) = (tree.get_child("lsb"), tree.get_child("msb")) { 57 | ( 58 | // TODO: `u32::parse` should not hide it's errors 59 | u32::parse(&msb, &()).map_err(|_| { 60 | SVDError::InvalidBitRange(InvalidBitRange::MsbLsb).at(tree.id()) 61 | })?, 62 | u32::parse(&lsb, &()).map_err(|_| { 63 | SVDError::InvalidBitRange(InvalidBitRange::MsbLsb).at(tree.id()) 64 | })?, 65 | BitRangeType::MsbLsb, 66 | ) 67 | } else if let (Some(offset), Some(width)) = 68 | (tree.get_child("bitOffset"), tree.get_child("bitWidth")) 69 | { 70 | // Special case because offset and width are directly provided 71 | // (ie. do not need to be calculated as in the final step) 72 | return Ok(BitRange { 73 | // TODO: capture that error comes from offset/width tag 74 | // TODO: `u32::parse` should not hide it's errors 75 | offset: u32::parse(&offset, &()).map_err(|_| { 76 | SVDError::InvalidBitRange(InvalidBitRange::ParseError).at(tree.id()) 77 | })?, 78 | width: u32::parse(&width, &()).map_err(|_| { 79 | SVDError::InvalidBitRange(InvalidBitRange::ParseError).at(tree.id()) 80 | })?, 81 | range_type: BitRangeType::OffsetWidth, 82 | }); 83 | } else { 84 | return Err(SVDError::InvalidBitRange(InvalidBitRange::Syntax).at(tree.id())); 85 | }; 86 | 87 | if start > end { 88 | return Err(SVDError::InvalidBitRange(InvalidBitRange::Size).at(tree.id())); 89 | } 90 | Ok(Self { 91 | offset: start, 92 | width: end - start + 1, 93 | range_type, 94 | }) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /svd-rs/src/array.rs: -------------------------------------------------------------------------------- 1 | use super::{Description, DimElement, Name}; 2 | use core::ops::{Deref, DerefMut}; 3 | 4 | /// A single SVD instance or array of instances 5 | #[derive(Clone, Debug, PartialEq, Eq)] 6 | pub enum MaybeArray { 7 | /// A single instance 8 | Single(T), 9 | /// An array of instances 10 | Array(T, DimElement), 11 | } 12 | 13 | impl Deref for MaybeArray { 14 | type Target = T; 15 | 16 | fn deref(&self) -> &T { 17 | match self { 18 | Self::Single(info) => info, 19 | Self::Array(info, _) => info, 20 | } 21 | } 22 | } 23 | 24 | impl DerefMut for MaybeArray { 25 | fn deref_mut(&mut self) -> &mut T { 26 | match self { 27 | Self::Single(info) => info, 28 | Self::Array(info, _) => info, 29 | } 30 | } 31 | } 32 | 33 | impl MaybeArray { 34 | /// Return `true` if instance is single 35 | pub const fn is_single(&self) -> bool { 36 | matches!(self, Self::Single(_)) 37 | } 38 | /// Return `true` if it is an array 39 | pub const fn is_array(&self) -> bool { 40 | matches!(self, Self::Array(_, _)) 41 | } 42 | } 43 | 44 | impl Name for MaybeArray 45 | where 46 | T: Name, 47 | { 48 | fn name(&self) -> &str { 49 | T::name(self) 50 | } 51 | } 52 | 53 | impl Description for MaybeArray 54 | where 55 | T: Description, 56 | { 57 | fn description(&self) -> Option<&str> { 58 | T::description(self) 59 | } 60 | } 61 | 62 | /// Return list of names of instances in array 63 | pub fn names<'a, T: Name>(info: &'a T, dim: &'a DimElement) -> impl Iterator + 'a { 64 | let name = info.name(); 65 | dim.indexes().map(move |i| { 66 | dim.dim_array_index 67 | .as_ref() 68 | .and_then(|dai| { 69 | dai.values 70 | .iter() 71 | .find(|e| e.value.map(|v| v.to_string().as_str() == i.deref()) == Some(true)) 72 | }) 73 | .map(|n| n.name.clone()) 74 | .unwrap_or_else(|| name.replace("[%s]", &i).replace("%s", &i)) 75 | }) 76 | } 77 | 78 | /// Return list of descriptions of instances in array 79 | pub fn descriptions<'a, T: Description>( 80 | info: &'a T, 81 | dim: &'a DimElement, 82 | ) -> impl Iterator> + 'a { 83 | let description = info.description(); 84 | dim.indexes().map(move |i| { 85 | dim.dim_array_index 86 | .as_ref() 87 | .and_then(|dai| { 88 | dai.values 89 | .iter() 90 | .find(|e| e.value.map(|v| v.to_string().as_str() == i.deref()) == Some(true)) 91 | }) 92 | .and_then(|n| n.description.clone()) 93 | .or_else(|| description.map(|d| d.replace("[%s]", &i).replace("%s", &i))) 94 | }) 95 | } 96 | 97 | #[cfg(feature = "serde")] 98 | mod ser_de { 99 | use super::*; 100 | use serde::{Deserialize, Deserializer, Serialize, Serializer}; 101 | 102 | #[derive(serde::Serialize)] 103 | struct SerArray<'a, T> { 104 | #[serde(flatten)] 105 | dim: &'a DimElement, 106 | #[serde(flatten)] 107 | info: &'a T, 108 | } 109 | 110 | #[derive(serde::Deserialize)] 111 | struct DeserArray { 112 | #[serde(flatten, default)] 113 | dim: Option, 114 | #[serde(flatten)] 115 | info: T, 116 | } 117 | 118 | impl Serialize for MaybeArray 119 | where 120 | T: Serialize, 121 | { 122 | fn serialize(&self, serializer: S) -> Result 123 | where 124 | S: Serializer, 125 | { 126 | match self { 127 | Self::Single(info) => info.serialize(serializer), 128 | Self::Array(info, dim) => SerArray:: { dim, info }.serialize(serializer), 129 | } 130 | } 131 | } 132 | 133 | impl<'de, T> Deserialize<'de> for MaybeArray 134 | where 135 | T: Deserialize<'de>, 136 | { 137 | fn deserialize(deserializer: D) -> Result 138 | where 139 | D: Deserializer<'de>, 140 | { 141 | let DeserArray { dim, info } = DeserArray::::deserialize(deserializer)?; 142 | if let Some(dim) = dim { 143 | Ok(Self::Array(info, dim)) 144 | } else { 145 | Ok(Self::Single(info)) 146 | } 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /svd-encoder/src/device.rs: -------------------------------------------------------------------------------- 1 | use svd_rs::Peripheral; 2 | 3 | use super::{new_node, Config, Element, Encode, EncodeChildren, EncodeError, XMLNode}; 4 | use crate::{ 5 | config::{DerivableSorting, Sorting}, 6 | svd::Device, 7 | }; 8 | 9 | impl Encode for Device { 10 | type Error = EncodeError; 11 | 12 | fn encode_with_config(&self, config: &Config) -> Result { 13 | let mut elem = Element::new("device"); 14 | if let Some(v) = &self.vendor { 15 | elem.children.push(new_node("vendor", v.clone())); 16 | } 17 | if let Some(v) = &self.vendor_id { 18 | elem.children.push(new_node("vendorID", v.clone())); 19 | } 20 | 21 | elem.children.push(new_node("name", self.name.clone())); 22 | 23 | if let Some(v) = &self.series { 24 | elem.children.push(new_node("series", v.clone())); 25 | } 26 | 27 | elem.children 28 | .push(new_node("version", self.version.clone())); 29 | 30 | elem.children 31 | .push(new_node("description", self.description.clone())); 32 | 33 | if let Some(v) = &self.license_text { 34 | elem.children.push(new_node("licenseText", v.clone())); 35 | } 36 | 37 | if let Some(v) = &self.cpu { 38 | elem.children 39 | .push(XMLNode::Element(v.encode_with_config(config)?)); 40 | } 41 | 42 | if let Some(v) = &self.header_system_filename { 43 | elem.children 44 | .push(new_node("headerSystemFilename", v.clone())); 45 | } 46 | 47 | if let Some(v) = &self.header_definitions_prefix { 48 | elem.children 49 | .push(new_node("headerDefinitionsPrefix", v.clone())); 50 | } 51 | 52 | elem.children.push(new_node( 53 | "addressUnitBits", 54 | format!("{}", self.address_unit_bits), 55 | )); 56 | 57 | elem.children 58 | .push(new_node("width", format!("{}", self.width))); 59 | 60 | elem.children.extend( 61 | self.default_register_properties 62 | .encode_with_config(config)?, 63 | ); 64 | 65 | let peripherals: Result, _> = 66 | if config.peripheral_sorting == DerivableSorting::Unchanged(None) { 67 | self.peripherals 68 | .iter() 69 | .map(|peripheral| peripheral.encode_node_with_config(config)) 70 | .collect() 71 | } else { 72 | sort_derived_peripherals(&self.peripherals, config.peripheral_sorting) 73 | .into_iter() 74 | .map(|peripheral| peripheral.encode_node_with_config(config)) 75 | .collect() 76 | }; 77 | 78 | elem.children.push({ 79 | let mut e = Element::new("peripherals"); 80 | e.children = peripherals?; 81 | XMLNode::Element(e) 82 | }); 83 | 84 | elem.attributes 85 | .insert(String::from("schemaVersion"), self.schema_version.clone()); 86 | elem.attributes 87 | .insert(String::from("xmlns:xs"), self.xmlns_xs.clone()); 88 | elem.attributes.insert( 89 | String::from("xs:noNamespaceSchemaLocation"), 90 | self.no_namespace_schema_location.clone(), 91 | ); 92 | 93 | Ok(elem) 94 | } 95 | } 96 | 97 | fn sort_peripherals(refs: &mut [&Peripheral], sorting: Option) { 98 | if let Some(sorting) = sorting { 99 | match sorting { 100 | Sorting::Offset => refs.sort_by_key(|p| p.base_address), 101 | Sorting::OffsetReversed => { 102 | refs.sort_by_key(|p| -(p.base_address as i32)); 103 | } 104 | Sorting::Name => refs.sort_by_key(|p| &p.name), 105 | } 106 | } 107 | } 108 | 109 | fn sort_derived_peripherals( 110 | peripherals: &[Peripheral], 111 | sorting: DerivableSorting, 112 | ) -> Vec<&Peripheral> { 113 | match sorting { 114 | DerivableSorting::Unchanged(sorting) => { 115 | let mut refs = peripherals.iter().collect::>(); 116 | sort_peripherals(&mut refs, sorting); 117 | refs 118 | } 119 | DerivableSorting::DeriveLast(sorting) => { 120 | let mut common_refs = peripherals 121 | .iter() 122 | .filter(|p| p.derived_from.is_none()) 123 | .collect::>(); 124 | let mut derived_refs = peripherals 125 | .iter() 126 | .filter(|p| p.derived_from.is_some()) 127 | .collect::>(); 128 | sort_peripherals(&mut common_refs, sorting); 129 | sort_peripherals(&mut derived_refs, sorting); 130 | common_refs.extend(derived_refs); 131 | common_refs 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /svd-rs/src/bitrange.rs: -------------------------------------------------------------------------------- 1 | /// Errors for bit ranges 2 | #[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)] 3 | pub enum Error { 4 | /// The bit range is 0 bits wide 5 | #[error("bitRange width of 0 does not make sense")] 6 | ZeroWidth, 7 | } 8 | 9 | /// A bit range, describing the [least significant bit](Self::lsb) and [most significant bit](Self::msb) 10 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 11 | pub struct BitRange { 12 | /// Value defining the position of the least significant bit of the field within the register 13 | pub offset: u32, 14 | 15 | /// Value defining the bit-width of the bitfield within the register 16 | pub width: u32, 17 | 18 | /// The underlying description of the bit range 19 | pub range_type: BitRangeType, 20 | } 21 | 22 | /// The style of bit range that describes a [BitRange] 23 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 24 | pub enum BitRangeType { 25 | /// A bit range in the format: `[:]` 26 | BitRange, 27 | /// A bit range described as offset and width 28 | OffsetWidth, 29 | /// A bit range described as lsb and msb as separate elements 30 | MsbLsb, 31 | } 32 | 33 | impl BitRange { 34 | /// Get the position of the least significant bit 35 | pub fn lsb(&self) -> u32 { 36 | self.offset 37 | } 38 | /// Get the position of the most significant bit 39 | pub fn msb(&self) -> u32 { 40 | self.offset + self.width - 1 41 | } 42 | /// Get the bit range in the format `[:]` 43 | pub fn bit_range(&self) -> String { 44 | format!("[{}:{}]", self.msb(), self.lsb()) 45 | } 46 | /// Construct a [`BitRange`] from a offset and width 47 | pub fn from_offset_width(offset: u32, width: u32) -> Self { 48 | Self { 49 | offset, 50 | width, 51 | range_type: BitRangeType::OffsetWidth, 52 | } 53 | } 54 | 55 | /// Construct a [`BitRange`] from a msb and lsb 56 | pub fn from_msb_lsb(msb: u32, lsb: u32) -> Self { 57 | Self { 58 | offset: lsb, 59 | width: msb - lsb + 1, 60 | range_type: BitRangeType::MsbLsb, 61 | } 62 | } 63 | /// Construct a [`BitRange`] from a string in the format `[:]` 64 | pub fn from_bit_range(text: &str) -> Option { 65 | if !text.starts_with('[') || !text.ends_with(']') { 66 | return None; 67 | } 68 | let mut parts = text[1..text.len() - 1].split(':'); 69 | let msb = parts.next()?.parse::().ok()?; 70 | let lsb = parts.next()?.parse::().ok()?; 71 | Some(Self { 72 | offset: lsb, 73 | width: msb - lsb + 1, 74 | range_type: BitRangeType::BitRange, 75 | }) 76 | } 77 | } 78 | 79 | #[cfg(feature = "serde")] 80 | mod ser_de { 81 | use super::*; 82 | use serde::{Deserialize, Deserializer, Serialize, Serializer}; 83 | 84 | #[derive(serde::Serialize, serde::Deserialize)] 85 | #[serde(untagged)] 86 | enum SerBitRange { 87 | #[serde(rename_all = "camelCase")] 88 | BitRange { 89 | bit_range: String, 90 | }, 91 | #[serde(rename_all = "camelCase")] 92 | OffsetWidth { 93 | bit_offset: u32, 94 | bit_width: u32, 95 | }, 96 | MsbLsb { 97 | lsb: u32, 98 | msb: u32, 99 | }, 100 | } 101 | 102 | impl From for SerBitRange { 103 | fn from(br: BitRange) -> Self { 104 | match br.range_type { 105 | BitRangeType::BitRange => SerBitRange::BitRange { 106 | bit_range: br.bit_range(), 107 | }, 108 | BitRangeType::OffsetWidth => SerBitRange::OffsetWidth { 109 | bit_offset: br.offset, 110 | bit_width: br.width, 111 | }, 112 | BitRangeType::MsbLsb => SerBitRange::MsbLsb { 113 | msb: br.msb(), 114 | lsb: br.lsb(), 115 | }, 116 | } 117 | } 118 | } 119 | 120 | impl Serialize for BitRange { 121 | fn serialize(&self, serializer: S) -> Result 122 | where 123 | S: Serializer, 124 | { 125 | let bit_range = SerBitRange::from(*self); 126 | bit_range.serialize(serializer) 127 | } 128 | } 129 | 130 | impl<'de> Deserialize<'de> for BitRange { 131 | fn deserialize(deserializer: D) -> Result 132 | where 133 | D: Deserializer<'de>, 134 | { 135 | match SerBitRange::deserialize(deserializer)? { 136 | SerBitRange::BitRange { bit_range } => BitRange::from_bit_range(&bit_range) 137 | .ok_or_else(|| serde::de::Error::custom("Can't parse bitRange")), 138 | SerBitRange::OffsetWidth { 139 | bit_offset, 140 | bit_width, 141 | } => Ok(BitRange::from_offset_width(bit_offset, bit_width)), 142 | SerBitRange::MsbLsb { msb, lsb } => Ok(BitRange::from_msb_lsb(msb, lsb)), 143 | } 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /svd-rs/src/registercluster.rs: -------------------------------------------------------------------------------- 1 | use super::{Cluster, Register}; 2 | 3 | /// A [cluster](crate::Cluster) or a [register](crate::Register) 4 | #[cfg_attr( 5 | feature = "serde", 6 | derive(serde::Deserialize, serde::Serialize), 7 | serde(rename_all = "lowercase") 8 | )] 9 | #[derive(Clone, Debug, PartialEq)] 10 | #[allow(clippy::large_enum_variant)] 11 | pub enum RegisterCluster { 12 | /// Register 13 | Register(Register), 14 | /// Cluster 15 | Cluster(Cluster), 16 | } 17 | 18 | impl From for RegisterCluster { 19 | fn from(reg: Register) -> Self { 20 | Self::Register(reg) 21 | } 22 | } 23 | 24 | impl From for RegisterCluster { 25 | fn from(cluser: Cluster) -> Self { 26 | Self::Cluster(cluser) 27 | } 28 | } 29 | 30 | impl RegisterCluster { 31 | /// Name of register or cluster 32 | pub fn name(&self) -> &String { 33 | match self { 34 | Self::Register(r) => &r.name, 35 | Self::Cluster(c) => &c.name, 36 | } 37 | } 38 | /// Description of register or cluster 39 | pub fn description(&self) -> &Option { 40 | match self { 41 | Self::Register(r) => &r.description, 42 | Self::Cluster(c) => &c.description, 43 | } 44 | } 45 | /// Specify the name from which to inherit data 46 | pub fn derived_from(&self) -> &Option { 47 | match self { 48 | Self::Register(r) => &r.derived_from, 49 | Self::Cluster(c) => &c.derived_from, 50 | } 51 | } 52 | /// Address offset of register or cluster 53 | pub fn address_offset(&self) -> u32 { 54 | match self { 55 | Self::Register(r) => r.address_offset, 56 | Self::Cluster(c) => c.address_offset, 57 | } 58 | } 59 | } 60 | 61 | /// Register iterator 62 | pub struct RegisterIter<'a> { 63 | pub(crate) all: std::slice::Iter<'a, RegisterCluster>, 64 | } 65 | 66 | impl<'a> std::iter::Iterator for RegisterIter<'a> { 67 | type Item = &'a Register; 68 | fn next(&mut self) -> Option { 69 | match self.all.next() { 70 | None => None, 71 | Some(RegisterCluster::Register(reg)) => Some(reg), 72 | _ => self.next(), 73 | } 74 | } 75 | } 76 | 77 | /// Mutable register iterator 78 | pub struct RegisterIterMut<'a> { 79 | pub(crate) all: std::slice::IterMut<'a, RegisterCluster>, 80 | } 81 | 82 | impl<'a> std::iter::Iterator for RegisterIterMut<'a> { 83 | type Item = &'a mut Register; 84 | fn next(&mut self) -> Option { 85 | match self.all.next() { 86 | None => None, 87 | Some(RegisterCluster::Register(reg)) => Some(reg), 88 | _ => self.next(), 89 | } 90 | } 91 | } 92 | 93 | /// Cluster iterator 94 | pub struct ClusterIter<'a> { 95 | pub(crate) all: std::slice::Iter<'a, RegisterCluster>, 96 | } 97 | 98 | impl<'a> std::iter::Iterator for ClusterIter<'a> { 99 | type Item = &'a Cluster; 100 | fn next(&mut self) -> Option { 101 | match self.all.next() { 102 | None => None, 103 | Some(RegisterCluster::Cluster(c)) => Some(c), 104 | _ => self.next(), 105 | } 106 | } 107 | } 108 | 109 | /// Mutable cluster iterator 110 | pub struct ClusterIterMut<'a> { 111 | pub(crate) all: std::slice::IterMut<'a, RegisterCluster>, 112 | } 113 | 114 | impl<'a> std::iter::Iterator for ClusterIterMut<'a> { 115 | type Item = &'a mut Cluster; 116 | fn next(&mut self) -> Option { 117 | match self.all.next() { 118 | None => None, 119 | Some(RegisterCluster::Cluster(c)) => Some(c), 120 | _ => self.next(), 121 | } 122 | } 123 | } 124 | 125 | /// Iterator over all registers 126 | pub struct AllRegistersIter<'a> { 127 | pub(crate) rem: Vec<&'a RegisterCluster>, 128 | } 129 | 130 | impl<'a> std::iter::Iterator for AllRegistersIter<'a> { 131 | type Item = &'a Register; 132 | fn next(&mut self) -> Option { 133 | while let Some(b) = self.rem.pop() { 134 | match b { 135 | RegisterCluster::Register(reg) => { 136 | return Some(reg); 137 | } 138 | RegisterCluster::Cluster(cluster) => { 139 | for c in cluster.children.iter().rev() { 140 | self.rem.push(c); 141 | } 142 | } 143 | } 144 | } 145 | None 146 | } 147 | } 148 | 149 | /// Mutable iterator over all registers 150 | pub struct AllRegistersIterMut<'a> { 151 | pub(crate) rem: Vec<&'a mut RegisterCluster>, 152 | } 153 | 154 | impl<'a> std::iter::Iterator for AllRegistersIterMut<'a> { 155 | type Item = &'a mut Register; 156 | fn next(&mut self) -> Option { 157 | while let Some(b) = self.rem.pop() { 158 | match b { 159 | RegisterCluster::Register(reg) => { 160 | return Some(reg); 161 | } 162 | RegisterCluster::Cluster(cluster) => { 163 | for c in cluster.children.iter_mut().rev() { 164 | self.rem.push(c); 165 | } 166 | } 167 | } 168 | } 169 | None 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /svd-rs/src/addressblock.rs: -------------------------------------------------------------------------------- 1 | use super::{BuildError, Protection, SvdError, ValidateLevel}; 2 | 3 | /// An uniquely mapped address block to a peripheral 4 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 5 | #[derive(Clone, Debug, PartialEq, Eq)] 6 | #[non_exhaustive] 7 | pub struct AddressBlock { 8 | /// Specifies the start address of an address block relative to the peripheral [`baseAddress`](crate::Peripheral::base_address). 9 | pub offset: u32, 10 | /// Specifies the number of [`addressUnitBits`](crate::Device::address_unit_bits) being covered by this address block. 11 | pub size: u32, 12 | /// Usage of the address block. 13 | pub usage: AddressBlockUsage, 14 | /// Specify the security privilege to access an address region 15 | #[cfg_attr( 16 | feature = "serde", 17 | serde(default, skip_serializing_if = "Option::is_none") 18 | )] 19 | pub protection: Option, 20 | } 21 | 22 | /// Usage of the address block. 23 | #[cfg_attr( 24 | feature = "serde", 25 | derive(serde::Deserialize, serde::Serialize), 26 | serde(rename_all = "kebab-case") 27 | )] 28 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 29 | pub enum AddressBlockUsage { 30 | /// Registers 31 | Registers, 32 | /// Buffer / Memory 33 | Buffer, 34 | /// Reserved 35 | Reserved, 36 | } 37 | 38 | impl Default for AddressBlockUsage { 39 | fn default() -> Self { 40 | Self::Registers 41 | } 42 | } 43 | 44 | impl AddressBlockUsage { 45 | /// Parse a string into an [`AddressBlockUsage`] value, returning [`Option::None`] if the string is not valid. 46 | pub fn parse_str(s: &str) -> Option { 47 | match s { 48 | "registers" => Some(Self::Registers), 49 | "buffer" => Some(Self::Buffer), 50 | "reserved" => Some(Self::Reserved), 51 | _ => None, 52 | } 53 | } 54 | 55 | /// Convert this [`AddressBlockUsage`] into a static string. 56 | pub const fn as_str(self) -> &'static str { 57 | match self { 58 | Self::Registers => "registers", 59 | Self::Buffer => "buffer", 60 | Self::Reserved => "reserved", 61 | } 62 | } 63 | } 64 | 65 | /// Builder for [`AddressBlock`] 66 | #[derive(Clone, Debug, Default, PartialEq, Eq)] 67 | pub struct AddressBlockBuilder { 68 | offset: Option, 69 | size: Option, 70 | usage: Option, 71 | protection: Option, 72 | } 73 | 74 | impl From for AddressBlockBuilder { 75 | fn from(d: AddressBlock) -> Self { 76 | Self { 77 | offset: Some(d.offset), 78 | size: Some(d.size), 79 | usage: Some(d.usage), 80 | protection: d.protection, 81 | } 82 | } 83 | } 84 | 85 | impl AddressBlockBuilder { 86 | /// Set the offset of the block 87 | pub fn offset(mut self, value: u32) -> Self { 88 | self.offset = Some(value); 89 | self 90 | } 91 | /// Set the size of the block 92 | pub fn size(mut self, value: u32) -> Self { 93 | self.size = Some(value); 94 | self 95 | } 96 | /// Set the usage of the block 97 | pub fn usage(mut self, value: AddressBlockUsage) -> Self { 98 | self.usage = Some(value); 99 | self 100 | } 101 | /// Set the protection of the block 102 | pub fn protection(mut self, value: Option) -> Self { 103 | self.protection = value; 104 | self 105 | } 106 | /// Validate and build a [`AddressBlock`]. 107 | pub fn build(self, lvl: ValidateLevel) -> Result { 108 | let de = AddressBlock { 109 | offset: self 110 | .offset 111 | .ok_or_else(|| BuildError::Uninitialized("offset".to_string()))?, 112 | size: self 113 | .size 114 | .ok_or_else(|| BuildError::Uninitialized("size".to_string()))?, 115 | usage: self 116 | .usage 117 | .ok_or_else(|| BuildError::Uninitialized("usage".to_string()))?, 118 | protection: self.protection, 119 | }; 120 | de.validate(lvl)?; 121 | Ok(de) 122 | } 123 | } 124 | 125 | impl AddressBlock { 126 | /// Make a builder for [`AddressBlock`] 127 | pub fn builder() -> AddressBlockBuilder { 128 | AddressBlockBuilder::default() 129 | } 130 | /// Modify an existing [`AddressBlock`] based on a [builder](AddressBlockBuilder). 131 | pub fn modify_from( 132 | &mut self, 133 | builder: AddressBlockBuilder, 134 | lvl: ValidateLevel, 135 | ) -> Result<(), SvdError> { 136 | if let Some(offset) = builder.offset { 137 | self.offset = offset; 138 | } 139 | if let Some(size) = builder.size { 140 | self.size = size; 141 | } 142 | if let Some(usage) = builder.usage { 143 | self.usage = usage; 144 | } 145 | if builder.protection.is_some() { 146 | self.protection = builder.protection; 147 | } 148 | self.validate(lvl) 149 | } 150 | /// Validate the [`AddressBlock`]. 151 | /// 152 | /// # Notes 153 | /// 154 | /// This doesn't do anything. 155 | pub fn validate(&self, _lvl: ValidateLevel) -> Result<(), SvdError> { 156 | Ok(()) 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /svd-rs/src/derive_from.rs: -------------------------------------------------------------------------------- 1 | //! Implementations of DeriveFrom, setting non-explicit fields. 2 | use crate::{ 3 | ClusterInfo, EnumeratedValues, FieldInfo, MaybeArray, PeripheralInfo, RegisterInfo, 4 | RegisterProperties, 5 | }; 6 | 7 | /// Fill empty fields of structure with values of other structure 8 | pub trait DeriveFrom { 9 | /// Derive contents 10 | fn derive_from(&self, other: &Self) -> Self; 11 | } 12 | 13 | impl DeriveFrom for ClusterInfo { 14 | fn derive_from(&self, other: &Self) -> Self { 15 | let mut derived = self.clone(); 16 | derived.default_register_properties = derived 17 | .default_register_properties 18 | .derive_from(&other.default_register_properties); 19 | derived.header_struct_name = derived 20 | .header_struct_name 21 | .or_else(|| other.header_struct_name.clone()); 22 | if derived.children.is_empty() { 23 | derived.children = other.children.clone(); 24 | } 25 | derived 26 | } 27 | } 28 | 29 | impl DeriveFrom for EnumeratedValues { 30 | fn derive_from(&self, other: &Self) -> Self { 31 | let mut derived = self.clone(); 32 | derived.usage = derived.usage.or(other.usage); 33 | if derived.values.is_empty() { 34 | derived.values = other.values.clone(); 35 | } 36 | derived 37 | } 38 | } 39 | 40 | impl DeriveFrom for PeripheralInfo { 41 | fn derive_from(&self, other: &Self) -> Self { 42 | let mut derived = self.clone(); 43 | derived.version = derived.version.or_else(|| other.version.clone()); 44 | derived.description = derived.description.or_else(|| other.description.clone()); 45 | derived.group_name = derived.group_name.or_else(|| other.group_name.clone()); 46 | derived.prepend_to_name = derived 47 | .prepend_to_name 48 | .or_else(|| other.prepend_to_name.clone()); 49 | derived.append_to_name = derived 50 | .append_to_name 51 | .or_else(|| other.append_to_name.clone()); 52 | derived.header_struct_name = derived 53 | .header_struct_name 54 | .or_else(|| other.header_struct_name.clone()); 55 | derived.default_register_properties = derived 56 | .default_register_properties 57 | .derive_from(&other.default_register_properties); 58 | derived.registers = derived.registers.or_else(|| other.registers.clone()); 59 | derived.address_block = derived 60 | .address_block 61 | .or_else(|| other.address_block.clone()); 62 | if derived.interrupt.is_empty() { 63 | derived.interrupt = other.interrupt.clone(); 64 | } 65 | derived 66 | } 67 | } 68 | 69 | impl DeriveFrom for RegisterInfo { 70 | fn derive_from(&self, other: &Self) -> Self { 71 | let mut derived = self.clone(); 72 | derived.description = derived.description.or_else(|| other.description.clone()); 73 | derived.properties = derived.properties.derive_from(&other.properties); 74 | derived.fields = derived.fields.or_else(|| other.fields.clone()); 75 | derived.write_constraint = derived.write_constraint.or(other.write_constraint); 76 | derived.read_action = derived.read_action.or(other.read_action); 77 | derived.modified_write_values = derived 78 | .modified_write_values 79 | .or(other.modified_write_values); 80 | derived 81 | } 82 | } 83 | 84 | impl DeriveFrom for RegisterProperties { 85 | fn derive_from(&self, other: &Self) -> Self { 86 | let mut derived = *self; 87 | derived.size = derived.size.or(other.size); 88 | derived.access = derived.access.or(other.access); 89 | derived.protection = derived.protection.or(other.protection); 90 | derived.reset_value = derived.reset_value.or(other.reset_value); 91 | derived.reset_mask = derived.reset_mask.or(other.reset_mask); 92 | derived 93 | } 94 | } 95 | 96 | impl DeriveFrom for FieldInfo { 97 | fn derive_from(&self, other: &Self) -> Self { 98 | let mut derived = self.clone(); 99 | derived.description = derived.description.or_else(|| other.description.clone()); 100 | derived.access = derived.access.or(other.access); 101 | if derived.enumerated_values.is_empty() { 102 | derived.enumerated_values = other.enumerated_values.clone(); 103 | } 104 | derived.write_constraint = derived.write_constraint.or(other.write_constraint); 105 | derived.read_action = derived.read_action.or(other.read_action); 106 | derived.modified_write_values = derived 107 | .modified_write_values 108 | .or(other.modified_write_values); 109 | derived 110 | } 111 | } 112 | 113 | impl DeriveFrom for MaybeArray 114 | where 115 | T: DeriveFrom + crate::Name, 116 | { 117 | fn derive_from(&self, other: &Self) -> Self { 118 | match (self, other) { 119 | (Self::Single(info), Self::Single(other_info)) => { 120 | Self::Single(info.derive_from(other_info)) 121 | } 122 | (Self::Single(info), Self::Array(other_info, other_dim)) => { 123 | if info.name().contains("%s") { 124 | let mut dim = other_dim.clone(); 125 | dim.dim_name = None; 126 | Self::Array(info.derive_from(other_info), dim) 127 | } else { 128 | Self::Single(info.derive_from(other_info)) 129 | } 130 | } 131 | (Self::Array(info, dim), Self::Single(other_info) | Self::Array(other_info, _)) => { 132 | Self::Array(info.derive_from(other_info), dim.clone()) 133 | } 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /svd-encoder/src/register.rs: -------------------------------------------------------------------------------- 1 | use svd_rs::Field; 2 | 3 | use super::{ 4 | new_node, Config, Element, ElementMerge, Encode, EncodeChildren, EncodeError, XMLNode, 5 | }; 6 | 7 | use crate::{ 8 | config::{change_case, format_number, DerivableSorting, Sorting}, 9 | svd::{Register, RegisterInfo}, 10 | }; 11 | 12 | impl Encode for Register { 13 | type Error = EncodeError; 14 | 15 | fn encode_with_config(&self, config: &Config) -> Result { 16 | match self { 17 | Self::Single(info) => info.encode_with_config(config), 18 | Self::Array(info, array_info) => { 19 | let mut base = Element::new("register"); 20 | base.merge(&array_info.encode_with_config(config)?); 21 | base.merge(&info.encode_with_config(config)?); 22 | Ok(base) 23 | } 24 | } 25 | } 26 | } 27 | 28 | impl Encode for RegisterInfo { 29 | type Error = EncodeError; 30 | 31 | fn encode_with_config(&self, config: &Config) -> Result { 32 | let mut elem = Element::new("register"); 33 | elem.children.push(new_node( 34 | "name", 35 | change_case(&self.name, config.register_name), 36 | )); 37 | 38 | if let Some(v) = &self.display_name { 39 | elem.children.push(new_node("displayName", v.clone())); 40 | } 41 | 42 | if let Some(v) = &self.description { 43 | elem.children.push(new_node("description", v.clone())); 44 | } 45 | 46 | if let Some(v) = &self.alternate_group { 47 | elem.children 48 | .push(new_node("alternateGroup", v.to_string())); 49 | } 50 | 51 | if let Some(v) = &self.alternate_register { 52 | elem.children.push(new_node( 53 | "alternateRegister", 54 | change_case(v, config.register_name), 55 | )); 56 | } 57 | 58 | elem.children.push(new_node( 59 | "addressOffset", 60 | format_number(self.address_offset, config.register_address_offset), 61 | )); 62 | 63 | elem.children 64 | .extend(self.properties.encode_with_config(config)?); 65 | 66 | if let Some(v) = &self.datatype { 67 | elem.children.push(v.encode_node_with_config(config)?); 68 | } 69 | 70 | if let Some(v) = &self.modified_write_values { 71 | elem.children.push(v.encode_node_with_config(config)?); 72 | } 73 | 74 | if let Some(v) = &self.write_constraint { 75 | elem.children.push(v.encode_node()?); 76 | } 77 | 78 | if let Some(v) = &self.read_action { 79 | elem.children.push(v.encode_node()?); 80 | } 81 | 82 | if let Some(v) = &self.fields { 83 | let children: Result, _> = 84 | if config.field_sorting == DerivableSorting::Unchanged(None) { 85 | v.iter() 86 | .map(|field| field.encode_node_with_config(config)) 87 | .collect() 88 | } else { 89 | sort_derived_fields(v, config.field_sorting) 90 | .into_iter() 91 | .map(|field| field.encode_node_with_config(config)) 92 | .collect() 93 | }; 94 | 95 | let children = children?; 96 | if !children.is_empty() { 97 | let mut fields = Element::new("fields"); 98 | fields.children = children; 99 | elem.children.push(XMLNode::Element(fields)); 100 | } 101 | } 102 | 103 | if let Some(v) = &self.derived_from { 104 | elem.attributes.insert( 105 | String::from("derivedFrom"), 106 | change_case(v, config.register_name), 107 | ); 108 | } 109 | 110 | Ok(elem) 111 | } 112 | } 113 | 114 | fn sort_fields(refs: &mut [&Field], sorting: Option) { 115 | if let Some(sorting) = sorting { 116 | match sorting { 117 | Sorting::Offset => refs.sort_by_key(|f| f.bit_offset()), 118 | Sorting::OffsetReversed => { 119 | refs.sort_by_key(|f| -(f.bit_offset() as i32)); 120 | } 121 | Sorting::Name => refs.sort_by_key(|f| &f.name), 122 | } 123 | } 124 | } 125 | 126 | fn sort_derived_fields(v: &[Field], sorting: DerivableSorting) -> Vec<&Field> { 127 | match sorting { 128 | DerivableSorting::Unchanged(sorting) => { 129 | let mut refs = v.iter().collect::>(); 130 | sort_fields(&mut refs, sorting); 131 | refs 132 | } 133 | DerivableSorting::DeriveLast(sorting) => { 134 | let mut common_refs = Vec::with_capacity(v.len()); 135 | let mut derived_refs = Vec::new(); 136 | for f in v.iter() { 137 | if f.derived_from.is_some() { 138 | derived_refs.push(f); 139 | } else { 140 | let mut derived = false; 141 | for ev in &f.enumerated_values { 142 | if ev.derived_from.is_some() { 143 | derived_refs.push(f); 144 | derived = true; 145 | break; 146 | } 147 | } 148 | if !derived { 149 | common_refs.push(f); 150 | } 151 | } 152 | } 153 | sort_fields(&mut common_refs, sorting); 154 | sort_fields(&mut derived_refs, sorting); 155 | common_refs.extend(derived_refs); 156 | 157 | common_refs 158 | } 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /tests/src/field.rs: -------------------------------------------------------------------------------- 1 | use super::run_test; 2 | use crate::svd::{ 3 | Access, BitRange, BitRangeType, DimElement, EnumeratedValue, EnumeratedValues, Field, 4 | FieldInfo, ValidateLevel, 5 | }; 6 | 7 | #[test] 8 | fn decode_encode() { 9 | let tests = vec![( 10 | Field::Array( 11 | FieldInfo::builder() 12 | .name("MODE%s".to_string()) 13 | .derived_from(Some("other_field".to_string())) 14 | .bit_range(BitRange::from_offset_width(24, 2)) 15 | .build(ValidateLevel::Strict) 16 | .unwrap(), 17 | DimElement::builder() 18 | .dim(2) 19 | .dim_increment(4) 20 | .dim_index(Some(vec!["10".to_string(), "20".to_string()])) 21 | .build(ValidateLevel::Strict) 22 | .unwrap(), 23 | ), 24 | " 25 | 26 | 2 27 | 0x4 28 | 10,20 29 | MODE%s 30 | 24 31 | 2 32 | 33 | ", 34 | " 35 | 36 | 2 37 | 0x4 38 | 10,20 39 | MODE%s 40 | 24 41 | 2 42 | 43 | ", 44 | )]; 45 | run_test::(&tests[..], None, None); 46 | 47 | let parse_config = svd_parser::Config::default(); 48 | let mut encode_config = svd_encoder::Config::default(); 49 | encode_config.update("field_name", "Snake"); 50 | encode_config.update("field_bit_range", "MsbLsb"); 51 | 52 | let tests = [( 53 | FieldInfo::builder() 54 | .name("MODE".to_string()) 55 | .derived_from(Some("other_field".to_string())) 56 | .bit_range(BitRange { 57 | offset: 24, 58 | width: 2, 59 | range_type: BitRangeType::OffsetWidth, 60 | }) 61 | .build(ValidateLevel::Strict) 62 | .unwrap(), 63 | " 64 | 65 | MODE 66 | 24 67 | 2 68 | 69 | ", 70 | " 71 | 72 | mode 73 | 24 74 | 25 75 | 76 | ", 77 | )]; 78 | 79 | run_test::(&tests[..], Some(parse_config), Some(encode_config)); 80 | } 81 | 82 | #[test] 83 | fn decode_encode_info() { 84 | let tests = vec![ 85 | ( 86 | FieldInfo::builder() 87 | .name("MODE".to_string()) 88 | .description(Some("Read Mode".to_string())) 89 | .bit_range(BitRange { 90 | offset: 24, 91 | width: 2, 92 | range_type: BitRangeType::OffsetWidth, 93 | }) 94 | .access(Some(Access::ReadWrite)) 95 | .enumerated_values(vec![EnumeratedValues::builder() 96 | .values(vec![EnumeratedValue::builder() 97 | .name("WS0".to_string()) 98 | .description(Some( 99 | "Zero wait-states inserted in fetch or read transfers".to_string(), 100 | )) 101 | .value(Some(0)) 102 | .is_default(None) 103 | .build(ValidateLevel::Strict) 104 | .unwrap()]) 105 | .build(ValidateLevel::Strict) 106 | .unwrap()]) 107 | .build(ValidateLevel::Strict) 108 | .unwrap(), 109 | " 110 | 111 | MODE 112 | Read Mode 113 | 24 114 | 2 115 | read-write 116 | 117 | 118 | WS0 119 | Zero wait-states inserted in fetch or read transfers 120 | 0 121 | 122 | 123 | 124 | ", 125 | " 126 | 127 | MODE 128 | Read Mode 129 | 24 130 | 2 131 | read-write 132 | 133 | 134 | WS0 135 | Zero wait-states inserted in fetch or read transfers 136 | 0 137 | 138 | 139 | 140 | ", 141 | ), 142 | ( 143 | FieldInfo::builder() 144 | .name("MODE".to_string()) 145 | .derived_from(Some("other_field".to_string())) 146 | .bit_range(BitRange { 147 | offset: 24, 148 | width: 2, 149 | range_type: BitRangeType::OffsetWidth, 150 | }) 151 | .build(ValidateLevel::Strict) 152 | .unwrap(), 153 | " 154 | 155 | MODE 156 | 24 157 | 2 158 | 159 | ", 160 | " 161 | 162 | MODE 163 | 24 164 | 2 165 | 166 | ", 167 | ), 168 | ]; 169 | 170 | run_test::(&tests[..], None, None); 171 | } 172 | -------------------------------------------------------------------------------- /svd-rs/src/enumeratedvalue.rs: -------------------------------------------------------------------------------- 1 | use super::{BuildError, Description, EmptyToNone, Name, SvdError, ValidateLevel}; 2 | 3 | /// Describes a single entry in the enumeration. 4 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 5 | #[derive(Clone, Debug, PartialEq, Eq)] 6 | #[non_exhaustive] 7 | pub struct EnumeratedValue { 8 | /// String describing the semantics of the value. Can be displayed instead of the value 9 | pub name: String, 10 | 11 | /// Extended string describing the value 12 | #[cfg_attr( 13 | feature = "serde", 14 | serde(default, skip_serializing_if = "Option::is_none") 15 | )] 16 | pub description: Option, 17 | 18 | /// Defines the constant for the bit-field as decimal, hexadecimal or binary number 19 | #[cfg_attr( 20 | feature = "serde", 21 | serde(default, skip_serializing_if = "Option::is_none") 22 | )] 23 | pub value: Option, 24 | 25 | /// Defines the name and description for all other values that are not listed explicitly 26 | #[cfg_attr( 27 | feature = "serde", 28 | serde(default, skip_serializing_if = "Option::is_none") 29 | )] 30 | pub is_default: Option, 31 | } 32 | 33 | /// Errors for [`EnumeratedValue::validate`] 34 | #[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)] 35 | pub enum Error { 36 | /// No value was specified 37 | #[error("EnumeratedValue has no `value` or `is_default`")] 38 | AbsentValue, 39 | /// Passed both value and isDefault, only checked in strict mode 40 | #[error("EnumeratedValue with `value` (passed {0:?}) should not have `is_default(True)`")] 41 | ValueAndDefault(Option), 42 | /// The value is not in range. 43 | #[error("Value {0} out of range [{} - {}]", .1.start, .1.end - 1)] 44 | OutOfRange(u64, core::ops::Range), 45 | } 46 | 47 | /// Builder for [`EnumeratedValue`] 48 | #[derive(Clone, Debug, Default, PartialEq, Eq)] 49 | pub struct EnumeratedValueBuilder { 50 | name: Option, 51 | description: Option, 52 | value: Option, 53 | is_default: Option, 54 | } 55 | 56 | impl From for EnumeratedValueBuilder { 57 | fn from(e: EnumeratedValue) -> Self { 58 | Self { 59 | name: Some(e.name), 60 | description: e.description, 61 | value: e.value, 62 | is_default: e.is_default, 63 | } 64 | } 65 | } 66 | 67 | impl EnumeratedValueBuilder { 68 | /// Set the name of the enumerated value. 69 | pub fn name(mut self, value: String) -> Self { 70 | self.name = Some(value); 71 | self 72 | } 73 | /// Set the description of the enumerated value. 74 | pub fn description(mut self, value: Option) -> Self { 75 | self.description = value; 76 | self 77 | } 78 | /// Set the value of the enumerated value. 79 | pub fn value(mut self, value: Option) -> Self { 80 | self.value = value; 81 | self 82 | } 83 | #[allow(clippy::wrong_self_convention)] 84 | /// Set if the enumerated value is defaulted for non-explicit values. 85 | pub fn is_default(mut self, value: Option) -> Self { 86 | self.is_default = value; 87 | self 88 | } 89 | /// Validate and build a [`EnumeratedValue`]. 90 | pub fn build(self, lvl: ValidateLevel) -> Result { 91 | let ev = EnumeratedValue { 92 | name: self 93 | .name 94 | .ok_or_else(|| BuildError::Uninitialized("name".to_string()))?, 95 | description: self.description.empty_to_none(), 96 | value: self.value, 97 | is_default: self.is_default, 98 | }; 99 | ev.validate(lvl)?; 100 | Ok(ev) 101 | } 102 | } 103 | 104 | impl EnumeratedValue { 105 | /// Enumerated value is defaulted for non-explicit values 106 | pub fn is_default(&self) -> bool { 107 | matches!(self.is_default, Some(true)) 108 | } 109 | /// Make a builder for [`EnumeratedValue`] 110 | pub fn builder() -> EnumeratedValueBuilder { 111 | EnumeratedValueBuilder::default() 112 | } 113 | /// Modify an existing [`EnumeratedValue`] based on a [builder](EnumeratedValueBuilder). 114 | pub fn modify_from( 115 | &mut self, 116 | builder: EnumeratedValueBuilder, 117 | lvl: ValidateLevel, 118 | ) -> Result<(), SvdError> { 119 | if let Some(name) = builder.name { 120 | self.name = name; 121 | } 122 | if builder.description.is_some() { 123 | self.description = builder.description.empty_to_none(); 124 | } 125 | if builder.value.is_some() { 126 | self.value = builder.value; 127 | } 128 | if builder.is_default.is_some() { 129 | self.is_default = builder.is_default; 130 | } 131 | self.validate(lvl) 132 | } 133 | /// Validate the [`EnumeratedValue`]. 134 | pub fn validate(&self, lvl: ValidateLevel) -> Result<(), SvdError> { 135 | if !lvl.is_disabled() { 136 | if lvl.is_strict() { 137 | super::check_name(&self.name, "name")?; 138 | } 139 | match (self.value.is_some(), self.is_default()) { 140 | (false, false) => Err(Error::AbsentValue.into()), 141 | (true, true) if lvl.is_strict() => Err(Error::ValueAndDefault(self.value).into()), 142 | _ => Ok(()), 143 | } 144 | } else { 145 | Ok(()) 146 | } 147 | } 148 | pub(crate) fn check_range(&self, range: &core::ops::Range) -> Result<(), SvdError> { 149 | match &self.value { 150 | Some(x) if !range.contains(x) => Err(Error::OutOfRange(*x, range.clone()).into()), 151 | _ => Ok(()), 152 | } 153 | } 154 | } 155 | 156 | impl Name for EnumeratedValue { 157 | fn name(&self) -> &str { 158 | &self.name 159 | } 160 | } 161 | 162 | impl Description for EnumeratedValue { 163 | fn description(&self) -> Option<&str> { 164 | self.description.as_deref() 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /svd-rs/src/registerproperties.rs: -------------------------------------------------------------------------------- 1 | use super::{Access, Protection, SvdError, ValidateLevel}; 2 | 3 | /// Errors from [`RegisterProperties::validate`] 4 | #[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)] 5 | pub enum Error { 6 | /// Value is too large 7 | #[error("Reset value 0x{0:x} doesn't fit in {1} bits")] 8 | ValueTooLarge(u64, u32), 9 | /// reset value is conflicting with the mask 10 | #[error("Reset value 0x{0:x} conflicts with mask '0x{1:x}'")] 11 | MaskConflict(u64, u64), 12 | /// Mask doesn't fit 13 | #[error("Mask value 0x{0:x} doesn't fit in {1} bits")] 14 | MaskTooLarge(u64, u32), 15 | } 16 | 17 | /// Register default properties 18 | #[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] 25 | pub struct RegisterProperties { 26 | /// Bit-width of register 27 | #[cfg_attr( 28 | feature = "serde", 29 | serde(default, skip_serializing_if = "Option::is_none") 30 | )] 31 | pub size: Option, 32 | 33 | /// Access rights for register 34 | #[cfg_attr( 35 | feature = "serde", 36 | serde(default, skip_serializing_if = "Option::is_none") 37 | )] 38 | pub access: Option, 39 | 40 | /// Specify the security privilege to access an address region 41 | #[cfg_attr( 42 | feature = "serde", 43 | serde(default, skip_serializing_if = "Option::is_none") 44 | )] 45 | pub protection: Option, 46 | 47 | /// Register value at RESET 48 | #[cfg_attr( 49 | feature = "serde", 50 | serde(default, skip_serializing_if = "Option::is_none") 51 | )] 52 | pub reset_value: Option, 53 | 54 | /// Define which register bits have a defined reset value 55 | #[cfg_attr( 56 | feature = "serde", 57 | serde(default, skip_serializing_if = "Option::is_none") 58 | )] 59 | pub reset_mask: Option, 60 | } 61 | 62 | impl RegisterProperties { 63 | /// Create a new [`RegisterProperties`]. 64 | pub fn new() -> Self { 65 | Self::default() 66 | } 67 | /// Modify an existing [`RegisterProperties`] based on another. 68 | 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 | /// Validate the [`RegisterProperties`] 92 | 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 | /// Set the size of the register properties. 99 | pub fn size(mut self, value: Option) -> Self { 100 | self.size = value; 101 | self 102 | } 103 | /// Set the access of the register properties. 104 | pub fn access(mut self, value: Option) -> Self { 105 | self.access = value; 106 | self 107 | } 108 | /// Set the protection of the register properties. 109 | pub fn protection(mut self, value: Option) -> Self { 110 | self.protection = value; 111 | self 112 | } 113 | /// Set the reset_value of the register properties. 114 | pub fn reset_value(mut self, value: Option) -> Self { 115 | self.reset_value = value; 116 | self 117 | } 118 | /// Set the reset_mask of the register properties. 119 | pub fn reset_mask(mut self, value: Option) -> Self { 120 | self.reset_mask = value; 121 | self 122 | } 123 | /// Validate and build a [`RegisterProperties`]. 124 | pub fn build(self, lvl: ValidateLevel) -> Result { 125 | self.validate(lvl)?; 126 | Ok(self) 127 | } 128 | } 129 | 130 | pub(crate) fn check_reset_value( 131 | size: Option, 132 | value: Option, 133 | mask: Option, 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)] 160 | mod 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 | } 186 | -------------------------------------------------------------------------------- /svd-rs/src/enumeratedvalues.rs: -------------------------------------------------------------------------------- 1 | use super::{EmptyToNone, EnumeratedValue, SvdError, Usage, ValidateLevel}; 2 | 3 | /// A map describing unsigned integers and their description and name. 4 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 5 | #[derive(Clone, Debug, PartialEq, Eq)] 6 | #[non_exhaustive] 7 | pub struct EnumeratedValues { 8 | /// Identifier for the whole enumeration section 9 | #[cfg_attr( 10 | feature = "serde", 11 | serde(default, skip_serializing_if = "Option::is_none") 12 | )] 13 | pub name: Option, 14 | 15 | /// Usage of the values 16 | #[cfg_attr( 17 | feature = "serde", 18 | serde(default, skip_serializing_if = "Option::is_none") 19 | )] 20 | pub usage: Option, 21 | 22 | /// Makes a copy from a previously defined enumeratedValues section. 23 | /// No modifications are allowed 24 | #[cfg_attr( 25 | feature = "serde", 26 | serde(default, skip_serializing_if = "Option::is_none") 27 | )] 28 | pub derived_from: Option, 29 | 30 | /// List of variants. The number of required items depends on the bit-width of the associated field. 31 | #[cfg_attr( 32 | feature = "serde", 33 | serde(default, skip_serializing_if = "Vec::is_empty") 34 | )] 35 | pub values: Vec, 36 | } 37 | 38 | /// Errors for [`EnumeratedValues::validate`] 39 | #[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)] 40 | pub enum Error { 41 | /// Enum is empty 42 | #[error("EnumeratedValues is empty")] 43 | Empty, 44 | } 45 | 46 | /// Builder for [`EnumeratedValues`] 47 | #[derive(Clone, Debug, Default, PartialEq, Eq)] 48 | pub struct EnumeratedValuesBuilder { 49 | name: Option, 50 | usage: Option, 51 | derived_from: Option, 52 | values: Option>, 53 | } 54 | 55 | impl From for EnumeratedValuesBuilder { 56 | fn from(e: EnumeratedValues) -> Self { 57 | Self { 58 | name: e.name, 59 | usage: e.usage, 60 | derived_from: e.derived_from, 61 | values: Some(e.values), 62 | } 63 | } 64 | } 65 | 66 | impl EnumeratedValuesBuilder { 67 | /// Set the name of the enumerated values 68 | pub fn name(mut self, value: Option) -> Self { 69 | self.name = value; 70 | self 71 | } 72 | /// Set the usage of the enumerated values 73 | pub fn usage(mut self, value: Option) -> Self { 74 | self.usage = value; 75 | self 76 | } 77 | /// Set the derived_from attribute for the enumerated values 78 | pub fn derived_from(mut self, value: Option) -> Self { 79 | self.derived_from = value; 80 | self 81 | } 82 | /// Set the values 83 | pub fn values(mut self, value: Vec) -> Self { 84 | self.values = Some(value); 85 | self 86 | } 87 | /// Validate and build a [`EnumeratedValues`]. 88 | pub fn build(self, lvl: ValidateLevel) -> Result { 89 | let evs = EnumeratedValues { 90 | name: self.name.empty_to_none(), 91 | usage: self.usage, 92 | derived_from: self.derived_from, 93 | values: self.values.unwrap_or_default(), 94 | }; 95 | evs.validate(lvl)?; 96 | Ok(evs) 97 | } 98 | } 99 | 100 | impl EnumeratedValues { 101 | /// Return default value if present 102 | pub fn default_value(&self) -> Option<&EnumeratedValue> { 103 | self.values.iter().find(|&v| v.is_default()) 104 | } 105 | 106 | /// Make a builder for [`EnumeratedValues`] 107 | pub fn builder() -> EnumeratedValuesBuilder { 108 | EnumeratedValuesBuilder::default() 109 | } 110 | /// Modify an existing [`EnumeratedValues`] based on a [builder](EnumeratedValuesBuilder). 111 | pub fn modify_from( 112 | &mut self, 113 | builder: EnumeratedValuesBuilder, 114 | lvl: ValidateLevel, 115 | ) -> Result<(), SvdError> { 116 | if builder.derived_from.is_some() { 117 | self.name = None; 118 | self.usage = None; 119 | self.values = Vec::new(); 120 | } else { 121 | if builder.name.is_some() { 122 | self.name = builder.name.empty_to_none(); 123 | } 124 | if builder.usage.is_some() { 125 | self.usage = builder.usage; 126 | } 127 | if let Some(values) = builder.values { 128 | self.values = values; 129 | } 130 | } 131 | self.validate(lvl) 132 | } 133 | /// Validate the [`EnumeratedValues`] 134 | pub fn validate(&self, lvl: ValidateLevel) -> Result<(), SvdError> { 135 | if !lvl.is_disabled() { 136 | if lvl.is_strict() { 137 | if let Some(name) = self.name.as_ref() { 138 | super::check_name(name, "name")?; 139 | } 140 | } 141 | if let Some(_dname) = self.derived_from.as_ref() { 142 | if lvl.is_strict() { 143 | super::check_derived_name(_dname, "derivedFrom")?; 144 | } 145 | Ok(()) 146 | } else if self.values.is_empty() { 147 | Err(Error::Empty.into()) 148 | } else { 149 | Ok(()) 150 | } 151 | } else { 152 | Ok(()) 153 | } 154 | } 155 | /// Validate the [`EnumeratedValues`] recursively. 156 | pub fn validate_all(&self, lvl: ValidateLevel) -> Result<(), SvdError> { 157 | for ev in &self.values { 158 | ev.validate(lvl)?; 159 | } 160 | self.validate(lvl) 161 | } 162 | pub(crate) fn check_range(&self, range: core::ops::Range) -> Result<(), SvdError> { 163 | for v in self.values.iter() { 164 | v.check_range(&range)?; 165 | } 166 | Ok(()) 167 | } 168 | /// Get the usage of these enumerated values. 169 | pub fn usage(&self) -> Option { 170 | if self.derived_from.is_some() { 171 | None 172 | } else { 173 | Some(self.usage.unwrap_or_default()) 174 | } 175 | } 176 | 177 | /// Get `enumeratedValue` by name 178 | pub fn get_value(&self, name: &str) -> Option<&EnumeratedValue> { 179 | self.values.iter().find(|e| e.name == name) 180 | } 181 | 182 | /// Get mutable `enumeratedValue` by name 183 | pub fn get_mut_value(&mut self, name: &str) -> Option<&mut EnumeratedValue> { 184 | self.values.iter_mut().find(|e| e.name == name) 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /svd-rs/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 6 | and this project adheres to [Semantic Versioning](http://semver.org/). 7 | 8 | ## [Unreleased] 9 | 10 | ## [v0.14.12] - 2025-03-11 11 | 12 | - Bump MSRV to 1.70.0 13 | - Add missing entry for `address_block` in `DeriveFrom` impl 14 | 15 | ## [v0.14.11] - 2025-02-10 16 | 17 | - Partially revert #283. Check name on "%s" when derive array 18 | 19 | ## [v0.14.10] - 2025-02-08 20 | 21 | - Do not derive `DimElement` 22 | - Revert the `riscv` elements, as well as the `unstable-riscv` feature. 23 | - Add `bitmask` for `FieldInfo`, `Field` and `RegisterInfo` 24 | 25 | ## [v0.14.9] - 2024-08-20 26 | 27 | - Add `riscv::Exception` for custom exception source enumerations. 28 | - Add `riscv` element for configuration parameters related to RISC-V targets. 29 | You must use the `unstable-riscv` feature to enable this exeperimental element. 30 | - Add `DataType` 31 | - Fix run-time panic for write constraint check 32 | 33 | ## [v0.14.8] - 2024-02-13 34 | 35 | - add `maybe_array` constructors 36 | - Bump MSRV to 1.65.0 37 | - Bump `regex` to 1.10 38 | 39 | ## [v0.14.7] - 2024-01-03 40 | 41 | - Bump MSRV to 1.61.0 42 | - use close range in `EnumeratedValue` error message 43 | 44 | ## [v0.14.6] - 2023-11-29 45 | 46 | - fix `validate` on `Disabled` level, remove `mut` 47 | - add `validate_all` for `Device` and childrens for recursive tree check 48 | 49 | ## [v0.14.5] - 2023-11-22 50 | 51 | - `default_value` for `EnumeratedValues` 52 | - moved `isDefault` and `value` check on `EnumeratedValue` to strict mode 53 | 54 | ## [v0.14.4] - 2023-11-15 55 | 56 | - Add `expand` functions for arrays 57 | - Fix `indexes_as_range` 58 | 59 | ## [v0.14.3] - 2023-10-24 60 | 61 | - Bump MSRV to 1.58.0 62 | - `array::names` returns custom names if specified 63 | - add `Description` trait and `array::descriptions` 64 | - add write constraint range check 65 | 66 | ## [v0.14.2] - 2023-05-10 67 | 68 | - Add support of `a-Z` for `dimIndex` 69 | - Add `name`, `description`, `address_offset` for `RegisterCluster` 70 | 71 | ## [v0.14.1] - 2022-10-23 72 | 73 | - (De)serialize `dimIndex` (from)to string 74 | 75 | ## [v0.14.0] - 2022-07-19 76 | 77 | - Bump MSRV to 1.56.0 (2021) 78 | 79 | ## [v0.13.2] - 2022-04-12 80 | 81 | - Fix `schema_version` deserialization 82 | 83 | ## [v0.13.1] - 2022-02-12 84 | 85 | - add `indexes_as_range` for `DimElement` 86 | 87 | ## [v0.13.0] - 2022-01-04 88 | 89 | - fixed `BitRange` deserializing 90 | - skip serializing optional fields in `Cpu` if empty 91 | - skip serializing `values` in `EnumeratedValues` if empty 92 | - add `names` function for arrays, `base_addresses` for `Peripheral`, 93 | `address_offsets` for `Register` and `Cluster`, `bit_offsets` for `Field` arrays 94 | - add missing fields in `Device`, require `version`, `description`, `address_unit_bits` and `width`, 95 | also `schema_version` is required, but skipped during (de)serialization 96 | - merge `register` with `registerinfo` modules same as other `info`s 97 | - camelCase for WriteConstraint serialization 98 | - `EnumeratedValues.usage()` now return `None` if it is derived, fix bug in usage check 99 | - Use generic `MaybeArray` enum for types which can be either collected into SVD arrays or have only one instance 100 | - `Name` trait for structures that has `name` field 101 | - improves in iterators 102 | - `get_enumerated_values` by usage 103 | 104 | ## [v0.12.1] - 2021-12-08 105 | 106 | - Rename `reg_iter` to `all_registers`, 107 | - Add `registers`, `clusters`, `fields` methods that create iterators 108 | - Add `get_register`, `get_cluster`, `get_field` (also `mut`) which take child by name 109 | 110 | ## [v0.12.0] - 2021-11-11 111 | 112 | - Bump dependencies 113 | - Add check for wrong size of `bitRange` width 114 | - Don't clone when serialize 115 | - Add optional entries to `Cpu` 116 | - `AddressBlock` & `Interrupt` now use builders 117 | - Add `dim_name` and `dim_array_index` to `DimElement` 118 | - Add `alternate_peripheral`, `prepend_to_name`, `append_to_name`, 119 | `header_struct_name` to `PeripheralInfo`, `alternate_cluster` to `ClusterInfo` 120 | - Add `protection` to `RegisterProperties` and `AddressBlock` 121 | - Add `readAction` to `RegisterInfo` and `FieldInfo` 122 | - Add `single` and `array` for `Info` types, 123 | `is_single` and `is_array` for `Peripheral`, `Cluster`, `Register` and `Field` 124 | - Add array support for peripherals 125 | 126 | ## [v0.11.2] - 2021-11-04 127 | 128 | - Implement `DeriveFrom` for `Cluster`, `Register` and `Field` 129 | 130 | ## [v0.11.1] - 2021-10-02 131 | 132 | - Reexport builders 133 | - Fix typo in Access::can_write 134 | 135 | ## [v0.11.0] - 2021-10-02 136 | - Splitted from `svd-parser` 137 | 138 | Previous versions in common [changelog](../CHANGELOG.md). 139 | 140 | [Unreleased]: https://github.com/rust-embedded/svd/compare/svd-rs-v0.14.12...HEAD 141 | [v0.14.12]: https://github.com/rust-embedded/svd/compare/svd-rs-v0.14.11...svd-rs-v0.14.12 142 | [v0.14.11]: https://github.com/rust-embedded/svd/compare/svd-rs-v0.14.10...svd-rs-v0.14.11 143 | [v0.14.10]: https://github.com/rust-embedded/svd/compare/svd-rs-v0.14.9...svd-rs-v0.14.10 144 | [v0.14.9]: https://github.com/rust-embedded/svd/compare/svd-rs-v0.14.8...svd-rs-v0.14.9 145 | [v0.14.8]: https://github.com/rust-embedded/svd/compare/svd-rs-v0.14.7...svd-rs-v0.14.8 146 | [v0.14.7]: https://github.com/rust-embedded/svd/compare/svd-rs-v0.14.6...svd-rs-v0.14.7 147 | [v0.14.6]: https://github.com/rust-embedded/svd/compare/svd-rs-v0.14.5...svd-rs-v0.14.6 148 | [v0.14.5]: https://github.com/rust-embedded/svd/compare/svd-rs-v0.14.4...svd-rs-v0.14.5 149 | [v0.14.4]: https://github.com/rust-embedded/svd/compare/svd-rs-v0.14.3...svd-rs-v0.14.4 150 | [v0.14.3]: https://github.com/rust-embedded/svd/compare/svd-parser-v0.14.2...svd-rs-v0.14.3 151 | [v0.14.2]: https://github.com/rust-embedded/svd/compare/svd-encoder-v0.14.2...svd-rs-v0.14.2 152 | [v0.14.1]: https://github.com/rust-embedded/svd/compare/v0.14.0...svd-rs-v0.14.1 153 | [v0.14.0]: https://github.com/rust-embedded/svd/compare/svd-rs-v0.13.2...v0.14.0 154 | [v0.13.2]: https://github.com/rust-embedded/svd/compare/svd-rs-v0.13.1...svd-rs-v0.13.2 155 | [v0.13.1]: https://github.com/rust-embedded/svd/compare/svd-parser-v0.13.1...svd-rs-v0.13.1 156 | [v0.13.0]: https://github.com/rust-embedded/svd/compare/svd-rs-v0.12.1...v0.13.0 157 | [v0.12.1]: https://github.com/rust-embedded/svd/compare/v0.12.0...svd-rs-v0.12.1 158 | [v0.12.0]: https://github.com/rust-embedded/svd/compare/svd-rs-v0.11.2...v0.12.0 159 | [v0.11.2]: https://github.com/rust-embedded/svd/compare/svd-rs-v0.11.1...svd-rs-v0.11.2 160 | [v0.11.1]: https://github.com/rust-embedded/svd/compare/v0.11.0...svd-rs-v0.11.1 161 | [v0.11.0]: https://github.com/rust-embedded/svd/compare/v0.10.2...v0.11.0 162 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 6 | and this project adheres to [Semantic Versioning](http://semver.org/). 7 | 8 | Newer releases see in [svd-rs/CHANGELOG.md](./svd-rs/CHANGELOG.md), 9 | [svd-parser/CHANGELOG.md](./svd-parser/CHANGELOG.md) and [svd-encoder/CHANGELOG.md](./svd-encoder/CHANGELOG.md). 10 | 11 | ## [v0.11.0] - 2021-10-02 12 | 13 | - [breaking-change] Split `svd-parser` on `svd-rs`, `svd-parser` and `svd-encoder` 14 | - [breaking-change] Use `roxmltree` in `svd-parser` instead of `xmltree` 15 | for fast parsing and better error debug. `Parse` trait now requires `Config` 16 | - [breaking-change] Bump `xmltree` in `svd-encoder` to 0.10. 17 | - Fixed cluster encoding 18 | - Added `as_str`/`parse_str` for simple enums 19 | - Added `indexes` iterator for `DimElement` 20 | - For structs with builders added `modify_from` method, `validate` now public 21 | - [breaking-change] `build` and `modify_from` take `ValidateLevel` 22 | - [breaking-change] multiple `addressBlocks` now supported in `Peripheral` 23 | - Added custom `serde` (de)serializers for `BitRange`, `Register`, 24 | `Cluster` and `Field`. `camelCase` and `kebab-case` are used 25 | where it's needed to be more like SVD. 26 | - [breaking-change] `Parse`, `Encode` implementation are moved 27 | in separate modules, same with tests. Builders and `Encode`'s 28 | use enum errors now instead of dynamical `anyhow`. 29 | - [breaking-change] change encode format of some numbers to be 30 | more compatible with STM vendor's SVDs 31 | - [breaking-change] resort tags when encode 32 | - [breaking-change] Use `RegisterProperties` in `RegisterInfo`, `Peripheral` nd `Device` 33 | instead of separate `size`, `access`, `reset_value` and `reset_mask` 34 | 35 | ## [v0.10.2] - 2021-04-30 36 | 37 | - Allow single valued `dimIndex` 38 | - Added `reg_iter`, `reg_iter_mut` methods on `Peripheral` and `Cluster` 39 | - Added `DerefMut` for `Cluster`, `Register` and `Field` 40 | - Added `display_name` to `RegisterInfo` 41 | - Added implementations of `From` for `TypeBuilder`'s 42 | 43 | ## [v0.10.1] - 2021-04-17 44 | 45 | - Added `DeriveFrom` implementation for `FieldInfo` 46 | 47 | ## [v0.10.0] - 2021-04-04 48 | 49 | - Added `strict` feature that hides part of checks 50 | - Builder pattern now is used for creating structures 51 | - Peripherals are processed in parallel with `rayon` 52 | - Serde now skips empty tags 53 | - Fix: bug in `addressUnitBits` 54 | - Fix: panic with 32 bit wide fields that have enumerated values 55 | - Fix: produce error on 0-width fields 56 | - Fix: error instead of panic when an array/cluster name is missing the `%s` placeholder 57 | - [breaking-change] Add support for 64 addresses, register values, enum values and writeConstraints 58 | - [breaking-change] Remove unproven flag 59 | 60 | ## [v0.9.0] - 2019-11-17 61 | 62 | - [breaking-change] make `ClusterInfo` `description` optional 63 | - [breaking-change] use `anyhow` Error instead of `failure` 64 | 65 | ## [v0.8.1] - 2019-11-03 66 | 67 | - Fix: make `derive_from` module public 68 | - Fix: enumerated_values empty check 69 | 70 | ## [v0.8.0] - 2019-11-03 71 | 72 | - [breaking-change] `RegisterClusterArrayInfo` renamed on `DimElement` 73 | - [breaking-change] `Defaults` field renamed on `RegisterProperties` 74 | and added into `Peripheral` and `ClusterInfo` 75 | - [breaking-change] `Field` splitted on `Field` enum and `FieldInfo` struct 76 | to support field arrays 77 | - Added `derived_from` into `Field` and `ClusterInfo` 78 | - [breaking-change] implement `DeriveFrom` for `ClusterInfo`, 79 | `RegisterInfo` and `EnumeratedValues` 80 | - Updated dependencies, use `Edition 2018` 81 | - Added missing `zeroToToggle` 82 | - Added serializing/deserializing with `serde` 83 | 84 | ## [v0.7.0] - 2019-01-11 85 | 86 | - [breaking-change] Major Refactor 87 | - Split SVD components into modules 88 | - Improved error handling 89 | - Added `untested` encoding functions 90 | - Added a bunch of missing fields 91 | - Added (and fixed) derivedFrom 92 | - Made register description optional 93 | 94 | 95 | ## [v0.6.0] - 2018-02-24 96 | 97 | ### Changed 98 | 99 | - Accept both 'X' and 'x' as "don't care" bits in literals. 100 | 101 | - Parse clusters of registers 102 | 103 | ## [v0.5.2] - 2017-07-04 104 | 105 | ### Added 106 | 107 | - A CPU field to the Device struct 108 | 109 | ## [v0.5.1] - 2017-04-29 110 | 111 | ### Added 112 | 113 | - A WriteConstraint field to the RegisterInfo struct. 114 | 115 | ## [v0.5.0] - 2017-04-23 116 | 117 | ### Added 118 | 119 | - [breaking-change] A WriteConstraint field to the Field struct. 120 | 121 | ### Changed 122 | 123 | - [breaking-change]. Added a private field to Device, Peripheral, RegisterInfo, 124 | RegisterArrayInfo, Defaults, EnumeratedValues and EnumeratedValue to be able 125 | to add more fields to them in the future without technically breaking backward 126 | compatibility. 127 | 128 | ## [v0.4.0] - 2017-04-03 129 | 130 | ### Changed 131 | 132 | - The type of `Peripheral.interrupt` changed from `Option` 133 | to `Vec` to properly reflect what the SVD allows. 134 | 135 | ## [v0.3.0] - 2017-02-18 136 | 137 | ### Changed 138 | 139 | - The type of `Field.enumeratedValues` changed from `Option` 140 | to `Vec` to properly reflect what the SVD allows. 141 | 142 | ## [v0.2.0] - 2016-12-21 143 | 144 | ### Added 145 | 146 | - Support for "register arrays". This converted the `struct Register` into an 147 | `enum` (to represent normal registers and register arrays) thus breaking 148 | construction of this item (which should be pretty rare). 149 | 150 | ## [v0.1.2] - 2016-12-07 151 | 152 | ### Added 153 | 154 | - Implemented `Copy` and `Clone` for several structs 155 | 156 | ## [v0.1.1] - 2016-10-09 157 | 158 | ### Fixed 159 | 160 | - the description of this crate 161 | 162 | ## v0.1.0 - 2016-10-09 [YANKED] 163 | 164 | ### Added 165 | 166 | - Initial SVD parser 167 | - A `parse` utility function to parse the contents of a SVD file (XML) 168 | 169 | [v0.11.0]: https://github.com/rust-embedded/svd/compare/v0.10.2...v0.11.0 170 | [v0.10.2]: https://github.com/rust-embedded/svd/compare/v0.10.1...v0.10.2 171 | [v0.10.1]: https://github.com/rust-embedded/svd/compare/v0.10.0...v0.10.1 172 | [v0.10.0]: https://github.com/rust-embedded/svd/compare/v0.9.0...v0.10.0 173 | [v0.9.0]: https://github.com/rust-embedded/svd/compare/v0.8.1...v0.9.0 174 | [v0.8.1]: https://github.com/rust-embedded/svd/compare/v0.8.0...v0.8.1 175 | [v0.8.0]: https://github.com/rust-embedded/svd/compare/v0.7.0...v0.8.0 176 | [v0.7.0]: https://github.com/rust-embedded/svd/compare/v0.6.0...v0.7.0 177 | [v0.6.0]: https://github.com/rust-embedded/svd/compare/v0.5.2...v0.6.0 178 | [v0.5.2]: https://github.com/rust-embedded/svd/compare/v0.5.1...v0.5.2 179 | [v0.5.1]: https://github.com/rust-embedded/svd/compare/v0.5.0...v0.5.1 180 | [v0.5.0]: https://github.com/rust-embedded/svd/compare/v0.4.0...v0.5.0 181 | [v0.4.0]: https://github.com/rust-embedded/svd/compare/v0.3.0...v0.4.0 182 | [v0.3.0]: https://github.com/rust-embedded/svd/compare/v0.2.0...v0.3.0 183 | [v0.2.0]: https://github.com/rust-embedded/svd/compare/v0.1.2...v0.2.0 184 | [v0.1.2]: https://github.com/rust-embedded/svd/compare/v0.1.1...v0.1.2 185 | [v0.1.1]: https://github.com/rust-embedded/svd/compare/v0.1.0...v0.1.1 186 | -------------------------------------------------------------------------------- /svd-encoder/src/peripheral.rs: -------------------------------------------------------------------------------- 1 | use svd_rs::RegisterCluster; 2 | 3 | use super::{ 4 | new_node, Config, Element, ElementMerge, Encode, EncodeChildren, EncodeError, XMLNode, 5 | }; 6 | 7 | use crate::{ 8 | config::{change_case, format_number, DerivableSorting, RcSorting, Sorting}, 9 | svd::{Peripheral, PeripheralInfo}, 10 | }; 11 | 12 | impl Encode for Peripheral { 13 | type Error = EncodeError; 14 | 15 | fn encode_with_config(&self, config: &Config) -> Result { 16 | match self { 17 | Self::Single(info) => info.encode_with_config(config), 18 | Self::Array(info, array_info) => { 19 | let mut base = Element::new("peripheral"); 20 | base.merge(&array_info.encode_with_config(config)?); 21 | base.merge(&info.encode_with_config(config)?); 22 | Ok(base) 23 | } 24 | } 25 | } 26 | } 27 | 28 | impl Encode for PeripheralInfo { 29 | type Error = EncodeError; 30 | 31 | fn encode_with_config(&self, config: &Config) -> Result { 32 | let mut elem = Element::new("peripheral"); 33 | elem.children.push(new_node( 34 | "name", 35 | change_case(&self.name, config.peripheral_name), 36 | )); 37 | 38 | if let Some(v) = &self.display_name { 39 | elem.children.push(new_node("displayName", v.to_string())); 40 | } 41 | 42 | if let Some(v) = &self.version { 43 | elem.children.push(new_node("version", v.to_string())); 44 | } 45 | 46 | if let Some(v) = &self.description { 47 | elem.children.push(new_node("description", v.to_string())); 48 | } 49 | 50 | if let Some(v) = &self.alternate_peripheral { 51 | elem.children.push(new_node( 52 | "alternatePeripheral", 53 | change_case(v, config.peripheral_name), 54 | )); 55 | } 56 | 57 | if let Some(v) = &self.group_name { 58 | elem.children.push(new_node("groupName", v.to_string())); 59 | } 60 | 61 | if let Some(v) = &self.prepend_to_name { 62 | elem.children.push(new_node( 63 | "prependToName", 64 | change_case(v, config.peripheral_name), 65 | )); 66 | } 67 | 68 | if let Some(v) = &self.append_to_name { 69 | elem.children.push(new_node( 70 | "appendToName", 71 | change_case(v, config.peripheral_name), 72 | )); 73 | } 74 | 75 | if let Some(v) = &self.header_struct_name { 76 | elem.children.push(new_node( 77 | "headerStructName", 78 | change_case(v, config.peripheral_name), 79 | )); 80 | } 81 | 82 | elem.children.push(new_node( 83 | "baseAddress", 84 | format_number(self.base_address, config.peripheral_base_address), 85 | )); 86 | 87 | elem.children.extend( 88 | self.default_register_properties 89 | .encode_with_config(config)?, 90 | ); 91 | 92 | if let Some(v) = &self.address_block { 93 | for ab in v { 94 | elem.children.push(ab.encode_node_with_config(config)?); 95 | } 96 | } 97 | 98 | let interrupts: Result, _> = self 99 | .interrupt 100 | .iter() 101 | .map(|interrupt| interrupt.encode_node_with_config(config)) 102 | .collect(); 103 | 104 | elem.children.append(&mut interrupts?); 105 | 106 | if let Some(v) = &self.registers { 107 | let children: Result, _> = match config.register_cluster_sorting { 108 | RcSorting::Unchanged(DerivableSorting::Unchanged(None)) => v 109 | .iter() 110 | .map(|e| e.encode_node_with_config(config)) 111 | .collect(), 112 | RcSorting::Unchanged(sorting) => sort_derived_register_cluster(v, sorting) 113 | .into_iter() 114 | .map(|e| e.encode_node_with_config(config)) 115 | .collect(), 116 | RcSorting::RegistersFirst(sorting) => rc_sort(v, sorting, true) 117 | .map(|e| e.encode_node_with_config(config)) 118 | .collect(), 119 | RcSorting::ClustersFirst(sorting) => rc_sort(v, sorting, false) 120 | .map(|e| e.encode_node_with_config(config)) 121 | .collect(), 122 | }; 123 | 124 | elem.children.push({ 125 | let mut e = Element::new("registers"); 126 | e.children = children?; 127 | XMLNode::Element(e) 128 | }); 129 | } 130 | 131 | if let Some(v) = &self.derived_from { 132 | elem.attributes.insert( 133 | String::from("derivedFrom"), 134 | change_case(v, config.peripheral_name), 135 | ); 136 | } 137 | 138 | Ok(elem) 139 | } 140 | } 141 | 142 | fn sort_register_cluster(refs: &mut [&RegisterCluster], sorting: Option) { 143 | if let Some(sorting) = sorting { 144 | match sorting { 145 | Sorting::Offset => refs.sort_by_key(|r| r.address_offset()), 146 | Sorting::OffsetReversed => { 147 | refs.sort_by_key(|r| -(r.address_offset() as i32)); 148 | } 149 | Sorting::Name => refs.sort_by_key(|r| r.name()), 150 | } 151 | } 152 | } 153 | 154 | fn sort_derived_register_cluster<'a>( 155 | rcs: impl IntoIterator, 156 | sorting: DerivableSorting, 157 | ) -> Vec<&'a RegisterCluster> { 158 | match sorting { 159 | DerivableSorting::Unchanged(sorting) => { 160 | let mut refs = rcs.into_iter().collect::>(); 161 | sort_register_cluster(&mut refs, sorting); 162 | refs 163 | } 164 | DerivableSorting::DeriveLast(sorting) => { 165 | let mut common_refs = Vec::new(); 166 | let mut derived_refs = Vec::new(); 167 | for rc in rcs { 168 | if rc.derived_from().is_some() { 169 | derived_refs.push(rc); 170 | } else { 171 | common_refs.push(rc); 172 | } 173 | } 174 | sort_register_cluster(&mut common_refs, sorting); 175 | sort_register_cluster(&mut derived_refs, sorting); 176 | common_refs.extend(derived_refs); 177 | common_refs 178 | } 179 | } 180 | } 181 | 182 | fn rc_sort( 183 | v: &[RegisterCluster], 184 | sorting: DerivableSorting, 185 | register_first: bool, 186 | ) -> impl Iterator { 187 | let reg_refs = v 188 | .iter() 189 | .filter(|rc| matches!(rc, RegisterCluster::Register(_))) 190 | .collect::>(); 191 | let reg_refs = sort_derived_register_cluster(reg_refs, sorting); 192 | 193 | let c_refs = v 194 | .iter() 195 | .filter(|rc| matches!(rc, RegisterCluster::Cluster(_))) 196 | .collect::>(); 197 | let c_refs = sort_derived_register_cluster(c_refs, sorting); 198 | if register_first { 199 | reg_refs.into_iter().chain(c_refs) 200 | } else { 201 | c_refs.into_iter().chain(reg_refs) 202 | } 203 | } 204 | --------------------------------------------------------------------------------