01signal.com

用 Vivado理解 Partial Reconfiguration

这是关于 Partial Reconfiguration或 Dynamic Function eXchange (DFX) 与 Xilinx的 Vivado的四篇系列文章中的第一篇。这篇文章的目的是解释这个主题的主要概念。这为下一篇文章奠定了基础,其中概述了使用 Partial Reconfiguration设置 FPGA 项目的实际步骤。

介绍

Partial reconfiguration 是一种技术,允许在 FPGA的某些部分替换逻辑,而其他部分正常工作。这包括用比特流(bitstream)为 FPGA 供电,就像最初的比特流在通电时对其功能进行编程一样。但是, Partial Reconfiguration 的比特流不会导致 FPGA 停止。相反,它适用于特定的逻辑单元(logic elements),并更新控制其行为的内存单元。它是特定逻辑 blocks(logic blocks)的 hot replacement 。

Xilinx的 FPGAs 从 Virtex-4 开始支持此功能(以及 Intel的 FPGAs 从 Series-V开始)。

这篇文章将介绍 Partial Reconfiguration 背后的概念,而不涉及实际的技术细节,为下一篇文章做准备,下一篇文章就是这样做的。因为所有内容都与本主题中的所有内容相关,所以在将其分解为单个操作之前了解整个框架非常重要。

再解释一下?

让我们从没有 Partial Reconfiguration的情况下如何更改 FPGA 的功能开始。假设在设计 hierarchy(design hierarchy)的某处有例化(instantiation)或模块。例如,在 Verilog中:

moduleA reconfig_ins
     (
      .clk(clk),
      .this(this_w),
      .that(that_w),
 [ ... ]
      );

或在 VHDL中:

reconfig_ins : moduleA
    port map(
      clk        => clk,
      this       => this_w,
      that       => that_w,
 [ ... ]
      );

显然,在项目中的某个地方有一个称为 moduleA.v 或 moduleA.vhd 的模块,或者一个称为 moduleA的 IP ,它填充了例化(连同它的 submodules)。所以我们执行项目的实现(implementation),得到一个比特流(bitstream)文件,并用它加载 FPGA 。到目前为止,通常的程序。

但是现在,假设我们在上面的代码中写的是 moduleB 而不是 moduleA ,并且对逻辑执行一次实现,得到一个比特流文件。为此,设计中必须有一 moduleB.v 或 moduleB.vhd,或称为 moduleB 的 IP 。

我们现在有两个比特流文件,它们的不同之处在于 instance 内部的逻辑,称为 reconfig_ins 。要在这两个比特流之间切换,我们需要用所需的比特流加载整 FPGA 。这涉及中断 FPGA的操作。

Partial Reconfiguration 是一种允许从一个版本更改到另一个版本而不会出现这种中断的技术: FPGA 保持正常工作,而 reconfig_ins 中的逻辑从 moduleA 变为 moduleB,反之亦然。几乎不用说,这仅靠两个设计中的一个实现(implementation)是不可能的。

moduleA 和 moduleB 统称为 Reconfigurable Modules (RM),就是说它们的逻辑可以借助 Partial Reconfiguration注入到 FPGA 中。

动机

使用 Partial Reconfiguration有几个原因,例如:

偏比特流

如果您对 FPGA 设计有一定的经验,那么您很可能已经习惯了一个简单的例程: 对设计的源代码(source code)(和 IPs)进行一些编辑,启动实现工具(implementation tools),并检查它是否正常。然后通过 JTAG将比特流加载到 FPGA 中。或者,使用比特流的 image 加载 flash device 。

因为这是我们都习惯的,所以很容易将比特流误认为是大量数据,这些数据充满了整 FPGA ,其中包含有关每个逻辑单元(logic element)应该如何表现的神秘信息。实际上,比特流由一系列命令组成,这些命令在 FPGA 加载时按顺序执行。事实上,普通的比特流会为整 FPGA 加载信息,但它是通过控制过程进度的几个命令完成的。这些命令的一个更重要的方面是它们决定了哪些逻辑单元加载了每条数据。

由于比特流本身说明了哪些逻辑单元受到影响,因此可以制造一个比特流,它只改变一些逻辑单元,而使其他逻辑单元保持不变。这是 Partial Reconfiguration的基石。

