四、mini2440裸机程序之MMU(下)

1.本实验相关管脚介绍

       1)原理图

 四、mini2440裸机程序之MMU(下)_第1张图片

        2)由上图可以看出,4个LED灯所对应的管脚:

名称

对应管脚

管脚功能

对应逻辑

LED1

GPB5

内部上拉输出

0:灯亮

1:灯灭

LED2

GPB6

内部上拉输出

0:灯亮

1:灯灭

LED3

GPB7

内部上拉输出

0:灯亮

1:灯灭

LED4

GPB8

内部上拉输出

0:灯亮

1:灯灭

 

2.相关的寄存器:

四、mini2440裸机程序之MMU(下)_第2张图片

 

          1)与LED相关:

注释:

①     GPBCON   :   用于配置端口B的各个管脚功能

②     GPBDAT    :   端口B的数据寄存器

③     GPBUP      :   用于配置端口B是否使用上拉功能

 

 

         2)与MMU相关

 

                  ①   CP15 register 1(control register)

相关bit(M,A,S,R,RR,V(可有可无))


注:

这个放在最后连着开启MMU时一次性设置就好了,需要设置的有:使能Icache,Dcache;使能数据地址对齐异常检查;关于覆写算法默认是ramdom,我们选择round-robin;关于异常向量表的起始位置,我们选择放在最低地址0x0就可以了(这个实验我们还不会用到向量表,所以也可以先忽略掉);endianness选择小端就行了,默认也是小端;开启MMU;

         关于R,S这两个值,要与AP关联上,由下图可以看出,当AP=00时,我们这里选择S:R=10:这样在AP=00,SPRO,UPNA;AP=01,SPR/W,UPNA;AP=10,SPR/W,UPRO;AP=11,SPW/R,UPR/W.

         综上所属,使用配置命令如下:

MRC p15 , 0 , R1 , c1 , c0 , 0  (R1 [29:0]= 000000000000000 1 0 1 00 0 1 0 1111 1 1 1B)

 四、mini2440裸机程序之MMU(下)_第3张图片

 

                  ②CP15 register 2(Translation table base register(TTB))

四、mini2440裸机程序之MMU(下)_第4张图片

注:

在启动MMU之前我们需要设置好TTB,使用配置命令如下:

MCR p15 , 0 , R1 , c2 , c0 , 0  (R1=页表基址)

 

                ③CP15 register 3(Domainaccess control register)


以下为在domain access control寄存器里的access control bits的取值及说明:

四、mini2440裸机程序之MMU(下)_第5张图片

这里我们根据顺序来设置:D0=00(No access)D1=01(Client)D2=11(Manager)其它都设置为10(reserved),使用命令配置如下:

MCR p15 , 0 , R1 , c3 , c0 , 0           (R1 = 10101010101010101010101010110100B)

 

 

                   ④CP15 register 5(fault status register)

 四、mini2440裸机程序之MMU(下)_第6张图片

    

              ⑤CP15 register 7(cacheoperations register)(write-only)

               这个寄存器用于管理ICache 和 DCache. 提供功能如下:


四、mini2440裸机程序之MMU(下)_第7张图片


格式如下:

四、mini2440裸机程序之MMU(下)_第8张图片

注:

开启MMU之前我们需要InvalidateCache,并且清除write buffer,所以使用命令配置如下:

MCR p15 , 0 , R1 , c7 , c7 , 0            (R1=SBZ)

MCR p15 , 0 , R1 , c7 , c10 , 4          (R1=SBZ)

     

                 ⑥CP15 register 8(TLB operation register )


可以使无效整个TBL(s),….其中还可以无效单独有一个记录项(通过使用MVA),MVA被提供在Rd里.当需要用上MVA时,register 10 的MVA格式如下:


注:

在开启MMU之前我们需要进行一次InvalidateTLBs。这样在开启MMU之后就可以自动重新设置TLB的内容了。所以使用命令配置如下:

MCR p15 , 0 , Rd , c8 , c7 , 0         (Rd=SBZ)

 

                ⑦CP15 register 10(TLB lockdown register)


格式如下:


复位时为0x0

 

3.程序流程图设计:

 

        1)主程序流程图:

 四、mini2440裸机程序之MMU(下)_第9张图片

        2)建立页表子程序流程图:

 四、mini2440裸机程序之MMU(下)_第10张图片

注:这里需要注意的是:要把物理地址为0~代码的结束地址,不然使用开启MMU指令后并不正确执行后续的指令,我个人的理解是:在开启MMU之后,CPU上所看到的指令地址会变成VA,这时如果不进行映射到相同的PA地址,那经过MMU之后就可能映射到不可预测的物理地址了或者出现异常。


        3)拷贝测试代码子程序流程图:

 

        4)初始化MMU子程序流程图:

