Pulse-width modulation and binary-coded modulation can produce the same average LED brightness, but they organize time differently. That difference affects comparators, schedulers, memory access, refresh rate, camera behavior, and how easily a design scales from one LED to a matrix.
The short answer
Use counter-comparator PWM for independent outputs and simple channels. Use BCM when many pixels share a bitplane and row scheduler. Neither method creates extra brightness resolution for free: both must fit the same clock, refresh, settling, and blanking budget.
| Design question | Conventional PWM | BCM / bitplane PWM |
|---|---|---|
| Core operation | Compare each level with a counter | Display each stored bit for its binary weight |
| Natural unit | One independently timed channel | A row or bank of pixels |
| Per-channel logic | Comparator plus register | Bit selection; one shared scheduler |
| Memory access | Read complete level words | Read one bitplane from many level words |
| Full-scale convention | Needs an explicit saturation choice | Code 255 naturally lights 255 of 255 slots |
| Timing risk | Runt pulses after mid-period updates | Short LSB planes and mixed-frame bitplanes |
Why the average brightness is equivalent
An 8-bit PWM counter creates 256 count positions. Ignoring the special full-on convention, a code of 173 enables the output for 173 of those positions. An 8-bit BCM cycle contains weighted planes of 1, 2, 4, 8, 16, 32, 64, and 128 base slots. The binary value 173 is 10101101, so the active weights are 128 + 32 + 8 + 4 + 1 = 173. The ordering changes; the integral does not.
Conventional PWM architecture
The usual FPGA implementation shares one free-running counter among multiple channels and gives each channel a comparator. For three RGB outputs this is exceptionally small and clear. Each level word remains available continuously, and changing the carrier frequency is a matter of counter width or clock-enable rate.
assign red_pwm = (counter < red_level);
assign green_pwm = (counter < green_level);
assign blue_pwm = (counter < blue_level);
Scale that structure to thousands of physical outputs and the number of comparators and pins becomes the real limitation. Multiplexed panels already require a row transaction, so a per-output continuous comparator is no longer the natural abstraction.
BCM architecture
BCM selects one bit from every active pixel and holds the resulting plane for its binary weight. The framebuffer supplies data while a shared scheduler owns plane order, latch timing, row address, output enable, and dwell. This is why BCM appears frequently in FPGA matrix controllers: adding pixels expands memory and shifting work without duplicating a full PWM counter for every channel.
The least-significant plane is also the hardest. At eight bits it receives only 1/255 of the available illuminated cycle; at twelve bits it receives 1/4095. After row multiplexing and blanking, that slot may become shorter than the panel or driver can settle. Effective color depth is limited by real timing, not merely by framebuffer width.
Update boundaries matter in both designs
Writing a PWM level in the middle of a period can create one unusually long or short pulse. Updating BCM memory while a frame is being scanned can combine low planes from one image with high planes from another. The robust solution is the same at two scales: separate requested state from active state, and transfer ownership at a known boundary.
- Latch PWM brightness when the counter wraps.
- Swap BCM framebuffers after the final plane and final row.
- Cross clock domains with a handshake, toggle, or asynchronous FIFO rather than synchronizing a multi-bit pixel bus one bit at a time.
Camera behavior and plane ordering
Two waveforms with the same average can photograph differently. Conventional edge-aligned PWM clusters one on interval. Basic BCM clusters long light or dark intervals according to plane order, especially around the most-significant bit. Reordering or splitting large planes can distribute energy across the frame and reduce visible bands, but it cannot repair an insufficient refresh budget. Measure with the target exposure time and rolling-shutter camera.
Decision checklist
- One status LED or a few RGB channels: choose conventional PWM.
- Many parallel LED outputs with enough comparators: shared-counter PWM remains simple.
- HUB75 or another row-multiplexed display: choose a bitplane scheduler such as BCM.
- External grayscale driver: send intensity words and let the device perform its specified PWM.
- Camera-critical lighting: prototype both timing patterns and measure the actual optical output.
Test the invariant, not the picture
A self-checking PWM test counts high clocks over one period. A BCM test counts illuminated base slots over all bitplanes. Both should equal the requested code under the chosen full-scale convention. The downloadable examples implement both tests and make the architectural difference directly observable.
Continue the series
Build each implementation in the parameterized PWM tutorial and the 8-bit BCM tutorial. For matrix timing, continue with the HUB75 row-scanning guide.
FPGA LED controller tutorial series
This article is part of the FPGA LED Controller Tutorials learning path. Continue with PWM vs. BCM, the HUB75 timing calculator, or SystemVerilog verification.