话虽如此, partial 比特流必须与 FPGA中已经加载的逻辑兼容。这不仅仅是覆盖错误的逻辑单元的问题: 初始比特流与部分比特流紧密耦合,特别是因为初始比特流使用在由部分比特流更改的区域内的逻辑和布线资源(routing resources)。当部分比特流相对于最初的比特流正确设置时,这种微妙的舞蹈就会被忽视。如果没有, FPGA 很可能会发疯,包括本应保持不变的功能。

加载部分比特流

Partial Configuration 比特流到 FPGA 的交付可以通过任何允许加载比特流的接口完成,只要该过程可以在 FPGA 运行时完成。这包括 JTAG 接口,因此 Partial Configuration 的 .bit 文件可以像往常一样与 Hardware Manager 一起加载。但更有趣的是,它可以在 FPGA自己的逻辑内部使用专用的 Internal Configuration Access Port (ICAP)完成。这个端口只能用于 Partial Reconfiguration,因为 FPGA的逻辑阵列(logic fabric)中加载比特流的部分在整个过程中必须保持完整。

ICAP 只是 FPGA子系统的一个接口,用于加载比特流,并没有说明比特流的来源。因此,对比特流数据如何到达 FPGA或存储位置和存储方式没有限制。它只是必须以某种方式可用于为 ICAP提供数据的 FPGA 逻辑部分。

例如, Xillybus 提供了一种简单的方法,可以通过 PCIe 或 USB 3.x 接口从计算机将比特流文件发送到 ICAP ,如果开发板有这种接口的话。

静态逻辑(Static logic)

要正确完成 Partial Reconfiguration ,必须尊重相反的部分: static logic。这是 FPGA 设计中必须保持不变的部件的总称,因此从最初的比特流加载时就存在。

这个逻辑在两个方面是静态的: 功能方面,这意味着逻辑由 FPGA 设计中的部件(HDL 和 IP)组成,这些部件在 FPGA的初始启动时不会中断。第二个同样重要的方面是,此逻辑的放置仅限于逻辑阵列中分配为静态的站点。在这些网站上,不允许以后进行任何操作。

在现实生活中的设计中,仅静态逻辑保持不变是不够的,但同样重要的是,当 FPGA 的其他部分发生变化时,它仍能正常运行。由于几乎可以肯定在静态逻辑和变化的逻辑之间有网络(nets)连接,因此确保一切顺利运行是 FPGA 设计师的责任。本系列的第三篇文章讨论了这一点。

静态逻辑和 reconfigurable 逻辑的分离

为了使 Partial Reconfiguration 成为可能,静态逻辑和 reconfigurable 逻辑之间必须严格分开。特别是, FPGA 上的物理逻辑单元必须分开,这样当 FPGA 与比特流一起加载时,任何包含静态逻辑的站点都不会受到影响。

要了解这需要什么,让我们首先看看我们都习惯了什么。

回想一下,常规 FPGA 实现进程从 HDL 设计的综合(synthesis)开始。请注意, HDL 中的例化和模块并不意味着它们之间有任何分离。反之亦然: 综合工具(synthesizer)将例化视为逻辑应该如何工作的描述。因此,综合工具可以自由地将整个设计视为一个大而扁平的逻辑。跨越模块边界的优化不仅是允许的,而且是需要的并且经常发生。例如,如果模块 X (module X)中的寄存器(register)恰好等同于模块 Y(module Y)中的某些完全不相关的寄存器,则删除寄存器中的一个,其余的一个用于两个模块(除非明确告知综合工具避免这样做)。

一旦 HDL 的综合完成, synthesized 网表将与设计的 IPs (如果有)混合。

接下来,将这大块逻辑单元放置在 FPGA的逻辑阵列各处,并以实现时序约束(timing constraints)和其他目标的方式布线。属于设计不同部分的逻辑可以打包到同一 slice中,也可以打包在 FPGA的相反部分中。即使是设计中最小的变化也可能导致显着不同的放置。这是混乱但无害的,因为每个实现都是独立的,谁在乎逻辑是如何分散在 FPGA的逻辑阵列上的。

回到 Partial Reconfiguration: 如前所述,要使此功能成为可能,静态逻辑和 reconfigurable 逻辑之间必须有明显的区别。为了确保这一点,使用了一种称为 Hierarchical 设计的技术。这个想法是将整个设计视为组件的集合,就像电路板上的物理组件一样。一方面是每个组件(即 instantiated 模块)都在逻辑阵列上分配了一个特定区域。由于每个组件都需要分离,因此单独执行综合显然是有意义的——就像您单独生产组件一样。

