基于FPGA的数字信号处理(5)--Signed的本质和作用

前言

Verilog中的signed是一个很多人用不好,或者说不太愿意用的一个语法。因为不熟悉它的机制,所以经常会导致运算结果莫名奇妙地出错。其实了解了signed以后,很多时候用起来还是挺方便的。

signed的使用方法主要有两种,其中一种是定义一个有符号数变量,例如:

reg 		[3:0] us_a;	//定义无符号数us_areg signed 	[3:0] s_a;	//定义有符号数s_a

这样定义以后,即使是将同一个值 1111 分别赋值给us_a和s_a,它们所表达的数也不同了,无符号数us_a是 15 ,而有符号数 s_a则是 -1 。但是,这个-1和15是人类或者说工具站在如何解释符号位的角度上解读的,在电路底层,它们的值都是一样的1111。所以signed相当于只是规定了如何来解读一个数的最高有效位。

signed还有一个用法是强制转换,把一个无符号数转换为有符号数类型。例如:

reg [7:0] regA;regA = $signed(-4);

这两个是signed的一般语法,而它的本质在我看来只有两点:

  1. 当计算不产生溢出的时候,signed只影响如何将2进制数解读为10进制数;
  2. 当计算产生溢出的时候,signed影响的是如何对高位扩展–无符号数高位扩展0,而有符号数则高位扩展符号位。

计算结果不溢出

来看下面的代码:

`timescale 1ns/1ns
module tb_test();reg 		[3:0] us_a,us_b;	//无符号加数a、加数b
reg signed	[3:0] s_a,s_b;		//有符号加数a、加数b
reg 		[3:0] us_sum;		//无符号和
reg signed	[3:0] s_sum;		//有符号和initial beginus_a = 4'b1001;		//9us_b = 4'b0001;		//1s_a  = 4'b1001;		//-7s_b  = 4'b0001;		//3#10	us_sum = us_a + us_b;	//9+1s_sum  = us_a + us_b;	//9+1
#10	us_sum = us_a + s_b;	//9+1s_sum  = us_a + s_b;	//9+1
#10us_sum = s_a + us_b;	//-7+1s_sum  = s_a + us_b;	//-7+1
#10	us_sum = s_a + s_b;		//-7+1s_sum  = s_a + s_b;		//-7+1end	endmodule

加数a和加数b分别定义成有符号数和无符号数,加数的和也分别定义成有符号数和无符号数,这样一共有2×2×2 =8 种组合。

给a赋值1001(即无符号数 9/有符号数 -7);给b赋值0001(即无符号数 1/有符号数 1),可以看到他们的计算结果都是一样的1010(即无符号数 10/有符号数 -6):

30d65ce02d30b406d9486035e9cf7eba_202404281141671_raw=true

这个结果和预期是一致的。两个二进制数的加法,因为和没有溢出,所以不管是定义成有符号的还是无符号的,它们的结果肯定都一样。原因就是前面说的第一点:

当计算不产生溢出的时候,signed只影响如何将2进制数解读为10进制数;


计算结果溢出

现在将上面的代码改一下,把和改成5位,使他们的结果需要强制溢出(扩宽)到5位,如下:

reg 		[4:0] us_sum;		//无符号和
reg signed	[4:0] s_sum;		//有符号和

仿真结果已经和上面不同了:

image-20240403115924965

前面3种计算的结果仍然没变,但是第4种计算(即两个有符号数相加)的结果从0_1010变成了1_1010,也就是说最高位的符号位扩展了一位。有符号数和无符号数的运算有一个规则:

不管运算结果是有符号数还是无符号数,只要右边运算式中有一个是无符号数,那么运算结果就是无符号数。

也就是说

  • 无符号数 + 无符号数 的结果是 无符号数
  • 有符号数 + 无符号数 的结果是 无符号数
  • 有符号数 + 有符号数 的结果是 有符号数

因为前3种运算中都有 无符号数 参与,所以不管 和 是定义成有符号数还是无符号数 ,它们的结果都是 无符号数,所以会在高位拓展0;而第4种运算则是只有 有符号数 参与,所以它们的结果都会在高位扩展符号位(即1)。

再来看一个例子:

`timescale 1ns/1ns
module tb_test();reg signed 	[1:0] s_a ;
reg 		[1:0] us_a;reg 		[3:0] s_s_a ;
reg 		[3:0] us_us_a;
reg 		[3:0] inv_s_a ;
reg 		[3:0] inv_us_a;initial begins_a  = -1;us_a = -1;s_s_a  = s_a;us_us_a = us_a;inv_s_a  = - s_a;inv_us_a = - us_a;
end	endmodule

仿真结果是这样的:

