From 2ea576665fe37b9973e668b99c80ad5737b1ca65 Mon Sep 17 00:00:00 2001 From: Rutherther Date: Wed, 6 Nov 2024 20:41:10 +0100 Subject: [PATCH] feat(fifo): initial --- fifo/README | 22 +++++++ fifo/src/bin2gray.vhd | 25 +++++++ fifo/src/counter.vhd | 40 ++++++++++++ fifo/src/dual_port_ram.vhd | 44 +++++++++++++ fifo/src/fifo.vhd | 126 ++++++++++++++++++++++++++++++++++++ fifo/src/resynchronizer.vhd | 41 ++++++++++++ 6 files changed, 298 insertions(+) create mode 100644 fifo/README create mode 100644 fifo/src/bin2gray.vhd create mode 100644 fifo/src/counter.vhd create mode 100644 fifo/src/dual_port_ram.vhd create mode 100644 fifo/src/fifo.vhd create mode 100644 fifo/src/resynchronizer.vhd diff --git a/fifo/README b/fifo/README new file mode 100644 index 0000000..41a06db --- /dev/null +++ b/fifo/README @@ -0,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 diff --git a/fifo/src/bin2gray.vhd b/fifo/src/bin2gray.vhd new file mode 100644 index 0000000..fcd71a7 --- /dev/null +++ b/fifo/src/bin2gray.vhd @@ -0,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; diff --git a/fifo/src/counter.vhd b/fifo/src/counter.vhd new file mode 100644 index 0000000..a508e24 --- /dev/null +++ b/fifo/src/counter.vhd @@ -0,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; diff --git a/fifo/src/dual_port_ram.vhd b/fifo/src/dual_port_ram.vhd new file mode 100644 index 0000000..a1e036e --- /dev/null +++ b/fifo/src/dual_port_ram.vhd @@ -0,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; diff --git a/fifo/src/fifo.vhd b/fifo/src/fifo.vhd new file mode 100644 index 0000000..3e9eb09 --- /dev/null +++ b/fifo/src/fifo.vhd @@ -0,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; diff --git a/fifo/src/resynchronizer.vhd b/fifo/src/resynchronizer.vhd new file mode 100644 index 0000000..42e1518 --- /dev/null +++ b/fifo/src/resynchronizer.vhd @@ -0,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; -- 2.48.1