S3C2416裸机开发系列四_外部内存初始化以及代码搬移

S3C2416裸机开发系列四

外部内存初始化以及代码搬移

象棋小子    1048272975

对于处理器来说,都不可能内置过大的内存,只保留一小块SRAM作为芯片启动用。例如S3C2416内部SRAM只有64k,其中8k是作为SteppingStone,用来做一些基本的初始化,并进一步引导用户的代码启动。用户的代码往往是在外部的内存空间执行,因为通过处理器的存储器接口,可以外扩较大的内存空间。对于较大型的设计,用户代码以及变量往往都是在外部内存空间执行或存放。因此笔者就S3C2416初始化外部DDR2以及代码从sd卡搬移到外部内存并执行作简单的介绍。

1. 内存控制器初始化

S3C2416支持多种存储器接口,如SRAM、DDR、DDR2等,一般设计都是采用DDR2存储器接口来外扩内存的。在使用外部内存之前,是必须先初始化存储器接口的,以确定访问时序等参数。

1.1. DDR2初始化流程

对于DDR2的初始化,S3C2416数据手册都是给出了详细的流程的:

1.1.1. Setting the BANKCFG &BANKCON1, 2, 3.

1.1.2. Wait 200us to allow DRAMpower and clock stabilize.

1.1.3. Wait minimum of 400 nsthen issue a PALL(pre-charge all) command. Program the INIT[1:0] to ‘01b’. Thisautomatically issues a PALL(pre-charge all) command to the DRAM.

1.1.4. Issue an EMRS command toEMR(2), provide LOW to BA0, High to BA1. Program the INIT[1:0] of ControlRegister1 to ‘11b’ & BANKCON3[31]=’1b’.

1.1.5. Issue an EMRS command toEMR(3), provide High to BA0 and BA1. Program the INIT[1:0] of Control Register1to ‘11b’ & BANKCON3[31:30]=’11b’.

1.1.6. Issue an EMRS to enableDLL and RDQS, nDQS, ODT disable.

1.1.7. Issue a Mode Register Setcommand for DLL reset.(To issue DLL Reset command, provide HIGH to A8 and LOWto BA0-BA1, and A13-A15.)  Program theINIT[1:0] to ‘10b’. & BANKCON3[8]=’1b’.

1.1.8. Issue a PALL(pre-chargeall) command. Program the INIT[1:0] to ‘01b’. This automatically issues aPALL(pre-charge all) command to the DRAM.

1.1.9. Issue 2 or moreauto-refresh commands.

1.1.10. Issue a MRS command withLOW to A8 to initialize device operation. Program the INIT[1:0] to ‘10b’. &BANKCON3[8]=’0b’.

1.1.11. Wait 200 clock afterstep 7, execute OCD Calibration.

1.1.12. The external DRAM is nowready for normal operation.

 

1.2. DDR2初始化代码实现

对于不同的DDR2,主要是配置第一步BANKCFG &BANKCON1, 2,其它初始化流程可以通用。BANKCFG主要是用来配置外扩DDR2的行地址线,列地址线以及接口位宽等。BANKCON1用来配置DDR2控制器一些控制属性,如自动预充,功耗控制,写缓存等。BANKCON2用来配置DDR2的时序参数,如果时序参数设置得过快,将无法初始化相应的DDR2,参数设置得过慢,将造成DDR2读写性能低。因此,需要对照相应的DDR2芯片数据手册时序参数来作设置,通常要比手册参数预留一定的裕度(如多1~2个clock)。笔者所用的DDR2型号为K4T51163QJ-BCE79(DDR2@400M 5-5-5),64MB,行地址线13,列地址线为10,16位总线。K4T51163QJ-BCE79给出的Active命令到发出Read/Write命令时间间隔tRCD=5tCK=12.5ns,Precharge命令到发送Active命令时间间隔tRP=5tCK=12.5ns,Read/Write命令发出后经过5tCK=12.5ns数据才有效(CAS锁存时间),Active命令到Precharge命令时间间隔tRAS=45ns,平均刷新周期间隔为tREFI=780us,指令刷新时间tRFC=105ns。S3C2416时序参数配置寄存器BANKCON2都是以HCLK为基准来计时序的clock的。例如在HCLK=133M情况下,设置BANKCON2中[1:0]为1(2个HCLK)即可满足DDR2芯片tRP=12.5ns的时序要求,为了保险,设置3个HCLK为宜。笔者在LowlevelInit.s汇编文件中实现DDR2初始化函数,不同的DDR2只需根据相应芯片参数修改BANKCFG & BANKCON1, 2,即可使用。

; DRAMcontroller base address

DRAM_BASE       EQU     0x48000000

BANKCFG_OFS     EQU 0x00

BANKCON1_OFS        EQU 0x04

BANKCON2_OFS        EQU 0x08

BANKCON3_OFS        EQU 0x0C

REFRESH_OFS     EQU 0x10

TIMEOUT_OFS     EQU 0x14

 

        PRESERVE8

        AREA   LOWLEVELINIT, CODE, READONLY

        ARM

        EXPORT ERAM_Init

;K4T51163QJ-BCE79(DDR2@400M 5-5-5),64MB,Raw Addr A0~A12,Column Addr A0~A9

; 设置DDR0 13位行地址,10位列地址,DDR2接口,16位总线

; DDR命令根据nRAS,nCAS,nWE,nCS控制总线分辨

; Active命令,打开行及bank地址

; Read命令,在Active后,打开列地址读

; Write命令,在Active后,打开列地址写

; Precharge命令,关闭bank,根据A[10]确定关闭指定或所有bank(只能同时访问一个bank)

; AUTOREFRESH or SELF REFRESH命令,刷新命令

; LOAD MODEREGISTER命令,写模式寄存器

ERAM_Init

        LDR     R0,=DRAM_BASE

        LDR     R1,=(2<<17)+(2<<11)+(0<<6)+(1<<1)+(1<<0)

        STR     R1,[R0, #BANKCFG_OFS]

; DQS delay3,Write buffer,Auto pre-charge,bank address 在高位

        LDR     R1,=(3<<28)+(1<<26)+(1<<8)+(0<<7)+ \

                             (1<<6)+(0<<5)+(1<<4)

        STR     R1,[R0, #BANKCON1_OFS]    

               

; s3c2416ddr2寄存器的clk设置值是相对HCLK的

; RAS [23:20]Row active time 45ns HCLK=133M DDR2=266M 6clock

; Active命令到Precharge命令的最小时间45ns

; ARFC[19:16] Row cycle time tRFC=105ns 14clock

; 指令刷新时间105ns

; CAS Latency[5:4] CAS latency control 12.5ns 2clock

; Read/Write命令发出后经过5tCK=12.5ns数据才有效

; tRCD [3:2]RAS to CAS delay 12.5ns 2clock

; Active命令需经5tCK=12.5ns后才发出Read/Write命令

; tRP [1:0]Row pre-charge time 12.5ns 2clock

; Precharge命令到发送Active命令5tCK=12.5ns

; 故两个Active命令所需的最小时间tRC=tRAS+tRP=57.5ns

        LDR     R1,=(6<<20)+(13<<16)+(3<<4)+(2<<2)+(2<<0)

        STR     R1,[R0, #BANKCON2_OFS]

 

; issue aPALL(pre-charge all) command,即Precharge命令

        LDR     R1,[R0, #BANKCON1_OFS]

        BIC     R1,R1, #0x03

        ORR     R1,R1, #0x01

        STR     R1,[R0, #BANKCON1_OFS]

 

; issue anEMRS(extern mode register) command to EMR(2)

        LDR     R1,=(0x2<<30)+(0<<23)+(0<<19)+(0<<16)

        STR     R1,[R0, #BANKCON3_OFS]

        LDR     R1,[R0, #BANKCON1_OFS]

        ORR     R1,R1, #0x03              

        STR     R1,[R0, #BANKCON1_OFS]

 

; issue anEMRS(extern mode register) command to EMR(3)

        LDR     R1,=(0x3<<30)

        STR     R1,[R0, #BANKCON3_OFS]

        LDR     R1,[R0, #BANKCON1_OFS]

        ORR     R1,R1, #0x03              

        STR     R1,[R0, #BANKCON1_OFS]

               

; issue anEMRS to enable DLL and RDQS, nDQS, ODT disable

        LDR     R1,=0xFFFF0000

        LDR     R2,[R0, #BANKCON3_OFS]

        BIC     R2,R2, R1

        LDR     R1,=(0x1<<30)+(0<<28)+(0<<27)+(1<<26)+ \

                             (7<<23)+(0<<19)+(0<<22)+(0<<18)+\

                             (0x0<<17)+(0<<16)             

        ORR     R1,R1, R2

        STR     R1,[R0, #BANKCON3_OFS]

        LDR     R1,[R0, #BANKCON1_OFS]

        ORR     R1,R1, #0x03              

        STR     R1,[R0, #BANKCON1_OFS]

 

; issue amode register set command for DLL reset

        LDR     R1,=0x0000FFFF

        LDR     R2,[R0, #BANKCON3_OFS]

        BIC     R2,R2, R1

        LDR     R1,=(0x1<<9)+(1<<8)+(0<<7)+(3<<4)                     

        ORR     R1,R1, R2

        STR     R1,[R0, #BANKCON3_OFS]

        LDR     R1,[R0, #BANKCON1_OFS]

        BIC     R1,R1, #0x03

        ORR     R1,R1, #0x02              

        STR     R1,[R0, #BANKCON1_OFS]

               

; Issue aPALL(pre-charge all) command 

        LDR     R1,[R0, #BANKCON1_OFS]

        BIC     R1,R1, #0x03

        ORR     R1,R1, #0x01

        STR     R1,[R0, #BANKCON1_OFS]

               

; Issue 2 ormore auto-refresh commands

        LDR     R1,=0x20

        STR     R1,[R0, #REFRESH_OFS]

               

 

; Issue a MRScommand with LOW to A8 to initialize device operation

        LDR     R1,=0x0000FFFF

        LDR     R2,[R0, #BANKCON3_OFS]

        BIC     R2,R2, R1

        LDR     R1,=(0x1<<9)+(0<<8)+(0<<7)+(3<<4)                 

        ORR     R1,R1, R2

        STR     R1,[R0, #BANKCON3_OFS]

        LDR     R1,[R0, #BANKCON1_OFS]

        BIC     R1,R1, #0x03

        ORR     R1,R1, #0x02              

        STR     R1,[R0, #BANKCON1_OFS]

               

; Wait 200clock, execute OCD Calibration

        LDR     R1,=200

0       SUBS    R1,R1, #1

        BNE     %B0

               

; Issue aEMRS1 command to over OCD Mode Calibration

        LDR     R1,=0xFFFF0000

        LDR     R2,[R0, #BANKCON3_OFS]

        BIC     R2,R2, R1

        LDR     R1,=(0x1<<30)+(0<<28)+(0<<27)+(1<<26)+ \

                     (0<<23)+(0<<19)+(0<<22)+(0<<18)+\

                     (0x0<<17)+(0<<16)             

        ORR     R1,R1, R2

        STR     R1,[R0, #BANKCON3_OFS]

        LDR     R1,[R0, #BANKCON1_OFS]

        ORR     R1,R1, #0x03              

        STR     R1,[R0, #BANKCON1_OFS]

 

; Refreshperiod is 7.8us, HCLK=100M, REFCYC=780

        LDR     R1,=780

        STR     R1,[R0, #REFRESH_OFS]

 

; issue aNormal mode

        LDR     R1,[R0, #BANKCON1_OFS]

        BIC     R1,R1, #0x03

        STR     R1,[R0, #BANKCON1_OFS]

               

        BX      LR

2. 代码搬移

S3C2416上电复位后会自动从启动设备拷贝用户最前面8k代码到Steppingstone中,这8k的代码称为BL1(bootloader1),用来进一步引导用户的其它代码到相应的运行域。外部内存初始化成功后,即可把代码从存储设备读出到外部内存的运行域,堆栈等变量也可放到外部内存空间(由链接文件指定)。

代码搬移首先要识别出启动设备,从而调用相应的设备拷贝函数。拷贝函数应确定代码在存储设备中的位置,代码的大小,以及这段代码的运行地址,这样才能正确地搬移代码到RAM位置并执行。代码在存储设备中的位置对于nand,是在0x0地址偏移处。SD/MMC分普通卡与SDHC(高容量卡,大于2G),对于普通卡sd/mmc,代码位置是从SD/MMC最末块地址倒数18块之前的位置(图2-1)。对于高容量卡,代码位置是从SD/MMC最末块地址倒数1026块之前的位置(图2-2)。


图2-1 普通卡启动块分配


图2-2 SDHC启动块分配

 

笔者此处暂时只考虑IROM SD/MMC启动,代码从SD/MMC搬移的情况。对于IROM启动,内部固化代码已进行一定的初始化,启动设备的一些信息已经记录在特定的内部RAM位置(图2-3),并且用户代码可以直接调用固化代码相关函数实现代码从SD/MMC读出到RAM,只要设置相应的参数,并跳转到0x40004008保存的函数地址处(图2-4),即可调用IROM内部固化代码。


图2-3 IROM SD/MMC启动时的全局变量

 

图2-4 SD/MMC块拷贝函数

 

SD/MMC的代码搬移函数实现如下:

; SD/MMCDevice Boot Block Assignment

eFuseBlockSize          EQU     1

SdReservedBlockSize     EQU     1

BL1BlockSize            EQU     16

SdBL1BlockStart     EQU     SdReservedBlockSize+ \

                        eFuseBlockSize +BL1BlockSize                 

globalBlockSizeHide     EQU     0x40003FFC

CopyMovitoMem           EQU     0x40004008

 

                EXPORT  __CodeSize__

                EXPORT  __CodeAddr__

; 引出代码搬移函数,以供启动代码调用

                EXPORT  CopyCodeToRAM

; 引入链接器产生符号,以确定代码运行位置,编译生成的大小

                IMPORT  ||Image$$ER_ROM0$$Base||

                IMPORT  ||Load$$ER_ROM0$$Length||  

                IMPORT  ||Load$$ER_ROM1$$Length||  

                IMPORT  ||Load$$RW_RAM$$RW$$Length||

 

; 链接器产生代码链接运行位置

__CodeAddr__    DCD     ||Image$$ER_ROM0$$Base||

; 链接器各个段需保存的代码以及需初始代的变量大小

__CodeSize__    DCD     ||Load$$ER_ROM0$$Length||+ \

                ||Load$$ER_ROM1$$Length|| + \

                ||Load$$RW_RAM$$RW$$Length||

                       

CopyCodeToRAM

        STMFD   SP!,{LR} ; 保存返回地址

MMC_SD_Boot ; 暂不对nand启动处理

; 不需要卡初始化

        LDR     R3, =0

; 拷贝sd卡代码到链接执行域内存代码处

        LDR     R2, __CodeAddr__

               

; 计算代码的大小,以block计,不足512字节的算1个block

; 代码的大小包括Code RO-data RW-data(代码需保存需初始化的RW的初始值)

; 代码保存在ROM中,应从加载域得到ROM的大小,而不是执行域,编译器可能压缩

; 代码段保存在加载域的ROM中

        LDR     R0, __CodeSize__

        LDR     R1,=0x1ff

        TST     R0,R1 ; 是否不足一个block(512Bytes)

        BEQ     %F0; 代码恰好block对齐,不用加多一个block

        ADD     R0,R0, #512               

0       LSR     R1,R0, #9 ; 得到代码的block大小

 

; 计算代码在SD/MMC卡中的block起启地址

        LDR     R4,=SdBL1BlockStart   

        LDR     R0,=globalBlockSizeHide

        LDR     R0,[R0] ; SD/MMC的总block块

        SUB     R0,R4 ; 减去保留块及BL1大小

        CMP     R1,#16 ; 代码不足8k,直接BL1处拷贝

        BLS     ParameterOK; 代码少于16个block跳转

        SUB     R0,R1 ; 再减去代码的大小,代码的block位置

 

; 调用IROM Movi拷贝函数,仅适用于IROM启动,卡访问时钟25M   

ParameterOK

        LDR     R4,=CopyMovitoMem

        LDR     R4,[R4]

        BLX     R4

        MOVS    R0,R0 ; 返回值确定函数成功还是失败

MMC_SD_Boot_Loop           

        BEQ     MMC_SD_Boot_Loop; 返回0说明拷贝失败

               

AfterCopy  

        LDMFD   SP!,{PC} ; 函数返回   

 

3. 链接文件

初始化外部RAM后,用链接文件指定对RAM的分配,笔者DDR2在Bank0,0x30000000处共64MB大小,分配前32M为代码区域,后32M为变量区域。

;*************************************************************

; ***Scatter-Loading Description File generated by uVision ***

;*************************************************************

 

LR_IROM10x30000000   {    ; load region size_region

; 启动代码8k在steppingstone中执行,启动代码相关的代码必须链接在8k

; 范围内,并且是与地址无关的,可链接到任意地址

  ER_ROM0 0x30000000 0x2000 {  ; load address = execution address

   s3c2416.o (RESET, +First)

   LowLevelInit.o

  }

  ER_ROM1 +0 0x2000000-0x2000 { 

   *(InRoot$$Sections)

   .ANY (+RO)    

  }

 

  RW_RAM 0x32000000 0x2000000  {  ; RWdata

   .ANY (+RW +ZI)

  }

}

 

4. 附录

启动代码中加入对外部内存初始化函数以及代码搬移函数的调用,即可编写实现任意大小的c功能函数了(c代码及变量使用总大小不超过外部RAM大小),编译后的可执行代码通过SdBoot工具进行格式转换,烧写进sd卡,设置从SD卡启动即可。

LowLevelInit.s,板级初始化代码实现,包括DDR2初始化函数实现,代码拷贝函数实现

s3c2416.sct,MDK链接文件,对初始化的外部内存进行链接分配。

http://pan.baidu.com/s/1o6ufy4a

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值