本篇將是系列文章的最后一篇,在MCU上進行實際的部署。 小編就不再給大家復習歷史了,直接開始正題。如果想溫習前兩部分內容,請點擊此處和此處。
首先給大家介紹一下我們將要使用的離線Flash燒寫工具,名為JFlashLite,他有一個兄弟叫JFLash,既然是Lite就是輕量版本的JFlash工具。
當然,需要大家自行下載Segger Jlink工具包。是的,我們需要一條JLink。隨后找到JFlashLite工具,他長這樣(請忽略這里的1176,我們還沒有配置):
比起下圖他的哥哥JFlash,是不是很清爽的界面:
當然,我們今天的主角是JFlashLite,稍后會介紹如何進行配置并下載
程序編寫
工具介紹完了,下面正式開始編寫配套程序,所使用的平臺是i.MX RT1060, 開發板是IMXRT1060EVKB:
劃分FLASH以及sdram區域,sdram區域負責加載flash中內容,加速內存訪問:我們的開發板上有一個8MB的Flash,內存映射地址為0x60000000,首先保留1MB的頭部區域以存儲代碼,其作用后續會提到。那么我們就剩下7MB大小的空間,由于數據一般較大,劃分4MB作為數據存儲區,剩下的3MB存儲模型。同樣的,在sdram區域,內存映射地址為0x80000000也同樣開辟這樣大的兩塊內存區,不過,為了防止數據溢出,我們僅針對于sdram區域,在數據區和模型區中間插入256B的數據保護區,并填充0xdeadbeef,最終的內存布局如下:
Flash上內存分配
Sdram內存分配
利用JflashLite工具進行燒寫:
打開JFlashLite工具點擊…選擇器件為1062xxxxA:
選擇對應的數據文件以及模型進行燒寫,燒寫地址要依據上述定義的分配,即模型數據燒寫到0x60100000,圖像數據燒寫到0x60400000:
選擇好之后,點擊Program Device即可進行燒寫;針對于模型數據,yao注意將以.tflite結尾的模型文件,重命名為.bin文件。
Scf文件編寫,主要考慮到,運行時,將flash中的數據,拷貝到sdram中,以提高運行速度,這里聲明兩個區域負責存儲ER_tflite_model以及ER_test_data
#define m_text_start 0x80000400 #define FLASH_LOAD 7 * 1024 * 1024 LR_m_text m_interrupts_start m_text_start+m_text_size-m_interrupts_start { ; load region size_region VECTOR_ROM m_interrupts_start FIXED m_interrupts_size { ; load address = execution address * (.isr_vector,+FIRST) } ER_m_text m_text_start FIXED m_text_size - FLASH_LOADER_SIZE { ; load address = execution address * (InRoot$$Sections) .ANY (+RO) } #if (defined(FLASH_LOAD)) ER_ test_data +0 EMPTY 4 * 1024 * 1024 {} ER_PLACEHOLDER1 +0 EMPTY FILL 0xdeadbeef 256{} ER_tflite_model +0 EMPTY 3 * 1024 * 1024 {} ER_PLACEHOLDER2+0 EMPTY FILL 0xdeadbeef 256{} ER_EMPTY m_text_start + m_text_size EMPTY 0{} #endif
主代碼編寫,針對于邊界溢出檢測代碼,簡單起見,只檢測首地址處值,不同則表示溢出,死循環等待
typedef struct { uint32_t n, h, w, c; uint8_t data[0]; }data_t; // use a split area, total 7MB: // tflite model 3MB // img_data 4MB #define FLASH_BASE (0x60100000) #define MB(x) (x * 1024 * 1024) #define lr_model_data_len (MB(3)) #define lr_model_data FLASH_BASE #define lr_model_data_end (lr_model_data + lr_model_data_len) #define lr_img_data_len (MB(4)) #define lr_img_data (lr_model_data_end) // 0x60200000 #define lr_img_data_end (lr_img_data + lr_img_data_len) // declare the sdram memory extern uint8_t Image$$ER_tflite_model$$ZI$$Base[]; #define model_data Image$$ER_tflite_model$$ZI$$Base extern uint8_t Image$$ER_test_data$$ZI$$Base[]; #define img_data Image$$ER_test_data$$ZI$$Base extern uint8_t Image$$ER_PLACEHOLDER1$$ZI$$Base[]; #define PLACEHOLDER1 Image$$ER_PLACEHOLDER1$$ZI$$Base extern uint8_t Image$$ER_PLACEHOLDER2$$ZI$$Base[]; #define PLACEHOLDER2 Image$$ER_PLACEHOLDER2$$ZI$$Base #define DO_MEMCPY(name) memcpy((void*)name, (void*)lr_##name, lr_##name##_len) #define PRE_INIT() do{ DO_MEMCPY(model_data); DO_MEMCPY(img_data); while(0xdeadbeef != *(uint32_t*) PLACEHOLDER1) ; while(0xdeadbeef != *(uint32_t*) PLACEHOLDER2) ; }while(0);
定義好了一些宏之后,就是模型的初始化函數:
void tflite_engine_init(){ #ifdef FLASH_LOAD PRE_INIT() #endif SysTick_Config(CLOCK_GetCoreSysClkFreq() / 1000); MODEL_AllocateTensor((void*)tensorArena, sizeof(tensorArena)); if (MODEL_Init(model_data) != kStatus_Success) { PRINTF("Failed initializing model"); for (;;) {} } }
測試數據如何獲取呢,利用我們剛才定義的data_t結構體:
data_t *image = (data_t*)img_data; image_data_ptr = image->data;
這樣,我們就拿到了存儲在flash并且已經被搬運到了sdram上的數據了,接下來就是編譯運行了。
實際運行與測試
測試時候,要注意首先確保我們的開發板已經是XIP啟動,即從Nor Flash啟動,并且保證在flash的頭部,燒寫過一個完整的可執行鏡像,比如hello_world程序,其中會包含Flash的一些配置信息,這一步小編就不再舉例,還請大家自行準備。
這樣,我們的BootRom會據此幫我們配置好Flash,程序中就不用手動調用Flash的初始化代碼了。
還要注意,代碼要全部運行在SDRAM或是其他介質上,因為我們已經將flash據為己有了。
下面是內存鏡像的樣子,首先是model:
再者是測試數據,前四個uint32類型的數據剛好是小編這里定義的數據長度100張128*128*3的rgb彩色圖:
連上板子和PC,并打開串口控制臺即可查看輸出結果,小編所選用的模型是一個水果識別的模型,下面是最后一組數據的輸出結果,證明我們的程序運行成功!
展望
當然,小編給大家分享的這個方法,不僅可以應用在神經網絡AI推理上,可以當作一個低配版的flashloader,以供大家靈活地更新靜態數據資源。
-
mcu
+關注
關注
146文章
17123瀏覽量
350995 -
FlaSh
+關注
關注
10文章
1633瀏覽量
147944 -
工具包
+關注
關注
0文章
46瀏覽量
9529
原文標題:一種基于MCU的神經網絡模型在線更新方案之MCU實戰篇
文章出處:【微信號:NXP_SMART_HARDWARE,微信公眾號:恩智浦MCU加油站】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論