跳转至

MI IPU CASCADE API


REVISION HISTORY

Revision No.
Description
Date
1.0 Initial release. 01/07/2025
1.1
  • Add new APIs 'MI_IPU_PCIECasWrapper_QueryRemoteInputBufInfo', 'MI_IPU_PCIECasWrapper_SetRemoteInputBuf', 'MI_IPU_PCIECasWrapper_GetRemoteInputBuf', 'MI_IPU_PCIECasWrapper_RunService' and 'MI_IPU_PCIECasWrapper_StopService'.
  • Add new struct 'MI_IPU_PCIECasServiceConfig_t', 'MI_IPU_PCIECasTensorDesc_t', 'MI_IPU_PCIECasInputDesc_t', 'MI_IPU_PCIECasTransferLinklist_t' and 'MI_IPU_PCIECasTransferDesc_t'.
  • Add new error code 'E_MI_IPU_PCIE_CAS_ERR_INVALID_SUBNET_ID', 'E_MI_IPU_PCIE_CAS_ERR_MODEL_CORRUPTED', 'E_MI_IPU_PCIE_CAS_ERR_MAP' and 'E_MI_IPU_PCIE_CAS_ERR_DEVICE_NOT_READY'.
  • Add 'ipu_service' demo code.
  • 02/28/2026

    1. 概述


    1.1. 模块说明

    IPU Cascade API 面向多IPU并行处理同一LLM大模型的应用场景,主要功能是在板端加速推演LLM大模型。IPU Cascade API 与通用 MI IPU 接口风格保持一致,便于在现有工程中扩展 PCIe 级联能力。

    1.2. 基本结构

    • RC 端应用:负责读取级联离线模型、创建设备/通道、准备输入输出 Tensor,控制EP端服务程序执行模型推理。
    • EP 端服务:运行在基于PCIE总线外接的IPU板卡,负责响应RC指令,并调用IPU Cascade API完成模型推理。

    1.3. 功能介绍

    IPU Cascade API支持以下功能:

    1. 支持本端、远端IPU板卡实现级联模型的协同推理

    2. 支持控制远端IPU板卡实现非级联模型的推理

    3. 支持多通道

    4. 支持指定IPU推理任务的优先级

    5. 支持单次推理单张输入和单次推理多张输入

    1.4. 应用场景

    • 大模型级联:把级联大模型拆分为多个子网并分布到多颗 IPU,提升吞吐与容量。
    • 远端推理加速:模型完全运行在 EP 上,RC 负责数据调度。

    1.5. 工作原理

    用户在调用IPU Cascade模块进行模型推演前,首先需要使用IPU SDK工具链将原始的大语言模型转换为硬件所支持的离线级联模型文件,然后通过调用IPU Cascade API在本地板端加载离线级联模型,控制远端板卡对离线级联模型推理进行加速。

    1.6. 接口调用流程

    1. MI_IPU_PCIECasWrapper_GetOfflineStaticInfo
    2. MI_IPU_PCIECasWrapper_CreateDevice
    3. MI_IPU_PCIECasWrapper_CreateCHN
    4. MI_IPU_GetInOutTensorDesc
    5. MI_IPU_PCIECasWrapper_SubNetInvoke
    6. MI_IPU_PCIECasWrapper_DestroyCHN
    7. MI_IPU_PCIECasWrapper_DestroyDevice

    1.7. 示例

    SDK 提供 sdk/release_feature/source/ipu/ipu_client/(RC)与 sdk/release_feature/source/ipu/ipu_service/(EP)示例,可直接参考其如何组织参数并调用公开 API。

    1. ipu_client

      #include <fcntl.h>
      #include <string.h>
      #include <sys/ioctl.h>
      #include <sys/types.h>
      #include <sys/mman.h>
      #include <unistd.h>
      #include <stdio.h>
      #include <errno.h>
      #include <pthread.h>
      #include <getopt.h>
      #include <stdbool.h>
      #include <stdlib.h>
      
      #include "mi_sys.h"
      #include "mi_ipu.h"
      #include "mi_ipu_pcie_cascade_datatype.h"
      #include "mi_ipu_pcie_cascade.h"
      #include "ipu_client.h"
      
      static void _showUsage(void)
      {
          printf("Usage: ./prog_ipu_ipu_client [-m] [-i][...]\n");
          printf(" -m,       model file\n");
          printf(" -i,       input tensor file\n");
          printf(" -l,       loop count, if not set, only invoke once\n");
          printf(" -n,       nbatch\n");
          printf(" -j,       subnet id\n");
          printf(" -a,       ipu affinity, multi core use\n");
          printf(" -p,       invoke priority, -10~39\n");
      }
      
      static MI_S32 IPU_Client_GetInputTensorData(char *InputPath, void **pInput, int *filelen_in)
      {
          int fd1;
          void *pmem = NULL;
          off_t filelen1;
      
          if (!InputPath){
              return -1;
          }
      
          fd1 = open(InputPath, O_RDONLY, 0644);
          if (fd1 < 0)
              return -1;
          filelen1 = lseek(fd1, 0L, SEEK_END);
          if (filelen1 <= 0){
              return -1;
          }
          lseek(fd1, 0L, SEEK_SET);
          pmem = mmap(NULL, filelen1, PROT_READ, MAP_SHARED, fd1, 0);
          close(fd1);
          if (pmem == MAP_FAILED)
              return -1;
          *pInput = pmem;
          *filelen_in = filelen1;
      
          return MI_SUCCESS;
      }
      
      static MI_S32 IPU_Client_InsertInputNbatch(void *pInput, MI_IPU_SubNet_InputOutputDesc_t *pDesc,
                                                          MI_IPU_BatchInvokeParam_t *pInvokeParam)
      {
          int cnt, batch_cnt;
          int total_cnt = 0;
          char *_pInput = (char *)pInput;
      
          for (batch_cnt = 0; batch_cnt < pInvokeParam->u32BatchN; batch_cnt++) {
              for (cnt = 0; cnt < pDesc->u32InputTensorCount; cnt++) {
                  memcpy(pInvokeParam->astArrayTensors[total_cnt].ptTensorData[0], (void *)_pInput, pDesc->astMI_InputTensorDescs[cnt].s32AlignedBufSize);
                  MI_SYS_FlushInvCache(pInvokeParam->astArrayTensors[total_cnt].ptTensorData[0], pDesc->astMI_InputTensorDescs[cnt].s32AlignedBufSize);
                  _pInput += pDesc->astMI_InputTensorDescs[cnt].s32AlignedBufSize;
                  total_cnt++;
              }
          }
          return MI_SUCCESS;
      }
      
      MI_S32 IPU_Client_AllocTensors(MI_IPU_SubNet_InputOutputDesc_t *pDesc,
                                          IpuClientTestParam_t *pstTestParam,
                                          IpuClientRunbufInfo_t *pstIpuRuntimeInfo,
                                          IpuClientTensorAddrInfo_t *pstTensorAddrInfo,
                                          EN_IpuBufType eBufType, MI_U32 u32DeviatonBytes)
      {
          int i, j, s32Ret;
          MI_U32 u32RequireBufSize = 0, u32Nbatch;
          MI_U32 u32InCnt;
      
          MI_PHY *pu64TensorPA;
          void **ppTensorVA;
      
          MI_PHY u64PABase;
          void *pVABase;
      
          u32Nbatch = pstTestParam->u32Nbatch;
          if (!u32Nbatch) {
              u32Nbatch = 1;
          }
      
          switch (eBufType) {
              case EN_INPUT_BUF:
                  for (j = 0; j < u32Nbatch; j++) {
                      for (i = 0; i < pDesc->u32InputTensorCount; i++) {
                          u32RequireBufSize += pDesc->astMI_InputTensorDescs[i].s32AlignedBufSize;
                          u32RequireBufSize += ALIGN_UP(u32DeviatonBytes, ALIGN_SIZE);
                          u32RequireBufSize += u32DeviatonBytes;
                      }
                  }
      
                  pu64TensorPA = &pstIpuRuntimeInfo->u64InTensorPA;
                  ppTensorVA = &pstIpuRuntimeInfo->pInTensorVA;
                  pstIpuRuntimeInfo->u32InTensorSize = u32RequireBufSize;
                  break;
              case EN_OUTPUT_BUF:
                  for (j = 0; j < u32Nbatch; j++) {
                      for (i = 0; i < pDesc->u32OutputTensorCount; i++) {
                          u32RequireBufSize += pDesc->astMI_OutputTensorDescs[i].s32AlignedBufSize;
                          u32RequireBufSize += ALIGN_UP(u32DeviatonBytes, ALIGN_SIZE);
                          u32RequireBufSize += u32DeviatonBytes;
                      }
                  }
      
                  pu64TensorPA = &pstIpuRuntimeInfo->u64OutTensorPA;
                  ppTensorVA = &pstIpuRuntimeInfo->pOutTensorVA;
                  pstIpuRuntimeInfo->u32OutTensorSize = u32RequireBufSize;
                  break;
              default:
                  u32RequireBufSize = 0;
                  printf("error: don't support tensor type: %d\n", eBufType);
                  return -1;
          }
      
          s32Ret = MI_SYS_MMA_Alloc(0, NULL, u32RequireBufSize, pu64TensorPA);
          if (s32Ret != MI_SUCCESS) {
              printf("fail to allocate %s buffer\n", eBufType==EN_INPUT_BUF?"input":"output");
              return -1;
          }
          s32Ret = MI_SYS_Mmap(*pu64TensorPA, u32RequireBufSize, ppTensorVA, TRUE);
          if (s32Ret != MI_SUCCESS) {
              printf("Error: fail to map %s address, error=%d\n", eBufType==EN_INPUT_BUF?"input":"output", s32Ret);
              return -1;
          }
      
          if (u32DeviatonBytes != 0) {
              u64PABase = ALIGN_UP(*pu64TensorPA, ALIGN_SIZE) + u32DeviatonBytes;
          } else {
              u64PABase = *pu64TensorPA;
          }
          pVABase = *ppTensorVA + (u64PABase - *pu64TensorPA);
      
          if (eBufType == EN_INPUT_BUF) {
              pstTensorAddrInfo->u32InCnt = pDesc->u32InputTensorCount * u32Nbatch;
              for (i = 0; i < u32Nbatch; i++) {
                  for (j = 0; j < pDesc->u32InputTensorCount; j++) {
                      pstTensorAddrInfo->astArrayTensors[i*pDesc->u32InputTensorCount+j].phyTensorAddr[0] = u64PABase;
                      pstTensorAddrInfo->astArrayTensors[i*pDesc->u32InputTensorCount+j].ptTensorData[0] = pVABase;
      
                      u64PABase += pDesc->astMI_InputTensorDescs[j].s32AlignedBufSize;
                      if (u32DeviatonBytes != 0) {
                          u64PABase = ALIGN_UP(u64PABase, ALIGN_SIZE) + u32DeviatonBytes;
                      }
                      pVABase = *ppTensorVA + (u64PABase - *pu64TensorPA);
                  }
              }
          } else {
              u32InCnt = pDesc->u32InputTensorCount * u32Nbatch;
              pstTensorAddrInfo->u32OutCnt = pDesc->u32OutputTensorCount * u32Nbatch;
              for (i = 0; i < u32Nbatch; i++) {
                  for (j = 0; j < pDesc->u32OutputTensorCount; j++) {
                      pstTensorAddrInfo->astArrayTensors[u32InCnt+i*pDesc->u32OutputTensorCount+j].phyTensorAddr[0] = u64PABase;
                      pstTensorAddrInfo->astArrayTensors[u32InCnt+i*pDesc->u32OutputTensorCount+j].ptTensorData[0] = pVABase;
                      u64PABase += pDesc->astMI_OutputTensorDescs[j].s32AlignedBufSize;
                      if (u32DeviatonBytes != 0) {
                          u64PABase = ALIGN_UP(u64PABase, ALIGN_SIZE) + u32DeviatonBytes;
                      }
                      pVABase = *ppTensorVA + (u64PABase - *pu64TensorPA);
                  }
              }
          }
          return 0;
      }
      
      void IPU_Client_FreeTensors(IpuClientRunbufInfo_t *pstIpuRuntimeInfo, EN_IpuBufType eBufType)
      {
          if (eBufType == EN_INPUT_BUF) {
              MI_SYS_Munmap(pstIpuRuntimeInfo->pInTensorVA, pstIpuRuntimeInfo->u32InTensorSize);
              MI_SYS_MMA_Free(0, pstIpuRuntimeInfo->u64InTensorPA);
          } else {
              MI_SYS_Munmap(pstIpuRuntimeInfo->pOutTensorVA, pstIpuRuntimeInfo->u32OutTensorSize);
              MI_SYS_MMA_Free(0, pstIpuRuntimeInfo->u64OutTensorPA);
          }
      }
      
      int main(int argc, char * argv[])
      {
          int opt;
          int count = 0;
          double linux_time = 0, total_linux_time = 0, total_ipu_time = 0;
          MI_U64 total_bw = 0, total_bw_rd = 0, total_bw_wr = 0;
          int  file_len_in = 0;
          void *pInput = NULL;
          MI_U32 u32ChnId;
          MI_S32 s32Ret;
          int cnt, batch_cnt;
          int total_cnt;
      
          struct timespec invoke_start, invoke_end;
          IpuClientTestParam_t stTestParam;
      
          MI_IPU_PCIECasDevAttr_t stCASDevAttr;
          MI_IPU_PCIECasOfflineModelStaticInfo_t stCASOfflineModelInfo;
          MI_IPU_ModelDesc_t *pstModelDesc;
          MI_IPU_SubNet_InputOutputDesc_t *pstTensorDesc;
      
          MI_IPU_SubNetBatchInvokeParam_t stSubNetInvokeParam;
          MI_IPU_BatchInvokeParam_t stInvokParam;
          MI_IPU_PCIECasSubNetBatchInvokeParam_t stIPUCASInvokeParam;
          MI_IPU_RuntimeInfo_t  stRuntimeInfo;
          IpuClientTensorAddrInfo_t stTensorAddrInfo;
          IpuClientRunbufInfo_t stIpuRunBufInfo;
          MI_IPU_DevAttr_t stIPUDevAttr;
          MI_IPUChnAttr_t stIPUChnAttr;
      
          memset(&stTestParam, 0, sizeof(stTestParam));
          while (true) {
              int option_index = 0;
              static struct option long_options[] =
              {
                  {"model",           required_argument, 0,  'm' },
                  {"input",           required_argument, 0,  'i' },
                  {"loop_count",      required_argument, 0,  'l' },
                  {"nbatch",          required_argument, 0,  'n' },
                  {"sub_net_id",      required_argument, 0,  'j' },
                  {"affinity",        required_argument, 0,  'a' },
                  {"priority",        required_argument, 0,  'p' }
              };
              opt = getopt_long(argc, argv, "m:i:o:l:n:j:a:p", long_options, &option_index);
              if (opt == -1) {
                  break;
              }
              switch (opt) {
                  case 'm':
                      stTestParam.ModelPath = optarg;
                      break;
                  case 'i':
                      stTestParam.InputPath = optarg;
                      break;
                      break;
                  case 'l':
                      stTestParam.u32LoopCount = strtoul(optarg, NULL, 16);
                      break;
                  case 'n':
                      stTestParam.u32Nbatch = strtoul(optarg, NULL, 16);
                      break;
                  case 'j':
                      stTestParam.u32SubNetId = strtoul(optarg, NULL, 10);
                      stTestParam.u32SubNetInvoke = 1;
                      break;
                  case 'a':
                      stTestParam.u32IpuAffinity = strtoul(optarg, NULL, 10);
                      break;
                  case 'p':
                      stTestParam.s32TaskPrio = strtoul(optarg, NULL, 10);
                      break;
                  default:
                      _showUsage();
                      return 0;
              }
          }
          if (argc <= 2 || !stTestParam.ModelPath || !stTestParam.InputPath) {
              _showUsage();
              goto SYS_EXIT;
          }
      
          if (stTestParam.u32LoopCount == 0)
              stTestParam.u32LoopCount = 1;
      
          if (stTestParam.u32Nbatch == 0)
              stTestParam.u32Nbatch = 1;
      
          if (stTestParam.u32SubNetInvoke == 0)
              stTestParam.u32SubNetInvoke = 1;
      
          MI_SYS_Init(0);
      
          s32Ret = MI_IPU_PCIECasWrapper_GetOfflineStaticInfo(NULL, stTestParam.ModelPath, &stCASOfflineModelInfo);
          if (s32Ret != MI_SUCCESS)
          {
              printf("fail to get %s static info\n", stTestParam.ModelPath);
              goto SYS_EXIT;
          }
      
          memset(&stIPUDevAttr, 0, sizeof(stIPUDevAttr));
          memset(&stCASDevAttr, 0, sizeof(stCASDevAttr));
          stIPUDevAttr.u32CoreMask = stTestParam.u32IpuAffinity;
          stIPUDevAttr.u32MaxVariableBufSize = stCASOfflineModelInfo.au32VariableBufferSize[E_MI_IPU_PCIE_CAS_LOCAL_VARIABLE_BUFFER_IDX];
      
          memcpy(&stCASDevAttr.stIPUDevAttr, &stIPUDevAttr, sizeof(stIPUDevAttr));
          memcpy(stCASDevAttr.au32MaxVariableBufSize, stCASOfflineModelInfo.au32VariableBufferSize, sizeof(MI_U32)*stCASOfflineModelInfo.u32SocNum);
          stCASDevAttr.u32SocNum = stCASOfflineModelInfo.u32SocNum;
      
          s32Ret = MI_IPU_PCIECasWrapper_CreateDevice(&stCASDevAttr, NULL, NULL, E_MI_IPU_PCIE_CAS_WORK_MODE_RC);
          if (s32Ret != MI_SUCCESS)
          {
              printf("Fail to Create Device, ret=%d\n", s32Ret);
              goto SYS_EXIT;
          }
      
          memset(&stIPUChnAttr, 0, sizeof(stIPUChnAttr));
          stIPUChnAttr.u32InputBufDepth = 0;
          stIPUChnAttr.u32OutputBufDepth = 0;
          stIPUChnAttr.u32BatchMax = stTestParam.u32Nbatch;
          stIPUChnAttr.u32SubNetId = stTestParam.u32SubNetId;
      
          s32Ret = MI_IPU_PCIECasWrapper_CreateCHN(&u32ChnId, &stIPUChnAttr, NULL, stTestParam.ModelPath, E_MI_IPU_PCIE_CAS_WORK_MODE_RC);
          if (s32Ret != MI_SUCCESS)
          {
              printf("Fail to Create CHN, ret=%d\n", s32Ret);
              goto DESTROY_DEV;
          }
      
          pstModelDesc = malloc(sizeof(MI_IPU_ModelDesc_t));
          if (!pstModelDesc)
          {
              printf("Fail to malloc model description\n");
              goto DESTROY_CHN;
          }
          memset(pstModelDesc, 0, sizeof(MI_IPU_ModelDesc_t));
      
          s32Ret = MI_IPU_GetSubNetDesc(u32ChnId, pstModelDesc);
          if (s32Ret != MI_SUCCESS)
          {
              printf("Get model desc failed!\n");
              goto DESTROY_CHN;
          }
          if (stTestParam.u32SubNetId >= pstModelDesc->u32SubNetNum)
          {
              printf("This model(sub net num %u) not support sub net id %u!\n", pstModelDesc->u32SubNetNum, stTestParam.u32SubNetId);
              goto DESTROY_CHN;
          }
          pstTensorDesc = &pstModelDesc->astSubNetDesc[stTestParam.u32SubNetId].stSubNetInputOutputDesc;
      
          // allocate input/output buffers
          memset(&stIpuRunBufInfo, 0, sizeof(stIpuRunBufInfo));
          s32Ret = IPU_Client_AllocTensors(pstTensorDesc, &stTestParam, &stIpuRunBufInfo, &stTensorAddrInfo, EN_INPUT_BUF, 0);
          if (s32Ret) {
              goto DESTROY_CHN;
          }
      
          s32Ret = IPU_Client_AllocTensors(pstTensorDesc, &stTestParam, &stIpuRunBufInfo, &stTensorAddrInfo, EN_OUTPUT_BUF, 0);
          if (s32Ret) {
              IPU_Client_FreeTensors(&stIpuRunBufInfo, EN_INPUT_BUF);
              goto DESTROY_CHN;
          }
      
          s32Ret = IPU_Client_GetInputTensorData(stTestParam.InputPath, &pInput, &file_len_in);
          if (s32Ret != MI_SUCCESS)
          {
              printf("Get input data failed!\n");
              goto FREE_TENSOR;
          }
      
          memset(&stInvokParam, 0, sizeof(stInvokParam));
          memset(&stSubNetInvokeParam, 0, sizeof(MI_IPU_SubNetBatchInvokeParam_t));
          memset(&stIPUCASInvokeParam, 0, sizeof(MI_IPU_PCIECasSubNetBatchInvokeParam_t));
          stInvokParam.u32BatchN = stTestParam.u32Nbatch;
          stInvokParam.s32TaskPrio = stTestParam.s32TaskPrio;
          stInvokParam.u32IpuAffinity = stTestParam.u32IpuAffinity;
      
          for (int i = 0; i < stTensorAddrInfo.u32InCnt + stTensorAddrInfo.u32OutCnt; i++)
          {
              stInvokParam.astArrayTensors[i] = stTensorAddrInfo.astArrayTensors[i];
          }
      
          stSubNetInvokeParam.eBatchMode = E_IPU_BATCH_N_BUF_MODE;
          stSubNetInvokeParam.stInvokeParam = stInvokParam;
          stIPUCASInvokeParam.stSubnNetBatchInvokeParam = stSubNetInvokeParam;
      
          s32Ret = IPU_Client_InsertInputNbatch(pInput, pstTensorDesc, &stInvokParam);
          if (s32Ret != MI_SUCCESS)
          {
              printf("Insert input failed\n");
              goto MUNMAP;
          }
      
          total_cnt = stInvokParam.u32BatchN * pstTensorDesc->u32InputTensorCount;
          // invalid output buffer
          for (batch_cnt = 0; batch_cnt < stInvokParam.u32BatchN; batch_cnt++)
          {
          for (cnt = 0; cnt < pstTensorDesc->u32OutputTensorCount; cnt++)
          {
              memset(stInvokParam.astArrayTensors[total_cnt].ptTensorData[0], 0, pstTensorDesc->astMI_OutputTensorDescs[cnt].s32AlignedBufSize);
              MI_SYS_FlushInvCache(stInvokParam.astArrayTensors[total_cnt].ptTensorData[0], pstTensorDesc->astMI_OutputTensorDescs[cnt].s32AlignedBufSize);
              total_cnt++;
          }
          }
      
          memset(&stRuntimeInfo, 0, sizeof(stRuntimeInfo));
          while (count  < stTestParam.u32LoopCount)
          {
              clock_gettime(CLOCK_MONOTONIC, &invoke_start);
              s32Ret = MI_IPU_PCIECasWrapper_SubNetInvoke(u32ChnId, &stIPUCASInvokeParam, &stRuntimeInfo, stTestParam.u32SubNetId, E_MI_IPU_PCIE_CAS_WORK_MODE_RC);
              if (s32Ret != MI_SUCCESS)
              {
                  printf("Invoke failed\n");
                  goto FREE_TENSOR;
              }
              clock_gettime(CLOCK_MONOTONIC, &invoke_end);
      
              linux_time = (invoke_end.tv_sec * 1000000 + invoke_end.tv_nsec / 1000)
                  - (invoke_start.tv_sec * 1000000 + invoke_start.tv_nsec / 1000);
              printf("Run model %s loop%d success, invoke time: %f us\n",
                  stTestParam.ModelPath, count, linux_time);
      
              printf("============ Loop%d ipu_time=%lluus\n", count, stRuntimeInfo.u64IpuTime);
              total_linux_time += linux_time;
              total_ipu_time += stRuntimeInfo.u64IpuTime;
              total_bw += stRuntimeInfo.u64BandWidth;
              total_bw_rd += stRuntimeInfo.u64BandWidthRead;
              total_bw_wr += stRuntimeInfo.u64BandWidthWrite;
              count++;
          }
          printf("cycles=%lfus linux_fps=%lffps ipu_fps=%lffps bandwidth_total=%llubytes bandwidth_rd=%llubytes bandwidth_wr=%llubytes\n",
              total_ipu_time/stTestParam.u32LoopCount,
              1/(total_linux_time/stTestParam.u32Nbatch/stTestParam.u32LoopCount/1000000),
              1/(total_ipu_time/stTestParam.u32LoopCount/1000000),
              total_bw/stTestParam.u32LoopCount, total_bw_rd/stTestParam.u32LoopCount, total_bw_wr/stTestParam.u32LoopCount);
      
      MUNMAP:
          if (stTestParam.InputPath)
              munmap(pInput, file_len_in);
      
      FREE_TENSOR:
          IPU_Client_FreeTensors(&stIpuRunBufInfo, EN_INPUT_BUF);
          IPU_Client_FreeTensors(&stIpuRunBufInfo, EN_OUTPUT_BUF);
      
      DESTROY_CHN:
          MI_IPU_PCIECasWrapper_DestroyCHN(u32ChnId, E_MI_IPU_PCIE_CAS_WORK_MODE_RC);
      
      DESTROY_DEV:
          MI_IPU_PCIECasWrapper_DestroyDevice(E_MI_IPU_PCIE_CAS_WORK_MODE_RC);
      
      SYS_EXIT:
          MI_SYS_Exit(0);
      
          return s32Ret;
      }
      
    2. ipu_service

      #include <stdio.h>
      #include <stdlib.h>
      #include <error.h>
      #include <sys/mman.h>
      #include <fcntl.h>
      #include <string.h>
      #include <sys/ioctl.h>
      #include <sys/types.h>
      #include <unistd.h>
      #include <errno.h>
      #include <pthread.h>
      #include <stdbool.h>
      #include <poll.h>
      #include <assert.h>
      #include <signal.h>
      #include <getopt.h>
      
      #include "mi_sys.h"
      #include "ipu_service.h"
      
      static MI_U32 g_u32MaxWorkThread = 0;
      
      static void _IPU_Service_ShowUsage(const char *progName)
      {
          printf("Usage: %s [options]\n", progName);
          printf("Options:\n");
          printf(" -t        show number of work thread\n");
          printf(" -h        show this help message\n");
      }
      
      static MI_S32 _IPU_Service_ParseArgs(int argc, char *argv[])
      {
          int opt;
          int option_index = 0;
      
          static struct option long_options[] =
          {
              {"t",     required_argument, 0,     't'},
              {"help",      no_argument,       0, 'h'},
              {0, 0, 0, 0}
          };
      
          while ((opt = getopt_long(argc, argv, "t:h", long_options, &option_index)) != -1)
          {
              switch (opt)
              {
                  case 't':
                  {
                      MI_U32 u32ThreadCount = strtoul(optarg, NULL, 10);
                      g_u32MaxWorkThread = u32ThreadCount;
                      break;
                  }
                  case 'h':
                      _IPU_Service_ShowUsage(argv[0]);
                      return 1; // Signal to exit after showing help
                  default:
                      printf("Unknown option: %c\n", opt);
                      _IPU_Service_ShowUsage(argv[0]);
                      return -1;
              }
          }
      
          // Check for remaining non-option arguments
          if (optind < argc)
          {
              printf("Unknown arguments: ");
              for (int i = optind; i < argc; i++)
              {
                  printf("%s ", argv[i]);
              }
              printf("\n");
              _IPU_Service_ShowUsage(argv[0]);
              return -1;
          }
      
          return 0;
      }
      
      int main(int argc, char *argv[])
      {
          MI_S32 s32Ret;
          MI_IPU_PCIECasServiceConfig_t stCasServiceCfg;
          // Parse command line arguments
          s32Ret = _IPU_Service_ParseArgs(argc, argv);
          if (s32Ret < 0)
          {
              return -1;
          }
          else if (s32Ret > 0)
          {
              return 0; // Help was shown, exit normally
          }
      
          printf("IPU Service starting with thread number: %u\n", g_u32MaxWorkThread);
      
          MI_SYS_Init(0);
          memset(&stCasServiceCfg, 0, sizeof(stCasServiceCfg));
          stCasServiceCfg.u32MaxWorkThread = g_u32MaxWorkThread;
          if (MI_IPU_PCIECasWrapper_RunService(&stCasServiceCfg) != MI_SUCCESS) {
              printf("Failed to start EP service\n");
              return -1;
          }
      
          MI_IPU_PCIECasWrapper_StopService();
          MI_SYS_Exit(0);
      
          printf("Service exited cleanly\n");
          return 0;
      }
      

    2. API参考


    2.1. 功能模块API

    API名 功能
    MI_IPU_PCIECasWrapper_GetOfflineStaticInfo 读取级联离线模型的静态信息
    MI_IPU_PCIECasWrapper_CreateDevice 创建级联设备
    MI_IPU_PCIECasWrapper_CreateCHN 创建级联通道
    MI_IPU_PCIECasWrapper_SubNetInvoke 触发级联模型推理
    MI_IPU_PCIECasWrapper_QueryRemoteInputBufInfo 查询远端input buffer信息
    MI_IPU_PCIECasWrapper_SetRemoteInputBuf 写入远端input buffer
    MI_IPU_PCIECasWrapper_GetRemoteInputBuf 读回远端input buffer
    MI_IPU_PCIECasWrapper_DestroyCHN 销毁级联通道
    MI_IPU_PCIECasWrapper_DestroyDevice 销毁级联设备
    MI_IPU_PCIECasWrapper_RunService 启动EP端服务
    MI_IPU_PCIECasWrapper_StopService 停止EP端服务

    2.2. MI_IPU_PCIECasWrapper_GetOfflineStaticInfo

    • 功能

      解析离线模型头信息,获取 SoC 数量、模型大小与变量缓冲需求。

    • 语法

      MI_S32 MI_IPU_PCIECasWrapper_GetOfflineStaticInfo(SerializedReadFunc pReadFunc,
                                                      char *pReadCtx,
                                                      MI_IPU_PCIECasOfflineModelStaticInfo_t *pStaticInfo);
      
    • 形参

      参数名称 描述 输入/输出
      pReadFunc 用户自定义读取文件函数(设为NULL默认使用IPU Cascade API提供的文件读取函数) 输入
      pReadCtx 模型路径 输入
      pStaticInfo 离线级联模型静态信息结构体指针 输出
    • 返回值

      • MI_SUCCESS 表示成功

      • 非MI_SUCCESS失败,参照错误码

    • 依赖

      • 头文件:mi_ipu_pcie_cascade.h

      • 库文件:libmi_ipu.so

    • 举例

      MI_IPU_PCIECasOfflineModelStaticInfo_t stStaticInfo;
      memset(&stStaticInfo, 0, sizeof(stStaticInfo));
      if (MI_IPU_PCIECasWrapper_GetOfflineStaticInfo(NULL,
      
                                                   stTestParam.ModelPath,
                                                   &stStaticInfo) != MI_SUCCESS)
      {
      
        printf("fail to read cascade model info\n");
        return -1;
      }
      

    2.3. MI_IPU_PCIECasWrapper_CreateDevice

    • 功能

      创建级联设备,初始化级联运行环境。

    • 语法

      MI_S32 MI_IPU_PCIECasWrapper_CreateDevice(MI_IPU_PCIECasDevAttr_t *pstIPUDevAttr,
      
                                              SerializedReadFunc pReadFunc,
                                              char *pReadCtx,
                                              MI_IPU_PCIECasWorkMode_e eWorkMode);
      
    • 形参

      参数名称 描述 输入/输出
      pstIPUDevAttr 级联设备属性,包含 SoC 数及各 SoC 变量区上限 输入
      pReadFunc 可选固件读取函数 输入
      pReadCtx 固件路径或上下文 输入
      eWorkMode 工作模式:E_MI_IPU_PCIE_CAS_WORK_MODE_RCE_MI_IPU_PCIE_CAS_WORK_MODE_EP 输入
    • 返回值

      • MI_SUCCESS 表示成功

      • 非MI_SUCCESS失败,参照错误码

    • 依赖

      • 头文件:mi_ipu_pcie_cascade.h

      • 库文件:libmi_ipu.so

    • 举例

      MI_IPU_PCIECasDevAttr_t stCasAttr;
      memset(&stCasAttr, 0, sizeof(stCasAttr));
      stCasAttr.u32SocNum = stStaticInfo.u32SocNum;
      memcpy(stCasAttr.au32MaxVariableBufSize,
      
           stStaticInfo.au32VariableBufferSize,
           sizeof(stCasAttr.au32MaxVariableBufSize));
      if (MI_IPU_PCIECasWrapper_CreateDevice(&stCasAttr,
      
                                           NULL,
                                           NULL,
                                           E_MI_IPU_PCIE_CAS_WORK_MODE_RC) != MI_SUCCESS)
      {
      
        printf("create cascade device failed\n");
        return -1;
      }
      

    2.4. MI_IPU_PCIECasWrapper_CreateCHN

    • 功能

      创建级联通道。

    • 语法

      MI_S32 MI_IPU_PCIECasWrapper_CreateCHN(MI_IPU_CHN *ptChnId,
      
                                           MI_IPUChnAttr_t *pstChnAttr,
                                           SerializedReadFunc pReadFunc,
                                           char *pReadCtx,
                                           MI_IPU_PCIECasWorkMode_e eWorkMode);
      
    • 形参

      参数名称 描述 输入/输出
      ptChnId 返回建立成功的通道号 输出
      pstChnAttr 通道属性(输入/输出队列深度、子网 ID 等) 输入
      pReadFunc 模型读取函数,可为空 输入
      pReadCtx RC:模型路径;EP:模型物理地址上下文 输入
      eWorkMode 工作模式:RC/EP 输入
    • 返回值

      • MI_SUCCESS 表示成功

      • 非MI_SUCCESS失败,参照错误码

    • 依赖

      • 头文件:mi_ipu_pcie_cascade.h

      • 库文件:libmi_ipu.so

    • 举例

      MI_IPU_CHN u32ChnId = 0;
      MI_IPUChnAttr_t stChnAttr;
      memset(&stChnAttr, 0, sizeof(stChnAttr));
      stChnAttr.u32InputBufDepth  = 2;
      stChnAttr.u32OutputBufDepth = 2;
      if (MI_IPU_PCIECasWrapper_CreateCHN(&u32ChnId,
      
                                        &stChnAttr,
                                        NULL,
                                        stTestParam.ModelPath,
                                        E_MI_IPU_PCIE_CAS_WORK_MODE_RC) != MI_SUCCESS)
      {
      
        printf("create cascade channel failed\n");
        return -1;
      }
      

    2.5. MI_IPU_PCIECasWrapper_SubNetInvoke

    • 功能

      执行级联模型推理。

    • 语法

      MI_S32 MI_IPU_PCIECasWrapper_SubNetInvoke(MI_IPU_CHN u32ChnId,
      
                                              MI_IPU_PCIECasSubNetBatchInvokeParam_t *pstInvokeParam,
                                              MI_IPU_RuntimeInfo_t *pstRuntimeInfo,
                                              MI_U32 u32SubNetId,
                                              MI_IPU_PCIECasWorkMode_e eWorkMode);
      
    • 形参

      参数名称 描述 输入/输出
      u32ChnId 待执行的通道号 输入
      pstInvokeParam 级联批处理参数,包含输入/输出 Tensor 信息与共享输出位置 输入
      pstRuntimeInfo 推理运行信息(时间、带宽等) 输出
      u32SubNetId 目标子网 ID 输入
      eWorkMode 工作模式:RC/EP 输入
    • 返回值

      • MI_SUCCESS 表示成功

      • 非MI_SUCCESS失败,参照错误码

    • 依赖

      • 头文件:mi_ipu_pcie_cascade.h

      • 库文件:libmi_ipu.so

    • 举例

      MI_IPU_PCIECasSubNetBatchInvokeParam_t stInvokeParam;
      memset(&stInvokeParam, 0, sizeof(stInvokeParam));
      stInvokeParam.stSubnNetBatchInvokeParam.u32BatchN = stTestParam.u32Nbatch;
      /* 根据模型填充输入/输出 Tensor */
      if (MI_IPU_PCIECasWrapper_SubNetInvoke(u32ChnId,
                                             &stInvokeParam,
                                             &stRuntimeInfo,
                                             stTestParam.u32SubNetId,
                                             E_MI_IPU_PCIE_CAS_WORK_MODE_RC) != MI_SUCCESS)
      {
          printf("invoke cascade subnet failed\n");
          return -1;
      }
      

    2.6. MI_IPU_PCIECasWrapper_QueryRemoteInputBufInfo

    • 功能

      查询指定 EP 的input tensor buffer信息(数量、大小与物理地址)。

    • 语法

      MI_S32 MI_IPU_PCIECasWrapper_QueryRemoteInputBufInfo(MI_IPU_CHN u32ChnId,
                                                           MI_U32 u32EpId,
                                                           MI_IPU_PCIECasInputDesc_t *pstCasInputDesc);
      
    • 形参

      参数名称 描述 输入/输出
      u32ChnId 待执行的通道号 ID 输入
      u32EpId 目标 EP 板卡编号 输入
      pstCasInputDesc 返回的远端输入 Tensor 描述指针,内含u32TensorCount以及每个 Tensor 的缓冲大小/地址 输出
    • 返回值

      • MI_SUCCESS 表示成功

      • 非MI_SUCCESS失败,参照错误码

    • 依赖

      • 头文件:mi_ipu_pcie_cascade.h

      • 库文件:libmi_ipu.so

    • 举例

      MI_IPU_PCIECasInputDesc_t stRemoteInputDesc;
      memset(&stRemoteInputDesc, 0, sizeof(stRemoteInputDesc));
      if (MI_IPU_PCIECasWrapper_QueryRemoteInputBufInfo(u32ChnId,
                                                        0,
                                                        &stRemoteInputDesc) != MI_SUCCESS)
      {
          printf("query remote input buffer failed\n");
          return -1;
      }
      

    2.7. MI_IPU_PCIECasWrapper_SetRemoteInputBuf

    • 功能

      使用DMA linklist模式将本端 tensor 数据写入远端 EP 的input buffer。

    • 语法

      MI_S32 MI_IPU_PCIECasWrapper_SetRemoteInputBuf(MI_IPU_CHN u32ChnId,
                                                     MI_U32 u32EpId,
                                                     MI_IPU_PCIECasTransferDesc_t *pstCasTransferDesc);
      
    • 形参

      参数名称 描述 输入/输出
      u32ChnId 待执行的通道号 输入
      u32EpId 目标 EP 板卡编号 输入
      pstCasTransferDesc linklist描述符,包含u32LinklistCount以及至多MI_IPU_PCIE_CASCADE_MAX_LINKLIST_CNT个搬运节点 输入
    • 返回值

      • MI_SUCCESS 表示成功

      • 非MI_SUCCESS失败,参照错误码

    • 依赖

      • 头文件:mi_ipu_pcie_cascade.h

      • 库文件:libmi_ipu.so

    • 举例

      MI_IPU_PCIECasTransferDesc_t stTransferDesc;
      memset(&stTransferDesc, 0, sizeof(stTransferDesc));
      stTransferDesc.u32LinklistCount = 1;
      stTransferDesc.astTransferLinklist[0].phySrcAddr = stLocalTensor.phyTensorAddr[0];
      stTransferDesc.astTransferLinklist[0].phyDstAddr = stRemoteInputDesc.astInputTensroDesc[0].phyTensorAddr;
      stTransferDesc.astTransferLinklist[0].u64TransferSize = stRemoteInputDesc.astInputTensroDesc[0].u64TensorBufSize;
      if (MI_IPU_PCIECasWrapper_SetRemoteInputBuf(u32ChnId,
                                                  0,
                                                  &stTransferDesc) != MI_SUCCESS)
      {
          printf("set remote input buffer failed\n");
          return -1;
      }
      

    2.8. MI_IPU_PCIECasWrapper_GetRemoteInputBuf

    • 功能

      使用DMA linklist模式从远端 EP input buffer读取数据。

    • 语法

      MI_S32 MI_IPU_PCIECasWrapper_GetRemoteInputBuf(MI_IPU_CHN u32ChnId,
                                                     MI_U32 u32EpId,
                                                     MI_IPU_PCIECasTransferDesc_t *pstCasTransferDesc);
      
    • 形参

      参数名称 描述 输入/输出
      u32ChnId 待执行的通道号 输入
      u32EpId 目标 EP 板卡编号 输入
      pstCasTransferDesc linklist描述符。每个节点指定远端源地址、本地目标地址及传输大小 输入
    • 返回值

      • MI_SUCCESS 表示成功

      • 非MI_SUCCESS失败,参照错误码

    • 依赖

      • 头文件:mi_ipu_pcie_cascade.h

      • 库文件:libmi_ipu.so

    • 举例

      MI_IPU_PCIECasTransferDesc_t stPullDesc;
      memset(&stPullDesc, 0, sizeof(stPullDesc));
      stPullDesc.u32LinklistCount = 1;
      stPullDesc.astTransferLinklist[0].phySrcAddr = stRemoteInputDesc.astInputTensroDesc[0].phyTensorAddr;
      stPullDesc.astTransferLinklist[0].phyDstAddr = stLocalTensor.phyTensorAddr[0];
      stPullDesc.astTransferLinklist[0].u64TransferSize = stRemoteInputDesc.astInputTensroDesc[0].u64TensorBufSize;
      if (MI_IPU_PCIECasWrapper_GetRemoteInputBuf(u32ChnId,
                                                  0,
                                                  &stPullDesc) != MI_SUCCESS)
      {
          printf("get remote input buffer failed\n");
      }
      

    2.9. MI_IPU_PCIECasWrapper_DestroyCHN

    • 功能

      销毁指定级联通道。

    • 语法

      MI_S32 MI_IPU_PCIECasWrapper_DestroyCHN(MI_IPU_CHN u32ChnId,
      
                                            MI_IPU_PCIECasWorkMode_e eWorkMode);
      
    • 形参

      参数名称 描述 输入/输出
      u32ChnId 待销毁的通道号 输入
      eWorkMode 工作模式:RC/EP 输入
    • 返回值

      • MI_SUCCESS 表示成功

      • 非MI_SUCCESS失败,参照错误码

    • 依赖

      • 头文件:mi_ipu_pcie_cascade.h

      • 库文件:libmi_ipu.so

    • 举例

      MI_IPU_PCIECasWrapper_DestroyCHN(u32ChnId, E_MI_IPU_PCIE_CAS_WORK_MODE_RC);
      

    2.10. MI_IPU_PCIECasWrapper_DestroyDevice

    • 功能

      销毁级联设备。

    • 语法

      MI_S32 MI_IPU_PCIECasWrapper_DestroyDevice(MI_IPU_PCIECasWorkMode_e eWorkMode);
      
    • 形参

      参数名称 描述 输入/输出
      eWorkMode 工作模式:RC/EP 输入
    • 返回值

      • MI_SUCCESS 表示成功

      • 非MI_SUCCESS失败,参照错误码

    • 依赖

      • 头文件:mi_ipu_pcie_cascade.h

      • 库文件:libmi_ipu.so

    • 举例

      MI_IPU_PCIECasWrapper_DestroyDevice(E_MI_IPU_PCIE_CAS_WORK_MODE_RC);
      

    2.11. MI_IPU_PCIECasWrapper_RunService

    • 功能

      在 EP 端启动级联服务,创建请求处理线程,监听RC端请求并处理。

    • 语法

      MI_S32 MI_IPU_PCIECasWrapper_RunService(const MI_IPU_PCIECasServiceConfig_t *pstConfig);
      
    • 形参

      参数名称 描述 输入/输出
      pstConfig EP 服务配置,包含最大工作线程、PCIe ID、心跳超时阈值及心跳周期 输入
    • 返回值

      • MI_SUCCESS 表示成功

      • 非MI_SUCCESS失败,参照错误码

    • 依赖

      • 头文件:mi_ipu_pcie_cascade.h

      • 库文件:libmi_ipu.so

    • 注意

      pstConfig参数可设置为NULL,设置为NULL时EP端服务采用默认配置

    • 举例

      MI_IPU_PCIECasServiceConfig_t stServiceCfg = {
          .u32MaxWorkThread = 4,
          .u32PcieId = 0,
          .u32HeartbeatTimeoutCountThreshold = 30,
          .s32HeartbeatTimeoutMs = 1000,
      };
      if (MI_IPU_PCIECasWrapper_RunService(&stServiceCfg) != MI_SUCCESS)
      {
          printf("run pcie cascade service failed\n");
          return -1;
      }
      

    2.12. MI_IPU_PCIECasWrapper_StopService

    • 功能

      关闭 EP 端服务线程并释放相关资源。

    • 语法

      MI_S32 MI_IPU_PCIECasWrapper_StopService(void);
      
    • 形参

      无。

    • 返回值

      • MI_SUCCESS 表示成功

      • 非MI_SUCCESS失败,参照错误码

    • 依赖

      • 头文件:mi_ipu_pcie_cascade.h

      • 库文件:libmi_ipu.so

    • 举例

      MI_IPU_PCIECasWrapper_StopService();
      

    3. 数据类型


    3.1. 数据类型定义

    数据类型 功能
    MI_IPU_PCIECasWorkMode_e 定义 RC/EP 工作模式
    MI_IPU_PCIECasOfflineModelStaticInfo_t 定义IPU离线级联模型静态信息结构体
    MI_IPU_PCIECasDevAttr_t 定义IPU级联设备属性结构体
    MI_IPU_PCIECasPosition_t 定义模型输出的位置与偏移结构体
    MI_IPU_PCIECasSubNetBatchInvokeParam_t 定义级联模型的子网批处理参数结构体
    MI_IPU_PCIECasServiceConfig_t 定义EP服务运行参数结构体
    MI_IPU_PCIECasTensorDesc_t 定义远端Tensor缓冲描述
    MI_IPU_PCIECasInputDesc_t 定义远端输入Tensor集合描述
    MI_IPU_PCIECasTransferLinklist_t 定义远端搬运链表节点结构体
    MI_IPU_PCIECasTransferDesc_t 定义远端搬运链表描述结构体

    3.2. MI_IPU_PCIECasWorkMode_e

    • 说明

      定义 IPU Cascade API 支持的工作模式,区分 RC 与 EP 两侧的接口调用。

    • 语法

      typedef enum {
          E_MI_IPU_PCIE_CAS_WORK_MODE_RC = 0,
          E_MI_IPU_PCIE_CAS_WORK_MODE_EP,
      } MI_IPU_PCIECasWorkMode_e;
      
    • 成员

      成员名称 描述
      E_MI_IPU_PCIE_CAS_WORK_MODE_RC 以 RC 模式调用 API
      E_MI_IPU_PCIE_CAS_WORK_MODE_EP 以 EP 模式调用 API

    3.3. MI_IPU_PCIECasOfflineModelStaticInfo_t

    • 说明

      定义IPU离线级联模型静态信息结构体

    • 语法

      typedef struct MI_IPU_PCIECasOfflineModelStaticInfo_s {
          MI_U32 u32SocNum;
          MI_U64 u64OfflineModelSize;
          MI_U32 au32VariableBufferSize[SGS_MULTI_SOC_NUM];
      } MI_IPU_PCIECasOfflineModelStaticInfo_t;
      
    • 成员

      成员名称 描述
      u32SocNum 运行离线级联模型需要的Soc数量
      u64OfflineModelSize 离线级联模型大小
      au32VariableBufferSize 各个Soc上离线模型需要的variable buffer size

    3.4. MI_IPU_PCIECasDevAttr_t

    • 说明

      定义IPU级联设备属性结构体

    • 语法

      typedef struct MI_IPU_PCIECasDevAttr_s {
          MI_U32 u32SocNum;
          MI_U32 au32MaxVariableBufSize[SGS_MULTI_SOC_NUM];
          MI_IPU_DevAttr_t stIPUDevAttr;
          MI_U32 au32Reserve[8];
      } MI_IPU_PCIECasDevAttr_t;
      
    • 成员

      成员名称 描述
      u32SocNum 运行离线级联模型需要的Soc数量
      au32MaxVariableBufSize 各个Soc上离线模型需要的variable buffer size
      stIPUDevAttr MI IPU 设备属性
      au32Reserve 预留位

    3.5. MI_IPU_PCIECasPosition_t

    • 说明

      定义级联离线模型输出偏移信息结构体

    • 语法

      typedef struct MI_IPU_PCIECasPosition_s {
          MI_U32 u32OutIndex;
          MI_U64 u64OffsetByBytes;
      } MI_IPU_PCIECasPosition_t;
      
    • 成员

      成员名称 描述
      u32OutIndex 需要偏移输出的下标
      u64OffsetByBytes 相对于输出起始地址的字节偏移

    3.6. MI_IPU_PCIECasSubNetBatchInvokeParam_t

    • 说明

      定义推理级联离线模型时的批处理参数信息

    • 语法

      typedef struct MI_IPU_PCIECasSubNetBatchInvokeParam_s {
          MI_IPU_SubNetBatchInvokeParam_t stSubnNetBatchInvokeParam;
          MI_IPU_PCIECasPosition_t * pstPosition;
          MI_U32 u32PositionNum;
      } MI_IPU_PCIECasSubNetBatchInvokeParam_t;
      
    • 成员

      成员名称 描述
      stSubnNetBatchInvokeParam 标准子网络批处理参数,继承自MI IPU
      pstPosition 离线级联模型输出偏移信息数组指针
      u32PositionNum 离线级联模型需要偏移的输出数量

    3.7. MI_IPU_PCIECasServiceConfig_t

    • 说明

      定义EP端服务运行配置。

    • 语法

      typedef struct MI_IPU_PCIECasServiceConfig_s {
          MI_U32  u32MaxWorkThread;
          MI_U32  u32PcieId;
          MI_U32  u32HeartbeatTimeoutCountThreshold;
          MI_S32  s32HeartbeatTimeoutMs;
          MI_U32  au32Reserve[8];
      } MI_IPU_PCIECasServiceConfig_t;
      
    • 成员

      成员名称 描述
      u32MaxWorkThread EP 服务允许的最大工作线程数
      u32PcieId 选用的 PCIe 设备ID
      u32HeartbeatTimeoutCountThreshold 心跳超时计数阈值,超过即判定 RC 断连
      s32HeartbeatTimeoutMs 心跳间隔时间(毫秒)
      au32Reserve 预留字段

    3.8. MI_IPU_PCIECasTensorDesc_t

    • 说明

      定义远端输入 Tensor 缓冲描述。

    • 语法

      typedef struct MI_IPU_PCIECasTensorDesc_s {
          MI_U64 u64TensorBufSize;
          MI_PHY phyTensorAddr;
      } MI_IPU_PCIECasTensorDesc_t;
      
    • 成员

      成员名称 描述
      u64TensorBufSize 当前 Tensor 缓冲大小(字节)
      phyTensorAddr 缓冲所在物理地址(远端 EP 视角)

    3.9. MI_IPU_PCIECasInputDesc_t

    • 说明

      定义远端 EP 输入 Tensor 信息。

    • 语法

      typedef struct MI_IPU_PCIECasInputDesc_s {
          MI_U32 u32TensorCount;
          MI_IPU_PCIECasTensorDesc_t astInputTensroDesc[MI_IPU_MAX_TENSOR_CNT];
      } MI_IPU_PCIECasInputDesc_t;
      
    • 成员

      成员名称 描述
      u32TensorCount 输入 Tensor 数量
      astInputTensroDesc 每个输入 Tensor 的物理地址与大小

    • 说明

      定义一次DMA搬运操作的linklist节点。

    • 语法

      typedef struct MI_IPU_PCIECasTransferLinkList_s {
          MI_PHY phySrcAddr;
          MI_PHY phyDstAddr;
          MI_U64 u64TransferSize;
      } MI_IPU_PCIECasTransferLinklist_t;
      
    • 成员

      成员名称 描述
      phySrcAddr 源物理地址(RC 或 EP)
      phyDstAddr 目标物理地址(RC 或 EP)
      u64TransferSize 本节点传输字节数

    3.11. MI_IPU_PCIECasTransferDesc_t

    • 说明

      定义一次或多次DMA搬运操作的linklist集合。

    • 语法

      typedef struct MI_IPU_PCIECasTransferDesc_s {
          MI_U32 u32LinklistCount;
          MI_IPU_PCIECasTransferLinklist_t astTransferLinklist[MI_IPU_PCIE_CASCADE_MAX_LINKLIST_CNT];
      } MI_IPU_PCIECasTransferDesc_t;
      
    • 成员

      成员名称 描述
      u32LinklistCount 搬运节点数量,不超过MI_IPU_PCIE_CASCADE_MAX_LINKLIST_CNT
      astTransferLinklist 搬运节点数组,依次描述每段搬运的源/目标地址及大小

    4. 错误码


    表4-1 IPU Cascade 错误码

    错误代码 宏定义 描述
    0 MI_SUCCESS Success
    1 E_MI_IPU_PCIE_CAS_ERR_INVALID_PARAM Invalid or missing parameter
    2 E_MI_IPU_PCIE_CAS_ERR_NOMEM Insufficient system memory
    3 E_MI_IPU_PCIE_CAS_ERR_NOBUF Cascade buffer unavailable or not allocated
    4 E_MI_IPU_PCIE_CAS_ERR_TIMEOUT Operation timeout
    5 E_MI_IPU_PCIE_CAS_ERR_PCIE_INIT_FAIL PCIe / communication initialization failure
    6 E_MI_IPU_PCIE_CAS_ERR_MISMATCH_MODEL Offline model mismatch with platform/mode
    7 E_MI_IPU_PCIE_CAS_ERR_INVALID_SOC_NUM Invalid SoC count declared by the model
    8 E_MI_IPU_PCIE_CAS_ERR_RC_INVOKE_FAIL RC-side inference failure
    9 E_MI_IPU_PCIE_CAS_ERR_EP_INVOKE_FAIL EP-side inference failure
    10 E_MI_IPU_PCIE_CAS_ERR_FAIL Unknown or general failure
    11 E_MI_IPU_PCIE_CAS_ERR_FILE_OPERATION File read/write failure
    12 E_MI_IPU_PCIE_CAS_ERR_CONNECTION_FAIL RC–EP connection failure
    13 E_MI_IPU_PCIE_CAS_ERR_MISMATCH_MSG Message type/content mismatch
    14 E_MI_IPU_PCIE_CAS_ERR_CONNECTION_TIMEOUT Heartbeat or connection timeout
    15 E_MI_IPU_PCIE_CAS_ERR_RESPONSE_THREAD_FAIL Response-thread creation/runtime failure
    16 E_MI_IPU_PCIE_CAS_ERR_NO_ENOUGH_EP Not enough active EPs
    17 E_MI_IPU_PCIE_CAS_ERR_INVALID_CHNID Invalid or nonexistent channel ID
    18 E_MI_IPU_PCIE_CAS_ERR_INVALID_SUBNET_ID Invalid subnet ID requested by RC
    19 E_MI_IPU_PCIE_CAS_ERR_MODEL_CORRUPTED Offline model is corrupted
    20 E_MI_IPU_PCIE_CAS_ERR_MAP Address mapping failure (MMU/MAP error)
    21 E_MI_IPU_PCIE_CAS_ERR_DEVICE_NOT_READY Device is not ready for the requested op