Square Wave Generator and PWM with a Numato Elbert v2 FPGA

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:

module squareWaveGenerator
    (
       input wire clk,         // input from clock source
       input wire [7:0] sw,    // input from dip switches
       output wire out,        // output to LED
       output wire scope       // output for oscilloscope
    );

 

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.

    // constant declarations
    localparam cycles = 1200000;   // divisor of mod counter (number of clock cycles)
    localparam N = 26;             // width of register = ceiling( log(2, 1200000*2*15) )

 

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.

    // internal signals
    reg  [N-1:0] counter_reg;      // register for counter
    wire [N-1:0] counter_next;     // next state output of counter

    wire [3:0] m , n;              // connections to input from dip switch
    assign m = ~sw[7:4];           // upper 4 switches assigned to decode ON interval multiplier
    assign n = ~sw[3:0];           // lower 4 switches assigned to decode OFF interval multiplier

 

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.

    // counter register
    always @(posedge clk)               // on positive clock edge
	counter_reg <= counter_next;   // update register contents to next state logic

    // next state logic:  if counter register exceeds current reset value, reset to 0, else increment by 1
    assign counter_next = (counter_reg >= (cycles * (m + n)) - 1) ? 8'b0 : counter_reg + 1;

 

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.

        // assign output logic
	// when counter register is counting up in ON interval, set output to 1, else: 
	// if counter register is counting up in OFF interval, set output to 0
        assign out = (counter_reg < (cycles * m)) ? 1'b1 : 1'b0;	

 

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.

NET "clk"          LOC = P129  | IOSTANDARD = LVCMOS33 | PERIOD = 12MHz;

NET "sw[0]"        LOC = P70   | PULLUP;
NET "sw[1]"        LOC = P69   | PULLUP;
NET "sw[2]"        LOC = P68   | PULLUP;
NET "sw[3]"        LOC = P64   | PULLUP;
NET "sw[4]"        LOC = P63   | PULLUP;
NET "sw[5]"        LOC = P60   | PULLUP;
NET "sw[6]"        LOC = P59   | PULLUP;
NET "sw[7]"        LOC = P58   | PULLUP;

NET "out"          LOC = P46;

NET "scope"        LOC = P31;

 

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 15= 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.

module pwmLed

    (
      input wire clk,         // input from clock source
      input wire [3:0] sw,    // input from dip switches
      output wire out,        // output to LED
      output wire scope       // output for oscilloscope
    );

    // constant declarations
    localparam totalCycles = 12000;     // divisor of mod counter (number of clock cycles before reset to 0) f = 1000 Hz
    localparam multiplier  = 800;       // 12000 / 15 = 800 cycles to multiply by 4 bit duty cycle input to get total time ON
    localparam N = 14;                  // width of register = ceiling( log(2, 1200000*15) )

 

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.

    // internal signals
    reg  [N-1:0] counter_reg;      // register for counter
    wire [N-1:0] counter_next;     // next state output of counter

    wire [3:0] dutyCycle;                  // connections to input from dip switch
    assign dutyCycle = ~sw[3:0];           // lower 4 switches assigned decode duty cycle = dc / 16

    // counter register
    always @(posedge clk)               // on positive clock edge
        counter_reg <= counter_next;   // update register contents to next state logic

 

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.

    // next state logic:  if counter register exceeds reset value, reset to 0, else increment by 1
    assign counter_next = (counter_reg >= totalCycles) ? 14'b0 : counter_reg + 1;

 

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.

    // output is 1 when counter is less than input duty cycle * multiplier : else
    // output is 0 for the remaining of the total cycles before the counter resets to 0
    assign out = (counter_reg < (dutyCycle * multiplier)) ? 1'b1 : 1'b0;
    // route LED output to oscilloscope output 
    assign scope = out; 
endmodule

 

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.

NET "clk"          LOC = P129  | IOSTANDARD = LVCMOS33 | PERIOD = 12MHz;

NET "sw[0]"        LOC = P70   | PULLUP;
NET "sw[1]"        LOC = P69   | PULLUP;
NET "sw[2]"        LOC = P68   | PULLUP;
NET "sw[3]"        LOC = P64   | PULLUP;

NET "out"          LOC = P46;

NET "scope"        LOC = P31;

 

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.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s