主要内容是对有符号数和无符号数在设计时,数据是怎样传递的,符号位是怎样来的,以及相关的几种运算设计应当遵循怎样的想法。
最近对加减乘除运算很困惑,主要是对于有符号数的运算的困扰,如果运算出现负数怎么办。对于数据的赋值应该是怎样的,里面的数据是怎样存储的。
找来找去不如直接看Verilog标准verilog 2001里面写的很清楚。
具体的部分是在4.1.5小节中讲述的。
几种运算符的使用如下:

运算 功能
a + b a加b
a - b a减b
a * b a乘以b
a / b a除以b
a % b a除以b的余数
a ** b a的b次方(在设计时,b只能是常量)

说明:
1、 整数除法将截断任何小数部分,也就是向下取整。对于除法运算符(/)或模数运算符(%),如果第二个操作数为零,则整个结果值应为x。模数运算符,例如y % z,当第一个操作数被第二个操作数除时给出余数,因此当z正好除y时为零。模数运算的结果应取第一个操作数的符号。
2、幂运算符(**)的第一个和第二个操作数以及其赋值结果,应当保证,全为有符号数或全为无符号数。如果第一个操作数为零且第二个操作数为非正,或者第一个操作数为负且第二个操作数不是整数值,则幂运算符的结果是未指定的。

对于取模运算符,其各种情况的运算结果如下:

运算 取余
3%2 1
-3%2 -1
2%2 0
-3’d3%2 1(-3‘d3被视为一个大的正数,除以2时余下1)

1.数据传递

在理解有符号和无符号数的运算之前,我觉得至少要明白我们在设计中,寄存器都是怎样进行赋值的,数据是怎样进行传递的,只有知道了其存储数据和传递数据的方式了,才能正确使用Verilog代码进行设计有符号数和无符号数的运算。
首先看其定义

Data type Interpretation
unsigned net 无符号数
signed net 有符号数 (按二进制补码存储)
unsigned reg 无符号数
signed reg 有符号数 (按二进制补码存储)
integer 有符号数 (按二进制补码存储)(可以理解为C语言的int型数据)
time 无符号数
real ,realtime 有符号数, 浮点类型

有符号数一般定义就是在数据类型前面加上signed,如果不加就是默认成无符号数。

1.1.常量赋值

如果用常量直接对数据进行赋值,如下代码方式:

reg  [3:0] regA;    //无符号
//reg signed [3:0] regA;    //有符号定义
always@(posedge clk)
beginregA <= 4'd10;   //正数赋值//regA <= 4'd10;  //负数赋值
end


结果如上图,如果直接赋值的话,正数是直接按照被赋值寄存器的位宽进行赋值的,而负数,无论是定义的有符号还是无符号寄存器,都是赋值其补码(不包括其符号位)(剩余的按符号位补)。如下图:

这里写一个被赋值寄存器位宽超过赋值的常量位宽的代码进行验证:

reg  [5:0]  a;
initial
begina = 4'd10;     //1#4a = -4'd10;
end

再写一个被赋值的寄存器位宽小于常量位宽的情况:

reg  [2:0]  a;
initial
begina = 4'd10;#4a = -4'd10;
end


仿真显示,跟上面分析的完全一致,所以被赋值的寄存器无论是否有符号定义,其赋值结果都是一样的。
这里需要注意一点,我们在用常量对寄存器进行赋值的时候,代码设计要写成十进制(或者16进制以及8进制)的形式才会按照这种高位补符号位,如果写成二进制,而二进制数据是默认为无符号数的,因此高位是直接补0的(无论赋给有符号数还是无符号数)。因此在设计运算电路的时候,尽量不使用二进制代码

这里在学习的过程中发现了一个常数表示方法:

reg [3:0] regA   //这里设置为4位,最高位作为符号位
always@(posedge clk)
begin
regA <= 3'sd5;
end

sd表示有符号十进制的意思,也就是说把上面5转换成二进制为101,其第一位为符号位,因此存入regA的数据是101再加上其符号位1101。如果表达式写成这样

regA <= -3'sd5;

应当这样进行理解,-5的二进制补码为011则其最高位为0所以最后存入寄存器regA的是0011

1.2.寄存器赋值

