Firmware
This page provides an overview of the QICK firmware components.
System Overview
The QICK firmware includes the following components:
tProcessor v2 - Real-time co-processor for sequencing and feedback
Signal Generators - DDS-based waveform generators for DAC outputs; QICK ships several generator cores (Signal Generator v6 (SG-v6) - QICK Firmware being the flagship arbitrary-envelope one), and a board’s standard firmware provisions its own mix – see Signal Generators - QICK Firmware
Readout System - ADC data acquisition with DDC, filtering, and averaging
Sampling frequency of ADC blocks is given by the variable soc.fs_adc.
Sampling frequency of DACs is stored in variable soc.fs_dac.
Fast-speed buffers were removed to save memory space. Raw data can be captured after x8 down-sampling.
System-level block diagram: the tProcessor sequences the signal generators (SG) and readouts, which sit between it and the DACs/ADCs; the Zynq PS configures everything over AXI (purple) and is the boundary between the PS (software) and PL (FPGA fabric) sides of the chip.
Signal Generators
QICK ships several signal-generator IP cores for the DAC-output stage – Signal Generator v6 (SG-v6) - QICK Firmware (arbitrary I/Q envelopes, the flagship/default choice), Interpolated Signal Generator v2 (axis_sg_int4_v2) - QICK Firmware (arbitrary envelope, 4x-interpolated, lighter-weight), Multiplexed Signal Generator with Mixer (axis_sg_mixmux8_v1) - QICK Firmware and Signal Generator Mux8 (axis_sg_mux8_v1) - QICK Firmware (fixed multi-tone/CW, up to 8 simultaneous tones per channel), and the legacy Signal Generator v4 (SG-v4) - QICK Firmware. Which one(s) a given board’s standard firmware uses, and how many of each, is a per-board decision – see Standard (“_std”) Firmware Per Board.
Signal Generator v6 (SG-v6), the flagship arbitrary-envelope core, has the following features:
Supports uploading I/Q envelopes
32-bit resolution for both frequency and phase
Configurable DDS with streaming mode (frequency can change sample-to-sample)
16 parallel samples per clock cycle (256-bit output bus)
Maximum envelope length: 65536 samples per channel
For the full generator family and which one fits which experiment, see Signal Generators - QICK Firmware. For complete SG-v6 documentation, see Signal Generator v6 (SG-v6) - QICK Firmware.
Readout System
QICK ships several readout IP variants for the DDC/decimation stage –
Readout v2 (axis_readout_v2) - QICK Firmware (PYNQ-register-configured, single tone),
Dynamic Readouts (axis_readout_v3 / axis_dyn_readout_v1) - QICK Firmware (axis_readout_v3/axis_dyn_readout_v1,
tProc-configured per shot, single tone), and PFB Readouts (axis_pfb_readout_v2 / v3 / v4) - QICK Firmware
(axis_pfb_readout_v2/v3/v4, a fixed polyphase-filter-bank
channelizer that fans one ADC input out to several simultaneous demodulated
channels). Which one(s) a given board’s standard firmware uses, and how many,
is a per-board decision – see Standard (“_std”) Firmware Per Board. All variants
feed the same downstream block, Average + Buffer (Averager + Buffer (axis_avg_buffer) - QICK Firmware):
Readout Block (architecture varies by variant – see the pages above):
Digital down-conversion (DDC)
FIR filtering
Decimation
Average + Buffer Block:
Raw sample storage
Coherent averaging
Triggered by a dedicated single-bit
trig_N_opin from the tProcessor – one per averaging buffer instance, not part of the same channel-numbered AXI-Stream space as the readout’s own configuration; see tProc Waveform Ports vs. Trigger Outputs
For complete documentation, see Readout v2 (axis_readout_v2) - QICK Firmware, Dynamic Readouts (axis_readout_v3 / axis_dyn_readout_v1) - QICK Firmware, and PFB Readouts (axis_pfb_readout_v2 / v3 / v4) - QICK Firmware (the DDC/decimation stage, in its several variants) and Averager + Buffer (axis_avg_buffer) - QICK Firmware (the averaged/raw buffer IP and its register map).
The tProcessor v2
The tProcessor (timing Processor) is a hard real-time co-processor inside the QICK FPGA. It runs user-written programs that control waveform generation, data acquisition, and feedback with nanosecond precision.
For complete documentation, see:
tProcessor v2 - reference manual - Full reference manual (architecture, instruction set, programming examples)
tProc v2 ASM Cheatsheet - Quick reference for assembly programming
Quick links to tProcessor topics:
2. Quick Reference Card - Most common instructions and condition codes
4. Complete Register Bank Reference - Complete register bank reference
9. Task‑Based Examples (Copy‑Paste Ready) - Copy-paste ready code examples
8. Peripherals Deep Dive - ARITH (multiply), DIV, LFSR
Standard (“_std”) Firmware Per Board
QICK does not use one universal channel layout. For each supported board,
the qick firmware repository ships a standard reference design
(“_std”, under firmware/Top/<board>/tpv2_std/) that provisions a
different mix of generator and readout IP suited to that board’s DAC/ADC tile
count and I/O capacity. A board’s other firmware builds (e.g. the ZCU216
“_demo” project) can provision yet another mix again. There is no fixed
“channel N is always generator type X” rule across boards – what follows is
each board’s own layout, traced directly from that board’s block-design
source (bd_2023-1.tcl, on branch feature/114-integrate-hog-fusesoc)
rather than assumed:
Board ( |
Interpolated Signal Generator v2 (axis_sg_int4_v2) - QICK Firmware |
Multiplexed Signal Generator with Mixer (axis_sg_mixmux8_v1) - QICK Firmware |
Dynamic Readouts (axis_readout_v3 / axis_dyn_readout_v1) - QICK Firmware (dyn v1) |
PFB Readouts (axis_pfb_readout_v2 / v3 / v4) - QICK Firmware (v4) |
||||
|---|---|---|---|---|---|---|---|---|
ZCU111 ( |
8 |
0 |
0 |
0 |
4 |
0 |
4 |
1 |
RFSoC4x2 ( |
2 |
0 |
0 |
0 |
3 |
0 |
2 |
1 |
ZCU216 ( |
4 |
11 |
1 |
0 |
2 |
1 |
10 |
1 |
ZCU216 |
2 |
2 |
1 |
1 |
1 (plus 1 |
0 (1 |
7 |
1 |
Counts are per-board instance counts of each IP core, obtained by grepping
each board’s bd_2023-1.tcl for its create_bd_cell lines – not a
per-channel numbering scheme. See Worked Example: ZCU216 tpv2_std below for how
the ZCU216 tpv2_std numbers above translate into an actual tProc
channel/DAC/ADC map.
Note
If using the Xilinx XM500 daughter board (ZCU111), be aware of DAC/ADC tile filters (a physical hardware property of that daughtercard, not of the firmware’s channel numbering):
DAC 229 CH0/CH1: High-pass (1 GHz) → output ≥ 1 GHz
DAC 229 CH2/CH3: Low-pass (1 GHz) → output ≤ 1 GHz
ADC 224 CH0/CH1: Low-pass (1 GHz) → input ≤ 1 GHz
DAC 228 channels: No filters
This note was not re-verified against the ZCU111 tpv2_std block
design in this pass (only the ZCU216 design was traced in detail); treat
the DAC/ADC tile numbers as Xilinx’s own physical labels, independent of
whichever tProc channel happens to feed them.
tProc Waveform Ports vs. Trigger Outputs
In tProc v2, two architecturally separate sets of qick_processor ports
are involved in driving generators/readouts and firing triggers – unlike
tProc v1, where a single AXIS-channel numbering space carried both (this
page previously described tProc v2 hardware using that older model; the
distinction below is the correction):
Waveform/config ports (
m0_axis..``mN_axis``, driven byWPORT_WR/QickProgram.pulse()) are full AXI-Stream ports that carry generator pulse commands and dynamic-readout per-shot configuration words. How many exist is a synthesis parameter ofqick_processor(CONFIG.OUT_WPORT_QTY) – 16 on ZCU216tpv2_std, matching its 16 total DAC channels/generator instances (4 SG-v6 + 1 SG-mixmux8 + 11 SG-int4-v2, per the table above).Trigger outputs (
trig_0_o..``trig_N_o``) are independent single-bit pins, each wired with a plainconnect_bd_net(not an AXI-Streamconnect_bd_intf_net) directly to one PMOD bit or one IP block’striggerinput. How many exist is a separate synthesis parameter (CONFIG.OUT_TRIG_QTY) – 20 on ZCU216tpv2_std. They are not part of the same numbering space as the waveform ports, and (also unlike tProc v1) they are not bits packed inside a generator channel’s data word either.
Both counts were read directly off qick_processor_0’s CONFIG.*
properties in the ZCU216 tpv2_std bd_2023-1.tcl this session; they
are synthesis-time parameters, so other boards’ firmware can and do
instantiate different totals (verify per board rather than assuming these
numbers).
Full ``trig_N_o`` mapping, ZCU216 ``tpv2_std`` (verified via ``connect_bd_net`` on ``qick_processor_0/trig_*_o``):
Trigger pin(s) |
Drives |
|---|---|
|
|
|
|
|
|
|
|
This replaces the old, incomplete claim that readout triggers were “bit 14”
and “bit 15” of a channel-0 data word – on ZCU216 tpv2_std there are 20
independent trigger pins, only 8 of which are PMOD, and the remaining 12
individually fire the board’s 10 avg_buffers, its one mr_buffer_et, and
its DDR4 capture path. A board with a different avg_buffer count (see
Standard (“_std”) Firmware Per Board) will have a different OUT_TRIG_QTY and a
correspondingly different mapping – always confirm against that board’s own
bd_*.tcl (or, from Python, treat the driver-level trigger/arm calls as
the source of truth rather than the raw pin numbers).
There is also a third, separate set of ports: feedback inputs
(s0_axis..``s9_axis`` on qick_processor_0, CONFIG.IN_PORT_QTY=10
on ZCU216 tpv2_std), each individually wired (via a per-channel clock
converter) to one averaging buffer’s m2_axis live-feedback output –
i.e. one real-time feedback input per avg_buffer, again not “2 input
channels” as this page previously claimed.
Worked Example: ZCU216 tpv2_std
The ZCU216 tpv2_std design is traced here in full (it is the flagship
board and what ships in the QICKbox). All of the following was read from
firmware/Top/216/tpv2_std/bd_2023-1.tcl (branch
feature/114-integrate-hog-fusesoc) in this session – generator counts
match Standard (“_std”) Firmware Per Board above.
DAC side – tProc waveform port to DAC channel:
The RFDC (usp_rf_data_converter_0) has 4 DAC tiles, 4 channels each
(s00..``s33``, 16 channels total). qick_processor_0’s core clock
(t_clk_i) is tied to DAC tile 2’s sample clock; SG-int4-v2 and
SG-mixmux8 (tiles 1-3) run on that same clock, so their tProc ports connect
straight through. DAC tile 0 (the 4 SG-v6 channels) runs on its own,
independent sample clock, so those 4 ports cross through a
clock-domain-crossing block (axis_cdcsync_v1_0, RTL parameter N=4)
first.
tProc port |
CDC/demux stage |
Generator instance |
Generator type |
DAC tile:ch |
|---|---|---|---|---|
|
|
|
SG-v6 |
tile 0 : 0 |
|
|
|
SG-v6 |
tile 0 : 1 |
|
|
|
SG-v6 |
tile 0 : 2 |
|
|
|
SG-v6 |
tile 0 : 3 |
|
|
|
SG-mixmux8 |
tile 1 : 0 |
|
direct (same clock domain as tProc core) |
|
SG-int4-v2 |
tile 1 : 1 |
|
direct |
|
SG-int4-v2 |
tile 1 : 2 |
|
direct |
|
SG-int4-v2 |
tile 1 : 3 |
|
direct |
|
SG-int4-v2 |
tile 2 : 0 |
|
direct |
|
SG-int4-v2 |
tile 2 : 1 |
|
direct |
|
SG-int4-v2 |
tile 2 : 2 |
|
direct |
|
SG-int4-v2 |
tile 2 : 3 |
|
direct |
|
SG-int4-v2 |
tile 3 : 0 |
|
direct |
|
SG-int4-v2 |
tile 3 : 1 |
|
direct |
|
SG-int4-v2 |
tile 3 : 2 |
|
direct |
|
SG-int4-v2 |
tile 3 : 3 |
Every qick_processor_0/mN_axis port passes through a sg_translator
instance before reaching its generator (18 sg_translator instances exist
in total: 16 feed the generators above, sg_translator_16/_17 feed the
two dynamic readouts’ config ports instead – see below). Register slices
(axis_register_slice_0-_7) also sit inline on the SG-v6 path, but
these stay on one clock (they don’t add a second CDC hop) – they’re
ordinary pipeline stages for timing closure, confirmed by both sides sharing
the same clock net in the .tcl.
m4_axis is the one port that isn’t a clean 1-generator mapping: RTL for
axis_tmux_v1 (firmware/ip/axis_tmux_v1/src/tmux.sv) shows it demuxes
one AXI-Stream input to one of N outputs by an address tag carried in the
data word itself (the top 8 bits), not a fixed per-port assignment. On this
board (CONFIG.N=3), that lets tProc channel 4 carry pulse commands for
the SG-mixmux8 generator and per-shot config words for both dynamic
readouts, distinguished by that tag – one of the demuxed outputs goes
straight to sg_translator_4 (mixmux8, same clock domain), and the other
two cross into the ADC-tile-2 clock domain via a second CDC block,
axis_cdcsync_v1_2 (N=2), before reaching sg_translator_16/_17
and then axis_dyn_readout_v1_0/_1’s s0_axis config ports.
On the CDC blocks’ purpose: axis_cdcsync_v1 (RTL:
firmware/ip/axis_cdcsync_v1/src/cdcsync.sv) exposes independent
input-side and output-side clock/reset pairs for up to N parallel
same-width streams – it is a clock-domain-crossing bridge, confirmed by
tracing each instance’s aclk nets in this board’s .tcl (input side tied
to the tProc core clock, output side tied to the destination tile’s own
clock). It is not part of the xcom multi-board synchronization
mechanism (XCOM: Multi-Board Network Synchronization and Communication) despite the similar name – that is a wholly
separate IP block with its own network/serial interface. Both CDC instances
on this board exist purely because DAC tile 0 and the ADC-tile-2-clocked
readout logic run on clocks independent of the tProc’s own core clock, not
for any cross-board purpose.
ADC side – readout instances:
axis_dyn_readout_v1_0– ADC input from tile 2, channel 0 (usp_rf_data_converter_0/m20_axis).axis_pfb_readout_v4_0– ADC input from tile 2, channel 1 (m21_axis); channelizes into 8 outputs,m0_axis..``m7_axis``.axis_dyn_readout_v1_1– ADC input from tile 2, channel 2 (m22_axis).
Each dynamic readout’s decimated output (m1_axis) and each of the PFB
readout’s 8 channelized outputs feed their own axis_broadcaster, which
fans out to (a) one dedicated axis_avg_buffer (1:1 – 10 total: 2 for the
dynamic readouts + 8 for the PFB readout’s 8 channels) and (b) a shared
10-input AXI-Stream switch (axis_switch_ddr) feeding the single DDR4
capture path, letting software pick which one of the 10 channels streams to
DDR4. Separately, the two dynamic readouts’ undecimated outputs
(m0_axis) feed a 2-input switch (axis_switch_mr) into the single
shared mr_buffer_et_0 instance. This matches, and was cross-checked
against, the independently-traced fan-out in PFB Readouts (axis_pfb_readout_v2 / v3 / v4) - QICK Firmware’s
“6. Real-World Usage: ZCU216 “tpv2_std”” section.
Checking Your Own Board’s Configuration
Every table on this page describes one specific board’s firmware build, traced from that build’s source. Your board may be a different one, or the same board with a different firmware build – do not assume generator counts, readout counts, or channel/trigger mappings from this page apply to your setup. Instead, read them from the running system:
soc.gens– list of generator drivers, in board-config order (index is not guaranteed to equal the tProc waveform-port number – see Worked Example: ZCU216 tpv2_std for why that mapping can be non-trivial).soc.readouts– list of readout drivers (mix of Readout v2 (axis_readout_v2) - QICK Firmware/Dynamic Readouts (axis_readout_v3 / axis_dyn_readout_v1) - QICK Firmware/PFB Readouts (axis_pfb_readout_v2 / v3 / v4) - QICK Firmware types, board-dependent).soc.avg_bufs– list of Averager + Buffer (axis_avg_buffer) - QICK Firmware drivers.print(soccfg)– full human-readable dump of the board’s configuration, including per-channel generator/readout types and every buffer’savg_maxlen/buf_maxlen.
Timing
FPGA clock: 384 MHz → period = 2.6 ns
DAC speed: 384 × 16 = 6144 MHz → resolution ~163 ps
ADC speed: 384 × 8 = 3072 MHz, decimated by 8 → resolution ~2.6 ns
Minimum DAC pulse length: 16 samples (shorter pulses can be zero-padded)
Firmware Parameters
Note
This table previously carried tProc-v1-era figures (8k-instruction
program memory, 256-sample stack, 16-bit gain) copied into this page
unchanged. tProc v2’s real numbers, confirmed against
tProcessor v1 (axis_tproc64x32_x8) - reference manual and tProcessor v2 - reference manual in this session, are quite
different – notably the stack is 8 entries deep, not 256, and
program/data memory sizes are synthesis parameters
(PMEM_AW/DMEM_AW), not a fixed “8k”/”4096”. As with everything
else on this page, treat board-specific sizes as something to read from
soccfg (or soc.tproc.cfg), not to assume.
Parameter |
Value |
|---|---|
Pulse memory length (per SG-v6) |
65536 samples ×2 (I/Q) = 128k total ( |
Decimated ADC buffer |
Board-dependent, |
Accumulated ADC buffer |
Board-dependent, |
tProc v2 program/data memory |
Synthesis parameters ( |
tProc v2 stack size |
8 entries (fixed, not a synthesis parameter) – see tProcessor v2 - reference manual |
Phase conversion |
\(\Delta \phi = 2\pi/2^{32}\) or \(360/2^{32}\) degrees |
Gain range (tProc v2 / |
32-bit signed – verified against RTL ( |