Multi-channel support
Configuring the number of channels
One Waveflow Rfdc represents multiple RF-ADC and RF-DAC datapaths. How many is set by two
parameters in the configuration:
n_rx— RF-ADC datapaths, for receive (RX)n_tx— RF-DAC datapaths, for transmit (TX)
n_rx = 0 (or n_tx = 0) is not an error: that is a transmit-only or receive-only converter, and
the absent path simply has no process, no rate check and no BFM model. Its endpoints still exist,
unbound, which costs nothing and keeps the endpoint set a property of the class rather than of a
build.
Interfaces
The two sides of an Rfdc count channels differently, and each takes the form its consumer wants:
| side | shape | why |
|---|---|---|
| fabric | n_rx AXI-Stream master ports + n_tx slave ports, one per channel |
it is what the IP presents, and one wide interleaved port would push a vendor packing rule into every design that touches a converter |
| RF | one RFSampIF per direction, carrying every channel of that direction in one block |
the RF environment and your logic both want the channels together; splitting it would give n_ch events per block period, against the whole point of block-rate modelling |
n_ch is the same number
The RF edge names its channel count n_ch; the converter names its n_rx and n_tx.
They are the same number — n_ch == n_rx on the RX edge, n_ch == n_tx on the TX edge — and each
name sits on the object it belongs to. Rfdc reads the edge’s value at bind and refuses a
disagreement, rather than picking a winner.
Binding the ports
Waveflow uses one AXI-Stream per RF-ADC/DAC datapath, following the same convention as the AMD
converters. In the Python model rx_streams and tx_streams are therefore lists, and you index them
when binding — even when there is only one path:
adc_axis.bind("master", rfdc.rx_streams[0]) # even with n_rx == 1
One spelling, no special case for the channel count that every example happens to use.
Future Vivado lowering
Connecting Waveflow-generated logic to real AMD RFDC blocks in Vivado is manual today. You
replace the Waveflow Rfdc with the corresponding AMD RFDC blocks and wire your logic to them; the
AXI-Stream interfaces are designed to match AMD’s channelization and bit packing, so the fabric side
lines up (see the AXI formatting notes). You then configure the converters from the
host by writing their AXI-Lite registers.
Two things Waveflow does not do: it does not model the AXI-Lite configuration of the RFDC blocks, and it does not generate a Vivado project wired to them. Both may come later.
Tiles
Some of the Waveflow documentation uses the word tile — not in AMD’s sense of it.
In the AMD IP a tile is a group of same-direction converters sharing a clock and a power-up
sequence: a Quad RF-ADC tile holds four RF-ADCs (in two pairs, each pair configurable for I/Q), a Dual
tile holds two, and RF-ADC and RF-DAC tiles are separate. A Waveflow Rfdc carries both
directions, so it spans an ADC tile and a DAC tile, and n_rx need not be a whole tile’s worth.
What the model does borrow from the tile is the epoch. t0_rx and t0_tx are each a tile’s
sample counter starting, and they are two separate numbers precisely because the ADC and the DAC are
two separate tiles — started separately, and often clocked at different rates.
Next
- Real and I/Q — what a sample on a channel is, and the two flags that say so.
- Quickstart — a converter wired end to end.
- The RF side — where
n_ch,blksizeand the sample clock are declared.
Source of truth: waveflow/hw/rfdc.py, waveflow/hw/rf_sample_if.py,
plans/adc_model.md § Channels, ports, and where I/Q lives.