RF FFT (compact)
Self-contained fixed-point radix-2 decimation-in-time FFT built in HLS (no vendor FFT IP, no VHDL wrapper). A compact ‘one-butterfly-per-clock’ core time-shares a single complex butterfly, so it costs only a handful of DSPs plus BRAM regardless of the transform length N. The input must be gapped (one sample every few system clocks, like SeqFIR); a full complex spectrum of N bins is streamed out one bin per clock with VALID_OUT and SOF.
Introduction
The FFT (compact) block computes the discrete Fourier transform of a framed baseband stream and emits the full complex spectrum:
X[k] = sum_{n=0..N-1} x[n] * exp(-j*2*pi*k*n / N), k = 0 .. N-1
It is a hand-written radix-2 decimation-in-time (DIT) transform in
fixed point (Vitis HLS), with no vendor FFT IP and no VHDL wrapper. The
core is deliberately compact: instead of a fully-pipelined pipeline
with log2(N) butterfly columns, it reuses one complex butterfly and
steps it one operation per system clock. This trades latency for area, so
the DSP cost stays tiny (about one time-shared complex multiply) no matter
how large N is - the cost is BRAM plus the per-frame latency.
Because it processes one butterfly per clock, the input must be gapped: present one new sample only every few system clocks (exactly the SeqFIR model). The SysClk / DataClk ratio you choose gives the core the idle clocks it needs to finish a frame; the plugin enforces a minimum at compile time.
Input may be Real (single IN channel, imaginary part tied to 0) or
Complex (IN_I / IN_Q). The output is always a full complex spectrum
(OUT_I / OUT_Q), N bins, streamed one bin per clock with VALID_OUT
high and SOF pulsing on bin 0.
Visual designer
This block uses a custom WebView2 designer instead of the standard property grid. Double-click the block on the schematic to open the FFT Designer, where you choose:
- Signal Type - Real or Complex input.
- FFT Length N - the transform size (power of 2,
Fs/Nbin spacing). - Scaling - Scaled (
>>1per stage, bounded ~1/Noutput) or Unscaled (full bit growth). - Input Bit Width and Twiddle Bit Width.
- SysClk / DataClk - the input gap (system clocks per input sample).
As you edit, the designer shows a live resource estimate (reused
butterfly DSPs, double-buffer + twiddle-ROM BRAM, per-frame latency and
throughput) and flags the SysClk/DataClk field red if it is below the
minimum needed to finish a frame in time. It also plots the DFT bins
along 0 .. Fs with the resulting resolution Fs/N. Fs is entered in
the designer only for these estimates; it is not a hardware pin. On save,
the chosen values are written back to the block properties and used to
synthesise the core.
For the inverse transform see Component_IFFT (IFFT).
Pin Description
SysClk/DataClk clocks. N strobes fill one frame.
InputSize + 4 scaled,
InputSize + log2(N) + 2 unscaled).
Properties
Real: one input channel (imag=0). Complex: I/Q input.
Input format. Real exposes a singleIN channel (imaginary tied
to 0); Complex exposes IN_I / IN_Q. Default Complex.
Default: Complex
Options: Real Complex
Transform size (power of 2). Frequency resolution = Fs/N.
Transform size N (power of 2: 8, 16, 32, … 4096). Sets the frequency resolutionFs/N, the number of output bins, the buffer/twiddle BRAM,
and the per-frame latency. Default 256.
Default: 256
Options: 8 16 32 64 128 256 512 1024 2048 4096
Bit width of the input sample(s) (signed).
Bit width of each signed input sample. Range 4 to 32, default 16.Default: 16
Options: 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
Bit width of the cos/sin twiddle factors.
Bit width of the cos/sin twiddle factors (12, 14, 16, 18 or 20). Wider twiddles lower the transform’s numerical noise floor at the cost of a larger twiddle ROM and wider multipliers. Default 18.Default: 18
Options: 12 14 16 18 20
Scaled: »1 each stage (bounded output, ~1/N). Unscaled: full bit growth (+log2 N bits).
Scaled:>>1 after every butterfly stage, giving a bounded output
(~1/N overall) with output width InputSize + 4. Unscaled: no
per-stage shift, full bit growth, output width InputSize + log2(N) + 2. Default Scaled.
Default: Scaled
Options: Scaled Unscaled
System clocks per input sample. Must be >= log2(N)/2 + 2 (one butterfly per clock).
SysClk / DataClk - the number of system clocks per input sample (the input gap). Must be>= floor(log2(N)/2) + 2 so the compact core
finishes a frame in time; the plugin errors at compile if it is too
low. Values 4, 8, 16, 32, 64, 128, 256. Default 16.
Default: 16
Options: 4 8 16 32 64 128 256
Config produced by the FFT Designer.
Hidden JSON configuration produced by the FFT Designer (WebView2). Not edited directly; it mirrors the properties above and is regenerated each time you save the designer.Usage
Radix-2 DIT algorithm
The transform is computed in place in three phases:
-
Bit-reversal permutation of the N input samples.
-
log2(N)butterfly stages; stageshasN/2radix-2 butterflies with span2^s. Each butterfly combines two pointsaandb:t = W_N^k * b a' = a + t b' = a - t -
Output of the N transformed bins in natural order.
The twiddle factor is W_N^k = cos(2*pi*k/N) - j*sin(2*pi*k/N) for the
forward FFT. Twiddles for k = 0 .. N/2-1 are pre-computed by the plugin,
quantised to signed TwiddleBits at scale 2^(TwiddleBits-2), and stored
as a small ROM (WR = cos, WI = -sin). The IFFT reuses the identical
core but flips the sin sign (WI = +sin) for exp(+j...).
Compact one-butterfly-per-clock architecture
The core keeps two ping-pong frame buffers so a new frame can load while the previous one is transformed and streamed out:
- Load - each
SAMPLE_INstrobe stores one input sample (bit-reversed) into the load buffer; after N samples the frame is full. - Compute - one radix-2 butterfly is evaluated per system clock. A
full frame is
(N/2) * log2(N)butterflies. - Output - the N bins are streamed one per clock,
VALID_OUThigh,SOFon bin 0.
Only one complex butterfly (about four real multipliers) exists in the fabric; it is time-shared across every butterfly of every stage. The area is therefore roughly constant in N - the price of large N is BRAM (buffers and twiddle ROM) and the per-frame latency, not DSPs.
Gapped input and the SysClk/DataClk ratio
Because compute is serial, the input cannot arrive every clock. Feed one
sample every SysClk / DataClk system clocks (strobe SAMPLE_IN on the
system clock when a new sample is valid). The plugin requires
SysClk / DataClk >= floor(log2(N) / 2) + 2
and raises a compile error otherwise (the designer flags it live). This
guarantees the (N/2)*log2(N)-clock compute plus the N-clock output finish
inside the N * ratio load window, so frames never collide. Frame
throughput is one frame every N * (SysClk/DataClk) system clocks.
Scaling vs unscaled bit growth
Each butterfly stage can grow the magnitude by up to a factor of 2, i.e.
by log2(N) bits across the whole transform.
- Scaled - the core does
>>1after every stage, so the transform is an overall1/N(bounded output, no bit growth from the algorithm). The output port keeps the input width plus a small 4-bit guard (OUT = InputSize + 4bits). Convenient, but the LSBs are truncated each stage. - Unscaled - no per-stage shift; the datapath carries the full growth,
so the output port is
InputSize + log2(N) + 2bits and represents the exact (unnormalised) DFT sum. No precision lost to truncation, wider bus.
Internally all state is carried in data_t = InputSize + StateGrowth
bits (StateGrowth = 4 scaled / log2(N)+2 unscaled), and twiddle products
use InputSize + StateGrowth + TwiddleBits + 1 bits before the
>> (TwiddleBits-2) arithmetic shift.
Real vs Complex input
- Complex -
IN_I + j*IN_Qtransformed directly; the output spectrum is general (all N bins independent). - Real - only
INis present; the imaginary input is forced to 0. The spectrum is Hermitian-symmetric (X[N-k] = conj(X[k])), so bins0 .. N/2carry all the information. The core still streams all N bins.
Frequency resolution (DFT bins)
The N output bins are uniformly spaced across the sampling frequency:
bin spacing = Fs / N
bin k center = k * Fs / N (k = 0 .. N-1; k > N/2 = negative freqs)
Larger N gives finer resolution at the cost of BRAM and latency.
Reset
RESET is the HLS synchronous reset (ap_rst); it clears the frame
counters and processing state so the next frame starts clean.
Typical applications
- Spectrum / channelisation of a captured baseband window.
- Frequency-domain feature extraction ahead of a detector.
- Fast convolution / correlation together with the IFFT block.
Resources & Timing
-
Latency: Per-frame, pipelined across ping-pong buffers: ~
N(load) +(N/2)*log2(N)(compute, one butterfly per clock) +N(output stream) system clocks. Dominated by the(N/2)*log2(N)compute term for large N. -
Throughput: One frame every
N * (SysClk/DataClk)system clocks; one output bin per clock during the N-clock output phase. Input is gapped (one sample everySysClk/DataClkclocks), not one per clock.
Implemented in Vitis HLS from a hand-written radix-2 DIT core (no vendor
FFT IP). Only one time-shared complex butterfly (~4 DSP multipliers)
is instantiated regardless of N. BRAM holds two N-point ping-pong frame
buffers (re+im, InputSize+StateGrowth bits each) plus the N/2-entry
twiddle ROM (2 * TwiddleBits per entry). Area is roughly constant in N;
the cost of large N is BRAM and latency. All ports use the ap_none /
ap_ctrl_none free-running interface (no AXI handshake).