Skip to content

Commit

Permalink
overridable interrupts and use interrupt+wfi in the delay module
Browse files Browse the repository at this point in the history
closes #8
  • Loading branch information
Jorge Aparicio committed Oct 8, 2016
1 parent a7fa00c commit 1096367
Show file tree
Hide file tree
Showing 11 changed files with 496 additions and 35 deletions.
73 changes: 73 additions & 0 deletions examples/interrupt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//! Exceptions handlers are smart! They help you find the source of crashes
//!
//! At the end there's a program that crashes. Run it with `(gdb) continue`:
//!
//! By the time you hit the exception, you'll see the following output in `itmdump`:
//!
//! ``` text
//! $ itmdump /tmp/itm.fifo
//! (..)
//! EXCEPTION HardFault @ PC=0x080000a2
//! ```
//!
//! which indicates that this is a "hard fault" exception. It also tells you were the crash
//! originated! The PC (Program Counter) value is the address of the instruction that generated the
//! crash. You can disassemble your program around that address using GDB:
//!
//! ``` text
//! (gdb) disas /m 0x080000a2
//! Dump of assembler code for function exception::main:
//! 32 pub extern "C" fn main() -> ! {
//! 0x08000092 <+0>: sub sp, #8
//! 0x08000094 <+2>: b.n 0x8000096 <exception::main+4>
//!
//! 33 // This reads beyond the boundary of available RAM (40KiB starting at
//! 34 // 0x4000_0000) and triggers a hard fault exception
//! 35 let _hard_fault_exception = unsafe {
//! 36 *((0x4000_0000 + 40 * 1024) as *const u32)
//! 0x08000096 <+4>: b.n 0x8000098 <exception::main+6>
//! 0x08000098 <+6>: b.n 0x800009a <exception::main+8>
//! 0x0800009a <+8>: movw r0, #40960 ; 0xa000
//! 0x0800009e <+12>: movt r0, #16384 ; 0x4000
//! 0x080000a2 <+16>: ldr r0, [r0, #0] ; <--
//! 0x080000a4 <+18>: str r0, [sp, #4]
//!
//! 37 };
//! 38
//! 39 loop {}
//! 0x080000a6 <+20>: b.n 0x80000a8 <exception::main+22>
//! 0x080000a8 <+22>: b.n 0x80000a8 <exception::main+22>
//!
//! End of assembler dump.
//! ```
//!
//! `ldr r0, [r0, #0]` is the faulty instruction. It tries to load the word at the address that `r0`
//! indicates. From the two previous instructions, you can tell that `r0` holds the value
//! `0x4000a000`.
//!
//! Wait! That's not everything. There's also a local `sf` variable that points to the stack frame
//! where the exception occurred. If you print it under GDB, you'll see the state of several
//! registers at the moment of the crash:
//!
//! ``` text
//! (gdb) p/x *sf
//! $1 = {r0 = 0x4000a000, r1 = 0x48001000, r2 = 0x55550000, r3 = 0x48001000,
//! r12 = 0x8003950, lr = 0x8000085, pc = 0x80000a2, xpsr = 0x1000000}
//! ```
//!
//! See? `r0 = 0x4000a000` that matches our expectations from reading the disassembly.
#![no_main]
#![no_std]

extern crate f3;

