diff --git a/.vscode/launch.json b/.vscode/launch.json
index a8ae86066661e2e0817ca9cd646eb8f32b80ff79..15356f85dc87ed067790775c1cab246c2de6c16c 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -9,6 +9,7 @@
             "request": "launch",
             "servertype": "openocd",
             "name": "itm 64Mhz (debug)",
+            "preLaunchTask": "cargo build --example itm",
             "executable": "./target/thumbv7em-none-eabihf/debug/examples/itm",
             "configFiles": [
                 "interface/stlink.cfg",
@@ -37,6 +38,7 @@
             "request": "launch",
             "servertype": "openocd",
             "name": "hello 16Mhz (debug)",
+            "preLaunchTask": "cargo build --example hello",
             "executable": "./target/thumbv7em-none-eabihf/debug/examples/hello",
             "configFiles": [
                 "interface/stlink.cfg",
@@ -47,6 +49,22 @@
             ],
             "cwd": "${workspaceRoot}"
         },
+        {
+            "type": "cortex-debug",
+            "request": "launch",
+            "servertype": "openocd",
+            "name": "crash (release)",
+            "preLaunchTask": "cargo build --example crash --release",
+            "executable": "./target/thumbv7em-none-eabihf/release/examples/crash",
+            "configFiles": [
+                "interface/stlink.cfg",
+                "target/stm32f4x.cfg"
+            ],
+            "postLaunchCommands": [
+                "b HardFault"
+            ],
+            "cwd": "${workspaceRoot}"
+        },
         {
             "type": "cortex-debug",
             "request": "launch",
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index 4e31401eb4cc7a17bf3acb4b2582471a349b674a..649f1aacb794503e5e07ca2741cbd87333a72b6e 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -26,6 +26,42 @@
                 "kind": "build",
                 "isDefault": true
             }
-        }
+        },
+        {
+            "type": "shell",
+            "label": "cargo build --example hello",
+            "command": "cargo build --example hello",
+            "problemMatcher": [
+                "$rustc"
+            ],
+            "group": {
+                "kind": "build",
+                "isDefault": true
+            }
+        },
+        {
+            "type": "shell",
+            "label": "cargo build --example crash --release",
+            "command": "cargo build --example crash --release",
+            "problemMatcher": [
+                "$rustc"
+            ],
+            "group": {
+                "kind": "build",
+                "isDefault": true
+            }
+        },
+        {
+            "type": "shell",
+            "label": "cargo build --example itm",
+            "command": "cargo build --example itm",
+            "problemMatcher": [
+                "$rustc"
+            ],
+            "group": {
+                "kind": "build",
+                "isDefault": true
+            }
+        },
     ]
 }
\ No newline at end of file
diff --git a/Cargo.toml b/Cargo.toml
index 6da4855573eb7cff30e7f704df2014e5020eaf0f..cc13cce1e280c56dc5e0ca4af013628478ac63f1 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -15,6 +15,7 @@ panic-halt = "0.2.0"
 # Uncomment for the panic example.
 panic-semihosting = "0.5.1"
 panic-itm = "0.4.0"
+bare-metal = "0.2.4"
 
 # Uncomment for the allocator example.
 # alloc-cortex-m = "0.3.5"
diff --git a/README.md b/README.md
index 63adb377beced12d03ec310512352289125cec19..ad2d2e1a7ca519292639736e833d00463b5f4dec 100644
--- a/README.md
+++ b/README.md
@@ -24,13 +24,15 @@ $ rustup target add thumbv7em-none-eabihf
 - `itmdump` (for ITM trace output)
 - `vscode` and `cortex-debug` (optional for an integrated debugging experience)
 
+* https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug
+
 ---
 
 ## Examples
 
 ---
 
-### Hello
+### Hello World! Building and Debugging an Application
 
 1. Connect your devkit using USB. To check that it is found you can run:
 
@@ -110,9 +112,9 @@ You have now compiled and debugged a minimal Rust `hello` example. `gdb` is a ve
 
 ---
 
-### ITM 
+### ITM Tracing
 
