Data Merger (ALT)
Intel/Altera build of the Data Merger: the same binary tree of two-way merger nodes with distributed hold registers, but with the per-input FIFOs built from the Intel dcfifo_mixed_widths megafunction instead of the Xilinx XPM FIFO. It is offered only on Altera-based boards; the arbitration, the back-pressure protocol, the latency and the pin set are identical to the Xilinx Data Merger.
Introduction
Principle of Operation
Data Merger (ALT) concentrates 2, 4, 8, 16 or 32 independent data streams
into one output stream. Each input has a data word (IN_x) and a data-valid
strobe (DV_x); the block emits one word at a time on OUT with DV_OUT, and
tells each source when to stop with BUSY[IN]_x.
Internally it is a binary tree of two-way merger nodes. Each node has one hold register per branch: if both branches present a word on the same clock it forwards branch A and parks branch B, releasing it on the next clock. Nothing is arbitrated centrally, so congestion is absorbed where it happens.
What “(ALT)” means
This block and the plain Data Merger are two builds of the same design.
The two VHDL files in the plugin (DataMerger.vhd and DataMerger_alt.vhd)
are byte-for-byte identical except for the points listed below, and the
component classes declare the same properties and the same pins.
| Data Merger | Data Merger (ALT) | |
|---|---|---|
| offered on | Xilinx boards (_ANY_XLX) |
Altera / Intel boards (_ANY_ALT) |
| input FIFO primitive | xpm_fifo_async (Xilinx XPM) |
dcfifo_mixed_widths (library altera_mf) |
| FIFO read mode | first-word-fall-through (READ_MODE = "fwft") |
first-word-fall-through (lpm_showahead = "ON") |
| FIFO reset | synchronous rst |
asynchronous aclr |
| device family baked into the FIFO | none (XPM is generic) | intended_device_family = "Cyclone V" |
default clock net if CLK is left open |
project acquisition clock | project asynchronous clock |
core DV_OUT / BUSY_OUT port type |
std_logic_vector(0 downto 0) |
std_logic |
| tree topology, arbitration, hold registers, latency | identical | identical |
| properties and pins | identical | identical |
In short: choose it because of the board, not because of the behaviour. If your target is an Intel FPGA, this is the merger that will compile; if it is a Xilinx FPGA, use the plain Data Merger. Everything you read about arbitration, ordering and flow control on the Data Merger page applies here unchanged.
Pin Description
IN0 on the canvas). Captured on the clocks
where DV_0 is high. One IN_x pin exists per channel, IN_0 …
IN_(N-1), where N is Number of inputs; all of them are Input Word
Size bits wide.
BUSY[IN]_0 rising, otherwise the word already parked
in the node’s hold register is overwritten and lost without warning.
IN_0. Channel 1 loses every collision
against channel 0 at the first tree node.
DV_0.
BUSY on the canvas). While high the
root node stops emitting, the hold registers fill from the leaves upward
and the BUSY[IN]_x outputs rise one after another. Tie it low (or leave
it unconnected) if the consumer never stalls.
aclr).
BUSY0 on the canvas). With
an input FIFO it is the FIFO’s wrfull; without one it is the tree node’s
own busy flag, which gives only one clock of slack. One pin per channel.
See the warning above before wiring it.
BUSY[IN]_0.
DV on the canvas). High for one clock per merged
word. Never asserted while BUSY[OUT] is high.
Properties
Set the number of input to the virtual block
Number of input channels, which sets the depth of the merger tree and therefore the minimum latency (one clock per level).
| value | tree levels | minimum latency |
|---|---|---|
| 2 | 1 | 1 clock |
| 4 | 2 | 2 clocks |
| 8 | 3 | 3 clocks |
| 16 | 4 | 4 clocks |
| 32 | 5 | 5 clocks |
Changing it rebuilds the symbol (the IN_x / DV_x / BUSY[IN]_x pins are
created from it). See the warning about the 16- and 32-input
configurations.
Default: 2
Default: 2
Options: 2 4 8 16 32
Set the size of the input buffer in front of each input
Depth, in words, of the per-channel input FIFO
(dcfifo_mixed_widths, lpm_numwords). 0 removes the FIFOs entirely and
wires the inputs straight into the tree, leaving only the one-word hold
register per branch as buffering. Larger values absorb bursts at the cost of
on-chip memory: the total is Input Fifo Size × Input Word Size ×
Number of inputs bits.
Allowed: 0, 64, 128, 512, 1024.
Default: 64
Default: 64
Options: 0 64 128 512 1024
Set the input word size in bits
Width in bits of every input word and of OUT. Range 2 to 1024, edited
in steps of 8. All channels share it — this block cannot merge streams of
different widths.
Default: 32
Default: 32
Range: 2 – 1024
⚙️ Detailed Operation
The merger node
Every node in the tree is one clocked process (entity merger in
DataMerger_alt.vhd) with two one-word hold registers, dHa and dHb, and
their occupancy flags. On each clock:
- Downstream busy (
dObusy = '1') → assert busy on both branches; any word arriving on A or B is written into that branch’s hold register. - Hold not empty → release A’s hold first, then B’s; a word arriving in the same clock is written into the hold register behind it.
- Hold empty, both branches valid → forward A, park B, assert busy on both.
- Hold empty, one branch valid → forward it, release the busy flags of any branch that is not holding.
Two consequences follow directly:
- Branch A wins every collision, so the input with the lower index has priority at every level of the tree. Under sustained congestion the highest channel numbers are served last.
- Order across channels is not preserved. Two words injected on the same
clock on
IN_0andIN_15travel through different numbers of nodes, so they arrive in an order that depends on the tree, not on the input timing. If order matters, put a timestamp in the word.
Back-pressure contract — one clock of slack, then silent loss
BUSY[IN]_x is registered: a node decides to assert busy on the clock it
accepts a word, and the source sees it on the next clock. The single hold
register is exactly the budget for that round trip:
- A source that stops within one clock of seeing
BUSY[IN]_xrise loses nothing — the in-flight word lands in the hold register. - A source that keeps asserting
DV_xfor a second clock while the node is holding and the output is busy overwrites the hold register. The old word is gone; there is no error flag, no overflow output, nothing in the stream to show it happened.
This is the single most common way to lose data with this block. With
Input Fifo Size > 0 the FIFO absorbs the burst and BUSY[IN]_x is the FIFO’s
full flag instead, which gives you the whole FIFO as slack — which is why the
default is 64 and not 0.
Input FIFOs (Intel dcfifo_mixed_widths)
With Input Fifo Size > 0, each channel gets one dcfifo_mixed_widths
instance:
| generic | value |
|---|---|
lpm_numwords |
Input Fifo Size |
lpm_width / lpm_width_r |
Input Word Size |
lpm_showahead |
"ON" (first word fall through) |
intended_device_family |
"Cyclone V" |
rdsync_delaypipe / wrsync_delaypipe |
4 |
overflow_checking / underflow_checking |
"ON" |
use_eab |
"ON" (on-chip memory blocks) |
wrfull drives BUSY[IN]_x directly, and a word is popped whenever the FIFO
is not empty and the tree node behind it is not busy. Two details are worth
knowing:
- Both FIFO clocks are tied to the same
clk, yet the instance is a dual-clock FIFO with four-stage synchroniser pipelines on each side. That costs several extra clocks of write-to-readable delay compared with the Xilinx build, and it means the empty/full flags are conservative. It does not affect correctness — only latency and the point at whichBUSY[IN]_xrises. aclris tied toRESET, so the FIFOs clear asynchronously. Give the block a clean, de-asserted reset before you start streaming.
With Input Fifo Size = 0 the FIFOs disappear, IN_x/DV_x go straight into
the tree and BUSY[IN]_x becomes the tree node’s own busy flag — one word of
slack, as described above.
Latency
Each merger node registers both its data and its data-valid output, so an uncontended word costs exactly one clock per tree level:
| Number of inputs | tree levels | minimum latency |
|---|---|---|
| 2 | 1 (merger) |
1 clock |
| 4 | 2 (four_way_merger) |
2 clocks |
| 8 | 3 (eight_way_merger) |
3 clocks |
| 16 | 4 (sixteen_way_merger) |
4 clocks |
| 32 | 5 (two 16-way trees + one node) | 5 clocks |
Add the input FIFO delay when Input Fifo Size > 0, and add one clock per collision the word loses on its way down. Aggregate throughput is one word per clock at the output regardless of the number of inputs — the tree serialises, it does not widen.
:::ALERT
16 and 32 inputs: verify before you rely on channels 8–11 (and 24–27).
In the final combining stage of sixteen_way_merger, the C branch takes its
data from the third sub-tree but its data-valid and busy from the
fourth:
four_way_merger5 : four_way_merger
dC => dO3, -- data from sub-tree 3
dCint => dOint4, -- but valid from sub-tree 4
dCbusy => dOBusy4, -- and busy from sub-tree 4
dOint3 and dObusy3 are consequently never used, so sub-tree 3 (inputs 8–11
of each 16-way group) is never told to stop and its words are only forwarded
when sub-tree 4 happens to present one. The same wiring is present in the
Xilinx DataMerger.vhd, so this is not an ALT-specific defect — but it only
affects the 16- and 32-input configurations. Until it is confirmed fixed,
prefer 8 inputs or fewer, or cascade two 8-input mergers into a third one.
:::
Practical configuration
| situation | Input Fifo Size |
|---|---|
sources that already respect BUSY within one clock |
0 |
| steady low-rate sources | 64 (default) |
| bursty event builders, several channels firing together | 512 or 1024 |
Input Word Size is free between 2 and 1024 bits (the editor steps it by 8), and every channel and the output share it. A common pattern is to reserve the top bits of the word for a channel id so the merged stream can be de-multiplexed in software — the merger itself adds no tag.
Resources & Timing
-
Latency: One clock per tree level (1 / 2 / 3 / 4 / 5 clocks for 2 / 4 / 8 / 16 / 32 inputs), plus the input FIFO delay when Input Fifo Size > 0, plus one clock for each collision a word loses on the way down
-
Throughput: One word per clock at OUT, shared by all channels
- Intel/Altera only (
_ANY_ALT). The FIFOs come from thealtera_mflibrary and name"Cyclone V"as the intended device family. - Buffering is
Input Fifo Size × Input Word Size × Number of inputsbits of on-chip memory, plus two hold registers of Input Word Size bits in every merger node. - No memory-mapped registers are generated: this block is pure fabric logic.