forked from zephyrproject-rtos/zephyr-lang-rust
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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]>
- Loading branch information
Showing
5 changed files
with
196 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
cmake_minimum_required(VERSION 3.20.0) | ||
|
||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) | ||
project(timer_rust) | ||
|
||
rust_cargo_application() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# Copyright (c) 2024 Linaro LTD | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
[package] | ||
# This must be rustapp for now. | ||
name = "rustapp" | ||
version = "3.7.0" | ||
edition = "2021" | ||
description = "Tests of timeers" | ||
license = "Apache-2.0 or MIT" | ||
|
||
[lib] | ||
crate-type = ["staticlib"] | ||
|
||
[dependencies] | ||
rand = { version = "0.8", default-features = false } | ||
rand_pcg = { version = "0.3.1", default-features = false } | ||
zephyr = "3.7.0" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# Copyright (c) 2024 Linaro LTD | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
CONFIG_RUST=y | ||
CONFIG_MAIN_STACK_SIZE=2048 | ||
|
||
# Timers need alloc | ||
CONFIG_RUST_ALLOC=y |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
// Copyright (c) 2024 Linaro LTD | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
#![no_std] | ||
|
||
extern crate alloc; | ||
|
||
use core::{pin::Pin, sync::atomic::Ordering}; | ||
|
||
use alloc::{boxed::Box, vec::Vec}; | ||
use rand::Rng; | ||
use rand_pcg::Pcg32; | ||
use zephyr::{ | ||
printkln, sync::{atomic::AtomicUsize, Arc}, time::{Duration, NoWait, Tick}, timer::{Callback, CallbackTimer, SimpleTimer, StoppedTimer} | ||
}; | ||
|
||
// Test the timers interface. There are a couple of things this tries to test: | ||
// 1. Do timers dynamically allocated and dropped work. | ||
// 2. Can simple timers count properly. | ||
// 3. Can we wait on a Simple timer. | ||
// 4. Do callbacks work with messages and semaphores. | ||
|
||
#[no_mangle] | ||
extern "C" fn rust_main() { | ||
printkln!("Tick frequency: {}", zephyr::time::SYS_FREQUENCY); | ||
timer_test(); | ||
printkln!("All tests passed"); | ||
} | ||
|
||
fn timer_test() { | ||
let mut rng = Pcg32::new(1, 1); | ||
|
||
// Track a global "stop" time when the entire test should be shut down. | ||
// let mut total_test = StoppedTimer::new().start_simple(Duration::secs_at_least(5), NoWait); | ||
let mut total_test = StoppedTimer::new().start_simple(Duration::secs_at_least(5), NoWait); | ||
|
||
// This simple timer lets us pause periodically to allow other timers to build up. | ||
let mut period = StoppedTimer::new().start_simple( | ||
Duration::millis_at_least(100), | ||
Duration::millis_at_least(100), | ||
); | ||
|
||
let mut simples: Vec<_> = (0..10).map(|_| TestSimple::new(&mut rng)).collect(); | ||
let atomics: Vec<_> = (0..10).map(|_| TestAtomic::new(&mut rng)).collect(); | ||
|
||
let mut count = 0; | ||
loop { | ||
// Wait for the period timer. | ||
let num = period.read_count_wait(); | ||
|
||
if num > 1 { | ||
// Getting this is actually a good indicator that we've overwhelmed ourselves with | ||
// timers, and are stress testing things. | ||
printkln!("Note: Missed period ticks"); | ||
} | ||
|
||
count += 1; | ||
|
||
if count % 10 == 0 { | ||
printkln!("Ticks {}", count); | ||
} | ||
|
||
if total_test.read_count() > 0 { | ||
break; | ||
} | ||
|
||
simples.iter_mut().for_each(|m| m.update()); | ||
} | ||
|
||
// Collect all of the times they fired. | ||
let simple_count: usize = simples.iter().map(|s| s.count).sum(); | ||
printkln!("Simple fired {} times", simple_count); | ||
let atomic_count: usize = atomics.iter().map(|s| s.count()).sum(); | ||
printkln!("Atomics fired {} times", atomic_count); | ||
|
||
printkln!("Period ticks: {}", count); | ||
|
||
// Now that everything is done and cleaned up, allow a little time to pass to make sure there | ||
// are no stray timers. We can re-use the total test timer. | ||
let mut total_test = total_test.stop().start_simple(Duration::millis_at_least(1), NoWait); | ||
total_test.read_count_wait(); | ||
} | ||
|
||
/// Test a SimpleTimer. | ||
/// | ||
/// This allocates a simple timer, and starts it with a small somewhat random period. It will track | ||
/// the total number of times that it fires when checked. | ||
struct TestSimple { | ||
timer: SimpleTimer, | ||
_delay: Tick, | ||
count: usize, | ||
} | ||
|
||
impl TestSimple { | ||
fn new(rng: &mut impl Rng) -> TestSimple { | ||
let delay = rng.gen_range(2..16); | ||
TestSimple { | ||
timer: StoppedTimer::new() | ||
.start_simple(Duration::from_ticks(delay), Duration::from_ticks(delay)), | ||
_delay: delay, | ||
count: 0, | ||
} | ||
} | ||
|
||
/// Update from the total count from the timer itself. | ||
fn update(&mut self) { | ||
self.count += self.timer.read_count() as usize; | ||
} | ||
} | ||
|
||
/// Test a callback using an atomic counter. | ||
/// | ||
/// This allocates a Callback timer, and uses the callback to increment an atomic value. | ||
struct TestAtomic { | ||
_timer: Pin<Box<CallbackTimer<Arc<AtomicUsize>>>>, | ||
counter: Arc<AtomicUsize>, | ||
} | ||
|
||
impl TestAtomic { | ||
fn new(rng: &mut impl Rng) -> TestAtomic { | ||
let delay = rng.gen_range(2..16); | ||
let counter = Arc::new(AtomicUsize::new(0)); | ||
TestAtomic { | ||
_timer: StoppedTimer::new().start_callback( | ||
Callback { | ||
call: Self::expiry, | ||
data: counter.clone(), | ||
}, | ||
Duration::from_ticks(delay), | ||
Duration::from_ticks(delay), | ||
), | ||
counter: counter.clone(), | ||
} | ||
} | ||
|
||
// Read the atomic count. | ||
fn count(&self) -> usize { | ||
self.counter.load(Ordering::Acquire) | ||
} | ||
|
||
/// Expire the function | ||
fn expiry(data: &Arc<AtomicUsize>) { | ||
data.fetch_add(1, Ordering::Relaxed); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
common: | ||
filter: CONFIG_RUST_SUPPORTED | ||
platform_allow: | ||
- qemu_cortex_m0 | ||
- qemu_cortex_m3 | ||
- qemu_riscv32 | ||
- qemu_riscv32/qemu_virt_riscv32/smp | ||
- qemu_riscv64 | ||
- qemu_riscv64/qemu_virt_riscv64/smp | ||
- nrf52840dk/nrf52840 | ||
tests: | ||
test.rust.timer: | ||
harness: console | ||
harness_config: | ||
type: one_line | ||
regex: | ||
- "All tests passed" |