在介绍OK6410的DRAM存储器的初始化之前,先把OK6410的存储资源搞清楚:
这部分内容摘于:点击打开链接
ROM是只读存储器,RAM是随机存储器。
区别:
1.ROM(Read Only Memory)掉电数据不丢失,但存储读取速度慢,所以常用作存放程序,存放bootloader,存放内核,存放文件系统;
2.RAM(Random Access Memory)掉电数据丢失,但存取速度快,常用作内存 ;
3.RAM还分为SRAM静态随机存储器(不用不断刷新就可以读取数据,速度快但是造价也高)和DRAM动态随机存储器(要不断刷新才能存储读取数据,造价相对较低) ;
4.DRAM还可以分为SDRAM、DDR、DDR2等,DDR的传输速率为SDRAM的2倍,而DDR2的传输速率为DDR的2倍。一般是2440板子用的SDRAM作为内存,6410板子用DDR作为内存,210板子用DDR2作为内存 ;
5.nandflash不能运行程序,只能存储数据、动引导系统,而在 SDRAM 上执行主程序代码。
明白以上的概念以后,就可以进行DRAM存储器的初始化了(也就是DDR的初始化),这部分学习分为以下几个内容:
一、背景知识
二、DRAM控制器初始化
三、SDRAM初始化
四、代码分析
五、结果验证
一、背景知识
1.OK6410内存映射
上图即为OK6410的内存空间说明,具体说明如下图
这部分比较长,我们把需要的东西提炼出来:从NandFlash启动的时候,6410会自动将NandFlash最前面的8K区域的的代码拷贝到“Stepping Stone”(英文直译为垫脚石,命运悲惨的一块SRAM,哈哈哈),拷贝完成后,会将Stepping Stone映射到0x00000000开始的8K区域,然后开始执行这8K代码,这些代码主要完成的工作包括:初始化硬件、设置中断向量表、设置堆栈,并把NandFlash中的执行代码拷贝到SDRAM中,所以,在这段代码不做SDRAM的初始化,你只能用8K的内存。
2.原理图
上图即为DDR原理图,可以看到芯片型号,同时可以看到DDR是由俩片该DDR并联得到的
3.DDR芯片
由上图可知,存储大小为32M*16位,即64MB,所以俩片总共为32M*16*2,即128MB存储空间
由上图,可知Bank2位,Row13位,Column10位,总共25位,即可以寻址32M,俩片组合为32位,所以32M*32=128MB,可以想一下为什么有这样的设定呢?
二、DRAM控制器初始化
1.DRAM控制器介绍
DRAM控制器相当于cpu控制DRAM的一个中介(如下图)
OK6410的控制器的特性如下图:
2.初始化操作顺序
1.设置P1MENCCMD为3'b100,使控制器进入配置状态
2.写存储器时间参数,芯片配置和ID配置寄存器(这些参数的设定要从芯片手册里找)
3.等待200us让SDRAM电源时钟稳定,cpu开始工作的时候,已经稳定下来了(就是说其实不用等)
4.执行存储器初始化
5.设置P1MEMCCMD的‘Memc_cmd’为0x'b000,让DRAM进入准备状态
6.检查P1MEMSTAT的‘Controller status’变为‘2'b01’
三、SDRAM初始化
1.设置P1DIRECTCMD控制器产生'NOP'存储器命令
2.设置P1DIRECTCMD为2b'00,使控制器产生'PrechargeAll'存储器命令
3.设置P1DIRECTCMD为2b'01,使控制器产生'Autorefresh'存储器命令
4.设置P1DIRECTCMD为2b'01,使控制器产生'Autorefresh'存储器命令
5.设置P1DIRECTCMD为2b'10,使控制器产生'MRS'存储器命令
6.如果存储器类型为移动SDR SDRAM,设置P1DIRECTCMD的'Memory command'为‘2b'10’(需要这一步,可以看之前的截图,芯片名那张,上面有写Mobile-DDR SDRAM )
四、代码分析
具体内容不会独立摘出来说了,可以看注释:
#include "common.h"
#define MEMCCMD 0x7e001004
#define P1REFRESH 0x7e001010
#define P1CASLAT 0x7e001014
#define MEM_SYS_CFG 0x7e00f120
#define P1MEMCFG 0x7e00100c
#define P1T_DQSS 0x7e001018
#define P1T_MRD 0x7e00101c
#define P1T_RAS 0x7e001020
#define P1T_RC 0x7e001024
#define P1T_RCD 0x7e001028
#define P1T_RFC 0x7e00102c
#define P1T_RP 0x7e001030
#define P1T_RRD 0x7e001034
#define P1T_WR 0x7e001038
#define P1T_WTR 0x7e00103c
#define P1T_XP 0x7e001040
#define P1T_XSR 0x7e001044
#define P1T_ESR 0x7e001048
#define P1MEMCFG2 0X7e00104c
#define P1_chip_0_cfg 0x7e001200
#define P1MEMSTAT 0x7e001000
#define P1MEMCCMD 0x7e001004
#define P1DIRECTCMD 0x7e001008
#define HCLK 133000000
#define nstoclk(ns) (ns/( 1000000000/HCLK)+1)
int sdram_init( void )
{
// 1.设置P1MENCCMD为3'b100,使控制器进入配置状态
set_val( MEMCCMD, 0x4 );
// 2.刷新频率设置,可以在芯片手册里找到,refresh项
set_val( P1REFRESH, nstoclk(7800) );
// 时间参数的设定,这部分都要从芯片手册查,先在ok6410的手册查相应的 //寄存器作用,然后到存储器芯片手册找到相关的值
set_val( P1CASLAT, ( 3 << 1 ) );
set_val( P1T_DQSS, 0x1 );
set_val( P1T_MRD, 0x2 );
set_val( P1T_RAS, nstoclk(45) );
set_val( P1T_RC, nstoclk(68) );
u32 trcd = nstoclk( 23 );
set_val( P1T_RCD, trcd | (( trcd - 3 ) << 3 ) );
u32 trfc = nstoclk( 80 );
set_val( P1T_RFC, trfc | ( ( trfc-3 ) << 5 ) );
u32 trp = nstoclk( 23 );
set_val( P1T_RP, trp | ( ( trp - 3 ) << 3 ) );
set_val( P1T_RRD, nstoclk(15) );
set_val( P1T_WR, nstoclk(15) );
set_val( P1T_WTR, 0x7 );
set_val( P1T_XP, 0x2 );
set_val( P1T_XSR, nstoclk(120) );
set_val( P1T_ESR, nstoclk(120) );
// 这部分设置上边的截图有(Address configuration)
set_nbit( P1MEMCFG, 0, 3, 0x2 ); //10列
set_nbit( P1MEMCFG, 3, 3, 0x2 ); //13行
set_zero( P1MEMCFG, 6 ); // A10/AP
set_nbit( P1MEMCFG, 15, 3, 0x2 ); // Burst 4
set_nbit( P1MEMCFG2, 0, 4, 0x5 );
set_2bit( P1MEMCFG2, 6, 0x1 ); //俩片 32 bit
set_nbit( P1MEMCFG2, 8, 3, 0x3 ); // Mobile DDR SDRAM
set_2bit( P1MEMCFG2, 11, 0x1 );
set_one( P1_chip_0_cfg, 16 ); //片外内存初始化
// 4.执行存储器初始化
set_val( P1DIRECTCMD, 0xc0000 ); // NOP
set_val( P1DIRECTCMD, 0x000 ); // precharge
set_val( P1DIRECTCMD, 0x40000 );// auto refresh
set_val( P1DIRECTCMD, 0x40000 );// auto refresh
set_val( P1DIRECTCMD, 0xa0000 ); // EMRS
set_val( P1DIRECTCMD, 0x80032 ); // MRS
set_val( MEM_SYS_CFG, 0x0 );
// 5.设置P1MEMCCMD的‘Memc_cmd’为0x'b000,让DRAM进入准备状态
set_val( P1MEMCCMD, 0x000 );
// 6.检查P1MEMSTAT的‘Controller status’变为‘2'b01’
while( !(( read_val( P1MEMSTAT ) & 0x3 ) == 0x1));
}
五、结果验证
可以由反汇编代码看到,程序开始地址变为0x50000000了,如下图:
PS:关于上面问的,为什么设置Bank2位,Row13位,Column10位的解答:由原理图可以看到,地址线远远不够寻址128MB,通过设置这些东西,多次寻址,可以以更少的地址线寻址更大的范围。
最后,这部分内容涉及的寄存器特别多,没有单独摘出来讲(毕竟懒癌晚期……),以上内容仅供参考,如有错误多谢指正。