作为一个接触Verilog将近3年的菜鸟大大小小的项目也写过不少。前些日子写一个项目时间比较赶某个模块嘎嘎写了大几百行代码后才开始仿真调式。解决掉不少显而易见的Bug后仿真可以出结果但结果总和软件对不上于是开始了长达1小时的Debug。当时找了好久都没有找到问题心态炸裂甚至认为是仿真软件出问题。哎菜是原罪。最后靠着一步一步修改代码才定位到了问题。哎都是优先级惹的祸而且是第2次遇到优先级的问题遂记录下来来警示自己。很多时候在保证结果正确的情况下设计者最好弄清楚运算优先级盲目地添加括号可能会导致EDA工具将本可以并行的运算变成串行运算使得逻辑级数增加时序变差。以下是当时项目中的源码大家是否能够看出问题。assign Pixel0_R pixel_out0[65:54] {12{sign_out0}} {11d0,sign_out0}; assign Pixel0_G pixel_out0[43:32] {12{sign_out0}} {11d0,sign_out0}; assign Pixel0_B pixel_out0[21:10] {12{sign_out0}} {11d0,sign_out0}; assign Pixel1_R pixel_out1[65:54] {12{sign_out1}} {11d0,sign_out1}; assign Pixel1_G pixel_out1[43:32] {12{sign_out1}} {11d0,sign_out1}; assign Pixel1_B pixel_out1[21:10] {12{sign_out1}} {11d0,sign_out1}; assign Pixel2_R pixel_out2[65:54] {12{sign_out2}} {11d0,sign_out2}; assign Pixel2_G pixel_out2[43:32] {12{sign_out2}} {11d0,sign_out2}; assign Pixel2_B pixel_out2[21:10] {12{sign_out2}} {11d0,sign_out2}; assign Pixel3_R pixel_out3[65:54] {12{sign_out3}} {11d0,sign_out3}; assign Pixel3_G pixel_out3[43:32] {12{sign_out3}} {11d0,sign_out3}; assign Pixel3_B pixel_out3[21:10] {12{sign_out3}} {11d0,sign_out3};想要实现的功能是先做与运算再进行加法运算。然而加法操作的优先级高于逻辑与运算的优先级所以正确的写法需要将逻辑与运算用括号括起来如下。assign Pixel0_R ( pixel_out0[65:54] {12{sign_out0}} ) {11d0,sign_out0}; assign Pixel0_G ( pixel_out0[43:32] {12{sign_out0}} ) {11d0,sign_out0}; assign Pixel0_B ( pixel_out0[21:10] {12{sign_out0}} ) {11d0,sign_out0}; assign Pixel1_R ( pixel_out1[65:54] {12{sign_out1}} ) {11d0,sign_out1}; assign Pixel1_G ( pixel_out1[43:32] {12{sign_out1}} ) {11d0,sign_out1}; assign Pixel1_B ( pixel_out1[21:10] {12{sign_out1}} ) {11d0,sign_out1}; assign Pixel2_R ( pixel_out2[65:54] {12{sign_out2}} ) {11d0,sign_out2}; assign Pixel2_G ( pixel_out2[43:32] {12{sign_out2}} ) {11d0,sign_out2}; assign Pixel2_B ( pixel_out2[21:10] {12{sign_out2}} ) {11d0,sign_out2}; assign Pixel3_R ( pixel_out3[65:54] {12{sign_out3}} ) {11d0,sign_out3}; assign Pixel3_G ( pixel_out3[43:32] {12{sign_out3}} ) {11d0,sign_out3}; assign Pixel3_B ( pixel_out3[21:10] {12{sign_out3}} ) {11d0,sign_out3};在写代码过程中除了容易忽略逻辑运算和算数运算的优先级更容易忽略的是移位运算和算数运算的优先级。以下举一个例子完成3*a的电路设计加深大家的印象代码如下。设计代码module Precedence( a, dout1, dout2 ); input [3:0]a; output [3:0]dout1; output [3:0]dout2; assign dout1 a 2 - a; assign dout2 (a 2) - a; endmodule仿真代码module Precedence_tb; reg [3:0]a; wire [3:0]dout1; wire [3:0]dout2; Precedence Precedence_inst( .a(a), .dout1(dout1), .dout2(dout2) ); integer i; initial begin for(i0;i16;ii1)begin a i; #10; end $stop; end endmodule仿真结果dout2的输出才是设计想要的3*a而dout1是先进行减法运算后再完成移位运算与想要的设计不符。下面给出IEEE Standard Verilog Hardware Description Language中的运算优先级规定给大家参考。不需要完全记住这个运算优先级只需要在写代码的时候有意识地去注意这个问题遇到不确定优先级的情况要么直接加括号解决要么查一下标准解决。本人喜欢尽量不用括号所以才遇到了这么些问题。上图中表示的优先级含义如下运算符种类描述 - ! ~ ~ | ~|^ ~^ ^~(unary)一元运算符正号无显式电路往往省略。-负号对操作数取反后加一以补码形式存储。!逻辑非将操作数视为布尔值。~按位取反逐位翻转。如~4b0011 4b1100。归约与所有位相与多比特变成单比特。如4b1111 1b1。~归约与非所有位相与后再取反多比特变成单比特。|归约或所有位相或多比特变成单比特。如 |4b0000 1b0。~|归约或非所有位相或后再取反多比特变成单比特。^归约异或所有位异或多比特变成单比特。如 ^4b1111 1b0。~^和^~归约同或所有位同或多比特变成单比特。**二元运算符幂运算** 幂运算硬件中不要用这个运算符。* / %二元运算符算术运算*乘法计算两数乘积。如果两数都是变量的话建议使用DSP或设计乘法器。/除法计算两数除法。建议使用DSP或设计除法器。%取模(取余)计算除法后的余数符号同被除数。建议使用DSP或设计模约简算法。 -(binary)二元运算符算术运算加法计算两数之和硬件映射为加法器电路。-减法计算两数差值本质上也是加法器。 二元运算符移位运算逻辑左移高位丢弃低位补0。如4b1100 1 4b1000。逻辑右移低位丢弃高位补0。如4b1100 1 4b0110。算术左移同逻辑左移符号位可能丢失。算术右移低位丢弃高位补符号位用于有符号数即用signed申明过。 二元运算符关系运算小于小于等于大于大于等于 ! !二元运算符关系运算逻辑相等操作数中有X或Z时结果为X。!逻辑不等全等严格匹配包括X和Z的比较。如4b1x01 4b1x01得1。!不全等(binary)二元运算符位运算 按位相与如4b1100 4b1010 4b1000^ ^~ ~^(binary)二元运算符位运算^ 按位异或如4b1100 ^ 4b1010 4b0110^~和~^ 按位同或如4b1100 ^ 4b1010 4b1001|(binary)二元运算符位运算| 按位相或如 4b1100 4b1010 4b1110二元运算符逻辑运算 逻辑与||二元运算符逻辑运算|| 逻辑或?:(conditional operator)三元运算符?: 三目运算condition ? expr1 : expr2;条件满足时结果为expr1否则为expr2。{} {{}}拼接与复制运算符{}拼接连接多个信号如{a, b}生成新总线。{{}}复制重复拼接如{4{2b01}} 8b01010101以上就是有关Verilog运算优先级的感悟欢迎大家在评论区进行探讨。细节决定成败啊
Verilog避坑:全是优先级惹的祸,运算符使用总结
作为一个接触Verilog将近3年的菜鸟大大小小的项目也写过不少。前些日子写一个项目时间比较赶某个模块嘎嘎写了大几百行代码后才开始仿真调式。解决掉不少显而易见的Bug后仿真可以出结果但结果总和软件对不上于是开始了长达1小时的Debug。当时找了好久都没有找到问题心态炸裂甚至认为是仿真软件出问题。哎菜是原罪。最后靠着一步一步修改代码才定位到了问题。哎都是优先级惹的祸而且是第2次遇到优先级的问题遂记录下来来警示自己。很多时候在保证结果正确的情况下设计者最好弄清楚运算优先级盲目地添加括号可能会导致EDA工具将本可以并行的运算变成串行运算使得逻辑级数增加时序变差。以下是当时项目中的源码大家是否能够看出问题。assign Pixel0_R pixel_out0[65:54] {12{sign_out0}} {11d0,sign_out0}; assign Pixel0_G pixel_out0[43:32] {12{sign_out0}} {11d0,sign_out0}; assign Pixel0_B pixel_out0[21:10] {12{sign_out0}} {11d0,sign_out0}; assign Pixel1_R pixel_out1[65:54] {12{sign_out1}} {11d0,sign_out1}; assign Pixel1_G pixel_out1[43:32] {12{sign_out1}} {11d0,sign_out1}; assign Pixel1_B pixel_out1[21:10] {12{sign_out1}} {11d0,sign_out1}; assign Pixel2_R pixel_out2[65:54] {12{sign_out2}} {11d0,sign_out2}; assign Pixel2_G pixel_out2[43:32] {12{sign_out2}} {11d0,sign_out2}; assign Pixel2_B pixel_out2[21:10] {12{sign_out2}} {11d0,sign_out2}; assign Pixel3_R pixel_out3[65:54] {12{sign_out3}} {11d0,sign_out3}; assign Pixel3_G pixel_out3[43:32] {12{sign_out3}} {11d0,sign_out3}; assign Pixel3_B pixel_out3[21:10] {12{sign_out3}} {11d0,sign_out3};想要实现的功能是先做与运算再进行加法运算。然而加法操作的优先级高于逻辑与运算的优先级所以正确的写法需要将逻辑与运算用括号括起来如下。assign Pixel0_R ( pixel_out0[65:54] {12{sign_out0}} ) {11d0,sign_out0}; assign Pixel0_G ( pixel_out0[43:32] {12{sign_out0}} ) {11d0,sign_out0}; assign Pixel0_B ( pixel_out0[21:10] {12{sign_out0}} ) {11d0,sign_out0}; assign Pixel1_R ( pixel_out1[65:54] {12{sign_out1}} ) {11d0,sign_out1}; assign Pixel1_G ( pixel_out1[43:32] {12{sign_out1}} ) {11d0,sign_out1}; assign Pixel1_B ( pixel_out1[21:10] {12{sign_out1}} ) {11d0,sign_out1}; assign Pixel2_R ( pixel_out2[65:54] {12{sign_out2}} ) {11d0,sign_out2}; assign Pixel2_G ( pixel_out2[43:32] {12{sign_out2}} ) {11d0,sign_out2}; assign Pixel2_B ( pixel_out2[21:10] {12{sign_out2}} ) {11d0,sign_out2}; assign Pixel3_R ( pixel_out3[65:54] {12{sign_out3}} ) {11d0,sign_out3}; assign Pixel3_G ( pixel_out3[43:32] {12{sign_out3}} ) {11d0,sign_out3}; assign Pixel3_B ( pixel_out3[21:10] {12{sign_out3}} ) {11d0,sign_out3};在写代码过程中除了容易忽略逻辑运算和算数运算的优先级更容易忽略的是移位运算和算数运算的优先级。以下举一个例子完成3*a的电路设计加深大家的印象代码如下。设计代码module Precedence( a, dout1, dout2 ); input [3:0]a; output [3:0]dout1; output [3:0]dout2; assign dout1 a 2 - a; assign dout2 (a 2) - a; endmodule仿真代码module Precedence_tb; reg [3:0]a; wire [3:0]dout1; wire [3:0]dout2; Precedence Precedence_inst( .a(a), .dout1(dout1), .dout2(dout2) ); integer i; initial begin for(i0;i16;ii1)begin a i; #10; end $stop; end endmodule仿真结果dout2的输出才是设计想要的3*a而dout1是先进行减法运算后再完成移位运算与想要的设计不符。下面给出IEEE Standard Verilog Hardware Description Language中的运算优先级规定给大家参考。不需要完全记住这个运算优先级只需要在写代码的时候有意识地去注意这个问题遇到不确定优先级的情况要么直接加括号解决要么查一下标准解决。本人喜欢尽量不用括号所以才遇到了这么些问题。上图中表示的优先级含义如下运算符种类描述 - ! ~ ~ | ~|^ ~^ ^~(unary)一元运算符正号无显式电路往往省略。-负号对操作数取反后加一以补码形式存储。!逻辑非将操作数视为布尔值。~按位取反逐位翻转。如~4b0011 4b1100。归约与所有位相与多比特变成单比特。如4b1111 1b1。~归约与非所有位相与后再取反多比特变成单比特。|归约或所有位相或多比特变成单比特。如 |4b0000 1b0。~|归约或非所有位相或后再取反多比特变成单比特。^归约异或所有位异或多比特变成单比特。如 ^4b1111 1b0。~^和^~归约同或所有位同或多比特变成单比特。**二元运算符幂运算** 幂运算硬件中不要用这个运算符。* / %二元运算符算术运算*乘法计算两数乘积。如果两数都是变量的话建议使用DSP或设计乘法器。/除法计算两数除法。建议使用DSP或设计除法器。%取模(取余)计算除法后的余数符号同被除数。建议使用DSP或设计模约简算法。 -(binary)二元运算符算术运算加法计算两数之和硬件映射为加法器电路。-减法计算两数差值本质上也是加法器。 二元运算符移位运算逻辑左移高位丢弃低位补0。如4b1100 1 4b1000。逻辑右移低位丢弃高位补0。如4b1100 1 4b0110。算术左移同逻辑左移符号位可能丢失。算术右移低位丢弃高位补符号位用于有符号数即用signed申明过。 二元运算符关系运算小于小于等于大于大于等于 ! !二元运算符关系运算逻辑相等操作数中有X或Z时结果为X。!逻辑不等全等严格匹配包括X和Z的比较。如4b1x01 4b1x01得1。!不全等(binary)二元运算符位运算 按位相与如4b1100 4b1010 4b1000^ ^~ ~^(binary)二元运算符位运算^ 按位异或如4b1100 ^ 4b1010 4b0110^~和~^ 按位同或如4b1100 ^ 4b1010 4b1001|(binary)二元运算符位运算| 按位相或如 4b1100 4b1010 4b1110二元运算符逻辑运算 逻辑与||二元运算符逻辑运算|| 逻辑或?:(conditional operator)三元运算符?: 三目运算condition ? expr1 : expr2;条件满足时结果为expr1否则为expr2。{} {{}}拼接与复制运算符{}拼接连接多个信号如{a, b}生成新总线。{{}}复制重复拼接如{4{2b01}} 8b01010101以上就是有关Verilog运算优先级的感悟欢迎大家在评论区进行探讨。细节决定成败啊