时序逻辑中阻塞赋值引起的仿真问题
最近写了一个分频的代码,但是在仿真中遇到了一个小问题,具体代码如下: 分频代码(该模块功能为将50MHz时钟分频为10Hz) module div_fre( //-------------------------------Port Declaration--------------------------------- output o_clk_100Hz, input i_sys_clk_50MHz, input i_sys_reset ); //-------------------------------Define Parameter--------------------------------- parameter div_cnt_max = 23'd4999999; parameter div_cnt_half = 23'd2499999; //-------------------------------Internal Registers------------------------------- reg [22:0] div_cnt = 23'd0; reg fall_en = 1'b0; reg rise_en = 1'b0; reg clk_temp = 1'b0; //-------------------------------Internal Wire------------------------------------ //-------------------------------Code Starts Here--------------------------------- always @ (posedge i_sys_clk_50MHz or posedge i_sys_reset) begin if(i_sys_reset) div_cnt <= 23'd0; else if(div_cnt == div_cnt_max) div_cnt <= 23'd0; else div_cnt <= div_cnt + 1; end /* always @ (posedge i_sys_clk_50MHz or posedge i_sys_reset) begin if(i_sys_reset) fall_en <= 1'b0; else begin if(div_cnt == div_cnt_half - 1) fall_en <= 1'b1; else fall_en <= 1'b0; end end always @ (posedge i_sys_clk_50MHz or posedge i_sys_reset) begin if(i_sys_reset) rise_en <= 1'b0; else begin if(div_cnt == div_cnt_max - 1) rise_en <= 1'b1; else rise_en <= 1'b0; end end always @ (posedge i_sys_clk_50MHz or posedge i_sys_reset) begin if(i_sys_reset) clk_temp <= 1'b1; else begin if(fall_en) clk_temp <= 1'b0; else if(rise_en) clk_temp <= 1'b1; else clk_temp <= clk_temp; end end */ always@ (posedge i_sys_clk_50MHz or posedge i_sys_reset) begin fall_en = (i_sys_reset) ? (1'b0) : ((div_cnt == div_cnt_half - 1) ? (1'b1) : (1'b0)); end always @ (posedge i_sys_clk_50MHz or posedge i_sys_reset) begin rise_en = (i_sys_reset) ? (1'b0) : ((div_cnt == div_cnt_max - 1) ? (1'b1) : (1'b0)); end always @ (posedge i_sys_clk_50MHz or posedge i_sys_reset) begin clk_temp = (i_sys_reset) ? (1'b1) : ((fall_en) ? (1'b0) : ((rise_en) ? (1'b1) : (clk_temp))); end assign o_clk_100Hz = clk_temp; endmodule 激励文件为: module div_fre_tb; // Inputs reg i_sys_clk_50MHz; reg i_sys_reset; // Outputs wire o_clk_100Hz; parameter HALF_PERIOD = 10; // Instantiate the Unit Under Test (UUT) div_fre uut ( .o_clk_100Hz(o_clk_100Hz), .i_sys_clk_50MHz(i_sys_clk_50MHz), .i_sys_reset(i_sys_reset) ); initial begin // Initialize Inputs i_sys_clk_50MHz = 1; i_sys_reset = 1; // Wait 100 ns for global reset to finish #100; i_sys_reset = 0; // Add stimulus here end initial begin #HALF_PERIOD forever #HALF_PERIOD i_sys_clk_50MHz = ~i_sys_clk_50MHz; end endmodule 功能仿真波形为:
从仿真波形可以看出来clk_temp跳变的时刻跟我所设计的跳变时刻不太符合,本来应该是i_sys_clk_50MHz的上升沿去采fall_en的高电平来产生clk_temp = 0,可是从仿真波形中可以看到clk_temp信号是在fall_en变高的一开始就进行了变化。我通过chipscope在线调试,将程序下载到FPGA中进行在线调试却是正常的,可以看到clk_temp在fall_en=1的结束位置进行了跳变。为什么功能仿真会出现这样的问题呢? 我重新看了代码,并重新用传统的always块的方法重新写了程序(具体为程序中注释的部分),如下: always @ (posedge i_sys_clk_50MHz or posedge i_sys_reset) begin if(i_sys_reset) fall_en <= 1'b0; else begin if(div_cnt == div_cnt_half - 1) fall_en <= 1'b1; else fall_en <= 1'b0; end end always @ (posedge i_sys_clk_50MHz or posedge i_sys_reset) begin if(i_sys_reset) rise_en <= 1'b0; else begin if(div_cnt == div_cnt_max - 1) rise_en <= 1'b1; else rise_en <= 1'b0; end end always @ (posedge i_sys_clk_50MHz or posedge i_sys_reset) begin if(i_sys_reset) clk_temp <= 1'b1; else begin if(fall_en) clk_temp <= 1'b0; else if(rise_en) clk_temp <= 1'b1; else clk_temp <= clk_temp; end end 通过仿真,波形如下:
可以看到仿真波形跟想要的是一样的。 通过代码对比,发现问题出在了 always@ (posedge i_sys_clk_50MHz or posedge i_sys_reset) begin fall_en = (i_sys_reset) ? (1'b0) : ((div_cnt == div_cnt_half - 1) ? (1'b1) : (1'b0)); end always @ (posedge i_sys_clk_50MHz or posedge i_sys_reset) begin rise_en = (i_sys_reset) ? (1'b0) : ((div_cnt == div_cnt_max - 1) ? (1'b1) : (1'b0)); end always @ (posedge i_sys_clk_50MHz or posedge i_sys_reset) begin clk_temp = (i_sys_reset) ? (1'b1) : ((fall_en) ? (1'b0) : ((rise_en) ? (1'b1) : (clk_temp))); end 这段代码中的阻塞赋值。我开始认为这段代码中每个always块中只有一句话,可以不用考虑阻塞和非阻塞赋值。但是出了问题锁定到这个地方的时候,详细想一想,由于是采用阻塞赋值,在时钟上升沿,两个always块是并行执行的。在verilog中两个always块的执行可以按照任何顺序执行。而这种先后顺序导致的仿真结果也是不一样的。所以以后写代码需要在此加以注意。 By:冰风溪谷 |