# FPGA Decimation Filter Design for Range Bin Reduction in the PLFM RADAR

> Learn how to design an FPGA decimation filter for range bin reduction in PLFM RADAR. Compress 1024 bins to 64 using decimation, peak detection, or averaging for efficient radar processing.

- Repository: [NawfalMotii79/PLFM_RADAR](https://github.com/NawfalMotii79/PLFM_RADAR)
- Tags: how-to-guide
- Published: 2026-08-20

---

**The PLFM RADAR project implements a dedicated FPGA decimator in `9_Firmware/9_2_FPGA/range_bin_decimator.v` that compresses 1024 matched-filter range bins into 64 bins using configurable simple decimation, peak detection, or averaging modes.**

The **FPGA decimation filter design for range bin reduction** is a critical stage in the PLFM RADAR signal-processing chain. Developed in the `NawfalMotii79/PLFM_RADAR` repository, this Verilog module shrinks the high-resolution range profile before Doppler processing, cutting downstream FPGA resource usage and data bandwidth. It sits immediately after the matched filter and feeds directly into the MTI and Doppler FFT stages.

## Architecture of the FPGA Decimation Filter for Range Bin Reduction

The decimator is parameterized with `INPUT_BINS = 1024`, `OUTPUT_BINS = 64`, and a fixed `DECIMATION_FACTOR = 16`. All core logic resides in `9_Firmware/9_2_FPGA/range_bin_decimator.v`, where a five-state finite-state machine (FSM) consumes validated complex samples and emits one decimated bin for every 16 input samples.

### Decimation Modes

The `decimation_mode` input selects how each 16-sample group is collapsed into a single output bin. According to lines 9–13 of `range_bin_decimator.v`, the supported modes are:

- **Simple decimation (`2'b00`)** – Takes the centre sample of each 16-sample group.
- **Peak detection (`2'b01`)** – Selects the sample with the largest magnitude, approximated as `` `|I| + |Q|` ``, within the group.
- **Averaging (`2'b10`)** – Sums the 16 signed I/Q samples and right-shifts by 4 to divide by 16.
- **Reserved (`2'b11`)** – Currently unused.

### Configurable Region of Interest

The `start_bin` parameter, described in lines 22–27, lets the operator skip an initial portion of the 1024-sample range profile. This enables region-of-interest processing; for example, discarding far-range bins to focus only on near-range targets.

### State Machine and Watchdog Protection

A five-state FSM—`ST_IDLE`, `ST_SKIP`, `ST_PROCESS`, `ST_EMIT`, and `ST_DONE`—orchestrates the pipeline (lines 85–92). To prevent indefinite stalls, a watchdog counter with `WATCHDOG_LIMIT = 256` clock cycles monitors the input stream. If no valid sample arrives within that window, the logic forces a return to `ST_IDLE`, as implemented in lines 69–73 and 246–255.

### Output Interface

The module exposes the following outputs on lines 46–51:

- `range_i_out` and `range_q_out` – The decimated complex sample.
- `range_valid_out` – A one-cycle pulse signalling a new valid bin.
- `range_bin_index` – A 6-bit counter ranging from 0 to 63.

## How the Decimation Filter Processes Samples

### Input Handling and Start-Bin Skipping

Each validated sample from the matched filter advances the FSM. When `start_bin` is greater than zero, the state machine enters `ST_SKIP` and discards samples until the programmed offset is reached, effectively shifting the processing window.

### Group Processing

Once in `ST_PROCESS`, the module handles groups of exactly 16 samples. Depending on the selected mode, it either latches the centre sample, tracks the maximum-magnitude sample using running registers (`peak_i`, `peak_q`, `peak_mag`), or accumulates signed I/Q values in `sum_i` and `sum_q`.

### Emission and Frame Completion

After the 16th sample of a group is processed, the FSM transitions to `ST_EMIT`. It asserts `range_valid_out`, drives the selected I/Q data onto `range_i_out` and `range_q_out`, and updates `range_bin_index`. Once 64 output bins have been emitted, the state machine moves to `ST_DONE` and then returns to `ST_IDLE`, preparing for the next chirp acquisition.

## Integration in the Radar Signal Chain

As documented in [`9_Firmware/9_3_GUI/v7/software_fpga.py`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/9_Firmware/9_3_GUI/v7/software_fpga.py) at lines 190–194, the decimator fits into the FPGA data-flow between the range and Doppler domains:

```text
quantize → range_fft → decimator → MTI → doppler_fft → ...

```

This placement ensures that the Doppler FFT engine operates on a manageable 64-bin range profile rather than the full 1024-bin matched-filter output.

## Verilog and Python Code Examples

### Verilog Instantiation

You can instantiate the decimator in a top-level receiver module such as `radar_receiver_final.v` as follows:

```verilog
range_bin_decimator #(
    .INPUT_BINS        (1024),
    .OUTPUT_BINS       (64),
    .DECIMATION_FACTOR (16)
) u_range_bin_decimator (
    .clk               (clk_400m),
    .reset_n           (reset_n),

    .range_i_in        (mf_i_out),
    .range_q_in        (mf_q_out),
    .range_valid_in   (mf_valid_out),

    .range_i_out       (decim_i_out),
    .range_q_out       (decim_q_out),
    .range_valid_out  (decim_valid_out),
    .range_bin_index  (decim_bin_index),

    .decimation_mode  (decim_mode),   // 2'b00, 2'b01, or 2'b10
    .start_bin        (10'd0),        // No skip by default
    .watchdog_timeout (watchdog_timeout)
);

```

### Python GUI Configuration

The host software configures the decimator and retrieves results through the STM32-mediated register map, as shown in [`software_fpga.py`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/software_fpga.py):

```python

# Configure decimation mode (0 = simple, 1 = peak, 2 = average)

self.fpga.write_register('range_decim_mode', 0b01)   # peak detection

# Trigger a new chirp and wait for decimated output

self.fpga.start_acquisition()

# Collect the 64 decimated bins (I/Q pairs)

dec_i = []
dec_q = []
while len(dec_i) < 64:
    resp = self.fpga.read_register('range_decim_data')
    if resp['valid']:
        dec_i.append(resp['i'])
        dec_q.append(resp['q'])

```

The `range_decim_mode` register maps directly to the `decimation_mode` input of the Verilog module, while `range_decim_data` returns the decimated complex samples.

## Verification Testbenches

The design is validated by two dedicated testbenches in the `9_Firmware/9_2_FPGA/tb/` directory:

- **`tb_range_bin_decimator.v`** – A unit-level testbench that exercises all four decimation modes using synthetic ramps, injected peaks, and known averaging patterns.
- **`tb_fullchain_realdata.v`** – A system-level integration testbench that verifies the decimator inside the complete radar chain. The check call on line 613 confirms that exactly 64 decimated range bins are produced per frame before Doppler processing begins.

Both simulations report pass/fail status based on whether the range bin decimator emits the expected 64-bin sequence.

## Summary

- The PLFM RADAR decimator in `range_bin_decimator.v` reduces 1024 matched-filter range bins to 64 using a fixed decimation factor of 16.
- Three active modes are available: simple centre-sample selection, peak detection based on `` `|I| + |Q|` ``, and arithmetic averaging via right-shift division.
- A five-state FSM with a 256-cycle watchdog prevents pipeline lockups when input data stalls.
- The block is inserted between the matched filter and the Doppler FFT, with runtime configuration handled through the Python driver in [`software_fpga.py`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/software_fpga.py).
- Dedicated testbenches verify both standalone behaviour and full-chain integration.

## Frequently Asked Questions

### What decimation factor does the PLFM RADAR FPGA decimator use?

The module decimates by a factor of 16, converting 1024 input range bins into 64 output bins. These values are set by the `INPUT_BINS`, `OUTPUT_BINS`, and `DECIMATION_FACTOR` parameters in `range_bin_decimator.v`.

### How does the range bin decimator prevent pipeline lockups?

A watchdog timer with a `WATCHDOG_LIMIT` of 256 clock cycles monitors the input stream and forces the state machine back to `ST_IDLE` if no valid sample arrives within that window. This safety mechanism is implemented in lines 69–73 and 246–255 of `range_bin_decimator.v`.

### Which decimation mode should I use for capturing strong targets versus uniform power estimation?

Select **peak detection** (`2'b01`) to retain the sample with the largest magnitude within each 16-sample group, which preserves strong localized returns. Choose **averaging** (`2'b10`) when you need the arithmetic mean of the group, computed by summing signed I/Q values and right-shifting by four.

### How is the decimator configured and monitored from the host software?

The Python GUI driver in [`software_fpga.py`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/software_fpga.py) writes the `range_decim_mode` register to select the operating mode and reads the `range_decim_data` register to capture the 64 decimated I/Q bins per chirp. This register map connects through the STM32-mediated FPGA interface, allowing real-time configuration without re-synthesizing the bitstream.