ALU实现w / ADDER

时间:2015-11-20 23:05:06

标签: logic vhdl hdl

您好我正在尝试创建一个带有少量操作码的32位加法器,除了两种情况之外我还能很好地工作,我似乎无法找到导致它们的原因..也许你可以帮助我?

减法情况总是由于某种原因而失败,并且ADDC情况在它应该使用由ADDS操作创建的c_reg中的进位位时无法计算正确的输出。

LIBRARY  ieee;
USE  ieee.std_logic_1164.ALL;
USE ieee.numeric_std.ALL;

ENTITY ALU IS 
    GENERIC(WIDTH : NATURAL := 32);
    PORT(Clk    : IN STD_LOGIC := '0';
         Reset  : IN STD_LOGIC := '0';
         A      : IN STD_LOGIC_VECTOR(WIDTH-1 DOWNTO 0) := (OTHERS => '0');
         B      : IN STD_LOGIC_VECTOR(WIDTH-1 DOWNTO 0) := (OTHERS => '0');
         Op     : IN STD_LOGIC_VECTOR(3 DOWNTO 0) := (OTHERS => '0');
         Outs   : OUT STD_LOGIC_VECTOR(WIDTH-1 DOWNTO 0));
END ALU;

ARCHITECTURE arch_ALU OF ALU IS

    COMPONENT adder
    PORT(OpA    : IN    STD_LOGIC_VECTOR(WIDTH-1 DOWNTO 0);
         OpB    : IN    STD_LOGIC_VECTOR(WIDTH-1 DOWNTO 0);
         Cin    : IN    STD_LOGIC;
         Cout   : OUT   STD_LOGIC;
         Result : OUT   STD_LOGIC_VECTOR(WIDTH-1 DOWNTO 0));
END COMPONENT;

    SIGNAL adder_output : STD_LOGIC_VECTOR(WIDTH-1 DOWNTO 0) := (OTHERS => '0');
    SIGNAL B_neg        : STD_LOGIC_VECTOR(WIDTH-1 DOWNTO 0) := (OTHERS => '0');
    SIGNAL c_flag   : STD_LOGIC := '0';
    SIGNAL c_reg    : STD_LOGIC := '0';
    SIGNAL cin      : STD_LOGIC := '0';

BEGIN

adder_comp : adder
   PORT MAP(OpA => A,
            OpB => B_neg,
            Cin => cin,
            Result => adder_output,
            Cout => c_flag);        

    WITH Op SELECT 
        B_neg <= NOT(B) WHEN "1000",
                     B  WHEN OTHERS;

    WITH Op SELECT
        cin <= '1'      WHEN "1000", -- SUB
                c_reg   WHEN "0111", -- ADDC
                '0'     WHEN OTHERS; -- ADD/ADDS    

    ALU_Process:
    PROCESS(Clk)
    BEGIN
        IF Reset = '0' THEN
            Outs <= (OTHERS => '0');
        ELSIF rising_edge(Clk) THEN
            CASE Op IS
                WHEN "0001" => Outs <= A AND B;
                WHEN "0010" => Outs <= A OR  B;
                WHEN "0011" => Outs <= A NOR B;
                WHEN "0100" => Outs <= A XOR B;
                WHEN "0101" => Outs <= adder_output; -- ADD 
                WHEN "0110" => Outs <= adder_output; -- ADDS
                    c_reg <= c_flag;    
                WHEN "0111" => Outs <= adder_output; -- ADDC
                WHEN "1000" => Outs <= adder_output; -- SUB
                WHEN "1001" => Outs <= STD_LOGIC_VECTOR(UNSIGNED(A) SLL to_integer(UNSIGNED(B(4 DOWNTO 0))));
                WHEN "1010" => Outs <= STD_LOGIC_VECTOR(unsigned(A) SRL to_integer(UNSIGNED(B(4 DOWNTO 0))));
                WHEN "1011" => Outs <= STD_LOGIC_VECTOR(shift_right(SIGNED(A),to_integer(UNSIGNED(B(4 DOWNTO 0)))));
                WHEN OTHERS => Outs <= (OTHERS => '0');
            END CASE;
        END IF;
    END PROCESS;
END arch_ALU;

只有ADDS操作才能将其执行写入c_reg,ADDC操作在计算输出时应考虑c_reg 加法器经过测试并正常工作,因此问题不在加法器设计中。

首先,我想了解减法的问题,因为它正在进行减法,但结果有点偏。例如:

A:h'E6A4960F B:h'7B494E34 Op:d'1000 出局:h'6B5B47DA,而它应该是h'6B5B47DB

A:h'EFDE31A3 B:h'0BCAB8FA Op:d'1000 出:h'E41378BB,而应该是h'E41378A9

你能发现我的错误吗?因为我当然不能..

2 个答案:

答案 0 :(得分:1)

虽然您没有提供Minimal, Complete, and Verifiable example,但读者至少可以测试您设计中减去的部分:

library  ieee;
use  ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity alu is 
    generic (width:  natural := 32);
    port (
        clk:     in  std_logic := '0';
        reset:   in  std_logic := '0';
        a:       in  std_logic_vector(width - 1 downto 0) := (others => '0');
        b:       in  std_logic_vector(width - 1 downto 0) := (others => '0');
        op:      in  std_logic_vector(3 downto 0) := (others => '0');
        outs:    out std_logic_vector(width - 1 downto 0)
    );
