• 一种简单的频率测试方法及Verilog实现


    对于频率测量,有很多的测试方法,这里我们介绍一种采用基准频率计数的方法测试频率。该测试方法非常容易理解。首先设计两个计数器BASE_CLK_CNT和CLK_TST_CNT,基准时钟BASE_CLK作为计数器BASE_CLK_CNT的输入时钟,被测信号CLK_TST作为CLK_TST_CNT的输入时钟。两个计数器同时开始计数,当CLK_TST_CNT计数到n时计数结束,这个时候根据BASE_CLK_CNT的值m和BASE_CLK的频率,可以计算出CLK_TST的频率。

    在上述的测频方法中,两个计数器同时开始计数是整个设计的难点。由于CLK_TST和BASE_CLK是两个异步信号,如果不对信号做特定的处理,就很难做到两个计数器同时开始计数。而且由于两个信号时异步信号,所以在做电路设计时,如果不做相应的处理,容易引起系统的不稳定。

    这里我们采用的方法是,用最基本的CDC电路来处理CLK_TST,使之转换为与BASE_CLK在同一时钟域的信号,然后再进行频率测量。这里最基本的CDC电路也就是把CLK_TST用BASE_CLK锁两拍,然后生成新的CLK_TST信号。下面是具体的verilog实现:CLK_TST为输入信号,CLK_TST_2Q为输出的与BASE_CLK同时钟域的信号。经过这样的处理,整个频率测量电路的设计就可以在同一个时钟域中进行。

    reg CLK_TST_Q;

    reg CLK_TST_2Q;

    always @(posedge BASE_CLK or negedge RESET)

    begin

    if(~RESET) begin

    CLK_TST_Q <= 1'b0;

    CLK_TST_2Q <= 1'b0;

    end

    else begin

    CLK_TST_Q <= CLK_TST;

    CLK_TST_2Q <= CLK_TST_Q;

    end

    end

    不过这样做的结果就是引入测量误差,其绝对值小于4个BASE_CLK周期(测试周期开始的点0~2 BASE_CLK周期,测试周期结束的点0~2BASE_CLK周期,综合起来,应该小于4个BASE_CLK周期,一般情况下,应该是小于2个BASE_CLK周期)。下图是上述电路的仿真波形:

    clip_image002

    通常情况下,带来的误差是两条竖线之间的长度,比较差的情况下,误差会再加上一个BASE_CLK的周期。

    CLK_TST_2Q的被测信号与BASE_CLK有相同的上升沿,所以我们在CLK_TST_2Q上升沿的时候让两个计数器都开始计数。当CLK_TST_CNT计数到n时,两个计数器同时停止计数。而对于n的选取,则要根据测试精度,CLK_TST的稳定度,BASE_CLK的频率等各方面的要求来设定。不过建议数值最好选用2*x。这样便于后继的数据处理,在计算CLK_TST的时候,可以直接对BASE_CLK_CNT进行移位处理。

    采用一个简单的状态机来实现这个测频电路。状态机有三个状态,FT_IDLE,FT_TST和FT_DOUT。系统复位或者一次测量结束以后,状态机进入FT_IDLE状态,当TST_EN信号有效的时候,开始进行频率测量。当CLK_TST_CNT的计数值达到n(在该例子中,我们选择64)的时候,进入FT_DOUT状态。FT_DOUT状态持续的时间,由一个小的计数器的计数值来决定,我们可以通过改变这个计数器的值来让数据输出的时间符合后继处理电路的要求。

    这个例程中直接给出BASE_CLK_CNT的值,CLK_TST的频率值还需要换算电路做进一步的处理,但这里不再给出。

    下面是测频电路的verilog实现:

    module FRE_TST(

    BASE_CLK, //base clock,default is 100MHz

    CLK_TST, //clock on test

    RESET, //async system reset

    TST_EN, //test enable signal

    DATA_OUT, //24bit test data out to uart

    DATA_OUT_EN //24bit test data out enable

    );

    input BASE_CLK;

    input CLK_TST;

    input RESET;

    input TST_EN;

    output [23:0] DATA_OUT;

    output DATA_OUT_EN;

    parameter FT_IDLE = 3'b001,

    FT_TST = 3'b010,

    FT_DOUT = 3'b100;

    reg [2:0] FTSTSM, FTSTSMNXT;

    wire PHASE_FT_IDLE = FTSTSM[0];

    wire PHASE_FT_TST = FTSTSM[1];

    wire PHASE_FT_DOUT = FTSTSM[2];

    wire PHASENXT_FT_DOUT = FTSTSMNXT[2];

    wire PHASENXT_FT_TST = FTSTSMNXT[1];

    reg CLK_TST_Q;

    reg CLK_TST_2Q;

    always @(posedge BASE_CLK or negedge RESET)

    begin

    if(~RESET)

    begin

    CLK_TST_Q <= 1'b0;

    CLK_TST_2Q <= 1'b0;

    end

    else

    begin

    CLK_TST_Q <= CLK_TST;

    CLK_TST_2Q <= CLK_TST_Q;

    end

    end

    reg [23:0] BASE_CLK_CNT;

    reg [11:0] CLK_TST_CNT;

    always @(posedge CLK_TST_2Q or negedge RESET) begin

    if(~RESET)

    CLK_TST_CNT <= 12'b0;

    else if(PHASE_FT_IDLE)

    CLK_TST_CNT <= 12'b0;

    else if(CLK_TST_CNT == 12'b0000_0100_0001)

    CLK_TST_CNT <= 12'b0;

    else if(PHASE_FT_TST )

    CLK_TST_CNT <= CLK_TST_CNT + 12'b1;

    else

    CLK_TST_CNT <= CLK_TST_CNT;

    end

    wire TST_END_TMP = (CLK_TST_CNT == 12'b0000_0100_0001) ; //count 65

    reg TST_END_TMP_Q;

    always @(posedge BASE_CLK or negedge RESET)

    begin

    if(~RESET)

    TST_END_TMP_Q <= 1'b0;

    else

    TST_END_TMP_Q <= TST_END_TMP;

    end

    wire TST_END = ~TST_END_TMP_Q & TST_END_TMP;

    reg [2:0] data_out_cnt;

    always @(posedge BASE_CLK or negedge RESET) begin

    if(~RESET)

    data_out_cnt <= 3'b0;

    else if(PHASE_FT_IDLE)

    data_out_cnt <= 3'b0;

    else if(PHASE_FT_DOUT)

    data_out_cnt <= data_out_cnt + 3'b1;

    else

    data_out_cnt <= data_out_cnt;

    end

    wire DATA_OUT_END;

    assign DATA_OUT_END = (data_out_cnt == 3'd5);

    always @(posedge BASE_CLK or negedge RESET) begin

    if(~RESET)

    BASE_CLK_CNT <= 24'h0;

    else if( PHASE_FT_TST )

    BASE_CLK_CNT <= BASE_CLK_CNT + 24'b1;

    else if(PHASE_FT_IDLE)

    BASE_CLK_CNT <= 24'h0;

    else

    BASE_CLK_CNT <= BASE_CLK_CNT;

    end

    always @(posedge BASE_CLK or negedge RESET) begin

    if(~RESET)

    FTSTSM <= FT_IDLE;

    else

    FTSTSM <= FTSTSMNXT;

    end

    always @(FTSTSM or TST_EN or CLK_TST_2Q or TST_END or DATA_OUT_END) begin

    FTSTSMNXT = FTSTSM;

    case (FTSTSM)

    FT_IDLE:

    if(TST_EN & CLK_TST_2Q)

    FTSTSMNXT = FT_TST;

    else

    FTSTSMNXT = FT_IDLE;

    FT_TST:

    if(TST_END)

    FTSTSMNXT = FT_DOUT;

    else

    FTSTSMNXT = FT_TST;

    FT_DOUT:

    if(DATA_OUT_END)

    FTSTSMNXT = FT_IDLE;

    else

    FTSTSMNXT = FT_DOUT;

    default:

    FTSTSMNXT = FT_IDLE;

    endcase

    end

    assign DATA_OUT_EN = PHASE_FT_DOUT;

    assign DATA_OUT = BASE_CLK_CNT;

    endmodule

    下面是仿真的结果:

    BASE_CLK的周期是5ns,CLK_TST的周期是1000ns。

    clip_image003

  • 相关阅读:
    short s1 = 1; s1 = s1 + 1;有什么错? short s1 = 1; s1 += 1;有什么错?
    SpringMVC常用的注解有哪些?
    Spring支持的ORM?
    什么是代理?
    一对一、一对多的关联查询 ?
    iHTML 的 form 提交之前如何验证数值文本框的内容全部为数字?
    解释JDBC抽象和DAO模块?
    Bean 工厂和 Application contexts 有什么区别?
    GitHub的注册
    HTML的学习
  • 原文地址:https://www.cnblogs.com/luxiaolai/p/2991335.html
Copyright © 2020-2023  润新知