ARM裸機程式之儲存管理器控制SDRAM
本文講的是s3c2440A晶片的儲存管理器,配套的開發板是友善之臂mini2440,首先貼出程式碼
head.s的程式碼:
.equ MEM_CTL_BASE, 0x48000000 @定義13個暫存器的首地址 .equ SDRAM_BASE, 0x30000000 @定義SDRAM的首地址 .text .global _start _start: bl disable_watch_dog bl memsetup bl copy_steppingstone_to_sdram @把程式碼從片內的SRAM複製到SDRAM裡面 ldr pc, =on_sdram on_sdram: ldr sp, =0x34000000 @設定棧指標,用來呼叫C函式 bl main halt_loop: b halt_loop disable_watch_dog: ldr r0, =0x53000000 mov r1, #0x0 str r1, [r0] mov pc, r14 copy_steppingstone_to_sdram: mov r0, #0x0 mov r1, #SDRAM_BASE mov r2, #4096 Loop: @用一個迴圈,從片內SRAM的0地址開始複製到SDRAM的0x30000000處 ldr r3, [r0], #4 str r3, [r1], #4 cmp r0, r2 bcc Loop mov pc, r14 memsetup: @設定儲存管理器相關的13個暫存器 mov r0, #MEM_CTL_BASE ldr r1, =0x22011110 @BWSCON str r1, [r0], #4 @把r1暫存器裡面的值放到r0所指的記憶體單元中,然後r0=r0+4 ldr r1, =0x00000700 str r1, [r0], #4 @BANKCON0 str r1, [r0], #4 @BANKCON1 str r1, [r0], #4 @BANKCON2 str r1, [r0], #4 @BANKCON3 str r1, [r0], #4 @BANKCON4 str r1, [r0], #4 @BANKCON5 ldr r1, =0x00018005 str r1, [r0], #4 @BANKCON6 str r1, [r0], #4 @BANKCON7 ldr r1, =0x008c07a3 str r1, [r0], #4 @REFRESH ldr r1, =0x000000b1 str r1, [r0], #4 @BANKSIZE ldr r1, =0x00000030 str r1, [r0], #4 @MRSRB6 str r1, [r0] @MRSRB7 mov pc, r14
led_key.c的程式碼:
#define GPBCON (*(volatile unsigned long *)0x56000010) #define GPBDAT (*(volatile unsigned long *)0x56000014) #define GPBUP (*(volatile unsigned long *)0x56000018) #define GPGCON (*(volatile unsigned long *)0x56000060) #define GPGDAT (*(volatile unsigned long *)0x56000064) #define GPB5_out (1<<(5*2)) #define GPB6_out (1<<(6*2)) #define GPB7_out (1<<(7*2)) #define GPB8_out (1<<(8*2)) #define GPB0_out (1<<(0*2)) #define GPG0_in ~(3<<(0*2)) #define GPG3_in ~(3<<(3*2)) #define GPG5_in ~(3<<(5*2)) #define GPG6_in ~(3<<(6*2)) #define GPG7_in ~(3<<(7*2)) #define GPG11_in ~(3<<(11*2)) int main() { unsigned long read_value; GPBCON |= (GPB5_out | GPB6_out | GPB7_out | GPB8_out | GPB0_out); GPBUP &= 0x1e1; GPBDAT |= 0x1e0; GPGCON &= (GPG0_in & GPG3_in & GPG5_in & GPG6_in & GPG7_in ); while(1) { read_value = GPGDAT; if(read_value & (1<<0)) { GPBDAT |= (1<<5); } else { GPBDAT &= (~(1<<5)); } if(read_value & (1<<3)) { GPBDAT |= (1<<6); } else { GPBDAT &= (~(1<<6)); } if(read_value & (1<<5)) { GPBDAT |= (1<<7); } else { GPBDAT &= (~(1<<7)); } if(read_value & (1<<6)) { GPBDAT |= (1<<8); } else { GPBDAT &= (~(1<<8)); } if(read_value & (1<<7)) { GPBDAT &= (~(1<<0)); } else { GPBDAT |= (1<<0); } } return 0; }
Makefile:
首先說明本程式主要實現的功能:本程式最終為了生成一個sdrampp1.bin的可執行檔案下載到開發板的NandFlash的0地址處,一旦下載進去,s3c2440A這塊晶片就會自動地把NandFlash的前4K程式碼複製到片內的steppingstone,也就是片內的SRAM裡,如果不加處理,一般的程式就從SRAM的0地址處開始運行了,例如上一篇部落格“arm裸機程式之LED燈”就是從SRAM的0地址處開始執行的,而本程式並非從SRAM的0地址開始執行,而是把片內的SRAM的4K程式碼複製到SDRAM的0地址處,最後從SDRAM的0地址處開始執行,所以Makefile中的程式碼段地址是0x30000000,並非上一篇部落格裡寫的0x00000000,從0x30000000處執行程式碼,在SDRAM的0x34000000處設定了一個棧指標,至於為什麼棧指標是這個,後面的內容會詳細解釋,最後呼叫C函式,這裡的C函式實現的功能是按下開發板上的k1鍵,led1亮,鬆開led1就滅,k2,k3,k4同樣道理控制其他3個led,k5控制蜂鳴器的響與不響.sdrampp1.bin : head.s led_key.c arm-linux-gcc -g -c -o head.o head.s arm-linux-gcc -g -c -o led_key.o led_key.c arm-linux-ld -Ttext 0x30000000 -g head.o led_key.o -o sdrampp1_elf arm-linux-objcopy -O binary -S sdrampp1_elf sdrampp1.bin arm-linux-objdump -D -m arm sdrampp1_elf > sdrampp1.dis clean: rm -rf sdrampp1.* *.o
首先看Makefile,注意到Makefile的第4行,要求把程式碼段放在0x3000000處,解釋如上。至於其他的內容和上一篇部落格的Makefile沒什麼不同,Makefile就分析到此。
下面看看head.s的程式碼,程式一開始一個bl跳轉指令,實現關閉看門狗的程式就不解釋了。接著是一個Memory Controller(儲存管理器)初始化的工作,這裡涉及到13個暫存器,分別是BWSCON暫存器,BANKCON0暫存器等等,如果一開始我解釋這些暫存器,你肯定不知道我要講會聽的雲裡霧裡,所以我先講點原理,把握整體再深究細節。我們先從開發板的硬體原理圖開始說起,因為是開發板的原理圖是最基本直觀的東西了,by the way,我用的開發板是mini2440,所以檢視的是mini2440原理圖,先上張圖先:
臥槽,你TMD給我上張這個圖片,先彆著急,我是想讓你看看著塊晶片有多少根地址匯流排和資料匯流排,正因為這張圖片長得是比較醜,所以我儘量把它講的生動,美化它,最終讓你愛上它。首先我們來明確兩個概念,什麼是地址匯流排,什麼是資料匯流排,顧名思義,地址匯流排是CPU傳送一個地址,地址匯流排決定了CPU所能訪問的最大記憶體空間,這個CPU是中的地址匯流排是ADDR0~ADDR26,總用27條地址匯流排吧,所以s3c2440A最大訪問的記憶體地址為2^27=128M,但是如果想讓這個CPU實現最大能訪問的記憶體空間達到1GB的空間怎麼辦呢,cpu還引出8根片選訊號nGCS0~nGCS7,所以總的定址空間就達到1GB了,雖然你在上面這張圖看不出來有多少個片選訊號,你可以在原理圖上搜索一下即可。其實在這裡,我就可以引出一張s3c2440 Memery Controller的地址空間分佈圖了,在上圖之前,我講一句話解釋資料匯流排到底怎麼回事:就是決定CPU每次傳送資料的大小,這裡有DARA0~DATA31,,,每次傳送32位的資料,每次傳送32位資料,其實就可以引出SDRAM的原理圖了,暫且不說,先看Memery Controller的地址空間分佈圖:
看到這麼一張大圖,先別慌,我比你更慌,不知道怎麼解釋,先驗證一下上面說的那段話,總共8個片選訊號,為了滿足1GB的地址空間的定址,其實在這裡每個片選對應一個bank,nGCS0對應的是Bank0,nGCS7對應Bank7,依次類推,只要選擇了一個片選訊號,例如我這裡假設懸著nGCS6的片選訊號,CPU的27根地址匯流排就能對0x30000000~0x38000000之間的地址進行定址了,不過這裡還要注意,並不是每個之間的每個地址都能進行儲存資料,定址什麼的,因為s3c2440的每個Bank其實都是外接裝置的,例如Bank0外接NorFlash,Bank4外接網絡卡DM9000,Bank6和Bank7外接SDRAM等,至於其他的我們暫時不用管,還可以涉及到晶片的2中啟動方式我這裡就不介紹了。這裡Bank6和Bank7都外接SDRAM是怎麼回事呢,這裡就和上面的資料匯流排聯絡在一起了,因為開發板外接了兩塊SDRAM,每個SDRAM有16根資料匯流排,為了和cup的資料匯流排的寬度保持一致,所以要接兩塊SDRAM,SDRAM的原理圖請看下圖:
要知道SDRAM的工作原理,首先看看下面幾個問題,下面是我其他部落格上看到的:
(1) 地址線為什麼從A2開始?
因為2440資料寬度為32位,按4位元組對齊,即地址只會是0x...0,0x...4,0x..C,0x...E,每次地址增加都是四個位元組,所以A0和A1沒什麼用。
(2) SDRM BANK 選擇輸入BA0/BA1為什麼連線的是A24,A25
因為系統記憶體容量為64M,32bit,由兩片64M 16bit的SDRM組成。表示64M的空間需要26根線,所以地址最高兩位為A25和A24。
(3) 64M需要26根線,為什麼實際只用到了A2~A14,A24,A25?
理論上應該將A2~A25直接連線到SDRAM來定址64M(之所以不是A0~A25,是因為每次訪問的是32bit),而實際上只把A2~A14這13根線連線到SDRAM的A0~A12,這是因為SDRAM訪問時地址是分兩次給的,即行地址和列地址,不需要一次輸入,行地址和列地址複用了A2~A14這13根線,這個SDRAM理論上可定址的最大範圍為2^13 * 2^13。
(4)為什麼板子上SDRAM的空間為0x30000000 ~ 0x34000000
根據2440 SPEC,SDRAM只能放在BANK6 或 BANK7 (nGCS6或nGCS7),起始地址分別為0x30000000和0x38000000,一個BANK的大小為128M,現在選擇BANK放SDRAM,而SDRAM的容量為64M(0x4000000),所以SDRAM的範圍就是0x30000000~0x34000000,為什麼是0x3....呢?因為你把nGCS6片選接到SDRAM晶片上了;當然後你也可以接nGCS7,不過地址就要變了,[A29,A28,A27]=3,即從0x38000000開始.
看了上述的問題與回答,應該能大致理解SDRAM的工作原理吧。
最後我分析一下,Memery Controller的13個暫存器:
1.BWSCON暫存器:中文名字叫做位寬和等待控制器。
STx位:啟動或禁止SDRAM的資料掩碼引腳,對於SDRAM,此位為0,對於SRAM,此位為1;
WSx:是否使用儲存器的WAIT訊號,通常設定為0.
DWx:用來設定相應的Bank資料匯流排的位寬。
所以這裡設定BWSCON暫存器的設定的值為0x22011110,bank7和bank6的位寬都為32位,其他的為16位,因外接的外設不同。還要注意一點的是Bank0,沒有WS0和DW0
2.BANKCON0和BANKCON1,BANKCON2,BANKCON3,BANKCON4,BANKCON5屬於同一類,用於設定外接裝置的訪問時序,具體要看晶片手冊上的時序圖,這裡預設設定為0x0700即可。
3.BANKCON6和BANKCON7暫存器:因為Bank6和Bank7可以接SDRAM,還可以接SRAM,SROM等等,所以在這個暫存器裡相應的位要指明接的是什麼,可以根據MT([16:15])來控制,還有就是想BANKCON1等暫存器一樣要設定時序了。這裡根據晶片手冊,設定為0x00018005即可。
4.REFRESH暫存器:中文名叫做重新整理控制暫存器,可以設定SDRAM的重新整理功能,要不要使能,重新整理模式,重新整理週期等等,這裡可以設定0x008c007a3,具體對照晶片手冊。
5.BANKSIZE暫存器:記住一點就是當多個SDRAM一起工作的時候,設定SDRAM在記憶體中的對映圖
6.MRSRB6和MRSRB7暫存器,就是設定Bank6和Bank7的一些特徵,知道一個設定值即可0x30,具體細節,我覺得沒有必要太深入了,暫時對SDRAM的研究不需要太深,畢竟最終目標是自己定製一個U-Boot。
請用批判的眼光看我的文章,組織上可能不怎麼到位,自己看看總能看的明白,歡迎交流討論!