Introduction: PID Controller VHDL

This project was my final project to complete my Honours Bachelor Degree from Cork Institute of Technology. This Tutorial is broken into two sections the first will cover the main body of PID code which is the main purpose of the project and the second section covers interfacing the code which was implemented on a Basys 3 development board and then interfaced to a ping pong ball levitation rig. The theoretical and built rig are shown in the images attached.



  • Vivado Design Suite

Implementation(in the brackets is what was used for my project)

  • FPGA Board which can input and output Digital/Analog signals(Basys 3)
  • a system which is controllable with a single feedback source(Ping Pong Ball Levitation Rig)


Step 1: Basic Control Theory

I thought that adding in some basic control theory would give anyone who would like to try and implement this code a good base to start from.

The diagram attached is the layout of a single loop controller.

r- Is the reference. This determines where the controller is desired to go.

e-Is the error. This is the difference between the value on your sensor and your reference. e.g. e=r-(d+output of sensor).

K-This is the controller. A controller can be comprised of three terms. These terms are P, I and D. All three terms have multipliers called Kp, Ki and Kd. These values determine the response of the controller.

  • P-Proportional. A strictly P controller will have an output proportional to the current error. A P controller is simple to implement and work fast but will never reach the value you set(reference).
  • I-Integral. A strictly integral controller will sum up the previous error which will eventually reach the desired reference. This controller is generally too slow to implement. Adding in a P term will decrease the time taken to reach the reference. The time which the input is being sampled must be taken into account the integral term is integrated with respect to time.
  • D-Derivative. The Derivative term will have a output which is dependant on the rate of change of error. This term is generally used with a P term or with a PI term. Since this is proportionally to the rate of change of error then a noisy single will have its noise amplified which can cause a system to be unstable. Time must also be taken into account as the derivative term is also with respect to time.

U- This is the control signal. This signal is an input to the rig. In the case of this project the u is a PWM signal input to the fan to change the speed.

G- This is the system which is being controlled. This system can be modelled mathematically in the S or Z Domain. The systems can be to the nth order but for someone getting started with control a first order system should probably be assumed as this is much easier to calculate. The is a Plethora of information on modelling system to be found online. Depending on the sampling time of the sensor the model of the system is either discrete or continuous. This has a drastic effect on the controller so research into both is advised.

d- This is disturbance which is added to the system. Disturbance is outside forces which the model of the system does not account for. A easy example of this would be a drone which you would want to hover at 5 metres a gust of wind comes and drops the drone 1 metre the controller will reposition the drone after the disturbance has happened. This is known as disturbance as wind is non-repeatable so this can't be modelled.

To tune the controller there are too many rules to name but some good ones which I was started with are Cohen Coon and Zieger Nichols.

Modelling a system is generally the most important part without an accurate model the controller which has been designed will not respond as desired.

There should be enough information here to understand on how the controller works along with some individual research and the code below a controller with any combination of the three terms can be implemented.

Step 2: Writing PID Code

The basic principle of the code found at the following link was taken and modified as this code did not work but t had many of the principles right which gave a good starting point. Original PID The code had several errors such as

  • Continuous Operation - the controller is inheritantly discrete so the controller had to be set up to only calculate all 3 terms when a new input was available. The work around for this simulation was to check if the input had changed since the last time. this only works to simulate the code working correctly.
  • Sample Time had no effect on integral and derivative term - The controller also did not consider the time in which the sample were being taken over, so a value called divider for time was added into to ensure the integral and derivative terms were operating over the correct interval.
  • Error could only be postive - when calculating the error there was also a problem as the error could never be negative meaning when the feedback signal had surpassed the reference value that the controller would continue to increment the output when it should be decrementing.
  • Gain values for the 3 terms were integers - in my experience I always found that values for the 3 terms in the controller to always be floating point numbers due to Basys 3 not having floating point number the values had to be given a numerator value and a denominator value which would serve as a work around the surpass this problem.

The code is attached below there is the main body of code and a testbench to simulate the code. The zip folder contains the code and testbench already in Vivado so that can be opened to save time. there is also a simulated test of the code which shows the output tracking the reference this proves that the code is functioning as intended.

Step 3: How to Modify for Your System

