0% found this document useful (0 votes)
6 views

Timestamp Peripherals For Precise Real-Time Programming

Using a Raspberry Pi Pico to do precise timing
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
6 views

Timestamp Peripherals For Precise Real-Time Programming

Using a Raspberry Pi Pico to do precise timing
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 11

Timestamp Peripherals for Precise Real-Time Programming

John Hui Kyle J. Edwards Stephen A. Edwards


[email protected] [email protected] [email protected]
Columbia University Columbia University Columbia University
New York, New York, USA New York, New York, USA New York, New York, USA
ABSTRACT
On microcontrollers, timer devices provide high-precision timing, 100 011 01 10

Output
@7 @5 @9 @6

Input
but that precision is lost when using high-level languages without Sslang
suitable abstractions for temporal behavior. So, for timing-sensitive Program
applications, programmers resort to low-level languages like C
6 9
which lack expressiveness and safety guarantees. Other program- 5 7
mers use specialized precision-timing hardware which is expensive
and difficult to obtain. Figure 1: Our approach: a peripheral interprets changes on
In this work, we achieve sub-microsecond precision from a high- input pins as timestamped events, which are passed to a real-
level real-time programming language on the RP2040, a cheap, time discrete-event simulator (the Sslang program), which
widely available microcontroller. Our work takes advantage of the sends timestamped output events to another peripheral that
RP2040’s Programmable I/O (PIO) devices, which are cycle-accurate generates precisely timed output waveforms.
coprocessors designed for implementing hardware protocols over
the RP2040’s GPIO pins.
We use the PIO devices to implement timestamp peripherals, These hardware-managed timestamps make it much easier to de-
which are input capture and output compare devices. We use times- velop and analyze real-time software. Lohstroh et al. [13] argue that
tamp peripherals to mediate I/O from programs written in Sslang, real-time programming models should provide software with some
a real-time programming language with deterministic concurrency. notion of logical time, an engineering fiction that is easier to reason
We show that timestamp peripherals help Sslang programs achieve about than physical models of time. Within a real-time system, the
the precise timing behavior prescribed by Sslang’s Sparse Synchro- timestamp peripherals we propose here form the boundary between
nous Programming model. the logical software and the physical external environment.
Timestamping in software, such as with an interrupt service
KEYWORDS routine that records a system timer value, is imprecise because
real time systems, concurrency control, computer languages, timing of interrupt response time uncertainty. Another approach would
be to implement such timestamping hardware in an FPGA with
ACM Reference Format:
a processor core, but such chips are substantially more expensive
John Hui, Kyle J. Edwards, and Stephen A. Edwards. 2023. Timestamp Periph-
erals for Precise Real-Time Programming. In 21st ACM-IEEE International than commodity microcontrollers.
Conference on Formal Methods and Models for System Design (MEMOCODE To demonstrate timestamp peripherals, we implement them on
’23), September 21–22, 2023, Hamburg, Germany. ACM, New York, NY, USA, the inexpensive (US$0.70), widely available RP2040 microcontroller,
11 pages. https://doi.org/10.1145/3610579.3611084 using its programmable input/output (PIO) blocks and interface
them with the Sparse Synchronous Model (SSM) runtime. The re-
1 INTRODUCTION sulting peripherals sample input pins at 16 MHz and allow output
Systems often have a mix of real-time requirements, ranging from changes to be scheduled with the same precision, far more accu-
picosecond-level precision to best-effort. This paper proposes times- rately than is possible using only the RP2040’s 1 MHz timer. Overall,
tamp peripherals—general-purpose peripherals that timestamp in- our system† gives users the ability to write high-level programs
put events and emit output events according to timestamps—as an that can measure and produce output signals with 62.5 ns precision.
interface between the hardest real-time layer and the first software In this paper, we describe and evaluate the performance of our
layer (Figure 1). While a handful of existing peripherals timestamp real-time software environment with timestamp peripherals. We
events, most are specialized. based our environment on Edwards and Hui’s [5] Sparse Synchro-
nous Model and propose a real-time language called Sslang (Sparse
This work was supported by the NIH under grant RF1MH120034-01.
Synchronous Language), described in Section 2. Sslang relies on
Permission to make digital or hard copies of all or part of this work for personal or timestamp peripherals, which we implemented on the RP2040 mi-
classroom use is granted without fee provided that copies are not made or distributed crocontroller and its PIO blocks, described in Section 3 and Section 4.
for profit or commercial advantage and that copies bear this notice and the full citation
on the first page. Copyrights for components of this work owned by others than the To determine the performance limits of our approach, we ran exper-
author(s) must be honored. Abstracting with credit is permitted. To copy otherwise, or iments and describe our findings in Section 5. Section 6 summarizes
republish, to post on servers or to redistribute to lists, requires prior specific permission
and/or a fee. Request permissions from [email protected].
related work; we conclude in Section 7.
MEMOCODE ’23, September 21–22, 2023, Hamburg, Germany
© 2023 Copyright held by the owner/author(s). Publication rights licensed to ACM.
ACM ISBN 979-8-4007-0318-8/23/09. . . $15.00
https://doi.org/10.1145/3610579.3611084 † Source code available at https://github.com/ssm-lang/pico-ssm
MEMOCODE ’23, September 21–22, 2023, Hamburg, Germany John Hui, Kyle J. Edwards, and Stephen A. Edwards

2 THE SPARSE SYNCHRONOUS MODEL sleep delay =


