FPGA频率测量的三种⽅法(直接测量法,间接测量法,等精
度测量法)
1、FPGA频率测量?
频率测量在电⼦设计和测量领域中经常⽤到,因此对频率测量⽅法的研究在实际⼯程应⽤中具有重要意义。
通常的频率测量⽅法有三种:直接测量法,间接测量法,等精度测量法。
2、直接测量法
2.1、⽅法
直接测量法也叫频率测量法,即在固定在时间t内对被测信号的脉冲数进⾏计数,然后求出单位时间内的脉冲数,即为被测信号的频率。
下图中的信号分别为:
sys_clk:系统的基准时钟
gate:根据基准时钟⽣成的闸门信号,⽤于⽣成⼀个固定的时间(例如1s,⽅便计算)
clk_fx:被测信号
gate是在基准时钟下⽣成的固定时间信号,它持续的时间 Tg = sys_clk ✖ 计数个数N(可设置);在gate持续为⾼的时间内,可使⽤被测信号clk_fx对其进⾏计数,计数个数为cnt(图中为5),则cnt个被测信号的周期即为gate时长。
此种⽅法的本质是:同样的时间内分别使⽤两种时钟计时,则有  Tg = Tfx---- Tsys_clk ✖ 计数个数N
= Tclk_fx ✖ cnt,公式变换后:  clk_fx = cnt ✖ sys_clk / 计数个数N (其中clk_fx为待测信号频率,sys_clk为基准时钟频率)
2.2、误差分析
从图可以看出,在gate为⾼电平期间,被测信号实际上差不多有六个周期被囊括在内,但是因为被测信号是相对与系统的异步信号,相位不同,第⼀个周期⽆法被采样,所以实际采样为5,这样造成的误差为⼀个被测信号周期。可以预见,这种测量⽅法带来的测量误差即为⼀个被测信号周期。
那么理论上测得的准确频率:clk_fxe = cnt ✖ sys_clk / 计数个数N----理论上cnt⽆误差
实际上测量的频率值:clk_fx = cnt±1 ✖ sys_clk / 计数个数N----cnt会存在⼀个周期的测量误差
测量误差 = |(clk_fxe - clk_fx)/ clk_fxe |  ✖ 100% = 1 / cnt ✖ 100%
所以测得的cnt越⼤,那么测出来的误差值就⼩,⽽cnt越⼤则代表被测信号的频率越⾼,所以可以推断该种测量⽅法适合测量⾼频信号;此外,选择的闸门时间越长则被测信号的个数越多,同样测量就越精确,但是增⼤闸门时间⼜会带来测量时间过长的问题,需要依据具体需求进⾏取舍。
2.3、Verilog代码
Verilog源码如下:
闸门时间设定为0.5s,⾮闸门时间也0.5s,则每1秒更新⼀次测量数据
使⽤计数器⽣成闸门时间,闸门时间取反得到⾮闸门时间
在闸门时间对被测信号计数
在⾮闸门时间更新测量数据
使⽤parameter定义参数,⽅便调⽤修改
//直接测量法(⾼频)
module cymometer_direct(
input    sys_clk  ,  //基准时钟,设计为50M(可更改)
input    sys_rst_n ,  //复位信号,低电平有效
input    clk_fx  ,  //待测信号
output reg [31:0] fre    //测量结果
);
紧凑型车和小型车的区别parameter TIME_SYS  = 20 ;    //系统时钟周期:20ns--频率=50MHz
parameter TIME_GATE = 500_000_000 ;  //500ms闸门设置的时间,单位:ns
localparam N = TIME_GATE / TIME_SYS;  //⽣成闸门需要计数的个数
reg  gate  ;    //闸门
reg [31:0]  cnt_gate ;    //⽤于⽣成闸门的计数器
reg [31:0]  cnt_fx  ;    //闸门时间内对被测信号计数
wire  gate_n  ;    //闸门取反,⽤于在⾮闸门时输出测得的频率值
assign gate_n = ~gate ;    //闸门取反,⽤于在⾮闸门时输出测得的频率值
//分频计数器,闸门时间设定为1ms,则每2ms测量⼀次
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
cnt_gate <=0;
gate <=0;
end
else begin
if(cnt_gate == N-1)begin
cnt_gate <= 0;
gate <= ~gate;
end
else
cnt_gate<=cnt_gate+1;
end
end
//闸门时间内对被测信号计数
always @(posedge clk_fx or negedge sys_rst_n)begin
if(!sys_rst_n)
cnt_fx <= 0;
else if(gate)
cnt_fx <= cnt_fx + 1;
else
cnt_fx <= 0;
end
//在⾮闸门时输出测得的频率值
always @(posedge gate_n or negedge sys_rst_n)begin
if(!sys_rst_n)
fre <= 0;
else
//TIME_GATE/cnt_fx=规定时间/被测信号个数=被测信号周期,取倒数即为频率
fre <= 1000_000_000/TIME_GATE * cnt_fx;
end
endmodule
2.4、仿真分析
Testbench:
设计被测信号周期为489*2=978ns,则其理论频率为1/978ns=1022494.88Hz;
`timescale 1ns/1ns //时间单位/精度
//------------<;模块及端⼝声明>----------------------------------------
module tb_cymometer_direct();
reg  sys_clk;
reg        sys_rst_n;
酷派怎么样reg        clk_fx;奥迪a1两门
wire [31:0] fre;
// defparam cymometer_direct_inst.TIME_GATE = 500_000;          //地址位宽奔驰e级论坛
//------------<;例化被测试模块>----------------------------------------
cymometer_direct cymometer_direct_inst(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.clk_fx  (clk_fx  ),
.fre        (fre  )
);
//------------<;设置初始测试条件>----------------------------------------
initial begin
sys_clk = 1'b0;    //初始时钟为0
sys_rst_n <= 1'b0;    //初始复位
clk_fx <= 1'b0;
#5        //5个时钟周期后
sys_rst_n <= 1'b1;    //拉⾼复位,系统进⼊⼯作状态
// #2500_000
// forever #2560 clk_fx = ~clk_fx;
end
//------------<;设置时钟>----------------------------------------------
always #10 sys_clk = ~sys_clk;  //系统时钟周期20ns
always #489 clk_fx = ~clk_fx;  //被测信号周期489*2ns = 978ns
endmodule
仿真如下图:
上图在闸门时间内测得的被测信号个数cnt_fx为511248,测得被测信号频率为1022496Hz;理论频率=1/978ns=1022494.88Hz(MHz级别)。
可以看出这个测量结果还是⽐较准确的。因为闸门时间够长,且被测信号⾃⾝频率就⽐较⾼(约1Mhz)。接下来更改⼀下Testbench,⽐较⼀下闸门时间对被测信号的影响以及被测信号⾃⾝频率⾼低对测量的影响:第1个模块:被测信号频率为1022494.88Hz(MHz级别),闸门时间0.5s
第2个模块:被测信号频率为1022494.88Hz(MHz级别),闸门时间0.5ms
马自达六汽车第3个模块:被测信号频率为76103.5Hz(KHz级别),闸门时间0.5s
第4个模块:被测信号频率为21.217Hz(Hz级别),闸门时间0.5s
//多变量对⽐测试
`timescale 1ns/1ns //时间单位/精度
//------------<;模块及端⼝声明>----------------------------------------
module tb_cymometer_direct();
reg  sys_clk;
reg  sys_rst_n;
reg        clk_fx1;
reg      clk_fx2;
reg        clk_fx3;
reg      clk_fx4;
wire [31:0] fre1;
wire [31:0] fre2;
wire [31:0] fre3;
wire [31:0] fre4;
defparam cymometer_direct_inst2.TIME_GATE = 500_000;          //重设闸门时间1ms
//------------<;例化被测试模块>----------------------------------------
cymometer_direct cymometer_direct_inst1(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.clk_fx  (clk_fx1 ),
.
fre        (fre1  )
);
cymometer_direct cymometer_direct_inst2(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.clk_fx  (clk_fx2 ),
.fre        (fre2  )
);
cymometer_direct cymometer_direct_inst3(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.
clk_fx  (clk_fx3 ),
.fre        (fre3  )
);
cymometer_direct cymometer_direct_inst4(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.clk_fx  (clk_fx4 ),
.fre        (fre4  )
);
//------------<;例化被测试模块>----------------------------------------
>发动机飞轮