From ee21e7d66a2975aa358f9a69185defa9fa5e4e85 Mon Sep 17 00:00:00 2001
From: Jorge Aparicio <jorge@japaric.io>
Date: Thu, 11 Jan 2018 15:26:18 +0100
Subject: [PATCH] make `Stim::write_*` methods take `&mut self` instead of
 `&self`

this prevents people from overlapping non-atomic write operations on the same stimulus port when
working with generators (cooperative tasks).

For example, with this change the following code won't compile

``` rust
let stim = &mut ITM.stim[0];
let a = || {
    loop {
        // ..
        for byte in b"Hello, world!".iter() {
            while !stim.is_fifo_ready() { yield }
            stim.write_u8(*byte);
        }
        // ..
    }
};

let b = || {
    loop {
        // ..
        for byte in b"The quick brown fox jumps over the lazy dog".iter() {
            while !stim.is_fifo_ready() { yield }
            stim.write_u8(*byte);
        }
        // ..
    }
};
```

A possible fix for the above code is to use different stimulus ports in each task (generator).
---
 src/itm.rs            | 12 ++++++------
 src/peripheral/itm.rs |  6 +++---
 src/peripheral/mod.rs | 12 +++++++++---
 3 files changed, 18 insertions(+), 12 deletions(-)

diff --git a/src/itm.rs b/src/itm.rs
index 5a2722d..02ada53 100644
--- a/src/itm.rs
+++ b/src/itm.rs
@@ -7,7 +7,7 @@ use aligned::Aligned;
 use peripheral::itm::Stim;
 
 // NOTE assumes that `bytes` is 32-bit aligned
-unsafe fn write_words(stim: &Stim, bytes: &[u32]) {
+unsafe fn write_words(stim: &mut Stim, bytes: &[u32]) {
     let mut p = bytes.as_ptr();
     for _ in 0..bytes.len() {
         while !stim.is_fifo_ready() {}
@@ -16,7 +16,7 @@ unsafe fn write_words(stim: &Stim, bytes: &[u32]) {
     }
 }
 
-struct Port<'p>(&'p Stim);
+struct Port<'p>(&'p mut Stim);
 
 impl<'p> fmt::Write for Port<'p> {
     fn write_str(&mut self, s: &str) -> fmt::Result {
@@ -26,7 +26,7 @@ impl<'p> fmt::Write for Port<'p> {
 }
 
 /// Writes a `buffer` to the ITM `port`
-pub fn write_all(port: &Stim, buffer: &[u8]) {
+pub fn write_all(port: &mut Stim, buffer: &[u8]) {
     unsafe {
         let mut len = buffer.len();
         let mut ptr = buffer.as_ptr();
@@ -84,7 +84,7 @@ pub fn write_all(port: &Stim, buffer: &[u8]) {
 /// // Or equivalently
 /// itm::write_aligned(&itm.stim[0], &Aligned(*b"Hello, world!\n"));
 /// ```
-pub fn write_aligned(port: &Stim, buffer: &Aligned<u32, [u8]>) {
+pub fn write_aligned(port: &mut Stim, buffer: &Aligned<u32, [u8]>) {
     unsafe {
         let len = buffer.len();
 
@@ -120,13 +120,13 @@ pub fn write_aligned(port: &Stim, buffer: &Aligned<u32, [u8]>) {
 }
 
 /// Writes `fmt::Arguments` to the ITM `port`
-pub fn write_fmt(port: &Stim, args: fmt::Arguments) {
+pub fn write_fmt(port: &mut Stim, args: fmt::Arguments) {
     use core::fmt::Write;
 
     Port(port).write_fmt(args).ok();
 }
 
 /// Writes a string to the ITM `port`
-pub fn write_str(port: &Stim, string: &str) {
+pub fn write_str(port: &mut Stim, string: &str) {
     write_all(port, string.as_bytes())
 }
diff --git a/src/peripheral/itm.rs b/src/peripheral/itm.rs
index 17cf869..fd4a2fd 100644
--- a/src/peripheral/itm.rs
+++ b/src/peripheral/itm.rs
@@ -33,17 +33,17 @@ pub struct Stim {
 
 impl Stim {
     /// Writes an `u8` payload into the stimulus port
-    pub fn write_u8(&self, value: u8) {
+    pub fn write_u8(&mut self, value: u8) {
         unsafe { ptr::write_volatile(self.register.get() as *mut u8, value) }
     }
 
     /// Writes an `u16` payload into the stimulus port
-    pub fn write_u16(&self, value: u16) {
+    pub fn write_u16(&mut self, value: u16) {
         unsafe { ptr::write_volatile(self.register.get() as *mut u16, value) }
     }
 
     /// Writes an `u32` payload into the stimulus port
-    pub fn write_u32(&self, value: u32) {
+    pub fn write_u32(&mut self, value: u32) {
         unsafe { ptr::write_volatile(self.register.get(), value) }
     }
 
diff --git a/src/peripheral/mod.rs b/src/peripheral/mod.rs
index d462bdb..bd658a4 100644
--- a/src/peripheral/mod.rs
+++ b/src/peripheral/mod.rs
@@ -9,7 +9,7 @@
 #![allow(private_no_mangle_statics)]
 
 use core::marker::PhantomData;
-use core::ops::Deref;
+use core::ops::{Deref, DerefMut};
 
 use interrupt;
 
@@ -262,8 +262,8 @@ pub struct ITM {
 
 impl ITM {
     /// Returns a pointer to the register block
-    pub fn ptr() -> *const itm::RegisterBlock {
-        0xE000_0000 as *const _
+    pub fn ptr() -> *mut itm::RegisterBlock {
+        0xE000_0000 as *mut _
     }
 }
 
@@ -275,6 +275,12 @@ impl Deref for ITM {
     }
 }
 
+impl DerefMut for ITM {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        unsafe { &mut *Self::ptr() }
+    }
+}
+
 /// Memory Protection Unit
 pub struct MPU {
     _marker: PhantomData<*const ()>,
-- 
GitLab