diff --git a/.vscode/launch.json b/.vscode/launch.json index 5b205905c670bdfcfef8c60a86851341c4cd9a82..7a6f29275a28bbf4596a036dfba642702f5a6b6e 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -94,6 +94,24 @@ ], "cwd": "${workspaceRoot}" }, + { + "type": "gdb", + "request": "attach", + "name": "bare0", + "gdbpath": "/usr/bin/arm-none-eabi-gdb", + "executable": "./target/thumbv7em-none-eabihf/debug/examples/bare0", + "target": ":3333", + "remote": true, + "autorun": [ + "monitor reset init", + "monitor arm semihosting enable", + "monitor tpiu config internal /tmp/itm.log uart off 64000000", + "monitor itm port 0 on", + "load", + "monitor reset init" + ], + "cwd": "${workspaceRoot}" + }, { "type": "gdb", "request": "attach", @@ -184,5 +202,23 @@ ], "cwd": "${workspaceRoot}" }, + { + "type": "gdb", + "request": "attach", + "name": "bare6", + "gdbpath": "/usr/bin/arm-none-eabi-gdb", + "executable": "./target/thumbv7em-none-eabihf/debug/examples/bare6", + "target": ":3333", + "remote": true, + "autorun": [ + "monitor reset init", + "monitor arm semihosting enable", + "monitor tpiu config internal /tmp/itm.log uart off 64000000", + "monitor itm port 0 on", + "load", + "monitor reset init" + ], + "cwd": "${workspaceRoot}" + }, ] } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index afdd4ba8a60ab65825de389cc99142f988286f4c..5ce0472bc834d9e7b1f11bd28f1f5f9d8bc81f9e 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -63,6 +63,18 @@ "isDefault": true } }, + { + "type": "shell", + "label": "xargo build --example bare0", + "command": "xargo build --example bare0", + "problemMatcher": [ + "$rustc" + ], + "group": { + "kind": "build", + "isDefault": true + } + }, { "type": "shell", "label": "xargo build --example bare1", diff --git a/README.md b/README.md index cba331e414b831fe520e7e9b05d756eef68487a9..e9e493176243860d2fb710f185515627e380bae0 100644 --- a/README.md +++ b/README.md @@ -123,6 +123,62 @@ The content of the file `r` is simply: monitor reset init ``` +--- +# Examples + +In the `examples` folder you find: + +## bare0.rs + +This is a simple bare metal applicaiton: + +- The `cortex-m-rt` provides a minimalistic runtime and startup of the ARM Cortex M. Providing: + + - Linker script, telling the linker howto generate the binary. + - The `reset` handler, initiating the memory (and FPU if available on the target), and calls the user `main` function. + - Default exception handlers. + +- A user defined `main()` + +``` rust +fn main() { + let mut x = 0; + + loop { + x += 1; + } +} +``` + +- User defined interrupt handlers. + +``` rust +// 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() { + // cortex_m::asm::bkpt(); +} +``` + +As seen, we need to tell the compiler in what `section` the interrupt "vector" (array) should go. The ARM-Cortex-M has 16 exceptions and serves a maximum of 240 interrupt sources. + +The target specific memory layout is given by the file `memory.x`. + +``` txt +/* STM32F401re */ +MEMORY +{ + FLASH : ORIGIN = 0x08000000, LENGTH = 512K + RAM : ORIGIN = 0x20000000, LENGTH = 96K +} +``` + +In effect, `bare0::main` will be stored in the `FLASH` memory segment, while our + + # License Licensed under either of diff --git a/examples/bare0.rs b/examples/bare0.rs new file mode 100644 index 0000000000000000000000000000000000000000..2ccb161787f6d0e68014743b43bb994c9858c989 --- /dev/null +++ b/examples/bare0.rs @@ -0,0 +1,30 @@ +//! bare0.rs +//! Simple bare metal application + +// feature to ensure symbols to be linked +#![feature(used)] +// build without the Rust standard library +#![no_std] + +// Minimal runtime / startup for Cortex-M microcontrollers +extern crate cortex_m_rt; + +static mut X: u32 = 10; +fn main() { + let mut x = unsafe { X }; + + loop { + x += 1; + unsafe { + X += 1; + assert!(x == X); + } + } +} + +// 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() {} diff --git a/examples/bare1.rs b/examples/bare1.rs index 16931d37ea55359086f33d852a9765de0ae575b8..4c5dcee41ec23c933896d03516ce48e6dceec986 100644 --- a/examples/bare1.rs +++ b/examples/bare1.rs @@ -7,27 +7,27 @@ #![no_std] // API to the ARM Cortex M Peripherals -extern crate cortex_m; +//extern crate cortex_m; // Minimal runtime / startup for Cortex-M microcontrollers extern crate cortex_m_rt; // Convenient tracing over semihosting and ITM -#[macro_use] -extern crate cortex_m_debug; +// #[macro_use] +// extern crate cortex_m_debug; #[inline(never)] fn main() { // ITM trace (fast) // start `itmdump` before `openocd` - ipln!("ITM: Hello World"); + // ipln!("ITM: Hello World"); // semihosting trace (slow) - sprintln!("SEMIHOSTING: Hello World"); + // sprintln!("SEMIHOSTING: Hello World"); // to prevent returning loop { - cortex_m::asm::nop(); + // cortex_m::asm::nop(); } } @@ -37,5 +37,5 @@ fn main() { static INTERRUPTS: [extern "C" fn(); 240] = [default_handler; 240]; extern "C" fn default_handler() { - cortex_m::asm::bkpt(); + // cortex_m::asm::bkpt(); } diff --git a/examples/bare4.rs b/examples/bare4.rs index 08d633ec001e1f60aa68e48a1acab31a1307f031..0dd033ee0ef6e99f87e734ee81b08faaf3abed1a 100644 --- a/examples/bare4.rs +++ b/examples/bare4.rs @@ -2,19 +2,24 @@ //! Simple bare metal application //! #![feature(used)] +#![feature(custom_attribute)] // needed for #[rustfmt_skip] #![no_std] extern crate cortex_m; extern crate cortex_m_rt; // Peripheral addresses as constants -const PERIPH_BASE: u32 = 0x40000000; -const AHB1PERIPH_BASE: u32 = PERIPH_BASE + 0x00020000; -const RCC_BASE: u32 = AHB1PERIPH_BASE + 0x3800; -const RCC_AHB1ENR: u32 = RCC_BASE + 0x30; -const GBPIA_BASE: u32 = AHB1PERIPH_BASE + 0x0000; -const GPIOA_MODER: u32 = GBPIA_BASE + 0x00; -const GPIOA_BSRR: u32 = GBPIA_BASE + 0x18; +#[rustfmt_skip] +mod address { + pub const PERIPH_BASE: u32 = 0x40000000; + pub const AHB1PERIPH_BASE: u32 = PERIPH_BASE + 0x00020000; + pub const RCC_BASE: u32 = AHB1PERIPH_BASE + 0x3800; + pub const RCC_AHB1ENR: u32 = RCC_BASE + 0x30; + pub const GBPIA_BASE: u32 = AHB1PERIPH_BASE + 0x0000; + pub const GPIOA_MODER: u32 = GBPIA_BASE + 0x00; + pub const GPIOA_BSRR: u32 = GBPIA_BASE + 0x18; +} +use address::*; // see the Reference Manual RM0368 (www.st.com/resource/en/reference_manual/dm00096844.pdf) // rcc, chapter 6 @@ -40,7 +45,6 @@ fn wait(i: u32) { fn main() { // power on GPIOA, RM0368 6.3.11 - let r = read_u32(RCC_AHB1ENR); // read write_u32(RCC_AHB1ENR, r | 1); // set enable @@ -49,8 +53,7 @@ fn main() { write_u32(GPIOA_MODER, r | 0b01 << (5 * 2)); // set output mode // and alter the data output through the BSRR register - // this is more efficient as the read register (in modify) - // is not needed. + // this is more efficient as the read register is not needed. loop { // set PA5 high, RM0368 8.4.7 diff --git a/examples/bare5.rs b/examples/bare5.rs index 3c19c2431839893c62e53f582046813c92345977..1e253e25b1ee80b513033cde583b98e178186078 100644 --- a/examples/bare5.rs +++ b/examples/bare5.rs @@ -8,13 +8,10 @@ extern crate cortex_m; extern crate cortex_m_rt; -// #[macro_use] -// extern crate cortex_m_debug; - +// C like API... mod stm32f40x { use core::{cell, ptr}; - // C like API... #[rustfmt_skip] mod address { pub const PERIPH_BASE: u32 = 0x40000000; @@ -123,9 +120,7 @@ fn wait(i: u32) { } fn main() { - // // power on GPIOA, RM0368 6.3.11 - // p.RCC.ahb1enr.modify(|_, w| w.gpioaen().set_bit()); - + // power on GPIOA, RM0368 6.3.11 let rcc = RCC::get(); // get the reference to RCC in memory unsafe { let r = (*rcc).AHB1ENR.read(); // read diff --git a/examples/bare6.rs b/examples/bare6.rs new file mode 100644 index 0000000000000000000000000000000000000000..493b814787224ae0d6cc27937fdf71ee22dcb496 --- /dev/null +++ b/examples/bare6.rs @@ -0,0 +1,43 @@ +//! bare6.rs +//! Simple bare metal application +//! +#![feature(used)] +#![feature(use_nested_groups)] +#![no_std] + +extern crate cortex_m; +extern crate cortex_m_rt; + +#[macro_use] +extern crate cortex_m_debug; + +use cortex_m::{asm::bkpt, peripheral::DWT}; + +// uses the DWT.CYCNT +// doc: ARM trm_100166_0001_00_en.pdf, chapter 9.2 +// we use the `cortex-m` abstraction +fn wait_cycles(nr_cycles: u32) { + unsafe { + let t = (*DWT.get()).cyccnt.read().wrapping_add(nr_cycles); + while ((*DWT.get()).cyccnt.read().wrapping_sub(t) as i32) < 0 {} + } +} + +fn main() { + unsafe { (*DWT.get()).enable_cycle_counter() }; + let mut i = 0; + loop { + ipln!("tick {}", i); + wait_cycles(64_000_000); + i += 1; // this will eventually cause a panic, when the counter wraps. + } +} + +// 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() { + cortex_m::asm::bkpt(); +} diff --git a/memory.x b/memory.x index 534d4786655e3734779bc36863f96c07c9c03a24..139422c3f532b20d1e796a6e7ffa13202936b6f3 100644 --- a/memory.x +++ b/memory.x @@ -1,6 +1,6 @@ -/* STM32F103C8V6 */ +/* STM32F401re */ MEMORY { - FLASH : ORIGIN = 0x08000000, LENGTH = 64K - RAM : ORIGIN = 0x20000000, LENGTH = 20K + FLASH : ORIGIN = 0x08000000, LENGTH = 512K + RAM : ORIGIN = 0x20000000, LENGTH = 96K }