有符号数的计算在 Verilog 中是一个很重要的问题(也很容易会被忽视),在使用 Verilog 语言编写 FIR 滤波器时,需要涉及到有符号数的加法和乘法,在之前的程序中我把所有的输入输出和中间信号都定义成有符号数,这样在计算时没有出现问题(实际在之前的程序中遇到了有符号和无符号数的问题,最后滤波结果不对,博客的程序是已经改正过的),下面实际试验一下 Verilog 的乘法问题;

1. 编写程序测试无符号数和有符号数的乘法

编写程序如下,其中,乘法的两个乘数分别是无符号、有符号的四种组合,输出的积也是分为无符号和有符号,共计 8 种可能;

module signed_test(

input [7:0] data_in_unsigned_1,

input [7:0] data_in_unsigned_2,

input signed [7:0] data_in_signed_1,

input signed [7:0] data_in_signed_2,

output [15:0] data_out_000,

output [15:0] data_out_001,

output [15:0] data_out_010,

output [15:0] data_out_011,

output signed [15:0] data_out_100,

output signed [15:0] data_out_101,

output signed [15:0] data_out_110,

output signed [15:0] data_out_111

);

//无符号 = 无符号 * 无符号

assign data_out_000 = data_in_unsigned_1 * data_in_unsigned_2;

//无符号 = 无符号 * 有符号

assign data_out_001 = data_in_unsigned_1 * data_in_signed_2;

//无符号 = 有符号 * 无符号

assign data_out_010 = data_in_signed_1 * data_in_unsigned_2;

//无符号 = 有符号 * 有符号

assign data_out_011 = data_in_signed_1 * data_in_signed_2;

//有符号 = 无符号 * 无符号

assign data_out_100 = data_in_unsigned_1 * data_in_unsigned_2;

//有符号 = 无符号 * 有符号

assign data_out_101 = data_in_unsigned_1 * data_in_signed_2;

//有符号 = 有符号 * 无符号

assign data_out_110 = data_in_signed_1 * data_in_unsigned_2;

//有符号 = 有符号 * 有符号

assign data_out_111 = data_in_signed_1 * data_in_signed_2;

endmodule

生成的 RTL 图如下:

可以看到,输出的积和符号无关,有符号数和无符号数实际上是同一个数,只看我们怎么定义它,比如乘积是 16 位的二进制 16’b1100_0000_0000_0011,当我们认为它是无符号数是,最高位的 1 就不是符号位,而是 2^15(2的15次方),这样这个数代表的十进制是 2^15 + 2^14 + 2^1 + 2^0 = 49155;

如果把 16 位的二进制 16’b1100_0000_0000_0011 当成是一个有符号数来看,那么最高位是符号位,且剩下的数据时原来的数据二进制表示后取反再加1(补码表示),要计算它对应的十进制数

(1)先去掉符号位,保留剩下的 15-bit 的 100_0000_0000_0011;

(2)把 100_0000_0000_0011 取反,得到 011_1111_1111_1100;

(3)把 011_1111_1111_1100 的最低位 + 1,得到 011_1111_1111_1101;

(4)011_1111_1111_1101 按照无符号数换算成十进制是 16381;

(5)把最高位符号位加上,0代表正数,1代表负数,所以最后换算是 -16831;

Windows 计算器默认最高位是符号位;

测试数据如下:

initial begin

data_in_unsigned_1 = 8'hff; //255

data_in_unsigned_2 = 8'hf0; //240

data_in_signed_1 = 8'hff; //-1

data_in_signed_2 = 8'hf0; //-16

#200;

data_in_unsigned_1 = 8'hff; //255

data_in_unsigned_2 = 8'h0f; //15

data_in_signed_1 = 8'hff; //-1

data_in_signed_2 = 8'h0f; //15

#200;

data_in_unsigned_1 = 8'd127; //127

data_in_unsigned_2 = 8'd15; //15

data_in_signed_1 = -8'sd127; //-127,十进制有符号数赋值,必须要用 sd 表示