The Sparse Synchronous Model [5, 7] is a discrete-event model let timer = new () / / Allocate a pure event variable
of computation for specifying real-time behavior. Like traditional after delay, timer <- () / / Schedule a wake-up
discrete-event systems, it is built around scheduled variable updates wait timer / / Suspend until then
managed by an event queue that executes them in temporal order;
its main novelty is a deterministic mechanism for resolving logically waitfor var value =
simultaneous events, inspired by the synchronous languages [4]. while deref var != value / / Current value is not value
Sslang is an imperative-functional language built on SSM that wait var / / Wait for update to var
provides scheduled variable updates, blocking waits on variables,
and parallel computation. Like Python, Sslang uses indentation to debounce delay input press =
signify grouping; like Haskell and OCaml, Sslang features strong loop
and static typing with type inference. Here is the “hello world” of waitfor input 0 // Active-low button pressed
the embedded world in Sslang, which blinks an LED at 10 Hz: press <- () // Send “press” event
sleep delay // Debounce
blink led = / / blink takes one parameter: led waitfor input 1 // Button released
loop / / Repeat the following lines sleep delay // Debounce
/ / 50 ms from now, toggle the value of led
after ms 50, led <- not (deref led) pulse period press output =
wait led / / Wait for led to be updated loop
wait press / / Wait for the “press” event
Here, blink led = defines the function blink with a single output <- 1 / / Pulse high immediately
argument led, an otherwise ordinary mutable variable that has been after period, output <- 0 / / Schedule low
connected to an output peripheral. loop starts an infinite loop that wait output / / Wait for low
begins by scheduling an update to toggles the led variable 50 ms
in the future. In that after statement, ms is a function applied to buttonpulse button led =
50 that computes the number of system ticks in a 50 ms duration, let press = new () / / Debounced button press signal
and deref reads the current value of led, a mutable variable. The par debounce (ms 10) button press
subsequent wait statement suspends this function’s execution until pulse (ms 200) press led
the next write to led; when execution resumes, the loop restarts.
While this example resembles a “blink” program in C or Python,
Figure 2: A debounced pulse generator in Sslang. The sleep
Sslang manipulates an event’s time with the same care as its value.
function pauses execution; waitfor pauses until a variable
In particular, logical time does not advance except at wait state-
takes the specified value; debounce filters a bouncy pushbut-
ments, meaning each loop iteration logically takes exactly 50 ms,
ton input into clean press events; pulse emits a pulse at each
regardless of how long the processor physically takes to execute
press event; buttonpulse runs debounce and pulse in parallel.
the machine instructions in the loop. Using a timestamp output pe-
ripheral with 62.5 ns precision, our implementation of this program
on the RP2040 generates a 10 Hz square wave that is as accurate
and precise as the microcontroller’s crystal oscillator.
Figure 2 showcases more of Sslang with a larger program that
transforms presses of a bouncy pushbutton switch into clean 200 ms
pulses. The oscilloscope traces in Figure 3 illustrates its behavior. The buttonpulse function, the main entry point to our program,
The first two functions illustrate how we build abstractions from runs debounce and pulse together. It creates a pure-event variable
Sslang primitives. The sleep function provides the familiar “sus- press to convey clean button-press events between debounce and
pend execution for a period of time” functionality. It creates a local pulse, which are run in parallel using the par statement. par runs
variable timer that conveys pure events (written “()” in Sslang), earlier operands at a higher priority than later operands, ensuring
schedules a future event on this variable, then waits for that update. that an event generated by debounce is seen instantly by pulse.
The waitfor function blocks until a given variable takes a given Following the techniques of Hui and Edwards [7], our Sslang
value, returning instantly if the variable already has that value. compiler generates C code that links against the SSM runtime [5],
Constructs such as sleep and waitfor are standard library functions. a discrete-event simulator that provides a tick function to execute
The debounce and pulse functions implement the two halves of the system for an instant, updating the event queue (a priority
our pulse generator. The debounce function is an infinite loop that heap) as needed. The SSM runtime library is platform-agnostic and
generates a pure event when it sees an active-low button pressed, requires a timing-aware platform runtime to call tick at the right
waits some time for any bouncing to subside, then waits again for time. The platform runtime is also responsible for managing vari-
the button to be released. The pulse function waits for a button- ables mapped to external I/O, scheduling external inputs as delayed
press event; after one arrives, pulse immediately sets the output assignments to input variables, and fowarding output variables
high while scheduling it to become low again in the future. The updates to the environment. Our RP2040 platform runtime does so
last wait makes pulse ignore any event before the end of the pulse. using timestamp peripherals, which we describe below in detail.
Timestamp Peripherals for Precise Real-Time Programming MEMOCODE ’23, September 21–22, 2023, Hamburg, Germany

The SMs provide precise timing by guaranteeing each non-


blocking instruction executes in a single cycle, followed by a fixed
number of stall cycles prescribed in the instruction itself. Blocking
instructions wait on events such as an inter-SM interrupt signal or
data arriving from the ARM cores. As such, programs on two SMs
execute in lockstep if neither block and their instruction counts
align (accounting for stall cycles). PIO devices send and receive data
from the ARM cores via two 4x32-bit FIFOs (one in each direction)
and may raise interrupts that can run interrupt service routines
(ISRs) on either of the two ARM cores.
Although the PIO was not designed for implementing times-
tamp peripherals, it is fast, predictable, and powerful enough to
do so. To timestamp inputs, we implement a precisely timed loop
(a)
that maintains a counter and emits a timestamped input update
event when it sees a change in input levels. The output system also
implements a counter with a precisely timed loop, but checks the
counter against an alarm time to emit a new output when the two
timestamps match.

4 THE RP2040 PLATFORM RUNTIME


To run Sslang programs in real-time, our RP2040 platform runtime
uses Edwards & Hui’s platform-agnostic SSM runtime library to
schedule internal events and processes. The RP2040 platform run-
time coordinates execution with the hardware timer and relays
external inputs and outputs to the SSM runtime.
The tick loop procedure for our RP2040 platform runtime, shown
(b) in Figure 4, keeps up with physical time while calling the SSM run-
time’s tick function to execute the Sslang program for an instant
Figure 3: Behavior of the debounced pulse generator program. and advancing logical time. We based this on Hui & Edwards [7].
The top trace (blue) is the active-low pushbutton input; the At each iteration, the platform runtime checks for available input
bottom (cyan) is the LED output. (a) The program emits two events that may have preempted internally-scheduled events, and
200 ms pulses in response to two button presses. (b) Zoomed forwards these as delayed assignments to the corresponding sched-
in, button bounces and the 20 µs reaction time become visible. uled variables. If there is nothing to be done, the tick loop sleeps
until it is time to execute the next SSM instant, or when some ex-
ternal input wakes up the system: it blocks on a semaphore until it
is unblocked by an interrupt service routine (ISR).
3 THE RP2040 AND ITS PIO BLOCKS The block diagram in Figure 5 illustrates how the tick loop com-
The RP2040 microcontroller, produced by the Raspberry Pi Founda- municates with the rest of the system. Notably, our platform run-
tion [19], features dual ARM Cortex-M0+ cores, 264 kB of on-chip time uses the RP2040’s cycle-accurate PIO hardware to predictably
SRAM, a 64-bit counter/timer with 1 µs precision, a QSPI interface manage external input and output events, isolated from main pro-
for off-chip flash memory backed by an execute-in-place cache, cessor delays. The input system uses a single SM that timestamps
and a direct memory access (DMA) controller. We run the core input events; the output system uses two SMs that emit output
processors at 128 MHz, clocked by a 12 MHz crystal-driven PLL. events at target timestamps specified by the running program. In
In addition to traditional peripherals such as GPIO and UARTs, the rest of this section, we describe our PIO input and output imple-
the RP2040 includes Programmable I/O (PIO) devices that execute mentation, and how we integrate them with the rest of the system.
tiny PIO-specific assembly language programs designed to act as
conduits between the RP2040’s 30 GPIO pins and its ARM cores.
Typical applications include “soft UARTs” and drivers for the un- 4.1 Timestamps and Clock Synchronization
usual serial protocol used by WS2812 color LEDs. The RP2040 Our RP2040 platform runtime uses 64-bit timestamps (time_t) that
provides two such PIO blocks, each comprised of four independent count at 16 MHz (62.5 ns), which we chose due to our PIO programs
state machines (SMs) that each have their own program counter, running 8-cycle loops at 128 MHz and the RP2040 system timer
two 32-bit shift registers, and two 32-bit scratch registers. While running at 1 MHz. At this speed, 32-bit timestamps would wrap
these 8 SMs provide ample parallelism, program memory is limited around in under 5 min; 64-bit timestamps give us 36,533 years.
to 32 instructions per PIO block shared among four SMs, they do We use the RP2040’s 1 MHz system timer as the master clock,
not have direct access to any memory, and the only arithmetic which measures time since it was started. We plan to eventually
operations they support are decrement and equality comparison. synchronize this clock to, say, a GPS reference.
MEMOCODE ’23, September 21–22, 2023, Hamburg, Germany John Hui, Kyle J. Edwards, and Stephen A. Edwards

