23 Commits

Author SHA1 Message Date
Jorge Aparicio
805b63afb1 v0.2.0 2017-07-07 18:34:47 -05:00
Jorge Aparicio
6780d81e4d Merge pull request #13 from japaric/dev
Revert "Remove 'monitor tpiu itm port 0 on' from .gdbinit"
2017-06-17 17:21:12 -05:00
Jorge Aparicio
59a780d0c4 Revert "Remove 'monitor tpiu itm port 0 on' from .gdbinit"
This reverts commit f88a44fd78.

It's required on OpenOCD 0.10.0
2017-06-17 17:18:48 -05:00
Jorge Aparicio
c03bded663 Merge pull request #12 from adamgreig/patch-1
Remove 'monitor tpiu itm port 0 on' from .gdbinit
2017-06-10 12:25:02 -05:00
Adam Greig
f88a44fd78 Remove 'monitor tpiu itm port 0 on' from .gdbinit
This happens automatically when openocd sets up the tpiu; see openocd manual 16.5.4.
2017-06-09 22:22:53 +01:00
Jorge Aparicio
9c37db3d3b v0.1.8 2017-05-30 19:41:27 -05:00
Jorge Aparicio
797e750a32 Merge pull request #11 from japaric/stext
bump cortex-m-rt version to v0.2.3; document the _stext symbol
2017-05-30 19:40:01 -05:00
Jorge Aparicio
ea13292cc4 bump cortex-m-rt version to v0.2.3; document the _stext symbol 2017-05-30 19:36:10 -05:00
Jorge Aparicio
207591ef4c Merge pull request #10 from japaric/swo
.gdbinit: add a commented out option to make the SWO pin functional when ...
2017-05-28 22:55:54 -05:00
Jorge Aparicio
0b22a8aabb .gdbinit: add a commented out option to make the SWO pin functional when ...
it's not connected to a programmer / debugger SWO pin
2017-05-28 22:53:53 -05:00
Jorge Aparicio
adda589c71 Merge pull request #9 from japaric/no-reset
gdbinit: don't reset the microcontroller
2017-05-28 21:11:49 -05:00
Jorge Aparicio
d4c6bde00f gdbinit: don't reset the microcontroller
simply `step` after the `load` command. This should just work now that we are
using cortex-m-rt v0.2.2
2017-05-28 21:09:38 -05:00
Jorge Aparicio
96e0b4e96b v0.1.7 2017-05-27 11:14:16 -05:00
Jorge Aparicio
f5fca936c6 Merge pull request #8 from japaric/heap
document how to use the heap and a dynamic allocator
2017-05-27 11:11:44 -05:00
Jorge Aparicio
f1329524c8 document how to use the heap and a dynamic allocator 2017-05-27 11:00:03 -05:00
Jorge Aparicio
2bb6e419af v0.1.6 2017-05-26 15:19:24 -05:00
Jorge Aparicio
0154a9efc7 Merge pull request #7 from japaric/runner
set default runner to arm-none-eabi-gdb
2017-05-26 15:17:04 -05:00
Jorge Aparicio
c6fafaedc2 set default runner to arm-none-eabi-gdb
with this `xargo run` will build the program *and* immediately start a GDB
session.
2017-05-26 15:12:21 -05:00
Jorge Aparicio
82e36ffe13 v0.1.5 2017-05-16 10:25:30 -05:00
Jorge Aparicio
d035016e65 Merge pull request #6 from japaric/incr-comp
warn against using CARGO_INCREMENTAL
2017-05-15 22:01:09 -05:00
Jorge Aparicio
362c715b19 warn against using CARGO_INCREMENTAL 2017-05-15 22:00:36 -05:00
Jorge Aparicio
8aa495ac66 v0.1.4 2017-05-13 15:18:50 -05:00
Jorge Aparicio
62453e1e94 add dependencies section to documentation
add 'used stable toolchain' sub-section to the troubleshooting section
2017-05-13 15:17:14 -05:00
25 changed files with 671 additions and 216 deletions