data_in_signed_2 = -8'sd15; //-15

#200;

data_in_unsigned_1 = 8'd128; //128

data_in_unsigned_2 = 8'd15; //15

data_in_signed_1 = -8'sd128; //-128

data_in_signed_2 = -8'sd15; //-15

#200;

data_in_unsigned_1 = 8'd127; //127

data_in_unsigned_2 = 8'd15; //15

data_in_signed_1 = -8'sd127; //-127

data_in_signed_2 = 8'sd15; //15

#200;

data_in_unsigned_1 = 8'd128; //128

data_in_unsigned_2 = 8'd15; //15

data_in_signed_1 = -8'sd128; //-128

data_in_signed_2 = 8'sd15; //15

#200;

data_in_unsigned_1 = 8'd127; //127

data_in_unsigned_2 = 8'd15; //15

data_in_signed_1 = 8'sd127; //127

data_in_signed_2 = -8'sd15; //-15

#200;

data_in_unsigned_1 = 8'd127; //127

data_in_unsigned_2 = 8'd15; //15

data_in_signed_1 = 8'sd127; //127

data_in_signed_2 = 8'sd15; //15

#200;

$stop;

end

2. 仿真分析

计算的结果仿真如下:

对上图分析:

(1)在 0 ~ 400 ns,仿真中使用十六进制赋值相同的十六进制数据给乘数,让乘数分别以无符号数和有符号数进行读取,可以看到对 8’hff(对应二进制 8’b1111_1111)以无符号数读取时是按照 原码 读取,对应十进制 255,以有符号数读取时是按照补码读取,按照上文所说的去掉符号位后取反、加1再计算十进制得 -1;

(2)直接赋值十进制数据,乘数在以无符号数读取时时按照原码读取,127就对应 8 位二进制数 8’b0111_1111,十进制 128 就对应 8 位二进制 8’b1000_0000;而以有符号数读取的时候是会直接转换为补码形式,如 -127,先去掉符号位是 127,对应 7 位二进制数 7’b111_1111,取反为 7’b000_0000,加 1 为 7’b000_0001,将符号位补回到最高位为 8’b1000_0001;对于 -128 的表示比较特殊,8-bit的二进制数最高位是符号位,表示正负,剩下的 7-bit 能够表示的数的范围是 0 ~ 127,前面加上 ± 就能表示 -127 ~ 127,其中有 2 个数很特殊就是 8’b0000_0000 和 8’b1000_0000,按照上面会出现 +0 和 -0,为了区分出这两个数,前人定义 8’b0000_0000 表示 0,而 8’b1000_0000 表示 -128,这样不仅能区分开两个数,还多表示了一个数 -128(整个计算机体系通用,其他位数时类似表示一个负数);

(3)实际上,观察下图数据可以发现,只有data_out_000 和 data_out_111 的数据时全部计算正确的,这也符合常理:

无符号 * 无符号 = 无符号(只有0和正数);

有符号 * 有符号 = 有符号(0,可正可负);

其它的计算为什么会出错呢?实际上这里遵循一个原则:

如果表达式中有一个无符号数,则所有的操作数都会被强行转换为无符号数;

这样也就解释了 0 ~ 400 ns 时的 data_out_001 和 data_out_010 的计算结果和 data_out_000 完全一致,它们都是把赋值的 8 位十六进制数当做无符号数计算的(这里不存在十进制到二进制原码、补码换算的问题,因为给的是十六进制);

当后面设计输入输出时,如果是有符号,那么将相关计算的输入/输出和中间量都显式的用 signed 定义;

3. 有符号数乘法的另一种计算

前面说的计算时将涉及到的相关量全部定义为有符号数是一种计算方法,此外,通常情况下可能会定义的无符号数,但是实际传入的是有符号数,比如下面的输入和输出都没有指定成 signed 有符号数,计算时默认是按照无符号数计算(实际上我感觉是把读取到的 8 位二进制数当做原码去算),此时若外部传入的数据实际上是有符号数(比如 FIR 滤波器传入了正负均有的待滤波信号),那么需要对符号位进行扩展来计算乘法和加法;