procedure tick_loop(invar, outvar): // The main tick loop, with PIO input and output variables
init_ssm_runtime() // Initialize the SSM runtime
tick() // Run the program for time zero
forever
rt ← timer_read() // Read the real time from the system timer
nt ← next_time() // Get the time of the next scheduled event
if input_queued() && input_peek().time < nt // Is there a pending input event before any other event?
schedule(invar, input_dequeue()) // . . . yes: move it from the PIO queue to the SSM runtime queue
elseif nt ≤ rt // Has the model fallen behind physical time?
tick() // . . . yes: run the program for an instant; update next time
if outvar.next_time ≠ ∞ // Is there a scheduled PIO output?
pio_output(outvar.next_time, // . . . yes: send it to the PIO
outvar.next_value)
elseif nt ≠ ∞ // Is there an event scheduled for the future?
set_alarm(nt) // . . . yes: schedule an alarm to wake up then
wait(semaphore) // Wait for the alarm or an input event
cancel_alarm() // If an input event awakened us, cancel the alarm
release(semaphore) // Release the semaphore if an alarm came just after an input event
else
wait(semaphore) // Wait for an input event

Figure 4: The RP2040 platform runtime tick loop, which calls tick() to advance model time, then sleeps until the next scheduled
event or external input. Based on the tick loop from Hui & Edwards [7].

pio0 RP2040 Platform Runtime ssm Runtime


pio Input Queue ssm event queue pio0
rx fifo
dma 3 2 4
Pins 3 5 1 @4 @3 @1 tx fifo
schedule time
16 MHz
32 bit Interrupt Routines 16 MHz
Capture sm Input Queue Sslang 32 bit

Peripheral 5 1 8 tick Program


Tick Alarm sm
isr @6 @5 @2
loop
wait irq4
irq0 post
pio isr tx fifo
System Timer Semaphore Pins
1 MHz value
64 bit irq Alarm isr Buffer sm
set_alarm

Figure 5: System block diagram. The Capture sm (in pio0) timestamps input pin events; the dma controller enqueues them.
The tick loop (Figure 4) gathers the next event from the input queues, schedules it in the ssm event queue, calls tick to run the
Sslang program for an instant, feeds updated time/value to Alarm and Buffer sms (also in pio0), sets an alarm, and sleeps.

Because the PIO programs cannot directly read the system timer, The initialization routine compensates for its own latency, which
we maintain two additional real-time clocks in the PIO programs we measured to be roughly 3 µs. We add this offset to the initial PIO
that need access to the current time. Fortunately, all three timers counter to ensure it runs slightly ahead of the system clock. This
are driven by clocks derived from the external 12 MHz crystal, so offset is critical for the correctness of the tick loop, which assumes
we set them to run at precisely the same rate. They will remain that if the PIO input queue is empty, future queued events will
synchronized provided we start them in phase. We initialize the have a greater timestamp than the current system clock time. If the
PIO counters with the code in Figure 6, which reads the system PIO counters were run behind the system clock, PIO timestamps
timer, sends the initial count value to the counting SMs, and starts could be smaller, violating this assumption. We verify our clocks
all the three SMs simultaneously. are synchronized using the loopback test described in Section 5.4.
Timestamp Peripherals for Precise Real-Time Programming MEMOCODE ’23, September 21–22, 2023, Hamburg, Germany

