不知道有沒有人像我一樣,長久以來將verilog中的有符號數視為不敢觸碰的禁區。不過俗話說啊解決恐懼的最好辦法就是直面恐懼,又有俗話說要想工其事必先利其器,還有俗話說磨刀不誤砍柴工,也有俗話說The only thing we have to fear is fear itself,所以今天咱們就嘗試對verilog中數據的符號屬性(有符號數和無符號數)探究根源。
本文的驗證環境基于VCS通過auto_testbench生成,相關的vcs命令行細節請在auto_testbench工程目錄下查詢。
先說明一個大前提,有符號數即補碼表示,無符號數即原碼表示或者說必然是整數的補碼表示,因此有符號數和無符號數均可以認為是數值的補碼。
為了省流,還是先甩結論。有符號數和無符號數的最本質區別就是:符號位的識別和高位拓展。除此之外,另一個區別就是從人的角度如何如何讀這個數,或者說$display(%d)打印時打印的值是什么(而從機器的角度它壓根就不區分signed和unsigned)。也就是說,如果不涉及到位寬拓展的事,有符號數和無符號數在verilog運算中可以說毫無差別。
所以先給出兩個結論,verilog中數據的符號屬性會影響兩件事:
1.符號位的識別和位寬拓展,有符號數最高位被識別為符號位,高位拓展時拓展符號位,無符號數高位拓展0;
2.數據的實際值(人的角度如何如何讀這個數);
顯然,這個這個結論和固有的認知出入有點大,不急咱們先看一個實驗。下面的代碼是一段不涉及到位寬拓展的運算,t0_va0為有符號數-1,也就是16'hffff:
wire signed[15:0]t0_en0 = t0_va0 + 16'hffff;
wire [15:0]t0_en1 = t0_va0 + 16'hffff;
wire signed[15:0]t0_en2 = t0_va0 + $signed(16'hffff);
wire [15:0]t0_en3 = t0_va0 + $signed(16'hffff);
wire signed[15:0]t0_en4 = t0_va0 - 16'h1;
wire signed[15:0]t0_en5 = t0_va0 - $unsigned(16'h1);
wire [15:0]t0_en6 = t0_va0 - 16'h1;
wire [15:0]t0_en7 = t0_va0 - $unsigned(16'h1);
仿真的結果如下:
可以看到所有的結果都是一致的。而如果將va0信號改成無符號數:
wire signed[15:0]t1_en0 = t1_va0 + 16'hffff;
wire [15:0]t1_en1 = t1_va0 + 16'hffff;
wire signed[15:0]t1_en2 = t1_va0 + $signed(16'hffff);
wire [15:0]t1_en3 = t1_va0 + $signed(16'hffff);
wire signed[15:0]t1_en4 = t1_va0 - 16'h1;
wire signed[15:0]t1_en5 = t1_va0 - $unsigned(16'h1);
wire [15:0]t1_en6 = t1_va0 - 16'h1;
wire [15:0]t1_en7 = t1_va0 - $unsigned(16'h1);
其仿真結果也是一致的:
而一旦涉及到運算結果位寬拓展結果就有所差別了,比如將結果的位寬都改成18bit:
wire signed[17:0]t2_en0 = t2_va0 + 16'hffff;
wire [17:0]t2_en1 = t2_va0 + 16'hffff;
wire signed[17:0]t2_en2 = t2_va0 + $signed(16'hffff);
wire [17:0]t2_en3 = t2_va0 + $signed(16'hffff);
wire signed[17:0]t2_en4 = t2_va0 - 16'h1;
wire signed[17:0]t2_en5 = t2_va0 - $unsigned(16'h1);
wire [17:0]t2_en6 = t2_va0 - 16'h1;
wire [17:0]t2_en7 = t2_va0 - $unsigned(16'h1);
看到的波形就是這樣:
這個事就有些神奇了,所以后面就是說明下這里面的區別,或者說在完成運算后工具是如何識別這個數的符號性以決定位寬拓展的結果的。在開始之前必須明確下一個有符號數(補碼),'h1fffe和'hfffe對于機器是沒有區別的,都是-2。
仿真工具在對一個運算結果進行位寬拓展時,會先識別這個數的符號屬性,那么具體是怎么識別的呢?
1.有符號數和有符號數運算,結果為有符號數;
2.有符號數和無符號數運算,結果為無符號數;
3.無符號數和無符號數運算,結果為無符號數;
4.運算結果外又通過signed和unsigned定向指定時,最終的符號屬性遵照指定結果;
5.等號左側信號聲明中的signed/unsigned不影響右側運算結果的符號數屬性;
好,咱們一個一個看。
有符號數和有符號數運算,結果為有符號數。
wire signed[17:0]t3_en0 = t3_va0 - $signed(16'h1);
wire signed[17:0]t3_en1 = t3_va0 + $signed(16'hffff);
wire [17:0]t3_en2 = t3_va0 - $signed(16'h1);
wire [17:0]t3_en3 = t3_va0 + $signed(16'hffff);
很顯然,兩個有符號數進行操作結果是有符號數,位寬拓展時拓展符號位。同時這個波形還說明了等號左側信號聲明中的signed/unsigned不影響右側運算結果的符號數屬性。
有符號數和無符號數運算,結果為無符號數。
wire signed[17:0]t3_en5 = t3_va0 + 16'hffff;
wire [17:0]t3_en6 = t3_va0 - 16'h1;
wire [17:0]t3_en7 = t3_va0 + 16'hffff;
顯然結果是無符號數,高位拓展時拓展的都是0。
無符號數和無符號數運算,結果為無符號數。這個不說了,顯而易見的事情。
運算結果外又通過signed和unsigned定向指定時,最終的符號屬性遵照指定結果。
wire signed[17:0]t3_en9 = $unsigned(t3_va0 + $signed(16'hffff));
wire [17:0]t3_en10 = $signed(t3_va0 - 16'h1);
wire [17:0]t3_en11 = $signed(t3_va0 + 16'hffff);
因為在運算之后又通過signed和unsigned指定了符號屬性,所以后面的位寬拓展就按照對應的屬性來了。再次進一步的佐證,等號左側信號聲明中的signed/unsigned不影響右側運算結果的符號數屬性。
好了現在我們再把前文的結論翻出來,verilog中的符號屬性會影響兩件事情:
1.符號位的識別和位寬拓展,有符號數最高位被識別為符號位,高位拓展時拓展符號位,無符號數高位拓展0;
2.數據的實際值(人的角度如何如何讀這個數);
再看這個測試就很好解釋了:
wire signed[17:0]t2_en0 = t2_va0 + 16'hffff;
//就是'hffff+'hffff = 'h1fffe
wire [17:0]t2_en1 = t2_va0 + 16'hffff;
//同上
wire signed[17:0]t2_en2 = t2_va0 + $signed(16'hffff);
//本質還是'hffff+'hffff = 'h1fffe,不過因為結果是有符號數,拓展位寬為18bit時拓展為18'h3fffe
wire [17:0]t2_en3 = t2_va0 + $signed(16'hffff);
//同上
wire signed[17:0]t2_en4 = t2_va0 - 16'h1;
//本質是'hffff-'h1,或者理解成'hffff+'hffff,加上一個負數位寬一定不需要高位拓展,所以結果是'hfffe
//再根據等號左側的需求拓展為18bit,無符號數拓展0,結果為18'fffe
wire signed[17:0]t2_en5 = t2_va0 - $unsigned(16'h1);
//同上
wire [17:0]t2_en6 = t2_va0 - 16'h1;
//同上
wire [17:0]t2_en7 = t2_va0 - $unsigned(16'h1);
//同上
再來補充一個,大家看看結果應該是啥:
顯然結果應該是18'h3fffe,因為左側的運算結果是有符號數的16'hfffe,拓展成18bit時拓展符號位1:
最后呢再看一組額外的實驗,感興趣的可以分析一下結果:
logic [1:0]va_u;
logic signed[1:0]va_s;
logic [2:0]vb_u;
logic [2:0]vb_s;
logic [31:0]vc_u;
logic [31:0]vc_s;
initial begin
va_u = -1;
va_s = -1;//signed
$display("va_u='b%0b, va_s='b%0b", va_u, va_s);
$display("va_u=%0d, va_s=%0d", va_u, va_s);
vb_u = -va_u;
vb_s = -va_s;
$display("vb_u='b%0b, vb_s='b%0b", vb_u, vb_s);
$display("vb_u=%0d, vb_s=%0d", vb_u, vb_s);
vc_u = va_u;
vc_s = va_s;
$display("vc_u='b%0b, vc_s='b%0b", vc_u, vc_s);
end
仿真的結果為:
va_u=3, va_s=-1
vb_u='b101, vb_s='b1
vb_u=5, vb_s=1
vc_u='b11, vc_s='b11111111111111111111111111111111
-
仿真器
+關注
關注
14文章
1017瀏覽量
83721 -
Verilog
+關注
關注
28文章
1351瀏覽量
110075 -
VCS
+關注
關注
0文章
79瀏覽量
9600
發布評論請先 登錄
相關推薦
評論