Skip to content

Commit eda3a85

Browse files
committed
tests: timer: Create timer test
Create a test to exercise the zephyr::timer timers. This uses both SimpleTimer and CallbackTimer intensely for 5 seconds and prints out the results. Signed-off-by: David Brown <[email protected]>
1 parent daa6f98 commit eda3a85

File tree

5 files changed

+196
-0
lines changed

5 files changed

+196
-0
lines changed

tests/timer/CMakeLists.txt

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
cmake_minimum_required(VERSION 3.20.0)
4+
5+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
6+
project(timer_rust)
7+
8+
rust_cargo_application()

tests/timer/Cargo.toml

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Copyright (c) 2024 Linaro LTD
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
[package]
5+
# This must be rustapp for now.
6+
name = "rustapp"
7+
version = "3.7.0"
8+
edition = "2021"
9+
description = "Tests of timeers"
10+
license = "Apache-2.0 or MIT"
11+
12+
[lib]
13+
crate-type = ["staticlib"]
14+
15+
[dependencies]
16+
rand = { version = "0.8", default-features = false }
17+
rand_pcg = { version = "0.3.1", default-features = false }
18+
zephyr = "3.7.0"

tests/timer/prj.conf

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Copyright (c) 2024 Linaro LTD
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
CONFIG_RUST=y
5+
CONFIG_MAIN_STACK_SIZE=2048
6+
7+
# Timers need alloc
8+
CONFIG_RUST_ALLOC=y

tests/timer/src/lib.rs

+145
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
// Copyright (c) 2024 Linaro LTD
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
#![no_std]
5+
6+
extern crate alloc;
7+
8+
use core::{pin::Pin, sync::atomic::Ordering};
9+
10+
use alloc::{boxed::Box, vec::Vec};
11+
use rand::Rng;
12+
use rand_pcg::Pcg32;
13+
use zephyr::{
14+
printkln, sync::{atomic::AtomicUsize, Arc}, time::{Duration, NoWait, Tick}, timer::{Callback, CallbackTimer, SimpleTimer, StoppedTimer}
15+
};
16+
17+
// Test the timers interface. There are a couple of things this tries to test:
18+
// 1. Do timers dynamically allocated and dropped work.
19+
// 2. Can simple timers count properly.
20+
// 3. Can we wait on a Simple timer.
21+
// 4. Do callbacks work with messages and semaphores.
22+
23+
#[no_mangle]
24+
extern "C" fn rust_main() {
25+
printkln!("Tick frequency: {}", zephyr::time::SYS_FREQUENCY);
26+
timer_test();
27+
printkln!("All tests passed");
28+
}
29+
30+
fn timer_test() {
31+
let mut rng = Pcg32::new(1, 1);
32+
33+
// Track a global "stop" time when the entire test should be shut down.
34+
// let mut total_test = StoppedTimer::new().start_simple(Duration::secs_at_least(5), NoWait);
35+
let mut total_test = StoppedTimer::new().start_simple(Duration::secs_at_least(5), NoWait);
36+
37+
// This simple timer lets us pause periodically to allow other timers to build up.
38+
let mut period = StoppedTimer::new().start_simple(
39+
Duration::millis_at_least(100),
40+
Duration::millis_at_least(100),
41+
);
42+
43+
let mut simples: Vec<_> = (0..10).map(|_| TestSimple::new(&mut rng)).collect();
44+
let atomics: Vec<_> = (0..10).map(|_| TestAtomic::new(&mut rng)).collect();
45+
46+
let mut count = 0;
47+
loop {
48+
// Wait for the period timer.
49+
let num = period.read_count_wait();
50+
51+
if num > 1 {
52+
// Getting this is actually a good indicator that we've overwhelmed ourselves with
53+
// timers, and are stress testing things.
54+
printkln!("Note: Missed period ticks");
55+
}
56+
57+
count += 1;
58+
59+
if count % 10 == 0 {
60+
printkln!("Ticks {}", count);
61+
}
62+
63+
if total_test.read_count() > 0 {
64+
break;
65+
}
66+
67+
simples.iter_mut().for_each(|m| m.update());
68+
}
69+
70+
// Collect all of the times they fired.
71+
let simple_count: usize = simples.iter().map(|s| s.count).sum();
72+
printkln!("Simple fired {} times", simple_count);
73+
let atomic_count: usize = atomics.iter().map(|s| s.count()).sum();
74+
printkln!("Atomics fired {} times", atomic_count);
75+
76+
printkln!("Period ticks: {}", count);
77+
78+
// Now that everything is done and cleaned up, allow a little time to pass to make sure there
79+
// are no stray timers. We can re-use the total test timer.
80+
let mut total_test = total_test.stop().start_simple(Duration::millis_at_least(1), NoWait);
81+
total_test.read_count_wait();
82+
}
83+
84+
/// Test a SimpleTimer.
85+
///
86+
/// This allocates a simple timer, and starts it with a small somewhat random period. It will track
87+
/// the total number of times that it fires when checked.
88+
struct TestSimple {
89+
timer: SimpleTimer,
90+
_delay: Tick,
91+
count: usize,
92+
}
93+
94+
impl TestSimple {
95+
fn new(rng: &mut impl Rng) -> TestSimple {
96+
let delay = rng.gen_range(2..16);
97+
TestSimple {
98+
timer: StoppedTimer::new()
99+
.start_simple(Duration::from_ticks(delay), Duration::from_ticks(delay)),
100+
_delay: delay,
101+
count: 0,
102+
}
103+
}
104+
105+
/// Update from the total count from the timer itself.
106+
fn update(&mut self) {
107+
self.count += self.timer.read_count() as usize;
108+
}
109+
}
110+
111+
/// Test a callback using an atomic counter.
112+
///
113+
/// This allocates a Callback timer, and uses the callback to increment an atomic value.
114+
struct TestAtomic {
115+
_timer: Pin<Box<CallbackTimer<Arc<AtomicUsize>>>>,
116+
counter: Arc<AtomicUsize>,
117+
}
118+
119+
impl TestAtomic {
120+
fn new(rng: &mut impl Rng) -> TestAtomic {
121+
let delay = rng.gen_range(2..16);
122+
let counter = Arc::new(AtomicUsize::new(0));
123+
TestAtomic {
124+
_timer: StoppedTimer::new().start_callback(
125+
Callback {
126+
call: Self::expiry,
127+
data: counter.clone(),
128+
},
129+
Duration::from_ticks(delay),
130+
Duration::from_ticks(delay),
131+
),
132+
counter: counter.clone(),
133+
}
134+
}
135+
136+
// Read the atomic count.
137+
fn count(&self) -> usize {
138+
self.counter.load(Ordering::Acquire)
139+
}
140+
141+
/// Expire the function
142+
fn expiry(data: &Arc<AtomicUsize>) {
143+
data.fetch_add(1, Ordering::Relaxed);
144+
}
145+
}

tests/timer/testcase.yaml

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
common:
2+
filter: CONFIG_RUST_SUPPORTED
3+
platform_allow:
4+
- qemu_cortex_m0
5+
- qemu_cortex_m3
6+
- qemu_riscv32
7+
- qemu_riscv32/qemu_virt_riscv32/smp
8+
- qemu_riscv64
9+
- qemu_riscv64/qemu_virt_riscv64/smp
10+
- nrf52840dk/nrf52840
11+
tests:
12+
test.rust.timer:
13+
harness: console
14+
harness_config:
15+
type: one_line
16+
regex:
17+
- "All tests passed"

0 commit comments

Comments
 (0)