“第021课 MMU和Cache”的版本间的差异
| 第1行: | 第1行: | ||
| + | =第001节_Cache简述及协处理器指令= | ||
| + | 如果对MMU ICache有所了解或者知道其概念作用,那么这节课可以跳过,我们很少会使用MMU或ICache | ||
| + | |||
| + | |||
| + | 在2440芯片里面除了CPU之外 | ||
| + | Instruction MMU 指令MMU | ||
| + | Data MMU 数据MMU | ||
| + | InstructionC ACHE(16KB) 指令cache | ||
| + | Data CACHE (16KB) 数据cache | ||
| + | |||
| + | 全都通过CP15协处理器来进行操作这些 | ||
| + | |||
| + | |||
| + | 协处理器的含义作用 | ||
| + | coprocessor协助主处理器做某些事情, | ||
| + | 比如在ARM系统中有cp0 – cp15一共16个协处理器,其中cp15负责管理mmu icache | ||
| + | |||
| + | 写一个程序,0到100求和 | ||
| + | int sum() | ||
| + | { | ||
| + | int I; | ||
| + | int sum =0; | ||
| + | for(i=0; I <= 100; i++) | ||
| + | sum += I; | ||
| + | return sum; | ||
| + | } | ||
| + | 查看反汇编代码 | ||
| + | |||
| + | 局部变量保存在栈中,也就是内存 | ||
| + | |||
| + | 70: e50b3014 str r3, [fp,#-20] //这个应该就是sum 假设地址是A | ||
| + | |||
| + | 78: e50b3010 str r3, [fp, #-16] //这个应该就是I 假设地址是B | ||
| + | ldr r3, [fp, #-16] //也就是地址B中取出值 | ||
| + | cmp r3, #100 //跟100比较 | ||
| + | //如果大于100程序跳到 a8 如果小于100则执行下面的for循环 | ||
| + | 从7c: | ||
| + | 到 a4 | ||
| + | 指令保存在内存中,CPU根据这些执行进行操作 | ||
| + | |||
| + | 1 不断的读写地址A和B | ||
| + | 2 不断的执行for循环里面代码 | ||
| + | 2.1 取指令 | ||
| + | 2.2 执行指令 | ||
| + | |||
| + | 问SDRAM非常慢,那么怎么提高程序执行效率? | ||
| + | 先引入一个感念, | ||
| + | 程序局部性原理 | ||
| + | 时间局部性:在同一段时间里,有极大的概率访问同一地址的指令或数据 | ||
| + | (在这个for循环中同一个地址指令经常被访问到) | ||
| + | 空间局部性: 有极大概率访问到相邻空间的指令/数据 | ||
| + | 我们在一个比较慢的SDRAM上能不能在CPU上开一个高速缓存,把这些指令放进高速缓存icache | ||
| + | |||
| + | |||
| + | 指令cache只有16KB 数据cache也只有16KB | ||
| + | 而我们的SDRAM有64MB空间,显然擦车不可能存储SDRAM中所有的内容,它只能存储一部分 | ||
| + | |||
| + | cache的示意图 | ||
| + | |||
| + | 以数据开始为例 | ||
| + | 1 程序要读地址A的数据 | ||
| + | ldr r0, [A的数据] | ||
| + | a. cpu以地址A查找cache,一开始cache无数据,导致cache miss | ||
| + | |||
| + | 返回一系列的数据,叫做cache line: 8word 32byte | ||
| + | |||
| + | b. cpu把地址A发到SDRAM,读入cache line,成为cache file 把地址A上的数据返回给CPU | ||
| + | 2 程序再次读取地址A的数据 | ||
| + | a cpu以地址A查找cache,cache hit有数据直接从cache返回数据给CPU | ||
| + | |||
| + | 3 程序要读地址B的数据,CPU也是以地址B查找数据,cache hint直接返回 | ||
| + | |||
| + | 4 cache满了,CPU访问C | ||
| + | a cache替换,置换老的数据 | ||
| + | b 填充新数据 | ||
| + | |||
| + | 数据写 | ||
| + | write buffer | ||
| + | 查看2410芯片手册 附录 appendix4-caches, write buffer | ||
| + | 585页 | ||
| + | |||
| + | 设置为NCNB (no cache no buffer)数据直接到达硬件不经过缓冲器 | ||
| + | |||
| + | 比如GPFDAT寄存器CPU读寄存器的时候想读到引脚状态,不应该从cache读取老的数据,而是不断直接访问硬件返回最新的数据 | ||
| + | 对于这些寄存器应该设置为NCNB | ||
| + | |||
| + | |||
| + | 不使用cache但数据写到buffer中,CPU就不管了 由write buffer直接进行写操作 | ||
| + | CPU直接操作下一条指令 | ||
| + | |||
| + | |||
| + | 第一种不使用cache buffer 适用于直接硬件操作 gpio 得到最新数据 | ||
| + | 第二种 不使用cache使用write buffer, cpu把写发给buffer,cpu就可以直接下一条指令 | ||
| + | 第三种 WT 写通方式 使用cache不使用buffer,马上写硬件 | ||
| + | CPU直接写给write buffer | ||
| + | 由write执行缓慢写操作 | ||
| + | 第四种 写回方式 | ||
| + | miss: cpu数据直接到达write buffer | ||
| + | hit: cpu数据写入cache标记为dirty,让后会在合适的时机由write buffer写给硬件 | ||
| + | |||
| + | 合适的时机 | ||
| + | cache替换时dirty会写给write buffer写给硬件 | ||
| + | 或者强制Flash cache 写给write buffer 写给硬件 | ||
| + | |||
| + | |||
| + | 下节讲协处理指令 | ||
| + | |||
| + | =第002节_协处理器指令_开启ICache代码示例= | ||
| + | |||
| + | |||
| + | CPU中还有许多协处理器来协助主处理功能 | ||
| + | 比如2440有CP0 ~ CP15一共16个协处理器 | ||
| + | |||
| + | CP15管理cache mmu | ||
| + | 我们启动cache需要操作CP15 | ||
| + | 协处理器指令 | ||
| + | 先看硬件结构 | ||
| + | |||
| + | CP15中也有许多寄存器 C0 ~ C15 启动C7’ 是备份寄存器 | ||
| + | |||
| + | 现在主CPU中某一个值R0传给CP15中的某一个寄存器 | ||
| + | |||
| + | 我们需要引入协处理器指令 | ||
| + | mrc | ||
| + | mov r1, r0 | ||
| + | 结果是r0 =传给=> r1 | ||
| + | mrc | ||
| + | c coprocessor =传给=> register | ||
| + | |||
| + | |||
| + | |||
| + | |||
| + | mcr 是把主处理器的值发给协处理器 | ||
| + | register =传给=> coprocessor | ||
| + | 查看一下语法格式 | ||
| + | 在2440中搜索mrc | ||
| + | |||
| + | 得到语法格式 | ||
| + | |||
| + | |||
| + | <MCR|MRC>{cond} p#,<expression1>,Rd,cn,cm{,<expression2>} | ||
| + | 举个例子 | ||
| + | |||
| + | mcr P15, 0, r1,c1 | ||
| + | 把主处理器的值发给协处理器 | ||
| + | expression1 值设置为0,表示用不到 | ||
| + | r1 是主cpu寄存器里面的值 | ||
| + | c1 是cp15寄存器里的值 | ||
| + | |||
| + | cm, 用不到,写为c0 | ||
| + | expression2 值设置为0,表示用不到 | ||
| + | cm和expression2用来区分哪一个c1,一般写为c0, 0 | ||
| + | |||
| + | 这条命令表示主cpu中r1 值写入 协处理器cp15 中的c1寄存器 | ||
| + | |||
| + | |||
| + | 反过来要从cp15寄存器读到主cpu寄存器 | ||
| + | mrc p15, 0, r1, c1, c0, 0 | ||
| + | 这条命令表示协处理器cp15 c1寄存器的值读出来写入主cpu的r1寄存器 | ||
| + | |||
| + | 2410手册中有讲cp15寄存器的作用 | ||
| + | |||
| + | 其中寄存器1控制寄存器 | ||
| + | 下图为介绍控制寄存器1的功能 | ||
| + | |||
| + | bit12位是控制cache指令的开启或者关闭,我们等下把bit 12设置为1 | ||
| + | |||
| + | c7里面有许多不同的寄存器,对应不同的功能 | ||
| + | |||
| + | 寄存器7表示用来操作cache,根据语法规则cm{,<expression2>} 来区分选择那个c7 | ||
| + | |||
| + | |||
| + | 接下来写程序使能cache | ||
| + | 注意2440里有data cache和指令cache 其中data cache要启用地址映射才可以使用,只能使用指令cache | ||
| + | |||
| + | 打开start.s | ||
| + | reset: | ||
| + | /* 关闭看门狗 */ | ||
| + | ldr r0, =0x53000000 | ||
| + | ldr r1, =0 | ||
| + | str r1, [r0] | ||
| + | |||
| + | /* 设置MPLL, FCLK : HCLK : PCLK = 400m : 100m : 50m */ | ||
| + | /* LOCKTIME(0x4C000000) = 0xFFFFFFFF */ | ||
| + | ldr r0, =0x4C000000 | ||
| + | ldr r1, =0xFFFFFFFF | ||
| + | str r1, [r0] | ||
| + | |||
| + | /* CLKDIVN(0x4C000014) = 0X5, tFCLK:tHCLK:tPCLK = 1:4:8 */ | ||
| + | ldr r0, =0x4C000014 | ||
| + | ldr r1, =0x5 | ||
| + | str r1, [r0] | ||
| + | |||
| + | /* 设置CPU工作于异步模式 */ | ||
| + | mrc p15,0,r0,c1,c0,0 | ||
| + | orr r0,r0,#0xc0000000 //R1_nF:OR:R1_iA | ||
| + | mcr p15,0,r0,c1,c0,0 | ||
| + | |||
| + | /* 设置MPLLCON(0x4C000004) = (92<<12)|(1<<4)|(1<<0) | ||
| + | * m = MDIV+8 = 92+8=100 | ||
| + | * p = PDIV+2 = 1+2 = 3 | ||
| + | * s = SDIV = 1 | ||
| + | * FCLK = 2*m*Fin/(p*2^s) = 2*100*12/(3*2^1)=400M | ||
| + | */ | ||
| + | ldr r0, =0x4C000004 | ||
| + | ldr r1, =(92<<12)|(1<<4)|(1<<0) | ||
| + | str r1, [r0] | ||
| + | |||
| + | /* 一旦设置PLL, 就会锁定lock time直到PLL输出稳定 | ||
| + | * 然后CPU工作于新的频率FCLK | ||
| + | */ | ||
| + | /* | ||
| + | 使能icache | ||
| + | */ | ||
| + | bl enable_icache | ||
| + | |||
| + | /* 设置内存: sp 栈 */ | ||
| + | /* 分辨是nor/nand启动 | ||
| + | * 写0到0地址, 再读出来 | ||
| + | * 如果得到0, 表示0地址上的内容被修改了, 它对应ram, 这就是nand启动 | ||
| + | * 否则就是nor启动 | ||
| + | */ | ||
| + | mov r1, #0 | ||
| + | ldr r0, [r1] /* 读出原来的值备份 */ | ||
| + | str r1, [r1] /* 0->[0] */ | ||
| + | ldr r2, [r1] /* r2=[0] */ | ||
| + | cmp r1, r2 /* r1==r2? 如果相等表示是NAND启动 */ | ||
| + | ldr sp, =0x40000000+4096 /* 先假设是nor启动 */ | ||
| + | moveq sp, #4096 /* nand启动 */ | ||
| + | streq r0, [r1] /* 恢复原来的值 */ | ||
| + | |||
| + | bl sdram_init | ||
| + | //bl sdram_init2 /* 用到有初始值的数组, 不是位置无关码 */ | ||
| + | |||
| + | /* 重定位text, rodata, data段整个程序 */ | ||
| + | bl copy2sdram | ||
| + | |||
| + | /* 清除BSS段 */ | ||
| + | bl clean_bss | ||
| + | |||
| + | /* 复位之后, cpu处于svc模式 | ||
| + | * 现在, 切换到usr模式 | ||
| + | */ | ||
| + | mrs r0, cpsr /* 读出cpsr */ | ||
| + | bic r0, r0, #0xf /* 修改M4-M0为0b10000, 进入usr模式 */ | ||
| + | bic r0, r0, #(1<<7) /* 清除I位, 使能中断 */ | ||
| + | msr cpsr, r0 | ||
| + | |||
| + | /* 设置 sp_usr */ | ||
| + | ldr sp, =0x33f00000 | ||
| + | |||
| + | ldr pc, =sdram | ||
| + | sdram: | ||
| + | bl uart0_init | ||
| + | |||
| + | bl print1 | ||
| + | /* 故意加入一条未定义指令 */ | ||
| + | und_code: | ||
| + | .word 0xdeadc0de /* 未定义指令 */ | ||
| + | bl print2 | ||
| + | |||
| + | swi 0x123 /* 执行此命令, 触发SWI异常, 进入0x8执行 */ | ||
| + | |||
| + | //bl main /* 使用BL命令相对跳转, 程序仍然在NOR/sram执行 */ | ||
| + | ldr lr, =halt | ||
| + | ldr pc, =main /* 绝对跳转, 跳到SDRAM */ | ||
| + | |||
| + | halt: | ||
| + | b halt | ||
| + | 如何使能icache 打开2410芯片手册 | ||
| + | enable_icache: | ||
| + | /* 设置协处理器使能icache */ | ||
| + | mrc p15, 0, r0, c1, c0, 0 | ||
| + | orr r0, r0, #(1<<12) /* r0 = r0 or (1<<12) */ | ||
| + | mcr p15, 0, r0, c1, c0, 0 //吧修改好的r0写给cp15的c1寄存器 | ||
| + | mov pc, lr | ||
| + | |||
| + | 刷屏效率变快 | ||
| + | |||
| + | |||
| + | |||
| 第5行: | 第286行: | ||
<categorytree mode=all background-color:white;">ARM裸机加强版</categorytree> | <categorytree mode=all background-color:white;">ARM裸机加强版</categorytree> | ||
| − | |||
[[Category:ARM裸机加强版]] | [[Category:ARM裸机加强版]] | ||
| − | |||
| − | |||
2018年3月6日 (二) 09:06的版本
第001节_Cache简述及协处理器指令
如果对MMU ICache有所了解或者知道其概念作用,那么这节课可以跳过,我们很少会使用MMU或ICache
在2440芯片里面除了CPU之外
Instruction MMU 指令MMU
Data MMU 数据MMU
InstructionC ACHE(16KB) 指令cache
Data CACHE (16KB) 数据cache
全都通过CP15协处理器来进行操作这些
协处理器的含义作用
coprocessor协助主处理器做某些事情,
比如在ARM系统中有cp0 – cp15一共16个协处理器,其中cp15负责管理mmu icache
写一个程序,0到100求和 int sum() { int I; int sum =0; for(i=0; I <= 100; i++) sum += I; return sum; } 查看反汇编代码
局部变量保存在栈中,也就是内存
70: e50b3014 str r3, [fp,#-20] //这个应该就是sum 假设地址是A
78: e50b3010 str r3, [fp, #-16] //这个应该就是I 假设地址是B ldr r3, [fp, #-16] //也就是地址B中取出值 cmp r3, #100 //跟100比较 //如果大于100程序跳到 a8 如果小于100则执行下面的for循环 从7c: 到 a4 指令保存在内存中,CPU根据这些执行进行操作
1 不断的读写地址A和B 2 不断的执行for循环里面代码 2.1 取指令 2.2 执行指令
问SDRAM非常慢,那么怎么提高程序执行效率? 先引入一个感念, 程序局部性原理 时间局部性:在同一段时间里,有极大的概率访问同一地址的指令或数据 (在这个for循环中同一个地址指令经常被访问到) 空间局部性: 有极大概率访问到相邻空间的指令/数据 我们在一个比较慢的SDRAM上能不能在CPU上开一个高速缓存,把这些指令放进高速缓存icache
指令cache只有16KB 数据cache也只有16KB
而我们的SDRAM有64MB空间,显然擦车不可能存储SDRAM中所有的内容,它只能存储一部分
cache的示意图
以数据开始为例 1 程序要读地址A的数据 ldr r0, [A的数据] a. cpu以地址A查找cache,一开始cache无数据,导致cache miss
返回一系列的数据,叫做cache line: 8word 32byte
b. cpu把地址A发到SDRAM,读入cache line,成为cache file 把地址A上的数据返回给CPU 2 程序再次读取地址A的数据 a cpu以地址A查找cache,cache hit有数据直接从cache返回数据给CPU
3 程序要读地址B的数据,CPU也是以地址B查找数据,cache hint直接返回
4 cache满了,CPU访问C a cache替换,置换老的数据 b 填充新数据
数据写 write buffer 查看2410芯片手册 附录 appendix4-caches, write buffer 585页
设置为NCNB (no cache no buffer)数据直接到达硬件不经过缓冲器
比如GPFDAT寄存器CPU读寄存器的时候想读到引脚状态,不应该从cache读取老的数据,而是不断直接访问硬件返回最新的数据 对于这些寄存器应该设置为NCNB
不使用cache但数据写到buffer中,CPU就不管了 由write buffer直接进行写操作
CPU直接操作下一条指令
第一种不使用cache buffer 适用于直接硬件操作 gpio 得到最新数据
第二种 不使用cache使用write buffer, cpu把写发给buffer,cpu就可以直接下一条指令
第三种 WT 写通方式 使用cache不使用buffer,马上写硬件
CPU直接写给write buffer
由write执行缓慢写操作
第四种 写回方式
miss: cpu数据直接到达write buffer
hit: cpu数据写入cache标记为dirty,让后会在合适的时机由write buffer写给硬件
合适的时机 cache替换时dirty会写给write buffer写给硬件 或者强制Flash cache 写给write buffer 写给硬件
下节讲协处理指令
第002节_协处理器指令_开启ICache代码示例
CPU中还有许多协处理器来协助主处理功能 比如2440有CP0 ~ CP15一共16个协处理器
CP15管理cache mmu 我们启动cache需要操作CP15 协处理器指令 先看硬件结构
CP15中也有许多寄存器 C0 ~ C15 启动C7’ 是备份寄存器
现在主CPU中某一个值R0传给CP15中的某一个寄存器
我们需要引入协处理器指令 mrc mov r1, r0 结果是r0 =传给=> r1 mrc c coprocessor =传给=> register
mcr 是把主处理器的值发给协处理器
register =传给=> coprocessor
查看一下语法格式
在2440中搜索mrc
得到语法格式
<MCR|MRC>{cond} p#,<expression1>,Rd,cn,cm{,<expression2>}
举个例子
mcr P15, 0, r1,c1 把主处理器的值发给协处理器 expression1 值设置为0,表示用不到 r1 是主cpu寄存器里面的值 c1 是cp15寄存器里的值
cm, 用不到,写为c0 expression2 值设置为0,表示用不到 cm和expression2用来区分哪一个c1,一般写为c0, 0
这条命令表示主cpu中r1 值写入 协处理器cp15 中的c1寄存器
反过来要从cp15寄存器读到主cpu寄存器
mrc p15, 0, r1, c1, c0, 0
这条命令表示协处理器cp15 c1寄存器的值读出来写入主cpu的r1寄存器
2410手册中有讲cp15寄存器的作用
其中寄存器1控制寄存器 下图为介绍控制寄存器1的功能
bit12位是控制cache指令的开启或者关闭,我们等下把bit 12设置为1
c7里面有许多不同的寄存器,对应不同的功能
寄存器7表示用来操作cache,根据语法规则cm{,<expression2>} 来区分选择那个c7
接下来写程序使能cache
注意2440里有data cache和指令cache 其中data cache要启用地址映射才可以使用,只能使用指令cache
打开start.s reset: /* 关闭看门狗 */ ldr r0, =0x53000000 ldr r1, =0 str r1, [r0]
/* 设置MPLL, FCLK : HCLK : PCLK = 400m : 100m : 50m */ /* LOCKTIME(0x4C000000) = 0xFFFFFFFF */ ldr r0, =0x4C000000 ldr r1, =0xFFFFFFFF str r1, [r0]
/* CLKDIVN(0x4C000014) = 0X5, tFCLK:tHCLK:tPCLK = 1:4:8 */ ldr r0, =0x4C000014 ldr r1, =0x5 str r1, [r0]
/* 设置CPU工作于异步模式 */ mrc p15,0,r0,c1,c0,0 orr r0,r0,#0xc0000000 //R1_nF:OR:R1_iA mcr p15,0,r0,c1,c0,0
/* 设置MPLLCON(0x4C000004) = (92<<12)|(1<<4)|(1<<0) * m = MDIV+8 = 92+8=100 * p = PDIV+2 = 1+2 = 3 * s = SDIV = 1 * FCLK = 2*m*Fin/(p*2^s) = 2*100*12/(3*2^1)=400M */ ldr r0, =0x4C000004 ldr r1, =(92<<12)|(1<<4)|(1<<0) str r1, [r0]
/* 一旦设置PLL, 就会锁定lock time直到PLL输出稳定 * 然后CPU工作于新的频率FCLK */ /* 使能icache */ bl enable_icache
/* 设置内存: sp 栈 */ /* 分辨是nor/nand启动 * 写0到0地址, 再读出来 * 如果得到0, 表示0地址上的内容被修改了, 它对应ram, 这就是nand启动 * 否则就是nor启动 */ mov r1, #0 ldr r0, [r1] /* 读出原来的值备份 */ str r1, [r1] /* 0->[0] */ ldr r2, [r1] /* r2=[0] */ cmp r1, r2 /* r1==r2? 如果相等表示是NAND启动 */ ldr sp, =0x40000000+4096 /* 先假设是nor启动 */ moveq sp, #4096 /* nand启动 */ streq r0, [r1] /* 恢复原来的值 */
bl sdram_init //bl sdram_init2 /* 用到有初始值的数组, 不是位置无关码 */
/* 重定位text, rodata, data段整个程序 */ bl copy2sdram
/* 清除BSS段 */ bl clean_bss
/* 复位之后, cpu处于svc模式 * 现在, 切换到usr模式 */ mrs r0, cpsr /* 读出cpsr */ bic r0, r0, #0xf /* 修改M4-M0为0b10000, 进入usr模式 */ bic r0, r0, #(1<<7) /* 清除I位, 使能中断 */ msr cpsr, r0
/* 设置 sp_usr */ ldr sp, =0x33f00000
ldr pc, =sdram sdram: bl uart0_init
bl print1 /* 故意加入一条未定义指令 */ und_code: .word 0xdeadc0de /* 未定义指令 */ bl print2
swi 0x123 /* 执行此命令, 触发SWI异常, 进入0x8执行 */
//bl main /* 使用BL命令相对跳转, 程序仍然在NOR/sram执行 */ ldr lr, =halt ldr pc, =main /* 绝对跳转, 跳到SDRAM */
halt: b halt 如何使能icache 打开2410芯片手册 enable_icache: /* 设置协处理器使能icache */ mrc p15, 0, r0, c1, c0, 0 orr r0, r0, #(1<<12) /* r0 = r0 or (1<<12) */ mcr p15, 0, r0, c1, c0, 0 //吧修改好的r0写给cp15的c1寄存器 mov pc, lr
刷屏效率变快