以32bit加法器为核心的加法、减法、乘法和除法计算器(ALU)

news2024/11/25 10:29:19

1 任务概述

实现一个以加法器为核心的计算器。
加法:能够实现32bit加法
减法:能够实现32bit减法
乘法:能够实现两个32bit数字的乘法,乘积为64bit
除法:能够实现两个32bit无符号数的除法,商为32bit,余数为32bit
解读:该部分的关键在于串并转换,状态数并不多,也没有必要简化;串并转换的关键在于计数器信号和使能信号;而且对于线路空闲时发送无效字节同样很重要,需要借助已存在的或者需要新建信号来支持。 

2 系统模块设计

在这里插入图片描述

图2-1 系统模块图
输入端口

  • Operand_X-32bit
    加数、被减数、乘数、被除数
  • Operand_Y-32bit
    加数、减数、乘数、除数
  • Opcode
    运算选择信号;3’b001-加法、3’b010-减法、3’b110-乘法、3’b111-除法、其余无效
  • clk
    时钟信号
    输出端口
  • Result-64bit
    64bit运算结果
    内部模块连线图
    在这里插入图片描述

图2-2 内部模块连线图
详细设计
3.1 加减法
引入补码可以将加法和减法统一起来,可以采用“联通符号位在内,按位取反,末位加一”的方法,由[y]_补求[-y]_补。
在这里插入图片描述

图3-1以加法器为核心的加减法器电路原理示意图
3.2 乘法
如果使用“符号位单独处理(异或运算),数值按照无符号数乘法”的方法处理有符号数乘法的话,因为一般机器采用补码表示,所以需要在补码和原码之间来回做变换,增加了操作步骤。
Booth乘法算法:
进行n轮加法、移位,最后再多来一次加法
每次加法可能+0 、+[x]补、+[-x]
每次移位是“补码的算数右移”
符号位参与运算
在2中,需要根据部分积 y 中的最低两位来确定加什么:
表2-1 最低两位操作表
y_i y
(i+1) y
(i+1) y_i 操作
0 0 0 +0
0 1 1 +[x]_补
1 0 -1 +[-x]_补
1 1 0 +0
在这里插入图片描述

图3-2以加法器为核心的Booth乘法电路原理示意图
3.3 除法
第三个版本将商寄存器和余数寄存器合并为一个左移的64-bit寄存器,从余数移位开始计算,运算完成后,结果的高低两个部分需要通过移位互换位置(处理器设计中低位是商,高位是余数)。
在这里插入图片描述

图3-3除法算法流程图
除法器采用一个32-bit的除法寄存器、一个32-bit的ALU和一个64-bit的余数寄存器:
在这里插入图片描述

图3-4除法模块示意图
笔者也编写了补码乘法——加减交替法,但是由于对于该算法理解不是很透彻,导致无法验证,遂放弃,于是只简单开发了无符号数乘法。
在这里插入图片描述

图3-5加减交替法有符号数除法电路示意图

4 对编码的说明

加法器
通过补码可以将加法和减法统一起来,如果是减法的话,可以通过+[y]_补 来实现。

