cathy 在 提交
本篇将是系列文章的最后一篇,在MCU上进行实际的部署。
小编就不再给大家复习历史了,直接开始正题。如果想温习前两部分内容,请点击< a href="http://mcu.eetrend.com/content/2022/100562111.html">此处和此处。
首先给大家介绍一下我们将要使用的离线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加油站
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。