Electronic – VHDL How to Design a Screen (Frame) Buffer

bufferfpgaportramvhdl

I am trying to use a screen buffer to store, change and output the bits of a video data to the DVI transmit interface.

  • I am using Altera Cyclone III development kit.
  • I will be using 1440×900@60Hz as resolution so my pixel clock rate is 106.7 MHz. The DVI interface is written and is tested within another project that was not using a screen buffer, but I think it is not a source to any problem.

I can not decide what kind of a RAM to use as the screen buffer. With some reading, I came to the conclusion that dual port inferred RAM would be the best choice. However, I am not sure how to use its read and write ports. I already managed to infer a ram block and instantiated it with using a function that generates a .mif file.

My current progress is as follows:

  • Words will be 16 bits long. This means that RAM will have 81K addresses.
  • The READ port will always have its write enable signal LOW (0).
  • The WRITE port will always have its write enable signal HIGH (1).

So I am trying to write and read SIMULTANEOUSLY. The clock for both is the same.

I did not embed any codes for simplicity, and I tried to be as clear as possible. If needed or wanted, I will post code snippets.

My question is the following: Is there a better way to approach to this issue, because my attempts up to now are not succesful. It seems that I can not write to RAM and I can not find the reason to it. Something tells me that I am having problems with timing, if someone can relate to it please help me.

Thanks!

Edit:

Yes I am storing 1 bit per pixel for storage purposes and am deciding the color in the DVI transmit block coming afterwards. The decision is based on the coordinates of the hcounter and vcounter of the DVI. I instantiate the RAM with '1' in every cell. I verify this by reading and outputting it to screen with a constant color:

 if vcounter < 900 then
   if hcounter < 1440 then
        if pixel_in_sgnl = '0' then
         dviRed  <= "11111111";
         dviGreen<= "00000000";
         dviBlue <= "00000000";
         else
         dviRed   <= "00000000";
         dviBlue  <= "11111111";
         dviGreen <= "00000000";
        end if;

However, when I try to modify the contents of the RAM and then read it and output to screen I still read them as 1, because the color does not change. For example in below code, I want to write "0000000000000000" to location 10000, and as a result I should observe a different colored line on a constant background. I hope I could be clear enough.

FB: FrameBuffer port map ( data_a => "0000000000000000",
                                data_b => "ZZZZZZZZZZZZZZZZ",   
                                addr_a => 10000, --address_write_sgnl,
                                addr_b => address_read_sgnl,
                                we_a  => '1',
                                we_b => '0',
                                clk_106 => DVI_clock,
                                q_a => open,
                                q_b => pixel_data_sgnl);

This is the inferred RAM I've found online and modified slightly:

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity FrameBuffer is
    port 
    (   
        data_a  : in std_logic_vector(15 downto 0);
        data_b  : in std_logic_vector(15 downto 0);
        addr_a  : in natural range 0 to 80999;
        addr_b  : in natural range 0 to 80999;
        we_a        : in std_logic ;
        we_b        : in std_logic ;
        clk_106     : in std_logic;
        q_a     : out std_logic_vector(15 downto 0);
        q_b     : out std_logic_vector(15 downto 0)
    );

end FrameBuffer;

architecture rtl of FrameBuffer is

    -- Build a 2-D array type for the RAM
    subtype word_t is std_logic_vector(15 downto 0);
    type memory_t is array(0 to 80999) of word_t;

    FUNCTION initialize_ram
        return memory_t is
        variable result : memory_t;
        BEGIN
            FOR i IN 0 to 80999 LOOP
                result(i) := "1111111111111111";
            END LOOP;
        RETURN result;
    END initialize_ram;


    -- Declare the RAM
    shared variable ram : memory_t :=initialize_ram ;

begin


    -- Port A
    process(clk_106)
    begin
        if(rising_edge(clk_106)) then -- Port A
            if(we_a = '1') then
                ram(addr_a) := data_a;
            -- Read-during-write on the same port returns NEW data
                q_a <= data_a;
            else
            -- Read-during-write on the mixed port returns OLD data
                q_a <= ram(addr_a);
            end if;
        end if;
    end process;

    -- Port B
    process(clk_106)
    begin
        if(rising_edge(clk_106)) then -- Port B
            if(we_b = '1') then
                ram(addr_b) := data_b;
            -- Read-during-write on the same port returns NEW data
                q_b <= data_b;
            else
            -- Read-during-write on the mixed port returns OLD data
                q_b <= ram(addr_b);
            end if;
        end if;
    end process;
end rtl;

Edit2:

I think I managed to solve the problem of writing to RAM, however I can still discuss about it to better my design. Now I am stuck with feeding my buffer output to DVI. As I mentioned earlier, I am holding 1 bit per pixel in the RAM, but RAM out width is 16 bits. So I need to store these 16 bits and send them to DVI one by one at each DVI clock edge. I do it as follows:

process(clk106M, locked_sgnl)
begin
  if locked_sgnl = '0' then
                address_counter <= (others => '0');
                counter <= "10000";
                pixel_in_register <= (others => '0');
  elsif rising_edge(clk106M) and (locked_sgnl = '1') then
     if (address_counter < "10011110001101000") then
                        if counter >= 16 then
                                pixel_in_register <= pixel_in;
                                address_counter <= address_counter + '1';
                                counter <= "00000";
                        else
                                pixel_in_sgnl <= pixel_in_register(0);
                                pixel_in_register <= '0' & pixel_in_register(15 downto 1);
                                counter <= counter + '1';
                        end if;
          else
                        address_counter <= (others => '0');
          end if;

 end if;
end process;

 buffer_address_dvi <= address_counter;

I use a register and hold the buffer output inside. Then at each clock cycle for 16 cycles, I output the LSB of the contents of the register and shift the data to right. What stucks in my head is that if I need to generate the read address earlier or not.

Currently if there is a 0 in buffer, that pixel corresponds to green. If 1, it corresponds to blue. What I do is that I instantiate the RAM full of '1's. Then I write '0' to a portion of it, and try to observe a thick horizontal line on the screen. Fortunately enough, I see that horizontal line, but it is not stationary. Its sweeping the screen from top to bottom or bottom to top, I can not differentiate. What might the cause of this particular issue?

Best Answer

Found it a couple of days ago:

I was increasing the frame buffer address not only during the active part of the DVI but also at the blanking periods as well. This resulted in a shift on the screen. Thank you all for your answers.