Topic 24 · Digital Electronics

Pulse Width Modulation (PWM)

PWM converts digital control into an analog-equivalent output by rapidly switching between HIGH and LOW. Master duty cycle, frequency, resolution, and Verilog counter-based PWM — with a live interactive waveform and calculator.

PWM Parameters

Duty Cycle
D = (Ton / Tperiod) × 100%
Fraction of the period the signal is HIGH. 0% = always LOW, 100% = always HIGH.
Average Voltage
Vavg = D × Vcc
A 75% duty cycle on 3.3V gives 0.75 × 3.3 = 2.475V average.
PWM Frequency
fpwm = fclk / 2N
N-bit counter at fclk. 8-bit at 25MHz → 97.66kHz PWM.
Resolution
Steps = 2N
8-bit = 256 steps (0.39%/step). 16-bit = 65536 steps (0.0015%/step).

How PWM Works in Digital Logic

A free-running N-bit counter increments on every clock edge and wraps from 2N−1 back to 0. The PWM output is a simple comparator: HIGH when counter < threshold, LOW otherwise.

Threshold (8-bit)Duty CycleVavg @ 3.3VUse Case
00%0.00VOff
3212.5%0.41VDim LED
6425%0.83VQuarter power
12850%1.65VHalf power / servo center
19275%2.48VThree-quarter power
255~100%3.30VFull on

PWM Waveform Visualizer

Duty Cycle 50%
Cycles shown 4
Duty Cycle
50.0%
Vavg @ 3.3V
1.650V
Ton (8-bit steps)
128 / 256
8-bit Threshold
128

Where PWM Is Used

ApplicationPWM FrequencyDuty Cycle EffectKey Detail
LED Brightness1–20kHzHigher duty = brighterAbove ~50Hz the eye sees smooth brightness, not flicker
DC Motor Speed1–100kHzHigher duty = fasterInductance of motor smooths current; back-EMF must be considered
Servo Motor50Hz (20ms frame)Pulse width → angle1ms = 0°, 1.5ms = 90°, 2ms = 180°; duty cycle is tiny (~5–10%)
DAC ApproximationHigh (audio: >44kHz)Duty cycle = output levelRC low-pass filter after PWM output; ripple = supply noise
SMPS (Buck/Boost)100kHz–1MHzDuty cycle sets VoutBuck: Vout = D × Vin; feedback loop controls D
Class-D Amplifier>400kHzAudio waveform encodedHigh efficiency (90%+) — output filter reconstructs audio

PWM Generator in Verilog

Basic 8-bit PWM Generator

// 8-bit counter-based PWM generator
// pwm_out = HIGH when counter < duty_cycle
module pwm_gen #(
  parameter N = 8          // counter bit-width
)(
  input  wire        clk,
  input  wire        rst_n,
  input  wire [N-1:0] duty,   // 0=0%, 255=~100% (8-bit)
  output wire        pwm_out
);
  reg [N-1:0] cnt;

  always @(posedge clk or negedge rst_n) begin
    if (!rst_n) cnt <= '0;
    else        cnt <= cnt + 1'b1;  // wraps at 2^N
  end

  assign pwm_out = (cnt < duty);
endmodule

Variable-Frequency PWM (Period Register)

// PWM with configurable period AND duty cycle
module pwm_vf #(
  parameter N = 16
)(
  input  wire        clk,
  input  wire        rst_n,
  input  wire [N-1:0] period,  // clock cycles per PWM period
  input  wire [N-1:0] duty,    // HIGH clock cycles (must be <= period)
  output reg         pwm_out
);
  reg [N-1:0] cnt;

  always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      cnt     <= '0;
      pwm_out <= 1'b0;
    end else begin
      if (cnt >= period - 1) cnt <= '0;
      else                    cnt <= cnt + 1;
      pwm_out <= (cnt < duty);
    end
  end
endmodule

Servo PWM Controller (50Hz, 1–2ms pulse)