module signed_test_2(

input [7:0] data_in_1,

input [7:0] data_in_2,

output [15:0] data_out_1,

output [15:0] data_out_2

);

对于乘法,需要扩展符号位 到 和积的位数相等,比如乘数a为 N-bit,乘数 b 为M-bit,两个相乘得到 N+M 位数据,此时需要对 a 扩展 M-bit 到 N+M 位,对 b 扩展 N-bit 到 N+M 位;

下面,使用 位拼接符 { } 来做演示,位拼接符可以按照二进制的位来进行高低位的拼接,假设 data_in_1= 8’b1000_0011,对于 {{8{data_in_1[7]}},data_in_1} 可以这样理解:

(1)先看 8{data_in_1[7]},表示取出 8-bit 数据 data_in_1 的最高位 data_in_1[7],重复 8 次,相当于

{ data_in_1[7], data_in_1[7], data_in_1[7], data_in_1[7], data_in_1[7], data_in_1[7], data_in_1[7], data_in_1[7] },

即高位扩展 8-bit 的 1;

(2){{8{data_in_1[7]}},data_in_1} 相当于在 data_in_1 的前面补上 8 个 data_in_1[7],即 结果为 16-bit 的 16’b1111_1111_1000_0011;

//不做符号位扩展,直接相乘

assign data_out_1 = data_in_1 * data_in_2;

//做符号位扩展,再相乘

assign data_out_2 = {{8{data_in_1[7]}},data_in_1} * {{8{data_in_2[7]}},data_in_2};

仿真测试数据如下,1 处用十六进制给出数据,2 处用有符号的十进制赋值,3 处是为了和 2 处对比,看最后赋值是否一样(看到有博客说 3 的赋值是错的,所以测试一下);

仿真结果如下,可以看到上图 2 处和 3 处的赋值在仿真时是同样的数据,把所有数据都用有符号的十进制数显示(右键数据 Radix -> Signed Decimal);

可以看到,data_out_1的结果是错的(没有补符号位),data_out_2的结果是对的(补符号位);

对有符号数的加法,同样的,要么相关的运算全部定义成有符号数,要么进行符号位的扩展,对于加法操作,只需要每个被加数扩展 1 位符号位即可;

除此之外,还可以调用乘法器的 IP 来代替 乘法符号 *,或者加法器的 IP 来代替 加法符号 +,在 IP 核中配置输入输出为有符号数即可。

MATLAB 与 FPGA无线通信、图像处理、数字信号处理系列 汇总

