Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
Loading items

Target

Select target project
  • pln/rtic_f4xx_nucleo
  • ironedde/rtic_f4xx_nucleo
  • inaule-6/rtic_f4xx_nucleo
  • rubenasplund/rtic_f4xx_nucleo
4 results
Select Git revision
Loading items
Show changes
Commits on Source (11)
# Some notes on Debugging usin Cortex Debug
In the `launch.json` three profiles are added.
- `Cortex Debug`, running a debug build.
- `Cortex Release`, running a release (optimized) build.
- `Cortex Nightly`, running a release build with inlined assembly (optimized).
All profiles apply to the currently active editor. (So if you e.g., try to debug this file you will get an error.)
## Short cuts
- Shift-Control-D, to get the list of launch profiles and select the one to use,
- F5, to run the last one selected, or
- F5, continue if already started,
- Shift-F5, to abort debug session,
- F10, to step over funciton (run function till it returns),
- F11, to step into function,
- Shift-F11, to step out of function (run function till it returns),
- Ctrl-Shift-Y, focus debug console.
In the console you can give `gdb` commands directly. Error handling and "tabbing" is sub-par compared to the terminal gdb, but at least it allows you to give `gdb` commands.
It implements a history over sessions, which is neat. (Arrow up/down).
## Console
Examples of useful `gdb` console commands:
> disassemble
> continue
> break *0x8000242
> x 0xe0001004
## Peripherals
The profiles link to `STM32F401.svd`. This file is patched with to include the `DWT` unit where you find the `CYCCNT` register. You can `Right-Click` on the register to choose display format.
This can be used in the lab to observe the content of `CYCCNT` (instead of `x 0xe0001004` in the console).
......@@ -25,6 +25,302 @@ xs:noNamespaceSchemaLocation="CMSIS-SVD_Schema_1_1.xsd">
<resetValue>0x0</resetValue>
<resetMask>0xFFFFFFFF</resetMask>
<peripherals>
<peripheral>
<name>DWT</name>
<version>1.0</version>
<description>Data Watchpoint Trace</description>
<baseAddress>0xE0001000</baseAddress>
<access>read-write</access>
<addressBlock>
<offset>0</offset>
<size>0x5C</size>
<usage>registers</usage>
</addressBlock>
<registers>
<register>
<name>CTRL</name>
<description>Control Register</description>
<addressOffset>0</addressOffset>
<size>32</size>
<fields>
<field>
<name>NUMCOMP</name>
<description>Number of comparators</description>
<bitOffset>28</bitOffset>
<bitWidth>4</bitWidth>
<access>read-write</access>
</field>
<field>
<name>NOTRCPKT</name>
<description>No trace sampling and exception tracing</description>
<bitOffset>27</bitOffset>
<bitWidth>1</bitWidth>
<access>read-write</access>
</field>
<field>
<name>NOEXTTRIG</name>
<description>No external match signals</description>
<bitOffset>26</bitOffset>
<bitWidth>1</bitWidth>
<access>read-write</access>
</field>
<field>
<name>NOCYCCNT</name>
<description>No cycle counter</description>
<bitOffset>25</bitOffset>
<bitWidth>1</bitWidth>
<access>read-write</access>
</field>
<field>
<name>NOPRFCNT</name>
<description>No profiling counters</description>
<bitOffset>24</bitOffset>
<bitWidth>1</bitWidth>
<access>read-write</access>
</field>
<field>
<name>Reserved_23</name>
<description>Reserved bit 23</description>
<bitOffset>23</bitOffset>
<bitWidth>1</bitWidth>
<access>read-only</access>
</field>
<field>
<name>CYCEVTENA</name>
<description>enable Cycle count event</description>
<bitOffset>22</bitOffset>
<bitWidth>1</bitWidth>
<access>read-write</access>
</field>
<field>
<name>FOLDEVTENA</name>
<description>enable Folded instruction count event</description>
<bitOffset>21</bitOffset>
<bitWidth>1</bitWidth>
<access>read-write</access>
</field>
<field>
<name>LSUEVTENA</name>
<description>enable Load Store Unit (LSU) count event</description>
<bitOffset>20</bitOffset>
<bitWidth>1</bitWidth>
<access>read-write</access>
</field>
<field>
<name>SLEEPEVTENA</name>
<description>enable Sleep count event</description>
<bitOffset>19</bitOffset>
<bitWidth>1</bitWidth>
<access>read-write</access>
</field>
<field>
<name>EXCEVTENA</name>
<description>enable interrupt overhead event</description>
<bitOffset>18</bitOffset>
<bitWidth>1</bitWidth>
<access>read-write</access>
</field>
<field>
<name>CPIEVTENA</name>
<description>enable CPI count event</description>
<bitOffset>17</bitOffset>
<bitWidth>1</bitWidth>
<access>read-write</access>
</field>
<field>
<name>EXCTRCENA</name>
<description>enable interrupt event tracing</description>
<bitOffset>16</bitOffset>
<bitWidth>1</bitWidth>
<access>read-write</access>
</field>
<field>
<name>Reserved_13_15</name>
<description>Reserved bits 13..15</description>
<bitOffset>13</bitOffset>
<bitWidth>3</bitWidth>
<access>read-write</access>
</field>
<field>
<name>PCSAMPLENA</name>
<description>enable POSTCNT as timer for PC sample packets</description>
<bitOffset>12</bitOffset>
<bitWidth>1</bitWidth>
<access>read-write</access>
</field>
<field>
<name>SYNCTAP</name>
<description>???</description>
<bitOffset>10</bitOffset>
<bitWidth>2</bitWidth>
<access>read-write</access>
</field>
<field>
<name>CYCTAP</name>
<description>???</description>
<bitOffset>9</bitOffset>
<bitWidth>1</bitWidth>
<access>read-write</access>
</field>
<field>
<name>POSTINIT</name>
<description>???</description>
<bitOffset>5</bitOffset>
<bitWidth>4</bitWidth>
<access>read-write</access>
</field>
<field>
<name>POSTPRESET</name>
<description>???</description>
<bitOffset>1</bitOffset>
<bitWidth>4</bitWidth>
<access>read-write</access>
</field>
<field>
<name>CYCCNTENA</name>
<description>enable cycle counter</description>
<bitOffset>0</bitOffset>
<bitWidth>1</bitWidth>
<access>read-write</access>
</field>
</fields>
</register>
<register>
<name>CYCCNT</name>
<description>Cycle Count Register</description>
<addressOffset>4</addressOffset>
<size>32</size>
</register>
<register>
<name>CPICNT</name>
<description>CPI Count Register</description>
<addressOffset>8</addressOffset>
<size>32</size>
</register>
<register>
<name>EXCCNT</name>
<description>Exception Overhead Count Register</description>
<addressOffset>0xC</addressOffset>
<size>32</size>
</register>
<register>
<name>SLEEPCNT</name>
<description>Sleep Count Register</description>
<addressOffset>0x10</addressOffset>
<size>32</size>
</register>
<register>
<name>LSUCNT</name>
<description>LSU Count Register</description>
<addressOffset>0x14</addressOffset>
<size>32</size>
</register>
<register>
<name>FOLDCNT</name>
<description>Folded-instruction Count Register</description>
<addressOffset>0x18</addressOffset>
<size>32</size>
</register>
<register>
<name>PCSR</name>
<description>Program Counter Sample Register</description>
<addressOffset>0x1C</addressOffset>
<size>32</size>
</register>
<register>
<name>COMP0</name>
<description>Comparator Register 0</description>
<addressOffset>0x20</addressOffset>
<size>32</size>
</register>
<register>
<name>MASK0</name>
<description>Mask Register 0</description>
<addressOffset>0x24</addressOffset>
<size>32</size>
</register>
<register>
<name>FUNCTION0</name>
<description>Function Register 0</description>
<addressOffset>0x28</addressOffset>
<size>32</size>
</register>
<register>
<name>RESERVED0</name>
<description>Reserved 0</description>
<addressOffset>0x2C</addressOffset>
<size>32</size>
</register>
<register>
<name>COMP1</name>
<description>Comparator Register 1</description>
<addressOffset>0x30</addressOffset>
<size>32</size>
</register>
<register>
<name>MASK1</name>
<description>Mask Register 1</description>
<addressOffset>0x34</addressOffset>
<size>32</size>
</register>
<register>
<name>FUNCTION1</name>
<description>Function Register 1</description>
<addressOffset>0x38</addressOffset>
<size>32</size>
</register>
<register>
<name>RESERVED1</name>
<description>Reserved 1</description>
<addressOffset>0x3C</addressOffset>
<size>32</size>
</register>
<register>
<name>COMP2</name>
<description>Comparator Register 2</description>
<addressOffset>0x40</addressOffset>
<size>32</size>
</register>
<register>
<name>MASK2</name>
<description>Mask Register 2</description>
<addressOffset>0x44</addressOffset>
<size>32</size>
</register>
<register>
<name>FUNCTION2</name>
<description>Function Register 2</description>
<addressOffset>0x48</addressOffset>
<size>32</size>
</register>
<register>
<name>RESERVED2</name>
<description>Reserved 2</description>
<addressOffset>0x4C</addressOffset>
<size>32</size>
</register>
<register>
<name>COMP3</name>
<description>Comparator Register 3</description>
<addressOffset>0x50</addressOffset>
<size>32</size>
</register>
<register>
<name>MASK3</name>
<description>Mask Register 3</description>
<addressOffset>0x54</addressOffset>
<size>32</size>
</register>
<register>
<name>FUNCTION3</name>
<description>Function Register 3</description>
<addressOffset>0x58</addressOffset>
<size>32</size>
</register>
</registers>
</peripheral>
<peripheral>
<name>ADC_Common</name>
<description>ADC common registers</description>
......
{
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
// List of extensions which should be recommended for users of this workspace.
"recommendations": [
"rust-lang.rust",
"matklad.rust-analyzer",
"marus25.cortex-debug",
],
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
"unwantedRecommendations": [
]
"unwantedRecommendations": []
}
\ No newline at end of file
......@@ -4,49 +4,58 @@
* https://marketplace.visualstudio.com/items?itemName=rust-lang.rust
* https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug
*/
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "cortex-debug",
"request": "launch",
"name": "Debug (QEMU)",
"servertype": "qemu",
"name": "Cortex Debug",
"servertype": "openocd",
"cwd": "${workspaceRoot}",
"preLaunchTask": "Cargo Build (debug)",
"preLaunchTask": "cargo build --examples",
"runToMain": true,
"executable": "./target/thumbv7m-none-eabi/debug/app",
/* Run `cargo build --example hello` and uncomment this line to run semi-hosting example */
//"executable": "./target/thumbv7m-none-eabi/debug/examples/hello",
"cpu": "cortex-m3",
"machine": "lm3s6965evb",
"svdFile": "${workspaceRoot}/.vscode/STM32F401.svd",
"configFiles": [
"interface/stlink-v2-1.cfg",
"target/stm32f4x.cfg"
],
"executable": "./target/thumbv7em-none-eabi/debug/examples/${fileBasenameNoExtension}",
"cpu": "cortex-m4",
},
{
/* Configuration for the STM32F303 Discovery board */
"type": "cortex-debug",
"request": "launch",
"name": "Debug (OpenOCD)",
"name": "Cortex Release",
"servertype": "openocd",
"cwd": "${workspaceRoot}",
"preLaunchTask": "Cargo Build (debug)",
"preLaunchTask": "cargo build --examples --release",
"runToMain": true,
"executable": "./target/thumbv7em-none-eabihf/debug/app",
/* Run `cargo build --example itm` and uncomment this line to run itm example */
// "executable": "./target/thumbv7em-none-eabihf/debug/examples/itm",
"device": "STM32F303VCT6",
"svdFile": "${workspaceRoot}/.vscode/STM32F401.svd",
"configFiles": [
"interface/stlink-v2-1.cfg",
"target/stm32f3x.cfg"
"target/stm32f4x.cfg"
],
"svdFile": "${workspaceRoot}/.vscode/STM32F303.svd",
"swoConfig": {
"enabled": true,
"cpuFrequency": 8000000,
"swoFrequency": 2000000,
"source": "probe",
"decoders": [
{ "type": "console", "label": "ITM", "port": 0 }
]
}
}
"executable": "./target/thumbv7em-none-eabi/release/examples/${fileBasenameNoExtension}",
"cpu": "cortex-m4",
},
{
"type": "cortex-debug",
"request": "launch",
"name": "Cortex Nightly",
"servertype": "openocd",
"cwd": "${workspaceRoot}",
"preLaunchTask": "cargo build --examples --release --nightly",
// "runToMain": true,
"svdFile": "${workspaceRoot}/.vscode/STM32F401.svd",
"configFiles": [
"interface/stlink-v2-1.cfg",
"target/stm32f4x.cfg"
],
"executable": "./target/thumbv7em-none-eabi/release/examples/${fileBasenameNoExtension}",
"cpu": "cortex-m4",
},
]
}
\ No newline at end of file
......@@ -4,60 +4,31 @@
"version": "2.0.0",
"tasks": [
{
/*
* This is the default cargo build task,
* but we need to provide a label for it,
* so we can invoke it from the debug launcher.
*/
"label": "Cargo Build (debug)",
"type": "process",
"command": "cargo",
"args": ["build"],
"type": "cargo",
"command": "build --example ${fileBasenameNoExtension}",
"problemMatcher": [
"$rustc"
],
"group": {
"kind": "build",
"isDefault": true
}
},
{
"label": "Cargo Build (release)",
"type": "process",
"command": "cargo",
"args": ["build", "--release"],
"problemMatcher": [
"$rustc"
],
"group": "build"
"group": "build",
"label": "cargo build --examples"
},
{
"label": "Cargo Build Examples (debug)",
"type": "process",
"command": "cargo",
"args": ["build","--examples"],
"type": "cargo",
"command": "build --example ${fileBasenameNoExtension} --release",
"problemMatcher": [
"$rustc"
],
"group": "build"
"group": "build",
"label": "cargo build --examples --release"
},
{
"label": "Cargo Build Examples (release)",
"type": "process",
"command": "cargo",
"args": ["build","--examples", "--release"],
"type": "cargo",
"command": "build --example ${fileBasenameNoExtension} --release --features nightly",
"problemMatcher": [
"$rustc"
],
"group": "build"
},
{
"label": "Cargo Clean",
"type": "process",
"command": "cargo",
"args": ["clean"],
"problemMatcher": [],
"group": "build"
},
"group": "build",
"label": "cargo build --examples --release --nightly"
}
]
}
\ No newline at end of file
......@@ -16,8 +16,8 @@ rtt-target = { version = "0.3.0", features = ["cortex-m"] }
# panic handlers
panic-halt = "0.2.0"
panic-semihosting = "0.5.6"
panic-rtt-target = { version = "0.1.1", features = ["cortex-m"] }
# panic-semihosting = "0.5.6"
# panic-rtt-target = { version = "0.1.1", features = ["cortex-m"] }
[dependencies.stm32f4]
version = "0.12.1"
......
......@@ -5,6 +5,7 @@
We assume Rust to be installed using [rustup](https://www.rust-lang.org/tools/install).
Additionally you need to install the `thumbv7em-none-eabi` target.
```shell
> rustup target add thumbv7em-none-eabi
```
......@@ -41,14 +42,36 @@ You may use any editor of choice. `vscode` supports Rust using the `rust-analyz
- `examples/rtt_timing.rs`
Here you will learn about cycle accurate timing measurements.
Here you will learn about cycle accurate timing measurements:
- Using instrumentation code (which introduces bloat and overhead).
- Non intrusive measurements using the on-chip debug unit and `gdb`.
- Code generation optimization
- Code generation optimization.
- Code inspection, `objdump`, debugging and interactive `disassemble`.
- Code trimming, RTIC is "A Zero-Cost Abstraction for Memory Safe Concurrency".
- `examples/timing_task.rs`
Here you learn about the Nested Vector Interrupt Controller (NVIC):
- Tasks are bound to interrupt vectors.
- Tasks can be pended either by code or by the environment (e.g. on arrival of serial data).
- The `bkpt` can be inserted in the code to trigger a breakpoint (useful to timing measurements).
- RTIC has zero-cost task dispatch overhead (well 2-clock cycles but will be fixed to zero).
- `examples/timing_resource.rs`
Here you will learn about resource handling in RTIC:
- Implementation of critical sections through priority masking (NVIC-BASEPRI).
- Direct access to non-preemptable resources.
- Comparison to threaded counterpart.
//! examples/timing_exam.rs
// #![deny(unsafe_code)]
// #![deny(warnings)]
#![no_main]
#![no_std]
use cortex_m::{asm, peripheral::DWT};
use panic_halt as _;
use rtic::cyccnt::{Duration, Instant, U32Ext};
use stm32f4::stm32f411;
#[no_mangle]
static mut T1_MAX_RP: u32 = 0;
#[no_mangle]
static mut T2_MAX_RP: u32 = 0;
#[no_mangle]
static mut T3_MAX_RP: u32 = 0;
#[rtic::app(device = stm32f411, monotonic = rtic::cyccnt::CYCCNT)]
const APP: () = {
struct Resources {
#[init(0)]
R1: u64, // non atomic data
#[init(0)]
R2: u64, // non atomic data
}
#[init(schedule = [t1, t2, t3])]
fn init(mut cx: init::Context) {
// Initialize (enable) the monotonic timer (CYCCNT)
cx.core.DCB.enable_trace();
cx.core.DWT.enable_cycle_counter();
cx.schedule.t1(cx.start + 100_000.cycles()).unwrap();
cx.schedule.t2(cx.start + 200_000.cycles()).unwrap();
cx.schedule.t3(cx.start + 50_000.cycles()).unwrap();
}
// Deadline 100, Inter-arrival 100
#[inline(never)]
#[task(schedule = [t1], priority = 1)]
fn t1(cx: t1::Context) {
asm::bkpt();
cx.schedule.t1(cx.scheduled + 100_000.cycles()).unwrap();
asm::bkpt();
// emulates timing behavior of t1
cortex_m::asm::delay(10_000);
asm::bkpt();
// 2) your code here to update T1_MAX_RP and
// break if deadline missed
}
// Deadline 200, Inter-arrival 200
#[inline(never)]
#[task(schedule = [t2], resources = [R1, R2], priority = 2)]
fn t2(cx: t2::Context) {
asm::bkpt();
cx.schedule.t2(cx.scheduled + 200_000.cycles()).unwrap();
asm::bkpt();
// 1) your code here to emulate timing behavior of t2
asm::bkpt();
// 2) your code here to update T2_MAX_RP and
// break if deadline missed
}
// Deadline 50, Inter-arrival 50
#[inline(never)]
#[task(schedule = [t3], resources = [R2], priority = 3)]
fn t3(cx: t3::Context) {
asm::bkpt();
cx.schedule.t3(cx.scheduled + 50_000.cycles()).unwrap();
asm::bkpt();
// 1) your code here to emulate timing behavior of t3
asm::bkpt();
// 2) your code here to update T3_MAX_RP and
// break if deadline missed
}
// RTIC requires that unused interrupts are declared in an extern block when
// using software tasks; these free interrupts will be used to dispatch the
// software tasks.
extern "C" {
fn EXTI0();
fn EXTI1();
fn EXTI2();
}
};
// !!!! NOTICE !!!!
//
// Use either vscode with the `Cortex Nightly` launch profile,
// or compile with the feature `--features nightly` in order to
// get inlined assembly!
//
// 1) For this assignment you should first generate a task set that
// matches the example task set from `klee_tutorial/srp_analysis/main.rs`.
//
// Assume that each time unit amounts to 1_000 clock cycles, then
// the execution time of `t1` should be 10_000 clock cycles.
//
// So, instead of measuring execution time of an existing application,
// you are to create a task set according to given timing properties.
//
// Do this naively, by just calling `asm::delay(x)`, where x
// amounts to the number of clock cycles to spend.
//
// Commit your repository once your task set is implemented.
//
// 2) Code instrumentation:
// Now its time to see if your scheduling analysis is accurate
// in comparison to a real running system.
//
// First explain in your own words how the `Instant` is
// used to generate a periodic task instance arrivals.
//
// `cx.schedule.t1(cx.scheduled + 100_000.cycles()).unwrap();`
//
// [Your answer here]
//
// Explain in your own words the difference between:
//
// `cx.schedule.t1(Instant::now() + 100_000.cycles()).unwrap();`
// and
// `cx.schedule.t1(cx.scheduled + 100_000.cycles()).unwrap();`
//
// [Your answer here]
//
// Explain in your own words why we use the latter
// in order to generate a periodic task.
//
// [Your answer here]
//
// Hint, look at https://rtic.rs/0.5/book/en/by-example/timer-queue.html
//
// Once you understand how `Instant` is used, document your crate:
// > cargo doc --open
//
// Once you have the documentation open, search for `Instant`
// Hint, you can search docs by pressing S.
//
// Now figure out how to calculate the actual response time.
// If the new response time is larger than the stored response time
// then update it (`T1_MAX_RP`, `T2_MAX_RP`, `T3_MAX_RP` respectively).
// If the response time is larger than the deadline, you should
// hit a `asm::bkpt()`, to indicate that an error occurred.
//
// You will need `unsafe` code to access the global variables.
//
// Explain why this is needed (there is a good reason for it).
//
// [Your answer here]
//
// Implement this functionality for all tasks.
//
// Commit your repository once you are done with the instrumentation.
//
// 3) Code Testing:
//
// Once the instrumentation code is in place, its finally time
// to test/probe/validate the system.
//
// Make sure that all tasks is initially scheduled from `init`.
//
// You can put WATCHES in vscode for the symbols
// WATCH
// `T1_MAX_RP`
// `T2_MAX_RP`
// `T3_MAX_RP`
// To see them being updated during the test.
//
// The first breakpoint hit should be:
// fn t3(cx: t3::Context) {
// asm::bkpt();
//
// Check the value of the CYCCNT register.
// (In vscode look under CORTEX PERIPHERALS > DWT > CYCCNT)
//
// Your values may differ slightly but should be in the same
// territory (if not, check your task implementation(s).)
//
// Task Entry Times, Task Nr, Response time Update
// 50240 t3 -
// 30362
// 100295 t3
// 30426
//
// 130595 t1
//
// At this point we can ask ourselves a number of
// interesting questions. Try answering in your own words.
//
// 3A) Why is there an offset 50240 (instead of 50000)?
//
// [Your answer here]
//
// 3B) Why is the calculated response time larger than the
// delays you inserted to simulate workload?
//
// [Your answer here]
//
// 3C) Why is the second arrival of `t3` further delayed?
//
// [Your answer here]
// Hint, think about what happens at time 100_000, what tasks
// are set to `arrive` at that point compared to time 50_000.
//
// 3D) What is the scheduled time for task `t1` (130595 is the
// measured time according to CYCYCNT).
//
// [Your answer here]
//
// Why is the measured value much higher than the scheduled time?
//
// [Your answer here]
//
// Now you can continue until you get a first update of `T1_MAX_RP`.
//
// What is the first update of `T1_MAX_RP`?
//
// [Your answer here]
//
// Explain the obtained value in terms of:
// Execution time, blocking and preemptions
// (that occurred for this task instance).
//
// [Your answer here]
//
// Now continue until you get a first timing measurement for `T2_MAX_RP`.
//
// What is the first update of `T2_MAX_RP`?
//
// [Your answer here]
//
// Now continue until you get a second timing measurement for `T1_MAX_RP`.
//
// What is the second update of `T3_MAX_RP`?
//
// [Your answer here]
//
// Now you should have ended up in a deadline miss right!!!!
//
// Why did this happen?
//
// [Your answer here]
//
// Compare that to the result obtained from your analysis tool.
//
// Do they differ, if so why?
//
// [Your answer here]
//
// Commit your repository once you completed this part.
//
// 4) Delay tuning.
//
// So there were some discrepancy between the timing properties
// introduced by the `delay::asm` and the real measurements.
//
// Adjust delays to compensate for the OH to make it fit to
// to the theoretical task set.
//
// In order to do so test each task individually, schedule ony one
// task from `init` at a time.
//
// You may need to insert additional breakpoints to tune the timing.
//
// Once you are convinced that each task now adheres to
// the timing specification you can re-run part 3.
//
// If some task still misses its deadline go back and adjust
// the timing until it just passes.
//
// Commit your tuned task set.
//
// 5) Final remarks and learning outcomes.
//
// This exercise is of course a bit contrived, in the normal case
// you would start out with a real task set and then pass it
// onto analysis.
//
// Essay question:
//
// Reflect in your own words on:
//
// - RTIC and scheduling overhead
// - Coupling in between theoretical model and measurements
// - How would an ideal tool for static analysis of RTIC models look like.
//
// [Your ideas and reflections here]
//
// Commit your thoughts, we will discuss further when we meet.
//! examples/timing_resources.rs
// #![deny(unsafe_code)]
// #![deny(warnings)]
#![deny(warnings)]
#![no_main]
#![no_std]
use core::ptr::read_volatile;
use cortex_m::{asm, peripheral::DWT};
use panic_halt as _;
use stm32f4::stm32f411;
......@@ -13,8 +12,10 @@ use stm32f4::stm32f411;
#[rtic::app(device = stm32f411)]
const APP: () = {
struct Resources {
// A resource
dwt: DWT,
#[init(0)]
shared: u64, // non atomic data
}
#[init]
......@@ -22,27 +23,32 @@ const APP: () = {
// Initialize (enable) the monotonic timer (CYCCNT)
cx.core.DCB.enable_trace();
cx.core.DWT.enable_cycle_counter();
rtic::pend(stm32f411::Interrupt::EXTI1);
init::LateResources { dwt: cx.core.DWT }
}
#[idle(resources = [dwt])]
fn idle(mut cx: idle::Context) -> ! {
#[task(binds = EXTI0, resources = [shared], priority = 2)]
fn exti0(cx: exti0::Context) {
asm::bkpt();
*cx.resources.shared += 1;
}
#[task(binds = EXTI1, resources = [dwt, shared], priority = 1)]
fn exti1(mut cx: exti1::Context) {
unsafe { cx.resources.dwt.cyccnt.write(0) };
asm::bkpt();
rtic::pend(stm32f411::Interrupt::EXTI0);
asm::bkpt();
loop {
continue;
}
}
#[task(binds = EXTI0)]
fn exti0(_cx: exti0::Context) {
cx.resources.shared.lock(|shared| {
// asm::bkpt();
*shared += 1;
// asm::bkpt();
});
asm::bkpt();
}
};
// Now we are going to have a look at the scheduling of RTIC tasks
// Now we are going to have a look at the resource management of RTIC.
//
// First create an objdump file:
// > cargo objdump --example timing_resources --release --features nightly -- --disassemble > timing_resources.objdump
......@@ -52,34 +58,35 @@ const APP: () = {
// You should find something like:
//
// 08000232 <EXTI0>:
// 8000232: 00 be bkpt #0
// 8000234: 00 20 movs r0, #0
// 8000236: 80 f3 11 88 msr basepri, r0
// 800023a: 70 47 bx lr
// 8000232: 40 f2 00 01 movw r1, #0
// 8000236: ef f3 11 80 mrs r0, basepri
// 800023a: 00 be bkpt #0
// 800023c: c2 f2 00 01 movt r1, #8192
// 8000240: d1 e9 00 23 ldrd r2, r3, [r1]
// 8000244: 01 32 adds r2, #1
// 8000246: 43 f1 00 03 adc r3, r3, #0
// 800024a: c1 e9 00 23 strd r2, r3, [r1]
// 800024e: 80 f3 11 88 msr basepri, r0
// 8000252: 70 47 bx lr
//
// Explain what is happening here in your own words.
//
// The application triggers the `exti0` task from `idle`, let's see
// how that pans out.
// [Your code here]
//
// > cargo run --example timing_resources --release --features nightly
// Then continue to the first breakpoint instruction:
// (gdb) c
// timing_resources::idle (cx=...) at examples/timing_resources.rs:32
// 32 asm::bkpt();
// Program
// received signal SIGTRAP, Trace/breakpoint trap.
// timing_resources::exti1 (cx=...) at examples/timing_resources.rs:39
// 39 asm::bkpt();
//
// (gdb) x 0xe0001004
// 0
//
// Here we see, that we have successfully set the cycle counter to zero.
// The `rtic::pend(stm32f411::Interrupt::EXTI0)` "emulates" the
// arrival/triggering of an external interrupt associated with
// the `exti0` task.
// 2
//
// (gdb) c
// timing_resources::APP::EXTI0 () at examples/timing_resources.rs:13
// 13 #[rtic::app(device = stm32f411)]
//
// Since `exti0` has a default prio = 1, it will preempt `idle` (at prio = 0),
// and the debugger breaks in the `exti0` task.
// received signal SIGTRAP, Trace/breakpoint trap.
// rtic::export::run<closure-0> (priority=2, f=...) at /home/pln/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-rtic-0.5.5/src/export.rs:38
//
// (gdb) x 0xe0001004
//
......@@ -92,9 +99,6 @@ const APP: () = {
// You should see that we hit the breakpoint in `exti0`, and
// that the code complies to the objdump EXTI disassembly.
//
// Confer to the document:
// https://community.arm.com/developer/ip-products/processors/b/processors-ip-blog/posts/beginner-guide-on-interrupt-latency-and-interrupt-latency-of-the-arm-cortex-m-processors
//
// What was the software latency observed to enter the task?
//
// [Your answer here]
......@@ -103,20 +107,160 @@ const APP: () = {
//
// [Your answer here]
//
// The debugger reports that the breakpoint was hit in the `run<closure>`.
// The reason is that the RTIC implements the actual interrupt handler,
// from within it calls a function `run` taking the user task as a function.
//
// (Functions in Rust can be seen as closures without captured variables.)
//
// Now we can continue to measure the round trip time.
//
// (gdb) c
// timing_resources::idle (cx=...) at examples/timing_resources.rs:34
// 34 asm::bkpt();
//
// received signal SIGTRAP, Trace/breakpoint trap.
// timing_resources::exti1 (cx=...) at examples/timing_resources.rs:41
// 41 asm::bkpt();
//
// (gdb) x 0xe0001004
//
// [Your answer here]
//
// You should have a total execution time in the range of 30-40 cycles.
//
// Explain the reason (for this case) that resource access in
// `exti0` was safe without locking the resource.
//
// [Your answer here]
//
// In `exti1` we also access `shared` but this time through a lock.
//
// (gdb) disassemble
// => 0x08000270 <+28>: bkpt 0x0000
// 0x08000272 <+30>: msr BASEPRI, r0
// 0x08000276 <+34>: movw r0, #0
// 0x0800027a <+38>: movt r0, #8192 ; 0x2000
// 0x0800027e <+42>: ldrd r2, r3, [r0]
// 0x08000282 <+46>: adds r2, #1
// 0x08000284 <+48>: adc.w r3, r3, #0
// 0x08000288 <+52>: strd r2, r3, [r0]
// 0x0800028c <+56>: movs r0, #240 ; 0xf0
// 0x0800028e <+58>: msr BASEPRI, r0
// 0x08000292 <+62>: bkpt 0x0000
// 0x08000294 <+64>: msr BASEPRI, r1
// 0x08000298 <+68>: bx lr
//
// We can now execute the code to the next breakpoint to get the
// execution time of the lock.
//
// (gdb) c
// received signal SIGTRAP, Trace/breakpoint trap.
// timing_resources::exti1 (cx=...) at examples/timing_resources.rs:47
// 47 asm::bkpt();
//
// (gdb) x 0xe0001004
//
// [Your answer here]
//
// Calculate the total time (in cycles), for this section of code.
//
// [Your answer here]
//
// You should get a value around 15 cycles.
//
// Now look at the "critical section", i.e., how many cycles
// are the lock held?
// To this end you need to insert `asm::bkpt()` on entry and exit
// inside the closure.
//
// cx.resources.shared.lock(|shared| {
// asm::bkpt();
// *shared += 1;
// asm::bkpt();
// });
//
// Change the code, and compile it from withing gdb
//
// If you debug in vscode, just Shift-F5 to terminate session, and F5 to start debugging.
//
// If debugging in terminal you may recompile without exiting the debug session:
//
// (gdb) shell cargo build --example timing_resources --release --features nightly
// Compiling app v0.1.0 (/home/pln/courses/e7020e/app)
// Finished release [optimized + debuginfo] target(s) in 0.32s
//
// and load the newly compiled executable:
// (gdb) load
// ...
// Transfer rate: 1 KB/sec, 406 bytes/write.
//
// Now you can continue until you hit the first breakpoint in the lock closure.
//
// (gdb) c
//
// received signal SIGTRAP, Trace/breakpoint trap.
// timing_resources::exti1::{{closure}} (shared=<optimized out>) at examples/timing_resources.rs:43
// 43 asm::bkpt();
//
// (gdb) x 0xe0001004
//
// [Your answer here]
//
// (gdb) c
//
// received signal SIGTRAP, Trace/breakpoint trap.
// timing_resources::exti1::{{closure}} (shared=0x20000000 <timing_resources::APP::shared>) at examples/timing_resources.rs:45
// 45 asm::bkpt();
//
// (gdb) x 0xe0001004
//
// [Your answer here]
//
// Looking at the EXTI0 (exti0) code, we see two additional
// instructions used to restore the BASEPRI register.
// This OH will be removed in next release of RTIC.
// So we can conclude RTIC to have a 2-cycle OH (in this case).
// (In the general case, as we will see later restoring BASEPRI
// is actually necessary so its just this corner case that is
// sub-optimal.)
// From a real-time perspective the critical section infers
// blocking (of higher priority tasks).
//
// How many clock cycles is the blocking?
//
// [Your answer here]
//
// Finally continue out of the closure.
//
// (gdb) c
// received signal SIGTRAP, Trace/breakpoint trap.
// timing_resources::exti1 (cx=...) at examples/timing_resources.rs:47
//
// (gdb) x 0xe0001004
//
// [Your answer here]
//
// This is the total execution time of:
//
// - pending a task `exti0` for execution
// - preempt `exti1`
// - inside `exti0` safely access and update a shared (non atomic) resource.
// - returning to `exti1`
// - inside `exti1` safely access and update a shared (non atomic) resource
//
// Notice here, the breakpoints infer some OH and may disable
// some potential LLVM optimizations, so we obtain a "safe" (pessimistic) estimate.
//
// http://www.diva-portal.se/smash/get/diva2:1005680/FULLTEXT01.pdf
//
// You find a comparison to a typical threaded counterpart `freeRTOS` in Table 1.
//
// Give a rough estimate based on this info how long the complete task `exti1`,
// would take to execute if written in FreeRTOS. (Include the context switch, to higher
// priority task, the mutex lock/unlock in both "threads".)
//
// Motivate your answer (not just a number).
//
// [Your answer here]
//
// Notice, the Rust implementation is significantly faster than the C code version
// of Real-Time For the Masses back in 2013.
//
// Why do you think RTIC + Rust + LLVM can do a better job than hand written
// C code + Macros + gcc?
//
// (Hint, what possible optimization can safely be applied by RTIC + Rust + LLVM.)
//
// [Your answer here]
//! examples/timing_resources.rs
// #![deny(unsafe_code)]
#![deny(warnings)]
#![no_main]
#![no_std]
use cortex_m::peripheral::DWT;
//use cortex_m::{asm, peripheral::DWT};
use panic_halt as _;
use stm32f4::stm32f411;
#[rtic::app(device = stm32f411)]
const APP: () = {
struct Resources {
dwt: DWT,
#[init(0)]
shared: u64, // non atomic data
}
#[init]
fn init(mut cx: init::Context) -> init::LateResources {
// Initialize (enable) the monotonic timer (CYCCNT)
cx.core.DCB.enable_trace();
cx.core.DWT.enable_cycle_counter();
init::LateResources { dwt: cx.core.DWT }
}
#[idle(resources = [shared])]
fn idle(_cx: idle::Context) -> ! {
// unsafe { cx.resources.dwt.cyccnt.write(0) };
// // asm::bkpt();
// rtic::pend(stm32f411::Interrupt::EXTI0);
// // asm::bkpt();
// cx.resources.shared.lock(|shared| {
// // asm::bkpt();
// *shared += 1;
// // asm::bkpt();
// });
// asm::bkpt();
loop {
continue;
}
}
#[task(binds = EXTI0, resources = [shared], priority = 2)]
fn exti0(cx: exti0::Context) {
// asm::bkpt();
*cx.resources.shared += 1;
}
#[task(binds = EXTI1, resources = [dwt, shared], priority = 1)]
fn exti1(mut cx: exti1::Context) {
unsafe { cx.resources.dwt.cyccnt.write(0) };
// asm::bkpt();
rtic::pend(stm32f411::Interrupt::EXTI0);
// asm::bkpt();
cx.resources.shared.lock(|shared| {
// asm::bkpt();
*shared += 1;
// asm::bkpt();
});
// asm::bkpt();
}
};
// Now we are going to have a look at the resource management of RTIC.
//
// First create an objdump file:
// > cargo objdump --example timing_resources --release --features nightly -- --disassemble > timing_resources.objdump
//
// Lookup the EXTI0 symbol (RTIC binds the exti0 task to the interrupt vector).
//
// You should find something like:
//
// 080002b6 <EXTI0>:
// 80002b6: 40 f2 00 00 movw r0, #0
// 80002ba: 00 be bkpt #0
// 80002bc: c2 f2 00 00 movt r0, #8192
// 80002c0: d0 e9 00 12 ldrd r1, r2, [r0]
// 80002c4: 01 31 adds r1, #1
// 80002c6: 42 f1 00 02 adc r2, r2, #0
// 80002ca: c0 e9 00 12 strd r1, r2, [r0]
// 80002ce: 00 20 movs r0, #0
// 80002d0: 80 f3 11 88 msr basepri, r0
// 80002d4: 70 47 bx lr
//
// Explain what is happening here in your own words.
//
// [Your code here]
//
// > cargo run --example timing_resources --release --features nightly
// Then continue to the first breakpoint instruction:
// (gdb) c
// timing_resources::idle (cx=...) at examples/timing_resources.rs:32
// 32 asm::bkpt();
//
// (gdb) x 0xe0001004
// 0
//
// (gdb) c
// timing_resources::exti0 (cx=...) at examples/timing_resources.rs:44
// 44 asm::bkpt();
//
// (gdb) x 0xe0001004
//
// [Your answer here]
//
// (gdb) disassemble
//
// [Your answer here]
//
// You should see that we hit the breakpoint in `exti0`, and
// that the code complies to the objdump EXTI disassembly.
//
// What was the software latency observed to enter the task?
//
// [Your answer here]
//
// Does RTIC infer any overhead?
//
// [Your answer here]
//
// Now we can continue to measure the round trip time.
//
// (gdb) c
//
// (gdb) x 0xe0001004
// timing_resources::idle (cx=...) at examples/timing_resources.rs:34
// 34 asm::bkpt();
//
// [Your answer here]
//
// You should have a total execution time in the range of 30 cycles.
//
// Explain the reason (for this case) that resource access in
// `exti0` was safe without locking the resource.
//
// [Your answer here]
//
// In `idle` we also access `shared` but this time through a lock.
//
// (gdb) disassemble
// => 0x0800026e <+26>: bkpt 0x0000
// 0x08000270 <+28>: ldrb r2, [r0, #0]
// 0x08000272 <+30>: cbz r2, 0x800028c <timing_resources::idle+56>
// 0x08000274 <+32>: movw r0, #0
// 0x08000278 <+36>: movt r0, #8192 ; 0x2000
// 0x0800027c <+40>: ldrd r1, r2, [r0]
// 0x08000280 <+44>: adds r1, #1
// 0x08000282 <+46>: adc.w r2, r2, #0
// 0x08000286 <+50>: strd r1, r2, [r0]
// 0x0800028a <+54>: b.n 0x80002b2 <timing_resources::idle+94>
// 0x0800028c <+56>: movs r2, #1
// 0x0800028e <+58>: movw r12, #0
// 0x08000292 <+62>: strb r2, [r0, #0]
// 0x08000294 <+64>: movs r2, #240 ; 0xf0
// 0x08000296 <+66>: msr BASEPRI, r2
// 0x0800029a <+70>: movt r12, #8192 ; 0x2000
// 0x0800029e <+74>: ldrd r3, r2, [r12]
// 0x080002a2 <+78>: adds r3, #1
// 0x080002a4 <+80>: adc.w r2, r2, #0
// 0x080002a8 <+84>: strd r3, r2, [r12]
// 0x080002ac <+88>: msr BASEPRI, r1
// 0x080002b0 <+92>: strb r1, [r0, #0]
// 0x080002b2 <+94>: bkpt 0x0000
//
// We can now execute the code to the next breakpoint to get the
// execution time of the lock.
//
// (gdb) c
// timing_resources::idle (cx=...) at examples/timing_resources.rs:36
// 36 asm::bkpt();
//
// (gdb) x 0xe0001004
//
// [Your answer here]
//
// Calculate the total time (in cycles), for this section of code.
//
// [Your answer here]
//
// You should get a value around 25 cycles.
//
// Now look at the "critical section", i.e., how many cycles
// are the lock held?
// To this end you need to insert `asm::bkpt()` on entry and exit
// inside the closure.
//
// cx.resources.shared.lock(|shared| {
// asm::bkpt();
// *shared += 1;
// asm::bkpt();
// });
//
// Change the code, and compile it from withing gdb
// (gdb) shell cargo build --example timing_resources --release --features nightly
// Compiling app v0.1.0 (/home/pln/courses/e7020e/app)
// Finished release [optimized + debuginfo] target(s) in 0.32s
//
// and load the newly compiled executable:
// (gdb) load
// ...
// Transfer rate: 1 KB/sec, 406 bytes/write.
//
// Now you can continue until you hit the first breakpoint in the lock closure.
//
// (gdb) c
// rtic::export::lock<u64,(),closure-0> (ptr=<optimized out>, priority=0x2000ffef, ceiling=1, nvic_prio_bits=4, f=...) at /home/pln/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-0.6.4/src/asm.rs:11
// 11 () => unsafe { llvm_asm!("bkpt" :::: "volatile") },
//
// (gdb) x 0xe0001004
//
// [Your answer here]
//
// (gdb) c
// rtic::export::lock<u64,(),closure-0> (ptr=<optimized out>, priority=0x2000ffef, ceiling=1, nvic_prio_bits=4, f=...) at /home/pln/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-0.6.4/src/asm.rs:11
// 11 () => unsafe { llvm_asm!("bkpt" :::: "volatile") },
//
// (gdb) x 0xe0001004
//
// [Your answer here]
//
// From a real-time perspective the critical section infers
// blocking (of higher priority tasks).
//
// How many clock cycles is the blocking?
//
// [Your answer here]
//
// Finally continue out of the closure.
//
// (gdb) c
// timing_resources::idle (cx=...) at examples/timing_resources.rs:40
// 40 asm::bkpt();
//
// (gdb) x 0xe0001004
//
// [Your answer here]
//
// This is the total execution time of.
//
// - pending a task `exti` for execution
// - preempt `idle`
// - inside `exti` safely access and update a shared (non atomic resource).
// - returning to `idle`
// - safely access and update a shared (non atomic) resource
//
// Notice here, the breakpoints infer some OH and may disable
// some potential LLVM optimizations.
//
//
......@@ -75,7 +75,7 @@ const APP: () = {
// timing_task::APP::EXTI0 () at examples/timing_task.rs:11
// 11 #[rtic::app(device = stm32f411)]
//
// Since `exti0` has a default prio = 1, it will preempt `idle` (at prio = 0),
// Since `exti0` has a default priority = 1, it will preempt `idle` (at priority = 0),
// and the debugger breaks in the `exti0` task.
// (Notice, RTIC translates logical priorities to hw priorities for you.)
//
......
......@@ -5,8 +5,8 @@
#![no_main]
#![no_std]
// use panic_halt as _;
use panic_rtt_target as _;
use panic_halt as _;
// use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use stm32f4;
......