Reputation: 453
I am currently trying to create a screen buffer in VHDL (for a device that sends video data via VGA). I am using Xilinx ISE 13.1, and i am a begginer in VHDL.
My idea was to create a large bidimensional array containing the RGB value of each pixel (on 8 bits).
I can write in the array with no problem, but it becomes more complicated when I have to read it : The synthesis becomes extremely long, and XST just completely saturates the memory untill the computer shuts down by itself.
Here is a simplified version of my code, just trying to draw a red 45° line :
entity Pilotage_ecran is
port(clk25 : in std_logic; --25MHz clock
red_out : out std_logic; --Untill the problem is solved, i use only 1 bit to set colors
green_out : out std_logic;
blue_out : out std_logic;
hs_out : out std_logic;
vs_out : out std_logic);
end Pilotage_ecran;
architecture Behavioral of Pilotage_ecran is
signal horizontal_counter : std_logic_vector (9 downto 0);
signal vertical_counter : std_logic_vector (9 downto 0);
signal drawing : std_logic; --Signal that is set to 1 when the active video area is reached
signal busy : std_logic; --Signal to avoid launching the drawing process twice in parallel
--The array (actually containing single bits instead of vectors untill I solve the problem)
type TAB_BUFFER is array(0 to 1023, 0 to 1023) of std_logic;
signal Ecran : TAB_BUFFER := (others=>'0');
begin
Main process :
process (clk25)
variable coordX : integer;
variable coordY : integer;
begin
if clk25'event and clk25 = '1' then
if (horizontal_counter >= "0010010000" ) -- 144 : limits of active video area
and (horizontal_counter < "1100010000" ) -- 784
and (vertical_counter >= "0000100111" ) -- 39
and (vertical_counter < "1100010000" ) -- 519
then
drawing <= '1';
coordX := conv_integer (horizontal_counter);
coordY := conv_integer (vertical_counter);
if Ecran(coordX,coordY) = '1' then --Here is the problem
red_out <= '1';
green_out <= '0';
blue_out <= '0';
else
red_out <= '0';
green_out <= '0';
blue_out <= '0';
end if;
else
drawing <= '0';
end if;
--Hsync and Vsync come after, but the code is safe and tested
end if;
end process;
Drawing process (actually drawing a line in an ugly way but i just wanted to get anything in the buffer).
draw :
process (drawing, clk25, busy)
--Coordinates of the starting point (actually random values...)
variable i : integer;
variable j : integer;
begin
if (drawing = '1') and clk25 = '1' and busy = '0' then
busy <= '1';
i :=300;
j :=300;
--The loop setting the coordinates of the line to '1'
loopx : while (i<=350) loop
Ecran(i,j) <= '1';
i := i+1;
j := j+1;
end loop loopx;
busy <='0';
end if;
end process draw;
end Behavioral;
The line that causes me troubles is the one where i try to access the value at some coordinates in the buffer:
if Ecran(coordX,coordY) = '1' then
I also tried to do like this :
red_out <= Ecran(coordX,coordY);
If i replace one of coordX or coordY by an integer value, it works fine (the display doesnt match the buffer but it works), but if i use variables for both of them it crashes during synthesis. I am pretty sure i did something wrong with the array (i just learned how to use them), even if it seems to match some working codes. I could also (and probably) be using a too large array.
If anyone have an idea of what i did wrong, or have a better method on how to create a screen buffer in vhdl, any help would be very appreciated.
Thank you very much in advance.
Upvotes: 0
Views: 1006
Reputation:
If I replace one of coordX or coordY by an integer value, it works fine (the display doesn't match the buffer but it works), but if i use variables for both of them it crashes during synthesis. I am pretty sure i did something wrong with the array (i just learned how to use them), even if it seems to match some working codes. I could also (and probably) be using a too large array.
The issue is you're trying to build a read multiplexer for a large memory comprised of small blocks of RAM. Enough blocks and the problem becomes intractable for you're particular build platform.
You could modify you video timing generator to uncouple display read addresses, allowing you in this case to define your TAB_BUFFER smaller (0 to 639, 0 to 479), saving two thirds of the memory (307,200 versus 1 M Pixels) . It's not clear that would scale the problem down small enough to make it synthesize, and if it does, whether it would be fast enough.
You'd likely also want to uncouple frame buffer addresses from the present horizontal and vertical counters anyway either using separate display address counters or otherwise by passing through zero as the first visible pixel on a line or first visible line in the display. The idea being this would save you from having to do address translation during writes.
You could try defining the memory design to overcome the inability to synthesize it. This could be as simple as doing placement or as complex as creating hierarchy. The idea is you take some of the hard work away from synthesis by giving it less work to do.
Upvotes: 1
Reputation: 6146
Im not sure how VGA works but looking at your code I believe there is something fundamentally wrong with your "Drawing Process". You are attempting to do something as a software developer would rather than a hardware developer.
In short, nesting most of the process under this if statement
if (drawing = '1') and clk25 = '1' and busy = '0' then
is your problem. Two reasons I can see. First you are incrementing counters in the loop at rate totally asynch to the clock (you say if clock = '1') which is high for a non finite period. The update of that incrementer is therefor limited only by propegation delay through the counter. All counters must be synchronous (if rising_edge(clk)) or (if clock = 1 and clk'event).
The other error has to do with your current combinatorial setup (asynch) which will go away if you make this into a synchronous process. By doing busy <= '1' right after the if statement in the combinatorial logic you are essentially deactivating that block in hardware. Depending on how you code your synchronous solution this may or may not be a problem for you.
Lastly, and again this is because you are thinking of programming software not hardware, you do busy<='1' at the beginning and busy<='0' at the end of the if statement. For either the synch or asynch design this will just result in busy <='0' no matter what. Instead, (when you go synchronous) have busy <= '0' outside of your if statement and busy <= '1' inside of your if statement. Every time the statement executes busy will be 1 for the entirety of the clock signal.
When you program VHDL you need to think about every single command executing either continuously (asynchronus) or periodically (synchronously with the clock). Even if an event in an if statement is not true, all the events in the if statement are being executed. The if statement merely acts as an "enable". Having said all of this, the following code might be doing what you want.
draw : process (clk25)
--Coordinates of the starting point (actually random values...)
variable i : integer := 300;
variable j : integer := 300;
begin
if (rising_edge(clk)) then
busy <= '0';
if (i<=350) then
busy <= '1';
Ecran(i,j) <= '1';
i := i+1;
j := j+1;
end if;
end if;
end process draw;
I think that your "drawer" is really just pre loading the large array and that your other process which is setting colors is in fact your writer? I got the impression that you were incrementing through an entire row then shifting down a column as you write red/green/blue bits to the screen? Again I don't know anything about VGA
Upvotes: 2