diff --git a/src/config.rs b/src/config.rs index 820c8763..cae6b790 100644 --- a/src/config.rs +++ b/src/config.rs @@ -46,6 +46,7 @@ pub struct Config { pub settings_file: Option, /// Chip-specific settings pub settings: Settings, + pub strict_safe_access: bool, } impl Config { diff --git a/src/generate/device.rs b/src/generate/device.rs index c16a8eea..7427f0bc 100644 --- a/src/generate/device.rs +++ b/src/generate/device.rs @@ -137,7 +137,14 @@ pub fn render(d: &Device, config: &Config, device_x: &mut String) -> Result Result Reg { } } +impl Reg { + /// Reads the contents of a `ReadableSideEffect` register. + /// + /// # Safety + /// + /// Reading this register has side effects. + #[inline(always)] + pub unsafe fn read(&self) -> R { + R { + bits: self.register.get(), + _reg: marker::PhantomData, + } + } +} + impl Reg { /// Writes the reset value to `Writable` register. /// @@ -74,7 +89,7 @@ impl Reg { /// ``` /// In the latter case, other fields will be set to their reset value. #[inline(always)] - pub fn write(&self, f: F) -> REG::Ux + _WRITE_SAFETY_ fn write(&self, f: F) -> REG::Ux where F: FnOnce(&mut W) -> &mut W, { @@ -117,7 +132,7 @@ impl Reg { /// let state = periph.reg.write_and(|w| State::set(w.field1())); /// ``` #[inline(always)] - pub fn from_write(&self, f: F) -> T + _WRITE_SAFETY_ fn from_write(&self, f: F) -> T where F: FnOnce(&mut W) -> T, { @@ -208,7 +223,7 @@ impl Reg { /// ``` /// Other fields will have the value they had before the call to `modify`. #[inline(always)] - pub fn modify(&self, f: F) -> REG::Ux + _WRITE_SAFETY_ fn modify(&self, f: F) -> REG::Ux where for<'w> F: FnOnce(&R, &'w mut W) -> &'w mut W, { @@ -228,6 +243,7 @@ impl Reg { value } + /// Modifies the contents of the register by reading and then writing it /// and produces a value. /// @@ -260,7 +276,7 @@ impl Reg { /// ``` /// Other fields will have the value they had before the call to `modify`. #[inline(always)] - pub fn from_modify(&self, f: F) -> T + _WRITE_SAFETY_ fn from_modify(&self, f: F) -> T where for<'w> F: FnOnce(&R, &'w mut W) -> T, { diff --git a/src/generate/register.rs b/src/generate/register.rs index 4bff2b8f..c96f9179 100644 --- a/src/generate/register.rs +++ b/src/generate/register.rs @@ -385,10 +385,19 @@ pub fn render_register_mod( }); if can_read { - let doc = format!("`read()` method returns [`{mod_ty}::R`](R) reader structure",); + let is_side_effect = register.read_action.is_some() && config.strict_safe_access; + let (trait_name, read_safety_doc) = if is_side_effect { + (quote!(ReadableSideEffect), " (unsafe)") + } else { + (quote!(Readable), "") + }; + + let doc = format!( + "`read()` method returns [`{mod_ty}::R`](R) reader structure{read_safety_doc}", + ); mod_items.extend(quote! { #[doc = #doc] - impl crate::Readable for #regspec_ty {} + impl crate::#trait_name for #regspec_ty {} }); } if can_write { diff --git a/src/main.rs b/src/main.rs index 07de10cb..5eb01444 100755 --- a/src/main.rs +++ b/src/main.rs @@ -301,6 +301,13 @@ Ignore this option if you are not building your own FPGA based soft-cores."), .action(ArgAction::Set) .value_parser(["off", "error", "warn", "info", "debug", "trace"]), ) + .arg( + Arg::new("strict_safe_access") + .long("strict-safe-access") + .help("Marks all write accesses and read accesses with side effects as unsafe") + .action(ArgAction::SetTrue), + ) + .version(concat!( env!("CARGO_PKG_VERSION"), include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt")) diff --git a/src/util.rs b/src/util.rs index a90b83b0..151e9f63 100644 --- a/src/util.rs +++ b/src/util.rs @@ -15,6 +15,10 @@ use syn::{ punctuated::Punctuated, token::PathSep, Lit, LitInt, PathArguments, PathSegment, Type, TypePath, }; +use regex::Regex; +use std::sync::OnceLock; + + use anyhow::{anyhow, Result}; pub const BITS_PER_BYTE: u32 = 8; @@ -177,13 +181,35 @@ pub fn escape_brackets(s: &str) -> String { }) } +static TAG_RE: OnceLock = OnceLock::new(); + /// Escape basic html tags and brackets pub fn escape_special_chars(s: &str) -> Cow<'_, str> { - if s.contains('[') { - escape_brackets(s).into() + let tag_re= TAG_RE.get_or_init(|| Regex::new(r"<[^>]+>|&[a-zA-Z0-9#]+;").unwrap()); + + if !s.contains('[') && !s.contains('&') && !s.contains('<') && !s.contains('>') { + return s.into(); + } + + let mut last_end = 0; + let mut escaped = String::new(); + + for mat in tag_re.find_iter(s) { + let part = &s[last_end..mat.start()]; + escaped.push_str(&part.replace('&', "&").replace('<', "<").replace('>', ">")); + escaped.push_str(mat.as_str()); + last_end = mat.end(); + } + let remaining = &s[last_end..]; + + escaped.push_str(&remaining.replace('&', "&").replace('<', "<").replace('>', ">")); + + if escaped.contains('[') { + escape_brackets(&escaped).into() } else { - s.into() + escaped.into() } + } pub fn name_of(maybe_array: &MaybeArray, ignore_group: bool) -> String { @@ -513,3 +539,15 @@ fn pascalcase() { assert_eq!(to_pascal_case("FOO_BAR_1_2"), "FooBar1_2"); assert_eq!(to_pascal_case("FOO_BAR_1_2_"), "FooBar1_2_"); } + +#[test] +fn test_escape_special_chars() { + + assert_eq!(escape_special_chars("Enable & disable"), "Enable & disable"); + assert_eq!(escape_special_chars("Wait < 10"), "Wait < 10"); + assert_eq!(escape_special_chars("This is bold"), "This is bold"); + assert_eq!(escape_special_chars("Use
"), "Use
"); + assert_eq!(escape_special_chars("A & B"), "A & B"); + assert_eq!(escape_special_chars("Array[0]"), "Array\\[0\\]"); +} + diff --git a/svd2rust-regress/src/tests.rs b/svd2rust-regress/src/tests.rs index f6c2f73d..68135b2b 100644 --- a/svd2rust-regress/src/tests.rs +++ b/svd2rust-regress/src/tests.rs @@ -27,6 +27,8 @@ pub enum Manufacturer { RaspberryPi, Renesas, Unknown, + GigaDevice, + WCH, } impl Manufacturer { @@ -51,6 +53,8 @@ impl Manufacturer { Renesas, TexasInstruments, Espressif, + GigaDevice, + WCH, ] } } diff --git a/svd2rust-regress/tests.yml b/svd2rust-regress/tests.yml index bf17e088..6b50d249 100644 --- a/svd2rust-regress/tests.yml +++ b/svd2rust-regress/tests.yml @@ -443,7 +443,11 @@ mfgr: SiFive chip: fu540 svd_url: https://raw.githubusercontent.com/riscv-rust/fu540-pac/master/fu540.svd - +- arch: riscv + mfgr: WCH + chip: ch32v30x + svd_url: https://raw.githubusercontent.com/ch32-rs/ch32v30x/main/ch32v30x.svd + # SiliconLabs - arch: cortex-m mfgr: SiliconLabs