Jump to content
  • 0

Display image using VGA from block RAM


khaledismail

Question

Hi everyone,

I am trying to display image pixels stored in block RAM .coe file though VGA on the board BASYS 3.

Description of what I have done so far, Passed this image to MATLAB to create a .coe file:

Kaleidoscope_300_300.jpg.4e9df6d645c9473cfc976594eb41523e.jpg

The image is a 300*300 pixels. The .coe file stores each pixel RGB data scanning from left to right horizontally then moves to the second row, imitating how the VGA code scans the screen. So the .coe file is 300 pixel* 300 pixel=90000 lines long where each line is 12 bits, Red=4 bits followed by Green=4 bits followed by Blue=4 bits.

 

This is a VHDL code to display the image. summary of code functionality: Divide main 100 MHz clock by four to get 25 MHz clock (required pixel frequency) , establish VGA synchronization and display the image on a 640 x 480 resolution @ 60 Hz. The code is shown below:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use IEEE.NUMERIC_STD.ALL;

entity vga_driver is
    Port ( clk : in  STD_LOGIC; --100 MHz main clock.
           Hsync : out  STD_LOGIC;
           Vsync : out  STD_LOGIC;
           R,G,B : out  STD_LOGIC_VECTOR (3 downto 0));
end vga_driver;

architecture Behavioral of vga_driver is

signal DFlipFlopOut1: STD_LOGIC;
signal DFlipFlopOut1_NOT: STD_LOGIC;
signal ClockDiv4: STD_LOGIC; -- 25 MHz Clock
signal ClockDiv4_NOT: STD_LOGIC;

constant picture_size : Integer:=90000; -- 300 Pixels* 300 Pixels picture= 90000 Pixels

--Signals for Block RAM
signal wea : STD_LOGIC_VECTOR(0 DOWNTO 0):="0";
signal addra : STD_LOGIC_VECTOR(16 DOWNTO 0):=(others=>'0');
signal dina : STD_LOGIC_VECTOR(11 DOWNTO 0):=(others=>'0');
signal douta : STD_LOGIC_VECTOR(11 DOWNTO 0):=(others=>'0');

	
	constant HD : integer := 639;  --  639   Horizontal Display (640)
	constant HFP : integer := 16;         --   16   Right border (front porch)
	constant HSP : integer := 96;       --   96   Sync pulse (Retrace)
	constant HBP : integer := 48;        --   48   Left boarder (back porch)
	
	constant VD : integer := 479;   --  479   Vertical Display (480)
	constant VFP : integer := 10;       	 --   10   Right border (front porch)
	constant VSP : integer := 2;				 --    2   Sync pulse (Retrace)
	constant VBP : integer := 33;       --   33   Left boarder (back porch)
	
	signal hPos : integer := 0;
	signal vPos : integer := 0;
	
	signal videoOn : std_logic := '0';
	
	component RisingEdge_DFlipFlop is
	   port(
       Q : out std_logic;    
       Clk :in std_logic;   
       D :in  std_logic    
    );
	end component ;

component Picture_Block_RAM is
  PORT (
  clka : IN STD_LOGIC;
  wea : IN STD_LOGIC_VECTOR(0 DOWNTO 0);
  addra : IN STD_LOGIC_VECTOR(16 DOWNTO 0);
  dina : IN STD_LOGIC_VECTOR(11 DOWNTO 0);
  douta : OUT STD_LOGIC_VECTOR(11 DOWNTO 0)
);
end component;

begin

DFlipFlopOut1_NOT<=not DFlipFlopOut1;
ClockDiv4_NOT<= not ClockDiv4;

--Pass Main 100 MHz clock to 2 cascaded DFlipFLops to divide frequency by 4. Result frequency= 25 MHz.
U1: RisingEdge_DFlipFlop Port map (clk=> clk, D=> DFlipFlopOut1_NOT, Q=>DFlipFlopOut1);
U2: RisingEdge_DFlipFlop Port map (clk=> DFlipFlopOut1, D=> ClockDiv4_NOT, Q=>ClockDiv4);

--Block RAM containing picture
U3: Picture_Block_RAM Port map (clka=>ClockDiv4, wea=>wea, addra=>addra, dina=>dina, douta=>douta);

