S5P4418裸机开发(十六):ache & MMU

Cache

cache 分为指令cache (Instruction cache)和数据cache (Data cache)。

  • 开启MMU后才能使用数据cache。
  • 三种映射方式:直接映射,全相联映射,组相联映射
  • 使用write buffer时,有两种数据写回方式:写穿式,写cache的同时写入主存;写回式,cache中的数据被换出或强制进行“清空”操作时才将更新的数据写回主存。
  • 写回式,如果cache miss,数据写到write buffer,write buffer再写到主存,而CPU不用等待写完成;如果cache命中,CPU直接写cache,在cache中的数据被换出或强制进行“清空”操作时,更新了的数据再写给writebuffer,再由write buffer写到主存。后面有图说的更详细。

更多:
[1]PU体系结构之cache小结
[2]cache结构与工作原理

MMU

Memory Management Unit 负责虚拟地址(Virtual Address)到物理地址(Physical Address)的映射,并提供硬件机制的内存访问权限检查。

  • 使用MMU,要在内存中创建页表(Page Table),页表中的每一项称为条目(Entry)或描述符(Descriptor)。
  • 页表存的是VA与PA的对应关系,32位CPU能寻址4GB虚拟空间,以段的方式进行转换时只用到一级页表,每个描述符对应1MB的虚拟地址,则需要4096个描述符来表示,每个描述符占4字节,则一级页表需要16KB的空间。
  • 一级页表的描述符格式,[1:0]区分类别
    在这里插入图片描述
    • 在段(Section)页表中,位[31:20]表示段基址(Section base address),段基址的低20位填充0后就是一块1MB物理地址空间的起始地址。MVA[19:0]则用来在这1MB空间中寻址。所以,描述符的位[31:20]和MVA[19:0]就构成了这个虚拟地址MVA对应的物理地址。
    • 位[11:10]表示AP,位[8:5]表示域(Domain),描述符的域、描述符的AP位、CP15寄存器C1的R/S/A位等联合作用于内存的访问权限检查。
    • 共16个域,CP15寄存器C3中的每两位对应一个域。域决定是否对某块内存进行权限检查
      在这里插入图片描述
      在这里插入图片描述
      下面是《ARM® Architecture Reference Manual, ARMv7-A and ARMv7-R edition 》中的说明。
      在这里插入图片描述
    • CP15寄存器C1中的A位表示是否对地址进行对齐检查。R位、S位和描述符的AP位则决定如何对某块内存进行权限检查
      在这里插入图片描述
    • 位[3],C,表示是否使用Cache。位[2],B,表示是否使用Write buffer。
      在这里插入图片描述
      在这里插入图片描述
      比如对寄存器读写时应要立即写具体地址,所以应选择NCNB。而代码段的程序则可以使用WB来加快读写速度。
  • 创建完页表后,把页表基值告诉MMU,通过操作CP15的C2寄存器来完成。
    在这里插入图片描述
  • 最后使能MMU,通过操作CP15的C1寄存器来完成。SCTLR
    在这里插入图片描述

代码

mmu.c

#define MMU_SECDESC_AP      (3<<10)	// AP位设置为11,即所有模式下允许任何访问
#define MMU_SECDESC_DOMAIN  (0<<5)	// 选择0域
#define MMU_SECDESC_NCNB    (0<<2)	// no cache no writebuffer
#define MMU_SECDESC_WB      (3<<2)	// cache and writebuffer
#define MMU_SECDESC_TYPE    ((1<<4) | (1<<1)) // 描述符类型是段

#define MMU_SECDESC_FOR_IO   (MMU_SECDESC_AP | MMU_SECDESC_DOMAIN | MMU_SECDESC_NCNB | MMU_SECDESC_TYPE)
#define MMU_SECDESC_FOR_MEM   (MMU_SECDESC_AP | MMU_SECDESC_DOMAIN | MMU_SECDESC_WB | MMU_SECDESC_TYPE)

#define MEM		0
#define IO		1

/*  创建一级页表
 *  VA          PA          CB
 *  ISRAM: 内部SRAM,可以使用cache和write buffer
 *  0xFFFF0000  0xFFFF0000  11
 * 	
 * 	DDR3: 裸机程序是放在这里
 * 	0x42C00000	0x42C00000  11
 *  
 *  Normal I/O:	寄存器部分,不应该使用cache和write buffer
 *  0xC0000000  0xC0000000  00
 *  0xC0100000  0xC0100000  00
 *  0xC0200000  0xC0200000  00
 * 
 *  PERIPHBASE:
 *  0xF0000000  0xF0000000  00
 * 
 *  Link addr: 链接地址是0x70000000,表示程序运行地址,会被映射到0x42C00000,可以使用cache和write buffer
 *  0x70000000  0x42C00000  11
 *  
 */

void create_secdesc(unsigned int *ttb, unsigned int va, unsigned int pa, int io){
	int index;

	index = va / 0x100000;	// 确定4096个描述符中的哪一个

	if (io)
		ttb[index] = (pa & 0xfff00000) | MMU_SECDESC_FOR_IO;
	else
		ttb[index] = (pa & 0xfff00000) | MMU_SECDESC_FOR_MEM;
}