void start_pio_counters(void) { .program input_capture


/ / Read the 1 MHz system timer ; X: The previous GPIO pins’ values
uint32_t tmr = timer_hw->timerawl; ; Y: Counter/timestamp value (decreasing)
/ / Convert to 16 MHz countdown value ; ISR: For reading GPIO pins
uint32_t ctr = ~((tmr + 3) << 4); ; OSR: Scratch
pull ; Read initial counter value
/ / Send initial count to input and output SMs mov y, osr
pio_sm_put(pio0, capture_sm); in pins, IN_PINS ; Read initial GPIO pin state
pio_sm_put(pio0, alarm_sm); in null, 32-IN_PINS ; Pad unused pin bits
mov x, isr [13] ; Save initial GPIO pin state
pio_set_sm_mask_enabled(pio0, / / Start SMs in sync
CAPTURE_SM | ALARM_SM | BUFFER_SM, true); .wrap_target
} cmp:
jmp y--, decr ; Decrement counter
decr:
Figure 6: Initializing PIO counters using the system timer.
mov osr, y ; Back up counter value
in pins, IN_PINS ; Read GPIO pin state
in pins, 32-IN_PINS ; Pad unused pin bits
4.2 The Input System
mov y, isr
The input system uses a single PIO SM to sample a group of input jmp x!=y, event ; Jump if state changed
pins at 16 MHz and send a sequence of timestamped changes to mov y, osr ; Restore counter value
the platform runtime. This Capture SM is conceptually simple: it jmp cmp ; Restart the loop
reads an initial counter value from the CPU to synchronize with
the system timer, then enters a loop that increments the counter event: ; Input value change: send event
and polls the input pins. If any input pin state has changed, the mov x, isr ; Remember new value
Capture SM emits the new pin values and current counter value push noblock ; . . . and enqueue it
into a FIFO, and interrupts the CPU to notify it of the input event. mov isr, osr ; Enqueue the current counter
The actual PIO code for this (Figure 7) is complicated because the push noblock
PIO instruction set is highly idiosyncratic. For example, only the irq 0 ; Notify CPU of the event
two scratch registers X and Y can be compared, and decrement can mov y, osr ; Restore counter
only be done as part of a conditional jump. To compensate for this jmp y--, cmp [3] ; Decrement counter, stall 3 cycles
limitation, we complement PIO counter values when we convert .wrap ; Restart the loop
them to and from the system timestamps that SSM uses (Figure 8).
We have tuned our PIO code so that the counter decrements void pio_irq0_isr(void) {
every eight cycles regardless of any input change, keeping the sem_post(&sem); / / Post to semaphore; awaken tick loop
counter synchronized with the system timer. We run the PIO at }
128 MHz, so our code samples and timestamps inputs at 16 MHz.
This frequency is a power-of-two multiple of the 1 MHz system
Figure 7: Input Capture PIO program and the ISR triggered
clock frequency, which lets us efficiently convert between the time
by irq 0. Every 8 cycles, this checks the input pins and, if
bases with bit-shifting (Figure 9).
they have changed, pushes the new value and the current
While our implementation samples inputs at 16 MHz, it cannot
time to the RX FIFO.
resolve consecutive events occurring faster than 8 MHz: when the
Capture SM detects an input event, it takes extra instructions to
send the captured event to the CPU. We pad these instructions to SM acts as a real-time alarm that triggers the Buffer SM to emit
eight cycles to keep the counter decrementing at a constant rate. a new value on the pins at the scheduled time. This split arose
To allow our system to handle longer input event bursts, we because a Sslang program can “change its mind” about when and
program a channel of the RP2040’s DMA controller to empty the which outputs need to be emitted. SSM semantics allow only one
4-word hardware RX FIFO from the Capture SM into a 64-word ring pending event per variable, but allows that pending event to be
buffer in main memory. We leverage the controller’s built-in support overwritten, which is useful, say, when handling timeout behavior.
for power-of-two-sized ring buffers, and use a trick to make the As such, we needed the output system to be able to reschedule
transfer continue indefinitely: a second channel, configured to start an alarm and the value to be written at that time, and the PIO’s
the moment the first channel completes, restarts the first channel. compulsory per-SM FIFOs were getting in the way.
Figure 10 shows the code for the Alarm SM: after reading an
4.3 The Output System initial counter value to synchronize with the system timer, it enters
The output system allows the Sslang program to schedule a single an 8-cycle loop, which we padded to operate at the same frequency
new value to be placed on the output pins at a specific 16 MHz as the input Capture loop. Each loop iteration, the Alarm SM checks
timestamp in the future. It does so with two PIO SMs: the Alarm for an updated alarm target before decrementing the counter and
MEMOCODE ’23, September 21–22, 2023, Hamburg, Germany John Hui, Kyle J. Edwards, and Stephen A. Edwards

uint32_t time_to_pio(time_t t) { .program output_alarm


return ~(uint32_t) t; / / PIO counter decrements ; X: The alarm counter value
} ; Y: Counter/timestamp value (decreasing)
; OSR: Reads the TX FIFO for a new alarm value
time_t pio_to_time(uint32_t ctr) { pull noblock ; Read initial counter value
time_t himask = ~(((time_t) 1 << 32) - 1); mov y, osr
time_t rt = read_timer(); / / System timer
time_t hi = rt & himask; / / Get top 32 bits continue:
time_t lo = (time_t) ~ctr; / / Counter decrements nop ; Stall for 1 cycle
if (lo & (1 << 31) && !(rt & (1 << 31))) {
hi -= (time_t) 1 << 32; / / Correct near epoch .wrap_target
} jmp y--, decr [3] ; Decrement counter, stall 3 cycles
return hi | lo; decr:
} pull noblock ; Read the new alarm value, if any
mov x, osr ; (OSR reads X on TX FIFO empty)
jmp x!=y, continue ; Loop again if alarm not reached
Figure 8: Conversion between 64-bit SSM time and the PIO’s
irq 4 ; Interrupt output buffer
32-bit 16 MHz decrementing counters. We take the top 32 bits
.wrap ; Loop again
from the system timer, being cautious around 32-bit epochs.

time_t read_timer(void) {
Figure 10: Output Alarm PIO program. Every 8 cycles, this
uint32_t lo = timer_hw->timelr; / / Latches timehr
decrements a counter, reads a new alarm value if the CPU
uint32_t hi = timer_hw->timehr;
has written one, and sends an interrupt to the Output Buffer
uint64_t us = ((uint64_t) hi << 32) | lo;
PIO program if the counter matches the alarm.
return us << 4; / / Convert 1 MHz timer to 16 MHz
} .program output_buffer
; ISR: Reads the initial GPIO state
void set_alarm(time_t t) { / / Convert to 1 MHz timer ; OSR: Holds output to GPIO
timer_hw->alarm[ALARM_NUM] = t >> 4; in pins, 32 ; Read current GPIO state
} mov osr, isr ; as the default output value
.wrap_target
Figure 9: Translation between SSM time and the RP2040’s wait 1 irq 4 ; Wait for IRQ from Alarm SM
64-bit 1 MHz timer. Reading the lower 32 bits from the timelr out pins, 32 ; Write OSR to GPIO pins
register latches the upper 32 bits, to avoid a wraparound race. .wrap ; Loop again

Figure 11: Output Buffer PIO program.


