Search code examples
vhdl

VHDL shift operators


Hi I have the program below that does what I want to do, shift 1 bit left or right depending on inputs s_right or s_enable. The numeric.std library contains shift operators and I want to start using them so I get a better grasp on the language but can find no good examples that show me the right way at using them

LIBRARY IEEE;
USE IEEE.std_logic_1164.all;
USE IEEE.numeric_std.all;


ENTITY  S_REG8 IS
port ( clk, s_enable, s_right, ser_in   :   in std_logic;
         ser_out                                :   out std_logic
        );
END ENTITY S_REG8;

ARCHITECTURE dflow OF S_REG8 IS
SIGNAL reg : std_logic_vector (7 DOWNTO 0); --7,6,5,4,3,2,1,0
SIGNAL selectors : std_logic_vector (1 DOWNTO 0);
BEGIN
SHIFT_REG:PROCESS (clk, s_enable, s_right)
    BEGIN
    selectors <= s_enable & s_right;
        IF clk'EVENT and clk ='1' THEN
            IF selectors <= "00" THEN
                reg (7 DOWNTO 0) <= reg (7 DOWNTO 0);
            ELSIF selectors <= "01" THEN
                reg (7 DOWNTO 0) <= reg (7 DOWNTO 0);
            ELSIF selectors <="10" THEN
                reg (0) <= ser_in;
                ser_out <= reg(7);
                --reg <= std_logic_vector(shift_left(unsigned(reg), 1);
                --SHIFT_LEFT (ARG: UNSIGNED; COUNT: NATURAL)
                reg (7 DOWNTO 1) <= reg (6 DOWNTO 0);

            ELSIF selectors <= "11" THEN
                reg (7) <= ser_in;
                ser_out <= reg(0);
                --reg <= shift_right(std_logic_vector(reg));
                reg (6 DOWNTO 0) <= reg (7 DOWNTO 1);                       
            END IF;
        END IF;
END PROCESS;
END ARCHITECTURE dflow; 

Any help would be great thanks.


Solution

  • From package numeric_std, the body:

      -- Id: S.1
      function SHIFT_LEFT (ARG: UNSIGNED; COUNT: NATURAL) return UNSIGNED is
      begin
        if (ARG'LENGTH < 1) then return NAU;
        end if;
        return UNSIGNED(XSLL(STD_LOGIC_VECTOR(ARG), COUNT));
      end SHIFT_LEFT;
    
      -- Id: S.2
      function SHIFT_RIGHT (ARG: UNSIGNED; COUNT: NATURAL) return UNSIGNED is
      begin
        if (ARG'LENGTH < 1) then return NAU;
        end if;
        return UNSIGNED(XSRL(STD_LOGIC_VECTOR(ARG), COUNT));
      end SHIFT_RIGHT;
    

    These call:

      -----------------Local Subprograms - shift/rotate ops-------------------------
    
      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;
    
      function XSRL (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-COUNT downto 0) := XARG(ARG_L downto COUNT);
        end if;
        return RESULT;   end XSRL;
    

    Where you find SHIFT_LEFT fills reg(0) with '0' and SHIFT_RIGHT fills reg(7) with '0'.

    You had previously assigned ser_in to reg(7) and reg(0) respectively, those assignments would be lost (the last assignment in a sequence of statements wins).

    So reverse the order of the assignments:

    architecture fie of s_reg8 is
        signal reg:         std_logic_vector (7 downto 0);
        signal selectors:   std_logic_vector (1 downto 0);
    begin
    
        -- make process purely clock synchrnous
        selectors <= s_enable & s_right;
        -- ser_out multiplexer instead of flip flop:
        ser_out <=  reg(7) when s_right =  '0' else
                    reg(0); --  when s_right = '1' else
                    -- 'X';
    shift_reg:
        process (clk)
        begin
            if rising_edge (clk) then -- immunity to metastability transitions
            -- if clk'event and clk ='1' then
                -- if selectors <= "00" then  -- redundant
                --     reg (7 downto 0) <= reg (7 downto 0);
                -- if selectors <= "01" then  -- redundant 
                --    reg (7 downto 0) <= reg (7 downto 0);
                -- elsif selectors <= "10" then
                if selectors = "10" then -- was elsif equality not 
                    reg <= std_logic_vector(shift_left(unsigned(reg), 1));
                    -- also added missing right paren
                    reg (0) <= ser_in;  -- change the order so this occurs
                    -- ser_out <= reg(7); -- no flip flop
                    -- reg <= std_logic_vector(shift_left(unsigned(reg), 1); 
                    -- SHIFT_LEFT (ARG: UNSIGNED; COUNT: NATURAL)
                    -- reg (7 downto 1) <= reg (6 downto 0);
    
                -- elsif selectors <= "11" then
                elsif selectors = "11" then
                    reg <= std_logic_vector(shift_right(unsigned(reg),1)); 
                    -- missing distance, proper type conversion
                    reg (7) <= ser_in;  -- change order so this assignment happens
                    -- ser_out <= reg(0); -- no flip flop
                    -- reg <= shift_right(std_logic_vector(reg));
                    -- reg (6 downto 0) <= reg (7 downto 1);                       
                end if;
            end if;
    end process;
    end architecture; 
    

    Notice this also gets rid of the ser_out flip flop using a 2:1 mux instead, get's rid of the superfluous 'hold' assignments to reg(7 downto 0), uses the rising_edge function for immunity to events from a metastability value on clk and moves the selectors assignment to a concurrent signal assignment, allowing the process to be purely clock synchronous.

    With a testbench (for shift right only):

    library ieee;
    use ieee.std_logic_1164.all;
    
    entity s_reg8_tb is
    end entity;
    
    architecture foo of s_reg8_tb is
        signal clk:             std_logic := '0';
        signal s_enable:        std_logic;
        signal s_right:         std_logic;
        signal ser_in:          std_logic;
        signal ser_out:         std_logic;
        constant ser_in_val0:   std_logic_vector (1 to 8) := x"B9";
        constant ser_in_val1:   std_logic_vector (1 to 8) := x"AC";
    begin
        CLOCK: -- clock period 20 ns
        process
        begin
            wait for 10 ns;
            clk <= not clk;
            if now > 800 ns then -- automagically stop the clock
                wait;
            end if;
        end process;
    DUT:
        entity work.s_reg8
            port map (
                clk => clk, 
                s_enable => s_enable, 
                s_right => s_right, 
                ser_in => ser_in,
                ser_out => ser_out
            );
    STIMULUS:
        process
        begin
            s_enable <= '1';
            s_right <= '1';
            for i in 1 to 8 loop
                ser_in  <= ser_in_val0(i);
                wait for 20 ns; -- one clock period
            end loop;
            for i in 1 to 8 loop
                ser_in  <= ser_in_val1(i);
                wait for 20 ns; -- one clock period
            end loop;   
            for i in 1 to 8 loop  -- so we get all val0 out
                ser_in  <= ser_in_val0(i);
                wait for 20 ns; -- one clock period
            end loop;   
            s_enable <= '0';
            wait for 20 ns;  -- one clock
            wait;      
        end process;
    end architecture;
    

    We get: s_reg8_tb.png

    Notice at this point we haven't tested s_enable nor s_right = '0', but SHIFT_RIGHT works. Will SHIFT_LEFT work?

    The secret was assigning the serial in to reg(0) or reg(7) after the shift function.