我在下面有一个代码,用于进行减法和加法。基本上,当Binv设置时,它应该减去,而Binv是0,它应该添加。不幸的是,它似乎是在有时设置Binv时添加,并在有时未设置时减去。以下是模拟的快照:
entity ADD_SUB is
Port ( A : in STD_LOGIC_VECTOR (31 downto 0);
B : in STD_LOGIC_VECTOR (31 downto 0);
Binv : in STD_LOGIC;
C_in: in STD_LOGIC;
S : out STD_LOGIC_VECTOR (31 downto 0);
TEST : out STD_LOGIC_VECTOR (31 downto 0);
C_out : out STD_LOGIC
);
end ADD_SUB;
architecture ADD_SUB_ARCH of ADD_SUB is
signal S_wider : std_logic_vector(32 downto 0);
begin
process (A,B,C_in,Binv)
begin
if Binv = '0' then
S_wider <= ( A(31) & A) + ( B(31) & B) + C_in;
elsif Binv = '1' then
S_wider <= (A(31)& A) + ('1'& not B) + '1';
else
S_wider <= std_logic_vector(to_signed(0,32));
end if;
S <= S_wider(31 downto 0);
C_out <= S_wider(32);
end process;
我得到了毫无意义的荒谬结果。在第一种情况下,你可以看到我试图做(50 - 30)(Binv是1)。我得到80这是错的。然而,你可以看到它在(30 - 50)上工作,我得到-20。第二个问题是我尝试做的事情(30 +( - 50)),但它显示为20。
结果完全没了,我看不出我出错的地方
答案 0 :(得分:1)
吉姆是完全正确的。
有几点可能值得。
首先,+ C_in
或+ not C_in
意味着两个32位加法,其中一个在合成期间被优化掉,只留下进位到剩余的加法。
其次,您实际上只是使用B
来操纵C_in
和Binv
。对于B
的补码+ X"00000001
,减法相当于添加两个补码。请注意,Jim将C_in
与Binv
反转,允许C_in
用于菊花链操作(例如64位加或减32位ALU)。
这两个点都使用以下代码进行说明,该代码也只使用numeric_std.unsigned
而且只需要无符号numeric_std."+"
:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity add_sub is
port (
a: in std_logic_vector (31 downto 0);
b: in std_logic_vector (31 downto 0);
binv: in std_logic;
c_in: in std_logic;
s: out std_logic_vector (31 downto 0);
test: out std_logic_vector (31 downto 0);
c_out: out std_logic
);
end entity;
architecture foo of add_sub is
begin
UNLABELLED:
process (a,b,c_in,binv)
variable x,y,z: std_logic_vector (33 downto 0);
begin
x := a(31) & a & '1'; -- this '1' generates a true carry in to z(1)
-- z(0) is optimized away as unused it's carry
-- retained as carry in to the next MS bit.
if binv = '0' then
y := b(31) & b & c_in;
elsif binv = '1' then
y := not b(31) & not b & not c_in;
else
y := (others => 'X'); -- 'X' on binv is propagated from b onto y
end if;
z := std_logic_vector( unsigned(x) + unsigned(y)); -- only one add
c_out <= z(33);
s <= z(32 downto 1);
end process;
end architecture;
上面的例子将C_in与A和B的LS位更直接地连接到加法器级,并给出:
(综合软件通常足够智能,可以使用Jim的形式修改为基于Binv
和A
和B
加上或减去的形式扩展到33没有任何直接位或位域操作的位,我们的综合工具已经有超过25年的时间才能做到正确。)
使用以下测试台生成波形:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity tb_add_sub is
end entity;
architecture foo of tb_add_sub is
signal a: std_logic_vector (31 downto 0) := (others =>'0');
signal b: std_logic_vector (31 downto 0) := (others =>'0');
signal binv: std_logic := '0';
signal c_in: std_logic := '0';
signal s: std_logic_vector (31 downto 0);
signal test: std_logic_vector (31 downto 0);
signal c_out: std_logic;
begin
DUT:
entity work.add_sub
port map (
a => a,
b => b,
binv => binv,
c_in => c_in,
s => s,
test => test,
c_out => c_out
);
STIMULUS:
process
begin
wait for 100 ns;
a <= std_logic_vector(to_signed(50,a'length));
b <= std_logic_vector(to_signed(30,b'length));
wait for 100 ns;
binv <= '1';
wait for 100 ns;
binv <= '0';
a <= std_logic_vector(to_signed(30,a'length));
b <= std_logic_vector(to_signed(-50,b'length));
wait for 100 ns;
binv <= '1';
b <= std_logic_vector(to_signed(50,b'length));
wait for 600 ns;
wait;
end process;
end architecture;
答案 1 :(得分:0)
enter code here
您的减法公式不正确。像@neodelphi建议的那样,它应该是:
A - B = A + (not B) + 1
然而,这并没有考虑到携带以及如何处理它。如果我没记错的话,减去借款:
A - B - C_in = A + (not B) + 1 - C_in = A + (not B) + (1 - C_in)
现在请注意:
(1 - C_in) = not C_in
现在,将其转换为VHDL。如果我忽略了你使用包std_logic_unsigned(Ahem)进行带符号数学运算的事实,你可以编写(类似于@neodelphi):
S_wider <= (A(31)& A) + (not B(31) & not B) + not C_in ;
注意在软件包std_logic_unsigned以及带有VHDL-2008的numeric_std中,添加std_ulogic没有问题。
我对类型和包的建议非常简单。如果您正在进行数学运算,请使用数学类型,如签名(在此处匹配数学)或无符号(对于其他情况)。我考虑这些文档的一部分。
此外,使用适当的类型非常重要,因为数学包允许您添加两个不同大小的数组值。如果您使用适当的类型,他们会为签名或&#39; 0&#39;执行相应的扩展复制符号位。填写无符号。
因此,如果您使用了signed类型,那么您可以使用第一个参数(A)来调整结果的大小,并且对B进行了懒惰并写入:
S_wider <= (A(31)& A) + not B + not C_in ;
BTW,测试&#39; 0&#39;和&#39; 1&#39;不以任何方式帮助硬件。我的建议是懒惰(和安全)并写:
if Binv = '0' then
S_wider <= ( A(31) & A) + ( B(31) & B) + C_in;
else
S_wider <= (A(31)& A) + (not B(31) & not B) + not C_in;
end if;
或者是偏执和警惕,并使输出是&#39; X&#39;当控制输入是&#39; X时。但是一定要仔细检查你的&#34; elsif&#34;表达式 - 当它更复杂时发现错误并且找到错误可能很有挑战性(意味着你最好有包含控件的所有可能输入值的测试用例):
if Binv = '0' then
S_wider <= ( A(31) & A) + ( B(31) & B) + C_in;
elsif Binv = '1' then
S_wider <= (A(31)& A) + (not B(31) & not B) + not C_in;
else
S_wider <= (others => 'X') ; -- X in propagates as X out can help debug
end if;
答案 2 :(得分:0)
AddSub模块只有一个控制输入,可以调用\ bar {add} / sub。这意味着,如果add_sub为零,则执行添加操作,如果其执行减法操作。
C_in和Binv之间存在稳固的关系。如果要添加Binv和C_in为零,如果要减去两者都是一个。
对于减法器,加法器的等式只是S := A + B + 0
,可以通过某些变换检索它:
S := A - B -- transform into an add operation
S := A + (- B) -- transform negative number using 2's complement
S := A + ( 2's complement of B) -- transform 2's complement into 1's complement
S := A + ((1's complement of B) + 1) -- transform 1's complement into bit wise not operation
S := A + ((bit wise not of B) + 1)
如果你将这两个方程结合起来,你会得到:
S := A + (B xor vector(add_sub)) + add_sub
所以在VHDL中这将是:
S_wider <= unsigned('0' & A) + unsigned('0' & (B xor (B'range => add_sub))) + unsigned((B'range => '0') & add_sub);
S <= S_wider(S'range);
C_out <= S_wider(S_width'high);
综合足够聪明,可以找到一个带有可切换常量输入3的3:1加法器作为addsub-macro块。如果您想执行签名的add / sub,请相应地交换转换函数和符号扩展名。