comparing it against the current alarm target, sending an interrupt
to the Buffer SM when the alarm target matches the counter. The
Buffer SM (Figure 11) program blocks on IRQ4 before sending data 5.1 Signal Generator a.k.a. Blink
to the output pins (IRQ4 is only visible within the PIO block where Our platform runtime is able to “tick” in as little as 13 µs, with the
the Output and Buffer SMs reside, and used for synchronizing SMs). following variant of the blink program from Section 2:
The main processor changes the Alarm target and the Buffer data
by writing to their respective FIFOs, as shown in Figure 12. Because loop / / The highest frequency Sslang blink program on RP2040
the Buffer SM does not poll its FIFO like the Alarm SM, the main after us 13, led <- 1 - deref led
processor injects a pull instruction to force the Buffer SM to read wait led
the new data from its FIFO. If there is not enough time to set up an This generates a 38.46 kHz square wave. Its speed is limited by
Alarm-triggered output, it injects an out instruction to directly emit interrupt latency, the time it takes for the interrupt service routine
the output at the expense of precise timing. This happens when a to post to the semaphore, the time for the main tick loop to acquire
Sslang program makes an instantaneous assignment to the output the semaphore, check the input queues, tick for an instant, and
variable, or when it schedules a delayed assignment for too soon. schedule a future update with the Alarm and Buffer SMs. The input
system goes unused here.
5 EXPERIMENTAL RESULTS
We run Sslang programs to evaluate our RP2040 platform runtime’s 5.2 Pulse Timer
input and output performance. We find that our timestamp periph- To test the precision of input timestamps and our system’s response
erals provide 62.5 ns timing precision on both input and outputs, to high input loads, we use the program in Figure 13, which mea-
but that the reaction time (input-to-output latency) of the RP2040 sures and reports the width of input pulses. Two parallel processes
platform runtime is at least 13 µs. measure pulse widths and samples the results once a second.
Timestamp Peripherals for Precise Real-Time Programming MEMOCODE ’23, September 21–22, 2023, Hamburg, Germany

void sched_pio_out(time_t t, uint32_t v) { freqcount input =


/ / Enqueue new buffer output value let count = new 0
pio_sm_put(pio0, buffer_sm, v); gate = new ()

/ / Make the SM read this output value: inject a pull instruction after ms 1000, gate <- ()
pio_sm_exec(pio0, buffer_sm, loop
pio_encode_pull(0, 1)); if updated gate / / Was gate assigned just now?
log_count (deref count)
if (t < read_timer() + OUTPUT_MARGIN)
/ / Deadline too close: immediately send the output value if updated input / / Was input assigned just now?
/ / by injecting an out instruction count <- 1 / / Yes: reinitialize count to 1
pio_sm_exec(pio0, buffer_sm, else
pio_encode_out(pio_pins, 32)); count <- 0 / / No: reinitialize count to 0
else {
/ / Set Alarm time after ms 1000, gate <- ()
uint32_t tgt = time_to_pio(t); wait gate / / Pause before counting again
pio_sm_put(pio0, alarm_sm, tgt);
} after ms 1000, gate <- ()
} else
count <- deref count + 1
wait gate || input / / Block until either is assigned
Figure 12: Function that schedules a new value/time event
on the output buffer and alarm programs
Figure 14: A frequency counter that reports the number of
events an input variable each second, after Krook et al. [12].
pulsewidth input = The updated function returns true when the variable was
let result = new 0 assigned in the current instant.
par loop
wait input / / Wait for rising edge
let b = now ()
wait input / / Wait for falling edge
let a = now () We test this program with 10 kHz pulses of varying widths and
result <- a - b / / Compute pulse width record the difference in timestamps between pulse edges. Table 1
loop shows the results. We observe a single least-significant bit of jit-
sleep (ms 1000) / / Pause between logging ter in all cases, likely an artifact of sampling. We attribute the
log_pwm (deref result) roughly 20 ppm errors in the long-period measurements to the
expected precision of the crystal oscillator.
While we do not expect correct results for pulses shorter than
Figure 13: A Sslang program to measure pulse width. Note the 62.5 ns sampling period, we were pleased that the resulting
that the now calls return model time, not wall-clock time. behavior was not absurd. The input SM was still able to observe
certain pulses and conclude that they were short.
Table 1: Pulse widths (in clock cycles) reported by the When short pulses are applied above 200 kHz, we begin to ob-
pulsewidth program serve sporadic but drastic measurement errors. For instance, with
a 320 kHz pulse signal with a 500 ns pulse, the program occasion-
ally reports 808 or 809 ticks instead of the expected 8 or 9. These
Pulse Input Expected Observed Jitter Error errors are due to incoming input events accumulating faster than
80 ms 1 280 000 1 280 021 1 22 the program can process them, overflowing the input ring buffer.
8 ms 128 000 128 002 1 3 It takes 32 events—16 cycles of the pulse signal—to overflow a
800 µs 12 800 12 800 1 1 256 B ring buffer; at 320 kHz, 16 cycles is 50 µs, accounting for the
80 µs 1 280 1 280 1 1 extra 800 ticks we observe.
8 µs 128 128 1 1
800 ns 12.8 13 1 0.2 5.3 Frequency Counter
80 ns 1.28 2 0.72
To further assess our RP2040 runtime’s ability under high input
40 ns 0.64 2 1.36
load, we implement the frequency counter from Krook et al. [12] in
Experimental data for the Sslang pulse timer program in Figure 13. Sslang, shown in Figure 14. This program measures the frequency
Units are 16 MHz clock cycles (62.5 ns). We attribute the 17 ppm of a signal by counting the number of events that appear on an
error in the 80 ms measurement to the crystal oscillator. input variable every second.
MEMOCODE ’23, September 21–22, 2023, Hamburg, Germany John Hui, Kyle J. Edwards, and Stephen A. Edwards

Table 2: Events observed by the frequency counter loopback d input output =


loop
Frequency Expected Events Observed Events let b = now ()
after d, output <- 1 - deref output
30 kHz 60000 60000 wait input / / Should be updated after d
40 kHz 80000 74271 let a = now ()
50 kHz 100000 72670 log_latency (a - b) / / Measure actual latency
60 kHz 120000 71390 sleep (ms 500) / / Pause between rounds
70 kHz 140000 70013
80 kHz 160000 68574
>90 kHz 180000 unstable Figure 15: A loopback program, tested with the input and
output pins connected externally.
Experimental data for the frequency counter program in Figure 14.

/ / Triggered on rising edge of INPUT_PIN


