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 [:]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中的有符号计算之认知补码的更多相关文章
- system verilog中的跳转操作
在verilog中,使用disable声明来从执行流程中的某一点跳转到另一点.特别地,disable声明使执行流程跳转到标注名字的声明组末尾,或者一个任务的末尾. verilog中的disable命令 ...
- system verilog中的类型转换(type casting)、位宽转换(size casting)和符号转换(sign casting)
类型转换 verilog中,任何类型的任何数值都用来给任何类型赋值.verilog使用赋值语句自动将一种类型的数值转换为另一种类型. 例如,当一个wire类型赋值给一个reg类型的变量时,wire类型 ...
- 一段比较有意思的代码——介绍system verilog中的新增幅值语句
system verilog中新加了很多幅值语句,虽然都只适用于阻塞幅值,但是在某些场合中非常实用. 下面是一段有意思的代码,覆盖了一些用法. package definitions; typedef ...
- 关于verilog中if与case语句不完整产生锁存器的问题 分类: FPGA 2014-11-08 17:39 260人阅读 评论(0) 收藏
在很多地方都能看到,verilog中if与case语句必须完整,即if要加上else,case后要加上default语句,以防止锁存器的发生,接下来就来说说其中原因. 一,什么是锁存器?锁存器与触发器 ...
- 关于Verilog 中的for语句的探讨
在C语言中,经常用到for循环语句,但在硬件描述语言中for语句的使用较C语言等软件描述语言有较大的区别. 在Verilog中除了在Testbench(仿真测试激励)中使用for循环语句外,在Test ...
- Verilog中锁存器与多路选择器
Verilog中锁存器与多路选择器 Verilog是一种硬件描述语言,它代表的是硬件. Verilog代表的就是逻辑门和连接线. 对于一个always@(*)控制的块而言,只要块中的表达式包含的任意的 ...
- verilog中always块延时总结
在上一篇博文中 verilog中连续性赋值中的延时中对assign的延时做了讨论,现在对always块中的延时做一个讨论. 观测下面的程序,@0时刻,输入的数据分别是0x13,0x14 . @2时刻, ...
- verilog中读取文件中的字符串_modelsim高级仿真
今天给个程序大家玩玩.因为今天遇到一个问题,就是要向UART发送指令,指令非常多,都是字符串.一直copy 函数 UART ("COMM_1"); UART ("COM ...
- verilog中的有符号数运算
verilog中的有符号数运算 http://hi.baidu.com/lixu1113/item/d00dc095f86aed48f142159a verilog中的有符号数运算 有符号数的计算:若 ...
随机推荐
- RN在Android打包发布App
参考资料:http://www.jianshu.com/p/b8811669bcb6 RN在Android打包发布App 1-:生成一个签名密钥你可以用keytool命令生成一个私有密钥.在Windo ...
- SQL2005中的事务与锁定(九)-(1)- 转载
------------------------------------------------------------------------ -- Author : HappyFlyStone - ...
- Spring中的destroy-method方法
1. Bean标签的destroy-method方法 配置数据源的时候,会有一个destroy-method方法 <bean id = "dataSource" class ...
- 远程桌面web连接
我们可以利用web浏览器搭配远程桌面技术来连接远程计算机,这个功能被称为远程桌面web连接(Remote desktop web connection),要享有此功能,请先在网络上一台window ...
- ETL技巧应用(高级应用介绍:准备区运用、 时间戳的运用、日志表的运用、使用调度)
1.1 准备区运用 a.在构建数据仓库时,数据源位于一服务器上,数据仓库在另一服务器端,数据源Server端访问频繁,并且数据量大,需要不断更新, b.建立准备区数据库: >将数据抽取到准 ...
- Linux uptime命令详解
常见的命令展示 uptime 08:21:34 up 36 min, 2 users, load average: 0.00, 0.00, 0.00 #当前服务器时间: 08:21:34 #当前服务器 ...
- Django2.0路由层-URLconf
目录 DJango2.0路由层-URLconf 概述 urlpatterns 实例 path转换器 自定义path转换器 使用正则表达式 命名组(有名分组) URLconf匹配请求URL中的哪些部分 ...
- SDN 第五次上机作业
1.搭建如下拓扑并连接控制器 2.下发相关流表和组表实现负载均衡 s1: s2: s3: s4: 3.抓包分析验证负载均衡 s4-eth1: s4-eth2: s4-eth3
- saltstack二次开发(一)
Saltstack简介 Salt是一个配置管理系统,能够维护预定义状态的远程节点(比如,确保指定的包被安装,指定的服务在运行),一个分布式远程执行系统,用来在远程节点(可以是单个节点,也可以是任意规则 ...
- 浅析Java虚拟机结构与机制[转]
本文旨在给所有希望了解JVM(Java Virtual Machine)的同学一个概念性的入门,主要介绍了JVM的组成部分以及它们内部工作的机制和原理.当然本文只是一个简单的入门,不会涉及过多繁杂的参 ...