基于S3C6410的ARM11学习(十一) DDR初始化
0赞
之前的程序都是在内部的stepping stone中运行的。但是stepping stone的大小是很小的。ARM11只有8K的大小。这么小的空间,要运行大型程序肯定是不行的。所以就需要外部的存储器。ARM11的外部存储器用的是ddr。所以就要对ddr进行初始化,同时将代码拷贝到ddr中。
先看看S3C6410以nandflash的启动。在上电的时候,CPU会自动把外部的nandflash的前8k数据拷贝到内部的stepping stone中。Stepping stone的地址是从0x0c000000开始的。然后CPU将stepping stone给映射到0x00000000地址处,然后CPU跳转到0地址处执行第一条代码。
不要被图中stepping stone的64M空间迷惑,其实是没有这么大的。只有8K大小。因为这空间很小,所以就需要外部加存储器。图中也说明了,外部存储器的地址从0x50000000开始。大小总共可以有256M。
0x00000000~0x07FFFFFF对应内部的镜像区。这个区用来镜像启动代码的。因为S3C6410支持多种启动方式。当选择一种外设存储设备启动方式的时候,会将该存储设备的首地址给映射到这个区域来。这样当cpu从0地址开始运行代码,就相当于从外设存储设备的首地址运行了。
0x08000000~ 0x0bFFFFFFF对应着内部ROM,但是IROM实际只有32KB,选择从IROM启动的时候,首先运行就是这里面的程序BL0,这部分代码由三星固化。
0x0c000000~ 0x0FFFFFFF对应内部SRAM,实际就是8KB的Steppingstone。
0X10000000~0X3FFFFFFF对应主存储区,这个区域用来访问挂在外部总线上的设备,比如oneNAND,NORFLASH。这个区域内被分割成6个bank。每个bank大小是128M。数据宽度最大支持16bit。每个bank由片选xm0CS[0]~xm0CS[5]选中。
0x50000000~0x6FFFFFFF。DRAM区域。这个区分为两个区,分别占256MB。可以通过片选xm1CS[0]和xm1CS[1]来进行选择。
我使用的OK6410的ddr大小是128M。起始地址是从0x50000000开始。
S3C6410对这些存储设备的操作都是通过控制器来控制的。因为存储器驱动是相对来说比较麻烦的,要产生对应的时序,来对存储器进行读写。如果让CPU来控制产生这些时序的话,那对CPU来说是一个很大的负担,所以在芯片中有存储控制器,CPU通过对直接对存储控制器读写数据,存储控制器自动的把数据按照存储器的时序对存储器进行读写。
这样的话,CPU访问数据就简单很多了。只需要向存储控制发读写信号,地址,数据就可以了。存储控制器自动的去外部存储器取数据或者写数据。
因为外部存储器是有很多的,一个固定的存储控制器是很难兼容控制所有存储器的。所以这个存储控制器就必须是要可配置的,并且外部存储器的控制信号也要符合固定的标准,即时序要是一样的,时间间隔可以不一样。这样才能统一起来。所以这里的ddr初始化,就是去配置ddr的存储控制器。让这个存储器能控制外部的ddr存储器。
这个时候的配置,就得要看外部的ddr存储器的数据手册了。知道各个时间间隔,这样才能配置ddr存储控制器。
上图是ddr的数据手册,大小是64MB。OK6410使用的是两片,所以总共大小是128M。
下面就要来对ddr控制器进行配置。
手册中有对ddr控制器初始化有说明顺序。所以在对ddr控制器初始化的时候,就必须要按照这个规定的顺序来。
整个流程
1、 向MEMC_CMD寄存器写入3’b100,使DRAM控制器进入配置状态
2、 写存储器时序参数,芯片配置,id配置寄存器。这里就要参考数据手册了。
3、 等待200us,让DRAM稳定。因为从上电到现在已经超过这时间了,所以这个可以省略
4、 执行存储器初始化序列
5、 向MEMC_CMD寄存器写入3’b000,让DRAM控制器进入准备状态
6、 检查MEMC_STAT寄存器的存储状态为为2’b01,表示准备好。
DRAM控制器支持两种dram,一种是SDR,一种是DDR。板子用的是DDR,所以上面流程中的执行存储器初始化序列,这里就是执行DDR的初始化序列。
1、 向MEM_CMD寄存器的命令位写入2’b11,使DRAM控制器产生NOP命令
2、 向MEM_CMD寄存器的命令位写入2’b00,使DRAM控制器产生预充电命令
3、 向MEM_CMD寄存器的命令位写入2’b01,使DRAM控制器产生自动刷新命令
4、 向MEM_CMD寄存器的命令位写入2’b01,使DRAM控制器产生自动刷新命令
5、 向MEM_CMD寄存器的命令位写入2’b10,使DRAM控制器产生MRS命令,EMRS的备份地址必须要设置
6、 向MEM_CMD寄存器的命令位写入2’b10,使DRAM控制器产生MRS命令, MRS的备份地址必须要设置
手册中提供的参数有的有错误,实际要参考寄存器中说明的值。
下面就按照这个流程来进行初始化
先使用宏定义将地址都定义一下
第一步:
向MEMC_CMD寄存器写入3’b100,使DRAM控制器进入配置状态
上图是这个寄存器的说明,只有低三位有用。写入100.也就是0x4。
第二步:
写存储器时序参数,芯片配置,id配置寄存器。这里就要参考数据手册了。
这里就要查看数据手册,对寄存器进行设置了。
这个是设置刷新周期的时间。ddr刷新周期的概念可以百度。
在手册中,有下面这个说明
说明刷新周期是7.8us。寄存器设置中的值是刷新周期,不过这周期是通过时钟周期数来设置的。如果这时钟周期是100ns。那么设置的值是78,就刚好是7.8us。在之前的时钟初始化中,把这时间设置为133M了。所以需要去计算一下需要多少个时钟。但是这里,直接使用公式
7800 / ( 1000000000/133000000 ) + 1
最前面的数是要设置的时间,以ns为单位。除的部分是表示133M时钟对应多少ns。这样一算下来,就可以得到需要多少个时钟可以得到7.8us。后面设置时间,也是通过这个方式,这样我们就不用去计算了。
这个是设置CAS参数的。就设置3:1位。最低位这里为0.
在数据手册中也有说明,这个参数可以是2,也可以是3.这里就设置为3。
这个是设置DQSS参数
也是查阅数据手册,在时序那一部分。
数据,这里就看前两列和最后一列,第一列是最小,第二列是最大。最后一个是单位。这里是以时钟为单位,最小是0.75个时钟。这里就可以设置为1.
这个是设置MRD参数。
手册中,参数是2个时钟。
这里,参数就设置为2.
设置RAS参数。
手册中
参数是45ns。所以就要用之前那个式子。
以下就不说明了。一直把各个时序参数设置完就可以了。
配置完P1T_ESR寄存后,时序就配置完了。接着就对芯片进行配置
对这个寄存器的第16位,第4位,第1位和第0位置1
配置读写突发模式是4.
后面6位是配置ddr的行列数的。这个从ddr数据手册中得到,行是13,列是10。所以后面6位值就是6’b010010。刚好就是0x12。
这个配置的位就多了。主要关心前面几个的配置。
这里,配置读延迟[12:11]是01,手册中也说这个是mobile DDR SDRAM使用。板子用的就是mobile DDR。存储器类型[10:8]是011,表示外部存储器是mobile DDR。存储器宽度[7:6]设置为01,表示是32位。一个DDR是16位,但是使用两片拼接的,所以宽度就是32位。后面几位的设置功能目前还不知道有什么用。
这个是对芯片的片选设置的。因DRAM可以有两个,所以有两个寄存器分别对应。板子只用了1个。就是chip0.所以这里就只设置第一个寄存器。
当地址在0x50000000的时候,DDR的片选是应该要有效的,这样DRAM控制器才能访问到DDR。寄存器的[15:8]就是这只这个地址的。说明是这个值和AXI address[31:24]位比较决定芯片是否选中。所以这里的8位值应该为0x50。
16位是存储器的选址方式。手册中有说明,bank-row-column。所以这一位要为1.
对于低8位。DDR的大小是128M。所以地址空间是0x5000_0000~0x57FF_FFFF。而低8位是屏蔽地址的。这里是设置为0XF8.就是将0x5000_0000~0x57FF_FFFF意外的地址屏蔽芯片使能。
所以这个参数的值就是0x150f8。
这里,参数都设置为0.选择最小延迟。
以上就把ddr控制器时序,芯片配置给配置好了。下面就是要进入存储器初始化序列。
第三步:存储器初始化序列
这个就按照序列来。
首先发出nop命令。
第[19:18]位写入11。那么写入的值就是0x000c0000。
发出预充电命令
发出两次自动刷新命令
发出MRS命令,EMRS的bank地址必须要设定
发出MRS命令,MRS的bank地址必须要设定
芯片的后面18位设置BANK地址和存储地址。
首先发第一个MRS命令,要设定EMRS 的BANK地址。这个也查手册
Bank地址是10,后面的地址都可以设置为0.
所以写入的参数就是0x000a0000.
发第一个MRS命令,要设定BANK地址。这个也查手册
Bank地址是00.因为之前设置CAS是3,突发模式使4.所以后面的地址为0x32。
所以整个值就是0x00080032。
这样,就完成了存储器的序列配置了。
第三步:向MEMC_CMD寄存器写入3’b000,让DRAM控制器进入准备状态
就往寄存器中写0就可以了。
第四步:检查MEMC_STAT寄存器的存储状态为为2’b01,表示准备好。
就是将这个寄存器的值给读出来,判断最后两位是否是01,不是的话就继续读,然后判断。是的话,说明DDR初始化完成。程序返回即可。
最后,在补充一个东西。在DDR初始化代码的前面,要加入如下代码:
这个寄存器是设置DDR的数据管脚作为数据线的。这个寄存器的第7位是设置DDR的数据线的。默认为是1,要改成0。这样DDR的数据管脚才作为DDR的数据线。
通过以上这些步骤,就完成了DDR控制器的初始化了。这样,在后面就可以对这DDR进行使用了。可以往里面写值,可以读值。
在DDR初始化中,要紧密的结合数据手册,因为要设置很多时序的参数以及芯片的一些参数。
对比STM32:
STM32中没有内存初始化这一步,因为STM32将内存做进芯片中去了。这样STM32就可以直接进行访问内存,而不用再进行初始化这一步。就和51单片机一样,内部有256K大小的内存。只不过STM32的内存比较大而已。
不过STM32也是外挂存储器的。因为STM32有FSMC(静态存储器控制器),能控制4种接口器件。
STM32手册中,也说明了FSMC的地址映射。分为4个块。如果要使用外部存储器的话,就可以使用FSMC,不过使用之前,肯定是要对FSMC进行一些配置的。和S3C6410的存储器配置一样的。
查阅资料,STM32是不支持DRAM的,支持PSRAM的。