赛灵思 XRT API 提供多进程支持,用于控制 AI 引擎阵列和 graph。它支持 AI 引擎阵列和 graph 上的 3 种操作模式。
- Exclusive Mode(专享模式)
- 全权访问 AI 引擎阵列或 graph。没有任何其它进程可对其进行访问。
- Primary Mode(基准模式)
- 全权访问 AI 引擎阵列或 graph。其它进程可对 AI 引擎阵列或 graph 进行非破坏性访问。
- Shared Mode(共享模式)
- 仅限对 AI 引擎阵列或 graph 进行非破坏性访问。
赛灵思 XRT 提供了下列 API,用于在 3 种模式下打开 AI 引擎阵列。
-
xrtAIEDeviceOpenExclusive
(专享模式) -
xrtAIEDeviceOpen
(基准模式) -
xrtAIEDeviceOpenShared
(共享模式)
注释: 如果应用不调用
xrtAIEDeviceOpen*
以获取器件句柄,那么默认情况下它将尝试获取基准上下文,同时尝试通过 XRT API 来访问 AI 引擎阵列。赛灵思 XRT 还提供了以下 API,以便在打开 AI 引擎阵列后打开 graph。
-
xrtGraphOpenExclusive
(专享模式) -
xrtGraphOpen
(基准模式) -
xrtGraphOpenShared
(共享模式)
AI 引擎阵列上的下列 API 在专享模式和基准模式下允许执行加载、复位和 GMIO 数据传输。
-
xrtDeviceLoadXclbin
-
xrtAIEResetArray
-
xrtAIESyncBO
-
xrtDeviceClose
AI 引擎阵列上的下列 API 在共享模式下允许执行非破坏性操作。
-
xrtDeviceLoadXclbin
:从 xclbin 读取元数据。 -
xrtDeviceClose
AI 引擎 graph 上的专享模式和基准模式下允许的 API 包括:
-
xrtGraphRun
-
xrtGraphWait
-
xrtGraphEnd
-
xrtGraphUpdateRTP
-
xrtGraphReadRTP
-
xrtGraphTimeStamp
-
xrtGraphClose
AI 引擎阵列和 graph 上的共享模式下允许的 API 包括:
-
xrtGraphUpdateRTP
(异步模式) -
xrtGraphReadRTP
-
xrtGraphTimeStamp
-
xrtGraphClose
以下规则适用于 AI 引擎阵列多进程支持。
- 在专享模式下,仅有一个进程能打开 AI 引擎阵列。如果 AI 引擎阵列在专享模式下打开,那么无论是在同一进程内还是在其它进程内,都无法在任何其它模式下再次打开。
- 在基准模式下,仅有一个进程能打开 AI 引擎阵列。如果 AI 引擎阵列在基准模式下打开,则在专享模式下或基准模式下无法再次打开,但在共享模式下可以将其再次打开。
以下规则适用于 AI 引擎 graph 多进程支持。
- 在任一进程内,graph 不应多次打开。
- 如果某个 AI 引擎 graph 已在专享模式下打开,则无法在任何模式下再次打开。
- 在共享模式下,可有多个进程打开 graph,但在基准模式下,仅允许在单个进程内打开此 graph。
必须先关闭 graph,然后才能关闭阵列。关闭所有已打开的 AI 引擎阵列和 graph 后,即可再次打开这些 AI 引擎阵列和 graph,并且前述规则再次适用。
注释: 当某个 graph 或 AI 引擎阵列由
xrtGraphClose
或 xrtDeviceClose
关闭后,仅关闭已打开的上下文。它并不会清除 AI 引擎阵列中的数据存储器、程序存储器、FIFO 和 DMA 状态。下图汇总了专享模式下的多进程支持(黑色:受支持,红色:不受支持,“*”:任意模式)。
图 1. 专享模式下的多进程支持
下图汇总了基准模式和共享模式下的多进程支持(黑色:受支持,红色:不受支持)。
图 2. 基准模式和共享模式下的多进程支持
建议多线程使用与多进程相同的模型。但由于 AI 引擎器件句柄和 graph 句柄可在线程之间共享,因此在线程之间使用相同的器件句柄或 graph 句柄是合规的。主机应用负责在线程之间同步 AI 引擎阵列状态和 graph 状态,当多线程是 AI 引擎阵列或 graph 的专享或基准所有者时,尤其如此。
以下提供了使用多进程的样本代码。
#include <stdlib.h>
#include <fstream>
#include <iostream>
#include <unistd.h>
#include <sys/wait.h>
#include "adf/adf_api/XRTConfig.h"
#include "experimental/xrt_kernel.h"
#include "graph.cpp"
//8192 matches 32 iterations of graph::run
#define OUTPUT_SIZE 8192
int value1[16] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
int value2[16] = {-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16};
using namespace adf;
int run(int argc, char* argv[],int id){
std::cout<<"Child process "<<id<<" start"<<std::endl;
//TARGET_DEVICE macro needs to be passed from gcc command line
if(argc != 2) {
std::cout << "Usage: " << argv[0] <<" <xclbin>" << std::endl;
return EXIT_FAILURE;
}
char* xclbinFilename = argv[1];
std::string graph_name=std::string("gr[")+std::to_string(id)+"]";
std::string rtp_inout_name=std::string("gr[")+std::to_string(id)+std::string("].k.inout[0]");
int ret;
int value_readback[16]={0};
if(fork()==0){//child child process
xrtDeviceHandle dhdl2=xrtAIEDeviceOpenShared(0);
ret=xrtDeviceLoadXclbinFile(dhdl2,xclbinFilename);
if(ret){
printf("child child Xclbin Load fail\n");
}
if(!dhdl2){
std::cout<<"child child device open error"<<std::endl;
return 1;
}else{
std::cout<<"child child device open pass"<<std::endl;
}
xuid_t uuid2;
ret=xrtDeviceGetXclbinUUID(dhdl2, uuid2);
if(ret){
std::cout<<"child child get xclbin uuid error"<<std::endl;
return 1;
}else{
std::cout<<"child child get xclbin uuid pass"<<std::endl;
}
auto ghdl2=xrtGraphOpenShared(dhdl2,uuid2,graph_name.data());
if(!ghdl2){
std::cout<<"child child graph open error"<<std::endl;
return 1;
}else{
std::cout<<"child child graph open pass"<<std::endl;
}
ret=xrtGraphReadRTP(ghdl2, rtp_inout_name.data(), (char*)value_readback, 16*sizeof(int));
if(ret){
std::cout<<"child child Graph RTP read fail"<<std::endl;
return 1;
}
std::cout<<"Add value read back are:";
for(int i=0;i<16;i++){
std::cout<<value_readback[i]<<",\t";
}
std::cout<<std::endl;
xrtGraphClose(ghdl2);
xrtDeviceClose(dhdl2);
std::cout<<"child child process exit"<<std::endl;
exit(0);
}
xrtDeviceHandle dhdl=xrtAIEDeviceOpen(0);
ret=xrtDeviceLoadXclbinFile(dhdl,xclbinFilename);
if(ret){
printf("Xclbin Load fail\n");
}
xuid_t uuid;
xrtDeviceGetXclbinUUID(dhdl, uuid);
auto ghdl=xrtGraphOpen(dhdl,uuid,graph_name.data());
if(!ghdl){
std::cout << "Graph Open error" << std::endl;
}else{
std::cout << "Graph Open ok" <<std::endl;
}
std::string rtp_in_name=std::string("gr[")+std::to_string(id)+std::string("].k.in[1]");
ret=xrtGraphUpdateRTP(ghdl,rtp_in_name.data(),(char*)value1,16*sizeof(int));
if(ret){
std::cout<<"Graph RTP update fail"<<std::endl;;
return 1;
}
xrtGraphRun(ghdl,16);
xrtGraphWait(ghdl,0);
std::cout<<"Graph wait done"<<std::endl;
//second run
ret=xrtGraphUpdateRTP(ghdl,rtp_in_name.data(),(char*)value2,16*sizeof(int));
if(ret!=0){
std::cout<<"Graph RTP update fail"<<std::endl;
return 1;
}else{
std::cout<<"Graph RTP update pass"<<std::endl;
}
xrtGraphRun(ghdl,16);
while(wait(NULL)>0){//Wait for child child process
}
ret=xrtGraphWait(ghdl,0);
if(ret){
std::cout << "Graph wait error" << std::endl;
}else{
std::cout<<"Graph done"<<std::endl;
}
xrtGraphClose(ghdl);
xrtDeviceClose(dhdl);
std::cout<<"Child process:"<<id<<" done"<<std::endl;
return 0;
}
int main(int argc, char* argv[])
{
try {
for(int i=0;i<GRAPH_NUM;i++){
if(fork()==0){//child
auto match = run(argc, argv,i);
std::cout << "TEST child " <<i<< (match ? " FAILED" : " PASSED") << "\n";
return (match ? EXIT_FAILURE : EXIT_SUCCESS);
}else{
size_t output_size_in_bytes = OUTPUT_SIZE * sizeof(int);
//TARGET_DEVICE macro needs to be passed from gcc command line
if(argc != 2) {
std::cout << "Usage: " << argv[0] <<" <xclbin>" << std::endl;
return EXIT_FAILURE;
}
char* xclbinFilename = argv[1];
int ret;
// Open xclbin
auto device = xrt::device(0); //device index=0
auto uuid = device.load_xclbin(xclbinFilename);
// s2mm & data_generator kernel handle
std::string s2mm_kernel_name=std::string("s2mm:{s2mm_")+std::to_string(i+1)+std::string("}");
xrt::kernel s2mm = xrt::kernel(device, uuid, s2mm_kernel_name.data());
std::string data_generator_kernel_name=std::string("data_generator:{data_generator_")+std::to_string(i+1)+std::string("}");
xrt::kernel data_generator = xrt::kernel(device, uuid, data_generator_kernel_name.data());
// output memory
auto out_bo=xrt::bo(device, output_size_in_bytes,s2mm.group_id(0));
auto host_out=out_bo.map<int*>();
auto s2mm_run = s2mm(out_bo, nullptr, OUTPUT_SIZE);//1st run for s2mm has started
auto data_generator_run = data_generator(nullptr, OUTPUT_SIZE);
// wait for s2mm done
std::cout<<"Waiting s2mm to complete"<<std::endl;
auto state = s2mm_run.wait();
std::cout << "s2mm "<<" completed with status(" << state << ")"<<std::endl;
out_bo.sync(XCL_BO_SYNC_BO_FROM_DEVICE);
int match = 0;
int counter=0;
for (int i = 0; i < OUTPUT_SIZE/2/16; i++) {
for(int j=0;j<16;j++){
if(host_out[i*16+j]!=counter+value1[j]){
std::cout<<"ERROR: num="<<i*16+j<<" out="<<host_out[i*16+j]<<std::endl;
match=1;
break;
}
counter++;
}
}
for(int i=OUTPUT_SIZE/2/16;i<OUTPUT_SIZE/16;i++){
for(int j=0;j<16;j++){
if(host_out[i*16+j]!=counter+value2[j]){
std::cout<<"ERROR: num="<<i*16+j<<" out="<<host_out[i*16+j]<<std::endl;
match=1;
break;
}
counter++;
}
}
std::cout << "TEST " <<i<< (match ? " FAILED" : " PASSED") << "\n";
while(wait(NULL)>0){//Wait for all child process
}
std::cout<<"all done"<<std::endl;
return (match ? EXIT_FAILURE : EXIT_SUCCESS);
}
}
}
catch (std::exception const& e) {
std::cout << "Exception: " << e.what() << "\n";
std::cout << "FAILED TEST\n";
return 1;
}
}