blob: cbcee982bb6cb67e9cbb653f1e2e54f3275dc3b5 [file] [log] [blame]
<!DOCTYPE HTML>
<html lang="en" class="light sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Exceptions - The Embedded Rust Book</title>
<!-- Custom HTML head -->
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff">
<link rel="icon" href="../favicon.svg">
<link rel="shortcut icon" href="../favicon.png">
<link rel="stylesheet" href="../css/variables.css">
<link rel="stylesheet" href="../css/general.css">
<link rel="stylesheet" href="../css/chrome.css">
<link rel="stylesheet" href="../css/print.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
<link rel="stylesheet" href="../fonts/fonts.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" id="highlight-css" href="../highlight.css">
<link rel="stylesheet" id="tomorrow-night-css" href="../tomorrow-night.css">
<link rel="stylesheet" id="ayu-highlight-css" href="../ayu-highlight.css">
<!-- Custom theme stylesheets -->
<!-- Provide site root and default themes to javascript -->
<script>
const path_to_root = "../";
const default_light_theme = "light";
const default_dark_theme = "navy";
</script>
<!-- Start loading toc.js asap -->
<script src="../toc.js"></script>
</head>
<body>
<div id="mdbook-help-container">
<div id="mdbook-help-popup">
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
<div>
<p>Press <kbd></kbd> or <kbd></kbd> to navigate between chapters</p>
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
<p>Press <kbd>?</kbd> to show this help</p>
<p>Press <kbd>Esc</kbd> to hide this help</p>
</div>
</div>
</div>
<div id="body-container">
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script>
try {
let theme = localStorage.getItem('mdbook-theme');
let sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script>
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
let theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
const html = document.documentElement;
html.classList.remove('light')
html.classList.add(theme);
html.classList.add("js");
</script>
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
<!-- Hide / unhide sidebar before it is displayed -->
<script>
let sidebar = null;
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
} else {
sidebar = 'hidden';
}
sidebar_toggle.checked = sidebar === 'visible';
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<!-- populated by js -->
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
<noscript>
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
</noscript>
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
<div class="sidebar-resize-indicator"></div>
</div>
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky">
<div class="left-buttons">
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</label>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
<button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">The Embedded Rust Book</h1>
<div class="right-buttons">
<a href="../print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
<a href="https://github.com/rust-embedded/book" title="Git repository" aria-label="Git repository">
<i id="git-repository-button" class="fa fa-github"></i>
</a>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script>
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<h1 id="exceptions"><a class="header" href="#exceptions">Exceptions</a></h1>
<p>Exceptions, and interrupts, are a hardware mechanism by which the processor
handles asynchronous events and fatal errors (e.g. executing an invalid
instruction). Exceptions imply preemption and involve exception handlers,
subroutines executed in response to the signal that triggered the event.</p>
<p>The <code>cortex-m-rt</code> crate provides an <a href="https://docs.rs/cortex-m-rt-macros/latest/cortex_m_rt_macros/attr.exception.html"><code>exception</code></a> attribute to declare exception
handlers.</p>
<pre><code class="language-rust ignore">// Exception handler for the SysTick (System Timer) exception
#[exception]
fn SysTick() {
// ..
}</code></pre>
<p>Other than the <code>exception</code> attribute exception handlers look like plain
functions but there's one more difference: <code>exception</code> handlers can <em>not</em> be
called by software. Following the previous example, the statement <code>SysTick();</code>
would result in a compilation error.</p>
<p>This behavior is pretty much intended and it's required to provide a feature:
<code>static mut</code> variables declared <em>inside</em> <code>exception</code> handlers are <em>safe</em> to use.</p>
<pre><code class="language-rust ignore">#[exception]
fn SysTick() {
static mut COUNT: u32 = 0;
// `COUNT` has transformed to type `&amp;mut u32` and it's safe to use
*COUNT += 1;
}</code></pre>
<p>As you may know, using <code>static mut</code> variables in a function makes it
<a href="https://en.wikipedia.org/wiki/Reentrancy_(computing)"><em>non-reentrant</em></a>. It's undefined behavior to call a non-reentrant function,
directly or indirectly, from more than one exception / interrupt handler or from
<code>main</code> and one or more exception / interrupt handlers.</p>
<p>Safe Rust must never result in undefined behavior so non-reentrant functions
must be marked as <code>unsafe</code>. Yet I just told that <code>exception</code> handlers can safely
use <code>static mut</code> variables. How is this possible? This is possible because
<code>exception</code> handlers can <em>not</em> be called by software thus reentrancy is not
possible. These handlers are called by the hardware itself which is assumed to be physically non-concurrent.</p>
<p>As a result, in the context of exception handlers in embedded systems, the absence of concurrent invocations of the same handler ensures that there are no reentrancy issues, even if the handler uses static mutable variables.</p>
<p>In a multicore system, where multiple processor cores are executing code concurrently, the potential for reentrancy issues becomes relevant again, even within exception handlers. While each core may have its own set of exception handlers, there can still be scenarios where multiple cores attempt to execute the same exception handler simultaneously.<br />
To address this concern in a multicore environment, proper synchronization mechanisms need to be employed within the exception handlers to ensure that access to shared resources is properly coordinated among the cores. This typically involves the use of techniques such as locks, semaphores, or atomic operations to prevent data races and maintain data integrity</p>
<blockquote>
<p>Note that the <code>exception</code> attribute transforms definitions of static variables
inside the function by wrapping them into <code>unsafe</code> blocks and providing us
with new appropriate variables of type <code>&amp;mut</code> of the same name.
Thus we can dereference the reference via <code>*</code> to access the values of the variables without
needing to wrap them in an <code>unsafe</code> block.</p>
</blockquote>
<h2 id="a-complete-example"><a class="header" href="#a-complete-example">A complete example</a></h2>
<p>Here's an example that uses the system timer to raise a <code>SysTick</code> exception
roughly every second. The <code>SysTick</code> exception handler keeps track of how many
times it has been called in the <code>COUNT</code> variable and then prints the value of
<code>COUNT</code> to the host console using semihosting.</p>
<blockquote>
<p><strong>NOTE</strong>: You can run this example on any Cortex-M device; you can also run it
on QEMU</p>
</blockquote>
<pre><code class="language-rust ignore">#![deny(unsafe_code)]
#![no_main]
#![no_std]
use panic_halt as _;
use core::fmt::Write;
use cortex_m::peripheral::syst::SystClkSource;
use cortex_m_rt::{entry, exception};
use cortex_m_semihosting::{
debug,
hio::{self, HostStream},
};
#[entry]
fn main() -&gt; ! {
let p = cortex_m::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);
// this is configured for the LM3S6965 which has a default CPU clock of 12 MHz
syst.set_reload(12_000_000);
syst.clear_current();
syst.enable_counter();
syst.enable_interrupt();
loop {}
}
#[exception]
fn SysTick() {
static mut COUNT: u32 = 0;
static mut STDOUT: Option&lt;HostStream&gt; = None;
*COUNT += 1;
// Lazy initialization
if STDOUT.is_none() {
*STDOUT = hio::hstdout().ok();
}
if let Some(hstdout) = STDOUT.as_mut() {
write!(hstdout, "{}", *COUNT).ok();
}
// IMPORTANT omit this `if` block if running on real hardware or your
// debugger will end in an inconsistent state
if *COUNT == 9 {
// This will terminate the QEMU process
debug::exit(debug::EXIT_SUCCESS);
}
}</code></pre>
<pre><code class="language-console">tail -n5 Cargo.toml
</code></pre>
<pre><code class="language-toml">[dependencies]
cortex-m = "0.5.7"
cortex-m-rt = "0.6.3"
panic-halt = "0.2.0"
cortex-m-semihosting = "0.3.1"
</code></pre>
<pre><code class="language-text">$ cargo run --release
Running `qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb (..)
123456789
</code></pre>
<p>If you run this on the Discovery board you'll see the output on the OpenOCD
console. Also, the program will <em>not</em> stop when the count reaches 9.</p>
<h2 id="the-default-exception-handler"><a class="header" href="#the-default-exception-handler">The default exception handler</a></h2>
<p>What the <code>exception</code> attribute actually does is <em>override</em> the default exception
handler for a specific exception. If you don't override the handler for a
particular exception it will be handled by the <code>DefaultHandler</code> function, which
defaults to:</p>
<pre><code class="language-rust ignore">fn DefaultHandler() {
loop {}
}</code></pre>
<p>This function is provided by the <code>cortex-m-rt</code> crate and marked as
<code>#[no_mangle]</code> so you can put a breakpoint on "DefaultHandler" and catch
<em>unhandled</em> exceptions.</p>
<p>It's possible to override this <code>DefaultHandler</code> using the <code>exception</code> attribute:</p>
<pre><code class="language-rust ignore">#[exception]
fn DefaultHandler(irqn: i16) {
// custom default handler
}</code></pre>
<p>The <code>irqn</code> argument indicates which exception is being serviced. A negative
value indicates that a Cortex-M exception is being serviced; and zero or a
positive value indicate that a device specific exception, AKA interrupt, is
being serviced.</p>
<h2 id="the-hard-fault-handler"><a class="header" href="#the-hard-fault-handler">The hard fault handler</a></h2>
<p>The <code>HardFault</code> exception is a bit special. This exception is fired when the
program enters an invalid state so its handler can <em>not</em> return as that could
result in undefined behavior. Also, the runtime crate does a bit of work before
the user defined <code>HardFault</code> handler is invoked to improve debuggability.</p>
<p>The result is that the <code>HardFault</code> handler must have the following signature:
<code>fn(&amp;ExceptionFrame) -&gt; !</code>. The argument of the handler is a pointer to
registers that were pushed into the stack by the exception. These registers are
a snapshot of the processor state at the moment the exception was triggered and
are useful to diagnose a hard fault.</p>
<p>Here's an example that performs an illegal operation: a read to a nonexistent
memory location.</p>
<blockquote>
<p><strong>NOTE</strong>: This program won't work, i.e. it won't crash, on QEMU because
<code>qemu-system-arm -machine lm3s6965evb</code> doesn't check memory loads and will
happily return <code>0 </code>on reads to invalid memory.</p>
</blockquote>
<pre><code class="language-rust ignore">#![no_main]
#![no_std]
use panic_halt as _;
use core::fmt::Write;
use core::ptr;
use cortex_m_rt::{entry, exception, ExceptionFrame};
use cortex_m_semihosting::hio;
#[entry]
fn main() -&gt; ! {
// read a nonexistent memory location
unsafe {
ptr::read_volatile(0x3FFF_0000 as *const u32);
}
loop {}
}
#[exception]
fn HardFault(ef: &amp;ExceptionFrame) -&gt; ! {
if let Ok(mut hstdout) = hio::hstdout() {
writeln!(hstdout, "{:#?}", ef).ok();
}
loop {}
}</code></pre>
<p>The <code>HardFault</code> handler prints the <code>ExceptionFrame</code> value. If you run this
you'll see something like this on the OpenOCD console.</p>
<pre><code class="language-text">$ openocd
(..)
ExceptionFrame {
r0: 0x3fff0000,
r1: 0x00000003,
r2: 0x080032e8,
r3: 0x00000000,
r12: 0x00000000,
lr: 0x080016df,
pc: 0x080016e2,
xpsr: 0x61000000,
}
</code></pre>
<p>The <code>pc</code> value is the value of the Program Counter at the time of the exception
and it points to the instruction that triggered the exception.</p>
<p>If you look at the disassembly of the program:</p>
<pre><code class="language-text">$ cargo objdump --bin app --release -- -d --no-show-raw-insn --print-imm-hex
(..)
ResetTrampoline:
8000942: movw r0, #0xfffe
8000946: movt r0, #0x3fff
800094a: ldr r0, [r0]
800094c: b #-0x4 &lt;ResetTrampoline+0xa&gt;
</code></pre>
<p>You can lookup the value of the program counter <code>0x0800094a</code> in the disassembly.
You'll see that a load operation (<code>ldr r0, [r0]</code> ) caused the exception.
The <code>r0</code> field of <code>ExceptionFrame</code> will tell you the value of register <code>r0</code>
was <code>0x3fff_fffe</code> at that time.</p>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../start/panicking.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../start/interrupts.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="../start/panicking.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../start/interrupts.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
</div>
<script>
window.playground_copyable = true;
</script>
<script src="../elasticlunr.min.js"></script>
<script src="../mark.min.js"></script>
<script src="../searcher.js"></script>
<script src="../clipboard.min.js"></script>
<script src="../highlight.js"></script>
<script src="../book.js"></script>
<!-- Custom JS scripts -->
</div>
</body>
</html>