ARM虚拟内存及缓冲区实现/管理(无OS)

来源:互联网 发布:复制信息再在淘宝打开 编辑:程序博客网 时间:2024/06/04 19:28

查到一些概念资料:

一)TLB

1)TLB的概述

TLB是一个内存管理单元用于改进虚拟地址到物理地址转换速度的缓存.

TLB是位于内存中的页表的cache,如果没有TLB,则每次取数据都需要两次访问内存,即查页表获得物理地址和取数据.

 

2)TLB的原理

当cpu对数据进行读请求时,CPU根据虚拟地址(前20位)到TLB中查找.
TLB中保存着虚拟地址(前20位)和页框号的对映关系,如果匹配到虚拟地址就可以迅速找到页框号,通过页框号与虚拟地址后12位的偏移组合得到最终的物理地址.
页框号可以理解为页表项

如果没在TLB中匹配到虚拟地址,就出现TLB丢失,需要到页表中查询页表项,如果不在页表中,说明要读取的内容不在内存,需要到磁盘读取.

TLB是MMU中的一块高速缓存,也是一种Cache.

在分页机制中,TLB中的数据和页表的数据关联,不是由处理器维护,而是由OS来维护,TLB的刷新是通过装入处理器中的CR3寄存器来完成.

如果MMU发现在TLB中没有命中,它在常规的页表查找后,用找到的页表项替换TLB中的一个条目.

 

二)cache
1)cache的概念:
cache是为了解决处理器与慢速DRAM设备之间巨大的速度差异而出现的
2)Cache的工作模式
数据回写(write-back):这是最高性能的模式,也是最典型的,在回写模式下,cache内容更改不需要每次都写回内存,直到一个新的cache要刷新或软件要求刷新时,才写回内存.
写通过(write-through):这种模式比回写模式效率低,因为它每次强制将内容写回内存,以额外地保存cache的结果,在这种模式写耗时,而读和回写模一样快,这都为了内存与cache相一致而付出的代价
预取(prefectching):一些cache允许处理器对cache line进行预取,以响应读请求,这样被读取的相邻内容也同时被读出来,如果读是随机的,将会使CPU变慢,预取一般与软件进行配合以达到最高性能.
三)内存一致性

内存一致性涉有到一系列的问题:

1)多处理要系统更新cache时,一个处理器修改了cache的内容,第二个处理器将不能访问这个cache,直到这个cache的内容被写内存.
在现代处理器中硬件已经做了精心的设计,确保这种事情不会发生,硬件负责保持cache在各个CPU之间一致.

2)外围硬件设备可以通过DMA(Direct Memory Access)访问内存,而不让处理器知道,也不会利用cache,这样在内存和cache之间就会出现不同步的情况.
管理DMA的操作是操作系统的工作,比如设备驱动程序,它将保证内存与cache的一致性.

3)当在cache中的数据比内存中的数据老时,称为stale.如果软件初始化DMA,使设备和RAM之间传递数据,那么软件必须告诉CPU,cache中的条目必须失效.

4)当在cache中的数据比内存中的数据新时,称为dirty.在设备驱动程序允许一个设备经DMA从内存读数据时,它必须确保所有的dirty条目写进内存.也叫做flushing或sync cache.

 

