diff --git a/CHANGELOG.md b/CHANGELOG.md index a435e45e..99f886f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added +- MSP430 API for atomically changing register bits, gated behind the `--nightly` flag +- New SVD test for `msp430fr2355` + +### Added + - Option `-o`(`--output-path`) let you specify output directory path ### Changed diff --git a/ci/svd2rust-regress/src/svd_test.rs b/ci/svd2rust-regress/src/svd_test.rs index 2f37e0d2..8463f994 100644 --- a/ci/svd2rust-regress/src/svd_test.rs +++ b/ci/svd2rust-regress/src/svd_test.rs @@ -6,7 +6,8 @@ use std::path::PathBuf; use std::process::{Command, Output}; const CRATES_ALL: &[&str] = &["bare-metal = \"0.2.0\"", "vcell = \"0.1.2\""]; -const CRATES_MSP430: &[&str] = &["msp430 = \"0.2.2\""]; +const CRATES_MSP430: &[&str] = &["msp430 = \"0.2.2\"", "msp430-rt = \"0.2.0\""]; +const CRATES_MSP430_NIGHTLY: &[&str] = &["msp430-atomic = \"0.1.2\""]; const CRATES_CORTEX_M: &[&str] = &["cortex-m = \"0.7.0\"", "cortex-m-rt = \"0.6.13\""]; const CRATES_RISCV: &[&str] = &["riscv = \"0.5.0\"", "riscv-rt = \"0.6.0\""]; const CRATES_XTENSALX6: &[&str] = &["xtensa-lx6-rt = \"0.2.0\"", "xtensa-lx6 = \"0.1.0\""]; @@ -134,6 +135,14 @@ pub fn test( Msp430 => CRATES_MSP430.iter(), XtensaLX => CRATES_XTENSALX6.iter(), }) + .chain(if nightly { + match &t.arch { + Msp430 => CRATES_MSP430_NIGHTLY.iter(), + _ => [].iter(), + } + } else { + [].iter() + }) .chain(PROFILE_ALL.iter()) .chain(FEATURES_ALL.iter()); diff --git a/ci/svd2rust-regress/src/tests.rs b/ci/svd2rust-regress/src/tests.rs index 1203c99e..91e6f8e0 100644 --- a/ci/svd2rust-regress/src/tests.rs +++ b/ci/svd2rust-regress/src/tests.rs @@ -4230,6 +4230,16 @@ pub const TESTS: &[&TestCase] = &[ should_pass: true, run_when: Always, }, + &TestCase { + arch: Msp430, + mfgr: TexasInstruments, + chip: "msp430fr2355", + svd_url: Some( + "https://raw.githubusercontent.com/YuhanLiin/msp430fr2355/rt-up/msp430fr2355.svd", + ), + should_pass: true, + run_when: Always, + }, &TestCase { arch: XtensaLX, mfgr: Espressif, diff --git a/src/generate/device.rs b/src/generate/device.rs index 777367ec..fbcca6fc 100644 --- a/src/generate/device.rs +++ b/src/generate/device.rs @@ -139,11 +139,13 @@ pub fn render(d: &Device, config: &Config, device_x: &mut String) -> Result Result Reg +where + Self: Readable + Writable, + REG::Ux: AtomicOperations + Default + core::ops::Not, +{ + /// Set high every bit in the register that was set in the write proxy. Leave other bits + /// untouched. The write is done in a single atomic instruction. + #[inline(always)] + pub unsafe fn set_bits(&self, f: F) + where + F: FnOnce(&mut W) -> &mut W, + { + let bits = f(&mut W { + bits: Default::default(), + _reg: marker::PhantomData, + }) + .bits; + REG::Ux::atomic_or(self.register.as_ptr(), bits); + } + + /// Clear every bit in the register that was cleared in the write proxy. Leave other bits + /// untouched. The write is done in a single atomic instruction. + #[inline(always)] + pub unsafe fn clear_bits(&self, f: F) + where + F: FnOnce(&mut W) -> &mut W, + { + let bits = f(&mut W { + bits: !REG::Ux::default(), + _reg: marker::PhantomData, + }) + .bits; + REG::Ux::atomic_and(self.register.as_ptr(), bits); + } + + /// Toggle every bit in the register that was set in the write proxy. Leave other bits + /// untouched. The write is done in a single atomic instruction. + #[inline(always)] + pub unsafe fn toggle_bits(&self, f: F) + where + F: FnOnce(&mut W) -> &mut W, + { + let bits = f(&mut W { + bits: Default::default(), + _reg: marker::PhantomData, + }) + .bits; + REG::Ux::atomic_xor(self.register.as_ptr(), bits); + } +} diff --git a/src/lib.rs b/src/lib.rs index d93073b4..20fb6995 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -108,15 +108,17 @@ //! $ cargo fmt //! ``` //! -//! The resulting crate must provide an opt-in "rt" feature and depend on these crates: -//! `msp430` v0.2.x, `msp430-rt` v0.2.x and `vcell` v0.1.x. Furthermore -//! the "device" feature of `msp430-rt` must be enabled when the "rt" feature is enabled. The -//! `Cargo.toml` of the device crate will look like this: +//! The resulting crate must provide opt-in "rt" feature and depend on these crates: `msp430` +//! v0.2.x, `msp430-rt` v0.2.x, and `vcell` v0.1.x. If the `--nightly` flag is provided to +//! `svd2rust`, then `msp430-atomic` v0.1.2 is also needed. Furthermore the "device" feature of +//! `msp430-rt` must be enabled when the "rt" feature is enabled. The `Cargo.toml` of the device +//! crate will look like this: //! //! ``` toml //! [dependencies] //! msp430 = "0.2.0" //! vcell = "0.1.0" +//! msp430-atomic = "0.1.2" # Only when using the --nightly flag //! //! [dependencies.msp430-rt] //! optional = true @@ -124,6 +126,7 @@ //! //! [features] //! rt = ["msp430-rt/device"] +//! unstable = ["msp430-atomic"] //! ``` //! //! ## Other targets @@ -477,7 +480,23 @@ //! ## the `--nightly` flag //! //! The `--nightly` flag can be passed to `svd2rust` to enable features in the generated api that are only available to a nightly -//! compiler. Currently there are no nightly features the flag is only kept for compatibility with prior versions. +//! compiler. The following features are gated by the `--nightly` flag: +//! +//! ### MSP430 +//! +//! Extends the register API with operations to atomically set, clear, and toggle specific bits. +//! The atomic operations allow limited modification of register bits without read-modify-write +//! sequences. As such, they can be concurrently called on different bits in the same register +//! without data races. +//! +//! Usage examples: +//! +//! ```ignore +//! // These can be called from different contexts even though they are modifying the same register +//! P1.p1out.set_bits(|w| unsafe { w.bits(1 << 1) }); +//! P1.p1out.clear(|w| unsafe { w.bits(!(1 << 2)) }); +//! P1.p1out.toggle(|w| unsafe { w.bits(1 << 4) }); +//! ``` #![recursion_limit = "128"] use quote::quote;