From 3fbebbd33e1276086d343809ef9e547f19f0122d Mon Sep 17 00:00:00 2001
From: Jorge Aparicio <japaricious@gmail.com>
Date: Tue, 7 Mar 2017 21:56:55 -0500
Subject: [PATCH] model the registers after `Cell`

---
 CHANGELOG.md |  19 +++++++--
 Cargo.toml   |   7 +++-
 src/lib.rs   | 110 +++++++++++++++++++++++++--------------------------
 3 files changed, 75 insertions(+), 61 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4d2da00..81394c4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
 
 ## [Unreleased]
 
+### Changed
+
+- The `modify` and `write` operations now take `&self`.
+
+- [breaking-change] `RO` and `RW` no longer implement `Sync`. This is required
+  to make the new `modify` and `write` sound.
+
+- [breaking-change] `RO`, `RW` and `WO` now require that the inner value be
+  `Copy`-able.
+
+- docs: remove "guarantee" about some operations being atomic as this crate may
+  be used in architectures different than the ARM Cortex-M.
+
 ## [v0.1.2] - 2016-10-15
 
 ### Added
@@ -25,6 +38,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
 
 - Read-Only (`RO`), Read-Write (`RW`) and Write-Only (`WO`) registers
 
-[Unreleased]: https://github.com/japaric/rustc-cfg/compare/v0.1.2...HEAD
-[v0.1.2]: https://github.com/japaric/rustc-cfg/compare/v0.1.1...v0.1.2
-[v0.1.1]: https://github.com/japaric/rustc-cfg/compare/v0.1.0...v0.1.1
+[Unreleased]: https://github.com/japaric/volatile-register/compare/v0.1.2...HEAD
+[v0.1.2]: https://github.com/japaric/volatile-register/compare/v0.1.1...v0.1.2
+[v0.1.1]: https://github.com/japaric/volatile-register/compare/v0.1.0...v0.1.1
diff --git a/Cargo.toml b/Cargo.toml
index 04019d8..569e620 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -5,8 +5,11 @@ authors = [
 ]
 description = "Volatile access to memory mapped hardware registers"
 documentation = "https://docs.rs/volatile-register"
-keywords = ["volatile", "register"]
+keywords = ["no-std", "volatile", "register"]
 license = "MIT OR Apache-2.0"
 name = "volatile-register"
 repository = "https://github.com/japaric/volatile-register"
-version = "0.1.2"
+version = "0.2.0"
+
+[dependencies]
+vcell = "0.1.0"
diff --git a/src/lib.rs b/src/lib.rs
index 83274c3..2139385 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -2,105 +2,103 @@
 //!
 //! # Usage
 //!
-//! ```
-//! use volatile_register::{RO, RW, WO};
+//! ``` no_run
+//! use volatile_register::RW;
 //!
-//! /// A struct that represents the memory mapped register block for the GPIO
-//! /// (General Purpose I/O) peripherals.
+//! // Create a struct that represents the memory mapped register block
+//! /// Nested Vector Interrupt Controller
 //! #[repr(C)]
-//! pub struct Gpio {
-//!     /// Control Register
-//!     cr: RW<u32>,
-//!     /// Input Data Register
-//!     idr: RO<u32>,
-//!     /// Output Data Register
-//!     odr: WO<u32>,
+//! pub struct Nvic {
+//!     /// Interrupt Set-Enable
+//!     pub iser: [RW<u32>; 8],
+//!     reserved0: [u32; 24],
+//!     /// Interrupt Clear-Enable
+//!     pub icer: [RW<u32>; 8],
+//!     reserved1: [u32; 24],
 //!     // .. more registers ..
 //! }
 //!
-//! /// Accessor to the register block associated to the GPIOA peripheral
-//! fn gpioa() -> &'static Gpio {
-//!     const ADDRESS: usize = 0x40010800;
-//!
-//!     unsafe { &*(ADDRESS as *const Gpio) }
-//! }
-//!
-//! /// Accessor to the register block associated to the GPIOC peripheral
-//! /// NOTE(unsafe) This function hands out mutable aliases to a single address.
-//! unsafe fn gpioc_mut() -> &'static mut Gpio {
-//!     const ADDRESS: usize = 0x40011000;
-//!
-//!     unsafe { &mut *(ADDRESS as *mut Gpio) }
-//! }
+//! // Access the registers by casting the base address of the register block
+//! // to the previously declared `struct`
+//! let nvic = 0xE000_E100 as *const Nvic;
+//! // Unsafe because the compiler can't verify the address is correct
+//! unsafe { (*nvic).iser[0].write(1) }
 //! ```
 
 #![deny(missing_docs)]
 #![no_std]
 
-use core::cell::UnsafeCell;
-use core::ptr;
+extern crate vcell;
+
+use vcell::VolatileCell;
 
 /// Read-Only register
-#[repr(C)]
-pub struct RO<T> {
-    register: T,
+pub struct RO<T>
+    where T: Copy
+{
+    register: VolatileCell<T>,
 }
 
 impl<T> RO<T>
     where T: Copy
 {
-    /// Uninterruptible if `T` is a word, halfword or byte
+    /// Reads the value of the register
     #[inline(always)]
     pub fn read(&self) -> T {
-        unsafe { ptr::read_volatile(&self.register) }
+        self.register.get()
     }
 }
 
 /// Read-Write register
-#[repr(C)]
-pub struct RW<T> {
-    register: T,
+pub struct RW<T>
+    where T: Copy
+{
+    register: VolatileCell<T>,
 }
 
 impl<T> RW<T>
     where T: Copy
 {
-    /// Uninterruptible if `T` is a word, halfword or byte
+    /// Performs a read-modify-write operation
+    ///
+    /// NOTE: `unsafe` because writes to a register are side effectful
     #[inline(always)]
-    pub fn read(&self) -> T {
-        unsafe { ptr::read_volatile(&self.register) }
+    pub unsafe fn modify<F>(&self, f: F)
+        where F: FnOnce(T) -> T
+    {
+        self.register.set(f(self.register.get()));
     }
 
-    /// Uninterruptible if `T` is a word, halfword or byte
+    /// Reads the value of the register
     #[inline(always)]
-    pub fn write(&mut self, value: T) {
-        unsafe {
-            ptr::write_volatile(&mut self.register, value);
-        }
+    pub fn read(&self) -> T {
+        self.register.get()
     }
 
-    /// Perform a read-modify-write, using `func` to perform the modification.
-    pub fn modify<F>(&mut self, func: F) where F: FnOnce(T) -> T {
-        let mut t = self.read();
-        t = func(t);
-        self.write(t);
+    /// Writes a `value` into the register
+    ///
+    /// NOTE: `unsafe` because writes to a register are side effectful
+    #[inline(always)]
+    pub unsafe fn write(&self, value: T) {
+        self.register.set(value)
     }
 }
 
 /// Write-Only register
-#[repr(C)]
-pub struct WO<T> {
-    register: UnsafeCell<T>,
+pub struct WO<T>
+    where T: Copy
+{
+    register: VolatileCell<T>,
 }
 
 impl<T> WO<T>
     where T: Copy
 {
-    /// Uninterruptible if `T` is a word, halfword or byte
+    /// Writes `value` into the register
+    ///
+    /// NOTE: `unsafe` because writes to a register are side effectful
     #[inline(always)]
-    pub fn write(&self, value: T) {
-        unsafe { ptr::write_volatile(self.register.get(), value) }
+    pub unsafe fn write(&self, value: T) {
+        self.register.set(value)
     }
 }
-
-unsafe impl<T> Sync for WO<T> where T: Sync {}
-- 
GitLab