ARM CPU地 址概念:
虚拟地址VA,变换后的虚拟地址MVA,物理地址PA
S3C2440以段(1M),页(64K,4K,1K)进行转换,段映射只有一级页表,页表共有4096的条目,地址A[31]-A[20]进行索引
这个页表要么记录对应1M物理空间的地址,要么存储二级页表的地址,用这12位来索引一级页表,得到一个4字节的描述符
描述符(0b10)包含该段访问权限,描述符的段索引和MVA就构成这个虚拟MVA对应的物理地址
TTB base代表一级页表的地址,写入CP15的C2寄存器一级页表必须16K对齐位[13:0].其中低两位[1:0]为0
以S3C2440的MMU为例
CPU发的VA经协处理器变换成MVA后,高12位作为页表索引,低20位作为段偏移地址,假设此时发出的MVA为0x30100000假设我们设置的页表基地址为0x33ff 8000 其[31:14]为0011 0011 1111 1111 10 在取MVA页索引号构成低14位 [13:2] 这里应该是
0011 0000 0001 00 低两位为0,因为ARM指令以字对齐,及一条指令占4字节 这样一组合就为 0x33ff 8C04,此时就在内存0x33ff 8C04 处取出描述符的高12位与MVA的低20位组成PA
我们的SDRAM位于BANK6,开始地址是0x30000000 ,以1M为一个段算下来刚好是第(0xC04/4)=769条描述符
MMU_SetMTT(0x30000000,0x30100000,0x30000000,RW_CB);   //bank6-1
MMU_SetMT(0x30200000,0x33e00000,0x30200000,RW_NCNB); //bank6-2
MMU_SetMTT(0x33f00000,0x33f00000,0x33f00000,RW_CB);   //bank6-3
以上是TQ2440的映射表,页基地址是0x33ff 8000 ,MVA为0x3000 0000 映射到0x3000 0000 在页表什么位置呢?
描述符表的计算及添加
设定:
      因为段描述符可以映射1M的空间,需要映射多少M就需要多少个连续的段描述符
1. 将虚拟结束地址和起始地址右移20位(除1M)相减,得段描述符数量用来控制需要循环的次数
2. 根据TLB和虚拟起始地址得到在页表描述符的起始地址
3. 将需要映射的物理地址右移20位(除1M)
4. 将移位后的物理地址填入起始位置页表描述符的[31:20]位,并将描述符属性加入描述符相应位置[11:0]AP、Domain、CB、10(段描述符标志)
5. 将描述符起始地址加4(描述符为4个字节)指向下一个描述符
6. 将移位后的物理地址加1(实际上在基地址的位[20]加1)指向下一M物理空间。
7. 循环至第4步,至到所有描述符被赋值。

流程示意图:


函数原型:
void MMU_SetMTT(int vaddrStart,int vaddrEnd,int paddrStart,int attr)
{
 volatile U32 *pTT;
 volatile int i,nSec;
 pTT=(U32 *)_MMUTT_STARTADDRESS+(vaddrStart>>20);
 nSec=(vaddrEnd>>20)-(vaddrStart>>20);
 for(i=0;i<=nSec;i++)*pTT++=attr |(((paddrStart>>20)+i)<<20);
}
分析上面函数可以知道
即0x33ff 8000 + 0x300<<2 = 0x33ff8C00 存的是0x3000000~0x300f ffff 的物理起始地址的描述符
0x33ff 8C04 存储的是0x30100000 0x301f ffff 起始地址的描述符
在研究MMU时遇到的问题主要就是查找页表的计算上,牢记一点,ARM指令是4字节对齐的,所以0x3000 0000 映射时 相对于基地址的偏移量并不是0x300 而是0x300<<2 ,主要看这句
pTT=(U32 *)_MMUTT_STARTADDRESS+(vaddrStart>>20);
_MMUTT_STARTADDRESS是一个地址,被强制转换成一个无符号整形指针,指向这个地址开始的四个单元空间,故在加上(vaddrStart>>20)时,是加上(vaddrStart>>20)<<2,比如
U32 *p;
p++; //p加了4个字节

由于一级页表由虚地址[31:20]进行索引,共计4096个为16k,由于TQ2440开发板为64M内存,故我们将其放在0x33ff8000~
0x33ff c000处,即64M内存的未端的16K内存中,0x33ff ff00开始存的中断向量

 

MMU的另一个功能:

内存的访问权限检查,它决定一块内存是否允许读/写,由CP15寄存器C3(域访问控制),描述符的域(Domain)CP15的寄存器C1的R/S/A位,描述符的AP位等联合作用。
“域“决定是否对某块内存进行权限检查
”AP“决定如何对某块内存进行权限检查
页表描述符:段,大页,小页,极小页
Ctt 和Btt位
C位为1表示允许Cache,这种情况下用B位来表示Write Through还是Write Back。有些页面不允许Cache,置C位为0,这种情况下可以用B位来选择是否允许使用Write Buffer。Write Buffer也是一种简单的Cache,CPU核执行写指令时可以把数据交给Write Buffer,然后由Write Buffer负责写回内存,这时CPU可以执行后续指令而不必等待写回内存这个较慢的操作结束。想一下,既然有Write Buffer,为什么没有Read Buffer?

 

