Skip to content

Commit

Permalink
[SW] fix timer denominator to be dynamic (#87)
Browse files Browse the repository at this point in the history
* fix(bsp,timers): dynamically detect pclk div
* bench(periodic): allow configurable pclk
* bench(periodic): use periodic API
* bench(periodic): use slice instead of tuple for timers. Enabled by new timer API. This is slightly more flexible.
  • Loading branch information
hegza authored Feb 21, 2025
1 parent 8aab319 commit 4bd4794
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 36 deletions.
14 changes: 8 additions & 6 deletions examples/atalanta_bsp/src/mtimer.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use crate::{
mask_u32,
mmap::{
MTIMECMP_HIGH_ADDR_OFS, MTIMECMP_LOW_ADDR_OFS, MTIMER_BASE, MTIME_CTRL_ADDR_OFS,
MTIME_HIGH_ADDR_OFS, MTIME_LOW_ADDR_OFS,
CFG_BASE, MTIMECMP_HIGH_ADDR_OFS, MTIMECMP_LOW_ADDR_OFS, MTIMER_BASE, MTIME_CTRL_ADDR_OFS,
MTIME_HIGH_ADDR_OFS, MTIME_LOW_ADDR_OFS, PERIPH_CLK_DIV_OFS,
},
read_u32, unmask_u32, write_u32, CPU_FREQ,
read_u32, read_u8_masked, unmask_u32, write_u32, CPU_FREQ,
};

/// Machine Timer
Expand Down Expand Up @@ -177,8 +177,7 @@ impl MTimerLo {
}
}

const PERIPH_CLK_DIV: u32 = 1;
const DENOM: u32 = CPU_FREQ / PERIPH_CLK_DIV;
const DENOM: u32 = CPU_FREQ;
pub type Duration = fugit::Duration<u64, 1, DENOM>;

pub struct OneShot(MTimer);
Expand All @@ -187,8 +186,11 @@ impl OneShot {
/// Schedules the `MachineTimer` interrupt to trigger after `duration`
#[inline]
pub fn start(&mut self, duration: Duration) {
// Read current peripheral clock divider to dynamically fixup fugit::Duration
let pclk_div = unsafe { read_u8_masked(CFG_BASE + PERIPH_CLK_DIV_OFS, 0xf) };

let cnt = self.0.counter();
self.0.set_cmp(cnt + duration.ticks());
self.0.set_cmp(cnt + duration.ticks() / pclk_div as u64);
self.0.enable();
}

Expand Down
21 changes: 15 additions & 6 deletions examples/atalanta_bsp/src/timer_group.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
use crate::{mask_u32p, mmap::apb_timer::*, read_u32p, unmask_u32p, write_u32p, CPU_FREQ};
use crate::{
mask_u32p,
mmap::{apb_timer::*, CFG_BASE, PERIPH_CLK_DIV_OFS},
read_u32p, read_u8_masked, unmask_u32p, write_u32p, CPU_FREQ,
};

/// Relocatable driver for PULP APB Timer IP
pub struct Timer(*mut RegisterBlock);
Expand Down Expand Up @@ -91,8 +95,7 @@ impl Timer {
}
}

const PERIPH_CLK_DIV: u32 = 1;
const DENOM: u32 = CPU_FREQ / PERIPH_CLK_DIV;
const DENOM: u32 = CPU_FREQ;
pub type Duration = fugit::Duration<u32, 1, DENOM>;

