diff options
author | Tristan Gingold <tgingold@free.fr> | 2017-06-12 05:38:52 +0200 |
---|---|---|
committer | Tristan Gingold <tgingold@free.fr> | 2017-06-12 05:38:52 +0200 |
commit | 15f250b53c21dc9898126d9476edb4322cdbb6b4 (patch) | |
tree | 3a15e42b4cceaf34f3279c8a3385f529e562d99b /testsuite/gna/issue301/src/ram_ctrl.vhd | |
parent | d986b9af22f4e927e887f1bd6f9ff5174dde4aca (diff) | |
download | ghdl-15f250b53c21dc9898126d9476edb4322cdbb6b4.tar.gz ghdl-15f250b53c21dc9898126d9476edb4322cdbb6b4.tar.bz2 ghdl-15f250b53c21dc9898126d9476edb4322cdbb6b4.zip |
Add reproducer for #301
Diffstat (limited to 'testsuite/gna/issue301/src/ram_ctrl.vhd')
-rw-r--r-- | testsuite/gna/issue301/src/ram_ctrl.vhd | 474 |
1 files changed, 474 insertions, 0 deletions
diff --git a/testsuite/gna/issue301/src/ram_ctrl.vhd b/testsuite/gna/issue301/src/ram_ctrl.vhd new file mode 100644 index 000000000..712738811 --- /dev/null +++ b/testsuite/gna/issue301/src/ram_ctrl.vhd @@ -0,0 +1,474 @@ +--! +--! Copyright (C) 2011 - 2014 Creonic GmbH +--! +--! This file is part of the Creonic Viterbi Decoder, which is distributed +--! under the terms of the GNU General Public License version 2. +--! +--! @file +--! @brief Viterbi decoder RAM control +--! @author Markus Fehrenz +--! @date 2011/12/13 +--! +--! @details Manage RAM behavior. Write and read data. +--! The decisions are sent to the traceback units +--! It is signaled if the data belongs to acquisition or window phase. +--! + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library dec_viterbi; +use dec_viterbi.pkg_param.all; +use dec_viterbi.pkg_param_derived.all; +use dec_viterbi.pkg_types.all; +use dec_viterbi.pkg_components.all; + + +entity ram_ctrl is + port( + clk : in std_logic; + rst : in std_logic; + + + -- + -- Slave data signals, delivers the LLR parity values. + -- + s_axis_input_tvalid : in std_logic; + s_axis_input_tdata : in std_logic_vector(NUMBER_TRELLIS_STATES - 1 downto 0); + s_axis_input_tlast : in std_logic; + s_axis_input_tready : out std_logic; + + + -- + -- Master data signals for traceback units, delivers the decision vectors. + -- + m_axis_output_tvalid : out std_logic_vector(1 downto 0); + m_axis_output_tdata : out t_ram_rd_data; + m_axis_output_tlast : out std_logic_vector(1 downto 0); + m_axis_output_tready : in std_logic_vector(1 downto 0); + + -- Signals the traceback unit when the decision bits do not belong to an acquisition. + m_axis_output_window_tuser : out std_logic_vector(1 downto 0); + + -- Signals whether this is the last decision vector of the window. + m_axis_output_last_tuser : out std_logic_vector(1 downto 0); + + + -- + -- Slave configuration signals, delivering the configuration data. + -- + + s_axis_ctrl_tvalid : in std_logic; + s_axis_ctrl_tdata : in std_logic_vector(31 downto 0); + s_axis_ctrl_tready : out std_logic +); +end entity ram_ctrl; + + +architecture rtl of ram_ctrl is + + ------------------------ + -- Type definition + ------------------------ + + -- + -- Record contains runtime configuration. + -- The input configuration is stored in a register. + -- It is received from a AXI4-Stream interface from the top entity. + -- + type trec_runtime_param is record + window_length : unsigned(BW_MAX_WINDOW_LENGTH - 1 downto 0); + acquisition_length : unsigned(BW_MAX_WINDOW_LENGTH - 1 downto 0); + end record trec_runtime_param; + + -- Types for finite state machines + type t_write_ram_fsm is (CONFIGURE, START, RUN, WAIT_FOR_TRACEBACK, WAIT_FOR_LAST_TRACEBACK); + type t_read_ram_fsm is (WAIT_FOR_WINDOW, TRACEBACK, WAIT_FOR_RAM, FINISH); + type t_read_ram_fsm_array is array (0 to 1) of t_read_ram_fsm; + + -- RAM controling types + type t_ram_data is array (3 downto 0) of std_logic_vector(NUMBER_TRELLIS_STATES - 1 downto 0); + type t_ram_addr is array (3 downto 0) of unsigned(BW_MAX_WINDOW_LENGTH - 1 downto 0); + type t_ram_rd_addr is array (1 downto 0) of unsigned(BW_MAX_WINDOW_LENGTH - 1 downto 0); + type t_ram_ptr is array (1 downto 0) of unsigned(1 downto 0); + type t_ram_ptr_int is array (1 downto 0) of integer range 3 downto 0; + + type t_ram_data_cnt is array (1 downto 0) of integer range 2 * MAX_WINDOW_LENGTH downto 0; + + + ------------------------ + -- Signal declaration + ------------------------ + + signal ram_buffer : t_ram_rd_data; + signal ram_buffer_full : std_logic_vector(1 downto 0); + + signal config : trec_runtime_param; + signal write_ram_fsm : t_write_ram_fsm; + signal read_ram_fsm : t_read_ram_fsm_array; + signal wen_ram : std_logic_vector(3 downto 0); + signal addr : t_ram_addr; + signal q_reg : t_ram_data; + + -- ram addess, number and data pointer + signal write_ram_ptr : unsigned(1 downto 0); + signal read_ram_ptr : t_ram_ptr; + signal read_ram_ptr_d : t_ram_ptr; + signal write_addr_ptr : unsigned(BW_MAX_WINDOW_LENGTH - 1 downto 0); + signal read_addr_ptr : t_ram_rd_addr; + + -- internal signals of outputs + signal m_axis_output_tvalid_int : std_logic_vector(1 downto 0); + signal m_axis_output_tlast_int : std_logic_vector(1 downto 0); + signal m_axis_output_window_tuser_int : std_logic_vector(1 downto 0); + signal m_axis_output_last_tuser_int : std_logic_vector(1 downto 0); + signal s_axis_input_tready_int : std_logic; + signal s_axis_ctrl_tready_int : std_logic; + + signal next_traceback : std_logic_vector(1 downto 0); + signal write_window_complete : std_logic; + signal write_last_window_complete : std_logic; + signal last_of_block : std_logic; + signal read_last_addr_ptr : unsigned(BW_MAX_WINDOW_LENGTH - 1 downto 0); +begin + + m_axis_output_tvalid <= m_axis_output_tvalid_int; + m_axis_output_tlast <= m_axis_output_tlast_int; + m_axis_output_window_tuser <= m_axis_output_window_tuser_int; + m_axis_output_last_tuser <= m_axis_output_last_tuser_int; + m_axis_output_tdata(0) <= q_reg(to_integer(read_ram_ptr_d(0))) when ram_buffer_full(0) = '0' else ram_buffer(0); + m_axis_output_tdata(1) <= q_reg(to_integer(read_ram_ptr_d(1))) when ram_buffer_full(1) = '0' else ram_buffer(1); + + + -- + -- When the output port is not ready to read the output of the RAM immediately + -- we have to remember the output value of the RAM in an extra register. + -- When the output is ready to read, we first use the ouput of the register + -- and only then the output of the RAM again. + -- + pr_buf_ram_output: process(clk) is + begin + if rising_edge(clk) then + if rst = '1' then + ram_buffer <= (others => (others => '0')); + ram_buffer_full <= (others => '0'); + else + + for i in 0 to 1 loop + if m_axis_output_tvalid_int(i) = '1' and m_axis_output_tready(i) = '0' and ram_buffer_full(i) = '0' then + ram_buffer(i) <= q_reg(to_integer(read_ram_ptr_d(i))); + ram_buffer_full(i) <= '1'; + end if; + + if m_axis_output_tvalid_int(i) = '1' and m_axis_output_tready(i) = '1' and ram_buffer_full(i) = '1' then + ram_buffer_full(i) <= '0'; + end if; + end loop; + + end if; + end if; + end process pr_buf_ram_output; + + ----------------------------- + -- Manage writing from ACS -- + ----------------------------- + s_axis_input_tready_int <= '0' when (write_ram_fsm = CONFIGURE) or + (write_ram_ptr = read_ram_ptr(0) and read_ram_fsm(0) /= WAIT_FOR_WINDOW) or + (write_ram_ptr = read_ram_ptr(1) and read_ram_fsm(1) /= WAIT_FOR_WINDOW) or + write_ram_fsm = WAIT_FOR_TRACEBACK or write_ram_fsm = WAIT_FOR_LAST_TRACEBACK else + '1'; + s_axis_input_tready <= s_axis_input_tready_int; + + s_axis_ctrl_tready_int <= '1' when (read_ram_fsm(0) = WAIT_FOR_WINDOW and read_ram_fsm(1) = WAIT_FOR_WINDOW and write_ram_fsm = CONFIGURE) else + '0'; + s_axis_ctrl_tready <= s_axis_ctrl_tready_int; + + -- Process for writing to the RAM + pr_write_ram: process(clk) is + variable v_window_length : unsigned(BW_MAX_WINDOW_LENGTH - 1 downto 0); + variable v_acquisition_length : unsigned(BW_MAX_WINDOW_LENGTH - 1 downto 0); + begin + if rising_edge(clk) then + if rst = '1' then + write_ram_fsm <= CONFIGURE; + write_addr_ptr <= (others => '0'); + write_ram_ptr <= (others => '0'); + wen_ram <= (others => '0'); + write_window_complete <= '0'; + write_last_window_complete <= '0'; + read_last_addr_ptr <= (others => '0'); + else + + case write_ram_fsm is + + -- + -- It is necessary to configure the decoder before each block + -- + when CONFIGURE => + write_window_complete <= '0'; + write_last_window_complete <= '0'; + if s_axis_ctrl_tvalid = '1' and s_axis_ctrl_tready_int = '1' then + v_window_length := unsigned(s_axis_ctrl_tdata(BW_MAX_WINDOW_LENGTH - 1 + 16 downto 16)); + v_acquisition_length := unsigned(s_axis_ctrl_tdata(BW_MAX_WINDOW_LENGTH - 1 downto 0)); + write_addr_ptr <= v_window_length - v_acquisition_length; + config.window_length <= v_window_length; + config.acquisition_length <= v_acquisition_length; + write_ram_fsm <= START; + + wen_ram(to_integer(write_ram_ptr)) <= '1'; + end if; + + + -- + -- After the decoder is configured, the decoder is waiting for a new block. + -- When the AXIS handshake is there the packet transmission begins. + -- The first write is a special case, since writing data starts at the acquisition length. + -- There is no complete window available afterwards. + -- + when START => + if s_axis_input_tvalid = '1' and s_axis_input_tready_int = '1' then + + if write_addr_ptr = config.window_length - 1 then + + -- When we switch to the next RAM, we reset the write addr. + write_addr_ptr <= (others => '0'); + + -- Switch to the next RAM. + write_ram_ptr <= write_ram_ptr + 1; + wen_ram(to_integer(write_ram_ptr)) <= '0'; + wen_ram(to_integer(write_ram_ptr + 1)) <= '1'; + + write_ram_fsm <= RUN; + else + write_addr_ptr <= write_addr_ptr + 1; + end if; + end if; + + + -- + -- The decoder is receiving data from the ACS. + -- + when RUN => + write_window_complete <= '0'; + write_last_window_complete <= '0'; + + if s_axis_input_tvalid = '1' and s_axis_input_tready_int = '1' then + write_addr_ptr <= write_addr_ptr + 1; + + if write_addr_ptr = config.window_length - 1 then + + -- When we switch to the next RAM, we reset the write addr. + write_addr_ptr <= (others => '0'); + + -- Switch to the next RAM. + write_ram_ptr <= write_ram_ptr + 1; + wen_ram(to_integer(write_ram_ptr)) <= '0'; + wen_ram(to_integer(write_ram_ptr + 1)) <= '1'; + + -- Indicate, that a complete window is within the RAM and traceback may start. + write_window_complete <= '1'; + + if read_ram_fsm(0) /= WAIT_FOR_WINDOW and read_ram_fsm(1) /= WAIT_FOR_WINDOW then + write_ram_fsm <= WAIT_FOR_TRACEBACK; + end if; + + else + write_addr_ptr <= write_addr_ptr + 1; + end if; + + if s_axis_input_tlast = '1' then + write_ram_fsm <= CONFIGURE; + wen_ram <= (others => '0'); + + write_last_window_complete <= '1'; + if (read_ram_fsm(0) /= WAIT_FOR_WINDOW and read_ram_fsm(1) /= WAIT_FOR_WINDOW) or write_window_complete = '1' then + write_ram_fsm <= WAIT_FOR_LAST_TRACEBACK; + end if; + read_last_addr_ptr <= write_addr_ptr; + + write_addr_ptr <= (others => '0'); + write_ram_ptr <= write_ram_ptr + 1; + end if; + end if; + + when WAIT_FOR_TRACEBACK => + if read_ram_fsm(0) = WAIT_FOR_WINDOW or read_ram_fsm(1) = WAIT_FOR_WINDOW then + write_ram_fsm <= RUN; + write_window_complete <= '0'; + end if; + + when WAIT_FOR_LAST_TRACEBACK => + if read_ram_fsm(0) = WAIT_FOR_WINDOW or read_ram_fsm(1) = WAIT_FOR_WINDOW then + write_ram_fsm <= CONFIGURE; + write_last_window_complete <= '0'; + end if; + + end case; + end if; + end if; + end process pr_write_ram; + + + ------------------------------------------- + -- Manage reading from RAM for traceback -- + ------------------------------------------- + + gen_read_ram: for i in 0 to 1 generate + pr_read_ram: process(clk) is + begin + if rising_edge(clk) then + if rst = '1' then + read_addr_ptr(i) <= (others => '0'); + read_ram_fsm(i) <= WAIT_FOR_WINDOW; + m_axis_output_tvalid_int(i) <= '0'; + m_axis_output_tlast_int(i) <= '0'; + m_axis_output_window_tuser_int(i) <= '0'; + m_axis_output_last_tuser_int(i) <= '0'; + read_ram_ptr(i) <= (others => '0'); + read_ram_ptr_d(i) <= (others => '0'); + else + + read_ram_ptr_d(i) <= read_ram_ptr(i); + case read_ram_fsm(i) is + + -- Wait for the next window to be ready within the RAM. + when WAIT_FOR_WINDOW => + read_addr_ptr(i) <= config.window_length - 1; + m_axis_output_tlast_int(i) <= '0'; + m_axis_output_tvalid_int(i) <= '0'; + m_axis_output_last_tuser_int(i) <= '0'; + m_axis_output_window_tuser_int(i) <= '0'; + read_ram_ptr(i) <= write_ram_ptr; + + -- We always start from the RAM, which was written last. + if write_window_complete = '1' and next_traceback(i) = '1' then + read_ram_ptr(i) <= write_ram_ptr - 1; + read_addr_ptr(i) <= read_addr_ptr(i) - 1; + read_ram_fsm(i) <= TRACEBACK; + m_axis_output_tvalid_int(i) <= '1'; + end if; + if write_last_window_complete = '1' and next_traceback(i) = '1' then + read_ram_ptr(i) <= write_ram_ptr - 1; + read_addr_ptr(i) <= read_last_addr_ptr; + read_ram_fsm(i) <= TRACEBACK; + m_axis_output_window_tuser_int(i) <= '1'; + end if; + + -- Perform the Traceback on the RAM data of the first RAM we need for acquisition and traceback. + when TRACEBACK => + m_axis_output_tlast_int(i) <= '0'; + m_axis_output_last_tuser_int(i) <= '0'; + m_axis_output_tvalid_int(i) <= '1'; + + if m_axis_output_tready(i) = '1' then + if read_addr_ptr(i) = 0 then + if read_ram_fsm(1 - i) = TRACEBACK and read_ram_ptr(1 - i) = read_ram_ptr(i) - 1 then + read_ram_fsm(i) <= WAIT_FOR_RAM; + else + read_addr_ptr(i) <= config.window_length - 1; + read_ram_ptr(i) <= read_ram_ptr(i) - 1; + read_ram_fsm(i) <= FINISH; + end if; + else + read_addr_ptr(i) <= read_addr_ptr(i) - 1; + end if; + + -- Signal the traceback unit, acquisition is over. + if read_addr_ptr(i) = config.window_length - config.acquisition_length - 1 then + m_axis_output_window_tuser_int(i) <= '1'; + end if; + end if; + + when WAIT_FOR_RAM => + m_axis_output_tvalid_int(i) <= '0'; + if read_ram_fsm(1 - i) /= TRACEBACK or read_ram_ptr(1 - i) /= read_ram_ptr(i) - 1 then + read_addr_ptr(i) <= config.window_length - 1; + read_ram_ptr(i) <= read_ram_ptr(i) - 1; + read_ram_fsm(i) <= FINISH; + end if; + + -- Get the remaining values from the second RAM we need for traceback (no acquisition values in this RAM) + when FINISH => + if m_axis_output_tvalid_int(i) <= '0' then + m_axis_output_tvalid_int(i) <= '1'; + read_addr_ptr(i) <= read_addr_ptr(i) - 1; + end if; + if m_axis_output_tready(i) = '1' then + + if read_addr_ptr(i) = config.window_length - config.acquisition_length then + m_axis_output_last_tuser_int(i) <= '1'; + read_addr_ptr(i) <= config.window_length - 1; + read_ram_fsm(i) <= WAIT_FOR_WINDOW; + + -- Check if the other read process finished processing. + if read_ram_fsm((i+1) mod 2) = WAIT_FOR_WINDOW and last_of_block = '1' then + m_axis_output_tlast_int(i) <= '1'; + end if; + + else + read_addr_ptr(i) <= read_addr_ptr(i) - 1; + end if; + end if; + end case; + end if; + end if; + end process pr_read_ram; + end generate gen_read_ram; + + -- This process decides which traceback unit is the next one to use. + pr_next_traceback: process(clk) is + begin + if rising_edge(clk) then + if rst = '1' then + next_traceback <= "01"; + last_of_block <= '0'; + else + if write_window_complete = '1' then + if next_traceback(0) = '1' then + next_traceback(0) <= '0'; + next_traceback(1) <= '1'; + else + next_traceback(0) <= '1'; + next_traceback(1) <= '0'; + end if; + end if; + + if s_axis_input_tlast = '1' then + last_of_block <= '1'; + end if; + + end if; + end if; + end process pr_next_traceback; + + ------------------------------ + --- Portmapping components --- + ------------------------------ + + gen_generic_sp_ram : for i in 0 to 3 generate + begin + + addr(i) <= write_addr_ptr when (write_ram_fsm = RUN or write_ram_fsm = START) and to_integer(write_ram_ptr) = i else + read_addr_ptr(0) when (to_integer(read_ram_ptr(0)) = i and (read_ram_fsm(0) = TRACEBACK or read_ram_fsm(0) = WAIT_FOR_RAM or read_ram_fsm(0) = FINISH)) or + (next_traceback(0) = '1' and write_window_complete = '1' and to_integer(read_ram_ptr(0)) = i) else + read_addr_ptr(1); + + inst_generic_sp_ram : generic_sp_ram + generic map( + DISTR_RAM => DISTRIBUTED_RAM, + WORDS => MAX_WINDOW_LENGTH, + BITWIDTH => NUMBER_TRELLIS_STATES + ) + port map( + clk => clk, + rst => rst, + wen => wen_ram(i), + en => '1', + a => std_logic_vector(addr(i)), + d => s_axis_input_tdata, + q => q_reg(i) + ); + end generate gen_generic_sp_ram; + +end architecture rtl; |