Structure of VHDL code for barrel shifter with behavior architecture

18,916

Solution 1

I think you're missing a complete understanding of how hardware description differs from writing software and how processes work in VHDL. That said, you can use several concurrent assignments to do what you want (a cascade style barrel shifter). See the model below:

-- note, entirely untested!
library ieee;
use ieee.std_logic_1164.all;

entity eds_shifter is
port ( a   : in  std_logic_vector(15 downto 0) ; --input
       pos : in  std_logic_vector(3 downto 0); -- position
       opc : in  std_logic_vector(3 downto 0); -- opcode
       y   : out std_logic_vector(15 downto 0) --output
    );
end entity eds_shifter ;

architecture behavior of eds_shifter is
    signal s1,s2,s3,s4 : std_logic_vector(15 downto 0); -- intermediate steps
begin
    --opcode 0000 is shift left, else is shift right
    s1 <= a(14 downto 0) & '0' when pos(0) = '1' and opc = "0000" else --maybe shift by 1 place
        '0' & a(15 downto 1) when pos(0) = '1' and opc /= "0000" else
        a;

    s2 <= s1(13 downto 0) & "00" when pos(1) = '1' and opc = "0000" else --maybe shift 2 places 
        "00" & s1(15 downto 2) when pos(1) = '1' and opc /= "0000" else
        s1;

    s3 <= s2(11 downto 0) & x"0" when pos(2) = '1' and opc = "0000" else --maybe shift 4 places
        x"0" & s2(15 downto 4) when pos(2) = '1' and opc /= "0000" else
        s2;

    s4 <= s3(7 downto 0) & x"00" when pos(3) = '1' and opc = "0000" else --maybe shift 8 places
        x"00" & s3(15 downto 8) when pos(3) = '1' and opc /= "0000" else
        s3;

    y <= s4;
end architecture behavior;

The recommended method of accomplishing shifting in general remains using the built-in functions, and they will likely turn into the same hardware post-synthesis if the optimizer is doing its job. While exercises like this are good for understanding how hardware works, realize that this is no more efficient, and it isn't useful as a small component of a larger project because it will increase code size and reduce readability for no benefit.

Solution 2

There are several issues in the code:

  • when can't be used as sequential statement (in process; before VHDL-2008), and the else part also looks like a statement, which is not correct syntax

  • signal can't be declared in process

  • The new value of a signal take a delta cycle before it is available, so the s* must be in the sensitivity list for the value to ripple through

  • VHDL provides standard shift functions, which should be used, instead of the home build (that even looks wrong). Note that VHDL shift in-fix operators (before VHDL-2008) are known to give unexpected results, so use the functions instead; read more here.

So based on this the code with process can be updated to:

library ieee;
use ieee.numeric_bit.all;

architecture behavior of eds_shifter is
begin
  process(a, pos, opc)                                                   -- input vectors
  begin
    if opc = "0000" then  -- Shift left
      y <= bit_vector(shift_left(unsigned(a), to_integer(unsigned(pos))));
    else  -- Assuming shift right
      y <= bit_vector(shift_right(unsigned(a), to_integer(unsigned(pos))));
    end if;
  end process;
end architecture behavior;

Or with concurrent when, then process can be replaced with:

y <= bit_vector(shift_left(unsigned(a), to_integer(unsigned(pos)))) when opc = "0000" else
     bit_vector(shift_right(unsigned(a), to_integer(unsigned(pos))));

Note bit_vector is not widely used, so consider updating to use std_logic_vector instead, whereby the code can be:

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

entity eds_shifter is
  port (a   : in  std_logic_vector(15 downto 0);  --input
        pos : in  std_logic_vector(3 downto 0);   -- position
        opc : in  std_logic_vector(3 downto 0);   -- opcode
        y   : out std_logic_vector(15 downto 0)   --output
        );
end entity eds_shifter;

architecture behavior of eds_shifter is
begin

  y <= std_logic_vector(shift_left(unsigned(a), to_integer(unsigned(pos)))) when (opc = "0000") else
       std_logic_vector(shift_right(unsigned(a), to_integer(unsigned(pos))));

end architecture behavior;
Share:
18,916
user3120471
Author by

user3120471

Updated on June 15, 2022

