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) 上使用多路复用器更有意义。
非常感谢
您好,我有下面的程序可以完成我想做的事情,根据输入 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) 上使用多路复用器更有意义。 非常感谢