01signal.com

Remote Update 与 Vivado上的 Partial Reconfiguration

这是关于 Partial Reconfiguration或 Dynamic Function eXchange (DFX) 与 Xilinx的 Vivado的四篇系列文章中的最后一篇。它主要面向那些想在 Remote Update 场景中使用 Partial Reconfiguration 的人。这篇文章是假设你已经阅读了前三篇。

介绍

讨论了 Partial Reconfiguration 的一般情况,然后讨论了 Vivado为此目的的常用程序,这篇文章为在 FPGA的逻辑的 Remote Update 上使用这种技术奠定了基础。

这种使用场景的主要问题是 partial 比特流需要与可能几年前生成的 initial 比特流兼容。因此,原始 Parent 实现必须在 reconfigurable 逻辑的实现(implementation)期间可用,或者必须重新生成以产生完全相同的结果——即完全相同的布局和布线(place and route)。

可以保持整 Vivado 项目完整,从而避免重新运行 Parent 实现。如果重复此实现,我也可能获得完全相同的结果。然而,基于这两种方法中的任何一种,都很难确保将来发布 partial 比特流的可能性。

这个问题有一个可靠的解决方案,但是要了解它是如何工作的,首先要熟悉 DCPs 和 OOCs。以下是对这两个主题的简要介绍。

设计 Checkpoint (Design Checkpoint)(DCP)

回想一下, Vivado 通过几 Design runs执行 FPGA 设计的实现,这些 Design runs通常命名为 synth_1、 impl_1 以及归类为 Out-of-Context module runs (OOCs) 的附加 runs 。

每个这样的 run 包括执行一个 Tcl 脚本,它生成一个临时的“in-memory project”,加载设计文件,设置属性和属性,并调用执行综合(synthesis)、 placing、布线(routing)、生成比特流(bitstreams)和其他任务的 Tcl 函数。

这 in-memory project 不会在磁盘上创建文件,与图形用户界面(GUI)中可见的 Vivado 项目无关。它是内存中的对象(object),允许使用 Tcl 命令执行许多顺序操作。

随着实现的发展,设计 CheckPoints (Design CheckPoints)(DCP) 文件被写入磁盘(借助 Tcl中的 write_checkpoint 命令)。 DCP 文件的内容是 in-memory project的 snapshot 。换句话说,它是一 database ,它反映了已加载的设计文件以及自加载以来对项目所做的操作。

例如,综合 run (synthesis run)(通常命名为 synth_1)可能会加载所有 HDL 文件、约束(constraint)文件和 IP 文件,然后使用一个名为 synth_design 的 Tcl 命令来执行 HDL 文件的综合。结果是所有这些 sources (包括 IPs)中的一个大网表(netlist)。但是请注意,网表作为 in-memory project的一部分存储在内存中。因此,综合 run的 Tcl 脚本对 write_checkpoint 进行函数调用以创建 DCP 文件,该文件是综合的产品。 synth_1 run到此结束。

事实上,由 synth_1 生成的 DCP 被称为 netlist DCP ,尽管它通常也包含来自 XDC 文件的约束。

随后出现的实现 run (implementation run)(通常称为 impl_1)创建一个新的 in-memory project 并读取此网表 DCP (netlist DCP)(以及其他)作为以下操作的起点。这 run 写入了几 DCP 文件,每个文件都是经过处理阶段的项目的 snapshot (例如 optimize 设计、 place、 physically optimize、 route 等)。

所有 runs,包括 synth_1,都可以将 DCPs 加载到它们的 in-memory project中。这是他们通常做的事情。

Out Of Context (OOC) Module Runs

在 Vivado中, IPs 通常是借助图形用户界面工具来配置的。完成后,脚本(script)会生成源 files (source files)(主要是 HDL 和约束文件)并在这些文件上执行综合。这会产生一个网表 DCP(netlist DCP),然后将其加载到主项目的综合 run 和实现 runs(implementation runs)中。通过消除重新生成 IPs 的 sources 并一遍又一遍地执行其综合的需要,这减少了整个项目的实现完成所需的时间。

Vivado run 获取原始产品( IP的配置信息、一些 HDL 文件或其他任何东西)并将其转换为网表 DCP,在 Vivado的术语中称为 Out-Of-Context run 。该术语仅与 Vivado相关,据我所知没有其他用途。这实际上是一个相当糟糕的名称选择。

这个网表 DCP 由综合 run 和实现 runs加载,通常借助 Tcl 命令 read_ip,通常归结为加载由 OOC run生成的 DCP 。

Vivado 还允许在主工程中选择一个 HDL 模块,并要求将其综合执行为 OOC (通过在 Project Manager的源树(source tree)中右键单击源(source)并选择“Set as Out-of-Context for Synthesis…”)。

