咖啡图片
正在将巧克力泡入咖啡
ntainer" style="display: none">
文章

Vivado 开发实践

知识储备

Vivado 开发实践

开发流程

FPGA 开发流程大致可以分为以下步骤:

  • 设计(Design):用 HDL 对硬件电路进行建模描述,需要定义电路功能实现、设计约束和测试用例。对于许多常用的功能,有现成的 IP 核作为轮子可以直接使用。测试用例是针对功能仿真的,由.sim 文件管理;设计约束主要由.xdc 文件管理。
  • 功能仿真(Simulation):又称前仿真,指在计算机的仿真器上运行 Verilog 代码,在不考虑硬件电路实现和绝大部分约束的情况下,仅验证逻辑功能是否正确。功能仿真是可选步骤。
  • 分析和综合(Systhesis):检查 HDL 代码是否符合综合规则,并由 EDA 在厂家提供的标准单元库和设计阶段中的约束下,编译出由 HDL 描述的逻辑网表并进行逻辑优化。逻辑网表是指一个标准的逻辑门或查找表的集合,是一个逻辑层面的结构,可以包括时序触发器、寄存器等时序元素,是逻辑门级别的描述。另外,注意不是所有的 HDL 语句都可以综合出相应的逻辑电路。硬核 IP 已经在芯片流片(制造)时,用晶体管电路实现并固化在硅片中了,不需要综合。
  • 布局布线(Implementation):分配引脚并确定内部电路的连接关系,进行 layout 和 IO planning,根据设计阶段的约束文件和综合出的逻辑网表,利用厂家提供的标准元件库对门级电路进行布局,在考虑各种约束和优化的情况下,将设计中的门级网表映射到 FPGA 的物理布局上并进行物理优化。完成这一步后就将 HDL 描述的模型电路转化为标准元件库组成的数字电路,且此时的电路已经包含了时延信息。
  • 时序分析(Analysis):指时序仿真,又称后仿真,用于检查信号延迟是否满足要求。时序分析有静态时序分析(STA)和动态时序分析(DTA),静态时序分析是通过计算每条路径的延迟来检查是否满足设计的时序约束;动态时序分析是模拟设计的运行,动态检查信号的传播延迟,用于验证时序边界。时序分析是可选步骤。
  • 生成比特流和板级调试(Bitstream):生成烧录到板子中的二进制文件.bit,并进行实际硬件的调试。生成比特流之前需要完成设计、综合和布局布线。比特流文件是一种专门为 FPGA 硬件配置而设计的格式,文件内容是经过优化的、以二进制形式存储的配置信息,包含了逻辑元件的映射、时序约束、硬件资源配置等所有信息,能够直接在 FPGA 上加载和执行。

设计约束包括:

  • 引脚约束:又称 IO 约束、引脚绑定,将逻辑信号与物理引脚进行对应
  • 时序约束:保证各信号按预期的时序顺序传输,在规定时间内到达正确的位置,主要类型包括时钟约束、输入输出延迟约束、时序路径约束等
  • 资源约束:又称面积约束,限制 FPGA 资源的使用量来适配目标 FPGA 的可用资源
  • 布局约束:用于控制设计中各逻辑单元的物理布局,确保逻辑单元被放置到 FPGA 的指定资源区域内,从而提高时序性能、减少信号延迟并优化资源使用

FPGA / ASIC 的设计常分层为行为级、RTL 级和门级。行为级只写算法逻辑,不考虑硬件细节;RTL 级描述寄存器之间的逻辑运算和数据流,合成工具能直接将 RTL 转换成门级电路;门级为门电路和触发器的连接,是 RTL 综合之后的结果。RTL(Register Transfer Level,寄存器传输级)是硬件描述语言 HDL 对电路行为的一种抽象层级,主要描述寄存器之间的数据传输和逻辑运算,不关注底层由多少与非门、触发器组成,而是更高一层的“时钟边沿+数据流动”。

Vivado 使用

Vivado 可以创建 RTL Project 和 Post-synthesis Project,RTL Project 是最常用的完整设计流程项目,覆盖从 RTL 代码生成到比特流生成的全流程;Post-synthesis Project 用于导入已经完成综合的网表,聚焦于后端实现(布局布线、时序优化),适合复用已有的综合结果、快速验证不同实现策略的效果。

