跳转至

vivado

阅读信息

3314 个字  11 分钟  本页总访问量:加载中...

注意

本文由AI生成。

未完待续。

编译流程

网表是什么?

网表(netlist)是一种描述电路结构的文件或数据,它记录了电路中有哪些逻辑单元(门电路、寄存器、加法器、乘法器、存储块等)以及这些单元之间的连接关系。可以理解为电路的“清单”和“连接图”。

综合(synthesis)完成后,源代码就被转换成网表,这时候电路已经从抽象的 Verilog/VHDL 变成了门级描述,但还没有对应到芯片上具体的物理位置。

比如:

  • 元件部分:列出电路中所有用到的逻辑块。
  • 网络部分:说明哪些逻辑块的输出连到哪些输入。

它的作用是作为综合和实现之间的桥梁,让实现阶段(implementation)能根据这个电路结构去做布局和布线。

网表示例

好的,我用一个最简单的电路:

Text Only
y = (a AND b) OR (c AND d)

也就是有两个与门,结果再进一个或门。

如果写成 Verilog,大概是这样:

Verilog
1
2
3
4
5
6
7
8
9
module simple(
    input a, b, c, d,
    output y
);
    wire w1, w2;
    assign w1 = a & b;
    assign w2 = c & d;
    assign y = w1 | w2;
endmodule

综合之后,工具会生成一个网表(netlist)。 网表格式有很多种(EDIF、Verilog netlist 等),这里我给你展示一个简单的、接近“清单+连线”的描述:

Text Only
# 元件清单
U1: AND2
U2: AND2
U3: OR2

# 网络连接
U1.A = a
U1.B = b
U1.Y = n1

U2.A = c
U2.B = d
U2.Y = n2

U3.A = n1
U3.B = n2
U3.Y = y

解释一下:

  • U1, U2, U3 是实例化的门电路。
  • AND2OR2 表示两输入的与门、或门。
  • n1n2 是综合过程中生成的中间网络。
  • 这张表就完整描述了电路结构:元件和它们之间的连线关系。

Synthesis 是什么?

Synthesis(综合) 是 FPGA/ASIC 设计流程中的一个关键步骤。它的作用是把你写的 硬件描述语言(HDL,比如 Verilog/VHDL)代码 转换成一个 逻辑电路网表(netlist)

通俗地说,就是把“抽象的代码”翻译成“由门电路、触发器、算术单元组成的硬件结构”。

主要过程包括:

  1. 解析 HDL 代码:检查语法和语义。
  2. 逻辑推导与优化:把高级的行为描述(如 if-else,算术运算)转换为基本的逻辑单元,同时尝试优化,减少面积或提高速度。
  3. 映射到器件库:根据目标 FPGA 芯片的资源,把逻辑功能实现成该芯片能提供的基本单元,比如 LUT(查找表)、FF(触发器)、DSP 块、RAM 块等。
  4. 生成网表:输出一个门级电路清单,供后续 implementation(实现:布局布线) 使用。

举个例子,你写了 Verilog:

Verilog
assign y = (a & b) | (c & d);

综合之后,工具会把它变成:

  • 两个 AND 门
  • 一个 OR 门
  • 以及这些门之间的连线关系

也就是我之前给你演示的网表。

一句话总结:Synthesis 就是把 HDL 源代码转换成逻辑电路(网表)的过程,是从“代码”到“电路”的桥梁。

Implementation 是什么?

Implementation(实现) 是 FPGA 设计流程中继 Synthesis(综合) 之后的一步。

综合完成后,你得到的只是一个逻辑电路的网表(知道有哪些门电路、寄存器,以及它们之间怎么连接),但这个电路还没有放到 FPGA 芯片的实际资源上。Implementation 的任务就是把这些逻辑真正“安置”到芯片内部。

主要步骤包括:

  1. Opt Design(优化设计):对综合出来的网表再做一些逻辑优化。
  2. Placement(布局):决定每个逻辑单元放到 FPGA 芯片上的哪个具体位置(比如哪一个 LUT、哪一个寄存器)。
  3. Routing(布线):为单元之间的连接分配芯片内部的连线路径。
  4. 时序检查:确认电路能满足你的时钟频率要求,不会因为布线延迟过大而出错。

