1. 项目概述在数字信号处理、频率合成器测试及通信系统中常常需要产生一个频率随时间线性变化的信号即扫频信号。本设计实现了一个参数可配置的扫频频率发生器通过状态机控制输出频率按设定步进递增并支持单次扫描和连续循环扫描两种模式。模块采用Verilog HDL编写具有结构清晰、可复用性强等特点并配套完整的Testbench进行功能验证。2. 模块端口说明信号名方向位宽功能描述clkI1系统时钟上升沿有效rst_nI1异步复位低电平有效SweepModeI1扫频使能高电平启动/连续模式StartFreqI32扫频起始频率StopFreqI32扫频截止频率StepFreqI32频率步进间隔TimeDelayI32每步频点保持的时钟周期数DACFreqO32当前输出的频率值3. 设计核心创新点灵活的参数配置起始/截止频率、步进值、驻留时间均可通过输入端口实时设置适应不同应用场景。单次与连续双模式仅通过SweepMode信号电平控制。脉冲式高电平触发单次扫描持续高电平实现自动循环扫描无需额外控制逻辑。鲁棒的复位处理异步复位后所有内部寄存器清零输出频率归零复位释放后进入空闲状态等待下次触发。防死锁状态机状态转移条件明确并处理了起始频率≥截止频率的边界情况直接停留在起始值。4. 内部结构及状态机设计4.1 主要寄存器ri_SweepMode,ri_StartFreq, … : 输入信号锁存保证在扫频过程中参数稳定。ro_DACFreq输出频率寄存器。r_delay_cnt延迟计数器用于计时频点驻留时间。4.2 状态机3状态localparam P_ST_IDLE 3b001; localparam P_ST_DELAY 3b010; localparam P_ST_STEP 3b100;IDLE空闲态。若SweepMode为高则加载起始频率到输出并跳转至DELAY。DELAY延迟态。计数器从0递增至TimeDelay-1完成后跳转至STEP。STEP步进态。若当前频率 停止频率则增加一个步进并返回DELAY否则回到IDLE完成一次扫描。连续扫频的实现当SweepMode持续为高时IDLE状态会立即重新触发从而自动开始下一轮扫描形成连续循环。5. 关键代码解析5.1 状态转移条件assign p_st_idle2p_st_delay_start state_cP_ST_IDLE (ri_SweepMode ); assign p_st_delay2p_st_step_start state_cP_ST_DELAY (r_delay_cnt ri_TimeDelay ); assign p_st_step2p_st_idle_start state_cP_ST_STEP (ro_DACFreq ri_StopFreq ); assign p_st_step2p_st_delay_start state_cP_ST_STEP (ro_DACFreq ri_StopFreq );5.2 输出频率更新逻辑always (posedge i_clk )begin if(i_rst) ro_DACFreq d0 ; else if(state_c P_ST_IDLE) ro_DACFreq (ri_StartFreq) ; // 空闲时预置起始频率 else if(state_c P_ST_STEP ro_DACFreq ri_StopFreq) ro_DACFreq (ri_StartFreq) ; // 扫频结束重置为起始 else if(state_c P_ST_STEP ro_DACFreq ri_StopFreq) ro_DACFreq (ro_DACFreq ri_StepFreq) ; else ro_DACFreq ro_DACFreq ; end5.3 延迟计数器always (posedge i_clk )begin if(i_rst) r_delay_cnt d0 ; else if(state_c P_ST_DELAY r_delay_cnt ri_TimeDelay) r_delay_cnt (r_delay_cnt d1 ) ; else r_delay_cnt d0 ; end6. Testbench设计与测试用例为了充分验证模块功能我们编写了包含任务封装的Testbench覆盖以下场景测试用例描述TC1基本扫频起始100停止500步进100延迟10个周期触发单次扫描验证结束回初始值TC2连续扫频起始1000停止2000步进200延迟5保持SweepMode为高观察循环TC3边界条件起始≥停止500→100验证状态机正确处理TC4复位干扰扫描过程中拉低复位检查输出清零释放复位后恢复到起始值6.1 任务封装示例task set_sweep_config; input [31:0] i_start, i_stop, i_step, i_delay; begin StartFreq i_start; StopFreq i_stop; StepFreq i_step; TimeDelay i_delay; end endtask task trigger_sweep; begin (posedge clk); SweepMode 1; (posedge clk); SweepMode 0; end endtask task check_freq; input [31:0] expected; input [255:0] msg; begin if (DACFreq ! expected) $display([ERROR] ...); else $display([PASS] ...); end endtask6.2 仿真结果预期仿真运行后控制台输出如下示例[INFO] Reset Released at time 50 TEST CASE 1: Basic Sweep [CONFIG] Start100, Stop500, Step100, Delay10 [TRIG] Sweep Triggered at time 110 ... [PASS] Time610: Freq100 OK (Sweep Finished, back to StartFreq) TEST CASE 2: Continuous Sweep ... [PASS] Continuous sweep loop detected correctly. TEST CASE 3: Boundary Test ... [PASS] Time...: Freq500 OK (Boundary Case: Start Stop, stay at StartFreq) TEST CASE 4: Reset During Sweep ... [PASS] Time...: Freq0 OK (Output should be 0 during Reset) [PASS] Time...: Freq100 OK (Output reset to StartFreq after Reset release)波形上可见频率值按节拍递增延迟周期内保持不变连续模式下周期重复。7. 完整代码7.1 顶层模块sweep_freq.vmodule sweep_freq( input clk , input rst_n , input SweepMode ,//扫频模式使能 input [31:0] StartFreq ,//扫频起始频率 input [31:0] StopFreq ,//扫频截止频率 input [31:0] StepFreq ,//扫频频率步进间隔 input [31:0] TimeDelay ,//扫频频率时间间隔 output [31:0] DACFreq ); /************************parameter***********************/ /************************REG*********************/ reg ri_SweepMode ; reg [31:0] ri_StartFreq ; reg [31:0] ri_StopFreq ; reg [31:0] ri_StepFreq ; reg [31:0] ri_TimeDelay ; reg [ 31: 0] ro_DACFreq ; reg [ 31: 0] r_delay_cnt ; /************************WIRE*********************/ wire i_clk ; wire i_rst ; /************************FSM*********************/ reg [ (3 - 1):0] state_c ; reg [ (3 - 1):0] state_n ; localparam P_ST_IDLE 3b001 ; localparam P_ST_DELAY 3b010 ; localparam P_ST_STEP 3b100 ; always (posedge i_clk) begin if (i_rst) begin state_c P_ST_IDLE ; end else begin state_c state_n; end end always (*) begin case(state_c) P_ST_IDLE :begin if(p_st_idle2p_st_delay_start) state_n P_ST_DELAY ; else state_n state_c ; end P_ST_DELAY :begin if(p_st_delay2p_st_step_start) state_n P_ST_STEP ; else state_n state_c ; end P_ST_STEP :begin if(p_st_step2p_st_idle_start) state_n P_ST_IDLE ; else if(p_st_step2p_st_delay_start) state_n P_ST_DELAY ; else state_n state_c ; end default : state_n P_ST_IDLE ; endcase end assign p_st_idle2p_st_delay_start state_cP_ST_IDLE (ri_SweepMode ); assign p_st_delay2p_st_step_start state_cP_ST_DELAY (r_delay_cnt ri_TimeDelay ); assign p_st_step2p_st_idle_start state_cP_ST_STEP (ro_DACFreq ri_StopFreq ); assign p_st_step2p_st_delay_start state_cP_ST_STEP (ro_DACFreq ri_StopFreq ); /************************CombineLogic*******************/ assign i_clk clk ; assign i_rst ~rst_n ; assign DACFreq ro_DACFreq ; /************************Inist***********************/ /************************Process***********************/ always (posedge i_clk )begin if(i_rst)begin ri_SweepMode d0 ; ri_StartFreq d0 ; ri_StopFreq d0 ; ri_StepFreq d0 ; ri_TimeDelay d0 ; end else begin ri_SweepMode SweepMode ; ri_StartFreq StartFreq ; ri_StopFreq StopFreq ; ri_StepFreq StepFreq ; ri_TimeDelay TimeDelay ; end end always (posedge i_clk )begin if(i_rst) ro_DACFreq d0 ; else if(state_c P_ST_IDLE) ro_DACFreq (ri_StartFreq) ; else if(state_c P_ST_STEP ro_DACFreq ri_StopFreq) ro_DACFreq (ri_StartFreq) ; else if(state_c P_ST_STEP ro_DACFreq ri_StopFreq) ro_DACFreq (ro_DACFreq ri_StepFreq) ; else ro_DACFreq ro_DACFreq ; end always (posedge i_clk )begin if(i_rst) r_delay_cnt d0 ; else if(state_c P_ST_DELAY r_delay_cnt ri_TimeDelay) r_delay_cnt (r_delay_cnt d1 ) ; else r_delay_cnt d0 ; end endmodule7.2 Testbenchtb_sweep_freq.vtimescale 1ns/1ps module tb_sweep_freq; // // Parameter Definitions // parameter CLK_PERIOD 10; // 100MHz parameter SIM_TIME 20000; // 仿真总时长 // // Interface Signals // reg clk; reg rst_n; reg SweepMode; reg [31:0] StartFreq; reg [31:0] StopFreq; reg [31:0] StepFreq; reg [31:0] TimeDelay; wire [31:0] DACFreq; // State Monitor (Hierarchical Reference) wire [2:0] current_state; assign current_state u_sweep_freq.state_c; // // DUT Instantiation // sweep_freq u_sweep_freq ( .clk (clk), .rst_n (rst_n), .SweepMode (SweepMode), .StartFreq (StartFreq), .StopFreq (StopFreq), .StepFreq (StepFreq), .TimeDelay (TimeDelay), .DACFreq (DACFreq) ); // // Clock Reset Generation // initial begin clk 0; forever #(CLK_PERIOD/2) clk ~clk; end initial begin rst_n 0; repeat(5) (posedge clk); rst_n 1; $display([INFO] Reset Released at time %0t, $time); end // // Tasks: Encapsulation for Stimulus // task set_sweep_config; input [31:0] i_start; input [31:0] i_stop; input [31:0] i_step; input [31:0] i_delay; begin StartFreq i_start; StopFreq i_stop; StepFreq i_step; TimeDelay i_delay; $display([CONFIG] Start%0d, Stop%0d, Step%0d, Delay%0d, i_start, i_stop, i_step, i_delay); end endtask task trigger_sweep; begin (posedge clk); SweepMode 1; (posedge clk); SweepMode 0; $display([TRIG] Sweep Triggered at time %0t, $time); end endtask task check_freq; input [31:0] expected; input [255:0] msg; begin if (DACFreq ! expected) begin $display([ERROR] Time%0t: Mismatch! Expected%0d, Actual%0d (%s), $time, expected, DACFreq, msg); end else begin $display([PASS] Time%0t: Freq%0d OK (%s), $time, DACFreq, msg); end end endtask // // Main Test Process // initial begin // 1. Initialization SweepMode 0; StartFreq 0; StopFreq 0; StepFreq 0; TimeDelay 0; // Wait for reset (posedge rst_n); repeat(2) (posedge clk); //------------------------------------------------------ // Test Case 1: Basic Frequency Sweep //------------------------------------------------------ $display(\n TEST CASE 1: Basic Sweep ); set_sweep_config(100, 500, 100, 10); // 100 - 500, step 100, delay 10 cycles trigger_sweep(); // 等待足够时间观察一次扫频过程 // Delay10, 共需扫频 100-200-300-400-500 // 预估时间(10 delay cycles 2 step cycles) * 5 steps approx repeat(80) (posedge clk); // 验证扫频结束后是否回到起始频率并停止 // 逻辑扫频完成后应停在 StartFreq (100) check_freq(100, Sweep Finished, back to StartFreq); //------------------------------------------------------ // Test Case 2: Continuous Sweep Mode //------------------------------------------------------ $display(\n TEST CASE 2: Continuous Sweep ); set_sweep_config(1000, 2000, 200, 5); (posedge clk); SweepMode 1; // 持续保持高电平 $display([TRIG] Continuous Mode ON); // 观察一个完整的循环周期1000 - ... - 2000 - 1000 // 等待频率从 1000 变到 2000再变回 1000 // 由于延迟为 5粗略等待 60 个周期观察一次循环 repeat(60) (posedge clk); if (DACFreq 1000) $display([PASS] Continuous sweep loop detected correctly.); else $display([WARN] Freq%0d, waiting for cycle restart..., DACFreq); // 再等待一段时间确认仍在循环 repeat(40) (posedge clk); if (DACFreq 1000 DACFreq 2000) $display([PASS] Still sweeping in loop. Freq%0d, DACFreq); SweepMode 0; // 停止扫频 (posedge clk); //------------------------------------------------------ // Test Case 3: Boundary Condition (Start Stop) //------------------------------------------------------ $display(\n TEST CASE 3: Boundary Test ); set_sweep_config(500, 100, 100, 5); // Start Stop trigger_sweep(); // 此时 StartFreq StopFreq状态机应立即判断完成并回到IDLE repeat(10) (posedge clk); check_freq(500, Boundary Case: Start Stop, stay at StartFreq); //------------------------------------------------------ // Test Case 4: Reset During Operation //------------------------------------------------------ $display(\n TEST CASE 4: Reset During Sweep ); set_sweep_config(100, 1000, 100, 20); trigger_sweep(); repeat(15) (posedge clk); // 运行一半 $display([INFO] Asserting Reset during sweep...); rst_n 0; // 拉低复位 repeat(3) (posedge clk); check_freq(0, Output should be 0 during Reset); rst_n 1; // 释放复位 repeat(2) (posedge clk); check_freq(100, Output reset to StartFreq after Reset release); //------------------------------------------------------ // Simulation End //------------------------------------------------------ $display(\n ALL TESTS COMPLETED ); #100; $stop; end endmodule8. 仿真环境与工具仿真器Modelsim / Vivado Simulator 均可时钟频率100MHz周期10ns可根据需要调整观察方式打印输出 波形查看DACFreq、状态等9. 总结本设计实现了一个功能完备、参数可调的扫频频率发生器其状态机结构简单且易于理解覆盖了单次和连续两种工作模式。通过全面的Testbench验证确保了模块在各种边界条件下的正确性。该模块可作为频率合成、信号发生器或测试仪器的核心部件具有较高的工程应用价值。如需进一步扩展可增加频率字输出幅度控制、非均匀步进或扫频方向控制等功能有兴趣的读者可在此基础上进行二次开发。欢迎留言交流若需工程文件可私信获取。