Vivado 是靠 tcl 工作的,GUI 界面是 tcl 的壳子,界面上的每一步操作都等价于在后台执行 tcl 命令。就像命令行和图像界面的关系一样,tcl 脚本的意义在于构建自动化的工程流程(创建工程、添加源文件、生成 IP、设置约束、综合实现、生成比特流、导出硬件),一些应用 tcl 的例子比如工程版本复现、批量生成 IP、根据顶层端口生成约束文件、多版本代码自动测试、不同优化性能下比较性能、快速验证多个参数组合的效果。Xilinx Tcl Store 是一个开源共享的 tcl 脚本库,其中有一些实用的 tcl 脚本,相当于 Vivado 的插件系统,可以扩展 Vivado 设计套件的核心功能。在 tcl store 中点击可以查看每个脚本支持的 tcl 命令。可以基于 tcl 脚本创建工程,Tools 选项卡中可以运行 tcl 脚本文件,也可以直接在 tcl console 中交互式运行 tcl 命令。Xilinx 官方关于 tcl 的文档为 UG894 和 UG835。

Vivado 有工程模式和非工程模式两种流程设计的模式。工程模式指直接用 Vivado 完成一套设计流程,先创建工程,然后让软件管理设计文件,生成报告信息等。非工程模式指用 Tcl 命令或者脚本来控制设计流,Vivado 不再对文件进行自动化管理,也不再报告相关信息,但是在每一个设计的阶段都可以进行新的设计分析以及约束分配,并且将更改后的设计以及约束直接更新到当前的设计流。

Tools 中有一些入门阶段接触不到但看起来似乎很实用的功能,先记录下来。Create and Package New IP 用于封装自定义 IP;Create Interface Definition 用于自定义接口标准;Partial Reconfiguration Wizard 用于实现 FPGA 的部分重配置功能,即在系统运行时动态更新 FPGA 部分区域的逻辑,用于自适应计算、多任务切换等需要灵活升级、资源分时复用的场景;Associate ELF Files 用于关联 elf 可执行文件,支持嵌入式程序的调试下载和运行;Generate Memory Configuration File 用于初始化 FPGA 的 BRAM、DDR 等存储器,指定上电初始数据;Compile Simulation Libraries 用于为第三方仿真工具编译 XIlinx IP 和硬件原语的仿真模型,确保第三方工具仿真时能正确调用 FPGA 底层硬件逻辑,保证仿真与硬件行为一致;Custom Commands 用于将自定义工具脚本命令集成到 Vivado 界面中;Language Templates 提供 Verilog、VHDL、Tcl 的代码模板;Settings 用于配置 Vivado 的全局参数。

Block Design 是 Vivado 里的图形化硬件系统设计方式;大多数情况下一个工程中只有一个 bd 文件,用来把系统全部搭建起来,不过也可以创建多个 bd 文件用于封装多个子系统。Open Block Design 可以选择打开项目中的某一个 Block Design。Generate Output Products 可以选择为某一个 Block Design 内的各个 IP Core 生成 HDL 底层实现文件,修改 BD 中的 IP 时需要重新执行。Create HDL Wrapper 是给 BD 生成一个顶层 HDL 文件,让综合器将 BD 当作一个普通 IP 来使用,生成的 Wrapper 就是综合/仿真时的入口文件,生成时可以选择让 Vivado 自动维护,修改 BD 时自动更新。通常先 Generate Output Products,再 Create HDL Wrapper。绑定引脚时以 Wrapper 中的声明为准。

Report Methodology 是 Vivado 的“设计规范检查器”(Design Methodology Checker),可检测 HDL 设计是否符合 Xilinx 推荐的一系列规则和设计实践,并给出优化和修复建议。Report DRC(Design Rule Check)用于检查设计是否违反 FPGA 实现、布线、逻辑、IP、XDC、BD 的“硬性规则”,例如 IO Bank 电压不兼容、时钟走线错误、跨 Bank 电压冲突、约束冲突等。Report Noise 是 Vivado / Xilinx 工具中用于评估 FPGA I/O 信号“噪声问题(Noise)”的分析工具。 它主要用于分析地弹(Ground Bounce)、串扰(Crosstalk)、SSO(同时开关输出)噪声等 I/O 电气问题,一般用不到。上面说的三个 report,在 RTL Analysis、Synthesis 和 Implementation 中都有,各自会分别给出前端 RTL 代码编写和后端约束和优化的建议。除此之外,Report Utilization 可以报告综合后或实现后使用的逻辑资源量。

