Chinaunix首页 | 论坛 | 博客
  • 博客访问: 62289
  • 博文数量: 28
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 10
  • 用 户 组: 普通用户
  • 注册时间: 2014-11-30 01:24
文章分类
文章存档

2017年(1)

2016年(5)

2015年(22)

我的朋友

分类: 嵌入式

2015-07-15 12:00:45

原文地址:TQ2440裸跑-MMU 作者:癞头和尚

0实验环境与实验例程

实验环境:TQ2440

实验例程:韦东山 嵌入式linux应用开发完全手册

1 makefile分析

点击(此处)折叠或打开

  1. objs := head.o init.o leds.o;obj是变量,在下面用$(obj)引用,类似于c语言中的宏

  2. mmu.bin : $(objs);mmu.bin是第一个目标,也是最终目标
  3. arm-linux-ld -Tmmu.lds -o mmu_elf $^;采用mmu.lds链接脚本,$^表示所有依赖的名字 head.o init.o leds.o
  4. arm-linux-objcopy -O binary -S mmu_elf $@   ;$@表示规则的目标文件
  5. arm-linux-objdump -D -m arm mmu_elf > mmu.dis
  6. ;
  7. %.o:%.c
  8. arm-linux-gcc -Wall -O2 -c -o $@ $<;$@表示规则的目标文件,$<表示第一个依赖的文件名

  9. %.o:%.S
  10. arm-linux-gcc -Wall -O2 -c -o $@ $<;$@表示规则的目标文件 ,$<表示第一个依赖的文件名

  11. clean:
  12. rm -f mmu.bin mmu_elf mmu.dis *.o;在make clean是执行

make执行过程

点击(此处)折叠或打开

  1. arm-linux-gcc -Wall -O2 -c -o head.o head.S
  2. arm-linux-gcc -Wall -O2 -c -o init.o init.c
  3. arm-linux-gcc -Wall -O2 -c -o leds.o leds.c
  4. arm-linux-ld -Tmmu.lds -o mmu_elf head.o init.o leds.o
  5. arm-linux-objcopy -O binary -S mmu_elf mmu.bin
  6. arm-linux-objdump -D -m arm mmu_elf > mmu.dis

2链接脚本3源代码