Like Krook et al., we subject our frequency counter to a square void gpio_rise_isr(void) {
wave signal that produces twice the number of events as the fre- gpio_put(OUTPUT_PIN, 1);
quency of the signal (one each for the rising and falling edge). busy_wait_us(100);
We are able to reliably measure frequencies below 37 kHz (74000 gpio_put(OUTPUT_PIN, 0);
events), with only 1 Hz of error due to sampling artifacts. Beyond }
this “reliability ceiling,” the program remains responsive, though
it logs lower event counts than expected; for instance, at 50 kHz,
Figure 16: The reactive 100 µs pulse generator program in C.
the program consistently counts 71390 instead of 100000. Table 2
Additional code configures the GPIO to generate an interrupt
shows our observations.
that runs this code (not shown).
Above 90 kHz, the frequency counter’s event counts are no
longer stable, though they continue to decrease as we push the
input frequency ever higher. We find that the program continues using the input variable. loopback should behave the same whether
to respond up to 740 kHz, albeit completely inaccurately. Above input and output refer to shorted external GPIO pins or the same
that, the program freezes, as the processor spends all of its time internal scheduled variable.
thrashing within the PIO ISR without any opportunity to make any We find that the latency this program logs is exactly equal to the
progress with the user program. prescribed delay d when d is above 17 µs, comparable to the speed
These results show our platform runtime outperforms Scoria’s, of the fastest blink program reported above.
whose frequency counter could only handle frequencies below In earlier runs of this experiment, we observed that the measured
14.5 kHz, and would freeze above that [12]. Our result comes despite latency consistently lagged 1 tick behind the prescribed latency.
the fact that our runtime only uses a 256 B ring buffer; Scoria used This error arises when the input and output SMs’ synchronous loops
4096 B. Part of Scoria’s degraded performance is due to its use of are not correctly phase-aligned, leading the input SM to sample the
Zephyr RTOS’s ISR and device abstractions, which Krook et al. show GPIO pin before the output SM writes the pin. We fixed this lag by
produce considerable overhead. The RP2040’s processor also runs starting the input SM a few instructions later to put it in phase.
twice as fast as the 64 MHz Cortex-M4 on the NRF52840-DK used The loopback test is also useful for detecting when the system
by Krook et al. However, we believe the RP2040 runtime remains clock is not correctly synchronized with the PIO counters, which we
responsive for workloads far beyond its 37 kHz reliability ceiling used to determine the 3 µs offset applied during the SM initialization
chiefly because the responsibility to timestamp events is delegated procedure (see Section 4.1). Because the delayed assignment to
to the PIO hardware, rather than in software. Our input ISR merely output takes place in the same instant as the event on input, a
posts to the semaphore to wake up the main thread. poorly calibrated system clock—running ahead of the PIO timers—
SSM was not designed for throughput; as its name suggests, it is would lead the tick loop to execute the instant before waiting long
best on sparse events, but it also performs well on input bursts. For enough for the input event to show up. When the tick loop tries to
example, we found that with a 256 B ring buffer, it could successfully schedule the late-arriving input event in a later iteration, the SSM
handle 3 MHz bursts of 28 events with no loss of accuracy because runtime complains that it cannot schedule a delayed assignment
the DMA controller could buffer them all before the software had to for an instant it has already executed, and throws a runtime error.
start processing them. A larger buffer would handle longer bursts.
5.5 Reactive 100 µs Pulse Generator
5.4 Loopback To compare the performance of our approach with a more tradi-
To compare the physical timing behavior of Sslang programs with tional C program, we wrote a reactive 100 µs pulse generator in
their logical behavior, we test our system with a loopback connec- C (Figure 16) and in Sslang (Figure 17). The program attempts to
tion running the program in Figure 15. This schedules a delayed match a 100 µs input pulse by “immediately” setting the output high
assignment to the output pin, and awaits events on the input pin. upon seeing input, then setting the output low after 100 µs.
Externally, we connect the output to the input pin, meaning we Our measurements (Figure 18) show the C program reacts faster
indirectly measure the timing of the delayed output assignment at first, but the Sslang falling edge is more accurate. The C program
Timestamp Peripherals for Precise Real-Time Programming MEMOCODE ’23, September 21–22, 2023, Hamburg, Germany

pulse input output =


loop
wait input
if deref input == 1
output <- 1
after us 100, output <- 0

Figure 17: The reactive 100 µs pulse generator in Sslang.

has a shorter reaction time (1.96 µs vs. 13.8 µs; see Figure 18b) be-
cause it eliminates most software overhead by performing all work
in an input-triggered ISR. The Sslang program times the falling (a)
edge significantly better because of our PIO timestamp peripherals.
The output of the C program is 1.43 µs–2.39 µs late (Figure 18c)
because of the initial latency and imprecision in the busy wait
loop, which polls the system timer. the Sslang program’s falling
output is 0 ns–62.4 ns late; that jitter in Figure 18d is purely due to
phase differences between the PIO’s 16 MHz sampling clock and
the frequency generator’s oscillator.
The Sslang output system uses two strategies to transmit output
variable assignments to the environment: for sufficiently later as-
signments, the output system sets the Alarm SM’s target counter to
trigger the Buffer SM when the event is scheduled for; for instanta- (b)
neous assignments, shorter delays, and when the system is running
behind, the output system instructs the Buffer SM to immediately
emit the event, rather than risk missing the output deadline while
programming the Alarm SM. The result is high-precision output
timing when possible, and best-effort timing otherwise.

6 RELATED WORK
6.1 Synchronous Software on Real Hardware
Our RP2040 runtime is not the first implementation of the Sparse
Synchronous Model on real hardware; Krook et al. previously devel- (c)
oped Zephyr bindings for Edwards & Hui’s SSM runtime to run pro-
grams on an NRF52840-DK development board [7, 12]. In contrast to
our work, Krook et al. implement the input and output timestamp-
ing in software: input event timestamps are captured during the
GPIO interrupt service routine, and output event timing depends
on when tick executes the output handler process. Though their
approach does not require specialized hardware like the RP2040’s
PIO, their timestamps’ accuracy is limited by the unpredictable
latency of the interrupt handler. Our RP2040 platform runtime can
capture and emit events far more reliably, as demonstrated by our
pulse generator experiment in Section 5.5. Our approach supports
Scoria-like non-timestamp peripherals alongside timestamp GPIO.
Other synchronous, discrete-event programming models have
also been implemented on real hardware. Jellum et al. [11] imple- (d)
ment an embedded target for Lingua Franca, a polyglot coordination
language that supports event-driven execution like SSM [14]. Like Figure 18: Behavior of the reactive 100 µs pulse generator.
Scoria, Jellum et al.’s embedded target is based on Zephyr RTOS and The top trace (blue) is the input; the middle (cyan) is the
manages timestamps in software. Their square wave generator’s C program’s output; the bottom (magenta) is from Sslang.
1 µs sleep-induced jitter appears consistent with that of our C im- (a) The C and Sslang programs try to match the 100 µs in-
plementation for the reactive pulse generator, and their 28 µs/35 µs put pulse. (b) C responds faster (1.96 µs) than Sslang (13.8 µs).
input/output latency reflects the kind of error we eliminate using (c) The C program’s falling edge is 1.43 µs–2.39 µs late, while
dedicated PIO hardware. (d) the Sslang program is at most 62.4 ns late.
MEMOCODE ’23, September 21–22, 2023, Hamburg, Germany John Hui, Kyle J. Edwards, and Stephen A. Edwards

