内存初始化:
loongson3_ddr2_config.S文件
大致过程分为个阶段:
- 检测四个DIMM插槽的mc0/1,将信息存在s1中(PROBE_NODE_DIMM)。
- 使用PROBE_NODE_DIMM,根据上一步获取的信息检测对应内存大小
- 配置内存控制器(mc_init)
- 配置L2 X-bar,s1中的MC*_ONLY位决定是否使用这个MC,然后根据s1中的MC*_MEMSIZE位决定内存大小以及如何配置L2 X-bar窗口。
注:目前仅使用一个内存控制器时,支持内存大小:512M、1G、2G、3G、4G;当使用mc0和mc1时,每个控制器只支持1G、2G或4G内存大小。
- 获取内存信息保存在s1中
叶子函数PROBE_NODE_DIMM
input:s1[31:16] ----- MC0的slot id信息 s1 = 0x00000000__ff100004
output:s1[31: 8] ------MC0 DIMM info s1 = 0x00000000__f0a31004
PROBE_NODE_DIMM代码流程:
move t8, ra
move t1, s1 //t1 = 0x00000000__ff100004
bal i2cinit //初始化i2c
nop
GET_MC0_ONLY //通过s1(ff100004)状态
bnez a1, 11f
nop
11:
//detect MC0 if not define MC1_ONLY
GET_MC1_ONLY
bnez a1, 12f
nop
//do auto probe DIMM
PRINTSTR("\r\nProbing DDR MC0 SLOT: ");
PRINTSTR("\r\nProbe MC0 slot 0.");
dli a1, 0xff
and s1, s1, a1 s1 = 0x4
GET_MC0_SLOT0_ID 获取slot0的id号
dli a0, 0x8
bgeu a1, a0, 1f //invalidate device id --- a1=0
nop
dsll a1, a1, 1
ori a0, a1, 0xa1
bal PROBE_DIMM;
nop;
/* 读取mc0的slot0后,获得slot0的dimm信息记录在s1中,s1 = 0x00000010__f0a30004 */
1:
//store slot 0 DIMM infor in t3
move t3, s1
PRINTSTR("\r\nProbe MC0 slot 1.");
dli a1, 0xff
and s1, s1, a1
GET_MC0_SLOT1_ID
dli a0, 0x8
bgeu a1, a0, 1f //invalidate device id
nop
dsll a1, a1, 1
ori a0, a1, 0xa1
bal PROBE_DIMM;
nop;
/* 读取mc0的slot1后,没有dimm,s1 = 0x00000000__00000004 */
1:
//store slot 1 DIMM infor in t4
move t4, s1
//compare the two slot DIMM infor and merge the CS_MAP and MC0_MEMSIZE if necessary
move s1, t3
GET_SDRAM_TYPE
beqz a1, 1f //a1 = 0x3, 不跳转
nop
move s1, t4
GET_SDRAM_TYPE
beqz a1, 2f //a0 = 0.
nop
2:
//no DIMM in slot 1 and slot 0 has DIMM
move s1, t3
b 4f
nop
4: //move DIMM_MEMSIZE to MC0_MEMSIZE
GET_DIMM_MEMSIZE //a1 = 0x10
dsll a1, a1, MC0_MEMSIZE_OFFSET //MC0_MEMSIZE_OFFSET = 8
dli a0, MC_MEMSIZE_MASK //MC_MEMSIZE_MASK = 0x7f
dsll a0, a0, MC0_MEMSIZE_OFFSET
not a0, a0
and s1, s1, a0
or s1, s1, a1 //s1 = 0x00000010__f0a31004
b 3f
nop
3:
//merge MC1 and MC0 DIMM infor to s1
dli a1, 0xffffff0000000000 //t0 = 0x00000000__00000004
and t0, t0, a1
dli a1, 0xffffffff
and s1, s1, a1
or s1, s1, t0 //s1 = 0x00000000__f0a31004
12:
move ra, t8
jr ra //返回
nop
在ls2k/ddr_dir/detect_node_dimm_all.S文件中,首先初始化i2c,调用叶子函数PROBE_DIMM通过i2c获取内存条的spd信息。0x8为DD2,0xB为DDR3。
s1[30]标识SDRAM的type。
s1[29]标识SDRAM的DIMM WIDTH。s1[28]标识SDRAM的DIMM WIDTH。
//s1[2.3]位标识cpu的SEL,s1[0.1]位标识cpu的NODE ID。
#define SDRAM_TYPE_OFFSET 30
#define DIMM_ECC_OFFSET 29
#define DIMM_TYPE_OFFSET 28
#define DIMM_WIDTH_OFFSET 27
#define ROW_SIZE_OFFSET 24
#define EIGHT_BANK_OFFSET 23
#define ADDR_MIRROR_OFFSET 22
#define COL_SIZE_OFFSET 20
#define MC_CS_MAP_OFFSET 16
#define MC1_CS_MAP_OFFSET 48
#define SDRAM_WIDTH_OFFSET 15
#define MC1_SDRAM_WIDTH_OFFSET 47
#define MC_CS_MAP_MASK (0xf)
#define MC1_MEMSIZE_OFFSET 40
#define MC0_MEMSIZE_OFFSET 8
#define MC_MEMSIZE_MASK (0x7f)
#define DIMM_MEMSIZE_OFFSET 32
#define DIMM_MEMSIZE_MASK (0x7f)
在PROBE_DIMM函数中,通过i2c总线获取上述相关信息,保存到s1相应的位。
- 校验s1保存的内存信息是否正常
代码流程:
GET_MC_SEL_BITS
dli a2, 3
bne a1, a2, 1f //获取cpu SEL信息
nop
1:
PRINTSTR("\r\ncheck NODE mem size\r\n"); //获取内存控制
GET_MC0_ONLY
bnez a1, 1f
nop
GET_MC1_ONLY
bnez a1, 2f
nop
1: //MC0_ONLY
GET_MC0_MEMSIZE
b 5f
nop
5:
beqz a1, 89f
nop
dli t5, 0x10
bgt a1, t5, 89f
nop
- 配置内存控制器
在mc_init函数(Targets/LS2K/ls2k/ddr_dir/ls3A8_ddr_config.S文件)中,使能内存控制器的配置空间(1MB @0x0ff0,0000)。获取SDRAM类型,按照DDR3类型配置。
获取RDIMM参数地址。跳转到ddr2_config进行内存参数的设置。
//t8保存了内存控制器(MC0)的配置空间的基地址。
//a2记录了ddr3_RDIMM_reg_data参数数据地址。
在ddr2_config函数中,将a2中记录的RDIMM寄存器参数拷贝到MC0的配置空间。然后设置tREFI(一般情况下,内存控制器会周期性的发送 AR(Auto-Refresh Command),每两个 AR 直接的时间间隔定义为 tREFI = 64ms / 8192 = 7.8 us。ls2k这里设置12us)、设置tRFC(SDRAM 完成一次刷新操作所需要的时间定义为 tRFC, 这个时间会随着 SDRAM Row 的数量的增加而变大)。
set_tREFI:
dli t1, DDR_FREQ
divu t1, t1, 10
dli a2, 78
dmulou a2, a2, t1
dsrl a2, a2, 8
// daddu a2, a2, 1
dli t1, 0x1c8
or t1, t1, t8
sb a2, 0x3(t1)
=====================================
dli t1, DDR_FREQ
dmulou t5, t5, t1
divu t5, t5, 100
dli t1, 0x1c8
or t1, t1, t8
sb t5, 0x2(t1) //tRFC = 140
未完。。。待续