Skip to content

wendadawen/PCIe-XDMA

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Xilinx FPGA PCIe-XDMA 教程:Alveo U280,加速核与BRAM交互,XDMA与BRAM交互,XDMA驱动加速核的启动与停止

Xilinx FPGA PCIe-XDMA的教程,基于PCIe-XDMA IP核,基于Alveo U280 FPGA开发板,该示例涵盖加速核与BRAM交互、XDMA与BRAM交互、XDMA驱动加速核的启动与停止、主机代码的编写

引言

前置保姆级知识可以参考:https://github.com/Reconfigurable-Computing/Xilinx-FPGA-PCIe-XDMA-Tutorial

Vitis HLS生成IP加速核

建立Vitis HLS工程

alt text

编写加速核

这段代码实现了一个加速核功能,通过PCIe-XDMA启动。其主要流程是从BRAM中读取数据A,将数据分块处理,每次读取BUFFER_SIZE大小的数据到本地缓冲区,对缓冲区中的每个数据元素左移两位(相当于乘以4) ,最后将处理后的数据写回BRAM的C中,完成数据的处理与存储更新。其中,BRAM我们使用AXI4进行读写。

#include <hls_math.h>
#include <ap_fixed.h>
#include <hls_stream.h>
#include <ap_int.h>

#define BUFFER_SIZE 256
#define DATA_SIZE 1024

const unsigned int c_len = DATA_SIZE / BUFFER_SIZE;
const unsigned int c_size = BUFFER_SIZE;

extern "C"
void vecmul_top(int* c, int* a, const int n_elements) {
	#pragma HLS INTERFACE mode=s_axilite port=n_elements
	#pragma HLS INTERFACE mode=s_axilite port=return  // 这个还是挺重要的,把加速核的启动信号放在s_axilite接口上,而不是电平信号,方便xdma直接启动加速器
    #pragma HLS INTERFACE mode=m_axi port = c bundle = gmem0 offset=slave
    #pragma HLS INTERFACE mode=m_axi port = a bundle = gmem0 offset=slave

    int bufferA[BUFFER_SIZE];
    for (int i = 0; i < n_elements; i += BUFFER_SIZE) {
        #pragma HLS LOOP_TRIPCOUNT min = c_len max = c_len
        int size = BUFFER_SIZE;
        if (i + size > n_elements) size = n_elements - i;
    readA:
        for (int j = 0; j < size; j++) {
            #pragma HLS LOOP_TRIPCOUNT min = c_size max = c_size
            bufferA[j] = a[i + j];
        }
    vmul_writeC:
        for (int j = 0; j < size; j++) {
            #pragma HLS LOOP_TRIPCOUNT min = c_size max = c_size
            c[i + j] = bufferA[j] << 2;
        }
    }
}

Vitis HLS 综合

  1. 设置Top函数alt text
  2. Run C Synthesis

在综合报告中的 HW Interfaces 板块可以看到综合后接口的信息:alt text

加速核的geme0被综合成为m_axi,其他参数例如控制信号CTRL(AP_START,AP_DONE,AP_IDLE,AP_READY等)、C、A、n_elements等被综合成为s_axi_control(也就是s_axi_lite)。

其中solution1/syn/verilog/vecmul_top_control_s_axi.v文件中能看到s_axi_control的地址信息情况,如下列:

//------------------------Address Info-------------------
// 0x00 : Control signals
//        bit 0  - ap_start (Read/Write/COH)
//        bit 1  - ap_done (Read/COR)
//        bit 2  - ap_idle (Read)
//        bit 3  - ap_ready (Read/COR)
//        bit 7  - auto_restart (Read/Write)
//        bit 9  - interrupt (Read)
//        others - reserved
// 0x04 : Global Interrupt Enable Register
//        bit 0  - Global Interrupt Enable (Read/Write)
//        others - reserved
// 0x08 : IP Interrupt Enable Register (Read/Write)
//        bit 0 - enable ap_done interrupt (Read/Write)
//        bit 1 - enable ap_ready interrupt (Read/Write)
//        others - reserved
// 0x0c : IP Interrupt Status Register (Read/TOW)
//        bit 0 - ap_done (Read/TOW)
//        bit 1 - ap_ready (Read/TOW)
//        others - reserved
// 0x10 : Data signal of c
//        bit 31~0 - c[31:0] (Read/Write)
// 0x14 : Data signal of c
//        bit 31~0 - c[63:32] (Read/Write)
// 0x18 : reserved
// 0x1c : Data signal of a
//        bit 31~0 - a[31:0] (Read/Write)
// 0x20 : Data signal of a
//        bit 31~0 - a[63:32] (Read/Write)
// 0x24 : reserved
// 0x28 : Data signal of n_elements
//        bit 31~0 - n_elements[31:0] (Read/Write)
// 0x2c : reserved
// (SC = Self Clear, COR = Clear on Read, TOW = Toggle on Write, COH = Clear on Handshake)

