Last time we outlined how to divide down an FPGAs clock frequency using a basic counter circuit. This allowed us to have varying blinking frequencies for our LEDs depending on which bits of the counter register we tied them to.
What if we needed to adjust for a specific frequency of blinking, of wanted to adjust the time the LED is on vs off? What we are looking for is a programmable square wave generator. I will show you how we can program an FPGA in Verilog to act as one that takes two 4-bit inputs to control the on/off periods of the square wave.
Once we have went through the process of adjusting the on/off timing of a square wave, creating an adjustable PWM output for an LED comes naturally. We will see how to program an FPGA to output a PWM signal that allows us to adjust the brightness of an LED based on a 4 bit input signal.
As before we will be using the affordable Numato Elbert V2 Spartan 3 FPGA Development board as it has the logic cells, switches, and LEDs we need.
A programmable square wave generator circuit is able to generate a pattern of variable ON and OFF intervals. We we be designing our input such that it determines these two intervals with the on-board 8 input dip switch module, so we will divide the inputs into two groups of 4 switches each. The upper 4 switches will be decoded as a 4 bit number called ‘m’ which will control the length of the ON interval, while the lower 4 switches will be decoded as a 4 bit number called ‘n’ which will control the length of the OFF interval.
We will use the numbers m and n as multipliers to a base number of clock cycles, such that they will scale the time for the ON and OFF intervals of the square wave. The base number of clock cycles will determine the frequency range of our square wave generator, and for now I will chose a number of clock cycles that will allow us to see visible differences in a blinking LED. We will chose 1,200,000 clock cycles for our base number, which for our 12 MHz clock, with a period of 83.33 ns per clock cycle, will amount to a 0.1 s base period. So depending on our inputs m and n we can scale the ON and OFF intervals of our wave from 0.1 s to 1.5 s each. Of course m or n could be zero if all the corresponding dip switches are off, but this will result in either a constant OFF output for m = 0, or a constant ON output for n = 0.
Let’s look at the code:
Our module will be squareWaveGenerator and will take an input signal from the clock source, an input signal array from the 8 dip switches, and two outputs signals to the LED and an oscilloscope to view the waveform.
Next we declare two constants, one representing the number of clock cycles in our base time period, and the other representing the width of our counter register.
We have to make sure our counter register can count up to the largest amount of clock cycles for the ON and OFF periods. Both ON and OFF time periods are maximized when both inputs, m and n, are 15. This will amount to 1,200,000 * 2 * 15 = 36,000,000 clock cycles. Taking the base 2 logarithm of that number gives us 25.1 which we round up to 26 to accommodate the largest possible number in the register.
Next we declare our internal signals. We start by creating a register array for the counter, and an array to route the next state logic signal to the counter register. We create two 4 bit arrays m and n and assign to them the complement states of the inputs from the switches. We use the complement as the Elbert V2 uses pull up resistors on the input switches, making them normally asserted.
We next invoke our counter register with an always block, where the counter register is updated with the next state logic on each positive edge of the clock signal.
The next state logic increments the counter register unless its current value is greater than (m + n) times the base amount of clock cycles, which if so resets the register. Recall from the square wave diagram that for the ON period our register has to go through m * cycles, where cycles represents our base amount of clock cycles, then n * cycles for the OFF period. So in total the register will count through ((m + n) * (cycles) – 1) clock cycles, before the register is reset to 0. Because we reset at 0 we need to subtract 1 from the number of clock cycles we count up to.
We use an if-else statement with the form (conditional) ? (statement 1) : (statement 2), where the conditional is a statement that evaluates to either true or false. Statement 1 is chosen if the conditional is true, else statement 2 is chosen. Our conditional checks if the counter register is greater than or equal to the base amount of clock cycles multiplied by the current m and n input values. If so, the next state logic for the counter is chosen to be 26 bits set to 0 (a reset), else the register is incremented by 1.
We next assign to our output either 1 or 0 depending on what the counter register value is. If the counter register is less than cycles * m clock cycles, we output 1, else we output 0. The next state logic of the counter register will keep the output 0 until the register reaches cycles*(m+n) – 1.
Finally we route the same output to another output GPIO which we will probe with an oscilloscope, and end the module.
The user constraint file used with the Numato Elbert V2 is shown above.
Now let’s now look at some blinking LEDs and output waveforms.
Here we see the blinking waveform for m = 1 and n = 1, which puts the LED ON for 0.1 s and OFF for 0.1 s. Thus our waveform should have a period of 0.2 s, and the LED should blink ON and OFF 5 times a second.
Next we see the LED blinking when m = 3 and n = 3. These input values have the LED ON for 0.3 s and OFF for 0.3 s, thus the period of the waveform is 0.6 s.
We now see the LED blinking with m = 6 and n = 1. These inputs have the LED ON for 0.6 s and OFF for 0.1 s, thus giving the waveform a period of 0.7 s.
Finally for m = n = 15 the LED is ON for 1.5 s and OFF for 1.5 seconds, and has a 3 s period.
There are 152 = 225 meaningful combinations of inputs m and n, excluding cases where m or n = 0.
What if we needed a different frequency range?
Lets say we wanted our waveform to have a maximum frequency of 1 MHz, when m = n = 1. We could set our base number of cycles = 6, which when m = n = 1 would give a period of 12 clock cycles for our waveform. Multiplying 12 clock cycles by our period per clock cycle of 83.33 ns gives us a total period of 99.99 µs or a frequency of 1 Mhz. We wouldn’t need such a large counter register anymore and instead would need one with a width of 8 bits since log2(6*2*15) = 7.49, which we round up to 8.
Ringing after vertical transitions of the wave becomes evident at higher frequencies . This ringing is caused by parasitic capacitance and inductance in the circuit.
Pulse Width Modulation, or PWM, varies the period that a square wave is ON versus the time it is OFF in such a way that the voltage across a load is “averaged” to be somewhere between the supply voltage and 0V.
The diagram above shows three different waveforms with varying proportions of ON (3.3V) vs OFF (0V) time, or what is known as a duty cycle. The higher the duty cycle, the larger the average voltage on a load will be.
Keep in mind that for PWM to work with an LED output we will need to have a frequency for our wave that is fast enough so that our brain is unable to differentiate ON and OFF periods and instead sees a solid output of light. We will use a frequency of 1000 Hz which is well above the minimum frequency where we stop perceiving flickering but not so fast that the LED doesn’t have enough time spent ON to light up effectively.
We will use the four lower dip switches as circuit inputs that decode a 4 bit number we will use to determine the duty cycle. Lets look at the code.
Input and output signals are mostly unchanged, aside from the wire array accommodating only 4 of the dip switches this time.
The constant totalCycles will be the total clock cycles within a 1000 Hz waveform’s period (12,000,000 Hz / 1000 Hz). The multiplier will be the amount of clock cycles within 1/15 of the total clock cycles (800), such that we can use the 4 bit input number of duty cycles to multiply the multiplier by to get the total clock cycles to turn the LED on depending on the duty cycle number. The 4 bit input number ranges from 0 to 15, so the LED can be on from 0 clock cycles (always off) to 15*800 = 12,000 clock cycles (always on), or somewhere in between.
Again we have the constant N which determines the width of our counter register.
Our internal signals and counter register code is unchanged from the square wave generator code. This time we declare an internal signal array “dutyCycle” and route the complement of the lower 4 input dip switches to it.
The next state logic resets the the counter to 0 if the register is greater than or equal to the total number of cycles (12000), else it increments the register by 1.
Our output logic is 1 if the register’s value is less than the current dutyCycle number * the multiplier of clock cycles, else 0 for the remaining clock cycles until the register is reset to 0.
Again for completeness, The ucf used with the Numato Elbert V2 is shown above.
Now let’s see how the circuit performs.
Above we see five test cases for the circuit. First none of the dip switches are on. Then I switch on each bit from right to left, such that if ‘0’ is an off switch and ‘1’ is an on switch, the input states over time are as follows: 0000, 0001, 0011, 0111, 1111. You can kind of see this in the animation on the right as I flip the switches. As I flip the switches on, the dutyCycle become greater and greater, which can be seen as the LED glows brighter with each switch flip.
In the beginning when none of the switches are on, dutyCycles = 0 which means our LED is on for 0/15 of the time, or off.
Next I switch the first lower bit on which makes dutyCycles = 1, so the LED is on for 1/15, or 6.66% of the time, as is verified by the oscilloscope capture above.
Then I switch the second bit on which makes dutyCycles = 3, so the LED is on for 3/15, or 20% of the time.
I switch the third bit on which makes dutyCycles = 7, so the LED is on for 7/15, or 46.6% of the time.
Finally I flip the last switch such that all four switches are on and the dutyCycle = 15, so the LED is always on.