Verilog基础知识(数值表示总结,signed,原码,反码,补码)

本文深入探讨了Verilog中进行有符号运算的方法,重点讲解了补码的概念及其在运算中的应用。通过实例解析,展示了如何处理正负数的运算,包括无符号数转有符号数的过程,以及在FPGA设计中如何正确处理进位和借位问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

以前虽然是用过verilog,但是只使用了其中最常见wire,reg类型数据,并且是无符号的,因为是及处理过程很多数据就是无符号的。但是想进一步拓展无符号数,或者其底层的补码形式存储与运算方式,就需要进一步学习了。

可以先看看这个:
https://blog.youkuaiyun.com/woodpeck/article/details/77747181
以便更好地;理解补码。
以下参考:https://www.cnblogs.com/douzi2/p/3401252.html

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会自动以负数补码的形式储存在你声明的寄存器中。其实如果你申明普通类型wire或者reg,它是当做无符号数的,并不是自动的,这是需要人为指定,尽管硬件顶层最终生的电路可能会以补码运算(不太确定),但仿真不会受影响,补码只是给计算机看的。而如果申明的有符号的寄存器类型,则必定会自动帮你转换好补码运算,仿真输出应该也是补码,对应着寄存器。但补码永远是给计算机看的,实际数值,应该需要转换。举一个例子:

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 

接下来讨论一下,第二种情况——输入的两个数是有符号的;(第一种情况其实是比第二种情况多见。)

这个请况比较麻烦,需要再次细分成几种小情况:

在这里插入图片描述

程序如下:

module Test
( 
    input CLK , 
    input RSTn,
    input [4:0]A,
    input [4:0]B,
    output reg [5:0]Result
); 

