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).
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!