Verilog中的有符號計數,一般是自己定義的而不是像C語言之類的定義一個有符號變量就好了。所以,要想在FPGA的世界里隨心所欲的進行有符號運算,必須先對補碼有一個很好的認知,然后再注意Verilog中編程的幾個特性,兩者缺一不可。
對補碼初步的認識:
1、正數的補碼與源碼相同,即正數的補碼是其本身。
2、負數的補碼,是對其源碼(除符號位)取反再加一,於是得到其補碼。
3、對負數的補碼(除符號位)取反再加一,於是得到其源碼。
4、正數的補碼被定義為其本身,所以不需以上操作。(其實你也可以理解為正數沒有補碼)
5、“計算機”儲存數時是以補碼的形式儲存的。
以四位二進制舉例(最高位是其符號位):
-7,負7的源碼:1_111;
-7,負7的補碼:1_001;
在此提出一個看法,幫助理解,補碼是給計算機看的,源碼是給人看的。
看看1 + (- 2) 如何計算,我們知道負數的話都是由補碼儲存的所以就是1 + (-2的補碼),及
0_001 + 1_110 = 1_111;(最高為為符號位),所以1111及-1的補碼(對負數的補碼(除符號位)取反再加一,於是得到其源碼)
這給我們了一個啟示,前面說過“Verilog中的有符號計數,一般是自己定義的”,那么在寫Verilog時我們把最高為作為符號位,我們通過最高位判斷該數的正負。
對於FPGA的有符號計算,我覺得應該從兩種情況進行分析。一種是:輸入的兩個數本來是無符號的,而由於運算導致結果是一個有符號的數(如1-7=-6);
另一種是:輸入的兩個數是有符號的。
對於第一種情況而言,1-7=-6,這個-6會自動以負數補碼的形式儲存在你聲明的寄存器中。舉一個例子:
input [4:0]A, input [4:0]B, output reg [4:0]Result
A,B作為兩個運算的數,Result儲存運算的結果,他們的最高位都是表示符號位。
假如:A = 0001,B = 0111;讓A-B,那么1-7=-6及Result = 1010(-6 的補碼,最高位是符號位),沒有問題。
假如:A = 0111,B = 0111;讓A+B,那么7+7=14及Result = 1110;此時如果最高位不是符號位那么Result=14沒錯,但是此時Result的最高為是符號位,所以結果是-1(1110是-1的補碼)。
也就是,一旦產生了進位,結果就錯了。所以我們改進一下。
input [3:0]A, input [3:0]B, output reg [4:0]Result
我們把Result增加一位,那么7+7=14及Result = 01110;這樣就對了;但是1-7,Result = 01010 = 10;這就錯了。因為對於一個4位數
而言-6 的補碼是1010,而對於一個5位數而言-6 的補碼是11010。所以Result = 11010才對,但是A和B是一個4位數結果只會產生1010而賦值
給一個5位數的Result結果只能是01010(系統是不會幫你把最高位置1的);所以這個1由我們自己置,正所謂,自己動手豐衣足食。對於補碼而言
還有一個特性,只要確定該數是一個負數的補碼,那么不管我們在符號位前面放置多少個1該數的值不變(但記住最高為始終是符號位),這類似與
一個正數前不管加多少個0它的值不變。舉個例子1_010,表示-6的補碼,那么1111111_010仍然是-6的補碼。所以當我們判斷該數是一個負數
補碼的話,我們就可以安心的給最前面加個1了。
對於第一種情況而言,我們可以把程序寫成這樣:
module Test ( input CLK , input RSTn, input [4:0]A, input [4:0]B, output reg [5:0]Result ); reg [1:0]i; reg [4:0]TempRes;//中間結果 always @(posedge CLK or negedge RSTn) if(!RSTn) begin Result <=6'd0; i <= 2'd0; TempRes <= 5'd0; end else case(i)
0:i <= i + 1'b1; 1://求出A+B的結果 begin
Result <= A + B;
i <= i + 1'b1; end 2://求出A-B的結果 begin
TempRes <= A - B;
Result <= TempRes[4] ? {1'b1,TempRes} : TempRes;
i <= i + 1'b1;
end
endcase
endmodule
這樣的話,運算時不管是產生借位或是進位,都不會出錯了,但前提是“第一種情況”——A,B都是無符號的數。現在我們反過來想想,為什么我要把Result擴充一位,
原因就是,為了避免產生進位是進位位會覆蓋符號位,因為對於一個有符號的4位數他的表示的正數范圍是0到7(呵呵,暫時把0划歸到正數吧,這樣平衡一些),負數范圍是-8到-1。
而對與第一種情況而言,能產生的最大數就是是0111 + 0111 = 1110這種情況,也就是說進位位不可能威脅到符號位,從而確保了最高位只可能表示符號位。
以上程序可以寫一個測試程序仿真,0:i <= i + 1'b1;這個是為了緩沖一個時鍾周期,用於A,B信號的輸入。測試程序如下:
initial begin RSTn = 0; #10; RSTn = 1; CLK = 1; forever #10 CLK = ~CLK; end always @(posedge CLK or negedge RSTn) if(!RSTn)begin A <= 5'd0; B <= 0; end else begin A <= -2; B <= 3;end
接下來討論一下,第二種情況——輸入的兩個數是有符號的;(第一種情況其實是比第二種情況多見。)
這個請況比較麻煩,需要再次細分成幾種小情況:
程序如下:
1 module Test 2 ( 3 input CLK , 4 input RSTn, 5 input [4:0]A, 6 input [4:0]B, 7 output reg [5:0]Result 8 ); 9 10 11 reg [1:0]i; 12 reg [4:0]TempRes;//中間結果 13 always @(posedge CLK or negedge RSTn) 14 if(!RSTn) begin Result <=6'd0; i <= 2'd0; TempRes <= 5'd0; end 15 else 16 case(i) 17 0:i <= i + 1'b1; 18 1://求出A+B的結果 19 begin 20 if(A[4] == 1)//A為負數的情況---------------------------- 21 begin 22 if(B[4] == 1) //B為負數 23 Result <= ~{1'b0,(~A + 1'b1 + ~B + 1'b1)} + 1'b1; 24 else //B為正數 25 begin 26 TempRes = B + A;//TempRes = B - (~A + 1'b1); 27 Result <= TempRes[4] ? {1'b1,TempRes} : TempRes; 28 end 29 end 30 else //A為正數的情況---------------------------- 31 begin 32 if(B[4] == 1) //B為負數 33 begin 34 TempRes = A + B;//TempRes = A - (~B + 1'b1); 35 Result <= TempRes[4] ? {1'b1,TempRes} : TempRes; 36 end 37 else //B為正數 38 Result <= A + B; 39 end 40 i <= i + 1'b1; 41 end 42 2://求出A-B的結果 43 begin 44 if(A[4] == 1) //A為負數的情況---------------------------- 45 begin 46 if(B[4] == 1) //B為負數 47 begin 48 TempRes = (~B + 1'b1) + A;//TempRes = (~B + 1'b1) - (~A + 1'b1); 49 Result <= TempRes[4] ? {1'b1,TempRes} : TempRes; 50 end 51 else //B為正數 52 Result <= ~{1'b0,(~A + 1'b1 + B)} + 1'b1; 53 end 54 else //A為正數的情況---------------------------- 55 begin 56 if(B[4] == 1) //B為負數 57 //Result <= A + (~B + 1'b1);//不知道為什么,這樣Result最高位會被置1????!!!!!! 58 Result <= {1'b0,A - B};//Result <= {1'b0,A + (~B + 1'b1)}; 59 else //B為正數 60 begin 61 TempRes <= A - B; 62 Result <= TempRes[4] ? {1'b1,TempRes} : TempRes; 63 end 64 end 65 end 66 endcase 67 68 endmodule
要理解上面程序,首先理解這一句:
Result <= ~{1'b0,(~A + 1'b1 + ~B + 1'b1)} + 1'b1;
這種情況是A,B都為負數,A - B的情況,A和B都是負數補碼,所以~A + 1'b1這樣的操作就是將負數補碼變成正數。將他們全變成正數后相加后,變成負數補碼的形式,儲存在Result中。類似於 -3 + -5 = -(3 + 5) = -8;
其他情況,自行分析。至於57行不能寫成那樣,是什么原因還不清楚,知道的朋友請告訴我一聲,先謝謝了。
//----------------------------------------------------------------------------------------------------------------------------------------
對了,要補充一點,之前分析到:對於一個有符號的4位數他的表示的正數范圍是1到7,負數范圍是-8到-1。
多多少少也感覺到一定不平衡,正數能表示到7,而負數卻能表示到-8。接就是1000~0111;-8~7這么個范圍。
根據第3條:3、對負數的補碼(除符號位)取反再加一,於是得到其源碼。
我們把-8的補碼去反加1,看看能不能得到正8,結果還是-8.我們來看看過程
1_000 ->取反(除符號位)->1_111-> 加1->1_000;我們再看看如果最高位不是符號位1000正好又是8!
這個也是補碼讓人頭暈的地方,對於一個5位數也一樣1_0000,表示-16,取反加1,還是-16.
1 module Test 2 ( 3 input CLK , 4 input RSTn, 5 input [4:0]A, 6 input [4:0]B, 7 output reg [5:0]Result 8 );
16 - (—16) = 32;而作為Result
而言,它的表示范圍是-32到31。
但Result無法表示到32,這不是有問題嗎?不是的,因為A和B是無法表示到16的頂多就是-16.所以以上情況不可能出現。
—— 宋桓公
2013-11-04