OpenRisc-40-or1200的MMU模块分析

引言

MMU(memory management unit),无论对于computer architecture designer还是OS designer,都是至关重要的部分,设计和使用的好坏,对性能影响比较大。

MMU,我觉得是硬件和软件配合最密切的部分之一,对于RISC CPU而言,更是这样。

前面,我们对or1200的整体memory hierarchy做了简单分析,了解了cache的映射方式,替换策略,写策略,以及cache的优化等等背景知识,并对or1200的具体实现做了分析。在现实中,cache往往和MMU紧密合作,完成CPU的访存操作。本小节就来分析一下or1200的MMU模块。


1,MMU产生原因

研究一个东西,首先要了解其来龙去脉,MMU也不例外,我们在分析MMU的工作机制之前先介绍一下MMU的产生原因。

当时,主要由两方面的因素导致了MMU的产生:

a,to allow efficient and safe sharing of memory among multiple programs, 
b,and to remove the programming burdens of a small, limited amount of main memory.

说明

a,从安全角度出发,确保多进程程序在执行时相互不影响。

b,从程序员的角度出发,采用MMU可以让程序员在编程时少受内容容量的限制。

现在而言,第一个原因占主要。


2,MMU的工作机制

在分析or1200的MMU实现之前,我们有必要先了解MMU的工作机制。

为了更清晰的了解MMU的工作过程,我假设了一个具体的例子,通过这个例子来说明其详细的工作步骤。

1>实例的环境假设

比如,我们编写了一个简单的应用层程序:


 

/*demo process, base on OS*/
int main()
{
    int test;
    test = 0x12345678;
}


 



1》假设其进程名称为demo。

2》假设片外SDRAM(内存)大小为32MB。其中内核空间16MB,用户空间16MB。

3》假设OS的内存管理方式是单级页式管理(还可能是段式,或页段式),虚拟地址和物理地址均为32-bit。

既然ps是8KB,也就是说最多需要的PTE的数量是:pte_num=32MB/8KB=4K。假设每个PTE是4Bytes,那么存放这些PTE一共需要的内存大小是:4Bytes X 4K=16KB。

4》假设OS在执行进程demo之前,给她分配的地址空间大小为6页(page size = 8KB),也就是48K。这48KB是连续的,其起始物理地址第0x801页,也就是0x801000。所以其地址空间是0x801页,0x802页,0x803页,0x804页,0x805页,0x806页,共6页。

既然其进程空间是48KB,每页是8K,也就是说OS需要给demo进程生成6个PTE(page table entry,页表项)。

5》假设这6个PTE存放在kernel空间的第0x600个页,即进程demo的PTE存放开始物理地址是0x600000,虚拟地址假设是0x12345000。


7》假设这进程这6页的地址空间的分配方式是:

代码段,1页;

bss段,1页;

栈,1页;

堆,1页;

数据段,2页。


8》假设进程demo中的变量test的地址在栈段,并且其页内偏移为0x1。

9》假设OS给进程demo中变量test分配的虚拟地址为0x2001,物理地址为:0x803001。

10》假设TLB的cache entry数量是64,映射方式是直接映射,也就是说通过VPN进行模运算就可以得到TLB的索引地址。


2>MMU的工作机制

有了上面的假设,那么MMU是如何工作的呢?

MMU的功能主要是虚实地址转换,PTE的cache(也就是TLB)。其具体过程,如下图所示:



其工作过程如下:


为了实现虚实地址转换,OS为每页创建了一个页表项,每个虚拟地址也分成了两部分,分别是VPN和INDEX,通过VPN的低6位(因为TLBcache是64 entry)定位到TLB的偏移量。

如果相应的偏移量处TLB hit,那么就可以直接得到对应的PTE,有了PTE,我们就可以得到PTE中的PPN,PPN和INDEX组合在一起,就是物理地址。

如果相应的偏移量处TLB miss或者页面异常,那么MMU产生一个TLB miss或者page fault异常,交给OS完成异常的处理(TLB的更新,和其它操作)。


对于本演示例子来说,变量test的虚拟地址是0x2001,可见其TLB entry偏移量是0x2,页内偏移量是0x1;VPN是0x2。那么,MMU是如何将这个虚拟地址换换成物理地址的呢?

首先,OS会根据进程demo的进程号(PID,OR也有CID(context ID)寄存器),得到进程demo的页表的存放的开始地址(0x600000),然后得到对应的PTE的地址(0x600008)根据TLB的偏移0x2,查看对应的TLB的第2个cache line,如果匹配,则进一步和MR(mach register)中的VPN比较,如果也匹配,好,恭喜你,TLB hit,并将对应的PTE(pte_2)的PPN(0x803)和INDEX(0x001)组合成物理地址,传给qmem模块,sb模块,biu模块,经dbus_arbiter,memory controller,最终实现读写SDRAM的对应地址。

如果TLB miss(对应的pte_2),那么OS查看异常寄存器,得到具体的异常信息,并最终将pte_2更新到TLB中,重新执行MMU操作,则TLB hit,完成转换过程。


上面的过程,如果用一副图来展示的话,如下所示:




3>关于多级页表

上面介绍的OS的页表是单级的,这样的话,在搜索对应的PTE时需要依次遍历所有的PTE表项,显然比较慢,为了加快搜索速度,linux采用了两级PTE页表。

其基本思想是将所有的PTE进行分组,每一组由一个PTD(PT directory,我自己给起的名字),每个PTD项对应一组PT。

这样,在搜索时,先确定其所在的页表目录,然后只需要遍历本目录中的PTE就可以了。

