All docsStellar LinkRF bench · by Stellar Systems v0.1.0

Radio Concepts

Bench Architecture

How the PL modem, the AD9361, the Linux drivers and satlinkd fit together: address map, DMA, clocks, the three signal paths and the daemon's services.

This page describes the SatLink system as a whole, from the FPGA fabric up to the network services. It is the map to keep in mind when reading a telemetry value or a report.

The Zynq, split in two#

flowchart TB
    subgraph PS["PS: dual Cortex-A9, Linux"]
      sd[satlinkd]
      uio["/dev/uio0 (1 MiB register window)"]
      iio["IIO: satlink-modem-tx / -rx, ad9361-phy, cf-ad9361-dds-core"]
      sd --> uio
      sd --> iio
    end
    subgraph PL["PL: xc7z020 fabric"]
      shell["shell: AXI-Lite decode, IRQ, loopback mux"]
      tx[tx_top]
      rx[rx_top]
      ch[channel_impair]
      mon[monitor_top]
      seq[profile_sequencer]
      dmac["axi_dmac tx / rx / adc"]
      ad["axi_ad9361 (LVDS)"]
    end
    uio -- "M_AXI_GP0, AXI-Lite" --> shell
    iio -- "S_AXI_HP, DMA" --> dmac
    shell --> tx & rx & ch & mon & seq
    dmac --> tx
    rx --> dmac
    tx --> ad
    ad --> rx
    ad <--> chip((AD9361))
  • Control plane. The PS reaches the PL's registers through one 1 MiB AXI-Lite window at 0x4000_0000, mapped into user space by the kernel's UIO driver as /dev/uio0 (named satlink). satlinkd reads and writes registers by name through a catalogue that shares its addresses with the daemon's own code (see PL Register Map).
  • Data plane. Bytes travel between the PS and the modem through ADI axi_dmac cores, exposed by a SatLink kernel module (satlink-modem.ko) as two IIO buffer devices, satlink-modem-tx and satlink-modem-rx. A third DMA channel captures raw ADC samples for IQ captures and the spectrum view.
  • RF front end. ADI's axi_ad9361 core connects the fabric to the AD9361's LVDS bus. The AD9361 itself is configured by the Linux ad9361-phy driver over SPI; its DAC core appears as cf-ad9361-dds-core.
RegionAddressContent
SatLink shell0x4000_0000, 1 MiBSeven 64 KiB windows: shell, RX DDC, RX demod, TX, monitor, common (sequencer), channel
axi_ad93610x4010_0000ADI interface core
axi_dmac TX0x4011_0000PS → modem DMA
axi_dmac RX0x4012_0000modem → PS DMA
axi_clkgen0x4013_0000Clock generator

The DMA wire format: le:u8/32#

The modem's byte streams are 8 bits wide, but the DMA moves 32-bit words. Each 32-bit little-endian word carries one useful byte in its low lane; the other three bytes are zero.

QuantityValue
IIO buffer length4096 samples ([datapath] buffer_length)
DMA block4096 × 4 = 16 384 bytes
Useful bytes per block4096

satlinkd expands and extracts the lanes for you: what you push through the API, the CLI or the link service is plain bytes, and what the link service publishes is lane-extracted. Two consequences matter:

  • A block is only submitted to the DMA when it is full. Low-rate traffic must be flushed (see Datapath and Transmission).
  • Frame lengths used for slot framing and transfer frames must divide 4096, or alignment is lost at the first flush.

Files for satlinkctl loopback and satlinkctl compare are in the raw le:u8/32 encoding, unless compare --dense is given.

Clocks#

ClockFrequencyDomain
fclk0100 MHzControl plane: AXI-Lite decode, registers, profile sequencer
clk_fpga_2 (FCLK2)50 MHzModem datapath (modem_clk) and the DMA streams
AD9361 l_clkSet by the AD9361's data rateThe axi_ad9361 core

Register accesses to modem blocks cross from fclk0 to the modem clock through a request and acknowledge synchroniser; samples cross between the modem clock and the AD9361 clock through two asynchronous FIFOs.

The three signal paths#

The chain target decides where the transmitter's samples go:

