Iterating over two dimensional signals in VHDL

78 Views Asked by At

I am trying to create a n-bit logical shift using generate function.

entity logical_shift_n is
    generic(WIDTH : positive :=8);
    port (A: in std_logic_vector(WIDTH-1 downto 0);
          S: in std_logic_vector(log2(WIDTH)-1 downto 0);
          D: out std_logic_vector(WIDTH-1 downto 0));
          type two_dim is array (0 to log2(WIDTH)-1) of std_logic_vector(WIDTH-1 downto 0);
end logical_shift_n;

architecture behavioral of logical_shift_n is

component mux is
  port(A,B : in std_logic_vector(WIDTH-1 downto 0);
         S   : in std_logic;
         O   : out std_logic_vector(WIDTH-1 downto 0));
end component;

signal D1 : two_dim :=(others=>(others=>'0'));
signal D0 : two_dim :=(others=>(others=>'0'));
signal Y : two_dim :=(others=>(others=>'0'));
begin
D0 <= A;
D1 <= A & '0';

reg: for i in 0 to log2(WIDTH)-1 generate
    DC : mux
    port map(
        A => D0,
        B => D1,
        S => S(i),
        O  => Y
        );
end generate;

D <= Y; 

end behavioral;

The problem that I am facing is

  1. I need to assign the first row of "D0" to "A" and first row of "D1" to "A & '0'" so that the first row of D0 contains the normal input A and first row of D1 contains the left shift of A by 1.

  2. Then I want to iterate over the rows (using the generate function) such that the output coming from my first multiplexer acts as an input to the next multiplexer and the second multiplexer should have a normal input (which is equal to previous output) like the one before it then another input which should be the previous output shifted by 2. Similarly, for third multiplexer, one of the inputs to it should be previous output shifted by 4 (2^(n-1)). And so forth.

Logical shift

To provide a better idea, I have also created a diagram. Even though, this diagram is only for 8 bits (three multiplexers), I want to use a generate function to generalize it to n bits.

I created a two dimensional signal. But I am not sure how to initialize its first rows. Furthermore, I am not sure how to iterate over the rows such that cascading of the multiplexers happen and the left shift (by 2^n) also happens where n=0,1,2......n

1

There are 1 best solutions below

1
user16145658 On

There are some number of ways to create a generic or parameter controlled logical (left) shifter.

Here's one based on your question code:

library ieee;                -- CHANGED, added missing mux
use ieee.std_logic_1164.all;

entity mux is
    generic (WIDTH: natural);
    port (
        A,B: in  std_logic_vector(WIDTH - 1 downto 0);
        S:   in  std_logic;
        O:   out std_logic_vector(WIDTH - 1 downto 0)
    );
end entity;

architecture foo of mux is
begin
    O <= A when S = '0' else B;
end architecture;

library ieee;                -- CHANGED Context clause added
use ieee.std_logic_1164.all;
use ieee.math_real.all;      -- ADDED for log2

