BKPT LabsDOCS/VIEWALYZER RECORDER/TRANSPORTS AND SNAPSHOTS VIEWALYZER · FIRMWARE RECORDER
VIEWALYZER · RECORDER API

Transports and snapshots

The recorder writes one packet stream; VA_TRANSPORT picks how it leaves the chip, and the host's connection file (.vacf) names the matching reader. This page pairs them up and covers the post-mortem ring.

Which transport

VA_TRANSPORT NEEDS ON THE TARGET HOST TRANSPORT (.VACF) THROUGHPUT WHEN
RAM_BUFFER (default)RAM for the ring (8 KB default); any Cortex-Mstlink-rambuf, jlink-rambufLimited by the probe's SWD memory reads and the SWD clock; the throughput/ examples measure it per probeEvery board; no pins beyond SWD; the only choice on M0/M0+/M23
ARM_ITMAn ITM and the SWO pin wired to the probe (Cortex-M3/M4/M7/M33)stlink-swo, jlink-swoThe SWO rate: 2 MHz is 200 KB/s, a STLINK-V3 takes up to 24 MHzHighest bandwidth, and the only transport that also carries hardware trace (PC samples, exception trace, data watches) on the same pin
JLINK_RTTSEGGER RTT (SEGGER_RTT.c in the build)stlink-rtt, jlink-rttSimilar to the RAM bufferProjects that already use RTT for logging
CUSTOM_TRANSPORTA byte pipe of your own (UART, USB CDC, radio) and core/viewalyzer_cobs.cserial, udpYour link's rateNo debug probe at run time, or a remote target

Every probe transport also takes --target-device, --speed-khz and, with several probes attached, --stlink-serial or --jlink-serial.

RAM buffer

#define VA_TRANSPORT    RAM_BUFFER
#define VA_RAMBUF_SIZE  16384u
#define VA_RAMBUF_MODE  VA_RAMBUF_MODE_DROP     /* DROP | BLOCK | WRAP */

The recorder keeps a ring with a control block in RAM; the probe reads it over SWD while the core runs, so the firmware never waits on a pin. The host finds the ring by the _VA_RAMBUF symbol when you pass --elf, or by scanning RAM for the magic tag:

{
  "transport": "stlink-rambuf",
  "target-device": "STM32G474RE",
  "speed-khz": 24000,
  "rambuf-scan-start": "0x20000000",
  "rambuf-scan-size": 131072
}
$ viewalyzer-cli capture --config nucleo_g474_rambuf.vacf --elf build/rambuf/Nucleo_G474_VA.elf --output run.vadb --duration 10

Modes: DROP loses the newest packet when the ring is full (the sequence byte tells the host how many); BLOCK spins in the writer until the probe drains, which keeps every event at the cost of timing; WRAP overwrites the oldest, turning the ring into a post-mortem window (below). On a part with a data cache place the ring in non-cacheable memory with VA_RAMBUF_ATTRIBUTES and a matching linker section; otherwise nothing is needed.

ITM over SWO

#define VA_TRANSPORT  ARM_ITM
#define VA_ITM_PORT   1                          /* port 0 stays free for printf */

The host programs the ITM, the TPIU and the trace clock through the probe on connect (init-swo, on by default), so the firmware does not touch the trace registers. The two sides must agree on the core clock and the SWO rate:

{
  "transport": "stlink-swo",
  "target-device": "STM32G474RE",
  "cpu-clock-hz": 170000000,
  "swo-freq-hz": 2000000,
  "itm-port": 1
}

The rate is bounded by the probe's receiver (2 MHz on a V2 ST-LINK and a J-Link over probe-rs, 24 MHz on a STLINK-V3) and must divide the core clock in whole numbers. When nobody drains the pin the ITM FIFO fills; the recorder detects the stall and drops rather than blocks, then re-arms from VA_TickOverflowCheck() once a host appears. Hardware trace (--dwt, --dwt-pc, --dwt-watch, the hardware-trace block) rides the same pin; see the CLI reference.

RTT

#define VA_TRANSPORT     JLINK_RTT
#define VA_RTT_CHANNEL   0
#define VA_CONFIGURE_RTT 1                       /* the recorder sizes the channel itself */
{ "transport": "stlink-rtt", "target-device": "STM32F446RE", "rtt-channel": 0 }

The host finds _SEGGER_RTT in RAM (or --rtt-address / --elf pins it) and drains the up-channel named ViewAlyzer, else the configured index. Works over an ST-LINK too: the RTT control block is ordinary memory.

Custom transport

#define VA_TRANSPORT CUSTOM_TRANSPORT

static void uart_send(const uint8_t *data, uint32_t length)
{
    HAL_UART_Transmit_DMA(&huart2, data, (uint16_t) length);   /* one COBS frame per call */
}

VA_RegisterTransportSend(uart_send);       /* before VA_Init */
VA_Init(SystemCoreClock);

Packets arrive COBS-framed and zero-delimited (core/viewalyzer_cobs.c does the framing); ship each buffer as is. On the host, the serial transport reads a port, udp listens on a socket; both expect COBS ("cobs": true by default):

{ "transport": "serial", "serial-port": "COM7", "baud": 921600 }
{ "transport": "udp", "udp-port": 17200 }

Buffered mode

VA_TRANSPORT_BUFFERED=1 stages packets in a RAM ring (VA_BUFFER_SIZE, a power of two) and ships them when you call VA_Drain() from the idle loop, so an ISR-heavy system never blocks on the pipe. Applies to ITM, RTT and custom transports; the RAM buffer transport is already a ring.

Post-mortem snapshots

Two ways to keep the last events across a crash:

  • VA_RAMBUF_MODE_WRAP: the live RAM buffer itself overwrites the oldest packets, so it always holds the most recent window.
  • VA_SNAPSHOT=1: a second ring (VA_SNAPSHOT_SIZE, with VA_SNAPSHOT_SETUP_SIZE reserved for the names) alongside any live transport.

In both cases call VA_SnapshotFreeze() from the fault or assert handler; the ring stops and the window is preserved through the handler, a reset loop, or a halted core. The host reads it without resetting the target:

$ viewalyzer-cli snapshot --config nucleo_g474_rambuf.vacf --elf firmware.elf --output crash.vadb

The envelope reports ring (post-mortem or live-drop), events, window_bytes, discarded_packets, wrapped, frozen; the recording opens like any other, with the last seconds before the freeze. viewalyzer-cli reset resets the target afterwards.

Sizing the transport

A packet is 7 bytes for an ISR edge or a task switch (type, sequence, id, 32-bit timestamp), 11 for a value, 12 plus the text for a marker. As a rule of thumb at 100 KB/s: a 1 kHz task switch rate with a few values per millisecond fits in a RAM buffer; a 10 kHz ISR with entry and exit traced (140 KB/s by itself) does not, and belongs on the SWO pin or should be sampled. The capture summary's lost_events and the sequence gaps tell you when the transport fell behind; VA_RAMBUF_MODE_BLOCK trades timing for completeness while you find out where.