1. 程式人生 > >ARM裸機程式之儲存管理器控制SDRAM

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 : 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
首先說明本程式主要實現的功能:本程式最終為了生成一個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控制蜂鳴器的響與不響.

首先看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。

請用批判的眼光看我的文章,組織上可能不怎麼到位,自己看看總能看的明白,歡迎交流討論!