基于程序访问的局部性有了TLB,CACHE
系统上电后或复位后,cache都是无效的
启动cache后,CPU写数据时有写穿式和回写式两种;
写穿式:CPU写数据到cache时,也写入内存,保证数据同步
回写式,数据一致保存于CAche中,只有当CACHE中数据被换出或者被强制清空时,才写入内存(有数据更新标记,才回写),从而保证数据的同步
cache的两个操作:
”清空“(要回写),”使无效“(不回写)
1.Icache
CP15 的C1寄存器[12]=1,使能Icache
Icache 与MMU的开启没有关系,即使没有开启MMU ,Icache也可以开启,能加快CPU取指令的速度,避免CPU每次都在内存中取指令
而Dcache必须在MMU开启之后才能开启!
在开启MMU以后,描述符里的Ctt=1时该段才允许被cache!
Icache开启后,CPU取指令的三种情况:
命中cache 且 Ctt = 1, 从Icache取指令
cache缺失且Ctt = 1,从内存中读取指令,并把该指令区域的8个字读入cache保存
Ctt = 0,直接从内存中读取

2.Dcache:
CP15的C1寄存Ccr[2] =1,使能Dcache
前面说的必须开启MMU才能开启Dcache,应为Dcache和write buffer 需要描述符中的C,B位来定义内存访问的权限
一旦Dcache关闭,所有数据都要读取内存,并且Dcache和write buffer 完全失效
而Dcache的工作模式由 Ctt & Ccr ,Btt决定一般设置为10
与TLB一样,Dcache也需要保持与内存数据同步
实际编程时应注意以下:
1)开启MMU前,使I/Dcache WB无效
2)关闭MMU前,清空I/Dcache,即回写数据
3)使用DMA操作可以被Cache的空间时,将内存数据发送出去后,要清空cache,将数据读入内存时,先无效cache
4)对于I/O设备,寄存器 ,不能使用cache,因为这些内容是volatile的!
关于操作CP15协处理器的指令格式
<MCR/MRC>{cond} p#, <expression1>,Rn,cn,cm,<expression2>
cond 为条件助记符
p#,这里是cp15
Rn  操作的ARM寄存器
cn,cm为CP15的寄存器
expression1/2为操作数
Cn为协处理器寄存器
Cm附加的目标寄存器或源操作数寄存器,用于区分一个编号的不同物理寄存器,当指令不需要提供附加信息时,将cm指定为C0,expression2指定为0
MCR P15,0,R4,C1,C0,0  R4->C1
MRC P15,0,R4,C1,C0,0  R4<-C1
C1寄存器位相关功能
M[0]:0禁止|1启用MMU
A[1]:0禁止地址对齐检查|1使能地址对齐检查
C[2]:0禁止数据CACHE|1使能数据CACHE
W[3]:0禁止写缓冲|1使能写缓冲
I[12]:0禁止代码CACHE|1使能代码CACHE


启用MMU后地址访问过程
当CPU请求存储器访问时,首先在TLB中查找虚拟地址,如果该虚拟地址对应的地址描述符不在TLB中时,读取内存中页表查询,并将该描述符添加到TLB中,如果TLB已满,根据一定算法进行替换。
在得到该地址描述符后,进行如下操作:
1从描述符表中得到对应的物理地址
2根据域访问控制和描述符中AP位确定是否允许对该内存进行操作。如不,则产生存储访问中止中断
3如访问被允许,根据描述符表中CB位决定是否缓存该内存的访问结果,如不,则根据得到的物理地址直接访问内存,如允许,则首先在CACHE中查找,命中则直接读取,如未命中,则根据物理地址直接访问内存,并将该数据读取到CACHE中。