arch/rp23xx: add /dev/timer driver and tickless OS on the RP2350 system TIMER#19530
Merged
Conversation
Contributor
|
please fix the conflict @casaroli |
The RP2350 has two system timer blocks (TIMER0, TIMER1), each a free-running 64-bit counter incremented once per microsecond by the TICKS block (set up in rp23xx_clock.c). They are independent of the ARM SysTick that drives the OS tick, so they are free for application use, but the port had no driver for them. Add rp23xx_timer.c, a NuttX timer lower-half that binds a block to a /dev/timerN device. It uses ALARM0 of the block, which matches the low 32 bits of the microsecond counter, to implement single-shot and periodic timeouts with 1 us resolution and a maximum interval of 2^32 - 1 us (~71.5 minutes). Periodic reloads are scheduled relative to the previous expiry to avoid drift, but never behind the counter (an alarm set in the past would not match until the 32-bit counter wraps). Enable with CONFIG_RP23XX_TIMER (which selects CONFIG_TIMER), then turn on each block independently: CONFIG_RP23XX_TIMER0 registers /dev/timer0 and CONFIG_RP23XX_TIMER1 registers /dev/timer1. A block claimed by the tickless oneshot (CONFIG_RP23XX_SYSTIMER_TICKLESS) is excluded from these choices in Kconfig, so the two features can be enabled together without colliding on the same block or its alarm IRQ. Tested on Pimoroni Pico Plus 2 (RP2350B) hardware with examples/timer and a CMSIS-DAP probe. The device registers as /dev/timer0; the ALARM0 interrupt fires at the programmed period (verified over SWD at the configured 1 s interval, not a busy loop), and the full path -- alarm match, the driver ISR, the timer notification and delivery of the SIGNO to user space -- reaches the example's signal handler and increments its counter. Assisted-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
The port could only run with a periodic ARM SysTick tick. Add an alarm/ oneshot lower-half backed by an RP2350 system timer block so the scheduler can run tickless, waking the CPU only when a timer actually expires. rp23xx_oneshot.c implements the ONESHOT_COUNT lower-half (mirroring the RISC-V mtimer driver). A timer block is a free-running 64-bit microsecond counter (clocked by the TICKS block, independent of the SysTick), which serves directly as the monotonic time base returned by current(), so timekeeping is exact to 1 us. The one adaptation versus a full 64-bit compare timer is that the RP2350 ALARM registers match only the low 32 bits of the counter: max_delay() is therefore capped below 2^32 counts (~71.5 minutes) so the scheduler never asks for a longer interval, and any deadline that is already due -- or that the counter reaches while the alarm is being armed -- is raised immediately through the INTF force register instead of waiting a full 32-bit wrap for the compare to match again. Enabled with CONFIG_RP23XX_SYSTIMER_TICKLESS (mutually exclusive with RP23XX_SYSTIMER_SYSTICK), which selects ONESHOT, ONESHOT_COUNT and ALARM_ARCH; up_timer_initialize() then hands the oneshot to up_alarm_set_lowerhalf(). ARCH_CHIP_RP23XX now selects ARCH_HAVE_TICKLESS. The block is selectable with CONFIG_RP23XX_SYSTIMER_TICKLESS_TIMER0 (default) or _TIMER1; the chosen block is claimed exclusively by the scheduler and is excluded from the /dev/timer driver (CONFIG_RP23XX_TIMER) in Kconfig, so the tickless clock and a /dev/timer device can coexist on different blocks. Tested on Pimoroni Pico Plus 2 (RP2350B) hardware with CONFIG_SCHED_TICKLESS and CONFIG_SCHED_TICKLESS_ALARM: the image boots to nsh and keeps accurate time -- "uptime" advances at real-time rate (17 s over a measured 17.4 s) and "sleep 4" blocks for 4.25 s of wall time -- confirming the oneshot both drives the scheduler and provides a correct 1 MHz monotonic clock. Assisted-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
casaroli
force-pushed
the
rp2350-timer-tickless-pr
branch
from
July 25, 2026 13:40
9995efe to
e851647
Compare
xiaoxiang781216
approved these changes
Jul 25, 2026
acassis
approved these changes
Jul 25, 2026
This file contains hidden or 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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The RP2350 has two independent system timer blocks (TIMER0, TIMER1), each a
free-running 64-bit microsecond counter clocked by the TICKS block (1 MHz) and
independent of the ARM SysTick that drives the OS tick. Each block has four
independent alarms (ALARM0–3), each with its own compare register and IRQ.
The port had no driver for these blocks. This PR adds two features built on
them and makes them share the hardware cleanly.
1.
/dev/timerdriver (rp23xx_timer.c)low 32 bits of the counter) for single-shot or periodic timeouts, 1 µs
resolution, up to 2³²−1 µs (~71.5 min) per interval.
never behind the counter (a past alarm would not match until the 32-bit
counter wraps).
CONFIG_RP23XX_TIMER; each block is toggled independently:CONFIG_RP23XX_TIMER0→/dev/timer0,CONFIG_RP23XX_TIMER1→/dev/timer1.2. Tickless OS (
rp23xx_oneshot.c)ONESHOT_COUNTalarm/oneshot lower-half (mirrorsarch/risc-v/src/common/riscv_mtimer.c) that drives the tickless schedulerthrough
up_alarm_set_lowerhalf(). The 64-bit counter is the monotonic timebase (
current()); ALARM0 is the next-event compare.only the low 32 bits of the counter.
max_delay()is capped below 2³² countsso the scheduler never asks for a longer interval, and any deadline already
due — or reached while the alarm is being armed — is forced immediately via
the INTF register instead of waiting a full 32-bit wrap.
CONFIG_RP23XX_SYSTIMER_TICKLESS(mutually exclusive withCONFIG_RP23XX_SYSTIMER_SYSTICK); the block is selectable withCONFIG_RP23XX_SYSTIMER_TICKLESS_TIMER0(default) /_TIMER1.ARCH_CHIP_RP23XXnow selectsARCH_HAVE_TICKLESS.Coexistence. The block chosen for tickless is claimed exclusively by the
scheduler and is removed from the
/dev/timerchoices in Kconfig(
RP23XX_TIMER0 depends on !RP23XX_SYSTIMER_TICKLESS_TIMER0, and likewise forTIMER1), so the two features never collide on the same block or its alarm IRQ.
Result:
/dev/timer0and/dev/timer1available;/dev/timer1available;/dev/timer0available.Impact
disabled. Default OS tick remains the ARM SysTick.
arm(rp23xx / RP2350). Boards: rp23xx common bringup registers theenabled
/dev/timerNdevice(s).symbols and the new
/dev/timerNdevice nodes.Testing
Hardware: RP2350 board (Pimoroni Pico Plus 2),
raspberrypi-pico-2:nshconfiguration, programmed over SWD with a Raspberry Pi Debug Probe (probe-rs)
and driven from the
nshconsole on UART0 through the same probe's UARTbridge. Both timer blocks were exercised in both roles, and in each build
the two features ran at the same time on different blocks.
Configuration A — tickless on TIMER0,
/dev/timer1on TIMER1RP23XX_SYSTIMER_TICKLESS+SYSTIMER_TICKLESS_TIMER0+SCHED_TICKLESS+SCHED_TICKLESS_ALARM+RP23XX_TIMER+RP23XX_TIMER1+EXAMPLES_TIMERwith
CONFIG_EXAMPLES_TIMER_DEVNAME="/dev/timer1".nsh.ls /devliststimer1andnot
timer0— the block the scheduler claimed is not offered as a/dev/timerdevice.examples/timeron/dev/timer1runs while tickless drives the schedulerfrom TIMER0 — this is the coexistence proof.
timeleftcounts downsmoothly within each 1 s interval and
nsignalsincrements 0 → 1 → 2 as theALARM0 matches are delivered to the user signal handler; the app stops the
timer and exits cleanly.
sleep N, measured by when the prompt returns):1 s → +0.014 s, 2 s → +0.061 s, 4 s → +0.053 s, 8 s → +0.063 s.
uptimeadvanced 123 s while the host wall clockadvanced 123.04 s — −0.034 % over two minutes, within
uptime's 1 sdisplay quantization.
Configuration B — tickless on TIMER1,
/dev/timer0on TIMER0The mirror image of A (
SYSTIMER_TICKLESS_TIMER1+RP23XX_TIMER0,CONFIG_EXAMPLES_TIMER_DEVNAME="/dev/timer0"), which exercises the block-selectpath for both features.
nsh;ls /devliststimer0and nottimer1.examples/timeron/dev/timer0runs to completion withnsignalsincrementing, while tickless runs on TIMER1.
8 s → +0.061 s.
uptime+123 s vs 123.02 s wall — −0.016 % over two minutes.The residual tens of milliseconds on
sleepare shell/scheduler round-tripoverhead, not clock drift: the two-minute rate measurements bound the drift at
well under 0.05 %.
Build / config matrix (all clean)
RP23XX_TIMER+RP23XX_TIMER0+RP23XX_TIMER1+EXAMPLES_TIMER/dev/timer1(config A above)/dev/timer0(config B above)tools/checkpatch.sh -f(nxstyle) and-g(patches + commit messages):clean on both commits.
to
nwhen only one is present.Note on the console used
Testing used the UART0 console rather than
usbnsh. On this board the USB-CDCconsole drops after a couple of commands; that is independent of this PR —
a pristine
raspberrypi-pico-2:usbnshbuild with none of these options enabledwas flashed as a control and behaved identically, which matches the existing
entry in the rp23xx documentation ("USB — Experimental — usbnsh configuration
is somewhat working with some data corruption"). Nothing in this PR touches the
USB device driver.