Comments

  • user3120471
    user3120471 almost 2 years

    I am trying to build a 16 bit barrel shifter with left and right shift capabilities. I am having some issues with how to structure the code so that it will do what I think I want to do.

    I have an opcode input that decides on the direction, an input vector to be shifted, an output vector, and a position vector with 4 bits.

    I am using the position vector to set a shift 'level' in a way. I want to check position(0) and if it is set to 1, shift one position. And then check position(1) and shift 2 positions, position(3) 4 positions, and position(3) 8 positions.

    I think that if I run through every position in the position vector and sequentially shift the bits it should eventually get all options. I will add the other opcode when this direction works properly.

    I am stuck on the assigning the intermediate vectors for the architecture. I am not sure what the proper method should be and I can not find anything in google. Perhaps a Case system would work better.

    entity eds_shifter is
    port ( a : in bit_vector(15 downto 0) ; --input
        pos : in bit_vector(3 downto 0); -- position
        opc : in bit_vector(3 downto 0); -- opcode
        y : out bit_vector(15 downto 0) --output
        );
    end entity eds_shifter ;
    
    architecture behavior of eds_shifter is
    begin
    process(a,pos,opc) -- input vectors 
    signal s1,s2,s3,s4 : bit_vector(15 downto 0); -- intermediate steps
    begin
       s1 <= a(14 downto 0) & '0' when pos(0) = '1' and opc = "0000" else -- shifting left by one
       s1 <= a when pos(0) = '0' and opc = "0000" else -- carryover vector to next shift position
       s2 <= s1(13 downto 0) & '0' when pos(1) = '1' and opc = "0000" else -- shift previous vector by 2
       s2 <= s1 when pos(1) = '0' and opc = "0000" else 
       s3 <= s2(12 downto 0) & '0' when pos(2) = '1' and opc = "0000" else -- shift another 4 places
       s3 <= s2 when pos(2) = '0' and opc = "0000" else 
       s4 <= s3(7 downto 0) & '0' when pos(3) = '1' and opc = "0000" else -- shift another 8 places
       s4 <= s3 when pos(3) = '0' and opc = "0000" else 
       y <= s4 when opc = "0000";
    end process;
    end architecture behavior;
    

    Error Message

    ** Error: */02/eds_shifter.vhd(14): Illegal sequential statement.
    ** Error: */02/eds_shifter.vhd(15): Type error resolving infix expression "<=" as type std.standard.bit_vector.
    

    EDIT

    Thank you for the detailed response. I understand that there are generic shift functions built in but I want to try to figure out how to implement it myself.

    Thanks for the tip about the bit_vector. From what I understand bit types are only ok to use when you are sure that the input is not multisourced. In this case it should probably be ok, but I will go ahead and keep it in mind for the future.

    I think the concept I want to try is sound. If you check each bit in position for a 1 and shift by the appropriate amount at the end the number of bits shifted will equal the value of the position vector.

    a = "1111 1111 1111 1111" and pos = "1010" so we need to shift by decimal 10 places. So we do the first iteration and it has no change, the second shifts by 2 bits, third iteration shifts by 0 bits, and the fourth shifts by 8 bits for a total of 10 bits shifted to get a="1111 1100 0000 0000".

    My issue is not with the specific shifting operation (perhaps it is wrong and if so will use a different method, right now I am simply curious about how to implement my idea), my issue is writing code that will update the vector and then check the next position in the position vector. I was looking at what you posted about the delta cycle and sensitivity list. VHDL can be confusing since you have to describe the real world in terms of operations rippling through a processor. And it requires a different thinking than with standard programming.

    I tried the below code but it only changes and shifts in my test bench once. I set up a test bench that runs through all combinations of positions but with the following code it only shifts at pos = 1000 and then it shifts 8 places. Is there any of forcing the code to check at each if statement, not only the last one?

    entity eds_shifter is
    port ( a : in bit_vector(15 downto 0) ; --input
        pos : in bit_vector(3 downto 0); -- position
        opc : in bit_vector(3 downto 0); -- opcode
        y : out bit_vector(15 downto 0) --output
        );
    end entity eds_shifter ;
    
    architecture behavior of eds_shifter is
    begin
    process(a,pos,opc) -- input vectors 
    begin
       if  pos(0) = '1' and opc = "0000" then  -- shifting left by one
          y <= a(14 downto 0) & "0";
       else
          y <= a;
       end if;
       if  pos(1) = '1' and opc = "0000" then  -- shifting left by two
          y <= a(13 downto 0) & "00";
       else
          y <= a;
       end if; 
       if  pos(2) = '1' and opc = "0000" then  -- shifting left by four
          y <= a(11 downto 0) & "0000";
       else
          y <= a;
       end if;
       if  pos(3) = '1' and opc = "0000" then  -- shifting left by eight
          y <= a(7 downto 0) & "00000000";
       else
          y <= a;
       end if;
    end process;
    

    end architecture behavior;

    Test Bench

    entity eds_shifter_tb is 
    end eds_shifter_tb;
    
    architecture behavior of eds_shifter_tb is
        signal opc: bit_vector(3 downto 0);
        signal pos: bit_vector(3 downto 0);
        signal Y : bit_vector (15 downto 0);
        signal a: bit_vector (15 downto 0);
    
    begin 
        dut: entity work.eds_shifter(behavior)
        port map(opc => opc,
                pos => pos,
                Y => Y,
                a => a); -- assigns all ports to entity spec
    
       a <= (others => '1'),
             (others => '1') after 30 ns;                   
       opc <= "0000",
              "0001" after 30 ns;
       pos <= "0000",
            "0001" after 2 ns,
            "0010" after 4 ns,
            "0011" after 6 ns,
            "0100" after 8 ns,
            "0101" after 10 ns,
            "0110" after 12 ns,
            "0111" after 14 ns,
            "1000" after 16 ns,
            "1001" after 18 ns,
            "1010" after 20 ns,
            "1011" after 22 ns,
            "1100" after 24 ns,
            "1101" after 26 ns,
            "1110" after 28 ns,
            "1111" after 30 ns,
            "0000" after 32 ns,
             "0001" after 34 ns,
             "0010" after 36 ns,
             "0011" after 38 ns,
             "0100" after 40 ns,
             "0101" after 42 ns,
             "0110" after 44 ns,
          "0111" after 46 ns,
          "1000" after 48 ns,
          "1001" after 50 ns,
          "1010" after 52 ns,
          "1011" after 54 ns,
          "1100" after 56 ns,
          "1101" after 58 ns,
          "1110" after 60 ns,
          "1111" after 62 ns;
    end behavior;