新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > ARM學習筆記之——MiniOS

        ARM學習筆記之——MiniOS

        作者: 時間:2016-11-20 來源:網絡 收藏

        1. 概述

        最近,我花了大量的時間學習了楊鑄老師寫的《深入淺出嵌入式底層軟件開發》,看完了ARM體系結構與編程這一章。在這章節的最后,作者做了一個用于總結前面所學內容的操作系統MiniOS,并附帶了其中的源代碼。我認真學習了其中的所有代碼,悟到了其中非常巧妙的構思。

        本文引用地址:http://www.104case.com/article/201611/318808.htm

        讀這個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_CRITICAL();//開啟中斷
        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號進程



        關鍵詞: ARMMiniO

        評論


        技術專區

        關閉
        主站蜘蛛池模板: 灌云县| 大荔县| 介休市| 佳木斯市| 灵武市| 芮城县| 海兴县| 临夏县| 岗巴县| 信阳市| 绩溪县| 彩票| 京山县| 济源市| 大埔区| 高密市| 平利县| 兴山县| 邯郸市| 泰和县| 铜川市| 德惠市| 宝丰县| 蒙自县| 邯郸市| 清苑县| 乌兰察布市| 青川县| 云龙县| 陇西县| 同仁县| 永昌县| 德格县| 长垣县| 绥阳县| 六枝特区| 盈江县| 泰和县| 满城县| 周至县| 株洲县|