Cache技术在星辰处理器中的应用

引言
目前,灵动微控制器产品体系中,适配了micropython的,有mm32f3(mm32f3273g9p,arm cortex-m3)和mm32f5(mm32f5277e9p,armchina star-mc1),从官方数据来看,使用星辰处理器(star-mc1)的mm32f5对指令的处理效率要高于使用cortex-m3处理器的mm32f3。如图x所示。
figure-arm-core-benchmark 图x 星辰处理器同其他arm处理器内核对比
然而,同一份micropython的启动过程,在使用同样主频(120mhz)的情况下,在mm32f5平台上运行,总是莫名其妙地慢好多。。。昨天跟同事hao聊sdk的样例工程对cache问题的处理策略时,偶然意识到,早期为mm32f5适配micropython的时候没有考虑过cache,随即赶紧翻出来micropython的代码,果然没开。好吧,更新micropython项目下mm32f5平台的启动代码,替换来自于sdk中的system_mm32f5277e.c和system_mm32f5277e.h文件。
/* * copyright 2022 mindmotion microelectronics co., ltd. * all rights reserved. * * spdx-license-identifier: bsd-3-clause */#include hal_device_registers.h#if defined (__vtor_present) && (__vtor_present == 1u)  extern uint32_t __vector_table;#endifvoid systeminit(void){#if defined (__fpu_present ) && (__fpu_present == 1u)  #if defined(__fpu_used) && (__fpu_used == 1u)    scb->cpacr |= (scb_cpacr_cp10_mask | scb_cpacr_cp11_mask); /* set cp10, cp11 full access */  #endif#endif /* __fpu_present */#if defined (__icache_present )&& (__icache_present == 1u)#ifndef icache_disabled  if (scb->clidr & scb_clidr_ic_msk)  {    scb_enableicache();  }#endif /* dcache_disabled */#endif /* __icache_present */#if defined (__dcache_present) && (__dcache_present == 1u)#ifndef dcache_disabled  if (scb->clidr & scb_clidr_ic_msk)  {    scb_enabledcache();  }#endif /* dcache_disabled */#endif /* __dcache_present */}/* eof. */  
其中,如果没有调用scb_enableicache()和scb_enabledcache()函数,默认关闭icache和dcache,配置宏__icache_present和__dcache_present来自于mm32f5270的芯片头文件mm32f5277e.h。
#define __star_rev                0x0100u   /* core revision r1p0 */#define __sauregion_present       0u        /* sau regions present */#define __mpu_present             1u        /* mpu present */#define __vtor_present            1u        /* vtor present */#define __nvic_prio_bits          3u        /* number of bits used for priority levels */#define __vendor_systickconfig    0u        /* set to 1 if different systick config is used */#define __fpu_present             1u        /* fpu present */#define __dsp_present             1u        /* dsp extension present */#define __icache_present          1u        /* define if an icache is present or not */#define __dcache_present          1u        /* define if an dcache is present or not */  
编译,验证。找来两块之前下载了micropython固件的mm32f5开发板,向其中一块板子下载启用icache和dcache之后的新固件。运行程序后,同使用之前版本固件的程序相比,果然有明显的提升。如图x所示。
video-mm32f5-micropython 图x 启用cache前后的micropython启动过程对比实物
图中板子运行程序启动micropython启动流程后,执行文件系统中python源文件,闪烁小灯。图中下方的板子使用了启用cache的程序,明显先完成micropython启动过程,先开始执行闪烁小灯的程序。
提交更新到代码仓库。bingo!
commit 46372ed15d5769b25775a209c56d62c4cfc3ac5d (head -> master, origin/master, origin/head)author: andrew su date:   wed jun 14 1106 2023 +0800    update the startup code to enable the icache for mm32f5.    - this fix would accelerate the speed of running the instruction      sequence on mm32f5, which has the icache and dcache integrated.    signed-off-by: andrew su   
启用cache后,之前在mm32f5微控制器平台上运行micropython小概率会出现hardfault的问题也得到的缓解,颇有“治好了某人多年老寒腿”的赶脚。^v^。
cache的工作原理
cache主要解决高速的cpu访问低速的存储器均衡速度差的问题,cache通过预取数据/命令的机制,低速但整块地从低速存储器中取数据块,然后快速但串行地向cpu送数据流。高速的处理器大多使用哈佛结构,即使用指令总线和数据总线分别取指令和数据,对应有icache和dcache。
cache能够有效工作基于几个基本前提:
空间局部性:在最近的未来,使用到的信息和当前使用的信息在空间上会是邻近的。这个因为数据大部分都是连续存储的。所以主存当中的数据都是成块传输到cache当中。
时间局部性:在最近的未来,使用到的信息可能是当前正在使用的信息。由于cpu本质上一个死循环,里面还有很多小循环的运行和操作,所以当前使用到的数据很可能会在循环当中,这样当前的数据有可能会在将来在再被调用一次。
另外,关于cache还有其余部分需要了解:
cache与主存的映射方式:解决主存内数据块和cache当中数据块的对应关系。
替换算法:cache小,主存大。如果cache中数据存满了之后,如何操作。
cache写策略:如果cpu修改了cache中的副本,如何确保cache中的数据和主存中的母本数据保持一致。
关于cache的工作原理,以及设计机制,可参考计算机专业考研四大专业课之《计算机体系结构》,以及参考文献中的《一文搞懂cache基本原理》。
需要关闭dcache的情况
在微控制器系统中,有时会需要直接使用内存里的数据同外设交互,而不进入cpu,例如使用dma(另一个总线主机ahb master,但不需要cache功能)相关,这种场景下,使用cache的意义就不大了,甚至可能会出现数据不一致的风险,此时,就需要关闭cache才能让系统正常工作。具体来说,icache可以继续启用,毕竟指令都是送到cpu中执行,但dcache需要关掉,否则通过cpu写入到内存的数据未能及时同步物理内存时(基于cache的写策略),启动dma时搬运的数据不一定是实际需要传送的数据。另外,所有使用到“内嵌”dma的外设模块的工程中,也需要小心谨慎地使用dcache,例如一些usb外设、enet外设、显示加速器、以数据块为操作单元的加速计算模块等。
关icache的情况虽然不多,但也存在,例如在涉及iap应用中,从存放指令的介质中擦除指令、写入新指令后,再读指令,实际的新指令可能尚未替换到icache中存放的旧指令,导致程序执行错误。
鱼和熊掌都想要
关闭dcache之后,cpu读数据的速度会明显慢很多,例如本文一开始展现的情况。怎样才能提升访问访问速度的同时,又能确保数据一致性呢?总不会人为频繁地开关cache吧(很多微控制器对启动cache的时机也有特别要求,需要在运行应用程序的一开始就要开启)。这里有两种可能的思路,供大家参考:
使用内存保护单元mpu
使用内存隔离/同步指令
这两种方法,分别是在空间上和时间上对数据进行隔离,控制仅在必要的空间上或时间上启用和关闭cache。
使用内存保护单元mpu
mpu(memory protection unit)内存保护单元在armv7-m架构下被引入。在 armv7-m架构下,cortex-m3和cortex-m4处理器对 mpu 都是选配的,不是必须的。armv8-m架构下继续沿用了mpu,星辰处理器star-mc1就使用了armv8-m。
mpu是一个可以编程的设备模块,可用来定义内存空间的属性,比如特权指令和非特权指令,以及cache是否可访问。armv7-m通常支持8个region,每个region 代表一段连续的区域。
关于mpu的用法,可参见参考文献中的《arm-mpu内存保护单元详解》和《armv8-m architecture reference manual》。
使用内存隔离/同步指令
arm的指令集中,有内存隔离指令dmb(data memory barrier)、dsb(data synchronization barrier)和isb(instruction synchronization barrier):
数据存储器隔离。dmb 指令保证:仅当所有在它前面的存储器访问操作都执行完毕后,才提交(commit)在它后面的存储器访问操作。
数据同步隔离。比 dmb 严格:仅当所有在它前面的存储器访问操作都执行完毕后,才执行在它后面的指令(亦即任何指令都要等待存储器访问操作——译者注)。
指令同步隔离。最严格:它会清洗流水线,以保证所有它前面的指令都执行完毕之后,才执行它后面的指令。
在一些arm程序代码中,会用到__dsb() 指令,特别是在一些中断处理函数中。例如:
//中断定时器pit中断处理函数void pit_led_handler(void){    /* clear interrupt flag.*/    pit_clearstatusflags(pit, kpit_chnl_0, kpit_timerflag);    pitisrflag = true;    __dsb();}  
程序通过中断信号进入中断处理函数时,首先应当清除相应的中断标志位,但有些cpu的时钟太快,快于中断使用的时钟,就会出现清除中断标志的动作还未完成,cpu就又一次重新进入同一个中断处理函数,导致死循环,__dsb() 指令的作用就是避免上述情况的发生。
总结
本文从修复micropython启动程序在mm32f5微控制器上比较慢的问题,体验了星辰处理器中cache的作用。简单介绍了cache的工作原理和机制,重点介绍了使用cache可能存在的风险,并进一步探讨了如何能用到cache高速存取的同时避免数据不一致的情况。


高电流栅极驱动器如何帮助系统实现更高的效率
LIS3DH:用于Pebble智能手表MEMS运动传感器
未来金融区块链数字资产交易系统开发形成社群经济体
金属3D打印的钥匙,将彻底杜绝被复制的可能
对于园艺照明行业的持续发展以及实现新一波的农业技术的因素分析
Cache技术在星辰处理器中的应用
小米全力压轴“小米6”,不管你买不买,反正我要买!
什么是Git基本操作指令
变频器的基本原理、分类及应用领域
基于NFC技术与RFID技术芯片实现双向预付费系统的设计
利用叶面积仪研究植物生长状况
软板(FPC)相关术语解释
智慧应急物资仓库管理系统(DW-S300)
光学指纹头与半导体指纹头优缺点分析
软银将在未来十年投入2万亿日元用于部署5G网络
Σ-Δ模数转换器 数字滤波器类型
电工知识—SIMATIC S7-1500 PLC典型模拟量输出接线图(4)
贞光科技:薄膜电容在新能源车中的作用有哪些?
SC5102接口可pin对pin兼容TLK2501
STM32单片机设计extern全局变量的定义解析