带加法器的 ALU 实现

ALU implementation w/ ADDER

你好,我正在尝试创建一个带有一些操作码的 32 位加法器,我已经让它工作得很好,除了两种情况,我似乎找不到导致它们的原因。也许你可以帮帮我?

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

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 作品:d'1000 出局:h'6B5B47DA 而它应该是 h'6B5B47DB

A : h'EFDE31A3 B : h'0BCAB8FA 作品:d'1000 输出:h'E41378BB 而应该是 h'E41378A9

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

虽然您没有提供 Minimal, Complete, and Verifiable example,但 reader 至少可以测试存在的设计减法部分:

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;

我写了一个快速而肮脏的过程加法器。这消除了您的加法器 entity/architecture 和组件声明。

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

这给出了:

如您所见,结果就是您声称的正确结果。

那么剩下的就是您的加法器或您的测试平台(将责任归咎于您的组件声明是一种延伸)。

因此我们从中得到的是您没有提供足够的信息来确定错误所在。

我做了这个小演示,因为这两个错误的错误位并不相同。如果你测试过你的加法器并确定它,它可能是减法时的刺激输入。