四、mini2440裸机程序之MMU(下)_第11张图片
        

 

 

4.程序设计:

①Makefile

demo_mmu.bin : demo_mmu.o led_display.o
    arm-linux-ld -Tmmu.lds -o demo_mmu_elf demo_mmu.o led_display.o
    arm-linux-objcopy -O binary -S demo_mmu_elf demo_mmu.bin
    arm-linux-objdump -D -m arm demo_mmu_elf > demo_mmu.dis

%.o : %.c
    arm-linux-gcc -Wall  -O2 -c -o $@ $<

%.o : %.S
    arm-linux-gcc -Wall -O2 -c -o $@ $<

clean :
    rm -f demo_mmu.bin demo_mmu_elf demo_mmu.dis *.o



②mmu.lds

SECTIONS {
    first    0x00000000 : {demo_mmu.o}
    second    0xB0005000 : AT(2048) {led_display.o}
}



demo_mmu.S


#define    WTCON        0x53000000

#define    BWSCON        0x48000000
#define    BANKCON6    0x4800001C
#define    REFRESH        0x48000024
#define    BANKSIZE    0x48000028
#define    MRSRB6        0x4800002C

#define    MMU_TTB        0x30000000
#define    MMU_FPTB    (0x30004000)
#define    NCNB_CTTBTT    (0x00<<2)
#define    WT_CTTBTT    (0x03<<2)
#define MANAGER_DOMAIN  (0x02<<5)
#define    SPUP_WR_SECTIONAP    (0x03<<10)
#define    SECTION_FLAG    (0x01<<4)|(0x02)
#define    FINE_FLAG    (0x13)
#define    SPUP_WR_TINYAP    (0x03<<4)
#define    TINY_FLAG    (0x03)    


.Text
.global    _start

_start:
    /******关闭看门狗**********/
    ldr    r0 , =WTCON
    mov    r1 , #0x0
    str    r1 , [r0]
    /*****END关闭看门狗********/


    
    /***调用初始化内存子程序***/
    bl    memory_init
    /**********END*************/

    /*****调用建立页表子程序***/
    bl    build_page
    /***********END************/



    /*****调用初始化MMU子程序**/
    bl    mmu_init
    /***********END************/

    @启动MMU
    mcr    p15 , 0 , r1 , c1 , c0 , 0         @设置control register.启动MMU

/**********让灯全亮************

    @把LED1-4管脚置为输出
    ldr    r0 , =GPBCON
    ldr    r1 , [r0]        @把GPBCON里的内容加载到r1里
    ldr    r2 , =(0xFF<<10)
    bic    r1 , r1 ,r2    @操作数取反码或上r1,用于清零工作
    ldr    r2 , =(0x55<<10)
    orr    r1 , r1 , r2
    str    r1 , [r0]    
    @灯全亮
    ldr    r0 , =GPBDAT
    ldr    r1 , [r0]    
    ldr    r2 , =(0x0F<<5)
    bic    r1 , r1 , r2
    str    r1 , [r0]
***********END***************/

    /****调用测试代码子程序****/
    bl    copy_from_bootsram_to_sdram
    /***********END************/
    @设置栈顶指针
    ldr    sp , =0xB4000000

/****************灯全灭**************/

    ldr    r0 , =GPBCON
    ldr    r1 , [r0]        /*把GPBCON里的内容加载到r1里*/
    bic    r1 , r1 , #0x3FC00    /*操作数取反码或上r1,用于清零工作*/
    orr    r1 , r1 , #0x15400
    str    r1 , [r0]    
    ldr    r0 , =GPBDAT
;    ldr    r1 , [r0]
;    bic    r1 , r1 , #0x1E0    
;    orr    r1 , r1 , #0x1E0
;    str    r1 , [r0]        /*此时4个LED等全灭*/

/***************END灯全灭***************/

    @跳转到测试代码执行
    ldr    pc , =main

halt_loop:
    b    halt_loop




