如何在 zynq 7020 上使用 C/C++ 实现 FPGA 协处理?

hap*_*_wy 5 fpga xilinx vivado zynq

我在学习vivadoHLS,教程u871已经介绍了HLS的使用方法,优化我的C/C++代码。但我想知道如何将它们加载到我的 zynq 7020 板上,让它在板上运行。

我要实现的是:主机(板载CPU)调用PL(FPGA)进行计算,并将参数发送给PL,PL再将结果返回给CPU。

例如,C 中的一个函数:add(int* a, int* b),它将分别添加 a[i] 和 b[i] 并返回一个数组int* 结果。, 通过HLS,我可以展开for循环,那么计算会更快。CPU将a和b的地址发送给PL,PL进行计算,并将结果地址发送回CPU。

在本教程中,它只介绍了如何使用 HLS,没有解释如何与 PL 和 CPU 通信,或者如何将其加载到板上以便它可以在板上运行。

请推荐教程或告诉我在哪里学习,非常感谢!!

Jon*_*let 5

这是一个相当复杂的主题,因为它们是解决方案的许多变体。这部分在ug871的第2章有介绍,可惜用的是EDK而不是Vivado。不过,Vivado HLS 概念是相同的。您还可以查看xapp890

基本上,Zynq 使用 AXI 端口连接到 PL。AXI 端口是经典的地址+数据总线。AXI 有两种类型,标准型和精简型。lite 版本不支持burst,专注于以牺牲性能为代价使用更少的面积,通常用于寄存器接口。标准 AXI 具有非常高的性能并支持突发,您通常使用它来连接到 DDR 内存。

Zynq 有几个 AXI 端口,作为主端口和从端口。从端口允许您的 IP 读/写 Zynq 的内存空间。主端口允许 Zynq 读/写内核的内存空间。几个端口有不同的性能,GP 应该用于低性能的 AXI-Lite,HP 到 IP,需要更直接地访问 Zynq DDR 内存。

连接 IP 的最简单方法是使用 AXI-lite。在 Vivado HLS 中,在地址 0 处定义寄存器 a,在地址 4 处定义寄存器 b,在地址 d 处定义寄存器 c(答案)。函数 add 看起来像:

int add(int a, int b)
{
    volatile int *my_ipaddr = MY_IP_BASEADDR; // Address is configured in Vivado block design

    *(my_ipaddr+0) = a;
    *(my_ipaddr+1) = b;
    return *(my_ipaddr+2);
}
Run Code Online (Sandbox Code Playgroud)

由于我不使用 Vivado HLS,因此我不确定如何操作。但是浏览 ug871 它涵盖了 AXI-Lite 寄存器接口。

第三种类型的 AXI 称为 AXI-Stream。它是一个没有地址的通信总线,只有数据存在并带有一些标志来同步流。它通常用于不关心地址的内核之间或与 AXI-DMA 引擎一起使用。主要问题是您不能将 AXI-Stream 直接连接到 Zynq、AFAIK。

一个示例应用程序是 xapp890,尽管它们使用 Video-DMA 内核,因为它是一个视频应用程序。它提供了更高性能的解决方案。在您的示例中,它将有一个输入从属 AXI-Stream 来接收 a/b,以及一个输出主 AXI-Stream 来返回 c。您将内核与 AXI-DMA IP 内核连接,伪代码为:

void add(int *ab, int *c, unsigned int length)
{
    XAxi_Dma_Start_Transfer((void *)ab, length, CHANNEL_MM2S); // Not actual function, MM2S stands for memory to stream
    XAxi_Dma_Start_Transfer((void *)c, length, CHANNEL_S2MM); // S2MM = stream to memory

    while(XAxi_Dma_Transfer_Done == 0) {} // Wait end of transfer
}
Run Code Online (Sandbox Code Playgroud)

这是很多信息,但希望它能让您理解应用笔记。总而言之,您的 IP 必须提供 AXI(Lite,流标准)接口来交换数据,您将这些数据连接到 Zynq AXI 端口。此外,您的 IP 也可以有中断信号。