本篇將是系列文章的最后一篇,在MCU上進(jìn)行實(shí)際的部署。 小編就不再給大家復(fù)習(xí)歷史了,直接開(kāi)始正題。如果想溫習(xí)前兩部分內(nèi)容,請(qǐng)點(diǎn)擊此處和此處。
首先給大家介紹一下我們將要使用的離線Flash燒寫(xiě)工具,名為JFlashLite,他有一個(gè)兄弟叫JFLash,既然是Lite就是輕量版本的JFlash工具。
當(dāng)然,需要大家自行下載Segger Jlink工具包。是的,我們需要一條JLink。隨后找到JFlashLite工具,他長(zhǎng)這樣(請(qǐng)忽略這里的1176,我們還沒(méi)有配置):
比起下圖他的哥哥JFlash,是不是很清爽的界面:
當(dāng)然,我們今天的主角是JFlashLite,稍后會(huì)介紹如何進(jìn)行配置并下載
程序編寫(xiě)
工具介紹完了,下面正式開(kāi)始編寫(xiě)配套程序,所使用的平臺(tái)是i.MX RT1060, 開(kāi)發(fā)板是IMXRT1060EVKB:
劃分FLASH以及sdram區(qū)域,sdram區(qū)域負(fù)責(zé)加載flash中內(nèi)容,加速內(nèi)存訪問(wèn):我們的開(kāi)發(fā)板上有一個(gè)8MB的Flash,內(nèi)存映射地址為0x60000000,首先保留1MB的頭部區(qū)域以存儲(chǔ)代碼,其作用后續(xù)會(huì)提到。那么我們就剩下7MB大小的空間,由于數(shù)據(jù)一般較大,劃分4MB作為數(shù)據(jù)存儲(chǔ)區(qū),剩下的3MB存儲(chǔ)模型。同樣的,在sdram區(qū)域,內(nèi)存映射地址為0x80000000也同樣開(kāi)辟這樣大的兩塊內(nèi)存區(qū),不過(guò),為了防止數(shù)據(jù)溢出,我們僅針對(duì)于sdram區(qū)域,在數(shù)據(jù)區(qū)和模型區(qū)中間插入256B的數(shù)據(jù)保護(hù)區(qū),并填充0xdeadbeef,最終的內(nèi)存布局如下:
Flash上內(nèi)存分配
Sdram內(nèi)存分配
利用JflashLite工具進(jìn)行燒寫(xiě):
打開(kāi)JFlashLite工具點(diǎn)擊…選擇器件為1062xxxxA:
選擇對(duì)應(yīng)的數(shù)據(jù)文件以及模型進(jìn)行燒寫(xiě),燒寫(xiě)地址要依據(jù)上述定義的分配,即模型數(shù)據(jù)燒寫(xiě)到0x60100000,圖像數(shù)據(jù)燒寫(xiě)到0x60400000:
選擇好之后,點(diǎn)擊Program Device即可進(jìn)行燒寫(xiě);針對(duì)于模型數(shù)據(jù),yao注意將以.tflite結(jié)尾的模型文件,重命名為.bin文件。
Scf文件編寫(xiě),主要考慮到,運(yùn)行時(shí),將flash中的數(shù)據(jù),拷貝到sdram中,以提高運(yùn)行速度,這里聲明兩個(gè)區(qū)域負(fù)責(zé)存儲(chǔ)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
主代碼編寫(xiě),針對(duì)于邊界溢出檢測(cè)代碼,簡(jiǎn)單起見(jiàn),只檢測(cè)首地址處值,不同則表示溢出,死循環(huán)等待
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);
定義好了一些宏之后,就是模型的初始化函數(shù):
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 (;;) {} } }
測(cè)試數(shù)據(jù)如何獲取呢,利用我們剛才定義的data_t結(jié)構(gòu)體:
data_t *image = (data_t*)img_data; image_data_ptr = image->data;
這樣,我們就拿到了存儲(chǔ)在flash并且已經(jīng)被搬運(yùn)到了sdram上的數(shù)據(jù)了,接下來(lái)就是編譯運(yùn)行了。
實(shí)際運(yùn)行與測(cè)試
測(cè)試時(shí)候,要注意首先確保我們的開(kāi)發(fā)板已經(jīng)是XIP啟動(dòng),即從Nor Flash啟動(dòng),并且保證在flash的頭部,燒寫(xiě)過(guò)一個(gè)完整的可執(zhí)行鏡像,比如hello_world程序,其中會(huì)包含F(xiàn)lash的一些配置信息,這一步小編就不再舉例,還請(qǐng)大家自行準(zhǔn)備。
這樣,我們的BootRom會(huì)據(jù)此幫我們配置好Flash,程序中就不用手動(dòng)調(diào)用Flash的初始化代碼了。
還要注意,代碼要全部運(yùn)行在SDRAM或是其他介質(zhì)上,因?yàn)槲覀円呀?jīng)將flash據(jù)為己有了。
下面是內(nèi)存鏡像的樣子,首先是model:
再者是測(cè)試數(shù)據(jù),前四個(gè)uint32類型的數(shù)據(jù)剛好是小編這里定義的數(shù)據(jù)長(zhǎng)度100張128*128*3的rgb彩色圖:
連上板子和PC,并打開(kāi)串口控制臺(tái)即可查看輸出結(jié)果,小編所選用的模型是一個(gè)水果識(shí)別的模型,下面是最后一組數(shù)據(jù)的輸出結(jié)果,證明我們的程序運(yùn)行成功!
展望
當(dāng)然,小編給大家分享的這個(gè)方法,不僅可以應(yīng)用在神經(jīng)網(wǎng)絡(luò)AI推理上,可以當(dāng)作一個(gè)低配版的flashloader,以供大家靈活地更新靜態(tài)數(shù)據(jù)資源。
-
mcu
+關(guān)注
關(guān)注
146文章
17162瀏覽量
351349 -
FlaSh
+關(guān)注
關(guān)注
10文章
1635瀏覽量
148091 -
工具包
+關(guān)注
關(guān)注
0文章
46瀏覽量
9541
原文標(biāo)題:一種基于MCU的神經(jīng)網(wǎng)絡(luò)模型在線更新方案之MCU實(shí)戰(zhàn)篇
文章出處:【微信號(hào):NXP_SMART_HARDWARE,微信公眾號(hào):恩智浦MCU加油站】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論