我们会从ICACHE的特性、架构,使用注意事项来介绍,最后会运行一个标准的Coremark程序来比较使能和不使能ICACHE的两种情况,L5的性能比较。从数字大家可以直观的看到,在使能了ICACHE之后,L5性能的显著提高。
这是ST的第一块Cache。但是在STM32系列家族产品里,大家并不是第一次使用cache,在STM32F7和H7的,基于CM7内核产品里,大家已经用过了,但是那个是内核的一部分,这里的ICACHE,从在系统框图里的位置来看,它位于CM33内核输出的C-Bus总线 和 AHB5总线矩阵之间。不再是内核的一部分,而是ST自己实现在L5上面的,和其他同样采用CM33内核的MCU相比,体现我们差异化的部分。
Cache的功能,众所周知,主要就是通过减少对 相对内核来说访问速度较慢的主存储器 的访问,提高系统性能,同时也能降低系统功耗。
在cache普通功能之上,ST对这个ICACHE还做了额外的优化,以进一步提高cache的性能。主要从提高cache并行性方面,提供cache命中率方面,以及在不幸发生了cache失效时,尽量降低失效延迟方面采取了相对应的措施。有一点需要首先说明,以尽早撇清大家可能的一个误解就是:这个模块名字叫做ICACHE,并不是说它只能对load指令进行缓存。它也可以对load数据进行缓存。但是同时另外一点要强调的是,它是一个单向Cache,只有读方向,没有写方向。因此这个ICACHE也没有和写操作相关的policy,诸如什么write through,write back等。我们可以理解为,它是用于对CODE区域的指令和常数数据,做缓存。这个Code区域,是ARM定义的0x0到0x2000 0000这512M字节的区域。因为只有对这个区域的访问,CM33内核才会从C-bus发出来,其他区域的访问,CM33内核是走S-bus发出来的。
根据前面课程的介绍,512K 片上用户Flash,无论内核是走安全视角的地址访问,还是从非安全视角的地址访问,都会落在0x2000 0000以内的地址访问,因此对片上Flash的访问***是可以被缓存的。SRAM,在以前的STM32芯片,物理地址都在0x2000 0000开始,使能了TZ后,会有一个在0x3000 0000的安全别名区。看起来好像都无法使用ICACHE的缓存。但是在前面一章讲SRAM时我们介绍了,L5的片上SRAM在设计上多给它挂了一对地址,0x0A00 0000开始的非安全别名地址,和0x0E00 0000开始的安全别名地址。从这一对地址访问SRAM,SRAM可以享受缓存功能。但是由于SRAM本身有足够快的访问速度,并且SRAM通常作为堆栈空间,而ICACHE也不支持写操作,因此一般不会让ICACHE的缓存功能作用于对SRAM的访问。但是ICACHE不仅仅提供缓存功能,后面我们再讲,现在概览阶段不展开。
ICACHE是一个Securable的外设。确切的讲,它的【控制部分】,或者说【寄存器部分】是Securable的。系统上电缺省状态下,ICACHE的寄存器可以被安全世界和非安全世界访问;我们也可以通过GTZC来把它配置成仅能被安全世界代码访问。但是无论如何,一旦配置好了之后,安全代码和非安全代码,都可以享受ICACHE的缓存功能。ICACHE还有一个安全特性就是,当系统检测到入侵事件,硬件会被ICACHE内容清空。
ICACHE直接挂在AHB5的总线矩阵上,而且是一个主设备,和SDMMC一样。
它由AHB时钟提供模块工作的心跳,在MCU复位信号稳定释放后,ICACHE自动启动一个cache invalidate的流程,把每条cache line里的valid位都清零,表示此时自己是一个空cache。invalidation流程结束后,cache默认处于关闭状态,需要用户自己去手动使能,以提高系统性能。使能之前,要特别注意,此时没有正在进行中的cache invalidation过程。这个过程除了会在MCU复位后自动启动,也可以由用户代码软件主动去invalidate cache,或者软件关闭ICACHE功能而触发。
如何确认当前是否有正在进行的cache invalidation过程呢,就是去检查状态寄存器中的一个busy标志即可。在invalidation过程中,BUSY标志被硬件一直保持1,invalidation过程结束后,BUSY标志被硬件清零,而BSYEND标志又被硬件置1了,并可以产生对应中断。
我们来自己仔细看一下ICACHE的功能框图。它位于图中左边的CM33内核,和图中右边的AHB总线矩阵之间。有一个AHB slave接口,接收来自CM33内核的AHB transaction;有两个AHB master接口,用于发起AHB transaction。这三个都是和代码执行流相关的。上面还有一个AHB slave接口,它是用来配置ICACHE模块里的寄存器的。下面是普通cache的存储结构,包括data memory和tag memory。我们这块ICACHE有8K字节的容量,指的纯粹是data memory这一块。中间部分,是cache模块的控制逻辑。除了普通cache具有的状态机FSM、cache line替换算法LRU,还有一个remap逻辑。
其实,ICACHE它不仅仅是一个指令和数据缓存区,它还可以把输入的AHB transaction地址进行替换,按照一定规则来分配这条transaction从两个AHB 主端口中的哪个出去。这里的地址替换,是在某些条件下才发生的,并不是每条CM33内核过来的transaction都做地址替换。图中有两条箭头,蓝色的箭头,表示ICACHE没有使能的情况下,或者该transaction不是cacheable的traffic,那么它直接bypass掉cache的普通缓冲功能,只是从入口的AHB slave端口进来,默认从AHB master1号端口出去。
如果这条transaction里面的地址是用的别名区,并且原生地址所在的region映射功能,有在ICACHE控制逻辑中使能,就从AHB master2号端口出去。这个就是区域地址remap,我们后面来讲。绿色的箭头表示,ICACHE功能使能了,并且该transaction是cacheable的traffic,那么就要按照典型cache操作那样,去根据transaction地址的某些位段,定位cache line,然后根据这条cache line是否valid以及它的tag地址信息是否匹配来做后续操作。
双AHB主端口输出,可以减少延迟。
比如,ICACHE默认把对片上Flash和SRAM的访问,当做快速访问,分配到AHB 端口1,把对外部memory的访问作为慢速访问,分配给AHB 端口2。如果对外部memory的访问,在cache中失效,需要读取外部存储区来充填cache line,将占用AHB 端口2。在这个过程中,如果有中断发生,需要去片上Flash取中断向量地址并执行,是可以同步地,在AHB1端口进行的,内核无需等待。
那么哪些transaction的traffic属于cacheable的呢?按照参考手册里说:根据AHB transaction memory lookup attribute来看,如果是1表示这条transaction是cacheable的;而这个AHB attribute的值,取决于MPU对该区域的设置。还有一个必要条件,要能够享受到ICACHE提供的缓存复位,transaction必须走C-AHB总线才能来到 ICACHE,如果transaction走的S-AHB总线,就直接到AHB总线矩阵上了,而根本来不到 ICACHE。所以transaction里面被寻址的区域,地址范围要在0到0x2000 0000之间,即ARM 内核视角的CODE region。按照STM32L5默认的存储区域实现,片上flash是原生就在这段地址里,SRAM的原生地址也可以落在这段地址里,而外部memory,本来是在0x6000 0000开始的地址,可以通过地址重映射,remap到0x2000 0000以下的空间,来享受ICACHE提供的指令和数据缓存服务。
落在这个地址范围内的访问,是cacheable traffic的必要条件之一。 ICACHE本身被enable,是tag memory和data memory会被用到的必要条件之二。还有一个条件要满足,就是用户对0x2000 0000以内的地址的区域,没有通过MPU去显式地disable cache功能。ARM内核默认这个区域范围都是cacheable的,用户可以使用MPU来override cacheable这个属性。比如当通过0x0A00 0000来使用SRAM的堆栈功能,就可以用MPU来配置那一段区域不要被cache;否则当写操作发生在这个堆栈上,就会被识别成一个cacheable的写操作,这会触发ICACHE的错误标志置位。
以上三个条件都满足时,这条transaction就是一个cacheable的traffic。它包含的地址信息,会去和tag memory中的记录做比较,然后就是去做一个传统cache的工作了:地址比对、如果hit就把对应数据直接返回;否则去读取真实物理区域,发起所谓的cache line refill。
当发生cache miss时,势必需要去读取真实物理区域,这就会增大内核等待时间。我们通过hit-under-miss机制,可以减小失效延迟。
Hit-under-miss,顾名思义就是,在前一次cache miss造成的cache line refill过程中,即使refill还没有完成fill 128个位,如果下一个transaction要求的数据就在已经被fill好的部分,ICACHE可以立即响应这次的请求,算是一个hitting request。
举个例子,如图:
在第一个时刻,CM33内核请求访问地址i上的数据,因为这个地址所在的区域,是在可cache的范围内,所以transaction走C-BUS,来到了ICACHE模块,ICACHE又是处于使能状态,因此去ICACHE的tag memory做查找。比较了一圈发现这个地址的内容没有被ICACHE缓存,于是发生了cache miss。那么ICACHE对存储区发出读请求,一次读取128位、即16个字节,来填到ICACHE的一条line中。
在第二个时刻,存储区读出的128位,给到ICACHE,填写它的某一条line。并且第一时间,把第一个时刻内核要求的Data_i所在的字,这里叫做“critical word”最先返回给内核。在ICACHE被refill 128个位的过程中,CM33内核又发出了第二个请求,希望访问地址j上的数据。而这个数据正好就在已经被fill的那条cache line上。
第三个时刻,ICACHE直接把已经在cache line中的Data_j返回给内核,避免了一次cache miss。虽然此时,这条cache line还没有填充完毕,它的标志位valid还没置上,但是ICACHE模块可以检测出这是一个可以马上响应的访问请求,而立马给与响应,不再耗费内核等待时间。 刚才说了cacheable的traffic,接下来再看一下,ICACHE功能框图里另外一条蓝色的路径,所谓的“non cacheable traffic” 当CPU发出的取指或者取数据,是target到0x0到0x1ffff ffff之外的区域,那么transaction根本不走C-AHB bus,也就根本谈不上是否cacheable。
这里说的non-cacheable traffic,是指还是通过C-Bus来到ICACHE模块,但是由于ICACHE没有使能,或者MPU设置了该transaction是不cacheable的。
这种traffic就是这个胶片里要讨论的non cacheable traffic。
既然不享受被缓存的福利,那为啥还要走到ICACHE这条路上来呢?那,就是为了用到这里的双AHB主端口。
对片上Flash和SRAM的访问,不使用地址重映射,就可以走C-AHB bus来到ICACHE。对Flash使用0x0800 0000或者0x0C00 0000;对于SRAM使用0x0A00 0000或者0x0E00 0000。如果发生cache miss,或者它是一个non cacheable的transaction的时候,就走AHB 端口1出去,访问实际的片上Flash或者SRAM。
对外部memory,不用地址重映射,就会走S-AHB bus;走重映射地址,才可以通过C-AHB bus来到ICACHE。如果发生cache miss,或者它是一个non cacheable的transaction,必须走AHB 端口2出去。外部memory重映射到0x0~0x1FFF FFFF范围内的什么地址,以及要走AHB2 端口出去,都必须事先通过ICACHE的控制寄存器配置好,方可正确使用。
对于Cacheable的transaction,ICACHE要发挥它的第一本职工作,缓存。
现在我们来看看这块ICACHE的物理组织结构,以及两种工作模式下各种的Cache参数。
右图就是默认工作模式,2-way set associative下的工作原理。2-way set associative,中文说法就是:两路集相关,或者两路组相关。是和1-way或者direct模式相对应的。
按照ICACHE的实现参数,在两路组相关模式下:S,代表set,=2;就是图中一左一右两个set;E,代表每个set里有几条cache line。2-way模式下,每个set有256条cache line,line的index有8位位段来表示;右图里可以看到,无论是tag memory,还是data memory都是有256行。每个cache line,在我们的ICACHE实现的是128位,16字节,因此B,就是byte offset,由4位的位段来表示。前三个参数,S,E,B相乘就是当前cache的总容量,8K字节。当某一条cache line被定位后,这16字节属于哪个地址的,它所在的line编号,已经确定了其中的8位地址位域,剩下高20位的地址,就是由这条cache line的tag地址信息来表示。
胶片中左下的表格,是在1-way,直接映射模式下的参数,这种模式下,只有一个set,因此S=1,而cache line有512行了,因此tag的位段只需要19位。
在默认工作模式下,当AHB slave端口传来一条CM33内核的transaction,检查里面的32位HADDR_in地址。根据中间的8位地址,确定要去检查哪一行cache line。行号确定了,由于是2 way,那么这一行有两个cache line供进一步审查。分别看way0和way1的cache line里,各自的tag信息,是否和HADDR_in的高20位位段匹配。如果有,则看那一个cache line的valid位如何。valid=1,说明cache hit,即缓存命中,直接把这条cache line的指定字节、或者字,或者半字返回给CPU。至于从这条cache line的16字节中挑哪里的内容返回,则由HADDR_in的低四位决定了。如果tag位段符合但是valid是0,或者干脆就没有匹配的tag位段,就是cache miss了。需要进行后续的cache line refill。从以上过程描述可以看出,2way模式下,给定一个访问请求中32位地址,和目标地址的中间8位地址匹配的全地址,在2way下,因为有2个tag可以存放,因此可以存两条不同的高20位地址;而1way下,只有一个tag。那么前者的命中率是会高一些。
在cache miss的情况下,一方面CPU去Flash存储区读出数据返回给CPU;一方面对Cache执行一次refill操作。Cache miss已经造成了数据访问的延迟,为了减小这个延迟,我们采取“critical-word-first”和”hit under miss”的策略。“hit under miss”前面有图解介绍过,后面我们也以图解的方式,介绍cache line refill 和 critical word first的策略。
在发生了Cache miss的情况下,势必要发起cache line refill。那么新读进来的数据放在哪条line上呢?
对于one-way模式,又称直接映射模式,没有其他选择,替换掉的就是由transaction地址中line这个位段定位出的那一行,由于是one-way,因此只有一个cache line。就把它换掉。以图里的例子,假设CPU发出的访问请求,地址是0x5123,按照One-way模式下的参数分配,低四位,3,是cache line里的目标字节的偏移量;往上9位,定位是去看哪一行,9位的位段,对应数字是0x112,即去看第274行。
如图,假设line 274,它此时的tag是0x03,和目标地址需要的tag 0x02不匹配。说明cache miss。那么CPU去Flash 0x0513读出的内容,就会放在line 274这里,把CCDD的内容替换掉,并且这一行的tag位域里的值,从0x03换成0x02。对于One-way cache,当发生cache miss导致的cache line refill时,没有替换策略一说。
但如果是另外一种模式,2-way,就不一样了。同样的CPU transaction目标地址,0x5123,在2-way模式下,line位域只有8位,就是0x12,因此去第18行,check tag。由于是2way,因此tag0和tag1都要check。在这个例子中,非常不幸,tag0和tag1的内容,分别是0x01和0x,都不是期望的0x05。所以进入Cache miss状态,需要读取Flash,得到128位的数据。读上来的新数据放在哪里呢?肯定是在line18,这是目标地址的line位段决定的,唯一需要抉择的是,是把way0的line frame踢出去,换上新内容,还是把way1的line frame踢出去,换成新内容。这就涉及到【替换算法】的问题。在STM32L5的这块ICACHE中,我们采用常见的LRU原则,即least recently used。把最近用的最少的那个信息块驱逐出去,这里驱逐是Cache中的一个专门术语,Eviction。
ICACHE还支持区域地址remap,因此对外部memory的访问,本来是0x6000 0000开始的地址区间,重映射后是0x2000 0000以下的地址空间。HADDR_in是0x2000 0000以下的地址值,存放在tag memory里的地址信息,是重映射后的地址。
进一步结合TrustZone来考虑,对于片上Flash,从0x0800 0000开始的原生地址,IDAU定义了对应在0x0C00 0000的别名地址空间。比如说对片上flash第一个字节的访问,可以使用0x0800 0000也可以使用0x0c00 0000。这两条地址,是以不同的tag信息存放在不同的cache line中的。
为了体会这一点,我们稍后来做一个hands-on。
在做hands on之前,我们把critical word first策略,简单介绍一下。
|