完成 Implementation 之后,你就得到一个完整的、和芯片资源绑定的电路设计结果,这时才能进一步生成 Bitstream(比特流文件 .bit),烧录到 FPGA 里运行。

可以这么理解:

  • Synthesis → 把 HDL 代码翻译成“电路图”
  • Implementation → 把“电路图”放到“芯片”上,并连好线
  • Bitstream → 把结果转成机器能直接加载的配置文件

Bitstream 是什么?

Bitstream(比特流文件,通常是 .bit.bin 是 FPGA 开发流程的最后产物。

它的本质是:一串二进制数据,里面记录了 FPGA 芯片中每个可编程资源(查找表 LUT、触发器 FF、布线开关、I/O 配置等)的具体配置状态。

FPGA 是一种 可重构硬件,出厂时内部都是“空白的开关矩阵”。只有把 bitstream 下载进去,它才知道:

  • 哪些 LUT 实现什么逻辑函数
  • 哪些寄存器存放数据
  • 哪些线要连通
  • 哪些引脚作为输入/输出,以及电平标准

整个设计经过 Synthesis(综合)→ Implementation(实现) 后,Vivado 就会根据布局布线的结果生成这个 bitstream 文件。然后你通过下载线(如 JTAG、USB)把 bitstream 烧录到 FPGA 上,芯片就能按你设计的电路来工作。

一句话总结:Bitstream 就是把你的 HDL 代码和电路设计“翻译”成 FPGA 能理解的二进制配置文件,是让芯片变成你电路的关键步骤。

IP 核封装

IP 核是什么?

IP 核(Intellectual Property Core,知识产权核)是一段经过封装、可以直接复用的硬件模块(逻辑电路设计),可以在不同项目中重复使用。

.xml 文件是什么?

在 Vivado 封装 IP 核的过程中,会生成一个 .xml 文件,这个文件的作用主要是描述和管理 IP 核的元信息。

.xml 文件记录这个 IP 的名称、版本号、参数、接口类型等信息,Vivado 可以根据这个文件知道如何识别和加载你的 IP。
如果你的 IP 核有可调参数(比如计数器宽度、时钟频率等),.xml 会保存这些参数的默认值和可选范围。
当你在 Vivado 的 IP Integrator 里拖拽 IP 时,软件读取 .xml 文件来显示 IP 的接口、端口和参数选项。

Example of .xml file
Text Only
<?xml version="1.0" encoding="UTF-8"?>
<spirit:component xmlns:xilinx="http://www.xilinx.com" xmlns:spirit="http://www.spiritconsortium.org/XMLSchema/SPIRIT/1685-2009" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<spirit:vendor>xilinx.com</spirit:vendor>
<spirit:model>
    <spirit:ports>
    <spirit:port>
        <spirit:wire>
        <spirit:direction>out</spirit:direction>
        <spirit:vector>
            <spirit:left spirit:format="long">4</spirit:left>
            <spirit:right spirit:format="long">0</spirit:right>
        </spirit:vector>
        </spirit:wire>
    </spirit:port>
    </spirit:ports>
</spirit:model>
<spirit:fileSets>
<spirit:description>MUX2T1_5_v1_0</spirit:description>
</spirit:component>

怎么生成网表文件?

Text Only
write_verilog -mode synth_stub E:/FPGA/ip/MUX2T1_5.v
  • write_verilog:让 Vivado 导出 Verilog 格式的文件。
  • -mode synth_stub:表示导出的是 stub 文件(空端口文件)。

  • Stub 文件只有模块名和端口定义,没有具体实现逻辑。

  • 主要用于在其他工程里调用这个 IP 时作为“占位符”。

  • E:/FPGA/ip/MUX2T1_5.v:指定生成的文件路径和文件名。

这一句的效果:生成一个 空壳 Verilog 文件,里面只有 module MUX2T1_5(...) 的端口定义,供你在其他地方引用。

Text Only
write_edif E:/FPGA/ip/MUX2T1_5.edf
  • write_edif:让 Vivado 把设计导出为 EDIF(Electronic Design Interchange Format) 文件。
  • E:/FPGA/ip/MUX2T1_5.edf:指定生成的文件路径和名字。

这一句的效果:生成一个 EDIF 格式的网表文件,里面是逻辑电路结构(门级连接),供后续工具或者其他项目使用。

所以总结一下:

  • 第一条命令导出 空壳 Verilog 接口文件,只保留端口定义。
  • 第二条命令导出 网表文件(.edf),保留逻辑实现。

这两步经常配合用:上层工程用 stub 文件识别端口,真正的功能由 EDIF 网表来实现。

调用 IP 核原理

你问的这个问题,其实就是 Vivado 调用 IP 核(Intellectual Property Core) 的内部原理,我帮你详细拆开讲一讲:

1. IP 核本质

在 Vivado 里,一个 IP 核其实就是一个“已经实现好的模块封装”,里面可能是:

  • 源代码(Verilog/VHDL),或者
  • 网表文件(EDIF .edf / DCP .dcp),甚至
  • 加密后的黑盒逻辑

它对外只暴露接口(端口),对内隐藏了具体实现。这样你在工程中使用时,就像调用普通模块一样。

2. 文件组成

当你创建/生成一个 IP 核时,Vivado 会生成一堆文件:

  • .xml 文件:描述这个 IP 的参数、接口特征等(元数据)。
  • .tcl 脚本:配合 Vivado 的 XGUI 界面使用,根据用户在 GUI 中的参数配置动态生成或更新 IP。
  • .edf / .dcp 文件:设计的网表或已实现的设计快照,是真正的逻辑实现。
  • stub 文件(.v 或 .vhdl):只有端口声明的“空模块”,让综合器知道接口定义。

3. 调用流程

当你在 Vivado 工程里“调用”一个 IP 核时,内部会发生这些事情:

  1. 解析 XML 描述 Vivado 会读取 .xml 文件,知道这个 IP 有哪些参数(比如位宽、复位信号极性)、有哪些输入输出端口。

  2. 执行 Tcl 脚本 Tcl 脚本会根据用户的配置(你在 GUI 或 Tcl Console 里选择的参数),自动生成相应的工程文件,比如:

  3. 生成/复制 stub 文件(空接口)

  4. 复制 .edf.dcp 文件到工程目录下
  5. 更新工程的 .xci.xco 文件(IP 配置文件)

  6. 引入网表 在综合和实现阶段,Vivado 会自动把对应的 .edf.dcp 加载进来,作为逻辑实现。因为 stub 文件只有端口,真正的逻辑是从网表来的。

  7. 实例化使用 在你的 HDL 代码里,只需要 U1: ip_core (...) 这样的实例化调用。综合器会发现这是个 stub,然后在 Implementation 阶段用对应的网表替换进去。

4. 一个类比

可以把 IP 核想成:

  • stub 文件 → 产品说明书(告诉你接口长什么样)
  • .xml + Tcl → 装配指南(告诉 Vivado 怎么把这东西装进工程)
  • .edf/.dcp → 真正的零件(逻辑实现)

当你“调用”一个 IP 时,Vivado 并不是重新生成逻辑,而是 根据 xml 特征 + Tcl 脚本,把现成的网表复制到你的工程里,并让综合/实现阶段识别并链接

5. 总结

调用 IP 核的原理就是:

  • GUI 参数配置 → Tcl 脚本解析 → 根据 xml 描述生成接口文件(stub)
  • 同时把网表(edf/dcp)加入工程
  • 综合阶段看见 stub,知道这是个黑盒
  • 实现阶段用网表替换黑盒 → 完整逻辑嵌入工程

I/O Buffer是什么?

在 FPGA 设计中,I/O Buffer(输入输出缓冲器)是一种特殊的硬件单元,用于连接 FPGA 内部逻辑与外部引脚,确保信号可以安全、可靠地进出芯片。

IBUF(输入缓冲器)把外部信号传入 FPGA 内部逻辑,提供电平匹配、噪声滤波和逻辑信号稳定性。OBUF(输出缓冲器)把 FPGA 内部逻辑信号驱动到外部引脚,提供足够的驱动能力,保证信号可以传输到外部电路。IOBUF(双向缓冲器)用于既能输入又能输出的引脚,内部逻辑通过控制信号决定当前是输入模式还是输出模式。

I/O Buffer 的作用是:匹配 FPGA 内部逻辑电平与外部接口电平,保证信号能安全传输;提供足够的驱动能力,应对外部负载和线路特性;防止 FPGA 内部信号直接暴露给外部电路,从而保护芯片。

举例来说,如果有一个 FPGA 输入引脚 A 和输出引脚 Y,输入时信号先经过 IBUF 进入内部逻辑,输出时内部逻辑信号先经过 OBUF 再驱动外部引脚。如果是双向引脚,可以用 IOBUF 控制方向。

在 Vivado 中,默认情况下,综合顶层模块时会自动在顶层 I/O 上插入 I/O Buffer。如果选择 out_of_context 综合,模块内部不会插入 I/O Buffer,因为顶层 I/O 由调用工程统一管理。

一句话总结:I/O Buffer 是 FPGA 内部逻辑和外部引脚之间的桥梁,确保信号可靠、安全地传入或传出芯片。

out-of-context是什么?

在 Vivado 中,当你对某个模块进行综合时,可以设置一个选项叫 out_of_context,意思是让这个模块 独立于顶层工程进行综合

通常,综合会考虑模块的整个上下文,包括顶层模块的输入输出和连接情况,同时会自动在顶层 I/O 引脚处插入 I/O 缓冲器(如 IBUFOBUFIOBUF),确保信号能正确驱动 FPGA 外部接口。但是有些情况下,你只想对某个子模块本身进行逻辑综合,不想把它的顶层 I/O 包含进来,这时就可以使用 out_of_context

使用 out_of_context 有几个效果:

  1. 不生成顶层 I/O 缓冲器

  2. 模块内部逻辑会被综合成门级网表,但顶层输入输出不会自动加上 I/O buffer。

  3. 这样生成的网表只包含内部逻辑,外部接口留空或当作黑盒处理。

  4. 加快综合速度

  5. 因为不需要处理顶层连接和 I/O 分配,Vivado 只关注模块内部逻辑。

  6. 对大型工程或 IP 封装尤其有用,可以独立生成网表。

  7. 方便模块复用

  8. 模块可以作为独立的逻辑块导出(比如 .edf.dcp 文件),供其他工程或顶层模块引用。

  9. 你在顶层工程中再实例化时,顶层 I/O 会根据实际需要加上缓冲器。

导出网表文件时设置OOC,不插入任何I/O Buffer:

启用 out_of_context 后,Vivado 把这个模块当作 独立的子工程或黑盒 来综合,只生成模块内部逻辑,不管它外面怎么连接,也不生成任何 I/O buffer。这样可以方便 IP 核封装、模块复用和分块综合。

在导出网表文件时设置 OOC(out_of_context),让模块不加入任何 I/O Buffer,是因为此时你只想生成模块内部的逻辑网表,而不关心它在顶层工程的引脚连接。

OOC 模式下,Vivado 会把模块当作一个独立子模块或黑盒来综合,只关注模块内部的逻辑功能,不生成顶层 I/O 相关的缓冲器。这样做有几个好处:

  1. 可以独立综合模块,提高综合速度,不受顶层引脚约束影响。
  2. 生成的网表更通用,便于在其他工程中复用,顶层 I/O 由调用工程统一处理。
  3. 避免在生成的网表里固定 I/O 连接,保留接口灵活性,使模块更像一个可重用的 IP 核。

简单来说,设置 OOC 并不加 I/O Buffer,是为了让导出的网表只包含内部逻辑,而顶层 I/O 由最终工程来决定,保证复用性和灵活性。