因此,让我们将这个概念与 Partial Reconfiguration联系起来,归结为与设计工作的两个主要区别:

Pblocks

Vivado对 floorplanning unit 的术语是 Pblock,它只是 Vivado内部信息的占位符。有 Tcl 函数可以创建 Pblock,将逻辑 cells (logic cells)添加到其中,然后添加 FPGA logic sites的组 (sets)。 Vivado 将此解释为 placement 约束,它要求添加到 Pblock 的逻辑 cells 只能放置在分配给它的 sites 中。所以说到底, Pblocks 就像 XDC 文件中的其他约束(constraints)一样。

Pblocks 通常使用 Vivado的图形用户界面(GUI)定义,方法是打开 synthesized 设计或 implemented 设计,并在 FPGA的图形表示上绘制矩形区域。这将创建一 Pblock ,其中包括绘制的矩形中的所有逻辑单元。更准确地说,并非所有类型的逻辑单元都包括在内,而仅包括允许用于 floorplanning 的那些(对于该 FPGA 系列)。所以 Vivado 将矩形转换为逻辑单元的范围。

通过编辑 XDC 文件手动设置这些范围同样可以。此外,它允许创建一个由多个矩形组成的区域,因此形状可以比一个矩形更复杂。但是 Xilinx的文档 (UG909) 建议尽量保持形状简单以避免布线(routing)的困难。

这是 Kintex-7的 XDC 文件的示例:

create_pblock pblock_pr_block_ins
add_cells_to_pblock [get_pblocks pblock_pr_block_ins] [get_cells -quiet [list pr_block_ins]]
resize_pblock [get_pblocks pblock_pr_block_ins] -add {SLICE_X118Y0:SLICE_X153Y99 SLICE_X118Y250:SLICE_X145Y349 SLICE_X0Y0:SLICE_X117Y349}
resize_pblock [get_pblocks pblock_pr_block_ins] -add {DSP48_X5Y100:DSP48_X5Y139 DSP48_X5Y0:DSP48_X5Y39 DSP48_X0Y0:DSP48_X4Y139}
resize_pblock [get_pblocks pblock_pr_block_ins] -add {RAMB18_X4Y0:RAMB18_X6Y39 RAMB18_X4Y100:RAMB18_X5Y139 RAMB18_X0Y0:RAMB18_X3Y139}
resize_pblock [get_pblocks pblock_pr_block_ins] -add {RAMB36_X4Y0:RAMB36_X6Y19 RAMB36_X4Y50:RAMB36_X5Y69 RAMB36_X0Y0:RAMB36_X3Y69}

下图显示了它在 implemented 设计中的外观。 reconfigurable partition (命名为 pblock_pr_block_ins,在本例中几乎不包含逻辑)以紫色绘制。它的形状被创建为三个矩形的 union (上面每 resize_pblock 命令中列出的三个范围)。

在此图中,所有 placed 逻辑均以青色绘制。逻辑绝大多数属于静态 region(static region),其局限在小范围内可见一斑。

Example of device view with Pblock partition

请注意,只有 slices、 RAMs 和 DSP48 受约束限制。这些是 Partial Reconfiguration 可以使用 series-7 FPGAs控制的唯一逻辑类型。其他一切,实际上除了“纯逻辑”之外的任何东西都必须属于静态设计(static design)。

使用 Ultrascale FPGAs 及更高版本,几乎任何逻辑都可以用 Partial Reconfiguration重新加载。

Pblocks有额外的限制,但这里没有必要重复 UG909 中的第 6-8 章。

无论如何,打开 implemented 设计是个好主意,无论如何,放大和缩小 FPGA的视图,并观察逻辑单元在 FPGA中的组织方式。特别注意,有相同类型的逻辑列。有时在列中间有几个逻辑单元打破了均匀性(特别是特殊的逻辑单元,如 ICAP 块、 PCIe 块等)。

值得一提的是, Pblocks 的话题并非专门针对 Partial Reconfiguration。例如,本节中所说的一切也适用于将 Pblocks 用于 hierarchical 设计。

更多关于 floorplanning

这里有几个违反直觉的事实: 尽管 floorplanning 的图形表示由在 FPGA的地图上绘制的形状组成,但它仅适用于由其 placement 约束控制的逻辑类型。所以如果 FPGA 的几乎所有 slices 都分配给 Partial Reconfiguration,那么完全被这些 slices 包围的其他逻辑单元的岛屿很可能属于静态逻辑。例如,如果 ICAP 块本身位于分配给 Partial Reconfiguration的矩形的中间,则没有任何问题。

