二、nand flash boot分析
S3C2410支持從nor/nand flash、eeprom等rom類型的介質(zhì)啟動。現(xiàn)在我想做的是從外部nand flash啟動,首先看datasheet第一部分:
可以明確,首先,s3c2410支持從nand flash存儲介質(zhì)啟動,其次,在硬件上,s3c2410除了提供相應(yīng)的邏輯外,還提供了一個4K的sram作為buffer用于nand啟動。最后,s3c2410支持從nand flash啟動之后的內(nèi)存分配形式(也就是說,從nand flash啟動之后和從nor flash啟動之后的內(nèi)存分配形式是不同的。s3c2410支持這兩種形式,可以說是相對于其他的MCU不同的地方。)
然后看datasheet的第六部分:nand flash controller。overview中首先講述了用nand flash代替nor flash作為啟動介質(zhì)的原因(成本低)。
可以很明顯的看出,s3c2410x啟動代碼從外部nand flash啟動的流程:上電復(fù)位后,s3c2410自動讀取nand flash的前4KBytes的數(shù)據(jù)到內(nèi)部sram buffer中,這個硬件的sram buffer被稱為“Steppingstone”。然后執(zhí)行下載到steppingstone的代碼,這部分代碼完成將nand flash的內(nèi)容復(fù)制到sdram中,在復(fù)制時(shí),利用硬件的ECC驗(yàn)證數(shù)據(jù)有效性。完成復(fù)制后,主程序就開始從sdram執(zhí)行。
過程應(yīng)該是很清晰。但是首先懷疑的是,s3c2410如何實(shí)現(xiàn)自動讀取nand flash的前4KBytes數(shù)據(jù)到內(nèi)部sram buffer當(dāng)中。網(wǎng)上為什么沒人就這點(diǎn)產(chǎn)生疑問,并深入分析呢?我分析可能有兩種方法:一是像at91rm9200一樣,內(nèi)部集成一個小的rom,固化代碼,這部分代碼的作用就是完成自動讀取功能;二是完全用硬件實(shí)現(xiàn)。仔細(xì)看了框圖,發(fā)現(xiàn)s3c2410只有internal sram buffer,并沒有rom,所以最大可能就是硬件實(shí)現(xiàn)??匆幌掠布驁Dfigure6-1,可以發(fā)現(xiàn)hardware
ECC編解碼器,可以看到internal buffer(4KB),另外注意的一個地方是,存在著control state machine和buffer control,而且之間有粗體線鏈接。也就是說明了用一個控制狀態(tài)機(jī)實(shí)現(xiàn)了自動讀取4KB數(shù)據(jù)的過程,完全的硬件實(shí)現(xiàn)。如下圖所示:
![]() 明確了這個問題之后,對從硬件上電到nand flash啟動就比較清晰了。而且,也就理解為什么vivi的stage1的head.S必須要小于4KB,因?yàn)閕nternal sram buffer只有4KB。如果要完成一個比較復(fù)雜的bootloader,那也應(yīng)該盡量簡化stage1,完成基本的初始化之后,把剩余的工作量都放到將nand flash的代碼搬移到sdram之后進(jìn)行。
三 memory controller分析
看datasheet第五部分。s3c2410比較特殊,支持1G的內(nèi)存空間,分為8個bank,每個bank128MBytes,128MB×8=1GB。但是在這8個bank中,又有所不同,并且nand flash不對應(yīng)任何bank,它是通過一組寄存器來訪問的(nand flash的地址不在8個bank中,所以它不對應(yīng)任何一個bank,它一般由處于高端地址的SFR區(qū)也就是特殊功能寄存器區(qū)來訪問,地址一般在0x48000000以上),可看上面框圖的register
bank。
可以推斷出,sdram應(yīng)該在bank6,起始地址固定為128M*6=0x30000000,在此之后,就要根據(jù)sdram的大小和位寬來決定了,而且有個注意的地方是,bank7必須和bank6一樣大小。參考figure5-1和table 5-1就非常清晰了。現(xiàn)在EDUKIT-III上用了兩片SDRAM,型號是HY57V561620CT-H,查看datasheet,它是4banks×4M×16bit=256Mbits=32Mbytes,那么兩片組合起來就是64MBytes,位寬是32bit,所以bank6的地址范圍是[0x30000000-0x33ffffff],bank7的地址范圍是[0x34000000-0x37ffffff],所以bank6的起始地址是固定的為0x30000000
,但是bank6的大小事不固定的取決于板子上面實(shí)際的SDRAM的位寬和大小,而bank7因?yàn)槭蔷o挨著bank6所以bank7的起始地址也是不固定的,大小同樣也是不固定的。查看s3c2410 table 5-2,可以知道bank選擇地址線為A[25:24]--->BA[1:0]。
S3C2410提供了外接ROM、 SRAM、 SDRAM、 NOR Flash、 NAND Flash的接口。 S3C2410外接存儲器的空間被分為8 個BANKS,每BANK容量為128M:當(dāng)訪問BANKx(x從0到7,對應(yīng)的地址范圍(x*128M到(x+1)*128M-1,BANK6、7有稍微差別)時(shí),片選信號nGCSx有效。本文所用的開發(fā)板,使了64M的NAND Flash和64M的SDRAM,NAND
Flash不對應(yīng)任何BANK,它是通過幾組
寄存器來訪問的,在上電后,NAND Flash開始的4k數(shù)據(jù)被自動地復(fù)制到芯片內(nèi)部一個被稱為“Steppingstone”的RAM上。Steppingstone被映射為地址0,上面的4k程序完成必要的初始化;SDRAM使用BANK6,它的物理起始地址6*128M=0x30000000。 關(guān)于sdram,還應(yīng)該知道刷新頻率和列寬度。HY57V561620CT-H datasheet中,有:
所以刷新頻率為64ms/8192=7.8125us。
查看PIN DESCRIPTION,可以看出A0-A12為地址,其中ROW Address為RA[0:12],Column Address為CA[0-8],顯然CAS的位數(shù)為9bits。
四、實(shí)驗(yàn)內(nèi)容分析
實(shí)驗(yàn)內(nèi)容很簡單,就是完成基本的初始化之后,把steppingstone的4K數(shù)據(jù)搬移到sdram中。然后在sdram中執(zhí)行燈循環(huán)點(diǎn)亮程序。結(jié)合這個實(shí)驗(yàn),也可以很清晰的明白,前面幾個基本實(shí)驗(yàn),從nand flash啟動后,所有代碼搬移到了steppingstone中,實(shí)際執(zhí)行時(shí)也是在steppingstone中,也就是boot internal sram(4KB)中執(zhí)行的,所以運(yùn)行時(shí)域和加載時(shí)域都是0x00000000,設(shè)置的堆棧可以是1024,也可以是4096,但是注意一是最大為4096,二是保證不與可執(zhí)行代碼發(fā)生沖突。在這個程序中,運(yùn)行時(shí)域和加載時(shí)域是不相同的。加載時(shí)域是0x00000000,但是運(yùn)行時(shí)域是0x30000000?!秙3c2410完全開發(fā)》對這個地方講解不是太詳細(xì)。經(jīng)過實(shí)驗(yàn),和王老師的幫助,弄清楚了到底怎么回事。現(xiàn)在關(guān)于運(yùn)行時(shí)域和加載時(shí)域的具體分析如下:
根據(jù)nand flash的特點(diǎn),初始代碼的加載時(shí)域?yàn)?x00000000,也就是當(dāng)前PC的值為0x00000000,兩種跳轉(zhuǎn)指令b(l)等只能相對尋址,最大范圍是+/-32MBytes,所以如果不改變PC的話,不可能能利用b或者bl跳轉(zhuǎn)到sdram的空間中。跳轉(zhuǎn)指令ldr則不受此尋址空間的限制,可以進(jìn)行絕對尋址。需要了解的一個細(xì)節(jié)就是,鏈接后所有的標(biāo)號都是基于運(yùn)行地址的,比如運(yùn)行地址為0x30000000,那么第一個標(biāo)號_start地址就是0x30000000,所以可以利用ldr的絕對尋址來完成到sdram的跳轉(zhuǎn)。下面根據(jù)編寫的sdram的反匯編來進(jìn)行分析:
先來分析這個工程中主Makefile的語法和含義:
CFLAGS := -I./include
//gcc的編譯選項(xiàng),表示要在當(dāng)前目錄的include目錄下查找相對應(yīng)的頭文件
LDFLAGS := -Ttext 0x30000000 //ld的鏈接選項(xiàng),表示在鏈接時(shí)將整個工程的代碼段也就是Text段加載到0x30000000 OBJS := $(patsubst %.s, %.o, $(wildcard arch/*.s))
//兩個Makefile中函數(shù)的運(yùn)用,在規(guī)則中,通配符會被自動展開。但在變量的定義和函數(shù)引用時(shí),通配符將失效。這種情況下如果需要通配符有效,就需要使用函數(shù)“wildcard”,它的用法是:$(wildcard
PATTERN...)
。在Makefile中,它被展開為已經(jīng)存在的、使用空格分開的、匹配此模式的所有文件列表,而patsubst函數(shù)則是替換函數(shù),上面的意思也就是將arch文件夾下面所有以.s結(jié)尾的文件全部展開,然后將這些以.s結(jié)尾的文件全部替換為以.o結(jié)尾
OBJS += $(patsubst %.c, %.o, $(wildcard init/*.c)) all: sdram
sdram: $(OBJS)
$(LD) $(LDFLAGS) $^ -o $@.o //將所有的.o文件也就是目標(biāo)文件全部鏈接到sdram.o文件中 $(OBJDUMP) -D $@.o >$@_s //將鏈接成的目標(biāo)文件sdram.o中的符號地址全部輸出到sdram_s中 $(OBJCOPY) -O binary -S $@.o $@ //利用objcopy將目標(biāo)文件sdram.o轉(zhuǎn)化為二進(jìn)制的格式sdram clean: find . -name "*.o" | xargs rm -f $(RM) sdram* debug:
@echo "OBJS: $(OBJS)" # compile rules
%.o: %.s $(CC) $(CFLAGS) -c $< -o $@ %.o: %.c $(CC) $(CFLAGS) -c $< -o $@ 在來分析在使用SDRAM之前需要初始化多個寄存器,這些寄存器很多都是類似的,并且由于我們只是使用BANK6所以大部分寄存器不用理會。一般我們將初始化的一些代碼,主要是對寄存器操作的代碼利用匯編代碼來編寫:
@ WDT Register
.equ WDTCON, 0x53000000 @ Memory Control Register Base Address .equ MEM_CTL_BASE, 0x48000000 @ Sdram Base Address .equ SDRAM_BASE, 0x30000000 @ Stack top address .equ stack_top, 0x34000000 @
@ start @ .text
.global _start _start:
@ disable watch dog timer mov r0, #WDTCON mov r1, #0x0 str r1, [r0] @ memory setup 初始化SDRAM寄存器
bl memsetup @ copy the 4K code from steppingstone 將steppingstone的4KB的數(shù)據(jù)復(fù)制到sdram的起始處
bl copy_steppingston_to_sdram @ jump to sdram space
ldr pc, =setup_stack //為什么將setup_stack標(biāo)號的地址賦給pc,就能跳轉(zhuǎn)到SDRAM中執(zhí)行,因?yàn)樵谇懊胬?span style="color:#000000">copy_steppingston_to_sdram已經(jīng)將程序從steppingstone復(fù)制到了SDRAM中來,所以當(dāng)前語句的下一句也就是在SDRAM中要執(zhí)行的下一句 setup_stack:
ldr sp, =stack_top //設(shè)置堆棧,一般將堆棧設(shè)置在BANK7中最大只能為4K bl main //跳轉(zhuǎn)到主函數(shù)去執(zhí)行,此時(shí)main函數(shù)也已經(jīng)復(fù)制到SDRAM中來
stop:
b stop @
@ sub routines @ @ r1: src base address
@ r2: dst base address @ r3: data block length copy_steppingston_to_sdram: //將steppingstone中4KB的內(nèi)容復(fù)制到sdram中
mov r1, #0x00000000 //基地址也就是steppingstone的地址 ldr r2, =SDRAM_BASE //目的地址也就是sdram的地址 mov r3, #4096 //復(fù)制的字節(jié)數(shù)也就是4KB 1: @ auto-indexing @ first transfer, and then write back to the base register r1 ldr r4, [r1], #4 //每次復(fù)制4個字節(jié) str r4, [r2], #4 @ r1 is equal to counter cmp r1, r3 bne 1b mov pc, lr
@ r1: memory control register base address
@ r2: memory control register table address @ r3: r1+13 words(because there is 13 registers) memsetup: //SDRAM寄存器設(shè)置初始化
mov r1, #MEM_CTL_BASE //與SDRAM相關(guān)的寄存器是從0x48000000地址開始的,每個寄存器占4個字節(jié) adrl r2, mem_cfg_val //adrl中等范圍的地址讀取,將mem_cfg_val標(biāo)號表示的地址值讀取到r2寄存器中來 add r3, r1, #13*4 //因?yàn)榭偣灿?3個寄存器,每個寄存器占4個字節(jié),r3作為循環(huán)的結(jié)束條件 1: @ write initial values to registers ldr r4, [r2], #4 //取出r2寄存器中的地址值對應(yīng)的值賦給r4,然后r2加上4,指向下一個初始值 str r4, [r1], #4 //將r4的值賦值給r1對應(yīng)的寄存器,然后將r1加上4,指向下一個寄存器 cmp r1, r3 bne 1b mov pc, lr //返回到調(diào)用處
.align 4
mem_cfg_val://相當(dāng)于定義一個寄存器初值表,將每個寄存器的初始值通過一個標(biāo)號全部定義在一起,因?yàn)槊總€寄存器的地址是規(guī)律的也就是相差4個字節(jié)所以可以很方便的通過查表來賦值 .long 0x22111110 @ BWSCON .long 0x00000700 @ BANKCON0 .long 0x00000700 @ BANKCON1 .long 0x00000700 @ BANKCON2 .long 0x00000700 @ BANKCON3 .long 0x00000700 @ BANKCON4 .long 0x00000700 @ BANKCON5 .long 0x00018005 @ BANKCON6 .long 0x00018005 @ BANKCON7 .long 0x008e07a3 @ REFRESH .long 0x000000b2 @ BANKSIZE .long 0x00000030 @ MRSRB6 .long 0x00000030 @ MRSRB7 .end
現(xiàn)在來分析這13個寄存器的初始值,寄存器每位分別表示什么含義:
1.BWSCON:對應(yīng)BANK0-BANK7,每BANK使用4位。這4位分別表示:
a.STx:啟動/禁止SDRAM的數(shù)據(jù)掩碼引腳,對于SDRAM,此位為0;對于 SRAM,此位為1。 b.WSx:是否使用存儲器的WAIT信號,通常設(shè)為0 c.DWx:使用兩位來設(shè)置存儲器的位寬:00-8位,01-16位,10-32位, 11-保留。 d.比較特殊的是BANK0對應(yīng)的4位,它們由硬件跳線決定,只讀。 對于本開發(fā)板,使用兩片容量為32Mbyte、位寬為16的SDRAM組成容量為64Mbyte、位寬為32的存儲器,所以其BWSCON相應(yīng)位為:0010。對于本開發(fā)板,BWSCON可設(shè)為0x22111110:其實(shí)我們只需要將BANK6對應(yīng)的4位設(shè)為0010即可,其它的是什么值沒什么影響,這個值是參考手冊上給出的。 2.BANKCON0-BANKCON5:我們沒用到,使用默認(rèn)值0x00000700即可
3.BANKCON6-BANKCON7:設(shè)為0x00018005 ,在8個BANK中,只有BANK6和BANK7可以使用SRAM或SDRAM,所以BANKCON6-7與BANKCON0-5有點(diǎn)不同:
a.MT([16:15]):用于設(shè)置本BANK外接的是SRAM還是SDRAM:SRAM-00,SDRAM-11 b.當(dāng)MT=11時(shí),還需要設(shè)置兩個參數(shù): Trcd([3:2]):RAS to CAS delay,設(shè)為推薦值01,因?yàn)樵谶@里我們知道RAS是0:12也就是13位,而CAS是0:8也就是9位,所以delay就是3 SCAN([1:0]):SDRAM的列地址位數(shù),對于本開發(fā)板使用的SDRAM HY57V561620CT-H,列地址位數(shù)為9,所以SCAN=01。如果使用其他型號的SDRAM,您需要查看它的數(shù)據(jù)手冊來決定SCAN的取值:00-8位,01-9位,10-10位。 4.REFRESH(SDRAM refresh control register):設(shè)為0x008e0000+ R_CNT ,其中R_CNT用于控制SDRAM的刷新周期,占用REFRESH寄存器的[10:0]位,23位為SDRAM Refresh Enable一般為1允許自動和自我刷新,22位為SDRAM刷新的模式,是auto還是self,一般我們選擇auto.
它的取值可如下計(jì)算(SDRAM時(shí)鐘頻率就是HCLK):
R_CNT = 2^11 + 1 – SDRAM時(shí)鐘頻率(MHz) * SDRAM刷新周期(uS)
在未使用PLL時(shí),SDRAM時(shí)鐘頻率等于晶振頻率12MHz;SDRAM的刷新周期在SDRAM的數(shù)據(jù)手冊上有標(biāo)明,在本開發(fā)板使用的SDRAM HY57V561620CT-H的數(shù)據(jù)手冊上,可看見這么一行“8192 refresh cycles / 64ms”:所以,刷新周期=64ms/8192 = 7.8125 uS。 對于本實(shí)驗(yàn),R_CNT = 2^11 + 1 – 12 * 7.8125 = 1955, REFRESH=0x008e0000 + 1955 = 0x008e07a3. 5.BANKSIZE:0x000000b2
位[7]=1:Enable burst operation 位[5]=1:SDRAM power down mode enable 位[4]=1:SCLK is active only during the access (recommended) 位[2:1]=010:BANK6、BANK7對應(yīng)的地址空間與BANK0-5不同。BANK0-5的地址空間都是固定的128M,地址范圍是(x*128M)到(x+1)*128M-1,x表示0到5。但是BANK7的起始地址是可變的,您可以從S3C2410數(shù)據(jù)手冊第5章“Table 5-1. Bank 6/7 Addresses”中了解到BANK6、7的地址范圍與地址空間的關(guān)系。本開發(fā)板僅使用BANK6的64M空間,我們可以令位 [2:1]=010(128M/128M)或001(64M/64M):這沒關(guān)系,多出來的空間程序會檢測出來,不會發(fā)生使用不存在的內(nèi)存的情況——后面介紹到的bootloader和linux內(nèi)核都會作內(nèi)存檢測,位[6]、位[3]沒有使用。 6.MRSRB6、MRSRB7:0x00000030
能讓我們修改的只有位[6:4](CL),SDRAM HY57V561620CT-H不支持CL=1的情況,所以位[6:4]取值為010(CL=2)或011(CL=3)。 為了讓程序結(jié)構(gòu)簡單一點(diǎn),我都使用函數(shù)調(diào)用的方式。第一條指令是禁止WATCH DOG,您如果細(xì)心的話,一定會發(fā)現(xiàn)程序LEDS運(yùn)行得有些不正常,那是因?yàn)閃ATCH DOG在不斷地重啟系統(tǒng)。以前為了程序簡單,我沒有把這段程序加上去。往WTCON寄存器(地址0x53000000)寫入0即可禁止WATCH DOG。第二條指令設(shè)置本節(jié)開頭所描述的13個寄存器,以便使用SDRAM。往下程序做的事情就是:將Steppingstone中的代碼復(fù)制到SDRAM中(起始地址為
0x30000000),然后向pc寄存器直接賦值跳到SDRAM中執(zhí)行下一條指令“l(fā)dr sp, =0x34000000”。
在目錄SDRAM下執(zhí)行make指令生成可執(zhí)行文件sdram后,下載到板子上運(yùn)行,可以發(fā)現(xiàn)與LEDS程序相比,LED閃爍得更慢:這就對了,外部SDRAM的性能比起內(nèi)部SRAM來說性能是差些。 把程序從性能更好的內(nèi)部SRAM移到外部SDRAM中去,是否多此一舉呢?內(nèi)部
SRAM只有4k大小,如果我們的程序大于4k,那么就不能指望利用內(nèi)部SRAM來運(yùn)行了。所以得想辦法把存儲在NAND Flash中的代碼,復(fù)制到SDRAM中去。對于NAND Flash中的前4k,芯片自動把它復(fù)制到內(nèi)部SRAM中,我們可以很輕松地再把它復(fù)制到SDRAM中(實(shí)驗(yàn)五中函數(shù)copy_steppingstone_to_sdram就做這事)。但是對于4k之后的代碼,復(fù)制它就不那么輕松了,這就是nand flash之后的問題啦。 分析完初始化的匯編代碼后,我們來看看生成的sdram二進(jìn)制可執(zhí)行文件反匯編的結(jié)果:
這里是反匯編的結(jié)果(因?yàn)殒溄拥刂肥?x30000000,并且head.s問可執(zhí)行文件的起始文件):
可以很明顯的看出,_start為0x30000000,stop為0x30000020。也就是說,經(jīng)過鏈接之后,symbol table中的存放位置都是基于運(yùn)行起始地址0x30000000的。但是需要注意的是,開始運(yùn)行是PC的值為0x00000000,雖然bl 30000044 <memsetup>是30000044,但是要注意,此處指令為bl,所以只能相對尋址,而不能夠絕對尋址,也就是說,它只能跳轉(zhuǎn)到距離0x30000000為0x44的位置,這點(diǎn)查看bl的匯編指令說明就比較清晰了。
利用上面的技巧,就可以把PC的值裝載到sdram的空間,因?yàn)橹按a搬移已經(jīng)完成了,所以,后續(xù)的工作都已經(jīng)工作在sdram的空間中了。
如果在利用objcopy去除了符號信息之后,反匯編之后的結(jié)果只能是以0開始的相對地址,也就看不出上面的東西了,所以,要理解還應(yīng)該是采用上面的分析方法。這點(diǎn)在《s3c2410完全開發(fā)》上是沒有詳細(xì)說明的。寫到這里,自己已經(jīng)比較清晰了。關(guān)于其他的分析,《s3c2410完全開發(fā)》已經(jīng)比較詳細(xì)了,可以參考。
|
|