一 看下机器上是什么fpga设备
root@bms-airtrunk-b-s11fpgav2-app-10-169-58-141:/data/# sudo fpgainfo fme
Board Management Controller, microcontroller FW version unavailable (I/O error)
Last Power Down Cause: Q){
Last Reset Cause: Software reset
Watchdog timeout
//****** FME ******//
Object Id : 0xEC00001
PCIe s:b:d:f : 0000:D8:00:0
Device Id : 0x09C4
Socket Id : 0x00
Ports Num : 01
Bitstream Id : 0x123000200000185
Bitstream Version : 0x30201
Pr Interface Id : 69528db6-eb31-577a-8c36-68f9faa081f6
Board Management Controller, microcontroller FW version unavailable (I/O error)
Last Power Down Cause: Q){
Last Reset Cause: Software reset
Watchdog timeout
//****** FME ******//
Object Id : 0xEC00000
PCIe s:b:d:f : 0000:3B:00:0
Device Id : 0x09C4
Socket Id : 0x00
Ports Num : 01
Bitstream Id : 0x123000200000185
Bitstream Version : 0x30201
Pr Interface Id : 69528db6-eb31-577a-8c36-68f9faa081f6
二 Hello world demo代码
#include <stdio.h> // 引入标准输入输出库,用于 printf() 打印信息`
`#include <opae/fpga.h> // 引入 OPAE FPGA API 头文件,提供 fpgaOpen/fpgaEnumerate/MMIO 等接口`
`#include <uuid/uuid.h> // 引入 UUID 处理库,用于 uuid_parse() 解析 AFU GUID 字符串`
`/*`
` * CHECK_RES 是一个宏,用来检查 OPAE API 的返回值。`
` *`
` * OPAE API 通常返回 fpga_result 类型:`
` * FPGA_OK 表示成功`
` * 其他值表示失败`
` *`
` * 如果 res 不是 FPGA_OK,就打印错误信息,并直接 return -1 退出程序。`
` */`
`#define CHECK_RES(res, msg) \`
` if (res != FPGA_OK) { \`
` printf("Error: %s (Result code: %d)\n", msg, res); \`
` return -1; \`
` }`
`int` `main()` `{` `// 程序入口函数`
` fpga_result res;` `// 保存 OPAE API 调用的返回结果`
` fpga_token token;` `// FPGA token,表示枚举到的某个 FPGA/AFU 对象`
`// 后续 fpgaOpen() 需要用它来打开设备`
` fpga_handle handle;` `// FPGA handle,表示已经打开的 AFU 句柄`
`// 后续 MMIO 读写都通过这个 handle 完成`
`uint32_t num_matches =` `0;` `// 保存 fpgaEnumerate() 找到的匹配对象数量`
` fpga_properties filter =` `NULL;` `// FPGA 枚举过滤器`
`// 用它指定"我要找什么类型的 FPGA 对象"`
` fpga_guid afu_guid;` `// 保存 AFU GUID 的二进制形式`
`// AFU GUID 用来精确匹配某个加速器`
`/*`
` * 创建一个 properties 对象。`
` *`
` * fpgaGetProperties(NULL, &filter) 的意思是:`
` * 不基于已有 token 获取属性,`
` * 而是创建一个空的 properties/filter 对象。`
` *`
` * 这个 filter 后面会被配置成:`
` * 只查找 FPGA_ACCELERATOR 类型`
` * 只匹配指定 AFU GUID`
` */`
` res =` `fpgaGetProperties(NULL,` `&filter);`
`CHECK_RES(res,` `"Failed to get properties");`
`/*`
` * 设置枚举对象类型为 FPGA_ACCELERATOR。`
` *`
` * 这是这段代码里非常关键的一行。`
` *`
` * FPGA_DEVICE 表示整个 FPGA 设备/FME 层;`
` * FPGA_ACCELERATOR 表示 Port 后面的 AFU 加速器。`
` *`
` * MMIO 读写通常是针对 AFU/Accelerator 做的,`
` * 所以这里必须用 FPGA_ACCELERATOR。`
` */`
` res =` `fpgaPropertiesSetObjectType(filter, FPGA_ACCELERATOR);`
`CHECK_RES(res,` `"Failed to set object type");`
`/*`
` * 把字符串形式的 AFU UUID 转成二进制 fpga_guid。`
` *`
` * 当前机器上 fpgainfo 显示的 Accelerator Id 是:`
` * 331db30c-9885-41ea-9081-f88b8f655caa`
` *`
` * 这里用这个 GUID 来确保程序打开的是正确的 AFU。`
` */`
`if` `(uuid_parse("331db30c-9885-41ea-9081-f88b8f655caa", afu_guid)` `<` `0)` `{`
`printf("Error: invalid AFU GUID\n");` `// UUID 字符串格式不合法时打印错误`
`return` `-1;` `// 退出程序`
`}`
`/*`
` * 把 AFU GUID 设置到 filter 里。`
` *`
` * 这样 fpgaEnumerate() 只会返回 GUID 匹配的 Accelerator。`
` */`
` res =` `fpgaPropertiesSetGUID(filter, afu_guid);`
`CHECK_RES(res,` `"Failed to set AFU GUID");`
`/*`
` * 枚举 FPGA 对象。`
` *`
` * 参数解释:`
` * &filter :过滤条件数组,这里只有一个 filter`
` * 1 :filter 数量`
` * &token :输出参数,用来保存找到的第一个匹配 token`
` * 1 :最多只取 1 个匹配结果`
` * &num_matches :实际找到的匹配数量`
` *`
` * 成功后:`
` * token 会指向匹配到的 AFU`
` * num_matches 会表示找到几个匹配项`
` */`
` res =` `fpgaEnumerate(&filter,` `1,` `&token,` `1,` `&num_matches);`
`CHECK_RES(res,` `"Failed to enumerate");`
`/*`
` * 枚举完成后,filter 已经不再需要,释放它。`
` *`
` * 注意:`
` * token 还要继续用,所以这里只释放 filter。`
` */`
`fpgaDestroyProperties(&filter);`
`/*`
` * 如果没有找到匹配的 AFU,说明:`
` * 1. FPGA 上没有加载这个 AFU;`
` * 2. GUID 写错;`
` * 3. 当前环境看不到这个 accelerator;`
` * 4. 驱动/设备节点有问题。`
` */`
`if` `(num_matches ==` `0)` `{`
`printf("No matching FPGA accelerator found!\n");`
`return` `-1;`
`}`
`/*`
` * 打开枚举到的 AFU。`
` *`
` * fpgaOpen() 成功后会得到 handle。`
` * 后面的 MMIO map/read/write 都基于这个 handle。`
` *`
` * 第三个参数 0 表示默认 open flags。`
` */`
` res =` `fpgaOpen(token,` `&handle,` `0);`
`CHECK_RES(res,` `"Failed to open FPGA accelerator");`
`/*`
` * 映射 MMIO region 0。`
` *`
` * MMIO = Memory-Mapped I/O。`
` * Host CPU 可以通过 MMIO 地址读写 FPGA 暴露出来的寄存器。`
` *`
` * 参数解释:`
` * handle :已经打开的 AFU handle`
` * 0 :MMIO region index,这里使用 region 0`
` * NULL :不返回直接 mmap 出来的指针`
` *`
` * 这里虽然传 NULL,但后面仍然可以使用:`
` * fpgaWriteMMIO32()`
` * fpgaReadMMIO32()`
` *`
` * 因为这些 API 内部会通过 handle 和 region index 完成访问。`
` */`
` res =` `fpgaMapMMIO(handle,` `0,` `NULL);`
`CHECK_RES(res,` `"Failed to prepare MMIO region 0");`
`/*`
` * 设置要访问的 AFU 寄存器偏移。`
` *`
` * 这里的 0x100 不是 CPU 虚拟地址,`
` * 而是 AFU MMIO region 0 内部的寄存器偏移。`
` *`
` * 是否有效取决于你的 AFU register map。`
` */`
`uint32_t offset =` `0x100;`
`/*`
` * 准备写入 FPGA 寄存器的数据。`
` */`
`uint32_t data_to_fpga =` `0x12345678;`
`/*`
` * 打印即将写入的数据。`
` */`
`printf("Host CPU is writing data to FPGA register: 0x%X\n", data_to_fpga);`
`/*`
` * 向 AFU 的 MMIO region 0 写一个 32-bit 数据。`
` *`
` * 参数解释:`
` * handle :AFU handle`
` * 0 :MMIO region index`
` * offset :寄存器偏移,这里是 0x100`
` * data_to_fpga :要写入的数据`
` *`
` * 实际含义是:`
` * 向 FPGA AFU 的 region 0 + 0x100 位置写入 0x12345678。`
` */`
` res =` `fpgaWriteMMIO32(handle,` `0, offset, data_to_fpga);`
`CHECK_RES(res,` `"Failed to write MMIO");`
`/*`
` * 准备一个变量,用来保存从 FPGA 读回来的 32-bit 数据。`
` */`
`uint32_t data_from_fpga =` `0;`
`/*`
` * 从 AFU 的 MMIO region 0 读取一个 32-bit 数据。`
` *`
` * 注意:`
` * 这里读的地址是 offset + 0x10,`
` * 也就是 0x100 + 0x10 = 0x110。`
` *`
` * 也就是说:`
` * 你写的是 0x100,`
` * 读的是 0x110。`
` *`
` * 因此读回来的值不一定等于刚才写入的 0x12345678。`
` *`
` * 这是否正确,取决于你的 AFU register map。`
` * 例如有些 AFU 设计是:`
` * 0x100 是输入寄存器`
` * 0x110 是输出寄存器`
` */`
` res =` `fpgaReadMMIO32(handle,` `0, offset,` `&data_from_fpga);`
`CHECK_RES(res,` `"Failed to read MMIO");`
`/*`
` * 打印从 FPGA 读回来的数据。`
` */`
`printf("Host CPU read data from FPGA register: 0x%X\n", data_from_fpga);`
`/*`
` * 如果程序走到这里,说明:`
` * AFU 枚举成功;`
` * AFU 打开成功;`
` * MMIO map 成功;`
` * MMIO write 成功;`
` * MMIO read 成功。`
` */`
`printf("Hello World! Communication successful!\n");`
`/*`
` * 解除 MMIO region 0 的映射。`
` *`
` * 这是 fpgaMapMMIO() 的配套释放操作。`
` */`
`fpgaUnmapMMIO(handle,` `0);`
`/*`
` * 关闭 AFU handle。`
` *`
` * 这是 fpgaOpen() 的配套释放操作。`
` */`
`fpgaClose(handle);`
`/*`
` * 销毁 token。`
` *`
` * 这是 fpgaEnumerate() 返回 token 后的配套释放操作。`
` */`
`fpgaDestroyToken(&token);`
`/*`
` * 返回 0,表示程序正常结束。`
` */`
`return` `0;`
`}`
`
创建 filter`
`→ 指定查找 FPGA_ACCELERATOR`
`→ 指定 AFU GUID`
`→ 枚举 AFU`
`→ 打开 AFU`
`→ map MMIO region 0`
`→ 写寄存器 0x100`
`→ 读寄存器 0x100`
`→ 释放资源`
`