DEV Community

Hedy
Hedy

Posted on

How to use FPGA to implement PID?

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:

  1. Error

e = setpoint - measurement

  1. Differences
  • de = e - e1
  • dde = e - 2*e1 + e2
  1. Multiply
  • p_term = A * de
  • i_term = B * e
  • d_term = C * dde
  1. Accumulate output

u = u_prev + p_term + i_term + d_term

  1. Saturation + anti-windup
  • Clamp u to actuator limits
  • If saturated, optionally stop/limit integral contribution (details below)
  1. 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)

  1. Decide actuator output range (e.g., PWM duty 0…65535)
  2. Decide measurement and setpoint scaling (ADC counts? physical units scaled?)
  3. Pick Q formats so products don’t overflow at worst-case error
  4. 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)

  1. Validate your PID gains in Python/MATLAB (floating point)
  2. Quantize to fixed point (same Q formats)
  3. Simulate HDL with a testbench using recorded signals
  4. Hardware test with step response + logging (ILA/SignalTap)

Top comments (0)