pub struct Periodic(Timer);
Expand All @@ -104,8 +107,11 @@ impl Periodic {
/// Also resets the internal counter.
#[inline]
pub fn set_period(&mut self, duration: Duration) {
// Read current peripheral clock divider to dynamically fixup fugit::Duration
let pclk_div = unsafe { read_u8_masked(CFG_BASE + PERIPH_CLK_DIV_OFS, 0xf) };

// Setting CMP also sets COUNTER
self.0.set_cmp(duration.ticks());
self.0.set_cmp(duration.ticks() / pclk_div as u32);
}

/// Schedules an interrupt to be fired every `duration`
Expand All @@ -114,9 +120,12 @@ impl Periodic {
/// interrupt ahead of schedule.
#[inline]
pub fn set_period_offset(&mut self, period: Duration, offset: Duration) {
// Read current peripheral clock divider to dynamically fixup fugit::Duration
let pclk_div = unsafe { read_u8_masked(CFG_BASE + PERIPH_CLK_DIV_OFS, 0xf) };

// Setting CMP also sets COUNTER, so we override that afterwards
self.0.set_cmp(period.ticks());
self.0.set_counter(offset.ticks());
self.0.set_cmp(period.ticks() / pclk_div as u32);
self.0.set_counter(offset.ticks() / pclk_div as u32);
}

/// Starts the timer
Expand Down
58 changes: 34 additions & 24 deletions examples/periodic_tasks/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,26 @@
#![allow(non_snake_case)]

use core::arch::asm;
use fugit::ExtU32;
use more_asserts as ma;

use bsp::{
clic::{Clic, Polarity, Trig},
embedded_io::Write,
interrupt,
mmap::apb_timer::{TIMER0_ADDR, TIMER1_ADDR, TIMER2_ADDR, TIMER3_ADDR},
mtimer::{self, MTimer},
nested_interrupt,
riscv::{
self,
asm::wfi,
mmap::{
apb_timer::{TIMER0_ADDR, TIMER1_ADDR, TIMER2_ADDR, TIMER3_ADDR},
CFG_BASE, PERIPH_CLK_DIV_OFS,
},
mtimer::{self, MTimer},
nested_interrupt, read_u32,
riscv::{self, asm::wfi},
rt::entry,
sprint, sprintln,
tb::signal_pass,
timer_group::Timer,
timer_group::{Periodic, Timer},
uart::*,
Interrupt, CPU_FREQ,
write_u32, Interrupt, CPU_FREQ,
};
use ufmt::derive::uDebug;

Expand Down Expand Up @@ -84,8 +85,17 @@ static mut TIMEOUT: bool = false;

#[entry]
fn main() -> ! {
// Assert that periph clk div is as configured
// !!!: this must be done prior to configuring any timing sensitive
// peripherals
write_u32(CFG_BASE + PERIPH_CLK_DIV_OFS, PERIPH_CLK_DIV as u32);

let mut serial = ApbUart::init(CPU_FREQ, 115_200);
sprintln!("[periodic_tasks (PCS={:?})]", cfg!(feature = "pcs"));
sprintln!(
"Periph CLK div = {}",
read_u32(CFG_BASE + PERIPH_CLK_DIV_OFS)
);
sprintln!("Running test {} times", RUN_COUNT);

sprintln!(
Expand Down Expand Up @@ -133,17 +143,17 @@ fn main() -> ! {
// Use mtimer for timeout
let mut mtimer = MTimer::instance().into_oneshot();

let mut timers = (
Timer::init::<TIMER0_ADDR>(),
Timer::init::<TIMER1_ADDR>(),
Timer::init::<TIMER2_ADDR>(),
Timer::init::<TIMER3_ADDR>(),
);
let timers = &mut [
Timer::init::<TIMER0_ADDR>().into_periodic(),
Timer::init::<TIMER1_ADDR>().into_periodic(),
Timer::init::<TIMER2_ADDR>().into_periodic(),
Timer::init::<TIMER3_ADDR>().into_periodic(),
];

timers.0.set_cmp(TASK0.period_ns * CYCLES_PER_US / 1_000);
timers.1.set_cmp(TASK1.period_ns * CYCLES_PER_US / 1_000);
timers.2.set_cmp(TASK2.period_ns * CYCLES_PER_US / 1_000);
timers.3.set_cmp(TASK3.period_ns * CYCLES_PER_US / 1_000);
timers[0].set_period(TASK0.period_ns.nanos());
timers[1].set_period(TASK1.period_ns.nanos());
timers[2].set_period(TASK2.period_ns.nanos());
timers[3].set_period(TASK3.period_ns.nanos());

// --- Test critical ---
unsafe {
Expand All @@ -156,10 +166,9 @@ fn main() -> ! {

// Test will end when MachineTimer fires
mtimer.start(TEST_DURATION);
timers.0.enable();
timers.1.enable();
timers.2.enable();
timers.3.enable();

// Start periodic timers
timers.iter_mut().for_each(Periodic::start);

unsafe { riscv::interrupt::enable() };

Expand Down Expand Up @@ -204,8 +213,9 @@ fn main() -> ! {
(TASK2_COUNT, TASK2),
(TASK3_COUNT, TASK3),
] {
// Assert task count is at least the expected count. There may be one less as the
// final in-flight task might get interrupted by the test end.
// Assert task count is at least the expected count. There may be one less as
// the final in-flight task might get interrupted by the test
// end.
ma::assert_ge!(
*count,
(TEST_DURATION.to_nanos() as usize / task.period_ns as usize) - 1
Expand Down

0 comments on commit 4bd4794

Please sign in to comment.