这并不奇怪,因为 Partial Reconfiguration 比特流是针对某些逻辑单元而其他的则完好无损。但是布线呢?如果 ICAP 的块卡在 reconfigurable 逻辑的中间,那线是怎么拉到静态逻辑的 slices 上的?

这将我们引向第二个违反直觉的事实: 静态设计的布线使用 reconfigurable region内部的资源。这个布线(routing)在整 Partial Reconfiguration 过程中保持稳定,否则它不会是静态逻辑。所以在 reconfigurable region内部, reconfigurable 逻辑的布线发生了变化,但静态逻辑的布线保持不变。如果说这整个话题有什么神奇之处,那就是这个小事实。这也是为什么使用与静态逻辑不兼容的 partial 比特流可能会完全破坏 FPGA 的原因。

当然,反过来是不正确的: 除了上面 XDC 示例中明确列出的资源外, reconfigurable 逻辑不使用任何资源。至于布线资源,加载 Partial Reconfiguration 比特流时静态 region 中的任何内容都不会改变,因此从这个意义上说, reconfigurable 逻辑永远不会影响静态 region。嗯,这几乎是真的: 如果 reconfigurable region 的形状不是一个普通的矩形, Vivado 可能会让布线超出 reconfigurable region 。这只发生在 Ultrascale FPGAs上,目的是改进布线。

此时应该清楚的是,绘制 floorplanning 的规则并不简单。好消息是 Vivado 产生了相当丰富的关键 Warnings (Critical Warnings)以响应违反 floorplanning规则的行为。因此,通过反复试验找到合适的 floorplan 是一种合理的工作方式。

Parent 实现和 Child 实现

关于 reconfigurable 逻辑的实现,需要注意的是, FPGA 中的每一条路径(path)都必须达到时序约束,并且在加载 reconfigurable 逻辑之前和之后都必须如此。因此,与静态逻辑分开的 reconfigurable 逻辑的实现是不可能的。相反,对于每个 reconfigurable 模块,实现总是在整 FPGA 上执行。时序约束(以及其他约束)在每个实现上强制执行。

为了澄清这一点,让我们从上面返回 moduleA 和 moduleB 的示例。对于此示例, Vivado 执行包含 moduleA 的完整设计的实现,然后对 moduleB执行相同操作。作为副产品,这两个选项中的每一个都有常规的比特流文件。

值得强调的是: 所有实现都生产完整的 initial 比特流和 partial 比特流。所有实现都是如此,无论它们是 Parent 还是 Child。因此,当 FPGA 启动时,可以使用这些 initial 比特流中的任何一个加载它。

为了能够使用 Partial Reconfiguration从 moduleA 迁移到 moduleB ,静态 partition (static partition)中的所有内容都必须完全相同。这包括逻辑本身、 placement 和布线。为了实现这一点, Vivado 在一种情况下(例如,使用 moduleA)执行实现作为 Parent 实现。然后它为所有其他场景执行 Child 实现(例如使用 moduleB)。本系列的最后一篇文章详细介绍了这是如何完成的,但长话短说:

Vivado 开始像 Hierarchical 设计一样为 moduleA 运行 Parent 实现。这意味着静态逻辑和 reconfigurable 逻辑的综合是分开完成的,并且 floorplanning 约束将 placements 强制到 FPGA上的不同站点中。除了这两个差异之外,还执行常规实现。特别是,执行 placement 和布线是为了在此特定场景下获得最佳结果(即使 floorplanning 约束和单独的综合可能会导致次优性能)。

下一步是为 moduleB执行 Child 实现。不需要静态设计的综合,因为这已经代表 Parent 实现完成。所以综合只在 reconfigurable 逻辑上进行。

实现的执行方式与 Parent 实现相同,但有一个关键区别: 所有静态逻辑的布局和布线(place and route)被强制与 Parent 实现的结果相同。鉴于此限制, reconfigurable 逻辑的布局和布线被执行以获得最佳结果。

所以 Parent 和 Child 之间关系的关键是 Child 实现从 Parent 实现结束的地方开始,但用自己的逻辑替换 reconfigurable 逻辑。然后 Child 实现照常继续,但不触及静态逻辑区域内的任何东西。

因为所有的 Child 实现都需要适应静态逻辑的 placements 和 routes,所以时序约束可能比设计的普通实现更难实现。实际上有两个障碍:

在选择应为 Parent 实现选择哪个 reconfigurable 模块时,应牢记这一点。例如,最难实现时序约束的可能是模块。或者模块代表另一个模块与静态逻辑的连接方式。或者可能,反过来: reconfigurable 模块实际上根本不包含逻辑(“grey box”),以实现静态逻辑的中性实现。

至于比特流的使用, Vivado对于一个有 Partial Reconfiguration 的项目的实现的范式是,当所有比特流都是最新的并且相互兼容时,实现就结束了。也就是说,实现的任何一个 initial 比特流都可以用来加载 FPGA 的开始。在此之后,任何一个实现的 partial 比特流都可以加载。

因此,第一个“Generate 比特流”启动了 Parent 实现和所有 Child 实现。在随后的编译(compilations)中, Vivado 照常只执行需要更新的 runs 。

Dynamic Function eXchange Wizard

这个可以从工具菜单启动的 Wizard的目的是定义 Parent 实现和 Child 实现,特别是哪个实现包含哪个 reconfigurable 模块。

通过查看它在添加 Child 实现时发出的 Tcl 命令来解释这 Wizard 是最简单的:

create_reconfig_module -name bpf -partition_def [get_partition_defs pr ]
add_files -norecurse /path/to/pr_block1.v  -of_objects [get_reconfig_modules bpf]
create_pr_configuration -name config_2 -partitions [list pr_block_ins:bpf ]
create_run child_0_impl_1 -parent_run impl_1 -pr_config config_2 -flow {Vivado Implementation 2020}

我将从最后一行到第一行向后遍历这 Tcl 序列:

所以在最后一行,创建了一 Child Implementation run 。新的 run 被命名为“child_0_impl_1”,它的 parent run 被选为“impl_1”。同样重要的是,这款新 run 的 configuration 设置为“config_2”。

“config_2”在第三行定义,表示“bpf”是用于名为“pr_block_ins”的 reconfigurable partition 的 reconfigurable 模块。 “pr_block_ins”上面已经提到过,那么“bpf”是什么?

在第一行中,创建了一 reconfig_module 并将其命名为“bpf”——这只是方便说明逻辑功能的任何名称。第二行表示将某 Verilog 文件添加到此 reconfigurable 模块。

所以总而言之,这四行创建了一个新的 Child 实现,并且说创建一个 reconfigurable 模块需要某 Verilog 文件的综合。此外,在 Tcl environment 中创建了两个对象(objects),“bpf”和“config_2”。

现在回到 Dynamic Function eXchange Wizard: 它是一个图形用户界面(GUI)工具,代表设计 sources(design sources)、 reconfigurable 模块、 configurations 和实现 runs(implementation runs)之间的关系。这只是一种方便的方式来传达信息以生成如上所示的 Tcl 命令。

这个工具可能看起来过于复杂,但那是因为示例很简单。在现实的设计中, reconfig_module 很可能有多个源 files(source files),并且可能分配有 IPs 。因此,图形用户界面让事情变得更简单。

但为什么需要 configuration (“config_2”)?为什么“bpf”和“pr_block_ins”之间没有用 create_run 命令建立连接?再一次,这是一个合理的问题,因为这篇文章仅限于一 reconfigurable partition。如果有多个这样的 partitions,则一 configuration 定义了哪 partition 得到哪个 reconfigurable 模块,因此给每个这样的组合命名是有意义的,例如 config_*。

那么如果有多 partitions,是否需要为每个可能的 reconfigurable 模块组合制作一个实现呢?这个问题与本系列文章无关,因此请随意跳到下一节。

回想一下, Vivado的实现是在整个设计上做的,即静态逻辑和 reconfigurable 逻辑合在一起,保证它符合时序约束的整体。因此,如果有多 partitions,使用 Partial Reconfiguration 的安全方法是使用它们的 partial 比特流加载所有 partitions ,这样所有 partial 比特流都是同一个实现 run(implementation run)的结果。换言之,所有 partial 比特流都是使用相同的 configuration (例如“config_2”)创建的,因此这些 partial 比特流的组合是经过工具验证的实现的结果。尤其是这个实现,号称能实现时序约束。

