Using array of std_logic_vector as a port type, with both ranges using a generic

后端 未结 2 1655
南笙
南笙 2020-12-28 11:11

Is it possible to create an entity with a port that is an array of std_logic_vectors, with both the size of the array and the std_logic_vector coming from generics? Ie. is i

相关标签:
2条回答
  • 2020-12-28 11:36

    Yes, it's possible.

    Your attempt with a two dimensional array is good, because nested 1 dimensional array need a fixed size in the inner dimensions. So the way to handle such a 2D array is to write some functions and procedures, which convert the 2D array into nested 1D vectors.

    I answered a similar question here:
    - Fill one row in 2D array outside the process (VHDL) and
    - Creating a generic array whose elements have increasing width in VHDL

    Here is my vectors package.

    And here is an example of an multiplexer for a FIFO interface, which is variable in data width as well as in input count. It uses a round robin arbiter to select the inputs.

    Entity 'PoC.bus.Stream.Mux':

    -- EMACS settings: -*-  tab-width: 2; indent-tabs-mode: t -*-
    -- vim: tabstop=2:shiftwidth=2:noexpandtab
    -- kate: tab-width 2; replace-tabs off; indent-width 2;
    -- 
    -- ============================================================================
    -- Authors:           Patrick Lehmann
    -- 
    -- License:
    -- ============================================================================
    -- Copyright 2007-2015 Technische Universitaet Dresden - Germany
    --                     Chair for VLSI-Design, Diagnostics and Architecture
    -- 
    -- Licensed under the Apache License, Version 2.0 (the "License");
    -- you may not use this file except in compliance with the License.
    -- You may obtain a copy of the License at
    -- 
    --    http://www.apache.org/licenses/LICENSE-2.0
    -- 
    -- Unless required by applicable law or agreed to in writing, software
    -- distributed under the License is distributed on an "AS IS" BASIS,
    -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    -- See the License for the specific language governing permissions and
    -- limitations under the License.
    -- ============================================================================
    
    library IEEE;
    use     IEEE.STD_LOGIC_1164.all;
    use     IEEE.NUMERIC_STD.all;
    
    library PoC;
    use     PoC.config.all;
    use     PoC.utils.all;
    use     PoC.vectors.all;
    
    
    entity Stream_Mux is
      generic (
        PORTS            : POSITIVE                  := 2;
        DATA_BITS        : POSITIVE                  := 8;
        META_BITS        : NATURAL                   := 8;
        META_REV_BITS    : NATURAL                   := 2
      );                 
      port (             
        Clock            : IN  STD_LOGIC;
        Reset            : IN  STD_LOGIC;
        -- IN Ports      
        In_Valid         : IN  STD_LOGIC_VECTOR(PORTS - 1 downto 0);
        In_Data          : IN  T_SLM(PORTS - 1 downto 0, DATA_BITS - 1 downto 0);
        In_Meta          : IN  T_SLM(PORTS - 1 downto 0, META_BITS - 1 downto 0);
        In_Meta_rev      : OUT T_SLM(PORTS - 1 downto 0, META_REV_BITS - 1 downto 0);
        In_SOF           : IN  STD_LOGIC_VECTOR(PORTS - 1 downto 0);
        In_EOF           : IN  STD_LOGIC_VECTOR(PORTS - 1 downto 0);
        In_Ack           : OUT STD_LOGIC_VECTOR(PORTS - 1 downto 0);
        -- OUT Port      
        Out_Valid        : OUT STD_LOGIC;
        Out_Data         : OUT STD_LOGIC_VECTOR(DATA_BITS - 1 downto 0);
        Out_Meta         : OUT STD_LOGIC_VECTOR(META_BITS - 1 downto 0);
        Out_Meta_rev     : IN  STD_LOGIC_VECTOR(META_REV_BITS - 1 downto 0);
        Out_SOF          : OUT STD_LOGIC;
        Out_EOF          : OUT STD_LOGIC;
        Out_Ack          : IN  STD_LOGIC
      );
    end;
    
    architecture rtl OF Stream_Mux is
      attribute KEEP               : BOOLEAN;
      attribute FSM_ENCODING       : STRING;
    
      subtype T_CHANNEL_INDEX is NATURAL range 0 to PORTS - 1;
    
      type T_STATE is (ST_IDLE, ST_DATAFLOW);
    
      signal State                 : T_STATE          := ST_IDLE;
      signal NextState             : T_STATE;
    
      signal FSM_Dataflow_en       : STD_LOGIC;
    
      signal RequestVector         : STD_LOGIC_VECTOR(PORTS - 1 downto 0);
      signal RequestWithSelf       : STD_LOGIC;
      signal RequestWithoutSelf    : STD_LOGIC;
    
      signal RequestLeft           : UNSIGNED(PORTS - 1 downto 0);
      signal SelectLeft            : UNSIGNED(PORTS - 1 downto 0);
      signal SelectRight           : UNSIGNED(PORTS - 1 downto 0);
    
      signal ChannelPointer_en     : STD_LOGIC;
      signal ChannelPointer        : STD_LOGIC_VECTOR(PORTS - 1 downto 0);
      signal ChannelPointer_d      : STD_LOGIC_VECTOR(PORTS - 1 downto 0)     := to_slv(2 ** (PORTS - 1), PORTS);
      signal ChannelPointer_nxt    : STD_LOGIC_VECTOR(PORTS - 1 downto 0);
      signal ChannelPointer_bin    : UNSIGNED(log2ceilnz(PORTS) - 1 downto 0);
    
      signal idx                   : T_CHANNEL_INDEX;
    
      signal Out_EOF_i             : STD_LOGIC;
    
    begin
      RequestVector       <= In_Valid AND In_SOF;
      RequestWithSelf     <= slv_or(RequestVector);
      RequestWithoutSelf  <= slv_or(RequestVector AND NOT ChannelPointer_d);
    
      process(Clock)
      begin
        if rising_edge(Clock) then
          if (Reset = '1') then
            State        <= ST_IDLE;
          else
            State        <= NextState;
          end if;
        end if;
      end process;
    
      process(State, RequestWithSelf, RequestWithoutSelf, Out_Ack, Out_EOF_i, ChannelPointer_d, ChannelPointer_nxt)
      begin
        NextState                 <= State;
    
        FSM_Dataflow_en           <= '0';
    
        ChannelPointer_en         <= '0';
        ChannelPointer            <= ChannelPointer_d;
    
        case State is
          when ST_IDLE =>
            if (RequestWithSelf = '1') then
              ChannelPointer_en    <= '1';
    
              NextState            <= ST_DATAFLOW;
            end if;
    
          when ST_DATAFLOW =>
            FSM_Dataflow_en        <= '1';
    
            if ((Out_Ack   AND Out_EOF_i) = '1') then
              if (RequestWithoutSelf = '0') then
                NextState          <= ST_IDLE;
              else
                ChannelPointer_en  <= '1';
              end if;
            end if;
        end case;
      end process;
    
      process(Clock)
      begin
        if rising_edge(Clock) then
          if (Reset = '1') then
            ChannelPointer_d    <= to_slv(2 ** (PORTS - 1), PORTS);
          elsif (ChannelPointer_en = '1') then
            ChannelPointer_d    <= ChannelPointer_nxt;
          end if;
        end if;
      end process;
    
      RequestLeft         <= (NOT ((unsigned(ChannelPointer_d) - 1) OR unsigned(ChannelPointer_d))) AND unsigned(RequestVector);
      SelectLeft          <= (unsigned(NOT RequestLeft) + 1)    AND RequestLeft;
      SelectRight         <= (unsigned(NOT RequestVector) + 1)  AND unsigned(RequestVector);
      ChannelPointer_nxt  <= std_logic_vector(ite((RequestLeft = (RequestLeft'range => '0')), SelectRight, SelectLeft));
    
      ChannelPointer_bin  <= onehot2bin(ChannelPointer);
      idx                 <= to_integer(ChannelPointer_bin);
    
      Out_Data            <= get_row(In_Data, idx);
      Out_Meta            <= get_row(In_Meta, idx);
    
      Out_SOF             <= In_SOF(to_integer(ChannelPointer_bin));
      Out_EOF_i           <= In_EOF(to_integer(ChannelPointer_bin));
      Out_Valid           <= In_Valid(to_integer(ChannelPointer_bin)) and FSM_Dataflow_en;
      Out_EOF             <= Out_EOF_i;
    
      In_Ack              <= (In_Ack  'range => (Out_Ack   and FSM_Dataflow_en)) AND ChannelPointer;
    
      genMetaReverse_0 : if (META_REV_BITS = 0) generate
        In_Meta_rev    <= (others => (others => '0'));
      end generate;
      genMetaReverse_1 : if (META_REV_BITS > 0) generate
        signal Temp_Meta_rev : T_SLM(PORTS - 1 downto 0, META_REV_BITS - 1 downto 0)    := (others => (others => 'Z'));
      begin
        genAssign : for i in 0 to PORTS - 1 generate
          signal row  : STD_LOGIC_VECTOR(META_REV_BITS - 1 downto 0);
        begin
          row    <= Out_Meta_rev AND (row'range => ChannelPointer(I));
          assign_row(Temp_Meta_rev, row, i);
        end generate;
        In_Meta_rev    <= Temp_Meta_rev;
      end generate;
    end architecture;
    
    0 讨论(0)
  • 2020-12-28 11:40

    This works with VHDL2008:

    library ieee;
    use ieee.std_logic_1164.all;
    use ieee.numeric_std.all;
    
    package bus_multiplexer_pkg is
            type bus_array is array(natural range <>) of std_logic_vector;
    end package;
    
    library ieee;
    use ieee.std_logic_1164.all;
    use ieee.numeric_std.all;
    use work.bus_multiplexer_pkg.all;
    
    entity bus_multiplexer is
            generic (bus_width : positive := 8;
                    sel_width : positive := 2);
            port (  i : in bus_array(2**sel_width - 1 downto 0)(bus_width - 1 downto 0);
                    sel : in std_logic_vector(sel_width - 1 downto 0);
                    o : out std_logic_vector(bus_width - 1 downto 0));
    end bus_multiplexer;
    
    architecture dataflow of bus_multiplexer is
    begin
            o <= i(to_integer(unsigned(sel)));
    end dataflow;
    

    And it can be used like this:

    library ieee;
    use ieee.std_logic_1164.all;
    use ieee.numeric_std.all;
    use work.all;
    use work.bus_multiplexer_pkg.all;
    
    entity bus_multiplexer_4 is
            generic (bus_width : positive := 8);
            port (  bus0, bus1, bus2, bus3 : in std_logic_vector(bus_width - 1 downto 0);
                    sel : in std_logic_vector(1 downto 0);
                    o : out std_logic_vector(bus_width - 1 downto 0));
    end bus_multiplexer_4;
    
    architecture structural of bus_multiplexer_4 is
            signal i : bus_array(3 downto 0)(bus_width - 1 downto 0);
    begin
            i <= (0 => bus0, 1 => bus1, 2 => bus2, 3 => bus3);
            u: entity bus_multiplexer generic map (bus_width => bus_width, sel_width => 2) port map (i => i, sel => sel, o => o);
    end;
    

    It doesn't work with VHDL93, however, because you can't leave the std_logic_vector unconstrained in the type definition, as stated in the question.

    Unfortunately, I don't know if there's any way to do anything similar without 2d arrays with VHDL93.

    Edit: Paebbels's answer shows how to do this in VHDL93 by using 2d arrays, with custom procedures to make it manageable. Since his example is quite big, here's also a minimal example of the same concept:

    library ieee;
    use ieee.std_logic_1164.all;
    use ieee.numeric_std.all;
    
    package bus_multiplexer_pkg is
            type bus_array is array(natural range <>, natural range <>) of std_logic;
    
            procedure slm_row_from_slv(signal slm : out bus_array; constant row : natural; signal slv : in std_logic_vector);
            procedure slv_from_slm_row(signal slv : out std_logic_vector; signal slm : in bus_array; constant row : natural);
    end package;
    
    package body bus_multiplexer_pkg is
            procedure slm_row_from_slv(signal slm : out bus_array; constant row : natural; signal slv : in std_logic_vector) is
            begin
                    for i in slv'range loop
                            slm(row, i) <= slv(i);
                    end loop;
            end procedure;
    
            procedure slv_from_slm_row(signal slv : out std_logic_vector; signal slm : in bus_array; constant row : natural) is
            begin
                    for i in slv'range loop
                            slv(i) <= slm(row, i);
                    end loop;
            end procedure;
    end package body;
    
    library ieee;
    use ieee.std_logic_1164.all;
    use ieee.numeric_std.all;
    use work.bus_multiplexer_pkg.all;
    
    entity bus_multiplexer is
            generic (bus_width : positive := 8;
                    sel_width : positive := 2);
            port (  i : in bus_array(2**sel_width - 1 downto 0, bus_width - 1 downto 0);
                    sel : in std_logic_vector(sel_width - 1 downto 0);
                    o : out std_logic_vector(bus_width - 1 downto 0));
    end bus_multiplexer;
    
    architecture dataflow of bus_multiplexer is
    begin
            slv_from_slm_row(o, i, to_integer(unsigned(sel)));
    end dataflow;
    

    And it can be used like this:

    library ieee;
    use ieee.std_logic_1164.all;
    use ieee.numeric_std.all;
    use work.all;
    use work.bus_multiplexer_pkg.all;
    
    entity bus_multiplexer_4 is
            generic (bus_width : positive := 8);
            port (  bus0, bus1, bus2, bus3 : in std_logic_vector(bus_width - 1 downto 0);
                    sel : in std_logic_vector(1 downto 0);
                    o : out std_logic_vector(bus_width - 1 downto 0));
    end bus_multiplexer_4;
    
    architecture structural of bus_multiplexer_4 is
            signal i : bus_array(3 downto 0, bus_width - 1 downto 0);
    begin
            slm_row_from_slv(i, 0, bus0);
            slm_row_from_slv(i, 1, bus1);
            slm_row_from_slv(i, 2, bus2);
            slm_row_from_slv(i, 3, bus3);
            u: entity bus_multiplexer generic map (bus_width => bus_width, sel_width => 2) port map (i => i, sel => sel, o => o);
    end;
    
    0 讨论(0)
提交回复
热议问题