浅析嵌入式系统之uboot详解(6)—关闭缓存和mmu

bootloader详解目录—废铁是怎么产生价值的

浅析嵌入式系统之uboot详解(1)—板子上电后uboot做了什么
浅析嵌入式系统之uboot详解(2)—CPU工作模式
浅析嵌入式系统之uboot详解(3)—看门狗
浅析嵌入式系统之uboot详解(4.1)—内部中断
浅析嵌入式系统之uboot详解(4.2)—外部中断(GPIO)
浅析嵌入式系统之uboot详解(4.3)—异常和异常向量
浅析嵌入式系统之uboot详解(5.1)—时钟分频
浅析嵌入式系统之uboot详解(5.2)—省电模式(番外)
浅析嵌入式系统之uboot详解(5.3)—PWM定时器(番外)
浅析嵌入式系统之uboot详解(6)—关闭缓存和mmu
浅析嵌入式系统之uboot详解(7)—初始化SDRAM


1. 引言

  写了几篇番外,介绍一些uboot相关的知识,稍微有点跑题,但问题不大,我们能hold住,让我们回归uboot启动流程,当设置完时钟分频以后,uboot就会执行cpu_init_crit汇编函数,这个函数的主要作用就是关闭缓存和mmu,然后调用lowlevel_init函数进行系统总线的初始化。

  为什么启动的时候,需要关闭缓存和mmu呢?我们先了解一下他们的作用。

2. 缓存和mmu

2.1 缓存及mmu概述

  缓存(CACHE)是主存(内存)和CPU通用寄存器之间设置的一个高速的、容量相对较小的存储器,把正在执行的指令地址附近的一部分指令或数据从主存调入这个存储器,供CPU在一段时间内使用,以提高程序的运行速度。

  mmu可以实现虚拟内存和内存保护等功能,完成对内存的操作和管理。

2.2 为什么关闭缓存和mmu?

  CACHE是高速缓冲存储器。CPU工作速度是很快的,而外部内存是工作很慢的,所以当CPU对内存访问的时候,是要等待内存访问结束的。所以中间CPU就在等待,这就浪费了时间。所以在CPU和内存之间加一个CACHE,当CPU写数据到内存中的时候,就先写入到CACHE中,然后CACHE再写入到内存中。CPU写CACHE是很快的,所以就提高了写数据的效率。读数据的话,CPU先在CACHE中去找数据,没有找到的话,CACHE将数据从内存中取出来,再给CPU,同时把这个数据存起来。当CPU在CACHE中找到数据的话,就直接使用这个数据,就不用再去内存中取数据了。

  Caches是CPU内部的一个2级缓存,它的作用是将常用的数据和指令放在CPU内部。Caches是通过CP15管理的,刚上电的时候,CPU还不能管理Caches。上电的时候指令Cache可关闭,也可不关闭,但数据Cache一定要关闭,否则可能导致刚开始的代码里面,去取数据的时候,从Cache里面取,而这时候RAM中数据还没有Cache过来,导致数据预取异常 。说到Caches就必须提到一个关键字Volatile,它的本质:是告诉编译器不要对我的代码进行优化,作用是让编写者感觉变量的变化情况。因为在优化时,会将常用的代码取出来放到Caches中,它没有从实际的物理地址去取,它直接从CPU的缓存中去取,但常用的代码就是为了检测一些常用变量的变化,如果正在取数据的时候发生跳变,那么就检测不到变量的变化了,所以在这种情况下要用Volatile关键字告诉编译器不要做优化,让cpu每次都从实际的物理地址中去取指令。其实这也是为什么要关闭数据缓存的原因,如果汇编指令读取的时候缓存中的数据,而实际物理地址的数据发生了变化,将导致cpu读取不到真实的最新的值。然而在C语言中是不会关闭Caches的,如果编写者要检测外界物理数据的变化,或变化太快,从Caches中取数据会有误差,就加一个关键字Volatile。

  同样,在板子启动的时候是没有对mmu进行初始化的,而且这个时候也用不到mmu,为了避免他们影响启动时的初始化,所以需要先关闭mmu和缓存。

3. 2440裸机分析

  2440的协处理器CP15总共有c0~c15这16个协处理器寄存器,各自具有一定的功能定义。但总的来说,cp15主要跟以下功能有关:

  1. 获取device id和cache type等一些CPU相关信息。
  2. MMU操作。包括MMU的使能和禁止,虚拟地址到物理地址的映射机制建立
  3. 访问权限控制。主要用来实现安全机制和linux的写时复制(copy on write)。
  4. 设置时钟模式。init.S中MMU_SetAsyncBusMode和MMU_SetFastBusMode这两个函数

  下面我会分别就以上4种应用详细分析,并配合代码进行讲解。

