打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
UBOOT 内存映射以及修改问题
二、nand flash boot分析
    S3C2410支持从nor/nand flash、eeprom等rom类型的介质启动。现在我想做的是从外部nand flash启动,首先看datasheet第一部分:

NAND Flash Boot Loader
· Supports booting from NAND flash memory
· 4KB internal buffer for booting
· Supports storage memory for NAND flash memory after booting

    可以明确,首先,s3c2410支持从nand flash存储介质启动,其次,在硬件上,s3c2410除了提供相应的逻辑外,还提供了一个4K的sram作为buffer用于nand启动。最后,s3c2410支持从nand flash启动之后的内存分配形式(也就是说,从nand flash启动之后和从nor flash启动之后的内存分配形式是不同的。s3c2410支持这两种形式,可以说是相对于其他的MCU不同的地方。)
    然后看datasheet的第六部分:nand flash controller。overview中首先讲述了用nand flash代替nor flash作为启动介质的原因(成本低)。

    S3C2410X boot code can be executed on an external NAND flash memory. In order to support NAND flash boot loader, the S3C2410X is equipped with an internal SRAM buffer called ‘Steppingstone’.When booting, the first 4
KBytes of the NAND flash memory will be loaded into Steppingstone and the boot code loaded into Steppingstone will be executed.
    Generally, the boot code will copy NAND flash content to SDRAM.Using hardware ECC, the NAND flash data validity will be checked. Upon the completion of thecopy, the main program will be executed on the SDRAM.

    可以很明显的看出,s3c2410x启动代码从外部nand flash启动的流程:上电复位后,s3c2410自动读取nand flash的前4KBytes的数据到内部sram buffer中,这个硬件的sram buffer被称为“Steppingstone”。然后执行下载到steppingstone的代码,这部分代码完成将nand flash的内容复制到sdram中,在复制时,利用硬件的ECC验证数据有效性。完成复制后,主程序就开始从sdram执行
    过程应该是很清晰。但是首先怀疑的是,s3c2410如何实现自动读取nand flash的前4KBytes数据到内部sram buffer当中。网上为什么没人就这点产生疑问,并深入分析呢?我分析可能有两种方法:一是像at91rm9200一样,内部集成一个小的rom,固化代码,这部分代码的作用就是完成自动读取功能;二是完全用硬件实现。仔细看了框图,发现s3c2410只有internal sram buffer,并没有rom,所以最大可能就是硬件实现。看一下硬件框图figure6-1,可以发现hardware ECC编解码器,可以看到internal buffer(4KB),另外注意的一个地方是,存在着control state machine和buffer control,而且之间有粗体线链接。也就是说明了用一个控制状态机实现了自动读取4KB数据的过程,完全的硬件实现。如下图所示:
    明确了这个问题之后,对从硬件上电到nand flash启动就比较清晰了。而且,也就理解为什么vivi的stage1的head.S必须要小于4KB,因为internal sram buffer只有4KB。如果要完成一个比较复杂的bootloader,那也应该尽量简化stage1,完成基本的初始化之后,把剩余的工作量都放到将nand flash的代码搬移到sdram之后进行。
三 memory controller分析
    看datasheet第五部分。s3c2410比较特殊,支持1G的内存空间,分为8个bank,每个bank128MBytes,128MB×8=1GB。但是在这8个bank中,又有所不同,并且nand flash不对应任何bank,它是通过一组寄存器来访问的(nand flash的地址不在8个bank中,所以它不对应任何一个bank,它一般由处于高端地址的SFR区也就是特殊功能寄存器区来访问,地址一般在0x48000000以上),可看上面框图的register bank。

— Total 8 memory banks
   Six memory banks for ROM, SRAM, etc.
   Remaining two memory banks for ROM, SRAM, SDRAM, etc.
