Verilog 加法器和減法器(5)


      前面二進制加法運算,我們並沒有提操作數是有符號數,還是無符號數。其實前面的二進制加法對於有符號數和無符號數都成立。比如前面的8位二進制加法運算,第一張圖我們選radix是unsigned,表示無符號加法,第二張圖我們選radix是decimal,表示有符號數,從圖中可知結果都是正確的。對於有符號數來說,負數默認是補碼的形式存在。假設二進制數是n位,則對於無符號數來說,表示范圍是0~(2^n) -1 ,對於有符號數,表示的范圍是-(2^(n-1))~2^(n-1) - 1


image


image

對於有符號數來說,通常還要知道加法結果數據是否溢出。有一種直觀的方法判斷結果是否溢出,就是如果兩個加數有相同的符號,但是它們的和與它們有不同的符號,則產生溢出。假設有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


功能驗證的波形圖如下:


image

對於有符號數的減法,我們也可以用加法來做,但是對於減數,我們要做以下變化,如果減數為正數,則變其為補碼表示的負數,如果其為補碼表示的負數,則把它轉化為正數。

assign y1 = y[n-1]?(~{y[n-1:0]}+1'b1):(~{1'b0,y[n-2:0]}+1'b1);

module subn_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;
  wire [n-1:0] y1;
  reg [n:0] c;
  integer k;

  //y commplement, if y=0, to negative with commplement,if y=1, to positive number. 
  assign y1 = y[n-1]?(~{y[n-1:0]}+1'b1):(~{1'b0,y[n-2:0]}+1'b1);

  always @(x,y1) begin
    c[0] = 1'b0;
	 for(k = 0; k < n; k = k + 1) begin
	   s[k] = x[k]^y1[k]^c[k];
		c[k+1] = (x[k]&y1[k])|(x[k]&c[k])|(y1[k]&c[k]);
	 end
	 cout = c[n];
	 overflow = (x[n-1]&y1[n-1]&~s[n-1])|(~x[n-1]&~y1[n-1]&s[n-1]);
	end

endmodule

module subn_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;
  wire [n-1:0] y1;
  integer k;
  //y commplement, if y=0, to negative with commplement,if y=1, to positive number. 
  assign y1 = y[n-1]?(~{y[n-1:0]}+1'b1):(~{1'b0,y[n-2:0]}+1'b1);
  assign {cout, s} = x + y1 ;
  assign overflow = (x[n-1]&y1[n-1]&~s[n-1])|(~x[n-1]&~y1[n-1]&s[n-1]);

 endmodule

testbench代碼為:

`timescale 1ns/1ns
`define clock_period 20

module subn_signed_tb;
  reg [7:0] x,y;

  wire cout;
  wire overflow;
  wire [7:0] s;
  reg clk;

  subn_signed #(.n(8)) subn_signed_0(
						.x(x),
						.y(y),
						.s(s),
						.cout(cout),
						.overflow(overflow)
                  );

  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

從功能驗證的波形圖中,我們可以看到見過是正確的。


image





































免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM