接口注意事项
虽然单核编程专注于单个AI引擎中算法的向量化,但多核编程考虑多个AI引擎内核,数据在它们之间流动。
ADF图可以包含与PS、PL和全局内存交互的单个内核或多个内核。每个AI引擎内核都有一个运行时比率。这个数字是以一次内核调用(处理一个数据块)所占用的周期数与周期预算的比率计算的。应用程序的周期预算通常根据预期的数据吞吐量和正在处理的块大小而固定。运行时比率被指定为ADF图中每个AI引擎内核的约束。
AI引擎编译器将多个内核分配到单个AI引擎中,如果它们的组合总运行时间比率小于1,并且多个内核适合AI引擎程序内存,并且如果总资源使用量(如流接口数量)不超过AI引擎瓦片限制。或者,编译器可以将它们分配到多个AI引擎中。
为AI引擎编程时,需要注意的是,每个AI引擎都能够访问两个32位AXI4流输入、两个32位AXI4流输出、一个384位级联流输入、一个384位级联流输出、两个256位数据加载和一个256位数据存储。然而,由于指令的长度,并非所有这些操作都可以在同一周期内执行。
为了最佳地使用硬件资源,了解ADF图与PS、PL和全局内存之间的数据传输、内核之间的数据传输、平衡数据移动以及尽可能减少内存或流停顿的不同方法至关重要。
AI引擎之间的数据移动
通常,有两种方法可以在内核之间传输数据-缓冲区或流。当使用缓冲器时,数据传输可以被实现为乒乓缓冲器,或者可选地,使用单个缓冲器。AI引擎工具将负责内核之间的缓冲区同步。设计人员需要在对应用程序进行分区时决定内核之间的缓冲区大小和可选的缓冲区位置。如果不同的数据缓冲区之间需要重叠,AI Engine工具提供了为缓冲区设置边距的选项,即自动复制数据的重叠。
当使用流时,数据移动涉及两个输入流端口以及两个输出流端口,沿着一个专用级联流输入端口和输出端口。流端口可以在每个端口上提供每周期32位或每四个周期128位。流接口是双向的,可以通过流端口读取或写入相邻或非相邻AI引擎。然而,级联流端口是单向的,并且仅在相邻AI引擎之间提供单向访问。
通过AI引擎数据存储器进行数据通信
在多个内核适合于单个AI引擎的情况下,可以使用AI引擎的本地数据存储器中或AI引擎直接访问的三个相邻存储器中的任何一个中的公共缓冲器来建立两个或更多个连续内核之间的通信。在这种情况下,只需要一个缓冲区,因为内核以循环方式一个接一个地执行。
对于内核在单独但相邻的AI引擎中的情况,可以通过在使用乒乓缓冲器的两个相邻AI引擎瓦片之间共享的数据存储器模块来执行通信。这些缓冲区可以在不同的内存条上,这样可以避免访问冲突。同步是通过锁完成的。AI引擎内核的输入和输出缓冲区通过与缓冲区相关联的锁来确保准备就绪。在这种类型的通信中,由于不需要DMA和AXI 4-Stream互连,因此节省了路由资源并消除了数据传输延迟。
通过内存和DMA进行数据通信
对于非相邻AI引擎,可以使用与每个AI引擎相关联的存储器模块中的DMA来建立类似的通信。在每个存储器模块中使用乒乓缓冲器,并通过锁进行同步。与共享内存通信相比,通信延迟和内存资源增加。
通过AXI4流互连进行数据通信
AI引擎可以直接通过AXI 4-Stream互连进行通信,而无需任何DMA和内存交互。数据可以从一个AI引擎发送到另一个AI引擎,或者通过流媒体接口广播。流连接的数据带宽为每周期32位,并且内置握手和背压机制。流连接可以是单播或多播。请注意,在多播通信的情况下,只有当所有目的地都准备好接收数据时,数据才会同时发送到所有目的地端口。
数据通信中的缓冲区与流
数据流图中的AI引擎内核操作的数据流是无限长的类型化值序列。这些数据流可以被分成称为缓冲区的单独块,并由内核处理。内核消耗输入数据块并产生输出数据块。可以指定一个初始化函数在内核开始处理输入数据之前运行。内核可以从内存中读取标量或向量,但是,每个读写操作的有效向量长度必须是128位的倍数。在内核执行之前,输入数据和输出数据的缓冲区被锁定。由于输入数据缓冲区需要在内核启动之前填充输入数据,因此与流接口相比,它会增加延迟。内核可以在数据缓冲区内执行随机访问,并且能够为需要来自先前缓冲区的一定数量的字节的算法指定余量。
内核还可以以逐个样本的方式访问数据流。流用于连续数据,并使用阻塞或非阻塞调用进行读取和写入。级联流仅支持阻塞访问。AI引擎支持两个32位流输入端口和两个32位流输出端口。阅读或写入数据流的有效向量长度必须为32或128位。当程序中独立数据流的数量超过可用硬件流通道或端口的数量时,包流非常有用。
PLIO端口属性用于建立跨AI引擎和PL边界的外部流连接。PLIO端口可以通过DMA S2 MM或MM 2S通道连接到AI引擎缓冲区,或直接连接到AI引擎流接口。这两种连接(PL从/到缓冲区或流)都受到AI引擎瓦片的流接口的限制,每个周期的限制为32位。但是,对于缓冲区接口,需要在内核启动之前填满ping或pong缓冲区。因此,从/到PL的缓冲器接口通常具有比流接口更大的延迟。
下表总结了内核之间缓冲区和流连接的差异。
注:
1.缓冲器背压,采集或不采集,发生在整个数据缓冲器上。
2.考虑到所有数据在内核边界可用。
图形代码是C++的,可以在内核源文件的单独文件中找到。编译器将AI Engine内核放入AI Engine数组中,处理内存需求并为数据流建立所有必要的连接。可以将具有低内核使用率的多个内核放置到单个瓦片中。
自由运行的AI引擎内核
AI Engine内核可以通过graph::run(-1)连续运行。这样,内核将在最后一次迭代完成后自动重新启动。注意事项:graph::run()without an argument运行AI Engine内核之前指定的迭代次数(如果图形在没有任何参数的情况下运行,则默认为无穷大)。如果图是以有限的迭代次数运行的,例如,mygraph.run(3); mygraph.run();第二个run调用也将运行三次迭代。
但是,它需要输入缓冲器和输出缓冲器在启动之前准备就绪。因此,它在内核执行迭代之间的开销很小。本节描述了一种方法来构造一种零开销且永远运行的内核。它被称为自由运行的AI引擎内核。自由运行的内核只能有流接口。具有无限迭代的循环可以在内核内部。举例来说,请注意:
自由运行的内核必须定义自己的图形。这个图形不能有任何其他非自由运行的内核,因为图形永远不会停止,非自由运行的内核在启动后将失去控制。包含自由运行内核的图必须是可以连接到其他图的顶层图,或者可以连接到PLIO或GMIO。自由运行图和其他图之间的示例连接如下所示。
自由运行图可以使用mygraph_free.run(-1)启动,也可以在加载后自动启动。
产品参数规格
使用运行时参数(RTP)是将数据传递给内核的另一种方式。支持两种类型的运行时参数执行模型。
1.异步参数可通过控制处理器(如Arm®处理器)随时更改。每次调用内核时都会读取它们。这意味着参数的更新发生在内核的不同执行之间,但它不要求更新以特定的模式发生。例如,这些类型的参数被用作不频繁改变的滤波器系数。
2.同步参数(触发参数)阻止内核执行,直到控制处理器(如Arm处理器)写入这些参数。在写入时,内核读取新的更新值并执行一次。完成后,它将被阻止执行,直到再次更新参数。这允许与正常流模型不同类型的执行模型,这对于其中阻塞同步很重要的某些更新操作可能是有用的。
理解AI Engine内核之间的RTP交互仅发生在内核执行边界内是非常重要的。这意味着,只有在源内核完成其当前迭代时,才能读取源内核的RTP输出。
注意:AI Engine内核的RTP端口需要在内核执行前后分别获取锁和释放锁。这将为每个内核迭代带来少量开销。在考虑将数据划分为帧时,必须根据系统级性能要求考虑开销。
AI引擎和PL内核数据通信
AI引擎阵列接口包含使用AXI 4-Stream连接在AI引擎和PL内核之间进行通信的模块。通常,PL接口通过流接口产生或使用数据。根据AI引擎内核是传送缓冲区数据还是流数据,可能会涉及DMA和乒乓缓冲区。
请注意,PL内核的运行频率低于AI Engine内核。数据必须跨越AI引擎时钟和PL时钟之间的时钟域。AMD Vitis ™环境自动处理时钟域交叉(CDC)路径。如果可能,建议将PL内核频率作为AI引擎频率的整数因子来运行。例如,AI Engine时钟频率的1/2或1/4。
通过GMIO进行DDR内存访问
文章来源:威视锐科技