Generated reference › FFT — Control Systems/Transforms
kind: generated#block#control-systems-transforms

FFT — Control Systems/Transforms

Control_Systems/Transforms/FFT · 1 input / 2 output port(s) at insert · exports to Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog, PLC Structured Text

Description#

The block's own DESCRIPTION_HTML, rendered verbatim — the same text the config dialog's info panel and the library navigator show. Fix a wrong sentence in the block's .cpp (R-D9), never here.

FFT

Control Systems / Transforms

The complex forward discrete Fourier transform of the window on its input, which is MATLAB's fft(u): X[k] = Σₙ u[n]·e−2πi·kn/N for k = 0 … N−1.

The answer is complex, so it leaves on two ports – the real part and the imaginary part – because an ICore signal carries doubles. If only the magnitude spectrum is wanted, FFT Magnitude computes it in one port and is bridged to Simulink; if only a few bins are wanted, Goertzel gives those bins without the rest.

Ports

  • u – the window to transform, a real vector: an [N,1] column or a [1,N] row, N from 1 to 32. Entry n is u[n] in the sum above, oldest first.
  • Re – the real part of X, one entry per bin, so N long and in the same orientation as the input.
  • Im – the imaginary part of the same X, the same size.

Parameters

  • Scaling – what multiplies the sum.
    • None (fft) – the transform exactly as fft defines it. The default.
    • 1/N – the same spectrum divided by the window length, so bin 0 reads the MEAN of the window rather than its sum. This is Simulink's Normalize option and the convention the inverse transform carries by default.
  • Sampling Time (s) – zero or less inherits the solver's rate; a positive value runs the block at that period.

N is not a parameter: it is the width of the input port, so the block and the signal reaching it cannot disagree about the transform length.

Code export

All ten targets: Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog and PLC Structured Text.

The N×N twiddle pairs are structural and are inlined into the generated arithmetic: they follow from the window length and the scaling alone, so every target emits two real dot products per bin and computes no sine or cosine at run time – those happened at export.

The three HDL targets are genuine synthesizable Q16.16: only multiplies and adds, accumulated at double width and shifted back once per entry. Every twiddle has magnitude at most 1, so no part of the matrix quantizes away – but note that an unscaled transform can reach N·max|u| on bin 0, and the format saturates near ±32768.

Simulink bridge

No equivalent a bridge can write (Support::None), and the reason is about the wire rather than the block. dspxfrm3/FFT exists and computes the same transform – simulated on an 8-point frame, it agreed with fft exactly – but it carries its whole answer on one complex port, and an ICore signal is real, so there is no port-to-port mapping the bridge could write. A model that needs this block in Simulink pairs dspxfrm3/FFT with simulink/Math Operations/Complex to Real-Imag, whose two outputs reproduce this block's Re and Im ports exactly (measured to 0.0). The bridge reports the block rather than dropping it, and code export verification still covers it across all ten languages.

Notes

  • Algebraic, with no state. The whole window arrives on the port, so one step is one transform and nothing carries over.
  • Measured against R2026a. The coefficients reproduce fft to 4.4e−16 over an 8-point window and to 6.7e−16 over a 5-point one, and dspxfrm3/FFT simulated on an 8-point frame agreed with fft exactly.
  • A direct sum, not a radix-2 algorithm. The name is MATLAB's and what is guaranteed is its answer: N need not be a power of two, and the cost is N² products rather than N·log N. The 32-entry limit exists because those products are unrolled into the exported core.
  • The angle is reduced modulo N before the trigonometry. kn reaches 961 at the longest window, and a cosine of ~6000 radians has already lost most of its accuracy to argument reduction; the exponential is periodic in kn with period N, so the reduced angle is the same number computed accurately.
  • The exact inverse is the IFFT block, which takes Re and Im back.
  • No state space. A fixed matrix over N presented samples is not an A/B/C/D pair evolving in time, so model reduction correctly declines to merge it.

Code facts#

FactValue
registered typeControl_Systems/Transforms/FFT
familyControl_Systems/Transforms
solver environment classICoreBlock_0_Control_Systems_1_Transforms_2_FFT
sourcesrc/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Transforms/FFT/ICoreBlock_0_Control_Systems_1_Transforms_2_FFT.cpp
headersrc/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Transforms/FFT/ICoreBlock_0_Control_Systems_1_Transforms_2_FFT.h
default size on canvas130 × 90 px
ports at insert1 in, 2 out
code generators implementedPython, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog, PLC Structured Text

Ports#

#DirectionSignal typeDescription label
1inICoreDoubleu
2outICoreDoubleRe
3outICoreDoubleIm

Ports the constructor creates. A block whose port list changes with its configuration adds or removes ports at load time; the count above is the one a freshly inserted block has.

Configuration variables#

Config variableDefaultSimulink parameter
ScalingNone (fft)%~%1/N~~None (fft)—

Every block also carries Sampling Time (s) from ICoreBlockSolverEnvironment: zero or less inherits the solver's rate, a positive value runs the block at that period.

supportSupport::None
Simulink path—
port-count rulePortsParam::None
SampleTime parameteryes

