FONT SIZE : AAA
It is surprisingly easy to implement a PWM module. You use a counter and compare the value of duty with the counter. If the counter is below the duty value, the output is high, and as soon as this isn’t true, the output is set low.
You can find the code for this module along with a module to test it in the downloads for this book (see Chapter 2). The project is called “ch07_pwm.”
The PWM module is called just “pwm” and has two inputs, pwm_clk and duty. Its single output, PWM_PIN, will be connected to the GPIO pin on which you want the PWM output:
module pwm(
input pwm_clk,
input [7:0] duty,
output reg PWM_PIN
);
The pwm_clk input is not the same as the system-wide clock of the FPGA. Generally, the PWM frequency is a lot lower than the 12 to 50 MHz of this book’s example boards. A common PWM frequency range is 500 Hz to several kilohertz. This PWM module uses an 8-bit counter, so the frequency at pwm_clk needs to be 256 times the desired PWM frequency. When you look at testing the module, you will see how you can use a prescaler counter to derive a lower clock frequency to use for the PWM module.
The remainder of the code for the PWM module is as follows:
reg [7:0] count = 0;
always @(posedge pwm_clk)
begin
count <= count + 1;
PWM_PIN <= (count < duty);
end
endmodule
The 8-bit counter count is incremented with the positive edge of the pwm_clk signal. Output PWM_PIN is then set to be the result of (count < duty). In other words, if count is less than duty, PWM_PIN will be 1; otherwise, it will be 0.
In this case, the module to test the PWM module so that you can see that it is working is more complicated than the pwm module itself. It will change the brightness of a LED (D1 on the Elbert 2, LED 0 on the Mojo IO Shield, and LED0 on the Papilio with LogicStart MegaWing) when you press the “Up” and “Down” buttons (SW1 and SW6 on the Elbert 2).
The UCF for Mojo is as follows:
# User Constraint File for PWM on Mojo with IO Shield
NET "CLK" LOC = P56;
# Switches
NET "switch_up" LOC = "P137" | PULLDOWN;
NET "switch_dn" LOC = "P139" | PULLDOWN;
# Output
NET "PWM_PIN" LOC = P97; # LED1
The tester module has inputs of the system clock (CLK) and two switch pins to vary the brightness of the LED up and down. The output will link to the LED whose brightness is to be changed. The code for the tester module is contained in the file pwm_tester.v:
module pwm_tester(
input CLK,
input switch_up,
input switch_dn,
output PWM_PIN
);
Wires and debouncer modules are defined in what should now be a familiar way:
wire s_up, s_dn;
debouncer d1(.CLK (CLK), .switch_input (switch_up), .trans_up (s_up));
debouncer d2(.CLK (CLK), .switch_input (switch_dn), .trans_up (s_dn));
A register duty is used to keep a value of duty cycle (0 to 255) that will be set using the “Up” and “Down” push switches.
The prescaler register is a 7-bit counter that will be used to divide the system clock frequency (50 MHz on the Mojo, 12 MHz on the Elbert 2, and 32 MHz on Papilio One) by 128. With a further division of 256 from the 8-bit counter in the PWM module, this will result in PWM frequencies of:
• Elbert 2: 12 MHz/128/256 = 366 Hz.
• Papilio One: 32 MHz/128/256 = 975 Hz
• Mojo: 50 MHz/128/256 = 1.53 kHz
The pwm module instance uses the output of bit 6 of the prescaler to provide the pwm_clk input to the PWM module:
reg [7:0] duty = 0;
reg [6:0] prescaler = 0; / CLK freq / 128 / 256 = 1.5kHz
pwm p(.pwm_clk (prescaler[6]), .duty (duty), .PWM_PIN (PWM_PIN));
The always block of the tester module increments the prescaler and then checks for any switch presses. The “Up” and “Down” switch presses increase or decrease the value of duty by 5:
always @(posedge CLK)
begin
prescaler <= prescaler + 1;
if (s_up)
begin
duty <= duty + 5;
end
if (s_dn)
begin
duty <= duty - 5;
end
end
endmodule
To keep the Verilog simple, there is no test to see if the value of duty exceeds 255. If it does, it will simply wrap around because only 8 bits are used for the duty, allowing a maximum range of 255.
Generate the bit file for the project, and load it onto your board. You will see how the “Up” and “Down” buttons increase and decrease the brightness of the LED. If you want to get a feel for how PWM works, try slowing the whole thing down by a factor of approximately 1000. To do this, change lines 13 and 14 of pwm_tester.v to add another 10 stages to the prescaler counter. The changes are highlighted in boldface here:
reg [16:0] prescaler = 0;
pwm p(.pwm_clk (prescaler[16]), .duty (duty), .PWM_PIN (PWM_PIN));
Manufacturer:Xilinx
Product Categories: FPGAs
Lifecycle:Obsolete -
RoHS: No RoHS
Manufacturer:Xilinx
Product Categories: FPGAs
Lifecycle:Active Active
RoHS: No RoHS
Manufacturer:Xilinx
Product Categories: FPGAs
Lifecycle:Active Active
RoHS:
Manufacturer:Xilinx
Product Categories: FPGAs
Lifecycle:Active Active
RoHS: No RoHS
Manufacturer:Xilinx
Product Categories: FPGAs (Field Programmable Gate Array)
Lifecycle:Active Active
RoHS: No RoHS
Support