FPGA强化(2):简易频率计

第34讲:简易频率计的设计与验证

常用频率测量法:频率测量法、周期测量法、等精度测量法

频率测量法:在时间T内对被测时钟信号的时钟周期N进行计数,然后求出单位时间内的时钟周期数,即为被测时钟信号的时钟频率。

周期测量法:先测量出被测时钟信号的时钟周期T,然后根据频率f=1/T求出被测时钟信号的频率。
在这里插入图片描述
在这里插入图片描述

子功能模块设计
在这里插入图片描述
系统框图设计
在这里插入图片描述
在这里插入图片描述

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 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       ;   //待检测时钟频率输出标志信号

//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];

//---------- clk_gen_inst ----------
clk_gen clk_gen_inst
(
    .areset (~sys_rst_n ),
    .inclk0 (sys_clk    ),

    .c0     (clk_stand  )
);

endmodule

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    ;   //计算得到的待检测信号时钟频率

//---------- 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

tb_freq_meter

`timescale  1ns/1ns
module tb_freq_meter();

//wire  define
wire    stcp    ;   //输出数据存储寄时钟
wire    shcp    ;   //移位寄存器的时钟输入
wire    ds      ;   //串行数据输入
wire    oe      ;

//reg   define
reg             sys_clk     ;
reg             sys_rst_n   ;
reg             clk_test    ;

//时钟、复位、待检测时钟的生成
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    ;

//------------- 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

猜你喜欢

转载自blog.csdn.net/qq_39236499/article/details/128018001