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