aboutsummaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
Diffstat (limited to 'examples')
-rw-r--r--examples/nrf52840-edf/.cargo/config.toml9
-rw-r--r--examples/nrf52840-edf/Cargo.toml27
-rw-r--r--examples/nrf52840-edf/build.rs35
-rw-r--r--examples/nrf52840-edf/memory.x12
-rw-r--r--examples/nrf52840-edf/src/bin/basic.rs194
5 files changed, 277 insertions, 0 deletions
diff --git a/examples/nrf52840-edf/.cargo/config.toml b/examples/nrf52840-edf/.cargo/config.toml
new file mode 100644
index 000000000..e0b9ce59e
--- /dev/null
+++ b/examples/nrf52840-edf/.cargo/config.toml
@@ -0,0 +1,9 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace nRF82840_xxAA with your chip as listed in `probe-rs chip list`
3runner = "probe-rs run --chip nRF52840_xxAA"
4
5[build]
6target = "thumbv7em-none-eabi"
7
8[env]
9DEFMT_LOG = "debug"
diff --git a/examples/nrf52840-edf/Cargo.toml b/examples/nrf52840-edf/Cargo.toml
new file mode 100644
index 000000000..1e8803233
--- /dev/null
+++ b/examples/nrf52840-edf/Cargo.toml
@@ -0,0 +1,27 @@
1[package]
2edition = "2021"
3name = "embassy-nrf52840-edf-examples"
4version = "0.1.0"
5license = "MIT OR Apache-2.0"
6publish = false
7
8[dependencies]
9# NOTE: "scheduler-deadline" and "embassy-time-driver" features are enabled
10embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "scheduler-deadline", "embassy-time-driver"] }
11embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
12embassy-nrf = { version = "0.7.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] }
13
14defmt = "1.0.1"
15defmt-rtt = "1.0.0"
16
17cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
18cortex-m-rt = "0.7.0"
19panic-probe = { version = "1.0.0", features = ["print-defmt"] }
20
21[profile.release]
22debug = 2
23
24[package.metadata.embassy]
25build = [
26 { target = "thumbv7em-none-eabi", artifact-dir = "out/examples/nrf52840-edf" }
27]
diff --git a/examples/nrf52840-edf/build.rs b/examples/nrf52840-edf/build.rs
new file mode 100644
index 000000000..30691aa97
--- /dev/null
+++ b/examples/nrf52840-edf/build.rs
@@ -0,0 +1,35 @@
1//! This build script copies the `memory.x` file from the crate root into
2//! a directory where the linker can always find it at build time.
3//! For many projects this is optional, as the linker always searches the
4//! project root directory -- wherever `Cargo.toml` is. However, if you
5//! are using a workspace or have a more complicated build setup, this
6//! build script becomes required. Additionally, by requesting that
7//! Cargo re-run the build script whenever `memory.x` is changed,
8//! updating `memory.x` ensures a rebuild of the application with the
9//! new memory settings.
10
11use std::env;
12use std::fs::File;
13use std::io::Write;
14use std::path::PathBuf;
15
16fn main() {
17 // Put `memory.x` in our output directory and ensure it's
18 // on the linker search path.
19 let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
20 File::create(out.join("memory.x"))
21 .unwrap()
22 .write_all(include_bytes!("memory.x"))
23 .unwrap();
24 println!("cargo:rustc-link-search={}", out.display());
25
26 // By default, Cargo will re-run a build script whenever
27 // any file in the project changes. By specifying `memory.x`
28 // here, we ensure the build script is only re-run when
29 // `memory.x` is changed.
30 println!("cargo:rerun-if-changed=memory.x");
31
32 println!("cargo:rustc-link-arg-bins=--nmagic");
33 println!("cargo:rustc-link-arg-bins=-Tlink.x");
34 println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
35}
diff --git a/examples/nrf52840-edf/memory.x b/examples/nrf52840-edf/memory.x
new file mode 100644
index 000000000..15b492bce
--- /dev/null
+++ b/examples/nrf52840-edf/memory.x
@@ -0,0 +1,12 @@
1MEMORY
2{
3 /* NOTE 1 K = 1 KiBi = 1024 bytes */
4 FLASH : ORIGIN = 0x00000000, LENGTH = 1024K
5 RAM : ORIGIN = 0x20000000, LENGTH = 256K
6
7 /* These values correspond to the NRF52840 with Softdevices S140 7.3.0 */
8 /*
9 FLASH : ORIGIN = 0x00027000, LENGTH = 868K
10 RAM : ORIGIN = 0x20020000, LENGTH = 128K
11 */
12}
diff --git a/examples/nrf52840-edf/src/bin/basic.rs b/examples/nrf52840-edf/src/bin/basic.rs
new file mode 100644
index 000000000..d888e17d1
--- /dev/null
+++ b/examples/nrf52840-edf/src/bin/basic.rs
@@ -0,0 +1,194 @@
1//! Basic side-by-side example of the Earliest Deadline First scheduler
2//!
3//! This test spawns a number of background "ambient system load" workers
4//! that are constantly working, and runs two sets of trials.
5//!
6//! The first trial runs with no deadline set, so our trial task is at the
7//! same prioritization level as the background worker tasks.
8//!
9//! The second trial sets a deadline, meaning that it will be given higher
10//! scheduling priority than background tasks, that have no deadline set
11
12#![no_std]
13#![no_main]
14
15use core::sync::atomic::{compiler_fence, Ordering};
16
17use defmt::unwrap;
18use embassy_executor::Spawner;
19use embassy_time::{Duration, Instant, Timer};
20use {defmt_rtt as _, panic_probe as _};
21
22#[embassy_executor::main]
23async fn main(spawner: Spawner) {
24 embassy_nrf::init(Default::default());
25
26 // Enable flash cache to remove some flash latency jitter
27 compiler_fence(Ordering::SeqCst);
28 embassy_nrf::pac::NVMC.icachecnf().write(|w| {
29 w.set_cacheen(true);
30 });
31 compiler_fence(Ordering::SeqCst);
32
33 //
34 // Baseline system load tunables
35 //
36
37 // how many load tasks? More load tasks means more tasks contending
38 // for the runqueue
39 let tasks = 32;
40 // how long should each task work for? The longer the working time,
41 // the longer the max jitter possible, even when a task is prioritized,
42 // as EDF is still cooperative and not pre-emptive
43 //
44 // 33 ticks ~= 1ms
45 let work_time_ticks = 33;
46 // what fraction, 1/denominator, should the system be busy?
47 // bigger number means **less** busy
48 //
49 // 2 => 50%
50 // 4 => 25%
51 // 10 => 10%
52 let denominator = 2;
53
54 // Total time window, so each worker is working 1/denominator
55 // amount of the total time
56 let time_window = work_time_ticks * u64::from(tasks) * denominator;
57
58 // Spawn all of our load workers!
59 for i in 0..tasks {
60 spawner.spawn(unwrap!(load_task(i, work_time_ticks, time_window)));
61 }
62
63 // Let all the tasks spin up
64 defmt::println!("Spinning up load tasks...");
65 Timer::after_secs(1).await;
66
67 //
68 // Trial task worker tunables
69 //
70
71 // How many steps should the workers under test run?
72 // More steps means more chances to have to wait for other tasks
73 // in line ahead of us.
74 let num_steps = 100;
75
76 // How many ticks should the worker take working on each step?
77 //
78 // 33 ticks ~= 1ms
79 let work_ticks = 33;
80 // How many ticks should the worker wait on each step?
81 //
82 // 66 ticks ~= 2ms
83 let idle_ticks = 66;
84
85 // How many times to repeat each trial?
86 let trials = 3;
87
88 // The total time a trial would take, in a perfect unloaded system
89 let theoretical = (num_steps * work_ticks) + (num_steps * idle_ticks);
90
91 defmt::println!("");
92 defmt::println!("Starting UNPRIORITIZED worker trials");
93 for _ in 0..trials {
94 //
95 // UNPRIORITIZED worker
96 //
97 defmt::println!("");
98 defmt::println!("Starting unprioritized worker");
99 let start = Instant::now();
100 for _ in 0..num_steps {
101 let now = Instant::now();
102 while now.elapsed().as_ticks() < work_ticks {}
103 Timer::after_ticks(idle_ticks).await;
104 }
105 let elapsed = start.elapsed().as_ticks();
106 defmt::println!(
107 "Trial complete, theoretical ticks: {=u64}, actual ticks: {=u64}",
108 theoretical,
109 elapsed
110 );
111 let ratio = ((elapsed as f32) / (theoretical as f32)) * 100.0;
112 defmt::println!("Took {=f32}% of ideal time", ratio);
113 Timer::after_millis(500).await;
114 }
115
116 Timer::after_secs(1).await;
117
118 defmt::println!("");
119 defmt::println!("Starting PRIORITIZED worker trials");
120 for _ in 0..trials {
121 //
122 // PRIORITIZED worker
123 //
124 defmt::println!("");
125 defmt::println!("Starting prioritized worker");
126 let start = Instant::now();
127 // Set the deadline to ~2x the theoretical time. In practice, setting any deadline
128 // here elevates the current task above all other worker tasks.
129 let meta = embassy_executor::Metadata::for_current_task().await;
130 meta.set_deadline_after(theoretical * 2);
131
132 // Perform the trial
133 for _ in 0..num_steps {
134 let now = Instant::now();
135 while now.elapsed().as_ticks() < work_ticks {}
136 Timer::after_ticks(idle_ticks).await;
137 }
138
139 let elapsed = start.elapsed().as_ticks();
140 defmt::println!(
141 "Trial complete, theoretical ticks: {=u64}, actual ticks: {=u64}",
142 theoretical,
143 elapsed
144 );
145 let ratio = ((elapsed as f32) / (theoretical as f32)) * 100.0;
146 defmt::println!("Took {=f32}% of ideal time", ratio);
147
148 // Unset the deadline, deadlines are not automatically cleared, and if our
149 // deadline is in the past, then we get very high priority!
150 meta.unset_deadline();
151
152 Timer::after_millis(500).await;
153 }
154
155 defmt::println!("");
156 defmt::println!("Trials Complete.");
157}
158
159#[embassy_executor::task(pool_size = 32)]
160async fn load_task(id: u32, ticks_on: u64, ttl_ticks: u64) {
161 let mut last_print = Instant::now();
162 let mut last_tick = last_print;
163 let mut variance = 0;
164 let mut max_variance = 0;
165 loop {
166 let tgt = last_tick + Duration::from_ticks(ttl_ticks);
167 assert!(tgt > Instant::now(), "fell too behind!");
168
169 Timer::at(tgt).await;
170 let now = Instant::now();
171 // How late are we from the target?
172 let var = now.duration_since(tgt).as_ticks();
173 max_variance = max_variance.max(var);
174 variance += var;
175
176 // blocking work
177 while now.elapsed().as_ticks() < ticks_on {}
178
179 if last_print.elapsed() >= Duration::from_secs(1) {
180 defmt::trace!(
181 "Task {=u32} variance ticks (1s): {=u64}, max: {=u64}, act: {=u64}",
182 id,
183 variance,
184 max_variance,
185 ticks_on,
186 );
187 max_variance = 0;
188 variance = 0;
189 last_print = Instant::now();
190 }
191
192 last_tick = tgt;
193 }
194}