// Servo PWM: 50Hz frame, 1ms–2ms pulse for 0°–180°
// Assumes 50MHz clock. 50Hz → 20ms period = 1_000_000 clk cycles
module servo_pwm (
  input  wire        clk,       // 50MHz
  input  wire        rst_n,
  input  wire [7:0]  angle,     // 0–180 degrees
  output reg         pwm_out
);
  // At 50MHz: 1ms = 50000 clk, 2ms = 100000 clk
  localparam PERIOD  = 1_000_000;  // 20ms at 50MHz
  localparam PULSE_MIN = 50_000;   // 1ms
  localparam PULSE_MAX = 100_000;  // 2ms

  reg [19:0] cnt;
  reg [16:0] pulse_w;

  always @(*) begin
    // Linear interpolation: pulse_w = 50000 + angle*(50000/180)
    pulse_w = PULSE_MIN + (angle * 277);  // ~50000/180 ≈ 277
  end

  always @(posedge clk or negedge rst_n) begin
    if (!rst_n) cnt <= 0;
    else if (cnt >= PERIOD-1) cnt <= 0;
    else cnt <= cnt + 1;
    pwm_out <= (cnt < pulse_w);
  end
endmodule

PWM Fan Controller with Temperature Lookup

// 8-bit PWM fan controller: hotter = faster fan
module fan_ctrl (
  input  wire       clk,
  input  wire       rst_n,
  input  wire [7:0] temp_c,   // 0–100°C
  output wire       pwm_out
);
  reg [7:0] duty;
  reg [7:0] cnt;

  always @(*) begin
    if      (temp_c < 40) duty = 8'd0;    // fan off below 40°C
    else if (temp_c < 50) duty = 8'd64;   // 25% — quiet
    else if (temp_c < 65) duty = 8'd128;  // 50% — moderate
    else if (temp_c < 80) duty = 8'd192;  // 75% — warm
    else                   duty = 8'd255;  // 100% — hot
  end

  always @(posedge clk or negedge rst_n) begin
    if (!rst_n) cnt <= 0;
    else        cnt <= cnt + 1;
  end

  assign pwm_out = (cnt < duty);
endmodule

Resolution vs PWM Frequency

For a fixed clock frequency, increasing the counter bit-width (resolution) reduces the PWM output frequency. To keep both high, you need a faster clock.

Clock8-bit (256 steps)10-bit (1024 steps)16-bit (65536 steps)
1 MHz3.9 kHz977 Hz15.3 Hz
25 MHz97.7 kHz24.4 kHz381 Hz
100 MHz390.6 kHz97.7 kHz1.53 kHz
500 MHz1.95 MHz488 kHz7.63 kHz

Rule of thumb: Choose bit-width based on the smoothness required by the load. For LED dimming, 8 bits is usually enough (the human eye can't distinguish more than ~256 brightness levels at typical frequencies). For precision servo or audio DAC, use 12–16 bits with a high-speed clock.

Frequently Asked Questions

What is PWM and how does it work?

PWM (Pulse Width Modulation) generates an analog-equivalent output using a digital signal that rapidly switches HIGH/LOW. The load (LED, motor) responds to the average voltage, which equals duty cycle × Vcc.

What is duty cycle?

Duty cycle = (Ton / Tperiod) × 100%. A 75% duty cycle on 3.3V gives 2.475V average. In an 8-bit PWM, setting threshold=192 out of 256 gives 75% duty.

How does counter-based PWM work?

A free-running N-bit counter increments each clock cycle and wraps at 2N. The output is HIGH when counter < threshold, LOW otherwise. Threshold = duty cycle × 2N.

What PWM frequency is needed for LEDs vs motors?

LEDs: above ~50Hz eliminates visible flicker; 1–20kHz is typical. DC motors: 1–50kHz avoids audible humming and reduces EMI. Servos: exactly 50Hz (20ms frame). SMPS: 100kHz–1MHz for compact magnetics.

How does PWM resolution relate to bit-width?

An N-bit counter gives 2N duty cycle steps. 8-bit = 256 steps (0.39% granularity). 10-bit = 1024 steps. More bits → finer control but lower PWM frequency for the same clock. Tradeoff: fpwm = fclk / 2N.

How do servos use PWM?

Servos expect a 50Hz (20ms) frame with a pulse of 1–2ms. The pulse width encodes angle: 1ms = 0°, 1.5ms = 90°, 2ms = 180°. The duty cycle is only 5–10%, but the absolute pulse time is what matters, not the percentage.

How do you write a PWM generator in Verilog?

Declare an N-bit register cnt that increments on every posedge clk. Assign pwm_out = (cnt < duty). That's it — the counter wraps naturally, creating the period; the comparator creates the pulse width.