URL
https://opencores.org/ocsvn/p9813_rgb_led_string_driver/p9813_rgb_led_string_driver/trunk
Subversion Repositories p9813_rgb_led_string_driver
[/] [p9813_rgb_led_string_driver/] [trunk/] [rtl/] [VHDL/] [fifo_pack.vhd] - Rev 2
Compare with Previous | Blame | View Log
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; package fifo_pack is -- Component declarations not provided any more. -- With VHDL '93 and newer, component declarations are allowed, -- but not required. -- -- Please to try direct instantiation instead, for example: -- -- instance_name : entity work.entity_name(beh) -- end fifo_pack; -------------------------------------------------------------- -- SWISS ARMY FIFO with fill level output -------------------------------------------------------------- -- Description: -- -- This is the same as "fifo_with_fill_level" but it has been -- coded to select whether Block RAMs or distributed RAMs are inferred. -- -- Note : When USE_BRAM=0, the behavior when reading the FIFO is to -- make read data available immediately during the clock cycle -- in which fifo_rd_i='1'. When USE_BRAM/=0, then an additional -- clock cycle occurs following the fifo_rd_i pulse, before the -- output data is available. -- Please be aware of this. -- -- library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; use IEEE.MATH_REAL.ALL; library work; use work.function_pack.all; entity swiss_army_fifo is generic ( USE_BRAM : integer := 1; -- Set to nonzero value for BRAM, zero for distributed RAM WIDTH : integer := 8; DEPTH : integer := 5; FILL_LEVEL_BITS : integer := 3; -- Should be at least int(floor(log2(DEPTH))+1.0) PF_FULL_POINT : integer := 3; PF_FLAG_POINT : integer := 2; PF_EMPTY_POINT : integer := 0 ); port ( sys_rst_n : in std_logic; -- Asynchronous sys_clk : in std_logic; sys_clk_en : in std_logic; reset_i : in std_logic; -- Synchronous fifo_wr_i : in std_logic; fifo_din : in unsigned(WIDTH-1 downto 0); fifo_rd_i : in std_logic; fifo_dout : out unsigned(WIDTH-1 downto 0); fifo_fill_level : out unsigned(FILL_LEVEL_BITS-1 downto 0); fifo_full : out std_logic; fifo_empty : out std_logic; fifo_pf_full : out std_logic; fifo_pf_flag : out std_logic; fifo_pf_empty : out std_logic ); end swiss_army_fifo; architecture beh of swiss_army_fifo is -- Constants constant FLG_WIDTH : integer := bit_width(DEPTH); -- Bit Width of memory address. Pointers are one bit wider, -- so that fill_level can represent the full quantity of -- items stored in the FIFO. This is important when DEPTH -- is an even power of 2. -- Signal Declarations signal rd_row : unsigned(FLG_WIDTH downto 0); signal wr_row : unsigned(FLG_WIDTH downto 0); signal fill_level : unsigned(FLG_WIDTH downto 0); signal ram_we_a : std_logic; signal ram_dout : unsigned(WIDTH-1 downto 0); TYPE STATE_TYPE IS (st_empty, st_data, st_full); signal current_state : STATE_TYPE ; signal bram_dat_b : unsigned(WIDTH-1 downto 0); BEGIN fifo_empty <= '1' when (current_state=st_empty) else '0'; fifo_full <= '1' when (current_state=st_full) else '0'; fifo_pf_full <= '1' when (fill_level>=PF_FULL_POINT or current_state=st_full) else '0'; fifo_pf_flag <= '1' when (fill_level>=PF_FLAG_POINT) else '0'; fifo_pf_empty <= '1' when (fill_level<=PF_EMPTY_POINT and current_state/=st_full) else '0'; fifo_fill_level <= resize(fill_level,FILL_LEVEL_BITS); ------------------------- -- The FIFO Fill Level fill_level_proc: process(wr_row, rd_row, current_state) begin if (current_state=st_empty) then fill_level <= (others=>'0'); elsif (wr_row>rd_row) then fill_level <= wr_row-rd_row; else fill_level <= DEPTH+(wr_row-rd_row); end if; end process; ------------------------- -- The FIFO memory -- Port A is the write side. -- Port B is dedicated to reading only. -- The hexfile is used to permit initialization of the RAM fifo_ram : entity work.swiss_army_ram(beh) generic map( USE_BRAM => USE_BRAM, WRITETHRU => 0, -- Set to nonzero value for writethrough mode USE_FILE => 0, -- Set to nonzero value to use INIT_FILE INIT_VAL => 0, INIT_SEL => 0, -- No generate loop here INIT_FILE => "foo.txt", -- ASCII hexadecimal init file name (not needed) FIL_WIDTH => 32, -- Bit width of init file lines ADR_WIDTH => FLG_WIDTH, DAT_WIDTH => WIDTH ) port map ( clk_a => sys_clk, clk_b => sys_clk, adr_a_i => wr_row(FLG_WIDTH-1 downto 0), adr_b_i => rd_row(FLG_WIDTH-1 downto 0), we_a_i => ram_we_a, en_a_i => sys_clk_en, dat_a_i => fifo_din, dat_a_o => open, we_b_i => '0', en_b_i => sys_clk_en, dat_b_i => bram_dat_b, dat_b_o => ram_dout ); bram_dat_b <= (others=>'0'); ram_we_a <= '1' when fifo_wr_i='1' and (current_state/=st_full or (current_state=st_full and fifo_rd_i='1')) else '0'; fifo_dout <= ram_dout; ------------------------- -- The FIFO state machine clocked : PROCESS(sys_clk, sys_rst_n) procedure do_write is begin if (wr_row=DEPTH-1) then -- Roll buffer index for non-power-of-two wr_row <= (others=>'0'); else wr_row<=wr_row+1; end if; end do_write; procedure do_read is begin if (rd_row=DEPTH-1) then -- Roll buffer index for non-power-of-two rd_row <= (others=>'0'); else rd_row<=rd_row+1; end if; end do_read; begin if (sys_rst_n = '0') then current_state <= st_empty; rd_row <= (others=>'0'); wr_row <= (others=>'0'); elsif (sys_clk'EVENT and sys_clk = '1') then if (sys_clk_en='1') then if (reset_i='1') then current_state <= st_empty; wr_row <= (others=>'0'); rd_row <= (others=>'0'); else case current_state is -- When empty, one can only read if also writing when st_empty => if (fifo_wr_i='1') then do_write; if (fifo_rd_i='1') then do_read; else current_state<=st_data; end if; end if; when st_data => if (fifo_wr_i='1') then do_write; if (fifo_rd_i='0' and fill_level=DEPTH-1) then current_state<=st_full; end if; end if; if (fifo_rd_i='1') then do_read; if (fifo_wr_i='0' and fill_level=1) then current_state<=st_empty; end if; end if; -- When full, one can only write if also reading when st_full => if (fifo_rd_i='1') then do_read; if (fifo_wr_i='1') then do_write; else current_state<=st_data; end if; end if; when others => null; end case; end if; end if; -- sys_clk_en end if; -- sys_clk end process clocked; end beh; -------------------------------------------------------------- -- VALIDATION FIFO with fill level output -------------------------------------------------------------- -- Description: -- -- This is the same as "swiss_army_fifo" but it has been given -- two head pointers. One of them is used for loading new data, -- and the other is used when the loaded data is to be validated, -- and for tracking the number of validated data entries contained -- within the FIFO. -- -- This FIFO was envisioned for working with packetized data, where -- a data validation check is available at the end of the packet, such -- as a CRC field. -- -- The principle at work here is that new data bytes are written -- using head pointer A, but if at the end of the packet, the data -- are deemed to be invalid, then head pointer A is "reset" back -- to the last valid point, thereby neatly "throwing away" the -- invalid data into the "bit bucket." On the other hand, if the data are -- deemed valid, then head pointer B is loaded to be equal to head -- pointer A, thereby causing the entire validated packet to become -- available for reading. -- -- The read side of the FIFO only presents validated data for reading. -- -- This situation gives rise to two sets of FIFO status outputs, -- one set for each side of the FIFO. Thus, there is a write fill -- level, and a read fill level, each with full accoutrements. -- -- The validation or invalidation is done by asserting the appropriate -- signal during a write cycle. If both signals are asserted at the -- same time, the signals are ignored, and no validation or invalidation -- is performed. -- -- Validation or invalidation can be performed at any time. If the data -- already loaded into the FIFO are validated when the FIFO writing side is -- full, then the reading side becomes full at that time. However, no new -- data can be written into the FIFO when it is full, for obvious reasons. -- -- On the other hand, if the FIFO is not full, and a new data byte is being -- written at the same time that an invalidation is being performed, then the -- new data value is technically written into the FIFO RAM, but it can never -- be read out of the storage since the pointers are updated at the same time, -- effectively cutting the new data value out of the valid FIFO area. -- This is as it should be, since the new data are technically considered -- invalid in that case. -- -- Further musings: -- Because validation/invalidation can happen even when writes are not being -- performed, one may consider the wr_dat_good_i and wr_dat_bad_i inputs as -- synchronous FIFO pointer load commands. Asserting wr_dat_good_i has the -- effect of loading wr_row_b so that it is the same as wr_row_a. Asserting -- wr_dat_bad_i has the effect of keeping wr_row_b set to the current wr_row_a -- value. -- -- Thus, if one were to tie wr_dat_good_i to '1' and wr_dat_bad_i to '0', then -- this "validation" FIFO operates identically to a regular FIFO, in which all -- data are considered valid at the time they are written into the FIFO. -- -- Note : When USE_BRAM=0, the behavior when reading the FIFO is to -- make read data available immediately during the clock cycle -- in which fifo_rd_i='1'. When USE_BRAM/=0, then an additional -- clock cycle occurs following the fifo_rd_i pulse, before the -- output data is available. -- Please be aware of this. -- -- library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; use IEEE.MATH_REAL.ALL; library work; use work.function_pack.all; entity validation_fifo is generic( USE_BRAM : integer := 1; -- Set to nonzero value for BRAM, zero for distributed RAM WIDTH : integer := 8; DEPTH : integer := 5; FILL_LEVEL_BITS : integer := 3; -- Should be at least int(floor(log2(DEPTH))+1.0) PF_FULL_POINT : integer := 3; PF_FLAG_POINT : integer := 2; PF_EMPTY_POINT : integer := 0 ); port ( sys_rst_n : in std_logic; -- Asynchronous sys_clk : in std_logic; sys_clk_en : in std_logic; reset_i : in std_logic; -- Synchronous -- Write Data Interface wr_i : in std_logic; -- Data loaded upon assertion wr_dat_good_i : in std_logic; -- FIFO data validated upon assertion wr_dat_bad_i : in std_logic; -- FIFO data invalidated upon assertion wr_dat_i : in unsigned(WIDTH-1 downto 0); -- Write Side Status wr_fill_level_o : out unsigned(FILL_LEVEL_BITS-1 downto 0); wr_full_o : out std_logic; wr_empty_o : out std_logic; wr_pf_full_o : out std_logic; wr_pf_flag_o : out std_logic; wr_pf_empty_o : out std_logic; -- Read Data Interface (valid data only) rd_i : in std_logic; rd_dat_o : out unsigned(WIDTH-1 downto 0); -- Read Side Status rd_fill_level_o : out unsigned(FILL_LEVEL_BITS-1 downto 0); rd_full_o : out std_logic; rd_empty_o : out std_logic; rd_pf_full_o : out std_logic; rd_pf_flag_o : out std_logic; rd_pf_empty_o : out std_logic ); end validation_fifo; architecture beh of validation_fifo is -- Constants constant FLG_WIDTH : integer := bit_width(DEPTH); -- Bit Width of memory address. Pointers are one bit wider, -- so that fill_level can represent the full quantity of -- items stored in the FIFO. This is important when DEPTH -- is an even power of 2. -- Signal Declarations signal rd_row : unsigned(FLG_WIDTH downto 0); signal wr_row_a : unsigned(FLG_WIDTH downto 0); signal wr_row_b : unsigned(FLG_WIDTH downto 0); signal fill_level_a : unsigned(FLG_WIDTH+1 downto 0); signal fill_level_b : unsigned(FLG_WIDTH+1 downto 0); signal ram_we_a : std_logic; signal ram_dout : unsigned(WIDTH-1 downto 0); TYPE STATE_TYPE IS (st_empty, st_data, st_full); signal current_state : STATE_TYPE ; signal bram_dat_b : unsigned(WIDTH-1 downto 0); signal fifo_level_a : unsigned(FILL_LEVEL_BITS-1 downto 0); signal fifo_full_a : std_logic; signal fifo_empty_a : std_logic; signal fifo_pf_full_a : std_logic; signal fifo_pf_flag_a : std_logic; signal fifo_pf_empty_a : std_logic; signal fifo_level_b : unsigned(FILL_LEVEL_BITS-1 downto 0); signal fifo_full_b : std_logic; signal fifo_empty_b : std_logic; signal fifo_pf_full_b : std_logic; signal fifo_pf_flag_b : std_logic; signal fifo_pf_empty_b : std_logic; BEGIN fifo_level_a <= resize(fill_level_a,FILL_LEVEL_BITS); fifo_full_a <= '1' when (fill_level_a=DEPTH) else '0'; fifo_empty_a <= '1' when (fill_level_a=0) else '0'; fifo_pf_full_a <= '1' when (fill_level_a>=PF_FULL_POINT) else '0'; fifo_pf_flag_a <= '1' when (fill_level_a>=PF_FLAG_POINT) else '0'; fifo_pf_empty_a <= '1' when (fill_level_a<=PF_EMPTY_POINT) else '0'; fifo_level_b <= resize(fill_level_b,FILL_LEVEL_BITS); fifo_full_b <= '1' when (fill_level_b=DEPTH) else '0'; fifo_empty_b <= '1' when (fill_level_b=0) else '0'; fifo_pf_full_b <= '1' when (fill_level_b>=PF_FULL_POINT) else '0'; fifo_pf_flag_b <= '1' when (fill_level_b>=PF_FLAG_POINT) else '0'; fifo_pf_empty_b <= '1' when (fill_level_b<=PF_EMPTY_POINT) else '0'; ------------------------- -- The FIFO Fill Level fill_level_a <= (others=>'0') when wr_row_a=rd_row else ('0' & wr_row_a)-('0' & rd_row) when wr_row_a>rd_row else (2**(FLG_WIDTH+1))+(('0' & wr_row_a)-('0' & rd_row)); fill_level_b <= (others=>'0') when wr_row_b=rd_row else ('0' & wr_row_b)-('0' & rd_row) when wr_row_b>rd_row else (2**(FLG_WIDTH+1))+(('0' & wr_row_b)-('0' & rd_row)); ------------------------- -- The FIFO memory -- Port A is the write side. -- Port B is dedicated to reading only. -- The hexfile is used to permit initialization of the RAM fifo_ram : entity work.swiss_army_ram(beh) generic map( USE_BRAM => USE_BRAM, WRITETHRU => 0, -- Set to nonzero value for writethrough mode USE_FILE => 0, -- Set to nonzero value to use INIT_FILE INIT_VAL => 0, INIT_SEL => 0, -- No generate loop here INIT_FILE => "foo.txt", -- ASCII hexadecimal init file name (not needed) FIL_WIDTH => 32, -- Bit width of init file lines ADR_WIDTH => FLG_WIDTH, DAT_WIDTH => WIDTH ) port map ( clk_a => sys_clk, clk_b => sys_clk, adr_a_i => wr_row_a(FLG_WIDTH-1 downto 0), adr_b_i => rd_row(FLG_WIDTH-1 downto 0), we_a_i => ram_we_a, en_a_i => sys_clk_en, dat_a_i => wr_dat_i, dat_a_o => open, we_b_i => '0', en_b_i => sys_clk_en, dat_b_i => bram_dat_b, dat_b_o => ram_dout ); bram_dat_b <= (others=>'0'); ram_we_a <= '1' when wr_i='1' and fifo_full_a='0' else '0'; rd_dat_o <= ram_dout; ------------------------- -- The FIFO writing process wr_proc : PROCESS(sys_clk, sys_rst_n) begin if (sys_rst_n = '0') then wr_row_a <= (others=>'0'); -- For unvalidated data wr_row_b <= (others=>'0'); -- For validated data elsif (sys_clk'event and sys_clk = '1') then if (sys_clk_en='1') then if (reset_i='1') then wr_row_a <= (others=>'0'); wr_row_b <= (others=>'0'); else if (wr_i='1') then if (fifo_full_a='1') then null; -- FIFO is full! Don't do any writes. -- However, still handle validation/invalidation if (wr_dat_good_i='1' and wr_dat_bad_i='0') then wr_row_b <= wr_row_a; end if; if (wr_dat_bad_i='1' and wr_dat_good_i='0') then wr_row_a <= wr_row_b; end if; else wr_row_a <= wr_row_a+1; -- Handle data validation/invalidation during writes if (wr_dat_good_i='1' and wr_dat_bad_i='0') then wr_row_b <= wr_row_a+1; end if; if (wr_dat_bad_i='1' and wr_dat_good_i='0') then wr_row_a <= wr_row_b; end if; end if; else -- Handle validation/invalidation when not writing if (wr_dat_good_i='1' and wr_dat_bad_i='0') then wr_row_b <= wr_row_a; end if; if (wr_dat_bad_i='1' and wr_dat_good_i='0') then wr_row_a <= wr_row_b; end if; end if; end if; end if; -- wr_clk_en end if; -- sys_clk end process wr_proc; ------------------------- -- The FIFO reading process rd_proc : PROCESS(sys_clk, sys_rst_n) begin if (sys_rst_n = '0') then rd_row <= (others=>'0'); elsif (sys_clk'event and sys_clk = '1') then if (sys_clk_en='1') then if (reset_i='1') then rd_row <= (others=>'0'); else if (rd_i='1') then if (fifo_empty_b='1') then null; -- FIFO is empty! Don't read anything. else rd_row <= rd_row+1; end if; end if; end if; end if; -- rd_clk_en end if; -- sys_clk end process rd_proc; -- Provide Output Status wr_fill_level_o <= fifo_level_a; wr_full_o <= fifo_full_a; wr_empty_o <= fifo_empty_a; wr_pf_full_o <= fifo_pf_full_a; wr_pf_flag_o <= fifo_pf_flag_a; wr_pf_empty_o <= fifo_pf_empty_a; rd_fill_level_o <= fifo_level_b; rd_full_o <= fifo_full_b; rd_empty_o <= fifo_empty_b; rd_pf_full_o <= fifo_pf_full_b; rd_pf_flag_o <= fifo_pf_flag_b; rd_pf_empty_o <= fifo_pf_empty_b; end beh; -------------------------------------------------------------- -- FIFO-LIFO with fill level output -------------------------------------------------------------- -- -- Author: John Clayton -- Update: Nov. 16, 2016 Copied "swiss_army_fifo" component, and revised it. -- -- Description ------------------------------------------------------------------------------- -- This is the same as "swiss_army_fifo" but it has been given the ability -- to pull the last written data back out of the FIFO. This is where the -- "LIFO" function incides, since FIFO signifies "First In, First Out" -- videlicet "LIFO" signifies "Last In, First Out." The LIFO function is -- also known as a hardware "stack." The stack is a data structure known -- to computer programmers in which a "push" operation adds a new data value -- to the top of the stack, while a "pop" operation removes the most recently -- added data value from the top of the stack. -- -- Initially,about five minutes of coding effort was put into transmogrifying -- the "fifo_pack" package into a new package, to be called "stack_pack." -- However, it was soon realized by the author that stacks are much less -- frequently needed as compared with FIFOs, and that furthermore adding a -- LIFO function to a FIFO would be a simple task, not requiring a complete -- rewrite of all the components in the fifo_pack. Also, when using the -- fifo_lifo as a simple stack or lifo, the fifo read port can be tied to -- constant signals, which will result in the FIFO tail pointer being -- "optimized out" of the design at synthesis time, resulting in a nice -- compact LIFO only function. The same optimization benefit is realized -- with regard to the status signals, whenever they are not needed. -- -- As with the swiss_army_fifo, this module is not well suited to crossing -- from one clock domain to another. For that purpose, there exist other, -- more suitable FIFOs in the world. -- -- Since this module retains characteristics of both a FIFO and a LIFO, it -- can be used to implement a "leaky stack" where the older contents can be -- emptied out. Alternately, it can be used to implement a FIFO in which -- elements of data which have been enqueued can be "uncommitted" and removed -- even after they were written into the FIFO. -- -- Ahh, yes, this brings us to the question of what priority is to be given -- when a fifo_rd and lifo_rd are both requested at the same time... Well, -- in the event that there are at least two items in the FIFO, then both -- operations can be performed simultaneously. Also, if there is only one -- item in the FIFO, but a write is being performed, then here again both -- read operations can be performed simultaneously. However, if only one item -- exists inside the FIFO, and both reads are requested at the same time, -- and there is no write proffered, then what is to be done? The answer is -- that one of the reads must be ignored, and the generic FIFO_RD_FIRST -- determines the behavior. If FIFO_RD_FIRST is non-zero, then the module -- gives priority to the fifo_rd input. Otherwise, the lifo_rd input is -- acted upon. Just as for the case of the plain FIFO, the user is -- responsible for determining further error conditions based on the fill -- level. -- -- Note : When USE_BRAM=0, the behavior when reading the FIFO is to -- make read data available immediately during the clock cycle -- in which fifo_rd_i='1'. When USE_BRAM/=0, then an additional -- clock cycle occurs following the fifo_rd_i pulse, before the -- output data is available. -- Please be aware of this. -- -- library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; use IEEE.MATH_REAL.ALL; library work; use work.function_pack.all; entity fifo_lifo is generic( USE_BRAM : integer := 1; -- Set to nonzero value for BRAM, zero for distributed RAM FIFO_RD_FIRST : integer := 0; -- Set non-zero to have fifo_rd take priority for the single item reading case WIDTH : integer := 8; DEPTH : integer := 5; FILL_LEVEL_BITS : integer := 3; -- Should be at least int(floor(log2(DEPTH))+1.0) PF_FULL_POINT : integer := 3; PF_FLAG_POINT : integer := 2; PF_EMPTY_POINT : integer := 0 ); port ( sys_rst_n : in std_logic; -- Asynchronous sys_clk : in std_logic; sys_clk_en : in std_logic; reset_i : in std_logic; -- Synchronous -- FIFO/LIFO data input fifo_wr_i : in std_logic; fifo_din : in unsigned(WIDTH-1 downto 0); -- LIFO data output lifo_rd_i : in std_logic; lifo_dout : out unsigned(WIDTH-1 downto 0); -- FIFO data output fifo_rd_i : in std_logic; fifo_dout : out unsigned(WIDTH-1 downto 0); -- FIFO/LIFO status fifo_fill_level : out unsigned(FILL_LEVEL_BITS-1 downto 0); fifo_full : out std_logic; fifo_empty : out std_logic; fifo_pf_full : out std_logic; fifo_pf_flag : out std_logic; fifo_pf_empty : out std_logic ); end fifo_lifo; architecture beh of fifo_lifo is -- Constants constant FLG_WIDTH : integer := bit_width(DEPTH); -- Bit Width of memory address. Pointers are one bit wider, -- so that fill_level can represent the full quantity of -- items stored in the FIFO. This is important when DEPTH -- is an even power of 2. -- Signal Declarations signal rd_row : unsigned(FLG_WIDTH downto 0); signal wr_row : unsigned(FLG_WIDTH downto 0); signal lf_row : unsigned(FLG_WIDTH downto 0); -- Used for LIFO read operations. signal ra_row : unsigned(FLG_WIDTH downto 0); -- Selection between wr_row and lf_row. signal fill_level : unsigned(FLG_WIDTH downto 0); signal ram_we_a : std_logic; signal ram_dout : unsigned(WIDTH-1 downto 0); type STATE_TYPE IS (st_empty, st_data, st_full); signal current_state : STATE_TYPE ; signal bram_dat_b : unsigned(WIDTH-1 downto 0); begin fifo_empty <= '1' when (current_state=st_empty) else '0'; fifo_full <= '1' when (current_state=st_full) else '0'; fifo_pf_full <= '1' when (fill_level>=PF_FULL_POINT or current_state=st_full) else '0'; fifo_pf_flag <= '1' when (fill_level>=PF_FLAG_POINT) else '0'; fifo_pf_empty <= '1' when (fill_level<=PF_EMPTY_POINT and current_state/=st_full) else '0'; fifo_fill_level <= resize(fill_level,FILL_LEVEL_BITS); ------------------------- -- The FIFO Fill Level fill_level_proc: process(wr_row, rd_row, current_state) begin if (current_state=st_empty) then fill_level <= (others=>'0'); elsif (wr_row>rd_row) then fill_level <= wr_row-rd_row; else fill_level <= DEPTH+(wr_row-rd_row); end if; end process; ------------------------- -- The FIFO memory -- Port A is the write side. -- Port B is dedicated to reading only. -- The hexfile is used to permit initialization of the RAM fifo_ram : entity work.swiss_army_ram(beh) generic map( USE_BRAM => USE_BRAM, WRITETHRU => 1, -- Set to nonzero value for writethrough mode USE_FILE => 0, -- Set to nonzero value to use INIT_FILE INIT_VAL => 0, INIT_SEL => 0, -- No generate loop here INIT_FILE => "foo.txt", -- ASCII hexadecimal init file name (not needed) FIL_WIDTH => 32, -- 4x the number of hex digits per line in INIT_FILE ADR_WIDTH => FLG_WIDTH, DAT_WIDTH => WIDTH ) port map ( clk_a => sys_clk, clk_b => sys_clk, adr_a_i => ra_row(FLG_WIDTH-1 downto 0), adr_b_i => rd_row(FLG_WIDTH-1 downto 0), we_a_i => ram_we_a, en_a_i => sys_clk_en, dat_a_i => fifo_din, dat_a_o => lifo_dout, we_b_i => '0', en_b_i => sys_clk_en, dat_b_i => bram_dat_b, dat_b_o => ram_dout ); -- Select the appropriate write address -- This was done so that LIFO reads are ready all the time, but FIFO writes -- are still accomodated when requested. ra_row <= wr_row when fifo_wr_i='1' else lf_row; -- Formulate the lf_row address, which is one less than the wr_row, because -- the wr_row points to a "fresh" location with nothing in it, while the -- lf_row selects the most recently written data item (the LI part of LIFO.) -- When the fifo_lifo is empty, this admittedly sets the lf_row pointing to -- the location in RAM which is before the tail pointer, so that a -- meaningless data word is output on the bus. The alternative would be to -- add logic to output a known constant on the bus instead. lf_row <= wr_row-1; bram_dat_b <= (others=>'0'); ram_we_a <= '1' when fifo_wr_i='1' and current_state/=st_full else '1' when fifo_wr_i='1' and current_state=st_full and fifo_rd_i='1' else '1' when fifo_wr_i='1' and current_state=st_full and lifo_rd_i='1' else '0'; fifo_dout <= ram_dout; ------------------------- -- The FIFO state machine clocked : process(sys_clk, sys_rst_n) procedure do_write is begin if (wr_row=DEPTH-1) then -- Roll buffer index for non-power-of-two wr_row <= (others=>'0'); else wr_row<=wr_row+1; end if; end do_write; procedure do_read is begin if (rd_row=DEPTH-1) then -- Roll buffer index for non-power-of-two rd_row <= (others=>'0'); else rd_row<=rd_row+1; end if; end do_read; begin if (sys_rst_n = '0') then current_state <= st_empty; rd_row <= (others=>'0'); wr_row <= (others=>'0'); elsif (sys_clk'EVENT and sys_clk = '1') then if (sys_clk_en='1') then if (reset_i='1') then current_state <= st_empty; wr_row <= (others=>'0'); rd_row <= (others=>'0'); else case current_state is -- When empty, one can only read if also writing when st_empty => if (fifo_wr_i='1') then if (lifo_rd_i='0' and fifo_rd_i='0') then do_write; current_state <= st_data; elsif (lifo_rd_i='0' and fifo_rd_i='1') then do_read; do_write; elsif (lifo_rd_i='1' and fifo_rd_i='0') then -- Here, the wr_row remains unchanged. null; elsif (lifo_rd_i='1' and fifo_rd_i='1') then -- This is a condition in which one of the read requests -- must, of necessity, be ignored. if (FIFO_RD_FIRST/=0) then do_write; do_read; else -- Here, the wr_row remains unchanged. null; end if; end if; end if; when st_data => -- As a coding choice, it has been decided to cut here with -- the fill level "analytical knife" first and then -- fully decode the read combinations as the second -- "analytical knife," making the treatment of writing the -- third "analytical knife" to be used. Then, the fourth -- analytical knife is the treatment of FIFO_RD_FIRST. -- The same logic can, of course, also be coded up correctly -- by applying the same knives in a different order, do you -- know it? BTW, the reference to analytical knives has been -- taken from Robert M. Pirsig's book -- "Zen and the Art of Motorcycle Maintenance." if (fill_level>1) then if (lifo_rd_i='0' and fifo_rd_i='0') then -- In this case can the full state be reached if (fifo_wr_i='1') then do_write; if (fill_level=DEPTH-1) then current_state <= st_full; end if; end if; elsif (lifo_rd_i='0' and fifo_rd_i='1') then if (fifo_wr_i='1') then do_read; do_write; else do_read; end if; elsif (lifo_rd_i='1' and fifo_rd_i='0') then if (fifo_wr_i='1') then null; else wr_row <= wr_row-1; end if; elsif (lifo_rd_i='1' and fifo_rd_i='1') then if (fifo_wr_i='1') then -- Here, the write and both reads get done, and the wr_row -- remains unchanged. do_read; else wr_row <= wr_row-1; do_read; end if; end if; elsif (fill_level=1) then if (lifo_rd_i='0' and fifo_rd_i='0') then if (fifo_wr_i='1') then do_write; if (fill_level=DEPTH-1) then current_state <= st_full; end if; end if; elsif (lifo_rd_i='0' and fifo_rd_i='1') then if (fifo_wr_i='1') then do_read; do_write; else do_read; current_state <= st_empty; end if; elsif (lifo_rd_i='1' and fifo_rd_i='0') then if (fifo_wr_i='1') then null; else wr_row <= wr_row-1; current_state <= st_empty; end if; elsif (lifo_rd_i='1' and fifo_rd_i='1') then if (fifo_wr_i='1') then -- Here, the write and both reads get done, and the wr_row -- remains unchanged. do_read; current_state <= st_empty; else -- This is a condition in which one of the read requests -- must, of necessity, be ignored. if (FIFO_RD_FIRST/=0) then do_read; current_state <= st_empty; else wr_row <= wr_row-1; current_state <= st_empty; end if; end if; end if; end if; -- When full, one can only write if also reading when st_full => if (lifo_rd_i='0' and fifo_rd_i='0') then null; elsif (lifo_rd_i='0' and fifo_rd_i='1') then if (fifo_wr_i='1') then do_read; do_write; else do_read; current_state <= st_data; end if; elsif (lifo_rd_i='1' and fifo_rd_i='0') then -- Here, wr_row remains unchanged. -- Other logic makes the write happen, however. null; elsif (lifo_rd_i='1' and fifo_rd_i='1') then wr_row <= wr_row-1; do_read; current_state <= st_data; end if; when others => null; end case; end if; end if; -- sys_clk_en end if; -- sys_clk end process clocked; end beh;