diff --git a/svd-encoder/CHANGELOG.md b/svd-encoder/CHANGELOG.md index cb7d00ec..7d1cb1ed 100644 --- a/svd-encoder/CHANGELOG.md +++ b/svd-encoder/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## Unreleased +- Adapt the `riscv` element to handle `riscv::Exception`. - Add `riscv` element for configuration parameters related to RISC-V targets. You must use the `unstable-riscv` feature to enable this exeperimental element. - Bump MSRV to 1.65.0 diff --git a/svd-encoder/src/riscv.rs b/svd-encoder/src/riscv.rs index dfb66f7a..0047a9fe 100644 --- a/svd-encoder/src/riscv.rs +++ b/svd-encoder/src/riscv.rs @@ -1,5 +1,5 @@ use super::{new_node, Config, Element, Encode, EncodeError, XMLNode}; -use crate::svd::riscv::{Hart, Priority, Riscv}; +use crate::svd::riscv::{Exception, Hart, Priority, Riscv}; impl Encode for Riscv { type Error = EncodeError; @@ -16,6 +16,15 @@ impl Encode for Riscv { } elem.children.push(XMLNode::Element(interrupts)); } + if !self.exceptions.is_empty() { + let mut exceptions = Element::new("exceptions"); + for exception in &self.exceptions { + exceptions + .children + .push(exception.encode_node_with_config(config)?); + } + elem.children.push(XMLNode::Element(exceptions)); + } if !self.priorities.is_empty() { let mut priorities = Element::new("priorities"); for priority in &self.priorities { @@ -37,6 +46,22 @@ impl Encode for Riscv { } } +impl Encode for Exception { + type Error = EncodeError; + + fn encode_with_config(&self, _config: &Config) -> Result { + let mut children = vec![new_node("name", self.name.clone())]; + if let Some(desc) = &self.description { + children.push(new_node("description", desc.clone())); + } + children.push(new_node("value", format!("{}", self.value))); + + let mut elem = Element::new("exception"); + elem.children = children; + Ok(elem) + } +} + impl Encode for Priority { type Error = EncodeError; diff --git a/svd-parser/CHANGELOG.md b/svd-parser/CHANGELOG.md index d9890a0d..61094d4a 100644 --- a/svd-parser/CHANGELOG.md +++ b/svd-parser/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## Unreleased +- Adapt the `riscv` element to handle `riscv::Exception`. - Add `riscv` element for configuration parameters related to RISC-V targets. You must use the `unstable-riscv` feature to enable this exeperimental element. - Bump MSRV to 1.65.0 diff --git a/svd-parser/src/riscv.rs b/svd-parser/src/riscv.rs index fe744e4a..b04457f1 100644 --- a/svd-parser/src/riscv.rs +++ b/svd-parser/src/riscv.rs @@ -1,5 +1,5 @@ use super::*; -use crate::svd::riscv::{Hart, Interrupt, Priority, Riscv}; +use crate::svd::riscv::{Exception, Hart, Interrupt, Priority, Riscv}; impl Parse for Riscv { type Object = Self; @@ -22,6 +22,15 @@ impl Parse for Riscv { builder = builder.core_interrupts(interrupts?); } + if let Some(exceptions) = tree.get_child("exceptions") { + let exceptions: Result, _> = exceptions + .children() + .filter(|t| t.is_element() && t.has_tag_name("exception")) + .map(|i| Exception::parse(&i, config)) + .collect(); + builder = builder.exceptions(exceptions?); + } + if let Some(priorities) = tree.get_child("priorities") { let priorities: Result, _> = priorities .children() @@ -29,7 +38,7 @@ impl Parse for Riscv { .map(|i| Priority::parse(&i, config)) .collect(); builder = builder.priorities(priorities?); - }; + } if let Some(harts) = tree.get_child("harts") { let harts: Result, _> = harts @@ -38,7 +47,7 @@ impl Parse for Riscv { .map(|i| Hart::parse(&i, config)) .collect(); builder = builder.harts(harts?); - }; + } builder .build(config.validate_level) @@ -46,6 +55,25 @@ impl Parse for Riscv { } } +impl Parse for Exception { + type Object = Self; + type Error = SVDErrorAt; + type Config = Config; + + fn parse(tree: &Node, config: &Config) -> Result { + if !tree.has_tag_name("exception") { + return Err(SVDError::NotExpectedTag("exception".to_string()).at(tree.id())); + } + + Exception::builder() + .name(tree.get_child_text("name")?) + .description(tree.get_child_text_opt("description")?) + .value(tree.get_child_u32("value")?) + .build(config.validate_level) + .map_err(|e| SVDError::from(e).at(tree.id())) + } +} + impl Parse for Priority { type Object = Self; type Error = SVDErrorAt; diff --git a/svd-rs/CHANGELOG.md b/svd-rs/CHANGELOG.md index 149c9f60..bcf7d220 100644 --- a/svd-rs/CHANGELOG.md +++ b/svd-rs/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## Unreleased +- Add `riscv::Exception` for custom exception source enumerations. - Add `riscv` element for configuration parameters related to RISC-V targets. You must use the `unstable-riscv` feature to enable this exeperimental element. - Add `DataType` diff --git a/svd-rs/src/riscv.rs b/svd-rs/src/riscv.rs index 7398ec9e..a78589ad 100644 --- a/svd-rs/src/riscv.rs +++ b/svd-rs/src/riscv.rs @@ -1,6 +1,10 @@ pub use super::Interrupt; use super::{BuildError, SvdError, ValidateLevel}; +/// Description of the custom exceptions in the device. +pub mod exception; +pub use exception::Exception; + /// Description of HARTs in the device. pub mod hart; pub use hart::Hart; @@ -25,6 +29,13 @@ pub struct Riscv { )] pub core_interrupts: Vec, + /// Exception enumeration values + #[cfg_attr( + feature = "serde", + serde(default, skip_serializing_if = "Vec::is_empty") + )] + pub exceptions: Vec, + /// Priority level enumeration values #[cfg_attr( feature = "serde", @@ -44,6 +55,7 @@ pub struct Riscv { #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct RiscvBuilder { core_interrupts: Option>, + exceptions: Option>, priorities: Option>, harts: Option>, } @@ -52,6 +64,7 @@ impl From for RiscvBuilder { fn from(riscv: Riscv) -> Self { Self { core_interrupts: Some(riscv.core_interrupts), + exceptions: Some(riscv.exceptions), priorities: Some(riscv.priorities), harts: Some(riscv.harts), } @@ -65,6 +78,12 @@ impl RiscvBuilder { self } + /// Set the exception enumeration values + pub fn exceptions(mut self, exceptions: Vec) -> Self { + self.exceptions = Some(exceptions); + self + } + /// Set the priority level enumeration values pub fn priorities(mut self, priorities: Vec) -> Self { self.priorities = Some(priorities); @@ -80,12 +99,18 @@ impl RiscvBuilder { /// Validate and build a [`Riscv`]. pub fn build(self, lvl: ValidateLevel) -> Result { let riscv = Riscv { - core_interrupts: self - .core_interrupts - .ok_or_else(|| BuildError::Uninitialized("core_interrupts".to_string()))?, - priorities: self - .priorities - .ok_or_else(|| BuildError::Uninitialized("priorities".to_string()))?, + core_interrupts: match self.core_interrupts { + Some(core_interrupts) => core_interrupts, + None => Vec::new(), + }, + exceptions: match self.exceptions { + Some(exceptions) => exceptions, + None => Vec::new(), + }, + priorities: match self.priorities { + Some(priorities) => priorities, + None => Vec::new(), + }, harts: self .harts .ok_or_else(|| BuildError::Uninitialized("harts".to_string()))?, @@ -110,6 +135,9 @@ impl Riscv { if let Some(core_interrupts) = builder.core_interrupts { self.core_interrupts = core_interrupts; } + if let Some(exceptions) = builder.exceptions { + self.exceptions = exceptions; + } if let Some(priorities) = builder.priorities { self.priorities = priorities; } @@ -124,12 +152,16 @@ impl Riscv { /// # Errors /// /// - If any of the core interrupt enumeration values are invalid + /// - If any of the exception enumeration values are invalid /// - If any of the priority level enumeration values are invalid /// - If any of the HART enumeration values are invalid pub fn validate(&self, lvl: ValidateLevel) -> Result<(), SvdError> { for ci in &self.core_interrupts { ci.validate(lvl)?; } + for e in &self.exceptions { + e.validate(lvl)?; + } for p in &self.priorities { p.validate(lvl)?; } diff --git a/svd-rs/src/riscv/exception.rs b/svd-rs/src/riscv/exception.rs new file mode 100644 index 00000000..24fcde70 --- /dev/null +++ b/svd-rs/src/riscv/exception.rs @@ -0,0 +1,114 @@ +use crate::{BuildError, Description, Name, SvdError, ValidateLevel}; + +/// Describes a exception source in the device +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +#[derive(Clone, Debug, PartialEq, Eq)] +#[non_exhaustive] +pub struct Exception { + /// The string represents the exception source name + pub name: String, + + /// The string describes the exception source + #[cfg_attr( + feature = "serde", + serde(default, skip_serializing_if = "Option::is_none") + )] + pub description: Option, + + /// Represents the enumeration index value associated to the exception source + pub value: u32, +} + +/// Builder for [`Exception`] +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct ExceptionBuilder { + name: Option, + description: Option, + value: Option, +} + +impl From for ExceptionBuilder { + fn from(d: Exception) -> Self { + Self { + name: Some(d.name), + description: d.description, + value: Some(d.value), + } + } +} + +impl ExceptionBuilder { + /// Set the name of the exception source + pub fn name(mut self, value: String) -> Self { + self.name = Some(value); + self + } + /// Set the description of the exception source + pub fn description(mut self, value: Option) -> Self { + self.description = value; + self + } + /// Set the value of the exception source + pub fn value(mut self, value: u32) -> Self { + self.value = Some(value); + self + } + /// Validate and build an [`Exception`]. + pub fn build(self, lvl: ValidateLevel) -> Result { + let de = Exception { + name: self + .name + .ok_or_else(|| BuildError::Uninitialized("name".to_string()))?, + description: self.description, + value: self + .value + .ok_or_else(|| BuildError::Uninitialized("value".to_string()))?, + }; + de.validate(lvl)?; + Ok(de) + } +} + +impl Exception { + /// Make a builder for [`Exception`] + pub fn builder() -> ExceptionBuilder { + ExceptionBuilder::default() + } + /// Modify an existing [`Exception`] based on a [builder](ExceptionBuilder). + pub fn modify_from( + &mut self, + builder: ExceptionBuilder, + lvl: ValidateLevel, + ) -> Result<(), SvdError> { + if let Some(name) = builder.name { + self.name = name; + } + if builder.description.is_some() { + self.description = builder.description; + } + if let Some(value) = builder.value { + self.value = value; + } + self.validate(lvl) + } + /// Validate the [`Exception`]. + /// + /// # Notes + /// + /// This doesn't do anything. + pub fn validate(&self, _lvl: ValidateLevel) -> Result<(), SvdError> { + Ok(()) + } +} + +impl Name for Exception { + fn name(&self) -> &str { + &self.name + } +} + +impl Description for Exception { + fn description(&self) -> Option<&str> { + self.description.as_deref() + } +} diff --git a/tests/src/riscv.rs b/tests/src/riscv.rs index e4b26d5a..8726b19f 100644 --- a/tests/src/riscv.rs +++ b/tests/src/riscv.rs @@ -1,6 +1,6 @@ use super::run_test; use crate::svd::{ - riscv::{Hart, Priority, Riscv}, + riscv::{Exception, Hart, Priority, Riscv}, Interrupt, ValidateLevel, }; @@ -27,6 +27,21 @@ fn decode_encode() { .unwrap(), ]; + let exceptions = vec![ + Exception::builder() + .name("InstructionAddressMisaligned".to_string()) + .description(Some("Instruction Address Misaligned".to_string())) + .value(0) + .build(ValidateLevel::Strict) + .unwrap(), + Exception::builder() + .name("InstructionAccessFault".to_string()) + .description(Some("Instruction Access Fault".to_string())) + .value(1) + .build(ValidateLevel::Strict) + .unwrap(), + ]; + let priorities = vec![ Priority::builder() .name("P0".to_string()) @@ -88,6 +103,7 @@ fn decode_encode() { let tests = vec![( Riscv::builder() .core_interrupts(interrupts) + .exceptions(exceptions) .priorities(priorities) .harts(harts) .build(ValidateLevel::Strict) @@ -111,6 +127,18 @@ fn decode_encode() { 11 + + + InstructionAddressMisaligned + Instruction Address Misaligned + 0 + + + InstructionAccessFault + Instruction Access Fault + 1 + + P0 @@ -181,6 +209,18 @@ fn decode_encode() { 11 + + + InstructionAddressMisaligned + Instruction Address Misaligned + 0 + + + InstructionAccessFault + Instruction Access Fault + 1 + + P0