Skip to content
Snippets Groups Projects
Commit 94ebfa71 authored by Per's avatar Per
Browse files

readme

parent 7bbafc33
No related branches found
No related tags found
No related merge requests found
......@@ -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
......@@ -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:
- 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.
// feature to ensure symbols to be linked
#![feature(used)]
// build without the Rust standard library
#![no_std]
- A user defined `main()`
// Minimal runtime / startup for Cortex-M microcontrollers
extern crate cortex_m_rt;
``` 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
......
......@@ -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 {}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment