包切换 graph 构造 - 2022.1 简体中文

Versal ACAP AI 引擎编程环境 用户指南 (UG1076)

Document ID
UG1076
Release Date
2022-05-25
Version
2022.1 简体中文

包切换式串流本质上是多路复用数据串流,在不同时间承载不同类型的数据。包切换串流不提供确定性时延,因为可能与其它包切换式串流之间存在资源争用。多路复用数据流动时以数据包数量为单位,其中包含 32 位包报头和可变数量的有效载荷码字数。报头码字需先于实际有效载荷数据发送,在包的最后一个码字上,TLAST 信号是必需的。为了将多路复用的数据串流表示为内核的输入或输出并分别加以区分,特此引入了两种数据类型,分别名为 input_pktstreamoutput_pktstream。如需了解有关包报头和数据类型的更多详细信息,请参阅 包串流运算

注释: 按约定,源自可编程逻辑的包以行来进行初始化,列则为 -1,-1。

为了显式控制包的多路复用和逆多路复用,在 ADF graph 库中添加了两个新的模板式节点类:pktsplit<n>pktmerge<n>。类为 pktmerge<n> 的节点实例表示 n:1 多路复用器,对应 n 条包串流生成单一包串流。类为 pktsplit<n> 的节点实例表示 1:n 逆多路复用器,对应一条包串流生成 n 条不同的包串流。在单一物理通道上,可允许的包串流最大数量为 32 (n ≤ 32)。如需了解更多详情,请参阅 自适应数据流 graph 规范引用

内核可将数据包作为数据窗口或者作为 input_pktstreamoutput_pktstream 的形式来进行接收。要将包串流连接到供 AI 引擎内核使用的数据窗口,请使用以下 graph 构造。

connect<pktstream, window<32>>

要将数据窗口从 AI 引擎内核连接到包串流,请使用以下 graph 构造。

connect<window<32>, pktstream>

要将包分割 (packet split) 连接到 AI 引擎内核,请使用以下 graph 构造。

connect<pktstream, pktstream>

要将包串流从 AI 引擎内核连接到包合并 (packet merge),请使用以下 graph 构造。

connect<pktstream, pktstream>

要连接往来 PLIO 连接的数据串流,请使用以下 graph 构造。

connect<input_port, pktstream>
connect<pktstream, output_port>
如果源和目标均为包串流,则可使用以下任一 graph 构造来指定。
connect<pktstream>

connect<pktstream,pktstream>

当内核以数据窗口形式接收数据包时,在该内核接收数据窗口之前,就会丢弃报头和 TLAST。如果内核写入输出数据窗口,则会自动插入包报头和 TLAST。

但如果内核接收数据的 input_pktstream,那么除了包数据外,内核还需要处理包报头和 TLAST。同样,如果内核发送数据的 output_pktstream,那么除了包数据外,内核还需要将包报头和 TLAST 一并插入输出串流。

注释: 如果内核以输入数据窗口形式接收包数据,您需要确保每个包的数据长度与窗口大小相匹配。

在以下示例中演示了这些概念。

class ExplicitPacketSwitching: public adf::graph {
  private: 
    adf:: kernel core[4]; 
    adf:: pktsplit<4> sp; 
    adf:: pktmerge<4> mg;
  public: 
    adf::port in; 
    adf::port out; 
  mygraph() { 
    core[0] = adf::kernel::create(aie_core1); 
    core[1] = adf::kernel::create(aie_core2); 
    core[2] = adf::kernel::create(aie_core3); 
    core[3] = adf::kernel::create(aie_core4); 
  
    adf::source(core[0]) = "aie_core1.cpp"; 
    adf::source(core[1]) = "aie_core2.cpp"; 
    adf::source(core[2]) = "aie_core3.cpp"; 
    adf::source(core[3]) = "aie_core4.cpp"; 
    sp = adf::pktsplit<4>::create(); 
    mg = adf::pktmerge<4>::create(); 
    for(int i=0;i<4;i++){
       adf::runtime<ratio>(core[i]) = 0.9;
       adf::connect<adf::pktstream, adf::window<32> > (sp.out[i], core[i].in[0]);
       adf::connect<adf::window<32>, adf::pktstream > (core[i].out[0], mg.in[i]);
    }
    adf::connect<adf::pktstream> (in, sp.in[0]); 
    adf::connect<adf::pktstream> (mg.out[0], out); 
  }
};

graph 具有一个输入 PLIO 端口和一个输出 PLIO 端口。源自 PL 的输入包串流按 4 个方向分割,并输入 4 个不同 AI 引擎内核。源自 4 个 AI 引擎内核的输出串流则会合并到一条包串流中并输出至 PL。Vitis 分析器的代码的Graph视图如下所示。

图 1. Graph 视图

以下提供了一个内核代码示例。

const uint32 pktType=0;
void aie_core1(input_pktstream *in,output_pktstream *out){
  readincr(in);//read header and discard
  uint32 ID=getPacketid(out,0);//for output pktstream
  writeHeader(out,pktType,ID); //Generate header for output

  bool tlast;
  for(int i=0;i<8;i++){
    int32 tmp=readincr(in,tlast);
    tmp+=1;
    writeincr(out,tmp,i==7);//TLAST=1 for last word
  }
}
注释: input_pktstream 作为整数输入来读取。

以下示例演示了内核代码接受和传输浮点数据类型的过程。

const uint32 pktType=0;
void aie_core1_float(input_pktstream *in,output_pktstream *out){
  readincr(in);//read header and discard
  uint32 ID=getPacketid(out,0);//for output pktstream
  writeHeader(out,pktType,ID); //Generate header for output

  bool tlast;
  for(int i=0;i<8;i++){
    int32 tmp=readincr(in,tlast);//read data as integer type
    float tmp_f=reinterpret_cast<float&>(tmp);//Reinterpret memory as float
    writeincr(out,tmp_f,i==7);//TLAST=1 for last word
  }
}