# CFAR Detection Algorithms (CA, GO, SO) in FPGA vs Software: A Complete Implementation Guide

> Implement CA GO SO CFAR detection algorithms in FPGA and software. This guide offers bit-accurate fixed-point FPGA vs floating-point Python implementation for optimal radar performance.

- Repository: [NawfalMotii79/PLFM_RADAR](https://github.com/NawfalMotii79/PLFM_RADAR)
- Tags: deep-dive
- Published: 2026-08-20

---

**The PLFM_RADAR project implements Cell-Averaging (CA), Greatest-Of (GO), and Smallest-Of (SO) CFAR detectors in both fixed-point FPGA hardware and floating-point Python software, with bit-accurate matching between the two.**

Constant-False-Alarm-Rate (CFAR) detection is essential for reliable target detection in radar systems. The **NawfalMotii79/PLFM_RADAR** repository provides a production-ready dual implementation that lets engineers run real-time detection on FPGA hardware while developing and validating algorithms in Python.

---

## Signal Chain Integration

Both implementations process the same radar signal chain, but at different stages:

| Stage | FPGA Implementation | Software Implementation |
|-------|---------------------|------------------------|
| **Location** | Integrated in RTL pipeline after DC-notch | Post-processing on Power (|data|²) map |
| **Chain** | `quantize → range_fft → decimator → MTI → doppler_fft → dc_notch → **CFAR** → RadarFrame` | Same chain via `cfar_2d` call after optional MTI/DC-notch |

In [`software_fpga.py`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/software_fpga.py) lines 9-11, the FPGA pipeline shows CFAR as a fixed hardware stage. The software equivalent in [`processing.py`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/processing.py) lines 62-78 allows optional insertion of CFAR as a configurable post-processing step.

---

## Configurable Parameters and Register Map

Both implementations share identical configuration parameters, exposed through memory-mapped registers in the FPGA and Python attributes in software.

### FPGA Register Addresses

Defined in [`radar_protocol.py`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/radar_protocol.py) and exposed via UART:

- `CFAR_GUARD (0x21)` — guard cell count
- `CFAR_TRAIN (0x22)` — training cell count  
- `CFAR_ALPHA (0x23)` — Q4.4 fixed-point scaling factor
- `CFAR_MODE (0x24)` — mode selector (0=CA, 1=GO, 2=SO)
- `CFAR_ENABLE (0x25)` — on/off flag

Reference: [`dashboard.py`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/dashboard.py) lines 759-762.

### Software Configuration Object

The same parameters in Python ([`software_fpga.py`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/software_fpga.py) lines 62-84):

```python
cfar_guard_cells      # matches CFAR_GUARD

cfar_training_cells   # matches CFAR_TRAIN

cfar_alpha_q44        # matches CFAR_ALPHA (Q4.4 format)

cfar_mode             # 0/1/2 for CA/GO/SO

cfar_enabled          # boolean flag

```

This parity ensures that register writes to hardware and attribute changes in software produce identical detection behavior.

---

## Algorithmic Core: Three CFAR Modes

The repository implements three classical CFAR variants:

| Mode | Algorithm | Best For |
|------|-----------|----------|
| **CA-CFAR** | Cell-Averaging: mean of all training cells | Homogeneous clutter |
| **GO-CFAR** | Greatest-Of: maximum of leading/trailing window means | Multiple targets, clutter edges |
| **SO-CFAR** | Smallest-Of: minimum of leading/trailing window means | Interfering targets |

### FPGA Implementation

The bit-accurate model in [`software_fpga.py`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/software_fpga.py) uses `_CFAR_MODE_MAP` to select between `run_cfar_ca`, `run_cfar_go`, and `run_cfar_so` (lines 62-64). These functions replicate the fixed-point RTL arithmetic using Q-format scaling.

### Software Implementation

The `processing.cfar_1d` function supports all three modes plus OS-CFAR (Ordered Statistic) via a simple `if/elif` cascade at lines 91-98:

```python

# From processing.py lines 91-98

if cfar_type == "CA-CFAR":
    noise_est = np.mean(training_cells)
elif cfar_type == "GO-CFAR":
    noise_est = max(np.mean(leading), np.mean(trailing))
elif cfar_type == "SO-CFAR":
    noise_est = min(np.mean(leading), np.mean(trailing))

```

---

## Numerical Precision and Performance

| Aspect | FPGA | Software |
|--------|------|----------|
| **Data type** | Fixed-point Q-format | `float64` NumPy arrays |
| **Timing** | Deterministic, <100 µs per frame | Unconstrained, host-dependent |
| **Resource use** | Optimized for Xilinx fabric | Unlimited memory/processing |
| **Use case** | Real-time embedded radar | Development, validation, prototyping |

The FPGA's fixed-point arithmetic is cycle-accurate to the RTL and processes one frame per clock cycle per pipeline stage. The software version prioritizes clarity and debugging flexibility over real-time performance.

---

## Practical Code Examples

### Running Software CFAR Pipeline

```python
from processing import RadarProcessor

# Configure detection parameters

proc = RadarProcessor(
    cfar_enabled=True,
    cfar_guard_cells=2,
    cfar_training_cells=8,
    cfar_alpha_q44=0x30,          # Q4.4 = 3.0

    cfar_type="GO-CFAR",          # "CA-CFAR", "GO-CFAR", "SO-CFAR", "OS-CFAR"

)

# Process 2-D range-Doppler map

processed_rdm, detections = proc.process_frame(raw_frame)

# detections: boolean mask for target locations

```

### Programming FPGA-Equivalent Model

```python
from software_fpga import SoftwareFPGA

fpga = SoftwareFPGA()

# Write equivalent of UART register settings

fpga.set_cfar_enable(True)
fpga.set_cfar_guard(2)
fpga.set_cfar_train(8)
fpga.set_cfar_alpha(0x30)       # Q4.4 fixed-point

fpga.set_cfar_mode(2)           # 2 → SO-CFAR

# Generate frame identical to hardware output

frame = fpga.process_chirps(iq_i, iq_q, frame_number=0)

```

### Mode Comparison in Software

```python

# CA-CFAR: average of all training cells

proc.cfar_type = "CA-CFAR"
r1, d1 = proc.process_frame(raw_frame)

# GO-CFAR: maximum noise estimate (better for clutter edges)

proc.cfar_type = "GO-CFAR"
r2, d2 = proc.process_frame(raw_frame)

# d1 and d2 differ: CA is more sensitive in uniform clutter,

# GO reduces false alarms at clutter boundaries

```

---

## Key Source Files

| File | Purpose | Critical Functions/Lines |
|------|---------|--------------------------|
| [`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) | Bit-accurate FPGA model | `_CFAR_MODE_MAP`, `run_cfar_*`, lines 62-84 |
| [`9_Firmware/9_3_GUI/v7/processing.py`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/9_Firmware/9_3_GUI/v7/processing.py) | Host-side CFAR implementation | `cfar_1d`, mode selection lines 91-98 |
| [`9_Firmware/9_3_GUI/radar_protocol.py`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/9_Firmware/9_3_GUI/radar_protocol.py) | Register address definitions | `CFAR_GUARD`, `CFAR_MODE`, etc. |
| [`9_Firmware/9_2_FPGA/tb/cosim/real_data/golden_reference.py`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/9_Firmware/9_2_FPGA/tb/cosim/real_data/golden_reference.py) | RTL verification reference | Hardware-accurate CFAR functions |
| [`9_Firmware/9_3_GUI/v7/dashboard.py`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/9_Firmware/9_3_GUI/v7/dashboard.py) | Real-time GUI control | Register write interface, lines 759-762 |

---

## When to Use Each Implementation

- **FPGA hardware**: Deployed systems requiring guaranteed latency, low power, and continuous operation in embedded environments

- **Software model ([`software_fpga.py`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/software_fpga.py))**: Pre-hardware verification, cosimulation with RTL, regression testing against golden references

- **Host processing ([`processing.py`](https://github.com/NawfalMotii79/PLFM_RADAR/blob/main/processing.py))**: Algorithm research, rapid iteration on CFAR variants, offline data analysis, generating training datasets

The dual implementation strategy allows the PLFM_RADAR project to maintain a single source of truth for CFAR parameters while optimizing for completely different execution environments.

---

## Summary

- The PLFM_RADAR repository provides **bit-accurate matching** between FPGA and Python CFAR implementations
- **Three modes** (CA, GO, SO) are selectable via unified configuration parameters across both platforms
- **Fixed-point Q-format arithmetic** in the FPGA ensures deterministic real-time performance
- **Floating-point NumPy operations** in software enable rapid prototyping and algorithm development
- **Shared register map** (`CFAR_GUARD`, `CFAR_TRAIN`, `CFAR_ALPHA`, `CFAR_MODE`, `CFAR_ENABLE`) guarantees identical behavior between hardware and simulation

---

## Frequently Asked Questions

### How do I switch between CA-CFAR and GO-CFAR in the PLFM_RADAR implementation?

Set the `cfar_mode` register to 0 for CA-CFAR or 1 for GO-CFAR. In Python, use `fpga.set_cfar_mode(1)` or assign `proc.cfar_type = "GO-CFAR"`. The mode change takes effect immediately on the next processed frame.

### What is the Q4.4 format used for `cfar_alpha_q44`?

Q4.4 is a fixed-point representation with 4 integer bits and 4 fractional bits. The value `0x30` equals 3.0 (0x30 >> 4 = 3). This format allows efficient hardware multiplication without floating-point units.

### Can the software implementation process data in real time like the FPGA?

No. The Python implementation uses `float64` NumPy arrays for clarity and is not optimized for real-time performance. For real-time operation, use the FPGA hardware or implement the fixed-point logic in a compiled language.

### Why does the FPGA implementation use smallest-of (SO) CA-CFAR as an option?

SO-CFAR selects the minimum of leading and trailing window noise estimates. This prevents target suppression when an interfering target appears in only one side of the training window—particularly valuable in dense target environments with the PLFM_RADAR hardware.