32位ALU加减运算结果异常问题排查求助
问题描述
我正在为大学项目设计一款处理器,回头检查代码时发现ALU单元的加减运算无法输出正确结果。例如在测试台中,将0x00000001与0x00000001相加,结果为0x00000000;进行减法运算时,结果却为0x00000010。
原VHDL代码
use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; entity ALU_32bit is Port ( A : in std_logic_vector (31 downto 0); B : in std_logic_vector (31 downto 0); ALU_Sel : in std_logic_vector (3 downto 0); ALU_Out : out std_logic_vector (31 downto 0); Zero : out std_logic; Cout : out std_logic; Ovf : out std_logic); end ALU_32bit; architecture Behavioral of ALU_32bit is signal ALU_result: std_logic_vector(31 downto 0); signal Total_result:std_logic_vector(32 downto 0) := (others => '0'); signal Carryout, Overflow : std_logic; begin process(A, B, ALU_Sel) begin case (ALU_Sel) is when "0000" => --Addition Total_result <= std_logic_vector(signed('0' & A) + signed('0' & B)); ALU_result <= Total_result(31 downto 0); Carryout <= Total_result(32); when "0001" => --Subtraction Total_result <= std_logic_vector(signed('0' & A) - signed('0' & B)); ALU_result <= Total_result(31 downto 0); Carryout <= Total_result(32); when "0010" => --Bitwise AND ALU_result <= A and B; when "0011" => --Bitwise OR ALU_result <= A or B; when "0100" => --Bitwise NOT ALU_result <= not A; when "1000" => --Numerical Right Shift ALU_result <= std_logic_vector(signed(A) srl 1); when "1001" => --Logical Right Shift ALU_result <= std_logic_vector(unsigned(A) srl 1); when "1010" => --Logical Left Shift ALU_result <= std_logic_vector(unsigned(A) sll 1); when "1100" => --Logical Left Rotate ALU_result <= std_logic_vector(unsigned(A) rol 1); when "1101" => --Logical Right Rotate ALU_result <= std_logic_vector(unsigned(A) ror 1); when others => --Default Case; No opcode ALU_result <= x"00000000"; end case; -- Overflow detection for addition if ((A(31) = '0' and B(31) = '0' and ALU_result(31) = '1') or (A(31) = '1' and B(31) = '1' and ALU_result(31) = '0')) then Overflow <= '1'; else Overflow <= '0'; end if; -- Overflow detection for subtraction if ((A(31) = '0' and B(31) = '0' and Total_result(31) = '1') or (A(31) = '1' and B(31) = '1' and Total_result(31) = '0')) then Overflow <= '1'; else Overflow <= '0'; end if; end process; ALU_Out <= ALU_result; Zero <= '1' when ALU_result = x"00000000" else '0'; Ovf <= Overflow; Cout <= Carryout; end Behavioral;
问题分析
- 非阻塞赋值时序错误:process中用
<=给Total_result赋值属于非阻塞赋值,赋值操作要等到process结束后才生效。同一process内紧接着读取Total_result时,取到的是上一次的旧值(初始为全0),这直接导致加法结果为0、减法结果错误。 - 减法溢出逻辑错误:原代码的减法溢出判断完全不符合有符号数减法的溢出规则,正确的溢出场景是被减数与减数符号相反,且结果符号与被减数相反。
- 算术右移操作符错误:
srl是逻辑右移,无法保留有符号数的符号位,算术右移应该使用sra操作符。
修正后的代码
use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; entity ALU_32bit is Port ( A : in std_logic_vector (31 downto 0); B : in std_logic_vector (31 downto 0); ALU_Sel : in std_logic_vector (3 downto 0); ALU_Out : out std_logic_vector (31 downto 0); Zero : out std_logic; Cout : out std_logic; Ovf : out std_logic); end ALU_32bit; architecture Behavioral of ALU_32bit is signal ALU_result: std_logic_vector(31 downto 0); signal Carryout, Overflow : std_logic; begin process(A, B, ALU_Sel) variable temp_total: std_logic_vector(32 downto 0); -- 用变量存储临时计算结果 begin -- 初始化默认值,避免综合器警告 ALU_result <= (others => '0'); Carryout <= '0'; Overflow <= '0'; case ALU_Sel is when "0000" => -- 加法 temp_total := std_logic_vector(signed('0' & A) + signed('0' & B)); ALU_result <= temp_total(31 downto 0); Carryout <= temp_total(32); -- 加法溢出检测:同符号相加结果异符号 if (A(31) = '0' and B(31) = '0' and temp_total(31) = '1') or (A(31) = '1' and B(31) = '1' and temp_total(31) = '0') then Overflow <= '1'; end if; when "0001" => -- 减法 (A - B) temp_total := std_logic_vector(signed('0' & A) - signed('0' & B)); ALU_result <= temp_total(31 downto 0); Carryout <= temp_total(32); -- 减法溢出检测:异符号相减结果与被减数异符号 if (A(31) = '0' and B(31) = '1' and temp_total(31) = '1') or (A(31) = '1' and B(31) = '0' and temp_total(31) = '0') then Overflow <= '1'; end if; when "0010" => -- 按位与 ALU_result <= A and B; when "0011" => -- 按位或 ALU_result <= A or B; when "0100" => -- 按位非 ALU_result <= not A; when "1000" => -- 算术右移 ALU_result <= std_logic_vector(signed(A) sra 1); -- 替换为sra操作符 when "1001" => -- 逻辑右移 ALU_result <= std_logic_vector(unsigned(A) srl 1); when "1010" => -- 逻辑左移 ALU_result <= std_logic_vector(unsigned(A) sll 1); when "1100" => -- 逻辑左旋转 ALU_result <= std_logic_vector(unsigned(A) rol 1); when "1101" => -- 逻辑右旋转 ALU_result <= std_logic_vector(unsigned(A) ror 1); when others => -- 默认情况 ALU_result <= x"00000000"; end case; end process; ALU_Out <= ALU_result; Zero <= '1' when ALU_result = x"00000000" else '0'; Ovf <= Overflow; Cout <= Carryout; end Behavioral;
关键修改说明
- 用变量替代信号存储临时结果:变量使用阻塞赋值
:=,能在process内立即获取计算后的新值,彻底解决非阻塞赋值的时序问题。 - 修正减法溢出检测逻辑:调整为符合有符号数减法的溢出判断规则。
- 替换算术右移操作符:将
srl改为sra,确保有符号数右移时符号位正确保留。 - 增加默认初始化:在process开头给信号赋默认值,避免综合器产生不必要的警告。
内容的提问来源于stack exchange,提问作者DilligentSlacker
相关产品推荐
相关产品推荐

