A configurable Pulse-Width Modulation (PWM) generator implemented in SystemVerilog for the DE0-CV Cyclone V FPGA. Duty cycle and period can be programmed at runtime using onboard switches and pushbuttons.
This repository contains a portfolio-refined version of the original EE 282 design, including improved clock-domain handling, synchronized inputs, explicit edge-case behavior, and automated verification. See Background for details.
The circuit drives a PWM output whose duty cycle and period are configurable at runtime through the board's switches and pushbuttons, so the waveform can be changed without reprogramming the FPGA.
Duty cycle and period are first loaded into shadow registers using SW[7:0]. The programmed values are then transferred into active buffers when KEY2 is pressed, allowing the PWM configuration to update cleanly at the start of a new cycle rather than changing unpredictably mid-waveform.
-
Clock-enable pulse (50 MHz → 5 MHz update rate)
Instead of generating a separate derived clock, a 4-bit counter produces a one-cycle enable pulse every 10 cycles of the 50 MHz system clock. All sequential PWM logic remains in the 50 MHz clock domain and advances only when the enable pulse is asserted. -
Two-stage synchronization and edge detection
Each pushbutton input passes through a two-stage synchronizer before edge detection. This reduces metastability risk from asynchronous button inputs and produces a single control event per detected rising edge.KEY0→ loads the period shadow register fromSW[7:0]KEY1→ loads the duty shadow register fromSW[7:0]KEY2→ transfersdutyandperiodintoduty_bufandperiod_buf, resets the counter, and starts a new PWM cycle
-
Shadow registers and active buffers
User-entered values are stored indutyandperiodfirst. The active PWM values are held induty_bufandperiod_buf. This separates configuration input from live PWM operation. -
Counter and duty comparison
An 8-bit counter advances once per 5 MHz clock-enable pulse. The PWM output remains high for the programmed number of duty ticks and low for the remainder of the programmed period. -
Edge cases
period = 0disables the PWM outputduty = 0produces a 0% duty cycleduty >= periodproduces a 100% duty cycle
-
Output
pwm_outis routed to FPGA pinPIN_N16for external measurement.
| Signal | Direction | FPGA Pin | Description |
|---|---|---|---|
clk_50MHz |
Input | PIN_M9 |
50 MHz board system clock |
SW[7:0] |
Input | U13, V13, T13, T12, AA15, AB15, AA14, AA13 | Period/duty configuration value |
key0 |
Input | PIN_U7 |
Load period from SW |
key1 |
Input | PIN_W9 |
Load duty from SW |
key2 |
Input | PIN_M7 |
Apply buffered configuration and restart PWM cycle |
pwm_out |
Output | PIN_N16 |
PWM waveform output |
Target device: Cyclone V 5CEBA4F23C7 on the DE0-CV board.
- SystemVerilog
- Quartus Prime Lite 24.1
- Questa Intel FPGA Starter Edition
- DE0-CV Cyclone V FPGA
- Oscilloscope for original hardware verification
The portfolio version was verified with a self-checking SystemVerilog testbench (Simulation/PWM_Top_tb.sv). For each test case, the testbench programs a period/duty pair through the same logical interface used by the design, measures the actual HIGH pulse width and full PWM period from pwm_out, and compares the measurements against the expected values.
| Test | Period | Duty | Measured HIGH Time | Measured Period | Result |
|---|---|---|---|---|---|
| 25% duty | 100 | 25 | 5 µs | 20 µs | PASS |
| 50% duty | 100 | 50 | 10 µs | 20 µs | PASS |
| 75% duty | 100 | 75 | 15 µs | 20 µs | PASS |
| Longer period / lower frequency | 200 | 50 | 10 µs | 40 µs | PASS |
This confirms that the duty cycle scales correctly with the programmed duty/period ratio and that increasing the programmed period reduces PWM frequency as expected.
pwm_out widens as the programmed duty value increases from 25% to 50% to 75%, while the programmed period remains constant.
The Questa transcript confirms that all automated test cases passed.
Full simulation trace
The full trace includes the configuration changes and all signals used during the verification run.
FPGA-PWM-Generator/
├── README.md
├── .gitignore
├── PWM_Top.qpf
├── PWM_Top.qsf
├── PWM_Top.sv
├── Original/
│ └── PWM_Top_original.sv
├── Simulation/
│ └── PWM_Top_tb.sv
└── Images/
├── pwm_duty_cycle_comparison.png
├── self_checking_test_results.png
└── pwm_full_verification.png
PWM_Top.sv— portfolio-refined SystemVerilog implementationPWM_Top.qpf— Quartus project filePWM_Top.qsf— Cyclone V device and pin assignmentsSimulation/PWM_Top_tb.sv— self-checking SystemVerilog testbenchOriginal/PWM_Top_original.sv— original EE 282 implementationImages/— simulation waveforms and verification screenshots
This project originated in EE 282: Digital System Design with Testability Lab at Minnesota State University, Mankato.
The original design was implemented on a DE0-CV FPGA and verified through a GPIO output using an oscilloscope. The portfolio version keeps the same project goal and user-facing behavior while refining the implementation.
Key portfolio refinements include:
- replacing the separate derived 5 MHz clock with a 5 MHz clock-enable pulse while keeping sequential logic in the 50 MHz clock domain
- adding two-stage synchronization for pushbutton inputs
- making 0% and 100% duty-cycle behavior explicit
- adding a self-checking SystemVerilog testbench with measured timing verification
The portfolio version supplements the original DE0-CV hardware and oscilloscope verification with automated simulation-based verification rather than replacing the original hardware testing.


