Commit 440a08f0 authored by Per Lindgren's avatar Per Lindgren

initial commit

parents
Pipeline #74 failed with stages
[target.thumbv7m-none-eabi]
# uncomment this to make `cargo run` execute programs on QEMU
# runner = "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel"
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
# uncomment ONE of these three option to make `cargo run` start a GDB session
# which option to pick depends on your system
# runner = "arm-none-eabi-gdb -q -x openocd.gdb"
# runner = "gdb-multiarch -q -x openocd.gdb"
# runner = "gdb -q -x openocd.gdb"
rustflags = [
# LLD (shipped with the Rust toolchain) is used as the default linker
"-C", "link-arg=-Tlink.x",
# removes hash for output file(s), seems dangerous
# "-C", "extra-filename=",
# "-C", "link-arg=-Tkeep-stack-sizes.x",
# "--emit", "llvm-ir,mir",
# "-Z", "emit-stack-sizes"
# if you run into problems with LLD switch to the GNU linker by commenting out
# this line
# "-C", "linker=arm-none-eabi-ld",
# if you need to link to pre-compiled C libraries provided by a C toolchain
# use GCC as the linker by commenting out both lines above and then
# uncommenting the three lines below
# "-C", "linker=arm-none-eabi-gcc",
# "-C", "link-arg=-Wl,-Tlink.x",
# "-C", "link-arg=-nostartfiles",
]
[build]
# Pick ONE of these compilation targets
# target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+
target = "thumbv7m-none-eabi" # Cortex-M3
# target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU)
# target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
[package]
authors = ["Per Lindgren <per.lindgren@ltu.se>"]
edition = "2018"
readme = "README.md"
name = "call-stack-test"
version = "0.1.0"
[dependencies]
cortex-m = "0.5.7"
cortex-m-rt = "0.6.3"
cortex-m-semihosting = "0.3.1"
panic-halt = "0.2.0"
# Uncomment for the panic example.
# panic-itm = "0.4.0"
# Uncomment for the allocator example.
# alloc-cortex-m = "0.3.5"
# Uncomment for the device example.
# [dependencies.stm32f30x]
# features = ["rt"]
# version = "0.7.1"
# this lets you use `cargo fix`!
[[bin]]
name = "call-stack-test"
test = false
bench = false
[profile.release]
codegen-units = 1 # better optimizations
# debug = true # symbols are nice and they don't increase the size on Flash
lto = true # better optimizations
//! Debugging a crash (exception)
//!
//! Most crash conditions trigger a hard fault exception, whose handler is defined via
//! `exception!(HardFault, ..)`. The `HardFault` handler has access to the exception frame, a
//! snapshot of the CPU registers at the moment of the exception.
//!
//! This program crashes and the `HardFault` handler prints to the console the contents of the
//! `ExceptionFrame` and then triggers a breakpoint. From that breakpoint one can see the backtrace
//! that led to the exception.
//!
//! ``` text
//! (gdb) continue
//! Program received signal SIGTRAP, Trace/breakpoint trap.
//! __bkpt () at asm/bkpt.s:3
//! 3 bkpt
//!
//! (gdb) backtrace
//! #0 __bkpt () at asm/bkpt.s:3
//! #1 0x080030b4 in cortex_m::asm::bkpt () at $$/cortex-m-0.5.0/src/asm.rs:19
//! #2 rust_begin_unwind (args=..., file=..., line=99, col=5) at $$/panic-semihosting-0.2.0/src/lib.rs:87
//! #3 0x08001d06 in core::panicking::panic_fmt () at libcore/panicking.rs:71
//! #4 0x080004a6 in crash::hard_fault (ef=0x20004fa0) at examples/crash.rs:99
//! #5 0x08000548 in UserHardFault (ef=0x20004fa0) at <exception macros>:10
//! #6 0x0800093a in HardFault () at asm.s:5
//! Backtrace stopped: previous frame identical to this frame (corrupt stack?)
//! ```
//!
//! In the console output one will find the state of the Program Counter (PC) register at the time
//! of the exception.
//!
//! ``` text
//! panicked at 'HardFault at ExceptionFrame {
//! r0: 0x2fffffff,
//! r1: 0x2fffffff,
//! r2: 0x080051d4,
//! r3: 0x080051d4,
//! r12: 0x20000000,
//! lr: 0x08000435,
//! pc: 0x08000ab6,
//! xpsr: 0x61000000
//! }', examples/crash.rs:106:5
//! ```
//!
//! This register contains the address of the instruction that caused the exception. In GDB one can
//! disassemble the program around this address to observe the instruction that caused the
//! exception.
//!
//! ``` text
//! (gdb) disassemble/m 0x08000ab6
//! Dump of assembler code for function core::ptr::read_volatile:
//! 451 pub unsafe fn read_volatile<T>(src: *const T) -> T {
//! 0x08000aae <+0>: sub sp, #16
//! 0x08000ab0 <+2>: mov r1, r0
//! 0x08000ab2 <+4>: str r0, [sp, #8]
//!
//! 452 intrinsics::volatile_load(src)
//! 0x08000ab4 <+6>: ldr r0, [sp, #8]
//! -> 0x08000ab6 <+8>: ldr r0, [r0, #0]
//! 0x08000ab8 <+10>: str r0, [sp, #12]
//! 0x08000aba <+12>: ldr r0, [sp, #12]
//! 0x08000abc <+14>: str r1, [sp, #4]
//! 0x08000abe <+16>: str r0, [sp, #0]
//! 0x08000ac0 <+18>: b.n 0x8000ac2 <core::ptr::read_volatile+20>
//!
//! 453 }
//! 0x08000ac2 <+20>: ldr r0, [sp, #0]
//! 0x08000ac4 <+22>: add sp, #16
//! 0x08000ac6 <+24>: bx lr
//!
//! End of assembler dump.
//! ```
//!
//! `ldr r0, [r0, #0]` caused the exception. This instruction tried to load (read) a 32-bit word
//! from the address stored in the register `r0`. Looking again at the contents of `ExceptionFrame`
//! we see that the `r0` contained the address `0x2FFF_FFFF` when this instruction was executed.
//!
//! ---
#![no_main]
#![no_std]
extern crate panic_halt;
use core::ptr;
use cortex_m_rt::entry;
#[entry]
fn main() -> ! {
unsafe {
// read an address outside of the RAM region; this causes a HardFault exception
ptr::read_volatile(0x2FFF_FFFF as *const u32);
}
loop {}
}
//! Using a device crate
//!
//! Crates generated using [`svd2rust`] are referred to as device crates. These crates provide an
//! API to access the peripherals of a device.
//!
//! [`svd2rust`]: https://crates.io/crates/svd2rust
//!
//! Device crates also provide an `interrupt!` macro (behind the "rt" feature) 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 && tail $_
//! [dependencies.stm32f103xx]
//! features = ["rt"]
//! version = "0.10.0"
//! ```
//!
//! ---
#![no_main]
#![no_std]
#[allow(unused_extern_crates)]
extern crate panic_halt;
use core::fmt::Write;
use cortex_m::peripheral::syst::SystClkSource;
use cortex_m_rt::entry;
use cortex_m_semihosting::hio::{self, HStdout};
use stm32f30x::{interrupt, Interrupt};
#[entry]
fn main() -> ! {
let p = cortex_m::Peripherals::take().unwrap();
let mut syst = p.SYST;
let mut nvic = p.NVIC;
nvic.enable(Interrupt::EXTI0);
// configure the system timer to wrap around every second
syst.set_clock_source(SystClkSource::Core);
syst.set_reload(8_000_000); // 1s
syst.enable_counter();
loop {
// busy wait until the timer wraps around
while !syst.has_wrapped() {}
// trigger the `EXTI0` interrupt
nvic.set_pending(Interrupt::EXTI0);
}
}
// try commenting out this line: you'll end in `default_handler` instead of in `exti0`
interrupt!(EXTI0, exti0, state: Option<HStdout> = None);
fn exti0(state: &mut Option<HStdout>) {
if state.is_none() {
*state = Some(hio::hstdout().unwrap());
}
if let Some(hstdout) = state.as_mut() {
hstdout.write_str(".").unwrap();
}
}
//! 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
//!
//! ---
#![deny(unsafe_code)]
#![no_main]
#![no_std]
extern crate panic_halt;
use core::fmt::Write;
use cortex_m::peripheral::syst::SystClkSource;
use cortex_m::Peripherals;
use cortex_m_rt::{entry, exception};
use cortex_m_semihosting::hio::{self, HStdout};
#[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(8_000_000); // period = 1s
syst.enable_counter();
syst.enable_interrupt();
loop {}
}
#[exception]
fn SysTick() {
static mut STDOUT: Option<HStdout> = None;
if STDOUT.is_none() {
*STDOUT = Some(hio::hstdout().unwrap());
}
if let Some(hstdout) = STDOUT.as_mut() {
hstdout.write_str(".").unwrap();
}
}
//! test of stack-sizes
#![no_main]
#![no_std]
extern crate panic_halt;
use cortex_m_rt::{entry, exception};
#[inline(never)]
fn t3() {
let t = [0, 1, 2];
unsafe { core::ptr::read_volatile(&t) };
}
#[inline(never)]
fn t2() {
let t = [0, 1];
unsafe { core::ptr::read_volatile(&t) };
t3();
}
#[inline(never)]
fn t() {
let t = [0, 1];
unsafe { core::ptr::read_volatile(&t) };
t2();
}
#[entry]
#[inline(never)]
fn main() -> ! {
t();
loop {}
}
#[exception]
fn SysTick() {
t2();
}
// cargo stack-sizes --example hello1 --release -- --emit=llvm-ir
//! test of stack-sizes
#![no_main]
#![no_std]
extern crate panic_halt;
use cortex_m_rt::entry;
#[inline(never)]
fn t() {
let t = [0, 1];
unsafe { core::ptr::read_volatile(&t) };
}
#[entry]
#[inline(never)]
fn main() -> ! {
let f = t;
f();
loop {}
}
// cargo stack-sizes --example hello2 --release -- --emit=llvm-ir
//! test of stack-sizes
#![no_main]
#![no_std]
extern crate panic_halt;
use cortex_m_rt::entry;
#[inline(never)]
fn t() {
let t = [0, 1];
unsafe { core::ptr::read_volatile(&t) };
}
#[inline(never)]
fn dispatcher(f: &Fn() -> ()) {
let t = [0, 1];
unsafe { core::ptr::read_volatile(&t) };
f();
}
#[entry]
fn main() -> ! {
dispatcher(&t);
loop {}
}
// cargo stack-sizes --example hello3 --release -- --emit=llvm-ir
//! test of stack-sizes
#![no_main]
#![no_std]
extern crate panic_halt;
use cortex_m_rt::entry;
trait Call {
fn call(&self) -> ();
}
struct T {}
impl Call for T {
#[inline(never)]
fn call(&self) {
let t = [0, 1];
unsafe { core::ptr::read_volatile(&t) };
}
}
#[entry]
fn main() -> ! {
let t = T {};
t.call();
loop {}
}
// cargo stack-sizes --example hello4 --release -- --emit=llvm-ir
//! test of stack-sizes
#![no_main]
#![no_std]
extern crate panic_halt;
use cortex_m_rt::{entry, exception};
trait Call {
fn call(&self, v: u32) -> ();
}
struct Call1 {}
impl Call for Call1 {
#[inline(never)]
fn call(&self, _v: u32) {
let t = [0, 1];
unsafe { core::ptr::read_volatile(&t) };
}
}
struct Call2 {}
impl Call for Call2 {
#[inline(never)]
fn call(&self, _v: u32) {
let t = [0, 1, 2];
unsafe { core::ptr::read_volatile(&t) };
}
}
static mut XX: &Call = &Call1 {};
#[entry]
#[inline(never)]
fn main() -> ! {
unsafe { XX.call(17) };
//let t = [0];
//unsafe { core::ptr::read_volatile(&t) };
loop {}
}
#[exception]
fn SysTick() {
unsafe {
XX = &Call2 {};
}
}
// cargo stack-sizes --example hello5 --release -- --emit=llvm-ir
//! test of stack-sizes
#![no_main]
#![no_std]
extern crate panic_halt;
use cortex_m_rt::{entry, exception};
#[inline(never)]
fn t3() {
let t = [0, 1, 2];
unsafe { core::ptr::read_volatile(&t) };
t1();
}
#[inline(never)]
fn t2() {
let t = [0, 1];
unsafe { core::ptr::read_volatile(&t) };
t3();
}
#[inline(never)]
fn t1() {
let t = [0, 1];
unsafe { core::ptr::read_volatile(&t) };
t2();
}
#[entry]
fn main() -> ! {
t1();
loop {}
}
#[exception]
fn SysTick() {
t2();
}
// cargo rustc --example hello6 --release -- -Z emit-stack-sizes --emit=llvm-ir
// or alternatively:
// cargo stack-sizes --example hello6 --release -- --emit=llvm-ir
// Compiling stack-test v0.1.0 (/home/pln/rust/stack-test)
// Finished release [optimized + debuginfo] target(s) in 0.20s
// address stack name
// 0x08000400 24 hello6::t3::hcf751622ef94577d
// 0x0800041e 16 hello6::t2::ha104632460530949
// 0x08000436 16 hello6::t1::h6a791942fd02a632
// 0x0800044e 0 main
// 0x08000454 0 SysTick
// 0x08000458 0 Reset
// 0x080006aa 0 UserHardFault_
// 0x080006ac 0 DefaultHandler_
// 0x080006ae 0 DefaultPreInit
//! test of stack-sizes
#![no_main]
#![no_std]
extern crate panic_halt;
use cortex_m_rt::{entry, exception};
trait Call {
fn call(&self) -> ();
}
struct T {}
impl Call for T {
#[inline(never)]
fn call(&self) {
let t = [0, 1];
unsafe { core::ptr::read_volatile(&t) };
}
}
struct U {}
impl Call for U {
#[inline(never)]
fn call(&self) {
let t = [0, 1, 2];
unsafe { core::ptr::read_volatile(&t) };
}
}
#[inline(never)]
fn dispatch<C>(x: C)
where
C: Call,
{
x.call();
let t = [0];
unsafe { core::ptr::read_volatile(&t) };
}
#[entry]
#[inline(never)]
fn main() -> ! {
let t = T {};
dispatch(t);
loop {}
}
#[exception]
#[inline(never)]
fn SysTick() {
let u = U {};
dispatch(u);
}
// cargo stack-sizes --example hello5 --release -- --emit=llvm-ir
//! test of stack-sizes
#![no_main]
#![no_std]
extern crate panic_halt;
use cortex_m_rt::{entry, exception};
trait Call {
fn call(&self) -> ();
}
struct T {}
impl Call for T {
#[inline(never)]
fn call(&self) {
let t = [0, 1];
unsafe { core::ptr::read_volatile(&t) };
}
}
unsafe impl Sync for T {}
unsafe impl Send for T {}
struct U {}
impl Call for U {
#[inline(never)]
fn call(&self) {
let t = [0, 1, 2];
unsafe { core::ptr::read_volatile(&t) };
}
}
unsafe impl Sync for U {}
unsafe impl Send for U {}
#[inline(never)]
fn dispatch<C>(x: C)
where
C: Call,
{
x.call();
let t = [0];
unsafe { core::ptr::read_volatile(&t) };
}
// unsafe { static X: &Call = unsafe { &U {} } };
#[entry]
#[inline(never)]
fn main() -> ! {
let t = T {};
dispatch(t);
loop {}
}
#[exception]
#[inline(never)]
fn SysTick() {
let u = U {};
dispatch(u);
}
// cargo stack-sizes --example hello5 --release -- --emit=llvm-ir
//! Sends "Hello, world!" through the ITM port 0
//!
//! ITM is much faster than semihosting. Like 4 orders of magnitude or so.
//!
//! **NOTE** Cortex-M0 chips don't support ITM.
//!
//! You'll have to connect the microcontroller's SWO pin to the SWD interface. Note that some
//! development boards don't provide this option.
//!
//! You'll need [`itmdump`] to receive the message on the host plus you'll need to uncomment two
//! `monitor` commands in the `.gdbinit` file.
//!
//! [`itmdump`]: https://docs.rs/itm/0.2.1/itm/
//!
//! ---
#![no_main]
#![no_std]
extern crate panic_halt;
use cortex_m::{iprintln, Peripherals};
use cortex_m_rt::entry;
#[entry]
fn main() -> ! {
let mut p = Peripherals::take().unwrap();
let stim = &mut p.ITM.stim[0];
iprintln!(stim, "Hello, world!");
loop {}
}
Call graph node <<null function>><<0x55dd1bcabeb0>> #uses=0\n CS<0x0> calls function \'_ZN4core3ptr13drop_in_place17hbe38e86a6acb8d72E\'\n CS<0x0> calls function \'_ZN46_$LT$hello5..Call1$u20$as$u20$hello5..Call$GT$4call17hc89c6364eab0a820E\'\n CS<0x0> calls function \'_ZN46_$LT$hello5..Call2$u20$as$u20$hello5..Call$GT$4call17ha0c1f0cf2718f4e6E\'\n CS<0x0> calls function \'main\'\n CS<0x0> calls function \'SysTick\'\n CS<0x0> calls function \'llvm.lifetime.start.p0i8\'\n CS<0x0> calls function \'llvm.lifetime.end.p0i8\'\n CS<0x0> calls function \'Reset\'\n CS<0x0> calls function \'__pre_init\'\n CS<0x0> calls function \'NonMaskableInt\'\n CS<0x0> calls function \'HardFault\'\n CS<0x0> calls function \'MemoryManagement\'\n CS<0x0> calls function \'BusFault\'\n CS<0x0> calls function \'UsageFault\'\n CS<0x0> calls function \'SVCall\'\n CS<0x0> calls function \'DebugMonitor\'\n CS<0x0> calls function \'PendSV\'\n CS<0x0> calls function \'DefaultHandler\'\n CS<0x0> calls function \'UserHardFault_\'\n CS<0x0> calls function \'DefaultHandler_\'\n CS<0x0> calls function \'DefaultPreInit\'\n\nCall graph node for function: \'BusFault\'<<0x55dd1bc8de60>> #uses=1\n CS<0x0> calls external node\n\nCall graph node for function: \'DebugMonitor\'<<0x55dd1bc8ebd0>> #uses=1\n CS<0x0> calls external node\n\nCall graph node for function: \'DefaultHandler\'<<0x55dd1bc8d140>> #uses=1\n CS<0x0> calls external node\n\nCall graph node for function: \'DefaultHandler_\'<<0x55dd1bc782b0>> #uses=1\n\nCall graph node for function: \'DefaultPreInit\'<<0x55dd1bc89900>> #uses=1\n\nCall graph node for function: \'HardFault\'<<0x55dd1bcb11b0>> #uses=1\n CS<0x0> calls external node\n\nCall graph node for function: \'MemoryManagement\'<<0x55dd1bcb12e0>> #uses=1\n CS<0x0> calls external node\n\nCall graph node for function: \'NonMaskableInt\'<<0x55dd1bcb1090>> #uses=1\n CS<0x0> calls external node\n\nCall graph node for function: \'PendSV\'<<0x55dd1bc8f6e0>> #uses=1\n CS<0x0> calls external node\n\nCall graph node for function: \'Reset\'<<0x55dd1bcb0d70>> #uses=1\n CS<0x55dd1bc72e08> calls function \'__pre_init\'\n CS<0x55dd1bc7f978> calls function \'main\'\n\nCall graph node for function: \'SVCall\'<<0x55dd1bc87600>> #uses=1\n CS<0x0> calls external node\n\nCall graph node for function: \'SysTick\'<<0x55dd1bcace00>> #uses=1\n\nCall graph node for function: \'UsageFault\'<<0x55dd1bc77510>> #uses=1\n CS<0x0> calls external node\n\nCall graph node for function: \'UserHardFault_\'<<0x55dd1bc87e60>> #uses=1\n\nCall graph node for function: \'_ZN46_$LT$hello5..Call1$u20$as$u20$hello5..Call$GT$4call17hc89c6364eab0a820E\'<<0x55dd1bcac310>> #uses=1\n\nCall graph node for function: \'_ZN46_$LT$hello5..Call2$u20$as$u20$hello5..Call$GT$4call17ha0c1f0cf2718f4e6E\'<<0x55dd1bcac3f0>> #uses=1\n\nCall graph node for function: \'_ZN4core3ptr13drop_in_place17hbe38e86a6acb8d72E\'<<0x55dd1bcac160>> #uses=1\n\nCall graph node for function: \'__pre_init\'<<0x55dd1bcb0e20>> #uses=2\n CS<0x0> calls external node\n\nCall graph node for function: \'llvm.lifetime.end.p0i8\'<<0x55dd1bc8cfa0>> #uses=1\n\nCall graph node for function: \'llvm.lifetime.start.p0i8\'<<0x55dd1bcacfa0>> #uses=1\n\nCall graph node for function: \'main\'<<0x55dd1bcac4d0>> #uses=2\n CS<0x55dd1bc78cb8> calls external node\n\n"
//! Changing the panicking behavior
//!
//! The easiest way to change the panicking behavior is to use a different [panic handler crate][0].
//!
//! [0]: https://crates.io/keywords/panic-impl
#![no_main]
#![no_std]
// Pick one of these panic handlers:
// `panic!` halts execution; the panic message is ignored
extern crate panic_halt;
// Reports panic messages to the host stderr using semihosting
// NOTE to use this you need to uncomment the `panic-semihosting` dependency in Cargo.toml
// extern crate panic_semihosting;
// Logs panic messages using the ITM (Instrumentation Trace Macrocell)
// NOTE to use this you need to uncomment the `panic-itm` dependency in Cargo.toml
// extern crate panic_itm;
use cortex_m_rt::entry;
#[entry]
fn main() -> ! {
panic!("Oops")
}
MEMORY
{
/* NOTE 1 K = 1 KiBi = 1024 bytes */
/* TODO Adjust these memory regions to match your device memory layout */
/* These values correspond to the LM3S6965, one of the few devices QEMU can emulate */
FLASH : ORIGIN = 0x08000000, LENGTH = 256K
RAM : ORIGIN = 0x20000000, LENGTH = 64K
}
/*
SECTIONS
{
.stack_sizes (INFO) :
{
KEEP(*(.stack_sizes));
}
}
*/
/* This is where the call stack will be allocated. */
/* The stack is of the full descending type. */
/* 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; */
/* Example of putting non-initialized variables into custom RAM locations. */
/* This assumes you have defined a region RAM2 above, and in the Rust
sources added the attribute `#[link_section = ".ram2bss"]` to the data
you want to place there. */