Skip navigation

Tag Archives: Learn with Me

In the second part of this article, we’re going to try and get a little more interesting stuff going on with our FPGA powered LED matrix. To start, we will try to get a simple image to display on the matrix. To do this we will need a way to store the image. VHDL allows for array types so we can accomplish this by creating an eight element array of eight bit vectors, giving us one bit per pixel. We can then use each of these vectors as a mask value to AND with the col signal. This is a little confusing (at least in my head), as each col value contains a bit for each row in the column, so we AND that with the col_mask value, which is essential the pixels for a row of the display. Starting with the same setup from last time, we are adding these new signals to the architecture.


 type display_t is array(7 downto 0) of STD_LOGIC_VECTOR(7 downto 0);
 signal display : display_t := ("00000000" ,    -- A simple image to display
											"01100110" , 	-- stored in an array
											"01100110" ,
											"00000000" ,
											"00000000" ,
											"11000011" ,
											"00111100" ,
											"00000000");

 signal col_mask : STD_LOGIC_VECTOR(7 downto 0) := "00000000";		--mask value used to activate only desired pixels
 signal row_count : unsigned(2 downto 0) := "000";						--pointer for array

Note that for the array, you have do declare an array type first, then declare an array of that type. Like many languages, we are allowed to instantiate the array with some values. Notice the nice smiley face. Also note that the array is declared 7 downto 0, meaning the first element essentially is the last one in the instantiation list. Since row 0 of our display is at the bottom, this means the image should appear just as the bits appear in the code. I also added a row_count signal, declared as unsigned because it will be used numerically. This will just count up as we scan the matrix rows and serve as a pointer to our array. Also added is the col_mask signal. This signal stores the current row loaded from the array. Below is the added code to increment the row_count and retrieve the col_mask. Note that the row_count is declared as 3 bits, meaning it will automatically rollover back to zero after 7.


					-- increment row count
					row_count <= row_count + 1;
					-- get column mask from current position in array
					col_mask <= display(conv_integer(row_count));

And then of course we AND the mask to the output.


			--combine column with mask to only display selected pixels
			A(15 downto 8) <= (col AND col_mask);

Here is the complete code for this example.

--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
 type display_t is array(7 downto 0) of STD_LOGIC_VECTOR(7 downto 0);
 signal display : display_t := ("00000000" ,    -- A simple image to display
											"01100110" , 	-- stored in an array
											"01100110" ,
											"00000000" ,
											"00000000" ,
											"11000011" ,
											"00111100" ,
											"00000000");

 signal col_mask : STD_LOGIC_VECTOR(7 downto 0) := "00000000";		--mask value used to activate only desired pixels
 signal row_count : unsigned(2 downto 0) := "000";						--pointer for array

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);
					-- increment row count
					row_count <= row_count + 1;
					-- get column mask from current position in array
					col_mask <= display(conv_integer(row_count));
				end if;
			end if;
			--copy signals to outputs
			A(7 downto 0) <= row;
			--combine column with mask to only display selected pixels
			A(15 downto 8) <= (col AND col_mask);
     end if;
   end process;


end Behavioral;

This give the following output.

Now if you noticed, it looks good, but that’s not the image we intended. Its shifted up by one row! See my question on Stack Overflow for an explanation of where I went wrong. Apparently signals are updated at the end of the process, so the value is definitely updated after the value is retrieved from the array. The fix is pretty simple, I just instantiate my row_count to 1 to compensate.

 signal row_count : unsigned(2 downto 0) := "001";						--pointer for array

Now it displays correctly.

Now for one more experiment in this installment, lets create a vertical scrolling effect. In the above example where I screwed it up the first time, having the wrong value for row_count shifts the image up and down. We can use this to create our scrolling effect. My first thought was to add a second process with a delay counter and just subtract one from the row_count value. However, that produces a compile error, “this signal is connected to multiple drivers.” I’m guessing that means its illegal to set a signal from multiple processes. So, instead we will add a new signal, row_offset, same type as row_count. Then we will use the second process to increment that, and just subtract it from row_count when indexing the array.

We add these two new signals.

 signal row_offset : unsigned(2 downto 0) := "000";					--offset to control vertical scrolling
 signal scroll_delay : STD_LOGIC_VECTOR(22 downto 0) := (others => '0'); --counter for vertical scroll delay

And we add this new process to the same architecture.

	vscroll: process(clk)
		begin

			if rising_edge(clk) then
				--counter is 23 bits, will rollover at .26 seconds
				scroll_delay <= scroll_delay+1;

				if scroll_delay = 0 then
					--decrement to mis-align row count, to give a vertical scrolling effect
					row_offset <= row_offset+1;
				end if;
			end if;
		end process;

The we modify the array access like this.

					-- get column mask from current position in array
					col_mask <= display(conv_integer(row_count) - conv_integer(row_offset));

I had to use the two separate conv_integer functions as the compiler complained of trying to do the math first, then calling conv_integer. Here is the complete code.

--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
 type display_t is array(7 downto 0) of STD_LOGIC_VECTOR(7 downto 0);
 signal display : display_t := ("00000000" ,    -- A simple image to display
											"01100110" , 	-- stored in an array
											"01100110" ,
											"00000000" ,
											"00000000" ,
											"11000011" ,
											"00111100" ,
											"00000000");

 signal col_mask : STD_LOGIC_VECTOR(7 downto 0) := "00000000";		--mask value used to activate only desired pixels
 signal row_count : unsigned(2 downto 0) := "001";						--pointer for array
 signal row_offset : unsigned(2 downto 0) := "000";					--offset to control vertical scrolling
 signal scroll_delay : STD_LOGIC_VECTOR(22 downto 0) := (others => '0'); --counter for vertical scroll delay

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);
					-- increment row count
					row_count <= row_count + 1;
					-- get column mask from current position in array
					col_mask <= display(conv_integer(row_count) - conv_integer(row_offset));
				end if;
			end if;
			--copy signals to outputs
			A(7 downto 0) <= row;
			--combine column with mask to only display selected pixels
			A(15 downto 8) <= (col AND col_mask);
     end if;
   end process;

	vscroll: process(clk)
		begin

			if rising_edge(clk) then
				--counter is 23 bits, will rollover at .26 seconds
				scroll_delay <= scroll_delay+1;

				if scroll_delay = 0 then
					--decrement to mis-align row count, to give a vertical scrolling effect
					row_offset <= row_offset+1;
				end if;
			end if;
		end process;


end Behavioral;



And here is the scrolling effect in action.

Next time we will see about making a simple animation with several frames. See you then!

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.