本博客的参考文章及相关资料下载 :

  • 1.本博客代码及参考手册下载



一. MMU 概念



1. ARM 存储


(1) ARM 的存储体系


ARM 存储 体系 简介 :​ ARM 处理器分为三个等级, 处理器寄存器 -> TCM 存储器 -> 辅助存储器, 由上到下, 处理速度依次变慢, 但是存储空间依次增加 ;


  • 1.处理器内部寄存器 :​ 处理器内部的 通用寄存器 和 状态字寄存器 等, 这些寄存器 ​访问速度很快, 但是数量很少​ ;
  • 2.TCM 紧耦合存储器 :​ Cache, 内存 等存储器;
  • 3.辅助存储器 :​ 开发板上的 NandFlash 达到 1G 大小的数量级别, SD 卡 等存储 设备; 该类型存储器 ​访问速度最慢, 但是数量最大​;
    【嵌入式开发】 ARM 关闭 MMU ( 存储体系 | I/D-Cache | MMU | CP15 寄存器 | C1 控制寄存器 | C7 寄存器 | 关闭 MMU  )_MMU



(2) Cache 由来


Cache 的由来 :​ Cache 用于解决 处理器 与 存储器 之间 数据传输效率低下的问题;


  • 1.没有 Cache 的 情况 :​ 处理器直接访问主存储器, 两者之间的 ​处理速度差别巨大​, 处理器的访问效率会被大大的拉低 ;
  • 2.有 Cache 的 情况 :​ Cache 位于 处理器 与 主存储器 之间, Cache 中存放主存储器的一些拷贝, 当处理器需要读取指定内容时, 先到 Cache 中去查看, 如果没有, 就 ​直接从主存储器中读取, 同时将数据也读取到 Cache 中​, 当处理器下一次在读取该数据的时候, 就可以​直接从 Cache 中获取该数据​;



(3) Cache 定义


Cache 定义 :


  • 1.定义 :​ Cache 是 小容量 高速度 的 存储器, 其速度 低于 处理器 高于 主存储器;
  • 2.对外透明 :​ Cache 的功能对外是透明的, 在 Cache 中, 保存哪些数据, 覆盖哪些数据都是操作系统决定的;
  • 3.Cache 功能划分 :​ 分为两类, ​① I-Cache 指令 Cache, 用于存放指令​; ​② D-Cache 数据 Cache, 用于存放数据​ ;
  • 4.图示 :​ 下图是 ​S3C6410X.pdf 芯片手册 1.2 章节​ 中的 I-Cache 和 D-Cache 的描述, 下图红框部分, I/D-Cache 都是 16KB 大小;

【嵌入式开发】 ARM 关闭 MMU ( 存储体系 | I/D-Cache | MMU | CP15 寄存器 | C1 控制寄存器 | C7 寄存器 | 关闭 MMU  )_物理地址_02




2. MMU


(1) 虚拟地址 与 物理地址


虚拟机地址 与 物理地址 :


  • 1.虚拟地址概念 :​ 程序中使用的地址 是 虚拟地址 ;
  • 2.物理地址概念 :​ 存储器物理存储单元的实际物理地址 ;
  • 3.虚拟地址的优势 :​ ​① 应用程序可以使用更大的存储空间​, ​② 解决不同程序之间的地址冲突问题​; 如果没有虚拟地址, 程序中直接使用物理地址, 那么程序必须使用指定的物理地址, 会产生冲突; 同时程序中使用的存储空间也被限制 了; 因此程序中直接使用实际的物理地址 是不可行的 ;
  • 4.MMU 作用 :​ ​MMU 可以 实现 物理地址 到 虚拟地址 之间的转换​ ;



(2) MMU 作用 及 关闭原因


MMU 作用 :​ 实现 物理地址 到 虚拟地址 的转换 ;


  • 1.MMU 与 Cache 的 位置 :​ 在 ​ ① ARM 11 之前, 处理器 -> Cache -> MMU -> 存储器​, ​ ② ARM 11 及 ARM 11 之后, 处理器 -> MMU -> Cache -> 存储器​, 访问 Cache 必须通过 MMU 将虚拟地址映射成物理地址后访问;
  • 2.关闭 MMU 原因 :​ 使用 MMU 和 Cache 必须经过一系列的配置, 之后才能正确的使用, 在 ARM 初始化 时, 还没有配置 MMU 和 Cache, 如果不关闭会出现错误;






二. 关闭 MMU 和 Cache