#[export_name = "main"]
pub extern "C" fn main() -> ! {
// This reads beyond the boundary of available RAM (40KiB starting at
// 0x4000_0000) and triggers an exception
let _hard_fault_exception =
unsafe { *((0x4000_0000 + 40 * 1024) as *const u32) };

loop {}
}
2 changes: 1 addition & 1 deletion examples/override-default-exception-handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub extern "C" fn main() -> ! {
#[export_name = "_default_exception_handler"] // <-- Important! Note the underscore.
pub extern "C" fn my_exception_handler() {
unsafe {
// .. you should reach this breakpoint!
// .. you'll reach THIS breakpoint!
bkpt!();
}

Expand Down
4 changes: 2 additions & 2 deletions examples/override-exception.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ pub extern "C" fn main() -> ! {
}

#[export_name = "_hard_fault"] // <-- Important! Note the underscore.
pub extern "C" fn my_hard_fault_handler() {
pub extern "C" fn my_hard_fault_exception_handler() {
unsafe {
// .. you should reach THIS breakpoint!
// .. you'll reach THIS breakpoint!
bkpt!();
}

Expand Down
30 changes: 30 additions & 0 deletions examples/override-interrupt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//! Override the TIM7 (or any other) interrupt handler
#![feature(asm)]
#![no_main]
#![no_std]

#[macro_use]
extern crate cortex_m;
extern crate f3;

use f3::delay;

#[export_name = "main"]
pub extern "C" fn main() -> ! {
// This function uses the TIM7 interrupt under the hood. After a second has
// passed, the `_tim7` interrupt handler will be called and ...
delay::ms(1_000);

loop {}
}

#[export_name = "_tim7"] // <-- Important! Note the underscore.
pub extern "C" fn my_tim7_interrupt_handler() {
unsafe {
// .. you'll reach THIS breakpoint!
bkpt!();
}

loop {}
}
75 changes: 48 additions & 27 deletions src/delay.rs
Original file line number Diff line number Diff line change
@@ -1,39 +1,39 @@
//! Delays
//!
//! # Implementation details
//!
//! This module uses the TIM7 peripheral and the `_tim7` interrupt under the hood.
use cortex_m::{self, asm};
use peripheral;

/// Blocks for `n` ms
// TODO interrupt + sleep (`wfi`)
pub fn ms(n: u16) {
#[allow(dead_code)]
#[export_name = "_tim7"]
#[linkage = "weak"]
pub unsafe extern "C" fn interrupt_handler() {
// Clear the update flag
let tim7 = peripheral::tim7_mut();
tim7.sr.write(0);
}

unsafe {
let tim6 = peripheral::tim6_mut();
let tim7 = peripheral::tim7_mut();

// The alarm (the "update event") will set off in `n` "ticks".
// One tick = 1 ms (see `init`)
tim6.arr.write(n);
tim6.cr1.write({
tim7.arr.write(n);
let cr1 = tim7.cr1.read();
tim7.cr1.write({
// Counter ENanbled
const CEN: u16 = 1 << 0;
// One Pulse Mode. Stop the counter after the next update event.
const OPM: u16 = 1 << 3;

OPM | CEN
cr1 | CEN
});
}

{
let sr = &peripheral::tim6().sr;

// Update Interrupt Flag. 1 = The alarm went off
const UIF: u16 = 1 << 0;

// Now we wait ...
while sr.read() != UIF {}
}

// Clear the flag
unsafe {
peripheral::tim6_mut().sr.write(0);
// XXX this assumes that `_tim7` is the only interrupt that can occur.
asm::wfi();
}
}

Expand All @@ -44,17 +44,38 @@ pub fn ms(n: u16) {
/// - Must be called once
/// - Must be called in an interrupt-free environment
pub unsafe fn init() {
let nvic = cortex_m::peripheral::nvic_mut();
let rcc = peripheral::rcc_mut();
let tim6 = peripheral::tim6_mut();
let tim7 = peripheral::tim7_mut();

// RCC: Enable TIM6
// RCC: Enable TIM7
let apb1enr = rcc.apb1enr.read();
rcc.apb1enr.write({
const TIM6EN: u32 = 1 << 4;
const TIM7EN: u32 = 1 << 5;

apb1enr | TIM6EN
apb1enr | TIM7EN
});

// TIM6: Set pre-scaler to 8_000 -> Frequency = 1 KHz
tim6.psc.write(7_999);
let cr1 = tim7.cr1.read();
tim7.cr1.write({
// Disable the clock
const CEN: u16 = 1 << 0;
// One Pulse Mode. Stop the counter after the next update event.
const OPM: u16 = 1 << 3;

(cr1 | OPM) & !CEN
});

// TIM7: Enable "update" interrupts
tim7.dier.write({
const UIE: u16 = 1 << 0;

UIE
});

// NVIC: Unmask the interrupt (N = 55)
nvic.iser[1].write(1 << (55 - 32));

// TIM7: Set pre-scaler to 8_000 -> Frequency = 1 KHz
tim7.psc.write(7_999);
}
5 changes: 2 additions & 3 deletions src/exception.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Exceptions
//!
//! All the exceptions prefixed with an underscore (`_`) can overridden by the top crate. Check out
//! [this example](../examples/override_exception/index.html).
//! All the exceptions prefixed with an underscore (`_`) can be overridden by the top crate. Check
//! out [this example](../examples/override_exception/index.html).
use cortex_m::{self, Handler, StackFrame};
use r0;
Expand Down Expand Up @@ -130,7 +130,6 @@ pub unsafe extern "C" fn reset() -> ! {
static _sidata: u32;
static mut _sbss: u32;
static mut _sdata: u32;

}

r0::zero_bss(&mut _sbss, &_ebss);
Expand Down
Loading

0 comments on commit 1096367

Please sign in to comment.