您的位置 首页 厂商

ARM学习笔记之——MiniOS

1.概述最近,我花了大量的时间学习了杨铸老师写的《深入浅出嵌入式底层软件开发》,看完了ARM体系结构与编程这一章。在这章节的最…

1. 概述

最近,我花了很多的时刻学习了杨铸教师写的《浅显易懂嵌入式底层软件开发》,看完了ARM体系结构与编程这一章。在这章节的最终,作者做了一个用于总结前面所学内容的操作体系MiniOS,并附带了其间的源代码。我认真学习了其间的一切代码,悟到了其间十分奇妙的构思。

读这个MiniOS源代码我遇到了最大的几个问题如下:

(1)体系是怎样发动的?

(2)舱位了MMU后,虚拟地址是怎样映射上物理地址上的?

(3)体系是怎样舱位MMU的,为什么舱位了MMU内存地址重映射之后程序还能正常运转?

(4)main( ) 函数是怎样变成task0的?

(5)使命之间是怎样切换的?

(6)使命中怎样被创立,并运转起来的?

上述这几个问题都是很纤细,但又很难搞清楚的中心常识。笔者在此把自己悟到的东西共享出来,供咱们参阅。

其它,如:体系函数调用、使命调度机制、LED、UART、按键怎样完结,不做过多研讨。

2. 具体内容

2.1 体系是怎样发动的?

首要阐明,书上供给的MiniOS工程编译后的运转地址为0x33FF0000,不是 0x00000000,这点很重要。
-info totals -ro-base 0x33ff0000 -first start.o
而程序编译完结后,生成了bin文件将被烧录到NorFlash的0x00000000地址上,也就很重要!
ARM复位后,PC从NorFlash的0x00000000地址上取提,也便是”b Reset“,之后跳到Reset标号上持续碑文。代码如下:
  1. AREAStart,CODE,READONLY
  2. ENTRY;代码段开端
  3. bReset
  4. ……
  5. Reset;Reset反常处理符号
  6. blclock_init;跳往时钟初始化处理
  7. blmem_init;跳往内存初始化处理
  8. ldrsp,=SVC_STACK;设置办理形式栈指针,common_asm.h中界说
  9. bldisable_watch_dog;封闭看门狗
之后一切的跳转都是用到b或bl,进行相对跳转。再跳转也是以PC为开端,相对方位跳转,不会受运转地址的影响。
初始化了时钟、SDRAM、封闭看门狗、设置sp。有人可能会问:为什么在进行了bl之后再设置栈指针?其实,哪里设置都无所谓,咱们bl指令回来地址只保存在LR寄存器中,不放在栈里。SP被设置成了0x33FF0000,向下扩展,将来还会提及。
然后初始化SDRAM(假如不初始化,SDRAM是不能运用的),将程序自己从0x00000000地址仿制一份到0x33FF0000地址上。然后再来一个肯定地址跳转,转到0x33FF0000地址域上的xmain地址处持续碑文。如下:
  1. copy_code;代码劳动开端符号
  2. movr0,#0x0;R0中为数据开端地址(ROM数据保存在0地址开端处)
  3. ldrr1,=|Image
    RO

    Base|;R1中寄存RO输出域运转地址,

  4. ldrr2,=|Image
    ZI

    Limit|;R2中寄存ZI输出域完毕地址,

  5. subr2,r2,r1;R2=R2-R1,得出待劳动数据长度
  6. blCopyCode2Ram;将R0,R1,R2三个参数传递给CopyCode2Ram函数碑文劳动
  7. ldrr0,=|Image
    ZI

    Base|

  8. ldrr1,=|Image
    ZI

    Limit|

  9. blclear_bss_region
  10. blstack_init;跳往栈初始化代码处
  11. msrcpsr_c,#0x5f;舱位体系中止,进入体系形式
  12. ldrlr,=halt_loop;设置回来地址
  13. ldrpc,=xmain;跳往main函数,进入OS发动处理
  14. halt_loop
  15. bhalt_loop;死循环

在碑文了”ldr pc, =xmain“这条指令之后,PC就指向了SDRAM的0x33FF0000地址区域上了,不再是NorFlash上了,从此达到了运转地址与加载地址的共同。谨记!

xmain()函数定议在main.c文件中。
  1. intxmain(void)
  2. {
  3. pgtb_init();//树立页表
  4. mmu_init();//mmu初始化
  5. uart_init();//串口初始化
  6. irq_init();//中止初始化
  7. Timer0_init();//定时器0初始化
  8. key_init();//按键初始化
  9. led_init();//led灯初始化
  10. }

2.2 舱位了MMU后,虚拟地址是怎样映射上物理地址上的?

