Timing and control
PWM generator
pwm
A WIDTH-bit counter sets the period (2^WIDTH clock cycles); out is high for duty cycles of each period while enable is high. period_start pulses at the start of each period, for updating duty without glitches.
Instantiate it
Paste into your design and connect the signals.
pwm #(.WIDTH(8)) u_pwm (
.clk(clk), .rst(rst),
.enable(1'b1), .duty(brightness),
.out(led), .period_start()
);Parameters
| Name | Default | Meaning |
|---|---|---|
| WIDTH | 8 | Counter bits: the period is 2^WIDTH clock cycles and duty has this many bits. |
Ports
| Name | Direction | Width | Meaning |
|---|---|---|---|
| clk | input | 1 | Clock; everything changes on its rising edge. |
| rst | input | 1 | Synchronous reset, active high. |
| enable | input | 1 | Output is low while this is low. |
| duty | input | WIDTH | High cycles per period (0 is always low). |
| out | output | 1 | The PWM signal. |
| period_start | output | 1 | Pulses at the start of each period. |
Add it to a project
It is copied to ip/pwm/pwm.sv with a README (ip/pwm/README.md). When your design needs it, the Architect (new designs) or the Architect (changes) suggests it and asks before using it. Or add it yourself:
- In the workspace: the Tapeo Hub button in the project header, then Add to project.
- In a terminal (in the project folder):
$ tapeo hub add pwm - In VS Code: Tapeo: Add from Tapeo Hub in the Command Palette.
How it is verified
Before every release Tapeo checks this block: its self-checking testbench passes in Icarus Verilog, Verilator -Wall reports nothing, and Yosys synthesizes it (67 generic cells at the default parameters). That shows it works for the tested cases; your copy is part of your project, and your own testbench checks it in your design.
Source
// PWM generator. A WIDTH-bit counter sets the period (2**WIDTH clock cycles);
// `out` is high for `duty` cycles of each period while `enable` is high.
// `period_start` pulses at the start of each period. Tapeo Hub (verified by Tapeo).
module pwm #(
parameter integer WIDTH = 8
) (
input logic clk,
input logic rst,
input logic enable,
input logic [WIDTH-1:0] duty,
output logic out,
output logic period_start
);
logic [WIDTH-1:0] counter;
always_ff @(posedge clk) begin
if (rst) counter <= '0;
else counter <= counter + 1'b1;
end
always_ff @(posedge clk) begin
if (rst) begin
out <= 1'b0;
period_start <= 1'b0;
end else begin
out <= enable && (counter < duty);
period_start <= (counter == '0);
end
end
endmodule