Firstly not all systems are the same one must analyse the inputs and outputs of the system. In my case the output of my rig which gave me a value for the position was an analog signal and the input from the system was a PWM signal. Meaning that an ADC conversion was needed. Fortunately the Basys 3 has a built in ADC so this was no problem the output of the IR sensor had to be scaled down to 0V-1V as this is the maximum range of the onboard ADC. This was done using a voltage divider circuit which was made from 1k resistors set up as a 3k resistor in series with a 1k resistor. The analog signal was now within range of the ADC. The PWM input to the fan is able to directly driven by the output of a PMOD port on the Basys 3.

Step 4: Taking Advantage of I/O on Basys 3

There is a number of I/O on the Basys 3 which allowed for easier debugging when the code was running. the I/O was set up as the following.

  • Seven Segment Display - This was used to show the value of the reference and value on the ADC in volts. The first two digits of the seven segment display show the two digits after the decimal place of the ADC value as the value is between 0-1V. The digits three and four on the seven segment display show the reference value in volts this also shows the first two digits after the decimal place as the range is also between 0-1V.
  • 16 LEDs - The LEDs were used to show the value of the output to ensure that the output was saturating and the output was changing correctly.

Step 5: Noise on the IR Sensor Output

There was noise on the sensor output to fix this problem an averaging block was put in place as this was sufficient and required very little work to complete.

Step 6: Overall Code Layout

There is one piece of code which hasn't been talked about yet. This code is a clock divider called trigger. this bit of code triggers the ADC code to sample. the ADC code takes a maximum of 2us to complete so then the current input and the previous input are averaged. 1us after this averaging the controller calculates P,I and D terms. the over all layout of the code and interfacing is shown in the makeshift connection diagram.

Step 7: Testing

The code was deployed on the Basys 3 and the following response was recorded. the reference changed between 2 values. which is the case in the completed project code attached. The attached video shows this response in real-time. The oscillations decay faster in the upper part of the tube as the controller was designed for this region but the controller doesn't work as well further down the tube as the system is non-linear.

Step 8: Modifications to Improve Project

The project worked as intended but there a few modifications which i would have made if the project could have been extended.

  • Implement digital filter to completely attenuate noise
  • set up the ADC code, Average code and Integration code to trigger sequentially.
  • use a different sensor for feedback as the non-linear response of this sensor caused a wide variety of problems with this project but that is more on the control side not the coding side.

Step 9: Extra Work

Over the course of the summer I wrote code for a cascade controller and implemented the modifications I recommended for the single loop PID controller.

Modifications made to regular PID controller

· FIR filter template implemented the coefficients must be changed to achieve the desired cut-off frequency. The current implementation is a 5-tap fir filter.

· The timing of the code has been setup so that the filter will propagate the new sample through and when the output is ready the integral term will be triggered which will mean the code can be modified to work at different time intervals with less effort to change code.

· The main for loop which drives the program has also been reduced as this for loop took 7 cycles previously this slowed down the maximum operating speed of the Controller but by reducing the for loop t 4 states this means that the main block of code can operate within 4 clock cycles.


This controller was tested and performed as intended I did not take pictures of this proof as this part of the project was just to keep the mind active. The code for testing aswell as the testbench will be available here so you can test the program before implementation.

Why use a cascade controller

A cascade controller controls two parts of the system. In this case a cascade controller would have an outer loop which is a controller which has feedback from the IR sensor. The inner loop has feedback in the form of time between the pulses from the tachometer which determines the rotational speed of the fan. By implementing control, a better response can be achieved out of the system.

How does the cascade controller work?

The controller outer loop will feed a value for the time between the pules to the inner loop controller. This controller will then increase or decrease the duty cycle to achieve the desired time between pulses.

Implementation of modifications on rig.

Unfortunately, I was unable to implement these modifications on the rig as I didn’t have access to it. I tested the revised single loop controller which works as intended. I have not tested the cascade controller yet. I am confident the controller will work but may require some slight modifications to work as intended.


I was unable to test the controller as it was difficult to simulate two input sources. The only problem I can see with the cascade controller being that as the outer loop tries to increase the setpoint supplied to the inner-loop that a larger set point is actually a lower RPS for the fan but this can be fixed easily. take the set point from the max value of the setpoint signal(4095 - setpoint - tacho_result).

Step 10: Conclusion

Overall the project functions as I intended when the project began so I am happy with the result. Thank you for taking the time to read my attempt at developing a PID controller in VHDL. If anyone is attempting to implement some variation of this on a system and requires some assitance for understanding the code contact me I will respond ASAP. Anyone who tries the extra work which was compled but not implemented please contact me for any hand. I would greatly appreciate it if anyone who does implement it let me know how it goes.