verilog幂次方_Verilog学习笔记——有符号数的乘法和加法相关推荐

  1. Verilog学习笔记——有符号数的乘法和加法

    有符号数的计算在 Verilog 中是一个很重要的问题(也很容易会被忽视),在使用 Verilog 语言编写 FIR 滤波器时,需要涉及到有符号数的加法和乘法,在之前的程序中我把所有的输入输出和中间信 ...

  2. C语言学习笔记-有符号数和无符号数相加的问题

    Q: 下面的代码输出是什么,为什么? void test(void) {unsigned int a = 6;int b = -10;((a + b) > 0) ? printf("a ...

  3. Latex学习笔记——常用符号的输入

    我的Latex学习笔记 箭头符号 集合符号 数学运算符号 箭头符号 符号 代码 右箭头→\rightarrow→ \rightarrow 左箭头←\leftarrow← \leftarrow 右推出符 ...

  4. 编译原理学习笔记20——符号表

    编译原理学习笔记20--符号表 20.1 符号表的组织与操作 20.2 符号表的内容 20.3 利用符号表分析名字的作用域 20.1 符号表的组织与操作 符号表 符号表的作用与组织 符号表的整理和查找 ...

  5. verilog幂次方_2的幂次方均值滤波的FPGA实现

    2 的幂次方点均值滤波 模块 CHNL_DA TA_A VERAGE 主要实现对 AD 采集数据的均值滤波,该模块目前支持 最大滤波点数为 128 ,原则上可以无限大,主要取决于模块中用于缓存先前采样 ...

  6. 学习笔记(符号扩展指令:SXTB和SXTH)

    前言 这些笔记主要是记录自己在学习CM3汇编中的一些问题(因为我们老师上stm32的嵌入式课是从基础汇编开始讲的,CM3汇编在网上能查到的例子讲解有点少,哭!),其中可能借鉴过网上一些大佬的文章,如果 ...

  7. 二进制补码计算——有符号数的乘法

    位数问题 a_width位的a,乘以B_width位的b,结果的位数是A_width + B_width. 定点小数问题 小数位数等于a的小数位数,加上b的小数位数之和. 补码相乘问题 补码 * 补码 ...

  8. verilog 移位运算符 说明_Verilog学习笔记基本语法篇(二)·········运算符...

    Verilog HDL的语言的运算符的范围很广,按照其功能大概可以分为以下几类: (1)算术运算符+,-,*,/,% 优先顺序 !~ *  /   % +    - <<    >& ...

  9. matlab学习笔记之——符号函数应该怎么作图?

    如何用符号函数来做图 最近在学习使用符号函数,在此过程中遇到了许多问题,尤其是我不太清楚应该用什么函数来画符号函数的图.之后我问了我的老师,也看了很多博文,总结了一些符号函数画图的方法,希望可以帮到大 ...

  10. 《C语言深度解剖》学习笔记之符号

    第2章 符号 1.注释符号 编译器会将注释剔除,用空格代替原来的注释 y=x /* p; 编译器提示出错的原因:实际上,编译器会把"/*"当作一段注释的开始,直到出现"* ...

最新文章

  1. 多线程(三)之ReentrantLock源码解析
  2. 关于 Angular 应用 tsconfig.json 中的 target 属性
  3. linux mysql定时增量备份_Mysql 日常备份和增量备份脚本(Linux)
  4. 三、Beautiful Soup解析库
  5. 深入解析 Go 中 Slice 底层实现
  6. Python:Python程序设计思维、计算生态、用户体验、程序设计模式
  7. gcc/g++版本切换与降级
  8. (Access denied for user 'root'@'localhost' (using password: NO))
  9. 三人表决器c语言实验报告,项目一:三人表决器的逻辑电路设计与制作
  10. 脚本自动化互相阅读文章教程
  11. 咸鱼前端—html常用标签
  12. Lua调试:getinfo详解
  13. 智能营销获客引流-入门-宁波慧客科技有限公司
  14. java画直方图_算法练习(11):Java直方图的绘制(1.1.32)
  15. Cesium资料汇总
  16. 牛顿法求解柯西分布参数的最大似然估计(P055)
  17. Win7下使用VirtualBox虚拟机安装OS X 10.9 Mavericks
  18. 【推荐系统】DUPN:Deep User Perception Network
  19. 基于QEMU-aarch64学习UEFI(EDK2)-1环境搭建
  20. oracle ola_访谈和书摘:Ola Bini,“实用的JRuby on Rails Web 2.0项目”

热门文章

  1. 从毛坯房到精装修,阿里云企业IT治理样板间助力云上管控和治理
  2. 【优化算法】寄生-捕食算法(PPA)【含Matlab源码 1801期】
  3. 毕设题目:Matlab疾病识别与分类
  4. 【表盘识别】基于matlab Hough变换指针式仪表识别(倾斜矫正)【含Matlab源码 1058期】
  5. 【优化算法】蝗虫优化算法(GOA)【含Matlab源码 936期】
  6. 【车间调度】基于matlab NSGA-2算法求解多目标车间调度问题【含Matlab源码 893期】
  7. 计算机应用技术与英语相关性,浅析计算机应用的技术专业的计算机专业英语的教学改进.doc...
  8. ai人工智能最新相关消息_我如何了解最新的AI研究
  9. linux清除占用端口,Linux中解除端口占用的方法
  10. Ubuntu16.04镜像下载