OOCs 的缺点是,当综合工具(synthesizer)将整个设计作为单个项目时,综合工具可以跨模块的边界执行某些优化。所以个别综合凭借 OOCs 可能会降低性能以及浪费资源。

OOCs 和 DCPs 与 Partial Reconfiguration

当一个源(source)文件被选为 reconfigurable 模块的 top-level 时, Vivado 为这个模块及其 submodules的综合创建一 OOC run 。这 run 生成一个网表 DCP 用于相关的实现。 Parent 实现和 Child 实现都是如此: reconfigurable 模块始终以单独的 DCP表示。

然而,这个网表 DCP 在 Parent 实现和 Child 实现中的使用方式不同: 分配给 Parent 实现的网表 DCP 由 synth_1 和 impl_1加载,就像在常规实现中加载 IP 一样。涉及 Partial Reconfiguration 的事实主要通过 floorplanning强加的 placement 约束影响这个过程。

另一方面, Child 实现没有专用的综合相位。相反,它将 reconfigurable 模块的网表 DCP 与来自 Parent 实现的最终 DCP (即布局和布线之后的 DCP )混合在一起。更准确地说, Child 实现取了 Parent 实现的最终 DCP,移除了 reconfigurable 逻辑的部分,并插入了自己的 reconfigurable 模块的网表 DCP 。有点像去除蔬菜的中间部分来制作酿西葫芦或类似的东西。

现在是时候将其分解为 Tcl 命令了。

Parent-Child 实现的螺母和螺栓

在 Tcl 文件中查找实现的工作原理,该文件具有项目的名称(加上 .tcl 后缀)。该文件与创建实现文件的目录位于同一目录中。

特别是 child 实现的实现脚本(implementation script)很有趣。相关用户指南 UG909中的第 3 章(“Vivado Software Flow”)显示并解释了脚本,即使没有直接这么说,这并非巧合。

就像刚才提到的, Parent的实现并没有什么特别之处,只是它的 reconfigurable 模块的网表依赖了一 DPC ,并且应用了 floorplanning 约束。它或多或少像任何 hierarchical 设计。

但随后 Parent 实现写入两个比特流(bitstreams)而不是一个,凭借如下 Tcl 命令:

write_bitstream -force -no_partial_bitfile theproject.bit
write_bitstream -force -cell pr_block_ins pr_block_ins_lpf_partial.bit

然后它创建 DCP 稍后由 Child 实现使用:

update_design -cell pr_block_ins -black_box
lock_design -level routing
write_checkpoint -force theproject_postroute_physopt_bb.dcp

回想一下,当这部分运行时,有一 in-memory project,它从加载网表 DCPs(netlist DCPs)开始,并经过布局和布线和所有其他优化。这三 Tcl 行是在写完比特流之后执行的,所以 in-memory project 是真正的最后阶段。

现在是打洞做西葫芦馅的好时机: update_design 命令将 reconfigurable 模块变成 black box。换句话说,它的所有逻辑都被移除了,为其他逻辑提供了进入的空间。

那么设计的布局和布线就通过 lock_design 指令锁定。之后将项目的 snapshot 写入 theproject_postroute_physopt_bb.dcp。 “bb”当然代表“Black Box”。

Child 实现的脚本中的相关部分如下:

create_project -in_memory -part xc7k325tffg900-2
set_property design_mode GateLvl [current_fileset]
add_files -quiet .../impl_1/theproject_postroute_physopt_bb.dcp
add_files -quiet .../two_synth_1/pr_block.dcp
set_property SCOPED_TO_CELLS pr_block_ins [get_files .../bpf_synth_1/pr_block.dcp]
link_design -top theproject -part xc7k325tffg900-2 -reconfig_partitions pr_block_ins
opt_design
write_checkpoint -force theproject_opt.dcp
[ ... ]

从这一点开始,它继续到布局和布线等。

注意上面的实现脚本只消耗两个源,而且都是 DCPs:

当实现继续运行时,第一 DCP 被锁定的事实确保了静态逻辑没有任何移动。尽管如此, reconfigurable 模块的布局和布线还是照常完成,基于网表 DCP。

附带说明一下,如果存在属于 reconfigurable 模块的 XCI IPs ,则在上面的两 add_files 命令之间为每 IP添加如下一行:

read_ip -quiet .../theproject.srcs/sources_1/ip/blkmem/blkmem.xci

不过,这对 Partial Reconfiguration来说并不特别——在任何实现中都是这样。

就在写这两个比特流之前, Child 实现验证它得到的 routed 设计与 Parent 实现的静态逻辑兼容,特别是关于布局和布线。

用于此的 Tcl 命令类似于:

pr_verify -full_check -initial /path/to/impl_1/theproject_postroute_physopt.dcp -additional /path/to/child_1_impl_1/theproject_routed.dcp -file child_1_impl_1_pr_verify.log

