LED display bugs are unusually visual: a single missed clock becomes a shifted column, an unsafe latch becomes a flash, and a clock-domain error becomes a torn frame. The fastest path to a fix is to translate the visible symptom into a timing hypothesis, then prove or reject it with a test pattern, assertion, oscilloscope, or logic analyzer.
Symptom-to-cause map
VHDL-2008 reference implementation
This article uses synthesizable VHDL-2008. The complete tested VHDL bundle is linked below; adapt clock constraints, I/O standards, and timing parameters to the target board and panel.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity hub75_row_scanner is
generic (COLUMNS : positive := 64; ROW_GROUPS : positive := 16);
port (
clk : in std_logic;
rst : in std_logic;
dwell_done: in std_logic;
pixel_bits: in std_logic_vector(5 downto 0);
hub_clk : out std_logic;
lat : out std_logic;
oe_n : out std_logic;
row_addr : out natural range 0 to ROW_GROUPS - 1;
rgb : out std_logic_vector(5 downto 0)
);
end entity;
architecture rtl of hub75_row_scanner is
type state_t is (blank, shift_low, shift_high, latch, set_row, display);
signal state : state_t := blank;
signal col : natural range 0 to COLUMNS - 1 := 0;
signal row : natural range 0 to ROW_GROUPS - 1 := 0;
begin
process (clk)
begin
if rising_edge(clk) then
if rst = '1' then
state <= blank; col <= 0; row <= 0;
else
case state is
when blank => col <= 0; state <= shift_low;
when shift_low => state <= shift_high;
when shift_high => if col = COLUMNS - 1 then state <= latch; else col <= col + 1; state <= shift_low; end if;
when latch => state <= set_row;
when set_row => state <= display;
when display => if dwell_done = '1' then if row = ROW_GROUPS - 1 then row <= 0; else row <= row + 1; end if; state <= blank; end if;
end case;
end if;
end if;
end process;
hub_clk <= '1' when state = shift_high else '0';
lat <= '1' when state = latch else '0';
oe_n <= '0' when state = display else '1';
row_addr <= row;
rgb <= pixel_bits when state = shift_low else (others => '0');
end architecture;
Download the VHDL-2008 FPGA LED controller examples
| Symptom | Likely causes |
|---|---|
| Whole display flickers | Low refresh rate, irregular frame timing, power droop |
| Faint previous row | OE active during latch or address change, insufficient blanking |
| Image shifted one column | Wrong clock count, block-RAM latency, data changed on wrong edge |
| Red and blue exchanged | Connector or bit-order mismatch |
| Horizontal tear | Framebuffer changed before frame boundary |
| Low-level shimmer | LSB dwell too short, jitter, unsynchronized brightness update |
| Camera bands | Refresh interaction with exposure or rolling shutter |
Use diagnostic patterns
- Solid red, green, and blue reveal channel mapping.
- Alternating columns reveal shift direction and off-by-one clocks.
- A single moving pixel reveals row/column addressing.
- One bitplane at a time reveals dwell-weight errors.
- A gray ramp reveals gamma, missing low bits, and framebuffer format mistakes.
Probe the timing contract
Capture CLK, LAT, OE, row address, and at least one color data line. Trigger on LAT. Count column clocks between latches, measure how long OE remains active for each plane, and confirm the row address is stable before light is enabled. A passthrough adapter or spare test points make this much easier than probing a ribbon cable.
Turn expectations into assertions
Separate logic faults from electrical faults
If the FPGA waveform is correct but the panel is not, inspect voltage levels, ground bounce, cable length, edge rate, current-supply wiring, and connector orientation. Reduce brightness and display one channel; if the failure changes with total current, power integrity is a stronger suspect than bitplane math.
A disciplined debug order
- Verify the clock and reset.
- Verify active polarities with a static output.
- Count shifts and check data setup around the external clock.
- Check blank-latch-address-enable ordering.
- Measure row and frame rates.
- Enable one bitplane, then add planes.
- Add framebuffer swaps only after static frames are clean.
- Finally test maximum brightness, cable length, and camera behavior.
Change one variable at a time and save known-good captures. LED controllers become manageable when every visible artifact is tied back to a small timing contract that can be measured.
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 VHDL-2008 verification.
Leave a Reply