Export RTL

alt text

Vivado 驱动加速核

  • 注意:接下来流程需要sudo权限,并且避免在虚拟环境中运行vivado(最好直接使用/tools/Xilinx/Vivado/2023.1/bin/vivado类似这样启动vivado)!否则会出现找不到fpga开发板或者运行失败的情况!

创建vivado工程

alt text alt text alt text

将加速IP核添加入Vivado

alt text

搭建blockdesign

建立blockdesign

在左侧 Flow Navigator → IP Integrator 中点 Create Block Design ,然后指定该 block design 的名称,保持默认即可。然后点 OK。

添加并配置 PCIe-XDMA IP

在 Diagram 中点上方的 "+" (Add IP) ,输入 xdma ,然后双击 "DMA/Bridge Subsystem for PCIe",如下图 。

alt text

然后就可以看到 Diagram 中出现了一个叫 xdma_0 的 IP 。双击这个 xdma_0 ,配置这个 IP 的参数,该 IP 的配置一共有6页。其中第二页如下图,你可以指定 PCIe 和 AXI 接口的相关信息。其中大多数选项与下图保持一致即可,但有一些选项你可以根据需要去修改:

  • Lane Width 可以自由指定,因为 NetFPGA 的 PCIe 为 x8,我们可以取 x1, x4, x8 。本例中取 x1 。
  • Maximum Link Speed 代表的是 PCIe 的速率,可以自由指定,2.5 GT/s 代表 PCIe Gen1, 5.0 GT/s 代表 PCIe Gen2,8.0 GT/s 代表 PCIe Gen3 。本例中取 2.5 GT/s
  • Reference Clock Frequency 是指 PCIe 参考时钟的频率,由于 FPGA 使用的是来自 PCIe 插口的,由电脑主板提供的 PCIe 参考时钟,而 PCIe 规定该时钟频率为 100MHz 。因此这里取 100MHz 。
  • AXI Data Width 是 AXI 总线中数据总线的宽度,也即一个周期最多可以读/写的比特数量。可以自由指定,但要和 AXI slave 保持一致。本例中取 64 bit。
  • AXI Clock Frequency 是 AXI 总线的时钟频率,可以自由指定,只要 AXI slave 能工作在这个频率下就行。本例中取 125 MHz 。 alt text

第三页的ID Initial Values是自动生成,无需手动更改,Class Code Lookup Assistant按照如图手动更改。 alt text

第四页按照如图配置。要勾选上AXI Lite,以便PCIe-XDMA可以控制加速核的启动与暂停。 alt text

第五页按照如图配置。 alt text

第六页保持默认。 alt text

同理添加并配置 Constant、AXI Interconnect、AXI BRAM Controller、Block Memory Gengerator、加速IP核

Constant:alt text

AXI Interconnect:alt text

AXI BRAM Controller:该 IP 的功能是充当 AXI master 和 “裸BRAM” 之间的桥梁,可以理解为进行一个协议转换——接收 AXI 总线的命令,把它转化为对 "裸BRAM" 的读写。

  • Data Width : 是 AXI 总线中数据总线的宽度,也即一个周期最多可以读/写的比特数量。可以自由指定。这里取 32 bit。
  • Memory Depth : 存储器深度,决定了 BRAM 的容量,在blockdesign不可手动调节,需通过address editor调节,取8K即代表这里的深度是2048。这里我们取 2048,则 BRAM 容量 = Memory Depth * Data Width = 2048 * 32 bits = = 8 K Bytes.(这里其实就对应这后面address editor的配置)。

alt text

Block Memory Gengerator:alt text

把 PCIe 相关的信号引出到外界

alt text

分配 AXI 地址映射

在 blockdesign 中,只要有 AXI master 和 AXI slave 对接的情况,就需要进行地址分配 。因为一个 AXI master 可能对应多个 AXI slave ,我们需要分配这些 AXI slave 在该 AXI master 中对应的地址 ——即使只有一个 AXI slave。