请注意,这将比较两 DCP 文件,而不考虑 in-memory project。 Parent 实现的 routed DCP 与 Child 实现的最终 DCP 进行比较。此比较的输出转到名为 *_pr_verify.log的文件。

这种比较保证了 partial 比特流是兼容的,即当 parent的比特流已经加载到 FPGA中时,它可以被加载。它通过静态 partition(static partition)的逻辑单元(logic elements)和布线。

如果存在不兼容,pr_verify 将返回失败状态。如果发生这种情况,将阻止在 Vivado的脚本中创建比特流。在编写自定义实现脚本时牢记这一步骤很重要。

这个验证没有理由失败,但如果失败了,比特流 generation (bitstream generation)就会失败,并出现很多错误,比如“ERROR: [Constraints 18-891] HDPRVerify-08: design check point .../impl_1/theproject_postroute_physopt.dcp places instance ... at site SLICE_X118Y125, yet design check point .../impl_2/theproject_routed.dcp does not. Both check point must have the same static placement result”。

这些错误消息可能会达到 100的限制,然后被静音。

Remote Update 场景解决方案

从上面回想一下,挑战是当 reconfigurable 逻辑的实现作为 Child 实现执行时,原始 Parent 实现的结果必须可用。

brute-force 解决方案是制作整 Vivado 项目目录的副本,以及它可能依赖的任何文件。当需要生成新的 partial 比特流时,恢复所有文件,强制 Parent 实现为最新,并仅为 Child 实现创建比特流。这种方法在技术上是可以的,但从长远来看可能会很烦人。如果选择这种方式,请务必手动运行 pr_verify 对原始 Parent 实现的 DCP 文件,因为这种方法不会检测到 Parent 实现的无意更改。

还有另外两种选择,它们基于关于 Parent 实现和 Child 实现如何交互的知识,即通过两 DCP 文件,如上所述。这两种选择的明显优势是您知道自己在做什么。

这两种选择背后的原理是确保 partial 比特流依赖于与 initial 比特流一起生成的两 DCP 文件,我将其称为Golden DCPs。

第一种选择是将 partial 比特流的实现作为 Tcl 脚本与 non-project flow进行。本质上,这意味着运行 Vivado 为 Child 实现创建的脚本,但是将其修改为使用 Golden DCPs。更准确地说,修改 link_design 命令和 pr_verify 命令的参数,使它们依赖于 Golden DCPs。

这种替代方案的主要缺点是运行带有 non-project flow 的 Tcl 脚本不能很好地与 Vivado的图形用户界面集成,因此处理消息、打开 implemented 设计进行审查等变得相当困难。

Golden DCPs hack

理想情况下,可以自动修改由 Vivado 为 Child Implementation run生成的脚本,因此它与 Golden DCPs相关。不幸的是,似乎没有可靠的方法来做到这一点。

但是 Vivado 允许定义 Tcl 脚本以在实现中的某些阶段之前和之后执行。这些脚本是从实现 run的脚本执行的,所以它们不能用来改变 run的脚本本身。

然而,这为一种有点丑陋的方法打开了大门,这是第二种选择: 这个想法是用 Golden DCPs覆盖 Parent 实现的 DCPs 。通过这样做, Child 实现可以正常运行,但依赖于 Golden DCPs,而不管 Parent 实现碰巧生成了什么。

这种方法的好处是与 Vivado 的常规工作习惯保持不变: 对 reconfigurable 逻辑进行了更改, Vivado 为 reconfigurable 模块的综合重新运行 OOC ,然后为生成 partial 比特流的 Child 实现。由于静态逻辑没有做任何改动,所以 Vivado 没有理由启动其相关的 runs。

实现这种 Golden DPC 复制方法的 Tcl 脚本是

if { [catch {
    set parentimpldir "[ file normalize "../impl_1"]"
    set goldendir "[ file normalize "/path/to/golden"]"

    file copy -force "[file normalize "$goldendir/theproject_postroute_physopt_bb.dcp"]" "$parentimpldir/"
    file copy -force "[file normalize "$goldendir/theproject_postroute_physopt.dcp"]" "$parentimpldir/"
} errmsg ] } {
    send_msg_id golden-reconfig-1 error "Failed to copy golden parent reconfiguration file(s): $errmsg"
    return -code error
}

此脚本假定 Parent 实现保存在与 Child 实现目录相邻的“impl_1”目录中(很可能是),并且两 Golden DCPs 存储在此脚本第 3 行中定义的目录中。

在设计 Runs (Design Runs)选项卡上右键单击 child run (例如 child_0_impl_1),选择“Change Run Settings…”并在打开的 dialog 中,将设计 Initialization (Design Initialization)的 tcl.pre (init_design)设置为脚本。或者,在 Tcl中,如果脚本保存为 golden_pr.tcl:

