前面二进制加法运算,我们并没有提操作数是有符号数,还是无符号数。其实前面的二进制加法对于有符号数和无符号数都成立。比如前面的8位二进制加法运算,第一张图我们选radix是unsigned,表示无符号加法,第二张图我们选radix是decimal,表示有符号数,从图中可知结果都是正确的。对于有符号数来说,负数默认是补码的形式存在。假设二进制数是n位,则对于无符号数来说,表示范围是0~(2^n) -1 ,对于有符号数,表示的范围是-(2^(n-1))~2^(n-1) - 1
对于有符号数来说,通常还要知道加法结果数据是否溢出。有一种直观的方法判断结果是否溢出,就是如果两个加数有相同的符号,但是它们的和与它们有不同的符号,则产生溢出。假设有n位有符号二进制数x,y,它们的和为s,则它们和溢出判断公式是 overflow = xn_1&yn-1&~sn-1 + ~xn_1&~yn-1&sn-1
修改后的有符号数加法代码为:
module addern_signed(x, y, s, cout, overflow);
parameter n=8;
input [n-1:0] x;
input [n-1:0] y;
output reg[n-1:0] s;
output reg cout;
output reg overflow;
reg [n:0] c;
integer k;
always @(x,y) begin
c[0] = 1'b0;
for(k = 0; k < n; k = k + 1) begin
s[k] = x[k]^y[k]^c[k];
c[k+1] = (x[k]&y[k])|(x[k]&c[k])|(y[k]&c[k]);
end
cout = c[n];
overflow = (x[n-1]&y[n-1]&~s[n-1])|(~x[n-1]&~y[n-1]&s[n-1]);
end
endmodule
module addern_signed(x, y, s, cout, overflow); parameter n=8; input [n-1:0] x; input [n-1:0] y; output [n-1:0] s; output cout; output overflow; integer k; assign {cout, s} = x + y ; assign overflow = (x[n-1]&y[n-1]&~s[n-1])|(~x[n-1]&~y[n-1]&s[n-1]); endmodule
修改后的testbench文件为:
`timescale 1ns/1ns `define clock_period 20 module addern_signed_tb; reg [7:0] x,y; wire cout; wire [7:0] s; reg clk; addern_signed #(.n(8)) addern_signed_0( .x(x), .y(y), .s(s), .cout(cout) ); initial clk = 0; always #(`clock_period/2) clk = ~clk; initial begin x = 0; repeat(20) #(`clock_period) x = $random; end initial begin y = 0; repeat(20) #(`clock_period) y = $random; end initial begin #(`clock_period*20) $stop; end endmodule
功能验证的波形图如下: