此文以quartus为例,使用Verilog语言简单编写验证。并对常见赋值运算进行介绍,读者可采用附带程序进行验证和理解。
很神奇的一件事,机器运算仅可以完成最简单的“1+1”,而“1+1”在二进制中可以完成不同bit的加减。他是如何实现的呢?
一.最简单的是加法,再加法中不会产生负数,所以直接对应位数相加,加满进一;
二.在减法中,机器也是通过加法来实现的,因为在机器中,负数用对应的补码来表示,并不表示负数。
1、大数减小数
a.(相同位宽减法)例如-8‘d8可以表示为1000_1000,首位表示正负,命名为符号位,1表示负,0表示正。
-8的补码为反码1111_0111(反码)加一,即1111_1000(补码)。
1111_0111(反码)+0000_0001=1111_1000(补码)
让我们来计算一下10-8(8bits),
0000_1010(10) | |
- | 0000_1000(8) |
0000_0010(2) |
如果我们把-8写为补码1111_1000,则此时应该为10+(-8)
0000_1010(10) | |
+ | 1111_1000(-8补码) |
1_0000_0010 |
可见此时,计算结果溢出第九位,按8bits截取结果依然正确。
b.(不同位宽减法)在不同位宽的计算中,显然加法不会出现问题,减法直接用源码去计算也不会出现问题(如果计算结果大于0),让我们来试一下不同位宽的减法使用补码计算是否还可以成立。8’d8-4'd4
0000_1000(8) | |
+ | _____1100(-4补码) |
0001_0100(2) |
此时按8bits高位截取发现出现错误。
问题在于短位宽应补齐至高位宽(按首位补),计算如下
0000_1000(8) | |
+ | 1111_1100(-4补码) |
1_0000_0100(2) |
c1(signed) = a(unsigned) - b(unsigned)
2.不妨让我们顺便试一下小数减大数
a.相同位宽操作8‘d4-8'd6,我们先采用源码计算
0000_0100(4) | |
- | 0000_0110(6) |
此时发现会被无限借位。
如果我们采用补码计算,
0000_0100(4) | |
+ | 1111_1010(-6补码) |
1111_1110(-2补码) |
此时计算正确。
c1(signed) = a(unsigned) - b(unsigned)
c(unsigned) = a(unsigned) - b(unsigned)
在仿真中,不管我们是否定义结果是否赋值为符号值,都默认算出补码。但是未声明符号值的结果将被视为1_1111_1110(510),如果再继续进行计算将出现错误。
b.我们再来试一下不同位宽的小数减大数,8‘d4-4'd6
0000_0100(4) | |
+ | 1111_1010(-6补码) |
1111_1110(-2补码) |
如果我们按最高位补位计算可以顺利进行。所以在机器计算中,默认负数以补码存在。
三,Verilog 运算中只要出现无符号位,此式按无符号位运算
Verilog 运算中只要出现无符号位,此式按无符号位运算,这句话经常看到,但是我们将无符号位的两个数相减,赋值给带符号为的值,例如c1(signed) = a(unsigned) - b(unsigned),由仿真结果来看,计算结果还是正确的,但是此时机器存储的并不认为是补码,而是源码,继续计算就会出错。所以我们尽可能的避免混用。
这句话用来描述逻辑运算会更适合。这就要提及较为简单而又interesting的比较器。
如果我们将无符号值与有符号值进行比较大小,此时会出现很大问题,因为这个运算将按无符号位计算,然而在负数中,机器以补码的方式存储,如果将补码视为源码,将是很大的正值。
例如,8'd6 与 -8’d2
6 | < | -2 |
0000_0110 | < | 1111_1110 |
例如,-8'd6 与 -8’d2
-6 | < | -2 |
1111_1010 | < | 1111_1110 |
在比较时,出现负数如何进行比较呢?
我们建议使用parameter signed L0=1‘b0;先定义被比较值为符号值。
reg signed [7:0] c1
if( c1 >= L0)
下面为.v文件,可直接复制。文章来源:https://www.toymoban.com/news/detail-694870.html
module signed_1(
input clk,
input rst_n,
input wire unsigned [7:0] a,
input wire unsigned [7:0] b,
input wire signed [4:0] a1,
input wire signed [8:0] b1,
output reg unsigned [9:0] c,
output reg signed [8:0] c1
);
parameter signed x0='d0;
always @(posedge clk or negedge rst_n)
begin
if (!rst_n)begin
c1 <= 'd0;
c <= 'd0;end
else if (a1 > x0)
c1 <= 'd1;
else if (a1 < x0)
c1 <=(a1);
end
endmodule
下面为测试仿真文件,可借助modelsim进行仿真验证。文章来源地址https://www.toymoban.com/news/detail-694870.html
`timescale 1ns/1ns
module signed_1_tb();
reg clk ;
reg rst_n ;
wire unsigned [7:0] a ;
wire unsigned [7:0] b ;
wire signed [4:0] a1 ;
wire signed [8:0] b1 ;
wire unsigned [9:0] c ;
wire signed [8:0] c1 ;
unassign a = 8'd8;
unassign b = 8'd5;
assign a1 = -5'd8;
assign b1 = 9'd5;
initial
begin
clk = 1'b0;
#3000 $stop;
end
always #10 clk = ~clk;
initial
begin
rst_n = 1'b0;
#80 rst_n = 1'b1;
end
signed_1 signed_0(
.clk (clk ),
.rst_n (rst_n ),
.a (a ),
.a1 (a1 ),
.b (b ),
.b1 (b1 ),
.c (c ),
.c1 (c1 )
);
endmodule
到了这里,关于基于FPGA的Verilog语言 signed unsigned 运算&&不同位宽运算(无聊的碎碎叨叨)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!