Forum Discussion

Altera_Forum's avatar
Altera_Forum
Icon for Honored Contributor rankHonored Contributor
13 years ago

SDRAM Framebuffer with two asynchronous clocks

Hi,

I want to build up a universal display tester using the DE2-115 Board.

First of all I've tried to use the altera video IP, but this was quite complex, so I decided to build all blocks on my own. The two main components are a configurable video signal generator and a 'smart' fifo which is connected to a sdram of the avalon bus. The signal generator has a pixel clock (rd_clk) and needs to get new data synchronously to this clock. The fifo has to ensure that the signal generator gets his data without any interrupts – except the 'invisible area' of the display. It consists

of an altera megafunction dual clock fifo, a avalon interface to the sdram controller and a logic which requests bursts to keep the fifo filled (see the attached code). The logic has to count the pixel\address as well. After the last pixel it resets the address and starts with the first pixel. The logic runs at 100Mhz. So far, so good. I've tested this system with 640x480 at 25MHz, 800x600 at 50MHz and 1024x768 at 65MHz. I derive the pixel clock from the main clk(100Mhz) using a pll. When I use frequencies like 25,50,65 Mhz then everything works perfectly. My Problem is, when I use frequencies like 24.94MHz or 33.6MHz (so that they are completely asynchronous to the main clk) then the fifo component fails.

The signal generator still works, but it doesn't get any data at all. Has anybody an idea why this happens? I'm a vhdl beginner, so please excuse my amateur code:)!

Thanks in advance!


LIBRARY ieee;
USE ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;
entity fifo is
   port
   (
      -- Fifo Interface 
      signal   clk_rd         :  in    std_logic; 
      signal   read_enable    :  in    std_logic;
      signal   data_out       :  out   std_logic_vector(31 downto 0);
      signal   full           :  out   std_logic;
      signal   empty          :  out   std_logic;
      signal   base_address   :  in    std_logic_vector(31 downto 0);
      
      -- Avalon Interface 
      signal   reset          :  in    std_logic := '0';
      signal   clk            :  in    std_logic := '0';
      signal   address        :  out   std_logic_vector(31 downto 0):= (others => '0');
      signal   read           :  out   std_logic := '1';
      signal   readdatavalid  :  in    std_logic := '0';
      signal   readdata       :  in    std_logic_vector(31 downto 0) := (others => '0');
      signal   waitrequest    :  in    std_logic := '1';
      signal   burstcount     :  out   std_logic_vector(7 downto 0) := "00000000";
      
      -- Debug Interface       
      signal   debug_in       :  in    std_logic_vector(63 downto 0);
      signal   debug_out      :  out   std_logic_vector(63 downto 0)
   );
end entity;
architecture fifo_a of fifo is
   constant buffer_size : integer := 30000; 
   constant total_pixels: integer := 1024 * 768;
   subtype  counter is integer range 0 to 65535; 
   signal   cnt_wr : std_logic_vector(14 downto 0) := (others => '0');
   signal   cnt_rd : std_logic_vector(14 downto 0) := (others => '0');
 
   type     t_state is
   (
      s_reset,
      s_idle,
      s_wait,
      s_request_burst,
      s_finish
   );
   signal read_state : t_state := s_reset;
   component fifotest IS
    PORT
    (
       aclr           : IN STD_LOGIC  := '0';
       data           : IN STD_LOGIC_VECTOR (31 DOWNTO 0);
       rdclk       : IN STD_LOGIC ;
       rdreq       : IN STD_LOGIC ;
       wrclk       : IN STD_LOGIC ;
       wrreq       : IN STD_LOGIC ;
       q           : OUT STD_LOGIC_VECTOR (31 DOWNTO 0);
       rdempty       : OUT STD_LOGIC ;
       rdusedw       : OUT STD_LOGIC_VECTOR (14 DOWNTO 0);
       wrfull       : OUT STD_LOGIC ;
       wrusedw       : OUT STD_LOGIC_VECTOR (14 DOWNTO 0)
    );
   END component fifotest;  
   
   