参考手册 :​ ​ ARM核 手册 Arm1176jzfs.pdf ( 基于 6410 开发板 ARM 11 )


  • 1.手册对应章节 :​ ​3.2.7 章节 c1, Control Register​;
  • 2.Arm1176jzfs.pdf手册下载地址


1. 关闭 MMU 和 Cache 的方法简介


(1) 关闭方法


关闭 MMU 和 Cache 简介 :


  • 1.关闭 Cache 和 MMU 步骤 :​ ​① 设置 ICache 和 DCache 失效​; ​② 关闭 ICache 和 DCache 以及 MMU​ ;
  • 2.操作方法 :​ MMU 和 Cache 关闭操作都是通过 CP15 协处理器 控制的, ​ ① C1 控制寄存器 控制 Cache 和 MMU 开启 / 关闭​ , ​② C7 寄存器 控制 Cache 的的 失效 操作​ ;



(2) C1 控制寄存器 ( 打开关闭 Cache )


C1 控制寄存器简介 :


  • 1.文档位置 :​ ​Arm1176jzfs.pdf 第 3.2.7 章节 c1, Control Register ​;
  • 2.I-Cache ( Instruction Cache ) 控制位 :​ ​第 12 位 控制 I-Cache 的开启 / 关闭​, 设置成 0 即 I-Cache 失效, 设置成 1 即 I-Cache 生效;
    【嵌入式开发】 ARM 关闭 MMU ( 存储体系 | I/D-Cache | MMU | CP15 寄存器 | C1 控制寄存器 | C7 寄存器 | 关闭 MMU  )_物理地址_03
  • 3.D-Cache ( Data Cache ) 控制位 :​ ​第 2 位 控制 D-Cache 的开启 / 关闭​, 设置成 0 即 I-Cache 失效, 设置成 1 即 I-Cache 生效;
    【嵌入式开发】 ARM 关闭 MMU ( 存储体系 | I/D-Cache | MMU | CP15 寄存器 | C1 控制寄存器 | C7 寄存器 | 关闭 MMU  )_通用寄存器_04
  • 4.MMU 控制位 :​ ​第 0 位 控制 MMU 生效 / 失效​, 设置成 0 即 MMU 失效, 设置成 1 即 MMU 生效;
    【嵌入式开发】 ARM 关闭 MMU ( 存储体系 | I/D-Cache | MMU | CP15 寄存器 | C1 控制寄存器 | C7 寄存器 | 关闭 MMU  )_MMU_05



(3) C7 Cache 操作寄存器 ( 使 Cache 失效 )


C7 寄存器 简介 :


  • 1.文档位置 :​ ​Arm1176jzfs.pdf 第 3.2.22 章节 c7, Cache operations ​;
  • 2.使 Cache 失效 的指令 :​ ​​MCR p15, 0, <Rd>, c7, c7, 0​​, 这是 文档 中表格 3-71 Cache 操作 中给出的;
    【嵌入式开发】 ARM 关闭 MMU ( 存储体系 | I/D-Cache | MMU | CP15 寄存器 | C1 控制寄存器 | C7 寄存器 | 关闭 MMU  )_MMU_06



2. 关闭 MMU 和 Cache 代码编写