entity logical_shift_n is
    generic (WIDTH:     positive := 8);
    port (
        A: in  std_logic_vector(WIDTH - 1 downto 0);
    -- CHANGED, added useage for log2 from package math_real, ceil for non powers of two
        S: in  std_logic_vector(integer(ceil(log2(real(WIDTH)))) - 1 downto 0);
        D: out std_logic_vector(WIDTH - 1 downto 0));
          
          -- CHANGED don't calculate length again and again:
          type two_dim is array (0 to S'LEFT) of 
                           std_logic_vector(WIDTH - 1 downto 0);
end entity logical_shift_n;

architecture behavioral of logical_shift_n is

    component mux is
        generic (WIDTH: natural);
        port (
            A,B: in  std_logic_vector(WIDTH - 1 downto 0);
            S:   in  std_logic;
            O:   out std_logic_vector(WIDTH - 1 downto 0)
        );
    end component;

    signal D1: two_dim; -- CHANGED -- :=(others=>(others=>'0'));
    -- signal D0: two_dim; -- CHANGED -- :=(others=>(others=>'0'));
    signal Y : two_dim; -- CHANGED -- :=(others=>(others=>'0'));
    
    -- CHANGED, BORROWED from -1997 numeric_std-body.vhdl
    function XSLL (ARG: STD_LOGIC_VECTOR; COUNT: NATURAL) return STD_LOGIC_VECTOR
        is
      constant ARG_L: INTEGER := ARG'LENGTH-1;
      alias XARG: STD_LOGIC_VECTOR(ARG_L downto 0) is ARG;
      variable RESULT: STD_LOGIC_VECTOR(ARG_L downto 0) := (others => '0');
    begin
      if COUNT <= ARG_L then
        RESULT(ARG_L downto COUNT) := XARG(ARG_L-COUNT downto 0);
      end if;
      return RESULT;
    end XSLL;
begin
-- D0 <= A;  CHANGED Either A for i = 0 or Y (i - 1) for i /= 0
-- D1 <= A & '0';

reg:
    for i in S'range generate  -- CHANGED, breakout i = 0 from rest -1993 syntax
I_EQ_0:
        if i = 0 generate
            D1(i) <= XSLL(A, 2 ** i);
DC:     
            mux
                generic map (WIDTH => WIDTH)
                port map (
                    A => A,      -- CHANGED   Y(0) not used
                    B => D1(i),  -- CHANGED, indexed name
                    S => S(i),
                    O  => Y(i)   -- CHANGED, indexed name
                );
  
        end generate;
I_NEQ_0:
        if i /= 0 generate 
            D1(i) <= XSLL(Y(i - 1), 2 ** i);
DC:
            mux
                generic map (WIDTH => WIDTH)
                port map (
                    A => Y(i - 1),  -- CHANGED
                    B => D1(i),     -- CHANGED, indexed name
                    S => S(i),
                    O  => Y(i)       -- CHANGED, indexed name
                );
        end generate;
    end generate;

    D <= Y(S'LEFT); 
    
end architecture behavioral;


library ieee;
use ieee.std_logic_1164.all;
use ieee.math_real.all;
use ieee.numeric_std.all;

entity logical_shift_n_tb is
end entity;

architecture foo of logical_shift_n_tb is
    
    constant WIDTH:     natural := 8;
    
    component logical_shift_n is
        generic (WIDTH:     positive := 8);
        port (
            A: in  std_logic_vector(WIDTH - 1 downto 0);
            S: in  std_logic_vector(integer(ceil(log2(real(WIDTH)))) - 1 downto 0);
            D: out std_logic_vector(WIDTH - 1 downto 0)
        );
    end component logical_shift_n;
    
    signal A:   std_logic_vector(WIDTH - 1 downto 0) := "01001100";
    signal S:   std_logic_vector(integer(ceil(log2(real(WIDTH)))) - 1 downto 0);
    signal D:   std_logic_vector(WIDTH - 1 downto 0);
    -- for IEEE Std 1076 revision less than 2008:
    function to_string (inp: std_logic_vector) return string is
        variable image_str: string (1 to inp'length);
        alias input_str:  std_logic_vector (1 to inp'length) is inp;
    begin
        for i in input_str'range loop
            image_str(i) := character'VALUE(std_ulogic'IMAGE(input_str(i)));
        end loop;
        return image_str;
    end function;
    
begin
UUT:
    logical_shift_n
        generic map ( WIDTH => WIDTH)
        port map (
            A => A,
            S => S,
            D => D
        );
STIMULI:
    process
    begin
        for i in 0 to 2 ** S'length - 1 loop
            S <= std_logic_vector(to_unsigned(i, S'length));
            wait for 5 ns;
            report LF & HT &
                   "A = " & to_string(A) & LF & HT &
                   "S = " & to_string(S) & LF & HT &
                   "D = " & to_string(D);
            wait for 5 ns;
        end loop;
        wait for 10 ns;
        wait;
    end process;
end architecture;

And that produces:

%: ghdl -r logical_shift_n_tb
logical_shift_n.vhdl:152:13:@5ns:(report note):
    A = 01001100
    S = 000
    D = 01001100
logical_shift_n.vhdl:152:13:@15ns:(report note):
    A = 01001100
    S = 001
    D = 10011000
logical_shift_n.vhdl:152:13:@25ns:(report note):
    A = 01001100
    S = 010
    D = 00110000
logical_shift_n.vhdl:152:13:@35ns:(report note):
    A = 01001100
    S = 011
    D = 01100000
logical_shift_n.vhdl:152:13:@45ns:(report note):
    A = 01001100
    S = 100
    D = 11000000
logical_shift_n.vhdl:152:13:@55ns:(report note):
    A = 01001100
    S = 101
    D = 10000000
logical_shift_n.vhdl:152:13:@65ns:(report note):
    A = 01001100
    S = 110
    D = 00000000
logical_shift_n.vhdl:152:13:@75ns:(report note):
    A = 01001100
    S = 111
    D = 00000000
%: 

noting the format of a report statement is VHDL tool implementation dependent.

The operative ideas changing your code include using the previous 'stage' Y as the unchanged input to a mux except for the first, which uses the unchanged A input. For the shifted input the function XSLL is purloined from the earliest release of IEEE package numeric_std, the package body where the declaration is not visible by use clause, which assigns an output slice based on the shift distance overwriting part of the output value which has an initial value of all '0's. The shift distance for D1(i) is 2 to the power of the S element for each stage except the first which derives from A instead. The Y output from the previous stage afterwards is used for the mux input not shifted and the D1(i) value is derived from the previous stage Y by shifting by the power of 2 to the distance.

The testbench is just a little something thrown together to demonstrate the code (with the independent implementation of mux) analyzes, elaborates and simulates without error.

There are two more ways in general, either recursive instantiation or recursive subprogram calls where a generic or parameter can define the number of 'stages' and is modified in subsequent instances or calls until no more shifts are required. These can eliminate the need for defining your own array type.

Here type two_dim is a single dimensional array type (it has one index) with an element type that is an array type (std_logic_vector).

A casual search reveals no source for a standard package log2 function returning an integer value. I threw in the encapsulating ceil function call on the off chance the length of A is not a power of two. If the shift distance exceeds WIDTH - 1 all '0's will be returned by function XSLL which is used here to encourage exploring preexisting packages.

An array value concatenated with a another array value or a value of it's subelement type ('0') has the length of the combined values which can cause issues during simulation or synthesis where the standard requires matching elements between the assignment target and the value expression and that isn't true with

D1 <= A & '0';

Here you could also note the type of D1 is two_dim while the type of A is std_logic_vector, the types of the two arrays are not the same nor is one a subtype of the other. That issue is overcome by the assignment target D1(i) in the generate statements where type two_dim has an element type of std_logic_vector. This also provides insight as to why VHDL refers to two_dim as a single dimensional array type with an element type of std_logic_vector, where the element type is an array type (besides the lack of more than one index). Your 'rows' are elements of a value of type two_dim, while you can slice an element or index a subelement you need to index an element to refer to a value of std_logic_vector.

The semantics of VHDL are formal, that is the terms that are defined in the standard (in italics) are used in further semantic description. VHDL is a hard language to dip a toe in and the casual user resists the emersion effort while the standard will be revised at intervals shorter than the time to achieve mastery of the syntax and semantics.