Skip to content

FPGA + LED Matrix, Part 1

Today, I’m learning about the magical and mystical world of FPGAs. FPGAs can be a challenging subject to get into for some of us, especially those of us who spent their days in college learning about programming computers, and not electrical engineering. Like many in this subject, I really had no idea where to start. So, I’ve setup a simple experiment using everyone’s favorite low cost FPGA dev board, the Papilio, a standard bread board, and a cheap red LED matrix from EBay.

If you go to the Papilio website like I did to start gathering knowledge, they seem to want to push you towards starting by loading an implementation of an AVR soft processor. Personally, I think it kinda defeats the purpose of trying to learn about FPGAs and VHDL, if you just load up a pre-built processor and go back to writing C code. Other than that, if you look around you will find nice step by step instructions for setting up a simple project. This includes the links and instructions for installing the Xilinx tools required to synthesize the VHDL into a bitstream that can be loaded on Papilio’s Xilinx Spartan 3 FPGA.

Once you’ve finished with setting up your first project from the tutorial on the Papilio site, let’s setup the bread board for our experiments. The idea here is simple, using 16 of the IO pins from the FPGA, we will drive the 8 rows and 8 columns of the LED matrix. With the IO pins set to output, it should be as simple as putting one column high, and one row low, thus creating a current through the diode in the correct direction. The diode action prevents the low column outputs from sinking current from the high rows. Now, a little bit of care is noted here, as I don’t know the maximum current source and sink rating of the IO pins, or if they contain any short kind of protection. Potentially, as I can only assume, connecting a high output directly to a low output would create a short circuit and damage your FPGA. Also, I should assume using too low of a value resistor could potentially draw too much current and damage the IO pins as well, but I could be completely wrong about that as well. If you followed the tutorial above to setup your project, all the output pins should be configured as LVCMOS33, which means a logic 1 will put 3.3 volts on the pin. In my circuit, I have 330 ohm resistor, so taking V = IR, that gives 10mA. That should be plenty to power the LED and doesn’t seem like it should be too much for the FPGA IO pins to handle. Again, I couldn’t find the spec for that, so its just a guess. You could use a higher value resistor as well, I just had those 330s in my parts bin. A 1K would probably work as well.

Here is the circuit. Basically, we have all the rows of the LED matrix attached to IO pins A0 to A7 on the Papilio board. The pinout and polarity of the matrix I figured out mostly from other sources online using similar parts, and a little probing with the multimeter. Im not sure why, but the most trouble I had with understanding this matrix is that the lower left hand corner in the picture is row 1, column 1, with the rows and columns increasing across and up. The actuall FPGA pin mapping to the logical Papilio ports is defined in that .UCF file you added in the above tutorial.


NET A(0)     LOC="P18"  | IOSTANDARD=LVCMOS33;                             # A0
NET A(1)     LOC="P23"  | IOSTANDARD=LVCMOS33;                             # A1
NET A(2)     LOC="P26"  | IOSTANDARD=LVCMOS33;                             # A2
NET A(3)     LOC="P33"  | IOSTANDARD=LVCMOS33;                             # A3
NET A(4)     LOC="P35"  | IOSTANDARD=LVCMOS33;                             # A4
NET A(5)     LOC="P40"  | IOSTANDARD=LVCMOS33;                             # A5
NET A(6)     LOC="P53"  | IOSTANDARD=LVCMOS33;                             # A6
NET A(7)     LOC="P57"  | IOSTANDARD=LVCMOS33;                             # A7
NET A(8)     LOC="P60"  | IOSTANDARD=LVCMOS33;                             # A8
NET A(9)     LOC="P62"  | IOSTANDARD=LVCMOS33;                             # A9
NET A(10)    LOC="P65"  | IOSTANDARD=LVCMOS33;                             # A10
NET A(11)    LOC="P67"  | IOSTANDARD=LVCMOS33;                             # A11
NET A(12)    LOC="P70"  | IOSTANDARD=LVCMOS33;                             # A12
NET A(13)    LOC="P79"  | IOSTANDARD=LVCMOS33;                             # A13
NET A(14)    LOC="P84"  | IOSTANDARD=LVCMOS33;                             # A14
NET A(15)    LOC="P86"  | IOSTANDARD=LVCMOS33;                             # A15

To create an image, we can turn the LEDs on and off, one at a time, really fast, so it makes it look like an image appears. This eliminates the need to have 64 IO lines to control each led. So the first experiment we can do is to generate a slow scan of all the LEDs to verify that everything is hooked up correctly and our VHDL code works. After a bit of experimentation, modifying the code from the Papilio tutorial, and a little Googling, I came up with this.


--Standard includes
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

--Entity definiton
entity First is
    Port ( A : out  STD_LOGIC_VECTOR (16 downto 0); -- maps to pins 0 to 16 of port A
			  clk : in STD_LOGIC); --clock signal
end First;