image-20240403122129280

  • s_a和us_a的赋值都是 -1,实际就是32‘b111···111,位宽只有2位,所以被截断到2’b11。
  • s_s_a是s_a的高位扩展,因为s_a是有符号数,所以高位扩展补符号位1,就从11变成1111;us_us_a是us_a的高位扩展,因为us_a是无符号数,所以高位扩展补0,从从11变成0011。
  • inv_s_a是s_a的数值的相反数,即补码按位取反后再加1,s_a先从11拓展到1111,再取反加1就变成了0001。从十进制的角度看 s_a(11)是 -1,而 inv_s_a是0001(1),符合相反数的关系。
  • inv_us_a是us_a的数值的相反数,即补码按位取反后再加1,us_a先从11拓展到0011,再取反加1就变成了1101。从十进制的角度看 us_a(11)是 3,而 inv_s_a是1101(-3),也符合相反数的关系。

signed的用法

自动扩展位宽

将一个数的位宽扩展,如果不使用signed语法,则首先需要判断其最高位是否为1,如果是则说明是负数,高位需要扩展1;如果不是则说明是正数,那么高位需要扩展0(正数也可直接赋值,因为不使用signed的话工具会自动扩展0),例如:

module test 
(input 		[3 : 0]	in,output 	reg	[4 : 0]	out
);always@(*)
beginif(~in)					//in是正数out = in;			//直接赋值,等价于高位补0 out = {1'b0,in};else					//in是负数out = {1'b1,in};	//高位保护1
endendmodule

如果使用signed就可以直接赋值了,例如:

module test 
(input	signed	[3 : 0]	in,output	signed	[4 : 0]	out
);assign out = in;			//直接赋值,等价于自动判断高位补符号位endmodule

简化运算

考虑两个4bits数的加法,为了防止结果溢出,把和的位宽设定成5bits。假如两个数分别为0101(5)和1100(-4),则二者的和应该为 (5 - 4 = 1),但实际结果为 0101 + 1100 = 10001(-15),二者显然对不上。

在不使用signed的情况下,我们需要做的是把两个加数分别扩展符号位,然后再相加。0101扩展到00101 ,1100扩展到11100,这样二者相加为 01001 + 11100 = 10_0001,因为结果只有5bits,所以会被截断到00001,也就是1,这样结果就能对上了。代码是这样写的:

module test 
(input	[3 : 0]	in1,in2,output	[4 : 0]	out
);assign out = {in1[3],in1} + {in2[3],in2};	//手动补充高位的符号位endmodule

而如果将加数都定义成signed类型,则可以直接相加,综合工具会自动在高位扩展符号位来完成计算:

module test 
(input	signed [3 : 0]	in1,in2,output	signed [4 : 0]	out
);assign out = in1 + in2;	//手动补充高位的符号位endmodule

移位操作

不使用signed的情况下,右移首先需要根据最高位的符号来判断这个数是正数还是负数,正数右移需要高位补0,而负数右移则需要高位补1。

比如 -4 右移一位的结果应该是 -2,即4’b1100 >>1 的结果应该是4’b1110,如果高位补0则成了4‘b0110(6),结果就明显错了。所以代码要这么写:

module test 
(input		[3 : 0]	in,output	reg	[3 : 0]	out
);//右移两位
always@(*)
beginout = in >> 2;			//首先右移两位,先默认在高位补0,后面再根据判断来修改if(in[3])				//in是负数out [3:2] = 2'b11;	//高位补的0替换成1
endendmodule

如果将数据定义成了signed类型,则不需要判断正负,可以直接使用 算术右移预算符 >>> 来做移位。算术右移时,高位补充的是符号位。算术右移需要和signed一起使用,因为无符号数的算术右移补充的是0。代码:

module test 
(input	signed [3 : 0]	in,output	signed [3 : 0]	out
);//右移两位
assign out = in >>> 2;	//算术右移高位会自动补符号位endmodule

比较

在不定义signed的情况下,只要比较中出现了负数,那么比较的结果就不能直接对比了,直接对比很容易出错,比如:

