【Verilog代码规范引起的国产安路编译器不能识别寄存器】
Verilog代码规范引起的编译器不能识别寄存器
- 问题简介
- 问题描述
- Xilinx Vivado编译器
- 国产安路TD_5.6.5_Release_119222编译器
- 解决办法
- 总结
问题简介
对不需要复位的寄存器放入了异步复位的always块中,不同编译器所生成的RTL硬件电路是不一样的。有的编译器会将其识别为不复位寄存器,而有的却将其视为输入到输出的组合逻辑;本文就这个问题进行阐述,最后给广大Verilog码农们一个建议(或许如今大家早已不在手撸代码,但这里算是给未来的自己一个回忆吧😀)。
问题描述
笔者目前使用了xilinx vivado编译器、国产安路编译器TD_5.6.5_Release_119222,因此针对这两个编译器对同一个verilog模块代码进行生成RTL视图做对比。
Xilinx Vivado编译器
现有一段CRC16CCITT-False Verilog代码中,该模块输入8bit数据,经过一个时钟周期产生出CRC16CCITT-False的CRC结果;由于输入带有一个数据有效信号,因此输出也需要带一个CRC校验结果有效的有效信号,具体代码如下:
`timescale 1ns / 1ps `default_nettype none `unconnected_drive pull0 //* 模块被例化未被驱动的IO直接驱动到GND module CRC16CCITT #( parameter INIT_BIT = 1'b1, parameter XOR_OUT_BIT = 1'b0, parameter IN_MOD = 1'b1, //*---- Data Input Mode (1 = Consecutive Data input 0 = Interval Data input) parameter REF_IN = "True", //*---- Data Reflect In ("True" = Reflect,"False" = No-Reflect) parameter REF_OUT = "True" //*---- Data Reflect Out("True" = Reflect,"False" = No-Reflect) )( input wire SysClk , input wire SysRst , input wire [07 : 00] iData , input wire iDataVali , output wire [15 : 00] oCrcDat , output wire oCrcDatVali ); //!--------------------------------------Reflect In--------------------------------------//! wire [07 : 00] RefDatIn; genvar i; generate for (i = 0;i < 8 ; i = i + 1 ) begin assign RefDatIn[i] = (REF_IN == "True") ? iData[7-i] : iData[i]; end endgenerate //!--------------------------------------------------------------------------------------//! //!--------------------------------------Reflect Out--------------------------------------//! reg [15 : 00] lfsr_q; reg [15 : 00] lfsr_c; reg CrcDatVali; wire [15 : 00] RefDatOut; wire [15 : 00] XorOutVal; wire [15 : 00] CrcResDat; assign oCrcDatVali = CrcDatVali; generate for (i = 0;i < 16 ;i = i + 1 ) begin assign RefDatOut[i] = lfsr_q[15-i] ; end endgenerate assign CrcResDat = (REF_OUT == "True") ? RefDatOut : lfsr_q; assign XorOutVal = XOR_OUT_BIT ? {16{1'b1}} : {16{1'b0}}; assign oCrcDat = CrcResDat ^ XorOutVal; //!---------------------------------------------------------------------------------------//! //!--------------------------------------CRC Cal--------------------------------------//! wire LfsrRstEn; assign LfsrRstEn = IN_MOD ? ~iDataVali : 1'b0; //*---- 16bit Input always @(*) begin lfsr_c[00] = lfsr_q[08] ^ lfsr_q[12] ^ RefDatIn[00] ^ RefDatIn[04]; lfsr_c[01] = lfsr_q[09] ^ lfsr_q[13] ^ RefDatIn[01] ^ RefDatIn[05]; lfsr_c[02] = lfsr_q[10] ^ lfsr_q[14] ^ RefDatIn[02] ^ RefDatIn[06]; lfsr_c[03] = lfsr_q[11] ^ lfsr_q[15] ^ RefDatIn[03] ^ RefDatIn[07]; lfsr_c[04] = lfsr_q[12] ^ RefDatIn[04]; lfsr_c[05] = lfsr_q[08] ^ lfsr_q[12] ^ lfsr_q[13] ^ RefDatIn[00] ^ RefDatIn[04] ^ RefDatIn[05]; lfsr_c[06] = lfsr_q[09] ^ lfsr_q[13] ^ lfsr_q[14] ^ RefDatIn[01] ^ RefDatIn[05] ^ RefDatIn[06]; lfsr_c[07] = lfsr_q[10] ^ lfsr_q[14] ^ lfsr_q[15] ^ RefDatIn[02] ^ RefDatIn[06] ^ RefDatIn[07]; lfsr_c[08] = lfsr_q[00] ^ lfsr_q[11] ^ lfsr_q[15] ^ RefDatIn[03] ^ RefDatIn[07]; lfsr_c[09] = lfsr_q[01] ^ lfsr_q[12] ^ RefDatIn[04]; lfsr_c[10] = lfsr_q[02] ^ lfsr_q[13] ^ RefDatIn[05]; lfsr_c[11] = lfsr_q[03] ^ lfsr_q[14] ^ RefDatIn[06]; lfsr_c[12] = lfsr_q[04] ^ lfsr_q[08] ^ lfsr_q[12] ^ lfsr_q[15] ^ RefDatIn[00] ^ RefDatIn[04] ^ RefDatIn[07]; lfsr_c[13] = lfsr_q[05] ^ lfsr_q[09] ^ lfsr_q[13] ^ RefDatIn[01] ^ RefDatIn[05]; lfsr_c[14] = lfsr_q[06] ^ lfsr_q[10] ^ lfsr_q[14] ^ RefDatIn[02] ^ RefDatIn[06]; lfsr_c[15] = lfsr_q[07] ^ lfsr_q[11] ^ lfsr_q[15] ^ RefDatIn[03] ^ RefDatIn[07]; end // always always @(posedge SysClk, posedge SysRst) begin CrcDatVali <= iDataVali; if(SysRst | (LfsrRstEn)) begin lfsr_q <= INIT_BIT ? {16{1'b1}} : {16{1'b0}};//*-----{16{1'b1}}; end else begin lfsr_q <= iDataVali ? lfsr_c : lfsr_q; end end // always //!----------------------------------------------------------------------------------//! endmodule `nounconnected_drive `default_nettype wire //!--------------------------------------End CRC16CCITT--------------------------------------上述代码中oCrcDatVali为数据输入有效信号iDataVali延迟一个时钟周期的信号,Xilinx Vivado生成的RTL视图如图所示:
从图中可以看出Xilinx Vivado生成一个CrcDatVali的寄存器,且并没任何复位操作,与设计意图一致。
国产安路TD_5.6.5_Release_119222编译器
同样使用国产安路TD_5.6.5_Release_119222编译器对上述代码进行编译生成RTL视图,如图所示
从生成的RTL视图中可以看出编译器将输入的iDataVali经过了两个Buff,然后直接输出到了oCrcDatVali端口中,这与代码中需要进行一个时钟周期的意思完全不一致。
此外笔者也是再芯片上进行在线调试,发现iDataVali与oCrcDatVali信号没有一个时钟周期的延迟。就这个问题,折腾了一天才最终定位此处。
解决办法
在代码中,对不需要复位的寄存器单独放入一个没有异步复位信号的alwasy块中进行处理,因此上述代码变为
`timescale 1ns / 1ps `default_nettype none `unconnected_drive pull0 //* 模块被例化未被驱动的IO直接驱动到GND module CRC16CCITT #( parameter INIT_BIT = 1'b1, parameter XOR_OUT_BIT = 1'b0, parameter IN_MOD = 1'b1, //*---- Data Input Mode (1 = Consecutive Data input 0 = Interval Data input) parameter REF_IN = "True", //*---- Data Reflect In ("True" = Reflect,"False" = No-Reflect) parameter REF_OUT = "True" //*---- Data Reflect Out("True" = Reflect,"False" = No-Reflect) )( input wire SysClk , input wire SysRst , input wire [07 : 00] iData , input wire iDataVali , output wire [15 : 00] oCrcDat , output wire oCrcDatVali ); //!--------------------------------------Reflect In--------------------------------------//! wire [07 : 00] RefDatIn; genvar i; generate for (i = 0;i < 8 ; i = i + 1 ) begin assign RefDatIn[i] = (REF_IN == "True") ? iData[7-i] : iData[i]; end endgenerate //!--------------------------------------------------------------------------------------//! //!--------------------------------------Reflect Out--------------------------------------//! reg [15 : 00] lfsr_q; reg [15 : 00] lfsr_c; reg CrcDatVali; wire [15 : 00] RefDatOut; wire [15 : 00] XorOutVal; wire [15 : 00] CrcResDat; assign oCrcDatVali = CrcDatVali; generate for (i = 0;i < 16 ;i = i + 1 ) begin assign RefDatOut[i] = lfsr_q[15-i] ; end endgenerate assign CrcResDat = (REF_OUT == "True") ? RefDatOut : lfsr_q; assign XorOutVal = XOR_OUT_BIT ? {16{1'b1}} : {16{1'b0}}; assign oCrcDat = CrcResDat ^ XorOutVal; //!---------------------------------------------------------------------------------------//! //!--------------------------------------CRC Cal--------------------------------------//! wire LfsrRstEn; assign LfsrRstEn = IN_MOD ? ~iDataVali : 1'b0; //*---- 16bit Input always @(*) begin lfsr_c[00] = lfsr_q[08] ^ lfsr_q[12] ^ RefDatIn[00] ^ RefDatIn[04]; lfsr_c[01] = lfsr_q[09] ^ lfsr_q[13] ^ RefDatIn[01] ^ RefDatIn[05]; lfsr_c[02] = lfsr_q[10] ^ lfsr_q[14] ^ RefDatIn[02] ^ RefDatIn[06]; lfsr_c[03] = lfsr_q[11] ^ lfsr_q[15] ^ RefDatIn[03] ^ RefDatIn[07]; lfsr_c[04] = lfsr_q[12] ^ RefDatIn[04]; lfsr_c[05] = lfsr_q[08] ^ lfsr_q[12] ^ lfsr_q[13] ^ RefDatIn[00] ^ RefDatIn[04] ^ RefDatIn[05]; lfsr_c[06] = lfsr_q[09] ^ lfsr_q[13] ^ lfsr_q[14] ^ RefDatIn[01] ^ RefDatIn[05] ^ RefDatIn[06]; lfsr_c[07] = lfsr_q[10] ^ lfsr_q[14] ^ lfsr_q[15] ^ RefDatIn[02] ^ RefDatIn[06] ^ RefDatIn[07]; lfsr_c[08] = lfsr_q[00] ^ lfsr_q[11] ^ lfsr_q[15] ^ RefDatIn[03] ^ RefDatIn[07]; lfsr_c[09] = lfsr_q[01] ^ lfsr_q[12] ^ RefDatIn[04]; lfsr_c[10] = lfsr_q[02] ^ lfsr_q[13] ^ RefDatIn[05]; lfsr_c[11] = lfsr_q[03] ^ lfsr_q[14] ^ RefDatIn[06]; lfsr_c[12] = lfsr_q[04] ^ lfsr_q[08] ^ lfsr_q[12] ^ lfsr_q[15] ^ RefDatIn[00] ^ RefDatIn[04] ^ RefDatIn[07]; lfsr_c[13] = lfsr_q[05] ^ lfsr_q[09] ^ lfsr_q[13] ^ RefDatIn[01] ^ RefDatIn[05]; lfsr_c[14] = lfsr_q[06] ^ lfsr_q[10] ^ lfsr_q[14] ^ RefDatIn[02] ^ RefDatIn[06]; lfsr_c[15] = lfsr_q[07] ^ lfsr_q[11] ^ lfsr_q[15] ^ RefDatIn[03] ^ RefDatIn[07]; end // always always @(posedge SysClk) begin //*---- Not Reset CrcDatVali <= iDataVali; end always @(posedge SysClk, posedge SysRst) begin if(SysRst | (LfsrRstEn)) begin lfsr_q <= INIT_BIT ? {16{1'b1}} : {16{1'b0}};//*-----{16{1'b1}}; end else begin lfsr_q <= iDataVali ? lfsr_c : lfsr_q; end end // always //!----------------------------------------------------------------------------------//! endmodule `nounconnected_drive `default_nettype wire //!--------------------------------------End CRC16CCITT--------------------------------------最后使用国产安路TD_5.6.5_Release_119222编译器对修改后的代码生成RTL如图所示
经过修改代码后,得到的RTL视图与代码设计思路一致,至此解决了输入与输出没有延时一个时钟周期问题。
总结
为了保证所设计的代码能在多平台运行,因此需要规范代码:
1、将不需要异步复位的寄存器放在一个没有任何复位信号的always块中
2、带有异步复位always块中所有信号都需要对其复位处理
3、尽量少用异步复位always块
在此呼吁国产安路加油!!!(修复该Bug)
