在Verilog HDL中,算术操作符和运算的符号性(有符号或无符号)是理解复杂表达式和赋值语句行为的关键。
算术操作符
Verilog中的算术操作符包括:
+(加法):支持一元加(正数)和二元加(两个数相加)。
-(减法):支持一元减(负数)和二元减(两个数相减)。
***(乘法):两个数相乘。
/(除法):整数除法,结果截断小数部分。
%(取模):求余数,结果的符号与第一个操作数相同。
****(幂运算):计算一个数的幂次方。
运算结果的符号性和位宽
符号性:Verilog中的变量可以是无符号的(默认)或有符号的(通过signed关键字指定)。当表达式中的操作数既有有符号又有无符号时,Verilog会按照特定的规则处理。
表达式中有一个无符号操作数,则其他操作数会被隐式转换为无符号数进行运算。这可能导致意想不到的结果,特别是当涉及到负数时。
位宽:运算结果的位宽由参与运算的操作数中最大的位宽决定。在赋值语句中,如果目标变量的位宽小于运算结果的位宽,则结果会被截断以匹配目标变量的位宽。
如目标变量的位宽大于运算结果的位宽,则结果可能会被符号扩展(对于有符号数)或零填充(对于无符号数)以匹配目标变量的位宽。
示例分析
有符号和无符号数的赋值:
reg [0:5] burst_data; // 无符号寄存器 integer mtx_addr; // 有符号整型变量 burst_data = -4'd12; // 赋值给无符号寄存器,结果为52(二进制110100) mtx_addr = -4'd12; // 赋值给有符号整型变量,结果为-12(二进制补码110100)
在这个例子中,虽然两个表达式都使用了相同的二进制位模式110100,但由于目标变量的符号性不同,因此解释也不同。
混合有符号和无符号操作数的运算:
burst_data = -4'd12 / 4; // 无符号除法,结果为61(二进制111101,解释为无符号数) mtx_addr = -4'd12 / 4; // 有符号除法,结果为-3(二进制补码表示)
在这个例子中,由于burst_data是无符号的,因此除法运算也是无符号的,结果为正数。而mtx_addr是有符号的,因此除法运算也是有符号的,结果为负数。
使用signed和unsigned系统函数:
burst_data = $signed(4'b1101) + 4'sb1001; // 结果为-5(因为$signed将4'b1101解释为-3) mtx_addr = $unsigned(5'sb11010 + 5'sh1C) - 5'b01001; // 结果取决于具体的运算过程
在这个例子中,$signed和$unsigned系统函数用于显式地将操作数转换为有符号或无符号数,以控制运算的符号性。
step1:确定输出结果位宽。(一个符号位+两个数据有效为的位宽之和,如1个4bit的有符号数A,和1个5bit的有符号数B相乘,A的有效位是3bit,B的有效位为4bit,则输出结果的有效位宽为7bit,总位宽为8bit)。
step2:计算输出结果的符号位。(两个数据的符号位进行异或,判断是正数还是负数)。
step3:计算输出结果的有效位。(直接将两个数据相乘,得到的结果就是输出结果的有效位)。
step4:将符号位和有效位拼接后输出。
例4:(-3)*(-6)
分析:-3用有符号数表示,原码为3bit的111。-6用有符号数表示,原码为4bit的1110。
step1:确定输出结果位宽。(-3的有效位是2bit,-6的有效位为3bit,则输出结果的有效位宽为5bit,总位宽为6bit)。
step2:计算输出结果的符号位。两个数据的符号位都是1,则1^1=0,则输出结果是正数。
step3:计算输出结果的有效位。2'b11 * 3'b110 = 5'b1_0010。
step4:将符号位和有效位拼接后输出。out=6'b01_0010(+18)
ps:如果输入是补码,则运算之前先转换为原码,在按照以上步骤即可。
无符号数
1.高位溢出赋给一个位宽不够的数
wire [3:0] a=4’b1111;//15 wire [3:0] b=4’b0010;//2 wire [3:0] c; assign c = a + b;//17=10001
wire [3:0] a=4’b1111; wire [3:0] b=4’b0010; wire [2:0] c; assign c = a + b;
有符号数
wire signed [3:0] a=4’b1111;//-1 wire signed [3:0] b=4’b0010;//2 wire signed [3:0] c; assign c =a + b;
wire signed [3:0] a=4’b1110;//-2 wire signed [3:0] b=4’b0001;//1 wire signed [3:0] c; assign c =a + b;
总结
在Verilog中,要特别注意操作数的符号性和位宽,以避免意外的运算结果。
当表达式中混合了有符号和无符号操作数时,要特别小心,因为Verilog会按照特定的规则进行隐式转换。
使用$signed和$unsigned系统函数可以显式地控制运算的符号性。
在编写Verilog代码时,应该清晰地指定变量的符号性和位宽,以确保代码的正确性和可读性。