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.

Verified by Tapeo67 generic cells at the defaultsPWM

Instantiate it

Paste into your design and connect the signals.

systemverilog
pwm #(.WIDTH(8)) u_pwm (
  .clk(clk), .rst(rst),
  .enable(1'b1), .duty(brightness),
  .out(led), .period_start()
);

Parameters

NameDefaultMeaning
WIDTH8Counter bits: the period is 2^WIDTH clock cycles and duty has this many bits.

Ports

NameDirectionWidthMeaning
clkinput1Clock; everything changes on its rising edge.
rstinput1Synchronous reset, active high.
enableinput1Output is low while this is low.
dutyinputWIDTHHigh cycles per period (0 is always low).
outoutput1The PWM signal.
period_startoutput1Pulses 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:

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.sv
// 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

← Every block in Tapeo Hub