Glitch Free PWM with Shadow Register
Motor controllers and LED drivers rely on Pulse Width Modulation (PWM) to regulate power delivery. If a host processor updates the duty cycle configuration register exactly when the PWM counter is in the middle of a period, the comparator can produce a runt pulse or an unpredictable glitch, potentially damaging downstream power electronics.
A glitch-free PWM generator solves this by buffering the requested duty cycle in a shadow register. The module maintains a 4-bit free-running counter that increments every clock cycle. When the host provides a new duty cycle alongside a valid signal, the shadow register captures this value immediately. However, the active comparison register driving the PWM output only updates from the shadow register at the exact moment the counter rolls over from 15 to 0. This ensures the duty cycle only changes at the boundaries of the PWM period.
The module operates on the positive edge of clk and uses an asynchronous active-low reset rst_n. On reset, the counter, shadow register, and active register all clear to 0, resulting in a 0 output. The pwm_out signal is evaluated combinationally and is driven high strictly when the counter is less than the active duty cycle register.
Cycle 1: rst_n=0 → cnt=0, duty_shadow=0, duty_active=0, pwm_out=0 Cycle 2: rst_n=1, duty_valid=1, duty_in=3 → duty_shadow=3, duty_active=0, cnt=1, pwm_out=0 Cycle 3: duty_valid=0 → cnt=2, duty_shadow=3, duty_active=0, pwm_out=0 ... Cycle 16: cnt=15, duty_shadow=3, duty_active=0 → pwm_out=0 Cycle 17: cnt=0, duty_shadow=3, duty_active=3 → pwm_out=1 Cycle 18: cnt=1, duty_shadow=3, duty_active=3 → pwm_out=1 Cycle 19: cnt=2, duty_shadow=3, duty_active=3 → pwm_out=1 Cycle 20: cnt=3, duty_shadow=3, duty_active=3 → pwm_out=0
{ "signal": [
{ "name": "clk", "wave": "p...|....." },
{ "name": "rst_n", "wave": "01..|....." },
{ "name": "duty_valid", "wave": "010.|....." },
{ "name": "duty_in", "wave": "x3x.|....." },
{ "name": "cnt", "wave": "=.=.|..=..", "data": ["0", "1", "15", "0", "1", "2"] },
{ "name": "duty_shadow", "wave": "0.3.|....." },
{ "name": "duty_active", "wave": "0...|..3.." },
{ "name": "pwm_out", "wave": "0...|..1.." }
], "head": { "text": "Duty cycle updates at counter rollover" } }| Signal | Direction | Width | Description | |--------|-----------|-------|-------------| | clk | input | 1 | Positive-edge triggered clock | | rst_n | input | 1 | Asynchronous active-low reset; all internal registers go to 0 | | duty_in | input | 4 | Requested duty cycle value | | duty_valid | input | 1 | When 1, captures duty_in into the shadow register on posedge clk | | pwm_out | output | 1 | Combinational output; 1 when cnt < duty_active, else 0 |
Constraints
- The clock is positive-edge triggered and reset is asynchronous active-low.
- The internal counter must be exactly 4 bits wide, rolling over from 15 to 0.
- The active duty cycle register must only update from the shadow register on the exact clock edge where the counter transitions from 15 to 0.
pwm_outmust be evaluated combinationally ascnt < duty_active.- If
duty_validand reset are asserted simultaneously, reset takes priority.
Topics
Solve this problem
Write the module in Verilog, SystemVerilog or VHDL. Your submission is compiled and simulated against a real testbench — you get the waveform back, not a stored answer.
This problem is part of Codiode Pro. The statement above is free to read.
The circuit builder and code editor need a desktop screen. On a phone, read the problem here and open it on a laptop to solve.