Zou et al. [20–22] develop PtidyOS to execute programs im- Programmable Real-time Unit (PRU) co-processors that execute
plemented using the Ptides programming model, a precursor to alongside the Beaglebone’s desktop-class processors.
Lingua Franca. PtidyOS features a preemptive EDF scheduler and Such PRET processors are currently much more expensive than
runs on the Luminary microcontroller, though it uses the same the RP2040 we used in this work, and it is unclear how precise tim-
software-based timestamping strategy as Scoria’s runtime and does ing (as opposed to predictable) can be achieved on these machines.
not appear to take advantage of the hardware’s input capture fea- The RP2040’s PIO blocks are technically PRET machines (their
tures. To help developers account for this latency, Zou et al. show parallel SMs even appear to be implemented with an interleaved
that that they can statically determine the schedulability of Ptides pipeline), but their lack of memory access and most arithmetic
programs by annotating actors and input/output ports with worst- operations make them far more limited than other PRET machines.
case latencies and simulating the execution, made possible by the
fixed actor topology of Ptides programs. Sslang trades the ability
to do such analysis for a more expressive programming model. 7 CONCLUSIONS
This work shows how software can achieve high timing precision
6.2 Timestamp Peripherals through access to peripherals that can timestamp input events and
schedule timestamped output events. We demonstrated a system
Certain microcontrollers’ peripherals are capable of a primitive
running on the RP2040, an inexpensive, commodity microcontroller,
form of timestamping termed input capture and output compare.
able to achieve 62.5 ns precision on both input and output, although
For example, Atmel ATmega328P microcontrollers [1] include in-
minimum reaction time is in the 13 µs range. We implemented the
put capture units that sample a 16-bit timer on the rising or falling
input and output systems as precisely timed programs running
edge of a single input. The Microchip PIC32 family of microcon-
on the RP2040’s novel PIO system, but similar results could be
trollers [16] possess similar functionality with a 32-bit timer and
achieved with peripherals implemented in an FPGA or directly on
also include an output compare device that can raise, lower, or
the processor chip.
toggle an output pin when the timer matches a target timestamp.
Although the RP2040 has a 64-bit 1 MHz system timer designed
These facilities are geared toward the measurement and generation
to be a master time base, limitations of the PIO system forced us to
of PWM signals, which are highly periodic and not bursty.
implement separate clocks within the PIO devices, which provided
We chose to implement our SSM runtime on the RP2040 rather
higher timing precision (these clocks run at 16 MHz) as well as
than on an ATmega or PIC32 device because we wanted to take
clock synchronization headaches. While the system clock and the
advantage of the RP2040’s 64-bit timer. Unlike the ATmega328P
PIO clocks run off the same crystal oscillator, it was very important
and PIC32, our timestamp peripherals are implemented using the
to start them in sync and in phase so that the peripheral timestamps
RP2040’s PIO device, and support reading from and writing to
did not “time travel” and cause unexpected behavior. This confirmed
multiple consecutive GPIO pins at the same time.
to us that synchronized clocks are key to implementing the Sparse
Timestamping hardware devices also exist for specific applica-
Synchronous Model.
tions. For instance, the IEEE’s Time-Sensitive Networking proto-
An early plan for the output system had it consuming a sequence
cols [8, 9] ensure deterministic networking between devices syn-
of time-value pairs from a FIFO, but this proved unworkable since
chronized using the Precision Timing Protocol. These devices work
SSM semantics allows a scheduled output event to be replaced
by timestamping network packets; Austad and Mathisen [2] show
with an earlier event. While the SSM runtime handles this with a
that this capability is useful for minimizing network-induced jitter
heap that supports re-insertion, implementing such a data structure
for distributed Lingua Franca programs.
with a PIO is impractical. This led us to the simpler mechanism
Certain Nordic Semiconductor SoCs, such as the NRF52 series [17],
presented above: separate time and value “registers” that can be
include a “programmable peripheral interconnect” system that can
overwritten when preemption is needed. The disadvantage of this
configure a timer to timestamp and schedule events on arbitrary pe-
approach is that the software runtime needs to perform a separate
ripherals including single GPIO pins. This feature appears to enable
action for each output event, even if the desired output sequence
timestamp peripherals, but we are unaware of any implementations.
is known in advance and could be buffered. For future work, we
plan to introduce non-preemptible events to Sslang for reducing
6.3 Timing-Predictable Hardware software load, combined with a DMA-assisted output queue for
Rather than rely on peripherals for precise timing, Precision Timed more reliable and precise burst outputs.
(PRET) machine architectures ensure predictable for the main pro- While the Input SM clock and the RP2040’s system timer are
cessor [6]. This approach typically sacrifices single-threaded perfor- synchronized, there is a small but difficult-to-characterize latency
mance in favor of highly parallel real-time workloads that benefit between when an input event is observed (and timestamped) and
from numerous timing-predictable cores. Jellum et al. [10] propose when the DMA controller makes that event available to the main
InterPRET as a hardware architecture for running Lingua Franca tick loop. The uncertainty arises from any DMA controller latency
programs. Their architecture is comparable to XMOS’s XCore ar- plus any interference from other bus traffic. While short, this latency
chitecture [15], a commercial PRET machine. raises the question of when the system can safely advance time past
Other approaches offload time-sensitive computation to timing- a certain point and be assured that no additional inputs will arrive
predictable co-processors. For instance, Vicuna [18] is a co-processor before that point. Interestingly, this is exactly the problem that
designed for massively parallel workloads. Meanwhile, the Beagle- Zou et al. [22] considered for distributed systems, even though our
bone family of development boards [3] feature timing-predictable system is not one that would traditionally be considered distributed.
Timestamp Peripherals for Precise Real-Time Programming MEMOCODE ’23, September 21–22, 2023, Hamburg, Germany

