新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > Arm 2440——Nand flash啟動模式詳解(LED程序為例)

        Arm 2440——Nand flash啟動模式詳解(LED程序為例)

        作者: 時間:2016-11-28 來源:網絡 收藏
        斷斷續續的研究arm也有2個月了,現在才感覺理解了arm在Nand flash模式下的啟動過程,現在來這里記錄下來以表達我無比喜悅的心情。閑話少說,趁著還沒有忘記學習過程中的感受,直接進入正題。

        大家都知道,arm在Nand flash啟動模式下啟動時系統會將Nand flash中的前4KB代碼拷貝到SRAM(也就是Steppingstone中),由SRAM配置中斷向量表和完成Nand flash訪問的必要初始化,然后將Nand flash中的全部程序代碼拷貝到SDRAM中,最后由SRAM跳轉到SDRAM,然后程序就正常執行了,這一過程看上去很簡單,但是真正理解這一過程還是不簡單的,盡管這樣,還是想告訴大家仔細理解還是比較容易理解這個過程的。如果您是ADS用戶,你省去了很多麻煩,但我不確定你省去的這些麻煩是否值得,這里介紹的是一種麻煩的方式,linux下的led程序。

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

        代碼Head.s

        1. .externmain
        2. .text
        3. .global_start
        4. _start:
        5. breset
        6. reset:
        7. ldrsp,=4096
        8. bldisable_watch_dog
        9. blclock_init
        10. blmemsetup
        11. blcopy_steppingstone_to_sdram
        12. ldrpc,=on_sdram
        13. on_sdram:
        14. msrcpsr_c,#0xdf
        15. ldrsp,=0x34000000
        16. ldrlr,=halt_loop
        17. ldrpc,=Main
        18. halt_loop:
        19. bhalt_loop

        我認為,最需要理解的就是這段代碼了。先簡單的解釋下這段代碼。

        (1)由于arm執行reset之后pc會被清零,也就是reset中斷的中斷入口地址,因此,第一條指令就是b reset,跳轉到reset中斷處理函數。

        (2)由于這里硬件配置都是C語言來完成的,而且我們的初始代碼比較小,完全不會超出4KB,因此可以在SRAM使用堆棧,故將SP設置為4096,以提供C運行環境

        (3)接下來的3個bl分別完成了關閉看門夠定時器,配置時鐘信號和存儲器配置的工作,第四個bl是將SRAM的4KB空間內的代碼拷貝到了SDRAM中。

        (4)接下來的ldr句將pc賦值為on_sdram的地址,實現了從SRAM到SDRAM的跳轉(下面會講為什么)

        (5)on_sdram中切換到了了系統模式然后分配了系統模式堆棧,將鏈接寄存器設置為halt_loop然后就跳轉到了SDRAM中的Main

        上面的解釋只是大體上說明了代碼的意思,但是初學者總會有個疑問就是為什么ldr pc,=on_sdram就實現了從SRAM到SDRAM的跳轉呢?我被這個問題困擾了很長時間,到今天才想明白了這個問題,問題的關鍵就是相對跳轉和絕對跳轉的問題。為了說明這個問題我先解釋一下bl指令跟ldr指令在執行過程中的區別。

        B指令是相對跳轉指令,B 指令是最簡單的跳轉指令。一旦遇到一個 B 指令,ARM 處理器將立即跳轉到給定的目標地址,從那里繼續執行。注意存儲在跳轉指令中的實際值是相對當前PC 值的一個偏移量,而不是一個絕對地址,它的值由匯編器來計算(參考尋址方式中的相對尋址)。它是 24 位有符號數,左移兩位后有符號擴展為 32 位,表示的有效偏移為 26 位(前后32MB 的地址空間),同樣的,BL、BX都是相對跳轉。

        LDR偽指令是將第二操作直接賦值給第一操作數,當執行ldr pc,=Main時是將Main的絕對地址賦值給了PC。

        好了,知道這兩個指令的區別之后我們來看代碼是如何實現的從SRAM到SDRAM的跳轉,首先需要指出,2440的開發板有SRAM和SDRAM,SRAM是從地址0x00000000開始的4KB內存空間,SDRAM是從0x30000000開始的64M空間。

        無論用ADS編譯還是用arm-linux-gcc編譯都會將代碼鏈接到0x30000000以后(也就是SDRAM中),ADS用戶可以通過查看ADS的工程配置,其中有項配置是RO起始地址是0x30000000,linux用戶在鏈接時需要用-T指定代碼的其實地址為0x30000000。

        根據編譯原理,在鏈接階段程序中函數的地址就已經確定了,也就是說函數的實際地址都在0x30000000之后,而程序的入口函數也就是這里的_start的地址就是0x300000000,其他函數都會大于這個數。

        但是由于arm上電后系統會將Nand flash的前4KB代碼拷貝到SRAM中,也就是_start函數開始的4KB指令將被拷貝到SRAM中執行,根據上例,在0x00000000處執行的指令就是“b reset”,由于b是相對跳轉,是在當前pc值的基礎上加減某個數而跳轉到將要執行的代碼處,因此,pc加減該數之后將到達reset函數的位置,故reset函數不能寫到4KB之外的空間中,否則arm的啟動將會失敗,同樣的,接下來的幾個bl都是執行的相對跳轉,所以都相對當前pc進行的跳轉,由于Nand flash總共只有64M的空間,所以相對跳轉是不可能會跳轉到SDRAM的,因為跳轉到SDRAM至少要發生0x30000000的跳轉,而這個相對位移遠遠大于64M。

        而ldr pc,=Main是將Main函數的實際地址賦值給pc,而Main的實際地址是在0x30000000之后,這樣,就從SRAM跳轉到了SDRAM。

        由于這個過程設計到了硬件格局和編譯原理,所以對一般人來講,理解起來確實比較困難,而且受本人水平限制,很多地方只能說是只可意會不可言傳,如果誤導了大家請大家諒解。當然如果看到這里還不能理解arm的啟動過程可以留言討論這個問題。下面是相關的其他代碼,我附在這里,2440addr.h沒有貼出,由于我也是使用arm自帶示例程序中的代碼,而且代碼有四千多行,多數地址是沒有用到的。其他的代碼如下

        代碼Init.s
        1. #include"2440addr.h"
        2. voiddisable_watch_dog(void);
        3. voidclock_init(void);
        4. voidmemsetup(void);
        5. voidcopy_steppingstone_to_sdram(void);
        6. voidinituart(void);
        7. voiddisable_watch_dog(void)
        8. {
        9. rWTCON=0;
        10. }
        11. voidclock_init(void)
        12. {
        13. rCLKDIVN=0x03;
        14. /*
        15. *如果HDIVN非0,CPU的總線模式應該從
        16. *“fastbusmode”變為“asynchronous
        17. *busmode”
        18. */
        19. __asm__(
        20. "mrcp15,0,r1,c1,c0,0"
        21. "orrr1,r1,#0xc0000000"
        22. "mcrp15,0,r1,c1,c0,0"
        23. );
        24. rMPLLCON=(92<<12)|(1<<4)|(2);
        25. //rMPLLCON=((0x5c<<12)|(0x01<<4)|(0x02));
        26. }
        27. voidmemsetup(void)
        28. {
        29. volatileunsignedlong*p=(volatileunsignedlong*)0x48000000;
        30. /*這個函數之所以這樣賦值,而不是像前面的實驗(比如mmu實驗)那樣將配置值
        31. *寫在數組中,是因為要生成”位置無關的代碼”,使得這個函數可以在被復制到
        32. *SDRAM之前就可以在steppingstone中運行
        33. */
        34. /*存儲控制器13個寄存器的值*/
        35. p[0]=0x22011110;//BWSCON
        36. p[1]=0x00000700;//BANKCON0
        37. p[2]=0x00000700;//BANKCON1
        38. p[3]=0x00000700;//BANKCON2
        39. p[4]=0x00000700;//BANKCON3
        40. p[5]=0x00000700;//BANKCON4
        41. p[6]=0x00000700;//BANKCON5
        42. p[7]=0x00018005;//BANKCON6
        43. p[8]=0x00018005;//BANKCON7
        44. /*REFRESH,
        45. *HCLK=12MHz:0x008C07A3,
        46. *HCLK=100MHz:0x008C04F4
        47. */
        48. p[9]=0x008C04F4;
        49. p[10]=0x000000B1;//BANKSIZE
        50. p[11]=0x00000030;//MRSRB6
        51. p[12]=0x00000030;//MRSRB7
        52. }
        53. voidcopy_steppingstone_to_sdram(void)
        54. {
        55. unsignedint*pdwSrc=(unsignedint*)0;
        56. unsignedint*pdwDest=(unsignedint*)0x30000000;
        57. while(pdwSrc<(unsignedint*)4096)
        58. {
        59. *pdwDest=*pdwSrc;
        60. pdwDest++;
        61. pdwSrc++;
        62. }
        63. }
        注意:由于我們的代碼比較小,遠小于4KB,因此arm啟動時自動拷貝到SRAM中的4KB代碼包含我們的全部代碼,因此copy操作我是將stepingstone中的4KB代碼拷貝到了SDRAM中,在實際應用中代碼多數是超過4KB,因此copy函數應該是將Nand flash中的全部代碼拷貝到SDRAM,這樣才能成功運行ARM。

        代碼Main.c:

        1. #include"2440addr.h"
        2. voidDelay(inti)
        3. {
        4. intm,n,p;
        5. for(m=0;m!=i;++m)
        6. {
        7. for(n=0;n!=255;++n)
        8. {
        9. for(p=0;p!=255;++p)
        10. ;
        11. }
        12. }
        13. }
        14. voidMain()
        15. {
        16. intcount;
        17. intleds[4]={0x1c0,0x1a0,0x160,0xe0};
        18. rGPBCON=0x00155555;
        19. rGPBUP=rGPBUP&0xFF00;
        20. while(1)
        21. {
        22. for(count=0;count!=4;++count)
        23. {
        24. rGPBDAT=leds[count];
        25. if(count%2)
        26. rGPBDAT+=1;
        27. Delay(2);
        28. }
        29. }
        30. }

        鏈接文件led.lds如下:
        1. SECTIONS
        2. {
        3. .=0x30000000;
        4. .text:{*(.text)}
        5. .rodataALIGN(4):{*(.rodata)}
        6. .dataALIGN(4):{*(.data)}
        7. .bssALIGN(4):{*(.bss)*(COMMON)}
        8. }

        makefile如下:
        1. objects:=Head.oInit.oMain.o
        2. led.bin:$(objects)
        3. arm-linux-ld-Tled.lds-nostdlib-oled_elf$^
        4. arm-linux-objcopy-Obinary-Sled_elf$@
        5. arm-linux-objdump-D-marmled_elf>led.dis
        6. %.o:%.c
        7. arm-linux-gcc-Wall-O2-c-o$@$<
        8. %.o:%.s
        9. arm-linux-gcc-Wall-O2-c-o$@$<;
        10. .PYTHON:clean
        11. clean:
        12. rm-fled.binled_elfled.dis*.o

        如上除了2440addr.h之外就都全了,另外需要指出的是2440addr.h中引用了Option.h,為了簡化代碼,可以將這句可以注釋掉,在我們這段代碼中完全用不到該文件相關功能。否則需要自行修改makefile文件完成Option.h相關的編譯和鏈接工作。

        好了,又浪費了大家這么長的時間,這里就不多說了,有什么問題求高手指出來。



        評論


        技術專區

        關閉
        主站蜘蛛池模板: 司法| 斗六市| 永宁县| 会昌县| 沂水县| 洞头县| 手机| 建德市| 宣汉县| 盘山县| 宜宾市| 灵石县| 略阳县| 股票| 昆山市| 平邑县| 广饶县| 荆州市| 郧西县| 云浮市| 汉川市| 金堂县| 凤凰县| 新安县| 杨浦区| 兰西县| 巴马| 五指山市| 襄城县| 承德市| 泰安市| 肥东县| 潼关县| 上饶县| 蚌埠市| 阳西县| 永昌县| 鲁甸县| 泊头市| 扎兰屯市| 海南省|