然而,如果 reconfigurable 模块没有相互交互(即 reconfigurable 模块的所有 top-level 端口都连接到静态逻辑,而不是相互连接),我无法弄清楚单独处理每 partition 会出现什么问题。事实上,如果混合使用来自不同 runs 的 partial 比特流, Vivado 并没有明确批准整 FPGA 的时序。但是既然所有的路径(paths)和静态逻辑都达到了时序约束,而且静态逻辑在所有实现 runs上都是一样的,那还不够好吗?官方文档似乎没有提供有关此问题的信息。

布线和 Partition 引脚

拼图中还缺少一件: 布线连接静态逻辑和 reconfigurable 逻辑。回想一下, Parent 实现以针对相关 configuration中包含的 reconfigurable 逻辑的最佳方式执行设计的布局和布线。但是 child的 reconfigurable 模块需要适配到同一 reconfigurable partition中,并与静态设计连接。布线至少有一部分属于静态逻辑,因此不能改变。

这就是 Partition Pins 的用武之地。从概念上讲,可以将 reconfigurable 逻辑视为一个物理组件,而将 partition 引脚视为连接到电路板的金属引脚。

但实际上, partition 引脚只是 FPGA资源在布线坐标系中的位置。它们是静态逻辑的布线结束的地方, reconfigurable 逻辑的布线继续的地方。它们唯一重要的是 Parent 实现和 Child 实现就它们的位置达成一致。

建立这些锚点不需要 LUTs 或触发器(flip-flops)等物理资源,它们不会贡献额外的布线时延(routing delay)。当然,通往和离开 partition 引脚的布线段创建了时延(delay),但 partition 引脚本身不添加任何时延。

partition 引脚的位置由工具自动选择, Parent 实现执行时, Child 实现被迫适应。也就是说,静态逻辑和 reconfigurable 逻辑之间的布线从 Parent 实现所说的地方开始, Child 实现只能在 reconfigurable partition内部尽力而为。事实证明,某些 partition 引脚被放置在对 Child的 reconfigurable 逻辑不利的位置,这可能会导致实现时序约束的困难。

Partition 引脚通常在靠近 reconfigurable partition周边的某个地方组合在一起。似乎 Vivado 旨在选择对特定设计不太专业的站点。

但是, partition 引脚可以在 reconfigurable partition内部的任何地方找到,如果这是在 Parent 实现期间实现时序约束所必需的。回想一下,静态设计可以使用 reconfigurable partition内部的布线资源。因此,静态布线(static routing)的某些部分进入 reconfigurable partition是没有问题的。

为防止时序约束和 partition 引脚出现问题,如果 reconfigurable 模块的输出端口(output ports)是寄存器并且输入也由寄存器采样是有益的。同样,静态逻辑最好类似地应用寄存器。事实上,只要有可能并且不会使设计变得复杂,遵循这条规则总是一个好主意。

Greybox

DFX Wizard 的另一件事是 greybox: 在 Edit Configuration 窗口中,可以将 greybox 指定为 reconfigurable 模块,而不是常规 reconfigurable 模块之一。 greybox 是由 Vivado生成的伪造模块。它适合真正的 reconfigurable 模块的端口,但不是真正的逻辑,而是每个端口引脚都有一 LUT 。为输入生成的 LUTs 在另一端没有任何连接,输出(outputs)的 LUTs 生成零值。对于 vector 端口,为 vector中的每个比特(bit)创建一 LUT 。

在 Parent 实现中使用 greybox 可能不是一个好主意,因为它使布局和布线进程太容易了。即使 reconfigurable 模块彼此之间有很大的不同,写一个简单的模块可能会更好,在某种程度上挑战工具。

但是为了创建具有最小逻辑的 initial 比特流文件,只包含 greybox 模块的 child 实现可能有用。回想一下,所有实现都生产完整的比特流,它们都可以用作 initial 比特流,因为它们都具有完全相同的静态逻辑。

Clearing 比特流(仅限Ultrascale )

这仅与 Ultrascale FPGAs ( Ultrascale+无关)有关。

如上所述,所有实现都创建了两个比特流: 一个比特流用于整个设计,可用于初始加载 FPGA ,包括相关的 reconfigurable 模块。第二个比特流用于 Partial Reconfiguration 与相同的 reconfigurable 模块。

对于 Ultrascale 设备,还有第三个比特流,即“clearing 比特流”,它是在每个实现上创建的。这个比特流需要在 partial 比特流之前发送到 FPGA 。请注意,发送到 FPGA 的 clearing 比特流必须与当前在 FPGA内部的逻辑匹配,而不是与即将加载的比特流匹配。因此,需要跟踪 FPGA的当前情况,而其他 FPGA 系列则不需要。

