zoukankan      html  css  js  c++  java
  • 时序逻辑中阻塞赋值引起的仿真问题

    最近写了一个分频的代码,但是在仿真中遇到了一个小问题,具体代码如下:

    分频代码(该模块功能为将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:冰风溪谷

  • 相关阅读:
    c#泛型的使用
    如何调试由于heap corruption导致的程序崩溃的简单示例
    Windows的SEH机理简要介绍
    利用定制行为扩展WCF之利用MessageInsepctor behaviourExtension扩展WCF行为(自定义消息头)
    欧拉函数
    JZOJ.1349 最小公约数
    关于扩展中国剩余定理(excrt)的证明与拙见
    【USACO 2021 US Open, Gold】United Cows of Farmer John & JZOJ7220
    线性求逆元
    【USACO 2021 January Contest, Platinum】Problem 1. Sum of Distances JZOJ.7241
  • 原文地址:https://www.cnblogs.com/icelyb24/p/2221617.html
Copyright © 2011-2022 走看看