uboot-step 8 内存初始化
内存的初始化相比前面的那些步骤略微的麻烦了些,为了能够比较清楚地说明下内存的初始化,(ps:虽然我也不是特别的清楚),首先简单的说下这篇文章的大概流程:
- 1.对内存相关的一些简单介绍
- 2.s3c6410的内存控制器的初始化流程
- 3.对照datasheet说明实现代码的工作流程
关于内存
内存的分类
这里所说的内存仅指RAM(RandomAccessMemory,随机存取存储器),它具有可以随机读写访问的特点,并且存储的数据在断电后会自动消失,现在所用的主要有以下两种类型:
- SRAM :静态RAM,不需要一直刷新,双稳态电路形式存储数据,集成度较低,体积大,造价高,容量小,通常用作Cache
- DRAM : 动态RAM, 需要不断刷新,通过MOS电容存储电荷来存储数据,结构简单,集成度高,成本低,便于大容量制造,常见的几种DRAM:
- SDRAM :Synchronous Dynamic RAM ,同步动态随机存储器,工作频率与系统的频率相同,逐渐被DDR SDRAM所取代
- DDR SDRAM : Double Data Rate SDRAM 双倍同步动态随机存储器,习惯上称之为DDR,相比SDRAM,速度和容量上有所提升,本质上虽然没有提高时钟频率,但是可以在时钟的上升沿和下降沿同时传输数据,因此其速度是标准SDRAM 的两倍
- mobile DDR : 相比pC端的DDR,电压更低,封装更小
内存的结构
内存的内部结构就像表格一样,每一张表格称为一个逻辑BANK,L_BANK,每张表格有若干行地址线和若干列地址线,寻址的时候,首先去找到数据在哪一个bank里面,然后在分别确定行地址和列地址,这样就可以找到需要的数据所在的单元了,由于成本,制造工艺的原因,无法将整个地址空间做到一个bank里面,现在一般都将整个地址空间分为4个L-bank,四个L-bank具有公用的行地址和列地址
内存的容量
一般内存容量的表示方法如上图所示,64M表示有多少个存储单元,32表示每个存储单位的容量为多少bit,可以将其换算为Byte,如上图所示的内存容量为64M32bit=644Byte=256MByte。
内存引脚说明
如上图所示,RAM的引脚主要有以下几种:
- 电源引脚: 数据电源VDDQ,VSSQ,电源VDD,VSS
- 时钟相关引脚: CK 上升沿和下降沿两个,CKE时钟使能引脚
- 片选引脚: CS 低有效
- 列,行地址,写使能引脚: 列,行地址选中使能,写使能引脚,低有效
- DMx引脚:输入数据屏蔽引脚
- BA0,1:bank选择引脚
- A[n:0]:地址线
- DQ:数据总线
- DQSx:输出数据闸门
下图是S3c6410 内存端口引脚说明,可以看出与上面的内存芯片的datasheet中的引脚说明正好一一对应
硬件原理图如下图所示:
更多内存相关的知识请找度娘或参考下面的链接
终极图解内存上
终极图解内存下
s3c6410 DRAMC初始化流程
S3c6410的DRAM 控制器支持SDR SDRAM,mobile SDR SDRAM ,DDR SDRAM,和mobile DDR SDRAM多种类型,根据芯片手册上的描述:
DRAM 控制器支持多达两片同样类型的最大256M的内存,所有的内存片都共享同样的引脚,除了时钟使能和片选信号,当上电复位以后,软件必须初始化DRAM控制器和连接到DRAM控制器上的内存,初始化流程需要参考SDRAM datasheet,可供参考的初始化流程如下图,第一张图是DRAM 控制器的初始化流程,第二张图是内存的初始化流程:
由于所采用的开发板上的内存芯片为Mobile DDR SDRAM,因此这里只贴出了这种类型的内存初始化流程,先来看下DRAM控制器的初始化流程:
- 1.编程memc_cmd 写入'3b100',使DRAM控制器进入Config (配置)状态
- 2.向相关的寄存器中写入内存时序参数,内存片的配置,id配置相关
- 3.等待200us,使内存上电和时钟稳定,但是其实当cpu开始工作的时候,内存电源和时钟都已经稳定了
- 4.执行内存初始化流程,即下面的流程
- 5.编程memc_cmd写入'3b000',使DRAM控制器进入’Ready‘(准备)状态
- 6.检查寄存器memc_stat 内存状态域直到内存状态为’2b01‘,这意味着DRAM控制器已经准备好了
看完了DRAM控制器的初始化流程,再来看下内存初始化的流程:
- 1.编程mem_cmd 在direct_cmd 写入’2b11‘,使DRAM控制器发送'NOP'命令到内存
- 2.编程mem_cmd 在direct_cmd 写入’2b00‘,使内存控制器发送’Prechargeall'命令到内存
- 3.编程mem_cmd 在direct_cmd 写入‘2b11’,使DRAM控制器发送'Autorefresh'命令到内存
- 4.编程mem_cmd 在direct_cmd 写入‘2b11’,使DRAM控制器发送'Autorefresh'命令到内存
- 5.编程mem_cmd '2b10'在direct_cmd,使DRAM 控制器发送MRS内存命令-EMRS的bank地址一定要设定
- 6.编程mem_cmd '2b10'在direct_cmd,使DRAM 控制器发送MRS内存命令-MRS的bank地址一定要设定
接下来按照上面的流程来进行内存的初始化
1,Dram控制器进入配置状态
按照上图的说明,向memc_cmd寄存器中写入100即可
2,内存时序参数,id等相关配置
-
Refresh period 配置
内存刷新周期设置,根据下图内存芯片手册的值,由s3c6410的时钟体系配置,内存使用的为AHB总线时钟,为133Mhz,7.8us对应寄存器的值应为7800*(1000 000 000/133 000 000),此值不是一个整数,最后在加上1,即为寄存器中的值
-
CAS latency
如下图,内存芯片手册上的数字为2或3,这里设为3
-
t_DQSS
如下图,内存芯片手册上的为0.75到1.25之间,这里设为1
-
t_MRD
如下图,内存芯片手册上的为2
-
t_RAS
如下图,内存芯片手册上的为40 到70000ns之间,我们大概选个时间就好
-
t_RC
-
t_RCD
-
t_RFC
-
t_RP
-
t_RRD
-
t_WR
-
t_WTR
-
t_XP
-
t_XSR
-
t_ESR
上面这些时序参数配置大部分在内存芯片手册上都可以找到,只要相应的填进去就好,实在没有的可以参考其它人的代码来写:
-
MEMCFG 寄存器 行列地址,burst length,内存片数量
这里内存芯片只有一片,芯片手册上相关数据如下:
-
MEMCFG2 寄存器 配置,内存类型,数据宽度等信息,这里32bit,mobileDDR内存
-
chipcfg 配置AXI 内存地址
-
usrcfg 用户寄存器
3.这样就完成了内存参数的设置,下面进行内存初始化流程
- 1、发送NOP命令
- 2、发送Prechargeall命令
- 3、发送Autofresh
- 4、发送Autofresh
- 5、发送mrs命令,设置mrs寄存器
-
6、发送mrs命令,设置emrs寄存器
对于5,6点的内存寄存器的设置需要根据内存芯片手册进行相关配置,如下图:
4.完成了内存的初始化流程之后,就剩下最后的ready了
- 发送ready到memc_cmd 命令000 使DRAM控制器进入ready状态
-
检查mem_stat寄存器,检查DRAM控制器是否已经进入ready状态
代码实现如下:
.text
.global mem_init
mem_init:
ldr r0, =0x7e00f120 @设置xm1data引脚为dramc 数据引脚
mov r1, #0x8
str r1, [r0]
ldr r0, =0x7e001004 @内存控制命令寄存器
mov r1, #0x4 @根据手册得知需要先进入配置模式
str r1, [r0]
ldr r0, =0x7e001010 @刷新寄存器地址
ldr r1, =( ( 7800 / ( 1000000000/133000000 ) + 1 ) ) @设置刷新时间
str r1, [r0]
ldr r0, =0x7e001014 @CAS latency寄存器
mov r1, #(3 << 1)
str r1, [r0]
ldr r0, =0x7e001018 @t_DQSS寄存器
mov r1, #0x1
str r1, [r0]
ldr r0, =0x7e00101c @T_MRD寄存器
mov r1, #0x2
str r1, [r0]
ldr r0, =0x7e001020 @t_RAS寄存器
ldr r1, =( ( 45 / ( 1000000000 / 133000000 ) + 1 ) )
str r1, [r0]
ldr r0, =0x7e001024 @t_RC寄存器
ldr r1, =( ( 68 / ( 1000000000 / 133000000 ) + 1 ) )
str r1, [r0]
ldr r0, =0x7e001028 @t_RCD寄存器
ldr r1, =( ( 23 / ( 1000000000 / 133000000 ) + 1 ) )
str r1, [r0]
ldr r0, =0x7e00102c @t_RFC寄存器
ldr r1, =( ( 80 / ( 1000000000 / 133000000 ) + 1 ) )
str r1, [r0]
ldr r0, =0x7e001030 @t_RP寄存器
ldr r1, =( ( 23 / ( 1000000000 / 133000000 ) + 1 ) )
str r1, [r0]
ldr r0, =0x7e001034 @t_rrd寄存器
ldr r1, =( ( 15 / ( 1000000000 / 133000000 ) + 1 ) )
str r1, [r0]
ldr r0, =0x7e001038 @t_wr寄存器
ldr r1, =( ( 15 / ( 1000000000 / 133000000 ) + 1 ) )
@ ldr r2, [r0]
str r1, [r0]
ldr r0, =0x7e00103c @t_wtr寄存器
mov r1, #0x07
str r1, [r0]
ldr r0, =0x7e001040 @t_xp寄存器
mov r1, #0x02
str r1, [r0]
ldr r0, =0x7e001044 @t_xsr寄存器
ldr r1, =( ( 120 / ( 1000000000 / 133000000 ) + 1 ) )
str r1, [r0]
ldr r0, =0x7e001048 @t_esr寄存器
ldr r1, =( ( 120 / ( 1000000000 / 133000000 ) + 1 ) )
str r1, [r0]
ldr r0, =0x7e00100c @内存控制配置寄存器
ldr r1, =0x00010012 @配置控制器
str r1, [r0]
ldr r0, =0x7e00104c @32位DRAM配置控制寄存器
ldr r1, =0x0b45
str r1, [r0]
ldr r0, =0x7e001200 @片选寄存器
ldr r1, =0x150f8
str r1, [r0]
ldr r0, =0x7e001304 @用户配置寄存器
mov r1, #0x0
str r1, [r0]
ldr r0, =0x7e001008
ldr r1, =0x000c0000 @nop
str r1, [r0]
ldr r1, =0x00000000 @Prechargeall
str r1, [r0]
ldr r1, =0x00040000 @Autofresh
str r1, [r0]
ldr r1, =0x000a0000 @emrs 寄存器设置
str r1, [r0]
ldr r1, =0x00080032 @mrs 设置
str r1, [r0]
ldr r0, =0x7e001004 @发送ready命令
mov r1, #0x0
str r1, [r0]
check_dmc1_ready: @检测ready命令是否已经完成
ldr r0, =0x7e001000
ldr r1, [r0]
mov r2, #0x3
and r1, r1, r2
cmp r1, #0x1
bne check_dmc1_ready
nop
mov pc, lr
此去经年
zhaiyk@sina.cn
August 3, 2016