Schematic 原理图中会显示逻辑连接,右键某个模块,其中的 floorplanning 可以将当前选中的逻辑布置在自定义的 FPGA 中的某个物理区域(Pblock,即 Physical Block)中,相当于创建对布置的空间约束,让实现工具按照设置的约束来放置逻辑,这些逻辑在实现时就会被限制在这个区域里。右键模块,其中的 report timing 可以只对与该模块相关的路径进行时序分析并输出报告,包括输入 → 模块 → 输出的关键路径。大型工程里跑整个 timing report 比较杂乱。工程师通常只关心:哪个模块拖慢时序?为什么某个模块无法达到 300MHz?该模块内部是否有深逻辑?跨模块路径是否有 delay?等问题。另外,schematic 的 timing report 基于 post-implementation netlist,如果还在综合阶段,只能看到综合后的估计时序。setup = 数据必须在时钟沿到来之前稳定一段时间;hold = 数据必须在时钟沿之后继续保持稳定一段时间。另外,schematic 中还可以选择将原理图展开到不同的层级。还可以选择连线并 mark debug,assign 到 debug hub 的某个 probe 上。

布线工具是 EDA 的自动布线算法,读取网表(逻辑单元和连接关系)并决定每个逻辑单元放在 FPGA 上的哪个位置(Place),决定用哪些具体的导线 + 哪些开关,把它们连起来(Route)。实现的效果是把抽象的“逻辑连接” → 映射到具体的 物理导线和开关配置。输出的结果是一个 bitstream 配置文件,用来配置 FPGA 内部的 SRAM 控制每个开关的开关状态。

PS 端的工作大致可以分为两部分,一是与 PL 端的交互接口,这部分主要是基于 AXI 协议的开发;二是各种外设的接口,这部分根据例程调用相应的 API 即可,类似于 HAL 库的开发方式。配置完 ZYNQ 核中的 PS 部分之后导出端口、连线;之后 Generate Output Products,生成 block 输出文件,包括 IP、例化模板、RTL 源文件、XDC 约束、第三方综合源文件等,再用 Block Design(bd 文件)Create HDL Wrapper,最后导出硬件信息生成 sdk 文件夹,这就包含了 PS 端的配置信息。SDK 部分通过导出的 hdf 文件启动并建立 App 工程,完成 App 的软件开发后,通过 Run 或 Debug 在板子上运行。

system.hdf 中包含了导出的硬件平台描述,用 SDK 打开后可以看到一张表格,其中 cell 一列表示 block design 中相应的硬件 IP 实例名,用于在软件层面标识。CPU 访问外设时,是通过 AXI 总线以内存映射寄存器的方式进行的,表格中 base address 和 high address 是分配给该外设的一段统一编址后的物理地址空间,每个外设都有一段寄存器空间。bsp 的 include 目录下包含了 xilinx 的各种头文件,其中包含各种宏定义和函数声明。xparameters.h 中定义了各个外设的基地址、器件 ID 和中断等。libsrc 目录下包含了外设函数定义和使用注释说明。lscript.ld 中定义了可用 memory 空间,栈和堆空间大小等,可根据需要修改。lscript.ld 中 psu_ddr_0_MEM_0 和 psu_ddr_1_MEM_0 的值是根据 system.hdf 文件来的,SDK 部分的代码也要与之保持一致。

IP 和 Module

Module 一般指用户写的 RTL 代码,是纯粹的 Verilog / VHDL / SystemVerilog 模块,完全掌握源码,可综合可仿真;IP(Intellectual Property Core)一般指 Xilinx 或第三方提供的与构建电路,经过了验证和优化,打包成黑盒,在工程中表现为 .xci 配置文件,存储了 IP 名称、参数配置、版本号、生成代码的路径等信息,用于告诉 Vivado 怎么生成这个 IP。Vivado 根据 .xci 文件 Generate Output Product,即生成 RTL、网表.dcp 或.edf、仿真模型、约束文件.xdc、wrapper 文件、资源报告等。有的 IP 只有 netlist(加密,看不到源码,不能直接修改逻辑),IP 可以配置,底层实现可能包含厂商专用的宏元件,用户不能直接手写,有的 IP 甚至包含仿真模型和约束文件。从使用上说,IP 是一个特殊的 Module。可以自定义 ip 或添加第三方 ip。

