我正在为4位ALU编写代码,当我想为左移操作编写时我遇到了问题。我有两个输入(operandA和operandB)。我想将操作数B转换为十进制(例如" 0010"转换为' 2')然后将操作数A转换为左边2次。我的代码已编译但我不确定它是否属实。提前谢谢。
entity ALU is
port(
reset_n : in std_logic;
clk : in std_logic;
OperandA : in std_logic_vector(3 downto 0);
OperandB : in std_logic_vector(3 downto 0);
Operation : in std_logic_vector(2 downto 0);
Start : in std_logic;
Result_Low : out std_logic_vector(3 downto 0);
Result_High : out std_logic_vector(3 downto 0);
Ready : out std_logic;
Errorsig : out std_logic);
end ALU;
architecture behavior of ALU is
signal loop_nr : integer range 0 to 15;
begin
process (reset_n, clk, operation)
variable tempHigh : std_logic_vector(4 downto 0);
begin
if (reset_n = '0') then
Result_Low <= (others => '0');
Result_High <= (others => '0');
Errorsig <= '0';
elsif (clk'event and clk = '1') then
case operation is
when "001" =>
for i in 0 to loop_nr loop
loop_nr <= to_integer(unsigned(OperandB));
Result_Low <= OperandA(2 downto 0)&'0';
Result_High <= tempHigh(2 downto 0) & OperandA(3);
end loop;
Ready <= '1';
Errorsig <= '0';
when "010" =>
Result_Low <= OperandB(0)& OperandA(3 downto 1);
Result_High <= OperandB(3 downto 1);
Ready <= '1';
when others =>
Result_Low <= (others => '0');
ready <= '0';
Errorsig <= '0';
end case;
end if;
end process;
end behavior;
答案 0 :(得分:2)
对于左移两次,语法应如下:
A&lt; = A sll 2; - 左移逻辑2位
我不太明白为什么需要以十进制转换操作数B.它可以用作二进制或十进制值,也可以用作与保存基数无关的任何时间点的十六进制值。
答案 1 :(得分:0)
在VHDL-2008之前,运营商sll
可能无法始终按预期工作(详情请参阅
here),
所以请考虑使用ieee.numeric_std
中的函数进行移位,例如:
y <= std_logic_vector(shift_left(unsigned(OperandA), to_integer(unsigned(OperandB))));
另请注意,Result_High
在端口中声明为std_logic_vector(3 downto
0)
,但在第41行中指定为Result_High <= OperandB(3 downto 1)
,
分配一个小于大小的位。
代码的假设是使用ieee.numeric_std
。
答案 2 :(得分:0)
你被催促使用sll之类的原因是因为一般情况 综合工具不支持具有非静态边界的循环语句 (loop_nr)。循环展开,需要静态值来确定方式 展开了许多循环迭代(要生成多少硬件)。
正如Morten指出你的代码没有分析,与你断言相反 它编译。</ p>
在代码的开头插入以下四行后,我们看到了 第41行的错误:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
--(blank, a spacer that doesn't show up in the code highlighter)
ghdl -a ALU.vhdl
ALU.vhdl:41:26: length of value does not match length of target
ghdl: compilation error
看起来像
Result_High <= '0' & OperandB(3 downto 1);
意图在案例陈述中,选择“010”(srl相当难 编码到1的距离,大概是为了匹配sll的正确行为 当量)。之后您的设计描述将进行分析。
此外,VHDL中未反映其他算法描述错误 语法或语义错误。
编写一个简单的测试平台:
library ieee;
use ieee.std_logic_1164.all;
entity alu_tb is
end entity;
architecture foo of alu_tb is
signal reset_n: std_logic := '0';
signal clk: std_logic := '0';
signal OperandA: std_logic_vector(3 downto 0) :="1100"; -- X"C"
signal OperandB: std_logic_vector(3 downto 0) :="0010"; -- 2
signal Operation: std_logic_vector(2 downto 0):= "001"; -- shft right
signal Start: std_logic; -- Not currently used
signal Result_Low: std_logic_vector(3 downto 0);
signal Result_High: std_logic_vector(3 downto 0);
signal Ready: std_logic;
signal Errorsig: std_logic;
begin
DUT:
entity work.ALU
port map (
reset_n => reset_n,
clk => clk,
OperandA => OperandA,
OperandB => OperandB,
Operation => Operation,
Start => Start,
Result_Low => Result_Low,
Result_High => Result_High,
Ready => Ready,
Errorsig => Errorsig
);
CLOCK:
process
begin
wait for 10 ns;
clk <= not clk;
if Now > 100 ns then
wait;
end if;
end process;
STIMULUS:
process
begin
wait for 20 ns;
reset_n <= '1';
wait;
end process;
end architecture;
给我们一个示范:
首先要说的是Result_High获得了一些'U'。这是 由tempHigh未初始化或分配引起的。
接下来要注意的是移位结果是错误的(Result_Low 和Result_High)。我希望你在Result_High和“0000”中想要一个“0011” Result_Low。
你看到正好一个左移的结果 - ('U','U','U','1') Result_Low中的Result_High和“1000”。
这是由在delta周期中执行循环语句引起的(没有干预 模拟时间段。在进程语句中只有一个驱动程序 每个信号。这样的净效果是只有一个未来值 对于当前模拟时间和最后分配的值将是 在电流的预计输出波形中安排的那个 模拟时间。 (基本上,循环语句中的赋值为 信号发生一次,因为连续值取决于赋值 发生它似乎只有一个任务。)
有两种方法可以解决此问题。首先是使用变量 在循环内部分配并将相应的信号分配给 循环语句后面的变量。如前所述,循环绑定不是 静态,你不能合成循环。
第二种方法是通过执行移位分配来消除循环 顺序。基本上每个时钟1个移位,在最后一个信号之后发出信号 发生转变。
还可以通过使用a来支持循环的静态边界问题 case语句(或在VHDL 2008中使用顺序条件信号 你的综合应该分配顺序选择的信号分配 工具供应商支持他们)。这具有在一个时钟内操作的优点。
注意所有这些都需要保持整数变量 to_integer(无符号(运算数))。
当您的综合工具供应商支持时,所有这一切都可以进行 sll(和另一种情况下的srl)或来自包的SHIFT_LEFT和SHIFT_RIGHT numeric_std,您可以使用它们。
不使用sll或SHIFT_LEFT的通用(pre VHDL 2008)修复可能是:
begin
process (reset_n, clk, operation)
variable tempHigh : std_logic_vector(4 downto 0);
variable loop_int: integer range 0 to 15;
begin
if (reset_n = '0') then
Result_Low <= (others => '0');
Result_High <= (others => '0');
Errorsig <= '0';
elsif (clk'event and clk = '1') then
case operation is
when "001" =>
loop_int := to_integer(unsigned(OperandB));
case loop_int is
when 0 =>
Result_Low <= OperandA;
Result_High <= (others => '0');
when 1 =>
Result_Low <= OperandA(2 downto 0) & '0';
Result_High <= "000" & OperandA(3);
when 2 =>
Result_Low <= OperandA(1 downto 0) & "00";
Result_High <= "00" & OperandA(3 downto 2);
when 3 =>
Result_Low <= OperandA(0) & "000";
Result_High <= "0" & OperandA(3 downto 1);
when 4 =>
Result_Low <= (others => '0');
Result_High <= OperandA(3 downto 0);
when 5 =>
Result_Low <= (others => '0');
Result_High <= OperandA(2 downto 0) & '0';
when 6 =>
Result_Low <= (others => '0');
Result_High <= OperandA(1 downto 0) & "00";
when 7 =>
Result_Low <= (others => '0');
Result_High <= OperandA(0) & "000";
when others =>
Result_Low <= (others => '0');
Result_High <= (others => '0');
end case;
-- for i in 0 to loop_nr loop
-- loop_nr <= to_integer(unsigned(OperandB));
-- Result_Low <= OperandA(2 downto 0)&'0';
-- Result_High <= tempHigh(2 downto 0) & OperandA(3);
-- end loop;
Ready <= '1';
Errorsig <= '0';
给出了:
正确答案(所有不使用信号loop_nr)。
请注意,case语句中的所有选项都不包含在simple中 试验台。
当然,与大多数事情一样,有两种以上的方法可以达到预期效果 结果
对于Result_High和,您可以使用连续的2到1个多路复用器 Result_Low,每个阶段从前一阶段的输出(或 OperandA作为第一阶段)作为A输入,选择是合适的 来自OperandB的'bit',以及前一阶段的多路复用器的B输入 输出逻辑上移1('0'填充)。
多路复用器可以是函数,组件或过程语句。通过 使用三对一多路复用器,您可以实现对称移位 操作指定的操作(左和右)。如果你想包括签名班次, 而不是'0'填充右移,您可以填充符号位值。 ...
对于有效的情况,您还应该为Ready&lt; ='0'分配 可以调度连续的操作值。
因为你对其中一个答案的评论需要使用一个带整数值的循环:
process (reset_n, clk, operation)
variable tempHigh : std_logic_vector(4 downto 0);
variable tempLow: std_logic_vector(3 downto 0); --added
variable loop_int: integer range 0 to 15; --added
begin
if (reset_n = '0') then
Result_Low <= (others => '0');
Result_High <= (others => '0');
Errorsig <= '0';
elsif (clk'event and clk = '1') then
case operation is
when "001" =>
tempLow := OperandA; --added
tempHigh := (others => '0'); --added
loop_int := to_integer(unsigned(OperandB)); --added
-- for i in 0 to loop_nr loop
-- loop_nr <= to_integer(unsigned(OperandB));
-- Result_Low <= OperandA(2 downto 0)&'0';
-- Result_High <= tempHigh(2 downto 0) & OperandA(3);
-- end loop;
-- More added:
if loop_int /= 0 then
for i in 1 to loop_int loop
tempHigh (3 downto 0) := tempHigh (2 downto 0) & tempLow(3);
-- 'read' tempLow(3) before it's updated
tempLow := tempLow(2 downto 0) & '0';
end loop;
Result_Low <= tempLow;
Result_High <= tempHigh(3 downto 0);
else
Result_Low <= OperandA;
Result_High <= (others => '0');
end if;
Ready <= '1';
Errorsig <= '0';
给出了:
为了证明结果的两半都工作,OperandA的默认值已经改为“0110”:
还要注意循环从1开始而不是0以防止你有一个额外的移位,并且检查非零loop_int以防止for循环执行至少一次。
在这些情况下是否可以制作可合成的循环?
是
循环必须解决所有可能的移位(loop_int的范围)并测试i
是否属于移位阈值:
process (reset_n, clk, operation)
variable tempHigh : std_logic_vector(4 downto 0);
variable tempLow: std_logic_vector(3 downto 0); --added
subtype loop_range is integer range 0 to 15;
variable loop_int: integer range 0 to 15; --added
begin
if (reset_n = '0') then
Result_Low <= (others => '0');
Result_High <= (others => '0');
Errorsig <= '0';
elsif (clk'event and clk = '1') then
case operation is
when "001" =>
tempLow := OperandA; --added
tempHigh := (others => '0'); --added
loop_int := to_integer(unsigned(OperandB)); --added
for i in loop_range loop
if i < loop_int then
tempHigh (3 downto 0) := tempHigh (2 downto 0) & tempLow(3);
-- 'read' tempLow(3) before it's updated
tempLow := tempLow(2 downto 0) & '0';
end if;
end loop;
Result_Low <= tempLow;
Result_High <= tempHigh(3 downto 0);