From 81852b15794ea5ad8140967f402af39f1156e46a Mon Sep 17 00:00:00 2001 From: yangshenbo Date: Sun, 5 Apr 2026 16:37:54 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E6=B8=A9=E5=BA=A6=E8=AE=A1?= =?UTF-8?q?=E6=A8=A1=E5=9D=97-=E3=80=8Bpulse=5Fcnt=20=EF=BC=8C=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E4=BB=BF=E7=9C=9F=E6=97=A0=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- rtl/pulse_cnt.v | 60 ++++++++++++++++ rtl/pulse_freq_10ms.v | 71 ------------------- tb/tb_pulse_cnt.sv | 160 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 220 insertions(+), 71 deletions(-) create mode 100644 rtl/pulse_cnt.v delete mode 100644 rtl/pulse_freq_10ms.v create mode 100644 tb/tb_pulse_cnt.sv diff --git a/rtl/pulse_cnt.v b/rtl/pulse_cnt.v new file mode 100644 index 0000000..e313a37 --- /dev/null +++ b/rtl/pulse_cnt.v @@ -0,0 +1,60 @@ +`timescale 1ns / 1ps + +module pulse_cnt #( + parameter CLK_FREQ = 50_000_000 +) ( + input wire clk, + input wire rst_n, + input wire sig_in, + input wire [23:0] win_us, + output reg [23:0] cnt_out, + output reg vld_out +); + + + reg [31:0] window_cnt; // 当前时钟周期计数 + reg [31:0] target_cnt; // 当前窗口所需时钟周期数 + + // 脉冲计数(宽度与输出一致,防止溢出) + reg [23:0] pulse_cnt; + + // 标志:是否正在计算新的 target_cnt(避免组合逻辑环路) + reg calc_done; + + reg sig_sync1, sig_sync2, sig_sync3; + wire sig_rise = sig_sync2 & ~sig_sync3; + + always @(posedge clk) begin + sig_sync1 <= sig_in; + sig_sync2 <= sig_sync1; + sig_sync3 <= sig_sync2; + end + + // 主控制逻辑 + always @(posedge clk or negedge rst_n) begin + if (!rst_n) begin + window_cnt <= 0; + pulse_cnt <= 0; + cnt_out <= 0; + vld_out <= 0; + target_cnt <= 24'd50_000; + + end else begin + vld_out <= 1'b0; + target_cnt <= ( {40'd0, win_us} * CLK_FREQ) / 1_000_000 ; + // 窗口计数结束条件:当前计数值到达 target_cnt + if (window_cnt >= target_cnt) begin + cnt_out <= pulse_cnt; + vld_out <= 1'b1; + // 复位窗口计数器与脉冲计数器,并触发重新计算目标值 + window_cnt <= 0; + pulse_cnt <= 0; + end else begin + window_cnt <= window_cnt + 1; + if (sig_rise) + pulse_cnt <= pulse_cnt + 1; + end + end + end + +endmodule \ No newline at end of file diff --git a/rtl/pulse_freq_10ms.v b/rtl/pulse_freq_10ms.v deleted file mode 100644 index c95c8e3..0000000 --- a/rtl/pulse_freq_10ms.v +++ /dev/null @@ -1,71 +0,0 @@ -`timescale 1ns / 1ps -////////////////////////////////////////////////////////////////////////////////// -// Company: -// Engineer: -// -// Create Date: 2026/03/22 18:53:43 -// Design Name: -// Module Name: pulse_freq_10ms -// Project Name: -// Target Devices: -// Tool Versions: -// Description: -// -// Dependencies: -// -// Revision: -// Revision 0.01 - File Created -// Additional Comments: -// -////////////////////////////////////////////////////////////////////////////////// - - -`timescale 1ns / 1ps - -module pulse_freq_10ms #( - parameter CLK_FREQ = 50_000_000, // 系统时钟频率(Hz),默认50MHz - parameter WINDOW_MS = 10 // 测量时间窗口(ms),默认10ms -) ( - input clk, // 系统时钟 - input rst_n, // 异步复位,低有效 - input vin, // 输入方波 - output reg [19:0] freq, // 时间窗口内脉冲计数(如果为1302,就是130.2k) - output reg valid // 测量完成有效脉冲 -); - - // 计算窗口计数最大值 - localparam WINDOW_CNT = (CLK_FREQ / 1000) * WINDOW_MS - 1; - - reg [31:0] cnt_window; // 窗口计时器(使用32位以防大数值) - reg [19:0] pulse_cnt; // 脉冲计数器 - reg vin_sync1, vin_sync2; - wire vin_rise; - - // 边沿检测 - always @(posedge clk) begin - vin_sync1 <= vin; - vin_sync2 <= vin_sync1; - end - assign vin_rise = vin_sync1 & ~vin_sync2; - - // 核心逻辑:窗口计数 + 脉冲计数 + 锁存输出 - always @(posedge clk or negedge rst_n) begin - if (!rst_n) begin - cnt_window <= 0; - pulse_cnt <= 0; - valid <= 0; - end else begin - valid <= 0; // 默认无效 - if (cnt_window == WINDOW_CNT) begin // 窗口时间到 - freq <= pulse_cnt; // 输出计数值 - valid <= 1; - cnt_window <= 0; - pulse_cnt <= 0; - end else begin - cnt_window <= cnt_window + 1; - if (vin_rise) pulse_cnt <= pulse_cnt + 1; - end - end - end - -endmodule \ No newline at end of file diff --git a/tb/tb_pulse_cnt.sv b/tb/tb_pulse_cnt.sv new file mode 100644 index 0000000..828974a --- /dev/null +++ b/tb/tb_pulse_cnt.sv @@ -0,0 +1,160 @@ +`timescale 1ns / 1ps + +module tb_pulse_cnt(); + + // 参数定义 + localparam CLK_FREQ = 50_000_000; // 50 MHz 时钟 + localparam CLK_PERIOD = 20ns; // 1/50MHz = 20ns + + // 信号声明 + reg clk; + reg rst_n; + reg [23:0] win_us; + reg sig_in; + wire [23:0] cnt_out; + wire vld_out; + + // 实例化被测模块 + pulse_cnt #( + .CLK_FREQ(CLK_FREQ) + ) dut ( + .clk (clk), + .rst_n (rst_n), + .sig_in (sig_in), + .win_us (win_us), + .cnt_out(cnt_out), + .vld_out(vld_out) + ); + + // 时钟生成 + initial begin + clk = 0; + forever #(CLK_PERIOD/2) clk = ~clk; + end + + // 测试激励 + initial begin + // 初始化 + rst_n = 0; + win_us = 24'd1000; // 默认窗口 1000 us = 1 ms + sig_in = 0; + + // 释放复位 + repeat(5) @(posedge clk); + rst_n = 1; + repeat(2) @(posedge clk); + + // -------------------------------------------------- + // 测试1:窗口内无脉冲 + // -------------------------------------------------- + $display("Test 1: No pulse, expect cnt_out = 0 after window"); + win_us = 24'd1000; // 1 ms 窗口 + wait (vld_out); + $display(" cnt_out = %0d (expected 0)", cnt_out); + #(CLK_PERIOD*2); + + // -------------------------------------------------- + // 测试2:固定频率脉冲 50 kHz (周期 20 us) + // -------------------------------------------------- + $display("Test 2: 50 kHz pulse, window = 1 ms -> expected 50 pulses"); + fork + begin : pulse_gen + forever begin + #10us sig_in = 1; + #10us sig_in = 0; + end + end + join_none + + wait (vld_out); + $display(" cnt_out = %0d (expected 50)", cnt_out); + #(CLK_PERIOD*2); + disable pulse_gen; + + // -------------------------------------------------- + // 测试3:脉冲频率 250 kHz (周期 2 us),窗口 1 ms -> 250 个脉冲 + // -------------------------------------------------- + $display("Test 3: 250 kHz pulse, window = 1 ms -> expected 250 pulses"); + fork + begin : pulse_gen2 + forever begin + #2us sig_in = 1; + #2us sig_in = 0; + end + end + join_none + + wait (vld_out); + $display(" cnt_out = %0d (expected 250)", cnt_out); + #(CLK_PERIOD*2); + disable pulse_gen2; + + // -------------------------------------------------- + // 测试4:动态改变窗口宽度 (2 ms),脉冲频率 250 kHz + // -------------------------------------------------- + $display("Test 4: 250 kHz pulse, window = 2 ms -> expected 500 pulses"); + win_us = 24'd2000; // 2 ms + + // 重新启动一个脉冲生成进程 + fork + begin : pulse_gen3 + forever begin + #2us sig_in = 1; // 250 kHz -> 周期 4 us + #2us sig_in = 0; + end + end + join_none + + wait (vld_out); + $display(" cnt_out = %0d (expected 500)", cnt_out); + #(CLK_PERIOD*2); + disable pulse_gen3; + // -------------------------------------------------- + // 测试5:边界情况 - 窗口极小 (1 us) + // -------------------------------------------------- + $display("Test 5: 500 kHz pulse, window = 1 us -> expected 0.5? rounded to 0 or 1?"); + win_us = 24'd2; + // 重新启动一个脉冲生成进程 + fork + begin : pulse_gen4 + forever begin + #1us sig_in = 1; + #1us sig_in = 0; + end + end + join_none + + wait (vld_out); + $display(" cnt_out = %0d (expected 500)", cnt_out); + #(CLK_PERIOD*2); + disable pulse_gen4; + + // -------------------------------------------------- + // 测试6:复位过程中行为 + // -------------------------------------------------- + $display("Test 6: Assert reset mid-window, check output goes to 0"); + win_us = 24'd1000; + #(500us); // 窗口中间复位 + rst_n = 0; + repeat(5) @(posedge clk); + rst_n = 1; + wait (vld_out); + $display(" cnt_out = %0d (should be fresh measurement)", cnt_out); + + #(10us); + $display("Simulation finished."); + $finish; + end + + // 监控输出有效标志并打印结果 + always @(posedge vld_out) begin + $display("[%t] vld_out asserted, cnt_out = %0d", $time, cnt_out); + end + + // 可选:波形转储 + initial begin + $dumpfile("tb_pulse_cnt.vcd"); + $dumpvars(0, tb_pulse_cnt); + end + +endmodule \ No newline at end of file