在 FPGA/SoC 设计语境中,IP 核(Intellectual Property Core)指的是一种可复用的逻辑模块实现,可以是 HDL 描述的、可综合到 FPGA 逻辑中的软核 IP;也可以是直接固化在硅片中、不需要综合的硬核 IP;还可以是固件宏块,类似于硬核 IP 但是核 FPGA 可编程逻辑共享工艺。例如:PS UART 在硬件上是 ZYNQ 芯片 PS 区域的一块 UART 控制电路(硬核 IP);在软件上,它暴露一组寄存器地址,CPU 通过 AXI 总线访问这些寄存器就能控制 UART;在引脚上,它把 TX 和 RX 信号通过 MIO 和 EMIO 引脚输出,连接到板上外设,比如 USB-UART 转换芯片。

Xilinx 提供了功能丰富的 IP,入门阶段接触比较多的有:

  • AXI 相关的:AXI Interconnect、AXI Stream FIFO、AXI DMA 等
  • FPGA 基本资源相关的:Block Memory Generator、FIFO Generator、Clocking Wizard、SelectIO Interface Wizard、XADC Wizard 等
  • 运算相关的:CORDIC、Divider Generator、Floating-point、FFT、DTF 等

像加法器、乘法器这样 Verilog 代码很容易实现的功能,也有相应的 IP。一行 Verilog 代码和工程级别的 IP 差距巨大,IP 的意义在于高性能的运算器,重要的是底层硬件结构。比如加法有 ripple-carry、carry lookahead、carry select、prefix adder 等实现方式;乘法有 DSP Silce、Booth、Wallace Tree 等实现方式;以及是否支持流水线,流水线级数如何配置;位宽的变化怎么处理;延迟 latency 怎么控制等。Verilog 代码的默认实现存在性能不一定够、时序不一定收敛、资源不一定最优、不确定实现的硬件资源可能是 DSP 或 LUT、跨时钟域和多周期路径无法处理、不支持 pipeline、频率上不去、不支持 ready/valid 的 backpressure 设计等一系列在实际工程中才会考虑的问题。

生成 IP 或模块时,一般会选择 Out-of-Context 综合/实现 (OOC),即单独综合/实现一个时序独立、不受外部逻辑影响的模块,因为 IP 模块可能很复杂,如果每次综合整个工程就会非常慢。OOC 相当于提前单独综合好,工程里直接引用已完成的网表。

常见 IP 使用

XDC 编写

普通 IO 口只需约束引脚号和电压,注意大小写,端口名称是数组的话用{ }括起来,端口名称必须和源代码中的名字一致,且不能和关键字一样。

管脚约束:set_property PACKAGE_PIN 引脚编号 [get_ports 端口名称]

电平信号约束:set_property IOSTANDARD 电平标准 [get_ports 端口名称]

1
2
3
4
5
6
7
8
9
10
11
12
13
set_property PACKAGE_PIN J16 [get_ports {led[3]}]
set_property PACKAGE_PIN K16 [get_ports {led[2]}]
set_property PACKAGE_PIN M15 [get_ports {led[1]}]
set_property PACKAGE_PIN M14 [get_ports {led[0]}]
set_property PACKAGE_PIN N15 [get_ports rstn]
set_property PACKAGE_PIN U18 [get_ports clk]

