Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1184908
  • 博文数量: 573
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 66
  • 用 户 组: 普通用户
  • 注册时间: 2016-06-28 16:21
文章分类

全部博文(573)

文章存档

2018年(3)

2016年(48)

2015年(522)

分类: LINUX

2015-12-02 11:37:45

head.S文件

点击(此处)折叠或打开

  1. @功能:设置SDRAM,将第2部分代码复制到SDRAM中,设置页表,启动MMU,然后跳到SDRAM中继续执行。
  2. @

  3. .text
  4. .global _start
  5. _start:
  6.         LDR SP, =4096 @ 调用c函数,必须设置栈,因为ARM栈是满栈递减的,所以取了个SDRAM地址的最大值。
  7.         BL disable_watch_dog @ 关闭watchdog,否则cpu会不断重启。
  8.         BL memsetup @ 设置存储控制器,以便使用SDRAM。
  9.         BL copy_2th_to_sdram @ 将第2部分代码leds.c,复制到SDRAM。
  10.         BL create_page_table @ 设置页表
  11.         BL mmu_init @ 启动mmu
  12.         LDR SP, =0xB4000000 @ 重新设置栈,取SDRAM的最大值0x34000000,这里指对应的虚拟地址的值是0xB4000000。
  13.         LDR PC, =0xB0004000 @ 跳到SDRAM的0x30004000,对应的虚拟地址0xB0004000中,继续执行第2部分代码
  14. MYLOOP:
  15.         B MYLOOP
init.c文件

