A => fifo/README +22 -0
@@ 1,22 @@
+This is a simple asynchronous fifo project.
+It is meant to be synthesisable, without any
+errors in synchronization between the clock domains.
+
+I don't know how to relibaly verify though.
+After research I found this article
+http://www.sunburst-design.com/papers/CummingsSNUG2002SJ_FIFO1.pdf
+Simulation and Synthesis Techniques for Asynchronous FIFO Design Clifford E. Cummings, Sunburst Design, Inc.,
+it looks very good. My fifo is similar to this one, but not the same.
+Specifically I decided to make full comparison simpler. I made the fifo SIZE - 1 long
+instead of SIZE. While SIZE number of positions in the memory is used, only
+SIZE - 1 can be used at single time. This is an unnecessary limitation, but
+on the other hand then it seems to me the full conditions is much simpler.
+I can just compare the next write address to current read address. When
+they match, the fifo is full. Normally there is the problem of distinguishing
+between an empty and full state - the fifo is full when pointers match,
+but it's also empty when pointers match. It depends on who caught to who.
+The article discusses a solution with flipping MSB bit that we add just for
+this.
+
+Currently the fifo design is written, but not tested at all not even
+in RTL. So I have not yet verified I did not make a silly mistake.<
\ No newline at end of file
A => fifo/src/bin2gray.vhd +25 -0
@@ 1,25 @@
+library ieee;
+use ieee.std_logic_1164.all;
+
+entity bin2gray is
+
+ generic (
+ WIDTH : natural);
+
+ port (
+ bin_i : in std_logic_vector(WIDTH - 1 downto 0);
+ gray_o : out std_logic_vector(WIDTH - 1 downto 0));
+
+end entity bin2gray;
+
+architecture a1 of bin2gray is
+
+begin -- architecture a1
+
+ G(WIDTH - 1) <= bin(WIDTH - 1);
+ --generate xor gates.
+ xors : for i in 0 to N - 1 generate
+ gray_o(i) <= bin_i(i+1) xor bin_i(i);
+ end generate xors;
+
+end architecture a1;
A => fifo/src/counter.vhd +40 -0
@@ 1,40 @@
+library ieee;
+use ieee.std_logic_1164.all;
+
+entity counter is
+
+ generic (
+ MAX : natural;
+ WIDTH : natural);
+
+ port (
+ clk_i : in std_logic;
+ rst_in : in std_logic;
+ increment_i : in std_logic;
+ count_o : out std_logic_vector(WIDTH - 1 downto 0));
+
+end entity counter;
+
+architecture a1 of counter is
+ signal curr_count : std_logic_vector(WIDTH - 1 downto 0);
+ signal next_count : std_logic_vector(WIDTH - 1 downto 0);
+begin -- architecture a1
+
+ set_counter: process (clk_i) is
+ begin -- process set_counter
+ if rising_edge(clk_i) then -- rising clock edge
+ if rst_in = '0' then -- synchronous reset (active low)
+ curr_count <= (others => '0');
+ else
+ curr_count <= next_count;
+ end if;
+ end if;
+ end process set_counter;
+
+ next_count <= curr_count when increment_i = '0' else
+ std_logic_vector(unsigned(curr_count) + 1) when curr_count < MAX else
+ (others => '0');
+
+ count_o <= curr_count;
+
+end architecture a1;
A => fifo/src/dual_port_ram.vhd +44 -0
@@ 1,44 @@
+library ieee;
+use ieee.std_logic_1164.all;
+
+entity dual_port_ram is
+
+ generic (
+ SIZE : natural;
+ SIZE_2LOG : natural;
+ WIDTH : natural);
+
+ port (
+ wr_clk_i : in std_logic;
+ wr_addr_i : in std_logic_vector(SIZE_2LOG downto 0);
+ wr_en_i : in std_logic;
+ wr_data_i : in std_logic_vector(WIDTH - 1 downto 0);
+
+ rd_clk_i : in std_logic;
+ rd_addr_i : in std_logic_vector(SIZE_2LOG downto 0);
+ rd_data_o : out std_logic_vector(WIDTH - 1 downto 0));
+
+end entity dual_port_ram;
+
+architecture a1 of dual_port_ram is
+ type mem_type is array (0 to SIZE-1) of std_logic_vector(WIDTH-1 downto 0);
+ signal data : mem_type;
+begin -- architecture a1
+
+ writer: process (wr_clk_i) is
+ begin -- process writer
+ if rising_edge(wr_clk_i) then -- rising clock edge
+ if wr_en_i = '1' then
+ data(wr_addr_i) <= wr_data_i;
+ end if;
+ end if;
+ end process writer;
+
+ reader: process (rd_clk_i) is
+ begin -- process reader
+ if rising_edge(rd_clk_i) then -- rising clock edge
+ rd_data_o <= data(rd_addr_i);
+ end if;
+ end process reader;
+
+end architecture a1;
A => fifo/src/fifo.vhd +126 -0
@@ 1,126 @@
+library ieee;
+use ieee.std_logic_1164.all;
+
+-- this fifo is meant to be used
+-- as a
+
+entity dfifo is
+
+ generic (
+ SIZE_2LOG : natural;
+ WIDTH : natural := 32);
+
+ port (
+ rst_in : in std_logic;
+ wr_clk_i : in std_logic;
+ wr_en_i : in std_logic;
+ wr_data_i : in std_logic_vector(WIDTH - 1 downto 0);
+ rd_clk_i : in std_logic;
+ rd_en_i : in std_logic;
+ rd_data_o : out std_logic_vector(WIDTH - 1 downto 0);
+ empty_o : out std_logic; -- valid on rd_clk?
+ full_o : out std_logic); -- valid on wr_clk?
+
+end entity dfifo;
+
+architecture a1 of dfifo is
+ constant SIZE : natural := 2**SIZE_2LOG;
+
+ signal wr_pos : std_logic_vector(SIZE_2LOG - 1 downto 0);
+ -- signal wr_pos_gray : std_logic_vector(SIZE_2LOG - 1 downto 0);
+ signal wr_next_pos_gray : std_logic_vector(SIZE_2LOG - 1 downto 0);
+ -- register of wr_pos and wr_next_pos sampled at rd_clock
+ -- signal wr_pos_gray_rsync : std_logic_vector(SIZE_2LOG - 1 downto 0);
+ signal wr_next_pos_gray_rsync : std_logic_vector(SIZE_2LOG - 1 downto 0);
+
+ signal rd_pos : std_logic_vector(SIZE_2LOG - 1 downto 0);
+ signal rd_pos_gray : std_logic_vector(SIZE_2LOG - 1 downto 0);
+ -- register of rd_pos_gray sampled at wr_clock
+ signal rd_pos_gray_wsync : std_logic_vector(SIZE_2LOG - 1 downto 0);
+
+ signal wr_en : std_logic;
+ signal rd_en : std_logic;
+
+ signal empty : std_logic;
+ signal full : std_logic;
+begin -- architecture a1
+
+ write_counter_gray : entity work.bin2gray
+ generic map (
+ WIDTH => SIZE_2LOG)
+ port map (
+ bin_i => std_logic_vector(unsigned(wr_pos) + 1),
+ gray_o => wr_next_pos_gray);
+
+ write_counter : entity work.counter
+ generic map (
+ WIDTH => SIZE_2LOG,
+ MAX => SIZE)
+ port map (
+ clk_i => wr_clk_i,
+ rst_in => rst_in,
+ increment_i => wr_en,
+ count_o => wr_pos);
+
+ read_counter_gray : entity work.bin2gray
+ generic map (
+ WIDTH => SIZE_2LOG)
+ port map (
+ bin_i => rd_pos,
+ gray_o => rd_pos_gray);
+
+ read_counter : entity work.counter
+ generic map (
+ WIDTH => SIZE_2LOG,
+ MAX => SIZE)
+ port map (
+ clk_i => rd_clk_i,
+ rst_in => rst_in,
+ increment_i => rd_en,
+ count_o => rd_pos);
+
+ ram : entity work.dual_port_ram
+ generic map (
+ SIZE => SIZE,
+ SIZE_2LOG => SIZE_2LOG,
+ WIDTH => WIDTH)
+ port map (
+ wr_clk_i => wr_clk_i,
+ wr_addr_i => wr_pos,
+ wr_en_i => wr_en,
+ wr_data_i => wr_data_i,
+
+ rd_clk_i => rd_clk_i,
+ rd_addr_i => rd_pos,
+ rd_data => rd_data_o);
+
+ -- resynchronizer_wr_pos_gray_rsync: entity work.resynchronizer
+ -- port map (
+ -- orig_clk_i => wr_clk_i,
+ -- target_clk_i => rd_clk_i,
+ -- sig_i => wr_pos_gray,
+ -- sig_o => wr_pos_gray_rsync);
+ resynchronizer_wr_pos_next_gray_rsync: entity work.resynchronizer
+ port map (
+ orig_clk_i => wr_clk_i,
+ target_clk_i => rd_clk_i,
+ sig_i => wr_next_pos_gray,
+ sig_o => wr_next_pos_gray_rsync);
+ resynchronizer_rd_pos_gray_wsync: entity work.resynchronizer
+ port map (
+ orig_clk_i => wr_clk_i,
+ target_clk_i => rd_clk_i,
+ sig_i => rd_pos_gray,
+ sig_o => rd_pos_gray_wsync);
+
+ -- NOTE: this is meant to be used in write domain
+ full <= '1' when rd_pos_gray_wsync = wr_next_pos_gray else '0';
+ -- NOTE: this is meant to be used in read domain
+ empty <= '1' when rd_pos_gray = wr_pos_gray_rsync else '0';
+
+ rd_en <= rd_en_i and not empty;
+ wr_en <= wr_en_i and not full;
+
+ empty_o <= empty;
+ full_o <= empty;
+end architecture a1;
A => fifo/src/resynchronizer.vhd +41 -0
@@ 1,41 @@
+library ieee;
+use ieee.std_logic_1164.all;
+
+entity resynchronizer is
+
+ generic (
+ WIDTH : natural := 2);
+
+ port (
+ orig_clk_i : in std_logic;
+ target_clk_i : in std_logic;
+ sig_i : in std_logic;
+ sig_o : out std_logic);
+
+end entity resynchronizer;
+
+architecture a1 of resynchronizer is
+ signal sig_sampled : std_logic;
+
+ signal curr_resync : std_logic_vector(WIDTH - 1 downto 0);
+ signal next_resync : std_logic_vector(WIDTH - 1 downto 0);
+begin -- architecture a1
+
+ orig_sample: process (orig_clk_i) is
+ begin -- process orig_sample
+ if rising_edge(orig_clk_i) then -- rising clock edge
+ sig_sampled <= sig_i;
+ end if;
+ end process orig_sample;
+
+ synchronizer: process (target_clk_i) is
+ begin -- process synchronizer
+ if rising_edge(target_clk_i) then -- rising clock edge
+ curr_resync <= next_resync;
+ end if;
+ end process synchronizer;
+
+ next_resync <= sig_sampled & curr_resync(WIDTH - 1 downto 1);
+ sig_o <= curr_resync(0);
+
+end architecture a1;