减法器模块VHDL生成错误的值

时间:2014-09-20 15:21:57

标签: vhdl

我在下面有一个代码,用于进行减法和加法。基本上,当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;

enter image description here

我得到了毫无意义的荒谬结果。在第一种情况下,你可以看到我试图做(50 - 30)(Binv是1)。我得到80这是错的。然而,你可以看到它在(30 - 50)上工作,我得到-20。第二个问题是我尝试做的事情(30 +( - 50)),但它显示为20。

结果完全没了,我看不出我出错的地方

3 个答案:

答案 0 :(得分:1)

吉姆是完全正确的。

有几点可能值得。

首先,+ C_in+ not C_in意味着两个32位加法,其中一个在合成期间被优化掉,只留下进位到剩余的加法。

其次,您实际上只是使用B来操纵C_inBinv。对于B的补码+ X"00000001,减法相当于添加两个补码。请注意,Jim将C_inBinv反转,允许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位更直接地连接到加法器级,并给出:

tb_add_sub_nvc.png (图像可以单击打开)

(综合软件通常足够智能,可以使用Jim的形式修改为基于BinvAB加上或减去的形式扩展到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,请相应地交换转换函数和符号扩展名。