REFERENCES Daniele Cattaneo (Eds.). Schloss Dagstuhl – Leibniz-Zentrum für Informatik,


[1] Atmel 2015. ATmega328P Datasheet. Atmel, San Jose, California. Dagstuhl, Germany, 3:1–3:13. https://doi.org/10.4230/OASIcs.NG-RES.2023.3
[2] Henrik Austad and Geir Mathisen. 2023. Bounding the End-to-End Execution [12] Robert Krook, John Hui, Bo Joel Svensson, Stephen A. Edwards, and Koen
Time in Distributed Real-Time Systems: Arguing the Case for Deterministic Claessen. 2022. Creating a Language for Writing Real-Time Applications for
Networks in Lingua Franca. In Proceedings of Cyber-Physical Systems and Internet the Internet of Things. In Proceedings of the International Conference on Formal
of Things Week 2023 (San Antonio, TX, USA) (CPS-IoT Week ’23). Association for Methods and Models for Codesign (MEMOCODE). Shanghai, China.
Computing Machinery, New York, NY, USA, 343–348. https://doi.org/10.1145/ [13] Marten Lohstroh, Edward A. Lee, Stephen A. Edwards, and David Broman. 2023.
3576914.3587499 Logical Time for Reactive Software. In Workshop on Time-Centric Reactive Soft-
[3] BeagleBoard Foundation 2023. BeagleBone System Reference Manual. BeagleBoard ware (TCRS). San Antonio, TX, USA, 313––318. https://doi.org/10.1145/3576914.
Foundation, Oakland Charter Township, Michigan. 3587494
[4] Albert Benveniste, Paul Caspi, Stephen A. Edwards, Nicolas Halbwachs, Paul Le [14] Marten Lohstroh, Christian Menard, Soroush Bateni, and Edward A. Lee. 2021.
Guernic, and Robert de Simone. 2003. The Synchronous Languages 12 Years Toward a Lingua Franca for Deterministic Concurrent Systems. ACM Transactions
Later. Proc. IEEE 91, 1 (Jan. 2003), 64–83. on Embedded Computing Systems 20, 4 (July 2021), 1–27. https://doi.org/10.1145/
[5] Stephen A. Edwards and John Hui. 2020. The Sparse Synchronous Model. In 3448128
Forum on Specification and Design Languages (FDL). Kiel, Germany. https://doi. [15] David May. 2012. The XMOS Architecture and XS1 Chips. IEEE Micro 32, 6 (Nov.
org/10.1109/FDL50818.2020.9232938 2012), 28–37. https://doi.org/10.1109/MM.2012.87
[6] Stephen A. Edwards and Edward A. Lee. 2007. The Case for the Precision Timed [16] Microchip 2010. PIC32 Family Reference Manual. Microchip, Chandler, Arizona.
(PRET) Machine. In Proceedings of the 44th Design Automation Conference. San [17] Nordic Semiconductor ASA 2021. NRF52840 Product Specification. Nordic Semi-
Diego, California, 264–265. conductor ASA. https://infocenter.nordicsemi.com/ v1.7.
[7] John Hui and Stephen A. Edwards. 2022. The Sparse Synchronous Model on [18] Michael Platzer and Peter Puschner. 2021. Vicuna: A Timing-Predictable
Real Hardware. ACM Transactions on Embedded Computing Systems (Dec. 2022). RISC-V Vector Coprocessor for Scalable Parallel Computation. In 33rd Euromi-
https://doi.org/10.1145/3572920 cro Conference on Real-Time Systems (ECRTS 2021) (Leibniz International Pro-
[8] IEEE Computer Society 2019. IEEE Standard for a Precision Clock Synchronization ceedings in Informatics (LIPIcs), Vol. 196), Björn B. Brandenburg (Ed.). Schloss
Protocol for Networked Measurement and Control Systems. IEEE Computer Society, Dagstuhl – Leibniz-Zentrum für Informatik, Dagstuhl, Germany, 1:1–1:18. https:
New York, New York. https://doi.org/10.1109/IEEESTD.2020.9120376 //doi.org/10.4230/LIPIcs.ECRTS.2021.1
[9] IEEE Computer Society 2021. IEEE Standard for Local and Metropolitan Area [19] Raspberry Pi Ltd 2023. RP2040 Datasheet. Raspberry Pi Ltd, Cambridge, England.
Networks–Audio Video Bridging (AVB) Systems. IEEE Computer Society, New [20] Yang Zhao, Jie Liu, and Edward A. Lee. 2007. A Programming Model for Time-
York, New York. https://doi.org/10.1109/IEEESTD.2021.9653970 Synchronized Distributed Real-Time Systems. In Proceedings of Real-Time Tech-
[10] Erling R. Jellum, Shaokai Lin, Peter Donovan, Chadlia Jerad, Edward Wang, nology and Applications Symposium (RTAS). 259–268. https://doi.org/10.1109/
Marten Lohstroh, Edward A. Lee, and Martin Schoeberl. 2023. InterPRET: A RTAS.2007.5
Time-Predictable Multicore Processor. In Proceedings of Cyber-Physical Systems [21] Jia Zou, Slobodan Matic, and Edward A. Lee. 2012. PtidyOS: A Lightweight
and Internet of Things Week 2023. San Antonio, TX, USA, 331–336. https://doi. Microkernel for Ptidesa Real-Time Systems. In Proceedings of IEEE Real-Time
org/10.1145/3576914.3587497 and Embedded Technology and Applications Symposium (RTAS), 2012. 209–218.
[11] Erling Rennemo Jellum, Shaokai Lin, Peter Donovan, Efsane Soyer, Fuzail Shakir, http://chess.eecs.berkeley.edu/pubs/858.html
Torleiv Bryne, Milica Orlandic, Marten Lohstroh, and Edward A. Lee. 2023. Be- [22] Jia Zou, Slobodan Matic, Edward A. Lee, Thomas Huining Feng, and Patri-
yond the Threaded Programming Model on Real-Time Operating Systems. In cia Derler. 2009. Execution Strategies for PTIDES, a Programming Model
Fourth Workshop on Next Generation Real-Time Embedded Systems (NG-RES 2023) for Distributed Embedded Systems. In Proceedings of Real-Time Technology
(Open Access Series in Informatics (OASIcs), Vol. 108), Federico Terraneo and and Applications Symposium (RTAS). San Francisco, California, 77–86. https:
//doi.org/10.1109/RTAS.2009.39

You might also like