`timescale 1ns/1ns
module tb_test();reg	[3:0] a,b;initial begina = 4'd1;	//1b = -4'd3;	//-3if(a > b)$display("a > -3");else$display("a < -3");
end	endmodule

这段打印的结果居然是:

a < -3

也就是说 1居然小于 -3 ?结果明显不对。如果不使用signed来定义变量,那么在判断两个数的大小时,首先需要判断符号位,然后才是判断剩余数的大小。这样写出来的代码非常麻烦。如果把两个数都定义成signed类型,那就可以直接对比了:

module test 
(input	signed [3:0]	in1,in2,output					out
);//当in1大于in2时输出1;其他时候输出0;不考虑二者相等的情况
assign out = (in1 > in2) ? 1'b1 : 1'b0;endmodule

测试结果符合预期:

image-20240403172322160

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/661568.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CentOS 环境 JDK 安装

1.jdk下载 &#xff08;1&#xff09;官网Java jdk 历史版本 http://www.oracle.com/technetwork/java/javase/archive-139210.html 选择以下x86 64位版本 &#xff08;2&#xff09;国内 编程宝库 java jdk镜像 地址&#xff1a; http://www.codebaoku.com/jdk/jdk-index…

# 从浅入深 学习 SpringCloud 微服务架构(七)Hystrix(3)

从浅入深 学习 SpringCloud 微服务架构&#xff08;七&#xff09;Hystrix&#xff08;3&#xff09; 一、hystrix&#xff1a;通过 Actuator 获取 hystrix 的监控数据 1、Hystrix 的监控平台介绍&#xff1a; 1&#xff09;Hystrix 除了实现容错功能&#xff0c;Hystrix 还…

RUSLE模型:一种新的土壤侵蚀强度计算方法

随着全球土地资源的日益枯竭和环境污染的加剧&#xff0c;对土壤侵蚀的研究变得愈发重要。土壤侵蚀不仅对农业生产和生态环境造成了严重影响&#xff0c;还直接威胁着人类的生存环境。因此&#xff0c;寻找一种精准可靠的土壤侵蚀强度计算方法具有重要意义。 ​ 近年来&#xf…

企业计算机服务器中了rmallox勒索病毒怎么处理,rmallox勒索病毒处理建议

在网络技术不断发展的时代&#xff0c;网络在企业中的应用广泛&#xff0c;可以为企业带来更多的便利&#xff0c;大大提升了企业的生产效率&#xff0c;但网络作为虚拟世界&#xff0c;在为企业提供便利的同时&#xff0c;也为企业数据安全带来严重威胁。近期&#xff0c;云天…

区块链技术:DAPP开发

随着科技的飞速发展&#xff0c;区块链技术逐渐渗透到各个领域&#xff0c;其中DAPP&#xff08;去中心化应用&#xff09;的发展尤为引人注目。作为一种新型的应用程序&#xff0c;DAPP正在重塑未来商业生态&#xff0c;其潜力无可估量。 一、DAPP的定义和特点 DAPP是指基于…

【MySQL精炼宝库】数据库的约束 | 表的设计 | 聚合查询 | 联合查询

目录 一、数据库约束 1.1 约束类型&#xff1a; 1.2 案例演示&#xff1a; 二、表的设计 2.1 一对一: 2.2 一对多: 2.3 多对多: 2.4 内容小结&#xff1a; 三、新增 四、查询 4.1 聚合查询&#xff1a; 4.1.1 聚合函数&#xff1a; 4.1.2 GROUP BY子句&#xff1a…

【电路笔记】-RC振荡器电路

RC振荡器电路 文章目录 RC振荡器电路1、概述2、RC 相移网络3、基本RC振荡器电路4、运算放大器RC振荡器5、运算放大器相位滞后RC振荡器电路6、RC振荡器示例11、概述 RC 振荡器使用放大器和 RC 反馈网络的组合,由于级之间的相移而产生输出振荡。 当单级晶体管放大器作为共发射…

pycharm中文件误删或者误操作,怎么恢复

恢复pycharm中文件误删或者误操作 恢复方法&#xff1a;1.xxxx.py文件误删2.xxxx.py文件内操作 在日常学习或练手时总会有一些迷之操作&#xff0c;一些文件被误删或者一些文件越改越糟&#xff0c;想要恢复操作之前的文件。 恢复方法&#xff1a; 1.选则误删文件的上级目录&…

【yolov8】yolov8剪枝训练流程

yolov8剪枝训练流程 流程&#xff1a; 约束剪枝微调 一、正常训练 yolo train model./weights/yolov8s.pt datayolo_bvn.yaml epochs100 ampFalse projectprun nametrain二、约束训练 2.1 修改YOLOv8代码&#xff1a; ultralytics/yolo/engine/trainer.py 添加内容&#…

【LocalAI】(10):在autodl上编译embeddings.cpp项目,转换bge-base-zh-v1.5模型成ggml格式,本地运行main成功

1&#xff0c;关于 localai LocalAI 是一个用于本地推理的&#xff0c;与 OpenAI API 规范兼容的 REST API。 它允许您在本地使用消费级硬件运行 LLM&#xff08;不仅如此&#xff09;&#xff0c;支持与 ggml 格式兼容的多个模型系列。支持CPU硬件/GPU硬件。 【LocalAI】&…

Mac数据恢复软件快速比较:适用于Macbook的10佳恢复软件

数据丢失导致无数个人和组织每天损失大量资金。更糟糕的是&#xff0c;某些文件具有货币价值和情感意义&#xff0c;使它们不可替代&#xff0c;并使数据恢复成为唯一可行的选择。最好的消息是Mac用户可以从各种数据恢复程序中进行选择。为了帮助您尽可能快速、轻松地恢复丢失的…

vue3步骤条带边框点击切换高亮

如果是div使用clip-path: polygon(0% 0%, 92% 0%, 100% 50%, 92% 100%, 0% 100%, 8% 50%);进行裁剪加边框没实现成功。目前这个使用svg完成带边框的。 形状可自行更改path 标签里的 :d“[num ! 1 ? ‘M 0 0 L 160 0 L 176 18 L 160 38 L 0 38 L 15.5 18 Z’ : ‘M 0,0 L 160,0…