点击(此处)折叠或打开

  1. /*init.c文件
  2. *功能:进行一些初始化,在steppingstone中进行。
  3. *此时mmu还未开启,使用物理地址。
  4. */

  5. /*看门狗寄存器*/
  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;
  15. }

  16. /*
  17. *功能:设置13个存储器控制器相关的寄存器,以使用SDRAM
  18. */
  19. void memsetup(void)
  20. {
  21.     /*SDRAM的13个寄存器的值*/
  22.     unsigned long const mem_cfg_val[]=
  23.     {
  24.         0x22011110,
  25.         0x00000700,
  26.         0x00000700,
  27.         0x00000700,
  28.         0x00000700,
  29.         0x00000700,
  30.         0x00000700,
  31.         0x00018005,
  32.         0x00018005,
  33.         0x008c07a3,
  34.         0x000000b1,
  35.         0x00000030,
  36.         0x00000030,
  37.     };
  38.     int i = 0;
  39.     volatile unsigned long * p = (volatile unsigned long *)MEM_CTL_BASE;
  40.     /*sizeof(unsigned long) = 4, 所以p+1,是4个字节的跳变。*/
  41.     for(i=0; i<13; i++)
  42.     {
  43.         p[i] = mem_cfg_val[i];
  44.     }
  45. }

  46. /*
  47. *功能:将第2部分关于leds.c的代码复制到SDRAM中。
  48. *注意:并没有把片内SRAM(steppingstone)的4k,全部拷贝到SDRAM中,只拷贝了steppingstone的2k~4k的一半。
  49. * 所以,leds.c的代码,在nand flash启动之后,这部分代码就存储在steppingstone中地址2048之后。
  50. * 另外也不是拷到SDRAM的起始地址0x30000000处,是跳过16k,拷到0x30004000处。
  51. * 因为,SDRAM的前16K用来装也表的段描述符了。
  52. */
  53. void copy_2th_to_sdram(void)
  54. {
  55.     unsigned int * src = (unsigned int *)2048;
  56.     unsigned int * des = (unsigned int *)0x30004000;

  57.     while(src < (unsigned int *)4096) /*steppingstone的2048开始的2k~4k都复制了,所以结束地址为4096。*/
  58.     {
  59.         *des = *src;
  60.         des++;
  61.         src++;
  62.     }    
  63. }


  64. /*
  65. *功能:创建页表,设置虚拟内存和物理内存之间的映射
  66. */
  67. void create_page_table(void)
  68. {
  69.     /*段描述符的一些宏定义*/
  70.     /*[11:10]=11,AP权限位:如果内存所在的域(比如下面选择了域0)在c3中对应的2位值是[01]:则AP的值才会被使用,其他值,不使用AP值。*/
  71.     #define MMU_FULL_ACCESS (3 << 10)
  72.     /*[8:5]=0000,表示这1M内存属于域0:域0在c3中是[1:0],其值在后面被设置成11:管理模式,不就行权限检查,容许任何访问。上面AP值是什么,无所谓了。*/
  73.     #define MMU_DOMAIN (0 << 5)
  74.     #define MMU_SPECIAL (1 << 4) /*[4]=1,规定为1*/
  75.     #define MMU_CACHEABLE (1 << 3) /*[3]=1,即Ctt=1:使能cache,Ctt=0:禁止cache*/
  76.     #define MMU_BUFFERABLE (1 << 2) /*[2]=1,即Btt=1:使能write buufer,Btt=0:禁止write buffer*/
  77.     #define MMU_SECTION (2) /*[1:0]=10,一级描述符的最低2位,00:无效,01:粗页表,10:段,11:细页表*/

  78.   /*不缓存*/
  79.     #define MMU_SECDESC (MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | MMU_SECTION)
  80.     /*带缓存*/
  81.     #define MMU_SECDESC_WB (MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | MMU_SECTION | MMU_CACHEABLE | MMU_BUFFERABLE)

  82.     #define MMU_SECTION_SIZE 0x00100000 /*段的大小,1M*/

  83.     unsigned long virtuladdr; /*虚拟地址*/
  84.     unsigned long physicaladdr; /*物理地址*/
  85.     
  86.     /*页表的基址:从SDRAM起始地址开始*/
  87.     unsigned long * mmu_table_base = (unsigned long *)0x30000000;

  88.     /*steppingstone的起始地址为0,第一部分的起始运行地址也是0.
  89.     *将0~1M的虚拟地址 映射 到steppingstone的同样的物理地址。
  90.     *在开启MMU之后,仍然能够运行第一部分的程序。
  91.     *映射成功之后:SDRAM物理地址:0x30000000,这个地址中的值是一个段描述符:0x00000C3E:
  92.     */
  93.     virtuladdr = 0x00000000;
  94.     physicaladdr = 0x00000000;
  95.     *(mmu_table_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | MMU_SECDESC_WB;

  96.     /*0x56000000是GPIO寄存器的起始物理地址:GPBCON,GPBDAT的物理地址0x56000010,0x56000014。
  97.     *将0xA0000000开始的1MB虚拟地址空间 映射 到0x56000000开始的1MB的物理地址空间,尽管有很实际物理地址不存在,但是这并不妨碍虚拟地址映射它。
  98.     *在开启MMU之后,第2部分leds.c程序,能够以0xA0000010, 0xA0000014来操作GPBCON,GPBDAT。
  99.     *映射成功之后:SDRAM物理地址:0x30002800,这个地址中的值是一个段描述符:0x56000C32:
  100.     */
  101.     virtuladdr = 0xA0000000;
  102.     physicaladdr = 0x56000000;
  103.     *(mmu_table_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | MMU_SECDESC;
  104.     
  105.     /*SDRAM的物理地址地址范围是:0Fx30000000~0x33FFFFF,共64M。
  106.     *将虚拟地址空间0xB0000000~0xB3FFFFFF,的64MB 映射 到0Fx30000000~0x33FFFFF,共64的物理地址空间,尽管有很实际物理地址中没有程序代码,但是这并不妨碍虚拟地址映射它。
  107.     *在开启MMU之后,第2部分leds.c程序,能够以0xB0000000~0xB3FFFFFF来操作。
  108.     *映射成功之后:SDRAM物理地址:0x30002C00,地址中的段描述符:0x300|00C3E
  109.     * SDRAM物理地址:0x30002C04,地址中的段描述符:0x301|00C3E
  110.     * SDRAM物理地址:0x30002C08,地址中的段描述符:0x302|00C3E
  111.     * SDRAM物理地址:0x30002C0C,地址中的段描述符:0x303|00C3E
  112.     * ......
  113.     * ......
  114.     * SDRAM物理地址:0x30002CF8,地址中的段描述符:0x33E|00C3E
  115.     * SDRAM物理地址:0x30002CFC,地址中的段描述符:0x33F|00C3E ;;;到这里,本程序64M空间已经映射完毕,共64个段描述符
  116.     * ......
  117.     * ......                
  118.     * SDRAM物理地址:0x30003FFC,地址中的段描述符:是第4096个段描述符;;;到这里SDRAM的前16K物理地址已经填满
  119.     *------------------------------------------------------------------------
  120.     * SDRAM物理地址:0x30004000, 不是存放的段描述符;;;剩下的内存起始的物理地址:从这里开始装的是leds.c程序。
  121.     *段描述符的介绍:
  122.     * [31:20]的高12位,是映射的段的物理地址
  123.     * [19:12]:未用
  124.     * [11:0]:段的访问权限
  125.     * [11: 10]:AP权限位:管理模式,不就行权限检查,容许任何访问
  126.     * [9]:未用
  127.     * [8:5]: 内存属于域0
  128.     * [4]=1: 规定为1
  129.     * [3]: 即Ctt=1:使能cache,Ctt=0:禁止cache
  130.     * [2]: 即Btt=1:使能write buufer,Btt=0:禁止write buffer
  131.     * [1:0]=11,一级描述符的最低2位,00:无效,01:粗页表,10:段,11:细页表
  132.     *注意:描述的最低2位,就确定地址映射的方式,也确定了页表中每个条目映射的地址大小。
  133.     * 段:规定是以1M的方式进行转换
  134.     * 大页:规定是以64KB的方式进行转换
  135.     * 小页:规定是以4KB的方式进行转换
  136.     * 极小页:规定是以1KB的方式进行转换
  137.     */
  138.     virtuladdr = 0xB0000000;
  139.     physicaladdr = 0x30000000;
  140.     while(virtuladdr < 0xB4000000)
  141.     {
  142.         *(mmu_table_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | MMU_SECDESC_WB;
  143.         /*注意:mmu_table_base地址每加1,是4个地址的跳变。也说明SDRAM物理4个字节的空间存放一个段描述符(32位)
  144.         下面+1M,是加4M,不要搞错了*/
  145.         virtuladdr += 0x100000;
  146.         physicaladdr += 0x100000;
  147.     }
  148. }


  149. /*
  150. *功能:启动MMU
  151. */
  152. void mmu_init(void)
  153. {
  154.     unsigned long ttb = 0x30000000;
  155. __asm__( /*GCC内联汇编函数:使用intel汇编语法格式,不是AT&T汇编语法格式*/
  156.     "mov r0, #0\n" /*每一句汇编必须用双引号挎起来,并以\n结尾。以字符串的形式传给编译器的*/
  157.     
  158.     /*下面3句汇编的后面2个参数比较特殊,需要参考ARM体系结构参考手册来设置
  159.     *没有附加信息时候:最后2个参数就是c0,0,或者只有c0.
  160.     *MCR,MRC这2个指令的具体使用方法在文档里面。
  161.     *CP15协处理器的16个32位寄存器c0~c16的具体介绍页在文档里面。
  162.     */
  163.     "mcr p15, 0, r0, c7, c7, 0\n" /*是无效整个ICache,DCache*/
  164.     "mcr p15, 0, r0, c7, c10, 4\n"     /*清空整个write buffer*/
  165.     "mcr p15, 0, r0, c8, c7, 0\n" /*是无效整个指令TLB,数据TLB*/
  166.     
  167.     "mov r4, %0\n" /*后面列表中第1个变量的值,传给r4寄存器*/
  168.     "mcr p15, 0, r4, c2, c0, 0\n" /*设置页表基址寄存器c2:把页表基地址告诉cpu*/

  169.     "mvn r0, #0\n"
  170.     "mcr p15, 0, r0, c3, c0, 0\n" /*域访问控制寄存器c3=0xFFFFFFFF:无论段描述符条目中的AP是什么,都不进行任何权限检查*/

  171.     /*下面一句是读出控制寄存器c1的值
  172.     *c1控制寄存器的低16位的含义如下:
  173.     * |31 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
  174.     * | SBZP/UNP | L4 | RR | V | I | Z | F | R | S | B | L | D | P | W | C | A | M |
  175.     * [0]:M : 0=关闭MMU,1=开启MMU
  176.     * [1]:A : 0=数据访问时不进行地址对齐检查,1=数据访问时进行地址对齐检查
  177.     * [2]:C : 0=禁止DCache, 1=使能DCache
  178.     * [7]:B : 0=CPU小字节序, 1=CPU大字节序
  179.     * [8]:S : 用来与页表中的描述符一起确定内存的访问权限
  180.     * [9]:R : 用来与页表中的描述符一起确定内存的访问权限
  181.     * [12]:I : 0=禁止ICache, 1=使能ICache
  182.     * [13]:V : 表示异常向量所在的位置
  183.     * 0=Low addresses = 0x00000000, 1=High addresses = 0xFFFF0000
  184.     * [14]:RR : 表示换出cache中的条目时使用的算法
  185.     * 0=Random replacement, 1=Round robin replacement
  186.     *
  187.     *
  188.     *
  189.     */
  190.     "mrc p15, 0, r0, c1, c0, 0\n"
  191.     "bic r0, r0, #0x3000\n" /*..11 .... .... .... 清除 V, I位*/
  192.     "bic r0, r0, #0x0300\n" /*.... ..11 .... .... 清除 R, S位*/
  193.     "bic r0, r0, #0x0087\n" /*.... ..00 1... .111 清除 B, C, A, M位*/
  194.     "orr r0, r0, #0x0002\n" /*.... .... .... ..1. 开启对齐检查*/
  195.     "orr r0, r0, #0x0004\n" /*.... .... .... .1.. 开启DCache*/
  196.     "orr r0, r0, #0x1000\n" /*...1 .... .... .... 开启ICache*/
  197.     "orr r0, r0, #0x0001\n" /*.... .... .... ...1 使能MMU*/
  198.     /*将修改后的值写入控制寄存器*/
  199.     "mcr p15, 0, r0, c1, c0, 0\n"

  200.     : /*没有输出列表*/
  201.     : "r" (ttb) /*ttb是c语言变量,会保存在Rn寄存器中,所以ttb属于输入。r:只读,并且ttb就是列表中的第1个变量,上面的%0将会找到ttb*/
  202.     );
  203. }


  204. /*
  205. MMU学习总结 :
  206. 1,没开 MMU 时,cpu核,mmu,所有的外设部件都使用物理地址。
  207.   启动MMU之后,cpu核对外发出虚拟地址va,va被转化成MVA,当VA>32M时,VA=MVA
  208. 2,如果使用段描述符(1次映射1M), 要映射完成所有的4G空间,需要4K个段描述符,一个段描述符占4个字节,
  209.   共需要4k*4个字节的内存来存放页表。
  210.   不一定每次都要全部映射,可以只有部分内存保存有段描述符。
  211.   本实验页表的基址,选择为了0x3000 00000
  212. 3, 如何将页表的基址告诉CPU?
  213.   这个页表的基址,保存在协处理器的寄存器C2中,这个寄存器称为 页表基址寄存器。
  214. 4, VA转化为MVA的过程:
  215.      比如:虚拟地址VA=0xA000 0010 对应的 物理地址 PA=0x5600 0010
  216.      4.1),cpu会读取页表基址寄存器的值 C2 = 0x3000 0000
  217.      4.2),截取 C2的31~14 共18位 + 虚拟地址VA的31~20位 共12位 + 最后2位默认是00 : 这32位组成一个新的地址,
  218.      其实,这个新的地址是物理地址 0x3000 2800,是页表基址之后的某个页表地址,里面存放的是段描述符。
  219.      4.3),这个地址中的内容是段描述符 值=0x5600 0C32(最后的12位,表示段描述符的格式)
  220.      4.4),截取 段描述符的 31~20 共12位 + 虚拟地址VA的19~0位 共20位 : 这32位组成一个新的地址。
  221.      其实,这个新的地址就是最后转化成的 物理地址PA=0x5600 0010。
  222. */
leds.c文件

点击(此处)折叠或打开

  1. #define GPBCON (*(volatile unsigned long *)0xA0000010)
  2. /*可以使用这个虚拟地址来访问,物理地址:0x56000010*/
  3. #define GPBDAT (*(volatile unsigned long *)0xA0000014)
  4. /*可以使用这个虚拟地址来访问,物理地址:0x56000014*/
  5. #define GPBUP (*(volatile unsigned long *)0xA0000018)
  6. /*可以使用这个虚拟地址来访问,物理地址:0x56000018*/

  7. #define GPB5_OUT (1<<(5*2))
  8. #define GPB6_OUT (1<<(6*2))
  9. #define GPB7_OUT (1<<(7*2))
  10. #define GPB8_OUT (1<<(8*2))

  11. #define GPB5_ON (~(1<<5))
  12. #define GPB6_ON (~(1<<6))
  13. #define GPB7_ON (~(1<<7))
  14. #define GPB8_ON (~(1<<8))

  15. #define GPB5_OFF (1<<5)
  16. #define GPB6_OFF (1<<6)
  17. #define GPB7_OFF (1<<7)
  18. #define GPB8_OFF (1<<8)


  19. /*
  20. *功能:延时函数
  21. *注意:static:静态函数,表明这个函数只在本文件有效,外部程序不能调用这个函数。
  22. * inline:内联函数:指示编译器插入一个函数的代码到调用者的代码中,也就是插入到实际上调用产生的地方。
  23. * 这样可以使得编译本文件时,wait嵌入到main中,编译结果中只有main一个函数,
  24. * 于是在连接时,main函数的地址就是连接文件制定的地址。
  25. * 而连接脚本文件mmu.lds中,指定了leds.o的运行时装载地址为0xB0004000.
  26. * 所以,在head.S中的“LDR PC, =0xB0004000”就是跳去执行main函数。
  27. */
  28. static inline void wait(volatile unsigned long num)
  29. {
  30.     int i = 0;
  31.     int j = 0;
  32.     for(i=0; i<100; i++)
  33.         for(j=0; j<num; j++);
  34. }


  35. /*
  36. *功能:使能ICache
  37. */
  38. static inline icache_enable()
  39. {
  40.     unsigned int temp = 1<<12;

  41.     asm(
  42.             "mrc p15,0,r0,c1,c0,0\n" /*读出控制寄存器c1的值到r0中*/
  43.             "orr r0,r0,%0\n" /*第12位I位置1,即使能ICache*/
  44.             "mcr p15,0,r0,c1,c0,0\n" /*将修改后的值写入控制寄存器*/
  45.             :
  46.             :"r"(temp)
  47.             :"r0"
  48.         );
  49. }


  50. /*
  51. *功能:禁止ICache
  52. */
  53. static inline icache_disable()
  54. {
  55.     unsigned int temp = 1<<12;

  56.     asm( /*GCC内联汇编函数*/
  57.             "mrc p15,0,r0,c1,c0,0\n"
  58.             "bic r0,r0,%0\n" /*第12位I位置0,即禁止ICache*/
  59.             "mcr p15,0,r0,c1,c0,0\n"
  60.             :
  61.             :"r"(temp)
  62.             :"r0"
  63.         );
  64. }


  65. int main(void)
  66. {
  67.     GPBCON = (GPB5_OUT | GPB6_OUT | GPB7_OUT | GPB8_OUT);
  68.     GPBUP = 0x1e0;
  69.     
  70.     icache_enable(); /*使能ICache时:灯闪烁很快*/
  71.     icache_disable(); /*禁止TCache时:灯闪烁很慢*/
  72.     while(1)
  73.     {
  74.         GPBDAT = (GPB5_ON & GPB6_ON & GPB7_ON & GPB8_ON);
  75.         wait(1000);
  76.         GPBDAT = (GPB5_OFF | GPB6_OFF | GPB7_OFF | GPB8_OFF);
  77.         wait(1000);
  78.     }

  79.     return 0;
  80. }
makefile文件

点击(此处)折叠或打开

  1. objs := head.o init.o leds.o
  2.     
  3. mmu.bin:$(objs)
  4.     arm-linux-ld -Tmmu.lds -o mmu_elf $^
  5.     arm-linux-objcopy -O binary -S mmu_elf $@
  6.     arm-linux-objdump -D -m arm mmu_elf > mmu.dis
  7.     
  8. %.o:%.c
  9.     arm-linux-gcc -Wall -O2 -c -o $@ $<
  10. %.o:%.S
  11.     arm-linux-gcc -Wall -O2 -c -o $@ $<
  12.     
  13. clean:
  14.     rm -f mmu.bin mmu_elf mmu.dis *.o
mmu.lds文件

点击(此处)折叠或打开

  1. SECTIONS{
  2.     first 0x00000000 : { head.o init.o }
  3.     second 0xB0004000 : AT(2048) { leds.o }
  4. }


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