This commit is contained in:
Jorge Aparicio
2017-07-07 18:34:47 -05:00
parent 6780d81e4d
commit 805b63afb1
23 changed files with 400 additions and 222 deletions

View File

@@ -6,10 +6,12 @@
//!
//! ``` text
//! [dependencies.core]
//! [dependencies.collections] # new
//! stage = 0
//!
//! [dependencies.collections] # NEW
//! stage = 0
//!
//! [dependencies.compiler_builtins]
//! features = ["mem"]
//! git = "https://github.com/rust-lang-nursery/compiler-builtins"
//! stage = 1
//! ```
@@ -21,7 +23,10 @@
//! # or edit the Cargo.toml file manually
//! $ cargo add alloc-cortex-m
//! ```
//!
//! ---
#[allow(deprecated)]
#![feature(collections)]
#![feature(used)]
#![no_std]
@@ -30,11 +35,14 @@
extern crate alloc_cortex_m;
#[macro_use]
extern crate collections;
#[macro_use]
extern crate cortex_m;
extern crate cortex_m_rt;
extern crate cortex_m_semihosting;
use core::fmt::Write;
use cortex_m::asm;
use cortex_m_semihosting::hio;
fn main() {
// Initialize the allocator
@@ -45,25 +53,26 @@ fn main() {
}
// Size of the heap in words (1 word = 4 bytes)
// WARNING: The bigger the heap the greater the chance to run into a
// stack overflow (collision between the stack and the heap)
// NOTE The bigger the heap the greater the chance to run into a stack
// overflow (collision between the stack and the heap)
const SIZE: isize = 256;
// End of the heap
let _eheap = (&mut _sheap as *mut _).offset(SIZE);
alloc_cortex_m::init(&mut _sheap as *mut _, _eheap);
alloc_cortex_m::init(&mut _sheap, _eheap);
}
// Growable array allocated on the heap
let xs = vec![0, 1, 2];
hprintln!("{:?}", xs);
let mut stdout = hio::hstdout().unwrap();
writeln!(stdout, "{:?}", xs).unwrap();
}
// As we are not using interrupts, we just register a dummy catch all handler
#[allow(dead_code)]
#[link_section = ".vector_table.interrupts"]
#[used]
#[link_section = ".rodata.interrupts"]
static INTERRUPTS: [extern "C" fn(); 240] = [default_handler; 240];
extern "C" fn default_handler() {

View File

@@ -8,27 +8,55 @@
//! In you run the example below, you'll be able to inspect the state of your
//! program under the debugger using these commands:
//!
//! ```
//! (gdb) # Stacked registers = program state during the crash
//! (gdb) print/x *_sr
//! $1 = cortex_m::exception::StackedRegisters {
//! ``` text
//! (gdb) # Exception frame = program state during the crash
//! (gdb) print/x *ef
//! $1 = cortex_m::exception::ExceptionFrame {
//! r0 = 0x2fffffff,
//! r1 = 0x2fffffff,
//! r2 = 0x0,
//! r3 = 0x0,
//! r12 = 0x0,
//! lr = 0x8000443,
//! pc = 0x8000190,
//! xpsr = 0x61000200,
//! lr = 0x8000481,
//! pc = 0x8000460,
//! xpsr = 0x61000000,
//! }
//!
//! (gdb) # What exception was triggered?
//! (gdb) print _e
//! $2 = cortex_m::exception::Exception::HardFault
//!
//! (gdb) # Where did we come from?
//! (gdb) backtrace
//! #0 cortex_m_rt::default_handler (ef=0x20004f54) at (..)
//! #1 <signal handler called>
//! #2 0x08000460 in core::ptr::read_volatile<u32> (src=0x2fffffff) at (..)
//! #3 0x08000480 in crash::main () at examples/crash.rs:68
//!
//! (gdb) # Nail down the location of the crash
//! (gdb) disassemble/m ef.pc
//! Dump of assembler code for function core::ptr::read_volatile<u32>:
//! 408 pub unsafe fn read_volatile<T>(src: *const T) -> T {
//! 0x08000454 <+0>: sub sp, #20
//! 0x08000456 <+2>: mov r1, r0
//! 0x08000458 <+4>: str r0, [sp, #8]
//! 0x0800045a <+6>: ldr r0, [sp, #8]
//! 0x0800045c <+8>: str r0, [sp, #12]
//!
//! 409 intrinsics::volatile_load(src)
//! 0x0800045e <+10>: ldr r0, [sp, #12]
//! 0x08000460 <+12>: ldr r0, [r0, #0]
//! 0x08000462 <+14>: str r0, [sp, #16]
//! 0x08000464 <+16>: ldr r0, [sp, #16]
//! 0x08000466 <+18>: str r1, [sp, #4]
//! 0x08000468 <+20>: str r0, [sp, #0]
//! 0x0800046a <+22>: b.n 0x800046c <core::ptr::read_volatile<u32>+24>
//!
//! 410 }
//! 0x0800046c <+24>: ldr r0, [sp, #0]
//! 0x0800046e <+26>: add sp, #20
//! 0x08000470 <+28>: bx lr
//!
//! End of assembler dump.
//! ```
//!
//! ---
#![feature(used)]
#![no_std]
@@ -48,9 +76,8 @@ fn main() {
}
// As we are not using interrupts, we just register a dummy catch all handler
#[allow(dead_code)]
#[link_section = ".vector_table.interrupts"]
#[used]
#[link_section = ".rodata.interrupts"]
static INTERRUPTS: [extern "C" fn(); 240] = [default_handler; 240];
extern "C" fn default_handler() {

58
examples/device.rs Normal file
View File

@@ -0,0 +1,58 @@
//! Using a device crate
//!
//! Crates generated using [`svd2rust`] are referred to as device crates. These
//! crates provides an API to access the peripherals of a device. When you
//! depend on one of these crates and the "rt" feature is enabled you don't need
//! link to the cortex-m-rt crate.
//!
//! [`svd2rust`]: https://crates.io/crates/svd2rust
//!
//! Device crates also provide an `interrupt!` macro to register interrupt
//! handlers.
//!
//! This example depends on the [`stm32f103xx`] crate so you'll have to add it
//! to your Cargo.toml.
//!
//! [`stm32f103xx`]: https://crates.io/crates/stm32f103xx
//!
//! ```
//! $ edit Cargo.toml && cat $_
//! [dependencies.stm32f103xx]
//! features = ["rt"]
//! version = "0.7.0"
//! ```
//!
//! ---
#![no_std]
extern crate cortex_m;
#[macro_use(exception, interrupt)]
extern crate stm32f103xx;
use cortex_m::interrupt;
fn main() {
interrupt::free(|cs| {
let _gpioa = stm32f103xx::GPIOA.borrow(cs);
// do something with GPIOA
});
}
exception!(SYS_TICK, tick, locals: {
ticks: u32 = 0;
});
fn tick(l: &mut SYS_TICK::Locals) {
l.ticks += 1;
// ..
}
interrupt!(TIM2, tock, locals: {
tocks: u32 = 0;
});
fn tock(l: &mut TIM2::Locals) {
l.tocks += 1;
// ..
}

View File

@@ -1,22 +1,27 @@
//! Prints "Hello, world!" on the OpenOCD console using semihosting
//!
//! ---
#![feature(used)]
#![no_std]
#[macro_use]
extern crate cortex_m;
extern crate cortex_m_rt;
extern crate cortex_m_semihosting;
use core::fmt::Write;
use cortex_m::asm;
use cortex_m_semihosting::hio;
fn main() {
hprintln!("Hello, world!");
let mut stdout = hio::hstdout().unwrap();
writeln!(stdout, "Hello, world!").unwrap();
}
// As we are not using interrupts, we just register a dummy catch all handler
#[allow(dead_code)]
#[link_section = ".vector_table.interrupts"]
#[used]
#[link_section = ".rodata.interrupts"]
static INTERRUPTS: [extern "C" fn(); 240] = [default_handler; 240];
extern "C" fn default_handler() {

View File

@@ -7,9 +7,11 @@
//! ITM is much faster than semihosting. Like 4 orders of magnitude or so.
//!
//! You'll need [`itmdump`] to receive the message on the host plus you'll need
//! to uncomment OpenOCD's ITM support in `.gdbinit`.
//! to uncomment the `monitor` commands in the `.gdbinit` file.
//!
//! [`itmdump`]: https://docs.rs/itm/0.1.1/itm/
//!
//! ---
#![feature(used)]
#![no_std]
@@ -21,19 +23,16 @@ extern crate cortex_m_rt;
use cortex_m::{asm, interrupt, peripheral};
fn main() {
interrupt::free(
|cs| {
let itm = peripheral::ITM.borrow(&cs);
interrupt::free(|cs| {
let itm = peripheral::ITM.borrow(&cs);
iprintln!(&itm.stim[0], "Hello, world!");
},
);
iprintln!(&itm.stim[0], "Hello, world!");
});
}
// As we are not using interrupts, we just register a dummy catch all handler
#[allow(dead_code)]
#[link_section = ".vector_table.interrupts"]
#[used]
#[link_section = ".rodata.interrupts"]
static INTERRUPTS: [extern "C" fn(); 240] = [default_handler; 240];
extern "C" fn default_handler() {

View File

@@ -1,17 +1,26 @@
//! Overriding an exception
//! Overriding an exception handler
//!
//! **NOTE** You have to disable the `cortex-m-rt` crate's "exceptions" feature
//! to make this work.
//! You can override an exception handler using the [`exception!`][1] macro.
//!
//! [1]: https://docs.rs/cortex-m-rt/0.3.2/cortex_m_rt/macro.exception.html
//!
//! The default exception handler can be overridden using the
//! [`default_handler!`][2] macro
//!
//! [2]: https://docs.rs/cortex-m-rt/0.3.2/cortex_m_rt/macro.default_handler.html
//!
//! ---
#![feature(used)]
#![no_std]
extern crate cortex_m;
#[macro_use(exception)]
extern crate cortex_m_rt;
use core::ptr;
use cortex_m::{asm, exception};
use cortex_m::asm;
fn main() {
unsafe {
@@ -20,25 +29,17 @@ fn main() {
}
}
extern "C" fn hard_fault(_: exception::HardFault) {
exception!(HARD_FAULT, handler);
fn handler() {
// You'll hit this breakpoint rather than the one in cortex-m-rt
asm::bkpt()
}
// When the "exceptions" feature is disabled, you'll have to provide this symbol
#[allow(dead_code)]
#[used]
#[link_section = ".rodata.exceptions"]
static EXCEPTIONS: exception::Handlers = exception::Handlers {
// This is the exception handler override
hard_fault: hard_fault,
..exception::DEFAULT_HANDLERS
};
// As we are not using interrupts, we just register a dummy catch all handler
#[allow(dead_code)]
#[used]
#[link_section = ".rodata.interrupts"]
#[link_section = ".vector_table.interrupts"]
static INTERRUPTS: [extern "C" fn(); 240] = [default_handler; 240];
extern "C" fn default_handler() {

View File

@@ -1,33 +1,56 @@
//! Redirecting `panic!` messages
//! Defining the panic handler
//!
//! The `cortex-m-rt` crate provides two options to redirect `panic!` messages
//! through these two Cargo features:
//! The panic handler can be defined through the `panic_fmt` [language item][1].
//! Make sure that the "abort-on-panic" feature of the cortex-m-rt crate is
//! disabled to avoid redefining the language item.
//!
//! - `panic-over-semihosting`. `panic!` messages will be printed to the OpenOCD
//! console using semihosting. This is slow.
//! [1]: https://doc.rust-lang.org/unstable-book/language-features/lang-items.html
//!
//! - `panic-over-itm`. `panic!` messages will be send through the ITM port 0.
//! This is much faster but requires ITM support on the device.
//!
//! If neither of these options is specified then the `panic!` message will be
//! lost. Note that all `panic!`s will trigger a debugger breakpoint.
//! ---
#![feature(core_intrinsics)]
#![feature(lang_items)]
#![feature(used)]
#![no_std]
extern crate cortex_m;
extern crate cortex_m_rt;
extern crate cortex_m_semihosting;
use core::fmt::Write;
use core::intrinsics;
use cortex_m::asm;
use cortex_m_semihosting::hio;
fn main() {
panic!("Oops");
}
#[lang = "panic_fmt"]
#[no_mangle]
unsafe extern "C" fn rust_begin_unwind(
args: core::fmt::Arguments,
file: &'static str,
line: u32,
col: u32,
) -> ! {
if let Ok(mut stdout) = hio::hstdout() {
write!(stdout, "panicked at '")
.and_then(|_| {
stdout
.write_fmt(args)
.and_then(|_| writeln!(stdout, "', {}:{}", file, line))
})
.ok();
}
intrinsics::abort()
}
// As we are not using interrupts, we just register a dummy catch all handler
#[allow(dead_code)]
#[link_section = ".vector_table.interrupts"]
#[used]
#[link_section = ".rodata.interrupts"]
static INTERRUPTS: [extern "C" fn(); 240] = [default_handler; 240];
extern "C" fn default_handler() {

View File

@@ -1,36 +0,0 @@
//! Register an interrupt handler
//!
//! NOTE Requires a device crate generated using `svd2rust`
#![feature(used)]
#![no_std]
extern crate cortex_m;
extern crate cortex_m_rt;
// NOTE this is the device crate
extern crate stm32f30x;
use cortex_m::asm;
use stm32f30x::interrupt;
fn main() {}
// NOTE each interrupt handler has a different signature
extern "C" fn my_interrupt_handler(_ctxt: interrupt::Tim7) {
asm::bkpt();
}
extern "C" fn another_interrupt_handler(_ctxt: interrupt::Exti0) {
asm::bkpt();
}
// Here we override only two interrupt handlers, the rest of interrupt are
// handled by the same interrupt handler
#[allow(dead_code)]
#[used]
#[link_section = ".rodata.interrupts"]
static INTERRUPTS: interrupt::Handlers = interrupt::Handlers {
Tim7: my_interrupt_handler,
Exti0: another_interrupt_handler,
..interrupt::DEFAULT_HANDLERS
};