我们切换到 Diagram 旁边的 Address Editor。按照如图配置:

alt text

在这里,我把AXI-Bram的Master Base Address设置成为了0xC0000000,换句话说,当 xdma_0 通过 AXI 总线对 0xC0000000这个地址进行读写时,读写的就是 axi_bram_ctrl_0 的起始位置。

另外,AXI-Bram的Range是8K,对应着AXI BRAM Controller的深度2048。

blockdesign 验证模块设计验证

我们切换回 Diagram ,然后点击上方的 "Validate Design" ,软件会帮我们检查该 blockdesign 中是否有错误。它只能检查出一些低级错误(比如 AXI 总线两侧的位宽不匹配),而功能性的正确性还需开发者自己保证。

alt text

生成 blockdesign 的 HDL Wrapper

alt text

然后它就会生成一个名为 design_1_wrapper 的 Verilog 源文件。双击这个文件可以看到它的代码,我们发现它的模块输入输出接口就是我们之前引出到 blockdesign 的外部的那些信号(也即 PCIe 时钟、复位、信号),如下:

alt text

注意到在这里,PCIe 的参考驱动时钟 sys_clk_0 是单端的,而 FPGA 引脚上收到的的 PCIe 参考时钟实际上是差分的。因此这里还需要一个单端转差分的 clock buffer 。另外,还需要对复位信号 sys_rst_n_0 进行 IO buffer 。这一步将在以下 Verilog 顶层模块中实现。

编写 FPGA 顶层的 Verilog 模块

下一步我们要编写一个 Verilog 模块,来调用这个 blockdesign 。同时实现 clock-buffer 和 IO-buffer 。

给工程添加一个 Verilog 文件,名为 fpga_top.v ,然后在其中编写代码如下。注意到:

  • fpga_top.v 调用了我们在上一步生成的 block design 的 HDL wrapper 。
  • fpga_top.v 调用了 IBUFDS_GTE4 和 IBUF ,分别是 clock-buffer 和 IO-buffer ,它们是 Xilinx 提供的原语 (primitive) ,对应着 Xilinx FPGA 种的资源。在 Vivado 的 Verilog 编程中不需要引入任何文件就能调用,但不能移植到其它 FPGA 厂商的 Verilog 工程中。
module fpga_top(
    input  wire       i_pcie_rstn,
    input  wire       i_pcie_refclkp, i_pcie_refclkn,
    input  wire [0:0] i_pcie_rxp, i_pcie_rxn,
    output wire [0:0] o_pcie_txp, o_pcie_txn
);

wire  pcie_rstn;
wire  pcie_refclk;
wire  pcie_refclk_gt;
wire  pcie_refclk_div2;

// Reset input buffer ----------------------------------------------
IBUF   sys_reset_n_ibuf (
    .I               ( i_pcie_rstn       ),
    .O               ( pcie_rstn         )
);