void create_page_table(){
    unsigned int *ttb = (unsigned int *)0x50000000;	// 基值选择一个未被使用的地方,4GB的一级页表占用16KB空间

	create_secdesc(ttb, 0xFFFF0000, 0xFFFF0000, MEM);
	create_secdesc(ttb, 0x42C00000, 0x42C00000, MEM);
	create_secdesc(ttb, 0xC0000000, 0xC0000000, IO);
	create_secdesc(ttb, 0xC0100000, 0xC0100000, IO);
	create_secdesc(ttb, 0xC0200000, 0xC0200000, IO);
	create_secdesc(ttb, 0xF0000000, 0xF0000000, IO);
	create_secdesc(ttb, 0x70000000, 0x42C00000, MEM);

}

问:为什么0x42C00000也要映射一遍?
答:创建页表、使能MMU之前,从2nboot跳到裸机程序,程序是从物理地址0x42C00000开始执行,使能MMU之后,PC值并没有变,还是从0x42C0XXXX处取指令,如果没有0x42C00000到0x42C00000的映射,则取不到正确的指令。

init.c

void mmu_enble(){

    create_page_table();     // 创建段页表

    __asm__(
        "ldr r0, =0x50000000\n"
        "mcr p15, 0, r0, c2, c0, 0\n"
    );  // 把页表基址告诉cp15

    __asm__(
        "ldr r0, =0xFFFFFFFF\n"
        "mcr p15, 0, r0, c3, c0, 0\n"
    );  // 设置域为0xffffffff, 即所有域都不进行权限检查
    
    __asm__(
        "mrc p15, 0, r0, c1, c0, 0\n"
        "orr r0, r0, #(1 << 12)\n"     // 使能ICache
        // "bic r0, r0, #(1 << 12)\n"     // 关闭ICache
        "orr r0, r0, #(1 << 2)\n"     // 使能DCache
        "orr r0, r0, #(1 << 0)\n"     // 使能MMU
        "mcr p15, 0, r0, c1, c0, 0\n"
    );  

}

效果测试

启动timer0timer0时钟频率设置为50kHz,计数器装载值设定为 5000000,主程序中两次 delay(20000000) 后打印计数器值。
主函数

void delay(uint t){
    while(t--);
}

int main(){
	while(1){
        led1_on();
        delay(20000000);
        led1_off();
        delay(20000000);
        // printf("TCON_TIMER0: %x\r\n", TCNTO0_TIMER0);
        if(++t < 10){
            printf("TCNTO0:[%x] %d\r\n", TCNTO0, t);
        }
    }
}
  • 关闭指令Cache,关闭数据Cache,关闭MMU
__asm__(
        "mrc p15, 0, r0, c1, c0, 0\n"
        // "orr r0, r0, #(1 << 12)\n"     // 使能ICache
        "bic r0, r0, #(1 << 12)\n"     // 关闭ICache
        // "orr r0, r0, #(1 << 2)\n"     // 使能DCache
        // "orr r0, r0, #(1 << 0)\n"     // 使能MMU
        "mcr p15, 0, r0, c1, c0, 0\n"
    ); 

在这里插入图片描述
每次间隔大概是 0x37d00;

  • 开启指令Cache,关闭数据Cache,关闭MMU
__asm__(
        "mrc p15, 0, r0, c1, c0, 0\n"
        "orr r0, r0, #(1 << 12)\n"     // 使能ICache
        // "bic r0, r0, #(1 << 12)\n"     // 关闭ICache
        // "orr r0, r0, #(1 << 2)\n"     // 使能DCache
        // "orr r0, r0, #(1 << 0)\n"     // 使能MMU
        "mcr p15, 0, r0, c1, c0, 0\n"
    );  

在这里插入图片描述
间隔0x37e00。效率还差了些?

  • 开启指令Cache,开启数据Cache,开启MMU
void mmu_enble(){

    create_page_table();     // 创建段页表

    __asm__(
        "ldr r0, =0x50000000\n"
        "mcr p15, 0, r0, c2, c0, 0\n"
    );  // 把页表基址告诉cp15

    __asm__(
        "ldr r0, =0xFFFFFFFF\n"
        "mcr p15, 0, r0, c3, c0, 0\n"
    );  // 设置域为0xffffffff, 不进行权限检查
    
    __asm__(
        "mrc p15, 0, r0, c1, c0, 0\n"
        "orr r0, r0, #(1 << 12)\n"     // 使能ICache
        // "bic r0, r0, #(1 << 12)\n"     // 关闭ICache
        "orr r0, r0, #(1 << 2)\n"     // 使能DCache
        "orr r0, r0, #(1 << 0)\n"     // 使能MMU
        "mcr p15, 0, r0, c1, c0, 0\n"
    );  

}

在这里插入图片描述
每次间隔 0x3ae8;比关闭数据Cache、关闭MMU时快了15倍。

注:MMU初始化放在进main函数之前比较好。

工程文件

11_mmu_cache

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值