diff --git a/Cargo.lock b/Cargo.lock
index 5c68c7525e565f298fba7f805997287155f39a78..3ed1e7022da246c38a1958394f3b5f0e205edd3b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -26,7 +26,7 @@ version = "0.0.0-alpha.0"
 dependencies = [
  "cortex-m-udf",
  "generic-array 0.13.2",
- "heapless 0.5.3 (git+https://github.com/japaric/heapless?branch=slab)",
+ "heapless 0.5.3",
  "pin-utils",
  "typenum",
 ]
@@ -46,6 +46,12 @@ dependencies = [
  "rustc_version",
 ]
 
+[[package]]
+name = "bare-metal"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603"
+
 [[package]]
 name = "byteorder"
 version = "1.3.4"
@@ -69,7 +75,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2954942fbbdd49996704e6f048ce57567c3e1a4e2dc59b41ae9fde06a01fc763"
 dependencies = [
  "aligned",
- "bare-metal",
+ "bare-metal 0.2.5",
  "volatile-register",
 ]
 
@@ -94,6 +100,56 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "cortex-m-rtfm"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fc65a600a5c8baf0a5fd3b2c0223b8d8fc7aa7d6d0c9a3350ce40a4da49ccea"
+dependencies = [
+ "cortex-m",
+ "cortex-m-rt",
+ "cortex-m-rtfm-macros",
+ "heapless 0.5.6",
+ "rtfm-core",
+ "version_check",
+]
+
+[[package]]
+name = "cortex-m-rtfm-macros"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa4a3fc88fff9970152a7f79bf6919d538395bc1f11ba90a1e6970a3521d8b88"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "rtfm-syntax",
+ "syn",
+]
+
+[[package]]
+name = "cortex-m-rtic"
+version = "0.5.5"
+dependencies = [
+ "bare-metal 1.0.0",
+ "cortex-m",
+ "cortex-m-rt",
+ "cortex-m-rtic-macros",
+ "heapless 0.5.6",
+ "rtic-core",
+ "version_check",
+]
+
+[[package]]
+name = "cortex-m-rtic-macros"
+version = "0.5.2"
+dependencies = [
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "rtic-syntax",
+ "syn",
+]
+
 [[package]]
 name = "cortex-m-semihosting"
 version = "0.3.5"
@@ -134,6 +190,12 @@ dependencies = [
  "byteorder",
 ]
 
+[[package]]
+name = "hashbrown"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
+
 [[package]]
 name = "heapless"
 version = "0.5.3"