点击(此处)折叠或打开

  1. SECTIONS {
  2. firtst 0x00000000 : { head.o init.o }
  3. second 0xB0004000 : AT(2048) { leds.o }  ;2048为加载地址,0xB0004000为重定位地址

3源代码

点击(此处)折叠或打开

  1. @*************************************************************************
  2. @ File:head.S
  3. @ 功能:设置SDRAM,将第二部分代码复制到SDRAM,设置页表,启动MMU,
  4. @ 然后跳到SDRAM继续执行
  5. @*************************************************************************

  6. .text
  7. .global _start
  8. _start:
  9.     ldr sp, =4096 @ 设置栈指针,以下都是C函数,调用前需要设好栈
  10.     bl disable_watch_dog @ 关闭WATCHDOG,否则CPU会不断重启
  11.     bl memsetup @ 设置存储控制器以使用SDRAM
  12.     bl copy_2th_to_sdram @ 将第二部分代码复制到SDRAM
  13.     bl create_page_table @ 设置页表
  14.     bl mmu_init @ 启动MMU
  15.     ldr sp, =0xB4000000 @ 重设栈指针,指向SDRAM顶端(使用虚拟地址)
  16.     ldr pc, =0xB0004000 @ 跳到SDRAM中继续执行第二部分代码
  17.     @ ldr pc, =main
  18. halt_loop:
  19.     b halt_loop

点击(此处)折叠或打开

  1. /*
  2.  * init.c: 进行一些初始化,在Steppingstone中运行
  3.  * 它和head.S同属第一部分程序,此时MMU未开启,使用物理地址
  4.  */

  5. /* WATCHDOG寄存器 */
  6. #define WTCON (*(volatile unsigned long *)0x53000000)
  7. /* 存储控制器的寄存器起始地址 */
  8. #define MEM_CTL_BASE 0x48000000


  9. /*
  10.  * 关闭WATCHDOG,否则CPU会不断重启
  11.  */
  12. void disable_watch_dog(void)
  13. {
  14.     WTCON = 0; // 关闭WATCHDOG很简单,往这个寄存器写0即可
  15. }

  16. /*
  17.  * 设置存储控制器以使用SDRAM
  18.  */
  19. void memsetup(void)
  20. {
  21.     /* SDRAM 13个寄存器的值 */
  22.     unsigned long const mem_cfg_val[]={ 0x22011110, //BWSCON
  23.                                             0x00000700, //BANKCON0
  24.                                             0x00000700, //BANKCON1
  25.                                             0x00000700, //BANKCON2
  26.                                             0x00000700, //BANKCON3
  27.                                             0x00000700, //BANKCON4
  28.                                             0x00000700, //BANKCON5
  29.                                             0x00018005, //BANKCON6
  30.                                             0x00018005, //BANKCON7
  31.                                             0x008C07A3, //REFRESH
  32.                                             0x000000B1, //BANKSIZE
  33.                                             0x00000030, //MRSRB6
  34.                                             0x00000030, //MRSRB7
  35.                                     };
  36.     int i = 0;
  37.     volatile unsigned long *p = (volatile unsigned long *)MEM_CTL_BASE;
  38.     for(; i < 13; i++)
  39.         p[i] = mem_cfg_val[i];
  40. }

  41. /*
  42.  * 将第二部分代码复制到SDRAM
  43.  */
  44. void copy_2th_to_sdram(void)
  45. {
  46.     unsigned int *pdwSrc = (unsigned int *)2048;
  47.     unsigned int *pdwDest = (unsigned int *)0x30004000;
  48.     
  49.     while (pdwSrc < (unsigned int *)4096)
  50.     {
  51.         *pdwDest = *pdwSrc;
  52.         pdwDest++;
  53.         pdwSrc++;
  54.     }
  55. }

  56. /*
  57.  * 设置页表
  58.  */
  59. void create_page_table(void)
  60. {

  61. /*
  62.  * 用于段描述符的一些宏定义
  63.  */
  64. #define MMU_FULL_ACCESS (3 << 10) /* 访问权限 */
  65. #define MMU_DOMAIN (0 << 5) /* 属于哪个域 */
  66. #define MMU_SPECIAL (1 << 4) /* 必须是1 */
  67. #define MMU_CACHEABLE (1 << 3) /* cacheable */
  68. #define MMU_BUFFERABLE (1 << 2) /* bufferable */
  69. #define MMU_SECTION (2) /* 表示这是段描述符 */
  70. #define MMU_SECDESC (MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | \
  71.                              MMU_SECTION)
  72. #define MMU_SECDESC_WB (MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | \
  73.                              MMU_CACHEABLE | MMU_BUFFERABLE | MMU_SECTION)
  74. #define MMU_SECTION_SIZE 0x00100000

  75.     unsigned long virtuladdr, physicaladdr;
  76.     unsigned long *mmu_tlb_base = (unsigned long *)0x30000000;
  77.     
  78.     /*
  79.      * Steppingstone的起始物理地址为0,第一部分程序的起始运行地址也是0,
  80.      * 为了在开启MMU后仍能运行第一部分的程序,
  81.      * 将0~1M的虚拟地址映射到同样的物理地址
  82.      */
  83.     virtuladdr = 0;
  84.     physicaladdr = 0;
  85.     *(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | \
  86.                                             MMU_SECDESC_WB;

  87.     /*
  88.      * 0x56000000是GPIO寄存器的起始物理地址,
  89.      * GPBCON和GPBDAT这两个寄存器的物理地址0x56000010、0x56000014,
  90.      * 为了在第二部分程序中能以地址0xA0000010、0xA0000014来操作GPBCON、GPBDAT,
  91.      * 把从0xA0000000开始的1M虚拟地址空间映射到从0x56000000开始的1M物理地址空间
  92.      */
  93.     virtuladdr = 0xA0000000;
  94.     physicaladdr = 0x56000000;
  95.     *(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | \
  96.                                             MMU_SECDESC;

  97.     /*
  98.      * SDRAM的物理地址范围是0x30000000~0x33FFFFFF,
  99.      * 将虚拟地址0xB0000000~0xB3FFFFFF映射到物理地址0x30000000~0x33FFFFFF上,
  100.      * 总共64M,涉及64个段描述符
  101.      */
  102.     virtuladdr = 0xB0000000;
  103.     physicaladdr = 0x30000000;
  104.     while (virtuladdr < 0xB4000000)
  105.     {
  106.         *(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | \
  107.                                                 MMU_SECDESC_WB;
  108.         virtuladdr += 0x100000;
  109.         physicaladdr += 0x100000;
  110.     }
  111. }

  112. /*
  113.  * 启动MMU
  114.  */
  115. void mmu_init(void)
  116. {
  117.     unsigned long ttb = 0x30000000;

  118. // ARM休系架构与编程
  119. // 嵌入汇编:LINUX内核完全注释
  120. __asm__(
  121.     "mov r0, #0\n"
  122.     "mcr p15, 0, r0, c7, c7, 0\n" /* 使无效ICaches和DCaches */
  123.     
  124.     "mcr p15, 0, r0, c7, c10, 4\n" /* drain write buffer on v4 */
  125.     "mcr p15, 0, r0, c8, c7, 0\n" /* 使无效指令、数据TLB */
  126.     
  127.     "mov r4, %0\n" /* r4 = 页表基址 */
  128.     "mcr p15, 0, r4, c2, c0, 0\n" /* 设置页表基址寄存器 */
  129.     
  130.     "mvn r0, #0\n"
  131.     "mcr p15, 0, r0, c3, c0, 0\n" /* 域访问控制寄存器设为0xFFFFFFFF,
  132.                                          * 不进行权限检查
  133.                                          */
  134.     /*
  135.      * 对于控制寄存器,先读出其值,在这基础上修改感兴趣的位,
  136.      * 然后再写入
  137.      */
  138.     "mrc p15, 0, r0, c1, c0, 0\n" /* 读出控制寄存器的值 */
  139.     
  140.     /* 控制寄存器的低16位含义为:.RVI ..RS B... .CAM
  141.      * R : 表示换出Cache中的条目时使用的算法,
  142.      * 0 = Random replacement;1 = Round robin replacement
  143.      * V : 表示异常向量表所在的位置,
  144.      * 0 = Low addresses = 0x00000000;1 = High addresses = 0xFFFF0000
  145.      * I : 0 = 关闭ICaches;1 = 开启ICaches
  146.      * R、S : 用来与页表中的描述符一起确定内存的访问权限
  147.      * B : 0 = CPU为小字节序;1 = CPU为大字节序
  148.      * C : 0 = 关闭DCaches;1 = 开启DCaches
  149.      * A : 0 = 数据访问时不进行地址对齐检查;1 = 数据访问时进行地址对齐检查
  150.      * M : 0 = 关闭MMU;1 = 开启MMU
  151.      */
  152.     
  153.     /*
  154.      * 先清除不需要的位,往下若需要则重新设置它们
  155.      */
  156.                                         /* .RVI ..RS B... .CAM */
  157.     "bic r0, r0, #0x3000\n" /* ..11 .... .... .... 清除V、I位 */
  158.     "bic r0, r0, #0x0300\n" /* .... ..11 .... .... 清除R、S位 */
  159.     "bic r0, r0, #0x0087\n" /* .... .... 1... .111 清除B/C/A/M */

  160.     /*
  161.      * 设置需要的位
  162.      */
  163.     "orr r0, r0, #0x0002\n" /* .... .... .... ..1. 开启对齐检查 */
  164.     "orr r0, r0, #0x0004\n" /* .... .... .... .1.. 开启DCaches */
  165.     "orr r0, r0, #0x1000\n" /* ...1 .... .... .... 开启ICaches */
  166.     "orr r0, r0, #0x0001\n" /* .... .... .... ...1 使能MMU */
  167.     
  168.     "mcr p15, 0, r0, c1, c0, 0\n" /* 将修改的值写入控制寄存器 */
  169.     : /* 无输出 */
  170.     : "r" (ttb) );
  171. }

点击(此处)折叠或打开

  1. /*
  2.  * leds.c: 循环点亮4个LED
  3.  * 属于第二部分程序,此时MMU已开启,使用虚拟地址
  4.  */

  5. #define GPBCON (*(volatile unsigned long *)0xA0000010) // 物理地址0x56000010
  6. #define GPBDAT (*(volatile unsigned long *)0xA0000014) // 物理地址0x56000014

  7. /*
  8.  * LED1,LED2,LED4对应GPB5、GPB6、GPB7、GPB8
  9.  */
  10. #define    GPB5_out    (1<<(5*2))
  11. #define    GPB6_out    (1<<(6*2))
  12. #define    GPB7_out    (1<<(7*2))
  13. #define    GPB8_out    (1<<(8*2))

  14. /*
  15.  * wait函数加上“static inline”是有原因的,
  16.  * 这样可以使得编译leds.c时,wait嵌入main中,编译结果中只有main一个函数。
  17.  * 于是在连接时,main函数的地址就是由连接文件指定的运行时装载地址。
  18.  * 而连接文件mmu.lds中,指定了leds.o的运行时装载地址为0xB4004000,
  19.  * 这样,head.S中的“ldr pc, =0xB4004000”就是跳去执行main函数。
  20.  */
  21. static inline void wait(volatile unsigned long dly)
  22. {
  23.     for(; dly > 0; dly--);
  24. }

  25. int main(void)
  26. {
  27.     unsigned long i = 0;
  28.     
  29.     // LED1,LED2,LED3,LED4对应的4根引脚设为输出
  30.     GPBCON = GPB5_out | GPB6_out | GPB7_out | GPB8_out;

  31.     while(1){
  32.         wait(30000);
  33.         GPBDAT = (~(i<<5));         // 根据i的值,点亮LED1,2,3,4
  34.         if(++i == 16)
  35.             i = 0;
  36.     }

  37.     return 0;
  38. }

4总结

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



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