aboutsummaryrefslogtreecommitdiff
path: root/examples/nrf52840-edf/src/bin/basic.rs
diff options
context:
space:
mode:
Diffstat (limited to 'examples/nrf52840-edf/src/bin/basic.rs')
-rw-r--r--examples/nrf52840-edf/src/bin/basic.rs194
1 files changed, 194 insertions, 0 deletions
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}