11 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
                        if(A[4] == 1)//A为负数的情况----------------------------
                        begin
                            if(B[4] == 1)    //B为负数
                                Result <= ~{1'b0,(~A + 1'b1 + ~B + 1'b1)} + 1'b1;
                            else             //B为正数
                                begin
                                    TempRes = B + A;//TempRes = B - (~A + 1'b1);
                                    Result <= TempRes[4] ? {1'b1,TempRes} : TempRes;
                                end
                        end
                        else         //A为正数的情况----------------------------
                        begin
                            if(B[4] == 1)    //B为负数
                                begin
                                    TempRes = A + B;//TempRes = A - (~B + 1'b1);
                                    Result <= TempRes[4] ? {1'b1,TempRes} : TempRes;
                                end
                            else             //B为正数
                                Result <= A + B;
                        end
                        i <= i + 1'b1;
                    end 
                2://求出A-B的结果
                    begin
                        if(A[4] == 1)    //A为负数的情况----------------------------
                        begin
                            if(B[4] == 1)    //B为负数
                            begin
                                TempRes = (~B + 1'b1) + A;//TempRes = (~B + 1'b1) - (~A + 1'b1);
                                Result <= TempRes[4] ? {1'b1,TempRes} : TempRes;
                            end
                            else             //B为正数
                                Result <= ~{1'b0,(~A + 1'b1 + B)} + 1'b1;
                        end
                        else            //A为正数的情况----------------------------
                        begin
                            if(B[4] == 1)    //B为负数
                            //Result <= A + (~B + 1'b1);//不知道为什么,这样Result最高位会被置1????!!!!!!
                            Result <= {1'b0,A - B};//Result <= {1'b0,A + (~B + 1'b1)};
                            else             //B为正数
                                begin
                                    TempRes <= A - B;
                                    Result <= TempRes[4] ? {1'b1,TempRes} : TempRes;
                                end
                        end
                    end
            endcase        

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.

module Test
 ( 
   input CLK , 
   input RSTn,
   input [4:0]A,
  input [4:0]B,
   output reg [5:0]Result
); 

16 - (—16) = 32;而作为Result
而言,它的表示范围是-32到31。
但Result无法表示到32,这不是有问题吗?不是的,因为A和B是无法表示到16的顶多就是-16.所以以上情况不可能出现。

—— 宋桓公
2013-11-04


上面说了这么多,说白了就是想自己定义一套规则,在全部使用无符号数类型的情况下,如何能够处理好各种正负数的运算。因为这个时候,既然是无符号数,负数的补码是不会出现在寄存器中的,需要任务指定。尽管电路物理层面(总和出来之后)可能是补码电路。

但是最近问了一下实验室做硬件的同学,其实verilog已经支持正负数类型(寄存器)的定义,并且综合出来的就是补码形式的电路,当然这个逻辑我们不用管,仿真有库,综合有库,电路物理层我们更不用操心了,自然会有相应的转换电路。

关于具体,专业一点的介绍,我推荐下面这篇博客:
https://blog.youkuaiyun.com/maxwell2ic/article/details/80596210#commentBox

Verilog基础知识(数值表示总结,signed,原码,反码,补码)

定点数

原码表示

MSB是符号位,0表示正数,1表示负数。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

反码表示(1’s complement)

正数的反码与原码相同,负数的反码是原码除符号位的所有位取反。

补码表示(2’s complement)

正数的原码、反码、补码相同;负数的补码等于反码加1,负数的原码与补码之间的相互转化关系均为除了符号位取反再加1。
在这里插入图片描述

补码的好处在于加减运算不需要判断符号位。B-A=B+(-A),-A的补码是A的补码所有位取反再加1
在这里插入图片描述

三种数制之间转换关系

正数原码、反码、补码相同;
负数补码为原码除符号位取反加1;
相反数数补码转换为所有位取反加1.
在这里插入图片描述

verilog中的常量类型

在这里插入图片描述

在这里插入图片描述

verilog中常量编译为二进制,均为补码(整数与原码相同,负数为除符号为取反加1)

在verilog中声明端口或者信号时,默认是无符号数,

wire [7:0] number;
reg [7:0] number;

转换为十进制为

在这里插入图片描述

如果需要指定为有符号数,需要特殊声明,则在硬件底层的二进制数均为补码形式

wire signed [7:0] number;
reg signed [7:0] number;

转换为十进制为
在这里插入图片描述
在verilog2001中可以特别地用’s来声明符号数,比如-4表示成一个3bit十六进制数位-3’sh4。
十进制的数永远都是有符号数.

举个例子

比如产生一个三角波,无符号8bit能产生0-255之间的数

module counter(
    clk,
    rst,

    out,
    );

input clk;    //clk
input rst;    //reset, high active

output reg [7:0] out;

reg [7:0] cnt;
reg state;

always @(posedge clk or posedge rst) begin
    if (rst) begin
        cnt <= 0;
        state <= 1'b0;
    end
    else begin
        case(state)
            1'b0: begin
                if(cnt==255) begin
                    cnt <= cnt;
                    state <= 1'b1;
                end
                else begin
                    cnt <= cnt+1;
                    state <= 1'b0;
                end
            end
            1'b1: begin
                 if(cnt==0) begin
                    cnt <= cnt;
                    state <= 1'b0;
                end
                else begin
                    cnt <= cnt-1;
                    state <= 1'b1;
                end
            end
        endcase
    end
end

always @(posedge clk or posedge rst) begin
    if (rst) begin
        out <= 0;    // reset
    end
    else
        out <= cnt;
end

endmodule

在这里插入图片描述

可以看到输出信号out[7:0]是从8‘b0000_0000(8’d0)到8’b1111_1111(8’d255)

而对于一个有符号的8bit三角波发生器

module signed_counter(
    clk,
    rst,

    out,
    );

input clk;    //clk
input rst;    //reset, high active

output reg signed [7:0] out;

reg signed [7:0] cnt;
reg state;

always @(posedge clk or posedge rst) begin
    if (rst) begin
        cnt <= -128;
        state <= 1'b0;
    end
    else begin
        case(state)
            1'b0: begin
                if(cnt==127) begin
                    cnt <= cnt;
                    state <= 1'b1;
                end
                else begin
                    cnt <= cnt+1;
                    state <= 1'b0;
                end
            end
            1'b1: begin
                 if(cnt==-128) begin
                    cnt <= cnt;
                    state <= 1'b0;
                end
                else begin
                    cnt <= cnt-1;
                    state <= 1'b1;
                end
            end
        endcase
    end
end

always @(posedge clk or posedge rst) begin
    if (rst) begin
        out <= -128;    // reset
    end
    else
        out <= cnt;
end

endmodule

在这里插入图片描述

可以看到输出是从8’b1000_0001(-8d’127)到8’b0000_0000(8d’127)这样的补码形式变化的。

更多参考:

https://blog.youkuaiyun.com/hasodron/article/details/10108353
http://www.cnblogs.com/lanlingshan/archive/2012/05/01/2478004.html
http://www.cnblogs.com/oomusou/archive/2007/11/25/971509.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值