数字频率计的Verilog设计

本文介绍了数字频率计的原理和实现,通过档位切换实现不同测量范围和精度,包括四个测量档位,覆盖1Hz至500MHz的频率。设计中详细阐述了数据通路的计数器、时钟分频和锁存器模块,以及控制通路的状态机设计。此外,还展示了各模块的VHDL代码,实现了1Hz至1000Hz的时钟分频。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

数字频率计

本设计的项目工程源码链接:数字频率计源码

功能描述

数字频率计用来测量被测信号的频率,并且本设计能根据被测信号自动切换测量档位,不同测量档位的测试精度不同,测量档位与对应的测量精度如下所示

  • 第一档:测量范围1-99999Hz,测量精度1Hz;
  • 第二档:测量范围100kHz-999.99kHz,测量精度10Hz;
  • 第三档:测量范围1MHz-9.9999MHz,测量精度100Hz;
  • 第四档:测量范围10MHz-99.999MHz,测量精度1kHz;

原理简述

数字频率计的原理十分简单,要测量被测信号的频率,就要搞清楚频率的定义,即1s中信号的周期数。我们根据频率的定义,那么只需要测量被测信号在1s中变化的次数就可以,就是1s中的周期数。

那么如何实现测量档位的切换呢?本数字频率计最终输出5个BCD码,也就是说如果没有档位切换功能的话最多只能显示1-99999Hz的范围;

为了实现档位的自动切换功能,我们在计数模块增加一个溢出的输出信号,如果计数模块发生溢出,说明被测信号的频率较高,在1s中变化的次数太多。那么我们就可以转换思路,不测量1s内的变化次数了,测量被测信号在0.1s中变化的次数,最终将输出的单位改为10Hz即可;如果仍发生溢出,继续减少这个单位时间的宽度,如从0.1s切换到0.01s,输出时继续改成相应的输出单位即可。如何改变这个单位时间的宽度在后续控制模块的设计中会给大家详细介绍。请大家好好理解上面这段话,这是档位切换的精髓,也是后续状态机设计的基础。

模块设计

顶层框图

顶层框图.png

本数字频率计从控制通路、数据通路两个方向展开设计;

控制通路给数据通路提供如下信号

  • 单位时间宽度的选择信号clk_sel[1:0]
  • 计数器的使能信号count_en和清零信号clear
  • 锁存器的使能信号latch_en;

控制通路暴露给顶层的输出信号

  • 最终的频率单位:Hz_1Hz_10Hz_100Hz_1000;
  • 被测信号的频率越界信号:over_range;

数据通路给控制通路提供如下信号

  • 在当前的单位时间内计数器是否溢出的标记信号overflow
  • 分频器输出的时钟信号clk_div_out,供状态机使用;

数据通路暴露给顶层的输出信号

  • 被测信号的频率,位宽为20(5个BCD码)

数据通路

数字频率计数据通路框图.png

数据通路包括计数器模块、时钟分频模块、锁存输出模块;

计数器模块

在计数器模块使能期间(en_in = 1),对输入的fin信号的上升沿进行计数;复位或清零信号生效时,计数器清零。

其中计数器模块由5个BCD计数器级联组成,BCD计数器完成从1-9的计数功能,并接受和产生进位信号,方便进行模块的级联操作。

BCD计数器的代码如下