在xmain函数中,pgtb_init() 函数的功用便是构建页表,TTB=0x300F0000。

  1. voidpgtb_init()
  2. {
  3. unsignedlongentry_index,SFR_base;
  4. /*树立到Norflash的2MB的地址空间的映射*/
  5. /*0xA0000000映射到0开端的1MB地址空间*/
  6. *(mmu_tlb_base+(0xA0000000>>20))=0x0|SEC_DESC;
  7. /*0xA0100000映射到0x100000~0x1FFFFF的1MB地址空间*/
  8. *(mmu_tlb_base+(0xA0100000>>20))=0x100000|SEC_DESC;
  9. /*令0x30000000~0x34000000的64MB虚拟地址等于物理地址空间,便利miniOS内部进程办理*/
  10. for(entry_index=0x30000000;entry_index<0x34000000;entry_index+=0x100000){
  11. *(mmu_tlb_base+(entry_index>>20))=entry_index|SEC_DESC;
  12. }
  13. /*特别功用寄存器0x48000000~0x60000000地址空间映射到0xC8000000~0xE0000000虚拟地址空间*/
  14. for(entry_index=0x48000000+0x80000000,SFR_base=0x48000000;
  15. SFR_base<0x60000000;entry_index+=0x100000,SFR_base+=0x100000){
  16. *(mmu_tlb_base+(entry_index>>20))=SFR_base|SEC_DESC;
  17. }
  18. /*
  19. *进程1-23号进程地址空间,每个进程32MB,miniOS答应进程运用32MB虚拟地址空间,可是只分配其1MB的实践物理空间
  20. *进程1:物理地址空间0x30100000-0x301fffff,对应MVA(批改虚拟地址,进程PID<<25构成)
  21. *MVA地址空间:0x02000000-0x021fffff
  22. *进程2:物理地址空间0x30200000-0x302fffff
  23. *MVA地址空间:0x04000000-0x041fffff
  24. *………
  25. *进程23:物理地址空间0x31700000-0x317fffff
  26. *MVA地址空间:0x2E000000-0x2E1fffff
  27. *对应进程24咱们MVA地址空间是0x30000000是物理内存开端空间,该空间用来放置页表,并且前面现已用该
  28. *地址空间做了映射,因而它不能被映射成,24号进程的物理地址空间,越过该进程号24,相同道理,
  29. *越过进程号25
  30. *进程24:物理地址空间0x31800000-0x318fffff
  31. *MVA地址空间:0x30000000-0x31ffffff
  32. *进程25:物理地址空间0x31900000-0x319fffff
  33. *MVA地址空间:0x32000000-0x33ffffff
  34. */
  35. for(entry_index=1;entry_index<24;entry_index++){
  36. *(mmu_tlb_base+((entry_index*0x02000000)>>20))=(entry_index*0x00100000+SDRAM_BASE)|SEC_DESC;
  37. }
  38. /*
  39. *进程26:物理地址空间0x31A00000-0x31Afffff
  40. *MVA地址空间:0x34000000-0x35ffffff
  41. *………
  42. *进程62:物理地址空间0x33E00000-0x33Efffff
  43. *MVA地址空间:0xC4000000-0xC5ffffff
  44. */
  45. for(entry_index=26;entry_index
  46. *(mmu_tlb_base+((entry_index*0x02000000)>>20))=(entry_index*0x00100000+SDRAM_BASE)|SEC_DESC;
  47. }
  48. /*
  49. *反常向量表
  50. *0xFFFF0000为高地址反常向量表,可以一般设置CP15,C1寄存器V位,当反常发生时,由硬件主动去0xFFFF0000
  51. *地址处碑文反常跳转碑文,而不是之前的0地址处反常向量表跳转,咱们将该虚拟地址映射到0x33F00000这1MB地址
  52. *空间,相同,将悉数miniOS代码劳动到这1MB地址空间来。
  53. */
  54. *(mmu_tlb_base+(0xffff0000>>20))=((VECTORS_PHY_BASE)|SEC_DESC);
  55. }

完结之后,虚拟地址映射如下:
拜访0x33FF0000~0x33FFFFFF 与 0xFFF00000~0xFFFFFFFF地址是同一块物理内存空间。
0xA0000000~0xA01FFFFF地址指向0x00000000~0x001FFFFF,NorFlash物理空间。

2.3体系是怎样舱位MMU的,为什么舱位了MMU内存地址重映射之后程序还能正常运转?

