Verilog/System Verilog 一直以来被冠以 残缺语言 的称号。在许多的地方,由于仿真器/编译器实现的机制问题,导致很多地方的代码写法十分反直觉。尤其是对IEEE标准实现的不同,导致同样的代码在不同的工具编译后,会产生不同的输出结果。这些问题其实大大破坏了HDL生态的构建和同一,尤其是,会让人在无关紧要的地方浪费大量debug的时间 (╯‵□′)╯︵┴─┴

  本文就有符号数和无符号数的仿真处理差异问题,做一些小小的记录,其中有语言的共性问题,也有不同仿真编译工具差异的问题。

Signed Packed Array

  对于加了signed描述符的 packed array, iverilog 和 VCS 对其的理解不同。首先 iverilog 会认为这个 array 中最小单元的数据分别都是有符号的,而 VCS 则会认为只有整体信号才是一个有符号数,其中内部的各个子数据单元按无符号处理。

module sign ();

logic signed [1 : 0] [2 : 0] sign_arr;

initial begin
    sign_arr[0] = 3'b101;
    sign_arr[1] = 3'b010;
    $display(sign_arr[0] > sign_arr[1]);
    $finish;
end

endmodule

对于以上示例代码,在 iverilog+vvp 的仿真流中输出为0,即按照有符号数进行比较。而在 vlogan+vcs 的仿真流中输出为1,按照无符号数进行比较。

Signed Shift

  算术位移>>>和逻辑位移>>可以算是初学者都会进行区分的概念,可是 VCS 不会。在 VCS 的仿真中,算术位移的位移个数,也就是右操作数,必须也要指定为有符号的,才会正确识别位移数的符号,并进行补齐。而在 iverilog 中,则没有这个问题。

module shift ();
    
logic [3 : 0] arr = 4'b1101;
logic [1 : 0] sf = 2'b01;
logic [3 : 0] res;

initial begin
   // VCS: res = $signed(arr) >>> $signed(sf);
   res = $signed(arr) >>> sf;
   $display("%b", res);
   $finish;
end

endmodule

对于以上示例代码,iverilog+vvp 输出为 1110,而 vlogan+vcs 输出为 0110。emmm,VCS好像只认定等号右边所有操作数都为有符号的才进行有符号的逻辑

Unsigned Add

  这个问题同时出现在 iverilog 和 VCS 中。在进行加法的时候,会首先对涉及到的操作数进行位数补齐,这个补齐是直接无视其他运算符的。正常人的逻辑一般是,操作数一层一层进行运算,直到加法,再进行补齐。

module add ();

logic [3 : 0] long = 4'b0011;
logic [1 : 0] short = 2'b11;

logic [3 : 0] res;

initial begin
    res = long + (~short); 
    $display("%b", res);
    $finish;
end
    
endmodule

这个示例代码,输出的结果都是1111,也就是先将short按无符号数运算补0到和long一样宽度,再进行取反,再算无符号加法。

Ternary Trap

  三元符常常被我用来简化代码,这样可以偷懒少写一些 if-else,但是实际上如果涉及到有符号数的运算,也挺难受的。因为三元符常常会用到很多信号进行运算或者逻辑判断,而当其中只有部分是有符号数运算时,这部分运算需要等号右边表达式所有信号都是声明为有符号时,才会正常工作,否则按无符号数进行运算。对于第一次遇见这个问题的人好坑啊!

module ternary ();

logic [1 : 0] flag = 1'b11;
logic [2 : 0] a = 3'b101;
logic [1 : 0] b = 2'b10;

logic [2 : 0] res;

initial begin
   // res = $signed(|flag) ? $signed(a) + $signed(b) : $signed(3'b000);
   res = (|flag) ? $signed(a) + $signed(b) : 3'b000;
   $display("%b", res);
   $finish;
end
                                                                   
endmodule

以上示例代码,仿真输出为111,也就是中间那个加法其实是按无符号数进行运算的。