`timescale 1ns / 1ps
//
// Engineer:@david
// Module Name: bcd_counter_10
// Description: BCD计数器,从0-9计数
//
module counter_10(
    input wire en_in,       //输入使能信号    
    input wire rst_n,       //复位信号    
    input wire clear,       //清零信号    
    input wire fin,         //待测信号    
    output reg en_out,      //输出使能,用于控制下一个计数器的状态,当输出使能有效时,下一个模10计数器计数加1    
    output reg [3:0] q      //计数器的输出,4位BCD码输出  
);
    
always@  (posedge fin or negedge rst_n) begin   //输入待测信号的上升沿作为敏感信号
    if(rst_n == 1'b0) begin
        en_out <= 1'b0;
        q      <=  'd0; 
    end
    else if(en_in == 1'b1) begin
        if(q == 4'b1001) begin
            q      <= 4'b0;
            en_out <= 1'b1;
        end
        else begin
            q      <= q + 1'b1;
            en_out <= 1'b0;
        end
    end
    else if(clear == 1'b1) begin
        en_out <= 1'b0;
        q      <=  'd0;
    end
    else begin
        en_out <= 1'b0;
        q      <=    q;
    end
end
    
endmodule

计数器模块的代码如下

`timescale 1ns / 1ps
//
// Engineer:@david
// Module Name: bcd_counter
// Description: 5个BCD计数器的级联模块,同时输出溢出信号
//
module counter(
    input wire                      fin,         //频率待测信号
    input wire                      rst_n,
    input wire                      en_in,       //输入使能信号
    input wire                      clear,

    output [3:0]                    q0,
    output [3:0]                    q1,
    output [3:0]                    q2,
    output [3:0]                    q3,
    output [3:0]                    q4,
    output reg                      overflow	  //计数器是否溢出
);

    wire en_out_0,en_out_1,en_out_2,en_out_3;
    wire en_out_overflow;

    counter_10  u_counter_q0 (
    .en_in                   ( en_in    ),
    .rst_n                   ( rst_n    ),
    .clear                   ( clear    ),
    .fin                     ( fin      ),

    .en_out                  ( en_out_0 ),
    .q                       ( q0       )
    );

    counter_10  u_counter_q1 (
    .en_in                   ( en_out_0 ),
    .rst_n                   ( rst_n    ),
    .clear                   ( clear    ),
    .fin                     ( fin      ),

    .en_out                  ( en_out_1 ),
    .q                       ( q1       )
    );

    counter_10  u_counter_q2 (
    .en_in                   ( en_out_1 ),
    .rst_n                   ( rst_n    ),
    .clear                   ( clear    ),
    .fin                     ( fin      ),

    .en_out                  ( en_out_2 ),
    .q                       ( q2       )
    );

    counter_10  u_counter_q3 (
    .en_in                   ( en_out_2 ),
    .rst_n                   ( rst_n    ),
    .clear                   ( clear    ),
    .fin                     ( fin      ),

    .en_out                  ( en_out_3 ),
    .q                       ( q3       )
    );

    counter_10  u_counter_q4 (
    .en_in                   ( en_out_3 ),
    .rst_n                   ( rst_n    ),
    .clear                   ( clear    ),
    .fin                     ( fin      ),

    .en_out                  ( en_out_overflow ),
    .q                       ( q4       )
    );

    always @(posedge fin or negedge rst_n) begin
        if(rst_n == 1'b0) begin
            overflow <= 1'b0;
        end
        else begin
            if(clear == 1'b1) begin
                overflow <= 1'b0;
            end
            else if(en_out_overflow == 1'b1) begin
                overflow <= 1'b1;
            end
            else begin
                overflow <= overflow;
            end
        end
    end

endmodule

时钟分频模块

因为我们有4个档位,所以我们要对时钟进行分频输出,需得到1Hz10Hz100Hz1000Hz的时钟输出,根据clk_sel[1:0]信号进行输出时钟的选择;本设计中默认的输入时钟频率为10MHz

`timescale 1ns / 1ps
//
// Engineer:@david
// Module Name: clk_div
// Description: input 10MHz clk_in
//              output clk_out:1Hz clk_1;10Hz clk_10;100Hz clk_100;1000Hz clk_1000
//
module clk_div(
    input                           clk_in,         //10MHz
    input                           rst_n,
    input [1:0]                     clk_sel,
    output reg                      clk_out     
);

    reg clk_1,clk_10,clk_100,clk_1000;
    reg [31:0] cnt_1,cnt_10,cnt_100,cnt_1000;

    //1Hz
    always @(posedge clk_in or negedge rst_n) begin
        if(rst_n == 1'b0) begin
            cnt_1 <= 'd0;
            clk_1 <= 1'b0;
        end
        else begin
            if(cnt_1 == 32'd4999999) begin
                cnt_1 <= 'd0;
                clk_1 <= ~clk_1;
            end
            else begin
                cnt_1 <= cnt_1 + 1'b1;
                clk_1 <= clk_1;
            end
        end
    end

    //10Hz
    always @(posedge clk_in or negedge rst_n) begin
        if(rst_n == 1'b0) begin
            cnt_10 <= 'd0;
            clk_10 <= 1'b0;
        end
        else begin
            if(cnt_10 == 32'd499999) begin
                cnt_10 <= 'd0;
                clk_10 <= ~clk_10;
            end
            else begin
                cnt_10 <= cnt_10 + 1'b1;
                clk_10 <= clk_10;
            end
        end
    end

    //100Hz
    always @(posedge clk_in or negedge rst_n) begin
        if(rst_n == 1'b0) begin
            cnt_100 <= 'd0;
            clk_100 <= 1'b0;
        end
        else begin
            if(cnt_100 == 32'd49999) begin         //ori = 49999
                cnt_100 <= 'd0;
                clk_100 <= ~clk_100;
            end
            else begin
                cnt_100 <= cnt_100 + 1'b1;
                clk_100 <= clk_100;
            end
        end
    end

    //1000Hz
    always @(posedge clk_in or negedge rst_n) begin
        if(rst_n == 1'b0) begin
            cnt_1000 <= 'd0;
            clk_1000 <= 1'b0;
        end
        else begin
            if(cnt_1000 == 32'd4999) begin
                cnt_1000 <= 'd0;
                clk_1000 <= ~clk_1000;
            end
            else begin
                cnt_1000 <= cnt_1000 + 1'b1;
                clk_1000 <= clk_1000;
            end
        end
    end

    always @(*) begin
        case(clk_sel)
            2'b00:  clk_out = clk_1;
            2'b01:  clk_out = clk_10;
            2'b10:  clk_out = clk_100;
            2'b11:  clk_out = clk_1000;
            default:clk_out = clk_1;
        endcase
    end

endmodule

锁存器模块

`timescale 1ns / 1ps
//
// Engineer:@david
// Module Name: latch_freq
// Description: 锁存器,完成结果的锁存
//
module latch_freq(
    input wire          clk_in,
    input wire          rst_n,
    input wire          latch_en,
    input wire [3:0]    q0,
    input wire [3:0]    q1,
    input wire [3:0]    q2,
    input wire [3:0]    q3,
    input wire [3:0]    q4,

    output reg [3:0]    d0,
    output reg [3:0]    d1,
    output reg [3:0]    d2,
    output reg [3:0]    d3,
    output reg [3:0]    d4
);

    always @(posedge clk_in or negedge rst_n) begin
        if(rst_n == 1'b0) begin
            d0 <= 'd0;
            d1 <= 'd0;
            d2 <= 'd0;
            d3 <= 'd0;
            d4 <= 'd0; 
        end
        else if(latch_en == 1'b1) begin
            d0 <= q0;
            d1 <= q1;
            d2 <= q2;
            d3 <= q3;
            d4 <= q4;
        end
        else begin
            d0 <= d0;
            d1 <= d1;
            d2 <= d2;
            d3 <= d3;
            d4 <= d4;
        end
    end

endmodule

控制通路

状态机设计

状态转移图.jpg

其中COUNT_1状态表示以1Hz的单位时间对输入信号进行计数,其它同理;ONE_2_TEN状态是指将单位时间从1Hz切换成10Hz,其它同理;如果COUNT_x状态未发生溢出,则进入DONE_x状态,表示计数完成且没溢出,打开锁存器的使能信号;LATCH_x状态就是用来输出频率的计量单位,本来我是将DONE_xLATCH_x合并为一个状态的,但是频率的单位和结果在输出时序上不一致,所以增加了LATCH_x状态来将他们的时序保持一致;

测量结果

25Hz

25Hz.png

250kHz

250kHz.png

5MHz

5MHz.png

50MHz

50MHz.png

频率越界(500MHz)

越界.png

结语

本设计采用数据通路和控制通路的思路进行设计,这是我在CPU设计中学习到的思路,发现这种思路对整体框架的把握更加到位,希望大家也能好好体会。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

日拱一卒_未来可期

若复习顺利望有闲钱的同学支持下

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值