View File

@@ -1,4 +1,5 @@
[target.thumbv6m-none-eabi]
runner = 'arm-none-eabi-gdb'
rustflags = [
"-C", "link-arg=-Tlink.x",
"-C", "linker=arm-none-eabi-ld",
@@ -6,6 +7,7 @@ rustflags = [
]
[target.thumbv7m-none-eabi]
runner = 'arm-none-eabi-gdb'
rustflags = [
"-C", "link-arg=-Tlink.x",
"-C", "linker=arm-none-eabi-ld",
@@ -13,6 +15,7 @@ rustflags = [
]
[target.thumbv7em-none-eabi]
runner = 'arm-none-eabi-gdb'
rustflags = [
"-C", "link-arg=-Tlink.x",
"-C", "linker=arm-none-eabi-ld",
@@ -20,6 +23,7 @@ rustflags = [
]
[target.thumbv7em-none-eabihf]
runner = 'arm-none-eabi-gdb'
rustflags = [
"-C", "link-arg=-Tlink.x",
"-C", "linker=arm-none-eabi-ld",

View File

@@ -1,9 +1,18 @@
target remote :3333
monitor arm semihosting enable
# if using ITM
# # send captured ITM to the file itm.fifo
# # (the microcontroller SWO pin must be connected to the programmer SWO pin)
# # 8000000 must match the core clock frequency
# monitor tpiu config internal itm.fifo uart off 8000000
# # OR: make the microcontroller SWO pin output compatible with UART (8N1)
# # 2000000 is the frequency of the SWO pin
# monitor tpiu config external uart off 8000000 2000000
# # enable ITM port 0
# monitor itm port 0 on
load
tbreak cortex_m_rt::reset_handler
monitor reset halt
continue
step

View File

@@ -5,7 +5,56 @@ This project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]
## v0.1.3 - 2017-05-12
## [v0.2.0] - 2017-07-07
### Changed
- [breaking-change] Bumped the cortex-m and cortex-m-rt versions to v0.3.0.
## [v0.1.8] - 2017-05-30
### Changed
- Bumped the cortex-m-rt dependency to v0.2.3, and documented the `_stext`
symbol (see memory.x).
## [v0.1.7] - 2017-05-27
### Added
- Documentation and an example about how to use the heap and a dynamic memory
allocator.
### Changed
- Bumped the `cortex-m-rt` dependency to v0.2.2
- Bumped the `cortex-m` dependency to v0.2.7
## [v0.1.6] - 2017-05-26
### Added
- Set the default runner in .cargo/config to `arm-none-eabi-gdb`. Now `xargo
run` will build the program and start a debug session.
## [v0.1.5] - 2017-05-16
### Added
- A warning about using CARGO_INCREMENTAL to the how to use and the
troubleshooting sections.
## [v0.1.4] - 2017-05-13
### Added
- A dependencies section to the documentation
### Changed
- Extend troubleshooting section
## [v0.1.3] - 2017-05-13
### Added
@@ -15,13 +64,13 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- Bumped the cortex-m crate version to v0.2.6
## v0.1.2 - 2017-05-07
## [v0.1.2] - 2017-05-07
### Fixed
- .gdbinit: jump to reset handler after loading the program.
## v0.1.1 - 2017-04-27
## [v0.1.1] - 2017-04-27
### Changed
@@ -33,7 +82,12 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- Initial release
[Unreleased]: https://github.com/japaric/cortex-m-quickstart/compare/v0.1.3...HEAD
[Unreleased]: https://github.com/japaric/cortex-m-quickstart/compare/v0.1.8...HEAD
[v0.1.8]: https://github.com/japaric/cortex-m-quickstart/compare/v0.1.7...v0.1.8
[v0.1.7]: https://github.com/japaric/cortex-m-quickstart/compare/v0.1.6...v0.1.7
[v0.1.6]: https://github.com/japaric/cortex-m-quickstart/compare/v0.1.5...v0.1.6
[v0.1.5]: https://github.com/japaric/cortex-m-quickstart/compare/v0.1.4...v0.1.5
[v0.1.4]: https://github.com/japaric/cortex-m-quickstart/compare/v0.1.3...v0.1.4
[v0.1.3]: https://github.com/japaric/cortex-m-quickstart/compare/v0.1.2...v0.1.3
[v0.1.2]: https://github.com/japaric/cortex-m-quickstart/compare/v0.1.1...v0.1.2
[v0.1.1]: https://github.com/japaric/cortex-m-quickstart/compare/v0.1.0...v0.1.1

View File

@@ -6,12 +6,16 @@ keywords = ["arm", "cortex-m", "template"]
license = "MIT OR Apache-2.0"
name = "cortex-m-quickstart"
repository = "https://github.com/japaric/cortex-m-quickstart"
version = "0.1.3"
version = "0.2.0"
[dependencies]
cortex-m = "0.2.6"
cortex-m-rt = "0.2.0"
cortex-m = "0.3.0"
cortex-m-semihosting = "0.2.0"
[dependencies.cortex-m-rt]
features = ["abort-on-panic"]
version = "0.3.2"
[profile.release]
lto = true
debug = true
lto = true

View File

@@ -1,6 +1,6 @@
[dependencies.core]
stage = 0
[dependencies.compiler_builtins]
features = ["mem"]
git = "https://github.com/rust-lang-nursery/compiler-builtins"
stage = 1

80
examples/allocator.rs Normal file
View File

@@ -0,0 +1,80 @@
//! How to use the heap and a dynamic memory allocator
//!
//! To compile this example you'll need to build the collections crate as part
//! of the Xargo sysroot. To do that change the Xargo.toml file to look like
//! this:
//!
//! ``` text
//! [dependencies.core]
//! stage = 0
//!
//! [dependencies.collections] # NEW
//! stage = 0
//!
//! [dependencies.compiler_builtins]
//! git = "https://github.com/rust-lang-nursery/compiler-builtins"
//! stage = 1
//! ```
//!
//! This example depends on the alloc-cortex-m crate so you'll have to add it
//! to your Cargo.toml:
//!
//! ``` text
//! # or edit the Cargo.toml file manually
//! $ cargo add alloc-cortex-m
//! ```
//!
//! ---
#[allow(deprecated)]
#![feature(collections)]
#![feature(used)]
#![no_std]
// This is the allocator crate; you can use a different one
extern crate alloc_cortex_m;
#[macro_use]
extern crate collections;
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
unsafe {
extern "C" {
// Start of the heap
static mut _sheap: usize;
}
// Size of the heap in words (1 word = 4 bytes)
// 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, _eheap);
}
// Growable array allocated on the heap
let xs = vec![0, 1, 2];
let mut stdout = hio::hstdout().unwrap();
writeln!(stdout, "{:?}", xs).unwrap();
}
// As we are not using interrupts, we just register a dummy catch all handler
#[link_section = ".vector_table.interrupts"]
#[used]
static INTERRUPTS: [extern "C" fn(); 240] = [default_handler; 240];
extern "C" fn default_handler() {
asm::bkpt();
}

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| {
interrupt::free(|cs| {
let itm = peripheral::ITM.borrow(&cs);
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
};

View File

@@ -9,8 +9,9 @@ main() {
itm
panic
crash
register-interrupt-handler
override-exception-handler
device
allocator
)
rm -rf src/examples

View File

@@ -8,5 +8,13 @@ MEMORY
/* This is where the call stack will be allocated. */
/* The stack is of the full descending type. */
/* NOTE Do NOT modify `_stack_start` unless you know what you are doing */
_stack_start = ORIGIN(RAM) + LENGTH(RAM);
/* You may want to use this variable to locate the call stack and static
variables in different memory regions. Below is shown the default value */
/* _stack_start = ORIGIN(RAM) + LENGTH(RAM); */
/* You can use this symbol to customize the location of the .text section */
/* If omitted the .text section will be placed right after the .vector_table
section */
/* This is required only on microcontrollers that store some configuration right
after the vector table */
/* _stext = ORIGIN(FLASH) + 0x400; */

View File

@@ -1,24 +1,29 @@
//! 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,10 +7,12 @@
//! 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)]
@@ -23,19 +25,16 @@
//! use cortex_m::{asm, interrupt, peripheral};
//!
//! fn main() {
//! interrupt::free(
//! |cs| {
//! interrupt::free(|cs| {
//! let itm = peripheral::ITM.borrow(&cs);
//!
//! 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,35 +1,58 @@
//! 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

@@ -8,28 +8,56 @@
//! 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) print _e
//! (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)]
@@ -50,9 +78,8 @@
//! }
//!
//! // 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,7 +1,15 @@
//! 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
//!
//! ---
//!
//! ```
//!
@@ -9,11 +17,12 @@
//! #![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 {
@@ -22,25 +31,17 @@
//! }
//! }
//!
//! 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,40 +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
//! };
//! ```
// Auto-generated. Do not modify.

62
src/examples/_5_device.rs Normal file
View File

@@ -0,0 +1,62 @@
//! 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;
//! // ..
//! }
//! ```
// Auto-generated. Do not modify.

View File

@@ -0,0 +1,84 @@
//! How to use the heap and a dynamic memory allocator
//!
//! To compile this example you'll need to build the collections crate as part
//! of the Xargo sysroot. To do that change the Xargo.toml file to look like
//! this:
//!
//! ``` text
//! [dependencies.core]
//! stage = 0
//!
//! [dependencies.collections] # NEW
//! stage = 0
//!
//! [dependencies.compiler_builtins]
//! git = "https://github.com/rust-lang-nursery/compiler-builtins"
//! stage = 1
//! ```
//!
//! This example depends on the alloc-cortex-m crate so you'll have to add it
//! to your Cargo.toml:
//!
//! ``` text
//! # or edit the Cargo.toml file manually
//! $ cargo add alloc-cortex-m
//! ```
//!
//! ---
//!
//! ```
//!
//! #[allow(deprecated)]
//! #![feature(collections)]
//! #![feature(used)]
//! #![no_std]
//!
//! // This is the allocator crate; you can use a different one
//! extern crate alloc_cortex_m;
//! #[macro_use]
//! extern crate collections;
//! 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
//! unsafe {
//! extern "C" {
//! // Start of the heap
//! static mut _sheap: usize;
//! }
//!
//! // Size of the heap in words (1 word = 4 bytes)
//! // 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, _eheap);
//! }
//!
//! // Growable array allocated on the heap
//! let xs = vec![0, 1, 2];
//!
//! let mut stdout = hio::hstdout().unwrap();
//! writeln!(stdout, "{:?}", xs).unwrap();
//! }
//!
//! // As we are not using interrupts, we just register a dummy catch all handler
//! #[link_section = ".vector_table.interrupts"]
//! #[used]
//! static INTERRUPTS: [extern "C" fn(); 240] = [default_handler; 240];
//!
//! extern "C" fn default_handler() {
//! asm::bkpt();
//! }
//! ```
// Auto-generated. Do not modify.

View File

@@ -4,5 +4,6 @@ pub mod _0_hello;
pub mod _1_itm;
pub mod _2_panic;
pub mod _3_crash;
pub mod _4_register_interrupt_handler;
pub mod _5_override_exception_handler;
pub mod _4_override_exception_handler;
pub mod _5_device;
pub mod _6_allocator;

View File

@@ -1,5 +1,15 @@
//! A template for building applications for ARM Cortex-M microcontrollers
//!
//! # Dependencies
//!
//! - Nightly Rust toolchain: `rustup default nightly`
//! - ARM linker: `sudo apt-get install binutils-arm-none-eabi`
//! - Cargo `clone` subcommand: `cargo install cargo-clone`
//! - GDB: `sudo apt-get install gdb-arm-none-eabi`
//! - OpenOCD: `sudo apt-get install OpenOCD`
//! - Xargo: `cargo install xargo`
//! - [Optional] Cargo `add` subcommand: `cargo install cargo-edit`
//!
//! # Usage
//!
//! - Clone this crate
@@ -32,11 +42,6 @@
//! FLASH : ORIGIN = 0x08000000, LENGTH = 256K
//! RAM : ORIGIN = 0x20000000, LENGTH = 40K
//! }
//!
//! /* This is where the call stack will be allocated. */
//! /* The stack is of the full descending type. */
//! /* NOTE Do NOT modify `_stack_start` unless you know what you are doing */
//! _stack_start = ORIGIN(RAM) + LENGTH(RAM);
//! ```
//!
//! - Optionally, set a default build target
@@ -52,10 +57,10 @@
//!
//! ``` text
//! # add a device crate, or
//! $ cargo add stm32f30x
//! $ cargo add stm32f103xx
//!
//! # add a board support crate
//! $ cargo add f3
//! $ cargo add blue-pill --git https://github.com/japaric/blue-pill
//! ```
//!
//! - Write the application or start from one of the examples
@@ -64,12 +69,16 @@
//! $ rm -r src/* && cp examples/hello.rs src/main.rs
//! ```
//!
//! - Disable incremental compilation. It doesn't work for embedded development.
//! You'll hit nonsensical linker errors if you use it.
//!
//! ``` text
//! $ unset CARGO_INCREMENTAL
//! ```
//!
//! - Build the application
//!
//! ``` text
//! # if not installed
//! $ cargo install xargo
//!
//! # NOTE this command requires `arm-none-eabi-ld` to be in $PATH
//! $ xargo build --release
//!
@@ -97,16 +106,26 @@
//!
//! - Flash the program
//!
//! ```
//! ``` text
//! # Launch OpenOCD on a terminal
//! $ openocd -f (..)
//! ```
//!
//! ```
//! # Start debug session
//! ``` text
//! # Start a debug session in another terminal
//! $ arm-none-eabi-gdb target/..
//! ```
//!
//! **NOTE** As of nightly-2017-05-14 or so and cortex-m-quickstart v0.1.6 you
//! can simply run `cargo run` or `cargo run --example $example` to build the
//! program, and immediately start a debug session. IOW, it lets you omit the
//! `arm-none-eabi-gdb` command.
//!
//! ``` text
//! $ cargo run --example hello
//! > # drops you into a GDB session
//! ```
//!
//! # Examples
//!
//! Check the [examples module](./examples/index.html)
@@ -201,6 +220,43 @@
//!
//! Solution: Use `xargo build`.
//!
//! ## Used the stable toolchain
//!
//! Error message:
//!
//! ``` text
//! $ xargo build
//! error: failed to run `rustc` to learn about target-specific information
//!
//! To learn more, run the command again with --verbose.
//! ```
//!
//! Solution: Switch to the nightly toolchain with `rustup default nightly`.
//!
//! ## Used `CARGO_INCREMENTAL=1`
//!
//! Error message:
//!
//! ``` text
//! $ xargo build
//! error: linking with `arm-none-eabi-ld` failed: exit code: 1
//! |
//! = note: "arm-none-eabi-ld" (..)
//! = note: arm-none-eabi-ld:
//! You must specify the exception handlers.
//! Create a non `pub` static variable with type
//! `cortex_m::exception::Handlers` and place it in the
//! '.rodata.exceptions' section. (cf. #[link_section]). Apply the
//! `#[used]` attribute to the variable to make it reach the linker.
//! arm-none-eabi-ld:
//! Invalid '.rodata.exceptions' section.
//! Make sure to place a static with type `cortex_m::exception::Handlers`
//! in that section (cf. #[link_section]) ONLY ONCE.
//! ```
//!
//! Solution: `$ unset CARGO_INCREMENAL`. And to be on the safe side, call
//! `cargo clean` and thrash the Xargo sysroot: `$ rm -rf ~/.xargo`
//!
//! ## Used `gdb` instead of `arm-none-eabi-gdb`
//!
//! Error message: