创建数据流 graph(包含内核) - 2022.1 简体中文

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

Document ID
UG1076
Release Date
2022-05-25
Version
2022.1 简体中文
以下进程描述了如何在 C++ 中构造数据流 graph。
  1. 在独立头文件(例如,project.h)内定义您的应用 graph 类。首先,添加自适应数据流(Adaptive Data Flow,ADF)头文件 (adf.h),并包含内核函数原型。ADF 库包含用于在 AI 引擎上定义和执行 graph 所需的所有构造。
    #include <adf.h>
    #include "kernels.h"
  2. 使用 adf 名称空间内定义的对象来定义 graph 类。所有用户 graph 均衍生自 graph 类。
    include <adf.h>
    #include "kernels.h"
    
    using namespace adf;
    
    class simpleGraph : public graph {
    private:
      kernel first;
      kernel second;
    };

    这是声明 2 个内核(firstsecond)的 graph 类定义的开头。

  3. 给 graph 添加顶层输入/输出对象 input_pliooutput_plio
    #include <adf.h>
    #include "kernels.h"
    
    using namespace adf;
    
    class simpleGraph : public graph {
    private:
      kernel first;
      kernel second;
    public:
      input_plio in;
      output_plio out;
    
    };
  4. 使用 kernel::create 函数利用 C 语言函数 simple 的功能来例化 firstsecond C++ 内核对象。
    #include <adf.h>
    #include "kernels.h"
    
    using namespace adf;
    
    class simpleGraph : public graph {
    private:
      kernel first;
      kernel second;
    public:
      input_plio in;
      output_plio out;
      simpleGraph() {
          first = kernel::create(simple);
          second = kernel::create(simple);
      }
    };
  5. 使用指定的 PLIO 宽度和输入/输出文件配置输入/输出对象,并添加连接信息,这等同于数据流 graph 中的信号线。在此处描述中,输入/输出对象供索引引用。在输入端口阵列 (in) 中,对 simple 函数中的第一个输入窗口或串流实参分配索引 0。后续输入实参按升序取连续索引值。在输出端口阵列 (out) 中,对 simple 函数中的第一个输出窗口或串流实参分配索引 0。后续输出实参按升序取连续索引值。
    #include <adf.h>
    #include "kernels.h"
    
    using namespace adf;
    
    class simpleGraph : public graph {
    private:
      kernel first;
      kernel second;
    public:
      input_plio in;
      output_plio out;
    
      simpleGraph() {
        first = kernel::create(simple);
        second = kernel::create(simple);
    
        in = input_plio::create(plio_32_bits, "data/input.txt");
        out = output_plio::create(plio_32_bits, "data/output.txt");
        connect< window<128> > net0 (in.out[0], first.in[0]);
        connect< window<128> > net1 (first.out[0], second.in[0]);
        connect< window<128> > net2 (second.out[0], out.in[0]);
      }
    };

    此图显示了先前 graph 代码中指定的 graph 连接。在 Vitis 分析器中打开编译结果时,即可查看 graph 连接情况。如需了解更多信息,请参阅 在 Vitis 分析器中查看编译结果。如前图所示,来自顶层的输入端口连接到第一个内核的输入端口中,第一个内核的输出端口连接到第二个内核的输入端口,第二个内核的输出端口连接到对顶层公开的输出。在缓冲器中收集外部来源的 128 字节数据(32 个复数 (complex) 样本)时,就会执行第一个内核。在 net0 连接处,将此指定为窗口参数。同样,第二个内核会在其中输入窗口包含有效数据时执行,此处有效数据是作为第一个内核的输出生成的,通过 net1 连接来表述。最后,第二个内核的输出会作为 net2 连接来连接到顶层输出端口,并指定第二个内核会在终止时生成 128 字节的数据。

  6. 为每个内核设置源文件和拼块用法。源文件 kernel.cc 包含内核的 first 源代码和内核的 second 源代码。然后,它还包含函数运行时相比于周期预算的比率(称为运行时比率),且必须介于 01 之间。周期预算是每个函数耗用来自其输入的数据(处理速率受限的输入数据串流时)或者在其输出上生成数据块(处理速率受限的输出数据串流时)所耗用的指令周期数。更改块大小可能会影响此周期预算。
    #include <adf.h>
    #include "kernels.h"
    
    using namespace adf;
    
    class simpleGraph : public graph {
    private:
      kernel first;
      kernel second;
    public:
      input_plio in;
      output_plio out;
    
      simpleGraph(){
    
        first = kernel::create(simple);
        second = kernel::create(simple);
    
        in = input_plio::create(plio_32_bits, "data/input.txt");
        out = output_plio::create(plio_32_bits, "data/output.txt");
        connect< window<128> > net0 (in.out[0], first.in[0]);
        connect< window<128> > net1 (first.out[0], second.in[0]);
        connect< window<128> > net2 (second.out[0], out.in[0]);
    
        source(first) = "kernels.cc";
        source(second) = "kernels.cc";
    
        runtime<ratio>(first) = 0.1;
        runtime<ratio>(second) = 0.1;
    
      }
    };
    注释: 如需了解更多信息,请参阅 运行时比率
  7. 定义包含您的 graph 类实例的顶层应用文件(例如,project.cpp)。
    #include "project.h"
    
    simpleGraph mygraph;
    
    int main(void) {
      adf::return_code ret;
      mygraph.init();
      ret=mygraph.run(<number_of_iterations>);
      if(ret!=adf::ok){
        printf("Run failed\n");
        return ret;
      }
      ret=mygraph.end();
      if(ret!=adf::ok){
        printf("End failed\n");
        return ret;
      }
      return 0;
    }
重要: 默认情况下,mygraph.run() 选项会指定永续运行的 graph。AI 引擎编译器会生成代码,以通过永续 while 循环来执行数据流 graph。要将 graph 的执行限制于调试和测试目的,请在 graph 代码中指定 mygraph.run(<number_of_iterations>)。对于迭代数量,可指定 1 或更大值。

ADF API 包含返回枚举类型 return_code,用于显示 API 运行状态。

main 程序是 graph 的驱动程序。它用于加载、执行和终止 graph。如需了解更多详情,请参阅 运行时 graph 控制 API

注释: 内核代码的编写方式必须确保将两个内核分配到相同核时,不会发生名称冲突。