其操作过程和单级页表相似,如下图所示:




4>小结

上面通过一个例子,说明了MMU的工作机制,大体可概括如下:

1》根据进程ID和VPN得到存放对应页表项的PTE的地址

2》从PTE中得到PPN

3》PPN与INDEX组合,得到物理地址

4》从上面的分析,可以看出,CPU对PTE的访问是非常频繁的,为了加快速度,将部分PTE放到cache里面,这个cache就是TLB。


3,or1200的MMU模块

对MMU的工作机制了解清楚之后,再来分析or1200的MMU的实现,我想就会容易一些吧。

IMMU和DMMU机制类似,这里只分析DMMU。

1>DMMU的参数

通过or1200_define.v中,关于DMMU的配置如下:

从中我们可以看出:

1》MR(匹配寄存器),TR(转换寄存器)各个域含义的定义:



2》页大小是8KB(13bit)

3》TLB entry的数量是64(6bit)



 

//////////////////////////////////////////////
//
// Data MMU (DMMU)
//

//
// Address that selects between TLB TR and MR
//
`define OR1200_DTLB_TM_ADDR	7

//
// DTLBMR fields
//
`define	OR1200_DTLBMR_V_BITS	0
`define	OR1200_DTLBMR_CID_BITS	4:1
`define	OR1200_DTLBMR_RES_BITS	11:5
`define OR1200_DTLBMR_VPN_BITS	31:13

//
// DTLBTR fields
//
`define	OR1200_DTLBTR_CC_BITS	0
`define	OR1200_DTLBTR_CI_BITS	1
`define	OR1200_DTLBTR_WBC_BITS	2
`define	OR1200_DTLBTR_WOM_BITS	3
`define	OR1200_DTLBTR_A_BITS	4
`define	OR1200_DTLBTR_D_BITS	5
`define	OR1200_DTLBTR_URE_BITS	6
`define	OR1200_DTLBTR_UWE_BITS	7
`define	OR1200_DTLBTR_SRE_BITS	8
`define	OR1200_DTLBTR_SWE_BITS	9
`define	OR1200_DTLBTR_RES_BITS	11:10
`define OR1200_DTLBTR_PPN_BITS	31:13

//
// DTLB configuration
//
`define	OR1200_DMMU_PS		13					// 13 for 8KB page size
`define	OR1200_DTLB_INDXW	6					// 6 for 64 entry DTLB	7 for 128 entries
`define OR1200_DTLB_INDXL	`OR1200_DMMU_PS				// 13			13
`define OR1200_DTLB_INDXH	`OR1200_DMMU_PS+`OR1200_DTLB_INDXW-1	// 18			19
`define	OR1200_DTLB_INDX	`OR1200_DTLB_INDXH:`OR1200_DTLB_INDXL	// 18:13		19:13
`define OR1200_DTLB_TAGW	32-`OR1200_DTLB_INDXW-`OR1200_DMMU_PS	// 13			12
`define OR1200_DTLB_TAGL	`OR1200_DTLB_INDXH+1			// 19			20
`define	OR1200_DTLB_TAG		31:`OR1200_DTLB_TAGL			// 31:19		31:20
`define	OR1200_DTLBMRW		`OR1200_DTLB_TAGW+1			// +1 because of V bit
`define	OR1200_DTLBTRW		32-`OR1200_DMMU_PS+5			// +5 because of protection bits and CI

//
// Cache inhibit while DMMU is not enabled/implemented
//
// cache inhibited 0GB-4GB		1'b1
// cache inhibited 0GB-2GB		!dcpu_adr_i[31]
// cache inhibited 0GB-1GB 2GB-3GB	!dcpu_adr_i[30]
// cache inhibited 1GB-2GB 3GB-4GB	dcpu_adr_i[30]
// cache inhibited 2GB-4GB (default)	dcpu_adr_i[31]
// cached 0GB-4GB			1'b0
//
`define OR1200_DMMU_CI			dcpu_adr_i[31]


 

2>DMMU模块代码分析

DMMU部分主要有两个模块:dmmu_top和dmmu_tlb。

1》dmmu_top

这个是DMMU的顶层模块,这里只将核心代码列了出来:


 

//
// Physical address is either translated virtual address or
// simply equal when DMMU is disabled
//
assign qmemdmmu_adr_o = dmmu_en ? {dtlb_ppn, dcpu_adr_i[`OR1200_DMMU_PS-1:0]} :
			          dcpu_adr_i;


从中可以看出最终的物理地址的组成部分,为PPN(19-bit)和虚拟地址的index(13-bit),共32bit。

 

如果DMMU禁止的话,DMMU则直接将虚拟地址传给qmem模块。


2》dmmu_tlb

这个是PTE的cache模块,主要一个MR和TR两个寄存器完成虚实转换。


 

//
// Assign outputs from Match registers
//
assign {vpn, v} = tlb_mr_ram_out;


 


 

//
// Assign outputs from Translate registers
//
assign {ppn, swe, sre, uwe, ure, ci} = tlb_tr_ram_out;


从中可以看出,PPN来源于DMMU的寄存器,这组寄存器由OS负责管理。

 


4,小结

本小节,我们对MMU的工作机制做了介绍,最后对or1200的DMMU的具体实现做了分析。这部分的代码还是很少的,所以说完成MMU的工作,or1200的硬件做的工作还是不多,主要靠OS来完成。感兴趣可以直接参考linux的mmu的部分代码。


5,参考文献

1,ORPSoC RTL code


 

posted on 2013-07-28 20:54  you Richer  阅读(609)  评论(0编辑  收藏  举报