关闭 MMU 和 Cache 代码编写 :


  • 1.设置标号 :​ 为本段代码设置一个标号, 让程序可以跳转到该处执行以下代码, ​​disable_mmu :​​ ;
  • 2.设置 I-Cache 和 D-Cache 失效 :​ 使 两个 Cache 都失效, 文档中 ​Arm1176jzfs.pdf 第 3.2.22 章节​ 给出的代码格式为 ​​MCR p15, 0, <Rd>, c7, c7, 0​​, 其中 Rd 通用寄存器 设置为 R0, 最终代码为 ​​MCR p15, 0, R0, c7, c7, 0​​ ;
  • 3.关闭 I-Cache 和 D-Cache 及 MMU :

    • ① 修改方式 :​ C1 控制寄存器中的 ​[0] 位 控制 MMU 开启/关闭​, ​[2] 位控制 D-Cache 开启/关闭​, ​[12] 位控制 I-Cache 开启/关闭​; 上述位 设置为 0 关闭, 设置为 1 开启;
    • ② C1 寄存器读写方式 :​ CP15 寄存器不能直接读取, 需要使用 ​​MRC​​ 来将协处理器中的内容读取到通用寄存器中, 语法格式为 ​​MRC{cond} P15,<Opcode_1>,<Rd>,<CRn>,<CRm>,<Opcode_2>​​ , 使用 ​​MCR​​ 将 Rd 寄存器中的值传送到 CP15 协处理器中, 语法格式为 ​​MCR{cond} P15,<Opcode_1>,<Rd>,<CRn>,<CRm>,<Opcode_2>​​ ;
    • ③ 位计算​ : 关闭 I/D-Cache 和 MMU 需要将 C1 寄存器的 [0](MMU), [2](D-Cache), [12] (I-Cache) 三位 设置为0; 其中 ​I-Cache 可以关闭, 也可以开启, 不是必须的​; 但是 ​D-Cache 和 MMU 必须关闭​, Bootloader 主要作用是将 Linux 内核下载到内存中, 如果下载的过程中 D-Cache 没有配置, 可能就将数据下载到了 Cache 中, 这样就会出现问题, 影响内核运行; 因此这里我们​只需要将 第 [0] 位 和 第 [1] 位 设置成 0​, 将 MMU 和 D-Cache 关闭, I-Cache 不作设置;
    • ④ 读取 C1 寄存器的值 :​ 使用 ​​MRC p15, 0, R0, c1, c0, 0​​ 将 c1 寄存器中的值 读取到 R0 通用寄存器中;
    • ⑤ 将指定位设置为 0 :​ 使用 bic 位清除指令, 将 R0 寄存器中的 第 0, 1, 2 三位 设置成0, 这里 第 1 位选择性设置, 为了方便计算 顺便将 第 1 位 也设置成 0, 代码为 ​​bic r0, r0, #0x7​​ ;
    • ⑥ 将 R0 寄存器中的值写回到 C1 寄存器中 :​ 使用 ​​MRC p15, 0, r0, c1, c0, 0​​ 指令, 将 R0 寄存器中的值 写回到 C1 寄存器中;


  • 4.设置程序跳转到返回点继续执行 :​ 使用 ​​BL​​ 指令跳转到 ​​disable_mmu​​ 标号处执行, 同时将返回地址存储到了 ​​LR​​ 寄存器中, 返回时跳转到 ​​LR​​ 寄存器中的地址执行即可, 使用 ​​mov pc, lr​​ 指令, 执行 lr 中地址指向的位置的代码;
  • 5.代码示例 :

disable_mmu : 
mcr p15,0,r0,c7,c7,0 @ 设置 I-Cache 和 D-Cache 失效
mrc p15,0,r0,c1,c0,0 @ 将 c1 寄存器中的值 读取到 R0 通用寄存器中
bic r0, r0, #0x00000007 @ 使用 bic 位清除指令, 将 R0 寄存器中的 第 0, 1, 2 三位 设置成0, 代表 关闭 MMU 和 D-Cache
mcr p15,0,r0,c1,c0,0 @ 将 R0 寄存器中的值写回到 C1 寄存器中
mov pc, lr @ 返回到 返回点处 继续执行后面的代码





三. 关闭 MMU 和 Cache 完整可编译执行代码



1. 汇编代码



汇编代码示例 :​ Bootloader 流程 : ① 初始化异常向量表 , ② 设置 svc 模式 , ③ 关闭看门狗, ④ 关闭中断, ⑤ 关闭 MMU ;

@****************************  
@File:start.S
@
@BootLoader 初始化代码
@****************************

.text @ 宏 指明代码段
.global _start @ 伪指令声明全局开始符号
_start: @ 程序入口标志
b reset @ reset 复位异常
ldr pc, _undefined_instruction @ 未定义异常, 将 _undefined_instruction 值装载到 pc 指针中
ldr pc, _software_interrupt @ 软中断异常
ldr pc, _prefetch_abort @ 预取指令异常
ldr pc, _data_abort @ 数据读取异常
ldr pc, _not_used @ 占用 0x00000014 地址
ldr pc, _irq @ 普通中断异常
ldr pc, _fiq @ 软中断异常

_undefined_instruction: .word undefined_instruction @ _undefined_instruction 标号存放了一个值, 该值是 32 位地址 undefined_instruction, undefined_instruction 是一个地址
_software_interrupt: .word software_interrupt @ 软中断异常
_prefetch_abort: .word prefetch_abort @ 预取指令异常 处理
_data_abort: .word data_abort @ 数据读取异常
_not_used: .word not_used @ 空位处理
_irq: .word irq @ 普通中断处理
_fiq: .word fiq @ 快速中断处理

undefined_instruction: @ undefined_instruction 地址存放要执行的内容
nop

software_interrupt: @ software_interrupt 地址存放要执行的内容
nop