@@ -146,13 +208,34 @@ dependencies = [
 
 [[package]]
 name = "heapless"
-version = "0.5.3"
+version = "0.5.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "10b591a0032f114b7a77d4fbfab452660c553055515b7d7ece355db080d19087"
+checksum = "74911a68a1658cfcfb61bc0ccfbd536e3b6e906f8c2f7883ee50157e3e2184f1"
 dependencies = [
  "as-slice",
  "generic-array 0.13.2",
  "hash32",
+ "stable_deref_trait",
+]
+
+[[package]]
+name = "indexmap"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2"
+dependencies = [
+ "autocfg",
+ "hashbrown",
+]
+
+[[package]]
+name = "lm3s6965"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8698042a7495160eac9f7298a32cd1ddbb6ad2780f766f5a99b4f0a6b915e0ad"
+dependencies = [
+ "bare-metal 0.2.5",
+ "cortex-m-rt",
 ]
 
 [[package]]
@@ -163,9 +246,10 @@ dependencies = [
  "chrono",
  "cortex-m",
  "cortex-m-rt",
+ "cortex-m-rtfm",
  "cortex-m-semihosting",
  "cortex-m-udf",
- "heapless 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "heapless 0.5.6",
  "nrf52840-pac",
  "panic-semihosting",
  "panic-udf",
@@ -177,7 +261,7 @@ version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e1b780a5afd2621774652f28c82837f6aa6d19cf0ad71c734fc1fe53298a2d73"
 dependencies = [
- "bare-metal",
+ "bare-metal 0.2.5",
  "cortex-m",
  "cortex-m-rt",
  "vcell",
@@ -202,6 +286,12 @@ dependencies = [
  "autocfg",
 ]
 
+[[package]]
+name = "panic-halt"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "de96540e0ebde571dc55c73d60ef407c653844e6f9a1e2fdbd40c07b9252d812"
+
 [[package]]
 name = "panic-semihosting"
 version = "0.5.3"
@@ -225,6 +315,30 @@ version = "0.1.0-alpha.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587"
 
+[[package]]
+name = "proc-macro-error"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
 [[package]]
 name = "proc-macro2"
 version = "1.0.8"
@@ -234,6 +348,22 @@ dependencies = [
  "unicode-xid",
 ]
 
+[[package]]
+name = "qemu"
+version = "0.0.0-alpha.0"
+dependencies = [
+ "async-cortex-m",
+ "chrono",
+ "cortex-m",
+ "cortex-m-rt",
+ "cortex-m-rtic",
+ "cortex-m-semihosting",
+ "heapless 0.5.6",
+ "lm3s6965",
+ "panic-halt",
+ "panic-semihosting",
+]
+
 [[package]]
 name = "quote"
 version = "1.0.2"
@@ -249,6 +379,40 @@ version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f"
 
+[[package]]
+name = "rtfm-core"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ec893edb2aa5b70320b94896ffea22a7ebb1cf3f942bb67cd5b60a865a63493"
+
+[[package]]
+name = "rtfm-syntax"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4455e23c34df3d66454e7e218a4d76a7f83321d04a806be614463341cec4116e"
+dependencies = [
+ "indexmap",
+ "proc-macro2",
+ "syn",
+]
+
+[[package]]
+name = "rtic-core"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab51fe832317e805f869b3d859f91aadf855c2c3da51f9b84bc645c201597158"
+
+[[package]]
+name = "rtic-syntax"
+version = "0.5.0-alpha.0"
+source = "git+https://github.com/rtic-rs/rtic-syntax?branch=master#291f25f01b2ef4ce55b95b37f0c1c430afbd954d"
+dependencies = [
+ "indexmap",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "rustc_version"
 version = "0.2.3"
@@ -308,6 +472,12 @@ version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "876e32dcadfe563a4289e994f7cb391197f362b6315dc45e8ba4aa6f564a4b3c"
 
+[[package]]
+name = "version_check"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
+
 [[package]]
 name = "volatile-register"
 version = "0.2.0"
diff --git a/Cargo.toml b/Cargo.toml
index eb4e3c1a502ad9e61db2fb9f3a8acfe166e6af8a..051404a5af42615ed8a0df23555b5cd15b7f5af9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -3,6 +3,7 @@ members = [
   "async-cortex-m",
   "cortex-m-udf",
   "nrf52",
+  "qemu",
   "panic-udf",
 ]
 
diff --git a/qemu/.cargo/config b/qemu/.cargo/config
new file mode 100644
index 0000000000000000000000000000000000000000..ca67a7f4924a4e873407f6830699552951587d34
--- /dev/null
+++ b/qemu/.cargo/config
@@ -0,0 +1,11 @@
+
+[target.'cfg(all(target_arch = "arm", target_os = "none"))']
+rustflags = [
+  "-C", "link-arg=-Tlink.x",
+]
+
+[target.thumbv7m-none-eabi]
+runner = "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel"
+
+[build]
+target = "thumbv7m-none-eabi" # Cortex-M3
\ No newline at end of file
diff --git a/qemu/Cargo.toml b/qemu/Cargo.toml
new file mode 100644
index 0000000000000000000000000000000000000000..31d199096f3cd5e92ab811b962c2a68cae7f721b
--- /dev/null
+++ b/qemu/Cargo.toml
@@ -0,0 +1,26 @@
+[package]
+authors = ["Per Lindgren <per.lindgren@ltu.se>"]
+edition = "2018"
+license = "MIT OR Apache-2.0"
+name = "qemu"
+publish = false
+version = "0.0.0-alpha.0"
+
+[dev-dependencies]
+cortex-m = "0.6.2"
+cortex-m-semihosting = "0.3.5"
+# cortex-m-udf = { path = "../cortex-m-udf" }
+heapless = "0.5.6"
+panic-semihosting = "0.5.3"
+panic-halt = "0.2.0"
+
+[dependencies]
+async-cortex-m = { path = "../async-cortex-m" }
+cortex-m = "0.6.2"
+cortex-m-rt = "0.6.12"
+lm3s6965 = "0.1.3"
+cortex-m-rtic = { version = "0.5.5", path = "../../cortex-m-rtic" }
+
+[dependencies.chrono]
+default-features = false
+version = "0.4.10"
diff --git a/qemu/README.md b/qemu/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..4f33aa0fce08fef774a212f3158711e302c3c880
--- /dev/null
+++ b/qemu/README.md
@@ -0,0 +1,3 @@
+# `nrf52`
+
+> RTFM Async examples on the nRF52840
diff --git a/qemu/build.rs b/qemu/build.rs
new file mode 100644
index 0000000000000000000000000000000000000000..1d3211e856949481835d40840da9f03444748b2d
--- /dev/null
+++ b/qemu/build.rs
@@ -0,0 +1,12 @@
+use std::{env, error::Error, fs, path::PathBuf};
+
+fn main() -> Result<(), Box<dyn Error>> {
+    let out_dir = &PathBuf::from(env::var("OUT_DIR")?);
+
+    // place the linker script somewhere the linker can find it
+    let filename = "memory.x";
+    fs::copy(filename, out_dir.join(filename))?;
+    println!("cargo:rustc-link-search={}", out_dir.display());
+
+    Ok(())
+}
diff --git a/qemu/examples/1-rtfm.rs b/qemu/examples/1-rtfm.rs
new file mode 100644
index 0000000000000000000000000000000000000000..5cf4e9791a079be215af79bdb3d8da20f890c0d5
--- /dev/null
+++ b/qemu/examples/1-rtfm.rs
@@ -0,0 +1,75 @@
+#![deny(unsafe_code)]
+// #![deny(warnings)]
+#![no_main]
+#![no_std]
+
+use async_cortex_m::{task, unsync::Mutex};
+use cortex_m::asm;
+use cortex_m_semihosting::{debug, hprintln};
+// use pac::Interrupt::TIMER0;
+use panic_halt as _; // panic handler
+use rtic::app;
+
+#[rtic::app(device = lm3s6965)]
+mod app {
+    #[resources]
+    struct Resources {
+        #[init(9)]
+        x: u32,
+    }
+
+    #[idle(resources = [x])]
+    fn idle(mut cx: idle::Context) -> ! {
+        static mut X: Mutex<i64> = Mutex::new(0);
+        let c: &'static _ = X;
+
+        cx.resources.x.lock(|x| {
+            hprintln!("x {}", x).ok();
+        });
+
+        task::spawn(async move {
+            loop {
+                hprintln!("A: before lock").ok();
+
+                let mut lock = c.lock().await;
+
+                hprintln!("A: before write {}", *lock).ok();
+                *lock += 1;
+                drop(lock);
+
+                hprintln!("A: after releasing the lock").ok();
+
+                hprintln!("A: manual yield").ok();
+                task::r#yield().await;
+            }
+        });
+
+        task::block_on(async {
+            loop {
+                hprintln!("B: before lock").ok();
+
+                // cannot immediately make progress; context switch to A
+                let mut lock = c.lock().await;
+
+                hprintln!("B: {}", *lock).ok();
+                *lock += 1;
+
+                if *lock > 10 {
+                    debug::exit(debug::EXIT_SUCCESS);
+                } else {
+                    drop(lock);
+                    hprintln!("B: yield").ok();
+                    task::r#yield().await;
+                }
+            }
+        });
+
+        loop {
+            continue;
+        }
+    }
+    #[task(binds = UART0, resources = [x])]
+    fn rt_task(cx: rt_task::Context) {
+        hprintln!("x {}", cx.resources.x).ok();
+    }
+}
diff --git a/qemu/examples/1-yield.rs b/qemu/examples/1-yield.rs
new file mode 100644
index 0000000000000000000000000000000000000000..2f2f53412f45f3c5536a2bf4b8761883c22c5bb5
--- /dev/null
+++ b/qemu/examples/1-yield.rs
@@ -0,0 +1,50 @@
+//! Yielding from a task
+//!
+//! Expected output:
+//!
+//! ```
+//! B: yield
+//! A: yield
+//! B: yield
+//! A: yield
+//! DONE
+//! ```
+
+#![deny(unsafe_code)]
+#![deny(warnings)]
+#![no_main]
+#![no_std]
+
+use async_cortex_m::task;
+use cortex_m_rt::entry;
+use cortex_m_semihosting::hprintln;
+use nrf52 as _; // memory layout
+use panic_udf as _; // panic handler
+
+#[entry]
+fn main() -> ! {
+    // task A
+    task::spawn(async {
+        loop {
+            hprintln!("A: yield").ok();
+            // context switch to B
+            task::r#yield().await;
+        }
+    });
+
+    // task B
+    task::block_on(async {
+        loop {
+            hprintln!("B1: yield").ok();
+
+            // context switch to A
+            task::r#yield().await;
+
+            hprintln!("B2: yield").ok();
+
+            task::r#yield().await;
+
+            hprintln!("DONE").ok();
+        }
+    })
+}
diff --git a/qemu/examples/2-rtfm.rs b/qemu/examples/2-rtfm.rs
new file mode 100644
index 0000000000000000000000000000000000000000..339ab86c993af2abd5476b27a8dbe9323748d8cc
--- /dev/null
+++ b/qemu/examples/2-rtfm.rs
@@ -0,0 +1,97 @@
+#![deny(unsafe_code)]
+// #![deny(warnings)]
+#![no_main]
+#![no_std]
+
+use async_cortex_m::{task, unsync::Mutex};
+use cortex_m::asm;
+use cortex_m_semihosting::hprintln;
+use pac::Interrupt::TIMER0;
+use panic_udf as _; // panic handler
+use rtfm::app;
+
+#[app(device = pac)]
+const APP: () = {
+    struct Resources {
+        #[init(9)]
+        rtfm_x:u64,
+    }
+
+    #[idle(resources = [rtfm_x])]
+    fn idle(mut cx: idle::Context) -> ! {
+        static mut X: Mutex<i64> = Mutex::new(0);
+        let c: &'static _ = X;
+        // static mut RT_X : impl rtfm::Mutex = &mut cx.resources.x;
+
+        // rt_x.lock(|x| {
+        //     hprintln!("x {}", x);
+        // });
+  
+        cx.resources.rtfm_x.lock(|x| {
+            hprintln!("x {}", x);
+        });
+
+        // let cc:&'static _ = &cx.resources.x;
+   
+        task::spawn(async {
+            loop {
+                hprintln!("A: before lock").ok();
+
+                // let mut lock = c.lock().await;
+
+                // hprintln!("A: before write {}", *lock).ok();
+                // *lock += 1;
+                // drop(lock);
+
+                hprintln!("A: after releasing the lock").ok();
+
+                hprintln!("A: manual yield").ok();
+                task::r#yield().await;
+                // cc.lock(|x| {
+                //     hprintln!("x {}", x);
+                // });
+
+                cx.resources.rtfm_x.lock(|x| {
+                    hprintln!("x {}", x);
+                });
+                
+            }
+        });
+
+        task::block_on(async {
+            loop {
+                hprintln!("B: before lock").ok();
+
+                // cannot immediately make progress; context switch to A
+                let mut lock = c.lock().await;
+
+                hprintln!("B: {}", *lock).ok();
+                *lock += 1;
+
+
+                if *lock > 10 {
+                    loop {
+                        asm::bkpt();
+                    }
+                } else {
+                    drop(lock);
+                    hprintln!("B: yield").ok();
+                    task::r#yield().await;
+                }
+
+                cx.resources.rtfm_x.lock(|x| {
+                    hprintln!("x {}", x);
+                });
+            }
+        });
+
+        loop {
+            continue;
+        }
+    }
+
+    #[task(priority = 2, binds = TIMER0, resources = [rtfm_x])]
+    fn rt_task(cx:rt_task::Context) {
+        hprintln!("x {}", cx.resources.rtfm_x);
+    }
+};
diff --git a/qemu/examples/2-share.rs b/qemu/examples/2-share.rs
new file mode 100644
index 0000000000000000000000000000000000000000..78afaa4d186a528f2b31578a93193970b0daf043
--- /dev/null
+++ b/qemu/examples/2-share.rs
@@ -0,0 +1,73 @@
+//! Sharing state between tasks using `Cell` and `RefCell`
+//!
+//! ```
+//! B: initialize x
+//! B: post a message through y
+//! B: yield
+//! A: x=42
+//! A: received a message through y: 42
+//! A: yield
+//! DONE
+//! ```
+
+#![deny(unsafe_code)]
+#![deny(warnings)]
+#![no_main]
+#![no_std]
+
+use core::cell::{Cell, RefCell};
+
+use async_cortex_m::task;
+use cortex_m::asm;
+use cortex_m_rt::entry;
+use cortex_m_semihosting::hprintln;
+use nrf52 as _; // memory layout
+use panic_udf as _; // panic handler
+
+#[entry]
+fn main() -> ! {
+    static mut X: Cell<i64> = Cell::new(0);
+    // not-async-aware, one-shot channel
+    static mut Y: RefCell<Option<i32>> = RefCell::new(None);
+
+    // only references with `'static` lifetimes can be sent to `spawn`-ed tasks
+    // NOTE we coerce these to a shared (`&-`) reference to avoid the `move` blocks taking ownership
+    // of the owning static (`&'static mut`) reference
+    let x: &'static Cell<_> = X;
+    let y: &'static RefCell<_> = Y;
+
+    // task A
+    task::spawn(async move {
+        hprintln!("A: x={}", x.get()).ok();
+
+        if let Some(msg) = y.borrow_mut().take() {
+            hprintln!("A: received a message through y: {}", msg).ok();
+        }
+
+        loop {
+            hprintln!("A: yield").ok();
+            // context switch to B
+            task::r#yield().await;
+        }
+    });
+
+    // task B
+    task::block_on(async {
+        hprintln!("B: initialize x").ok();
+        x.set(42);
+
+        hprintln!("B: post a message through y").ok();
+        *y.borrow_mut() = Some(42);
+
+        hprintln!("B: yield").ok();
+
+        // context switch to A
+        task::r#yield().await;
+
+        hprintln!("DONE").ok();
+
+        loop {
+            asm::bkpt();
+        }
+    })
+}
diff --git a/qemu/examples/3-mutex.rs b/qemu/examples/3-mutex.rs
new file mode 100644
index 0000000000000000000000000000000000000000..f437572ad40205b5c91c93d5935cc1232f705a96
--- /dev/null
+++ b/qemu/examples/3-mutex.rs
@@ -0,0 +1,84 @@
+//! Mutex shared between tasks
+//!
+//! "When to use `Mutex` instead of a `RefCell`?" Both abstractions will give you an exclusive
+//! (`&mut-`) reference to the data and that reference can survive across `yield`s (either explicit
+//! , i.e. `task::yield`, or implicit, `.await`).
+//!
+//! The difference between the two is clear when contention occurs. If two or more tasks contend for
+//! a `RefCell`, as in they both call `borrow_mut` on it, you'll get a panic. On the other hand, if
+//! you use a `Mutex` in a similar scenario, i.e. both tasks call `lock` on it, then one of them
+//! will "asynchronous" wait for (i.e. not resume until) the other task to release (releases) the
+//! lock.
+//!
+//! Expected output:
+//!
+//! ```
+//! B: before recv
+//! A: before send
+//! A: after send
+//! A: yield
+//! B: 42
+//! DONE
+//! ```
+//!
+//! Try to replace the `Mutex` with `RefCell` and re-run the example
+
+#![deny(unsafe_code)]
+#![deny(warnings)]
+#![no_main]
+#![no_std]
+
+use async_cortex_m::{task, unsync::Mutex};
+use cortex_m::asm;
+use cortex_m_rt::entry;
+use cortex_m_semihosting::hprintln;
+use nrf52 as _; // memory layout
+use panic_udf as _; // panic handler
+
+#[entry]
+fn main() -> ! {
+    static mut X: Mutex<i64> = Mutex::new(0);
+
+    task::block_on(async {
+        loop {
+            hprintln!("A: before lock").ok();
+
+            let mut lock = X.lock().await;
+
+            hprintln!("A: before write {}", *lock).ok();
+            *lock += 1;
+            drop(lock);
+
+            hprintln!("A: after releasing the lock").ok();
+
+            hprintln!("A: manual yield").ok();
+            task::r#yield().await;
+        }
+    });
+
+    task::block_on(async {
+        loop {
+            hprintln!("B: before lock").ok();
+
+            // cannot immediately make progress; context switch to A
+            let mut lock = X.lock().await;
+
+            hprintln!("B: {}", *lock).ok();
+            *lock += 1;
+
+            if *lock > 10 {
+                loop {
+                    asm::bkpt();
+                }
+            } else {
+                drop(lock);
+                hprintln!("B: yield").ok();
+                task::r#yield().await;
+            }
+        }
+    });
+
+    loop {
+        continue;
+    }
+}
diff --git a/qemu/examples/3-mutex2.rs b/qemu/examples/3-mutex2.rs
new file mode 100644
index 0000000000000000000000000000000000000000..429cc1ae32773a00d1c19f936d2fdd252edc4eb7
--- /dev/null
+++ b/qemu/examples/3-mutex2.rs
@@ -0,0 +1,59 @@
+#![deny(unsafe_code)]
+#![deny(warnings)]
+#![no_main]
+#![no_std]
+
+use async_cortex_m::{task, unsync::Mutex};
+use cortex_m::asm;
+use cortex_m_rt::entry;
+use cortex_m_semihosting::hprintln;
+use nrf52 as _; // memory layout
+use panic_udf as _; // panic handler
+
+#[entry]
+fn main() -> ! {
+    static mut X: Mutex<i64> = Mutex::new(0);
+    let c: &'static _ = X;
+    task::spawn(async move {
+        loop {
+            hprintln!("A: before lock").ok();
+
+            let mut lock = c.lock().await;
+
+            hprintln!("A: before write {}", *lock).ok();
+            *lock += 1;
+            drop(lock);
+
+            hprintln!("A: after releasing the lock").ok();
+
+            hprintln!("A: manual yield").ok();
+            task::r#yield().await;
+        }
+    });
+
+    task::block_on(async {
+        loop {
+            hprintln!("B: before lock").ok();
+
+            // cannot immediately make progress; context switch to A
+            let mut lock = c.lock().await;
+
+            hprintln!("B: {}", *lock).ok();
+            *lock += 1;
+
+            if *lock > 10 {
+                loop {
+                    asm::bkpt();
+                }
+            } else {
+                drop(lock);
+                hprintln!("B: yield").ok();
+                task::r#yield().await;
+            }
+        }
+    });
+
+    loop {
+        continue;
+    }
+}
diff --git a/qemu/examples/4-channel.rs b/qemu/examples/4-channel.rs
new file mode 100644
index 0000000000000000000000000000000000000000..acac752add554fcfba92973760dd6eca371b872d
--- /dev/null
+++ b/qemu/examples/4-channel.rs
@@ -0,0 +1,61 @@
+//! Message passing between tasks using a MPMC channel
+//!
+//! Expected output:
+//!
+//! ```
+//! B: before recv
+//! A: before send
+//! A: after send
+//! A: yield
+//! B: 42
+//! DONE
+//! ```
+
+#![deny(unsafe_code)]
+#![deny(warnings)]
+#![no_main]
+#![no_std]
+
+use async_cortex_m::{task, unsync::Channel};
+use cortex_m::asm;
+use cortex_m_rt::entry;
+use cortex_m_semihosting::hprintln;
+use nrf52 as _; // memory layout
+use panic_udf as _; // panic handler
+
+#[entry]
+fn main() -> ! {
+    static mut C: Channel<i32> = Channel::new();
+
+    // coerce to a shared (`&-`) reference to avoid _one_ of the `move` blocks taking ownership of
+    // the owning static (`&'static mut`) reference
+    let c: &'static _ = C;
+
+    task::spawn(async move {
+        hprintln!("A: before send").ok();
+
+        c.send(42).await;
+
+        hprintln!("A: after send").ok();
+
+        loop {
+            hprintln!("A: yield").ok();
+            task::r#yield().await;
+        }
+    });
+
+    task::block_on(async move {
+        hprintln!("B: before recv").ok();
+
+        // cannot immediately make progress; context switch to A
+        let msg = c.recv().await;
+
+        hprintln!("B: {}", msg).ok();
+
+        hprintln!("DONE").ok();
+
+        loop {
+            asm::bkpt();
+        }
+    })
+}
diff --git a/qemu/examples/5-heartbeat.rs b/qemu/examples/5-heartbeat.rs
new file mode 100644
index 0000000000000000000000000000000000000000..85ced4829985c6cf03768578b038b041c417215f
--- /dev/null
+++ b/qemu/examples/5-heartbeat.rs
@@ -0,0 +1,42 @@
+//! Yielding from a task
+//!
+//! Expected output:
+//!
+//! ```
+//! B: yield
+//! A: yield
+//! B: yield
+//! A: yield
+//! DONE
+//! ```
+
+#![deny(unsafe_code)]
+#![deny(warnings)]
+#![no_main]
+#![no_std]
+
+use core::time::Duration;
+
+use async_cortex_m::task;
+use cortex_m_rt::entry;
+use nrf52::{led::Red, timer::Timer};
+use panic_udf as _; // panic handler
+
+#[entry]
+fn main() -> ! {
+    let mut timer = Timer::take();
+
+    let dur = Duration::from_millis(100);
+    task::block_on(async {
+        loop {
+            Red.on();
+            timer.wait(dur).await;
+            Red.off();
+            timer.wait(dur).await;
+            Red.on();
+            timer.wait(dur).await;
+            Red.off();
+            timer.wait(12 * dur).await;
+        }
+    })
+}
diff --git a/qemu/examples/6-hello.rs b/qemu/examples/6-hello.rs
new file mode 100644
index 0000000000000000000000000000000000000000..4d4df083641b4620a1f56409131701293d13f9a4
--- /dev/null
+++ b/qemu/examples/6-hello.rs
@@ -0,0 +1,42 @@
+//! Spam "Hello, world!" over the serial line (@ 9600 bauds)
+//!
+//! TXD = P0.06
+//! RXD = P0.08
+
+#![deny(unsafe_code)]
+#![deny(warnings)]
+#![no_main]
+#![no_std]
+
+use core::time::Duration;
+
+use async_cortex_m::task;
+use cortex_m_rt::entry;
+use nrf52::{led::Red, serial, timer::Timer};
+use panic_udf as _; // panic handler
+
+#[entry]
+fn main() -> ! {
+    // heartbeat task
+    let mut timer = Timer::take();
+    let dur = Duration::from_millis(100);
+    task::spawn(async move {
+        loop {
+            Red.on();
+            timer.wait(dur).await;
+            Red.off();
+            timer.wait(dur).await;
+            // Red.on();
+            // timer.wait(dur).await;
+            // Red.off();
+            timer.wait(12 * dur).await;
+        }
+    });
+
+    let (mut tx, _rx) = serial::take();
+    task::block_on(async {
+        loop {
+            tx.write(b"Hello, world!\n\r").await;
+        }
+    })
+}
diff --git a/qemu/examples/7-echo.rs b/qemu/examples/7-echo.rs
new file mode 100644
index 0000000000000000000000000000000000000000..085a6ebb1aa22cf2a6d3a95c66c428e3d1d6e81e
--- /dev/null
+++ b/qemu/examples/7-echo.rs
@@ -0,0 +1,44 @@
+//! Echo back data received over the serial line (@ 9600 bauds)
+//!
+//! TXD = P0.06
+//! RXD = P0.08
+
+#![deny(unsafe_code)]
+#![deny(warnings)]
+#![no_main]
+#![no_std]
+
+use core::time::Duration;
+
+use async_cortex_m::task;
+use cortex_m_rt::entry;
+use nrf52::{led::Red, serial, timer::Timer};
+use panic_udf as _; // panic handler
+
+#[entry]
+fn main() -> ! {
+    // heartbeat task
+    let mut timer = Timer::take();
+    let dur = Duration::from_millis(100);
+    task::spawn(async move {
+        loop {
+            Red.on();
+            timer.wait(dur).await;
+            Red.off();
+            timer.wait(dur).await;
+            Red.on();
+            timer.wait(dur).await;
+            Red.off();
+            timer.wait(12 * dur).await;
+        }
+    });
+
+    let (mut tx, mut rx) = serial::take();
+    task::block_on(async {
+        let mut buf = [0; 16];
+        loop {
+            rx.read(&mut buf).await;
+            tx.write(&buf).await;
+        }
+    })
+}
diff --git a/qemu/examples/8-sensor.rs b/qemu/examples/8-sensor.rs
new file mode 100644
index 0000000000000000000000000000000000000000..aaca911f46f5333b3c2a175584c0a0a1a189ee5b
--- /dev/null
+++ b/qemu/examples/8-sensor.rs
@@ -0,0 +1,114 @@
+//! Print sensor data on demand
+
+#![deny(unsafe_code)]
+#![deny(warnings)]
+#![no_main]
+#![no_std]
+
+use core::{cell::Cell, fmt::Write as _, time::Duration};
+
+use async_cortex_m::{task, unsync::Mutex};
+use cortex_m_rt::entry;
+use heapless::{consts, String};
+use nrf52::{led::Red, scd30::Scd30, serial, timer::Timer, twim::Twim};
+use panic_udf as _; // panic handler
+
+#[derive(Clone, Copy)]
+enum State {
+    NotReady,
+    Ready,
+    Error,
+}
+
+#[entry]
+fn main() -> ! {
+    // shared state
+    static mut STATE: Cell<State> = Cell::new(State::NotReady);
+    // range: 0 - 40,000 ppm
+    static mut CO2: Cell<u16> = Cell::new(0);
+    // range: 0 - 100 %
+    static mut RH: Cell<u8> = Cell::new(0);
+    // range: -40 - 70 C
+    static mut T: Cell<i8> = Cell::new(0);
+    static mut M: Option<Mutex<Twim>> = None;
+
+    let co2: &'static _ = CO2;
+    let state: &'static _ = STATE;
+    let rh: &'static _ = RH;
+    let t: &'static _ = T;
+
+    // heartbeat task
+    let mut timer = Timer::take();
+    let dur = Duration::from_millis(100);
+    task::spawn(async move {
+        loop {
+            Red.on();
+            timer.wait(dur).await;
+            Red.off();
+            timer.wait(dur).await;
+            Red.on();
+            timer.wait(dur).await;
+            Red.off();
+            timer.wait(12 * dur).await;
+        }
+    });
+
+    // task to print sensor info on demand
+    let (mut tx, mut rx) = serial::take();
+    task::spawn(async move {
+        let mut tx_buf = String::<consts::U32>::new();
+        let mut rx_buf = [0];
+
+        loop {
+            rx.read(&mut rx_buf).await;
+
+            // carriage return;
+            if rx_buf[0] == 13 {
+                match state.get() {
+                    State::Error => {
+                        tx.write(b"fatal error: I2C error\n").await;
+
+                        loop {
+                            task::r#yield().await;
+                        }
+                    }
+
+                    State::NotReady => {
+                        tx.write(b"sensor not ready; try again later\n").await;
+                    }
+
+                    State::Ready => {
+                        let co2 = co2.get();
+                        let t = t.get();
+                        let rh = rh.get();
+
+                        tx_buf.clear();
+                        // will not fail; the buffer is big enough
+                        let _ = writeln!(&mut tx_buf, "CO2: {}ppm\nT: {}C\nRH: {}%", co2, t, rh);
+                        tx.write(tx_buf.as_bytes()).await;
+                    }
+                }
+            }
+        }
+    });
+
+    // task to continuously poll the sensor
+    let twim = M.get_or_insert(Mutex::new(Twim::take()));
+    let mut scd30 = Scd30::new(twim);
+    task::block_on(async {
+        loop {
+            if let Ok(m) = scd30.get_measurement().await {
+                co2.set(m.co2 as u16);
+                rh.set(m.rh as u8);
+                t.set(m.t as i8);
+                state.set(State::Ready);
+            } else {
+                state.set(State::Error);
+
+                loop {
+                    task::r#yield().await;
+                }
+            }
+        }
+    })
+}
diff --git a/qemu/examples/9-clock.rs b/qemu/examples/9-clock.rs
new file mode 100644
index 0000000000000000000000000000000000000000..002d597a185745944f20bcc3659ee6cd9c583342
--- /dev/null
+++ b/qemu/examples/9-clock.rs
@@ -0,0 +1,262 @@
+//! Interactive serial console with access to the clock and gas sensor
+//!
+//! Example interaction (with local echo enabled):
+//!
+//! ```
+//! > help
+//! Commands:
+//! help              displays this text
+//! date              display the current date and time
+//! sensors           displays the gas sensor data
+//! set date %Y-%m-%d changes the date
+//! set time %H:%M:%S changes the time
+//! > sensors
+//! CO2: 652ppm
+//! T: 26C
+//! RH: 23%
+//! > set time 18:49:30
+//! > date
+//! 2020-02-28 18:49:32
+//! ```
+
+#![deny(unsafe_code)]
+#![deny(warnings)]
+#![no_main]
+#![no_std]
+
+use core::{
+    cell::Cell,
+    fmt::Write as _,
+    str::{self, FromStr},
+    time::Duration,
+};
+
+use async_cortex_m::{task, unsync::Mutex};
+use chrono::{Datelike as _, NaiveDate, NaiveTime};
+use cortex_m_rt::entry;
+use heapless::{consts, String, Vec};
+use nrf52::{
+    ds3231::{self, Ds3231},
+    led::Red,
+    scd30::Scd30,
+    serial,
+    timer::Timer,
+    twim::Twim,
+};
+use panic_udf as _; // panic handler
+
+#[derive(Clone, Copy)]
+enum SensorState {
+    NotReady,
+    Ready,
+    Error,
+}
+
+#[entry]
+fn main() -> ! {
+    // shared state
+    static mut STATE: Cell<SensorState> = Cell::new(SensorState::NotReady);
+    // range: 0 - 40,000 ppm
+    static mut CO2: Cell<u16> = Cell::new(0);
+    // range: 0 - 100 %
+    static mut RH: Cell<u8> = Cell::new(0);
+    // range: -40 - 70 C
+    static mut T: Cell<i8> = Cell::new(0);
+    static mut M: Option<Mutex<Twim>> = None;
+
+    let co2: &'static _ = CO2;
+    let state: &'static _ = STATE;
+    let rh: &'static _ = RH;
+    let t: &'static _ = T;
+
+    // heartbeat task
+    let mut timer = Timer::take();
+    let dur = Duration::from_millis(100);
+    task::spawn(async move {
+        loop {
+            Red.on();
+            timer.wait(dur).await;
+            Red.off();
+            timer.wait(dur).await;
+            Red.on();
+            timer.wait(dur).await;
+            Red.off();
+            timer.wait(12 * dur).await;
+        }
+    });
+
+    let twim = M.get_or_insert(Mutex::new(Twim::take()));
+    let mut scd30 = Scd30::new(twim);
+    task::spawn(async move {
+        loop {
+            // TODO instead of continuously polling the sensor we should only read it out when new
+            // data is ready (there's a pin that signals that), roughly every 2 seconds.
+            // Alternatively, we could send this task to sleep for 2 seconds after new data is read
+            let res = scd30.get_measurement().await;
+
+            if let Ok(m) = res {
+                co2.set(m.co2 as u16);
+                rh.set(m.rh as u8);
+                t.set(m.t as i8);
+                state.set(SensorState::Ready);
+
+                // adds fairness; avoids starving the task below
+                task::r#yield().await;
+            } else {
+                state.set(SensorState::Error);
+
+                loop {
+                    task::r#yield().await;
+                }
+            }
+        }
+    });
+
+    let (mut tx, mut rx) = serial::take();
+    let mut ds3231 = Ds3231::new(twim);
+    task::block_on(async {
+        let mut input = Vec::<u8, consts::U64>::new();
+        let mut tx_buf = String::<consts::U32>::new();
+
+        'prompt: loop {
+            tx.write(b"> ").await;
+
+            input.clear();
+            loop {
+                // not the most elegant way to have a responsive input
+                // Ideally, we want to use a large `rx_buf` and instead read its contents only when
+                // there has been no new data on the bus for a while
+                let mut rx_buf = [0];
+                rx.read(&mut rx_buf).await;
+
+                if input.push(rx_buf[0]).is_err() {
+                    tx.write(b"input buffer is full\n").await;
+                    continue 'prompt;
+                }
+
+                if let Ok(s) = str::from_utf8(&input) {
+                    // complete command
+                    if s.ends_with('\r') {
+                        if let Ok(cmd) = s[..s.len() - 1].parse::<Command>() {
+                            match cmd {
+                                Command::Date => {
+                                    match ds3231.get_datetime().await {
+                                        Ok(datetime) => {
+                                            let mut s = String::<consts::U32>::new();
+                                            // will not fail; the buffer is big enough
+                                            let _ = writeln!(&mut s, "{}", datetime);
+                                            tx.write(s.as_bytes()).await;
+                                        }
+
+                                        Err(ds3231::Error::Twim(..)) => {
+                                            tx.write(b"error communicating with the RTC\n").await;
+                                        }
+
+                                        Err(ds3231::Error::InvalidDate) => {
+                                            tx.write(b"invalid date stored in the RTC\n").await;
+                                        }
+                                    }
+                                }
+
+                                Command::SetDate(date) => {
+                                    // in `Command::parse_str` we validate the input date so no
+                                    // `InvalidDate` error should be raised here
+                                    if ds3231.set_date(date).await.is_err() {
+                                        tx.write(b"error communicating with the RTC\n").await;
+                                    }
+                                }
+
+                                Command::SetTime(time) => {
+                                    if ds3231.set_time(time).await.is_err() {
+                                        tx.write(b"error communicating with the RTC\n").await;
+                                    }
+                                }
+
+                                Command::Sensors => {
+                                    tx_buf.clear();
+
+                                    // will not fail; the buffer is big enough
+                                    let _ = writeln!(
+                                        &mut tx_buf,
+                                        "CO2: {}ppm\nT: {}C\nRH: {}%",
+                                        co2.get(),
+                                        t.get(),
+                                        rh.get()
+                                    );
+                                    tx.write(tx_buf.as_bytes()).await;
+                                }
+
+                                Command::Help => {
+                                    tx.write(
+                                        b"Commands:
+help              displays this text
+date              display the current date and time
+sensors           displays the gas sensor data
+set date %Y-%m-%d changes the date
+set time %H:%M:%S changes the time
+",
+                                    )
+                                    .await;
+                                }
+                            }
+                        } else {
+                            tx.write(b"invalid command; try `help`\n").await;
+                        }
+
+                        // new prompt; clear command buffer
+                        continue 'prompt;
+                    }
+                }
+            }
+        }
+    })
+}
+
+enum Command {
+    Date,
+    Help,
+    Sensors,
+    SetDate(NaiveDate),
+    SetTime(NaiveTime),
+}
+
+impl FromStr for Command {
+    type Err = Error;
+
+    fn from_str(mut s: &str) -> Result<Command, Error> {
+        const CMD_DATE: &str = "date";
+        const CMD_HELP: &str = "help";
+        const CMD_SENSORS: &str = "sensors";
+        const CMD_SET_DATE: &str = "set date ";
+        const CMD_SET_TIME: &str = "set time ";
+
+        s = s.trim();
+
+        Ok(if s == CMD_DATE {
+            Command::Date
+        } else if s == CMD_HELP {
+            Command::Help
+        } else if s == CMD_SENSORS {
+            Command::Sensors
+        } else if s.starts_with(CMD_SET_DATE) {
+            let date = NaiveDate::parse_from_str(&s[CMD_SET_DATE.len()..], "%Y-%m-%d")
+                .map_err(|_| Error)?;
+            let year = date.year();
+            // the RTC can only handle a span of roughly 200 years
+            if year < 2000 || year > 2199 {
+                return Err(Error);
+            }
+
+            Command::SetDate(date)
+        } else if s.starts_with(CMD_SET_TIME) {
+            let time = NaiveTime::parse_from_str(&s[CMD_SET_TIME.len()..], "%H:%M:%S")
+                .map_err(|_| Error)?;
+
+            Command::SetTime(time)
+        } else {
+            return Err(Error);
+        })
+    }
+}
+
+struct Error;
diff --git a/qemu/memory.x b/qemu/memory.x
new file mode 100644
index 0000000000000000000000000000000000000000..dbcd285013f39ae817a158482e2b4c0d5a4f2e3b
--- /dev/null
+++ b/qemu/memory.x
@@ -0,0 +1,6 @@
+MEMORY
+{
+  /* NOTE 1 K = 1 KiBi = 1024 bytes */
+  FLASH : ORIGIN = 0x00000000, LENGTH = 1M
+  RAM : ORIGIN = 0x20000000, LENGTH = 32K
+}
diff --git a/qemu/openocd.cfg b/qemu/openocd.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..080b5b48c55cf5e6214b07ab5b798e5c41616330
--- /dev/null
+++ b/qemu/openocd.cfg
@@ -0,0 +1,6 @@
+#source [find interface/cmsis-dap.cfg]
+source [find interface/jlink.cfg]
+
+transport select swd
+
+source [find target/nrf52.cfg]
\ No newline at end of file
diff --git a/qemu/openocd.gdb b/qemu/openocd.gdb
new file mode 100644
index 0000000000000000000000000000000000000000..d42cf8ce519bcb0fd952bff86cde0140d5a72997
--- /dev/null
+++ b/qemu/openocd.gdb
@@ -0,0 +1,22 @@
+target extended-remote :3333
+
+# print demangled symbols
+set print asm-demangle on
+
+# set backtrace limit to not have infinite backtrace loops
+set backtrace limit 32
+
+# detect unhandled exceptions, hard faults and panics
+break DefaultHandler
+break HardFault
+break rust_begin_unwind
+
+# *try* to stop at the user entry point (it might be gone due to inlining)
+break main
+
+monitor arm semihosting enable
+
+load
+
+# start the process but immediately halt the processor
+stepi
\ No newline at end of file