cr_x16 HDL Worker

Time-shared (folded) implementation of a 4th-power Costas carrier-recovery and derotation loop operating across 16 MSK channels with one physical datapath.

Detail

The worker is built around three concurrent processes that share the single clock domain (ctl_in.clk). Reset is the logical OR of ctl_in.reset and the init writable property.

Loop (error) path — 9-stage pipeline

Channels enter the pipeline one per clock in round-robin positional order. A two-wide sideband pipeline (ch_p(0..9), vld_p(0..9)) carries the channel id and valid flag alongside the data so that each per-channel RAM is indexed by the channel that is in-flight at that stage, not by an external injection counter. This prevents the cross-channel attribution error that would arise from the 1-clock valid/data skews present in the sequential carrier_correction_4p design.

  • S0 — capture: i0/q0 ← input_in.data[31:16] / [15:0].

  • S1 — complex square: i_sqr = i0*i0, q_sqr = q0*q0, iq_2x = 2*(i0*q0) (full 32-bit products).

  • S2 — combine to s²: sqr_re = i_sqr - q_sqr, sqr_im = iq_2x; MSB-slice truncation back to 16 bits (combinational, outside the clocked process).

  • S3 — square again: same structure on the s² slices, producing s⁴ products.

  • S4 — combine to s⁴: same MSB-slice truncation back to 16 bits.

  • S5 — 4x NCO mix: the sin_cos_lut ROM is looked up with the top 10 bits of phase_4x[ch_p(4)] one clock before this stage’s registered multiply; mix_a = quad_im * cos_4x, mix_b = quad_re * sin_4x. The 4x NCO phase is advanced by freq_4x[ch] on every valid beat at this stage (use-then-advance).

  • S6 — phase error: pll_err_raw = mix_a - mix_b (cross-product form of the Costas discriminant, sign-extended to SAMPLE_W + SINUSOID_W + 1 = 29 bits).

  • S7 — IIR low-pass filter: lpf_acc[ch] += pll_err_raw - (lpf_acc[ch] >> LPF_ALPHA) where LPF_ALPHA = 10. The pre-update accumulator value, right-shifted by LPF_ALPHA, forms lpf_out (16 bits), which feeds both the PI and the lock detector. Lock detection runs an IIR energy tracker on |lpf_out| with a compile-time threshold of 2^(LOCK_ACC_W-6) (LOCK_ACC_W = 24).

  • S8 — PI filter: integral accumulator i_acc[ch] is updated and saturated to [MAX_ACC_NEG, MAX_ACC_POS] (overflow-wrap protection); i_val = (i_acc[ch] * ig) >> i_shift, p_val = (pg * lpf_out) >> p_shift where ig/pg are the 24-bit gain vectors from cc_lpf_i_gain/cc_lpf_p_gain and the shifts come from cc_lpf_i_shift/cc_lpf_p_shift. cc_lpf_freeze holds i_acc without zeroing; cc_lpf_zero clears i_acc and forces i_val to zero.

  • S9 — NCO update: freq_4x[ch] = p_val + i_val.

Fixed-point summary: input samples are Q1.15 (signed 16-bit); the s² and s⁴ intermediate products are computed at full width and the MSB slice restores Q1.15 at each squaring stage; the IIR accumulator is 40 bits (SAMPLE_W + SINUSOID_W + LPF_ALPHA + 2); the PI accumulators are 32-bit signed with MAX_ACC_POS = 2^30, MAX_ACC_NEG = 3*2^30; the NCO frequency word is 32 bits (the top 10 bits select the sin/cos ROM phase).

Derotation (output) path — 2-stage pipeline

This path runs in parallel with and independently of the loop path. On every accepted input beat:

  • Stage A — the 1x derotation NCO phase for cur_ch is looked up in the second sin_cos_lut ROM (phase = top 10 bits of phase_1x[cur_ch]); the four products i*cos, q*sin, i*sin, q*cos are computed. The 1x NCO phase is then advanced by freq_4x[cur_ch] >> 2.

  • Stage B — i_out = i*cos + q*sin, q_out = q*cos - i*sin; the unity-gain restoring slice ([SAMPLE_W + SINUSOID_W - 2 : SINUSOID_W - 1], i.e. bits [25:11] of a 28-bit product) maps the output back to Q1.15.

Output FIFO

A 128-entry synchronous FIFO (7-bit address, {iq[31:0], som, eom} per entry) decouples the fixed-latency derotation pipeline from downstream backpressure. can_accept drops when the FIFO occupancy reaches FIFO_DEPTH - 8, giving enough headroom for the samples already in the derotation pipeline to drain without loss.

Volatile status

cc_freq_word[ch] is freq_4x[ch] >> 2 (the 1x equivalent NCO frequency word in the same units as the 32-bit NCO). cc_lock[ch] is set when the IIR lock-energy tracker falls below the compile-time threshold.

Latency from input sample to derotated output sample: 2 clock cycles (derotation path A + B), plus FIFO read latency of 1 cycle when output is immediately ready — approximately 3 cycles total. The loop path has a 9-cycle latency that affects only the frequency-word update, not the derotated sample timing.

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: complex_short_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: complex_short_timed_sample-prot

    • Worker EOF: true

    • Clock: input

    • Data width: 32

Utilisation

Utilization reporting is not currently implemented.