22
\$\begingroup\$

I've built a multiplexer which takes 2 inputs: one array of std_logic_vector and one std_logic_vector to select the correct array. It should be written more generic, but I'm not sure how. The length of SEL grows if the length of X grows (4-bit if 16 inputs, 6-bit if 64, etc.). I'm currently using custom types as input, but this makes making the code more generic problematic.

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

LIBRARY work;
USE work.type_arrays.ALL;

entity mux_16to1 is
    Port ( SEL : in  STD_LOGIC_VECTOR ( 3 downto 0);
           X   : in  A16_SLV16;
           Y   : out STD_LOGIC_VECTOR (15 downto 0));
end mux_16to1;

architecture sel of mux_16to1 is
begin
with SEL select
    Y <= X(0) when "0000",
         X(1) when "0001",
         X(2) when "0010",
         X(3) when "0011",
         X(4) when "0100",
         X(5) when "0101",
         X(6) when "0110",
         X(7) when "0111",
         X(8) when "1000",
         X(9) when "1001",
         X(10) when "1010",
         X(11) when "1011",
         X(12) when "1100",
         X(13) when "1101",
         X(14) when "1110",
         X(15) when "1111",
         "0000000000000000"  when others;
end sel;

With work.type_arrays consisting of:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

PACKAGE type_arrays IS
    SUBTYPE SLV16 IS std_logic_vector( 15 downto 0 );
    TYPE A16_SLV16 IS ARRAY ( 0 to 15 ) OF SLV16;
    TYPE A256_SLV16 IS ARRAY ( 0 to 255 ) OF SLV16;
END type_arrays;

Does anyone have a solution/idea to make it generic? I want to use this mux for 256*16-bit solutions and perhaps even 1024*24-bit as well. Writing the whole thing by hand can't be the VHDL way to do it.

The alternative is letting a generator write the code, but I'd like to prevent that if possible.

\$\endgroup\$
0

1 Answer 1

19
+100
\$\begingroup\$

There are 4 ways to a solve this:

Solution 1:

You can define a fixed width STD_LOGIC_VECTOR (SLV), which can be used to construct other vectors (let's call them STD_LOGIC_VECTOR_VECTORs (SLVV).

subtype T_SLV_8  is STD_LOGIC_VECTOR(7 downto 0);        -- define a Byte
type    T_SLVV_8 is array(NATURAL range <>) of T_SLV_8;  -- define a new unconstrained vector of Bytes.

Now you can define your entity as follows:

entity mux_slvv_8 is
  generic (
    PORTS  : POSITIVE  := 4
  );
  port (
    sel  : in  STD_LOGIC_VECTOR(log2ceilnz(PORTS) - 1 downto 0);
    X    : in  T_SLVV_8(PORTS - 1 downto 0);
    Y    : out T_SLV_8
  );
end;

The internal logic can be reduced to:

architecture rtl of mux_slvv_8 is
begin
  Y <= X(to_integer(unsigned(sel)));
end;

This can be improved by extracting to_integer(unsigned(..)) into a function called to_index.

Solution 2:

The next step would be, to be also flexible in how many bits are multiplexed at once. So let's introduce a generic BITS to specify the data bits. VHDL has a problem to define array of unconstrained arrays (this is solved with VHDL-2008, but not all vendors support this feature).

So solution 2 uses a flat vector to pass all bits to the mux.

entity mux_flat is
  generic (
    PORTS  : POSITIVE  := 4;
    BITS   : POSITIVE  := 8
  );
  port (
    sel  : in  STD_LOGIC_VECTOR(log2ceilnz(PORTS) - 1 downto 0);
    X    : in  STD_LOGIC_VECTOR((BITS * PORTS) - 1 downto 0);
    Y    : out STD_LOGIC_VECTOR(BITS - 1 downto 0)
  );
end;

architecture rtl of mux_flat is
  type T_SLVV is array(NATURAL range <>) of STD_LOGIC_VECTOR(BITS - 1 downto 0);
  signal mux_in : T_SLVV(PORTS - 1 downto 0)
begin
  gen : for i in 0 to PORTS - 1 generate
    -- convert flat vector to slvv
    mux_in(i) <= X(((i + 1) * BITS) - 1 downto (i * BITS));
  end generate;
  Y <= mux_in(to_integer(unsigned(sel)));
end;

Solution 3:

Being flexible in PORTS and BITS can also be done by using a real 2-dimensional array -> STD_LOGIC_MATRIX (SLM).

type T_SLM is array(NATURAL range <>, NATURAL range <>) of STD_LOGIC;

This would be the entity declaration:

entity mux_slm is
  generic (
    PORTS  : POSITIVE  := 4;
    BITS   : POSITIVE  := 8
  );
  port (
    sel  : in  STD_LOGIC_VECTOR(log2ceilnz(PORTS) - 1 downto 0);
    X    : in  T_SLM(PORTS - 1 downto 0, BITS - 1 downto 0);
    Y    : out STD_LOGIC_VECTOR(BITS - 1 downto 0)
  );
end;

You could also write a generate loop to do some wiring or you can define some functions to do that.

-- get a matrix row
function get_row(slm : T_SLM; RowIndex : NATURAL) return STD_LOGIC_VECTOR is
  variable slv : STD_LOGIC_VECTOR(slm'high(2) downto slm'low(2)); -- Xilinx iSIM work-around, because 'range(2) = 'range(1); tested with ISE/iSIM 14.2
begin
  for i in slv'range loop
    slv(i) := slm(RowIndex, i);
  end loop;
  return slv;
end function;

And thats the corresponding architecture:

architecture rtl of mux_slm is
begin
  Y <= get_Row(X, to_integer(unsigned(sel)));
end;

Solution 4:

If your tool supports VHDL-2008 and arrays of unconstrained vectors, then you can use this type:

type T_SLVV is array(NATURAL range <>) of STD_LOGIC_VECTOR;

Type Conversion:

The following images illustrates all possible type conversions regarding: SL, SLV, SLVV and SLM.

enter image description here

If you are interested in more, I could upload my complete collection of vector, vector-vector and matrix types, functions and procedures. Please give me a hint.

Edit:

Here is the package PoC.common.vectors. It's included in my PicoBlaze Library which is currently in beta state. The release of PoC is also planned, but not so progressed. So I included some necessary packages and modules into this library. The source code license is 'Apache License 2.0'.

\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.