end alu;

architecture arch_alu of alu is

--     component adder
--     port (
--          opa:     in    std_logic_vector(width - 1 downto 0);
--          opb:     in    std_logic_vector(width - 1 downto 0);
--          cin:     in    std_logic;
--          cout:    out   std_logic;
--          result:  out   std_logic_vector(width - 1 downto 0)
--     );
-- end component;

    procedure adder (
         signal opa:     in    std_logic_vector(width - 1 downto 0);
         signal opb:     in    std_logic_vector(width - 1 downto 0);
         signal cin:     in    std_logic;
         signal cout:    out   std_logic;
         signal result:  out   std_logic_vector(width - 1 downto 0) 
    ) is 
        variable sum:   unsigned (width downto 0);
    begin
        sum := unsigned('0' & opa) + unsigned(opb) + unsigned'(""& cin);
        result <= std_logic_vector(sum (width - 1 downto 0));
        cout <= sum(width);
    end procedure;

    signal adder_output:  std_logic_vector(width - 1 downto 0) := (others => '0');
    signal b_neg:         std_logic_vector(width - 1 downto 0) := (others => '0');
    signal c_flag:        std_logic := '0';
    signal c_reg:         std_logic := '0';
    signal cin:           std_logic := '0';

begin

adder_comp:  
    adder
       -- port map (
       (
           opa => a,
           opb => b_neg,
           cin => cin,
           result => adder_output,
           cout => c_flag
       );        

    with op select 
        b_neg <= not b when "1000",
                     b  when others;

    with op select
        cin <= '1'     when "1000", -- sub
                c_reg  when "0111", -- addc
               '0'     when others; -- add/adds    

alu_process:
    process(clk)
    begin
        if reset = '0' then
            outs <= (others => '0');
        elsif rising_edge(clk) then
            case op is
                when "0001" => outs <= a and b;
                when "0010" => outs <= a or  b;
                when "0011" => outs <= a nor b;
                when "0100" => outs <= a xor b;
                when "0101" => outs <= adder_output; -- add 
                when "0110" => outs <= adder_output; -- adds
                               c_reg <= c_flag;    
                when "0111" => outs <= adder_output; -- addc
                when "1000" => outs <= adder_output; -- sub
                when "1001" => outs <= std_logic_vector (
                    unsigned(a) sll to_integer(unsigned(b(4 downto 0)))
                );
                when "1010" => outs <= std_logic_vector (
                    unsigned(a) srl to_integer(unsigned(b(4 downto 0)))
                );
                when "1011" => outs <= std_logic_vector ( 
                    shift_right(signed(a),to_integer(unsigned(b(4 downto 0))))
                );
                when others => outs <= (others => '0');
            end case;
        end if;
    end process;
end arch_alu;

library ieee;
use ieee.std_logic_1164.all;

entity alu_tb is
end entity;

architecture foo of alu_tb is
    constant width: integer := 32;
    signal clk:     std_logic := '0';
    signal reset:   std_logic := '0';
    signal a:       std_logic_vector(width - 1 downto 0) := (others => '0');
    signal b:       std_logic_vector(width - 1 downto 0) := (others => '0');
    signal op:      std_logic_vector(3 downto 0) := (others => '0');
    signal outs:    std_logic_vector(width - 1 downto 0);
begin 
CLOCK:
    process
    begin
        wait for 10 ns;
        clk <= not clk;
        if Now > 90 ns then
            wait;
        end if;
    end process;
DUT:
    entity work.alu 
        port map (
            clk => clk,
            reset => reset,
            a  => a,
            b =>  b,
            op => op,
            outs => outs  
        );

STIMULUS:
    process
    begin
        wait for 20 ns;
        reset <= '1';
        a <= x"E6A4960F";
        b <= x"7B494E34";
        op <= "1000";
        wait for 20 ns;
        a <= x"EFDE31A3";
        b <= x"0BCAB8FA";
        wait for 20 ns;
        wait;
    end process;
end architecture;

我写了一个快速而又脏的程序加法器。这消除了您的加法器实体/体系结构和组件声明。

我为两次减法添加了一个简单的测试平台,这消除了您的测试平台或测试程序。

这就是:

alu_tb.png

正如您所看到的,结果是您声称的正确结果。

那么剩下的就是你的加法器或你的测试平台(这是一个责备组件声明的延伸)。

因此,我们从中获得的是您没有提供足够的信息来确定错误的位置。

我做了这个小小的演示,因为这两个错误没有共同的错误位。如果你已经测试了加法器并且确定了它,那么在减去它时可能就是刺激输入。

答案 1 :(得分:0)

要从A中减去B,您不必使用等式A + (not B)A + (not B) + 1。添加此1,您之前的两个计算是正确的(您可以使用进位输入添加1而不计算它)。

只有当操作码为case时,c_reg才会指定0110但这样做,您永远不会重置c_reg的值。您必须在所有其他情况下将其分配为0,以便在操作码为0110时具有正确的值。

另一个细节:使用ResetClk信号添加到灵敏度列表中,以便创建一个触发器。

这就是我所看到的。但是,如果您使用ADDS操作添加一些错误执行的示例,则更容易理解问题的位置。