add_files -fileset utils_1 -norecurse /path/to/golden_pr.tcl
set_property STEPS.INIT_DESIGN.TCL.PRE [ get_files /path/to/golden_pr.tcl -of [get_fileset utils_1] ] [get_runs child_0_impl_1]

使用此脚本时要记住的重要一点是忽略 Vivado 提供的有关 Parent 实现的所有信息,以及代表其生成的任何文件。 Vivado 可能会打开它的 Implemented Design 图形用户界面并显示它的报告,但这一切很可能是完全无关的。所以这是一个混乱的开端。

第二个可能的烦恼是 Vivado 可能会不时运行 Parent 实现以响应项目设置的更改,甚至是约束文件中的更改。这可以通过右键单击设计 Runs 选项卡中的相关行并选择“Force Up-to-Date”来解决。该菜单仅在 run 处于 Out-of-Date 状态时出现,即完成但 Vivado 认为需要刷新。相关的 Tcl 命令例如

set_property needs_refresh false [get_runs synth_1]

与 Golden DCPs一起保存哪些文件

显然, Golden DCPs 必须保存在安全的地方,以保持将来生成兼容比特流文件的能力。除了这些,将整 Vivado 项目压缩成 .tar.gz / .zip 文件以快速恢复实际上是一个好主意。或者,除此之外,可以使用 File > Project > Archive… 或类似的东西生成 project archive

archive_project /path/to/theproject.xpr.zip -force -include_local_ip_cache -include_config_settings

但是请注意,项目文件以及 Vivado 项目中的其他文件都可能包含 absolute 路径,因此将项目部署到另一个目录或另一台计算机上可能无法按预期工作。存档也是如此。

使用哪 Vivado 版本已写入所有可能的报告文件和 .xpr 项目文件中,但记下这一点不会有什么坏处。可能保留该 Vivado版本的可执行副本,即使没有明显的理由说明软件升级很重要。将来自一 Vivado 版本的 Golden DCPs 与来自另一个版本的 reconfigurable 逻辑的网表 DCP 混合可能会正常工作。但这不是 Vivado 计划要做的事情。

总而言之,最小的文件集是:

请注意, clearing 比特流在 Ultrascale FPGAs 上的使用与 FPGA中已经存在的逻辑有关。这就是为什么必须保存与 initial 比特流相关的 clearing 比特流。另请注意,如果 initial 比特流是某些 Child 实现的结果,则必须保存该实现的 clearing 比特流。

至于保存 Parent 实现的 sources 很重要,因为它决定了静态逻辑和 reconfigurable 逻辑之间的连接。例如,如果编辑 sources ,将端口添加到 reconfigurable 逻辑,并且此端口出现例化(instantiation),则 partition 引脚的集合会更改。因此, reconfigurable 逻辑的网表将具有原始静态设计(static design)中未出现的外部引脚。

当发生这种不匹配时, child 实现会失败并显示一条错误消息,例如“ERROR: [Netlist 29-77] Could not replace (cell 'pr_block_bb', library 'work_pr_block_ins_pr_block_ins_4', file 'NOFILE') with (cell 'pr_block', library 'work', file 'pr_block.edf') because of a port interface mismatch; in strict mode, no extra ports are allowed. 8 ports are missing on the original cell. 5 of the missing ports are: 'thingy[7]' 'thingy[6]' 'thingy[5]' 'thingy[1]' 'thingy[0]'”。

在这种情况下实际上失败的是 link_design 命令(见上文),它将静态逻辑和组合逻辑(combinatorial logic)粘合在一起。

避免这个问题的简单明了的方法是不改变设计的静态(static)部分,也不改变 reconfigurable 逻辑的端口 list (port list)。

但是,在项目的静态部分进行更改实际上是可以的: 只是 reconfigurable 模块的例化必须保持不变。只要实现顺利通过,包括最后的验证,都没有问题。

概括

尽管对于 Partial Reconfiguration的 Remote Update 使用没有完全平滑的解决方案,但仍有一些可用的策略来实现这一目标。

重要的一点是,原则上,只需两 Golden DCPs 即可为现有项目构建和验证 partial 比特流。

无论此处介绍的策略看起来多么不合常规,请记住, pr_verify 执行的验证是对 partial 比特流和静态逻辑之间已经存在的兼容性的全面检查。只要使用正确的 Golden DCP 进行此验证,并且测试通过,就无需担心其他任何问题。另外,当然, Child 实现可以达到时序约束(timing constraints),但对于设计的任何实现都是如此。

此页面由英文自动翻译。 如果有不清楚的地方,请参考原始页面
Copyright © 2021-2024. All rights reserved. (b4b9813f)