begin
    c0: fifotest        
    port map
    (
        aclr        =>  debug_in(0), --last visible Pixel clears
        data        =>  readdata,   
        wrclk        =>  clk,  
        wrfull        =>  debug_out(9),    
        wrusedw        =>  cnt_wr,
        wrreq        =>  readdatavalid,    
        rdclk        =>  clk_rd,   
        rdreq        =>  read_enable,
        q            =>  data_out,   
        rdempty        =>  debug_out(8),    
        rdusedw        =>  cnt_rd    
    );
  
   p0: process( clk, reset, waitrequest, readdata, readdatavalid )
   
      variable pixels_left    : integer := 0;
      variable addr_cnt       : integer := 0;
      variable need_burst     : std_logic := '0';
      variable burst_length   : counter := 0;
      variable addr_offset    : counter := 0;
      variable read_cnt       : counter := 0;
   
   begin
  
      if ( reset = '1' ) then
 
         pixels_left := 0;       
         read_cnt := 0;             
         addr_cnt := 0;
         read_state <= s_idle;     
      elsif ( clk'event and clk = '1' ) then -- Auf steigende Flanke triggern
         -- liegen gültige Daten an...?
         if readdatavalid = '1' then       --> ... dann wurden diese gerade eingelesen (unten)
            read_cnt := read_cnt - 1;      --> Anzahl der ausstehenden Daten verringern.          
         end if;
         case read_state is
            when s_idle =>
               if debug_in(0) = '1' then
                  read_state <= s_wait;
                  read_cnt := 0;
               end if;
               
            when s_wait =>            
               -- Werte initialisieren
               burst_length := 0;
               addr_offset := 0;
               burstcount <= "00000000";
               need_burst := '1';            
               if pixels_left >= 128 then
                  burst_length := 128;
                  addr_offset := 512;
                  burstcount <= "10000000"; -- 128
               elsif pixels_left >= 64 then
                  burst_length := 64;
                  addr_offset := 256;
                  burstcount <= "01000000"; -- 64
               elsif pixels_left >= 32 then
                  burst_length := 32;               
                  addr_offset := 128;
                  burstcount <= "00100000"; -- 32
               elsif pixels_left >= 16 then
                  burst_length := 16;               
                  addr_offset := 64;
                  burstcount <= "00010000"; -- 16
               elsif pixels_left >= 8 then
                  burst_length := 8;               
                  addr_offset := 32;
                  burstcount <= "00001000"; -- 8
               elsif pixels_left >= 4 then
                  burst_length := 4;               
                  addr_offset := 16;
                  burstcount <= "00000100"; -- 4
               elsif pixels_left >= 2 then
                  burst_length := 2;               
                  addr_offset := 8;
                  burstcount <= "00000010"; -- 2
               elsif pixels_left = 1 then
                  burst_length := 1;
                  addr_offset := 4;
                  burstcount <= "00000001"; -- 1
               elsif pixels_left = 0 then               
                  need_burst := '0';
                     addr_cnt := conv_integer(base_address);
                     pixels_left := total_pixels;                   
               end if;
               
               if ( need_burst = '1' ) then      
                  if ( (cnt_wr + read_cnt) <= (buffer_size - burst_length) ) then -- Ist noch Platz im Puffer?
                                                                  -- Ja                                                      
                     addr_cnt := addr_cnt + addr_offset;             -- Adresse erhöhen
                     pixels_left := pixels_left - burst_length;      -- Anzahl der verbleibenden Pixel verringern
                     read_state <= s_request_burst;                  -- Nächsten Burst anfordern ...
                     read_cnt := read_cnt + burst_length;            -- ... und Anzahl der ausstehenden Daten erhöhen
                  else                                            -- Nein
                     read_state <= s_wait;                           -- Hier warten               
                  end if;                          
               else
                  read_state <= s_finish;
               end if;
               
            when s_request_burst =>
               if waitrequest /= '1' then          -- Müssen wir warten?
                                                   -- Nein: Somit wurde gerade ein Burst angefordert!
                  read_state <= s_wait;            -- Hier warten 
               else
                  read_state <= s_request_burst;                 
               end if;
                  
            when s_finish =>
               if read_cnt = 0 then                  
                     read_state <= s_idle;
               else
                     read_state <= s_finish;               
               end if;
               
            when others =>
               --read_state <= s_reset;                 
         end case;
         -- Avalon-Interface aktualisieren
         address <= conv_std_logic_vector(addr_cnt,32);
      end if;
      
   end process p0;
   
    read <= '1' when read_state = s_request_burst else '0';
   
end architecture;

5 Replies

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Isn't there any professional out there who can give me a little advice?

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    First a general comment:

    LIBRARY ieee;
    USE ieee.std_logic_1164.all;
    use ieee.std_logic_arith.all;
    use ieee.std_logic_unsigned.all;
    use ieee.numeric_std.all;
    Using at the same time numeric_std and the non standard std_logic_arith and std_logic_unsigned is known to cause some problems. You should remove std_logic_arith and std_logic_unsigned, and use the signed and unsigned types provided by numeric_std to do all your arithmetic operations.

    Then you don't seem to have anything connected to your full and empty ports.

    What clock domain is the base_address vector comming from? If it is clk_rd then you can't use it directly in your process, that is clocked on the clk domain. You need proper synchronization. Any resource on the net abouk clock domain crossing in VHDL should help you find out how.

    Another problem could come from the fitter and TimeQuest. By default it will consider both clocks from being in the same domain, and if you have any signal crossing between the two domains it will determine the timing appropriately. If the two clocks are synchronous then this isn't really a problem and it can work. But if they are completely asynchronous this task is impossible and the final design won't meet timing requirements. You need to declare the two clocks in different groups in Timequest so that it doesn't try to optimize the timing between the two of them. You also need to be sure that any signal going from one domain to the other is properly synchronized. It is already done for the data path because you are using a dual clock FIFO, but check any other control signal that crosses the boundary.
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Dear Daixiwen,

    thank you very much for your detailed reply.

    --- Quote Start ---

    Using at the same time numeric_std and the non standard std_logic_arith and std_logic_unsigned is known to cause some problems. You should remove std_logic_arith and std_logic_unsigned, and use the signed and unsigned types provided by numeric_std to do all your arithmetic operations.

    --- Quote End ---

    I will try to modify the code in that way to get rid of these 'arithmetic' collisions.

    --- Quote Start ---

    Then you don't seem to have anything connected to your full and empty ports.

    --- Quote End ---

    Yes, that's true. I didn't think that I have to monitor these signals as I count all words going in and out. Do you think that this is not enough?

    --- Quote Start ---

    What clock domain is the base_address vector comming from? If it is clk_rd then you can't use it directly in your process, that is clocked on the clk domain. You need proper synchronization. Any resource on the net abouk clock domain crossing in VHDL should help you find out how.

    --- Quote End ---

    No no, the base_address is a kind of constant, which points at the beginning of the image data in the ram. It's just a static address offset and has no dedicated clk.

    --- Quote Start ---

    Another problem could come from the fitter and TimeQuest. By default it will consider both clocks from being in the same domain, and if you have any signal crossing between the two domains it will determine the timing appropriately. If the two clocks are synchronous then this isn't really a problem and it can work. But if they are completely asynchronous this task is impossible and the final design won't meet timing requirements. You need to declare the two clocks in different groups in Timequest so that it doesn't try to optimize the timing between the two of them. You also need to be sure that any signal going from one domain to the other is properly synchronized. It is already done for the data path because you are using a dual clock FIFO, but check any other control signal that crosses the boundary.

    --- Quote End ---

    To my shame I have to admit that I've not used timequest up to now, because I don't know how to configure it.

    I thought that the most critial points would be done by the altera megafunction fifo, which is capable of handling asynchronous clocks. Regarding the maximum operation clock, I've always hoped that 100Mhz are far enough away from the Cyclone IV capabilities. I do definitely know that the timing 'performance' of a project is a very basic and important point and I intend to learn TimeQuest!! I'm quite skilled in programming microcontrollers, but the parallel architecture of a fpga is often not very easy!! Again, thank you very much for you valuable tips! I will try to take everything you said into consideration! Kind regards!:)
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    If you don't have any other signal that is crossing the clock domains then you shouldn't have to do a lot in Timequest. You are right that is base_address is constant you don't need to do anything with it either.

    You should at least declare your clocks in Timequest to be sure everything is ok. A Cyclone IV is usually capable of handling signals at 100MHz, but the fitter needs to know this to be sure the time constraints are met.

    The official Timequest documentation isn't very easy to read and use, but you can use instead rsyc's great user guide on the wiki (http://www.alterawiki.com/wiki/timequest_user_guide)
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Thank you!

    I'll keep you updated with the progress!

    Have a nice day!