svd_rs/
device.rs

1use super::{
2    BuildError, Cpu, Description, EmptyToNone, Name, Peripheral, RegisterProperties, SvdError,
3    ValidateLevel,
4};
5
6/// Errors for [`Device::validate`]
7#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
8pub enum Error {
9    /// Device has no peripherals
10    #[error("Device must contain at least one peripheral")]
11    EmptyDevice,
12}
13
14/// The top element in a SVD file. Describes information specific to a device.
15#[cfg_attr(
16    feature = "serde",
17    derive(serde::Deserialize, serde::Serialize),
18    serde(rename_all = "camelCase")
19)]
20#[derive(Clone, Debug, PartialEq)]
21#[non_exhaustive]
22pub struct Device {
23    /// Specify the vendor of the device using the full name
24    #[cfg_attr(
25        feature = "serde",
26        serde(default, skip_serializing_if = "Option::is_none")
27    )]
28    pub vendor: Option<String>,
29
30    /// Specify the vendor abbreviation without spaces or special characters
31    #[cfg_attr(
32        feature = "serde",
33        serde(default, skip_serializing_if = "Option::is_none", rename = "vendorID")
34    )]
35    pub vendor_id: Option<String>,
36
37    /// The string identifies the device or device series. Device names are required to be unique
38    pub name: String,
39
40    /// Specify the name of the device series
41    #[cfg_attr(
42        feature = "serde",
43        serde(default, skip_serializing_if = "Option::is_none")
44    )]
45    pub series: Option<String>,
46
47    /// Define the version of the SVD file
48    pub version: String,
49
50    /// Describe the main features of the device (for example CPU, clock frequency, peripheral overview)
51    pub description: String,
52
53    /// The text will be copied into the header section of the generated device header file and shall contain the legal disclaimer
54    #[cfg_attr(
55        feature = "serde",
56        serde(default, skip_serializing_if = "Option::is_none")
57    )]
58    pub license_text: Option<String>,
59
60    /// Describe the processor included in the device
61    #[cfg_attr(
62        feature = "serde",
63        serde(default, skip_serializing_if = "Option::is_none")
64    )]
65    pub cpu: Option<Cpu>,
66
67    /// Specify the file name (without extension) of the device-specific system include file
68    #[cfg_attr(
69        feature = "serde",
70        serde(default, skip_serializing_if = "Option::is_none")
71    )]
72    pub header_system_filename: Option<String>,
73
74    /// This string is prepended to all type definition names generated in the CMSIS-Core device header file
75    #[cfg_attr(
76        feature = "serde",
77        serde(default, skip_serializing_if = "Option::is_none")
78    )]
79    pub header_definitions_prefix: Option<String>,
80
81    /// Define the number of data bits uniquely selected by each address
82    pub address_unit_bits: u32,
83
84    /// Define the number of data bit-width of the maximum single data transfer supported by the bus infrastructure
85    pub width: u32,
86
87    /// Default properties for all registers
88    #[cfg_attr(feature = "serde", serde(flatten))]
89    pub default_register_properties: RegisterProperties,
90
91    /// Group to define peripherals
92    pub peripherals: Vec<Peripheral>,
93
94    /// Specify the underlying XML schema to which the CMSIS-SVD schema is compliant.
95    #[cfg_attr(feature = "serde", serde(skip, default = "default_xmlns_xs"))]
96    pub xmlns_xs: String,
97
98    /// Specify the file path and file name of the CMSIS-SVD Schema
99    #[cfg_attr(
100        feature = "serde",
101        serde(skip, default = "default_no_namespace_schema_location")
102    )]
103    pub no_namespace_schema_location: String,
104
105    /// Specify the compliant CMSIS-SVD schema version
106    #[cfg_attr(feature = "serde", serde(skip, default = "default_schema_version"))]
107    pub schema_version: String,
108}
109
110fn default_xmlns_xs() -> String {
111    "http://www.w3.org/2001/XMLSchema-instance".into()
112}
113fn default_no_namespace_schema_location() -> String {
114    format!(
115        "CMSIS-SVD_Schema_{}.xsd",
116        default_schema_version().replace('.', "_")
117    )
118}
119fn default_schema_version() -> String {
120    "1.1".into()
121}
122
123/// Builder for [`Device`]
124#[derive(Clone, Debug, Default)]
125pub struct DeviceBuilder {
126    vendor: Option<String>,
127    vendor_id: Option<String>,
128    name: Option<String>,
129    series: Option<String>,
130    version: Option<String>,
131    description: Option<String>,
132    license_text: Option<String>,
133    cpu: Option<Cpu>,
134    header_system_filename: Option<String>,
135    header_definitions_prefix: Option<String>,
136    address_unit_bits: Option<u32>,
137    width: Option<u32>,
138    default_register_properties: RegisterProperties,
139    peripherals: Option<Vec<Peripheral>>,
140    xmlns_xs: Option<String>,
141    no_namespace_schema_location: Option<String>,
142    schema_version: Option<String>,
143}
144
145impl From<Device> for DeviceBuilder {
146    fn from(d: Device) -> Self {
147        Self {
148            vendor: d.vendor,
149            vendor_id: d.vendor_id,
150            name: Some(d.name),
151            series: d.series,
152            version: Some(d.version),
153            description: Some(d.description),
154            license_text: d.license_text,
155            cpu: d.cpu,
156            header_system_filename: d.header_system_filename,
157            header_definitions_prefix: d.header_definitions_prefix,
158            address_unit_bits: Some(d.address_unit_bits),
159            width: Some(d.width),
160            default_register_properties: d.default_register_properties,
161            peripherals: Some(d.peripherals),
162            xmlns_xs: Some(d.xmlns_xs),
163            no_namespace_schema_location: Some(d.no_namespace_schema_location),
164            schema_version: Some(d.schema_version),
165        }
166    }
167}
168
169impl DeviceBuilder {
170    /// Set the vendor of the device.
171    pub fn vendor(mut self, value: Option<String>) -> Self {
172        self.vendor = value;
173        self
174    }
175    /// Set the vendor_id of the device.
176    pub fn vendor_id(mut self, value: Option<String>) -> Self {
177        self.vendor_id = value;
178        self
179    }
180    /// Set the name of the device.
181    pub fn name(mut self, value: String) -> Self {
182        self.name = Some(value);
183        self
184    }
185    /// Set the series of the device.
186    pub fn series(mut self, value: Option<String>) -> Self {
187        self.series = value;
188        self
189    }
190    /// Set the version of the device.
191    pub fn version(mut self, value: String) -> Self {
192        self.version = Some(value);
193        self
194    }
195    /// Set the description of the device.
196    pub fn description(mut self, value: String) -> Self {
197        self.description = Some(value);
198        self
199    }
200    /// Set the license_text of the device.
201    pub fn license_text(mut self, value: Option<String>) -> Self {
202        self.license_text = value;
203        self
204    }
205    /// Set the cpu of the device.
206    pub fn cpu(mut self, value: Option<Cpu>) -> Self {
207        self.cpu = value;
208        self
209    }
210    /// Set the header_system_filename of the device.
211    pub fn header_system_filename(mut self, value: Option<String>) -> Self {
212        self.header_system_filename = value;
213        self
214    }
215    /// Set the header_definitions_prefix of the device.
216    pub fn header_definitions_prefix(mut self, value: Option<String>) -> Self {
217        self.header_definitions_prefix = value;
218        self
219    }
220    /// Set the address unit bits of the device.
221    pub fn address_unit_bits(mut self, value: u32) -> Self {
222        self.address_unit_bits = Some(value);
223        self
224    }
225    /// Set the width of the device.
226    pub fn width(mut self, value: u32) -> Self {
227        self.width = Some(value);
228        self
229    }
230    /// Set the default register properties of the device.
231    pub fn default_register_properties(mut self, value: RegisterProperties) -> Self {
232        self.default_register_properties = value;
233        self
234    }
235    /// Set the peripherals of the device.
236    pub fn peripherals(mut self, value: Vec<Peripheral>) -> Self {
237        self.peripherals = Some(value);
238        self
239    }
240    /// Set the xmlns_xs version of the device.
241    pub fn xmlns_xs(mut self, value: String) -> Self {
242        self.xmlns_xs = Some(value);
243        self
244    }
245    /// Set the no_namespace_schema_location version of the device.
246    pub fn no_namespace_schema_location(mut self, value: String) -> Self {
247        self.no_namespace_schema_location = Some(value);
248        self
249    }
250    /// Set the schema version of the device.
251    pub fn schema_version(mut self, value: String) -> Self {
252        self.schema_version = Some(value);
253        self
254    }
255    /// Validate and build a [`Device`].
256    pub fn build(self, lvl: ValidateLevel) -> Result<Device, SvdError> {
257        let schema_version = self.schema_version.unwrap_or_else(default_schema_version);
258        let device = Device {
259            vendor: self.vendor,
260            vendor_id: self.vendor_id,
261            name: self
262                .name
263                .ok_or_else(|| BuildError::Uninitialized("name".to_string()))?,
264            series: self.series,
265            version: self
266                .version
267                .or_else(|| {
268                    if !lvl.is_strict() {
269                        Some("1.0".into())
270                    } else {
271                        None
272                    }
273                })
274                .ok_or_else(|| BuildError::Uninitialized("version".to_string()))?,
275            description: self
276                .description
277                .or_else(|| {
278                    if !lvl.is_strict() {
279                        Some("".into())
280                    } else {
281                        None
282                    }
283                })
284                .ok_or_else(|| BuildError::Uninitialized("description".to_string()))?,
285            license_text: self.license_text,
286            cpu: self.cpu,
287            header_system_filename: self.header_system_filename,
288            header_definitions_prefix: self.header_definitions_prefix,
289            address_unit_bits: self
290                .address_unit_bits
291                .or_else(|| if !lvl.is_strict() { Some(8) } else { None })
292                .ok_or_else(|| BuildError::Uninitialized("addressUnitBits".to_string()))?,
293            width: self
294                .width
295                .or_else(|| if !lvl.is_strict() { Some(32) } else { None })
296                .ok_or_else(|| BuildError::Uninitialized("width".to_string()))?,
297            default_register_properties: self.default_register_properties.build(lvl)?,
298            peripherals: self
299                .peripherals
300                .ok_or_else(|| BuildError::Uninitialized("peripherals".to_string()))?,
301            xmlns_xs: self.xmlns_xs.unwrap_or_else(default_xmlns_xs),
302            no_namespace_schema_location: self
303                .no_namespace_schema_location
304                .unwrap_or_else(default_no_namespace_schema_location),
305            schema_version,
306        };
307        device.validate(lvl)?;
308        Ok(device)
309    }
310}
311
312impl Device {
313    /// Make a builder for [`Device`]
314    pub fn builder() -> DeviceBuilder {
315        DeviceBuilder::default()
316    }
317    /// Modify an existing [`Device`] based on a [builder](DeviceBuilder).
318    pub fn modify_from(
319        &mut self,
320        builder: DeviceBuilder,
321        lvl: ValidateLevel,
322    ) -> Result<(), SvdError> {
323        if builder.vendor.is_some() {
324            self.vendor = builder.vendor.empty_to_none();
325        }
326        if builder.vendor_id.is_some() {
327            self.vendor_id = builder.vendor_id.empty_to_none();
328        }
329        if let Some(name) = builder.name {
330            self.name = name;
331        }
332        if builder.series.is_some() {
333            self.series = builder.series.empty_to_none();
334        }
335        if let Some(version) = builder.version {
336            self.version = version;
337        }
338        if let Some(description) = builder.description {
339            self.description = description;
340        }
341        if builder.license_text.is_some() {
342            self.license_text = builder.license_text.empty_to_none();
343        }
344        if builder.cpu.is_some() {
345            self.cpu = builder.cpu;
346        }
347        if builder.header_system_filename.is_some() {
348            self.header_system_filename = builder.header_system_filename.empty_to_none();
349        }
350        if builder.header_definitions_prefix.is_some() {
351            self.header_definitions_prefix = builder.header_definitions_prefix.empty_to_none();
352        }
353        if let Some(address_unit_bits) = builder.address_unit_bits {
354            self.address_unit_bits = address_unit_bits;
355        }
356        if let Some(width) = builder.width {
357            self.width = width;
358        }
359        self.default_register_properties
360            .modify_from(builder.default_register_properties, lvl)?;
361        if let Some(peripherals) = builder.peripherals {
362            self.peripherals = peripherals;
363        }
364        if let Some(xmlns_xs) = builder.xmlns_xs {
365            self.xmlns_xs = xmlns_xs;
366        }
367        if let Some(no_namespace_schema_location) = builder.no_namespace_schema_location {
368            self.no_namespace_schema_location = no_namespace_schema_location;
369        }
370        if let Some(schema_version) = builder.schema_version {
371            self.schema_version = schema_version;
372        }
373        self.validate(lvl)
374    }
375
376    /// Validate the [`Device`]
377    pub fn validate(&self, lvl: ValidateLevel) -> Result<(), SvdError> {
378        if !lvl.is_disabled() {
379            // TODO
380            if self.peripherals.is_empty() {
381                return Err(Error::EmptyDevice.into());
382            }
383        }
384        Ok(())
385    }
386    /// Validate the [`Device`] recursively
387    pub fn validate_all(&self, lvl: ValidateLevel) -> Result<(), SvdError> {
388        if let Some(cpu) = self.cpu.as_ref() {
389            cpu.validate(lvl)?;
390        }
391        self.default_register_properties.validate(lvl)?;
392        for p in &self.peripherals {
393            p.validate_all(lvl)?;
394        }
395        self.validate(lvl)
396    }
397
398    /// Get peripheral by name
399    pub fn get_peripheral(&self, name: &str) -> Option<&Peripheral> {
400        self.peripherals.iter().find(|f| f.name == name)
401    }
402
403    /// Get mutable peripheral by name
404    pub fn get_mut_peripheral(&mut self, name: &str) -> Option<&mut Peripheral> {
405        self.peripherals.iter_mut().find(|f| f.name == name)
406    }
407}
408
409impl Name for Device {
410    fn name(&self) -> &str {
411        &self.name
412    }
413}
414
415impl Description for Device {
416    fn description(&self) -> Option<&str> {
417        Some(&self.description)
418    }
419}