Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

[SOLVED] VHDL How to Design a Screen (Frame) Buffer

Status
Not open for further replies.

yttuncel

Junior Member level 3
Junior Member level 3
Joined
Nov 18, 2012
Messages
30
Helped
0
Reputation
0
Reaction score
0
Trophy points
1,286
Visit site
Activity points
1,663
Hello,

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 1440x900@60Hz for my 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.
 

  • 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.
Do you mean read enable?

Did you change the default behavior of the read enable? It should be active high, unless you forced it to generate an active low. (I don't have Altera tools installed so I can't check to see if that is an option or not)

yttuncel said:
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.
A simple dual port RAM should work fine, if you are controlling it as described (no error in you description), then you have a design issue. A dual port RAM won't use the write enable signal LOW to read. A dual port RAM will have separate read and write enables.

If you are inferring the RAM then you should at a minimum post the RAM code. You may have coded something that doesn't infer a RAM correctly.
 

Code:
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;

This is my inferred RAM. I confirmed it from the design summary, it does indeed use memory bits inside the FPGA. The reason I used write in both cases is because I coded the RAM by using write enable signals. However I do not think it is the root to any problem.

I think I managed to write data inside the RAM, but now I am stuck with feeding my data to DVI. For the time being I hold 1 bit per pixel inside the ram, and conclude the 24bit color inside the DVI block I designed. The color depends on the coordinate of the hcounter and vcounter. So, reading 16 bits from the RAM is not enough to pass it on to DVI, I need to pass it bit by bit at each clock edge.

Currently I do it as follows:

Code:
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 that particular issue?
 

Code:
  elsif rising_edge(clk106M) and (locked_sgnl = '1') then
This isn't a normal asynchronous reset FF template with that extra redundant "and (locked_sgnl = '1')" included. I guess synthesis tools have gotten better I actually had to check that it would synthesize correctly. My advice don't write your FF processes like this.

Code:
if (address_counter < "10011110001101000") then
Really you have to use binary? Why can't you use 16#13C68# or 10#81000# it's a lot easier to read.

As coded your address_counter cycles from 0 through 16#13C68# (i.e. 0-81000), which I suspect is 1 more than you want to count. You probably wanted to count from 0-80999, which counts 81000 times then rolls over. I also suspect that you wanted to have counter cycle on 16 counts as it is it cycles for 17 counts: 0-16. You really want counter to be like this:

Code VHDL - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
signal counter : unsigned(3 downto 0);
process (clk, rst)
begin
  if (rst = '1') then
    counter <= (others => '0');
  elsif rising_edge(clk) then
    -- increments when 14, rolls over to 0 if current counter value is 15.
    if (counter < 16-1) then
      counter <= counter + 1;
    else
      counter <= (others => '0');
    end if;
  end if;
end process;


This will count from 0-15 giving you a cycle time of 16 counts.

The combination of these counter errors is likely the reason you are seeing a scrolling green bar.
 

I am trying to solve this issue for hours, and still didnt make any progress. The screen is still scrolling after I played with the counter values, i.e. cycle times. I have rewritten most components in the project but they seem to work fine. I still think its a timing issue, but I just cant get rid of it. I do not think I have any issues with address generation, but honestly at this point I cant be sure about anything. What are the typical steps to debug or check if a circuit is working as intended? Can someone give me any advice?

Thanks!!
 

Solved.

The mistake was the address generation. Frame Buffer read address was generated in the blanking periods of the DVI interface whereas it should only be generated in the active region of the screen.
 

....I do not think I have any issues with address generation, but honestly at this point I cant be sure about anything.
Solved.
The mistake was the address generation. Frame Buffer read address was generated in the blanking periods of the DVI interface whereas it should only be generated in the active region of the screen.
What are the typical steps to debug or check if a circuit is working as intended? Can someone give me any advice

First off never assume anything you did was correct unless you've proven it works in both simulation and in hardware. You assumed the address generation was correct, perhaps generating the addresses was correct in sequence, but it has to be both correct in the sequence of addresses generated and when they are generated. Verify everything and always assume you screwed up. It's the reason I've always been one of the best debuggers of complex systems everywhere I've worked. Lack of ego, attention for details, and a knack for envisioning what could go wrong so the root cause of a problem can be found.

I think ego is the biggest problem with most people debugging stuff, they think they did everything right and their code is perfect, so they keep looking for the problem outside their code.
 

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top