--architecture definition
architecture Behavioral of First is
 --Interial signals
 signal counter : STD_LOGIC_VECTOR(22 downto 0) := (others => '0'); --counter to control state and delay
 signal row : STD_LOGIC_VECTOR(7 downto 0) := "11111110";				--signal for LED rows
 signal col : STD_LOGIC_VECTOR(7 downto 0) := "00000001";				--signal for LED columns
begin
	--Process Definition
	count: process(clk)
   begin
		-- triggers action on rising edge of clock signal
     if rising_edge(clk) then
	    --increment counter
       counter <= counter+1;
		 --clock period is 31.25ns, counter is 22 bits, should roll over every 260ms
			--trigger each time counter rolls over back to zero
			if counter = 0 then
				-- Left Rotate col
				col <= col(6 downto 0) & col(7);
				-- Trigger when last column becomes active
				if col = "10000000" then
					-- Left rotate row
					row <= row(6 downto 0) & row(7);
				end if;
			end if;
			--copy signals to outputs
			A(7 downto 0) <= row;
			A(15 downto 8 ) <= col;
     end if;
   end process;


end Behavioral;

I’ve commented the code to try and make it easy to follow. The important thing to remember is that VHDL code defines hardware, it DOES NOT RUN. As a programmer this is a difficult concept to grasp, as I am so ingrained in the idea of procedural code, running instructions one at a time. For the most part, everything in VHDL happens simultaneously, with the IF statements basically defining when each block of stuff can happen. For our current experiment, we want things to run in a procedural manner, lighting one LED at a time in order. To do this, we have to implement a primitive state machine, using a counter to create a delay and the current row and col values as the state, triggering off of the clk signal. Staring at the beginning, we have our counter signal defined as 22 down to 0 (23 bits), defaulted to all zeros. The size of the counter was chosen so that at our clock period of 31.25ns, the counter would roll over back to zero in about 260ms, giving a nice slow visible scan of the martix. Row is an 8 bit value we are defaulting to “11111110” and col is an 8 bit value defaulting to “00000001”. As you see in the defaults, row 1 and column 1 are defaulted to be on. To create the scan, these two values will be rotated, so that the row that is set to 0 and the column that is set to 1 will light the correct LED. The first if statement detects and activates on the rising edge of the clock signal. Again, not a procedural language, when we hit the rising edge of the clock, everything in the block will be run. Two basic assignments happen in this block, first we add one to the counter signal, second we copy the row and col values to the outputs of port A. The nested if activates when the counter rolls over and hits zero again. When that happens we rotate the col signal to the left. We do that by taking the right most 6 bits and appending the 7th bit to the end of it. Another nested if detects when col reaches the value “10000000”, meaning its on the last column, we rotate the row signal.

If you generate the bit file for the code above and load it into your board you should see the LED scroll through each row and column on the display. After we’ve verified that it in fact scans the matrix in the correct direction and that all the LEDs light up, we can just decrease the size of the counter signal to increase the scroll speed. If we take it down to 9 bits, it should scan through the entire matrix in less than 1ms. This will give the impression that all the LEDs are on at once.


--Standard includes
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

--Entity definiton
entity First is
    Port ( A : out  STD_LOGIC_VECTOR (16 downto 0); -- maps to pins 0 to 16 of port A
			  clk : in STD_LOGIC); --clock signal
end First;

--architecture definition
architecture Behavioral of First is
 --Interial signals
 signal counter : STD_LOGIC_VECTOR(8 downto 0) := (others => '0'); --counter to control state and delay
 signal row : STD_LOGIC_VECTOR(7 downto 0) := "11111110";				--signal for LED rows
 signal col : STD_LOGIC_VECTOR(7 downto 0) := "00000001";				--signal for LED columns
begin
	--Process Definition
	count: process(clk)
   begin
		-- triggers action on rising edge of clock signal
     if rising_edge(clk) then
	    --increment counter
       counter <= counter+1;
		 --clock period is 31.25ns, counter is 9 bits, should scan whole matrix in < 1ms
			--trigger each time counter rolls over back to zero
			if counter = 0 then
				-- Left Rotate col
				col <= col(6 downto 0) & col(7);
				-- Trigger when last column becomes active
				if col = "10000000" then
					-- Left rotate row
					row <= row(6 downto 0) & row(7);
				end if;
			end if;
			--copy signals to outputs
			A(7 downto 0) <= row;
			A(15 downto 8 ) <= col;
     end if;
   end process;


end Behavioral;

Again, all that changed is the counter size, but running it now shows a fully lit matrix.

OK! We’ve learned a bit, and managed to generate a signal that can drive an LED matrix. Next time, I’ll be attempting to modify this code to read some simple images from memory and display them on the matrix.

2 thoughts on “FPGA + LED Matrix, Part 1”

  1. Tnx for these exelent tutorials…pls go ahead about how to driving rgb led panel dotmatrix,for example 16*32 rgb panels…tnx

  2. Hello man, I’m developing a digital systems project with an 8×8 led matrix, I followed your code as a basis to form a Mario Bros, but I need the mario led to move, could you give me some idea?

Leave a Reply

Your email address will not be published. Required fields are marked *

 characters available