Caveat (shown to the user): no single-block equivalent, and the reason is the WIRE rather than the block. dspxfrm3/FFT exists in the DSP System Toolbox and computes the same transform -- simulated on an 8-point frame, it agreed with MATLAB's fft exactly -- but it carries its whole answer on ONE COMPLEX port, while this block presents the real and imaginary parts on two real ports because an ICore signal carries doubles. There is no port-to-port mapping to write. In Simulink, pair dspxfrm3/FFT with simulink/Math Operations/Complex to Real-Imag: its two outputs reproduce this block's Re and Im ports exactly, measured to 0.0. FFT Magnitude is the bridged block for the magnitude spectrum alone

Catalog contract: src/ICoreBlocks/ICoreCoder/ICoreCommandSystem/SimulinkBridge/ICoreSimulinkBlockCatalog.h

Description vs code#

The lists agree. check_block_descriptions.py finds no disagreement between the description's Ports, Parameters, Code export and Simulink bridge lists and the code's.

The verdict above is tools/docs/check_block_descriptions.py (P7.1), which compares LISTS. It cannot read a sentence: "stateless" on a block with a state, an initial-value semantic the recursion does not implement, a "not synthesizable" caveat the HDL banner contradicts. That is the agent audit (P7.3) on BLOCK_DESCRIPTION_AUDIT.md, and this tool's green is not a substitute for one.

File banner (developer view)#

The top comment of the block's .cpp — the maths, the realization and the export strategy, addressed to whoever changes it. It must not contradict the description above (P7.5).

FFT -- the complex forward discrete Fourier transform of a window (MATLAB fft) X[k] = SUM over n of u[n] * exp(-2*pi*i*k*n/N), k = 0 .. N-1

The answer is COMPLEX and an ICore wire carries doubles, so it leaves on TWO ports, Re and Im. That is the arrangement Goertzel and Chirp-Z Transform already use, and it is also why this block is Support::None -- see the catalog entry below.

ONE MATRIX PRODUCT, TWICE. The twiddle pair cos/sin(-2*pi*k*n/N) depends on the window LENGTH and on nothing else, so the N*N pairs are derived once -- when the input port's size settles -- and every target inlines them as two real dot products per output entry. No emitted core computes a sine or a cosine; it multiplies and adds.

⚠ THE ANGLE IS REDUCED MODULO N BEFORE THE TRIG CALL. k*n reaches (N-1)^2 = 961 at the block's longest window, and cos() of an argument near 6000 radians has lost most of its low-order accuracy to argument reduction. exp(-2*pi*i*k*n/N) is periodic in k*n with period N, so ((k*n) mod N) is the SAME number computed from a small angle -- which is what lets this block match fft() to a few ulp at N = 32 instead of to 1e-12.

⚠ THIS IS A DIRECT SUM, NOT A RADIX-2 ALGORITHM. The name is MATLAB's, and what the block guarantees is fft()'s ANSWER: N need not be a power of two, the cost is N*N products, and the window is bounded at 32 because those products are UNROLLED into the exported core.

⚠ MEASURED AGAINST R2026a rather than asserted. The coefficients here were run through a stub harness on the same vectors a MATLAB batch was given: over the rig's 8-point window they reproduce fft() to 4.4e-16, over a 5-point window (NOT a power of two, which is the point of that case) to 6.7e-16, and over an 8-point COMPLEX window to 8.9e-16. Separately, dspxfrm3/FFT was SIMULATED on an 8-point frame and agreed with fft() EXACTLY, to 0.0.

ALGEBRAIC: the whole window is presented on the input port, so one step completes one transform and nothing is held between samples. No state space -- the map is linear in the input, but a fixed matrix over a WINDOW is not an A/B/C/D pair evolving in time.

Sample results#

FFT — Repeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sampleFFT — Repeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sample-202012345t (s)in ICoreDouble-Out-0out ICoreDouble-Out-0out ICoreDouble-Out-1
tin ICoreDouble-Out-0out ICoreDouble-Out-0out ICoreDouble-Out-1
0-2-20
0.40.50.50
0.8-2-20
1.20.50.50
1.6-2-20
20.50.50
2.4-2-20
2.80.50.50
3.2-2-20
3.60.50.50
4-2-20
4.40.50.50
4.8-2-20
5.20.50.50

Every 4th of 60 samples, from the table stimulus.

The same rig also ran:

StimulusWhat it isOutput range
impulseImpulse: one sample of 1 at k = 5, 0 elsewhere (Repeating Sequence Stair)0 … 1
rampRamp: slope 1 from t = 00 … 5.9
sineSine Wave: amplitude 1, 2 rad/s, no phase, no bias-1 … 0.9996
stepStep: 0 -> 1 at t = 1 s0 … 1

Plotted: table — Repeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sample

Category static · sample time 0.1 · 60 steps · commit 3c100aff6f27235305db4ad4d572f32e342718ad · produced by docsSample --out <folder> --blocks DCT IDCT FFT IFFT --steps 60 · data docs/generated/samples/Control_Systems__Transforms__FFT.json · the SVG is generated from those numbers by tools/docs/plot_svg.py, so it is a run and not a drawing (R-D10).