// Ref clock input buffer ----------------------------------------------
IBUFDS_GTE4 #(
    .REFCLK_HROW_CK_SEL(2'b00)
) refclk_ibuf (
  .CEB  (1'b0),           // 常使能
  .I    (i_pcie_refclkp),  // 差分时钟P
  .IB   (i_pcie_refclkn),  // 差分时钟N
  .O    (pcie_refclk_gt),  // GT直接输出
  .ODIV2(pcie_refclk_div2) // 分频输出
);
assign pcie_refclk = pcie_refclk_div2;

// block design's top (the HDL wrapper) ----------------------------------------------
design_1_wrapper design_1_wrapper_i (
    .pcie_mgt_0_rxn  ( i_pcie_rxn        ),
    .pcie_mgt_0_rxp  ( i_pcie_rxp        ),
    .pcie_mgt_0_txn  ( o_pcie_txn        ),
    .pcie_mgt_0_txp  ( o_pcie_txp        ),
    .sys_clk_0       ( pcie_refclk       ),
    .sys_clk_gt_0    ( pcie_refclk_gt    ),
    .sys_rst_n_0     ( pcie_rstn         )
);

endmodule

编写约束文件 .xdc

create_clock -name sys_clk -period 10 [get_ports i_pcie_refclkp]

set_false_path -from [get_ports i_pcie_rstn]
set_property PULLUP true [get_ports i_pcie_rstn]
set_property IOSTANDARD LVCMOS18 [get_ports i_pcie_rstn]

set_property PACKAGE_PIN BH26 [get_ports i_pcie_rstn]

set_property CONFIG_VOLTAGE 1.8 [current_design]

set_property LOC [get_package_pins -of_objects [get_bels [get_sites -filter {NAME =~ *COMMON*} -of_objects [get_iobanks -of_objects [get_sites GTYE4_CHANNEL_X1Y7]]]/REFCLK0P]] [get_ports i_pcie_refclkp]
set_property LOC [get_package_pins -of_objects [get_bels [get_sites -filter {NAME =~ *COMMON*} -of_objects [get_iobanks -of_objects [get_sites GTYE4_CHANNEL_X1Y7]]]/REFCLK0N]] [get_ports i_pcie_refclkn]

set_false_path -to [get_pins -hier *sync_reg[0]/D]

综合、实现、烧录

  • 有用的东西:烧录完恢复原有的固件可以使用:boot_hw_device [lindex [get_hw_devices] 0]

查看 PCIe XDMA 设备是否被识别

lspci | grep -i xilinx

运行 lspci 命令来看看 PCIe 设备是否被正常识别。如果发现其中有 "Memory controller: Xilinx..." ,说明识别成功。

alt text

在 Linux 主机上加载 PCIe XDMA 驱动

  • 注意:请尽量使用sudo来操控命令

下载XDMA驱动

https://github.com/Xilinx/dma_ip_drivers

编译 XDMA 驱动

可以跟着这个readme走: https://github.com/Xilinx/dma_ip_drivers/tree/master/XDMA/linux-kernel

也可以:

  • cd dma_ip_drivers/XDMA/linux-kernel
  • cd xdma
  • sudo make install
  • cd tools
  • sudo make
  • 如果想重新编译
    • sudo rm *.o
    • sudo rm *.ko
    • sudo make install

安装XDMA驱动

  • cd tests
  • sudo ./load_driver.sh

如果驱动加载成功,则显示 "DONE" 。

然后我们运行以下命令,会发现 /dev 目录下出现一系列 xdma 设备。

$ ls /dev/xdma*
...
/dev/xdma0_c2h_0
...
/dev/xdma0_h2c_0

遇到的问题

  • 若无法在vivado找到板子,首先要检查是否在虚拟环境,确保不要在虚拟环境,其次是不是sudo打开
  • 安装xdma及其容易出现问题,首先应该检查所使用的命令是否是sudo运行
  • 其次若 sudo ./load_driver出现

alt text

可以尝试运行

  • sudo rmmod xdma
  • sudo modprobe xdma

来重新安装xdma,或许就可以了,有时候使用load_driver.sh脚本不会成功,但是手动使用这两个命令行就会成功,暂时原因不明。

驱动加载成功的标志是运行ls /dev/xdma*出现该有的设备,如:alt text

Host驱动程序

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <errno.h>  
#define min(a, b) ((a) < (b) ? (a) : (b))

//------------------------Address Info-------------------
// 0x00 : Control signals
//        bit 0  - ap_start (Read/Write/COH)
//        bit 1  - ap_done (Read/COR)
//        bit 2  - ap_idle (Read)
//        bit 3  - ap_ready (Read/COR)
//        bit 7  - auto_restart (Read/Write)
//        bit 9  - interrupt (Read)
//        others - reserved
// 0x04 : Global Interrupt Enable Register
//        bit 0  - Global Interrupt Enable (Read/Write)
//        others - reserved
// 0x08 : IP Interrupt Enable Register (Read/Write)
//        bit 0 - enable ap_done interrupt (Read/Write)
//        bit 1 - enable ap_ready interrupt (Read/Write)
//        others - reserved
// 0x0c : IP Interrupt Status Register (Read/TOW)
//        bit 0 - ap_done (Read/TOW)
//        bit 1 - ap_ready (Read/TOW)
//        others - reserved
// 0x10 : Data signal of c
//        bit 31~0 - c[31:0] (Read/Write)
// 0x14 : Data signal of c
//        bit 31~0 - c[63:32] (Read/Write)
// 0x18 : reserved
// 0x1c : Data signal of a
//        bit 31~0 - a[31:0] (Read/Write)
// 0x20 : Data signal of a
//        bit 31~0 - a[63:32] (Read/Write)
// 0x24 : reserved
// 0x28 : Data signal of n_elements
//        bit 31~0 - n_elements[31:0] (Read/Write)
// 0x2c : reserved
// (SC = Self Clear, COR = Clear on Read, TOW = Toggle on Write, COH = Clear on Handshake)
//------------------------Address Info-------------------

// bram设备地址偏移量,来源于在vivado中自己的手动设置,具体是address editor中设置的
#define BRAM_S_AXI_BASE 0xC0000000
// 控制寄存器的地址偏移量,来源于在vivado中自己的手动设置,具体是address editor中设置的
#define CONTROL_S_AXI_BASE 0x00000000

// 寄存器地址偏移量(基于AXI-Lite地址映射),来源于上面👆,上面的信息来源于vitis HLS生成的s-axi的verilog文件:solution1/impl/verilog/vecmul_top_control_s_axi.v
#define CTRL_REG        CONTROL_S_AXI_BASE+0x00  // 控制寄存器,控制加速核的启动和状态,不同的位表示不同的状态和控制信号
#define C_LOW_REG       CONTROL_S_AXI_BASE+0x10  // C寄存器低32位,存储加速核的变量C的地址
#define C_HIGH_REG      CONTROL_S_AXI_BASE+0x14  // C寄存器高32位,存储加速核的变量C的地址
#define A_LOW_REG       CONTROL_S_AXI_BASE+0x1c  // A寄存器低32位,存储加速核的变量A的地址
#define A_HIGH_REG      CONTROL_S_AXI_BASE+0x20  // A寄存器高32位,存储加速核的变量A的地址
#define N_ELEMENTS_REG  CONTROL_S_AXI_BASE+0x28  // N元素寄存器,存储加速核的变量N的值

// 控制寄存器位定义,对应上面👆的CTRL_REG寄存器
#define AP_START_BIT    (1 << 0)
#define AP_DONE_BIT     (1 << 1)
#define AP_IDLE_BIT     (1 << 2)



// description : read data from device to local memory (buffer), (i.e. device-to-host),主要服务master axi
// parameter :
//       dev_fd : device instance
//       addr   : source address in the device
//       buffer : buffer base pointer
//       size   : data size
// return:
//       int : 0=success,  -1=failed
int dev_read (int dev_fd, uint64_t addr, void *buffer, uint64_t size) {
    if ( addr != lseek(dev_fd, addr, SEEK_SET) )                                 // seek
        return -1;                                                               // seek failed
    if ( size != read(dev_fd, buffer, size) )                                    // read device to buffer
        return -1;                                                               // read failed
    return 0;
}


// description : write data from local memory (buffer) to device, (i.e. host-to-device),主要服务master axi
// parameter :
//       dev_fd : device instance
//       addr   : target address in the device
//       buffer : buffer base pointer
//       size   : data size
// return:
//       int : 0=success,  -1=failed
int dev_write (int dev_fd, uint64_t addr, void *buffer, uint64_t size) {
    if ( addr != lseek(dev_fd, addr, SEEK_SET) )                                 // seek
        return -1;                                                               // seek failed
    if ( size != write(dev_fd, buffer, size) )                                   // write device from buffer
        return -1;                                                               // write failed
    return 0;
}


// m_axi_lite写操作,主要服务master axi lite
void write_reg(void* base, int offset, uint32_t val) {
    *((volatile uint32_t*)(base + offset)) = val;
}

// m_axi_lite读操作,主要服务master axi lite
uint32_t read_reg(void* base, int offset) {
    return *((volatile uint32_t*)(base + offset));
}

int main() {

    int n_elements = 1024;

    int fd_regs;
    int fd_c2h, fd_h2c;
    void *buffer_regs;
    volatile uint32_t *buffer_c2h;
    volatile uint32_t *buffer_h2c;
    const char *device_regs = "/dev/xdma0_user";  // XDMA用户空间设备
    const char *device_c2h = "/dev/xdma0_c2h_0"; // XDMA C2H设备
    const char *device_h2c = "/dev/xdma0_h2c_0"; // XDMA H2C设备
    
    /*=========打开设备,包括XDMA用户空间设备,XDMA C2H设备和DMA H2C设备=========*/
    if ((fd_regs = open(device_regs, O_RDWR)) < 0) {
        printf("打开用户空间设备设备失败\n");
        close(fd_regs);
        return EXIT_FAILURE;
    }

    if((fd_c2h = open(device_c2h, O_RDWR)) < 0) {
        printf("打开C2H设备失败\n");
        close(fd_c2h);
        return EXIT_FAILURE;
    }

    if((fd_h2c = open(device_h2c, O_RDWR)) < 0) {
        printf("打开H2C设备失败\n");
        close(fd_h2c);
        return EXIT_FAILURE;
    }

    /*=======在host主机分配缓冲空间buffer========*/
    buffer_regs = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd_regs, 0);
    if (buffer_regs == MAP_FAILED) {
        printf("*** ERROR: mmap failed\n");
        return EXIT_FAILURE;
    }
    
    buffer_h2c = (uint32_t*)malloc(1024 * 1024);
    if (buffer_h2c == NULL) {
        printf("*** ERROR: buffer_h2c failed to allocate buffer\n");
        return EXIT_FAILURE;
    }

    buffer_c2h = (uint32_t*)malloc(1024 * 1024);
    if (buffer_c2h == NULL) {
        printf("*** ERROR: buffer_c2h failed to allocate buffer\n");
        return EXIT_FAILURE;
    }

    /*========初始化H2C缓冲区========*/
    for (int i = 0; i < n_elements; i++) {
        buffer_h2c[i] = i;  // 填充数据
    }

    if ( dev_write(fd_h2c, BRAM_S_AXI_BASE, buffer_h2c, n_elements * 4) ) { 
        printf("*** ERROR: failed to write\n");
        return EXIT_FAILURE;
    }

    printf("H2C缓冲区: \n");
    for (int i = 0; i < min(5 * 8, n_elements); i++) {
        printf("\t%u ", buffer_h2c[i]);
        if ((i + 1) % 8 == 0) {
            printf("\n");
        }
    }
    printf("\n");

    /*========初始化加速核的参数列表=========*/
    write_reg(buffer_regs, C_LOW_REG, 0xC0000000);  // 写入bram的第一个地址,因为本示例中bram在address editor中配置的第一个地址是0xC0000000
    write_reg(buffer_regs, C_HIGH_REG, 0x00000000);
    write_reg(buffer_regs, A_LOW_REG, 0xC0000000);  // 写入bram的第一个地址,因为本示例中bram在address editor中配置的第一个地址是0xC0000000
    write_reg(buffer_regs, A_HIGH_REG, 0x00000000);
    write_reg(buffer_regs, N_ELEMENTS_REG, n_elements);
    printf("寄存器初始化完成...\n");
    write_reg(buffer_regs, CTRL_REG, AP_START_BIT); 
    printf("加速器已启动...\n");

    
    /*=======等待加速器完成===========*/
    while (1) {
        uint32_t ctrl_reg = read_reg(buffer_regs, CTRL_REG);
        printf("==================\n");
        printf("C寄存器状态: %08X%08X\n", read_reg(buffer_regs, C_HIGH_REG), read_reg(buffer_regs, C_LOW_REG));
        printf("A寄存器状态: %08X%08X\n", read_reg(buffer_regs, A_HIGH_REG), read_reg(buffer_regs, A_LOW_REG));
        printf("N元素寄存器状态: %u\n", read_reg(buffer_regs, N_ELEMENTS_REG));
        printf("控制寄存器状态: \n");
        printf("\tap_start: %d, ap_done: %d\n", (ctrl_reg & AP_START_BIT) != 0, (ctrl_reg & AP_DONE_BIT) != 0);
        printf("\tap_idle: %d, ap_ready: %d\n", (ctrl_reg & AP_IDLE_BIT) != 0, (ctrl_reg & AP_DONE_BIT) != 0);
        usleep(100);  // 100us轮询间隔,1000000是1秒
        if (ctrl_reg & AP_DONE_BIT) {
            printf("加速器计算完成!\n");
            printf("==================\n");
            break;
        }
    }
    
    /*========取出加速器计算结果===========*/
    if ( dev_read(fd_c2h, BRAM_S_AXI_BASE, buffer_c2h, n_elements * 4) ) { 
        printf("*** ERROR: failed to read\n");
        return EXIT_FAILURE;
    }

    printf("C2H缓冲区:\n");
    for (int i = 0; i < min(5 * 8, n_elements); i++) {
        printf("\t%u ", buffer_c2h[i]);
        if ((i + 1) % 8 == 0) {
            printf("\n");
        }
    }
    printf("\n");
    
    /*========释放空间==========*/
    close(fd_c2h);
    close(fd_h2c);
    close(fd_regs);
    free((void*)buffer_c2h);
    free((void*)buffer_h2c);
    if (munmap(buffer_regs, 4096) < 0) {
        printf("*** ERROR: munmap failed\n");
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}

若host文件名为xdma_rw.c,则编译命令是:gcc xdma_rw.c -o xdma_rw,运行命令是sudo ./xdma_rw,以下是成功结果:

alt text

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors