diff --git a/.vscode/launch.json b/.vscode/launch.json index 7a6f29275a28bbf4596a036dfba642702f5a6b6e..12dae9b3574a60eb351821f4e774703ece40a1e4 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -220,5 +220,23 @@ ], "cwd": "${workspaceRoot}" }, + { + "type": "gdb", + "request": "attach", + "name": "bare0 Release", + "gdbpath": "/usr/bin/arm-none-eabi-gdb", + "executable": "./target/thumbv7em-none-eabihf/release/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}" + }, ] } \ No newline at end of file diff --git a/README.md b/README.md index e9e493176243860d2fb710f185515627e380bae0..9d12913b3cadda229c0724b864f61a76bec64661 100644 --- a/README.md +++ b/README.md @@ -124,6 +124,7 @@ monitor reset init ``` --- + # Examples In the `examples` folder you find: @@ -131,39 +132,52 @@ In the `examples` folder you find: ## bare0.rs This is a simple bare metal applicaiton: +``` rust +//! bare0.rs +//! Simple bare metal application -- The `cortex-m-rt` provides a minimalistic runtime and startup of the ARM Cortex M. Providing: +// feature to ensure symbols to be linked +#![feature(used)] +// build without the Rust standard library +#![no_std] - - 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. +// Minimal runtime / startup for Cortex-M microcontrollers +extern crate cortex_m_rt; -- A user defined `main()` - -``` rust +static mut X: u32 = 10; fn main() { - let mut x = 0; + let mut x = unsafe { X }; loop { x += 1; + unsafe { + X += 1; + assert!(x == X); + } } } -``` - -- 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(); -} +extern "C" fn default_handler() {} ``` -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 [cortex-m-rt](https://github.com/japaric/cortex-m-rt) crate 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. + +- The user defined `main` function defines the applicaiton behavior. + +- User defined interrupt handlers, in this case just dummy handlers (never returning). + + + +As seen, we need to tell the compiler in what `section` the interrupt "vector" (array) should go. The ARM-Cortex-M architecture defines 16 exceptions and serves a maximum of 240 interrupt sources. The target specific memory layout is given by the file `memory.x`. @@ -176,7 +190,250 @@ MEMORY } ``` -In effect, `bare0::main` will be stored in the `FLASH` memory segment, while our +## Compilng and Debugging + +First compile the example either through the `vscode` Tasks (Ctrl-Shift-B) or using the console: + +``` shell +> xargo build --example bare0 +``` + +Before running the program, we can have a look at the genertate binary. + +``` shell +> arm-none-eabi-objdump -h target/thumbv7em-none-eabihf/debug/examples/bare0 +target/thumbv7em-none-eabihf/debug/examples/bare0: file format elf32-littlearm + +Sections: +Idx Name Size VMA LMA File off Algn + 0 .vector_table 00000400 08000000 08000000 00010000 2**2 + CONTENTS, ALLOC, LOAD, READONLY, DATA + 1 .text 0000052e 08000400 08000400 00010400 2**2 + CONTENTS, ALLOC, LOAD, READONLY, CODE + 2 .rodata 000000b8 08000930 08000930 00010930 2**4 + CONTENTS, ALLOC, LOAD, READONLY, DATA + 3 .bss 00000004 20000000 20000000 00030000 2**2 + ALLOC + 4 .data 00000004 20000004 080009e8 00020004 2**2 + CONTENTS, ALLOC, LOAD, DATA + ... +target/thumbv7em-none-eabihf/debug/examples/bare0: file format elf32-littlearm +``` + +- `.vector_table` section contains the `exception` and `interrupt` handler addresses. In total 0x400 bytes (in hex), i.e., 256 word size vectors (1k byte). +- `.text` section holds the program code (code from `cortex-m-rt` and the user `main` in our case). In total 0x52e bytes (in hex). +- `.rodata` section, constants (including `X_INIT`). In total 0xb8 bytes. +- `.bss` section, zero-initated static heap variables. Here the `Y` is stored (`u32` is 4 bytes). +- `.data` section, static heap variables that is initiated at run-time (by `cortex-m-rt` before user `main` is called. + +We can further inspect the binary: + +``` shell +> arm-none-eabi-objdump -s -j .vector_table target/thumbv7em-none-eabihf/debug/examples/bare0 + +target/thumbv7em-none-eabihf/debug/examples/bare0: file format elf32-littlearm + +Contents of section .vector_table: + 8000000 00800120 01040008 c3070008 c3070008 ... ............ + 8000010 c3070008 c3070008 c3070008 00000000 ................ + 8000020 00000000 00000000 00000000 c3070008 ................ + 8000030 c3070008 00000000 c3070008 c3070008 ................ + 8000040 5f050008 5f050008 5f050008 5f050008 _..._..._..._... +target/thumbv7em-none-eabihf/debug/examples/bare0: file format elf32-littlearm +``` + +Gives a raw dump of the `.vector_table`. The first 16 entries are the exception handlers, where the first entry (0x0800000) is the stack pointer (0x00800120 raw = 0x20018000 interpreted as little-endian). The stack pointer is set just outside the ram area (96kb starting at 0x20000000), recall that the stack pointer is decremented first upon stack allocations so this is "safe". The second vector (0x0800004), is the `reset` vector (0x80000401). (The odd address casts the processor into `thumb` mode using 16 bit instructions for higher code density.) + +An even more detailed view can be obtianed: + +``` shell +arm-none-eabi-objdump -C -d -j .vector_table target/thumbv7em-none-eabihf/debug/examples/bare0 + +target/thumbv7em-none-eabihf/debug/examples/bare0: file format elf32-littlearm + + +Disassembly of section .vector_table: + +08000000 <_svector_table>: + 8000000: 20018000 .word 0x20018000 + +08000004 <cortex_m_rt::RESET_VECTOR>: + 8000004: 08000401 .... + +08000008 <EXCEPTIONS>: + 8000008: 080007c3 080007c3 080007c3 080007c3 ................ + 8000018: 080007c3 00000000 00000000 00000000 ................ + 8000028: 00000000 080007c3 080007c3 00000000 ................ + 8000038: 080007c3 080007c3 ........ + +08000040 <_eexceptions>: + 8000040: 0800055f .word 0x0800055f +``` + +This is more informative, here we see the address as interpreted in little-endian. also we see, that the `cortex_m_rt::RESET_VECTOR` is located at address 08000004 (pointing to the handler located at 0x08000401.). The interrupt vectors all point to +0x0800055f (odd address here for `thumb` mode). + + +Now lets have a closer look at the user `reset` (startup code): + +``` shell +arm-none-eabi-objdump -C -S target/thumbv7em-none-eabihf/debug/examples/bare0 > objdump.out +``` + +In the `objdump.out` file we find: + +``` rust +08000400 <cortex_m_rt::reset_handler>: +/// The reset handler +/// +/// This is the entry point of all programs +#[cfg(target_arch = "arm")] +#[link_section = ".reset_handler"] +unsafe extern "C" fn reset_handler() -> ! { + 8000400: b580 push {r7, lr} + 8000402: 466f mov r7, sp + 8000404: b082 sub sp, #8 + r0::zero_bss(&mut _sbss, &mut _ebss); + 8000406: f240 0000 movw r0, #0 + 800040a: f2c2 0000 movt r0, #8192 ; 0x2000 + 800040e: f240 0104 movw r1, #4 + 8000412: f2c2 0100 movt r1, #8192 ; 0x2000 + 8000416: f000 f8c3 bl 80005a0 <r0::zero_bss> + 800041a: e7ff b.n 800041c <cortex_m_rt::reset_handler+0x1c> + r0::init_data(&mut _sdata, &mut _edata, &_sidata); + + ... + + + main() + 8000458: f000 f99d bl 8000796 <cortex_m_rt::reset_handler::main> + 800045c: e7ff b.n 800045e <_einterrupts+0x5e> + } + } + + // If `main` returns, then we go into "reactive" mode and simply attend + // interrupts as they occur. + loop { + 800045e: e7ff b.n 8000460 <_einterrupts+0x60> + asm!("wfi" :::: "volatile"); + 8000460: bf30 wfi + loop { + 8000462: e7fd b.n 8000460 <_einterrupts+0x60> + +``` + +Here we see that the `reset_handler` intitates static heap variables (by 0 or value accordingly) before calling `main` (and puts the processor in sleep mode in case `main` returns). The user `main` has the symbol `bare0::main`, which is unknown to the `cortex-m-rt` library (defining the generic `reset_handler`). Hence the, `reset_handler` calls a compiler generated (trampoline) `main`, that in turn calls `bare0::main` (the user `main`). + +Finally, we can have a look at the user `main`. + +``` rust +0800048e <bare0::main>: +const X_INIT: u32 = 10; + +static mut X: u32 = X_INIT; +static mut Y: u32 = 0; + +fn main() { + 800048e: b580 push {r7, lr} + 8000490: 466f mov r7, sp + 8000492: b084 sub sp, #16 + let mut x = unsafe { X }; + 8000494: f240 0004 movw r0, #4 + 8000498: f2c2 0000 movt r0, #8192 ; 0x2000 + 800049c: 6800 ldr r0, [r0, #0] + 800049e: 9002 str r0, [sp, #8] + + loop { + 80004a0: e7ff b.n 80004a2 <bare0::main+0x14> + x += 1; + 80004a2: 9802 ldr r0, [sp, #8] + 80004a4: 1c41 adds r1, r0, #1 + 80004a6: 2201 movs r2, #1 + 80004a8: 4281 cmp r1, r0 + 80004aa: bf28 it cs + 80004ac: 2200 movcs r2, #0 + 80004ae: 2a00 cmp r2, #0 + 80004b0: 9101 str r1, [sp, #4] + 80004b2: d146 bne.n 8000542 <bare0::main+0xb4> + 80004b4: e7ff b.n 80004b6 <bare0::main+0x28> + 80004b6: 9801 ldr r0, [sp, #4] + 80004b8: 9002 str r0, [sp, #8] + unsafe { + X += 1; + 80004ba: f240 0104 movw r1, #4 + 80004be: f2c2 0100 movt r1, #8192 ; 0x2000 + 80004c2: 6809 ldr r1, [r1, #0] + 80004c4: 1c4a adds r2, r1, #1 + 80004c6: 2301 movs r3, #1 + 80004c8: 428a cmp r2, r1 + 80004ca: bf28 it cs + 80004cc: 2300 movcs r3, #0 + 80004ce: 2b00 cmp r3, #0 + 80004d0: 9200 str r2, [sp, #0] + 80004d2: d13d bne.n 8000550 <bare0::main+0xc2> + 80004d4: e7ff b.n 80004d6 <bare0::main+0x48> + 80004d6: f240 0004 movw r0, #4 + 80004da: f2c2 0000 movt r0, #8192 ; 0x2000 + 80004de: 9900 ldr r1, [sp, #0] + 80004e0: 6001 str r1, [r0, #0] + Y = X; + 80004e2: 6802 ldr r2, [r0, #0] + 80004e4: f240 0300 movw r3, #0 + 80004e8: f2c2 0300 movt r3, #8192 ; 0x2000 + 80004ec: 601a str r2, [r3, #0] + assert!(x == X && X == Y); + 80004ee: 9a02 ldr r2, [sp, #8] + 80004f0: 6800 ldr r0, [r0, #0] + 80004f2: 4282 cmp r2, r0 + 80004f4: d008 beq.n 8000508 <bare0::main+0x7a> + 80004f6: e003 b.n 8000500 <bare0::main+0x72> + 80004f8: 2001 movs r0, #1 + 80004fa: f807 0c01 strb.w r0, [r7, #-1] + 80004fe: e010 b.n 8000522 <bare0::main+0x94> + 8000500: 2000 movs r0, #0 + 8000502: f807 0c01 strb.w r0, [r7, #-1] + 8000506: e00c b.n 8000522 <bare0::main+0x94> + 8000508: f240 0004 movw r0, #4 + 800050c: f2c2 0000 movt r0, #8192 ; 0x2000 + 8000510: 6800 ldr r0, [r0, #0] + 8000512: f240 0100 movw r1, #0 + 8000516: f2c2 0100 movt r1, #8192 ; 0x2000 + 800051a: 6809 ldr r1, [r1, #0] + 800051c: 4288 cmp r0, r1 + 800051e: d0eb beq.n 80004f8 <bare0::main+0x6a> + 8000520: e7ee b.n 8000500 <bare0::main+0x72> + 8000522: f817 0c01 ldrb.w r0, [r7, #-1] + 8000526: 43c0 mvns r0, r0 + 8000528: 07c0 lsls r0, r0, #31 + 800052a: 2800 cmp r0, #0 + 800052c: d008 beq.n 8000540 <bare0::main+0xb2> + 800052e: e7ff b.n 8000530 <bare0::main+0xa2> + 8000530: f640 10dc movw r0, #2524 ; 0x9dc + 8000534: f6c0 0000 movt r0, #2048 ; 0x800 + 8000538: 6800 ldr r0, [r0, #0] + 800053a: f000 f9c1 bl 80008c0 <core::panicking::panic> + 800053e: defe udf #254 ; 0xfe + loop { + 8000540: e7af b.n 80004a2 <bare0::main+0x14> + x += 1; + 8000542: f640 106c movw r0, #2412 ; 0x96c + 8000546: f6c0 0000 movt r0, #2048 ; 0x800 + 800054a: f000 f9b9 bl 80008c0 <core::panicking::panic> + 800054e: defe udf #254 ; 0xfe + X += 1; + 8000550: f640 1084 movw r0, #2436 ; 0x984 + 8000554: f6c0 0000 movt r0, #2048 ; 0x800 + 8000558: f000 f9b2 bl 80008c0 <core::panicking::panic> + 800055c: defe udf #254 ; 0xfe + ```` + +We see that the compiler has genereted quite some code for the small `main` function. + +We can compile the `bare0` application in -- release mode instead. + +``` shell +> xargo build --release --example bare0 +``` # License diff --git a/examples/bare0.rs b/examples/bare0.rs index 2ccb161787f6d0e68014743b43bb994c9858c989..fdb45a28df683bc4b3cdffb70910d37397f8bcbc 100644 --- a/examples/bare0.rs +++ b/examples/bare0.rs @@ -9,7 +9,12 @@ // Minimal runtime / startup for Cortex-M microcontrollers extern crate cortex_m_rt; -static mut X: u32 = 10; +const X_INIT: u32 = 10; + +static mut X: u32 = X_INIT; +static mut Y: u32 = 0; + +#[inline(never)] fn main() { let mut x = unsafe { X }; @@ -17,7 +22,8 @@ fn main() { x += 1; unsafe { X += 1; - assert!(x == X); + Y = X; + assert!(x == X && X == Y); } } } @@ -27,4 +33,6 @@ fn main() { #[used] static INTERRUPTS: [extern "C" fn(); 240] = [default_handler; 240]; -extern "C" fn default_handler() {} +extern "C" fn default_handler() { + loop {} +}