Readout v2 (axis_readout_v2) - QICK Firmware
axis_readout_v2 is the digital down-converter (DDC) IP that sits between
the RFDC’s ADC output and Averager + Buffer (axis_avg_buffer) - QICK Firmware. It is the readout used on
tProc-v1-style single-tone (non-multiplexed, non-PFB) readout channels. The
module lives under firmware/ip/axis_readout_v2/ and is exposed to Python
through qick.drivers.readout.AxisReadoutV2.
1. General Description
axis_readout_v2 mixes the ADC’s real-valued input samples down to
baseband with an on-chip DDS, then applies an FIR anti-aliasing filter and
decimates by 8 before handing the complex (I, Q) stream to
Averager + Buffer (axis_avg_buffer) - QICK Firmware. Unlike the newer PFB-based readouts, it demodulates a
single, software-tunable frequency (no simultaneous multi-tone channelizer).
The datapath, in order:
down_conversion (
down_conversion.v) – complex multiply of the real ADC input against a local DDS tone (dds_compiler_0), 16 parallel lanes (N_DDS=16samples/clock, matching the RFDC’s parallel output).down_conversion_fir (
down_conversion_fir.v) – FIR low-pass (fir_compiler_0, fixed coefficients compiled into the bitstream) plus decimation by 8.Output to
axis_avg_buffer’ss_axis.
Top-level block diagram of the current fusesoc-ported RTL
(readout_top_ro_v2.sv / down_conversion_ro_v2.sv), verified
directly against that source. Note: this reflects N_DDS=8
(fixed by the shared, reused fir_decim8_dynro block) and a 3-way
OUTSEL mux (product/dds/raw ADC) inside down_conversion_ro_v2,
which differs from the N_DDS=16 / no-OUTSEL description above
– that prose describes the pre-fusesoc-port firmware/ip/
axis_readout_v2/ RTL and hasn’t been reconciled with the port yet.
2. Frequency/Output Control (ctrl.sv)
Register writes don’t take effect directly – ctrl.sv packs
freq/phase/nsamp/outsel/mode into a single 83-bit
descriptor pushed through a FIFO (driven by the WE_REG pulse), then
drains that FIFO into the N_DDS DDS phase accumulators:
Field |
Width |
Description |
|---|---|---|
|
32 bits |
DDS tuning word (downconversion frequency). |
|
32 bits |
DDS phase offset. |
|
16 bits |
Sample count used when |
|
2 bits |
Output source (see register map). |
|
1 bit |
0: run for |
The complex multiply itself: each of the 16 lanes takes one 16-bit real ADC
sample and multiplies it against that lane’s complex DDS output
(dds_compiler_0, 32-bit {Q,I}), producing a 32-bit signed product per
component, rounded (add 2**15, keep bits [31:16]) back down to 16-bit
I/Q before the output mux – this is the “product” outsel value.
3. Register Map
Register |
Width |
Description |
|---|---|---|
|
32 bits |
Downconversion DDS frequency. |
|
32 bits |
DDS phase offset. |
|
16 bits |
Sample count for one-shot mode ( |
|
2 bits |
0: product (mixer output, the normal downconverted signal). 1: dds (raw local oscillator tone, for calibration/debugging). 2: input (bypass – the raw ADC sample, delay-matched to the other paths). |
|
1 bit |
0: NSAMP (stop after |
|
1 bit (pulse) |
Toggling this 0->1->0 pushes the current FREQ/PHASE/NSAMP/OUTSEL/MODE register values into the descriptor FIFO as one atomic update. |
4. Key Parameters
Parameter |
Value |
Notes |
|---|---|---|
Downsampling ratio |
8 |
Fixed FIR decimation; exposed to Python as |
DDS/phase width |
32 bits |
|
IQ offset |
0 |
Unlike the muxed/PFB readouts, |
tProc control |
none |
This readout is PYNQ/software-configured only; it has no
|
5. Python Interface
As with the buffer (Averager + Buffer (axis_avg_buffer) - QICK Firmware), normal programs configure this
readout via QickProgram.declare_readout() (freq, phase,
sel, gen_ch), not by writing registers directly – see
Readout System - QICK Firmware’s Python Interface section for the full
declare_readout()/trigger()/acquire() workflow.
AxisReadoutV2.set_all()/set_all_int() exist for direct register
access but are explicitly for debugging: “Normally the [readout] is
configured based on parameters supplied in QickProgram.declare_readout().”