Quick Start(快速上手)
本指南将引导你完成一个基于FPGA的PWM波发生器设计,从新建工程到上板验证,共十个步骤。整个流程在Vivado 2020.1环境下完成,目标器件为Xilinx Artix-7 (XC7A35T)。
- 新建Vivado工程,选择目标器件(如xc7a35ticsg324-1L)。
- 创建顶层模块pwm_generator_top,定义输入clk、rst_n,输出pwm_out。
- 编写PWM核心模块,包含一个计数器counter和比较器,实现占空比可调。
- 添加时钟分频模块(如将50MHz分频至1kHz),用于设定PWM周期。
- 编写Testbench,模拟输入时钟和复位,观察pwm_out波形。
- 运行行为仿真,验证PWM波形周期和占空比是否符合预期(如周期1ms,占空比50%)。
- 添加XDC约束文件,分配时钟引脚和输出引脚。
- 综合、实现并生成比特流,下载到FPGA开发板。
- 用示波器或逻辑分析仪测量输出引脚,观察PWM波形。
- 调整占空比参数(如修改比较值),重新编译下载,验证波形变化。
前置条件与环境
| 项目 | 推荐值 | 说明/替代方案 |
|---|---|---|
| 器件/板卡 | Xilinx Artix-7 (XC7A35T) | 其他7系列或Spartan-6,需调整引脚约束 |
| EDA版本 | Vivado 2020.1 或更高 | ISE 14.7(仅支持旧器件) |
| 仿真器 | Vivado Simulator (xsim) | ModelSim/QuestaSim |
| 时钟/复位 | 板载50MHz晶振,低电平有效复位 | 其他频率需调整分频系数 |
| 接口依赖 | 至少1个GPIO输出引脚 | LED也可作为简易指示 |
| 约束文件 | XDC文件,包含时钟周期和引脚分配 | UCF(旧工具) |
| 开发板 | Nexys A7-50T 或类似 | 任何带FPGA的板卡 |
目标与验收标准
本设计的目标是实现一个参数化的PWM波发生器,具备以下功能点和验收标准:
- 功能点:产生频率可调、占空比可调的PWM信号,频率范围为1Hz~1MHz(取决于时钟和计数器宽度)。
- 性能指标:输出频率误差小于5%,占空比分辨率不低于1%(8位计数器可达到0.39%)。
- 资源/Fmax:在Artix-7上综合后LUT消耗小于50,FF消耗小于30,最大工作频率不低于200MHz。
- 关键波形/日志:仿真波形显示pwm_out周期稳定,占空比与设定值一致;上板后示波器测量结果匹配。
实施步骤
工程结构推荐
pwm_generator/ ├── rtl/ │ ├── pwm_core.v │ ├── clk_div.v │ └── pwm_top.v ├── sim/ │ └── tb_pwm.v ├── constr/ │ └── pwm.xdc └── vivado/ └── pwm.xpr顶层模块pwm_top.v例化pwm_core和clk_div,clk_div将50MHz分频至1kHz作为pwm_core的时钟。
关键模块代码
以下为pwm_core.v的核心代码,实现计数器与比较器:
module pwm_core #( parameter WIDTH = 8, parameter DIV_CLK = 50000 // 分频系数,用于产生PWM周期时钟 )( input wire clk, input wire rst_n, input wire [WIDTH-1:0] duty, // 占空比设定值,0~2^WIDTH-1 output reg pwm_out ); reg [WIDTH-1:0] counter; reg clk_div_en; // 时钟分频逻辑(略,可独立为clk_div模块) // 计数器 always @(posedge clk or negedge rst_n) begin if (!rst_n) counter <= 0; else if (clk_div_en) counter <= counter + 1; end // 比较器 always @(posedge clk or negedge rst_n) begin if (!rst_n) pwm_out <= 0; else if (counter < duty) pwm_out <= 1; else pwm_out <= 0; end endmodule该模块采用计数器与比较器结构:计数器在每个分频时钟使能时递增,当计数值小于占空比设定值duty时输出高电平,否则输出低电平。通过修改duty值即可改变占空比。
时钟分频模块
时钟分频模块clk_div.v将50MHz输入时钟分频至目标PWM周期时钟。例如,若要产生1kHz的PWM周期,需将50MHz分频50000次。分频系数可通过参数传递,便于复用。
module clk_div #( parameter DIV_FACTOR = 50000 )( input wire clk, input wire rst_n, output reg clk_out ); reg [15:0] cnt; always @(posedge clk or negedge rst_n) begin if (!rst_n) begin cnt <= 0; clk_out <= 0; end else if (cnt == DIV_FACTOR-1) begin cnt <= 0; clk_out <= 1; end else begin cnt <= cnt + 1; clk_out <= 0; end end endmoduleTestbench编写
Testbench用于验证PWM波形的正确性。以下为tb_pwm.v的核心结构:
module tb_pwm; reg clk; reg rst_n; wire pwm_out; // 实例化顶层模块 pwm_generator_top uut ( .clk(clk), .rst_n(rst_n), .pwm_out(pwm_out) ); // 时钟生成 initial begin clk = 0; forever #10 clk = ~clk; // 50MHz end // 复位与激励 initial begin rst_n = 0; #100 rst_n = 1; #2000 $stop; end // 波形观察 initial begin $monitor("Time=%0t, pwm_out=%b", $time, pwm_out); end endmodule约束文件(XDC)
约束文件pwm.xdc用于分配时钟和输出引脚,并指定时钟周期。以下为示例:
# 时钟约束 create_clock -period 20.000 -name sys_clk [get_ports clk] # 引脚分配 set_property PACKAGE_PIN E3 [get_ports clk] set_property IOSTANDARD LVCMOS33 [get_ports clk] set_property PACKAGE_PIN C2 [get_ports rst_n] set_property IOSTANDARD LVCMOS33 [get_ports rst_n] set_property PACKAGE_PIN J15 [get_ports pwm_out] set_property IOSTANDARD LVCMOS33 [get_ports pwm_out]验证结果
完成仿真后,观察pwm_out波形。若设置duty=128(8位计数器,占空比50%),且分频后周期为1ms,则pwm_out应输出500μs高电平、500μs低电平的方波。仿真波形应显示周期稳定、占空比准确。上板后,用示波器测量输出引脚,波形应与仿真一致。
排障指南
- 仿真无波形:检查Testbench中时钟和复位信号是否正常;确认模块实例化端口连接正确。
- 占空比错误:检查duty值是否在0~2^WIDTH-1范围内;确认比较器逻辑正确。
- 上板无输出:检查XDC引脚分配是否正确;确认比特流已正确下载;用万用表测量引脚电平。
- 频率偏差大:检查时钟分频系数计算;确认输入时钟频率准确。
扩展建议
- 多通道PWM:例化多个pwm_core模块,每个使用独立的duty值,可生成多路PWM信号。
- 动态调节:通过UART或SPI接口接收外部命令,实时更新duty值,实现动态占空比调节。
- 死区插入:在PWM输出中加入死区时间,适用于H桥电机驱动等应用。
参考资源
- Xilinx Vivado Design Suite User Guide
- Artix-7 FPGA Data Sheet
- PWM基本原理与应用文档
附录
附录A:完整工程源码(含所有RTL文件、Testbench和约束文件)可从相关技术社区获取。附录B:常见问题解答(FAQ)持续更新中。



