Verilog中的有符號計算之認知補碼


  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;

   這種情況是AB都為負數,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

 

 

 

 

 

 

 

 

 

 

 

 

     

 

 


免責聲明!

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



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