Frame Merge 4 (frame_merge4)
Merges four msk_decode_x16 decoded-frame streams into a single output
stream, so the 64-channel demod uses a single FPGA-to-CPU DMA interconnect.
Tested platforms include xsim.
Design
frame_merge4 accepts four uchar_timed_sample input streams — one per
msk_decode_x16 worker — and serialises them onto a single output stream
using round-robin, frame-atomic arbitration.
Each msk_decode_x16 worker emits decoded frames as pairs of WSI messages:
first a metadata message (opcode metadata) whose low byte carries the
abs_bin channel tag (port_index * 16 + channel), immediately followed
by a sample message (opcode sample) carrying the 134-byte decoded frame.
The host software demultiplexes on abs_bin to route each frame to the
correct channel.
The arbiter operates as a two-state FSM:
ST_ARB — scans inputs
in0..in3in round-robin order (advancing the start pointer after each grant) and locks onto the first input that has a beat available.ST_FWD — forwards all beats from the locked input verbatim —
data,opcode,som,eom, andbyte_enable— until (and including) the beat whereopcode = sampleANDeom = '1'. This is the last byte of the 134-byte frame message. Only then does the FSM return to ST_ARB and advance the round-robin pointer.
This frame-atomic policy guarantees that the metadata message and its
corresponding frame message are always forwarded as a contiguous unit.
Message-level interleaving would pair a metadata beat from one channel with
frame bytes from another, corrupting the abs_bin tag.
Because all four inputs are low-rate (approximately 58 KB/s each at the OPV
waveform data rate), no input FIFOs are required. Backpressure is handled
naturally: the selected input is consumed (take asserted) only when the
downstream asserts ready. The three non-selected inputs experience
backpressure silently; they do not need to be stalled at the source because
their producers hold data until taken.
EOF propagation is conservative: output.eof is asserted only when all
four inputs have signalled EOF, and no pending beats remain (pv =
"0000"), and the arbiter is idle (ST_ARB). This prevents premature
EOF while any input still holds un-forwarded frames.
Interface
<?xml version='1.0' encoding='utf-8'?>
<!-- frame_merge4: merge FOUR msk_decode_x16 outputs into ONE, so the 64-channel
demod (4x msk_decode_x16, port_index 0..3) uses a single FPGA->CPU DMA
interconnect instead of four.
Each input is the uchar_timed_sample decoded-frame stream: per decoded frame
a metadata message (abs_bin in the value-low byte) followed by a 134-byte
sample message (the frame). abs_bin already encodes port_index*16+ch, so the
four streams are uniquely tagged and the host can demux on abs_bin.
Arbitration is round-robin and FRAME-ATOMIC: once an input is selected, its
metadata message AND the following sample (frame) message are forwarded as a
unit before re-arbitrating, so a frame's abs_bin tag always stays paired with
its frame bytes (message-level interleaving would split them). Output rate is
low (~4 x 58 KB/s), so no buffering is needed. -->
<ComponentSpec>
<port name="in0" producer="false" protocol="uchar_timed_sample-prot"/>
<port name="in1" producer="false" protocol="uchar_timed_sample-prot"/>
<port name="in2" producer="false" protocol="uchar_timed_sample-prot"/>
<port name="in3" producer="false" protocol="uchar_timed_sample-prot"/>
<port name="output" producer="true" protocol="uchar_timed_sample-prot"/>
</ComponentSpec>
Ports
Inputs:
in0: Input decoded-frame stream from msk_decode_x16 port-index 0 (abs_bins 0..15).Protocol:
uchar_timed_sample-protOptional:
False
in1: Input decoded-frame stream from msk_decode_x16 port-index 1 (abs_bins 16..31).Protocol:
uchar_timed_sample-protOptional:
False
in2: Input decoded-frame stream from msk_decode_x16 port-index 2 (abs_bins 32..47).Protocol:
uchar_timed_sample-protOptional:
False
in3: Input decoded-frame stream from msk_decode_x16 port-index 3 (abs_bins 48..63).Protocol:
uchar_timed_sample-protOptional:
False
Outputs:
output: Merged output stream; frames from all four inputs serialised in round-robin order.Protocol:
uchar_timed_sample-protOptional:
False
Opcode Handling
Both metadata and sample opcodes are forwarded unchanged. The arbiter
treats the metadata opcode message as the first half of an atomic frame
unit and continues forwarding until the subsequent sample opcode EOM is
consumed. No opcode is dropped, modified, or re-ordered within a frame unit.
Properties
None.
Implementations
frame_merge4(HDL)Round-robin, frame-atomic 4-to-1 merge of
uchar_timed_sampledecoded-frame streams.
Dependencies
The frame_merge4 HDL worker depends on:
ieee.std_logic_1164
ieee.numeric_std
ocpi.types(foruchar_timed_sampleopcode constants)
Limitations
Limitations of frame_merge4 are:
The arbiter assumes each input produces strictly paired metadata + sample messages. An input that emits a metadata message without a following sample message (e.g. due to upstream truncation) will hold the arbiter in ST_FWD indefinitely, stalling all other inputs.
All four input ports must be connected. Leaving an input undriven will cause the arbiter to wait for a beat from that port and stall downstream.
Round-robin fairness is approximate: a lagging input will be skipped during ST_ARB if it has no beat available, but will be preferred at the next opportunity. Under sustained asymmetric load, faster inputs receive proportionally more grants.
Testing
The unit test drives all four input ports simultaneously with four distinct
frames each (16 frames total). Each frame carries a self-identifying payload
(byte[0] = abs_bin, byte[i] = abs_bin + frame_index + i) and is
preceded by a metadata message whose low byte encodes the same abs_bin.
The verify script checks that:
All 16 frames arrive at the single output, with no drops or duplicates.
Every frame is intact (correct length and correct payload bytes).
Each frame’s
abs_binin the metadata message matchespayload[0], proving frame-atomic pairing was never violated.
Tested platforms: None
Component testing not completed.