数字信号处理算法FPGA实现学习笔记6:signed关键字声明的用法
升余弦滤波兽
2024年04月29日 13:55
收录于文集
共6篇

前文所讨论的所有内容我们在Verilog代码中均没有声明Signed,这样我们的模型都是统一当成普通的二进制数,他并不知道这些二进制数有没有符号,是正数还是负数。我们需要扩展符号位或者手动补符号位去教会它是正是负,包括溢出的判断中我们也是根据二进制底层的机理去对相应的溢出位做一个判断。

这样基于底层的机理写法对不是很清楚底层原理的代码阅读者来说,读起来会很费劲。而如果使用了signed声明,会使整个Verilog代码读起来非常易懂,和高级语言像c语言这种读起来一样。

但是signed的使用也有很多注意事项,在进行这个关键字讨论之前,从本质机理上分析上来说,无论你写不写sigend声明,这个二进制数就是这个二进制数,你写不写都是这个数,不会发生任何改变,唯一改变的地方就是工具看到这个二进制数之后,它会明白在做运算和算术右移位的时候,需要做符号位扩展和补齐(好吧其实它还是不懂到底是负数还是正数,他只知道要取最高位补齐和扩展。。。就像以前大学复习期末考试的时候我们不太懂背后的原理,但是懂得做题方法),它根据这种做题方法就能做到我们试图教会它的正负数相加原理。我个人理解哈,signed的区别应该就是在这里。

在我们上一篇文章讨论的例子中,4位表示的-8和2位表示的-2相加,我们要对其进行符号位扩展,扩展到和输出一样的位宽,不然结果会错,但是如果我们对这些信号加了signed声明,那我们就不需要扩展了,直接相加就可以,工具看到这个signed声明,他相当于复习了“考试的做题方法”,他会自动对你的数进行符号位扩展,以确保运算结果正确而不丢失符号位。这是在加减乘除上的好处。

另一个好处就是在向右移位的时候,他会自动补齐符号位。

右移有算术右移和逻辑右移,逻辑右移就是>>,无论是有符号数还是无符号数还是都是高位补零。重点是算术逻辑,如果是没加signed声明,我们想实现算术右移,必须要手动补符号位,而如果用了signed,我们可以直接使用>>>,这样工具会自动帮我们补齐符号位。这是在符号位算术右移上的好处。

等一等为什么不说左移呢?因为左移无论你加没加signed都一样,大家都是向左移,而我们的符号位是在MSB的,左移都会补零,所以,对于有符号数的左移,为了防止把符号位挤掉,我们需要手动去按住符号位,然后把剩下的位左移并且在右边补零。不过最好的方法就是留足位宽。

还有一个好处就是用在比较上的好处,如果我们使用了signed声明,例如a和b都是有符号数,一个是3’b100(-4),一个是3‘b011(3),如果我们不写signed声明,从正常的二进制的比较上来说,100>011,但是如果我们对他们做了signed声明,工具会自动把他们当成有符号数补码,显然100<011,结果是完全相反的。这是两个信号的比较。

那么如果是变量和数值比较呢,比如a = 3'b100,a是signed声明的,即-4,和常数3’b110(6),注意,只要你写了带位宽表示的数,就一定当成无符号数,什么意思呢,你写了3'd6这表达方法,工具会把他当成无符号数,而当运算或者比较中同时出现无符号数和有符号数的时候,会全部当成无符号数处理。即运算符里要不全是无符号数要不全是有符号数,不然计算结果可能会出错,那我们想比较a<3'b110的话必须把3'b110变成有符号数,两种办法,一种是写成6而不是3'd6这种带位宽的表示方法,这样工具就会自动把6当成有符号数,第二种办法是使用系统函数$signed(),但是要注意,使用这个函数前,要先对符号位扩展,如果你直接使用$signed(3'b110),它会把最高位1当成符号位,那你的+6就变成-2了,所以正确的使用方法应该是$signed({1’b0,3‘b110}),这样转换出来的就是正数了。

还有一个要注意的点是在使用signed声明的时候记得把三目运算符和双目运算符分开,不然容易混淆。如 assign z = a?(x+y):z,其中x和y都是有符号数,其他都是无符号数,这样直接写的话最好x和y也会被当成无符号数,那么x+y的结果就要有可能是错误的了,妥当的写法应该是先

assign f = x + y;assign f = a?y:z;这样分开写就好了