05_简易频率计的设计与验证
1. 理论学习
2. 实验目标
利用所学知识,设计一个基于等精度测量原理的简易频率计,对输入的未知时钟信号做频率测量,并将测量结果在数码管上显示。
要求:标准时钟信号频率为 100MHz,实际闸门时间大于或等于 1s,目的是减小误差,提高测量精度。
3. 模块设计
3.1 顶层模块
3.1 频率计算模块
4. 波形图
频率计算模块
设计的信号都是基于这个方法图来的
软件闸门
计数器为1.5 s
实际闸门 ,以被测信息的时钟周期为标准。
为了与被测的时钟信号呈现整数关系,这时候看待检测时钟信号 的上升沿,Gate_s是高电平拉高,然后检测到时钟信号为上升沿 为Gate_s低电平拉低。
对被的时钟周期计数,看看有几个N
被测时钟信号时钟周期的寄存,采用延迟一个时钟周期,找到下降沿拉高信号,在flag信号为高电平时然后寄存。
开始对标准时钟信号进行计数,由于IP核产生,标准的频率为100MHZ,方式和测试时钟信号差不多。实际闸门 gata_a 为高电平时,开始对标准时钟信号开始计数
计算标志信号
5. RTL
5.1 freq_meter_calc
`timescale 1ns/1ns
module freq_meter_calc
(
input wire sys_clk , //系统时钟,频率50MHz
input wire sys_rst_n , //复位信号,低电平有效
input wire clk_test , //待检测时钟
output reg [33:0] freq //待检测时钟频率
);
//********************************************************************//
//****************** Parameter And Internal Signal *******************//
//********************************************************************//
//parameter define
parameter CNT_GATE_S_MAX = 28'd74_999_999 , //软件闸门计数器计数最大值
CNT_RISE_MAX = 28'd12_500_000 ; //软件闸门拉高计数值
parameter CLK_STAND_FREQ = 28'd100_000_000 ; //标准时钟时钟频率
//wire define
wire clk_stand ; //标准时钟,频率100MHz
wire gate_a_fall_s ; //实际闸门下降沿(标准时钟下)
wire gate_a_fall_t ; //实际闸门下降沿(待检测时钟下)
//reg define
reg [27:0] cnt_gate_s ; //软件闸门计数器
reg gate_s ; //软件闸门
reg gate_a ; //实际闸门
reg gate_a_stand ; //实际闸门打一拍(标准时钟下)
reg gate_a_test ; //实际闸门打一拍(待检测时钟下)
reg [47:0] cnt_clk_stand ; //标准时钟周期计数器
reg [47:0] cnt_clk_stand_reg ; //实际闸门下标志时钟周期数
reg [47:0] cnt_clk_test ; //待检测时钟周期计数器
reg [47:0] cnt_clk_test_reg ; //实际闸门下待检测时钟周期数
reg calc_flag ; //待检测时钟时钟频率计算标志信号
reg [63:0] freq_reg ; //待检测时钟频率寄存
reg calc_flag_reg ; //待检测时钟频率输出标志信号
//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//cnt_gate_s:软件闸门计数器
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_gate_s <= 28'd0;
else if(cnt_gate_s == CNT_GATE_S_MAX)
cnt_gate_s <= 28'd0;
else
cnt_gate_s <= cnt_gate_s + 1'b1;
//gate_s:软件闸门
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
gate_s <= 1'b0;
else if((cnt_gate_s>= CNT_RISE_MAX)
&& (cnt_gate_s <= (CNT_GATE_S_MAX - CNT_RISE_MAX)))
gate_s <= 1'b1;
else
gate_s <= 1'b0;
//gate_a:实际闸门
always@(posedge clk_test or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
gate_a <= 1'b0;
else
gate_a <= gate_s;
//cnt_clk_stand:标准时钟周期计数器,计数实际闸门下标准时钟周期数
always@(posedge clk_stand or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_clk_stand <= 48'd0;
else if(gate_a == 1'b0)
cnt_clk_stand <= 48'd0;
else if(gate_a == 1'b1)
cnt_clk_stand <= cnt_clk_stand + 1'b1;
//cnt_clk_test:待检测时钟周期计数器,计数实际闸门下待检测时钟周期数
always@(posedge clk_test or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_clk_test <= 48'd0;
else if(gate_a == 1'b0)
cnt_clk_test <= 48'd0;
else if(gate_a == 1'b1)
cnt_clk_test <= cnt_clk_test + 1'b1;
//gate_a_stand:实际闸门打一拍(标准时钟下)
always@(posedge clk_stand or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
gate_a_stand <= 1'b0;
else
gate_a_stand <= gate_a;
//gate_a_fall_s:实际闸门下降沿(标准时钟下)
assign gate_a_fall_s = ((gate_a_stand == 1'b1) && (gate_a == 1'b0))
? 1'b1 : 1'b0;
//cnt_clk_stand_reg:实际闸门下标志时钟周期数
always@(posedge clk_stand or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_clk_stand_reg <= 32'd0;
else if(gate_a_fall_s == 1'b1)
cnt_clk_stand_reg <= cnt_clk_stand;
//gate_a_test:实际闸门打一拍(待检测时钟下)
always@(posedge clk_test or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
gate_a_test <= 1'b0;
else
gate_a_test <= gate_a;
//gate_a_fall_t:实际闸门下降沿(待检测时钟下)
assign gate_a_fall_t = ((gate_a_test == 1'b1) && (gate_a == 1'b0))
? 1'b1 : 1'b0;
//cnt_clk_test_reg:实际闸门下待检测时钟周期数
always@(posedge clk_test or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_clk_test_reg <= 32'd0;
else if(gate_a_fall_t == 1'b1)
cnt_clk_test_reg <= cnt_clk_test;
//calc_flag:待检测时钟时钟频率计算标志信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
calc_flag <= 1'b0;
else if(cnt_gate_s == (CNT_GATE_S_MAX - 1'b1))
calc_flag <= 1'b1;
else
calc_flag <= 1'b0;
//freq_reg:待检测时钟信号时钟频率寄存
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
freq_reg <= 64'd0;
else if(calc_flag == 1'b1)
freq_reg <= (CLK_STAND_FREQ * cnt_clk_test_reg / cnt_clk_stand_reg);
//calc_flag_reg:待检测时钟频率输出标志信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
calc_flag_reg <= 1'b0;
else
calc_flag_reg <= calc_flag;
//freq:待检测时钟信号时钟频率
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
freq <= 34'd0;
else if(calc_flag_reg == 1'b1)
freq <= freq_reg[33:0];
//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
//---------- clk_gen_inst ----------
clk_gen clk_gen_inst
(
.areset (~sys_rst_n ),
.inclk0 (sys_clk ),
.c0 (clk_stand )
);
endmodule
5.2 freq_meter
`timescale 1ns/1ns
module freq_meter
(
input wire sys_clk , //系统时钟,频率50MHz
input wire sys_rst_n , //复位信号,低电平有效
input wire clk_test , //待检测时钟
output wire clk_out , //生成的待检测时钟
output wire stcp , //输出数据存储寄时钟
output wire shcp , //移位寄存器的时钟输入
output wire ds , //串行数据输入
output wire oe
);
//wire define
wire [33:0] freq ; //计算得到的待检测信号时钟频率
//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
//---------- clk_gen_test_inst ----------
clk_test_gen clk_gen_test_inst
(
.areset (~sys_rst_n ), //复位端口,高电平有效
.inclk0 (sys_clk ), //输入系统时钟
.c0 (clk_out ) //输出生成的待检测时钟信号
);
//------------- freq_meter_calc_inst --------------
freq_meter_calc freq_meter_calc_inst
(
.sys_clk (sys_clk ), //系统时钟,频率50MHz
.sys_rst_n (sys_rst_n ), //复位信号,低电平有效
.clk_test (clk_test ), //待检测时钟
.freq (freq ) //待检测时钟频率
);
//------------- seg_595_dynamic_inst --------------
seg_595_dynamic seg_595_dynamic_inst
(
.sys_clk (sys_clk ), //系统时钟,频率50MHz
.sys_rst_n (sys_rst_n ), //复位信号,低有效
.data (freq/1000 ), //数码管要显示的值
.point (6'b001000 ), //小数点显示,高电平有效
.seg_en (1'b1 ), //数码管使能信号,高电平有效
.sign (1'b0 ), //符号位,高电平显示负号
.stcp (stcp ), //输出数据存储寄时钟
.shcp (shcp ), //移位寄存器的时钟输入
.ds (ds ), //串行数据输入
.oe (oe ) //输出使能信号
);
endmodule
6. Testbench
6.1 tb_freq_meter
`timescale 1ns/1ns
module tb_freq_meter();
//********************************************************************//
//****************** Parameter And Internal Signal *******************//
//********************************************************************//
//wire define
wire stcp ; //输出数据存储寄时钟
wire shcp ; //移位寄存器的时钟输入
wire ds ; //串行数据输入
wire oe ;
//reg define
reg sys_clk ;
reg sys_rst_n ;
reg clk_test ;
//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//时钟、复位、待检测时钟的生成
initial
begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
#200
sys_rst_n <= 1'b1;
#500
clk_test = 1'b1;
end
always #10 sys_clk = ~sys_clk ; //50MHz系统时钟
always #100 clk_test= ~clk_test ; //5MHz待检测时钟
//重定义软件闸门计数时间,缩短仿真时间
defparam freq_meter_inst.freq_meter_calc_inst.CNT_GATE_S_MAX = 240 ;
defparam freq_meter_inst.freq_meter_calc_inst.CNT_RISE_MAX = 40 ;
//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
//------------- freq_meter_inst -------------
freq_meter freq_meter_inst
(
.sys_clk (sys_clk ), //系统时钟,频率50MHz
.sys_rst_n (sys_rst_n ), //复位信号,低电平有效
.clk_test (clk_test ), //待检测时钟
.clk_out (clk_out ), //生成的待检测时钟
.stcp (stcp ), //输出数据存储寄时钟
.shcp (shcp ), //移位寄存器的时钟输入
.ds (ds ), //串行数据输入
.oe (oe )
);
endmodule