— Seven fixed memory bank start address
— One flexible memory bank start address and programmable bank size

    可以推断出,sdram应该在bank6,起始地址固定为128M*6=0x30000000,在此之后,就要根据sdram的大小和位宽来决定了,而且有个注意的地方是,bank7必须和bank6一样大小。参考figure5-1和table 5-1就非常清晰了。现在EDUKIT-III上用了两片SDRAM,型号是HY57V561620CT-H,查看datasheet,它是4banks×4M×16bit=256Mbits=32Mbytes,那么两片组合起来就是64MBytes,位宽是32bit,所以bank6的地址范围是[0x30000000-0x33ffffff],bank7的地址范围是[0x34000000-0x37ffffff],所以bank6的起始地址是固定的为0x30000000 ,但是bank6的大小事不固定的取决于板子上面实际的SDRAM的位宽和大小,而bank7因为是紧挨着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:当访问BANKx(x从0到7,对应的地址范围(x*128M到(x+1)*128M-1,BANK6、7有稍微差别)时,片选信号nGCSx有效。本文所用的开发板,使了64M的NAND Flash和64M的SDRAM,NAND Flash不对应任何BANK,它是通过几组
寄存器来访问的
,在上电后,NAND  Flash开始的4k数据被自动地复制到芯片内部一个被称为“Steppingstone”的RAM上。Steppingstone被映射为地址0,上面的4k程序完成必要的初始化;SDRAM使用BANK6,它的物理起始地址6*128M=0x30000000。
    关于sdram,还应该知道刷新频率和列宽度。HY57V561620CT-H datasheet中,有:

· Auto refreshand self refresh
· 8192 refresh cycles / 64ms
· Programmable Burst Length and Burst Type
  - 1, 2, 4, 8or Full page for Sequential Burst
  - 1, 2, 4or 8 for Interleave Burst
· Programmable CAS Latency ; 2, 3 Clocks

    所以刷新频率为64ms/8192=7.8125us。
    查看PIN DESCRIPTION,可以看出A0-A12为地址,其中ROW Address为RA[0:12],Column Address为CA[0-8],显然CAS的位数为9bits。
四、实验内容分析
 
    实验内容很简单,就是完成基本的初始化之后,把steppingstone的4K数据搬移到sdram中。然后在sdram中执行灯循环点亮程序。结合这个实验,也可以很清晰的明白,前面几个基本实验,从nand flash启动后,所有代码搬移到了steppingstone中,实际执行时也是在steppingstone中,也就是boot internal sram(4KB)中执行的,所以运行时域和加载时域都是0x00000000设置的堆栈可以是1024,也可以是4096,但是注意一是最大为4096,二是保证不与可执行代码发生冲突。在这个程序中,运行时域和加载时域是不相同的。加载时域是0x00000000,但是运行时域是0x30000000。《s3c2410完全开发》对这个地方讲解不是太详细。经过实验,和王老师的帮助,弄清楚了到底怎么回事。现在关于运行时域和加载时域的具体分析如下:
 
    根据nand flash的特点,初始代码的加载时域为0x00000000,也就是当前PC的值为0x00000000,两种跳转指令b(l)等只能相对寻址,最大范围是+/-32MBytes,所以如果不改变PC的话,不可能能利用b或者bl跳转到sdram的空间中。跳转指令ldr则不受此寻址空间的限制,可以进行绝对寻址。需要了解的一个细节就是,链接后所有的标号都是基于运行地址的,比如运行地址为0x30000000,那么第一个标号_start地址就是0x30000000,所以可以利用ldr的绝对寻址来完成到sdram的跳转。下面根据编写的sdram的反汇编来进行分析:
先来分析这个工程中主Makefile的语法和含义:
CFLAGS := -I./include //gcc的编译选项,表示要在当前目录的include目录下查找相对应的头文件
LDFLAGS := -Ttext 0x30000000 //ld的链接选项,表示在链接时将整个工程的代码段也就是Text段加载到0x30000000
OBJS := $(patsubst %.s, %.o, $(wildcard arch/*.s))//两个Makefile中函数的运用,在规则中,通配符会被自动展开。但在变量的定义和函数引用时,通配符将失效。这种情况下如果需要通配符有效,就需要使用函数wildcard”,它的用法是:$(wildcard PATTERN...)。在Makefile中,它被展开为已经存在的、使用空格分开的、匹配此模式的所有文件列表,而patsubst函数则是替换函数,上面的意思也就是将arch文件夹下面所有以.s结尾的文件全部展开,然后将这些以.s结尾的文件全部替换为以.o结尾
OBJS += $(patsubst %.c, %.o, $(wildcard init/*.c))
all: sdram
sdram: $(OBJS)
 $(LD) $(LDFLAGS) $^ -o $@.o  //将所有的.o文件也就是目标文件全部链接到sdram.o文件中
 $(OBJDUMP) -D $@.o >$@_s   //将链接成的目标文件sdram.o中的符号地址全部输出到sdram_s中
 $(OBJCOPY) -O binary -S $@.o $@  //利用objcopy将目标文件sdram.o转化为二进制的格式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的数据复制到sdram的起始处
 bl copy_steppingston_to_sdram
 @ jump to sdram space
 ldr pc, =setup_stack //为什么将setup_stack标号的地址赋给pc,就能跳转到SDRAM中执行,因为在前面利用copy_steppingston_to_sdram已经将程序从steppingstone复制到了SDRAM中来,所以当前语句的下一句也就是在SDRAM中要执行的下一句
setup_stack:
 ldr sp, =stack_top //设置堆栈,一般将堆栈设置在BANK7中最大只能为4K
 bl main //跳转到主函数去执行,此时main函数也已经复制到SDRAM中来
stop:
 b stop
@
@ sub routines
@
@ r1: src base address
@ r2: dst base address
@ r3: data block length
copy_steppingston_to_sdram: //将steppingstone中4KB的内容复制到sdram中
 mov r1, #0x00000000 //基地址也就是steppingstone的地址
 ldr r2, =SDRAM_BASE //目的地址也就是sdram的地址
 mov r3, #4096 //复制的字节数也就是4KB
1:
 @ auto-indexing
 @ first transfer, and then write back to the base register r1
 ldr r4, [r1], #4 //每次复制4个字节
 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寄存器设置初始化
 mov     r1, #MEM_CTL_BASE //与SDRAM相关的寄存器是从0x48000000地址开始的,每个寄存器占4个字节
 adrl    r2, mem_cfg_val //adrl中等范围的地址读取,将mem_cfg_val标号表示的地址值读取到r2寄存器中来
 add     r3, r1, #13*4 //因为总共有13个寄存器,每个寄存器占4个字节,r3作为循环的结束条件
1:
 @ write initial values to registers
 ldr     r4, [r2], #4 //取出r2寄存器中的地址值对应的值赋给r4,然后r2加上4,指向下一个初始值
 str     r4, [r1], #4 //将r4的值赋值给r1对应的寄存器,然后将r1加上4,指向下一个寄存器
 cmp     r1, r3
 bne     1b
        mov     pc, lr //返回到调用处
.align 4
mem_cfg_val://相当于定义一个寄存器初值表,将每个寄存器的初始值通过一个标号全部定义在一起,因为每个寄存器的地址是规律的也就是相差4个字节所以可以很方便的通过查表来赋值
 .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
现在来分析这13个寄存器的初始值,寄存器每位分别表示什么含义:
1.BWSCON:对应BANK0-BANK7,每BANK使用4位。这4位分别表示:
a.STx:启动/禁止SDRAM的数据掩码引脚,对于SDRAM,此位为0;对于
SRAM,此位为1。

b.WSx:是否使用存储器的WAIT信号,通常设为0
c.DWx:使用两位来设置存储器的位宽:00-8位,01-16位,10-32位,
11-保留。
d.比较特殊的是BANK0对应的4位,它们由硬件跳线决定,只读。 对于本开发板,使用两片容量为32Mbyte、位宽为16的SDRAM组成容量为64Mbyte、位宽为32的存储器,所以其BWSCON相应位为:0010。对于本开发板,BWSCON可设为0x22111110:其实我们只需要将BANK6对应的4位设为0010即可,其它的是什么值没什么影响,这个值是参考手册上给出的。
2.BANKCON0-BANKCON5:我们没用到,使用默认值0x00000700即可 
3.BANKCON6-BANKCON7:设为0x00018005 ,在8个BANK中,只有BANK6和BANK7可以使用SRAM或SDRAM,所以BANKCON6-7与BANKCON0-5有点不同:
a.MT([16:15]):用于设置本BANK外接的是SRAM还是SDRAM:SRAM-00,SDRAM-11
b.当MT=11时,还需要设置两个参数:
Trcd([3:2]):RAS to CAS delay,设为推荐值01,因为在这里我们知道RAS是0:12也就是13位,而CAS是0:8也就是9位,所以delay就是3
SCAN([1:0]):SDRAM的列地址位数,对于本开发板使用的SDRAM HY57V561620CT-H,列地址位数为9,所以SCAN=01。如果使用其他型号的SDRAM,您需要查看它的数据手册来决定SCAN的取值:00-8位,01-9位,10-10位。
4.REFRESH(SDRAM refresh control register):设为0x008e0000+ R_CNT ,其中R_CNT用于控制SDRAM的刷新周期,占用REFRESH寄存器的[10:0]位,23位为SDRAM Refresh Enable一般为1允许自动和自我刷新,22位为SDRAM刷新的模式,是auto还是self,一般我们选择auto.
它的取值可如下计算(SDRAM时钟频率就是HCLK): 
  R_CNT = 2^11 + 1 – SDRAM时钟频率(MHz) * SDRAM刷新周期(uS)
在未使用PLL时,SDRAM时钟频率等于晶振频率12MHz;SDRAM的刷新周期在SDRAM的数据手册上有标明,在本开发板使用的SDRAM HY57V561620CT-H的数据手册上,可看见这么一行“8192 refresh cycles / 64ms”:所以,刷新周期=64ms/8192 = 7.8125 uS。 对于本实验,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对应的地址空间与BANK0-5不同。BANK0-5的地址空间都是固定的128M,地址范围是(x*128M)到(x+1)*128M-1,x表示0到5。但是BANK7的起始地址是可变的,您可以从S3C2410数据手册第5章“Table 5-1. Bank 6/7 Addresses”中了解到BANK6、7的地址范围与地址空间的关系。本开发板仅使用BANK6的64M空间,我们可以令位
[2:1]=010(128M/128M)或001(64M/64M)
:这没关系,多出来的空间程序会检测出来,不会发生使用不存在的内存的情况——后面介绍到的bootloader和linux内核都会作内存检测,位[6]、位[3]没有使用。
6.MRSRB6、MRSRB7:0x00000030
 能让我们修改的只有位[6:4](CL),SDRAM HY57V561620CT-H不支持CL=1的情况,所以位[6:4]取值为010(CL=2)或011(CL=3)。
 为了让程序结构简单一点,我都使用函数调用的方式。第一条指令是禁止WATCH DOG,您如果细心的话,一定会发现程序LEDS运行得有些不正常,那是因为WATCH DOG在不断地重启系统。以前为了程序简单,我没有把这段程序加上去。往WTCON寄存器(地址0x53000000)写入0即可禁止WATCH DOG。第二条指令设置本节开头所描述的13个寄存器,以便使用SDRAM。往下程序做的事情就是:将Steppingstone中的代码复制到SDRAM中(起始地址为 0x30000000),然后向pc寄存器直接赋值跳到SDRAM中执行下一条指令“ldr sp, =0x34000000”。
 在目录SDRAM下执行make指令生成可执行文件sdram后,下载到板子上运行,可以发现与LEDS程序相比,LED闪烁得更慢:这就对了,外部SDRAM的性能比起内部SRAM来说性能是差些。 把程序从性能更好的内部SRAM移到外部SDRAM中去,是否多此一举呢?内部
SRAM只有4k大小,如果我们的程序大于4k,那么就不能指望利用内部SRAM来运行了
。所以得想办法把存储在NAND Flash中的代码,复制到SDRAM中去。对于NAND Flash中的前4k,芯片自动把它复制到内部SRAM中,我们可以很轻松地再把它复制到SDRAM中(实验五中函数copy_steppingstone_to_sdram就做这事)。但是对于4k之后的代码,复制它就不那么轻松了,这就是nand flash之后的问题啦。
分析完初始化的汇编代码后,我们来看看生成的sdram二进制可执行文件反汇编的结果:
这里是反汇编的结果(因为链接地址是0x30000000,并且head.s问可执行文件的起始文件):

Disassembly of section .text:

30000000 <_start>:
30000000:       e3a00453        mov     r0, #1392508928 ; 0x53000000
30000004:       e3a01000        mov     r1, #0  ; 0x0
30000008:       e5801000        str     r1, [r0]
3000000c:       eb00000c        bl      30000044 <memsetup>
30000010:       eb000003        bl      30000024 <copy_steppingston_to_sdram>
30000014:       e59ff088        ldr     pc, [pc, #88]   ; 300000a4 <mem_cfg_val+0x34> //这里面的#88不是很理解

30000018 <setup_stack>:
30000018:       e3a0d30d        mov     sp, #872415232  ; 0x34000000
3000001c:       eb000023        bl      300000b0 <main> //注意这里c代码中的函数也会被加载进来

30000020 <stop>:
30000020:       eafffffe        b       30000020 <stop>

    可以很明显的看出,_start为0x30000000,stop为0x30000020。也就是说,经过链接之后,symbol table中的存放位置都是基于运行起始地址0x30000000的。但是需要注意的是,开始运行是PC的值为0x00000000,虽然bl 30000044 <memsetup>是30000044,但是要注意,此处指令为bl,所以只能相对寻址,而不能够绝对寻址,也就是说,它只能跳转到距离0x30000000为0x44的位置,这点查看bl的汇编指令说明就比较清晰了。

        ldr pc,=setup_stack
setup_stack:
        ldr sp, =stack_top
        bl main

    利用上面的技巧,就可以把PC的值装载到sdram的空间,因为之前代码搬移已经完成了,所以,后续的工作都已经工作在sdram的空间中了。
    如果在利用objcopy去除了符号信息之后,反汇编之后的结果只能是以0开始的相对地址,也就看不出上面的东西了,所以,要理解还应该是采用上面的分析方法。这点在《s3c2410完全开发》上是没有详细说明的。写到这里,自己已经比较清晰了。关于其他的分析,《s3c2410完全开发》已经比较详细了,可以参考。
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
S3C2410内存设置
LDQM UDQM
对Bootloader中SDRAM初始化过程的诠释 - Bootloader相关 - 幽灵空间 - 步入嵌入式 ARM Linux
基于S3C2410的土壤墒情监测系统设计1
BootLoader各步骤的原理及实现方法
嵌入式开发实战1——jflash烧写开发板
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服