-The `hello` example uses the `semihosting` interface to emit the trace information (appearing in the `openocd` terminal). The drawback is that `semihosting` is incredibly slow as it involves a lot of machinery to process each character. (Essentially, it writes a character to a given position in memory, runs a dedicated break instruction, `openocd` detecects the break, reads the character at the given postition in memory and emits the character to the console.)
+The `hello.rs` example uses the `semihosting` interface to emit the trace information (appearing in the `openocd` terminal). The drawback is that `semihosting` is incredibly slow as it involves a lot of machinery to process each character. (Essentially, it writes a character to a given position in memory, runs a dedicated break instruction, `openocd` detecects the break, reads the character at the given postition in memory and emits the character to the console.)
 
 A better approach is to use de bultin in ITM (Instrumentation Trace Macrocell), designed to more efficently implement tracing. The onboard `stlink` programmer can put up to 4 characters into an ITM package, and transmit that to the host (`openocd`). `openocd` can process the incoming data and send it to a file or FIFO queue. The ITM package stream needs to be decoded (header + data). To this end we use the `itmdump` tool.
 
@@ -123,7 +125,7 @@ $ mkfifo /tmp/itm.log
 $ itmdump -f /tmp/itm.log -F
 ```
 
-Now you can compile and run the `itm` application using the same steps as the `hello` programg. In the `itmdump` console you should now have the trace output.
+Now you can compile and run the `itm.rs` application using the same steps as the `hello` program. In the `itmdump` console you should now have the trace output.
 
 ``` console
 $ mkfifo /tmp/itm.log
@@ -181,13 +183,22 @@ A third alternative would be to store the panic message in some non-volatile mem
 
 ---
 
-### Exception Handling
+### Exception Handling and Core Peripheral Access
 
 The ARM Cortex-M processors features a set of *core* peripherpherals and *exception* handlers. These offer basic functionality independent of vendor (NXP, STM, ...). The `SysTick` perihperal is a 24-bit countdown timer, that raises a `SysTick` exception when hitting 0 and reloads the set value. Seen as a real-time system, we can dispatch the `SysTick` task in a periodic fashion (without accumulated drift under some additional constraints).
 
-In the example a `.` is emitted by the `SysTick` handler using `semihosting`. Running the example should give you a periodic updated of the `openocd` console.
+In the `exception.rs` example  a `.` is emitted by the `SysTick` handler using `semihosting`. Running the example should give you a periodic updated of the `openocd` console.
+
+The `exception_itm.rs` and `exception_itm_raw.rs` uses the ITM instead. The difference is the way
+they gain access to the `ITM` periphereal. In the first case we *steal* the whole set of core peripherals (stealing works only in `--release` mode), while the in the second case we use *raw* pointer access to the `ITM`. In both cases, the code is *unsafe*, as there is no guarantee that other tasks may access the peripheral simultaneously (causing a conflict/race). Later we will see how the concurrency problem is solved in RTFM to offer safe access to peripherals.
+
+---
+
+### Crash - Analysing the Exception Frame
+
+In case the execution of an intstruction fails, a `HardFault` exception is raised by the hardware, and the `HardFault` handler is executed. We can define our own handler as in example `crash.rs`. In `main` we attempt to read an illegal address, causing a `HardFault`, and we hit a breakpoint (`openocd.gdb` script sets a breakbpoint at the `HardFualt` handler). From there you can print the exception frame, reflecting the state of the MCU when the error occured. You can use `gdb` to give a `back trace` of the call-stack leading up to the error. See the example for detailed information.
+
 
-As an exercise make it use the ITM instead.
 
 
 ---
@@ -349,11 +360,18 @@ This ensures 1) the program will not yet again overflow the ITM buffer, 2) the f
 
 # Visual Studio Code
 
