【Verilog】表达式位宽与符号判断机制
缘起于p1课下alu算数位移设计。查了好多资料,最后发现还是主要在翻译官方文档。浪费了超多时间啊,感觉还是没搞透,还是先以应用为导向放一放,且用且归纳
1.表达式位宽 expression bit length
身为硬件描述语言,Verilog表达式运算过程中必然要严肃考虑位宽问题
表达式的位宽由式中操作数和语境决定
表达式按位宽确定方式分为两类:
- self-determined expression
位宽仅有表达式自身确定,不会受语境影响也不影响语境中其他表达式(在复合表达式中某些位置上的子表达式,如i>>j
中的表达式j
、i?j:k
中的表达式i
)。 - context-determined expression
位宽由表达式本身和其所属表达式(父子表达式)共同决定(如阻塞赋值操作的RHS位宽由其自身和LHS被赋值变量的位宽决定)。
下表中i
,j
,k
表示"单操作数表达式",L(i)
表示表达式i的位宽
注意:RHS中context-determined表达式位宽受左值LHS影响,这点和符号性判断不同
这点尚未弄清存疑,且看示例
例0:缩减运算符相关(包含了优先级内容)
module trial(
output C,
output D,
output E,
output F,
output [7:0] Da,
output [7:0] Ea
);
wire [7:0] A = 8'b1011_1100;
wire B = 1;
assign C = ^A; // expect:1 ; got:1
assign D = ^A && B; // expect:1 ; got:1
assign E = ^A & B; // expect:0 ; got:1 ---> 缩减运算符优先级低了
assign F = ^(A & B); // expect:0 ; got:0
assign Da = ^A && B;
assign Ea = ^A & B;
endmodule
例1:所谓中间值位宽
reg [15:0] a,b,ans;
assign ans = (a + b) >> 1; //结果本意是将结果右移一位保留溢出的高位
根据i >> j
规则,j为self-determined表达式不影响总表达式位宽,i为(a + b)
位宽和左值ans
均为16,因而总表达式位宽为16。a + b
运算结束值即为16位,溢出被丢弃,所以达不成原本的目的。
修改为ans = (a + b + 0) >> 1;
强行将位宽改为0(Integer)的32位,便可达成目的。
例2:三元运算符表达式位宽
reg [3:0] a,b,c;
reg [4:0] d;
initial begin
a = 9; b = 8; c = 1;
$display("%b", c ? (a&b) : d);
end
根据i ? j : k
规则,i为self-determined表达式不影响总表达式位宽,max{L(j),L(k)}为d
的5位,因而总表达式位宽为5,输出01000
。
例3:综合由语境决定位宽
reg [3:0] a;
reg [5:0] b;
reg [15:0] c;
initial begin
a = 4'hf; b = 6'ha;
$display("%h", a*b); // a*b 位宽为6
c = a ** b; // a ** b 位宽是L(a)为4,受左值c的影响位宽最终为16。 c获得值16'hac61
$display("%h", c);
c = {a ** b}; // a ** b 位宽是L(a)为4,在{}内每一项均为self-determined表达式,最终位宽为4。 c获得值4'h1再扩展成16'h1
$display("%h", c);
end
最终输出
16
ac61
1
2.表达式符号性 expression signedness
Verilog计算表达式前,需要确定表达式的符号性,规则如下:
- 仅取决于RHS操作数,与LHS无关(与位宽确定有别,如
assign a = b ? c : d;
中LHS符号性与a
自身无关) - 十进制数视为signed
- 进制表示数视为unsigned,除非使用进制前加s特殊标明(
4'd3
无符号,4'sd3
有符号) - 位选择(不论是否选全)、位拼接(不论操作数)结果视为unsigned
- 比较表达式结果视为unsigned
- 实数强转整形表达式结果视为signed
- self-determined expression:符号性取决于其中操作数
- context-determined expression:
- 若存在操作数为real,则结果视为real
- 若存在操作数为unsigned,则结果视为unsigned
- 若所有操作数为signed,则结果视为signed
- 若存在操作数为real,则结果视为real
- 变量本身是unsigned(不过存了01串罢了,不要因为
reg [3:0]a = 2'sb11
就认为变量a是signed),除非声明时附加关键字signed
确定整个表达式的符号性后,便会向内层表达式传递符号性,直至各操作数。
$signed(exp)
函数计算传入的exp
并返回与其值和位宽均相同的数据,将其符号性改为signed。可以看作屏蔽了外部表达式的符号性传递。
例:含三元运算符的表达式
testbench中使ALUop恒为
3'd5
,A=4'b1101
input [3:0] A, input [3:0] B, input [2:0] ALUOp, output [3:0] C
assgin C = (ALUOp == 3'd5) ? A >>> B : 4'sd0;
分析:
在三元运算符(ALUop == 3'd5)
属于self-determined表达式,不会影响符号性判断。因而看后半部分。4'sd0
为符号数;A >>> B
表达式中B
为self-determined表达式(操作数),所以符号性仅看A
。A
无符号,因而总表达式RHS无符号。
传递符号性后,A
无符号数经算数位移值为4'b0110
,再赋给C
。
assgin C = (ALUOp == 3'd5) ? $signed(A) >>> B : 4'sd0;
分析:
$signed()
使$signed(A)
表达式有符号,则$signed(A) >>> B
表达式有符号,则总表达式RHS有符号。
传递符号性后,$signed(A)
(A
相当于被$signed()
隔绝,仍无符号)被视为有符号数,算数位移后值为4'b1110
assgin C = (ALUOp == 3'd4) ? A + B :
(ALUOp == 3'd5) ? $signed(A) >>> B : 4'sd0 ;
分析:
嵌套式三元运算符的结构。A + B
表达式无符号(其中中A
、B
无符号),因而总表达式RHS无符号。
传递符号性:(ALUOp == 3'd5) ? $signed(A) >>> B : 4'sd0
无符号;$signed(A) >>> B
无符号;$signed(A)
无符号(A
被$signed()
保护),算数位移后值为4'b0110
assgin C = (ALUOp == 3'd4) ? A + B :
(ALUOp == 3'd5) ? $signed($signed(A) >>> B) : 4'sd0 ;
分析:
同上,总表达式RHS无符号。
传递符号性:(ALUOp == 3'd5) ? $signed(A) >>> B : 4'sd0
无符号;$signed($signed(A) >>> B)
无符号。$signed(A) >>> B
被保护,因而$signed(A)
仍视为有符号,算数位移后值为4'b1110
。