您被敦促使用 sll 之类的原因通常是因为
综合工具不支持具有非静态边界的循环语句
(loop_nr)。循环展开,需要一个静态值来确定如何
展开了许多循环迭代(要生成多少硬件)。
正如 Morten 指出的那样,与您的断言相反,您的代码没有分析
它可以编译。
在代码开头插入以下四行后,我们看到
第 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);
在case语句中,选择“010”(一个srl等价的hard
编码到 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 中想要一个“0011”,在中想要一个“0000”
Result_Low。
您会看到恰好一次左移的结果 - ('U','U','U','1')
Result_High 和 Result_Low 中的“1000”。
这是由于在增量循环中执行循环语句(没有干预
模拟时间流逝)。在流程声明中只有一个驱动程序
每个信号。其最终结果是只有一个未来价值
对于当前模拟时间,最后分配的值将是
在当前的投影输出波形中安排的一个
模拟时间。 (本质上,循环语句中的赋值
信号出现一次,因为连续的值取决于分配
发生它看起来只有一个任务)。
有两种方法可以解决此问题。首先是使用变量
在循环内部分配,并将相应的信号分配给
循环语句后面的变量。如前所述,循环绑定不是
静态的,你不能合成循环。
第二种方法是通过执行移位分配来消除循环
依次。基本上每个时钟 1 个班次,在最后一个之后发出就绪信号
发生转变。
还可以通过使用
case 语句(或在 VHDL 2008 中使用顺序条件信号
如果您的合成应分配顺序选择的信号分配
工具供应商支持它们)。这具有在一个时钟内运行的优势。
请注意,所有这些都需要持有一个整数变量
to_integer(unsigned(OperandB)).
当您的综合工具供应商支持时,所有这些都可以忽略
sll(另一种情况下为 srl)或 SHIFT_LEFT 和 SHIFT_RIGHT 来自包
numeric_std,你可以使用它们。
不使用 sll 或 SHIFT_LEFT 的通用(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 语句中的所有选项都没有包含在简单的
测试台。
当然,与大多数事情一样,获得所需的方法不止两种
结果。
您可以为 Result_High 和
Result_Low,每个阶段从前一阶段的输出馈送(或
第一阶段的操作数A)作为A输入,选择是适当的
来自 OperandB 的“位”,以及前一级多路复用器的 B 输入
输出逻辑移位 1(填充“0”)。
多路复用器可以是函数、组件或过程语句。经过
使用三对一多路复用器,您可以实现对称移位
操作指定操作(左和右)。如果您想包括带符号的班次,
而不是“0”填充右移,您可以填充符号位值。 ...
您还应该为那些有效的情况分配 Ready
而且因为您对其中一个答案的评论需要使用具有整数值的循环:
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';
这给出了:
为了证明 Result 的两半都在工作,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);