Xilinx
HLS
Block Preview

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.

FFT Designer

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/N bin spacing).
  • Scaling - Scaled (>>1 per stage, bounded ~1/N output) 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

IN_I Input InputSize bit BIT VECTOR
In-phase (I) input sample (present only when Signal Type = Complex). Signed, InputSize bits.
IN_Q Input InputSize bit BIT VECTOR
Quadrature (Q) input sample (present only when Signal Type = Complex). Signed, InputSize bits.
SAMPLE_IN Input 1 bit BIT
Input sample strobe. Pulse high for one system clock each time a new input sample is valid on IN / IN_I / IN_Q. Samples must be gapped: at most one every SysClk/DataClk clocks. N strobes fill one frame.
CLK Input 1 bit BIT
System clock input. Default: Acquisition clock. All timing (the gap between samples, one butterfly per clock, one bin out per clock) is in this domain.
Default: Default Board Clock
RESET Input 1 bit BIT
HLS synchronous reset (ap_rst). Default: Global reset.
Default: Default Board Reset
OUT_I Output OutputSize bit BIT VECTOR
Real part of the spectrum bin. Streamed one bin per clock when VALID_OUT is high. Signed, OutputSize bits (InputSize + 4 scaled, InputSize + log2(N) + 2 unscaled).
OUT_Q Output OutputSize bit BIT VECTOR
Imaginary part of the spectrum bin. Streamed one bin per clock when VALID_OUT is high. Signed, OutputSize bits.
VALID_OUT Output 1 bit BIT
Output valid. High for the N clocks over which one frame’s bins are streamed out (bin 0 .. bin N-1).
SOF Output 1 bit BIT
Start of frame. Pulses high for one clock on bin 0 of each output frame, coincident with VALID_OUT, marking the DC bin.
IN InputSize bit
Real input sample (present only when Signal Type = Real). Signed, InputSize bits. The imaginary part is tied to 0 internally.

Properties

Property window

Signal Type SignalType

Real: one input channel (imag=0). Complex: I/Q input.

Input format. Real exposes a single IN channel (imaginary tied to 0); Complex exposes IN_I / IN_Q. Default Complex.

Default: Complex

Options: Real Complex

FFT Length N FFTLength

Transform size (power of 2). Frequency resolution = Fs/N.

Transform size N (power of 2: 8, 16, 32, … 4096). Sets the frequency resolution Fs/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

Input Bit Width InputSize

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

Twiddle Bit Width TwiddleBits

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

Scaling Scaling

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

SysClk / DataClk ClockRatio

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 (JSON, use editor) FFTProject

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:

  1. Bit-reversal permutation of the N input samples.

  2. log2(N) butterfly stages; stage s has N/2 radix-2 butterflies with span 2^s. Each butterfly combines two points a and b:

          t = W_N^k * b
        a' = a + t
        b' = a - t
      
  3. 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_IN strobe 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_OUT high, SOF on 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 >>1 after every stage, so the transform is an overall 1/N (bounded output, no bit growth from the algorithm). The output port keeps the input width plus a small 4-bit guard (OUT = InputSize + 4 bits). 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) + 2 bits 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_Q transformed directly; the output spectrum is general (all N bins independent).
  • Real - only IN is present; the imaginary input is forced to 0. The spectrum is Hermitian-symmetric (X[N-k] = conj(X[k])), so bins 0 .. N/2 carry 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 every SysClk/DataClk clocks), 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).