跳至正文

Verilog语法解析——位宽截断补齐机制与十进制数的表示方法

Verilog语法解析——位宽截断补齐机制与十进制数的表示方法-1


前言

之前写过一个Verilog功能模块——符号位扩展,功能是将位宽小的有符号数赋值给位宽大的有符号数,保证数值不变。后来我发现根本不需要这么麻烦,只需要将原始数据设置成有符号数即可,Verilog编译器会自动补齐符号位。为此我详细研究了一下Verilog的位宽截断与补齐机制。

对于二进制或十六进制,位宽截断和补齐是很明显的,但对于十进制则需要先弄清楚,十进制在Verilog中如何转换为二进制,然后才能清楚如何对十进制表示的数进行位宽截断或补齐,为止,我又研究了一下Verilog中十进制数的二进制表示。

本文基于的仿真工具是Vivado 2021.2,基于的Verilog版本为Verilog-2001。本文未测试更多的编译器和Verilog版本,但可以预料到这些基础的机制应适用于所有编译器和Verilog版本


一. Testbench

module signExtension_tb();

timeunit 1ns;
timeprecision 1ps;

logic clk;
localparam CLKT = 2;
initial begin
  clk = 0;
  forever #(CLKT / 2) clk = ~clk;
end

localparam INPUT_WIDTH = 8;
localparam OUTPUT_WIDTH = 4;

logic [INPUT_WIDTH-1  : 0] din;
logic [OUTPUT_WIDTH-1 : 0] dout;

assign dout = din;

initial begin
  #(CLKT) din = 8'h04;
  #(CLKT) din = 8'h34;
  #(CLKT) din = 8'hA4;
  #(CLKT) din = 8'hF4;
  #(CLKT) $stop;
end

endmodule

二. 位宽截断

localparam INPUT_WIDTH = 8;
localparam OUTPUT_WIDTH = 4;

// (不同类型的din与dout定义)

assign dout = din;

initial begin
  #(CLKT) din = 8'h04;
  #(CLKT) din = 8'h33;
  #(CLKT) din = 8'hAA;
  #(CLKT) din = 8'hFF;
  #(CLKT) $stop;
end

2.1 无符号数 → 无符号数的位宽截断

logic [INPUT_WIDTH-1  : 0] din;
logic [OUTPUT_WIDTH-1 : 0] dout;

高位舍弃,低位保留。

2.2 无符号数 → 有符号数的位宽截断

logic [INPUT_WIDTH-1  : 0] din;
logic signed [OUTPUT_WIDTH-1 : 0] dout;

高位舍弃,低位保留。

2.3 有符号数 → 无符号数的位宽截断

logic signed [INPUT_WIDTH-1  : 0] din;
logic [OUTPUT_WIDTH-1 : 0] dout;

高位舍弃,低位保留。

2.4 有符号数 → 有符号数的位宽截断

logic signed [INPUT_WIDTH-1  : 0] din;
logic signed [OUTPUT_WIDTH-1 : 0] dout;

高位舍弃,低位保留。

2.3 位宽截断总结

高位舍弃,低位保留


三. 位宽补齐

localparam INPUT_WIDTH = 4;
localparam OUTPUT_WIDTH = 8;

// (不同类型的din与dout定义)

assign dout = din;

initial begin
  #(CLKT) din = 4'h4;
  #(CLKT) din = 4'h3;
  #(CLKT) din = 4'hA;
  #(CLKT) din = 4'hF;
  #(CLKT) $stop;
end

3.1 无符号数 → 无符号数的位宽补齐

logic [INPUT_WIDTH-1  : 0] din;
logic [OUTPUT_WIDTH-1 : 0] dout;

低位保留,高位补0。

3.2 无符号数 → 有符号数的位宽补齐

logic [INPUT_WIDTH-1  : 0] din;
logic signed [OUTPUT_WIDTH-1 : 0] dout;

低位保留,高位补0。

