zoukankan      html  css  js  c++  java
  • 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

     

     

     

     

         

     

     

  • 相关阅读:
    EntityFramework之领域驱动设计实践(三)(转)
    System.Collections.ObjectModel.Collection
    EntityFramework之领域驱动设计实践 (一)(转)
    C#中Collection,List和ArrayList的区别(转)
    家常菜
    WCF开发实战系列二:使用IIS发布WCF服务(转)
    Broken Flowers(破碎之花)
    EntityFramework之领域驱动设计实践(五)(转)
    用Restful方式调用WCF进行上传下载(转)
    EntityFramework之领域驱动设计实践(二)(转)
  • 原文地址:https://www.cnblogs.com/douzi2/p/3401252.html
Copyright © 2011-2022 走看看