assign Result[32:0] = Operand_X+B+add_cin;
    always @(Operand_Y or Opcode) begin
        if (Opcode == 3'b010) 
            B = ~Operand_Y;
        else if (Opcode == 3'b001)
            B = Operand_Y;
    end
    always @(Opcode) begin
        if (Opcode == 3'b010)
            add_cin = 1'b1;
        else if (Opcode == 3'b001)
            add_cin = 1'b0;
    end
    assign Result = {{31{Result[32]}},Result[32:0]};

乘法器
以加法器为核心,经过33个周期实现两个32bit的数据相乘。

always @(posedge clk or posedge rst) begin
        if (rst) 
            Result <= 65'b0;
        else if (Opcode == 3'b110) begin
            Result <= {32'b0,Operand_X,1'b0};
        end else begin
            Result <= {Add_Result[31],Add_Result,Result[32:1]};
        end
    end

加法器两个输入信号的生成

always @(posedge rst or Result or Operand_X or Busy) begin
        if (rst)
            Add_A <= 32'b0;  
        else if (Busy) 
            Add_A <= Result[64:33];
        else
            Add_A <= Operand_X;
    end

    always @(Busy or Multiplicand or Operand_Y) begin
        if (Busy) 
            Operand_Y_inner <= Multiplicand;
        else 
            Operand_Y_inner <= Operand_Y;
    end

    always @(Opcode_inner or Operand_Y_inner) begin
        if (Opcode_inner == 2'b10)
            Add_B <= Operand_Y_inner;
        else if (Opcode_inner == 2'b11)
            Add_B <= ~Operand_Y_inner;
        else 
            Add_B <= 32'b0;
    end

    always @(Opcode_inner) begin
        if (Opcode_inner == 2'b11)
            Add_Cin <= 1'b1;
        else 
            Add_Cin <= 1'b0; 
end
	if (Result[1:0] == 2'b00 || Result[1:0] == 2'b11)
                Opcode_inner <= 2'b00;
            else if (Result[1:0] == 2'b01) 
                Opcode_inner <= 2'b10;
            else 
                Opcode_inner <= 2'b11;

除法器
32bit无符号数除法,经过33个周期得出结果。

always @(posedge clk or posedge rst) begin
        if (rst) begin
            result <= 64'b0; 
        end else if (Opcode == 3'b111) begin
            result <= {31'b0,Operand_X,1'b0};
        end else if (result[63:32] >= Operand_Y)
            result = {mid_result[30:0],result[31:0],1'b1};
        else 
            result = {result[62:32],result[31:0],1'b0} ;
    end
     
    always @(result) begin
        mid_result = result[63:32] - Operand_Y;
end
	assign Remainder= (Operand_Y == 32'b0)?32'bz:{1'b0,result[63:33]};
    assign Quotient = result[31:0];
加减交替法实现补码有符号除法:未仿真验证

在这里插入图片描述

图4-1 加减交替法算法步骤

//符号扩展
    assign operand_x = {Operand_X[31],Operand_X};
    assign operand_y = {Operand_Y[31],Operand_Y};
    //0 不等,1 相等
    always @(Add_A or Add_B or Add_Cin) begin
        Result = Add_A + Add_B + Add_Cin;
    end
    
    always @(posedge rst or posedge clk) begin
        if (rst) 
            Mul_Counter <= 5'b0;
        else if (Opcode == 3'b110) 
            Mul_Counter <= 5'b11111;
        else if (Busy) 
            Mul_Counter <= Mul_Counter - 1;
    end

    always @(posedge clk or posedge rst) begin
        if (rst)
            Busy <= 1'b0;
        else if (Opcode == 3'b110) 
            Busy <= 1'b1;
        else if (Busy == 1'b1 && Mul_Counter == 5'b00000) begin
            Busy <= 1'b0;
        end
    end

    always @(operand_x or Busy or Add_A) begin
        if (Busy) 
            Add_A <= {Add_A[32:1],1'b0};
        else
            Add_A <= operand_x;
    end

    // 1 同号,0 异号
    always @(Opcode_inner or operand_y) begin
        if (Opcode_inner == 1'b0)
            Add_B <= operand_y;
        else if (Opcode_inner == 1'b1)
            Add_B <= ~operand_y;
    end

    always @(Opcode_inner) begin
        if (Opcode_inner == 1'b1)
            Add_Cin <= 1'b1;
        else 
            Add_Cin <= 1'b0; 
    end

    always @(Opcode_inner or Quotient or Busy) begin
        if (!Busy) begin
            Quotient <= 32'b0;
        end else begin
            if (Opcode_inner == 1'b1) 
                Quotient <= {Quotient[31:1],1'b1};
            else
                Quotient <= {Quotient[31:1],1'b0};
        end
    end

    always @(Busy or Result or Opcode or operand_y) begin
        if (Busy) begin
            if (Result[32:31] == operand_y[32:31])
                Opcode_inner <= 1'b1;
            else 
                Opcode_inner <= 1'b0;
        end else begin 
            if (Opcode == 3'b100) 
                Opcode_inner <= 1'b0;
            else if (Opcode == 3'b101) 
                Opcode_inner <= 1'b1;
            else 
                Opcode_inner <= 1'b0; 
        end
    end
    assign Remainder = Result[31:0];

顶层设计模块
为了使得获取乘法和除法结果的设计更为简便,将muler和divider模块中的计数器改为计33计数器,使得在最后一个周期,即第33个周期输出结果,该周期之后,对应的Busy信号将清零。
reg [63:0] Result;
wire [63:0] Result1,Result2,Result3;
wire Buys1,Busy2;
assign Opc = Opcode;
add_sub as1(
.Operand_X(Operand_X),
.Operand_Y(Operand_Y),
.Opcode(Opc),
.Result(Result1)
);
muler m1(
.clk(clk),
.rst(rst),
.Opcode(Opc),
.Operand_X(Operand_X),
.Operand_Y(Operand_Y),
.Busy(Busy1),
.Cout(Cout),
.Product(Result2)
);
divider D1(
.clk(clk),
.rst(rst),
.Opcode(Opc),
.Operand_X(Operand_X),
.Operand_Y(Operand_Y),
.Busy(Busy2),
.Result(Result3)
);
always @(Result3 or Result1 or Result2) begin
if (Opcode == 3’b010||Opcode == 3’b001)
Result <= Result1;
else if (Busy1)
Result <= Result2;
else if (Busy2)
Result <= Result3;
else
Result <= 64’bz;
end
Testbench开发
5.1. Testbench开发思路
该题目的仿真并不算难点,仅有的难点在于计算下一个计算周期的Busy信号的产生,即下一次测试数据的打入时间至少在前一次数据计算完成后。
由于该计算器由三个部分完成,所以采用三个部分先单独测试,然后在进行整体测试的思路。
在这里插入图片描述

图5-1 项目文件结构图
5.2. Testbench说明
加减法
表5-1 加减法测试用例及测试结果表
X Y 是否通过

{$random}%{2^32}	{$random}%{2^32}32'h0007 (7)	32'hfffffff4(-12)32'h7fff	32'h7fff	是
32'hffff_fffd(-3)	32'hffff_fff4(-12)32'h8000_0000( -2^31)	32'h0001(1)	是
	代码
	initial begin 
        Operand_X = {$random}%{2^32};
        Operand_Y = {$random}%{2^32};
        Opcode = 3'd1;
        res = Operand_X+Operand_Y;
        #10;res1 = Result;
        if (res == res1) begin
            $display("[time@%t]: x=%h,y=%h,Opcode=%h -------PASS",$time,Operand_X,Operand_Y,Opcode);
        end else begin
            $display("[time@%t]ERROR: x=%h,y=%h,Opcode=%h",$time,Operand_X,Operand_Y,Opcode);
            //$finish;
        end

        #10; 
        Operand_X = 32'h0007; Operand_Y = 32'hfffffff4;
        Opcode = 3'd1;
        #10;res1 = Result; //7+(-12)= -5;
        if (64'hffff_ffff_ffff_fffb == res1) begin
            $display("[time@%t]: x=%h,y=%h,Opcode=%h -------PASS",$time,Operand_X,Operand_Y,Opcode);
        end else begin
            $display("[time@%t]ERROR: x=%h,y=%h,Opcode=%h",$time,Operand_X,Operand_Y,Opcode);
            //$finish;
        end

        #10; 
        Operand_X = 32'h7fff;
        Operand_Y = 32'h7fff;
        Opcode = 3'd1;
        #10;res1 = Result;
        if (64'h0000_FFFE == res1) begin
            $display("[time@%t]: x=%h,y=%h,Opcode=%h -------PASS",$time,Operand_X,Operand_Y,Opcode);
        end else begin
            $display("[time@%t]ERROR: x=%h,y=%h,Opcode=%h",$time,Operand_X,Operand_Y,Opcode);
            //$finish;
        end

        #10;
        Operand_X = 32'hffff_fffd;//-3
        Operand_Y = 32'hffff_fff4;//-12
        Opcode = 3'd2;
        #10;res1 = Result; //-3-(-12) = 9
        if (64'h0000_0009 == res1) begin
            $display("[time@%t]: x=%h,y=%h,Opcode=%h-------PASS",$time,Operand_X,Operand_Y,Opcode);
        end else begin
            $display("[time@%t]ERROR: x=%h,y=%h,Opcode=%h",$time,Operand_X,Operand_Y,Opcode);
            //$finish;
        end

        #10;
        Operand_X = 32'h8000_0000;//-2^31
        Operand_Y = 32'h0001;//1
        Opcode = 3'd2;
        #10;res1 = Result; //
        if (64'hffff_ffff_7fff_ffff == res1) begin
            $display("[time@%t]: x=%h,y=%h,Opcode=%h-------PASS",$time,Operand_X,Operand_Y,Opcode);
        end else begin
            $display("[time@%t]ERROR: x=%h,y=%h,Opcode=%h",$time,Operand_X,Operand_Y,Opcode);
            //$finish;
        end
        #20;
        $finish;
    end

乘法

always begin
        #5; clk = ~clk;
    end

    initial begin
        #15;
        Operand_X = 32'b0000_0000_0000_0000_0000_0111_0110_0001;
        Operand_Y = 32'b0000_0000_0000_0000_1001_0111_0110_0001;
        // Operand_X = 32'h6;
        // Operand_Y = 32'h2;
        #330;
        #15;
        Operand_X = $random;
        Operand_Y = $random;
        #330;
        #15;
        Operand_X = 32'b1000_0000_0000_0000_0000_0111_0110_0001;
        Operand_Y = 32'b1000_0000_0000_0000_1001_0111_0110_0001;
        #330;
        #15;
        Operand_X = 32'b0111_1111_1111_1111_1111_1111_1111_1111;
        Operand_Y = 32'b1000_0100_1001_0111_1110_1100_0111_1000;
        #330;
    end

    always begin
        Opcode = 3'b000;
        #15;
        Opcode = 3'b110;
        #5;
        Opcode = 3'b000;
        #330;
    end

    initial begin
        clk = 0;
        rst = 0;
    end

    always begin
        #5;
        rst = 1;
        #5;
        rst = 0;
        #10;
        #330;
    end

除法

always begin
        #5; clk = ~clk;
    end

    initial begin
        #15;
        Operand_X = 32'h000A;
        Operand_Y = 32'h0002;
        #330;
        #15;
        Operand_X = 2;
        Operand_Y = 0;
        #330;
        #15;
        Operand_X = 32'd18;
        Operand_Y = 32'h5;
        #330;
        #15;
        Operand_X = 32'd34;
        Operand_Y = 32'h6;
        #330;
    end

    always begin
        Opcode = 3'b000;
        #15;
        Opcode = 3'b111;
        #5;
        Opcode = 3'b000;
        #330;
    end

    initial begin
        clk = 0;
        rst = 0;
    end

    always begin
        #5;
        rst = 1;
        #5;
        rst = 0;
        #10;
        #330;
    end

整体测试
//初始化模块

alu_top al_tp1(
        .Operand_X(Operand_X),
        .Operand_Y(Operand_Y),
        .Opcode(Opcode),
        .clk(clk),
        .rst(rst),
        .Opc(Opc),
        .Busy1(Busy1),
        .Busy2(Busy2),
        .Result1(Result1),
        .Result2(Result2),
        .Result3(Result3),
        .Result(Result)
    );
    always begin
        #5;clk = ~clk;
    end
    initial begin
        #5;
        //加法器
        Operand_X = 32'h0007; Operand_Y = 32'hfffffff4; 
        #10;res1 = Result; //7+(-12)= -5;
        if (64'hffff_ffff_ffff_fffb == res1) begin
            $display("[time@%t]: x=%h,y=%h,Opcode=%h -------PASS",$time,Operand_X,Operand_Y,Opcode);
        end else begin
            $display("[time@%t]ERROR: x=%h,y=%h,Opcode=%h",$time,Operand_X,Operand_Y,Opcode);
        end
        //乘法器
        #15;
        Operand_X = 32'b0000_0000_0000_0000_0000_0111_0110_0001;
        Operand_Y = 32'b0000_0000_0000_0000_1001_0111_0110_0001;
        #330;
        //除法器
        #15;
        Operand_X = 32'd18;
        Operand_Y = 32'h5;
    end

    always begin
        Opcode = 3'b000;
        #5;
        Opcode = 3'd1;
        #10;
        //乘法器
        Opcode = 3'b000;
        #10;
        Opcode = 3'b110;
        #5;
        #5;
        Opcode = 3'b000;
        #325;
        //除法器
        Opcode = 3'b000;
        #10;
        Opcode = 3'b111;
        #5;
        #5;
        Opcode = 3'b000;
        #330;
    end

    initial begin
        #5;
        #10;
        //乘法器
        clk = 1;
        rst = 0;
        #15;
        #330;
        //除法器
        clk = 1;
        rst = 0;
        #15;
        #330;
    end

    always begin
        #5;
        #10;
        //乘法器
        #5;
        rst = 1;
        #5;
        rst = 0;
        #10;
        #325;
        //除法器
        #5;
        rst = 1;
        #5;
        rst = 0;
        #10;
        #330;
    end
### 6仿真结果

6.1 加减法

图6-1 加减法仿真成功图
6.2 乘法

图6-2 乘法测试用例1仿真成功

图6-3乘法测试用例2仿真成功

图6-3乘法测试用例3仿真成功

图6-4乘法测试用例4仿真成功
6.3 除法

图6-5除法测试用例1仿真成功

图6-6除法测试用例2(除数为零)仿真成功

图6-7除法测试用例3仿真成功

图6-8除法测试用例4仿真成功
总结
编写Testbench文件时,首先要搞清楚每个信号是怎么产生的,其次要弄清楚信号之间的产生逻辑,结合实际想要完成的功能,在仿真过程中,发现并修改Design Source文件的错误。
在复杂逻辑,以及类似于多bit数据寄存器等难以通过肉眼直接观察正误的项目中,要优先理清楚Design Source文件中的逻辑,避免逻辑错误,而不是想着在仿真过程中发现逻辑错误,尽可能节 省项目完成时间。
仿真过程中尽可能把出现的所有信号都显示出来,这样的话更容易发现问题所在。
针对次题目,仿真过程中需要将十进制和二进制补码等相互转换才更容易观察出正误,如果采用自行判断的话,笔者目前还没有掌握方法——将十进制转化成二进制补码,因为计算机中多采用补码,所以待学习。
附:代码
alu_top.v

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2023/09/30 20:51:22
// Design Name: 
// Module Name: alu_top
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module alu_top(
    input   [31:0]  Operand_X,Operand_Y,
    input   [2:0]   Opcode,
    input           clk,
    input           rst,
    input   [63:0]  Result1,Result2,Result3,
    input           Busy1,Busy2,
    output  [2:0]   Opc,
    output  [63:0]  Result

);
    //reg     [31:0]  Operand_X,Operand_Y;
    reg   [63:0]  Result;
    wire [63:0] Result1,Result2,Result3;
    //reg     [2:0]   Opcode;
    //reg             clk,rst;
 
    wire            Buys1,Busy2;
    assign Opc = Opcode;
    add_sub as1(
        .Operand_X(Operand_X),
        .Operand_Y(Operand_Y),
        .Opcode(Opc),
        .Result(Result1)
    );

    muler m1(
        .clk(clk),
        .rst(rst),
        .Opcode(Opc),
        .Operand_X(Operand_X),
        .Operand_Y(Operand_Y),
        .Busy(Busy1),
        .Cout(Cout),
        .Product(Result2)
    );

    divider D1(
        .clk(clk),
        .rst(rst),
        .Opcode(Opc),
        .Operand_X(Operand_X),
        .Operand_Y(Operand_Y),
        .Busy(Busy2),
        .Result(Result3)
    );

    // always @(posedge clk) begin
    //     Result <= 64'b1;
    // end
    // assign Result = (Opcode == 3'b010||Opcode==3'b001)?Result1:
    //                     (Opcode == 3'b110)?Result2:
    //                     (Opcode == 3'b111)?Result3:
    //                     64'bZ;
    always @(Result3 or Result1 or Result2) begin
        if (Opcode == 3'b010||Opcode == 3'b001) 
            Result <= Result1;
        else if (Busy1) 
            Result <= Result2;
        else if (Busy2)
            Result <= Result3;
        else 
            Result <= 64'bz;
    end
endmodule


add_sub.v

module add_sub (
    input [31:0]    Operand_X,Operand_Y,
    input [2:0]     Opcode,
    output[63:0]    Result
);
    wire Cout;
    reg [31:0] B;
    reg add_cin;
    wire [31:0] res;
    assign {Cout,res} = Operand_X+B+add_cin;
    always @(Operand_Y or Opcode) begin
        if (Opcode == 3'b010) 
            B = ~Operand_Y;
        else if (Opcode == 3'b001)
            B = Operand_Y;
    end
    always @(Opcode) begin
        if (Opcode == 3'b010)
            add_cin = 1'b1;
        else if (Opcode == 3'b001)
            add_cin = 1'b0;
    end
    reg c;
    assign Result ={{32{c}},res};
    always @(Cout or res) begin
        if ((Operand_X[31]^Operand_Y[31])==0) begin
            if (Opcode == 3'b001&&Cout) c = Operand_X[31];
            else c = res[31];
        end else begin
            if (Opcode == 3'b010&&Cout) c = Operand_X[31];
            else c = res[31];
        end
        
    end
endmodule

muler.v


module muler(
    input           rst,
    input           clk,
    input  [2:0]    Opcode,
    input  [31:0]   Operand_X,
    input  [31:0]   Operand_Y,
    output          Busy,
    output          Cout,
    output [63:0]   Product         
);
    reg         Busy,Cout;
    reg [64:0]  Result;
    reg [31:0]  Add_A,Add_B,Add_Result;
    reg         Add_Cin;
    reg [5:0]   Mul_Counter;
    reg [31:0]  Multiplicand,Operand_Y_inner;
    reg [1:0]   Opcode_inner;
    //00:add 0,01:sub multiplicand; 10:add multipicand;11:reserved
    always @(Add_A or Add_B or Add_Cin) begin
        {Cout,Add_Result} = Add_A+Add_B+Add_Cin;
    end
    assign Product = Result[64:1];
    always @(posedge rst or posedge clk) begin
        if (rst) 
            Mul_Counter <= 6'b0;
        else if (Opcode == 3'b110) 
            Mul_Counter <= 6'b100000;
        else if (Busy) 
            Mul_Counter <= Mul_Counter - 1;
    end

    always @(posedge clk or posedge rst) begin
        if (rst)
            Busy <= 1'b0;
        else if (Opcode == 3'b110) 
            Busy <= 1'b1;
        else if (Busy == 1'b1 && Mul_Counter == 6'b000000) begin
            Busy <= 1'b0;
        end
    end

    always @(posedge clk or posedge rst) begin
        if (rst) 
            Multiplicand <= 32'b0;
        else if (Opcode == 3'b110) 
            Multiplicand <= Operand_Y;
    end

    always @(posedge clk or posedge rst) begin
        if (rst) 
            Result <= 65'b0;
        else if (Opcode == 3'b110) begin
            Result <= {32'b0,Operand_X,1'b0};
        end else begin
            Result <= {Add_Result[31],Add_Result,Result[32:1]};
        end
    end

    always @(posedge rst or Result or Operand_X or Busy) begin
        if (rst)
            Add_A <= 32'b0;  
        else if (Busy) 
            Add_A <= Result[64:33];
        else
            Add_A <= Operand_X;
    end

    always @(Busy or Multiplicand or Operand_Y) begin
        if (Busy) 
            Operand_Y_inner <= Multiplicand;
        else 
            Operand_Y_inner <= Operand_Y;
    end

    always @(Opcode_inner or Operand_Y_inner) begin
        if (Opcode_inner == 2'b10)
            Add_B <= Operand_Y_inner;
        else if (Opcode_inner == 2'b11)
            Add_B <= ~Operand_Y_inner;
        else 
            Add_B <= 32'b0;
    end

    always @(Opcode_inner) begin
        if (Opcode_inner == 2'b11)
            Add_Cin <= 1'b1;
        else 
            Add_Cin <= 1'b0; 
    end

    always @(Busy or Result or Opcode) begin
        if (Busy) begin
            if (Result[1:0] == 2'b00 || Result[1:0] == 2'b11)
                Opcode_inner <= 2'b00;
            else if (Result[1:0] == 2'b01) 
                Opcode_inner <= 2'b10;
            else 
                Opcode_inner <= 2'b11;
        end else begin
            if (Opcode == 3'b100) 
                Opcode_inner <= 2'b10;
            else if (Opcode == 3'b101) 
                Opcode_inner <= 2'b11;
            else 
                Opcode_inner <= 2'b00; 
        end
    end

endmodule

divider.v


module divider (
    input           rst,
    input           clk,
    input  [2:0]    Opcode,
    input  [31:0]   Operand_X,
    input  [31:0]   Operand_Y,
    output          Busy,
    // output          Cout,
    output [31:0]   Quotient,
    output [31:0]   Remainder,  
    output [63:0]   Result  
);
    wire[31:0]  Quotient,Remainder;
    reg [63:0]  result;//hi-remainder,lo-quotient;
    reg         Busy;
    
    reg [5:0]   Mul_Counter;
    reg [31:0]  mid_result;
    wire[63:0]  Result;
    assign Result = {Remainder,Quotient};//高位是余数,低位是商
    always @(posedge clk or posedge rst) begin
        if (rst) begin
            result <= 64'b0; 
        end else if (Opcode == 3'b111) begin
            result <= {31'b0,Operand_X,1'b0};
        end else if (result[63:32] >= Operand_Y)
            result = {mid_result[30:0],result[31:0],1'b1};
        else 
            result = {result[62:32],result[31:0],1'b0} ;
    end
     
    always @(result) begin
        mid_result = result[63:32] - Operand_Y;
    end

    always @(posedge clk or posedge rst) begin
        if (rst) begin
            Busy <= 1'b0;
        end else if (Opcode == 3'b111) begin
            Busy <= 1'b1;
        end else if (Mul_Counter == 6'b0)
            Busy <= 1'b0;
    end


    always @(posedge rst or posedge clk) begin
        if (rst) 
            Mul_Counter <= 6'b0;
        else if (Opcode == 3'b111) 
            Mul_Counter <= 6'b100000;
        else if (Busy) 
            Mul_Counter <= Mul_Counter - 1;
    end

    assign Remainder= (Operand_Y == 32'b0)?32'bz:{1'b0,result[63:33]};
    assign Quotient = result[31:0];
endmodule


alu_top.v



module alu_top_tb();
    reg     [31:0]  Operand_X,Operand_Y;
    wire    [63:0]  Result,Result1,Result2,Result3;
    reg     [2:0]   Opcode;
    wire     [2:0]   Opc;
    reg             clk,rst;
    reg     [63:0]  res1;
    wire            Busy1,Busy2;

    alu_top al_tp1(
        .Operand_X(Operand_X),
        .Operand_Y(Operand_Y),
        .Opcode(Opcode),
        .clk(clk),
        .rst(rst),
        .Opc(Opc),
        .Busy1(Busy1),
        .Busy2(Busy2),
        .Result1(Result1),
        .Result2(Result2),
        .Result3(Result3),
        .Result(Result)
    );
    always begin
        #5;clk = ~clk;
    end
    initial begin
        #5;
        //加法器
        Operand_X = 32'h0007; Operand_Y = 32'hfffffff4; 
        #10;res1 = Result; //7+(-12)= -5;
        if (64'hffff_ffff_ffff_fffb == res1) begin
            $display("[time@%t]: x=%h,y=%h,Opcode=%h -------PASS",$time,Operand_X,Operand_Y,Opcode);
        end else begin
            $display("[time@%t]ERROR: x=%h,y=%h,Opcode=%h",$time,Operand_X,Operand_Y,Opcode);
        end
        //乘法器
        #15;
        Operand_X = 32'b0000_0000_0000_0000_0000_0111_0110_0001;
        Operand_Y = 32'b0000_0000_0000_0000_1001_0111_0110_0001;
        #330;
        //除法器
        #15;
        Operand_X = 32'd18;
        Operand_Y = 32'h5;
    end


    always begin
        Opcode = 3'b000;
        #5;
        Opcode = 3'd1;
        #10;
        //乘法器
        Opcode = 3'b000;
        #10;
        Opcode = 3'b110;
        #5;
        #5;
        Opcode = 3'b000;
        #325;
        //除法器
        Opcode = 3'b000;
        #10;
        Opcode = 3'b111;
        #5;
        #5;
        Opcode = 3'b000;
        #330;
    end

    initial begin
        #5;
        #10;
        //乘法器
        clk = 1;
        rst = 0;
        #15;
        #330;
        //除法器
        clk = 1;
        rst = 0;
        #15;
        #330;
    end

    always begin
        #5;
        #10;
        //乘法器
        #5;
        rst = 1;
        #5;
        rst = 0;
        #10;
        #325;
        //除法器
        #5;
        rst = 1;
        #5;
        rst = 0;
        #10;
        #330;
    end
endmodule

add_sub_tb.v

module add_sub_tb;
    // 定义信号
    reg  [31:0] Operand_X, Operand_Y;
    reg  [2:0]  Opcode;
    wire [63:0] Result;

    // 实例化被测试模块
    add_sub uut (
        .Operand_X(Operand_X),
        .Operand_Y(Operand_Y),
        .Opcode(Opcode),
        .Result(Result)
    );
    reg [63:0] res,res1;
    
    // 初始化信号
    initial begin 
        Operand_X = {$random}%{2^32};
        Operand_Y = {$random}%{2^32};
        Opcode = 3'd1;
        res = Operand_X+Operand_Y;
        #10;res1 = Result;
        if (res == res1) begin
            $display("[time@%t]: x=%h,y=%h,Opcode=%h -------PASS",$time,Operand_X,Operand_Y,Opcode);
        end else begin
            $display("[time@%t]ERROR: x=%h,y=%h,Opcode=%h",$time,Operand_X,Operand_Y,Opcode);
            //$finish;
        end

        #10; 
        Operand_X = 32'h0007; Operand_Y = 32'hfffffff4;
        Opcode = 3'd1;
        #10;res1 = Result; //7+(-12)= -5;
        if (64'hffff_ffff_ffff_fffb == res1) begin
            $display("[time@%t]: x=%h,y=%h,Opcode=%h -------PASS",$time,Operand_X,Operand_Y,Opcode);
        end else begin
            $display("[time@%t]ERROR: x=%h,y=%h,Opcode=%h",$time,Operand_X,Operand_Y,Opcode);
            //$finish;
        end

        #10; 
        Operand_X = 32'h7fff;
        Operand_Y = 32'h7fff;
        Opcode = 3'd1;
        #10;res1 = Result;
        if (64'h0000_FFFE == res1) begin
            $display("[time@%t]: x=%h,y=%h,Opcode=%h -------PASS",$time,Operand_X,Operand_Y,Opcode);
        end else begin
            $display("[time@%t]ERROR: x=%h,y=%h,Opcode=%h",$time,Operand_X,Operand_Y,Opcode);
            //$finish;
        end

        #10;
        Operand_X = 32'hffff_fffd;//-3
        Operand_Y = 32'hffff_fff4;//-12
        Opcode = 3'd2;
        #10;res1 = Result; //-3-(-12) = 9
        if (64'h0000_0009 == res1) begin
            $display("[time@%t]: x=%h,y=%h,Opcode=%h-------PASS",$time,Operand_X,Operand_Y,Opcode);
        end else begin
            $display("[time@%t]ERROR: x=%h,y=%h,Opcode=%h",$time,Operand_X,Operand_Y,Opcode);
            //$finish;
        end

        #10;
        Operand_X = 32'h8000_0000;//-2^31
        Operand_Y = 32'h0001;//1
        Opcode = 3'd2;
        #10;res1 = Result; //
        if (64'hffff_ffff_7fff_ffff == res1) begin
            $display("[time@%t]: x=%h,y=%h,Opcode=%h-------PASS",$time,Operand_X,Operand_Y,Opcode);
        end else begin
            $display("[time@%t]ERROR: x=%h,y=%h,Opcode=%h",$time,Operand_X,Operand_Y,Opcode);
            //$finish;
        end
        #20;
        $finish;
    end



endmodule

tb_muler.v


module tb_muler( );
    reg         rst,clk;
    wire        Busy,Cout;  
    reg [2:0]   Opcode;
    wire[63:0]  Result;
    reg [31:0]  Operand_X,Operand_Y;
    
    muler m1(
        .rst(rst),
        .clk(clk),
        .Opcode(Opcode),
        .Operand_X(Operand_X),
        .Operand_Y(Operand_Y),
        .Busy(Busy),
        .Cout(Cout),
        .Product(Result)
    );

    always begin
        #5; clk = ~clk;
    end

    initial begin
        #15;
        Operand_X = 32'b0000_0000_0000_0000_0000_0111_0110_0001;
        Operand_Y = 32'b0000_0000_0000_0000_1001_0111_0110_0001;
        // Operand_X = 32'h6;
        // Operand_Y = 32'h2;
        #330;
        #15;
        Operand_X = $random;
        Operand_Y = $random;
        #330;
        #15;
        Operand_X = 32'b1000_0000_0000_0000_0000_0111_0110_0001;
        Operand_Y = 32'b1000_0000_0000_0000_1001_0111_0110_0001;
        #330;
        #15;
        Operand_X = 32'b0111_1111_1111_1111_1111_1111_1111_1111;
        Operand_Y = 32'b1000_0100_1001_0111_1110_1100_0111_1000;
        #330;
    end

    always begin
        Opcode = 3'b000;
        #15;
        Opcode = 3'b110;
        #5;
        Opcode = 3'b000;
        #330;
    end

    initial begin
        clk = 0;
        rst = 0;
    end

    always begin
        #5;
        rst = 1;
        #5;
        rst = 0;
        #10;
        #330;
    end
endmodule

tb_divider.v


module tb_muler( );
    reg         rst,clk;
    wire        Busy,Cout;  
    reg [2:0]   Opcode;
    wire[63:0]  Result;
    reg [31:0]  Operand_X,Operand_Y;
    
    muler m1(
        .rst(rst),
        .clk(clk),
        .Opcode(Opcode),
        .Operand_X(Operand_X),
        .Operand_Y(Operand_Y),
        .Busy(Busy),
        .Cout(Cout),
        .Product(Result)
    );

    always begin
        #5; clk = ~clk;
    end

    initial begin
        #15;
        Operand_X = 32'b0000_0000_0000_0000_0000_0111_0110_0001;
        Operand_Y = 32'b0000_0000_0000_0000_1001_0111_0110_0001;
        // Operand_X = 32'h6;
        // Operand_Y = 32'h2;
        #330;
        #15;
        Operand_X = $random;
        Operand_Y = $random;
        #330;
        #15;
        Operand_X = 32'b1000_0000_0000_0000_0000_0111_0110_0001;
        Operand_Y = 32'b1000_0000_0000_0000_1001_0111_0110_0001;
        #330;
        #15;
        Operand_X = 32'b0111_1111_1111_1111_1111_1111_1111_1111;
        Operand_Y = 32'b1000_0100_1001_0111_1110_1100_0111_1000;
        #330;
    end

    always begin
        Opcode = 3'b000;
        #15;
        Opcode = 3'b110;
        #5;
        Opcode = 3'b000;
        #330;
    end

    initial begin
        clk = 0;
        rst = 0;
    end

    always begin
        #5;
        rst = 1;
        #5;
        rst = 0;
        #10;
        #330;
    end
endmodule

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1056745.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一款简化Python自然语言处理的开源库

迷途小书童 读完需要 3分钟 速读仅需 1 分钟 1 简介 TextBlob 是一个 Python 库&#xff0c;用于处理文本数据的自然语言处理&#xff08;NLP&#xff09;任务。它提供了简单且易于使用的 API&#xff0c;使得对文本进行分析、情感分析、词性标注、名词短语提取等任务变得更加简…

ARP和DDOS攻击防御介绍

ARP攻击如何产生的&#xff1f; ARP如何进行有效的防御&#xff1f; ARP地址解析协议 已知对方ip地址&#xff0c;求得对方mac地址 交换机会自动学习&#xff1a; 当pc1想访问外网&#xff0c;会向外发一个广播包&#xff0c;交换机会收到一个广播包 ARP地址表&#xff1a; …

Netgear R6700v3 1.0.4.102(CVE-2021-27239)

漏洞信息 此漏洞允许网络相邻攻击者在受影响的NETGEAR R6400和R6700固件版本1.0.4.98路由器上执行任意代码。利用此漏洞不需要身份验证。该漏洞存在于upnpd服务中&#xff0c;upnpd服务默认监听UDP 1900端口。SSDP消息中精心制作的MX报头字段可能会触发固定长度的基于堆栈的缓…

Python教程:PyQt5需要学习,哪些知识点??

PyQt5是基于图形程序框架Qt5的Python语言实现&#xff0c;由一组Python模块构成。它可用于Python 2和3&#xff0c;拥有超过620个类和6000个函数和方法。这是一个跨平台的工具包&#xff0c;可以运行在所有主要的操作系统&#xff0c;包括UNIX、Windows、Mac OS、Linux等。 #我…

打开英雄联盟缺少d3dcompiler_43.dll有哪些处理方法

五招步骤解决电脑d3dcompiler_43.dll文件丢失的问题&#xff01; 在使用电脑软件的过程中&#xff0c;我们可能会遇到一些奇怪的问题。其中之一就是打开某些软件时&#xff0c;系统提示找不到d3dcompiler_43.dll文件。这个错误通常出现在使用DirectX 11的应用程序中&#xff0…

CCF CSP认证 历年题目自练Day19

题目一 试题编号&#xff1a; 201812-1 试题名称&#xff1a; 小明上学 时间限制&#xff1a; 1.0s 内存限制&#xff1a; 512.0MB 问题描述&#xff1a; 题目背景   小明是汉东省政法大学附属中学的一名学生&#xff0c;他每天都要骑自行车往返于家和学校。为了能尽可能充…

力扣刷题-哈希表-求两个数组的交集Ⅱ

350. 两个数组的交集 II 给你两个整数数组 nums1 和 nums2 &#xff0c;请你以数组形式返回两数组的交集。返回结果中每个元素出现的次数&#xff0c;应与元素在两个数组中都出现的次数一致&#xff08;如果出现次数不一致&#xff0c;则考虑取较小值&#xff09;。可以不考虑…

【C语言】模拟实现strcpy

strcpy是常用的字符串函数 目录 介绍&#xff1a;模拟实现&#xff1a; 介绍&#xff1a; 我们可以知道此函数是为了拷贝字符串的 代码示例&#xff1a; #include<string.h>int main() {char str1[20] { 0 };char str2[] "hello world";strcpy(str1, str2)…

2023.10.01 homework

一个小数&#xff0c;小数位乘以3等于10.8&#xff0c;小数位乘以7等于13.2&#xff0c;求小数&#xff08;题目不是很严谨&#xff09;

自定义类型:结构体、位段、枚举、联合

文章目录 前言1. 结构体a. 关键字&#xff1a;structb. 结构体基础知识&#xff1a;c. 结构体声明&#xff1a;d. 特殊的声明e. 结构的自引用f. 结构体变量的定义与初始化<font color red>g. 结构体内存对齐h. 结构体传参 2. 位段a. 设计目的&#xff1a;节省空间b. 什么…

黑马程序员RabbitMQ入门到实战教程【高级篇】学习笔记

目录 一、发送者的可靠性 1.1、生产者重试机制 1.2、生产者确认机制 1.3、实现生产者确认 1.3.1、开启生产者确认 1.3.2、定义ReturnCallback 1.3.3、定义ConfirmCallback 二、MQ的可靠性 2.1、数据持久化 2.1.1.交换机持久化 2.1.2、队列持久化 2.1.3、消息持久化…

正则表达式 Regular Expression学习

该文章内容为以下视频的学习笔记&#xff1a; 10分钟快速掌握正则表达式_哔哩哔哩_bilibili正则表达式在线测试工具&#xff1a;https://regex101.com/, 视频播放量 441829、弹幕量 1076、点赞数 19330、投硬币枚数 13662、收藏人数 26242、转发人数 2768, 视频作者 奇乐编程学…

红包雨高并发压测记录(200台机器压测实录)

压测5000线程10秒内循环5次&#xff0c;5台2核心4线程的机器&#xff0c;QPS2500&#xff0c;每台机器需要承受500的QPS 压测10000线程10秒内循环5次&#xff0c;10台2核心4线程的机器&#xff0c;QPS5000&#xff0c;每台机器需要承受500的QPS 压测200000线程10秒内循环5次&am…

Overloud TH-U Complete for Mac:演绎您的音乐世界

Overloud TH-U Complete for Mac是一款功能强大的吉他谱曲软件&#xff0c;可以让您在Mac电脑上轻松进行吉他模拟、录音和混音等操作&#xff0c;创作属于自己的音乐作品。 Overloud TH-U Complete for Mac提供了丰富的吉他模拟和音效库&#xff0c;涵盖了多种吉他放大器、箱体…

SpringCloud(一)Eureka、Nacos、Feign、Gateway

文章目录 概述微服务技术对比 Eureka服务远程调用服务提供者和消费者Eureka注册中心搭建注册中心服务注册服务发现Ribbon负载均衡负载均衡策略饥饿加载 NacosNacos与Eureka对比Nacos服务注册Nacos服务分集群存储NacosRule负载均衡服务实例权重设置环境隔离 Nacos配置管理配置热…

GD32F103x 定时器

1. 定时器的基本介绍 STM32的定时器主要分为三种&#xff1a;高级定时器、通用定时器、基本定时器。 即&#xff1a;高级定时器具有捕获/比较通道和互补输出&#xff0c;死区时间&#xff0c;通用定时器只有捕获/比较通道&#xff0c;基本定时器没有以上两者。 1. 基本定时…

【图像处理】【应用程序设计】加载,编辑和保存图像数据、图像分割、色度键控研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

minikube如何设置阿里云镜像以及如何解决dashboard无法打开的解决方案_已设置图床

minikube如何设置阿里云镜像以及如何解决dashboard无法打开的解决方案 minikube dashboard报错 considerconsider-Dell-G15-5511:~$ minikube dashboard &#x1f914; 正在验证 dashboard 运行情况 ... &#x1f680; 正在启动代理... &#x1f914; 正在验证 proxy 运行…

LabVIEW工业虚拟仪器的标准化实施

LabVIEW工业虚拟仪器的标准化实施 创建计算机化的测试和测量系统&#xff0c;从计算机桌面控制外部测量硬件设备&#xff0c;以及在计算机屏幕上显示的类似仪器的面板上查看来自外部设备的测试或测量数据&#xff0c;所有这些都需要虚拟仪器系统软件。该软件允许用户执行所有这…

链表经典面试题(四)

分割链表 1.题目2.详细的图文分析3.详细的注释和代码 1.题目 2.详细的图文分析 我们会定义4个指向分割链表的指向指针,分别来表示两个链表的头和尾 并且将数据一一的放到两个链表中,最后再将它们串起来,代码中有详细注释. 3.详细的注释和代码 public class Partition {public…