Decimate-by-N (decimate3)
Decimates a positional 16-sample interleaved frame stream by a configurable
integer factor, forwarding one complete message per every factor input
messages per channel.
Tested platforms include xsim, zynq, zynq_ultra.
Design
The decimate3 worker sits between the channelizer output (12 samples per
symbol at the channelizer’s output rate) and the msk_demod_x16 / front-end
fold workers (4 samples per symbol). With the default factor = 3 it
reduces the per-channel sample rate by 3:1, matching the 12 SPS channelizer
output to the 4 SPS demodulator input.
The channelizer emits 16-sample sample_op_e messages — one complete
snapshot of all 16 bins per output frame. decimate3 operates at the
message (frame) level, not at the individual sample level: it keeps or
drops entire 16-sample messages. This preserves the positional framing that
downstream workers rely on to identify bins by slot.
The keep/drop decision is made by a free-running phase counter
(frame_phase, 8 bits, range 0..``factor``-1) that advances by one on every
input SOM beat. A message is kept when its SOM arrives while
frame_phase = 0. The decision is latched into fwd_reg at the SOM and
held through the entire message so that all 16 subsequent words in the same
message are forwarded. The combinatorial signal fwd_comb merges both
cases: (SOM and phase=0) or (not SOM and fwd_reg), ensuring the SOM word
itself is included in the forwarded set without a one-cycle gap.
No anti-alias filtering is applied; the implementation is a pure
sample-dropping decimator. This is correct for the OPV use-case because the
channelizer’s per-channel prototype filter already band-limits each bin to well
within the post-decimation Nyquist bandwidth. If the channelizer’s rolloff is
insufficient for a given waveform, replace decimate3 with a polyphase-FIR
decimator.
Kept messages are forwarded verbatim (SOM, EOM, data, opcode, byte-enable all
pass through unchanged). The worker always accepts input (take tracks
input_in.ready); no back-pressure is applied upstream.
Latency: zero additional pipeline stages for forwarded messages; dropped
messages produce no output. The fwd_reg latched on SOM introduces one
clock of pipelining relative to SOM, but the SOM word itself is forwarded
combinatorially via fwd_comb, so the total end-to-end latency for a kept
message is zero extra cycles.
Interface
<?xml version='1.0' encoding='utf-8'?>
<!-- decimate3: per-channel N:1 decimator on complex_short_timed_sample.
Sits between the channelizer (SPS=12) and msk_demod_x16 (SPS=4): keeps
1 of every `factor` (default 3) IQ samples PER CHANNEL, where the channel
is identified by the metadata(id=0, value=abs_bin) tag. Metadata-transparent:
the kept sample's metadata is forwarded so the demod still sees the tag.
NOTE: this is a simple sample-dropping decimator (no anti-alias FIR); it
relies on the channelizer's per-channel prototype filtering. If aliasing
degrades demod lock, replace with a polyphase-FIR decimator. -->
<ComponentSpec>
<property name="factor" type="uchar" writable="true" default="3"
description="Per-channel decimation factor: keep 1 of every N samples per channel"/>
<port name="input" producer="false" protocol="complex_short_timed_sample-prot"/>
<port name="output" producer="true" protocol="complex_short_timed_sample-prot"/>
</ComponentSpec>
Ports
Inputs:
input: Positional 16-sample interleaved frame stream from the channelizer; complex_short_timed_sample-prot, sample opcode, 16 IQ words per message at 12 SPS per channel.Protocol:
complex_short_timed_sample-protOptional:
False
Outputs:
output: Decimated frame stream; same positional framing as input, forwarded at 1/factor of the input message rate (4 SPS per channel at factor=3).Protocol:
complex_short_timed_sample-protOptional:
False
Opcode Handling
decimate3 passes opcodes through from input to output without modification.
The keep/drop decision is applied to all input messages regardless of opcode;
only messages that arrive when frame_phase = 0 are forwarded. In normal
operation all input messages carry the sample_op_e opcode.
Properties
factor: Per-channel decimation factor: keep 1 of every N samples per channel.Type:
ucharAccess:
Parameter:
FalseWritable:
TrueInitial:
FalseVolatile:
False
Default value:
3
Implementations
decimate3(HDL)HDL implementation of a frame-level integer decimator for the
complex_short_timed_sampleprotocol, targeting 3:1 reduction of 12 SPS channelizer output to 4 SPS demodulator input.
Dependencies
The decimate3 HDL worker depends on:
ieee.std_logic_1164
ieee.numeric_std
ocpi.types(OpenCPI-generated protocol opcode constants)
No external primitives or generated files are required.
Limitations
Limitations of decimate3 are:
No anti-alias filtering. The implementation drops samples without any FIR pre-filter; correctness relies on the channelizer’s prototype filter having already band-limited each bin.
The
factorproperty must be at least 1. A value of 0 is clamped to 1 (no decimation) by the registered process, but this is not enforced by the component spec.The decimation factor is applied uniformly across all 16 channels in the frame; per-channel factors are not supported.
The
factorproperty is read once perSOMevent. Changing it while data is flowing will take effect on the next SOM but may produce a partial phase wrap if the old and new factors differ.
Testing
There is no dedicated unit test directory for decimate3. The worker is
exercised as part of the folded-chain assembly test (folded_demod_x16_asm),
where the output message rate and sample values are confirmed to match the
expected 4 SPS per channel after 3:1 decimation.
Tested platforms: None