memory_init:
/*******内存初始化子程序*********/

    @BWSCON[27:24] = 0 0 10B
    ldr    r0 , =BWSCON
    ldr    r1 , [r0]
    ldr    r2 , =(0x0F<<24)
    bic    r1 , r1 , r2
    ldr    r2 , =(0x02<<24)
    orr    r1 , r1 , r2
    str    r1 , [r0]    

    @BANKCON6[16:15]=11B;BANKCON6[3:0]=00 01B
    ldr    r0 , =BANKCON6
    ldr    r1 , [r0]
    ldr    r2 , =(0x03<<15)
    bic    r1 , r1 , r2
    orr    r1 , r1 , r2
    ldr    r2 , =0x0F
    bic    r1 , r1 , r2
    ldr    r2 , = 0x01
    orr    r1 , r1 , r2
    str    r1 , [r0]

    @REFRESH[23:18] = 1 0 00 00B;REFRESH[10:0] = 0x7A3
    ldr    r0 , =REFRESH
    ldr    r1 , [r0]
    ldr    r2 , =(0x3F<<18)
    bic    r1 , r1 , r2
    ldr    r2 , =(0x20<<18)
    orr    r1 , r1 , r2
    ldr    r2 , =0x7FF
    bic    r1 , r1 , r2
    ldr    r2 , = 0x7A3
    orr    r1 , r1 , r2
    str    r1 , [r0]

    @BANKSIZE[7:0] = 1 0 1 1 0 001 B
    ldr    r0 , =BANKSIZE
    ldr    r1 , [r0]
    ldr    r2 , =0xFF
    bic    r1 , r1 , r2
    ldr    r2 , =0xB1
    orr    r1 , r1 , r2
    str    r1 , [r0]    

    @MRSRB6[11:0] = 0 00 011 0 000 B
    ldr    r0 , =MRSRB6
    ldr    r1 , [r0]
    ldr    r2 , =0x3FF
    bic    r1 , r1 , r2
    ldr    r2 , =0x030
    orr    r1 , r1 , r2
    str    r1 , [r0]    

    mov    pc , lr        @函数返回
/******END内存初始化子程序*******/




build_page:
/********建立页表子程序***********/

/*对0地址处的映射*/
    ldr    r1 , =0x00000000        @VA起始地址
    ldr    r2 , =0x00000000        @PA起始地址

     @*(MMU_TTB&0xFFFFC000+VA>>20<<2) = (PA&0xFFF00000)|(SPUP_WR(0x03)<<AP(10))|(MANAGER(0x02)<<DOMAIN(5))|(0x01<<4)|(NCNB_CTTBTT(0x00)<<Ctt_Btt(2))|(SECTION_FLAG(0x02))
    ldr    r0 , =(MMU_TTB & 0xFFFFC000)
    mov    r3 , r1 , LSR#20
    add    r0 , r0 , r3  , LSL#2       @r0存入段描述符所在一级页表条目地址
    
    mov    r3 , r2 , LSR#20
    mov    r3 , r3 , LSL#20
    mov    r4 , #0x0
    ldr    r4 , =SPUP_WR_SECTIONAP | MANAGER_DOMAIN | WT_CTTBTT | SECTION_FLAG
    orr    r3 , r3 , r4                @r3存入要放进页表的段描述符

    str    r3 , [r0]                   @往一级描述符地址放入对应的段描述符

/*END对0地址处的映射*/


/*对内存的映射*/
@VA:0xB0000000~0xB3FFFFFF------>PA:0x30000000~0x33FFFFFF
@using section-mapped
@总共64M,需要映射64次,每一次VA跟PA都要自加1M
    ldr    r1 , =0xB0000000        @VA起始地址
    ldr    r2 , =0x30000000        @PA起始地址
    ldr    r5 , =0xB4000000        @循环计数初值
l:      @*(MMU_TTB&0xFFFFC000+VA>>20<<2) = (PA&0xFFF00000)|(SPUP_WR(0x03)<<AP(10))|(MANAGER(0x02)<<DOMAIN(5))|(0x01<<4)|(WT(0x03)<<Ctt_Btt(2))|(SECTION_FLAG(0x02))
    ldr    r0 , =(MMU_TTB & 0xFFFFC000)
    mov    r3 , r1 , LSR#20
    add    r0 , r0 , r3 , LSL#2        @r0存入段描述符所在一级页表条目地址
    
    mov    r3 , r2 , LSR#20
    mov    r3 , r3 , LSL#20
    mov    r4 , #0x0
    ldr    r4 , =SPUP_WR_SECTIONAP | MANAGER_DOMAIN | WT_CTTBTT | SECTION_FLAG
    orr    r3 , r3 , r4                @r3存入要放进页表的段描述符

    str    r3 , [r0]                   @往一级描述符地址放入对应的段描述符
    
    add    r1 , r1 , #0x100000
    add    r2 , r2 , #0x100000
    cmp    r1 , r5
    bne    l                          @如果VA没加到结尾就跳转到前一个l执行
/*END对内存的映射*/

/*对IO相关寄存器的映射*/
@VA:0xA0000000~0xA00003FF------>PA:0x56000000~0x560003FF
@using tiny page-mapped
    ldr    r1 , =0xA0000000        @VA起始地址
    ldr    r2 , =0x56000000        @PA起始地址
@因为IO相关寄存器地址范围不超过1K,所以我这里选择tiny page直接对1K进行设置