3.1 获取device id和cache type

  这两个信息存在p15的c0寄存器中,有意思的是c0寄存器有两个实体,一个用来存device id(叫做c0.0),另一个用来存cache type(叫做c0.1)这两个寄存器在读取时命令不同,请参看以下示例代码。

U32 P15_ReadID(void)
{
	U32 id;
	__asm{
		mrc	p15,0,id,c0,c0;
	}
	return id;
}
 
U32 P15_ReadCacheType(void)
{
	U32 id;
	__asm{
		mrc	p15,0,id,c0,c0,1;
	}
	return id;
}

对以上代码的说明:

  • CPU从p15读出寄存器只能使用mrc指令。同样,写入p15寄存器只能使用mcr指令。这两个指令也是p15唯一可以接受的两个指令。
  • __asm{ }用来在c代码中内嵌汇编。
  • 从代码中可以看出,读出c0.0和c0.1的代码,在opcode2处有所不同。(读出c0.1时,opcode2为1.c0.0时opcode2省略)。
  • 我使用的S3C2440实际读出的device id为 0x41129200;cache type为:0xd172172。和Jlink Commander识别出得参数对比是相同的。

3.2 MMU操作

  和MMU有关的p15寄存器为c1(control register)和c2(TTB translation table base register)。其中c2比较简单,就是用来储存从虚拟地址到物理地址的地址转换表的基地址的(转换表存放在内存中,譬如可以放在0x30000000地址),因此我们在初始化mmu的时候,只要将规划的转换表基地址用mcr指令传送到该c2寄存器即可。而c1寄存器为控制寄存器,详细定义如下:

Register 1 - Control (read/write) 
All values set to 0 at power-up. 
o    Bit    0 - On-chip MMU turned off (0) or on (1) 用来关闭或使能MMU
o    Bit    1 - Address alignment fault disabled (0) or enabled (1) 关闭或打开地址对齐检查
o    Bit    2 - Data cache turned off (0) or on (1) 数据cache打开或关闭
o    Bit    3 - Write buffer turned off (0) or on (1) 
o    Bit    7 - Little-endian operation if 0, big-endian if 1 用来选择大小端格式
o    Bit    8 - System bit - controls the MMU permission system 
o    Bit    9 - ROM bit - controls the MMU permission system bit8(S bit ) and bit9(R bit)用来管理MMU访问权限,第3部分会详述
o    Bit  12 - Instruction cache turned off (0) or on (1)” 指令cache打开或关闭
o    Bit  13 - Base location of exception registers. 0x00000000(0) or 0xffff0000(1)上电启动地址。
o    Bit  14 - Round robin replacement ,random replacement(0) or round-robin replacement(1).不太懂这个
o    Bit  15 ~ Bit29   reserved
o    Bit  30  nF bit bit30 和 bit31共同用来决定总线模式。 iA:nF = 00 FastBus mode 
o    Bit  31  iA bit 
Registe bitsNameFunctionValue
31iA bitAsynchronous clock select见时钟模式表
30nF bitnotFastBus select见时钟模式表
29:15-ReservedRead = Unpredictable
Write = should be zero
14RR bitRound robin replacement0 = Random replacement
1 = Round robin replacement
13V bitBase location of exception register(异常寄存器基地址)0 = Low address = 0x0000 0000
1 = High address = 0xFFFF 0000
12I bitInstruction cache enable0 = Instruction cache disable
1 = Instruction cache enable
11:10-ReservedRead = 00
Write = 00
9R bitROM protection见3.3部分
8S bitSystem protection见3.3部分
7B bitBig-endian/little-endian0 = Little-endian operation
1 = Big-endian operation
6:3-ReservedRead = 1111
Write = 1111
2C bitData cache enable0 = data cache disable
1 = data cache enable
1A bitAlignment fault enableData address alignment fault checking(地址对齐检查)
0 = 禁用地址对齐检查功能
1 = 使能地址对齐检查功能
0M bitMMU enable0 = MMU disable
1 = MMU enable

  时钟模式表

Clocking mode(时钟模式)iAnF
Fastbus mode (快速总线模式)00
Reserved10
Synchronous (同步模式)01
Asynchronous (异步模式)11

  结合以上对c1寄存器的位定义的分析,我们来看看下面这个函数:

void MMU_Init(void)
{
 	__asm{
 
    	mov r0,#0x30000000; 	// r0=TTBase 即页表的基地址
      	mcr p15,0,r0,c2,c0,0; 	// C2中存放地址转换表基地址
    
      	mvn r0, #0;   			// 数据取反传送指令
        mcr p15,0,r0,c3,c0,0; 	// 访问类型为管理者权限
 
   	mrc p15,0,r0,c1,c0,0; 	// 读出协处理器C1
   	orr r0,r0,#01;   		// 或操作,使最低位为1
   	mcr p15,0,r0,c1,c0,0; 	// 给C1赋值 
  	}	
}

  函数中使用ARM寄存器r0作为和协处理器寄存器的接口。mcr p15,0,r0,c2,c0,0这句将r0中得值(0x30000000,这个是我们规划的转换表的基地址)放入(因此是mcr,所以是从ARM寄存器到p15协处理器寄存器)c2中。c2即是p15中的转换表基址。

	mrc p15,0,r0,c1,c0,0; 	// 读出协处理器C1
   	orr r0,r0,#01;   		// 或操作,使最低位为1
   	mcr p15,0,r0,c1,c0,0; 	// 给C1赋值 
    典型的读--写三步操作,目的就是将c1寄存器的bit0置1而同时不影响其他位。
    根据上面的寄存器定义可知,c1的bit0为MMU enable or disable,因此该三句代码实际上是打开了MMU。
    (注意MMU打开前后,地址空间发生了变化。MMU打开前程序是工作在物理地址空间的,而MMU打开后程序便工作在了虚拟地址空间)

   c1中跟MMU有关的还有data cache和instruction cache的打开和关闭,S bit和 R bit联合组成的访问权限控制等位。另外,c1中还有其他一些我们可能会用到的信息位。
  譬如:Bit7用来选择大小端模式,bootloader中一般会有代码,通过设置该位来告知CPU当前板子使用的是little endian or big endian。(因为该位上电默认是0,因此默认的模式为小端模式。); Bit13为上电启动地址,也就是reset excetion复位异常的入口地址。上电默认也是0,因此默认的上电启动地址为0x00000000。当我们使用mcr指令将该位置1时,便可以将reset exception设置到 0xffff0000,记得这个好像是为了WinCE的移植支持设计的。

   下面是uboot中关闭mmu和cache的cpu_init_crit函数:

/*
 *************************************************************************
 *
 * CPU_init_critical registers
 *
 * setup important registers
 * setup memory timing
 *
 *************************************************************************
 */


#ifndef CONFIG_SKIP_LOWLEVEL_INIT
cpu_init_crit:
	/*
	 * flush v4 I/D caches
	 */
	mov	r0, #0
	mcr	p15, 0, r0, c7, c7, 0	/* flush v3/v4 cache */
	mcr	p15, 0, r0, c8, c7, 0	/* flush v4 TLB */

	/*
	 * disable MMU stuff and caches
	 */
	mrc	p15, 0, r0, c1, c0, 0
	bic	r0, r0, #0x00002300	@ clear bits 13, 9:8 (--V- --RS)
	bic	r0, r0, #0x00000087	@ clear bits 7, 2:0 (B--- -CAM)
	orr	r0, r0, #0x00000002	@ set bit 2 (A) Align
	orr	r0, r0, #0x00001000	@ set bit 12 (I) I-Cache
	mcr	p15, 0, r0, c1, c0, 0

	/*
	 * before relocating, we have to setup RAM timing
	 * because memory timing is board-dependend, you will
	 * find a lowlevel_init.S in your board directory.
	 */
	mov	ip, lr

	bl	lowlevel_init

	mov	lr, ip
	mov	pc, lr
#endif /* CONFIG_SKIP_LOWLEVEL_INIT */

   在关闭CACHE之前,需要要做一件事,就是要将CACHE的原有的数据给清掉。因为有可能之前,里面就有一些数据了,这个操作也是由CP15下的一个寄存器控制的。
   清除完cache中的数据后,就可以关闭mmu和cache了。

   接下来就是跳转到lowlevel_init函数,进行其他的初始化。

3.3 访问权限控制

   访问权限控制主要由c3(domain access control register)和c1中的S bit 和R bit共同控制。详细情况请参考 http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0151c/I273867.html
   从上面链接中的介绍可以看出,c3中的32bit正好组成16组,每组占用2bit,这两bit若为0x00,那么具体的访问规则则由S bit + R bit决定。若为0x01或0x10或0x11则S bit和R bit不起作用。 其实所有的情况都包含在下面这张表中了
在这里插入图片描述

3.4 设置bus mode

   bootloader中有两个函数是用来设置bus mode的,关于这个bus mode不是很清楚具体情况,我们这里只是分析其操作过程。

MMU_SetAsyncBusMode
     mrc p15,0,r0,c1,c0,0  ;  读出c1
     orr r0,r0,#R1_nF:OR:R1_iA  ;  
     mcr p15,0,r0,c1,c0,0  ; R1的nF和iA位置1,11对应 Asynchronus bus mode
     MOV_PC_LR

MMU_SetFastBusMode
   mrc p15,0,r0,c1,c0,0
   bic r0,r0,#R1_iA:OR:R1_nF  ; 清零nF和iA这两位,00对应 fast bus mode
   mcr p15,0,r0,c1,c0,0
   MOV_PC_LR
  • 1
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ʚ兔子的先森ɞ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值