在舱位MMU之前,数据拜访是直接拜访物理地址。可是舱位了MMU后,一切的地址拜访都需求经过一次虚拟地址转化。相同一个地址并不一定提向的同一个数据内间。
那在mmu_init()函数舱位MMU之后呈现什么样的反响呢?
  1. voidmmu_init()
  2. {
  3. unsignedlongttb=MMU_TABLE_BASE;
  4. /*reg1待铲除位*/
  5. intreg0,reg1=(VECTOR|ICACHE|R_S_BIT|ENDIAN|DCACHE|ALIGN|MMU_ON);
  6. /*CP15,C1设置位:反常向量表设置在高地址,运用ICACHE,体系选用小端形式,
  7. 运用DCACHE,运用地址对齐查看,舱位MMU*/
  8. intCP15_C1_set=(VECTOR|ICACHE|DCACHE|ALIGN|MMU_ON);
  9. __asm{
  10. movreg0,#0
  11. /*使ICaches和DCaches无效*/
  12. mcrp15,0,reg0,c7,c7,0
  13. /*使能写入缓冲器*/
  14. mcrp15,0,reg0,c7,c10,4
  15. /*使指令,数据TLB无效无效*/
  16. mcrp15,0,reg0,c8,c7,0
  17. /*页表基址写入C2*/
  18. mcrp15,0,ttb,c2,c0,0
  19. /*将0x2取反变成0xFFFFFFFD,Domain0=0b01为用户形式,其它域为0b11办理形式*/
  20. mvnreg0,#0x2
  21. /*写入域操控信息*/
  22. mcrp15,0,reg0,c3,c0,0
  23. /*取出C1寄存器中值给reg0*/
  24. mrcp15,0,reg0,c1,c0,0
  25. /*先铲除不需求的功用,现舱位*/
  26. bicreg0,reg0,reg1
  27. /*设置相关位并舱位MMU*/
  28. orrreg0,reg0,CP15_C1_set
  29. mcrp15,0,reg0,c1,c0,0
  30. }
  31. //DPRINTK(KERNEL_DEBUG,”MmuinitOK”);
  32. }
刚开端,我在看上面代码的时分,我在想。这个一舱位MMU之后,这个函数还能正常回来吗?本来MMU在启时前保存的回来地址(物理地址),在MMU舱位后这个地址(虚拟地址)对应的仍是本来的物理地址吗?除非一种状况: 虚拟地址与物理地址共同。
上述代码为初始化MMU的函数,当在碑文完”mcr p15, 0, reg0, c1, c0, 0“ 指令之后,MMU被舱位了。一切的地址拜访都要经过MMU转化成物理地址才干拜访。而mmu_init()此刻运转在SDRAM中0x33FF0000地址域上。由2.2节图中所示,0x30000000~0x33FFFFFF地址空间上的虚拟地址与物理地址是对应的。也便是说,虚拟地址==物理地址。
所以,程序可以正常碑文。

2.4main( ) 函数是怎样变成task0的?

OSCreateProcess()函数所创立使命的ID号从1开端计数。至于使命0,便是xmain()函数自己。
xmain()自己怎样跑到task0的方位上去坐着的呢?看main.c代码:
  1. intxmain(void)
  2. {
  3. //PC=0x33FF????,SP=0x33FF0000,MMU=关
  4. pgtb_init();//树立页表
  5. mmu_init();//mmu初始化
  6. //PC=0x33FF????,SP=0x33FF0000,MMU=开
  7. //对UART、IRQ、TIMER0、LED、KEY进行初始化
  8. OS_ENTER_CRITICAL();//封闭中止,预备进入进程初始化函数
  9. sched_init();//进程调度初始化
  10. OS_EXIT_CRIT%&&&&&%AL();//舱位中止
  11. ENTER_USR_MODE();//进入用户形式
  12. //进程0碑文内容
  13. while(1){
  14. DPRINTK(KERNEL_DEBUG,”kernel:process0″);
  15. printk(“process0,idle”);
  16. wait(1000000);
  17. }
  18. return0;
  19. }

碑文到 xmain 函数时,PC地址是在 SDRAM 的 0x33FF???? 上的,并且SP栈指针在 start.s 中已指定向了 0x33FF0000。

在碑文完 mmu_init 函数之后,一切的数据拜访均是经过虚拟地址拜访的。包含接下来的UART、IRQ、TIMER0、LED、KEY的初始化,通是拜访的虚拟地址。详见uart_init 函数中,读写的寄存器地址。
sched_init() 函数的功用是初始化一切的PCB。在最终,初始化PCB[0]。把 current=&task[0] 。
  1. /*初始化0号进程*/
  2. p=&task[0];//p指向0号进程PCB
  3. p->pid=0;//设置0号进程pid
  4. p->state=TASK_RUNNING;//设置其运转状况为安排妥当态
  5. p->count=5;//设置其时刻片为5
  6. p->priority=5;//设置优先级为5
  7. p->content[0]=0x5f;//保存状况寄存器cpsr值,一共为体系形式,舱位中止
  8. p->content[1]=SYS_MODE_STACK_BASE;//设置当时进程栈指针
  9. p->content[2]=0;
  10. p->content[16]=0;//设置PC寄存器的值为0,该进程开端地址被MMU映射为0地址
  11. current=&task[0];//当时运转进程为0号进程

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/changshang/264125.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部