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.rs191
1 files changed, 191 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..6a7eb3c4b
--- /dev/null
+++ b/examples/nrf52840-edf/src/bin/basic.rs
@@ -0,0 +1,191 @@
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};
16use embassy_executor::{raw::Deadline, Spawner};
17use embassy_time::{Duration, Instant, Timer};
18use {defmt_rtt as _, panic_probe as _};
19
20#[embassy_executor::main]
21async fn main(spawner: Spawner) {
22 embassy_nrf::init(Default::default());
23
24 // Enable flash cache to remove some flash latency jitter
25 compiler_fence(Ordering::SeqCst);
26 embassy_nrf::pac::NVMC.icachecnf().write(|w| {
27 w.set_cacheen(true);
28 });
29 compiler_fence(Ordering::SeqCst);
30
31 //
32 // Baseline system load tunables
33 //
34
35 // how many load tasks? More load tasks means more tasks contending
36 // for the runqueue
37 let tasks = 32;
38 // how long should each task work for? The longer the working time,
39 // the longer the max jitter possible, even when a task is prioritized,
40 // as EDF is still cooperative and not pre-emptive
41 //
42 // 33 ticks ~= 1ms
43 let work_time_ticks = 33;
44 // what fraction, 1/denominator, should the system be busy?
45 // bigger number means **less** busy
46 //
47 // 2 => 50%
48 // 4 => 25%
49 // 10 => 10%
50 let denominator = 2;
51
52 // Total time window, so each worker is working 1/denominator
53 // amount of the total time
54 let time_window = work_time_ticks * u64::from(tasks) * denominator;
55
56 // Spawn all of our load workers!
57 for i in 0..tasks {
58 spawner.must_spawn(load_task(i, work_time_ticks, time_window));
59 }
60
61 // Let all the tasks spin up
62 defmt::println!("Spinning up load tasks...");
63 Timer::after_secs(1).await;
64
65 //
66 // Trial task worker tunables
67 //
68
69 // How many steps should the workers under test run?
70 // More steps means more chances to have to wait for other tasks
71 // in line ahead of us.
72 let num_steps = 100;
73
74 // How many ticks should the worker take working on each step?
75 //
76 // 33 ticks ~= 1ms
77 let work_ticks = 33;
78 // How many ticks should the worker wait on each step?
79 //
80 // 66 ticks ~= 2ms
81 let idle_ticks = 66;
82
83 // How many times to repeat each trial?
84 let trials = 3;
85
86 // The total time a trial would take, in a perfect unloaded system
87 let theoretical = (num_steps * work_ticks) + (num_steps * idle_ticks);
88
89 defmt::println!("");
90 defmt::println!("Starting UNPRIORITIZED worker trials");
91 for _ in 0..trials {
92 //
93 // UNPRIORITIZED worker
94 //
95 defmt::println!("");
96 defmt::println!("Starting unprioritized worker");
97 let start = Instant::now();
98 for _ in 0..num_steps {
99 let now = Instant::now();
100 while now.elapsed().as_ticks() < work_ticks {}
101 Timer::after_ticks(idle_ticks).await;
102 }
103 let elapsed = start.elapsed().as_ticks();
104 defmt::println!(
105 "Trial complete, theoretical ticks: {=u64}, actual ticks: {=u64}",
106 theoretical,
107 elapsed
108 );
109 let ratio = ((elapsed as f32) / (theoretical as f32)) * 100.0;
110 defmt::println!("Took {=f32}% of ideal time", ratio);
111 Timer::after_millis(500).await;
112 }
113
114 Timer::after_secs(1).await;
115
116 defmt::println!("");
117 defmt::println!("Starting PRIORITIZED worker trials");
118 for _ in 0..trials {
119 //
120 // PRIORITIZED worker
121 //
122 defmt::println!("");
123 defmt::println!("Starting prioritized worker");
124 let start = Instant::now();
125 // Set the deadline to ~2x the theoretical time. In practice, setting any deadline
126 // here elevates the current task above all other worker tasks.
127 Deadline::set_current_task_deadline_after(theoretical * 2).await;
128
129 // Perform the trial
130 for _ in 0..num_steps {
131 let now = Instant::now();
132 while now.elapsed().as_ticks() < work_ticks {}
133 Timer::after_ticks(idle_ticks).await;
134 }
135
136 let elapsed = start.elapsed().as_ticks();
137 defmt::println!(
138 "Trial complete, theoretical ticks: {=u64}, actual ticks: {=u64}",
139 theoretical,
140 elapsed
141 );
142 let ratio = ((elapsed as f32) / (theoretical as f32)) * 100.0;
143 defmt::println!("Took {=f32}% of ideal time", ratio);
144
145 // Unset the deadline, deadlines are not automatically cleared, and if our
146 // deadline is in the past, then we get very high priority!
147 Deadline::clear_current_task_deadline().await;
148
149 Timer::after_millis(500).await;
150 }
151
152 defmt::println!("");
153 defmt::println!("Trials Complete.");
154}
155
156#[embassy_executor::task(pool_size = 32)]
157async fn load_task(id: u32, ticks_on: u64, ttl_ticks: u64) {
158 let mut last_print = Instant::now();
159 let mut last_tick = last_print;
160 let mut variance = 0;
161 let mut max_variance = 0;
162 loop {
163 let tgt = last_tick + Duration::from_ticks(ttl_ticks);
164 assert!(tgt > Instant::now(), "fell too behind!");
165
166 Timer::at(tgt).await;
167 let now = Instant::now();
168 // How late are we from the target?
169 let var = now.duration_since(tgt).as_ticks();
170 max_variance = max_variance.max(var);
171 variance += var;
172
173 // blocking work
174 while now.elapsed().as_ticks() < ticks_on {}
175
176 if last_print.elapsed() >= Duration::from_secs(1) {
177 defmt::trace!(
178 "Task {=u32} variance ticks (1s): {=u64}, max: {=u64}, act: {=u64}",
179 id,
180 variance,
181 max_variance,
182 ticks_on,
183 );
184 max_variance = 0;
185 variance = 0;
186 last_print = Instant::now();
187 }
188
189 last_tick = tgt;
190 }
191}