diff --git a/.rustfmt.toml b/.rustfmt.toml
index bd7a3e01..4699f4de 100644
--- a/.rustfmt.toml
+++ b/.rustfmt.toml
@@ -2,6 +2,6 @@ array_layout = "Block"
fn_args_layout = "Block"
fn_call_style = "Block"
generics_indent = "Block"
-max_width = 80
+max_width = 120
where_style = "Rfc"
write_mode = "overwrite"
\ No newline at end of file
diff --git a/examples/.keep b/examples/.keep
new file mode 100644
index 00000000..e69de29b
diff --git a/examples/normalise.sh b/examples/normalise.sh
new file mode 100755
index 00000000..ec7c83ad
--- /dev/null
+++ b/examples/normalise.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+#End to end tests require some normalisation of files to correct for varying formatting by vendors (because the output format is static).
+
+# Normalise hexadecimal case:
+gsed -i 's|\(0x[0-9a-fA-F]*\)|\L\1|g' $1
+
+# Normalise hexadecimal lengths
+gsed -i 's|>0x[0]\{1,2\}\([0-9a-f]\{1,2\}\)<|>0x\1<|g' $1
+
+# Swap ST resets to full register width
+gsed -i 's|0x00|0x00000000|g' $1
+
+# Swap register sizes from hex to dec
+gsed -i 's|0x20|32|g' $1
+
+# Remove displayName props because we don't care
+gsed -i 's|\(\s*[a-zA-Z0-9]*[\r\n]*\)||g' $1
+
+# Remove empty descriptions
+gsed -i 's|||g' $1
\ No newline at end of file
diff --git a/src/access.rs b/src/access.rs
new file mode 100644
index 00000000..f4bd57ae
--- /dev/null
+++ b/src/access.rs
@@ -0,0 +1,85 @@
+
+use std::collections::HashMap;
+
+use xmltree::Element;
+
+use helpers::{ParseElem, EncodeElem};
+
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum Access {
+ ReadOnly,
+ ReadWrite,
+ ReadWriteOnce,
+ WriteOnce,
+ WriteOnly,
+}
+
+impl ParseElem for Access {
+ fn parse(tree: &Element) -> Access {
+ let text = try_get_child!(tree.text.as_ref());
+
+ match &text[..] {
+ "read-only" => Access::ReadOnly,
+ "read-write" => Access::ReadWrite,
+ "read-writeOnce" => Access::ReadWriteOnce,
+ "write-only" => Access::WriteOnly,
+ "writeOnce" => Access::WriteOnce,
+ _ => panic!("unknown access variant: {}", text),
+ }
+ }
+}
+
+impl EncodeElem for Access {
+ fn encode(&self) -> Element {
+ let text = match *self {
+ Access::ReadOnly => String::from("read-only"),
+ Access::ReadWrite => String::from("read-write"),
+ Access::ReadWriteOnce => String::from("read-writeOnce"),
+ Access::WriteOnly => String::from("write-only"),
+ Access::WriteOnce => String::from("writeOnce"),
+ };
+
+ Element {
+ name: String::from("access"),
+ attributes: HashMap::new(),
+ children: Vec::new(),
+ text: Some(text),
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn decode_encode() {
+ let types = vec![
+ (Access::ReadOnly, String::from("read-only")),
+ (
+ Access::ReadWrite,
+ String::from("read-write")
+ ),
+ (
+ Access::ReadWriteOnce,
+ String::from("read-writeOnce")
+ ),
+ (
+ Access::WriteOnly,
+ String::from("write-only")
+ ),
+ (
+ Access::WriteOnce,
+ String::from("writeOnce")
+ ),
+ ];
+
+ for (a, s) in types {
+ let tree1 = &try_get_child!(Element::parse(s.as_bytes()));
+ let access = Access::parse(tree1);
+ assert_eq!(access, a, "Parsing `{}` expected `{:?}`", s, a);
+ let tree2 = &access.encode();
+ assert_eq!(tree1, tree2, "Encoding {:?} expected {}", a, s);
+ }
+ }
+}
diff --git a/src/addressblock.rs b/src/addressblock.rs
new file mode 100644
index 00000000..46ce46f6
--- /dev/null
+++ b/src/addressblock.rs
@@ -0,0 +1,73 @@
+
+use std::collections::HashMap;
+
+use xmltree::Element;
+
+use elementext::ElementExt;
+use helpers::{ParseElem, EncodeElem, new_element};
+use parse;
+
+#[derive(Clone, Debug, PartialEq)]
+pub struct AddressBlock {
+ pub offset: u32,
+ pub size: u32,
+ pub usage: String,
+}
+
+impl ParseElem for AddressBlock {
+ fn parse(tree: &Element) -> AddressBlock {
+ AddressBlock {
+ offset: try_get_child!(parse::u32(try_get_child!(tree.get_child("offset")))),
+ size: try_get_child!(parse::u32(try_get_child!(tree.get_child("size")))),
+ usage: try_get_child!(tree.get_child_text("usage")),
+ }
+ }
+}
+
+impl EncodeElem for AddressBlock {
+ fn encode(&self) -> Element {
+ Element {
+ name: String::from("addressBlock"),
+ attributes: HashMap::new(),
+ children: vec![
+ new_element("offset", Some(format!("{}", self.offset))),
+ new_element("size", Some(format!("0x{:08.x}", self.size))),
+ new_element("usage", Some(self.usage.clone())),
+ ],
+ text: None,
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn decode_encode() {
+ let types = vec![
+ (
+ AddressBlock {
+ offset: 0,
+ size: 0x00000800,
+ usage: String::from("registers"),
+ },
+ String::from(
+ "
+ 0
+ 0x00000800
+ registers
+ ",
+ )
+ ),
+ ];
+
+ for (a, s) in types {
+ let tree1 = &try_get_child!(Element::parse(s.as_bytes()));
+ let v = AddressBlock::parse(tree1);
+ assert_eq!(v, a, "Parsing `{}` expected `{:?}`", s, a);
+ let tree2 = &v.encode();
+ assert_eq!(tree1, tree2, "Encoding {:?} expected {}", a, s);
+ }
+ }
+}
diff --git a/src/bitrange.rs b/src/bitrange.rs
new file mode 100644
index 00000000..f0a0fb4e
--- /dev/null
+++ b/src/bitrange.rs
@@ -0,0 +1,146 @@
+
+use xmltree::Element;
+
+use helpers::{ParseElem, EncodeChildren, new_element};
+use parse;
+
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum BitRangeType {
+ BitRange,
+ OffsetWidth,
+ MsbLsb,
+}
+
+// TODO: reimplement equality
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub struct BitRange {
+ pub offset: u32,
+ pub width: u32,
+ pub range_type: BitRangeType,
+}
+
+impl ParseElem for BitRange {
+ fn parse(tree: &Element) -> BitRange {
+ let (end, start, range_type): (u32, u32, BitRangeType) = if let Some(range) = tree.get_child("bitRange") {
+ let text = try_get_child!(range.text.as_ref());
+
+ assert!(text.starts_with('['));
+ assert!(text.ends_with(']'));
+
+ let mut parts = text[1..text.len() - 1].split(':');
+
+ (
+ try_get_child!(try_get_child!(parts.next()).parse()),
+ try_get_child!(try_get_child!(parts.next()).parse()),
+ BitRangeType::BitRange,
+ )
+ } else if let (Some(lsb), Some(msb)) = (tree.get_child("lsb"), tree.get_child("msb")) {
+ (
+ try_get_child!(parse::u32(msb)),
+ try_get_child!(parse::u32(lsb)),
+ BitRangeType::MsbLsb,
+ )
+ } else {
+ return BitRange {
+ offset: try_get_child!(parse::u32(try_get_child!(tree.get_child("bitOffset")))),
+ width: try_get_child!(parse::u32(try_get_child!(tree.get_child("bitWidth")))),
+ range_type: BitRangeType::OffsetWidth,
+ };
+ };
+
+ BitRange {
+ offset: start,
+ width: end - start + 1,
+ range_type: range_type,
+ }
+ }
+}
+
+
+impl EncodeChildren for BitRange {
+ fn encode_children(&self) -> Vec {
+ match self.range_type {
+ BitRangeType::BitRange => {
+ vec![
+ new_element(
+ "bitRange",
+ Some(format!(
+ "[{}:{}]",
+ self.offset + self.width - 1,
+ self.offset
+ ))
+ ),
+ ]
+ }
+ BitRangeType::MsbLsb => {
+ vec![
+ new_element("lsb", Some(format!("{}", self.offset))),
+ new_element("msb", Some(format!("{}", self.offset + self.width - 1))),
+ ]
+ }
+ BitRangeType::OffsetWidth => {
+ vec![
+ new_element("bitOffset", Some(format!("{}", self.offset))),
+ new_element("bitWidth", Some(format!("{}", self.width))),
+ ]
+ }
+ }
+ }
+}
+
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn decode_encode() {
+ let types = vec![
+ (
+ BitRange {
+ offset: 16,
+ width: 4,
+ range_type: BitRangeType::BitRange,
+ },
+ String::from(
+ "
+ [19:16]
+ ",
+ )
+ ),
+ (
+ BitRange {
+ offset: 16,
+ width: 4,
+ range_type: BitRangeType::OffsetWidth,
+ },
+ String::from(
+ "
+ 164
+ ",
+ )
+ ),
+ (
+ BitRange {
+ offset: 16,
+ width: 4,
+ range_type: BitRangeType::MsbLsb,
+ },
+ String::from(
+ "
+ 1619
+ ",
+ )
+ ),
+ ];
+
+ for (a, s) in types {
+ let tree1 = &try_get_child!(Element::parse(s.as_bytes()));
+ let value = BitRange::parse(tree1);
+ assert_eq!(value, a, "Parsing `{}` expected `{:?}`", s, a);
+ let mut tree2 = new_element("fake", None);
+ tree2.children = value.encode_children();
+ assert_eq!(tree1, &tree2, "Encoding {:?} expected {}", a, s);
+ }
+ }
+}
diff --git a/src/cluster.rs b/src/cluster.rs
new file mode 100644
index 00000000..74329217
--- /dev/null
+++ b/src/cluster.rs
@@ -0,0 +1,56 @@
+
+use std::ops::Deref;
+use std::collections::hash_map::*;
+
+use xmltree::Element;
+
+use helpers::{ParseElem, EncodeElem};
+use clusterinfo::ClusterInfo;
+use registerclusterarrayinfo::RegisterClusterArrayInfo;
+
+#[derive(Clone, Debug, PartialEq)]
+pub enum Cluster {
+ Single(ClusterInfo),
+ Array(ClusterInfo, RegisterClusterArrayInfo),
+}
+
+impl ParseElem for Cluster {
+ fn parse(tree: &Element) -> Cluster {
+ assert_eq!(tree.name, "cluster");
+
+ let info = ClusterInfo::parse(tree);
+
+ if tree.get_child("dimIncrement").is_some() {
+ let array_info = RegisterClusterArrayInfo::parse(tree);
+ assert!(info.name.contains("%s"));
+ if let Some(ref indices) = array_info.dim_index {
+ assert_eq!(array_info.dim as usize, indices.len())
+ }
+ Cluster::Array(info, array_info)
+ } else {
+ Cluster::Single(info)
+ }
+ }
+}
+
+impl EncodeElem for Cluster {
+ fn encode(&self) -> Element {
+ Element {
+ name: String::from("cluster"),
+ attributes: HashMap::new(),
+ children: Vec::new(),
+ text: None,
+ }
+ }
+}
+
+impl Deref for Cluster {
+ type Target = ClusterInfo;
+
+ fn deref(&self) -> &ClusterInfo {
+ match *self {
+ Cluster::Single(ref info) => info,
+ Cluster::Array(ref info, _) => info,
+ }
+ }
+}
diff --git a/src/clusterinfo.rs b/src/clusterinfo.rs
new file mode 100644
index 00000000..09895106
--- /dev/null
+++ b/src/clusterinfo.rs
@@ -0,0 +1,65 @@
+
+use xmltree::Element;
+use either::Either;
+
+
+use elementext::ElementExt;
+
+use helpers::{ParseElem, EncodeElem, new_element};
+use parse;
+
+use access::Access;
+use register::Register;
+use cluster::Cluster;
+use registercluster::cluster_register_parse;
+
+
+#[derive(Clone, Debug, PartialEq)]
+pub struct ClusterInfo {
+ pub name: String,
+ pub description: String,
+ pub header_struct_name: String,
+ pub address_offset: u32,
+ pub size: Option,
+ pub access: Option,
+ pub reset_value: Option,
+ pub reset_mask: Option,
+ pub children: Vec>,
+ // Reserve the right to add more fields to this struct
+ pub(crate) _extensible: (),
+}
+
+impl ParseElem for ClusterInfo {
+ fn parse(tree: &Element) -> ClusterInfo {
+ ClusterInfo {
+ name: try_get_child!(tree.get_child_text("name")),
+ description: try_get_child!(tree.get_child_text("description")),
+ header_struct_name: try_get_child!(tree.get_child_text("headerStructName")),
+ address_offset: {
+ try_get_child!(parse::u32(try_get_child!(tree.get_child("addressOffset"))))
+ },
+ size: tree.get_child("size").map(
+ |t| try_get_child!(parse::u32(t)),
+ ),
+ access: tree.get_child("access").map(Access::parse),
+ reset_value: tree.get_child("resetValue").map(|t| {
+ try_get_child!(parse::u32(t))
+ }),
+ reset_mask: tree.get_child("resetMask").map(
+ |t| try_get_child!(parse::u32(t)),
+ ),
+ children: tree.children
+ .iter()
+ .filter(|t| t.name == "register" || t.name == "cluster")
+ .map(cluster_register_parse)
+ .collect(),
+ _extensible: (),
+ }
+ }
+}
+
+impl EncodeElem for ClusterInfo {
+ fn encode(&self) -> Element {
+ new_element("fake", None)
+ }
+}
diff --git a/src/cpu.rs b/src/cpu.rs
new file mode 100644
index 00000000..afa04e66
--- /dev/null
+++ b/src/cpu.rs
@@ -0,0 +1,119 @@
+
+use std::collections::HashMap;
+
+use xmltree::Element;
+
+use elementext::ElementExt;
+use parse;
+
+use helpers::{ParseElem, EncodeElem, new_element};
+use endian::Endian;
+
+#[derive(Clone, Debug, PartialEq)]
+pub struct Cpu {
+ pub name: String,
+ pub revision: String,
+ pub endian: Endian,
+ pub mpu_present: bool,
+ pub fpu_present: bool,
+ pub nvic_priority_bits: u32,
+ pub has_vendor_systick: bool,
+
+ // Reserve the right to add more fields to this struct
+ pub(crate) _extensible: (),
+}
+
+impl Cpu {
+ pub fn is_cortex_m(&self) -> bool {
+ self.name.starts_with("CM")
+ }
+}
+
+impl ParseElem for Cpu {
+ fn parse(tree: &Element) -> Cpu {
+ // TODO: not sure where CPU comes from here?
+ // EFM32 SVDs appear to have "device" key with similar stuff under it.
+ assert_eq!(tree.name, "cpu");
+
+ Cpu {
+ name: try_get_child!(tree.get_child_text("name")),
+ revision: try_get_child!(tree.get_child_text("revision")),
+ endian: Endian::parse(try_get_child!(tree.get_child("endian"))),
+ mpu_present: try_get_child!(parse::bool(try_get_child!(tree.get_child("mpuPresent")))),
+ fpu_present: try_get_child!(parse::bool(try_get_child!(tree.get_child("fpuPresent")))),
+ nvic_priority_bits: try_get_child!(parse::u32(try_get_child!(tree.get_child("nvicPrioBits")))),
+ has_vendor_systick: try_get_child!(parse::bool(
+ try_get_child!(tree.get_child("vendorSystickConfig")),
+ )),
+
+ _extensible: (),
+ }
+ }
+}
+
+impl EncodeElem for Cpu {
+ fn encode(&self) -> Element {
+ Element {
+ name: String::from("cpu"),
+ attributes: HashMap::new(),
+ children: vec![
+ new_element("name", Some(self.name.clone())),
+ new_element("revision", Some(self.revision.clone())),
+ self.endian.encode(),
+ new_element("mpuPresent", Some(format!("{}", self.mpu_present))),
+ new_element("fpuPresent", Some(format!("{}", self.fpu_present))),
+ new_element("nvicPrioBits", Some(format!("{}", self.nvic_priority_bits))),
+ new_element(
+ "vendorSystickConfig",
+ Some(format!("{}", self.has_vendor_systick))
+ ),
+ ],
+ text: None,
+ }
+ }
+}
+
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn decode_encode() {
+ let types = vec![
+ (
+ Cpu {
+ name: String::from("EFM32JG12B500F512GM48"),
+ revision: String::from("5.1.1"),
+ endian: Endian::Little,
+ mpu_present: true,
+ fpu_present: true,
+ nvic_priority_bits: 8,
+ has_vendor_systick: false,
+ _extensible: (),
+ },
+ String::from(
+ "
+
+ EFM32JG12B500F512GM48
+ 5.1.1
+ little
+ true
+ true
+ 8
+ false
+
+ ",
+ )
+ ),
+ ];
+
+ for (a, s) in types {
+ let tree1 = &try_get_child!(Element::parse(s.as_bytes()));
+ let value = Cpu::parse(tree1);
+ assert_eq!(value, a, "Parsing `{}` expected `{:?}`", s, a);
+ let tree2 = value.encode();
+ assert_eq!(tree1, &tree2, "Encoding {:?} expected {}", a, s);
+ }
+ }
+}
diff --git a/src/defaults.rs b/src/defaults.rs
new file mode 100644
index 00000000..3cc512b8
--- /dev/null
+++ b/src/defaults.rs
@@ -0,0 +1,108 @@
+
+use xmltree::Element;
+
+use helpers::{ParseElem, EncodeElem, EncodeChildren, new_element};
+use parse;
+
+use access::Access;
+
+/// Register default properties
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub struct Defaults {
+ pub size: Option,
+ pub reset_value: Option,
+ pub reset_mask: Option,
+ pub access: Option,
+ // Reserve the right to add more fields to this struct
+ pub(crate) _extensible: (),
+}
+
+impl ParseElem for Defaults {
+ fn parse(tree: &Element) -> Defaults {
+ Defaults {
+ size: tree.get_child("size").map(
+ |t| try_get_child!(parse::u32(t)),
+ ),
+ reset_value: tree.get_child("resetValue").map(|t| {
+ try_get_child!(parse::u32(t))
+ }),
+ reset_mask: tree.get_child("resetMask").map(
+ |t| try_get_child!(parse::u32(t)),
+ ),
+ access: tree.get_child("access").map(Access::parse),
+ _extensible: (),
+ }
+ }
+}
+
+impl EncodeChildren for Defaults {
+ fn encode_children(&self) -> Vec {
+ let mut children = Vec::new();
+
+ match self.size {
+ Some(ref v) => {
+ children.push(new_element("size", Some(format!("0x{:08.x}", v))));
+ }
+ None => (),
+ };
+
+ match self.reset_value {
+ Some(ref v) => {
+ children.push(new_element("resetValue", Some(format!("0x{:08.x}", v))));
+ }
+ None => (),
+ };
+
+ match self.reset_mask {
+ Some(ref v) => {
+ children.push(new_element("resetMask", Some(format!("0x{:08.x}", v))));
+ }
+ None => (),
+ };
+
+ match self.access {
+ Some(ref v) => {
+ children.push(v.encode());
+ }
+ None => (),
+ };
+
+ children
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn decode_encode() {
+ let example = String::from(
+ "
+
+ 0xaabbccdd
+ 0x11223344
+ 0x00000000
+ read-only
+
+ ",
+ );
+
+ let expected = Defaults {
+ size: Some(0xaabbccdd),
+ reset_value: Some(0x11223344),
+ reset_mask: Some(0x00000000),
+ access: Some(Access::ReadOnly),
+ _extensible: (),
+ };
+
+ let tree1 = &try_get_child!(Element::parse(example.as_bytes()));
+
+ let parsed = Defaults::parse(tree1);
+ assert_eq!(parsed, expected, "Parsing tree failed");
+
+ let mut tree2 = new_element("mock", None);
+ tree2.children = parsed.encode_children();
+ assert_eq!(tree1, &tree2, "Encoding value failed");
+ }
+}
diff --git a/src/device.rs b/src/device.rs
new file mode 100644
index 00000000..7d401a19
--- /dev/null
+++ b/src/device.rs
@@ -0,0 +1,100 @@
+use xmltree::Element;
+
+use std::collections::HashMap;
+
+
+use elementext::ElementExt;
+use helpers::{ParseElem, EncodeElem, new_element};
+use parse;
+
+use cpu::Cpu;
+use defaults::Defaults;
+use peripheral::Peripheral;
+
+
+
+#[derive(Clone, Debug)]
+pub struct Device {
+ pub name: String,
+ schema_version: String,
+ pub version: String,
+ pub description: String,
+ pub address_unit_bits: u32,
+ pub width: u32,
+ pub cpu: Option,
+ pub peripherals: Vec,
+ pub defaults: Defaults,
+ // Reserve the right to add more fields to this struct
+ pub(crate) _extensible: (),
+}
+
+impl ParseElem for Device {
+ fn parse(tree: &Element) -> Device {
+ Device {
+ schema_version: tree.attributes.get("schemaVersion").unwrap().clone(),
+ name: try_get_child!(tree.get_child_text("name")),
+ version: try_get_child!(tree.get_child_text("version")),
+ description: try_get_child!(tree.get_child_text("description")),
+ address_unit_bits: try_get_child!(parse::u32(
+ try_get_child!(tree.get_child("addressUnitBits")),
+ )),
+ width: try_get_child!(parse::u32(try_get_child!(tree.get_child("width")))),
+ cpu: tree.get_child("cpu").map(Cpu::parse),
+ peripherals: try_get_child!(tree.get_child("peripherals"))
+ .children
+ .iter()
+ .map(Peripheral::parse)
+ .collect(),
+ defaults: Defaults::parse(tree),
+ _extensible: (),
+ }
+ }
+}
+
+impl EncodeElem for Device {
+ fn encode(&self) -> Element {
+ let mut elem = Element {
+ name: String::from("device"),
+ attributes: HashMap::new(),
+ children: vec![
+ new_element("name", Some(self.name.clone())),
+ new_element("version", Some(self.version.clone())),
+ new_element("description", Some(self.description.clone())),
+ new_element(
+ "addressUnitBits",
+ Some(format!("{}", self.address_unit_bits))
+ ),
+ new_element("width", Some(format!("{}", self.width))),
+ ],
+ text: None,
+ };
+
+ elem.attributes.insert(
+ String::from("xmlns:xs"),
+ String::from("http://www.w3.org/2001/XMLSchema-instance"),
+ );
+ elem.attributes.insert(
+ String::from("schemaVersion"),
+ format!("{}", self.schema_version),
+ );
+ elem.attributes.insert(
+ String::from("xs:noNamespaceSchemaLocation"),
+ format!("CMSIS-SVD_Schema_{}.xsd", self.schema_version),
+ );
+
+ match self.cpu {
+ Some(ref v) => {
+ elem.children.push(v.encode());
+ }
+ None => (),
+ };
+ elem.children.push(Element {
+ name: String::from("peripherals"),
+ attributes: HashMap::new(),
+ children: self.peripherals.iter().map(Peripheral::encode).collect(),
+ text: None,
+ });
+
+ elem
+ }
+}
diff --git a/src/elementext.rs b/src/elementext.rs
new file mode 100644
index 00000000..7e49c306
--- /dev/null
+++ b/src/elementext.rs
@@ -0,0 +1,33 @@
+
+use xmltree::Element;
+
+#[macro_export]
+macro_rules! try_get_child {
+ ($e:expr) => {
+ $e.expect(concat!(file!(), ":", line!(), " ", stringify!($e)))
+ }
+}
+
+pub trait ElementExt {
+ fn get_child_text(&self, k: K) -> Option
+ where
+ String: PartialEq;
+ fn debug(&self);
+}
+
+impl ElementExt for Element {
+ fn get_child_text(&self, k: K) -> Option
+ where
+ String: PartialEq,
+ {
+ self.get_child(k).map(|c| try_get_child!(c.text.clone()))
+ }
+
+ fn debug(&self) {
+ println!("<{}>", self.name);
+ for c in &self.children {
+ println!("{}: {:?}", c.name, c.text)
+ }
+ println!("{}>", self.name);
+ }
+}
diff --git a/src/endian.rs b/src/endian.rs
new file mode 100644
index 00000000..63ae2742
--- /dev/null
+++ b/src/endian.rs
@@ -0,0 +1,72 @@
+
+use std::collections::HashMap;
+
+use xmltree::Element;
+
+use helpers::{ParseElem, EncodeElem};
+
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum Endian {
+ Little,
+ Big,
+ Selectable,
+ Other,
+}
+
+impl ParseElem for Endian {
+ fn parse(tree: &Element) -> Endian {
+ let text = try_get_child!(tree.text.as_ref());
+
+ match &text[..] {
+ "little" => Endian::Little,
+ "big" => Endian::Big,
+ "selectable" => Endian::Selectable,
+ "other" => Endian::Other,
+ _ => panic!("unknown endian variant: {}", text),
+ }
+ }
+}
+
+impl EncodeElem for Endian {
+ fn encode(&self) -> Element {
+ let text = match *self {
+ Endian::Little => String::from("little"),
+ Endian::Big => String::from("big"),
+ Endian::Selectable => String::from("selectable"),
+ Endian::Other => String::from("other"),
+ };
+
+ Element {
+ name: String::from("endian"),
+ attributes: HashMap::new(),
+ children: Vec::new(),
+ text: Some(text),
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn decode_encode() {
+ let types = vec![
+ (Endian::Little, String::from("little")),
+ (Endian::Big, String::from("big")),
+ (
+ Endian::Selectable,
+ String::from("selectable")
+ ),
+ (Endian::Other, String::from("other")),
+ ];
+
+ for (e, s) in types {
+ let tree1 = &try_get_child!(Element::parse(s.as_bytes()));
+ let endian = Endian::parse(tree1);
+ assert_eq!(endian, e, "Parsing `{}` expected `{:?}`", s, e);
+ let tree2 = &endian.encode();
+ assert_eq!(tree1, tree2, "Encoding {:?} expected {}", e, s);
+ }
+ }
+}
diff --git a/src/enumeratedvalue.rs b/src/enumeratedvalue.rs
new file mode 100644
index 00000000..8897932b
--- /dev/null
+++ b/src/enumeratedvalue.rs
@@ -0,0 +1,115 @@
+
+use std::collections::HashMap;
+
+use xmltree::Element;
+
+use elementext::ElementExt;
+use helpers::{EncodeElem, new_element};
+use parse;
+
+
+#[derive(Clone, Debug, PartialEq)]
+pub struct EnumeratedValue {
+ pub name: String,
+ pub description: Option,
+ pub value: Option,
+ pub is_default: Option,
+ // Reserve the right to add more fields to this struct
+ pub(crate) _extensible: (),
+}
+
+impl EnumeratedValue {
+ pub fn parse(tree: &Element) -> Option {
+ assert_eq!(tree.name, "enumeratedValue");
+
+ Some(EnumeratedValue {
+ name: try_get_child!(tree.get_child_text("name")),
+ description: tree.get_child_text("description"),
+ value: tree.get_child("value").map(
+ |t| try_get_child!(parse::u32(t)),
+ ),
+ is_default: tree.get_child_text("isDefault").map(|t| {
+ try_get_child!(t.parse())
+ }),
+ _extensible: (),
+ })
+ }
+}
+
+impl EncodeElem for EnumeratedValue {
+ fn encode(&self) -> Element {
+ let mut base = Element {
+ name: String::from("enumeratedValue"),
+ attributes: HashMap::new(),
+ children: vec![new_element("name", Some(self.name.clone()))],
+ text: None,
+ };
+
+ match self.description {
+ Some(ref d) => {
+ let s = (*d).clone();
+ base.children.push(new_element("description", Some(s)));
+ }
+ None => (),
+ };
+
+ match self.value {
+ Some(ref v) => {
+ base.children.push(new_element(
+ "value",
+ Some(format!("0x{:08.x}", *v)),
+ ));
+ }
+ None => (),
+ };
+
+ match self.is_default {
+ Some(ref v) => {
+ base.children.push(new_element(
+ "isDefault",
+ Some(format!("{}", v)),
+ ));
+ }
+ None => (),
+ };
+
+ base
+ }
+}
+
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn decode_encode() {
+ let example = String::from(
+ "
+
+ WS0
+ Zero wait-states inserted in fetch or read transfers
+ 0x00000000
+ true
+
+ ",
+ );
+ let expected = EnumeratedValue {
+ name: String::from("WS0"),
+ description: Some(String::from(
+ "Zero wait-states inserted in fetch or read transfers",
+ )),
+ value: Some(0),
+ is_default: Some(true),
+ _extensible: (),
+ };
+
+ let tree1 = &try_get_child!(Element::parse(example.as_bytes()));
+
+ let parsed = EnumeratedValue::parse(tree1).unwrap();
+ assert_eq!(parsed, expected, "Parsing tree failed");
+
+ let tree2 = &parsed.encode();
+ assert_eq!(tree1, tree2, "Encoding value failed");
+ }
+}
diff --git a/src/enumeratedvalues.rs b/src/enumeratedvalues.rs
new file mode 100644
index 00000000..4d93a542
--- /dev/null
+++ b/src/enumeratedvalues.rs
@@ -0,0 +1,141 @@
+
+use std::collections::HashMap;
+
+use xmltree::Element;
+
+use elementext::ElementExt;
+use helpers::{ParseElem, EncodeElem, new_element};
+
+use usage::Usage;
+use enumeratedvalue::EnumeratedValue;
+
+#[derive(Clone, Debug, PartialEq)]
+pub struct EnumeratedValues {
+ pub name: Option,
+ pub usage: Option,
+ pub derived_from: Option,
+ pub values: Vec,
+ // Reserve the right to add more fields to this struct
+ pub(crate) _extensible: (),
+}
+
+impl ParseElem for EnumeratedValues {
+ fn parse(tree: &Element) -> EnumeratedValues {
+ assert_eq!(tree.name, "enumeratedValues");
+
+ EnumeratedValues {
+ name: tree.get_child_text("name"),
+ usage: tree.get_child("usage").map(Usage::parse),
+ derived_from: tree.attributes.get(&"derivedFrom".to_owned()).map(|s| {
+ s.to_owned()
+ }),
+ values: tree.children
+ .iter()
+ .filter_map(EnumeratedValue::parse)
+ .collect(),
+ _extensible: (),
+ }
+ }
+}
+
+impl EncodeElem for EnumeratedValues {
+ fn encode(&self) -> Element {
+ let mut base = Element {
+ name: String::from("enumeratedValues"),
+ attributes: HashMap::new(),
+ children: Vec::new(),
+ text: None,
+ };
+
+ match self.name {
+ Some(ref d) => {
+ base.children.push(new_element("name", Some((*d).clone())));
+ }
+ None => (),
+ };
+
+ match self.usage {
+ Some(ref v) => {
+ base.children.push(v.encode());
+ }
+ None => (),
+ };
+
+ match self.derived_from {
+ Some(ref v) => {
+ base.attributes.insert(
+ String::from("derivedFrom"),
+ (*v).clone(),
+ );
+ }
+ None => (),
+ }
+
+ for v in &self.values {
+ base.children.push(v.encode());
+ }
+
+ base
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn decode_encode() {
+ let example = String::from(
+ "
+
+
+ WS0
+ Zero wait-states inserted in fetch or read transfers
+ 0x00000000
+ true
+
+
+ WS1
+ One wait-state inserted for each fetch or read transfer. See Flash Wait-States table for details
+ 0x00000001
+
+
+ ",
+ );
+
+ let expected = EnumeratedValues {
+ name: None,
+ usage: None,
+ derived_from: Some(String::from("fake-derivation.png")),
+ values: vec![
+ EnumeratedValue {
+ name: String::from("WS0"),
+ description: Some(String::from(
+ "Zero wait-states inserted in fetch or read transfers",
+ )),
+ value: Some(0),
+ is_default: Some(true),
+ _extensible: (),
+ },
+ EnumeratedValue {
+ name: String::from("WS1"),
+ description: Some(String::from(
+ "One wait-state inserted for each fetch or read transfer. See Flash Wait-States table for details",
+ )),
+ value: Some(1),
+ is_default: None,
+ _extensible: (),
+ },
+ ],
+ _extensible: (),
+ };
+
+ let tree1 = &try_get_child!(Element::parse(example.as_bytes()));
+
+ let parsed = EnumeratedValues::parse(tree1);
+ assert_eq!(parsed, expected, "Parsing tree failed");
+
+ let tree2 = &parsed.encode();
+ assert_eq!(tree1, tree2, "Encoding value failed");
+ }
+}
diff --git a/src/field.rs b/src/field.rs
new file mode 100644
index 00000000..d90b7cfe
--- /dev/null
+++ b/src/field.rs
@@ -0,0 +1,155 @@
+
+use std::collections::HashMap;
+use xmltree::Element;
+
+use elementext::ElementExt;
+use helpers::{ParseElem, EncodeElem, EncodeChildren, new_element};
+
+use access::Access;
+use writeconstraint::WriteConstraint;
+use bitrange::BitRange;
+use enumeratedvalues::EnumeratedValues;
+
+
+#[derive(Clone, Debug, PartialEq)]
+pub struct Field {
+ pub name: String,
+ pub description: Option,
+ pub bit_range: BitRange,
+ pub access: Option,
+ pub enumerated_values: Vec,
+ pub write_constraint: Option,
+ // Reserve the right to add more fields to this struct
+ pub(crate) _extensible: (),
+}
+
+impl ParseElem for Field {
+ fn parse(tree: &Element) -> Field {
+ assert_eq!(tree.name, "field");
+
+ Field {
+ name: try_get_child!(tree.get_child_text("name")),
+ description: tree.get_child_text("description"),
+ bit_range: BitRange::parse(tree),
+ access: tree.get_child("access").map(Access::parse),
+ enumerated_values: tree.children
+ .iter()
+ .filter(|t| t.name == "enumeratedValues")
+ .map(EnumeratedValues::parse)
+ .collect::>(),
+ write_constraint: tree.get_child("writeConstraint").map(
+ WriteConstraint::parse,
+ ),
+ _extensible: (),
+ }
+ }
+}
+
+impl EncodeElem for Field {
+ fn encode(&self) -> Element {
+ let mut elem = Element {
+ name: String::from("field"),
+ attributes: HashMap::new(),
+ children: vec![
+ new_element("name", Some(self.name.clone())),
+ new_element("description", self.description.clone()),
+ ],
+ text: None,
+ };
+
+ // Add bit range
+ elem.children.append(&mut self.bit_range.encode_children());
+
+ match self.access {
+ Some(ref v) => {
+ elem.children.push(v.encode());
+ }
+ None => (),
+ };
+
+ let mut enumerated_values: Vec = self.enumerated_values.iter().map(|v| v.encode()).collect();
+ elem.children.append(&mut enumerated_values);
+
+ match self.write_constraint {
+ Some(ref v) => {
+ elem.children.push(v.encode());
+ }
+ None => (),
+ };
+
+ elem
+ }
+}
+
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use bitrange::BitRangeType;
+ use enumeratedvalue::EnumeratedValue;
+
+ #[test]
+ fn decode_encode() {
+ let types = vec![
+ (
+ Field {
+ name: String::from("MODE"),
+ description: Some(String::from("Read Mode")),
+ bit_range: BitRange {
+ offset: 24,
+ width: 2,
+ range_type: BitRangeType::OffsetWidth,
+ },
+ access: Some(Access::ReadWrite),
+ enumerated_values: vec![
+ EnumeratedValues {
+ name: None,
+ usage: None,
+ derived_from: None,
+ values: vec![
+ EnumeratedValue {
+ name: String::from("WS0"),
+ description: Some(String::from(
+ "Zero wait-states inserted in fetch or read transfers",
+ )),
+ value: Some(0),
+ is_default: None,
+ _extensible: (),
+ },
+ ],
+ _extensible: (),
+ },
+ ],
+ write_constraint: None,
+ _extensible: (),
+ },
+ String::from(
+ "
+
+ MODE
+ Read Mode
+ 24
+ 2
+ read-write
+
+
+ WS0
+ Zero wait-states inserted in fetch or read transfers
+ 0x00000000
+
+
+
+ ",
+ )
+ ),
+ ];
+
+ for (a, s) in types {
+ let tree1 = &try_get_child!(Element::parse(s.as_bytes()));
+ let v = Field::parse(tree1);
+ assert_eq!(v, a, "Parsing `{}` expected `{:?}`", s, a);
+ let tree2 = &v.encode();
+ assert_eq!(tree1, tree2, "Encoding {:?} expected {}", a, s);
+ }
+ }
+}
diff --git a/src/helpers.rs b/src/helpers.rs
new file mode 100644
index 00000000..774ddb68
--- /dev/null
+++ b/src/helpers.rs
@@ -0,0 +1,34 @@
+
+use std::collections::HashMap;
+
+use xmltree::Element;
+
+// ParseElem parses an object from an XML element
+pub trait ParseElem {
+ fn parse(tree: &Element) -> Self;
+}
+
+// ParseChildren parses an object from children of an XML element
+pub trait ParseChildren {
+ fn parse_children(tree: &Element) -> Self;
+}
+
+// EncodeElem trait encodes an object to an XML element
+pub trait EncodeElem {
+ fn encode(&self) -> Element;
+}
+
+// EncodeChildren trait encodes an object to a vector of child elements
+pub trait EncodeChildren {
+ fn encode_children(&self) -> Vec;
+}
+
+// Helper to assist with creation of simple elements
+pub fn new_element(name: &str, text: Option) -> Element {
+ Element {
+ name: String::from(name),
+ attributes: HashMap::new(),
+ children: Vec::new(),
+ text: text,
+ }
+}
diff --git a/src/interrupt.rs b/src/interrupt.rs
new file mode 100644
index 00000000..e2e38290
--- /dev/null
+++ b/src/interrupt.rs
@@ -0,0 +1,77 @@
+
+use std::collections::HashMap;
+
+use xmltree::Element;
+
+
+use elementext::ElementExt;
+use helpers::{ParseElem, EncodeElem, new_element};
+use parse;
+
+
+
+#[derive(Clone, Debug, PartialEq)]
+pub struct Interrupt {
+ pub name: String,
+ pub description: Option,
+ pub value: u32,
+}
+
+impl ParseElem for Interrupt {
+ fn parse(tree: &Element) -> Interrupt {
+ Interrupt {
+ name: try_get_child!(tree.get_child_text("name")),
+ description: tree.get_child_text("description"),
+ value: try_get_child!(parse::u32(try_get_child!(tree.get_child("value")))),
+ }
+ }
+}
+
+impl EncodeElem for Interrupt {
+ fn encode(&self) -> Element {
+ Element {
+ name: String::from("interrupt"),
+ attributes: HashMap::new(),
+ children: vec![
+ new_element("name", Some(self.name.clone())),
+ new_element("description", self.description.clone()),
+ new_element("value", Some(format!("{}", self.value))),
+ ],
+ text: None,
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn decode_encode() {
+ let types = vec![
+ (
+ Interrupt {
+ name: String::from("test"),
+ description: Some(String::from("description")),
+ value: 14,
+ },
+ String::from(
+ "
+
+ test
+ description
+ 14
+ ",
+ )
+ ),
+ ];
+
+ for (a, s) in types {
+ let tree1 = &try_get_child!(Element::parse(s.as_bytes()));
+ let v = Interrupt::parse(tree1);
+ assert_eq!(v, a, "Parsing `{}` expected `{:?}`", s, a);
+ let tree2 = &v.encode();
+ assert_eq!(tree1, tree2, "Encoding {:?} expected {}", a, s);
+ }
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 2461b5d2..9d4e954d 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -22,642 +22,164 @@
//! - [SVD file database](https://github.com/posborne/cmsis-svd/tree/master/data)
//! - [Sample SVD file](https://www.keil.com/pack/doc/CMSIS/SVD/html/svd_Example_pg.html)
-#![deny(warnings)]
+//#![deny(warnings)]
extern crate either;
extern crate xmltree;
-
-use std::ops::Deref;
-
-use either::Either;
use xmltree::Element;
-macro_rules! try {
- ($e:expr) => {
- $e.expect(concat!(file!(), ":", line!(), " ", stringify!($e)))
- }
-}
-
+#[macro_use]
+mod elementext;
mod parse;
-/// Parses the contents of a SVD file (XML)
-pub fn parse(xml: &str) -> Device {
- Device::parse(xml)
-}
+mod helpers;
+use helpers::*;
+mod endian;
+pub use endian::Endian;
+mod access;
+pub use access::Access;
+mod usage;
+pub use usage::Usage;
+mod enumeratedvalue;
+pub use enumeratedvalue::EnumeratedValue;
+mod enumeratedvalues;
+pub use enumeratedvalues::EnumeratedValues;
+mod defaults;
+pub use defaults::Defaults;
+mod writeconstraintrange;
+pub use writeconstraintrange::WriteConstraintRange;
+mod writeconstraint;
+pub use writeconstraint::WriteConstraint;
+mod bitrange;
+pub use bitrange::BitRange;
+mod interrupt;
+pub use interrupt::Interrupt;
+mod addressblock;
+pub use addressblock::AddressBlock;
+mod field;
+pub use field::Field;
+mod register;
+pub use register::Register;
+mod clusterinfo;
+pub use clusterinfo::ClusterInfo;
+mod cluster;
+pub use cluster::Cluster;
+mod registerinfo;
+pub use registerinfo::RegisterInfo;
+mod registerarrayinfo;
+pub use registerarrayinfo::RegisterArrayInfo;
+mod registerclusterarrayinfo;
+pub use registerclusterarrayinfo::RegisterClusterArrayInfo;
+mod registercluster;
+mod peripheral;
+pub use peripheral::Peripheral;
+mod cpu;
+pub use cpu::Cpu;
+mod device;
+pub use device::Device;
-trait ElementExt {
- fn get_child_text(&self, k: K) -> Option
- where
- String: PartialEq;
- fn debug(&self);
-}
-
-impl ElementExt for Element {
- fn get_child_text(&self, k: K) -> Option
- where
- String: PartialEq,
- {
- self.get_child(k).map(|c| try!(c.text.clone()))
- }
-
- fn debug(&self) {
- println!("<{}>", self.name);
- for c in &self.children {
- println!("{}: {:?}", c.name, c.text)
- }
- println!("{}>", self.name);
- }
-}
-
-#[derive(Clone, Debug)]
-pub struct Device {
- pub name: String,
- pub cpu: Option,
- pub peripherals: Vec,
- pub defaults: Defaults,
- // Reserve the right to add more fields to this struct
- _extensible: (),
-}
-
-impl Device {
- /// Parses a SVD file
- ///
- /// # Panics
- ///
- /// If the input is an invalid SVD file (yay, no error handling)
- pub fn parse(svd: &str) -> Device {
- let tree = &try!(Element::parse(svd.as_bytes()));
-
- Device {
- name: try!(tree.get_child_text("name")),
- cpu: tree.get_child("cpu").map(Cpu::parse),
- peripherals: try!(tree.get_child("peripherals"))
- .children
- .iter()
- .map(Peripheral::parse)
- .collect(),
- defaults: Defaults::parse(tree),
- _extensible: (),
- }
- }
-}
-
-#[derive(Clone, Copy, Debug, PartialEq)]
-pub enum Endian {
- Little,
- Big,
- Selectable,
- Other
-}
-
-impl Endian {
- fn parse(tree: &Element) -> Endian {
- let text = try!(tree.text.as_ref());
-
- match &text[..] {
- "little" => Endian::Little,
- "big" => Endian::Big,
- "selectable" => Endian::Selectable,
- "other" => Endian::Other,
- _ => panic!("unknown endian variant: {}", text),
- }
- }
-}
-
-
-#[derive(Clone, Debug)]
-pub struct Cpu {
- pub name: String,
- pub revision: String,
- pub endian: Endian,
- pub mpu_present: bool,
- pub fpu_present: bool,
- pub nvic_priority_bits: u32,
- pub has_vendor_systick: bool,
-
- // Reserve the right to add more fields to this struct
- _extensible: (),
-}
-
-impl Cpu {
- fn parse(tree: &Element) -> Cpu {
- assert_eq!(tree.name, "cpu");
-
- Cpu {
- name: try!(tree.get_child_text("name")),
- revision: try!(tree.get_child_text("revision")),
- endian: Endian::parse(try!(tree.get_child("endian"))),
- mpu_present: try!(parse::bool(try!(tree.get_child("mpuPresent")))),
- fpu_present: try!(parse::bool(try!(tree.get_child("fpuPresent")))),
- nvic_priority_bits:
- try!(parse::u32(try!(tree.get_child("nvicPrioBits")))),
- has_vendor_systick:
- try!(parse::bool(try!(tree.get_child("vendorSystickConfig")))),
-
- _extensible: (),
- }
- }
-
- pub fn is_cortex_m(&self) -> bool {
- self.name.starts_with("CM")
- }
-}
-#[derive(Clone, Debug)]
-pub struct Peripheral {
- pub name: String,
- pub group_name: Option,
- pub description: Option,
- pub base_address: u32,
- pub interrupt: Vec,
- /// `None` indicates that the `` node is not present
- pub registers: Option>>,
- pub derived_from: Option,
- // Reserve the right to add more fields to this struct
- _extensible: (),
-}
-
-impl Peripheral {
- fn parse(tree: &Element) -> Peripheral {
- assert_eq!(tree.name, "peripheral");
-
- Peripheral {
- name: try!(tree.get_child_text("name")),
- group_name: tree.get_child_text("groupName"),
- description: tree.get_child_text("description"),
- base_address: try!(parse::u32(try!(tree.get_child("baseAddress")))),
- interrupt: tree.children
- .iter()
- .filter(|t| t.name == "interrupt")
- .map(Interrupt::parse)
- .collect::>(),
- registers: tree.get_child("registers").map(|rs| {
- rs.children.iter().map(cluster_register_parse).collect()
- }),
- derived_from: tree.attributes.get("derivedFrom").map(
- |s| {
- s.to_owned()
- },
- ),
- _extensible: (),
- }
- }
-}
-
-#[derive(Clone, Debug)]
-pub struct Interrupt {
- pub name: String,
- pub description: Option,
- pub value: u32,
-}
-
-impl Interrupt {
- fn parse(tree: &Element) -> Interrupt {
- Interrupt {
- name: try!(tree.get_child_text("name")),
- description: tree.get_child_text("description"),
- value: try!(parse::u32(try!(tree.get_child("value")))),
- }
- }
-}
-
-#[derive(Clone, Debug)]
-pub struct ClusterInfo {
- pub name: String,
- pub description: String,
- pub header_struct_name: String,
- pub address_offset: u32,
- pub size: Option,
- pub access: Option,
- pub reset_value: Option,
- pub reset_mask: Option,
- pub children: Vec>,
- // Reserve the right to add more fields to this struct
- _extensible: (),
-}
-
-#[derive(Clone, Debug)]
-pub struct RegisterInfo {
- pub name: String,
- pub description: String,
- pub address_offset: u32,
- pub size: Option,
- pub access: Option,
- pub reset_value: Option,
- pub reset_mask: Option,
- /// `None` indicates that the `` node is not present
- pub fields: Option>,
- pub write_constraint: Option,
- // Reserve the right to add more fields to this struct
- _extensible: (),
-}
-
-#[derive(Clone, Debug)]
-pub struct RegisterClusterArrayInfo {
- pub dim: u32,
- pub dim_increment: u32,
- pub dim_index: Option>,
-}
-
-fn cluster_register_parse(tree: &Element) -> Either {
- if tree.name == "register" {
- Either::Left(Register::parse(tree))
- } else if tree.name == "cluster" {
- Either::Right(Cluster::parse(tree))
- } else {
- unreachable!()
- }
-}
-
-impl Cluster {
- fn parse(tree: &Element) -> Cluster {
- assert_eq!(tree.name, "cluster");
-
- let info = ClusterInfo::parse(tree);
-
- if tree.get_child("dimIncrement").is_some() {
- let array_info = RegisterClusterArrayInfo::parse(tree);
- assert!(info.name.contains("%s"));
- if let Some(ref indices) = array_info.dim_index {
- assert_eq!(array_info.dim as usize, indices.len())
- }
- Cluster::Array(info, array_info)
- } else {
- Cluster::Single(info)
- }
- }
-}
-
-#[derive(Clone, Debug)]
-pub enum Cluster {
- Single(ClusterInfo),
- Array(ClusterInfo, RegisterClusterArrayInfo),
-}
-
-impl Deref for Cluster {
- type Target = ClusterInfo;
-
- fn deref(&self) -> &ClusterInfo {
- match *self {
- Cluster::Single(ref info) => info,
- Cluster::Array(ref info, _) => info,
- }
- }
-}
-
-#[derive(Clone, Debug)]
-pub enum Register {
- Single(RegisterInfo),
- Array(RegisterInfo, RegisterClusterArrayInfo),
-}
-
-impl Deref for Register {
- type Target = RegisterInfo;
-
- fn deref(&self) -> &RegisterInfo {
- match *self {
- Register::Single(ref info) => info,
- Register::Array(ref info, _) => info,
- }
- }
-}
-
-impl ClusterInfo {
- fn parse(tree: &Element) -> ClusterInfo {
- ClusterInfo {
- name: try!(tree.get_child_text("name")),
- description: try!(tree.get_child_text("description")),
- header_struct_name: try!(tree.get_child_text("headerStructName")),
- address_offset: {
- try!(parse::u32(try!(tree.get_child("addressOffset"))))
- },
- size: tree.get_child("size").map(|t| try!(parse::u32(t))),
- access: tree.get_child("access").map(Access::parse),
- reset_value:
- tree.get_child("resetValue").map(|t| try!(parse::u32(t))),
- reset_mask:
- tree.get_child("resetMask").map(|t| try!(parse::u32(t))),
- children: tree.children
- .iter()
- .filter(|t| t.name == "register" || t.name == "cluster")
- .map(cluster_register_parse)
- .collect(),
- _extensible: (),
- }
- }
-}
-
-impl RegisterInfo {
- fn parse(tree: &Element) -> RegisterInfo {
- RegisterInfo {
- name: try!(tree.get_child_text("name")),
- description: try!(tree.get_child_text("description")),
- address_offset: {
- try!(parse::u32(try!(tree.get_child("addressOffset"))))
- },
- size: tree.get_child("size").map(|t| try!(parse::u32(t))),
- access: tree.get_child("access").map(Access::parse),
- reset_value:
- tree.get_child("resetValue").map(|t| try!(parse::u32(t))),
- reset_mask:
- tree.get_child("resetMask").map(|t| try!(parse::u32(t))),
- fields:
- tree.get_child("fields")
- .map(|fs| fs.children.iter().map(Field::parse).collect()),
- write_constraint: tree.get_child("writeConstraint")
- .map(WriteConstraint::parse),
- _extensible: (),
- }
- }
-}
-
-impl RegisterClusterArrayInfo {
- fn parse(tree: &Element) -> RegisterClusterArrayInfo {
- RegisterClusterArrayInfo {
- dim: try!(tree.get_child_text("dim").unwrap().parse::()),
- dim_increment: try!(tree.get_child("dimIncrement").map(|t| {
- try!(parse::u32(t))
- })),
- dim_index: tree.get_child("dimIndex").map(|c| {
- parse::dim_index(try!(c.text.as_ref()))
- }),
- }
- }
-}
-
-impl Register {
- fn parse(tree: &Element) -> Register {
- assert_eq!(tree.name, "register");
-
- let info = RegisterInfo::parse(tree);
-
- if tree.get_child("dimIncrement").is_some() {
- let array_info = RegisterClusterArrayInfo::parse(tree);
- assert!(info.name.contains("%s"));
- if let Some(ref indices) = array_info.dim_index {
- assert_eq!(array_info.dim as usize, indices.len())
- }
- Register::Array(info, array_info)
- } else {
- Register::Single(info)
- }
- }
-}
-
-#[derive(Clone, Copy, Debug, PartialEq)]
-pub enum Access {
- ReadOnly,
- ReadWrite,
- ReadWriteOnce,
- WriteOnce,
- WriteOnly,
-}
-
-impl Access {
- fn parse(tree: &Element) -> Access {
- let text = try!(tree.text.as_ref());
-
- match &text[..] {
- "read-only" => Access::ReadOnly,
- "read-write" => Access::ReadWrite,
- "read-writeOnce" => Access::ReadWriteOnce,
- "write-only" => Access::WriteOnly,
- "writeOnce" => Access::WriteOnce,
- _ => panic!("unknown access variant: {}", text),
- }
- }
-}
-
-#[derive(Clone, Debug)]
-pub struct Field {
- pub name: String,
- pub description: Option,
- pub bit_range: BitRange,
- pub access: Option,
- pub enumerated_values: Vec,
- pub write_constraint: Option,
- // Reserve the right to add more fields to this struct
- _extensible: (),
-}
-
-impl Field {
- fn parse(tree: &Element) -> Field {
- assert_eq!(tree.name, "field");
-
- Field {
- name: try!(tree.get_child_text("name")),
- description: tree.get_child_text("description"),
- bit_range: BitRange::parse(tree),
- access: tree.get_child("access").map(Access::parse),
- enumerated_values: tree.children
- .iter()
- .filter(|t| t.name == "enumeratedValues")
- .map(EnumeratedValues::parse)
- .collect::>(),
- write_constraint: tree.get_child("writeConstraint")
- .map(WriteConstraint::parse),
- _extensible: (),
- }
- }
-}
-
-#[derive(Clone, Copy, Debug)]
-pub struct BitRange {
- pub offset: u32,
- pub width: u32,
-}
-
-impl BitRange {
- fn parse(tree: &Element) -> BitRange {
- let (end, start): (u32, u32) = if let Some(range) =
- tree.get_child("bitRange") {
- let text = try!(range.text.as_ref());
-
- assert!(text.starts_with('['));
- assert!(text.ends_with(']'));
-
- let mut parts = text[1..text.len() - 1].split(':');
-
- (try!(try!(parts.next()).parse()), try!(try!(parts.next()).parse()))
- } else if let (Some(lsb), Some(msb)) =
- (tree.get_child("lsb"), tree.get_child("msb")) {
- (try!(parse::u32(msb)), try!(parse::u32(lsb)))
- } else {
- return BitRange {
- offset: try!(parse::u32(try!(tree.get_child("bitOffset")))),
- width: try!(parse::u32(try!(tree.get_child("bitWidth")))),
- };
- };
-
- BitRange {
- offset: start,
- width: end - start + 1,
- }
- }
-}
-
-#[derive(Clone, Copy, Debug)]
-pub struct WriteConstraintRange {
- pub min: u32,
- pub max: u32,
-}
-
-impl WriteConstraintRange {
- fn parse(tree: &Element) -> WriteConstraintRange {
- WriteConstraintRange {
- min: try!(try!(tree.get_child_text("minimum")).parse()),
- max: try!(try!(tree.get_child_text("maximum")).parse()),
- }
- }
-}
-
-#[derive(Clone, Copy, Debug)]
-pub enum WriteConstraint {
- WriteAsRead(bool),
- UseEnumeratedValues(bool),
- Range(WriteConstraintRange),
-}
-
-impl WriteConstraint {
- fn parse(tree: &Element) -> WriteConstraint {
- if tree.children.len() == 1 {
- let ref field = tree.children[0].name;
- // Write constraint can only be one of the following
- match field.as_ref() {
- "writeAsRead" => {
- WriteConstraint::WriteAsRead(
- try!(
- tree.get_child(field.as_ref())
- .map(|t| try!(parse::bool(t)))
- ),
- )
- }
- "useEnumeratedValues" => {
- WriteConstraint::UseEnumeratedValues(
- try!(
- tree.get_child(field.as_ref())
- .map(|t| try!(parse::bool(t)))
- ),
- )
- }
- "range" => {
- WriteConstraint::Range(
- try!(
- tree.get_child(field.as_ref())
- .map(WriteConstraintRange::parse)
- ),
- )
- }
- v => panic!("unknown variant: {}", v),
- }
- } else {
- panic!("found more than one element")
- }
- }
-}
-
-/// Register default properties
-#[derive(Clone, Copy, Debug)]
-pub struct Defaults {
- pub size: Option,
- pub reset_value: Option,
- pub reset_mask: Option,
- pub access: Option,
- // Reserve the right to add more fields to this struct
- _extensible: (),
-}
-
-impl Defaults {
- fn parse(tree: &Element) -> Defaults {
- Defaults {
- size: tree.get_child("size").map(|t| try!(parse::u32(t))),
- reset_value:
- tree.get_child("resetValue").map(|t| try!(parse::u32(t))),
- reset_mask:
- tree.get_child("resetMask").map(|t| try!(parse::u32(t))),
- access: tree.get_child("access").map(Access::parse),
- _extensible: (),
- }
- }
-}
-
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-pub enum Usage {
- Read,
- Write,
- ReadWrite,
-}
-
-impl Usage {
- fn parse(tree: &Element) -> Usage {
- let text = try!(tree.text.as_ref());
-
- match &text[..] {
- "read" => Usage::Read,
- "write" => Usage::Write,
- "read-write" => Usage::ReadWrite,
- _ => panic!("unknown usage variant: {}", text),
- }
- }
-}
-
-#[derive(Clone, Debug)]
-pub struct EnumeratedValues {
- pub name: Option,
- pub usage: Option,
- pub derived_from: Option,
- pub values: Vec,
- // Reserve the right to add more fields to this struct
- _extensible: (),
-}
-
-impl EnumeratedValues {
- fn parse(tree: &Element) -> EnumeratedValues {
- assert_eq!(tree.name, "enumeratedValues");
-
- EnumeratedValues {
- name: tree.get_child_text("name"),
- usage: tree.get_child("usage").map(Usage::parse),
- derived_from: tree.attributes
- .get(&"derivedFrom".to_owned())
- .map(|s| s.to_owned()),
- values: tree.children
- .iter()
- .filter_map(EnumeratedValue::parse)
- .collect(),
- _extensible: (),
- }
- }
-}
-
-#[derive(Clone, Debug)]
-pub struct EnumeratedValue {
- pub name: String,
- pub description: Option,
- pub value: Option,
- pub is_default: Option,
- // Reserve the right to add more fields to this struct
- _extensible: (),
-}
+/// Parses the contents of a SVD file (XML)
+pub fn parse(xml: &str) -> Device {
+ let tree = &try_get_child!(Element::parse(xml.as_bytes()));
+ Device::parse(tree)
+}
+
+pub fn encode(device: &Device) -> Element {
+ device.encode()
+}
+
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use std::fs;
+ use std::process::Command;
+ use std::fs::File;
+ use std::io::prelude::*;
+ use std::path::Path;
+
+ #[test]
+ fn decode_encode() {
+ let path = String::from("./examples");
+
+ let files: Vec = fs::read_dir(&path)
+ .unwrap()
+ .map(|res| res.unwrap())
+ .filter(|all| !all.metadata().unwrap().is_dir())
+ .map(|file| file.file_name().into_string().unwrap())
+ .filter(|filename| {
+ !(filename.starts_with(".") || filename.starts_with("_")) && filename.ends_with(".svd")
+ })
+ .map(|filename| {
+ String::from(Path::new(&filename).file_stem().unwrap().to_str().unwrap())
+ })
+ .collect();
+
+ println!("Files: {:?}", files);
+
+ for name in files {
+ let source_file = format!("{}/{}.svd", path, name);
+ let original_file = format!("target/{}-original.svd", name);
+ let encoded_file = format!("target/{}-encoded.svd", name);
+ let diff_file = format!("target/{}-diff.svd", name);
+
+ // Load orignal target file
+ let mut xml = String::new();
+ let mut f = fs::File::open(&source_file).unwrap();
+ f.read_to_string(&mut xml).unwrap();
+
+ // Parse device info
+ let device = parse(&xml);
+
+ // Encode device info
+
+ encode(&device).write(File::create(&encoded_file).unwrap());
+
+ // Normalize source info
+ let output = Command::new("xmllint")
+ .arg("--c14n")
+ .arg(source_file.clone())
+ .output()
+ .unwrap();
+ let mut f = File::create(original_file.clone()).unwrap();
+ f.write_all(&output.stdout).unwrap();
+
+ let output = Command::new("xmllint")
+ .arg("--format")
+ .arg(source_file.clone())
+ .output()
+ .unwrap();
+ let mut f = File::create(original_file.clone()).unwrap();
+ f.write_all(&output.stdout).unwrap();
+
+ // Normalise encoded info
+ let output = Command::new("xmllint")
+ .arg("--c14n")
+ .arg(encoded_file.clone())
+ .output()
+ .unwrap();
+ let mut f = File::create(encoded_file.clone()).unwrap();
+ f.write_all(&output.stdout).unwrap();
+
+ let output = Command::new("xmllint")
+ .arg("--format")
+ .arg(encoded_file.clone())
+ .output()
+ .unwrap();
+ let mut f = File::create(encoded_file.clone()).unwrap();
+ f.write_all(&output.stdout).unwrap();
+
+ // Diff normalised source and output
+ let output = Command::new("diff")
+ .arg(original_file)
+ .arg(encoded_file)
+ .output()
+ .unwrap();
+ let mut f = File::create(diff_file).unwrap();
+ f.write_all(&output.stdout).unwrap();
-impl EnumeratedValue {
- fn parse(tree: &Element) -> Option {
- if tree.name != "enumeratedValue" {
- return None;
}
-
- Some(
- EnumeratedValue {
- name: try!(tree.get_child_text("name")),
- description: tree.get_child_text("description"),
- value: tree.get_child("value").map(|t| try!(parse::u32(t))),
- is_default: tree.get_child_text("isDefault").map(
- |t| {
- try!(t.parse())
- },
- ),
- _extensible: (),
- },
- )
}
}
diff --git a/src/parse.rs b/src/parse.rs
index aeb507f0..2f9eb95a 100644
--- a/src/parse.rs
+++ b/src/parse.rs
@@ -1,7 +1,8 @@
use xmltree::Element;
+
pub fn u32(tree: &Element) -> Option {
- let text = try!(tree.text.as_ref());
+ let text = try_get_child!(tree.text.as_ref());
if text.starts_with("0x") || text.starts_with("0X") {
u32::from_str_radix(&text["0x".len()..], 16).ok()
@@ -16,19 +17,19 @@ pub fn u32(tree: &Element) -> Option {
}
pub fn bool(tree: &Element) -> Option {
- let text = try!(tree.text.as_ref());
+ let text = try_get_child!(tree.text.as_ref());
match text.as_ref() {
"0" => Some(false),
"1" => Some(true),
- _ => text.parse::().ok()
+ _ => text.parse::().ok(),
}
}
pub fn dim_index(text: &str) -> Vec {
if text.contains('-') {
let mut parts = text.splitn(2, '-');
- let start = try!(try!(parts.next()).parse::());
- let end = try!(try!(parts.next()).parse::()) + 1;
+ let start = try_get_child!(try_get_child!(parts.next()).parse::());
+ let end = try_get_child!(try_get_child!(parts.next()).parse::()) + 1;
(start..end).map(|i| i.to_string()).collect()
} else if text.contains(',') {
diff --git a/src/peripheral.rs b/src/peripheral.rs
new file mode 100644
index 00000000..b8106442
--- /dev/null
+++ b/src/peripheral.rs
@@ -0,0 +1,150 @@
+
+use std::collections::HashMap;
+
+use xmltree::Element;
+use either::Either;
+
+use elementext::ElementExt;
+use helpers::{ParseElem, EncodeElem, new_element};
+use parse;
+
+use interrupt::Interrupt;
+use register::Register;
+use cluster::Cluster;
+use addressblock::AddressBlock;
+use registercluster::cluster_register_parse;
+
+
+#[derive(Clone, Debug)]
+pub struct Peripheral {
+ pub name: String,
+ pub version: Option,
+ pub display_name: Option,
+ pub group_name: Option,
+ pub description: Option,
+ pub base_address: u32,
+ pub address_block: Option,
+ pub interrupt: Vec,
+ /// `None` indicates that the `` node is not present
+ pub registers: Option>>,
+ pub derived_from: Option,
+ // Reserve the right to add more fields to this struct
+ pub(crate) _extensible: (),
+}
+
+impl ParseElem for Peripheral {
+ fn parse(tree: &Element) -> Peripheral {
+ assert_eq!(tree.name, "peripheral");
+
+ Peripheral {
+ name: try_get_child!(tree.get_child_text("name")),
+ version: tree.get_child_text("version"),
+ display_name: tree.get_child_text("displayName"),
+ group_name: tree.get_child_text("groupName"),
+ description: tree.get_child_text("description"),
+ base_address: try_get_child!(parse::u32(try_get_child!(tree.get_child("baseAddress")))),
+ address_block: tree.get_child("addressBlock").map(AddressBlock::parse),
+ interrupt: tree.children
+ .iter()
+ .filter(|t| t.name == "interrupt")
+ .map(Interrupt::parse)
+ .collect::>(),
+ registers: tree.get_child("registers").map(|rs| {
+ rs.children.iter().map(cluster_register_parse).collect()
+ }),
+ derived_from: tree.attributes.get("derivedFrom").map(|s| s.to_owned()),
+ _extensible: (),
+ }
+ }
+}
+
+impl EncodeElem for Peripheral {
+ fn encode(&self) -> Element {
+ let mut elem = Element {
+ name: String::from("peripheral"),
+ attributes: HashMap::new(),
+ children: vec![new_element("name", Some(self.name.clone()))],
+ text: None,
+ };
+
+ match self.version {
+ Some(ref v) => {
+ elem.children.push(
+ new_element("version", Some(format!("{}", v))),
+ );
+ }
+ None => (),
+ };
+ match self.display_name {
+ Some(ref v) => {
+ elem.children.push(new_element(
+ "displayName",
+ Some(format!("{}", v)),
+ ));
+ }
+ None => (),
+ };
+ match self.group_name {
+ Some(ref v) => {
+ elem.children.push(new_element(
+ "groupName",
+ Some(format!("{}", v)),
+ ));
+ }
+ None => (),
+ };
+ match self.description {
+ Some(ref v) => {
+ elem.children.push(new_element(
+ "description",
+ Some(format!("{}", v)),
+ ));
+ }
+ None => (),
+ };
+ elem.children.push(new_element(
+ "baseAddress",
+ Some(format!("0x{:.08x}", self.base_address)),
+ ));
+ match self.address_block {
+ Some(ref v) => {
+ elem.children.push(v.encode());
+ }
+ None => (),
+ };
+
+ elem.children.append(&mut self.interrupt
+ .iter()
+ .map(Interrupt::encode)
+ .collect());
+ match self.registers {
+ Some(ref v) => {
+ elem.children.push(Element {
+ name: String::from("registers"),
+ attributes: HashMap::new(),
+ children: v.iter()
+ .map(|&ref e| if e.is_left() {
+ e.clone().left().unwrap().encode()
+ } else {
+ e.clone().right().unwrap().encode()
+ })
+ .collect(),
+ text: None,
+ });
+ }
+ None => (),
+ };
+
+ match self.derived_from {
+ Some(ref v) => {
+ elem.attributes.insert(
+ String::from("derivedFrom"),
+ format!("{}", v),
+ );
+ }
+ None => (),
+ }
+
+ elem
+ }
+}
diff --git a/src/register.rs b/src/register.rs
new file mode 100644
index 00000000..b5c7ed62
--- /dev/null
+++ b/src/register.rs
@@ -0,0 +1,62 @@
+
+use std::ops::Deref;
+
+use xmltree::Element;
+
+use helpers::{ParseElem, EncodeElem};
+
+use registerinfo::RegisterInfo;
+use registerarrayinfo::RegisterArrayInfo;
+
+
+#[derive(Clone, Debug, PartialEq)]
+pub enum Register {
+ Single(RegisterInfo),
+ Array(RegisterInfo, RegisterArrayInfo),
+}
+
+
+impl Deref for Register {
+ type Target = RegisterInfo;
+
+ fn deref(&self) -> &RegisterInfo {
+ match *self {
+ Register::Single(ref info) => info,
+ Register::Array(ref info, _) => info,
+ }
+ }
+}
+
+
+impl ParseElem for Register {
+ // TODO handle "clusters", return `Register` not an `Option`
+ fn parse(tree: &Element) -> Register {
+ assert_eq!(tree.name, "register");
+
+ let info = RegisterInfo::parse(tree);
+
+ if tree.get_child("dimIncrement").is_some() {
+ let array_info = RegisterArrayInfo::parse(tree);
+ assert!(info.name.contains("%s"));
+ if let Some(ref indices) = array_info.dim_index {
+ assert_eq!(array_info.dim as usize, indices.len())
+ }
+ Register::Array(info, array_info)
+ } else {
+ Register::Single(info)
+ }
+ }
+}
+
+impl EncodeElem for Register {
+ fn encode(&self) -> Element {
+ match *self {
+ Register::Single(ref info) => info.encode(),
+ Register::Array(ref info, ref _array_info) => {
+ // TODO: fix this (does not encode array stuff)
+ // Not even slightly sure what to do here
+ info.encode()
+ }
+ }
+ }
+}
diff --git a/src/registerarrayinfo.rs b/src/registerarrayinfo.rs
new file mode 100644
index 00000000..a7023a20
--- /dev/null
+++ b/src/registerarrayinfo.rs
@@ -0,0 +1,41 @@
+
+use std::collections::HashMap;
+
+use xmltree::Element;
+
+use elementext::ElementExt;
+use helpers::{ParseElem, EncodeElem};
+use parse;
+
+
+#[derive(Clone, Debug, PartialEq)]
+pub struct RegisterArrayInfo {
+ pub dim: u32,
+ pub dim_increment: u32,
+ pub dim_index: Option>,
+}
+
+impl ParseElem for RegisterArrayInfo {
+ fn parse(tree: &Element) -> RegisterArrayInfo {
+ RegisterArrayInfo {
+ dim: try_get_child!(tree.get_child_text("dim").unwrap().parse::()),
+ dim_increment: try_get_child!(tree.get_child("dimIncrement").map(|t| {
+ try_get_child!(parse::u32(t))
+ })),
+ dim_index: tree.get_child("dimIndex").map(|c| {
+ parse::dim_index(try_get_child!(c.text.as_ref()))
+ }),
+ }
+ }
+}
+
+impl EncodeElem for RegisterArrayInfo {
+ fn encode(&self) -> Element {
+ Element {
+ name: String::from("NOPE"),
+ attributes: HashMap::new(),
+ children: Vec::new(),
+ text: None,
+ }
+ }
+}
diff --git a/src/registercluster.rs b/src/registercluster.rs
new file mode 100644
index 00000000..c1ccac09
--- /dev/null
+++ b/src/registercluster.rs
@@ -0,0 +1,17 @@
+
+use xmltree::Element;
+use either::Either;
+
+use helpers::ParseElem;
+use register::Register;
+use cluster::Cluster;
+
+pub fn cluster_register_parse(tree: &Element) -> Either {
+ if tree.name == "register" {
+ Either::Left(Register::parse(tree))
+ } else if tree.name == "cluster" {
+ Either::Right(Cluster::parse(tree))
+ } else {
+ unreachable!()
+ }
+}
diff --git a/src/registerclusterarrayinfo.rs b/src/registerclusterarrayinfo.rs
new file mode 100644
index 00000000..e7de398a
--- /dev/null
+++ b/src/registerclusterarrayinfo.rs
@@ -0,0 +1,33 @@
+
+use xmltree::Element;
+
+use elementext::ElementExt;
+use helpers::{ParseElem, EncodeElem, new_element};
+use parse;
+
+#[derive(Clone, Debug, PartialEq)]
+pub struct RegisterClusterArrayInfo {
+ pub dim: u32,
+ pub dim_increment: u32,
+ pub dim_index: Option>,
+}
+
+impl ParseElem for RegisterClusterArrayInfo {
+ fn parse(tree: &Element) -> RegisterClusterArrayInfo {
+ RegisterClusterArrayInfo {
+ dim: try_get_child!(tree.get_child_text("dim").unwrap().parse::()),
+ dim_increment: try_get_child!(tree.get_child("dimIncrement").map(|t| {
+ try_get_child!(parse::u32(t))
+ })),
+ dim_index: tree.get_child("dimIndex").map(|c| {
+ parse::dim_index(try_get_child!(c.text.as_ref()))
+ }),
+ }
+ }
+}
+
+impl EncodeElem for RegisterClusterArrayInfo {
+ fn encode(&self) -> Element {
+ new_element("FAKE", None)
+ }
+}
diff --git a/src/registerinfo.rs b/src/registerinfo.rs
new file mode 100644
index 00000000..c1d990b1
--- /dev/null
+++ b/src/registerinfo.rs
@@ -0,0 +1,205 @@
+
+use std::collections::HashMap;
+
+use xmltree::Element;
+
+use elementext::ElementExt;
+use helpers::{ParseElem, EncodeElem, new_element};
+use parse;
+
+use Field;
+use access::Access;
+use writeconstraint::WriteConstraint;
+
+
+#[derive(Clone, Debug, PartialEq)]
+pub struct RegisterInfo {
+ pub name: String,
+ pub description: String,
+ pub address_offset: u32,
+ pub size: Option,
+ pub access: Option,
+ pub reset_value: Option,
+ pub reset_mask: Option,
+ /// `None` indicates that the `` node is not present
+ pub fields: Option>,
+ pub write_constraint: Option,
+ // Reserve the right to add more fields to this struct
+ pub(crate) _extensible: (),
+}
+
+impl ParseElem for RegisterInfo {
+ fn parse(tree: &Element) -> RegisterInfo {
+ RegisterInfo {
+ name: try_get_child!(tree.get_child_text("name")),
+ description: try_get_child!(tree.get_child_text("description")),
+ address_offset: {
+ try_get_child!(parse::u32(try_get_child!(tree.get_child("addressOffset"))))
+ },
+ size: tree.get_child("size").map(
+ |t| try_get_child!(parse::u32(t)),
+ ),
+ access: tree.get_child("access").map(Access::parse),
+ reset_value: tree.get_child("resetValue").map(|t| {
+ try_get_child!(parse::u32(t))
+ }),
+ reset_mask: tree.get_child("resetMask").map(
+ |t| try_get_child!(parse::u32(t)),
+ ),
+ fields: tree.get_child("fields").map(|fs| {
+ fs.children.iter().map(Field::parse).collect()
+ }),
+ write_constraint: tree.get_child("writeConstraint").map(
+ WriteConstraint::parse,
+ ),
+ _extensible: (),
+ }
+ }
+}
+
+impl EncodeElem for RegisterInfo {
+ fn encode(&self) -> Element {
+ let mut elem = Element {
+ name: String::from("register"),
+ attributes: HashMap::new(),
+ children: vec![
+ new_element("name", Some(self.name.clone())),
+ new_element("description", Some(self.description.clone())),
+ new_element(
+ "addressOffset",
+ Some(format!("0x{:x}", self.address_offset))
+ ),
+ ],
+ text: None,
+ };
+
+ match self.size {
+ Some(ref v) => {
+ elem.children.push(
+ new_element("size", Some(format!("{}", v))),
+ );
+ }
+ None => (),
+ };
+
+ match self.access {
+ Some(ref v) => {
+ elem.children.push(v.encode());
+ }
+ None => (),
+ };
+
+ match self.reset_value {
+ Some(ref v) => {
+ elem.children.push(new_element(
+ "resetValue",
+ Some(format!("0x{:08.x}", v)),
+ ));
+ }
+ None => (),
+ };
+
+ match self.reset_mask {
+ Some(ref v) => {
+ elem.children.push(new_element(
+ "resetMask",
+ Some(format!("0x{:08.x}", v)),
+ ));
+ }
+ None => (),
+ };
+
+ match self.fields {
+ Some(ref v) => {
+ let fields = Element {
+ name: String::from("fields"),
+ attributes: HashMap::new(),
+ children: v.iter().map(Field::encode).collect(),
+ text: None,
+ };
+ elem.children.push(fields);
+ }
+ None => (),
+ };
+
+ match self.write_constraint {
+ Some(ref v) => {
+ elem.children.push(v.encode());
+ }
+ None => (),
+ };
+
+ elem
+ }
+}
+
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use bitrange::*;
+
+ #[test]
+ fn decode_encode() {
+ let types = vec![
+ (
+ RegisterInfo {
+ name: String::from("WRITECTRL"),
+ description: String::from("Write Control Register"),
+ address_offset: 8,
+ size: Some(32),
+ access: Some(Access::ReadWrite),
+ reset_value: Some(0x00000000),
+ reset_mask: Some(0x00000023),
+ fields: Some(vec![
+ Field {
+ name: String::from("WREN"),
+ description: Some(String::from("Enable Write/Erase Controller")),
+ bit_range: BitRange {
+ offset: 0,
+ width: 1,
+ range_type: BitRangeType::OffsetWidth,
+ },
+ access: Some(Access::ReadWrite),
+ enumerated_values: Vec::new(),
+ write_constraint: None,
+ _extensible: (),
+ },
+ ]),
+ write_constraint: None,
+ _extensible: (),
+ },
+ String::from(
+ "
+
+ WRITECTRL
+ Write Control Register
+ 0x8
+ 32
+ read-write
+ 0x00000000
+ 0x00000023
+
+
+ WREN
+ Enable Write/Erase Controller
+ 0
+ 1
+ read-write
+
+
+
+ ",
+ )
+ ),
+ ];
+
+ for (a, s) in types {
+ let tree1 = &try_get_child!(Element::parse(s.as_bytes()));
+ let v = RegisterInfo::parse(tree1);
+ assert_eq!(v, a, "Parsing `{}` expected `{:?}`", s, a);
+ let tree2 = &v.encode();
+ assert_eq!(tree1, tree2, "Encoding {:?} expected {}", a, s);
+ }
+ }
+}
diff --git a/src/types.rs b/src/types.rs
new file mode 100644
index 00000000..dfd1133c
--- /dev/null
+++ b/src/types.rs
@@ -0,0 +1,101 @@
+
+// Encoding type
+#[derive(Debug)]
+enum Encoding {
+ Bin,
+ Oct,
+ Dec,
+ Hex,
+}
+
+// Uint type for less lossy encoding/decoding
+#[derive(Debug)]
+pub struct Uint {
+ pub value: u32,
+ width: usize,
+ encoding: Encoding,
+}
+
+// Equality based only on value
+impl PartialEq for Uint {
+ fn eq(&self, other: &Self) -> bool {
+ self.value == other.value
+ }
+}
+
+impl Uint {
+ pub fn parse(text: &str) -> Uint {
+ if text.starts_with("0x") || text.starts_with("0X") {
+ Uint {
+ value: u32::from_str_radix(&text["0x".len()..], 16).unwrap(),
+ width: text.len()-2,
+ encoding: Encoding::Hex,
+ }
+ } else if text.starts_with('#') {
+ Uint {
+ value: u32::from_str_radix(&str::replace(&text["#".len()..], "x", "0"), 2).unwrap(),
+ width: text.len()-1,
+ encoding: Encoding::Bin,
+ }
+ } else if text.starts_with('0') {
+ Uint {
+ value: u32::from_str_radix(text, 8).unwrap(),
+ width: text.len()-1,
+ encoding: Encoding::Oct,
+ }
+ } else {
+ Uint {
+ value: text.parse().unwrap(),
+ width: text.len(),
+ encoding: Encoding::Dec,
+ }
+ }
+ }
+
+ pub fn encode(&self) -> String {
+ match self.encoding {
+ Encoding::Dec => {
+ let base = &format!("{}", self.value);
+ let packing = String::from_utf8(vec!['0' as u8; self.width - base.len()]).unwrap();
+ format!("{}{}", packing, base)
+ },
+ Encoding::Hex => {
+ let base = format!("{:.x}", self.value);
+ let packing = String::from_utf8(vec!['0' as u8; self.width - base.len()]).unwrap();
+ format!("0x{}{}", packing, base)
+ },
+ Encoding::Oct => {
+ let base = &format!("{:o}", self.value);
+ let packing = String::from_utf8(vec!['0' as u8; self.width - base.len()]).unwrap();
+ format!("0{}{}", packing, base)
+ },
+ Encoding::Bin => {
+ let base = format!("{:b}", self.value);
+ let packing = String::from_utf8(vec!['0' as u8; self.width - base.len()]).unwrap();
+ format!("#{}{}", packing, base)
+ },
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn uint_decode_encode() {
+ let tests = vec![
+ ("104", Uint{value: 104, width: 3, encoding: Encoding::Dec}),
+ ("0x013a", Uint{value: 314, width: 4, encoding: Encoding::Hex}),
+ ("01232", Uint{value: 666, width: 4, encoding: Encoding::Oct}),
+ ("#0101", Uint{value: 5, width: 4, encoding: Encoding::Bin}),
+ ];
+
+ for (text, value) in tests {
+ let a = Uint::parse(text);
+ assert_eq!(a, value);
+ let b = value.encode();
+ assert_eq!(b, text);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/usage.rs b/src/usage.rs
new file mode 100644
index 00000000..b66a5e29
--- /dev/null
+++ b/src/usage.rs
@@ -0,0 +1,66 @@
+
+use std::collections::HashMap;
+
+use xmltree::Element;
+
+use helpers::{ParseElem, EncodeElem};
+
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub enum Usage {
+ Read,
+ Write,
+ ReadWrite,
+}
+
+impl ParseElem for Usage {
+ fn parse(tree: &Element) -> Usage {
+ let text = try_get_child!(tree.text.as_ref());
+
+ match &text[..] {
+ "read" => Usage::Read,
+ "write" => Usage::Write,
+ "read-write" => Usage::ReadWrite,
+ _ => panic!("unknown usage variant: {}", text),
+ }
+ }
+}
+
+impl EncodeElem for Usage {
+ fn encode(&self) -> Element {
+ let text = match *self {
+ Usage::Read => String::from("read"),
+ Usage::Write => String::from("write"),
+ Usage::ReadWrite => String::from("read-write"),
+ };
+
+ Element {
+ name: String::from("usage"),
+ attributes: HashMap::new(),
+ children: Vec::new(),
+ text: Some(text),
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn decode_encode() {
+ let types = vec![
+ (Usage::Read, String::from("read")),
+ (Usage::Write, String::from("write")),
+ (Usage::ReadWrite, String::from("read-write")),
+ ];
+
+ for (e, s) in types {
+ let tree1 = &try_get_child!(Element::parse(s.as_bytes()));
+ let elem = Usage::parse(tree1);
+ assert_eq!(elem, e, "Parsing `{}` expected `{:?}`", s, e);
+ let tree2 = &elem.encode();
+ assert_eq!(tree1, tree2, "Encoding {:?} expected {}", e, s);
+ }
+ }
+}
diff --git a/src/writeconstraint.rs b/src/writeconstraint.rs
new file mode 100644
index 00000000..63acc6c2
--- /dev/null
+++ b/src/writeconstraint.rs
@@ -0,0 +1,91 @@
+
+use std::collections::HashMap;
+
+use xmltree::Element;
+
+use helpers::{ParseElem, EncodeElem, new_element};
+use parse;
+
+use writeconstraintrange::WriteConstraintRange;
+
+
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum WriteConstraint {
+ WriteAsRead(bool),
+ UseEnumeratedValues(bool),
+ Range(WriteConstraintRange),
+}
+
+impl ParseElem for WriteConstraint {
+ fn parse(tree: &Element) -> WriteConstraint {
+ if tree.children.len() == 1 {
+ let ref field = tree.children[0].name;
+ // Write constraint can only be one of the following
+ match field.as_ref() {
+ "writeAsRead" => {
+ WriteConstraint::WriteAsRead(try_get_child!(tree.get_child(field.as_ref()).map(|t| {
+ try_get_child!(parse::bool(t))
+ })))
+ }
+ "useEnumeratedValues" => {
+ WriteConstraint::UseEnumeratedValues(try_get_child!(tree.get_child(field.as_ref()).map(|t| {
+ try_get_child!(parse::bool(t))
+ })))
+ }
+ "range" => {
+ WriteConstraint::Range(try_get_child!(tree.get_child(field.as_ref()).map(
+ WriteConstraintRange::parse,
+ )))
+ }
+ v => panic!("unknown variant: {}", v),
+ }
+ } else {
+ panic!("found more than one element")
+ }
+ }
+}
+
+impl EncodeElem for WriteConstraint {
+ fn encode(&self) -> Element {
+ let v = match *self {
+ WriteConstraint::WriteAsRead(v) => new_element("writeAsRead", Some(format!("{}", v))),
+ WriteConstraint::UseEnumeratedValues(v) => new_element("useEnumeratedValues", Some(format!("{}", v))),
+ WriteConstraint::Range(v) => v.encode(),
+ };
+
+ Element {
+ name: String::from("WriteConstraint"),
+ attributes: HashMap::new(),
+ children: vec![v],
+ text: None,
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn decode_encode() {
+ let examples = vec![
+ (
+ String::from(
+ "true",
+ ),
+ WriteConstraint::WriteAsRead(true)
+ ),
+ ];
+
+ for (example, expected) in examples {
+ let tree1 = &try_get_child!(Element::parse(example.as_bytes()));
+
+ let parsed = WriteConstraint::parse(tree1);
+ assert_eq!(parsed, expected, "Parsing tree failed");
+
+ let tree2 = &parsed.encode();
+ assert_eq!(tree1, tree2, "Encoding value failed");
+ }
+
+ }
+}
diff --git a/src/writeconstraintrange.rs b/src/writeconstraintrange.rs
new file mode 100644
index 00000000..fe92ad65
--- /dev/null
+++ b/src/writeconstraintrange.rs
@@ -0,0 +1,38 @@
+
+use std::collections::HashMap;
+
+use xmltree::Element;
+
+
+use elementext::ElementExt;
+use helpers::{ParseElem, EncodeElem, new_element};
+
+
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub struct WriteConstraintRange {
+ pub min: u32,
+ pub max: u32,
+}
+
+impl ParseElem for WriteConstraintRange {
+ fn parse(tree: &Element) -> WriteConstraintRange {
+ WriteConstraintRange {
+ min: try_get_child!(try_get_child!(tree.get_child_text("minimum")).parse()),
+ max: try_get_child!(try_get_child!(tree.get_child_text("maximum")).parse()),
+ }
+ }
+}
+
+impl EncodeElem for WriteConstraintRange {
+ fn encode(&self) -> Element {
+ Element {
+ name: String::from("range"),
+ attributes: HashMap::new(),
+ children: vec![
+ new_element("min", Some(format!("0x{:08.x}", self.min))),
+ new_element("max", Some(format!("0x{:08.x}", self.max))),
+ ],
+ text: None,
+ }
+ }
+}