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 [:]A,
input [:]B,
output reg [:]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 [:]A,
input [:]B,
output reg [4:]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 [:]A,
input [:]B,
output reg [:]Result
); reg [:]i;
reg [:]TempRes;//中间结果
always @(posedge CLK or negedge RSTn)
if(!RSTn) begin Result <='d0; i <= 2'd0; TempRes <= 'd0; end
else
case(i)
          0:i <= i + 1'b1;
://求出A+B的结果
begin
              Result <= A + B;
i <= i + '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 = ; #; RSTn = ;
CLK = ; forever # CLK = ~CLK;
end always @(posedge CLK or negedge RSTn)
if(!RSTn)begin A <= 'd0; B <= 0; end
else begin A <= -; B <= ;end

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

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

程序如下:

 module Test
(
input CLK ,
input RSTn,
input [:]A,
input [:]B,
output reg [:]Result
); reg [:]i;
reg [:]TempRes;//中间结果
always @(posedge CLK or negedge RSTn)
if(!RSTn) begin Result <='d0; i <= 2'd0; TempRes <= 'd0; end
else
case(i)
:i <= i + 'b1;
://求出A+B的结果
begin
if(A[] == )//A为负数的情况----------------------------
begin
if(B[] == ) //B为负数
Result <= ~{'b0,(~A + 1'b1 + ~B + 'b1)} + 1'b1;
else //B为正数
begin
TempRes = B + A;//TempRes = B - (~A + 1'b1);
Result <= TempRes[] ? {'b1,TempRes} : TempRes;
end
end
else //A为正数的情况----------------------------
begin
if(B[] == ) //B为负数
begin
TempRes = A + B;//TempRes = A - (~B + 1'b1);
Result <= TempRes[] ? {'b1,TempRes} : TempRes;
end
else //B为正数
Result <= A + B;
end
i <= i + 'b1;
end
://求出A-B的结果
begin
if(A[] == ) //A为负数的情况----------------------------
begin
if(B[] == ) //B为负数
begin
TempRes = (~B + 'b1) + A;//TempRes = (~B + 1'b1) - (~A + 'b1);
Result <= TempRes[] ? {'b1,TempRes} : TempRes;
end
else //B为正数
Result <= ~{'b0,(~A + 1'b1 + B)} + 'b1;
end
else //A为正数的情况----------------------------
begin
if(B[] == ) //B为负数
//Result <= A + (~B + 1'b1);//不知道为什么,这样Result最高位会被置1????!!!!!!
Result <= {'b0,A - B};//Result <= {1'b0,A + (~B + 'b1)};
else //B为正数
begin
TempRes <= A - B;
Result <= TempRes[] ? {'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 [:]A,
input [:]B,
output reg [:]Result
);

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

                                              —— 宋桓公
2013-11-04

Verilog中的有符号计算之认知补码的更多相关文章

  1. system verilog中的跳转操作

    在verilog中,使用disable声明来从执行流程中的某一点跳转到另一点.特别地,disable声明使执行流程跳转到标注名字的声明组末尾,或者一个任务的末尾. verilog中的disable命令 ...

  2. system verilog中的类型转换(type casting)、位宽转换(size casting)和符号转换(sign casting)

    类型转换 verilog中,任何类型的任何数值都用来给任何类型赋值.verilog使用赋值语句自动将一种类型的数值转换为另一种类型. 例如,当一个wire类型赋值给一个reg类型的变量时,wire类型 ...

  3. 一段比较有意思的代码——介绍system verilog中的新增幅值语句

    system verilog中新加了很多幅值语句,虽然都只适用于阻塞幅值,但是在某些场合中非常实用. 下面是一段有意思的代码,覆盖了一些用法. package definitions; typedef ...

  4. 关于verilog中if与case语句不完整产生锁存器的问题 分类: FPGA 2014-11-08 17:39 260人阅读 评论(0) 收藏

    在很多地方都能看到,verilog中if与case语句必须完整,即if要加上else,case后要加上default语句,以防止锁存器的发生,接下来就来说说其中原因. 一,什么是锁存器?锁存器与触发器 ...

  5. 关于Verilog 中的for语句的探讨

    在C语言中,经常用到for循环语句,但在硬件描述语言中for语句的使用较C语言等软件描述语言有较大的区别. 在Verilog中除了在Testbench(仿真测试激励)中使用for循环语句外,在Test ...

  6. Verilog中锁存器与多路选择器

    Verilog中锁存器与多路选择器 Verilog是一种硬件描述语言,它代表的是硬件. Verilog代表的就是逻辑门和连接线. 对于一个always@(*)控制的块而言,只要块中的表达式包含的任意的 ...

  7. verilog中always块延时总结

    在上一篇博文中 verilog中连续性赋值中的延时中对assign的延时做了讨论,现在对always块中的延时做一个讨论. 观测下面的程序,@0时刻,输入的数据分别是0x13,0x14 . @2时刻, ...

  8. verilog中读取文件中的字符串_modelsim高级仿真

    今天给个程序大家玩玩.因为今天遇到一个问题,就是要向UART发送指令,指令非常多,都是字符串.一直copy 函数 UART ("COMM_1");  UART ("COM ...

  9. verilog中的有符号数运算

    verilog中的有符号数运算 http://hi.baidu.com/lixu1113/item/d00dc095f86aed48f142159a verilog中的有符号数运算 有符号数的计算:若 ...

随机推荐

  1. 负载均衡(Load Balancing)学习笔记(三)

    本文讲述实现负载均衡的常用算法. 轮询法(Round Robin) 轮询法是负载均衡中最常用的算法,它容易理解也容易实现.轮询法是指负载均衡服务器(load balancer)将客户端请求按顺序轮流分 ...

  2. CSS| 解决子级用css float浮动 而父级div没高度不能自适应高度

    解决子级用css float浮动 而父级div没高度不能自适应高度 解决子级对象使用css float浮动 而父级div不能自适应高度,不能被父级内容撑开解决方法,父级div没有高度解决方法. 最外层 ...

  3. teradata 数据定义

    teradata 数据定义 创建表的可选项 是否允许记录重复 set 不允许记录重复 multiset 允许记录重复 数据保护 fallback       fallback    使用fallbac ...

  4. DOS 总结

    shutdown -s -t 30 指定在30秒之后自动关闭计算机. + L 返回登录页面 netstat 最近访问IP Regedit 打开注册表

  5. java使用elasticsearch分组进行聚合查询(group by)-项目中实际应用

    java连接elasticsearch 进行聚合查询进行相应操作 一:对单个字段进行分组求和 1.表结构图片: 根据任务id分组,分别统计出每个任务id下有多少个文字标题 .SQL:select id ...

  6. DevExpress07、DataNavigator、 ControlNavigator

    https://documentation.devexpress.com/WindowsForms/DevExpress.XtraEditors.DataNavigator.class 1.DataN ...

  7. Python3中遇到UnicodeEncodeError: 'ascii' codec can't encode characters in ordinal not in range(128)

    在 linux服务器上运行代码报错: Python3中遇到UnicodeEncodeError: ‘ascii’ codec can’t encode characters in ordinal no ...

  8. Tengine 2.1.2 (nginx/1.6.2)安装配置,淘宝 Web 服务器

    简介 Tengine是由淘宝网发起的Web服务器项目.它在Nginx的基础上,针对大访问量网站的需求,添加了很多高级功能和特性.Tengine的性能和稳定性已经在大型的网站如淘宝网,天猫商城等得到了很 ...

  9. 【洛谷】【单调队列】P2032 扫描

    [题目描述:] 有一个 1 ∗ n 的矩阵,有 n 个正整数. 现在给你一个可以盖住连续的 k 的数的木板. 一开始木板盖住了矩阵的第 1 ∼ k 个数,每次将木板向右移动一个单位,直到右端与第 n ...

  10. div宽度随屏幕大小变化

    题目: 一个页面上两个div左右铺满整个浏览器, 要保证左边的div一直为100px,右边的div跟随浏览器大小变化, 比如浏览器为500,右边div为400,浏览器为900,右边div为800. 方 ...