The SatLink modem is written in SystemVerilog and runs entirely in the FPGA fabric. This section describes it from the outside in: the conventions shared by every block here, the Transmitter Chain and Receiver Chain, and the PL Register Map.
Streams and samples#
Blocks are connected by AXI-Stream interfaces with tvalid/tready back-pressure.
| Stream | Width | Content |
|---|---|---|
| Byte streams (framer → FEC → scrambler → marker → fill) | 8 bits | Bytes, or one symbol per byte after coding: the symbol sits in the low 1 (BPSK/GMSK) or 2 (QPSK) bits |
| Sample streams (modulators → DUC → AD9361, AD9361 → DDC → demodulator) | 32 bits | Complex {Q, I}, each a 16-bit signed Q15 value |
| DMA streams (PS ↔ modem) | 32 bits | le:u8/32: one useful byte in the low lane |
The AD9361 converters are 12-bit. On the transmit side the DAC takes the top 12 bits of each
16-bit sample; on the receive side the 12-bit ADC sample arrives right-aligned and
sign-extended, and rx_ingress packs it into the 16-bit datapath with a programmable Q8.8 gain.
Fixed-point conventions#
| Format | Meaning | Examples |
|---|---|---|
| Q15 | Signed 16-bit, 1.0 = 32 768 (0x7FFF ≈ 1.0) | Samples, DUC gain, channel attenuation |
| Q8.8 | Unsigned, 1.0 = 0x0100 | RX ingress gain |
| Q4.12 | Unsigned, 1.0 = 0x1000, range 0 to < 16 | AGC gain |
| Q10.6 | Unsigned | Demodulator EVM, % of full scale |
| Q32 phase | 32-bit phase increment, full turn = 2³² | NCOs: f · 2³² / fs |
freq_q | Phase increment per symbol, full turn = 2³² | Carrier loop frequency: cfo_hz = freq_q · Rs / 2³² |
Design rules worth knowing as a user#
Four samples per symbol, fixed. The RRC kernels are built at synthesis time for 4 samples per symbol. The symbol rate is changed by the DUC/DDC factor and the AD9361 rate, never by the sps. See RF Fundamentals.
Double-buffered configuration. Most configuration registers are written to a shadow bank and take effect only when the profile sequencer commits, at a moment when no sample is moving. A register read returns the active value. See Bench Architecture.
Hard decisions. The receiver slices on the sign of each component. No symmetric amplitude
change (gain, clipping, saturation) can flip a bit; only a sign flip can. Amplitude experiments
therefore show nothing in bit or frame counts. Look at agc_gain, EVM and the demodulator's
signal and error powers instead.
Headroom in front of the matched filter. The receive AGC regulates power before the
matched filter, whose DC gain is about 2.8. Its target is therefore set 12 dB below a full-scale
QPSK (|A|² = 0x0409C0C9, about 5820 per component), so the filter's peaks do not clip. A clipped
QPSK symbol lands on the diagonal, where the carrier loop's error is zero, and the loop becomes
blind to phase.
Accumulators sized from the bound. The FIR accumulators are sized from A · Σ|h|, the worst
case, not from a measured amplitude: the RRC has negative side lobes, so the worst input is the
one matched to sign(h).
Symmetric FIR folding. The 33-tap RRC filters exploit linear-phase symmetry through the DSP48 pre-adders, which is what lets the whole design fit the Zynq-7020's 220 DSP slices.
Where the modem is verified#
Every block has a cocotb testbench under pl/sim/ (Verilator):
. .venv/bin/activate
cd pl && make -C sim -f $PWD/tools/verilator.mk TEST=<name> PL_ROOT=$PWD simA green simulation does not guarantee a working bitstream: Vivado is stricter than Verilator, and
a bench can only prove what it asserts. The hardware-topology loopback bench (test_hw_loopback)
mirrors the product's real top-level wiring, loopback mux included, for that reason.