如何使用Verilog计算输入信号的频率?

如何使用Verilog计算输入信号的频率?,verilog,system-verilog,Verilog,System Verilog,我试图计算来自直流电机的反馈信号的频率,以找出电机的速度(以转/秒为单位),或者至少是它的旋转速度。来自电机的反馈信号是方波,192个正边缘相当于一圈 我用50兆赫兹作为我的输入时钟信号。我试图设计一个模块,将50兆赫的时钟和电机反馈作为输入,并输出电机的速率/速度。我已经挣扎了一段时间,因为Verilog不允许我在多个always块中使用一个变量。请帮我完成这个项目。谢谢大家! 第一步是决定是测量信号的频率还是周期。正如Hida所指出的,通过计算每个输入上升沿之间的50MHz脉冲,可以测量信号

我试图计算来自直流电机的反馈信号的频率,以找出电机的速度(以转/秒为单位),或者至少是它的旋转速度。来自电机的反馈信号是方波,192个正边缘相当于一圈


我用50兆赫兹作为我的输入时钟信号。我试图设计一个模块,将50兆赫的时钟和电机反馈作为输入,并输出电机的速率/速度。我已经挣扎了一段时间,因为Verilog不允许我在多个always块中使用一个变量。请帮我完成这个项目。谢谢大家!

第一步是决定是测量信号的频率还是周期。正如Hida所指出的,通过计算每个输入上升沿之间的50MHz脉冲,可以测量信号周期。然后,您必须将一个常数除以该周期才能获得RPM或RPS

但是RPM与脉冲信号的频率成线性关系,而不是与周期成线性关系。因此,通过计算固定周期内的脉冲数,更容易直接测量

接下来,您必须决定测量系统的速度。如果你计算一整秒钟的脉冲数,你会得到RPS*192,这是很好的精度,但每秒只输出一次,可能太慢了。如果你数到1/192秒,你会直接得到RPS,但可能会失去精度。下面的示例测量1/32秒,得到6*RPS或RPM/10。您应该选择一个测量周期以满足要求,并调整位宽度和常数以适应要求。通过选择正确的采样周期,您甚至可以直接获得所需单位的输出,或者至少是它们的方便倍数

  • 将输入时钟从50MHz除以所需的采样率(周期)。在本例中,使用从1到1562450(即50000000/32)的计数器创建32Hz周期。或者使用从1562449到0的向下计数器

  • 跟踪输入信号以找到其上升沿。如果它在一个50Mhz时钟上为低,在下一个时钟上为高,则它是脉冲的上升沿。由于输入信号相对于50MHz时钟的速度非常慢,因此将其视为数据并进行异步采样。注意:在实际设计中,可能需要对输入信号进行去抖动和注册,因为它与50MHz时钟是异步的。否则,采样的任何噪声或反弹将向计数器添加虚假脉冲。本例假设信号调节已经完成

  • 每次计数器达到零,一个周期结束。注册当前边缘计数,使其在下一周期内不会更改。然后将边数重置回零,并再次开始为下一周期计算边数

  • 另一方面,您可以看到测试台和模块中的某些位置在多个always块(例如“计数器”)中使用信号。你可以在Verilog模拟器上玩这个

    警告:这只是一个非常简单的例子。生产代码将明确所有位宽度,将为周期结束信号分配一根导线,而不是复制逻辑。时钟计数器相当宽,可能需要预先缩放,以及许多其他细节。但是它在模拟器中运行,并且应该在50Mhz的FPGA中合成并工作良好


    模块rpm\u计数器(
    输入时钟,//50Mhz
    输入信号,//0至1Mhz信号
    输出[15:0]rpmout//RPM/10,例如3000RPM时为300
    );
    //计数器==0@32 Hz
    reg[20:0]计数器=0;
    始终@(posedge clk)开始
    如果(计数器==0)
    
    计数器第一步是决定是测量信号的频率还是周期。正如Hida所指出的,通过计算每个输入上升沿之间的50MHz脉冲,可以测量信号周期。然后,您必须将一个常数除以该周期才能获得RPM或RPS

    但是RPM与脉冲信号的频率成线性关系,而不是与周期成线性关系。因此,通过计算固定周期内的脉冲数,更容易直接测量

    接下来,您必须决定测量系统的速度。如果你计算一整秒钟的脉冲数,你会得到RPS*192,这是很好的精度,但每秒只输出一次,可能太慢了。如果你数到1/192秒,你会直接得到RPS,但可能会失去精度。下面的示例测量1/32秒,得到6*RPS或RPM/10。您应该选择一个测量周期以满足要求,并调整位宽度和常数以适应要求。通过选择正确的采样周期,您甚至可以直接获得所需单位的输出,或者至少是它们的方便倍数

  • 将输入时钟从50MHz除以所需的采样率(周期)。在本例中,使用从1到1562450(即50000000/32)的计数器创建32Hz周期。或者使用从1562449到0的向下计数器

  • 跟踪输入信号以找到其上升沿。如果它在一个50Mhz时钟上为低,在下一个时钟上为高,则它是脉冲的上升沿。由于输入信号相对于50MHz时钟的速度非常慢,因此将其视为数据并进行异步采样。注意:在实际设计中,可能需要对输入信号进行去抖动和注册,因为它与50MHz时钟是异步的。否则,采样的任何噪声或反弹将向计数器添加虚假脉冲。本例假设信号调节已经完成

  • 每次计数器达到零,一个周期结束。注册当前边缘计数,使其在下一周期内不会更改。然后将边数重置回零,并再次开始为下一周期计算边数

  • 另一方面,您可以看到测试台和模块中的某些位置在多个位置使用信号
    module rpm_counter(
        input clk, // 50Mhz
        input signal, // 0 to 1Mhz signal
        output [15:0]rpmout // RPM/10 e.g. 300 for 3000RPM
    );
    
    // counter == 0 @ 32 Hz
    reg [20:0]counter = 0;
    always @(posedge clk) begin
        if(counter == 0)
            counter <= 1562500 - 1; // 3-2-1-0 is period of 4, not 3
        else
            counter <= counter - 1;
    end
    
    // simple flip/flip to store last value of signal
    reg last = 0;
    always @(posedge clk) begin
        last <= signal;
    end
    
    // when counter == 0, register current count and reset edge counter
    // otherwise increment edge counter for each rising edge of the pulse
    reg [15:0]edges = 0;
    reg [15:0]outreg = 0;
    always @(posedge clk) begin
        if(counter == 0) begin
            outreg <= edges;
            edges <= 0;
        end
        // detect edge, was low and now high
        else if(~last & signal)
            edges <= edges + 1;
    end
    
    assign rpmout = outreg;
    
    endmodule
    
    module cheesy_testbench;
    
    wire [15:0]out;
    initial
      begin
        #100000000;
        $display("0x%x is %d RPM", out, out*10);
        $finish;
      end
    
    reg clk50 = 1;
    always
      #10 clk50 = ~clk50; // 10+10 = 20ns = 50Mhz
    
    reg sig = 1;
    always
      #45208 sig = ~sig; // 45208ns*2 period @3456RPM
    
    rpm_counter UUT (
      .clk(clk50),
      .signal(sig),
      .rpmout(out)
    );
    
    endmodule