上一节已经分析了常量赋值到寄存器,因此下面的理解应该就很轻松了,可以提前思考一下,如果是无符号寄存器传数据,应该是位宽不够时,向高位补0,如果是有符号寄存器传数据,此时应该就是跟常量赋值类似,位宽不够,向高位补符号位。下面进行验证。
注:
上面已经说了被赋值的寄存器的定义是否为signed,其赋值的方案都是一样的,因此下面验证时,被赋值的寄存器均设置为无符号了。

1.2.1.无符号寄存器传数据

reg  [3:0]  a;
reg  [6:0]  b;   //验证高位补什么数据
reg  [1:0]  c;   //验证位宽较低时怎样截数据
always@(posedge clk)
beginb <=  a;c <=  a;
end
initial
beginclk = 1;#4a= 4'b1010;#4a= 4'b0101;#20$stop;
end


从仿真的二进制结果来看,将a定义成无符号数的时候,如果被赋值寄存器的位宽较大,则向高位补0,较小,则直接截取对应低位。与前面的想法一样。

1.2.2.有符号寄存器传数据

这里只将上面的a和b定义成有符号数即可。

reg  signed  [3:0]  a;
reg signed  [6:0]  b;    //当a的输入也是有符号数的时候,b可以不定义成无符号数。//因为默认向高位补符号位

为什么b也定义成有符号数:因为如果不这样,当写代码的时候a的输入用二进制表示的,会导致不能向高位补符号位,而是补0,这里把b也定义成有符号数,就是为了实现当a的位宽不够时,能够向高位补符号位。

同样看二进制结果,因为a和b被定义成了有符号寄存器,所以最高位为符号位,则在赋值的时候,向高位补符号位,而不是0。

综上:
所有的常数赋值,正数的数据都是直接转换成二进制,然后向高位补符号位,最后存入寄存器中,而负数数据都是转换成二进制补码,然后向高位补符号位,存入寄存器中的。

1.3.总结

对各种传输数据进行一个总结:

有符号常量就是指用10进制、16进制、8进制的表示,无符号常量就是指2进制表示。
因此我们在进行有符号运算的时候,一定要保证输入输出都定义成有符号数,同时也要注意输入寄存器的输入端的数据是有符号还是无符号,以及他们的位宽是多少。

2.运算

2.1.加法

2.1.1.无符号

代码:

reg [3:0] a,b;
reg [4:0] c;    //结果多写一位,主要是防止溢出。
always@(posedge clk)
beginc <=  a + b;
end


仿真正确。

2.1.2.有符号

代码:

reg signed [3:0] a,b;  //实际上,前面已经说了,无论输入是否定义成有符号数,其存入的数据都是补码。但是加法器的输出必须被定义成有符号数,这样才能正确传递符号位。
reg signed [4:0] c;    //结果多写一位,主要是防止溢出。
always@(posedge clk)
beginc <=  a + b;
end

对于输入的数据a和b(位宽width),我们需要保证输入数据的绝对值最大不超过width-1也就是范围在-2(width-1)~2(width-1)
测试:

a = 4'd4;
b = -4'd3;
#20
a = -4'd4;
b = 4'd2;
#20


验证正确。
注:
如果输入或输出均不定义成有符号数

其运算出的结果应当是这样的,综合之后使用的是5位加法器,其最终结果如果转换成有符号,为-15,无符号,则为17 。

因此在设计的时候,还是全部带上有符号数,这样也方便后续数据传递。

2.2.减法

2.2.1.无符号

代码:

reg [3:0] a,b;
reg [4:0] c;    //结果多写一位,主要是防止溢出。
always@(posedge clk)
beginc <=  a - b;
end

测试

注:
无符号肯定要保证输入输出都是正数才行,也就是被减数必须大于等于减数。

2.2.2.有符号

代码:

reg signed [3:0] a,b;
reg signed  [4:0] c;    //结果多写一位,主要是防止溢出。
always@(posedge clk)
beginc <=  a - b;
end


这里看一下硬件电路的实现:
代码:

