VHDL 移位运算符

VHDL shift operators

您好,我有下面的程序可以完成我想做的事情,根据输入 s_right 或 s_enable 向左或向右移动 1 位。 numeric.std 库包含移位运算符,我想开始使用它们,以便更好地掌握这门语言,但找不到能告诉我正确使用它们的方法的好例子

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; 

如有帮助将不胜感激。

来自包 numeric_std,正文:

  -- 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;

这些调用:

  -----------------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;

你在哪里找到 SHIFT_LEFT 用 '0' 填充 reg(0) 和 SHIFT_RIGHT 用 '0' 填充 reg(7)

您之前已将 ser_in 分别分配给 reg(7)reg(0),这些分配将丢失(语句序列中的最后一个分配获胜)。

所以颠倒作业的顺序:

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; 

请注意,这也使用 2:1 多路复用器摆脱了 ser_out 触发器,摆脱了对 reg(7 downto 0) 的多余 'hold' 赋值,使用 rising_edge 函数用于对来自 clk 上的亚稳态值的事件免疫,并将 selectors 分配移动到并发信号分配,从而使该过程完全时钟同步。

使用测试台(仅适用于右移):

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;

我们得到:

请注意,此时我们尚未测试 s_enable 或 s_right = '0',但 SHIFT_RIGHT 有效。 SHIFT_LEFT 行得通吗?

秘密是在 shift 函数之后将序列号分配给 reg(0) 或 reg(7)。

感谢user1155120的详细回复。我已经使用下面的描述来模拟通过寄存器左右移动一位。

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);
SIGNAL selectors:   std_logic_vector (1 downto 0);
BEGIN
selectors <=  s_right & s_enable;
ser_out <=  reg(7) when selectors = "01" else
            reg(0); 
shift_reg:
PROCESS (clk)
BEGIN
    IF rising_edge (clk) THEN
        IF selectors = "01" THEN
            reg <= std_logic_vector(shift_left(unsigned(reg), 1));
            reg (0) <= ser_in;
            --  ser_out <= reg (7); 
        ELSIF selectors = "11" THEN
            reg <= std_logic_vector(shift_right(unsigned(reg),1)); 
            reg (7) <= ser_in; 
            --  ser_out <= reg (0);
        END IF;
    END IF;
END PROCESS;
END ARCHITECTURE;       

对于仿真,我一直在使用 Quartus II ModSim,我从中得到以下结果:

结果看起来不错。将单个 1 位状态添加到寄存器中,我可以看到它根据输入 s_right 或 s_enable 的切换移动到寄存器的左侧或右侧。 与我添加到原始描述中的加法锁存器相比,在 set_out 和 reg(0) 和 (7) 上使用多路复用器更有意义。 非常感谢