prefetch_abort: @ prefetch_abort 地址存放要执行的内容
nop

data_abort: @ data_abort 地址存放要执行的内容
nop

not_used: @ not_used 地址存放要执行的内容
nop

irq: @ irq 地址存放要执行的内容
nop

fiq: @ fiq 地址存放要执行的内容
nop

reset: @ reset 地址存放要执行的内容
bl set_svc @ 跳转到 set_svc 标号处执行
bl disable_watchdog @ 跳转到 disable_watchdog 标号执行, 关闭看门狗
bl disable_interrupt @ 跳转到 disable_interrupt 标号执行, 关闭中断
bl disable_mmu @ 跳转到 disable_mmu 标号执行, 关闭 MMU

set_svc:
mrs r0, cpsr @ 将 CPSR 寄存器中的值 导出到 R0 寄存器中
bic r0, r0, #0x1f @ 将 R0 寄存器中的值 与 #0x1f 立即数 进行与操作, 并将结果保存到 R0 寄存器中, 实际是将寄存器的 0 ~ 4 位 置 0
orr r0, r0, #0xd3 @ 将 R0 寄存器中的值 与 #0xd3 立即数 进行或操作, 并将结果保存到 R0 寄存器中, 实际是设置 0 ~ 4 位 寄存器值 的处理器工作模式代码
msr cpsr, r0 @ 将 R0 寄存器中的值 保存到 CPSR 寄存器中
mov pc, lr @ 返回到 返回点处 继续执行后面的代码

#define pWTCON 0x7e004000 @ 定义看门狗控制寄存器 地址 ( 6410开发板 )
disable_watchdog:
ldr r0, =pWTCON @ 先将控制寄存器地址保存到通用寄存器中
mov r1, #0x0 @ 准备一个 0 值, 看门狗控制寄存器都设置为0 , 即看门狗也关闭了
str r1, [r0] @ 将 0 值 设置到 看门狗控制寄存器中
mov pc, lr @ 返回到 返回点处 继续执行后面的代码

disable_interrupt:
mvn r1,#0x0 @ 将 0x0 按位取反, 获取 全 1 的数据, 设置到 R1 寄存器中
ldr r0,=0x71200014 @ 设置第一个中断屏蔽寄存器, 先将 寄存器 地址装载到 通用寄存器 R0 中
str r1,[r0] @ 再将 全 1 的值设置到 寄存器中, 该寄存器的内存地址已经装载到了 R0 通用寄存器中

ldr r0,=0x71300014 @ 设置第二个中断屏蔽寄存器, 先将 寄存器 地址装载到 通用寄存器 R0 中
str r1,[r0] @ 再将 全 1 的值设置到 寄存器中, 该寄存器的内存地址已经装载到了 R0 通用寄存器中
mov pc, lr @ 返回到 返回点处 继续执行后面的代码

disable_mmu :
mcr p15,0,r0,c7,c7,0 @ 设置 I-Cache 和 D-Cache 失效
mrc p15,0,r0,c1,c0,0 @ 将 c1 寄存器中的值 读取到 R0 通用寄存器中
bic r0, r0, #0x00000007 @ 使用 bic 位清除指令, 将 R0 寄存器中的 第 0, 1, 2 三位 设置成0, 代表 关闭 MMU 和 D-Cache
mcr p15,0,r0,c1,c0,0 @ 将 R0 寄存器中的值写回到 C1 寄存器中
mov pc, lr @ 返回到 返回点处 继续执行后面的代码



2. 链接器脚本


gboot.lds 链接器脚本 代码解析 :


  • 1.指明输出格式 ( 处理器架构 ) :​ 使用 ​​OUTPUT_ARCH(架构名称)​​ 指明​输出格式, 即处理器的架构​, 这里是 arm 架构的, ​​OUTPUT_ARCH(arm)​​ ;
  • 2.指明输出程序的入口 :​ 设置编译输出的程序入口位置, 语法为 ​​ENTRY(入口位置)​​, 在上面的 Start.S 中设置的程序入口是 ​​_start​​, 代码为 ​​ENTRY(_start)​​ ;
  • 3.设置代码段 :​ 使用 ​​.text :​​ 设置代码段;
  • 4.设置数据段 :​ 使用 ​​.data :​​ 设置数据段;
  • 5.设置 BSS 段 :​ 使用 ​​.bss :​​ 设置 BSS 段;

    • ( 1 ) 记录 BSS 段的起始地址 :​ ​​bss_start = .;​​ ;
    • ( 2 ) 记录 BSS 段的结束地址 :​ ​​bss_end = .;​​ ;


  • 6.对齐 :​ 每个段都需要设置内存的对齐格式, 使用 ​​. = ALIGN(4);​​ 设置四字节对齐即可;
  • 7.代码示例 :