@Level one addr=(MMU_TTB&0xFFFFC000+VA>>20<<2)
@*(Level one addr)=(MMU_FPTB&0xFFFFF000)|(MANAGER(0x02)<<DOMAIN(5))|(0x01<<4)|(FINE_FLAG(0x03))
@Level two addr=(MMU_FPTB&0xFFFFF000+(VA>>8)&0xFFC)
@*(Level two addr)=(PA&0xFFFFFC00)|(SPUP_WR(0x03)<<AP(4))|(NCNB(00B)<<Ctt_Btt(2))|(TINY_FLAG(0x03))
    ldr    r0 , =MMU_TTB & 0xFFFFC000
    mov    r3 , r1 , LSR#20
    add    r0 , r0 , r3 , LSL#2        @r0存入小页描述符所在的一级页表条目地址
    
    ldr    r3 , =(MMU_FPTB & 0xFFFFF000) | MANAGER_DOMAIN | FINE_FLAG
    str    r3 , [r0]                   @往一级页表描述符地址放入对应的小页描述符

    ldr    r0 , =(MMU_FPTB & 0xFFFFF000)
    mov    r3 , r1 , LSR#8
    ldr    r4 , =0xFFC
    and    r3 , r3 , r4                
    add    r0 , r0 , r3                @r0存入微小页描述符所在的二级细页表条目地址
    
    mov    r3 , r2 , LSR#10
    mov    r3 , r3 , LSL#10
    ldr    r4 , =SPUP_WR_TINYAP | NCNB_CTTBTT | TINY_FLAG    
     orr    r3 , r3 , r4                @r3存入微小页描述符
    str    r3 , [r0]                   @往一级页表描述符索引到的地址放入对应的小页描述符
/*END对IO相关寄存器的映射*/

@返回到主程序
    mov    pc , lr        
/********END建立页表子程序********/



copy_from_bootsram_to_sdram:
/*******拷贝测试代码子程序********/
@我们把测试代码加载在0x800=2048处(加载地址)
@拷贝到0x30005000=0x30000000(转换表基址)+0x4000(转换表大小)+0x1000(细页表大小)
    mov    r1 , #0x800        @源地址
    ldr     r2 , =0xB0005000   @目标地址
l1:
    ldr    r3 , [r1] , #0x04
    str    r3 , [r2] , #0x04
    cmp    r1 , #0x1000
    bne    l1
    mov    pc , lr
/******END拷贝测试代码子程序******/


mmu_init:
/*********初始化MMU子程序*********/
    mov    r1 , #0x0
    mcr    p15 , 0 , r1 , c7 , c7 , 0        @Invalidate Caches
    mcr    p15 , 0 , r1 , c7 , c10, 4        @清除write buffer
    mcr    p15 , 0 , r1 , c8 , c7 , 0        @Invalidate TLBs
    ldr    r1 , =MMU_TTB
    mcr    p15 , 0 , r1 , c2 , c0 , 0        @设置TTB register
        ldr    r1 , =0xAAAAAAB4
    mcr    p15 , 0 , r1 , c3 , c0 , 0        @设置domain access control register
    
    mrc    p15 , 0 , r1 , c1 , c0 , 0        @读取cp15 control register放入r1
    ldr    r2 , =0x3FFFFFFF
    bic    r1 , r1 , r2
    ldr    r2 , =0x517F
    orr    r1 , r1 , r2
        @如果现在就启动了MMU,那么下面的返回地址就变成虚拟地址,所以跳转后再启动MMU
    mov    pc , lr       @返回主程序
/******END初始化MMU子程序*********/

/************************************************END************************************************************/


④led_display.c


#define    GPBCON    (*(volatile unsigned long *)0xA0000010)
#define    GPBDAT    (*(volatile unsigned long *)0xA0000014)

/**
*如果不开启-O2以上优化,gcc编译器不提供inline优化,所以写入的inline只在编译时加入了-O2优化选项才会有效,这时for里面的循环参数可以不加volatile
*如果开启了-O2以上优化,gcc提供的inline优化有效,如果for循环里做编译器认为没意义的事,循环参数加上volatile声明。
*/

static inline void  delay(volatile unsigned long dly)
{
    for(; dly > 0; dly--)

}
int main()
{
    unsigned long i = 0;
    GPBCON &= ~(0xFF<<10);
    GPBCON |= (0x55<<10);    //把GPB5~8都置为输出功能
    while(1){
        delay(500000);        
        GPBDAT = (~(i<<5));         // 根据i的值,点亮LED1,2,3,4
        if(++i == 16)
            i = 0;
    }
    return 0;
}


你可能感兴趣的:(四、mini2440裸机程序之MMU(下))