Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

vhdl

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 it possible to create eg. a bus multiplexer with both the bus width and bus count configurable?

entity bus_multiplexer is
        generic (bus_width : positive := 8;
                sel_width : positive := 2);
        port (  i : in array(integer range 2**sel_width - 1 downto 0) of std_logic_vector(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;

The above doesn't seem to work because the array type needs to be defined separately. Defining the type before the port also does not work, as then it expects the entity definition to end after it. Defining it after the port definition doesn't work since it'd be used before that. Defining it in a package doesn't work because the type definition doesn't seem to like having an unconstrained range in the "base type".

Is it possible to somehow do this in VHDL-93? (What about VHDL-2008?)

Defining the type as array(natural range <>, natural range <>) of std_logic in the package works - as in the port definition doesn't give an error - but actually using it if it's defined that way seems to be quite unwieldy.

Is there some sane way to use it like this? Is there some simple way to map N separate std_logic_vectors to a port defined like that, and likewise for the actual output logic?

I tried the original and o <= i(to_integer(unsigned(sel)), bus_width - 1 downto 0), but neither worked. I know I could do it one bit at a time, but I'd prefer something simpler. And while the bit-by-bit -approach might be okay for the internal implementation, I certainly wouldn't want to have to do that for the port mapping every time I use the component...

Is there some sane(-ish) way to do this?

(Addendum: I know there are some similar questions, but most of them don't deal with the case of both ranges coming from generics, and were solved using a type definition in a package. The one that did talk about two generic dimensions apparently didn't need the input to come from distinct std_logic_vectors and ended up using the "2d-array of std_logic" method, which doesn't work for me (at least without further clarification about how to use it without losing one's sanity))

like image 906
Aleksi Torhamo Avatar asked Feb 12 '15 02:02

Aleksi Torhamo


2 Answers

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;
like image 97
Aleksi Torhamo Avatar answered Sep 28 '22 00:09

Aleksi Torhamo


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;
like image 35
Paebbels Avatar answered Sep 27 '22 23:09

Paebbels