查看: 2608|回复: 2

[原创] tq2440学习笔记—TQ2440裸跑-MMU

[复制链接]
  • TA的每日心情
    奋斗
    2017-4-18 08:38
  • 签到天数: 110 天

    连续签到: 1 天

    [LV.6]常住居民II

    发表于 2014-8-19 17:02:04 | 显示全部楼层 |阅读模式
    分享到:
    本帖最后由 霹雳之火 于 2014-8-19 17:55 编辑

            0实验环境与实验例程        
    实验环境:TQ2440
    实验例程:韦东山 嵌入式linux应用开发完全手册
            1 makefile分析                                                            
            
                            
    • objs := head.o init.o leds.o;obj是变量,在下面用$(obj)引用,类似于c语言中的宏
    • mmu.bin : $(objs);mmu.bin是第一个目标,也是最终目标
    • arm-linux-ld -Tmmu.lds -o mmu_elf $^;采用mmu.lds链接脚本,$^表示所有依赖的名字 head.o init.o leds.o
    • arm-linux-objcopy -O binary -S mmu_elf $@   ;$@表示规则的目标文件
    • arm-linux-objdump -D -m arm mmu_elf > mmu.dis ;
    • %.o:%.c
    • arm-linux-gcc -Wall -O2 -c -o $@ $<;$@表示规则的目标文件,$<表示第一个依赖的文件名
    •   %.o:%.S
    •   arm-linux-gcc -Wall -O2 -c -o $@ $<;$@表示规则的目标文件 ,$<表示第一个依赖的文件名
    • clean:
    •   rm -f mmu.bin mmu_elf mmu.dis *.o;在make clean是执行
            

            make执行过程                                                             
    • arm-linux-gcc -Wall -O2 -c -o head.o head.S
    • arm-linux-gcc -Wall -O2 -c -o init.o init.c
    • arm-linux-gcc -Wall -O2 -c -o leds.o leds.c
    • arm-linux-ld -Tmmu.lds -o mmu_elf head.o init.o leds.o
    • arm-linux-objcopy -O binary -S mmu_elf mmu.bin
    •   arm-linux-objdump -D -m arm mmu_elf > mmu.dis
                   
            
    2链接脚本3源代码                        
            
                            
    • SECTIONS {
    •    firtst    0x00000000 : { head.o init.o }
    •   second    0xB0004000 : AT(2048) { leds.o }  ;2048为加载地址,0xB0004000为重定位地址
            

            3源代码                                                      
            
                            
    • @*************************************************************************
    •   @ File:head.S
    •   @ 功能:设置SDRAM,将第二部分代码复制到SDRAM,设置页表,启动MMU,
    •   @       然后跳到SDRAM继续执行
    •   @*************************************************************************
    •   .text
    •   .global _start
    •    _start:
    • ldr sp, =4096                       @ 设置栈指针,以下都是C函数,调用前需要设好栈
    • bl disable_watch_dog               @ 关闭WATCHDOG,否则CPU会不断重启
    • bl memsetup                        @ 设置存储控制器以使用SDRAM
    • bl copy_2th_to_sdram               @ 将第二部分代码复制到SDRAM
    • bl create_page_table               @ 设置页表
    • bl mmu_init                        @ 启动MMU
    • ldr sp, =0xB4000000                 @ 重设栈指针,指向SDRAM顶端(使用虚拟地址)
    •   ldr pc, =0xB0004000                 @ 跳到SDRAM中继续执行第二部分代码
    • @ ldr pc, =main
    • halt_loop:
    •   b   halt_loop
                                                
    • /*
    • * init.c: 进行一些初始化,在Steppingstone中运行
    • * 它和head.S同属第一部分程序,此时MMU未开启,使用物理地址
    • */
    • /* WATCHDOG寄存器 */
    • #define WTCON (*(volatile unsigned long *)0x53000000)
    •   /* 存储控制器的寄存器起始地址 */
    •    #define MEM_CTL_BASE    0x48000000
    •   /*
    • * 关闭WATCHDOG,否则CPU会不断重启
    • */
    • void disable_watch_dog(void)
    • {
    • WTCON = 0; // 关闭WATCHDOG很简单,往这个寄存器写0即可
    • }
    • /*
    • * 设置存储控制器以使用SDRAM
    • */
    •   void memsetup(void)
    • {
    • /* SDRAM 13个寄存器的值 */
    •   unsigned long const mem_cfg_val[={ 0x22011110, //BWSCON
    •                                                                             0x00000700, //BANKCON0
    •                                                                             0x00000700, //BANKCON1
    •                                                                             0x00000700, //BANKCON2
    •                                                                             0x00000700, //BANKCON3
    •                                                                             0x00000700, //BANKCON4
    •                                                                             0x00000700, //BANKCON5
    •                                                                             0x00018005, //BANKCON6
    •                                                                             0x00018005, //BANKCON7
    •                                                                             0x008C07A3, //REFRESH
    •                                                                             0x000000B1, //BANKSIZE
    •                                                                             0x00000030, //MRSRB6
    •                                                                             0x00000030, //MRSRB7
    •                                                                     };
    •    int i = 0;
    • volatile unsigned long *p = (volatile unsigned long *)MEM_CTL_BASE;
    •    for(; i < 13; i++)
    •    p[i = mem_cfg_val[i;
    •    }
    • /*
    • * 将第二部分代码复制到SDRAM
    • */
    •   void copy_2th_to_sdram(void)
    •    {
    •   unsigned int *pdwSrc = (unsigned int *)2048;
    • unsigned int *pdwDest = (unsigned int *)0x30004000;
    • while (pdwSrc < (unsigned int *)4096)
    •     {
    •   *pdwDest = *pdwSrc;
    •      pdwDest++;
    •       pdwSrc++;
    •   }
    •      }
    •   /*
    •    * 设置页表
    •   */
    • 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) /* cacheable */
    •   #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;
    •    /*
    •   * Steppingstone的起始物理地址为0,第一部分程序的起始运行地址也是0,
    •   * 为了在开启MMU后仍能运行第一部分的程序,
    • * 将0~1M的虚拟地址映射到同样的物理地址
    •    */
    •   virtuladdr = 0;
    •    physicaladdr = 0;
    •   *(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | \
    •                                                                             MMU_SECDESC_WB;
    • /*
    •   * 0x56000000是GPIO寄存器的起始物理地址,
    •   * GPBCON和GPBDAT这两个寄存器的物理地址0x56000010、0x56000014,
    • * 为了在第二部分程序中能以地址0xA0000010、0xA0000014来操作GPBCON、GPBDAT,
    • * 把从0xA0000000开始的1M虚拟地址空间映射到从0x56000000开始的1M物理地址空间
    •    */
    •   virtuladdr = 0xA0000000;
    •    physicaladdr = 0x56000000;
    •    *(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | \
    •                                                                             MMU_SECDESC;
    • /*
    • * SDRAM的物理地址范围是0x30000000~0x33FFFFFF,
    • * 将虚拟地址0xB0000000~0xB3FFFFFF映射到物理地址0x30000000~0x33FFFFFF上,
    •   * 总共64M,涉及64个段描述符
    •    */
    • virtuladdr = 0xB0000000;
    • physicaladdr = 0x30000000;
    • while (virtuladdr < 0xB4000000)
    • {
    • *(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | \
    •                                                                                 MMU_SECDESC_WB;
    • virtuladdr += 0x100000;
    • physicaladdr += 0x100000;
    •    }
    • }
    • /*
    •   * 启动MMU
    • */
    •   void mmu_init(void)
    • {
    • unsigned long ttb = 0x30000000;
    • // ARM休系架构与编程
    • // 嵌入汇编:LINUX内核完全注释
    • __asm__(
    •    "mov    r0, #0\n"
    •     "mcr    p15, 0, r0, c7, c7, 0\n" /* 使无效ICaches和DCaches */
    •   "mcr    p15, 0, r0, c7, c10, 4\n" /* drain write buffer on v4 */
    •   "mcr    p15, 0, r0, c8, c7, 0\n" /* 使无效指令、数据TLB */
    • "mov    r4, %0\n" /* r4 = 页表基址 */
    • "mcr    p15, 0, r4, c2, c0, 0\n" /* 设置页表基址寄存器 */
    • "mvn    r0, #0\n"
    • "mcr    p15, 0, r0, c3, c0, 0\n"
    • /* 域访问控制寄存器设为0xFFFFFFFF,
    • * 不进行权限检查
    • */
    • /*
    •   * 对于控制寄存器,先读出其值,在这基础上修改感兴趣的位,
    •   * 然后再写入
    •   */
    •   "mrc    p15, 0, r0, c1, c0, 0\n" /* 读出控制寄存器的值 */
    • /* 控制寄存器的低16位含义为:.RVI ..RS B... .CAM
    •   * R : 表示换出Cache中的条目时使用的算法,
    • * 0 = Random replacement;1 = Round robin replacement
    •   * V : 表示异常向量表所在的位置,
    • * 0 = Low addresses = 0x00000000;1 = High addresses = 0xFFFF0000
    • * I : 0 = 关闭ICaches;1 = 开启ICaches
    • * R、S : 用来与页表中的描述符一起确定内存的访问权限
    • * B : 0 = CPU为小字节序;1 = CPU为大字节序
    • * C : 0 = 关闭DCaches;1 = 开启DCaches
    • * A : 0 = 数据访问时不进行地址对齐检查;1 = 数据访问时进行地址对齐检查
    • * M : 0 = 关闭MMU;1 = 开启MMU
    •    */
    •     /*
    •     * 先清除不需要的位,往下若需要则重新设置它们
    •   */
    •                                                                         /* .RVI ..RS B... .CAM */
    •    "bic    r0, r0, #0x3000\n" /* ..11 .... .... .... 清除V、I位 */
    •    "bic    r0, r0, #0x0300\n" /* .... ..11 .... .... 清除R、S位 */
    •   "bic    r0, r0, #0x0087\n" /* .... .... 1... .111 清除B/C/A/M */
    •   /*
    • * 设置需要的位
    • */
    •    "orr    r0, r0, #0x0002\n" /* .... .... .... ..1. 开启对齐检查 */
    •   "orr    r0, r0, #0x0004\n" /* .... .... .... .1.. 开启DCaches */
    •   "orr    r0, r0, #0x1000\n" /* ...1 .... .... .... 开启ICaches */
    • "orr    r0, r0, #0x0001\n" /* .... .... .... ...1 使能MMU */
    • "mcr    p15, 0, r0, c1, c0, 0\n" /* 将修改的值写入控制寄存器 */
    •    : /* 无输出 */
    •    : "r" (ttb) );
    •   }
            


                                                    点击(此处)折叠或打开               
            
                            
    • /*
    •   * leds.c: 循环点亮4个LED
    •   * 属于第二部分程序,此时MMU已开启,使用虚拟地址
    •   */
    • #define GPBCON (*(volatile unsigned long *)0xA0000010) // 物理地址0x56000010
    • #define GPBDAT (*(volatile unsigned long *)0xA0000014) // 物理地址0x56000014
    • /*
    •   * LED1,LED2,LED4对应GPB5、GPB6、GPB7、GPB8
    •   */
    •   #define    GPB5_out    (1<<(5*2))
    •   #define    GPB6_out    (1<<(6*2))
    •   #define    GPB7_out    (1<<(7*2))
    •   #define    GPB8_out    (1<<(8*2))
    •    /*
    •    * wait函数加上“static inline”是有原因的,
    •    * 这样可以使得编译leds.c时,wait嵌入main中,编译结果中只有main一个函数。
    •    * 于是在连接时,main函数的地址就是由连接文件指定的运行时装载地址。
    •    * 而连接文件mmu.lds中,指定了leds.o的运行时装载地址为0xB4004000,
    •   * 这样,head.S中的“ldr pc, =0xB4004000”就是跳去执行main函数。
    •   */
    •   static inline void wait(volatile unsigned long dly)
    •   {
    •   for(; dly > 0; dly--);
    •    }
    •   int main(void)
    •    {
    •   unsigned long i = 0;
    • // LED1,LED2,LED3,LED4对应的4根引脚设为输出
    • GPBCON = GPB5_out | GPB6_out | GPB7_out | GPB8_out;
    •   while(1){
    • wait(30000);
    •   GPBDAT = (~(i<<5));         // 根据i的值,点亮LED1,2,3,4
    •     if(++i == 16)
    • i = 0;
    •   }
    • return 0;
    • }
            

        4总结      
    mmu主要讲虚拟地址转换为物理地址
    makefile的命令前必须为tab,不能为空格

    回复

    使用道具 举报

  • TA的每日心情
    奋斗
    2016-8-15 09:28
  • 签到天数: 222 天

    连续签到: 1 天

    [LV.7]常住居民III

    发表于 2014-8-19 19:18:43 | 显示全部楼层
    参考学习!!!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2016-8-15 09:30
  • 签到天数: 162 天

    连续签到: 1 天

    [LV.7]常住居民III

    发表于 2014-8-19 22:45:02 | 显示全部楼层
    多谢楼主分享
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /2 下一条



    手机版|小黑屋|与非网

    GMT+8, 2024-4-19 11:47 , Processed in 0.146254 second(s), 19 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2020, Tencent Cloud.