diff options
Diffstat (limited to 'testsuite/gna/issue301')
-rw-r--r-- | testsuite/gna/issue301/Makefile | 71 | ||||
-rw-r--r-- | testsuite/gna/issue301/packages/pkg_components.vhd | 194 | ||||
-rw-r--r-- | testsuite/gna/issue301/packages/pkg_helper.vhd | 58 | ||||
-rw-r--r-- | testsuite/gna/issue301/packages/pkg_param.vhd | 78 | ||||
-rw-r--r-- | testsuite/gna/issue301/packages/pkg_param_derived.vhd | 73 | ||||
-rw-r--r-- | testsuite/gna/issue301/packages/pkg_trellis.vhd | 185 | ||||
-rw-r--r-- | testsuite/gna/issue301/packages/pkg_types.vhd | 37 | ||||
-rw-r--r-- | testsuite/gna/issue301/repro.vhdl | 40 | ||||
-rw-r--r-- | testsuite/gna/issue301/src/acs.vhd | 134 | ||||
-rw-r--r-- | testsuite/gna/issue301/src/axi4s_buffer.vhd | 130 | ||||
-rw-r--r-- | testsuite/gna/issue301/src/branch_distance.vhd | 111 | ||||
-rw-r--r-- | testsuite/gna/issue301/src/dec_viterbi.vhd | 399 | ||||
-rw-r--r-- | testsuite/gna/issue301/src/generic_sp_ram.vhd | 90 | ||||
-rw-r--r-- | testsuite/gna/issue301/src/ram_ctrl.vhd | 474 | ||||
-rw-r--r-- | testsuite/gna/issue301/src/recursion.vhd | 96 | ||||
-rw-r--r-- | testsuite/gna/issue301/src/reorder.vhd | 129 | ||||
-rw-r--r-- | testsuite/gna/issue301/src/traceback.vhd | 101 | ||||
-rwxr-xr-x | testsuite/gna/issue301/testsuite.sh | 29 |
18 files changed, 2429 insertions, 0 deletions
diff --git a/testsuite/gna/issue301/Makefile b/testsuite/gna/issue301/Makefile new file mode 100644 index 000000000..95610d340 --- /dev/null +++ b/testsuite/gna/issue301/Makefile @@ -0,0 +1,71 @@ +############################################################################### +# Copyright (c) 2015 Potential Ventures Ltd +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Potential Ventures Ltd, +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL POTENTIAL VENTURES LTD BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### + +TOPLEVEL_LANG ?= vhdl + +ifneq ($(TOPLEVEL_LANG),vhdl) + +all: + @echo "Skipping test due to TOPLEVEL_LANG=$(TOPLEVEL_LANG) not being vhdl" +clean:: + +else + +TOPLEVEL = dec_viterbi_top + +ifeq ($(OS),Msys) +WPWD=$(shell sh -c 'pwd -W') +else +WPWD=$(shell pwd) +endif + +COCOTB?=$(WPWD)/../../.. + +SRC_BASE = $(COCOTB)/tests/designs/viterbi_decoder_axi4s + +RTL_LIBRARY = dec_viterbi + +VHDL_SOURCES = $(SRC_BASE)/packages/pkg_helper.vhd \ + $(SRC_BASE)/packages/pkg_param.vhd \ + $(SRC_BASE)/packages/pkg_param_derived.vhd \ + $(SRC_BASE)/packages/pkg_types.vhd \ + $(SRC_BASE)/packages/pkg_components.vhd \ + $(SRC_BASE)/packages/pkg_trellis.vhd \ + $(SRC_BASE)/src/generic_sp_ram.vhd \ + $(SRC_BASE)/src/axi4s_buffer.vhd \ + $(SRC_BASE)/src/branch_distance.vhd \ + $(SRC_BASE)/src/traceback.vhd \ + $(SRC_BASE)/src/acs.vhd \ + $(SRC_BASE)/src/ram_ctrl.vhd \ + $(SRC_BASE)/src/reorder.vhd \ + $(SRC_BASE)/src/recursion.vhd \ + $(SRC_BASE)/src/dec_viterbi.vhd \ + +include $(COCOTB)/makefiles/Makefile.inc +include $(COCOTB)/makefiles/Makefile.sim + +endif diff --git a/testsuite/gna/issue301/packages/pkg_components.vhd b/testsuite/gna/issue301/packages/pkg_components.vhd new file mode 100644 index 000000000..fc86159aa --- /dev/null +++ b/testsuite/gna/issue301/packages/pkg_components.vhd @@ -0,0 +1,194 @@ +--! +--! 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 Component declarations for Viterbi decoder +--! @author Markus Fehrenz +--! @date 2011/04/07 +--! +--! + +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; + +package pkg_components is + + component axi4s_buffer is + generic ( + DATA_WIDTH : natural := 1 + ); + port ( + clk : in std_logic; + rst : in std_logic; + + input : in std_logic_vector(DATA_WIDTH - 1 downto 0); + input_valid : in std_logic; + input_last : in std_logic; + input_accept : out std_logic; + + output : out std_logic_vector(DATA_WIDTH - 1 downto 0); + output_valid : out std_logic; + output_last : out std_logic; + output_accept : in std_logic + ); + end component axi4s_buffer; + + component branch_distance is + generic( + EDGE_WEIGHT : in std_logic_vector(NUMBER_PARITY_BITS - 1 downto 0) + ); + port( + clk : in std_logic; + rst : in std_logic; + + s_axis_input_tvalid : in std_logic; + s_axis_input_tdata : in t_input_block; + s_axis_input_tlast : in std_logic; + s_axis_input_tready : out std_logic; + + m_axis_output_tvalid : out std_logic; + m_axis_output_tdata : out std_logic_vector(BW_BRANCH_RESULT - 1 downto 0); + m_axis_output_tlast : out std_logic; + m_axis_output_tready : in std_logic + + ); + end component branch_distance; + + component acs is + generic( + initialize_value : in signed(BW_MAX_PROBABILITY - 1 downto 0) + ); + port( + clk : in std_logic; + rst : in std_logic; + + s_axis_inbranch_tvalid : in std_logic; + s_axis_inbranch_tdata_low : in std_logic_vector(BW_BRANCH_RESULT - 1 downto 0); + s_axis_inbranch_tdata_high : in std_logic_vector(BW_BRANCH_RESULT - 1 downto 0); + s_axis_inbranch_tlast : in std_logic; + s_axis_inbranch_tready : out std_logic; + + s_axis_inprev_tvalid : in std_logic; + s_axis_inprev_tdata_low : in std_logic_vector(BW_MAX_PROBABILITY - 1 downto 0); + s_axis_inprev_tdata_high : in std_logic_vector(BW_MAX_PROBABILITY - 1 downto 0); + s_axis_inprev_tready : out std_logic; + + m_axis_outprob_tvalid : out std_logic; + m_axis_outprob_tdata : out std_logic_vector(BW_MAX_PROBABILITY - 1 downto 0); + m_axis_outprob_tready : in std_logic; + + m_axis_outdec_tvalid : out std_logic; + m_axis_outdec_tdata : out std_logic; + m_axis_outdec_tlast : out std_logic; + m_axis_outdec_tready : in std_logic + ); + end component acs; + + component ram_ctrl is + port( + clk : in std_logic; + rst : in std_logic; + + 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; + + 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); + m_axis_output_window_tuser : out std_logic_vector(1 downto 0); + m_axis_output_last_tuser : out std_logic_vector(1 downto 0); + + 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 component ram_ctrl; + + component generic_sp_ram is + generic( + DISTR_RAM : boolean; + WORDS : integer; + BITWIDTH : integer + ); + port( + clk : in std_logic; + rst : in std_logic; + + wen : in std_logic; + en : in std_logic; + + a : in std_logic_vector(BW_MAX_WINDOW_LENGTH - 1 downto 0); + d : in std_logic_vector(BITWIDTH - 1 downto 0 ); + q : out std_logic_vector(BITWIDTH - 1 downto 0) + ); + end component generic_sp_ram; + + component trellis_traceback is + port( + clk : in std_logic; + rst : in std_logic; + + 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; + s_axis_input_window_tuser : in std_logic; + s_axis_input_last_tuser : in std_logic; + + m_axis_output_tvalid : out std_logic; + m_axis_output_tdata : out std_logic; + m_axis_output_tlast : out std_logic; + m_axis_output_last_tuser : out std_logic; + m_axis_output_tready : in std_logic + ); + end component trellis_traceback; + + component reorder is + port( + clk : in std_logic; + rst : in std_logic; + + s_axis_input_tvalid : in std_logic; + s_axis_input_tdata : in std_logic; + s_axis_input_tlast : in std_logic; + s_axis_input_last_tuser : in std_logic; + s_axis_input_tready : out std_logic; + + m_axis_output_tvalid : out std_logic; + m_axis_output_tdata : out std_logic; + m_axis_output_tlast : out std_logic; + m_axis_output_last_tuser : out std_logic; + m_axis_output_tready : in std_logic + ); + end component reorder; + + component recursionx is + port( + clk : in std_logic; + rst : in std_logic; + + s_axis_input_tvalid : in std_logic; + s_axis_input_tdata : in std_logic; + s_axis_input_tlast : in std_logic; + s_axis_input_tready : out std_logic; + + m_axis_output_tvalid : out std_logic; + m_axis_output_tdata : out std_logic; + m_axis_output_tlast : out std_logic; + m_axis_output_tready : in std_logic + ); + end component recursionx; + +end package pkg_components; diff --git a/testsuite/gna/issue301/packages/pkg_helper.vhd b/testsuite/gna/issue301/packages/pkg_helper.vhd new file mode 100644 index 000000000..8fe59743a --- /dev/null +++ b/testsuite/gna/issue301/packages/pkg_helper.vhd @@ -0,0 +1,58 @@ +--! +--! 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 Helper package with useful functions +--! @author Markus Fehrenz +--! @date 2011/12/02 +--! + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + + +package pkg_helper is + + --! + --! Return the log_2 of an natural value, i.e. the number of bits required + --! to represent this unsigned value. + --! + function no_bits_natural(value_in : natural) return natural; + + --! Return maximum of two input values + function max(value_in_a, value_in_b : natural) return natural; + +end pkg_helper; + + +package body pkg_helper is + + function no_bits_natural(value_in: natural) return natural is + variable v_n_bit : unsigned(31 downto 0); + begin + if value_in = 0 then + return 0; + end if; + v_n_bit := to_unsigned(value_in, 32); + for i in 31 downto 0 loop + if v_n_bit(i) = '1' then + return i + 1; + end if; + end loop; + return 1; + end no_bits_natural; + + function max(value_in_a, value_in_b : natural) return natural is + begin + if value_in_a > value_in_b then + return value_in_a; + else + return value_in_b; + end if; + end function; + +end pkg_helper; diff --git a/testsuite/gna/issue301/packages/pkg_param.vhd b/testsuite/gna/issue301/packages/pkg_param.vhd new file mode 100644 index 000000000..1bc3f6602 --- /dev/null +++ b/testsuite/gna/issue301/packages/pkg_param.vhd @@ -0,0 +1,78 @@ +--! +--! 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 Parameters +--! @author Markus Fehrenz +--! @date 2011/07/01 +--! +--! @details This is the configuration file of the Viterbi decoder. +--! Any changes for parameters should be done here. +--! Changing parameters somewhere else may result in a malicious +--! behavior. +--! + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + + +package pkg_param is + ----------------------------------- + -- Convolutional Code Parameters -- + ----------------------------------- + + + -- + -- Set the number of parity values + -- This has to correspond to PARITY_POLYNOMIALS + -- + constant NUMBER_PARITY_BITS : natural := 2; + type t_parity is array (NUMBER_PARITY_BITS - 1 downto 0) of natural; + + -- + -- Set parity polynoms in decimal notation + -- NUMBER_PARITY_BITS has to correspond to the number of elements + -- Examples: WiFi : [121,91] or [121,91,101] + -- CDMA : [491,369] or [367,435,369] or [501,441,331,315] + -- GSM : [27,19] or [27,21,31] + -- DAB : [91,121,101,91] + -- WiMAX: [91,121,117] + -- + constant PARITY_POLYNOMIALS : t_parity := (121,91); + + + -- + -- Set a recursive polynomial + -- Set to 0 if no recursion is used + -- Setting this arbitrary may result in a worse error correction ability + -- + constant FEEDBACK_POLYNOMIAL : natural := 0; + + + ----------------------------- + -- Architecture Parameters -- + ----------------------------- + + -- + -- Set bit width of LLR input + -- Recommended values: 3 or 4 + -- + constant BW_LLR_INPUT : natural := 4; + + -- + -- Set the maximum window length which shall be allowed at runtime. + -- Recommended: at least 6 * constraint length + -- + constant MAX_WINDOW_LENGTH : natural := 96; + + -- + -- Set to 'true' if distributed RAM shall be used + -- Set to 'false' if block RAM shall be used + -- + constant DISTRIBUTED_RAM : boolean := true; + +end package pkg_param; diff --git a/testsuite/gna/issue301/packages/pkg_param_derived.vhd b/testsuite/gna/issue301/packages/pkg_param_derived.vhd new file mode 100644 index 000000000..f6e77cada --- /dev/null +++ b/testsuite/gna/issue301/packages/pkg_param_derived.vhd @@ -0,0 +1,73 @@ +--! +--! 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 Derived parameters +--! @author Markus Fehrenz +--! @date 2011/07/04 +--! +--! @details This constants are derived from constants defined in pkg_param. +--! In order to prevent errors, there is no user choice for these parameters. +--! + +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_helper.all; + + +package pkg_param_derived is + + -- Calculation of constraint length. + function calc_constraint_length return natural; + + -- Memory depth of the encoder shift register. + constant ENCODER_MEMORY_DEPTH : natural; + + -- Number of trellis states corresponds to the nubmer of ACS units. + constant NUMBER_TRELLIS_STATES : natural; + + -- Number of branch units for a single polynomial set + constant NUMBER_BRANCH_UNITS : natural; + + -- Bitwidth constants are needed for type conversions + constant BW_TRELLIS_STATES : natural; + constant BW_MAX_WINDOW_LENGTH : natural; + constant BW_BRANCH_RESULT : natural; + constant BW_MAX_PROBABILITY : natural; + +end package pkg_param_derived; + + +package body pkg_param_derived is + + function calc_constraint_length return natural is + variable v_maximum : natural := 0; + begin + + -- Find the position of the leftmost bit in the polynomials. + for i in NUMBER_PARITY_BITS - 1 downto 0 loop + v_maximum := max(v_maximum, no_bits_natural(PARITY_POLYNOMIALS(i))); + end loop; + v_maximum := max(v_maximum, no_bits_natural(FEEDBACK_POLYNOMIAL)); + return v_maximum; + end function calc_constraint_length; + + + constant ENCODER_MEMORY_DEPTH : natural := calc_constraint_length - 1; + + constant NUMBER_TRELLIS_STATES : natural := 2 ** ENCODER_MEMORY_DEPTH; + + constant NUMBER_BRANCH_UNITS : natural := 2 ** NUMBER_PARITY_BITS; + + constant BW_TRELLIS_STATES : natural := no_bits_natural(NUMBER_TRELLIS_STATES - 1); + constant BW_MAX_WINDOW_LENGTH : natural := no_bits_natural(MAX_WINDOW_LENGTH - 1); + constant BW_BRANCH_RESULT : natural := no_bits_natural((2 ** (BW_LLR_INPUT - 1)) * NUMBER_PARITY_BITS) + 1; + constant BW_MAX_PROBABILITY : natural := no_bits_natural(((2 ** (BW_LLR_INPUT - 1)) * NUMBER_PARITY_BITS) * 4 * ENCODER_MEMORY_DEPTH); +end package body pkg_param_derived; diff --git a/testsuite/gna/issue301/packages/pkg_trellis.vhd b/testsuite/gna/issue301/packages/pkg_trellis.vhd new file mode 100644 index 000000000..a797d3ac9 --- /dev/null +++ b/testsuite/gna/issue301/packages/pkg_trellis.vhd @@ -0,0 +1,185 @@ +--! +--! 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 Trellis parameter calculations (e.g., transitions, init values). +--! @author Markus Fehrenz +--! @date 2011/07/27 +--! +--! + +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; + + +package pkg_trellis is + + type t_prev_base is array (1 downto 0) of std_logic_vector(BW_TRELLIS_STATES - 1 downto 0); + type t_previous_states is array (NUMBER_TRELLIS_STATES - 1 downto 0) of t_prev_base; + + type t_trans_base is array (1 downto 0) of std_logic_vector(NUMBER_PARITY_BITS - 1 downto 0); + type t_transitions is array (NUMBER_TRELLIS_STATES - 1 downto 0) of t_trans_base; + + type t_trans_base_signed is array (1 downto 0) of std_logic_vector(NUMBER_PARITY_BITS downto 0); + type t_transitions_signed is array (NUMBER_TRELLIS_STATES - 1 downto 0) of t_trans_base_signed; + + + -- + -- This function calculates the previous states of each state. + -- The values are used to connect the ACS units. + -- + function calc_previous_states return t_previous_states; + + + -- + -- This function calculates corresponding transitions to a trellis sate. + -- The values are used to connect branch units to ACS units. + -- + function calc_transitions return t_transitions; + + + -- + -- This function calculates the initialization values for trellis metrics. + -- The values are used as a constant and written to the ACS unit, every time a new block arrives. + -- + function calc_initialize return t_node_s; + + constant PREVIOUS_STATES : t_previous_states; + constant TRANSITIONS : t_transitions; + constant INITIALIZE_TRELLIS : t_node_s; + +end package pkg_trellis; + + +package body pkg_trellis is + + + function calc_previous_states return t_previous_states is + variable v_prev_states : t_previous_states := (others=>(others=>(others => '0'))); + variable v_state0, v_state1 : std_logic_vector(BW_TRELLIS_STATES - 1 downto 0); + begin + for i in NUMBER_TRELLIS_STATES - 1 downto 0 loop + v_state0 := std_logic_vector(to_unsigned(i,BW_TRELLIS_STATES)); + v_state1 := v_state0(BW_TRELLIS_STATES - 2 downto 0) & '0'; + v_prev_states(i)(0) := v_state1; + v_state1 := v_state0(BW_TRELLIS_STATES - 2 downto 0) & '1'; + v_prev_states(i)(1) := v_state1; + end loop; + return v_prev_states; + end function calc_previous_states; + + + function calc_transitions return t_transitions is + variable v_transitions : t_transitions_signed := (others => (others => (others => '0'))); + variable v_transitions_out : t_transitions := (others => (others => (others => '0'))); + variable v_one_transition : std_logic_vector(NUMBER_PARITY_BITS - 1 downto 0); + variable v_next_state : unsigned(ENCODER_MEMORY_DEPTH - 1 downto 0) := (others => '0'); + variable v_state, v_states : unsigned(ENCODER_MEMORY_DEPTH downto 0); + variable v_bit : std_logic := '0'; + begin + + -- + -- It is possible to reduce code size at this stage, if feedback is handled differently, + -- but the complexity will increase. + -- + + for i in NUMBER_TRELLIS_STATES - 1 downto 0 loop + + -- + -- for input : 0 + -- determine correct input with feedback + -- + v_next_state := to_unsigned(i,ENCODER_MEMORY_DEPTH) and to_unsigned(FEEDBACK_POLYNOMIAL, ENCODER_MEMORY_DEPTH); + for k in ENCODER_MEMORY_DEPTH - 1 downto 0 loop + v_bit := v_bit xor v_next_state(k); + end loop; + v_state(ENCODER_MEMORY_DEPTH) := v_bit; + v_state(ENCODER_MEMORY_DEPTH - 1 downto 0) := to_unsigned(i,ENCODER_MEMORY_DEPTH); + v_next_state := v_state(ENCODER_MEMORY_DEPTH downto 1); + v_bit := '0'; + + -- determine paritybits + for j in NUMBER_PARITY_BITS - 1 downto 0 loop + v_states := v_state and to_unsigned(PARITY_POLYNOMIALS(j), ENCODER_MEMORY_DEPTH + 1); + for k in ENCODER_MEMORY_DEPTH downto 0 loop + v_bit := v_bit xor v_states(k); + end loop; + v_one_transition(j) := v_bit; + v_bit := '0'; + end loop; + + -- decide where to save the parity result + if v_transitions(to_integer(v_next_state))(1)(NUMBER_PARITY_BITS) = '0' then + v_transitions(to_integer(v_next_state))(1)(NUMBER_PARITY_BITS) := '1'; + v_transitions(to_integer(v_next_state))(1)(NUMBER_PARITY_BITS - 1 downto 0) := v_one_transition; + else + v_transitions(to_integer(v_next_state))(0)(NUMBER_PARITY_BITS - 1 downto 0) := v_one_transition; + end if; + + -- + -- for input: 1 + -- determine correct input with feedback + -- + v_next_state := to_unsigned(i,ENCODER_MEMORY_DEPTH) and to_unsigned(FEEDBACK_POLYNOMIAL, ENCODER_MEMORY_DEPTH); + for k in ENCODER_MEMORY_DEPTH - 1 downto 0 loop + v_bit := v_bit xor v_next_state(k); + end loop; + v_state(ENCODER_MEMORY_DEPTH) := '1' xor v_bit; + v_state(ENCODER_MEMORY_DEPTH - 1 downto 0) := to_unsigned(i,ENCODER_MEMORY_DEPTH); + v_next_state := v_state(ENCODER_MEMORY_DEPTH downto 1); + v_bit := '0'; + + -- determine paritybits + for j in NUMBER_PARITY_BITS - 1 downto 0 loop + v_states := v_state and to_unsigned(PARITY_POLYNOMIALS(j), ENCODER_MEMORY_DEPTH + 1); + for k in ENCODER_MEMORY_DEPTH downto 0 loop + v_bit := v_bit xor v_states(k); + end loop; + v_one_transition(j) := v_bit; + v_bit := '0'; + end loop; + + -- decide where to save parity result + if v_transitions(to_integer(v_next_state))(1)(NUMBER_PARITY_BITS) = '0' then + v_transitions(to_integer(v_next_state))(1)(NUMBER_PARITY_BITS) := '1'; + v_transitions(to_integer(v_next_state))(1)(NUMBER_PARITY_BITS - 1 downto 0) := v_one_transition; + else + v_transitions(to_integer(v_next_state))(0)(NUMBER_PARITY_BITS - 1 downto 0) := v_one_transition; + end if; + end loop; + + -- truncate, the bit, used to decide where to save parity result + for i in NUMBER_TRELLIS_STATES - 1 downto 0 loop + v_transitions_out(i)(1) := v_transitions(i)(1)(NUMBER_PARITY_BITS - 1 downto 0); + v_transitions_out(i)(0) := v_transitions(i)(0)(NUMBER_PARITY_BITS - 1 downto 0); + end loop; + + return v_transitions_out; + end function calc_transitions; + + + function calc_initialize return t_node_s is + variable v_initialize : t_node_s; + begin + v_initialize(0) := to_signed(0, BW_MAX_PROBABILITY); + for i in NUMBER_TRELLIS_STATES - 1 downto 1 loop + v_initialize(i) := to_signed(- 2 ** (BW_MAX_PROBABILITY - 2), BW_MAX_PROBABILITY); + end loop; + return v_initialize; + end function calc_initialize; + + + constant PREVIOUS_STATES : t_previous_states := calc_previous_states; + constant TRANSITIONS : t_transitions := calc_transitions; + constant INITIALIZE_TRELLIS : t_node_s := calc_initialize; + +end package body pkg_trellis; diff --git a/testsuite/gna/issue301/packages/pkg_types.vhd b/testsuite/gna/issue301/packages/pkg_types.vhd new file mode 100644 index 000000000..5acdd4ebe --- /dev/null +++ b/testsuite/gna/issue301/packages/pkg_types.vhd @@ -0,0 +1,37 @@ +--! +--! 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 Global types for the Viterbi decoder +--! @author Markus Fehrenz +--! @date 2011/07/04 +--! +--! @details Most types are shared and used in different context. +--! Changing single types should be done with adding an additional type. +--! + +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; + +package pkg_types is + + -- Parity structure: p1_bit, p2_bit, ..., pN_bit + type t_input_block is array (NUMBER_PARITY_BITS - 1 downto 0) of signed(BW_LLR_INPUT - 1 downto 0); + + -- Types are used for bulk information to ACS and branch unit. + type t_node_s is array (NUMBER_TRELLIS_STATES - 1 downto 0) of signed(BW_MAX_PROBABILITY - 1 downto 0); + type t_node is array (NUMBER_TRELLIS_STATES - 1 downto 0) of std_logic_vector(BW_MAX_PROBABILITY - 1 downto 0); + type t_branch is array (NUMBER_BRANCH_UNITS - 1 downto 0) of std_logic_vector(BW_BRANCH_RESULT - 1 downto 0); + + -- RAM Data + type t_ram_rd_data is array (1 downto 0) of std_logic_vector(NUMBER_TRELLIS_STATES - 1 downto 0); + +end package pkg_types; diff --git a/testsuite/gna/issue301/repro.vhdl b/testsuite/gna/issue301/repro.vhdl new file mode 100644 index 000000000..04369e197 --- /dev/null +++ b/testsuite/gna/issue301/repro.vhdl @@ -0,0 +1,40 @@ +package pkgc is + constant width : natural; +end pkgc; + +package body pkgc is + constant width : natural := 4; +end pkgc; + +use work.pkgc.all; +package pkgcomp is + component comp is + generic (val : bit_vector (width -1 downto 0)); + port (b : out bit); + end component; +end pkgcomp; + +use work.pkgc.all; +entity comp is + generic (val : bit_vector (width -1 downto 0)); + port (b : out bit); +end comp; + +architecture behav of comp is +begin + b <= val (val'left); +end behav; + +entity repro is +end repro; + +use work.pkgc.all; +use work.pkgcomp.all; + +architecture behav of repro is + signal res : bit; +begin + inst : comp + generic map (val => "0010") + port map (b => res ); +end behav; diff --git a/testsuite/gna/issue301/src/acs.vhd b/testsuite/gna/issue301/src/acs.vhd new file mode 100644 index 000000000..958050a39 --- /dev/null +++ b/testsuite/gna/issue301/src/acs.vhd @@ -0,0 +1,134 @@ +--! +--! 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 Add-compare-select unit for trellis processing. +--! @author Markus Fehrenz +--! @date 2011/07/04 +--! +--! @details The ACS decides which path is the the surviving trellis path. +--! In the design there are 2^{K-1} ACS instances. +--! + +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_helper.all; + + +entity acs is + generic( + + -- Reset value + INITIALIZE_VALUE : in signed(BW_MAX_PROBABILITY - 1 downto 0) + ); + port( + clk : in std_logic; + rst : in std_logic; + + -- + -- Values from branch distance, signed values in std_logic_vector + -- high is located in the upper half. + -- + s_axis_inbranch_tvalid : in std_logic; + s_axis_inbranch_tdata_low : in std_logic_vector(BW_BRANCH_RESULT - 1 downto 0); + s_axis_inbranch_tdata_high : in std_logic_vector(BW_BRANCH_RESULT - 1 downto 0); + s_axis_inbranch_tlast : in std_logic; + s_axis_inbranch_tready : out std_logic; + + -- + -- Probabilities from previous nodes, signed values in std_logic_vector + -- high is located in the upper half. + -- + s_axis_inprev_tvalid : in std_logic; + s_axis_inprev_tdata_low : in std_logic_vector(BW_MAX_PROBABILITY - 1 downto 0); + s_axis_inprev_tdata_high : in std_logic_vector(BW_MAX_PROBABILITY - 1 downto 0); + s_axis_inprev_tready : out std_logic; + + -- probability result of the add compare and select + m_axis_outprob_tvalid : out std_logic; + m_axis_outprob_tdata : out std_logic_vector(BW_MAX_PROBABILITY - 1 downto 0); + m_axis_outprob_tready : in std_logic; + + -- decision result of the add compare and select + m_axis_outdec_tvalid : out std_logic; + m_axis_outdec_tdata : out std_logic; + m_axis_outdec_tlast : out std_logic; + m_axis_outdec_tready : in std_logic + ); +end entity acs; + + +architecture rtl of acs is + + signal s_axis_inbranch_tlast_d : std_logic; + signal m_axis_outdec_tvalid_int : std_logic; + signal s_axis_inbranch_tready_int : std_logic; + +begin + + s_axis_inbranch_tready_int <= '1' when m_axis_outdec_tready = '1' or m_axis_outdec_tvalid_int = '0' else + '0'; + s_axis_inbranch_tready <= s_axis_inbranch_tready_int; + m_axis_outdec_tvalid <= m_axis_outdec_tvalid_int; + + -- Add branch to previous, compare both paths and select survivor. + pr_add_compare : process(clk) is + variable v_diff, v_high, v_low : signed(BW_MAX_PROBABILITY - 1 downto 0); + begin + if rising_edge(clk) then + if rst = '1' then + m_axis_outdec_tvalid_int <= '0'; + m_axis_outdec_tdata <= '0'; + m_axis_outdec_tlast <= '0'; + m_axis_outprob_tvalid <= '0'; + s_axis_inprev_tready <= '0'; + s_axis_inbranch_tlast_d <= '0'; + m_axis_outprob_tdata <= std_logic_vector(INITIALIZE_VALUE); + else + -- If this is the last value, prepare for processing of next incoming value. + if s_axis_inbranch_tlast_d = '1' then + m_axis_outprob_tdata <= std_logic_vector(INITIALIZE_VALUE); + s_axis_inbranch_tlast_d <= '0'; + m_axis_outdec_tvalid_int <= '0'; + end if; + if m_axis_outdec_tvalid_int = '1' and m_axis_outdec_tready = '1' then + m_axis_outdec_tvalid_int <= '0'; + end if; + + -- Process only if we receive valid data. + if s_axis_inbranch_tvalid = '1' and s_axis_inbranch_tready_int = '1' then + s_axis_inbranch_tlast_d <= s_axis_inbranch_tlast; + + -- Add. + v_low := signed(s_axis_inbranch_tdata_low) + signed(s_axis_inprev_tdata_low); + v_high := signed(s_axis_inbranch_tdata_high) + signed(s_axis_inprev_tdata_high); + + -- Use modulo normalization, do not extend the sign here! + v_diff := v_low - v_high; + + -- Compare, select the correct path. + if v_diff < 0 then + m_axis_outdec_tdata <= '1'; + m_axis_outprob_tdata <= std_logic_vector(v_high); + else + m_axis_outdec_tdata <= '0'; + m_axis_outprob_tdata <= std_logic_vector(v_low); + end if; + m_axis_outdec_tvalid_int <= '1'; + end if; + + m_axis_outdec_tlast <= s_axis_inbranch_tlast; + end if; + end if; + end process pr_add_compare; + +end architecture rtl; diff --git a/testsuite/gna/issue301/src/axi4s_buffer.vhd b/testsuite/gna/issue301/src/axi4s_buffer.vhd new file mode 100644 index 000000000..8e479162e --- /dev/null +++ b/testsuite/gna/issue301/src/axi4s_buffer.vhd @@ -0,0 +1,130 @@ +--! +--! Copyright (C) 2012 - 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 AXI4-Stream buffer that allows to buffer the accept-signal. +--! @author Matthias Alles +--! @date 2012/04/18 +--! +--! @details +--! One problem when concatenating multiple AXI4-Stream builind blocks is that +--! the accept signal has to pass from the very last component to the input +--! of the very first component. Only then it is possible to have an interruption +--! free data processing within the whole chain. The drawback of this approach is +--! that the accept signal has a long path and high fanouts. +--! This entity allows to use registers on the accept signals by introducing buffers +--! for storing the input values. It should improve timing of bigger building blocks. +--! + +library ieee; +use ieee.std_logic_1164.all; + + +entity axi4s_buffer is + generic ( + DATA_WIDTH : natural := 1 + ); + port ( + + clk : in std_logic; + rst : in std_logic; + + -- Input data handling + ---------------------- + + input : in std_logic_vector(DATA_WIDTH - 1 downto 0); + input_valid : in std_logic; + input_last : in std_logic; + input_accept : out std_logic; + + + -- Output data handling + ----------------------- + output : out std_logic_vector(DATA_WIDTH - 1 downto 0); + output_valid : out std_logic; + output_last : out std_logic; + output_accept : in std_logic +); +end entity axi4s_buffer; + + +architecture rtl of axi4s_buffer is + + + signal input_accept_int : std_logic; + + signal output_reg : std_logic_vector(DATA_WIDTH - 1 downto 0); + signal output_last_reg : std_logic; + signal output_valid_reg : std_logic; + + signal buffer_full : std_logic; + signal buffer_data : std_logic_vector(DATA_WIDTH - 1 downto 0); + signal buffer_last : std_logic; + +begin + + input_accept <= input_accept_int; + + output <= output_reg; + output_last <= output_last_reg; + output_valid <= output_valid_reg; + + -- + -- This process registers all signals. + -- No combinatorial logic is bypassed from input to output and vice versa. + -- + pr_reg: process(clk) is + begin + if rising_edge(clk) then + if rst = '1' then + output_reg <= (others => '0'); + output_last_reg <= '0'; + output_valid_reg <= '0'; + + input_accept_int <= '1'; + + buffer_full <= '0'; + buffer_data <= (others => '0'); + buffer_last <= '0'; + else + + -- + -- Data is coming, buf output data can't be sent => Store input data in buffer + -- and remove input_accept signal! + -- + if input_valid = '1' and input_accept_int = '1' and output_valid_reg = '1' and output_accept = '0' then + buffer_data <= input; + buffer_last <= input_last; + buffer_full <= '1'; + input_accept_int <= '0'; + end if; + + -- + -- Output data is being read but there is data in the buffer waiting for being sent + -- => Use the buffer data! + -- + if output_accept = '1' and output_valid_reg = '1' and buffer_full = '1' then + output_reg <= buffer_data; + output_last_reg <= buffer_last; + output_valid_reg <= '1'; + buffer_full <= '0'; + input_accept_int <= '1'; + + -- + -- Data is being read and buffer is empty => Use input data directly! + -- Output register is empty => Use input data directly! + -- + elsif (output_accept = '1' and output_valid_reg = '1') or output_valid_reg = '0' then + output_reg <= input; + output_last_reg <= input_last; + output_valid_reg <= input_valid; + end if; + + end if; + end if; + end process pr_reg; + +end architecture rtl; diff --git a/testsuite/gna/issue301/src/branch_distance.vhd b/testsuite/gna/issue301/src/branch_distance.vhd new file mode 100644 index 000000000..31aa9a4fd --- /dev/null +++ b/testsuite/gna/issue301/src/branch_distance.vhd @@ -0,0 +1,111 @@ +--! +--! 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 Branch distance calculation unit. +--! @author Markus Fehrenz +--! @date 2011/08/04 +--! +--! @details Each branch has to be calculated only once. +--! The branch calculations are configured with a generic. +--! There is no limitation in branch calculations. +--! + +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; + + +entity branch_distance is + generic( + EDGE_WEIGHT : in std_logic_vector(0 to NUMBER_PARITY_BITS - 1) + ); + port( + clk : in std_logic; + rst : in std_logic; + + -- + -- Input LLR values + -- + s_axis_input_tvalid : in std_logic; + s_axis_input_tdata : in t_input_block; + s_axis_input_tlast : in std_logic; + s_axis_input_tready : out std_logic; + + + -- + -- Output branch metrics, going to ACS unit. + -- + m_axis_output_tvalid : out std_logic; + m_axis_output_tdata : out std_logic_vector(BW_BRANCH_RESULT - 1 downto 0); + m_axis_output_tlast : out std_logic; + m_axis_output_tready : in std_logic + ); +end entity branch_distance; + + +architecture rtl of branch_distance is + + signal m_axis_output_tvalid_int : std_logic; + signal s_axis_input_tready_int : std_logic; + +begin + + -- We are ready, when we are allowed to write to the output, or the output is idle. + s_axis_input_tready_int <= '1' when m_axis_output_tready = '1' else + '0'; + + -- Connect internal versions of signal to output port. + s_axis_input_tready <= s_axis_input_tready_int; + m_axis_output_tvalid <= m_axis_output_tvalid_int; + + + -- Calculation of specific branch distance with a geometric distance function. + pr_branch : process(clk) is + variable v_branch_result : integer; + begin + if rising_edge(clk) then + if rst = '1' then + m_axis_output_tvalid_int <= '0'; + m_axis_output_tdata <= (others => '0'); + m_axis_output_tlast <= '0'; + else + + if m_axis_output_tvalid_int = '1' and m_axis_output_tready = '1' then + m_axis_output_tvalid_int <= '0'; + m_axis_output_tlast <= '0'; + end if; + + if s_axis_input_tready_int = '1' and s_axis_input_tvalid = '1' then + v_branch_result := 0; + + for i in NUMBER_PARITY_BITS - 1 downto 0 loop + + -- + -- Either the value is added or subtracted, depending on + -- the current branch metric we are computing. + -- + if EDGE_WEIGHT(i) = '0' then + v_branch_result := v_branch_result + to_integer(s_axis_input_tdata(i)); + else + v_branch_result := v_branch_result - to_integer(s_axis_input_tdata(i)); + end if; + end loop; + m_axis_output_tdata <= std_logic_vector(to_signed(v_branch_result, BW_BRANCH_RESULT)); + m_axis_output_tvalid_int <= '1'; + m_axis_output_tlast <= s_axis_input_tlast; + end if; + + end if; + end if; + end process pr_branch; + +end architecture rtl; diff --git a/testsuite/gna/issue301/src/dec_viterbi.vhd b/testsuite/gna/issue301/src/dec_viterbi.vhd new file mode 100644 index 000000000..f81d281a8 --- /dev/null +++ b/testsuite/gna/issue301/src/dec_viterbi.vhd @@ -0,0 +1,399 @@ +--! +--! 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 top entity, connecting all decoder units. +--! @author Markus Fehrenz +--! @date 2011/12/05 +--! +--! @details +--! The AXI std_logic_vector input is mapped to an internal information type. +--! Further the correct output order is handled. +--! + +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; +use dec_viterbi.pkg_trellis.all; + + +entity dec_viterbi_top is + port( + + -- + -- The core only uses AXI4-Stream interfaces, + -- based on AMBA4 AXI4-Stream Protocol with restrictions according to + -- Xilinx Migration, described in Xilinx AXI Reference UG761 (v13.3). + -- + + aclk : in std_logic; + + -- Synchronous reset, active low. + aresetn : in std_logic; + + + -- + -- Slave (input) data signals + -- Delivers the parity LLR values, one byte per LLR value. + -- + s_axis_input_tvalid : in std_logic; + s_axis_input_tdata : in std_logic_vector(31 downto 0); + s_axis_input_tlast : in std_logic; + s_axis_input_tready : out std_logic; + + + -- + -- Master (output) data signals + -- Delivers the decoded systematic (payload) bits. + -- + m_axis_output_tvalid : out std_logic; + m_axis_output_tdata : out std_logic; + m_axis_output_tlast : out std_logic; + m_axis_output_tready : in std_logic; + + + -- + -- Slave (input) configuration signals + -- Configures window length and acquisition length. + -- + s_axis_ctrl_tvalid : in std_logic; + s_axis_ctrl_tdata : in std_logic_vector(31 downto 0); + s_axis_ctrl_tlast : in std_logic; + s_axis_ctrl_tready : out std_logic +); +end entity dec_viterbi_top; + + +architecture rtl of dec_viterbi_top is + + alias clk is aclk; + signal rst : std_logic; + + -- split tdata into input array + signal input : t_input_block; + + -- buffer signals + signal buffer_tdata : std_logic_vector(31 downto 0); + signal buffer_tvalid : std_logic; + signal buffer_tlast : std_logic; + + -- branch signals + signal branch_tvalid : std_logic_vector(NUMBER_BRANCH_UNITS - 1 downto 0); + signal branch_tdata : t_branch; + signal branch_tlast : std_logic_vector(NUMBER_BRANCH_UNITS - 1 downto 0); + signal branch_tready : std_logic_vector(NUMBER_BRANCH_UNITS - 1 downto 0); + + -- acs signals + signal acs_tvalid : std_logic_vector(NUMBER_TRELLIS_STATES - 1 downto 0); + signal acs_tlast : std_logic_vector(NUMBER_TRELLIS_STATES - 1 downto 0); + signal acs_tready : std_logic_vector(NUMBER_TRELLIS_STATES - 1 downto 0); + signal acs_dec_tdata : std_logic_vector(NUMBER_TRELLIS_STATES - 1 downto 0); + signal acs_prob_tdata : t_node; + + -- ram signals + signal ram_tready : std_logic; + signal ram_tvalid, ram_tlast, ram_window_tuser, ram_last_tuser : std_logic_vector(1 downto 0); + signal ram_tdata : t_ram_rd_data; + + -- traceback signals + signal traceback_tvalid, traceback_tdata : std_logic_vector(1 downto 0); + signal traceback_tready, traceback_tlast : std_logic_vector(1 downto 0); + signal traceback_last_tuser : std_logic_vector(1 downto 0); + + -- reorder signals + signal reorder_tready, reorder_tvalid : std_logic_vector(1 downto 0); + signal reorder_tdata, reorder_tlast : std_logic_vector(1 downto 0); + signal reorder_last_tuser : std_logic_vector(1 downto 0); + + -- output signals + signal output_tready : std_logic_vector(1 downto 0); + signal current_active : integer range 1 downto 0; + +begin + + -- + -- There is always one byte of data for each LLR value, even though each + -- LLR value is represented with BW_LLR_INPUT bits. Hence, only + -- BW_LLR_INPUT bits are extracted from the byte. + -- + gen_input_assignment: for i in NUMBER_PARITY_BITS - 1 downto 0 generate + begin + input(i) <= signed(buffer_tdata(8 * i + BW_LLR_INPUT - 1 downto 8 * i)); + end generate gen_input_assignment; + + rst <= not aresetn; + + ------------------------------ + --- Portmapping components --- + ------------------------------ + + ------------------------------------- + -- AXI4S input buffer + -------------------------------------- + + inst_axi4s_buffer: axi4s_buffer + generic map( + DATA_WIDTH => 32 + ) + port map( + clk => clk, + rst => rst, + + input => s_axis_input_tdata, + input_valid => s_axis_input_tvalid, + input_last => s_axis_input_tlast, + input_accept => s_axis_input_tready, + + output => buffer_tdata, + output_valid => buffer_tvalid, + output_last => buffer_tlast, + output_accept => branch_tready(0) + ); + + ------------------------------------- + -- Branch metric unit + -------------------------------------- + + gen_branch_distance : for i in NUMBER_BRANCH_UNITS - 1 downto 0 generate + begin + inst_branch_distance : branch_distance + generic map( + EDGE_WEIGHT => std_logic_vector(to_unsigned(i, NUMBER_PARITY_BITS)) + ) + port map( + clk => clk, + rst => rst, + + s_axis_input_tvalid => buffer_tvalid, + s_axis_input_tdata => input, + s_axis_input_tlast => buffer_tlast, + s_axis_input_tready => branch_tready(i), + + m_axis_output_tvalid => branch_tvalid(i), + m_axis_output_tdata => branch_tdata(i), + m_axis_output_tlast => branch_tlast(i), + m_axis_output_tready => acs_tready(0) + ); + end generate gen_branch_distance; + + + ------------------------------------- + -- ACS unit (path metric calculation) + -------------------------------------- + + gen_acs : for i in 0 to NUMBER_TRELLIS_STATES - 1 generate + signal inbranch_tdata_low : std_logic_vector(BW_BRANCH_RESULT - 1 downto 0); + signal inbranch_tdata_high : std_logic_vector(BW_BRANCH_RESULT - 1 downto 0); + signal inprev_tdata_low : std_logic_vector(BW_MAX_PROBABILITY - 1 downto 0); + signal inprev_tdata_high : std_logic_vector(BW_MAX_PROBABILITY - 1 downto 0); + begin + inbranch_tdata_low <= branch_tdata(to_integer(unsigned(TRANSITIONS(i)(0)))); + inbranch_tdata_high <= branch_tdata(to_integer(unsigned(TRANSITIONS(i)(1)))); + inprev_tdata_low <= acs_prob_tdata(to_integer(unsigned(PREVIOUS_STATES(i)(0)))); + inprev_tdata_high <= acs_prob_tdata(to_integer(unsigned(PREVIOUS_STATES(i)(1)))); + + inst_acs : acs + generic map( + initialize_value => INITIALIZE_TRELLIS(i) + ) + port map( + clk => clk, + rst => rst, + + s_axis_inbranch_tvalid => branch_tvalid(0), + s_axis_inbranch_tdata_low => inbranch_tdata_low, + s_axis_inbranch_tdata_high => inbranch_tdata_high, + s_axis_inbranch_tlast => branch_tlast(0), + s_axis_inbranch_tready => acs_tready(i), + + s_axis_inprev_tvalid => '1', + s_axis_inprev_tdata_low => inprev_tdata_low, + s_axis_inprev_tdata_high => inprev_tdata_high, + s_axis_inprev_tready => open, + + m_axis_outprob_tvalid => open, + m_axis_outprob_tdata => acs_prob_tdata(i), + m_axis_outprob_tready => '1', + + m_axis_outdec_tvalid => acs_tvalid(i), + m_axis_outdec_tdata => acs_dec_tdata(i), + m_axis_outdec_tlast => acs_tlast(i), + m_axis_outdec_tready => ram_tready + ); + end generate gen_acs; + + + ------------------------------- + -- Traceback + ------------------------------- + + inst_ram_ctrl : ram_ctrl + port map ( + clk => clk, + rst => rst, + + s_axis_input_tvalid => acs_tvalid(0), + s_axis_input_tdata => acs_dec_tdata, + s_axis_input_tlast => acs_tlast(0), + s_axis_input_tready => ram_tready, + + m_axis_output_tvalid => ram_tvalid, + m_axis_output_tdata => ram_tdata, + m_axis_output_tlast => ram_tlast, + m_axis_output_tready => traceback_tready, + m_axis_output_window_tuser => ram_window_tuser, + m_axis_output_last_tuser => ram_last_tuser, + + s_axis_ctrl_tvalid => s_axis_ctrl_tvalid, + s_axis_ctrl_tdata => s_axis_ctrl_tdata, + s_axis_ctrl_tready => s_axis_ctrl_tready + ); + + + gen_inst_trellis_traceback : for i in 1 downto 0 generate + begin + inst_trellis_traceback : trellis_traceback + port map( + clk => clk, + rst => rst, + + s_axis_input_tvalid => ram_tvalid(i), + s_axis_input_tdata => ram_tdata(i), + s_axis_input_tlast => ram_tlast(i), + s_axis_input_tready => traceback_tready(i), + s_axis_input_window_tuser => ram_window_tuser(i), + s_axis_input_last_tuser => ram_last_tuser(i), + + m_axis_output_tvalid => traceback_tvalid(i), + m_axis_output_tdata => traceback_tdata(i), + m_axis_output_tlast => traceback_tlast(i), + m_axis_output_last_tuser => traceback_last_tuser(i), + m_axis_output_tready => reorder_tready(i) + ); + end generate gen_inst_trellis_traceback; + + + ------------------------------- + -- Reverse output order + ------------------------------- + + gen_inst_reorder : for i in 1 downto 0 generate + begin + inst_reorder : reorder + port map( + clk => clk, + rst => rst, + + s_axis_input_tvalid => traceback_tvalid(i), + s_axis_input_tdata => traceback_tdata(i), + s_axis_input_tlast => traceback_tlast(i), + s_axis_input_last_tuser => traceback_last_tuser(i), + s_axis_input_tready => reorder_tready(i), + + m_axis_output_tvalid => reorder_tvalid(i), + m_axis_output_tdata => reorder_tdata(i), + m_axis_output_tlast => reorder_tlast(i), + m_axis_output_last_tuser => reorder_last_tuser(i), + m_axis_output_tready => output_tready(i) + ); + end generate gen_inst_reorder; + + + ------------------------------ + -- Recursive codes handling -- + ------------------------------ + + gen_inst_recursion : if FEEDBACK_POLYNOMIAL /= 0 generate + signal reorder_recursion_tvalid : std_logic; + signal reorder_recursion_tdata : std_logic; + signal reorder_recursion_tlast : std_logic; + signal recursion_tready : std_logic; + begin + inst_recursion : recursionx + port map( + clk => clk, + rst => rst, + + s_axis_input_tvalid => reorder_recursion_tvalid, + s_axis_input_tdata => reorder_recursion_tdata, + s_axis_input_tlast => reorder_recursion_tlast, + s_axis_input_tready => recursion_tready, + + m_axis_output_tvalid => m_axis_output_tvalid, + m_axis_output_tdata => m_axis_output_tdata, + m_axis_output_tlast => m_axis_output_tlast, + m_axis_output_tready => m_axis_output_tready + ); + + ------------------------------- + -- Output interface handling + ------------------------------- + + reorder_recursion_tvalid <= '1' when reorder_tvalid(0) = '1' or reorder_tvalid(1) = '1' else + '0'; + + reorder_recursion_tdata <= reorder_tdata(0) when current_active = 0 else + reorder_tdata(1); + + reorder_recursion_tlast <= '1' when reorder_tlast(0) = '1' or reorder_tlast(1) = '1' else + '0'; + + output_tready(0) <= '1' when (current_active = 0) and m_axis_output_tready = '1' else + '0'; + output_tready(1) <= '1' when (current_active = 1) and m_axis_output_tready = '1' else + '0'; + end generate gen_inst_recursion; + + + + no_recursion: if FEEDBACK_POLYNOMIAL = 0 generate + + ------------------------------- + -- Output interface handling + ------------------------------- + + m_axis_output_tdata <= reorder_tdata(0) when current_active = 0 else + reorder_tdata(1); + + m_axis_output_tvalid <= '1' when reorder_tvalid(0) = '1' or reorder_tvalid(1) = '1' else + '0'; + + m_axis_output_tlast <= '1' when reorder_tlast(0) = '1' or reorder_tlast(1) = '1' else + '0'; + + output_tready(0) <= '1' when (current_active = 0) and m_axis_output_tready = '1' else + '0'; + output_tready(1) <= '1' when (current_active = 1) and m_axis_output_tready = '1' else + '0'; + end generate no_recursion; + + + recursion : if FEEDBACK_POLYNOMIAL /= 0 generate + begin + end generate recursion; + + + -- Check and merge reordering outputs and block if necessary. + pr_reorder_tready : process(clk) is + begin + if rising_edge(clk) then + if rst = '1' then + current_active <= 0; + else + if reorder_tvalid(current_active) = '1' and m_axis_output_tready = '1' and reorder_last_tuser(current_active) = '1' then + current_active <= 1 - current_active; + end if; + end if; + end if; + end process pr_reorder_tready; + +end architecture rtl; diff --git a/testsuite/gna/issue301/src/generic_sp_ram.vhd b/testsuite/gna/issue301/src/generic_sp_ram.vhd new file mode 100644 index 000000000..a418ad54d --- /dev/null +++ b/testsuite/gna/issue301/src/generic_sp_ram.vhd @@ -0,0 +1,90 @@ +--! +--! Copyright (C) 2010 - 2012 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 Generic single port RAM with a single read/write port +--! @author Matthias Alles +--! @date 2010/09/28 +--! + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library dec_viterbi; +use dec_viterbi.pkg_helper.all; + + +entity generic_sp_ram is + generic( + DISTR_RAM : boolean := false; + WORDS : integer := 8; + BITWIDTH : integer := 8 + ); + port( + clk : in std_logic; + rst : in std_logic; + + wen : in std_logic; + en : in std_logic; + + a : in std_logic_vector(no_bits_natural(WORDS - 1) - 1 downto 0); + d : in std_logic_vector(BITWIDTH - 1 downto 0); + q : out std_logic_vector(BITWIDTH - 1 downto 0) + ); +end generic_sp_ram; + + +architecture rtl of generic_sp_ram is + + type t_ram is array(WORDS - 1 downto 0) of + std_logic_vector(BITWIDTH - 1 downto 0); + signal sp_ram : t_ram := (others => (others => '0')); + + function get_ram_style_xilinx(dist_ram : in boolean) return string is + begin + if dist_ram then + return "pipe_distributed"; + else + return "block"; + end if; + end function; + + function get_ram_style_altera(dist_ram : in boolean) return string is + begin + if dist_ram then + return "MLAB, no_rw_check"; + else + return "AUTO"; + end if; + end function; + + attribute RAM_STYLE : string; + attribute RAM_STYLE of sp_ram : signal is get_ram_style_xilinx(DISTR_RAM); + + attribute ramstyle : string; + attribute ramstyle of sp_ram : signal is get_ram_style_altera(DISTR_RAM); + +begin + + -- + -- Do not register the address for reading, since the synthesis doesn't + -- recognize then that this is a single-port RAM. + -- + pr_sp_ram_rw: process(clk) + begin + if rising_edge(clk) then + if en = '1' then + if wen = '1' then + sp_ram(to_integer(UNSIGNED(a))) <= d; + else + q <= sp_ram(to_integer(UNSIGNED(a))); + end if; + end if; + end if; + end process pr_sp_ram_rw; + +end rtl; 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; diff --git a/testsuite/gna/issue301/src/recursion.vhd b/testsuite/gna/issue301/src/recursion.vhd new file mode 100644 index 000000000..932058653 --- /dev/null +++ b/testsuite/gna/issue301/src/recursion.vhd @@ -0,0 +1,96 @@ +--! +--! 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 Recursion unit for recursive code. +--! @author Markus Fehrenz +--! @date 2011/01/12 +--! +--! @details The recusion handling buffers the reorder ouput and +--! calculates the correct output depending on the feedback polynomial. +--! + +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; + +entity recursionx is + port( + clk : in std_logic; + rst : in std_logic; + + -- + -- Decoded bits input from the reordering units in std_logic + -- + s_axis_input_tvalid : in std_logic; + s_axis_input_tdata : in std_logic; + s_axis_input_tlast : in std_logic; + s_axis_input_tready : out std_logic; + + -- + -- Output decoded bits convolved with the feedback polynomial + -- + m_axis_output_tvalid : out std_logic; + m_axis_output_tdata : out std_logic; + m_axis_output_tlast : out std_logic; + m_axis_output_tready : in std_logic + ); +end entity recursionx; + +architecture rtl of recursionx is + signal recursion_sreg : unsigned(ENCODER_MEMORY_DEPTH downto 0); + signal s_axis_input_tready_int : std_logic; + signal m_axis_output_tvalid_int : std_logic; + +begin + s_axis_input_tready_int <= '1' when m_axis_output_tready = '1' or m_axis_output_tvalid_int = '0' else + '0'; + + s_axis_input_tready <= s_axis_input_tready_int; + m_axis_output_tvalid <= m_axis_output_tvalid_int; + + -- Use the feedback polynomial to convolve the global path. + pr_recursion : process(clk) is + variable v_bit : std_logic := '0'; + variable v_recursion_state : unsigned(ENCODER_MEMORY_DEPTH downto 0); + begin + if rising_edge(clk) then + if rst = '1' then + recursion_sreg <= (others => '0'); + m_axis_output_tdata <= '0'; + m_axis_output_tlast <= '0'; + else + m_axis_output_tvalid_int <= s_axis_input_tvalid; + + if s_axis_input_tvalid = '1' and s_axis_input_tready_int = '1' then + + -- move current decoded output bits into shift register and reset if last flag is valid + if s_axis_input_tlast = '1' then + recursion_sreg <= (others => '0'); + else + recursion_sreg <= s_axis_input_tdata & recursion_sreg(ENCODER_MEMORY_DEPTH downto 1); + end if; + + -- convolve with feedback polynomial with the output register. + v_bit := '0'; + v_recursion_state := (s_axis_input_tdata & recursion_sreg(ENCODER_MEMORY_DEPTH downto 1)) and + ('1' & to_unsigned(FEEDBACK_POLYNOMIAL, ENCODER_MEMORY_DEPTH)); + for i in ENCODER_MEMORY_DEPTH downto 0 loop + v_bit := v_bit xor v_recursion_state(i); + end loop; + m_axis_output_tdata <= v_bit; + + m_axis_output_tlast <= s_axis_input_tlast; + end if; + end if; + end if; + end process pr_recursion; + +end architecture rtl; diff --git a/testsuite/gna/issue301/src/reorder.vhd b/testsuite/gna/issue301/src/reorder.vhd new file mode 100644 index 000000000..c29069098 --- /dev/null +++ b/testsuite/gna/issue301/src/reorder.vhd @@ -0,0 +1,129 @@ +--! +--! 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 Reorder twisted output due to windowing +--! @author Markus Fehrenz +--! @date 2011/05/12 +--! +--! @details The windowing output is twisted. +--! The correct order is simply rebuilt by reversing +--! the output of each traceback unit. +--! + +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; + + +entity reorder is + port( + clk : in std_logic; + rst : in std_logic; + + -- + -- Traceback unit output, twisted order + -- + s_axis_input_tvalid : in std_logic; + s_axis_input_tdata : in std_logic; + s_axis_input_tlast : in std_logic; + s_axis_input_last_tuser : in std_logic; + s_axis_input_tready : out std_logic; + + -- + -- Viterbi decoder output, original (input) order. + -- + m_axis_output_tvalid : out std_logic; + m_axis_output_tdata : out std_logic; + m_axis_output_tlast : out std_logic; + m_axis_output_last_tuser : out std_logic; -- Last bit of one traceback window + m_axis_output_tready : in std_logic + ); +end entity reorder; + + +architecture rtl of reorder is + + -- used to store one reversed output of a traceback unit + signal buffer_sreg : unsigned(MAX_WINDOW_LENGTH - 1 downto 0); + signal buffer_cnt : unsigned(BW_MAX_WINDOW_LENGTH - 1 downto 0); + signal buffer_end : integer range ENCODER_MEMORY_DEPTH downto 0; + signal send_output, last_window : boolean; + + signal s_axis_input_tready_int : std_logic; + +begin + + s_axis_input_tready <= s_axis_input_tready_int; + s_axis_input_tready_int <= '1' when not(send_output) else + '0'; + +-- m_axis_output_tvalid <= '1' when send_output and m_axis_output_tready= '1' else + m_axis_output_tvalid <= '1' when send_output else + '0'; + m_axis_output_tdata <= buffer_sreg(0); + + m_axis_output_tlast <= '1' when buffer_cnt = ENCODER_MEMORY_DEPTH and last_window else + '0'; + + -- Reorder the global path given from an traceback unit with the help of a shift register. + pr_reorder : process(clk) is + begin + if rising_edge(clk) then + if rst = '1' then + buffer_sreg <= (others => '0'); + buffer_cnt <= (others => '0'); + send_output <= false; + last_window <= false; + buffer_end <= 0; + m_axis_output_last_tuser <= '0'; + else + + -- store output of traceback unit + if s_axis_input_tvalid = '1' and s_axis_input_tready_int = '1' then + if s_axis_input_tlast = '1' then + last_window <= true; + buffer_end <= ENCODER_MEMORY_DEPTH; + end if; + if s_axis_input_last_tuser = '1' then + send_output <= true; + buffer_sreg <= buffer_sreg(MAX_WINDOW_LENGTH - 2 downto 0) & s_axis_input_tdata; + else + buffer_sreg <= buffer_sreg(MAX_WINDOW_LENGTH - 2 downto 0) & s_axis_input_tdata; + buffer_cnt <= buffer_cnt + 1; + end if; + end if; + + -- send reordered data to the output + if m_axis_output_tready = '1' and send_output then + buffer_sreg <= '0' & buffer_sreg(MAX_WINDOW_LENGTH - 1 downto 1); + + -- Next transfer will be the last one of this window. + if buffer_cnt = 1 then + m_axis_output_last_tuser <= '1'; + end if; + + -- This was the last data transfer. Tailbits are cut off + if buffer_cnt = buffer_end then + send_output <= false; + last_window <= false; + buffer_end <= 0; + buffer_cnt <= (others => '0'); + m_axis_output_last_tuser <= '0'; + else + buffer_cnt <= buffer_cnt - 1; + end if; + end if; + end if; + end if; + end process pr_reorder; + +end architecture rtl; diff --git a/testsuite/gna/issue301/src/traceback.vhd b/testsuite/gna/issue301/src/traceback.vhd new file mode 100644 index 000000000..b3da85734 --- /dev/null +++ b/testsuite/gna/issue301/src/traceback.vhd @@ -0,0 +1,101 @@ +--! +--! 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 Traceback unit for a viterbi decoder +--! @author Markus Fehrenz +--! @date 2011/07/11 +--! +--! @details The traceback unit only processes a data stream. +--! There is no knowledge about the decoder configuration. +--! The information about acquisition and window lengths is received from ram control. +--! + +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; + + +entity trellis_traceback is + port( + -- general signals + clk : in std_logic; + rst : in std_logic; + + 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; + s_axis_input_window_tuser : in std_logic; + s_axis_input_last_tuser : in std_logic; + + m_axis_output_tvalid : out std_logic; + m_axis_output_tdata : out std_logic; + m_axis_output_tlast : out std_logic; + m_axis_output_last_tuser : out std_logic; + m_axis_output_tready : in std_logic + ); +end entity trellis_traceback; + + +architecture rtl of trellis_traceback is + + signal current_node : unsigned(BW_TRELLIS_STATES - 1 downto 0); + signal m_axis_output_tvalid_int : std_logic; + signal s_axis_input_tready_int : std_logic; + +begin + s_axis_input_tready_int <= '1' when m_axis_output_tready = '1' or m_axis_output_tvalid_int = '0' else + '0'; + s_axis_input_tready <= s_axis_input_tready_int; + + m_axis_output_tvalid <= m_axis_output_tvalid_int; + + + -- Traceback the ACS local path decisions and output the resulting global path. + pr_traceback : process(clk) is + begin + if rising_edge(clk) then + if rst = '1' then + m_axis_output_tvalid_int <= '0'; + m_axis_output_tdata <= '0'; + m_axis_output_tlast <= '0'; + m_axis_output_last_tuser <= '0'; + current_node <= (others => '0'); + else + + if m_axis_output_tready = '1' then + m_axis_output_tvalid_int <= '0'; + end if; + + -- calculate the decoded bit with an shift register + if s_axis_input_tvalid = '1' and s_axis_input_tready_int = '1' then + + m_axis_output_tlast <= s_axis_input_tlast; + m_axis_output_last_tuser <= s_axis_input_last_tuser; + + -- handle tvalid output signal + if s_axis_input_window_tuser = '1' then + m_axis_output_tvalid_int <= '1'; + m_axis_output_tdata <= current_node(BW_TRELLIS_STATES - 1); + end if; + + -- last value of current window? + if s_axis_input_last_tuser = '1' then + current_node <= to_unsigned(0, BW_TRELLIS_STATES); + else + current_node <= current_node(BW_TRELLIS_STATES - 2 downto 0) + & s_axis_input_tdata(to_integer(current_node(BW_TRELLIS_STATES - 1 downto 0))); + end if; + end if; + end if; + end if; + end process pr_traceback; +end architecture rtl; diff --git a/testsuite/gna/issue301/testsuite.sh b/testsuite/gna/issue301/testsuite.sh new file mode 100755 index 000000000..96fae042b --- /dev/null +++ b/testsuite/gna/issue301/testsuite.sh @@ -0,0 +1,29 @@ +#! /bin/sh + +. ../../testenv.sh + +analyze repro.vhdl +elab_simulate repro + +analyze --work=dec_viterbi packages/pkg_helper.vhd +analyze --work=dec_viterbi packages/pkg_param.vhd +analyze --work=dec_viterbi packages/pkg_param_derived.vhd +analyze --work=dec_viterbi packages/pkg_types.vhd +analyze --work=dec_viterbi packages/pkg_components.vhd +analyze --work=dec_viterbi packages/pkg_trellis.vhd +analyze --work=dec_viterbi src/generic_sp_ram.vhd +analyze --work=dec_viterbi src/axi4s_buffer.vhd +analyze --work=dec_viterbi src/branch_distance.vhd +analyze --work=dec_viterbi src/traceback.vhd +analyze --work=dec_viterbi src/acs.vhd +analyze --work=dec_viterbi src/ram_ctrl.vhd +analyze --work=dec_viterbi src/reorder.vhd +analyze --work=dec_viterbi src/recursion.vhd +analyze --work=dec_viterbi src/dec_viterbi.vhd + +elab_simulate --work=dec_viterbi dec_viterbi_top + +clean +clean dec_viterbi + +echo "Test successful" |