set_property IOSTANDARD LVCMOS33 [get_ports {led[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports rstn]
set_property IOSTANDARD LVCMOS33 [get_ports clk]

BOARD_PIN 是 Vivado 板级接口约束(Board Interface Constraint)系统的一部分属性。它不是约束 FPGA 封装引脚的“物理约束”(比如 PACKAGE_PIN), 而是 Vivado 板卡定义(Board Definition)机制中自动生成的“逻辑绑定信息”。Vivado 的 Board 文件系统允许:选择某块开发板(如 ZCU102、ZedBoard、VC707);然后直接把 IP 的接口(如 AXI GPIO、UART、I2C)连接到 板卡接口(Board Interface);Vivado 自动知道这些接口应该连到哪个物理引脚、使用什么 IOSTANDARD。这种自动化的“板卡接口”约束就叫 Board Interface Constraint,它在内部通过命令实现:

1
set_property BOARD_PIN "some_pin_name" [get_ports <port_name>]

Petalinux

为了在 Xilinx 的硬件平台上运行 Linux,需要使用 Petalinux 工具。Petalinux 不是 Linux 内核,而是一套配置开发环境的工具,降低 uboot、内核、根文件系统的配置的工作量,可以从 Vivado 导出的硬件信息自动完成相关软件的配置。Petalinux 本身基于 Yocto Project(嵌入式 linux 定制框架)构建,内置了针对赛灵思硬件的交叉编译工具链(如 arm-xilinx-linux-gnueabi),支持在 x86 主机上编译针对 ARM 架构的 Linux 内核、驱动和应用程序。Petalinux 编译后会生成嵌入式系统的核心镜像,包括 Linux 内核镜像(定制化的 Linux 内核)、设备树 Device Tree Blob(描述硬件拓扑)、根文件系统 rootfs(包含系统库、命令行工具、应用程序等,python 就在这里面)、启动加载器 bootloader(默认使用针对 Xilinx 硬件优化的 u-boot)

下面简单记录按照 Alinx 厂家的教用 Petalinux 制作板子镜像并固化到 SD 卡的过程,详细步骤见教程。由于 Petalinux 对系统版本和设置有严格要求,这里按照 Alinx 厂家的教程,在 PC 的虚拟机上安装 ubuntu16.04(或者双系统也行)并在 ubuntu 上面安装 Petalinux2017.4。用 Petalinux 定制 Linux 系统涉及 Vivado 工程和 petalinux 工程,在 Vivado 中编译生成 bit 文件,导出硬件信息并得到包含硬件信息的 hdf 文件,Petalinux 根据 hdf 文件配置 uboot ,内核、文件系统等。

1
2
3
4
5
6
7
8
9
10
11
12
# 创建Petalinux工程
petalinux-create --type project --template zynq --name ax_peta
# 基于Vivado导出的hdf文件,由配置界面配置硬件信息
petalinux-config --get-hw-description ../linux_base.sdk
# 由配置界面配置内核
petalinux-config -c kernel
# 由配置界面配置根文件系统
petalinux-config -c rootfs
# 编译
petalinux-build
# 生成BOOT文件
petalinux-package --boot --fsbl ./images/linux/zynq_fsbl.elf --fpga --u-boot --force

在 PC 的 Linux 上用 disk 工具,分区出 FAT 和 EXT,此时可以把文件放入 SD 卡的 EXT 分区中(Windows 系统是不显示 EXT 分区的,Linux 可以,所以放文件要在 PC 端的 Linux 下操作);然后将工程目录 images –> linux 目录中的 BOOT.BIN 和 image.ub 复制到 SD 卡的 FAT 分区即可。如果需要打包成 img 镜像,使用 imageUSB 工具即可。

Petalinux 的版本和 python 的版本是绑定的,但 Xilinx 的官方文档中并没有给出对应关系,目前已知 Petalinux2023.1 对应 python3.10.6。如果需要改 python 版本就需要尝试安装不同的 Petalinux 版本并完成整套的 Linux 系统定制,在板子上运行起来编译出的镜像之后,由 python3 –version 才能查到这个 Petalinux 版本对应的 python 版本是否符合要求。

另外再多说一句图形界面相关的问题(完全可以不用图形界面拥抱命令行,不过已经问过了相关的情况,这里就一起记录下来了),Petalinux 自带桌面系统 matchbox,但这个系统与 ZYNQ 7000 架构的适配有 bug;Alinx 厂家是自己移特制 Linux 内核和经过移植的 Debian 桌面文件系统,理论上应该也可以自己移植 Linux 其它发行版如 ubuntu 的桌面系统,但移植时涉及文件系统,python 版本和库也要在此时一并作好处理;不过移植 Linux 在没有接触过的情况下工作量太大坑太多,并且图形界面也不是刚需,这里就不配置了。

Verilog 经典电路实现

判断溢出

1
2
3
4
5
6
7
8
9
10
11
12
13
module top_module (
    input [7:0] a,
    input [7:0] b,
    output [7:0] s,
    output overflow
);
    wire temp, flag1, flag2;
    assign s = a + b;
    // $bits()是获得变量位宽的语法糖
    assign flag1 = (a[$bits(a)-1] ^ b[$bits(b)-1] == 0);
    assign flag2 = (a[$bits(a)-1] ^ s[$bits(s)-1] == 1);
    assign overflow = (flag1 && flag2) ? 1'b1 : 1'b0;
endmodule

同步复位和异步复位

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 同步复位
module top_module (
    input clk,
    input reset,
    input [7:0] d,
    output [7:0] q
);
    always @(negedge clk) begin
        q <= (reset == 1'b0) ? d : 8'h34;
     end

endmodule

//异步复位
module top_module (
    input clk,
    input areset,
    input [7:0] d,
    output [7:0] q
);

    always @(posedge clk or posedge areset) begin
        q <= (areset == 1'b1) ? 1'b0 : d;
     end
endmodule

Debug 经验

  1. 对于时序逻辑和组合逻辑的综合电路,建议分开把组合逻辑部分和时序逻辑部分分开写,遵循组合逻辑用阻塞赋值,时序逻辑用非阻塞赋值的原则。若组合逻辑部分也用非阻塞,会出现因并行导致时序逻辑用的是上一时刻的值(组合逻辑还没更新完,时序逻辑就并行执行了)。而阻塞赋值有always块中对reg赋值,以及assign中直接对wire赋值两种;具体使用根据变量类型以及要描述的组合逻辑的复杂程度决定。
  2. 状态机分 Moore 和 Mealy 两类:Moore 状态机的输出只与当前状态有关而与当前输入无关,即输入与输出隔离;Mealy 状态机的输出与当前状态和当前输入都有关,输入变化输出立即变化,响应比 Moore 状态机快一个时钟周期。一般使用三段式状态机,三个步骤分别为:传递寄存器状态(时序逻辑、非阻塞);根据当前状态确定下一个状态(组合逻辑、阻塞);由状态确定输出(组合逻辑、阻塞)。另外,可以分别用状态方程和输出方程代替状态机的后两个步骤中冗长的 case 写法。
  3. always块有两种用法:always @ (*)用于对组合逻辑建模,输出对所有输入敏感,这种always块用阻塞赋值;always @ (posedge/negedge xxx)用于对时序逻辑建模,只在时钟上升/下降沿更新输出,这种always块用非阻塞赋值。assign同样是用于组合逻辑的,可以看作是always @ (*)的简便用法,always @ (*)块相较于assign可以表达更复杂的组合逻辑。always @ (*)case可构成 mux。
  4. 不指定位宽时,默认为 32 位的位宽,建议显式指定位宽。另外为了方便表示,建议都使用十进制 d 而不用二进制 b 或其它进制表示数值。
  5. 模块内部可以声明 wire 作为中间变量来表示中间结果。模块连线时,对于悬空无连接的端口,建议显式.xxx()来表示悬空,而不要不写这个端口。
  6. xdc 文件中约束的端口需要与顶层模块中的一致,管脚约束是加在顶层的设计文件上的
  7. axi_gpio 的中断会在任一 GPIO 接口数值变化时产生一段时间的高电平,无论数值由 0 变 1 还是由 1 变 0
  8. 移位需要初始化,如果不初始化默认全 0,移多少位都还是全 0,没有现象的。初始化在可综合的实际电路中一般是靠复位信号实现的,initial 是不可综合的;rst 信号在 7010 上是由 ps_block 给出的。另外,ps_block 还会给出一个 clk,也可以用。
  9. 在实例化端口连接时,inout 不能接 output
  10. PS 端的 IO 分配是固定的,自然也不需要在 Vivado 中分配管脚,但需要建立 Vivado 工程中配置 PS 管脚,也需要将 ARM 添加到工程中才能使用。
  11. IOBUF 的 IO 引脚不能被 FPGA 内部逻辑当作信号源。不能把 IOBUF 的 IO 端口(即 .IO)连接到一个非顶层的 inout 信号,哪怕那个信号最终又连接到顶层的 inout。只有顶层 inout 才能合法地驱动 IOBUF 的 IO 端。
  12. 打开了 deign 还 implementation 失败就重新 synthesis 一下,即使提示 up-to-date。(重新 setup debug 之后会这样)
  13. FatFs 库函数编译时报错未定义:read_ddr/Debug 目录下的 objects.mk 文件,在 LIBS 中加上-lxilffs。或者换一个有 xiff 库的 bsp 然后再换回来(这样可以刷新一下)
  14. 在 AXI Interconnect 中,“哪个 master 通道连接到哪个 slave 通道”, 并不是物理上固定连接,而是通过 Vivado Address Editor 的地址映射 + 连接矩阵 定义的。
  15. DONE pin is not high on target FPGA:这是从 SDK 启动时有时会出现的提示,需要在 SDK 中为所启动程序的 run configuration 中配置 program fpga,让板子在启动时先把 bitstream 加载进去再启动程序。(这是 Vivado 2017 版本的解决方案)。DONE 引脚本身表示 PL 加载成功,FPGA 被正确配置完成后,DONE 引脚电平会被拉高。
  16. 多线程编程的消息传递中,两个线程并行对共享的 DDR 地址读写时,不能保证读取在写入之后才发生。为了确保读取到写入的 flag,可以死等。要注意多线程不要访问互斥资源。
  17. verilog 默认以 unsigned 处理数据,对于有符号数需要用 signed 声明,否则会影响对符号位的解释。另外,即使信号声明为 signed,一旦参与运算的表达式中有 unsigned 或默认推断为无符号的部分,Verilog 会 自动把整个表达式转为 unsigned 计算。写 $signed() 是为了“锁死表达式为有符号运算”,防止 Verilog 自行提升为无符号算术。
  18. 乘法运算要以两倍扩宽处理,结果要做截断判断处理,否则可能发生溢出。
  19. DUT(Device Under Test)和 testbench 中的信号类型对应关系:DUT 中的 input 需要 tb 来驱动,因此用可以在过程块中赋值的 reg;DUT 中的 output 不由 tb 赋值,只连接 wire 来接受即可;常量也可以用 wire,在声明的时候就设定好值。
  20. 仿真中可以使用 real 的数据类型和 task 结构,但不能直接综合到 FPGA 中,因此不能用在设计中。
  21. 搞不出来的时候就单独测试模块,输入自己给定值。
  22. AXI 规范要求 AR/R 事务一旦开始,不允许中途中止。一旦在 R 通道阶段突然把状态打回 IDLE,不再给 RREADY(在 IDLE 状态默认 RREADY=0),但从端仍可能继续送数据。这会导致 RVALID 有数据了,但状态机认为自己“没请求”/“不该收了”,现象是数据在 S_RD_WAIT 就出现,这是因为 slave 正常返回了 RDATA,但主机状态机不在正常处理路径。
  23. 状态机的 state_next 是时序逻辑赋值,否则会延后两个周期,甚至卡死在某一个状态。流水线是一种隐式的状态机,由各级数据的 valid 信号来标示状态。
  24. 负数的十六进制表示,是正数十六进制的表示取补码
  25. 加减法需要对齐 Q,乘除法不用;比较的时候,位宽和精度 Q 要一致
  26. 理论上,所有的赋值语句左右位宽都要一致,只不过有的运算会自动扩展扩宽。自动扩展位宽的运算包括加减法、乘法、整数除法、取模、各类比较运算。
  27. 符号位扩展要复制最高位(符号位),即正数补 0,负数补 1。
  28. 右移的时候,是有可能丢失精度的。
  29. 数据不同步时,要么给 backpressure,要么就 buffer 缓存起来,不然会丢数据;方案选择上,能 backpressure 就用,不行就缓存。
  30. 入口处通常会配备 skid,用于缓存上游数据已经送来,但下游突然拉低 tready 导致数据送不出去的那一拍数据。中间计算步骤的工作数据和相应的 valid 信号记为 stage_data 和 stage_valid。

参考资料

HDL Bits

HDL Bits 参考答案

Verilog 高级教程

VSCode 中的 Verilog 开发插件配置

Vivado 波形使用技巧

tcl store 手动更新

UG949:UltraFast 设计方法论

Xilinx petalinux BSP

AXI 简介

AXI 的特性

AXI 详细介绍

本文由作者按照 CC BY 4.0 进行授权
/body>