Horizontal_position_counter:process(ClockDiv4)
begin

	if(ClockDiv4'event and ClockDiv4 = '1')then
		if (hPos = (HD + HFP + HSP + HBP)) then
			hPos <= 0;
		else
			hPos <= hPos + 1;
		end if;
	end if;
end process;

Vertical_position_counter:process(ClockDiv4, hPos)
begin

	if(ClockDiv4'event and ClockDiv4 = '1')then
		if(hPos = (HD + HFP + HSP + HBP))then
			if (vPos = (VD + VFP + VSP + VBP)) then
				vPos <= 0;
			else
				vPos <= vPos + 1;
			end if;
		end if;
	end if;
end process;

Horizontal_Synchronisation:process(ClockDiv4, hPos)
begin

	if(ClockDiv4'event and ClockDiv4 = '1')then
		if((hPos <= (HD + HFP)) OR (hPos > HD + HFP + HSP))then
			HSYNC <= '1';
		else
			HSYNC <= '0';
		end if;
	end if;
end process;

Vertical_Synchronisation:process(ClockDiv4, vPos)
begin

	if(ClockDiv4'event and ClockDiv4 = '1')then
		if((vPos <= (VD + VFP)) OR (vPos > VD + VFP + VSP))then
			VSYNC <= '1';
		else
			VSYNC <= '0';
		end if;
	end if;
end process;

video_on:process(ClockDiv4, hPos, vPos)
begin

	if(ClockDiv4'event and ClockDiv4 = '1')then
		if(hPos <= HD and vPos <= VD)then
			videoOn <= '1';
		else
			videoOn <= '0';
		end if;
	end if;
end process;


draw:process(ClockDiv4, hPos, vPos, videoOn)
begin

	if(ClockDiv4'event and ClockDiv4 = '1')then
		if(videoOn = '1')then
            if (unsigned(addra)<picture_size) then
                R<=douta(11 downto 8); G<=douta(7 downto 4); B<=douta(3 downto 0);
                addra<=STD_LOGIC_VECTOR(unsigned(addra)+1);
			else
				R<=(others=>'0');G<=(others=>'0');B<=(others=>'0');
			end if;
		else
			R<=(others=>'0');G<=(others=>'0');B<=(others=>'0');
			addra<=(others=>'0');
		end if;
	end if;
end process;


end Behavioral;

My problem is that the image does not display as expected. I get this displayed on my screen:

Faulty_image.thumb.jpg.feef6a02e13ef52241236db63ea4d596.jpg

As you see, there are 2 problems immediately noticed.

1st: The image is not the same, obviously.

2nd: The image should not take the whole display since it is 300 * 300 pixels while the resolution is 640*480 pixels meaning that some data is being repeated without intention. 

The default display of BASYS 3 is this. I am putting this just for reference so you can know how my screen displays 640*480 resolution:

BASYS3_default_dispaly.thumb.jpg.edbefbd08fff0697e6669d9e441ee09e.jpg

I tested the VHDL code by printing colors on my screen by direct output assignment and it works as intended. So the problem is probably with accessing the block RAM.

A snippet from my .coe file:

MEMORY_INITIALIZATION_RADIX=2;
MEMORY_INITIALIZATION_VECTOR=
011101010100,
010101110111,
000110011111,
010110101100,
000110111111,
001000100010,
001000100010,
001000100010,
001000100011,
000101100100,
100110100001,
111011010110,
111110110110,
111110000101,
111110010100,
:
:
--it goes on and on until last line, line number 90002, 90000 since 300*300 pixels= 90000. The added two is due to the first 2 lines.
:
:
101100100010;

I am stuck at this point. Where could the problem be?

Link to comment
Share on other sites

5 answers to this question

Recommended Posts

@khaledismail,

Sounds like you got yourself stuck in FPGA Hell;)

Looking over your code, a quick first glance shows that you are using a logically generated clock.  This is, in general, a very bad idea--one that can lead to hardware/simulation mismatch.  A better approach would be to use a clock enable line.

A common reason for ending up in FPGA Hell is not simulating your IP.   

The difficulty with the position you are in is that not many simulators will co-simulate the VGA your design connects to. 

I know I had similar problems when using my Basys3 board and getting video to work faultlessly.  (I was reading from flash, and decompressing the video stream since the Basys3 flash wasn't fast enough to keep up.)  In the end, I needed to write a VGA simulator so that I could "see" what was going on within all the traces within my design in order to find the bugs.  You can find that VGA simulator on-line here, or even read about it here.  The repo that contains it even includes an example project that reads from block RAM, and outputs the results onto a VGA output for the simulator.  The sad part of this design is that it is in Verilog, and uses Verilator--a Verilog only simulation utility.  However, I know there exist VHDL simulators that support a VPI interface--you might be able (with a bit of work) to get your design to work within that environment.  That might help.  (Alternatively, you might choose to re-implement your design in a real language :P )

Hope this helps,

Dan

P.S.  For those others reading, you may wish to know that @khaledismail has also posted on Xilinx's forums.  If you don't see a solution here, you might (eventually) find one there.

Link to comment
Share on other sites

Thank you very much @D@n ! Your website is rich with guidelines and tips.

Following your advice, I changed the way I divide my clock. I used a PLL from the Vivado (Clocking Wizard) to generate 25 MHz clock from 100 MHZ clock. Before doing that, I had performed test bench for my top module, but all output signals were appearing to be (undefined). Using the PLL, The test bench runs fine now!

I found out what where the problem was. I was resetting the BRAM address at a wrong point in the code. Which lead to a repeated display of the first row of the image only. Now the image appears like this:

513932862_Displayedimage.thumb.jpg.99fdb410dd886ba62499dc55e009dec4.jpg

As seen, it's close to the original image but skewed for some reason. So far I am very happy with the result and hopefully will try to know the reason of such skew.

Link to comment
Share on other sites

Hi @khaledismail,

Glad to hear you have made progress with your project. I think the image is showing this way because of a resolution difference between the image and the screen resolution. I would try making the coe file image the same resolution as the screen resolution and see if this resolves the issue.

thank you,

Jon

 

 

Link to comment
Share on other sites

@khaledismail,

I agree with @jpeyron somewhat.  I think you have at least two bugs in your design.  One of them is that your VSYNC/HSYNC signals and pixel rate aren't necessarily matching the screen resolution you are trying to create.  The second bug is that your block RAM values aren't synchronized with your VSYNC/HSYNC signals.

All of these can be easily discovered through either formal verification or extended simulation.  If you go the simulation route, your goal should be to repeat that image within the simulator, then you'll have all the "evidence" you need to find the bug.

Dan

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...