在S3C2440A user manual.pdf 的第五章有 Memory Controller的介紹。設置memory controller是爲了明確各個bank(硬件決定出的地址空間)屬性,包括數據位寬、WAIT信號、訪問時序、刷新週期、bank起始位置和大小 (僅對bank六、7)及SDRAM模式。《嵌入式Linux應用開發徹底手冊》第6章對各寄存器有較詳細敘述,再配上 S3C2440A user manual.pdf 各寄存器個字段含義應屬明確。只惋惜這些還不夠,舉例來講要設置SDRAM各參數,沒有SDRAM數據手冊是無法作的,在這個百花齊放的世界裏真沒什麼是 通吃的。html
《嵌入式Linux應用開發徹底手冊》對應的SDRAM是K4S561632C,我手頭的mini2440開發板上的號稱是 HY57V561620FTP,但芯片上的絲印被刻意劃掉了,隱約看到一個M的logo和沒法辨認的一串數字,並且2片還不同!面對此情此景除了發自肺 腑的一句「你妹呀~」實在不知再說些什麼。linux
mini2440 板子入手在nor 上已有可正常運行的 supervivi,進入它的命令行模式,輸入:
mem info,它自曝了 memory controller 各寄存器設置,既然它工做正常就用這套值吧redis
Supervivi> mem info RAM Information: Default ram size: 64M Real ram size : 64M Free memory : 61M RAM mapped to : 0x30000000 - 0x34000000 Flash memory mapped to : 0x10000000 - 0x12000000 Available memory region : 0x30000000 - 0x33d80000 Stack base address : 0x33defffc Current stack pointer : 0x33defb6c Memory control register vlaues BWSCON = 0x22111112 BANKCON0 = 0x00000700 BANKCON1 = 0x00000700 BANKCON2 = 0x00000700 BANKCON3 = 0x00000700 BANKCON4 = 0x00000700 BANKCON5 = 0x00000700 BANKCON6 = 0x00018009 BANKCON7 = 0x00018009 REFRESH = 0x008e04eb BANKSIZE = 0x000000b2 MRSRB6 = 0x00000030 MRSRB7 = 0x00000030 DSC0 = 0x00000000 DSC1 = 0x00000000
準 備工做還有如何拷貝內存數據。真正有意義的是從flash 拷貝到sdram但鑑於我還沒到這步,先搞點無心義的從片內ram到sdram吧,這裏的重點是「位置無關指令」這個概念。若是這個解決了剩下就是位置相 關指令的定位,換言之怎麼連接,怎麼加載,也就是如何保證運行地址的正確性。shell
S3C2440A 是 ARM920T 處理器類型,屬於 ARMv4T 架構,其位置無關指令包括 b bl。
假 想我有一些大個函數必須在 sdram 中調用,它們開機時是存在 flash 中的,那麼必須先設置好內存控制器,而後從flash中讀出這些函數的機器碼寫到sdram中去,而後再「跳」到sdram 中執行之。mini2440的sdram 起始地址是在bank6的(S3C2440A容許在bank7也掛sdram,但mini2440把它們都掛在bank6了)故這些大個函數在鏈接時必須 指定其所處段的運行地址在 0x30000000 之上。
位置相關指令要用以運行地址計算出來的絕對地址,上面那些大個函數中的絕對地址必定會在0x30000000之上,在沒有初始化內存控制器以前bank6中的地址是不存在的,此時只能用位置無關指令。這些指令不管你在連接時指定的運行地址是什麼都能經過相對值正確跳轉。小程序
軟件分紅2部分,一個彙編用於必要寄存器初始化和拷貝內存,再加設置sp和跳轉main;一個cpp用於模擬大塊頭。
先是 head.s
sass
.text .global _start _start: bl kill_dog bl control_mem bl copy2sdram ldr pc, =sdram sdram: mov sp, #0x34000000 bl main _end: b _end kill_dog: mov r0, #0x53000000 mov r1, #0 str r1, [r0] mov pc, lr control_mem: mov r0, #0x48000000 ldr r1, =0x22111112 str r1, [r0], #4 mov r1, #0x00000700 str r1, [r0], #4 mov r1, #0x00000700 str r1, [r0], #4 mov r1, #0x00000700 str r1, [r0], #4 mov r1, #0x00000700 str r1, [r0], #4 mov r1, #0x00000700 str r1, [r0], #4 mov r1, #0x00000700 str r1, [r0], #4 ldr r1, =0x00018009 str r1, [r0], #4 ldr r1, =0x00018009 str r1, [r0], #4 ldr r1, =0x008e04eb str r1, [r0], #4 mov r1, #0x000000b2 str r1, [r0], #4 mov r1, #0x00000030 str r1, [r0], #4 mov r1, #0x00000030 str r1, [r0], #4 mov r1, #0x00000000 str r1, [r0], #4 mov r1, #0x00000000 str r1, [r0], #4 mov pc, lr copy2sdram: mov r0, #0 mov r1, #0x30000000 ldr r2, =0x1000 loop: ldr r3, [r0], #4 str r3, [r1], #4 cmp r0, r2 bne loop mov pc, lr
注意其中 ldr pc, =sdram
架構
sdram.cpp 是個用LED 模擬簡譜的小程序,若是誰有光感設備把LED轉成聲音......算了......我仍是考慮mini2440上的揚聲器吧:app
class CNumberedMusicalNotation { public: CNumberedMusicalNotation( void ); ~CNumberedMusicalNotation( void ); void dao( void ); void rai( void ); void mi( void ); void fa( void ); void suo( void ); void la( void ); void xi( void ); private: void latency( void ); unsigned long* data; }; CNumberedMusicalNotation::CNumberedMusicalNotation() { unsigned long* gpbcon = reinterpret_cast<unsigned long*>(0x56000010); *gpbcon = 0x15400; // enable GPB output data = reinterpret_cast<unsigned long*>(0x56000014); *data = ~0; } CNumberedMusicalNotation::~CNumberedMusicalNotation() { *data = ~0; latency(); } void CNumberedMusicalNotation::dao() { // led1 - GPB5 *data = ~(1<<5); latency(); } void CNumberedMusicalNotation::rai() { // led2 - GPB6 *data = ~(1<<6); latency(); } void CNumberedMusicalNotation::mi() { // led3 - GPB7 *data = ~(1<<7); latency(); } void CNumberedMusicalNotation::fa() { // led4 - GPB8 *data = ~(1<<8); latency(); } void CNumberedMusicalNotation::suo() { // led1+3 - GPB5+7 *data = ~(1<<5 | 1<<7); latency(); } void CNumberedMusicalNotation::la() { // led2+4 - GPB6+8 *data = ~(1<<6 | 1<<8); latency(); } void CNumberedMusicalNotation::xi() { // led1+4 - GPB5+8 *data = ~(1<<5 | 1<<8); latency(); } void CNumberedMusicalNotation::latency() { volatile int i; for ( i = 0; i < 10000; i++ ); } int __attribute__((__long_call__)) main() { CNumberedMusicalNotation n; n.dao(); n.rai(); n.mi(); n.suo(); n.suo(); n.la(); n.suo(); n.mi(); n.dao(); n.rai(); n.mi(); n.mi(); n.rai(); n.dao(); n.rai(); n.dao(); n.rai(); n.mi(); n.suo(); n.suo(); n.la(); n.suo(); n.mi(); n.dao(); n.rai(); n.mi(); n.mi(); n.rai(); n.rai(); n.dao(); return 0; }
最後是Makefile函數
all: sdram.elf sdram.elf : arm-linux-gcc -c -g -O2 -o head.o head.s arm-linux-g++ -c -g -O2 -o sdram.o sdram.cpp arm-linux-ld -Ttext 0x30000000 -o sdram.elf head.o sdram.o arm-linux-objcopy -O binary -S sdram.elf sdram.bin arm-linux-objdump -D -m arm sdram.elf > sdram.dis clean: rm -f *.o *.elf *.dis *.bin
注意其中 -Ttext 0x30000000oop
這些解釋有些費勁,配合 sdram.dis 看看就好說了(節)
sdram.elf: file format elf32-littlearm Disassembly of section .text: 30000000 <_start>: 30000000: eb000005 bl 3000001c <kill_dog> 30000004: eb000008 bl 3000002c <control_mem> 30000008: eb000027 bl 300000ac <copy2sdram> 3000000c: e59ff0b8 ldr pc, [pc, #184] ; 300000cc <loop+0x14> 30000010 <sdram>: 30000010: e3a0d30d mov sp, #872415232 ; 0x34000000 30000014: eb000105 bl 30000430 <main> 30000018 <_end>: 30000018: eafffffe b 30000018 <_end> ...... 300000ac <copy2sdram>: 300000ac: e3a00000 mov r0, #0 ; 0x0 300000b0: e3a01203 mov r1, #805306368 ; 0x30000000 300000b4: e3a02a01 mov r2, #4096 ; 0x1000 300000b8 <loop>: 300000b8: e4903004 ldr r3, [r0], #4 300000bc: e4813004 str r3, [r1], #4 300000c0: e1500002 cmp r0, r2 300000c4: 1afffffb bne 300000b8 <loop> 300000c8: e1a0f00e mov pc, lr 300000cc: 30000010 .word 0x30000010 300000d0: 22111112 .word 0x22111112 300000d4: 00018009 .word 0x00018009 300000d8: 008e04eb .word 0x008e04eb ...... 30000430 <main>: 30000430: e24dd008 sub sp, sp, #8 ; 0x8 30000434: e3a03000 mov r3, #0 ; 0x0 30000438: e58d3004 str r3, [sp, #4] 3000043c: e59d2004 ldr r2, [sp, #4] 30000440: e3a00c27 mov r0, #9984 ; 0x2700 30000444: e280000f add r0, r0, #15 ; 0xf 30000448: e3a01456 mov r1, #1442840576 ; 0x56000000 3000044c: e1520000 cmp r2, r0 30000450: e2433021 sub r3, r3, #33 ; 0x21 30000454: e3a02b55 mov r2, #87040 ; 0x15400 30000458: e5813014 str r3, [r1, #20] 3000045c: e5812010 str r2, [r1, #16] 30000460: ca000006 bgt 30000480 <main+0x50>
_start 開始位置爲什麼是 30000000?由於 -Ttext 0x30000000。再看 ldr pc, =sdram 被翻譯成什麼了?
3000000c: e59ff0b8 ldr pc, [pc, #184] ; 300000cc <loop+0x14>
而 300000cc 處是什麼呢?
300000cc: 30000010 .word 0x30000010
這 意味着 pc 將被賦值 0x30000010, 這顯然是編譯器計算出的位置相關代碼。而以前都是位置無關代碼,雖然他們指望的運行基址是0x30000000,但由於位置無關,放在哪裏也都行了,這正 是從flash 拷貝到 sdram 所須要的,由於reset 後pc是指向 0 的。
下面是 arm-linux-gdb 的調試過程,這裏須要一些「新」指令。
首先,看看你的板子從哪裏啓動的,這裏假設是從nand,片內ram爲始於0x00000000的4k。
[szs@szs mine]$ arm-linux-gdb GNU gdb (Sourcery G++ Lite 2008q3-72) 6.8.50.20080821-cvs Copyright (C) 2008 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "--host=i686-pc-linux-gnu --target=arm-none-linux-gnueabi". For bug reporting instructions, please see: <https://support.codesourcery.com/GNUToolchain/>. (gdb) target remote 127.0.0.1:3333 Remote debugging using 127.0.0.1:3333 0x00000000 in ?? () (gdb) load sdram.elf Loading section .text, size 0xc84 lma 0x30000000 Loading section .ARM.exidx, size 0x68 lma 0x30000c84 Start address 0x30000000, load size 3308 Transfer rate: 28 KB/sec, 1654 bytes/write. (gdb) i r r0 0x1b4 436 r1 0x200 512 r2 0xdb 219 r3 0x40 64 r4 0x57600 357888 r5 0x0 0 r6 0x60000 393216 r7 0x0 0 r8 0x33edffe0 871235552 r9 0x33fdd970 872274288 r10 0x33fd7600 872248832 r11 0x0 0 r12 0x100 256 sp 0x33f0ffd4 0x33f0ffd4 lr 0x7e0 2016 pc 0x30000000 0x30000000 fps 0x0 0 cpsr 0x800000d3 2147483859
默 認的load將.text 放在了0x30000000處,這是由於連接時指定了 -Ttext 0x30000000,pc 也被設置到了 0x30000000,但這對應一個剛剛 reset halt 過的開發板,0x30000000 顯然尚不可用!!作過load後的片內ram裏有什麼呢
(gdb) x/16xw 0 0x0: 0xea000014 0xe59ff014 0xe59ff014 0xe59ff014 0x10: 0xe59ff014 0xe59ff014 0xe59ff014 0xe59ff014 0x20: 0x33f80260 0x33f802c0 0x33f80320 0x33f80380 0x30: 0x33f803e0 0x33f80440 0x33f80460 0xdeadbeef
對比一下sdram.bin顯然不是你的機器碼,這樣就算把 pc 設置到0也不會運行你的程序的。how?
(gdb) load sdram.elf 0xd0000000 Loading section .text, size 0xc84 lma 0x0 Loading section .ARM.exidx, size 0x68 lma 0xc84 Start address 0x30000000, load size 3308 Transfer rate: 40 KB/sec, 1654 bytes/write. (gdb) set $pc=0 (gdb) i r r0 0x194 404 r1 0x200 512 r2 0xcb 203 r3 0x4e000000 1308622848 r4 0x57200 356864 r5 0x0 0 r6 0x60000 393216 r7 0x0 0 r8 0x33edffe0 871235552 r9 0x33fdd970 872274288 r10 0x33fd7200 872247808 r11 0x0 0 r12 0x100 256 sp 0x33f0ffd4 0x33f0ffd4 lr 0x7e0 2016 pc 0x0 0 fps 0x0 0 cpsr 0x800000d3 2147483859 (gdb) si 0x0000001c in ?? () (gdb) si 0x00000020 in ?? () (gdb) si 0x00000024 in ?? () (gdb) si 0x00000028 in ?? () (gdb) si 0x00000004 in ?? () (gdb) c Continuing.
load sdram.elf 時加個參數 0xd0000000,爲什麼?由於你想讓gdb 幫您把運行地址爲0x30000000的段裝載到0位置去,你須要給它一個偏移,0x30000000加 什麼能獲得0呢,只有靠溢出了。隨後還要
set $pc=0
若是你有興趣再
x/16xw 0
對比一下你的 sdram.bin 吧。經過 si 能夠看到 pc 再經歷 0 -> 1c -> 20 -> 24 -> 28 後又回到4 了,顯然是殺狗回來了。恭喜,c 後看着 LED 給你彈小調吧。
PS: 64M sdram 是如何算出的?S3C2440A 給 sdram 的數據線有32根D0~D31,這意味着一次能夠取出4個字節;但地址線只有13根A2~A14,如何尋址64M呢?原來選址不是一次完成的,先行再列,額外還有個頁。先用A2~A14指定行,再以約定時序用A2~A10指定列,配合A24~A25指定的頁,一個能夠尋址 13 + 9 + 2 = 24 即 16M 地址空間。由於位寬是16bit,故這表明着 32MB sdram 容量,再把2片這樣的 sdram 並連尋址,用D0~D31順序鏈接獲取輸出就能獲得 64M了。