PWM Waveform

Time to Create a Pulse Width Modulation Circuit

For this next project I’ll create a Pulse Width Modulation circuit. What is Pulse Width Modulation? Pulse Width Modulation (PWM), is a method of controlling average voltage or power in a digital circuit. It accomplishes this by varying the on/off duty cycle of the output signal.

A duty cycle of 0 results in 0% time in the on state. It produces a constant 0V PWM output. Similarly, a duty cycle of 1 results in 100% time in the on state. It produces a constant Vmax output. And a duty cycle of, say, 60% says that the signal should be in the on state 60% of the time and off for the remaining 40% of the time.

PWM circuits are very useful in digital electronics. Some example applications include varying the intensity of an LED using a microcontroller with a fixed output voltage. Or varying the speed of a motor. Or regulating the temperature of a heating element.

A PWM circuit is a relatively simple sequential circuit. For input ports we will want both a clk and a rst signal. And of course we will also want an output port for the pwm signal. In order to adjust the duty cycle we will also want an input port that I will call dty. To make the circuit more flexible I will make the size of the dty port a configurable parameter. If you need a refresher on configurable parameters you may want to review the Configurable Counter Circuit post.

The dty port provides an input value that represents the percentage of on vs. off time for the PWM output. For an 8-bit PWM, dty values of 0 to 255 correspond to 0% to 100% duty cycle. I will use a very simple counter and comparator to compute the on vs. off time for the PWM output.

The Verilog Code

The code for the complete PWM circuit is shown below. Let’s walk through it to see how it works.

module pwm
    #(parameter BITS = 8)
    (
        input clk,
        input rst,
        input [BITS - 1 : 0] dty,
        output pwm
    );

    // state registers
    reg rPwm;
    reg [BITS - 1 : 0] rDuty;

    // next state signals
    wire pwmNext;
    wire [BITS - 1 : 0] dutyNext;
                
    // update our state variables
    always @(posedge clk, posedge rst)
    begin
        if (rst) begin
            rPwm <= 0;
            rDuty <= 0;
        end else begin
            rPwm <= pwmNext;
            rDuty <= dutyNext;
        end
    end
        
    // compute the next state values
    assign dutyNext = rDuty + 1;
    assign pwmNext = rDuty < dty;

    // output logic
    assign pwm = rPwm;
    
endmodule

Starting on lines 11 and 23, I declare two registers to hold the current PWM output state and the duty counter. And on lines 15 and 16, I declare signals to hold the next PWM output value pwmNext, and the next duty counter value dutyNext. Inside our always block you can see that if rst is raised, the pwm output and duty counter are set to zero. Otherwise, on each clk tick rPwm and rDuty are updated with their next values.

One lines 31 and 32 you can see where I use continuous assignments to compute the next values for the duty counter dutyNext and the pwm output pwmNext. Computing dutyNext is quite simple, we just increment the duty cycle counter value by one. If the current duty cycle counter rDuty is less than the value of dty then the PWM output should be on. Otherwise it should be off.

It is hopefully clear at this point that a PWM circuit has a lot in common with a counter/timer. In particular you should note that the bit size of the PWM effectively defines a counter of that size. And the dty input functions as the limit value for the counter. In many ways, the main difference between a PWM and a counter/timer lies in how we define and compute the output signal.

Simulation Result

As usual, I test my circuit designs using a Verilog Test-bench under the simulator. Here is the Verilog for the test code. We’ve seen a number of test-benches at this point so I won’t walk through the code. If you have questions please leave a comment.

module pwm_test();

    localparam T = 20;
    localparam BITS = 3;
    
    reg clk, rst;
    reg [7:0] dty;
    wire pwm;
        
    pwm #(.BITS(BITS)) uut(
        .clk(clk),
        .rst(rst),
        .dty(dty),
        .pwm(pwm)
    );

    // generate clock signal    
    always
    begin
        clk = 1'b1;
        #(T/2);
        clk = 1'b0;
        #(T/2);
    end

    // reset the circuit for first half clock cycle
    initial
    begin
        rst = 1'b1;
        #(T/2);
        rst = 1'b0;
    end
    
    initial
    begin
        // set the duty cycle to 50% (4 is half of 2 ^ 3)
        dty = 4;
        
        // wait for reset to end
        @(negedge rst);

        repeat (3000) @(negedge clk);
                        
        $stop; 
    end    
    
endmodule

Running the simulation for this project produces the following waveform output. For the test I configured the PWM circuit to have 3 bits of resolution. Which means that the duty value can very from [0..7] and thus it has a range of 8.

Looking at the dty value on the third line, you should see that it’s value is 4. This represents a 50% duty cycle for a 3-bit PWM circuit. Looking at the next line down shows the pwm output signal. Counting clk ticks, you should observe that pwm is high for 4 cycles and then low for another 4 cycles. So it appears that we do indeed have a 50% duty cycle PWM output at a frequency of about 12.5 MHz (100Mhz / 8).

PWM Waveform
PWM Waveform

And that’s it for now. As always, the source code for this project is on github.

Please Like and Share these posts on social media. And remember that if you have feedback, questions or suggestions regarding this post, please leave a comment!

Discover more from FPGA Coding

Subscribe now to keep reading and get access to the full archive.

Continue reading