module top(input clk,input signed [3:0] a,input signed [3:0] b,output  [4:0] o
);
reg signed [3:0] a0,b0;  //实际上,前面已经说了,无论输入是否定义成有符号数,其存入的数据都是补码。但是加法器的输出必须被定义成有符号数,这样才能正确传递符号位。
reg signed [4:0] c;    //结果多写一位,主要是防止溢出。
always@(posedge clk)
begina0 <= a;b0 <= b;c <=  a0 - b0;
end
assign o = c;
endmodule


结果如上图,是通过加法器来实现减法运算的,通过综合出的电路可知道
是一个6位的加法器
被减数和减数经过一位拼接送入加法器A端,即

A[5:0] = {a0[3],a0,1'b1}
B[5:0] = {~{a0[3],a0},1'b1}

然后加法器输出舍弃最低位,输出到下一级寄存器,即

c[4:0] = OUT[5:1]

这里手算一下:

2.3.除法

2.3.1.无符号

除法比较简单,结果就是两数相除之后取整

reg  [3:0] a,b;
reg   [3:0] c;    //结果不需要扩展,因为除完只会变小,不会变大
always@(posedge clk)
beginc <=  a / b;
end

module top(input clk,input signed [3:0] a,input signed [3:0] b,output  [3:0] o
);
reg  [3:0] a0,b0;
reg  [3:0] c;
always@(posedge clk)
begina0 <= a;b0 <= b;c <=  a0 / b0;
end
assign o = c;
endmodule

2.3.2.有符号

reg  signed [3:0] a,b;
reg  signed [4:0] c;    //结果多写一位,主要是防止溢出。
always@(posedge clk)
beginc <=  a / b;
end


可见其结果的符号与两个操作数有关,也就是符合我们正常的除法运算规律。

module top(input clk,input signed [3:0] a,input signed [3:0] b,output signed [3:0] o
);
reg  signed [3:0] a0,b0;
reg  signed [3:0] c;
always@(posedge clk)
begina0 <= a;b0 <= b;c <=  a0 / b0;
endassign o = c;endmodule


查看最后综合出来的电路,有符号和无符号的电路都是一样的,但是其内部不知道是怎样算的,不过功能是正确的。

2.3.3.实现除法的四舍五入

  1. 移位实现
    有时候在硬件算法中,实现最多的就是除以2n 而且大部分是要求四舍五入。
    如果要求四舍五入,假设被除数为a,我们要除以22,结果设为c;
wire signed [3:0] a;
wire signed [3:0] c;
assign c = (a + 2) >>> 2;//加上2的n-1次方再进行移位

  1. 除法实现

—待更新—

2.4乘法

2.4.1.无符号

reg   [3:0] a,b;
reg  [7:0] c;
always@(posedge clk)
beginc <=  a * b ;
end

2.4.2.有符号

reg  signed [3:0] a,b;
reg  signed [7:0] c;      //输出的位宽只需要大于等于a和b的位宽之和减一就可以了
always@(posedge clk)
beginc <=  a * b ;
end

易错

在设计的时候尝尝会有常数参与运算,当进行有符号运算时,比如

wire signed [3:0] a;
wire signed [7:0] c;
assign c = a + 3'd2;

如果输入端有负数,这就会导致结果出现错误,如下图的第三组和第五组。

如果将表达式写成

assign c = a + 2;

或改成

wire signed [2:0] data;
assign data = 2'd2;
assign c = a + data;

结果:

这样才是正确的。
所以在设计的时候,需要注意这个点。

Verilog有符号和无符号运算设计分析相关推荐

  1. Verilog -- 有符号与无符号的加法和乘法运算

    目录 Verilog中有符号与无符号的加法和乘法运算 无符号乘法和加法 有符号乘法和加法 有符号和无符号运算 参考: https://blog.csdn.net/vivid117/article/de ...

  2. 【C语言进阶深度学习记录】二 有符号与无符号

    今天学习C语言中的有符号与无符号 文章目录 1 计算机中的符号位 1.1 有符号数的表示法 1.2 无符号数的表示法 1.3 signed 和 unsigned 2 实验-当有符号数与无符号数进行运算 ...

  3. 深入理解计算机系统(2.4)---C语言的有符号与无符号、二进制整数的扩展与截断...

    开篇请各位猿友允许LZ啰嗦几句,最近一直在写计算机系统原理这系列文章,也已经下定决心要把这本书的内容写完.主要目的其实是为了巩固LZ的理解,另外也想把这些内容分享给猿友们,毕竟LZ觉得这些内容对程序猿 ...

  4. C语言中的有符号与无符号(挖坑)

    1.计算机中的符号位,计算机如何表示正数和负数? 数据类型的最高位用于标识数据的符号 最高位为1,表明这个数为负数 最高位为0,表明这个数为正数 2.有符号数的表示法 在计算机内部用补码表示有符号数 ...

  5. C语言之(有关%d和%u的有关内容,输出方法)(有符号和无符号在内存中的存储情况)(整形无符号数和有符号数是如何进行计算的,整形无符号数和有符号数在循环中的应用举例)

    在C语言的课本中,我们常见的是%u,但我们平时在写代码常用的确是%d,它们二者之间有什么区别呢? 表示形式上 %u表示输入输出格式说明符,是按照unsigned int(无符号十进制数据)格式输入或输 ...

  6. linux有符号和无符号,汇编中有符号与无符号数的区分

    转载自:http://hi.baidu.com/asmsky/blog/item/7290d20076cab6da277fb5b8.html 一.只有一个标准! 在汇编语言层面,声明变量的时候,没有 ...

  7. c语言无符号扩展,深入理解计算机系统(2.4)---C语言的有符号与无符号、二进制整数的扩展与截断...

    开篇请各位猿友允许LZ啰嗦几句,最近一直在写计算机系统原理这系列文章,也已经下定决心要把这本书的内容写完.主要目的其实是为了巩固LZ的理解,另外也想把这些内容分享给猿友们,毕竟LZ觉得这些内容对程序猿 ...

  8. C++有符号和无符号整型

    首先有符号正整型和无符号整型的范围分别如下: 有符号 当表示正数时,最高位为符号位(符号位为0),最大的正数是 0111 1111 1111 1111 1111 1111 1111 1111 即2^3 ...

  9. c语言中的无符号字符指什么,深入分析C语言中的有符号和无符号

    有符号和无符号指的是数字那么有符号和无符号的区别是什么,我们这里一起来看在C语言中的有符号和无符号分析吧,希望这篇文章能够对各位有用. 就像我们必须决定某个量使用整数还是实数,使用多大的范围数一样,我 ...

最新文章

  1. webstorm / intellij IDEA / phpstorm license server 激活链接 (过期留言更新)
  2. Bmob云IM实现头像更换并存入Bmob云数据库中(1.拍照替换,2.相册选择)
  3. Silverlight与Flash的技术比较(图)
  4. .NET Core 2.0 特性介绍和使用指南
  5. Java Fork / Join进行并行编程
  6. mac 内核调试环境搭建
  7. 开奖|1024中奖名单公布以及Postman资料分享
  8. Map 参数按Key重新排序,重组成String
  9. wordpress专属线报网主题模板
  10. web渗透测试入门01
  11. macbook桌面的文件突然消失的解决方案
  12. 带宽与码元的关系_比特率与带宽什么关系
  13. openEuler 的安装及内核编译
  14. matlab直观数据处理,霍尔效应实验的MATLAB数据处理
  15. 如何利用python准确预测双色球开奖结果
  16. DAB-Deformable-DETR代码学习记录之模型构建
  17. Linux就这个范儿 第13章 打通任督二脉
  18. ios系统下input边框有默认阴影
  19. Android未找到分区,Android System分区文件丢失分析
  20. SQL Server数据库通过身份证号获取年龄段分布

热门文章

  1. sugar orm使用介绍
  2. 用MFC制作一个图片浏览器
  3. 长虹50q1n 显示服务器端异常,长虹智能电视显示网络配置错误解决方法分享
  4. matlab2017 区别,有什么区别。*和*在matlab中
  5. 什么是js?js的基本使用
  6. 快递代收告别野蛮生长 “蓝店模式”正当时
  7. 国务院办公厅发文,电子签名、电子印章再迎利好
  8. 陌陌充值显示服务器异常,陌陌充值会员总是失败 陌陌会员充值未到账解决办法...
  9. 如何更改图片分辨率?在线修改分辨率的方法分享
  10. Leetcode 804. 唯一摩尔斯密码词