The catalogued PL registers by name, address and access; the shell control bits; reading and writing registers safely from the CLI, the API and scenarios.
The PL exposes one 1 MiB AXI-Lite window at 0x4000_0000, split into seven 64 KiB windows; each
modem block has a 256-byte sub-window inside its window. satlinkd addresses registers through a
catalogue of names (ps/crates/satlink-pl-driver/src/catalog.rs) whose addresses are the same
constants the daemon itself uses, so a name in the CLI and a name in the firmware cannot mean
different things.
satlinkctl reg list deframer # filter by substring
satlinkctl reg get rx_demod.deframer.frame_cnt
satlinkctl reg set tx.scrambler.ctrl 0xFF01# write, then read back
satlinkctl pl # shell, IRQ and sequencer snapshot
Route
Purpose
GET /api/v1/pl/regs?filter=<substr>&values=true
List catalogued registers, optionally with their values
GET /api/v1/pl/regs/{name}
Read one: {"name", "addr", "access", "value"}
POST /api/v1/pl/regs/{name}
Write one: {"value": 65281}; returns the value read back
GET /api/v1/pl/registers/{addr}
Read by address (only catalogued addresses are safe)
GET /api/v1/pl/status, /pl/profile-sequencer, /pl/interrupts
Decoded snapshots
ws://<board>:8080/ws/pl/registers
Every register write, as it happens
A manual write marks the active profile dirty. In scenarios, use a
reg: step; to follow a register over a
run, add it to [telemetry] watch_registers.
Remember the two traps of reading back:
Shadow registers (most configuration registers of the modem and the channel emulator)
return the active value: a write reads back as the old value until the profile sequencer
commits.
Pulse bits (clear bits, monitor.power_meter.ctrl[1]) read 0 by design.
0 IDLE, 1 STAGED, 2 VALIDATING, 3 WAIT_SAFE, 4 COMMIT, 5 REARM (7 after a manual pulse)
common.seq.seq_status, seq_err_code
Status and error code (1 = validation refused)
common.seq.seq_timeout, seq_profile_id
Timeout and the id of the last committed profile (0xC0DE for a channel-only commit)
To commit staged writes by hand, write 0x101 (apply and validation OK) to
common.seq.seq_ctrl; 0x1 alone is refused with error 1. After a manual pulse the sequencer
sits in state 7 and ignores the next commit until something re-arms it (a profile apply does).
SEQ_STATE = 3 that does not clear means some stream never goes idle: the datapath is jammed.
Every register the daemon knows, with its absolute address and access (RW read/write, RO read
only, W1C write 1 to clear). Descriptive or inert registers are flagged in the
Transmitter Chain and Receiver Chain pages.
The DAC core of the AD9361 interface (dac_data_sel, the DDS tones) lies outside this window. The
daemon reaches it through the cf-ad9361-dds-core IIO driver; see
Chain Conditioning.