本篇將是系列文章的最后一篇,在MCU上進(jìn)行實(shí)際的部署。 小編就不再給大家復(fù)習(xí)歷史了,直接開始正題。如果想溫習(xí)前兩部分內(nèi)容,請點(diǎn)擊此處和此處。
首先給大家介紹一下我們將要使用的離線Flash燒寫工具,名為JFlashLite,他有一個兄弟叫JFLash,既然是Lite就是輕量版本的JFlash工具。
當(dāng)然,需要大家自行下載Segger Jlink工具包。是的,我們需要一條JLink。隨后找到JFlashLite工具,他長這樣(請忽略這里的1176,我們還沒有配置):
比起下圖他的哥哥JFlash,是不是很清爽的界面: 
當(dāng)然,我們今天的主角是JFlashLite,稍后會介紹如何進(jìn)行配置并下載
程序編寫
工具介紹完了,下面正式開始編寫配套程序,所使用的平臺是i.MX RT1060, 開發(fā)板是IMXRT1060EVKB:
劃分FLASH以及sdram區(qū)域,sdram區(qū)域負(fù)責(zé)加載flash中內(nèi)容,加速內(nèi)存訪問:我們的開發(fā)板上有一個8MB的Flash,內(nèi)存映射地址為0x60000000,首先保留1MB的頭部區(qū)域以存儲代碼,其作用后續(xù)會提到。那么我們就剩下7MB大小的空間,由于數(shù)據(jù)一般較大,劃分4MB作為數(shù)據(jù)存儲區(qū),剩下的3MB存儲模型。同樣的,在sdram區(qū)域,內(nèi)存映射地址為0x80000000也同樣開辟這樣大的兩塊內(nèi)存區(qū),不過,為了防止數(shù)據(jù)溢出,我們僅針對于sdram區(qū)域,在數(shù)據(jù)區(qū)和模型區(qū)中間插入256B的數(shù)據(jù)保護(hù)區(qū),并填充0xdeadbeef,最終的內(nèi)存布局如下:
Flash上內(nèi)存分配

Sdram內(nèi)存分配

利用JflashLite工具進(jìn)行燒寫:
打開JFlashLite工具點(diǎn)擊…選擇器件為1062xxxxA:

選擇對應(yīng)的數(shù)據(jù)文件以及模型進(jìn)行燒寫,燒寫地址要依據(jù)上述定義的分配,即模型數(shù)據(jù)燒寫到0x60100000,圖像數(shù)據(jù)燒寫到0x60400000:


選擇好之后,點(diǎn)擊Program Device即可進(jìn)行燒寫;針對于模型數(shù)據(jù),yao注意將以.tflite結(jié)尾的模型文件,重命名為.bin文件。
Scf文件編寫,主要考慮到,運(yùn)行時,將flash中的數(shù)據(jù),拷貝到sdram中,以提高運(yùn)行速度,這里聲明兩個區(qū)域負(fù)責(zé)存儲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
主代碼編寫,針對于邊界溢出檢測代碼,簡單起見,只檢測首地址處值,不同則表示溢出,死循環(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 (;;) {}
}
}
測試數(shù)據(jù)如何獲取呢,利用我們剛才定義的data_t結(jié)構(gòu)體:
data_t *image = (data_t*)img_data; image_data_ptr = image->data;
這樣,我們就拿到了存儲在flash并且已經(jīng)被搬運(yùn)到了sdram上的數(shù)據(jù)了,接下來就是編譯運(yùn)行了。
實(shí)際運(yùn)行與測試
測試時候,要注意首先確保我們的開發(fā)板已經(jīng)是XIP啟動,即從Nor Flash啟動,并且保證在flash的頭部,燒寫過一個完整的可執(zhí)行鏡像,比如hello_world程序,其中會包含F(xiàn)lash的一些配置信息,這一步小編就不再舉例,還請大家自行準(zhǔn)備。
這樣,我們的BootRom會據(jù)此幫我們配置好Flash,程序中就不用手動調(diào)用Flash的初始化代碼了。
還要注意,代碼要全部運(yùn)行在SDRAM或是其他介質(zhì)上,因為我們已經(jīng)將flash據(jù)為己有了。
下面是內(nèi)存鏡像的樣子,首先是model:

再者是測試數(shù)據(jù),前四個uint32類型的數(shù)據(jù)剛好是小編這里定義的數(shù)據(jù)長度100張128*128*3的rgb彩色圖:

連上板子和PC,并打開串口控制臺即可查看輸出結(jié)果,小編所選用的模型是一個水果識別的模型,下面是最后一組數(shù)據(jù)的輸出結(jié)果,證明我們的程序運(yùn)行成功!

展望
當(dāng)然,小編給大家分享的這個方法,不僅可以應(yīng)用在神經(jīng)網(wǎng)絡(luò)AI推理上,可以當(dāng)作一個低配版的flashloader,以供大家靈活地更新靜態(tài)數(shù)據(jù)資源。
-
mcu
+關(guān)注
關(guān)注
147文章
18924瀏覽量
398002 -
FlaSh
+關(guān)注
關(guān)注
10文章
1748瀏覽量
155506 -
工具包
+關(guān)注
關(guān)注
0文章
48瀏覽量
9897
原文標(biāo)題:一種基于MCU的神經(jīng)網(wǎng)絡(luò)模型在線更新方案之MCU實(shí)戰(zhàn)篇
文章出處:【微信號:NXP_SMART_HARDWARE,微信公眾號:恩智浦MCU加油站】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
如何在TINA上進(jìn)行電荷放大器的仿真?
請問如何在keil μVision 5上進(jìn)行ARM編譯器的代碼優(yōu)化?
如何在原電路上進(jìn)行添加保護(hù)電路和修改 ,請高手指導(dǎo)!
如何在STM32F103上進(jìn)行CAN通信的管腳重映射配置和使用
如何在MCU上進(jìn)行內(nèi)存管理與分配
如何在MDK5上進(jìn)行STM32設(shè)計跑馬燈程序并進(jìn)行編譯及仿真?
如何在esp8266 Node MCU的硬件上部署LVGL
如何在Ubuntu系統(tǒng)上進(jìn)行中文支持
使用Devtron在Kubernetes多集群上進(jìn)行開發(fā)
如何在樹莓派 AI HAT+上進(jìn)行YOLO目標(biāo)檢測?
如何在樹莓派 AI HAT+上進(jìn)行YOLO姿態(tài)估計?
如何在MCU上進(jìn)行實(shí)際的部署
評論