加载 clearing 比特流会关闭 reconfigurable 模块,即使它并没有真正改变逻辑。此模块的输出端口可能会显示任何值,直到新的 partial 比特流已加载并启动。

根据 UG909的说法,加载错误 reconfigurable 模块的 clearing 比特流(即不是与 FPGA中的逻辑匹配的 clearing 比特流)也可能会破坏静态逻辑,从而导致 reconfiguration 机制发生故障。

Xilinx的文档似乎对如果在未先加载 clearing 比特流的情况下加载 partial 比特流会发生什么情况含糊不清。在 UG909 的第 9 章中,它首先说: “Prior to loading in a partial bitstream for a new Reconfigurable Module, the existing Reconfigurable Module must be cleared”。所以结论是 clearing 比特流是强制性的。

但在下面几行,同一指南显示“If a clearing bit file is not loaded, initialization routines (GSR) have no effect”。这意味着完全不使用 clearing 比特流是可以的,如果所有同步逻辑单元(synchronous elements)(原则上是RAMs 和触发器)在未知状态下唤醒是可以的。在我自己跳过 clearing 比特流的轶事实验中,我没有发现任何问题,但这并不能证明什么。

因此,对于 Ultrascale FPGAs,肯定需要跟踪 FPGA中加载的内容。例如,这可以通过将输出端口添加到 reconfigurable 模块来完成, reconfigurable 模块具有一个常数值(一段 ID 代码),每个 reconfigurable 模块都不同。这允许静态逻辑识别当前加载的 reconfigurable 模块。无论如何,像这样的 ID 代码都是一个好主意。

Plugin 与 Remote Update 用例

Vivado 采用的这种 parent-child 方法显然是为特定用例设计的,我将其称为 plugin usage: 通过加载当前需要的 reconfigurable 模块来降低 FPGA的成本,而不是一直在 FPGA 中拥有所有可能的功能。例如,如果 FPGA 用于实现多个 image 滤波器, Partial Reconfiguration 允许将每个滤波器(filter)实现为一个 reconfigurable 模块,并且只用需要的滤波器重新加载 FPGA 。

Xilinx 使用术语“Dynamic Function eXchange”(DFX)来表示 Partial Configuration,似乎反映了该技术的主要预期用途。

当这是 Partial Reconfiguration的目的时, parent-child 方法效果很好。生成完整的比特流 files (bitstream files)套件。任何一个 initial 比特流都可以用来初始化 FPGA,以后所有的 reconfigurable 比特流都可以用于 Partial Reconfiguration。当项目的新版本发布时,由所有 bitfiles组成的整个套件都会被替换。

但是还有另一种使用模式,我将其称为 Remote Update。那时 Partial Reconfiguration 被用作版本升级的手段,可能在遥远的将来。在这种使用场景下, initial 比特流是在某个时间点发布的,之后无法更改。稍后, partial 比特流发布,这些必须与 initial 比特流兼容。这些后来的版本可能会持续数年。

对于 Remote Update, parent-child 方法可能很难按原样使用。即使在没有重复 parent的实现的情况下运行 Child 实现以获得新的 partial 比特流是可能的,但长时间这样做可能很困难。例如,静态逻辑的源代码意外更改会使 parent的设计失效,从而导致 Parent 实现重复。结果,新的静态逻辑与之前的不兼容,因此基于它的 partial 比特流不能与原来的 initial 比特流一起使用。

因此,如果 Partial Reconfiguration 旨在作为一种随时间连续更新 FPGA 设计的方法,则实现过程需要一些操作。该主题将在本系列的最后一篇文章中讨论。

压缩比特流

这与 Partial Reconfiguration没有直接关系,但有时需要一个小的 initial 比特流文件,特别是为了确保 FPGA的快速启动。在这种情况下, Partial Reconfiguration 成为快速启动后完成启动过程的一种手段。这可以从相同的数据源(例如 SPI flash)或完全不同的数据源(例如 PCIe 接口)完成。

initial 比特流和 partial 比特流都允许压缩比特流。

这是要添加到 XDC 文件的行,以便请求压缩的比特流:

set_property bitstream.general.compress true [current_design]

理论部分到此结束。下一篇文章展示了配置项目以使用 Partial Reconfiguration的实际步骤。

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