msk_demod_x16 HDL Worker

HDL implementation of the 16-channel OPV MSK demodulator: 16 parallel msk_demod_frontend instances and one time-shared Viterbi decoder at 16:1.

Detail

This worker integrates a complete OPV demodulator chain — carrier recovery, timing recovery, soft-quantization, frame synchronisation, and Viterbi decoding — for 16 channels, sharing the computationally expensive Viterbi across all channels via time-multiplexing.

Input demux. Each channelizer output port delivers a 16-sample sample message per frame period: SOM on word 0 (bin 0), EOM on word 15 (bin 15), with Q in bits 31:16 and I in bits 15:0. A 4-bit position counter (pos) resets to 0 on SOM and increments on each subsequent word. On each accepted word, fe_data_valid(slot) is pulsed for exactly one clock and fe_i_in / fe_q_in are driven from the current word. The demux only processes words when all_buf_clear = '1' (no channel has a pending frame awaiting decode), implementing coarse input backpressure to prevent BRAM overrun.

16 parallel front-ends. The gen_fe generate statement instantiates 16 msk_demod_frontend entities. Each front-end contains:

  • carrier_correction_4p: a fourth-power Costas loop with 32-bit NCO and PI loop filter. Proportional and integral gains are shared across all 16 front-ends via the cc_lpf_p_gain / cc_lpf_i_gain / cc_lpf_p_shift / cc_lpf_i_shift properties. To avoid a VRFC 10-3093 error in XSim (“neither a static name nor a globally static expression”), the resized gain values are pre-computed into cc_p_gain_slv / cc_i_gain_slv signals before being mapped to all 16 port maps. The default shifts (p_shift=16, i_shift=22) are tuned for Charlie-to-Charlie over-the-air operation and achieve lock over approximately ±8 kHz.

  • burst_detect (from the dsp primitive library): energy accumulator with exponential decay; burst_det and 24-bit energy_out are readable via the volatile property arrays.

  • msk_demod_channel: matched-filter correlator with Gardner timing-error detector and PI loop filter. Outputs a 16-bit soft_out and omega_current (timing NCO step, readable per channel).

  • soft_quantizer: maps the 16-bit soft value to a 3-bit signed soft_q.

  • frame_sync: preamble correlator and state machine (HUNT → VERIFY → LOCKED) that asserts frame_start on the first payload symbol of each detected frame and payload_valid for all payload symbols in VERIFY or LOCKED state. locked mirrors the LOCKED state.

dec_soft_valid(ch) is the AND of fe_soft_q_val_d(ch) and fe_payload_valid(ch), gating the collect process to payload symbols only.

BRAM soft collect and 16:1 scheduler. The back-end is architecturally identical to msk_decode_x16. 32 banks (bank = ch*2 + buf_sel) are laid out as a flat 1-D signal soft_bram of 131,072 4-bit entries (32 * 4096) with ram_style="block", inferred as 32 BRAM18 primitives. The flat 1-D layout and registered bank index (wr_bank_r, rd_bank_r) are required for reliable block RAM inference in Vivado (see Limitations).

The write process tracks a per-channel buffer selector (wr_buf_sel) and write index (wr_idx). At frame_start, the write index resets; at index 2143, buf_ready_set(ch) is pulsed, the selector toggles, and rd_buf_sel(ch) records which buffer was just filled for the scheduler. When any buf_ready flag is set, all_buf_clear goes low and input_out.take is de-asserted, stalling the input for the duration of the decode.

The 7-state scheduler (SCHED_IDLE / SCHED_RD0 / SCHED_RD1 / SCHED_DRIVE / SCHED_LAST1 / SCHED_LAST2 / SCHED_WAIT) scans the 16 buf_ready flags in fixed priority order. For the winning channel it issues reads via rd_bank_r / rd_addr_r, accounting for the 2-cycle BRAM read latency (two extra states drain the pipeline after the last issued address). On the first symbol (sym_count = 0), dec_frame_str is asserted and abs_bin = port_index*16 + decode_ch is enqueued into the 4-entry tag FIFO. After all 2144 symbols are delivered, the scheduler waits in SCHED_WAIT for dec_byte_last from opv_frame_decoder, captures the path metric into ch_path_metric(decode_ch), and returns to SCHED_IDLE.

Output FSM and FIFOs. opv_frame_decoder emits up to 134 bytes per frame into a 256-entry output FIFO. The output FSM begins a frame only when the FIFO holds at least 134 bytes, guaranteeing that EOM is asserted on the 134th byte without stalling. A 4-entry tag FIFO carries one abs_bin byte per in-flight decode. The metadata message layout matches struct.pack("<LLQ", id=0, pad=0, value=abs_bin): 16 bytes total (4 id + 4 pad + 8 value), with abs_bin placed in value byte 0.

Worker Properties

  • ocpi_buffer_size_output

    • Type: ushort

    • Access:

      • Parameter: False

      • Writable: False

      • Initial: True

      • Volatile: False

      • Read back: False

    • Default value: 0

  • ocpi_blocked_output

    • Type: ulong

    • Access:

      • Parameter: False

      • Writable: False

      • Initial: False

      • Volatile: True

      • Read back: False

    • Default value: None

  • ocpi_max_latency_output

    • Type: ushort

    • Access:

      • Parameter: True

      • Writable: False

      • Initial: False

      • Volatile: False

      • Read back: False

    • Default value: None

  • ocpi_latency_output

    • Type: ushort

    • Access:

      • Parameter: False

      • Writable: False

      • Initial: False

      • Volatile: True

      • Read back: False

    • Default value: None

  • ocpi_messages_output

    • Type: ulong

    • Access:

      • Parameter: False

      • Writable: False

      • Initial: False

      • Volatile: True

      • Read back: False

    • Default value: None

  • ocpi_bytes_output

    • Type: ulong

    • Access:

      • Parameter: False

      • Writable: False

      • Initial: False

      • Volatile: True

      • Read back: False

    • Default value: None

Worker Ports

    Inputs:

  • input

    • Protocol: complex_short_timed_sample

    • Optional: False

  • Outputs:

  • output

    • Protocol: uchar_timed_sample

    • Optional: False

Worker Ports

Inputs:

  • input:

    • Type: StreamInterface

    • Protocol: complex_short_timed_sample-prot

    • ClockDirection: in

    • Data width: 32

Outputs:

  • output:

    • Type: StreamInterface

    • Protocol: uchar_timed_sample-prot

    • Worker EOF: true

    • Clock: input

    • Data width: 8

Utilisation

Utilization reporting is not currently implemented.