TargetPathEmits RFUses the AD9361
pl (PL loopback)TX chain → channel emulator → RX chain, GLOBAL_CTRL[0] = 1NoNo
device-loopbackTX chain → AD9361 → internal digital loopback, before the mixers → RX chainNoYes (LVDS and DAC source only)
airTX chain → AD9361 → mixers → TX1 connector … RX1 connector → RX chainYesYes
  • The PL loopback is the reference: it has no analog path and no frequency or phase error, and it is the only path with the channel emulator.
  • The device loopback proves the LVDS interface in both directions. The LOs, gains and attenuation have no effect on it at all, and nothing reaches a connector; calling it "RF" is a classic source of confusion.
  • The air target is the only one that reaches the connectors. Your cabling decides whether the signal comes back into RX1 (an external loopback) or reaches another radio.

A profile's radio.loopback flag writes the loopback mux on every apply. Chain conditioning applies the profile first and sets the mode afterwards, so the mux ends up where the target wants it. See Chain Conditioning.

Shadow registers and the profile sequencer#

Most modem configuration registers are double-buffered. A write lands in the shadow bank, the datapath keeps running on the active bank, and the profile sequencer swaps them in one cycle when asked. Its states:

SEQ_STATENameMeaning
0x00IDLENothing pending
0x01STAGEDWrites staged
0x02VALIDATINGWaiting for the software's validation bit
0x03WAIT_SAFEWaiting for a safe point: 64 cycles with no sample on the TX or RX stream
0x04COMMITSwapping banks
0x05REARMRe-arming

A commit only happens at a safe point. If some stream never goes quiet, the sequencer stays in WAIT_SAFE and the apply times out: SEQ_STATE = 0x03 is therefore also a sign that the datapath is jammed. Reads always return the active bank, so a staged write reads back as the old value until it is committed.

Inside satlinkd#

satlinkd is one Rust process built from a set of crates under ps/crates/:

ServiceCrateRole
PL driversatlink-pl-driverUIO register access, the register catalogue, write tracing (to /dev/kmsg when enabled)
Profile managersatlink-profile, satlink-profile-managerLoad, validate and translate profiles; stage and commit
Radio runtimesatlink-radio-runtimeAD9361 control over IIO, NCO offsets, chain conditioning, tones, receiver recovery
Datapathsatlink-datapath, satlink-packetIIO buffers, block assembly, le:u8/32, slot and stream framing, CSP/CCSDS packet building
Scenario enginesatlink-scenario, satlink-scenario-engineLoad and run scenarios, sample telemetry, evaluate assertions, write reports
Campaign enginesatlink-campaign, satlink-campaign-engineRun campaigns, keep the cursor on persistent storage
Telemetrysatlink-telemetryPoll PL and AD9361 metrics at poll_interval_ms
Link servicesatlink-link, satlink-ccsdsZeroMQ bridge, CCSDS transfer frames
IQ and spectrumsatlink-iq, satlink-spectrumIQ captures and FFT from the ADC capture DMA
APIsatlink-apiREST, WebSocket, OpenAPI
Storesatlink-storeSQLite database of runs, reports and profile revisions
Observabilitysatlink-observabilityLogging, Prometheus exporter on port 9090

The run store lives at /var/lib/satlink/satlink.db, on the RAM disk: reports do not survive a reboot. Export the ones you need (see Analysis & Reporting). Campaign cursors live on the persistent /mnt/jffs2 partition so a campaign can resume after a reboot it asked for.

Where things are configured#

WhatWhereChanged by
Radio configurationProfiles, /opt/satlink/dsl/profiles/*.yamlFirmware build, or PUT /api/v1/profiles/{name} (in memory only)
Test timelinesScenarios, /opt/satlink/dsl/scenarios/**/*.yamlFirmware build, or POST /api/v1/scenarios (written to the RAM disk)
Test roadmapsCampaigns, /opt/satlink/dsl/campaigns/*.yamlFirmware build
Daemon behaviour/etc/satlinkd/satlinkd.tomlFirmware build (stellar-fw/overlay/etc/satlinkd/satlinkd.toml)
The modem itselfThe bitstream, system_top.bin on the SD cardA PL build and a release

Stellar Link · v0.1.0

↑↓ to moveEnter to open