3.3 有符号数 → 无符号数的位宽补齐

logic signed [INPUT_WIDTH-1  : 0] din;
logic [OUTPUT_WIDTH-1 : 0] dout;

低位保留,高位补符号位。

3.4 有符号数 → 有符号数的位宽补齐

logic signed [INPUT_WIDTH-1  : 0] din;
logic signed [OUTPUT_WIDTH-1 : 0] dout;

低位保留,高位补符号位。

3.5 位宽补齐总结

无符号数 → 无/有 :低位保留,高位补0

有符号数 → 无/有 :低位保留,高位补符号位


四. Verilog中十进制数的二进制表示

Verilog中十进制有两种表示方法:

4.1 不指定位宽的十进制数表示

initial begin
  #(CLKT) din = 'd4// 100
  #(CLKT) din = -'d100// 1001_1100
  #(CLKT) din = 200// 1100_1000
  #(CLKT) din = 2147483648//
  #(CLKT) din = -2147483648// 1000_0000_0000_0000_0000_0000_0000_0000
  #(CLKT) $stop;
end

不指定位宽有两种写法:'d100‘ 或 100。两者是完全等效的。

在Verilog中,不指定位宽的十进制数默认为32位补码能表示的数的范围-2147483648 ~ -2147483647

我用的Linter工具是Vivado(xvlog),超出此范围,会有警告:[VRFC 10-2581] 2147483648 as 32-bit signed integer overflows, using -2147483648 insteadxvlog(VRFC 10-2581),如下图所示。

-2147483648也会报同样的警告,但这个是误报。

以下三种写法等效:

din = -'d100;
din = -100;

wire signed [31:0] a = -32'
d100;
din = a;

无论din定义有无符号,都不影响结果,从二. 位宽截断三.位宽补齐 我们知道,等号左边的变量如何定义不影响位宽截断或补齐,而等号右边的源数据有无符号也仅对补齐有影响

总结:在Verilog中,不指定位宽的十进制数等价于wire signed [31:0] num 即32位二进制补码。它对等号左边变量的赋值满足本文截断/补齐中总结的规律

4.2 指定位宽的十进制表示

在Verilog中,指定位宽的十进制等价于对其二进制补码进行截断或补齐

如:

  1. ‘6d64等价于补码8’b0100_0000进行6位截断,实际为6’b00_0000即全0,这有点反直觉,但仿真结果确实是这样;

  2. -7’d66等价于补码8’b1011_1110进行7位截断,实际为7’b011_1110即62,这也很奇怪,所以不要犯这种超出位宽表示范围的错误,像这种写法:

    wire [6:0] c = -7'd66;

    Linter工具并不会报错误和警告,犯这种错误排查非常困难且浪费时间,特别是在大工程中

  3. ’16d4 等价于补码4’b0100进行16位补齐,高位补符号位0;

  4. -33’d2147483648等价于补码32’b1000_0000_0000_0000_0000_0000_0000_0000进行补齐,高位补符号位1;


五. 总结

  1. 不指定位宽的十进制等价于wire signed [31:0] num 即32位二进制补码;
  2. 指定位宽的十进制等价于对其二进制补码进行截断或补齐;
  3. 位宽截断:高位舍弃,低位保留;
  4. 位宽补齐:无符号数 → 无/有 :低位保留,高位补0;
  5. 位宽补齐:有符号数 → 无/有 :低位保留,高位补符号位;

如果本文对你有所帮助,欢迎点赞、转发、收藏、评论让更多人看到,赞赏支持就更好了。

如果对文章内容有疑问,请务必清楚描述问题,留言评论或私信告知我,我看到会回复。


徐晓康的博客持续分享高质量硬件、FPGA与嵌入式知识,软件,工具等内容,欢迎大家关注。

0 0 投票数
文章评分
订阅评论
提醒
0 评论
内联反馈
查看所有评论
0
希望看到您的想法,请您发表评论x
目录