-It is possible to run `gdb` from within the `vscode`. `vscode` is highly configurable, (keyboard shortcuts, keymaps, plugins etc.). Using the default setup, and the `cortex-debug` plugin you can:
+`vscode` is highly configurable, (keyboard shortcuts, keymaps, plugins etc.) There is Rust support through the `rls-vscode` plugin (https://github.com/rust-lang/rls-vscode).
+
+It is possible to run `arm-none-eabi-gdb` from within the `vscode` using the `cortex-debug` plugin (https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug).
+
+For general informaiton regarding debugging in `vscode`, see https://code.visualstudio.com/docs/editor/debugging.
+
+Some useful (default) shortcuts:
+
+- `CTRL+m` compilation tasks, (e.g., compile all examples `cargo build --examples`). Cargo is smart and just re-compiles what is changed.
 
-- `CTRL+m` compile all examples (`cargo build --examples`). Cargo is smart and just re-compiles what is changed.
+- `CTRL+d` debug launch configurations, enter debug mode to choose a binary (e.g., `itm 64MHz (debug)`)
 
-- `CTRL+d` enter debug mode to choose a binary. (`itm 64MHz (debug)`)
 - `F5` to start. It will open the `cortex_m_rt/src/lib.rs` file, which contains the startup code. From there you can continue `F5` again.
 - `F6` to break. The program will now be in the infinite loop.
 - You can view the ITM trace in the `OUTPUT` tab, choose the dropdown `SWO: ITM [port 0, type console]`. It should now display:
diff --git a/examples/exception_itm.rs b/examples/exception_itm.rs
index 90799e78d483506077120035e88469d7fa4b2394..01144a86e562a0346d0c786d23fe2d0ef0bbbf65 100644
--- a/examples/exception_itm.rs
+++ b/examples/exception_itm.rs
@@ -13,18 +13,14 @@
 extern crate panic_halt;
 
 use cortex_m::peripheral::syst::SystClkSource;
+use cortex_m::{iprint, Peripherals};
 use cortex_m_rt::{entry, exception};
-use cortex_m::{iprint, iprintln, Peripherals};
 
 #[entry]
 fn main() -> ! {
-    cortex_m::asm::bkpt();
-    let mut p = Peripherals::take().unwrap();
+    let p = Peripherals::take().unwrap();
     let mut syst = p.SYST;
 
-    let stim = &mut p.ITM.stim[0];
-    iprintln!(stim, "start");
-
     // configures the system timer to trigger a SysTick exception every second
     syst.set_clock_source(SystClkSource::Core);
     syst.set_reload(16_000_000); // period = 1s
@@ -36,6 +32,14 @@ fn main() -> ! {
 
 #[exception]
 fn SysTick() {
+    // Here we steal all the peripherals.
+    //
+    // This is unsafe, as some other task/tasks may access the peripherals
+    // simultaneously, causing a conflict/race.
+    //
+    // The operation is checked in `debug` mode but not in release
+    // using a `debug_assert`. As the periherals is already taken
+    // by main, we need to compile in `--release` mode, to avoid a panic.
     let mut p = unsafe { cortex_m::Peripherals::steal() };
     let stim = &mut p.ITM.stim[0];
     iprint!(stim, ".");
diff --git a/examples/exception_itm_raw.rs b/examples/exception_itm_raw.rs
new file mode 100644
index 0000000000000000000000000000000000000000..a4f7872c26fd45929b6b05e4ce5e365cb6822eac
--- /dev/null
+++ b/examples/exception_itm_raw.rs
@@ -0,0 +1,41 @@
+//! Overriding an exception handler
+//!
+//! You can override an exception handler using the [`#[exception]`][1] attribute.
+//!
+//! [1]: https://rust-embedded.github.io/cortex-m-rt/0.6.1/cortex_m_rt_macros/fn.exception.html
+//!
+//! Notice, steal will panic! in debug mode, due to a `debug_assert` (ignored in release).
+//! ---
+
+#![no_main]
+#![no_std]
+
+extern crate panic_halt;
+
+use cortex_m::peripheral::{syst::SystClkSource, ITM};
+use cortex_m::{iprint, Peripherals};
+use cortex_m_rt::{entry, exception};
+
+#[entry]
+fn main() -> ! {
+    let p = Peripherals::take().unwrap();
+    let mut syst = p.SYST;
+
+    // configures the system timer to trigger a SysTick exception every second
+    syst.set_clock_source(SystClkSource::Core);
+    syst.set_reload(16_000_000); // period = 1s
+    syst.enable_counter();
+    syst.enable_interrupt();
+
+    loop {}
+}
+
+#[exception]
+fn SysTick() {
+    // here we access `ITM` using a *raw* pointer
+    // this is unsafe, as there may be other tasks accessing the peripheral
+    // simultaneously (and that might cause a conflict/race)
+    let itm = unsafe { &mut *ITM::ptr() };
+    let stim = &mut itm.stim[0];
+    iprint!(stim, ".");
+}
diff --git a/openocd.gdb b/openocd.gdb
index fa7f424954e287720f0c3ca90064829f37d71cf7..ea08e46bcfff317a2503291c69b6e56a4077b2ab 100644
--- a/openocd.gdb
+++ b/openocd.gdb
@@ -5,8 +5,8 @@ set print asm-demangle on
 
 # detect unhandled exceptions, hard faults and panics
 #break DefaultHandler
-#break UserHardFault
-#break rust_begin_unwind
+break HardFault
+break rust_begin_unwind
 
 # *try* to stop at the user entry point (it might be gone due to inlining)
 break main