FPGA延时(Verilog HDL)

 简介:

    可以在任意时刻启动,可以重复启动,延时时长可调,单位可切换(ms/us),在50MHz时钟下的延时范围是1ms-85899ms/1us-85899us。                                                                                                                                                                            

 源代码和modelsim仿真代码:

 module delay

 //#(parameter N )  //可以延时N*1ms/us

  (input clk,rst_n,
   input start,     //start上升沿有效
   input delay_unit, //延时单位,high:ms/low:us
   output finish,finish_pose); //finish上升沿有效
   reg start_reg0,start_reg1;  //start两级缓存,用于边沿检测
   reg finish_reg0,finish_reg1;  //finish两级缓存
   reg [31:0]cnt;  //固定32位宽计数
   reg [31:0]cnt_full;
   reg restart; //重新开始
   wire start_pose,full;
   always @(posedge clk or negedge rst_n)
        begin
            if(!rst_n)
               begin 
                   cnt <= 32'd0;
                   cnt_full <= 32'd10; //避免一开始finish置位
                   restart <= 1'b0;
                   start_reg0 <= 1'b0; start_reg1 <= 1'b0; 
                   finish_reg0 <= 1'b0; finish_reg1 <= 1'b0;
               end
           else
              begin
                  start_reg0 <= start; start_reg1 <= start_reg0;
                  finish_reg1 <= finish_reg0;  
                  /**检测计时单位**/
                  if(delay_unit)  
                     cnt_full <= 32'd50_000*2-32'd2;  //计时2ms  实际例化时用N代替: 32'd50_000*N-32'd2
                  else
                     cnt_full <= 32'd50*2-32'd2;      //计时2us
                 /***************/
                /**是否重新开始**/
                if(start_pose)  //检测到起始时刻 
                    restart <= 1'b1;  
               /****计时完成****/
               else if(full)  //延时结束
                  begin
                      cnt <= 32'd0;  //cnt归零
                      finish_reg0 <= 1'b1;  //finish响应
                      restart <= 1'b0;
                  end
             /***************/
             /****计时开始****/
             else if(restart) 
                begin
                    finish_reg0 <= 1'b0;    //新一轮延时finish复位
                    cnt <= cnt+1'b1;
                end
           /***************/
           /**等待新一轮计时**/
           else 
              begin
                  cnt <= cnt;
                  finish_reg0 <= finish_reg0;
                  restart <= restart;
              end
    end  
end
 
  assign start_pose = (~start_reg1&start_reg0)?1'b1:1'b0;  //start上升沿检测
  assign finish_pose = (~finish_reg1&finish_reg0)?1'b1:1'b0;  //finish上升沿检测
  assign full = (cnt_full-cnt==0)?1'b1:1'b0;   //检测是否计满

  assign finish = finish_reg0;

endmodule

/**************************************************************************************************/                                     /***************************************modelsim********************************************/  

`timescale 1ns/1ps
module delay_tb();
   reg clk,rst_n;
   reg start;
   wire finish,finish_pose;
  
  delay delay_u0
  (.clk(clk),
   .rst_n(rst_n),
   .start(start),
   .delay_unit(1'b1),
   .finish(finish),
   .finish_pose(finish_pose));
   //defparam delay_u0.N = 2; //延时2ms
  
  initial
    begin
clk = 1'b0;
        rst_n = 1'b0;
        start = 1'b0;
#1000 rst_n = 1'b1;
#4010 start = 1'b1;                                                                                                                                                                 #50     start = 1'b0;                                                                                                                                                            end

  always #10 clk = ~clk;
endmodule

思路:

          start端口给上升沿启动延时,延时结束端口finish置位(重新启动延时后复位)、finish的上升沿检测在模块内部已做好(端口finish_pose),直接调用即可,端口delay_unit置高选择ms,置低选择us,#(parameter N )”是计时时长,例如计时8ms: "N = 8,  .delay_unit(1'b1)",实际例化时只需“defparam  例化名.N = 数值”。因为modelsim无法识别这种调用,所以直接用数值代替N进行测试。边沿检测会消耗两个时钟周期,所以cnt_full需要减2,并且将finish_reg0直接连到finish输出端口而不是用finish_reg1连接finish。

延时2ms测试的起始时刻(5010ns)和结束时刻(2005010ns):