相关阅读
数字IC基础https://blog.csdn.net/weixin_45791458/category_12365795.html?spm=1001.2014.3001.5482
本文是对数字IC基础:有符号数和无符号数的加减运算一文中的谈到的有符号数加减法的算法进行Verilog实现,有关算法细节请阅读原文,本文不会过多谈到原理相关问题。
虽然有符号加减和无符号加减在底层都是使用同样的补码加法器结构,但我们首先分别设计有符号加减法器和无符号加减法器,然后再将其组成一个完整的加减计算单元。
一个有符号数加减法器的Verilog描述如例1所示。
// 例1
module signed_adder(input signed [7:0] A, B, input mode, output reg signed [7:0] Sum, output reg OF);
always@(*) begin
if (mode == 0) begin //有符号加法
Sum = A + B;
OF = ((A[7] == B[7]) & (Sum[7] != A[7])); //溢出
end
else begin //有符号减法
Sum = A + (~B) + 1'b1;
OF = ((A[7] != B[7]) & (Sum[7] != A[7])); //溢出
end
end
endmodule
例2是例1的一个简单testbench,测试波形如图1所示。
// 例2
module signed_adder_tb;
// 输入信号
reg signed [7:0] A;
reg signed [7:0] B;
reg mode;
// 输出信号
wire signed [7:0] Sum;
wire OF;
// 实例化被测模块
signed_adder uut (
.A(A),
.B(B),
.mode(mode),
.Sum(Sum),
.OF(OF)
);
// 测试过程
initial begin
// 初始化信号
$monitor("Time=%0t | A=%d, B=%d, mode=%b -> Sum=%d, OF=%b", $time, A, B, mode, Sum, OF);
// 测试加法,没有溢出
A = 8'sd50; B = 8'sd30; mode = 0; #10;
// 测试加法,产生溢出
A = 8'sd100; B = 8'sd50; mode = 0; #10;
// 测试减法,没有溢出
A = 8'sd50; B = 8'sd30; mode = 1; #10;
// 测试减法,产生溢出
A = -8'sd100; B = 8'sd50; mode = 1; #10;
// 测试加法,负数相加没有溢出
A = -8'sd50; B = -8'sd20; mode = 0; #10;
// 测试减法,负数相减没有溢出
A = -8'sd50; B = -8'sd20; mode = 1; #10;
// 测试极端情况,加法最大正数与最小负数
A = 8'sd127; B = -8'sd128; mode = 0; #10;
// 测试极端情况,减法最大正数与最小负数
A = 8'sd127; B = -8'sd128; mode = 1; #10;
$stop;
end
endmodule
图1 有符号数加减法器测试的波形
一个无符号加减法器的Verilog描述如例3所示。
// 例3
module unsigned_adder(input [7:0] A, B, input mode, output reg [7:0] Sum, output reg CF);
reg cout;
always@(*) begin
if (mode == 0) begin //无符号加法
{cout, Sum} = A + B;
CF = cout; //进位
end
else begin //无符号减法
{cout, Sum} = A + (~B) + 1'b1;
CF = cout; //借位
end
end
endmodule
其中A和B会按照算法中谈到的一样,先补零拓展成8位数(这是自动进行的,进一步的位宽拓展问题可以阅读Verilog基础:表达式位宽的确定(位宽拓展)_verilog定义位宽-CSDN博客这篇文章),然后再按照有符号加减的运算。
例4是例3的一个简单testbench,测试波形如图2所示。
// 例4
module unsigned_adder_tb;
// 输入信号
reg [7:0] A;
reg [7:0] B;
reg mode;
// 输出信号
wire [7:0] Sum;
wire CF;
// 实例化被测模块
unsigned_adder uut (
.A(A),
.B(B),
.mode(mode),
.Sum(Sum),
.CF(CF)
);
// 测试过程
initial begin
// 初始化信号
$monitor("Time=%0t | A=%d, B=%d, mode=%b -> Sum=%d, CF=%b", $time, A, B, mode, Sum, CF);
// 测试无符号加法,没有进位
A = 8'd50; B = 8'd30; mode = 0; #10;
// 测试无符号加法,产生进位
A = 8'd200; B = 8'd100; mode = 0; #10;
// 测试无符号减法,没有借位
A = 8'd50; B = 8'd20; mode = 1; #10;
// 测试无符号减法,产生借位
A = 8'd20; B = 8'd50; mode = 1; #10;
// 测试加法,A和B都为最大值,产生进位
A = 8'd255; B = 8'd255; mode = 0; #10;
// 测试减法,A和B相等,没有借位
A = 8'd100; B = 8'd100; mode = 1; #10;
// 测试极端情况,加法,A为最大值,B为最小值,不产生进位
A = 8'd255; B = 8'd0; mode = 0; #10;
// 测试极端情况,减法,A为最小值,B为最大值,产生借位
A = 8'd0; B = 8'd255; mode = 1; #10;
$stop;
end
endmodule
图2 无符号数加减法器测试的波形
可以将两者结合,这样一个可以进行有符号数加减法和无符号数加减法的通用计算器就诞生了,如例5所示。
// 例5
module adder(input [7:0] A, B, input [1:0] mode, output reg [7:0] Sum, output reg CF_OF);
reg cout;
always@(*) begin
if (mode == 2'b00) begin //有符号加法
Sum = A + B;
CF_OF = ((A[7] == B[7]) & (Sum[7] != A[7])); //溢出
end
else if (mode == 2'b01) begin //有符号减法
Sum = A + (~B) + 1'b1;
CF_OF = ((A[7] != B[7]) & (Sum[7] != A[7])); //溢出
end
else if (mode == 2'b10) begin //无符号加法
{cout, Sum} = A + B;
CF_OF = cout; //进位
end
else if (mode == 2'b11) begin //无符号减法
{cout, Sum} = A + (~B) + 1'b1;
CF_OF = cout; //借位
end
end
endmodule
例6是该通用计算器的testbench。
// 例6
module adder_tb;
// 输入信号
reg [7:0] A;
reg [7:0] B;
reg [1:0] mode;
// 输出信号
wire [7:0] Sum;
wire CF_OF;
// 实例化被测模块
adder uut (
.A(A),
.B(B),
.mode(mode),
.Sum(Sum),
.CF_OF(CF_OF)
);
// 测试过程
initial begin
// 初始化监视器
$monitor("Time=%0t | A=%d, B=%d, mode=%b -> Sum=%d, CF_OF=%b", $time, A, B, mode, Sum, CF_OF);
// 测试模式 00: 有符号加法,没有溢出
A = 8'sd50; B = 8'sd30; mode = 2'b00; #10;
// 测试模式 00: 有符号加法,产生溢出
A = 8'sd100; B = 8'sd100; mode = 2'b00; #10;
// 测试模式 01: 有符号减法,没有溢出
A = 8'sd50; B = 8'sd30; mode = 2'b01; #10;
// 测试模式 01: 有符号减法,产生溢出
A = -8'sd100; B = 8'sd100; mode = 2'b01; #10;
// 测试模式 10: 无符号加法,没有进位
A = 8'd50; B = 8'd30; mode = 2'b10; #10;
// 测试模式 10: 无符号加法,产生进位
A = 8'd200; B = 8'd100; mode = 2'b10; #10;
// 测试模式 11: 无符号减法,没有借位
A = 8'd100; B = 8'd50; mode = 2'b11; #10;
// 测试模式 11: 无符号减法,产生借位
A = 8'd50; B = 8'd100; mode = 2'b11; #10;
// 测试极端情况,最大正值和最小负值
A = 8'sd127; B = -8'sd128; mode = 2'b00; #10; // 有符号加法
A = 8'd255; B = 8'd1; mode = 2'b10; #10; // 无符号加法
$stop;
end
endmodule