verilog中直接使用*,/符号进行数学计算时,位宽不匹配的截位问题
案例:
input [31:0] PWM_FREQ,
input [31:0] PWM_Duty_cycle,
reg [31:0] total_period_cycles;
reg [31:0] high_period_cycles;
total_period_cycles <= (100_000_000 / PWM_FREQ) ;
high_period_cycles <= (total_period_cycles * PWM_Duty_cycle) / 1000000 ;
分析:
当前,所有涉及到的信号均定义为32位宽。
high_period_cycles值计算的时候,涉及到了两级运算。先进行total_period_cycles * PWM_Duty_cycle运算,在边界情况,本运算的值会超过32位。*
当按照图示无特殊处理时,由于 high_period_cycles 的赋值操作中又紧接着有一个除以 1000000,这将导致最终除法的结果值不会超过32位。因此,尽管乘法操作可能会产生一个64位的结果,但由于紧接着的除法操作,Vivado编译器在优化时会考虑到这一点,并可能将中间值保存为32位宽的数,以避免不必要的资源消耗;
当对(total_period_cycles * PWM_Duty_cycle) / 1000000 ;中的1000000进行位数约束的时候, 即64'd1000000 。编译器会保留所有的64位作为最终运算值进行下一步运算.因为如果 1000000 是一个64位的常量,那么反推乘法操作 total_period_cycles *PWM_Duty_cycle 的值至少也会存在64位宽。因此会把(total_period_cycles * PWM_Duty_cycle)默认优化成64位。***
结论
- 减少直接使用*、/的符号运算
- 避免在单个语句中进行多步运算
- 如果避免不了多步运算,运算中涉及到的常数需要规定位宽,这样编译器对于中间值也会自动匹配
- 每一步运算均要考虑溢出问题,当位宽未定义足时,编译器默认会截取低位数据