第二章:开发环境搭建——Vivado安装与配置、IP核管理、Verilog基础回顾
好,咱们正式开始动手了。这一章我带你搭好开发环境。说白了,工欲善其事,必先利其器。Vivado 就是我们手里的那把“器”。
很多新手一上来就急着写代码,结果环境没配好,跑个仿真都报错,心态直接崩了。我刚开始用 Vivado 时也踩过不少坑,今天把这些经验都抖出来,你照着做就行。
2.1 Vivado 安装与配置
Vivado 是 Xilinx 家的官方工具,版本迭代很快。我个人习惯用 Vivado 2021.1 或 2022.2,这两个版本比较稳定,社区资源也多。
2.1.1 下载与安装
去 Xilinx 官网下载 WebPACK 版本就行,免费的,功能足够我们做脉冲输出和编码器接口。安装时注意几点:
- 磁盘空间:至少留 100GB,Vivado 全家桶装完大概 60-80GB。
- 操作系统:Windows 10/11 或 Ubuntu 18.04/20.04 都行。我个人更推荐 Linux,跑大型工程时稳定很多。
- 安装路径:不要有中文!不要有空格!我见过有人装在“D:\程序\Vivado”下,结果编译报错找半天原因。
我曾经因为磁盘空间不足,安装到一半卡死,强制退出后注册表乱了,最后重装系统才解决。所以,安装前一定检查磁盘剩余空间。
2.1.2 配置要点
装完后,第一件事是配置 Vivado 的环境变量。如果你用 Windows,把安装目录下的 bin 文件夹加到 PATH 里。这样你就能在命令行里直接敲 vivado 启动了。
第二件事,设置 默认工程路径。在 Vivado 的 Settings 里,把 Default Project Directory 改到一个好找的位置。我习惯放在 D:/FPGA_Projects/,每个工程一个文件夹,命名规则是 日期_项目名,比如 20250215_PulseOutput。
打开 Vivado 后,先点
Help → About → Manage Licenses,确认 License 状态。如果是 WebPACK 版本,不需要额外 License,但如果是申请了 Node-Locked License,记得把 MAC 地址填对。
2.2 IP 核管理
IP 核,说白了就是 Xilinx 帮你写好的功能模块。你想想看,如果每个计数器、每个 FIFO 都要自己从头写,那得累死。Vivado 里集成了大量 IP 核,我们直接拿来用就行。
2.2.1 如何添加 IP 核
在 Vivado 的 IP Catalog 里搜索你要的 IP。比如我们要做脉冲输出,可能会用到 Clocking Wizard(时钟管理)或 Counter(计数器)。
双击 IP 核,会弹出配置界面。这里要注意:配置参数一定要和你的设计匹配。比如 Clocking Wizard 里,输入时钟是 50MHz,你想要输出 100MHz,那就把倍频系数设成 2。
IP 核配置完成后,Vivado 会生成一个
.xci 文件。这个文件就是 IP 核的“身份证”,不要手动删除或改名。我见过有人为了“整理文件”,把 .xci 文件挪了位置,结果工程打不开。
2.2.2 自定义 IP 核
有时候官方 IP 核不够用,比如我们要做一个 编码器四倍频解码模块,官方没有现成的。这时候可以自己封装一个 IP 核。
步骤很简单:
- 写好 Verilog 代码,仿真通过。
- 在 Vivado 里点
Tools → Create and Package New IP。 - 选择
Package your current project,把代码打包成 IP。 - 以后在其他工程里,直接就能在 IP Catalog 里找到它。
我个人习惯把常用的模块都封装成 IP,比如 PWM 发生器、SPI 接口、编码器接口。这样新项目启动时,直接拖 IP 核,省时省力。
2.3 硬件描述语言基础回顾
Verilog 和 VHDL,你至少得会一个。我个人更常用 Verilog,因为语法简洁,社区资源多。但如果你公司要求用 VHDL,那也没问题,核心思想是一样的。
2.3.1 Verilog 基础结构
一个完整的 Verilog 模块长这样:
module pulse_generator (
input wire clk, // 系统时钟
input wire rst_n, // 异步复位,低有效
input wire [7:0] period, // 脉冲周期
output reg pulse_out // 脉冲输出
);
reg [7:0] cnt;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt <= 8'd0;
pulse_out <= 1'b0;
end else begin
if (cnt == period - 1) begin
cnt <= 8'd0;
pulse_out <= 1'b1;
end else begin
cnt <= cnt + 1;
pulse_out <= 1'b0;
end
end
end
endmodule
这段代码实现了一个可配置周期的脉冲发生器。注意看 always 块里的写法:posedge clk or negedge rst_n,这是标准的异步复位写法。我刚开始学的时候,总把 negedge rst_n 写成 posedge rst_n,结果复位一直不生效,仿真跑出来全是 X 态。
2.3.2 阻塞赋值 vs 非阻塞赋值
这是新手最容易搞混的地方。简单说:
- 阻塞赋值(=):用在组合逻辑中,比如
assign语句或always @(*)块里。 - 非阻塞赋值(<=):用在时序逻辑中,比如
always @(posedge clk)块里。
为什么会这样?因为非阻塞赋值能避免 竞争冒险。我曾经在一个项目里,把时序逻辑里的 <= 写成了 =,结果仿真波形看起来没问题,上板后计数器乱跳。查了两天才发现是赋值方式错了。
永远不要在同一个
always 块里混用阻塞和非阻塞赋值。这是铁律,没有例外。
2.3.3 参数化设计
好的 Verilog 代码应该是可配置的。比如上面的脉冲发生器,如果把 period 写成固定值 100,那换个场景就得改代码。用 parameter 可以解决这个问题:
module pulse_generator #(
parameter WIDTH = 8
) (
input wire clk,
input wire rst_n,
input wire [WIDTH-1:0] period,
output reg pulse_out
);
// 内部逻辑同上,只是把 8 改成了 WIDTH
endmodule
这样,例化时只要传不同的 WIDTH 值,就能生成不同位宽的计数器。我在做编码器接口时,经常用参数化设计来适配不同分辨率的编码器。
2.4 本章知识体系
下面这张图总结了本章的核心内容,你可以对照着检查自己掌握了多少:
嗯,这一章的内容就这些。环境搭好了,语言基础也回顾了,接下来就可以真正开始写脉冲输出模块了。记住,工具只是手段,理解原理才是关键。