Chinaunix首页 | 论坛 | 博客

分类: LINUX

2012-12-05 19:40:15

    ARM 微处理器可支持多达 16 个协处理器,用于各种协处理操作,在程序执行的过程中,每个协处理器只执行针对自身的协处理指令,忽略 ARM 处理器和其它协处理器的指令。CP15 —— 系统控制协处理器(the system control coprocessor),通过协处理器指令 mcr 和 mrc 来控制时钟模式、caches、MMU 等。CP15 包含 16 个 32 位寄存器,其编号 0 ~ 15。
   
  
mcr/mrc {}

, , , , , {,>
: 执行条件码,忽略时指令无条件执行。
: 协处理器行为操作码,对于 CP15,永远是 0b000,否则结果未知。
: 作为源寄存器的 ARM 寄存器,不能使 r15/pc,否则结果未知。
: 作为目标寄存器的协处理器寄存器,编号 c0 ~ c15。
两者组合决定对协处理器寄存器进行所需的操作,如果没有指定,则将 设为 c0, 设为 0,否则结果未知。

控制寄存器 c1
   

TTB 基址寄存器 c2
    CP15 的 c2 寄存器保存的是页表的基地址,即一级映射描述符表的基地址。写入时 bit[13:0] 必须为0,读出时 bit[13:0] 不可预知。
read TTB register : mrc p15,0,Rd,c2,c0,0
write TTB register : mcr p15,0,Rd,c2,c0,0 
cache 操作寄存器 c7
    CP15 的 c7 寄存器用来控制 cache 和写缓存,它是一个只写寄存器,读操作将产生不可预知的后果。 不同组合实现不同功能。
invalidate Icache & Dcache : mcr p15,0,Rd,c7,c7,0
invalidate Icache : mcr p15,0,Rd,c7,c5,0
drain write on buffer v4 : mcr p15,0,Rd,c7,c10,4
TLB 操作寄存器 c8
    c8 寄存器用来控制清除 TLB 的内容,是只写寄存器,读操作将产生不可预知的后果。 不同组合实现不同功能。
invalidate TLB(s) : mcr p15,0,Rd,c8,c7,0

设置页表:
void create_page_table(void)
{
    /* 用于段描述符的一些宏定义 */
#define MMU_FULL_ACCESS   (3 << 10)   /* 访问权限 */
#define MMU_DOMAIN        (0 << 5)    /* 属于哪个域 */
#define MMU_SPECIAL       (1 << 4)    /* 必须是1 */
#define MMU_CACHEABLE     (1 << 3)    /* cache able */
#define MMU_BUFFERABLE    (1 << 2)    /* buffer able */
#define MMU_SECTION       (2)         /* 表示这是段描述符 */
#define MMU_SECDESC       (MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | \
                           MMU_SECTION)
#define MMU_SECDESC_WB    (MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | \
                           MMU_CACHEABLE | MMU_BUFFERABLE | MMU_SECTION)
#define MMU_SECTION_SIZE  0x00100000


    unsigned long virtuladdr, physicaladdr;
    unsigned long *mmu_tlb_base = (unsigned long *)0x30000000;
    /* 设置映射表项 */
    virtuladdr = 0x30000000;
    physicaladdr = 0x30000000;
    while (virtuladdr < 0x34000000)
    {
        *(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr \
                                    & 0xFFF00000) | MMU_SECDESC_WB;
        virtuladdr += 0x100000;
        physicaladdr += 0x100000;
    }
    virtuladdr = 0x48000000;
    physicaladdr = 0x48000000;
    while (virtuladdr < 0x60000000)
    {
        *(mmu_tlb_base +
(virtuladdr >> 20)) = (physicaladdr \
                                    & 0xFFF00000) |
MMU_SECDESC;
        virtuladdr += 0x100000;
        physicaladdr += 0x100000;
    }
}
当页表设置好后就可以开启 MMU:
void mmu_init(void)
{
    unsigned long ttb = 0x30000000;
    __asm__( 
       
/* MMU_DisableDCache */     
        "mrc p15,0,r0,c1,c0,0\n"
        "bic r0,r0,#0x0004\n"
        "mcr p15,0,r0,c1,c0,0\n" 
       
/* MMU_DisableICache */   
       
"mrc p15,0,r0,c1,c0,0\n"
        "bic r0,r0,#0x1000\n"
        "mcr p15,0,r0,c1,c0,0\n" 
  

       
/* MMU_InvalidateICache */
        "mcr p15,0,r0,c7,c5,0\n"   
        /* MMU_DisableMMU */
        "mrc p15,0,r0,c1,c0,0\n"
        "bic r0,r0,#0x0001\n"
        "mcr p15,0,r0,c1,c0,0\n"
        /* MMU_InvalidateTLB */
        "mcr p15,0,r0,c8,c7,0\n"
        /* MMU_WriteTTB */
        "mov r4,%0\n"
        "mcr p15,0,r4,c2,c0,0\n"
        /* 域访问控制寄存器设为0xffffffff,即不进行域权限检查 */
        "mvn r0,#0\n"                  
        "mcr p15,0,r0,c3,c0,0\n"
        /* 对于c1,先读出其值,在此基础上修改感兴趣的位,然后写入 */
        "mcr p15,0,r0,c1,c0,0\n"
        /* 先清除不需要的位,往下若需要则重新设置它们 */
        "bic r0,r0,#0x3000\n"    /* 清V、I位  */
        "bic r0,r0,#0x0300\n"    /* 清R、S位 */
        "bic r0,r0,#0x0087\n"    /* 清B、C、A、M位 */
        /* 设置需要的位 */
        "orr r0,r0,#0x0002\n"    /* 开启对齐检查 */
        "orr r0,r0,#0x0004\n"    /* 开启Dcache */
        "orr r0,r0,#0x1000\n"    /* 开启Icache */
        "orr r0,r0,#0x0001\n"    /* 使能MMU */
        "mcr p15,0,r0,c1,c0,0\n"    /* 将修改的值写入c1 */
        :    /* 输出 */
        : "r"(ttb)    /* 输入 */
    );
}

小结:
1.启动 MMU 后,如果地址不变,不需要重新设置 SP。
2.内存和寄存器的映射属性不一样,切记!
3.MMU 可以在子函数中初始化。
4.设置 MMU 的时候如果之前已经开启 cache,必须先关闭
cache
   
       ——Be loyal to dream. Be brave to try!    linux_xpj@opencores.org

阅读(2257) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~