Forum Discussion
Altera_Forum
Honored Contributor
14 years agoSDRAM 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
Honored Contributor
Isn't there any professional out there who can give me a little advice?
- Altera_Forum
Honored Contributor
First a general comment:
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.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; - Altera_Forum
Honored 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
Honored 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
Honored Contributor
Thank you!
I'll keep you updated with the progress! Have a nice day!