iT邦幫忙

2024 iThome 鐵人賽

DAY 9
0
佛心分享-SideProject30

從零開始打造超邊緣AI應用系列 第 9

DAY 9 - 分析 X-Cube-AI 程式碼

  • 分享至 

  • xImage
  •  

接下來幾天,為了更加理解要如何使用 X-Cube-AI,會開始分析前面產出的原始碼。

程式檔案架構

首先先來了解一下程式碼檔案的架構,下圖是如果使用 CubeMX 產出,會有這兩個跟 AI 息息相關的檔案:
https://ithelp.ithome.com.tw/upload/images/20240923/201689278z9D8sZCRT.png
其中:

  • Middlewares/ST/AI 底下放的是 X-Cube-AI 共通的函式庫以及 header ,同一個系列晶片會使用相同的函式庫,而裡面 Lib 底下就是 ST 所提供的 AI Inference Library 實現,例如 U585 就是 "NetworkRuntime900_CM33_GCC.a"(很可惜提供的不是原始碼版本)
  • X-CUBE-AI 底下放的則是本次應用的 Model 相關資料,及 ST 提供的一些 Application Template,接下來會先主要分析這裡的部分。

打開 X-CUBE-AI/App 資料夾以後,其中以 "network_" 為開頭的,(或是看你的 model 是命名什麼)就是本次的 model 相關程式,而其他的分別則是 Application template 或是平台相關 porting 程式。

從頭開始分析

接下來我們先打開位於 Core/Src 下的 main.c,來看看到底程式一開始都是怎麼開始呼叫的。(Startup相關程式並沒有被修改到,所以沒有在主程式呼叫之前額外呼叫什麼,請安心食用)
底下先先簡單擷取 main.c 中的 main()

int main(void)
{
  // ...
  // 此處省略大量初始化呼叫
  MX_X_CUBE_AI_Init();       // <-- 這邊開始進行 AI 的 Initialization
  // ... 
  
  while (1)
  {
    MX_X_CUBE_AI_Process(); // <-- 這邊開始進行 AI Application Process
  }
}

接著進入 MX_X_CUBE_AI_INIT(); 中一探究竟,程式碼在 app_x-cube-ai.c 底下:

void MX_X_CUBE_AI_Init(void)
{
    MX_UARTx_Init();            // <-- 這邊先 Initialization UART 以便輸出結果
    aiSystemPerformanceInit();  // <-- 這邊進行 Performance Test 程式的程式
}

接著進入 aiSystemPerformanceInit(),在 aiSystemPerformance.c 中:

int aiSystemPerformanceInit(void)
{
  LC_PRINT("\r\n#\r\n");
  LC_PRINT("# %s %d.%d\r\n", _APP_NAME_ , _APP_VERSION_MAJOR_,
      _APP_VERSION_MINOR_ );
  LC_PRINT("#\r\n");
  
  systemSettingLog();

  crcIpInit();
  cyclesCounterInit();

  aiInit();

  srand(3); /* deterministic outcome */

  return 0;
}

首先可以先看到以 LC_ 為開頭的三行程式,以及 systemSettingLog(),實際上就只是在印出一些系統訊息,而 LC 是代表 Low cost,主要是用來實現低成本的資料輸出,相關程式可以去看 lc_print.c/lc_print.h 這兩個檔案。
crcIpInit()cyclesCounterInit() 分別為打開 CRC 的 clock 以及為了次是 Performance 打開的 dwt counter,這邊也先略過,主要專注接下來的 aiInit()

再開啟同樣位於 aiSystemPerformance.c 中的 aiInit() 後,可以看到如下:

static int aiInit(void)
{
  int res = -1;
  const char *nn_name;
  int idx;

  aiPlatformVersion();

  /* Reset the contexts -------------------------------------------- */
  for (idx=0; idx < AI_MNETWORK_NUMBER; idx++) {
    net_exec_ctx[idx].handle = AI_HANDLE_NULL;
  }

  /* Discover and initialize the network(s) ------------------------ */
  LC_PRINT("Discovering the network(s)...\r\n");

  idx = 0;
  do {
    nn_name = ai_mnetwork_find(NULL, idx);
    if (nn_name) {
      LC_PRINT("\r\nFound network \"%s\"\r\n", nn_name);
      res = aiBootstrap(&net_exec_ctx[idx], nn_name);
      if (res)
        nn_name = NULL;
    }
    idx++;
  } while (nn_name);

  return res;
}

當執行到 aiPlatformVersion() 時,基本上一開始所有的資料就會都輸出出來,對應到的就是 UART 輸出的底下這段:

#
# AI system performance 7.1
#
Compiled with GCC 13.3.1
STM32 device configuration...
 Device       : DevID:0x0482 (STM32U575/585) RevID:0x2001
 Core Arch.   : M33 - FPU  used
 HAL version  : 0x01060000
 SYSCLK clock : 160 MHz
 HCLK clock   : 160 MHz
 ICACHE       : True
 Timestamp    : SysTick + DWT (delay(1)=1.004 ms)

AI platform (API 1.1.0 - RUNTIME 9.0.0)

接著開始進入建立 network instance 的過程,首先會先尋找是否包含 network 資料,就是使用 ai_mnetwork_find(NULL, idx) 進行尋找,若有找到則會傳入 aiBootstrap(&net_exec_ctx[idx], nn_name) 開始建立 Network。

這邊的 aiBootstrap 會經過幾個階段:

  1. ai_mnetwork_create(nn_name, &ctx->handle, NULL)
  2. ai_mnetwork_get_report(ctx->handle, &ctx->report)
  3. ai_mnetwork_init(ctx->handle)
  4. aiPrintNetworkInfo(&ctx->report)

這四個階段,然後完成初始化。

但再更深入的去看這幾個函數做了什麼之前,我們先來看一個在 app_x-cube-ai.h 底下定義的結構體 ai_network_entry_t

typedef struct {
    const char *name;
    ai_buffer *config;
    ai_bool (*ai_data_params_get)(ai_network_params* params);
    ai_bool (*ai_get_report)(ai_handle network, ai_network_report* report);
    ai_error (*ai_create)(ai_handle* network, const ai_buffer* network_config);
    ai_error (*ai_get_error)(ai_handle network);
    ai_handle (*ai_destroy)(ai_handle network);
    ai_bool (*ai_init)(ai_handle network, const ai_network_params* params);
    ai_i32 (*ai_run)(ai_handle network, const ai_buffer* input, ai_buffer* output);
    ai_i32 (*ai_forward)(ai_handle network, const ai_buffer* input);
    ai_handle * activations;
} ai_network_entry_t;

這個結構體是用來記錄並儲存 neural network 資訊的,並且透過 function pointer 存儲執行的方法。
接著可以看到在 app_x-cube-ai.c 底下透過 const 定義的資料:

static const ai_network_entry_t networks[AI_MNETWORK_NUMBER] = {
    {
        .name = (const char *)AI_NETWORK_MODEL_NAME,
        .config = AI_NETWORK_DATA_CONFIG,
        .ai_get_report = ai_network_get_report,
        .ai_create = ai_network_create,
        .ai_destroy = ai_network_destroy,
        .ai_get_error = ai_network_get_error,
        .ai_init = ai_network_init,
        .ai_run = ai_network_run,
        .ai_forward = ai_network_forward,
        .ai_data_params_get = ai_network_data_params_get,
        .activations = data_activations0
    },
};

而這邊每個資料就可以在 refer 到 network 相關的程式碼(就是前面說的開頭名稱是 network_ 檔案相關的程式)。

那今天就先分析到這邊,明天繼續拆解初始化神經網路的細節程式。


上一篇
Day 8 - 開始使用 X.Cube.AI 插件
下一篇
Day 10 - 分析 X-Cube-AI 程式碼 (2)
系列文
從零開始打造超邊緣AI應用13
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言