msk_decode_x16 HDL Worker
HDL implementation of the 16-channel OPV back-end decoder: per-channel double-buffered BRAM soft collection and a single time-shared Viterbi decoder.
Detail
This worker implements only the shared back-end of the OPV demodulator (the
Viterbi decoder and output framing). All DSP front-end processing (carrier
correction, matched filter / timing recovery, differential detection, soft
quantization, frame synchronisation) is performed by the upstream folded workers
cr_x16 → ted_x16 → dsd_x16 → fs_x16.
Input parser. The input stream uses complex_short_timed_sample
metadata/sample framing produced by fs_x16. Per payload symbol, a
metadata message (4 words) carries the local channel index (0..15) in byte 2
of the value field; the following one-word sample message carries
bit 16 = frame_start and bits 2:0 = 3-bit signed soft decision. The
parser registers the current channel index (cur_in_ch) on word 2 of each
metadata message, then on the subsequent sample beat asserts
dec_soft_valid(ch) and latches fe_soft_q_d(ch) / fe_frame_start(ch).
BRAM soft collect. 32 banks are laid out as a flat 1-D signal
soft_bram of type bram_flat_t (N_BANKS*BRAM_DEPTH entries of 4-bit
cells) with the attribute ram_style="block". Bank addressing is
flat_addr = bank * 4096 + symbol_index. The power-of-2 depth of 4096 (only
entries 0..2143 are used per bank) is required to keep the index in-range and
allow Vivado to infer BRAM18; a non-power-of-2 depth of 2144 triggers Synth
8-7186 (“incorrect usage of block RAM”) and causes the array to fall back to
flip-flops. A flat 1-D array with a registered outer index (wr_bank_r,
rd_bank_r) is likewise required — an array-of-arrays indexed by a dynamic
outer index is seen by Vivado as a “non-memory variable” (Synth 8-3514) and
also falls back to flip-flops.
The write process maintains per-channel write pointers (wr_idx) and a
double-buffer select bit (wr_buf_sel). At frame_start, the write
pointer resets to 0 and the first soft symbol is written with address 0.
Subsequent symbols advance the pointer. At symbol 2143 (ENCODED_BITS-1)
the bank’s bank_ready flag is set, the buffer select bit is toggled, and
collection continues into the alternate bank. A sample beat whose target bank
is already marked ready is stalled via input_out.take = not input_stall,
preventing the write process from overwriting an undecoded frame.
Scheduler (7-state FSM).
SCHED_IDLE: scans all 32bank_readyflags (priority = lowest bank index); on the first ready bank, recordsdecode_chanddecode_bank, issues a BRAM read for address 0, and transitions toSCHED_RD0.SCHED_RD0: issues the read for address 1; transitions toSCHED_RD1.SCHED_RD1: issues the read for address 2; transitions toSCHED_DRIVE.SCHED_DRIVE:bram_rd_dataholds the symbol atsym_count. Assertsdec_soft_vld; onsym_count = 0also assertsdec_frame_strand enqueuesabs_bin = port_index*16 + decode_chinto the tag FIFO. Issues the next BRAM read and incrementsdrive_idxuntil the last address (ENCODED_BITS) has been issued, then transitions toSCHED_LAST1.SCHED_LAST1/SCHED_LAST2: drain the 2-cycle BRAM read pipeline, firingdec_soft_vldfor symbols 2142 and 2143. AtSCHED_LAST2, clearsbank_ready(decode_bank)and transitions toSCHED_WAIT.SCHED_WAIT: waits fordec_byte_lastfromopv_frame_decoder, then captures the path metric and returns toSCHED_IDLE.
Total re-drive time per channel: 2146 clocks (2144 symbols + 2 pipeline drain cycles). Viterbi decode time: approximately 70,881 clocks (~0.71 ms at 100 MHz). At 16:1 share, all 16 channels decode within 11.4 ms, well under the 40 ms OPV frame period (28.5% utilisation).
Output FSM. Waits in OST_IDLE until the tag FIFO is non-empty and the
output byte FIFO holds at least 134 bytes (one complete frame). Emits a 16-byte
metadata message (id = 0, pad = 0, value byte 0 = abs_bin) then
a 134-byte sample message. Frame length is controlled by a byte counter,
not FIFO drain level, ensuring EOM is always asserted on byte 133. After input
EOF and all banks clear, asserts output_out.eof to terminate the application.
Source files. viterbi_decoder_k7_soft.vhd and
opv_frame_decoder.vhd are shared with msk_demod.hdl and referenced
via relative path in the OWD.
Worker Properties
ocpi_buffer_size_outputType:
ushortAccess:
Parameter:
FalseWritable:
FalseInitial:
TrueVolatile:
FalseRead back:
False
Default value:
0
ocpi_blocked_outputType:
ulongAccess:
Parameter:
FalseWritable:
FalseInitial:
FalseVolatile:
TrueRead back:
False
Default value:
None
ocpi_max_latency_outputType:
ushortAccess:
Parameter:
TrueWritable:
FalseInitial:
FalseVolatile:
FalseRead back:
False
Default value:
None
ocpi_latency_outputType:
ushortAccess:
Parameter:
FalseWritable:
FalseInitial:
FalseVolatile:
TrueRead back:
False
Default value:
None
ocpi_messages_outputType:
ulongAccess:
Parameter:
FalseWritable:
FalseInitial:
FalseVolatile:
TrueRead back:
False
Default value:
None
ocpi_bytes_outputType:
ulongAccess:
Parameter:
FalseWritable:
FalseInitial:
FalseVolatile:
TrueRead back:
False
Default value:
None
Worker Ports
inputProtocol:
complex_short_timed_sampleOptional:
False
outputProtocol:
uchar_timed_sampleOptional:
False
Inputs:
Outputs:
Worker Ports
Inputs:
input:Type:
StreamInterfaceProtocol:
complex_short_timed_sample-protClockDirection:
inData width:
32
Outputs:
output:Type:
StreamInterfaceProtocol:
uchar_timed_sample-protWorker EOF:
trueClock:
inputData width:
8
Utilisation
Utilization reporting is not currently implemented.