|
rcbench
Servo and ESC test bench: panel, IOmcu and shared C
|
A trace of INA3221 CH1's 1ms samples as text lines, for a debug build of the IOmcu (its SENSE_TRACE build option). More...
#include <stdatomic.h>#include <stdbool.h>#include <stddef.h>#include <stdint.h>#include "sense_sched.h"Go to the source code of this file.
Data Structures | |
| struct | sense_trace_rec_t |
| One record: 12 bytes. More... | |
| struct | sense_trace_src_t |
| Core 1's own. More... | |
| struct | sense_trace_mark_t |
| A trigger line waiting. More... | |
| struct | sense_trace_watch_t |
| One PWM slot's pulse as last seen. More... | |
| struct | sense_trace_out_t |
| Core 0's own. More... | |
| struct | sense_trace_t |
| A trace of the FAST channel's samples, shared between the two cores. More... | |
Enumerations | |
| enum | sense_trace_kind_t { SENSE_TRACE_CURRENT = 0 , SENSE_TRACE_BUS , SENSE_TRACE_CLIP_HI , SENSE_TRACE_CLIP_LO , SENSE_TRACE_CFG , SENSE_TRACE_SHUNT } |
| What a record holds. More... | |
| enum | sense_trace_trig_t { SENSE_TRACE_TRIG_CMD = 0 , SENSE_TRACE_TRIG_EDGE , SENSE_TRACE_TRIG_KEY , SENSE_TRACE_MARK_DEST , SENSE_TRACE_MARK_STEP } |
| What started or extended a trace. More... | |
| enum | sense_trace_end_t { SENSE_TRACE_END_NONE = 0 , SENSE_TRACE_END_TIME , SENSE_TRACE_END_SETUP , SENSE_TRACE_END_KEY } |
| Why a trace ended. | |
| enum | sense_trace_stage_t { SENSE_TRACE_IDLE = 0 , SENSE_TRACE_HEAD_T , SENSE_TRACE_HEAD_H , SENSE_TRACE_BODY } |
| Where the header stands. More... | |
Functions | |
| bool | sense_trace_init (sense_trace_t *tr, sense_trace_rec_t *buf, uint32_t size) |
A trace over buf of size records. | |
| void | sense_trace_feed (sense_trace_t *tr, const sense_sched_t *s, uint64_t tick_us) |
Core 1, after each tick of s; NULL while no bus is open. tick_us is the time the caller read before the tick, on the clock the schedule reads: no time the tick's samples carry lies before it. | |
| void | sense_trace_trigger (sense_trace_t *tr, sense_trace_trig_t kind, uint64_t at_us, uint16_t ch, uint16_t us) |
Core 0: a trigger of kind at at_us on the clock the schedule reads; ch and us are a cmd trigger's channel and pulse. | |
| uint16_t | sense_trace_rendered (uint16_t pulse_us, uint32_t top) |
The pulse a PWM slice renders when asked for pulse_us with its counter wrapping at top: the frame's length at most, as the driver clamps it. | |
| bool | sense_trace_pulse (sense_trace_t *tr, unsigned slot, uint16_t ch, uint16_t pulse_us, uint64_t now_us) |
Core 0, every pass, for each PWM slot: the output channel ch it renders and the pulse, 0 for none. | |
| void | sense_trace_unwatch (sense_trace_t *tr, unsigned slot) |
Core 0: slot slot renders no PWM pulse of its own – unbound, or another driver's: the next pulse seen on it starts its watch afresh. | |
| void | sense_trace_key (sense_trace_t *tr, int c, uint64_t now_us) |
| Core 0: a character from the console. | |
| size_t | sense_trace_pump (sense_trace_t *tr, uint64_t now_us, bool connected, char *out, size_t room) |
Core 0, every pass: up to room bytes of whole lines into out; returns their length. connected false drops a trace under way. | |
| bool | sense_trace_active (const sense_trace_t *tr) |
| Whether a trace is under way. | |
| uint32_t | sense_trace_held (const sense_trace_t *tr) |
| Records the ring holds. | |
| int32_t | sense_trace_code (int32_t ua, uint32_t shunt_uohm) |
The shunt code of ua on a shunt of shunt_uohm: the inverse of ina3221_current_ua(), 0 for no shunt. | |
A trace of INA3221 CH1's 1ms samples as text lines, for a debug build of the IOmcu (its SENSE_TRACE build option).
No released image links this file. CH1 here is the schedule's FAST channel (sense_sched.h), CH1 of the one part on a bench set up through SENSE; with no FAST channel nothing is traced but the set-up.
Two sides, one on each core, with a ring of records between them:
Core 1 sense_trace_feed(), once per 1ms tick after the schedule has run. It reads the schedule's memory and nothing else: no bus transaction, no clock. CH1's newest sample comes out of the schedule's own history (sense_sched_t.hist), CH1's newest bus voltage and whether the sample was clipped out of the window being filled. A voltage is kept only with the current sample of its tick, so each voltage line belongs to the sample line before it. The INA3221's set-up and state go into the ring as records of their own when they change, so they keep their place among the samples. At most 4 records a call, 2 while the set-up stands. A full ring drops the record and counts it; the call never waits. A set-up record that found no room is written again at the next call, and counted each time it finds none. Core 0 sense_trace_trigger(), sense_trace_pulse(), sense_trace_key() and sense_trace_pump(), from the main loop. The pump hands back whole lines, never more bytes than the caller has room for, so a write of them does not wait on the console.
The ring is single-producer, single-consumer and lock-free: core 1 writes a record and then moves head, core 0 reads a record and then moves tail, each index stored with release and loaded with acquire ordering. Two counts pass beside the ring, both core 1's: the records dropped so far (drops) and the start of the last tick whose records are in (fed). The ring's size is the caller's, a power of two.
Dropped records. The first record kept after a drop carries the count of the records dropped before it, and a $L line stands before its line. A sample dropped while a set-up record waits for room is counted after that record: core 1 adds it to the count once the record is in, so a trace that ends at the set-up change does not count it. A drop with no record kept after it is in drops alone: core 1 adds to drops at the drop and stores fed at the end of the tick, core 0 reads fed, then drops, then the ring's head. A ring found empty after the two holds every record kept before them, so drops less the counts those records carried is what was dropped since the last of them; core 0 writes a $L line for it at once and takes it off the count of the record that carries it later. No end line is written before that: an end line counts every dropped record whose time lies before the trace's end. It can also count records dropped at or after the end, up to the first record kept there; no later trace counts them again. A trace that is dropped writes no end line, and its $L lines count for nothing: a later trace that holds the record a count rides on says the count again.
A trace. With no trace running the pump keeps the newest SENSE_TRACE_PRE records, none of them older than the last change of the set-up or the state, and discards the rest. A trigger starts a trace: the header, the records the ring holds from before the trigger, then every record until the trace's end, SENSE_TRACE_EDGE_MS after a command or a capture edge and SENSE_TRACE_KEY_MS after the console command. A trigger during a trace adds its line and moves the end to its own when that lies later; it never shortens a trace. The console's stop moves the end to the time of the stop, or to just past the trace's own trigger when that lies ahead; a trigger line whose time lies at or past that end is dropped. A trigger at or past a trace's end, or after its stop, is not that trace's, however much of the trace the console still owes: it waits, up to SENSE_TRACE_MARKS of them, and starts the next trace when the end line is written. A trace also ends at a record that changes the INA3221's shunt or Configuration; a trigger whose line was not written by then starts the next trace, unless the console stopped this one. However a trace ends, every record from before its end is written first. With the ring empty the end line waits for core 1: after each tick's records the feed publishes the time the tick started at, and a tick that started at or past the end brings no record of before it. A trigger line waits for its time the same way. Core 1 not seen past the time SENSE_TRACE_STALL_MS after it: the line is written all the same, and the end line says so.
Times are counts of 0.1ms modulo 2^32, compared as the later of two that lie less than 2^31 (59.6h) apart. None is kept to be compared for longer: a slot's last change is forgotten once the slot is seen SENSE_TRACE_HOLD_MS past it, and a trace the console has not taken SENSE_TRACE_OWED_MAX_T after its end is dropped with the triggers that waited for it.
Triggers. cmd a PWM slot renders another pulse width than in the pass before, SENSE_TRACE_HOLD_MS or more after that slot's last change (sense_trace_pulse()). Changes closer together are one slewed command: they move the trace's end, and once the slot has held still for SENSE_TRACE_HOLD_MS a $D line gives the pulse the command ended at. A command that waits for the next trace takes its later changes with it: that trace ends SENSE_TRACE_EDGE_MS after the last of them and has the $D line; they take one of the SENSE_TRACE_MARKS places. A command whose trigger found a queue full has no $D line. A pulse going to 0 is the bank letting go, not a command: a slewed command ends there, and the next pulse is a command however soon it comes. The pulse is the one the slice renders: no longer than its frame (sense_trace_rendered()). edge the capture's PWM edge, when a capture is armed. key t on the console. x ends a trace.
Lines, format version SENSE_TRACE_FORMAT. Each ends CR LF and is at most SENSE_TRACE_LINE_MAX bytes with them. Times t are the 64-bit microsecond clock divided by 100, modulo 2^32: 0.1ms, as the capture counts.
$T v=3 n=N trig=cmd|edge|key t=T ms=M len=L The trace numbered N (modulo 65536) starts. T is the trigger's time, M the millisecond tick at it (modulo 2^32), L the length in ms the trigger asks for. $H dt_us=1000 shunt_uohm=R cfg=0xCCCC on=B rst=K The sample period, CH1's shunt in micro-ohms, the Configuration value the driver holds and reads back every 40ms, whether the part is online, and its reset count modulo 256: as they stand at the trace's first record. All 0 with the bus closed. The lines after these two are in the order of their times, a $D line excepted: a trigger line stands behind the samples taken before its time, the first trigger's behind the samples from before the trigger. $C t=T ch=C us=P a cmd trigger: output channel C renders P µs in the frame that starts at T $D t=T ch=C us=P cmd=S channel C's slewed command ended at P µs; T is its last change, S its $C line's T: the command the line ends. Counted with the trigger lines. Written once the pulse has stood still for SENSE_TRACE_HOLD_MS (50ms), so it follows the sample lines up to about 50ms past T; T, not the line's place, is its time. $E t=T an edge trigger $K t=T a key trigger D,I a CH1 sample: D is its time less the previous sample's, 0.1ms, for the first sample less the header's T (negative for a sample from before the trigger); I is the shunt code, 40µV a step: I * 40 / R amps. A clipped sample reads the end of the range it is at or past, 4095 or -4096; 4094 and -4095 are values. vU CH1's bus voltage, U mV, read in the tick of the sample line before it $S on=B rst=K the part's state or reset count changed here: the samples above it were taken before $L n=X X records are missing here: the ring was full. With no record kept since, the line stands behind the last record written. $Z n=N s=S v=V l=X m=A ml=B e=t|s|k|T|S|K The trace ends: S sample lines, V voltage lines, X records missing, A trigger lines, B triggers that found the trigger queue full. e is the reason: its time, a changed set-up, the console. A trace that ends at a changed set-up leaves the triggers after the change to the next trace, and with them those that found the queue full after it; with such on both sides of the change both traces count all of them. A trace that ends between a slewed command's $C line and its $D line has the $C line alone. A capital letter: core 1 was not seen past the end, or past a trigger line's time, within SENSE_TRACE_STALL_MS, so a sample of before that time can be missing or stand behind the line. The counts saturate: S and X at 99999999, V at 9999999, A and B at 9999.
A console that is not connected: the pump is told so, drops a trace under way without a line and counts it (out.abandoned).
Pure C, no SDK (software development kit): core 1's side in sense_trace_feed.c, core 0's in sense_trace.c. Host-tested in test_sense_trace_feed, test_sense_trace, test_sense_trace_next, test_sense_trace_setup and test_sense_trace_lines.
SPDX-License-Identifier: MIT
| enum sense_trace_kind_t |
What a record holds.
| enum sense_trace_stage_t |
| enum sense_trace_trig_t |
| bool sense_trace_active | ( | const sense_trace_t * | tr | ) |
Whether a trace is under way.
| tr | The trace. |
| int32_t sense_trace_code | ( | int32_t | ua, |
| uint32_t | shunt_uohm ) |
The shunt code of ua on a shunt of shunt_uohm: the inverse of ina3221_current_ua(), 0 for no shunt.
| ua | The current, in µA. |
| shunt_uohm | Shunt resistance, in µΩ. |
| void sense_trace_feed | ( | sense_trace_t * | tr, |
| const sense_sched_t * | s, | ||
| uint64_t | tick_us ) |
Core 1, after each tick of s; NULL while no bus is open. tick_us is the time the caller read before the tick, on the clock the schedule reads: no time the tick's samples carry lies before it.
| tr | The trace. |
| s | The schedule that has ticked. |
| tick_us | The time the caller read before the tick, in µs. |
| uint32_t sense_trace_held | ( | const sense_trace_t * | tr | ) |
Records the ring holds.
| tr | The trace. |
| bool sense_trace_init | ( | sense_trace_t * | tr, |
| sense_trace_rec_t * | buf, | ||
| uint32_t | size ) |
A trace over buf of size records.
False, and nothing usable, unless size is a power of two from 2 to 2^24.
| tr | The trace to set up. |
| buf | The record buffer. |
| size | Number of records in buf. |
| void sense_trace_key | ( | sense_trace_t * | tr, |
| int | c, | ||
| uint64_t | now_us ) |
Core 0: a character from the console.
| tr | The trace. |
| c | The character from the console. |
| now_us | The current time, in µs. |
| bool sense_trace_pulse | ( | sense_trace_t * | tr, |
| unsigned | slot, | ||
| uint16_t | ch, | ||
| uint16_t | pulse_us, | ||
| uint64_t | now_us ) |
Core 0, every pass, for each PWM slot: the output channel ch it renders and the pulse, 0 for none.
A slot seen with another channel than before is watched afresh. True when this is a command: the caller then gives the time of the frame that carries it to sense_trace_trigger(), as a SENSE_TRACE_TRIG_CMD for every true return. That trigger sets the command time the slot's later $D line carries; without it the line carries an earlier command's time, or 0 on a slot that has none.
| tr | The trace. |
| slot | The PWM slot. |
| ch | The output channel the slot renders. |
| pulse_us | The pulse, in µs; 0 for none. |
| now_us | The current time, in µs. |
| size_t sense_trace_pump | ( | sense_trace_t * | tr, |
| uint64_t | now_us, | ||
| bool | connected, | ||
| char * | out, | ||
| size_t | room ) |
Core 0, every pass: up to room bytes of whole lines into out; returns their length. connected false drops a trace under way.
| tr | The trace. |
| now_us | The current time, in µs. |
| connected | false drops a trace under way. |
| out | Receives the lines. |
| room | Bytes that fit in out. |
| uint16_t sense_trace_rendered | ( | uint16_t | pulse_us, |
| uint32_t | top ) |
The pulse a PWM slice renders when asked for pulse_us with its counter wrapping at top: the frame's length at most, as the driver clamps it.
| pulse_us | The requested pulse, in µs. |
| top | The counter's wrap value. |
| void sense_trace_trigger | ( | sense_trace_t * | tr, |
| sense_trace_trig_t | kind, | ||
| uint64_t | at_us, | ||
| uint16_t | ch, | ||
| uint16_t | us ) |
Core 0: a trigger of kind at at_us on the clock the schedule reads; ch and us are a cmd trigger's channel and pulse.
| tr | The trace. |
| kind | The trigger kind. |
| at_us | Time of the trigger, in µs. |
| ch | The channel of a cmd trigger. |
| us | The pulse of a cmd trigger, in µs. |
| void sense_trace_unwatch | ( | sense_trace_t * | tr, |
| unsigned | slot ) |
Core 0: slot slot renders no PWM pulse of its own – unbound, or another driver's: the next pulse seen on it starts its watch afresh.
| tr | The trace. |
| slot | The PWM slot. |