Implementing a PID on an FPGA is very doable—and usually better than a microcontroller when you need high sample rate, low jitter, and deterministic latency. The key is to implement a discrete-time PID using fixed-point math, with proper saturation/anti-windup, and a clean sample tick.
Below is a practical, FPGA-friendly way to do it.
1) Pick a discrete PID form that’s FPGA-friendly
Standard (position) form

Works, but the integral sum can grow large and you’ll spend effort managing overflow.
FPGA-friendly (incremental / velocity) form (recommended)
This form reduces accumulator range issues and is easy to pipeline:
𝑢[𝑛]=𝑢[𝑛−1]+𝐴(𝑒[𝑛]−𝑒[𝑛−1])+𝐵𝑒[𝑛]+𝐶(𝑒[𝑛]−2𝑒[𝑛−1]+𝑒[𝑛−2])
Where typically:
- 𝐴=𝐾𝑝
- 𝐵=𝐾𝑖𝑇𝑠
- 𝐶=𝐾𝑑/𝑇𝑠
Why it’s great on FPGA
- Uses only a few differences + multiplies + adds each sample
- One state register for output (u[n-1]) + small history (e[n-1], e[n-2])
- Easy saturation at the end
2) Build the FPGA PID datapath (what blocks you implement)
At each sample tick:
- Error
e = setpoint - measurement
- Differences
- de = e - e1
- dde = e - 2*e1 + e2
- Multiply
- p_term = A * de
- i_term = B * e
- d_term = C * dde
- Accumulate output
u = u_prev + p_term + i_term + d_term
- Saturation + anti-windup
- Clamp u to actuator limits
- If saturated, optionally stop/limit integral contribution (details below)
- Update history registers
- e2 <= e1
- e1 <= e
- u_prev <= u_sat
3) Fixed-point design (the “make it work in hardware” part)
Choose a Q format
Example starting point:
- Error e: signed 16-bit
- Coefficients A,B,C: signed 18-bit (fits nicely with DSP slices)
- Internal products: 34–36-bit
- Accumulator/output: 32–40-bit, then truncate/round to actuator width
Typical formats:
- Signals: Q1.15 (16-bit signed)
- Coeffs: Q3.14 or Q5.12 depending on your gain range
- Accumulator: keep extra headroom: Q8.24 or similar
Scaling workflow (practical)
- Decide actuator output range (e.g., PWM duty 0…65535)
- Decide measurement and setpoint scaling (ADC counts? physical units scaled?)
- Pick Q formats so products don’t overflow at worst-case error
- Add:
- rounding when shifting back down
- saturation after add/accumulate
4) Sample tick and determinism
Your PID should update at a fixed rate 𝑓𝑠=1/𝑇𝑠.
Common FPGA approach:
- A hardware timer/counter generates pid_tick every Ts
- On pid_tick, latch inputs (setpoint, measurement) and run the PID pipeline
- Pipeline latency might be a few FPGA clocks, but the update interval stays exact
Tip: If your ADC sample rate is the master clock, use “ADC data valid” as pid_tick.
5) Derivative filtering (important in real systems)
Raw derivative amplifies noise. A common FPGA-safe solution:
- Compute derivative on measurement instead of error, or
- Apply a 1st-order low-pass to derivative:
𝑑𝑓[𝑛]=𝑑𝑓[𝑛−1]+𝛼(𝑑[𝑛]−𝑑𝑓[𝑛−1])
That’s just an extra multiply + accumulator (very FPGA-friendly).
6) Anti-windup (don’t let the integrator explode)
Even with incremental form, the “I contribution” can drive output into saturation and keep integrating.
Simple anti-windup options:
Option A: Conditional integration (easy)
Only apply the I term when not saturated or when error would move output back toward range.
- If u saturated high and e > 0 → skip I
- If u saturated low and e < 0 → skip I
Option B: Back-calculation (more control)
Adjust integrator based on saturation difference:
𝐼←𝐼+𝐾𝑎𝑤(𝑢𝑠𝑎𝑡−𝑢)
(also FPGA-friendly, one extra multiply)
7) Resource mapping tips (Xilinx/Intel FPGAs)
- Use DSP slices for the multiplies (3 multiplies for P/I/D, plus optional derivative filter)
- Use pipelining to meet timing:
- Stage 1: compute e,de,dde
- Stage 2: multiply
- Stage 3: sum + saturate
- If you need many PID loops (multi-axis), you can:
- Fully parallel: best performance, more DSP usage
- Time-multiplex: reuse multipliers across channels with scheduling (cheaper DSP)
8) How PID connects to the outside world on FPGA
Typical chain:
ADC / sensor capture → scaling → PID → output scaling → PWM / DAC / motor driver
Examples:
- Motor control: PID output drives PWM duty or current reference
- Temperature control: PID output drives PWM to heater + SSR
- Position control: PID output drives torque/current loop setpoint
9) Verification workflow (fast and reliable)
- Validate your PID gains in Python/MATLAB (floating point)
- Quantize to fixed point (same Q formats)
- Simulate HDL with a testbench using recorded signals
- Hardware test with step response + logging (ILA/SignalTap)

Top comments (0)