OUTPUT_ARCH(arm)        /*指明处理器结构*/  
ENTRY(_start) /*指明程序入口 在 _start 标号处*/
SECTIONS {
. = 0x50008000; /*整个程序链接的起始位置, 根据开发板确定, 不同开发板地址不一致*/

. = ALIGN(4); /*对齐处理, 每段开始之前进行 4 字节对齐*/
.text : /*代码段*/
{
start.o (.text) /*start.S 转化来的代码段*/
*(.text) /*其它代码段*/
}

. = ALIGN(4); /*对齐处理, 每段开始之前进行 4 字节对齐*/
.data : /*数据段*/
{
*(.data)
}

. = ALIGN(4); /*对齐处理, 每段开始之前进行 4 字节对齐*/
bss_start = .; /*记录 bss 段起始位置*/
.bss : /*bss 段*/
{
*(.bss)
}
bss_end = .; /*记录 bss 段结束位置*/
}



3. Makefile 编译脚本


makefile 文件编写 :


  • 1.通用规则 ( 汇编文件编译规则 ) :​ ​汇编文件 编译 成同名的 .o 文件​, 文件名称相同, 后缀不同, ​​%.o : %.S​​, 产生过程是 ​​arm-linux-gcc -g -c $^​​ , 其中 ​​^​​ 标识是所有的依赖文件, 在该规则下 start.S 会被变异成 start.o ;
  • 2.通用规则 ( C 文件编译规则 ) :​ C 代码编译成同名的 .o 文件, ​​%.o : %.c​​ , 产生过程是 ​​arm-linux-gcc -g -c $^​​ ;
  • 3.设置最终目标 :​ 使用 ​​all:​​ 设置最终编译目标;

    • ( 1 ) 依赖文件 :​ ​产生最终目标需要依赖 start.o 文件​, 使用 ​​all: start.o​​ 表示最终目标需要依赖该文件;
    • ( 2 ) 链接过程 :​ ​​arm-linux-ld -Tgboot.lds -o gboot.elf $^​​, 需要使用链接器脚本进行连接, ​①链接工具是 arm-linux-ld 工具​, ​②使用 ​​-Tgboot.lds​​ 设置链接器脚本 是刚写的 gboot.lds 链接器脚本​, ​③输出文件是 gboot.elf 这是个中间文件​, ​④ 依赖文件是 ​​$^​​ 代表所有的依赖​;
    • ( 3 ) 转换成可执行二进制文件 :​ ​​arm-linux-objcopy -O binary gboot.elf gboot.bin​​, 使用 ​​-O binary​​ 设置输出二进制文件, 依赖文件是 ​​gboot.elf​​, 输出的可执行二进制文件 即 结果是 ​​gboot.bin​​ ;


  • 4.makefile 文件内容 :

all: start.o #依赖于 start.o  
arm-linux-ld -Tgboot.lds -o gboot.elf $^ #使用链接器脚本, 将 start.o 转为 gboot.elf
arm-linux-objcopy -O binary gboot.elf gboot.bin #将 gboot.elf 转化为可以直接在板子上执行的 gboot.bin 文件

%.o : %.S #通用规则, 如 start.o 是由 start.S 编译来的, -c 是只编译不链接
arm-linux-gcc -g -c $^

%.o : %.c #通用规则, 如 start.o 是由 start.c 编译来的, -c 是只编译不链接
arm-linux-gcc -g -c $^

.PHONY: clean
clean: #清除编译信息
rm *.o *.elf *.bin



4. 编译输出可执行文件


编译过程 :


  • 1.文件准备 : 将 汇编代码 ( start.S ) 链接器脚本 ( gboot.lds ) makefile 文件 拷贝到编译目录​ ;
  • 2.执行编译命令 :​ ​​make​​ ;
  • 3.编译结果 :​ 可以看到 生成了 编译目标文件 start.o, 链接文件 gboot.elf, 可执行的二进制文件 gboot.bin ;
    【嵌入式开发】 ARM 关闭 MMU ( 存储体系 | I/D-Cache | MMU | CP15 寄存器 | C1 控制寄存器 | C7 寄存器 | 关闭 MMU  )_物理地址_07


本博客的参考文章及相关资料下载 :

  • 1.本博客代码及参考手册下载