DDR

以下DDR开发指南仅针对Rockchip平台。

第1章 DDR 常见问题解答-FAQ

1.1 如何看懂 DDR 打印信息

DDR 打印信息包括 loader 中的打印和 kernel 中的打印。 loader 中打印的解析如下:

DDR Version 1.05 20170712// DDR 初始化代码的版本信息,⽤于核对版本。从这⾏开始,已经进⼊DDR初始化代码
In
SRX // 有SRX,说明是热重启;没有SRX,说明是冷开机。有的芯⽚没加这个功能,⼀直都是没有SRX的
Channel a: DDR3 400MHz // 下⾯都是DDR容量的详细信息,具体解析可以看"如何查看DDR的容量"章节
Bus Width=32 Col=10 Bank=8 Row=15 CS=1 Die Bus-Width=16 Size=1024MB
Channel b: DDR3 400MHz
Bus Width=32 Col=10 Bank=8 Row=15 CS=1 Die Bus-Width=16 Size=1024MB
Memory OK // DDR⾃测的结果,第⼀个Memroy OK是Channel a的⾃测结果
Memory OK // 是Channel b的⾃测结果。有报错,说明焊接有问题;没报错,说明当前⾃测没问题,整个DDR是否能稳定⼯作,还得看后续阶段的运⾏结果。
OUT // 这⾏打印之后,就退出了DDR初始化代码

kernel 中打印的解析如下:

[ 0.528564] DDR DEBUG: version 1.00 20150126 //版本信息
[ 0.528690] DDR DEBUG: Channel a: //DDR容量详细信息
[ 0.528701] DDR DEBUG: DDR3 Device
[ 0.528716] DDR DEBUG: Bus Width=32 Col=10 Bank=8 Row=15 CS=1 Total
Capability=1024MB
[ 0.528727] DDR DEBUG: Channel b:
[ 0.528736] DDR DEBUG: DDR3 Device
[ 0.528750] DDR DEBUG: Bus Width=32 Col=10 Bank=8 Row=15 CS=1 Total
Capability=1024MB 
// 后⾯还有其他DDR打印信息,可以不⽤管,是写DDR驱动⼈员⽤的
// DDR DEBUG的字样打印结束,就是kernel中DDR初始化完成了

kernel 3.10 还会有如下打印,是 DDR 变频模块的输出信息,如下:

[ 1.473637] ddrfreq: verion 1.2 20140526 //DDR变频模块版本
[ 1.473653] ddrfreq: normal 396MHz video_1080p 240MHz video_4k 396MHz dualview 396MHz idle 0MHz suspend 200MHz reboot 396MHz   // DDR各个场景对应的频率,是从dts表格中读取出来的
[ 1.473661] ddrfreq: auto-freq=1 //负载变频功能是否开启,1表⽰开启,0表⽰关闭
[ 1.473667] ddrfreq: auto-freq-table[0] 240MHz //负载变频的频率表格
[ 1.473673] ddrfreq: auto-freq-table[1] 324MHz
[ 1.473678] ddrfreq: auto-freq-table[2] 396MHz
[ 1.473683] ddrfreq: auto-freq-table[3] 528MHz
//如果在这段打印过程中系统卡死了,很可能是DDR变频有问题

kernel 3.10后的版本,kernel中不再有DDR容量信息的打印了。

1.2 如何将RK给的 DDR bin 合成成完整可用的 loader

参考如下步骤可以将RK给的 DDR bin 合成成完整可用的 loader。

  1. 将 DDR bin 放在 U-Boot ⼯程的 rk\rkbin\bin\对应⽬录下

  2. 删除原有的 DDR bin ⽂件

  3. 将新的 DDR bin 改名为删除掉的名字

  4. 编译 U-Boot(详⻅《Rockchip-Developer-Guide-UBoot-nextdev.pdf》),就会⽣成对应的 loader ⽂件

芯片平台 路径 Note
RK1808 rk/rvbin/bin/rk1x/rk1808_ddr_XXXMHz_vX.XX.bin
RK3036 rk/rvbin/bin/rk30/rk3036_ddr_XXXMHz_vX.XX.bin 1
RK3126/B/C rk/rvbin/bin/rk31/rk3126_ddr3_300MHz_vX.XX.bin
RK3128 rk/rvbin/bin/rk31/rk3128_ddr_300MHz_vX.XX.bin
PX3SE rk/rvbin/bin/rk31/px3se_ddr_300MHz_vX.XX_uartX.bin
RK3288 rk/rvbin/bin/rk32/rk3288_ddr_400MHz_vX.XX.bin
RK322x系列 rk/rvbin/bin/rk32/rk322x_ddr_XXXMHz_vX.XX.bin
RK3308 rk/rvbin/bin/rk33/rk3308_ddr_XXXMHz_uartX_mX_vX.XX.bin
PX30 rk/rvbin/bin/rk33/px30_ddr_333MHz_vX.XX.bin
RK3326 rk/rvbin/bin/rk33/rk3326_ddr_333MHz_vX.XX.bin
RK3368 rk/rvbin/bin/rk33/rk3368_ddr_600MHz_vX.XX.bin
RK322xh rk/rvbin/bin/rk33/rk322xh_ddr_333MHz_vX.XX.bin
RK3328 rk/rvbin/bin/rk33/rk3328_ddr_XXXMHz_vX.XX.bin
RK3399 rk/rvbin/bin/rk33/rk3399_ddr_XXXMHz_vX.XX.bin 2
RK3399 PRO rk/rvbin/bin/rk33/rk3399pro_ddr_XXXMHz_vX.XX.bin
RK3562 rk/rvbin/bin/rk35/rk3562_ddr_XXXMHz_vX.XX.bin
RK3566 rk/rvbin/bin/rk35/rk3566_ddr_XXXMHz_vX.XX.bin
RK3568 rk/rvbin/bin/rk35/rk3588_ddr_lp4_XXXXMHz_lp5_XXXXMHz_vX.XX.bin
RK3528 rk/rvbin/bin/rk35/rk3528_ddr_XXXMHz_vX.XX.bin 3
RV1126 rk/rvbin/bin/rv11/rv1126_ddr_XXXMHz_vX.XX.bin
RV1106 rk/rvbin/bin/rv11/rv1106_ddr_XXXMHz_vX.XX.bin
RV1108 rk/rvbin/bin/rv11/rv1108_ddr_vX.XX.bin
Note 1:具体⽤哪个频率由 rk\rkbin\RKBOOT\RK3036_ECHOMINIALL.ini 或 RK3036MINIALL.ini ⽂件中指定。其中 RK3036_ECHOMINIALL.ini 是 ECHO 产品使⽤的,其他产品都使⽤RK3036MINIALL.ini。⾄于哪个是 ECHO 产品,请联系产品部的⼈。
Note 2:具体⽤哪个频率由 rk\rkbin\RKBOOT\RK3399MINIALL.ini ⽂件中指定
Note 3:⽤于PCB⾮2层、DDR⾮4BIT的RK3528硬件设计。2层PCB或4BIT的DDR,参考后⾯的“DDR bin特殊说明”
Note 4:新平台,都是按照类似的命名规则放在rk\rkbin\bin\⽬录下,可以⾃⾏查找

1.3 DDR bin特殊说明

对rk\rkbin\bin下的DDR bin⽂件,有些存在特殊的地⽅,需要说明 **RK3399 由于RK3399不同DDR类型⽀持的频率不同,而DDR bin名字没有按类型来说明频率,导致⼀些客⼾出现 混乱,下⾯为统⼀说明。 image.png

RV1126/RK3566/RK3568/RK3588/RK3528/RK3562等DDR bin包含多频率点的平台 这种平台的DDR bin⽂件中,都包含有4个DDR频率,其中最⾼的那个频率点在DDR bin名称中体现,如 rv1126_ddr_924MHz_v1.05.bin ,最后⼀个频点是924M。 kernel中只能使⽤这4个频率。如果想修改4个频率,⻅"修改DDR bin⽂件"章节 ⼀般默认4个频率如下, image.png

从loader的串口log中查看,如下:

DDR ... v1.14
LPDDR4X, 2112MHz
channel[0] BW=16 Col=10 Bk=8 CS0 Row=16/0 CS1 Row=16/0 CS=2 Die BW=16 Size=2048MB
channel[1] BW=16 Col=10 Bk=8 CS0 Row=16/0 CS1 Row=16/0 CS=2 Die BW=16 Size=2048MB
channel[2] BW=16 Col=10 Bk=8 CS0 Row=16/0 CS1 Row=16/0 CS=2 Die BW=16 Size=2048MB
channel[3] BW=16 Col=10 Bk=8 CS0 Row=16/0 CS1 Row=16/0 CS=2 Die BW=16 Size=2048MB
...
change to F1: 528MHz /* 4个DDR频率 */
change to F2: 1068MHz
change to F3: 1560MHz
change to F0: 2112MHz
out

也可以通过 rkbin/tools/ddrbin_tool ⼯具,读取出当前的4个频率,如:

./ddrbin_tool px30 -g gen_param.txt px30_ddr_333MHz_v1.15.bin

具体使⽤⻅ddrbin_tool使⽤说明

eyescan后缀的bin 以eyescan后缀的DDR bin,⽤于合成能获取DDR信号⼆维眼图的Loader,如:

rk\rkbin\bin\rk35\rk3566_ddr_1056MHz_eyescan_v1.16.bin
rk\rkbin\bin\rk35\rk3588_ddr_lp4_2112MHz_lp5_2736MHz_eyescan_v1.11.bin
rk\rkbin\bin\rk35\rk3562_ddr_1332MHz_eyescan_v1.04.bin
rk\rkbin\bin\rk35\rk3568_ddr_1560MHz_eyescan_v1.16.bin

ultra后缀的bin ⽤于电⼦书的超级待机,搭配电⼦书的硬件使⽤。

rk\rkbin\bin\rk35\rk3566_ddr_XXXMHz_ultra_v1.10.bin
rk\rkbin\bin\rk35\rk3562_ddr_XXXMHz_ultra_v1.05.bin

tb结尾 ⽤于快速开机,使⽤时必须选择对应DDR类型的bin。

rk\rkbin\bin\rv11\rv1126_tpl_XXXMHz_ddr4_tb_v1.08.bin
rk\rkbin\bin\rv11\rv1126_tpl_XXXMHz_ddr3_tb_v1.08.bin
rk\rkbin\bin\rv11\rv1126_tpl_XXXMHz_lp3_tb_v1.08.bin
rk\rkbin\bin\rv11\rv1126_tpl_XXXMHz_lp4_tb_v1.08.bin

⽤于RV1106快速开机,⽤于DDR3

rk\rkbin\bin\rv11\rv1106_ddr_924MHz_tb_v1.13.bin

3528搭配PCB使⽤ ⽤于4BIT的DDR设计

rk\rkbin\bin\rk35\rk3528_ddr_1056MHz_4BIT_PCB_v1.07.bin

⽤于2层的PCB设计

rk\rkbin\bin\rk35\rk3528_ddr_1056MHz_2L_PCB_v1.07.bin

1.4 修改DDR bin文件

上⾯章节提到的DDR bin⽂件,⽤于开机时,初始化DDR。通过RK提供的⼯具,可以修改DDR bin⽂件,达到修改DDR初始化参数、DDR频率、关闭串口、串口波特率等功能。

rk_ddrBin_tool_windows 优先推荐使⽤rk_ddrBin_tool_windows,其是有界⾯的⼯具,容易上⼿。 下载地址: ✔ ⼯具的Help下,有使⽤指南。

rkbin/tools/ddrbin_tool 此⼯具是命令⾏模式,在rkbin⼯程下, rkbin/tools/ddrbin_tool 说明⽂档是 rkbin/tools/ddrbin_tool_user_guide.txt

修改DDR频率 对那些只⽀持4个DDR频率的SOC,如RV1109/RV1126/RK3566/RK3568/RK3588/RK3528/RK3562等,可以修改如下参数:

序号

参数名称

配置值

有效值

Unit

备注

1

lp4_freq

2112

300-2133

MHz

DDR初始化频率(FSP_0频率)

2

lp4_f1_freq

528

300-2133

MHz

DDR FSP_1频率,用于变频

3

lp4_f2_freq

1068

300-2133

MHz

DDR FSP_2频率,用于变频

3

lp4_f3_freq

1560

300-2133

MHz

DDR FSP_3频率,用于变频

改DEBUG串口号及DEBUG串口波特率

参数名称 配置值 有效值 Unit 备注
uart id 0 - - 串口ID,0xf=关闭串口打印
uart iomux 0 - - 串口IOMUX
uart bandrate 1500000 - bps 串口波特率,支持115200或者1500000

1.5 如何修改 U-Boot 中的 DDR 频率

RK3588在loader初始化DDR时,会⼀起初始化4个DDR频率,供后续的kernel使⽤,且默认以最⾼频退出loader,继续后⾯的程序。 如下的F1,F2,F3,F0就是这4个频率,默认以F0退出。

DDR ... v1.14
LPDDR4X, 2112MHz
channel[0] BW=16 Col=10 Bk=8 CS0 Row=16/0 CS1 Row=16/0 CS=2 Die BW=16 Size=2048MB
channel[1] BW=16 Col=10 Bk=8 CS0 Row=16/0 CS1 Row=16/0 CS=2 Die BW=16 Size=2048MB
channel[2] BW=16 Col=10 Bk=8 CS0 Row=16/0 CS1 Row=16/0 CS=2 Die BW=16 Size=2048MB
channel[3] BW=16 Col=10 Bk=8 CS0 Row=16/0 CS1 Row=16/0 CS=2 Die BW=16 Size=2048MB
...
change to F1: 528MHz /* 4个DDR频率 */
change to F2: 1068MHz
change to F3: 1560MHz
change to F0: 2112MHz
out

当需要修改U-Boot下的DDR频率,即只要修改退出loader的频率。通过"修改DDR bin⽂件"所述⼯具,找到boot_fsp参数,可以选择以F0/F1/F2/F3的哪个频率作为退出loader的频率。从而实现修改U-Boot中的DDR频率。

序号

参数名称

配置值

有效值

Unit

备注

9

boot_fsp

0/1/2/3

-

-

完成DDR初始化后进入系统的DDR频率(F0/F1/F2/F3)

1.6 如何 enable/disable kernel 中的 DDR 变频功能

先确认该芯⽚⽀持 kernel 中的 DDR 变频功能,如果⽀持,可以通过如下⽅式 enable 或 disable 变频功能。

1.6.1 ≥kernel 4.4

对于 kernel 4.4及以后版本,需要找到 dts 中最终的 dmc 节点,将 status 改成 disabled 就可以 disable kernel 的 DDR 变频功能。相反的,改成 okay,就会 enable DDR 变频功能。

Note 1: 通过dts dmc节点enable DDR 变频后,根据实际产品硬件使⽤的电源⽅案检查DMC 节点下的“center-supply”属性是否有正确配置。该属性的配置值为给SOC端DDR模块供电的regulator节点名,RK3399默认为center-supply = <&vdd_center>,RK3588默认为center-supply = <&vdd_ddr_s0>,RK3568/RV1126/PX30等其他平台默认为center-supply = <&vdd_logic>。如果DMC节点缺失该属性会
导致dmc驱动加载失败,kernel log如下:
rockchip-dmc dmc: Cannot get the regulator "center"

Note 2: 由于早期代码,dmc 节点有依赖于 dfi 节点,如果 dfi 节点为 disabled,也会导致 dmc 节点⽆
效。所以最好 dfi 节点的 status 保持跟 dmc ⼀致。
举例如下,RK3399 EVB 板的最终 dmc 节点在 arch/arm64/boot/dts/rockchip/rk3399-evb.dtsi 中
&dfi {
status = "okay";
};
&dmc {
center-supply = <&vdd_center>; /* 这⾥需要客⼾根据实际硬件电路来配置 */
status = "okay"; /* enable kernel DDR变频 */
........
};

&dfi {
status = "disabled";
};
&dmc {
status = "disabled"; /* disable kernel DDR变频 */
........
};

1.6.2 kernel 3.10

对于 kernel 3.10,需要找到 dts 中最终的 clk_ddr_dvfs_table 节点,将 status 改成 disabled 就可以 disable kernel 的 DDR 变频功能。相反的,改成 okay,就会 enable DDR 变频功能。

/* 这样的表格enable DDR变频 */
static struct cpufreq_frequency_table dvfs_ddr_table[] = {
{.frequency = 200 * 1000 + DDR_FREQ_SUSPEND, .index = 1050 * 1000},
{.frequency = 300 * 1000 + DDR_FREQ_VIDEO, .index = 1050 * 1000},
{.frequency = 400 * 1000 + DDR_FREQ_NORMAL, .index = 1125 * 1000},
{.frequency = CPUFREQ_TABLE_END},
};
/* 这样的表格disable DDR变频 */
static struct cpufreq_frequency_table dvfs_ddr_table[] = {
//{.frequency = 200 * 1000 + DDR_FREQ_SUSPEND, .index = 1050 * 1000},
//{.frequency = 300 * 1000 + DDR_FREQ_VIDEO, .index = 1050 * 1000},
{.frequency = 400 * 1000 + DDR_FREQ_NORMAL, .index = 1125 * 1000},
{.frequency = CPUFREQ_TABLE_END},
};

1.7 如何让 kernel 一次 DDR 变频都不运行

上⼀个标题所讲的修改只是开启或关闭变频功能,即 DDR 不会在平时系统运⾏时变频,但有⼀次例外是不受上⾯修改控制的,即开机的第⼀次 ddr_init 时,会运⾏⼀次变频,⽤于更新 DDR timing,让性能更⾼。如果想让 kernel 中⼀次 DDR 变频都不运⾏,包括 ddr_init。除了上⼀个标题讲的“如何 enable/disablekernel 中的 DDR 变频功能”要做以外。还需要修改代码:

1.7.1 ≥kernel 4.4

只要按上⼀个标题讲的“如何 enable/disable kernel 中的 DDR 变频功能”做,就⼀次 DDR 变频都不会运⾏了。

1.7.2 kernel 3.10

其他版本kernel因为目前使用较少,本次不做详细介绍!如有需要了解,请联系我司技术团队。

1.8 如何查看 DDR 的容量

如果只是简单的想看 DDR 有多⼤容量,可以⽤如下命令,查看 MemTotal 容量,这个容量会⽐ DDR 实际容量小⼀点点,⾃⼰往上取到标准容量就可以了。

root@rk3399:/ # cat /proc/meminfo
MemTotal: 3969804 kB

如果需要看 DDR 容量的详细信息,按如下步骤: 在 2 个地⽅有 DDR 容量的打印,loader 中的 DDR 初始化阶段和 kernel 中 DDR 的初始化阶段。kernel 4.4及以后版本,中全部没有 DDR 容量信息的打印;kernel 3.10 不全有。loader 中的 DDR 详细信息,所有芯⽚都有。loader 中的 DDR 容量打印,必须⽤串口才能抓到,如果使⽤ adb,是抓不到这部分信息的。 具体情况如下:

芯片名称

kernel 3.0/3.10 Loader信息

kernel 4.4+ Loader信息

RK3026

✅ 有详细信息

✅ 有详细信息

RK3028A

✅ 有详细信息

✅ 有详细信息

RK3036

✅ 有详细信息

❌ 无信息

RK3066

✅ 有详细信息

✅ 有详细信息

RK3126B/C (带trust.imag固件)

✅ 有详细信息

❌ 无信息

RK3126B (不带trust侵犯了固件)

✅ 有详细信息

✅ 有详细信息

RK3126

✅ 有详细信息

✅ 有详细信息

RK3128

✅ 有详细信息

✅ 有详细信息

RK3188

✅ 有详细信息

✅ 有详细信息

RK3288

✅ 有详细信息

✅ 有详细信息

RK322x

✅ 有详细信息

❌ 无信息

RK322xh

✅ 有详细信息

❌ 无信息

RK3328

✅ 有详细信息

❌ 无信息

RK3368

✅ 有详细信息

❌ 无信息

RK3399

✅ 有详细信息

❌ 无信息

RV1108

Other SOC

❌ 无信息

DDR 的详细信息,会包含有:DDR 类型、DDR 频率、通道信息(Channel a\Channel b)、总线数据位宽(bus width/BW)、⾏数量(row)、列数量(col/column)、bank 数量(bank/BK)、⽚选数量(CS)、单颗颗粒的数据位宽(Die Bus width/Die BW)、单个通道的 DDR 总容量(size/Total Capability)。 如果需要整机的总容量,芯⽚只有⼀个 DDR 通道的,整机容量就等于 size/Total Capability。芯⽚有 2 个通道的,整机容量等于 2 个通道的 size/Total Capability 相加。如下是 loader 中打印的 DDR 容量详细信息:

DDR Version 1.05 20170712
In
Channel a: DDR3 400MHz
Bus Width=32 Col=10 Bank=8 Row=15 CS=1 Die Bus-Width=16 Size=1024MB
Channel b: DDR3 400MHz
Bus Width=32 Col=10 Bank=8 Row=15 CS=1 Die Bus-Width=16 Size=1024MB
Memory OK
Memory OK
OUT

如下是 kernel 中打印的 DDR 容量详细信息:

[ 0.528564] DDR DEBUG: version 1.00 20150126
[ 0.528690] DDR DEBUG: Channel a:
[ 0.528701] DDR DEBUG: DDR3 Device
[ 0.528716] DDR DEBUG: Bus Width=32 Col=10 Bank=8 Row=15 CS=1 Total
Capability=1024MB
[ 0.528727] DDR DEBUG: Channel b:
[ 0.528736] DDR DEBUG: DDR3 Device
[ 0.528750] DDR DEBUG: Bus Width=32 Col=10 Bank=8 Row=15 CS=1 Total
Capability=1024MB
[ 0.528762] DDR DEBUG: addr=0xd40000

1.9 修改DDR容量

⽬前所有RK平台的DDR容量都是⾃动识别的,不需要客⼾配置DDR容量。此处提供修改DDR容量的⽅法,主要⽤于客⼾评估性能或评估减少DDR容量的影响。 在《Rockchip_Developer_Guide_UBoot_Nextdev_CN》-> "CH08调试⼿段" -> "修改DDR容量",提供了另⼀种修改⽅法。 开机时DDR初始化代码会把DDR容量传递给U-Boot,U-Boot会去除⼀些安全内存后再传递给内核。⽤⼾可以在U-Boot阶段修改传递给内核的DDR容量。

修改步骤: 1、找到代码

./arch/arm/mach-rockchip/param.c

2、理解原理

struct memblock *param_parse_ddr_mem(int *out_count)
{
    ......
    for (i = 0, n = 0; i < count; i++, n++) {
    base = t->u.ddr_mem.bank[i];
    size = t->u.ddr_mem.bank[i + count];
    printf("base:0x%llx, size:0x%llx\n", base, size); //加这⾏打印
    /* 0~4GB */
    }
    if (base < SZ_4GB) {
    mem[n].base = base;
    mem[n].size = ddr_mem_get_usable_size(base, size);
    ......
    }
}

按上述修改后,重新编译U-Boot,重新烧录,在新的U-Boot开机log中,有如下输出:

U-Boot 2017.09-g0236dc3682-220712-dirty #hcy (Aug 04 2022 - 10:46:07 +0800)
Model: Rockchip RK3568 Evaluation Board
PreSerial: 2, raw, 0xfe660000
DRAM: base:0x0, size:0x40000000 //这⾏是我们增加的输出

其中

base:0x0, size:0x40000000 

表⽰有1块DRAM空间,基地址是0x0,⼤小是0x40000000 在U-Boot引导kernel前,有U-Boot去掉⼀些安全内存后,传递给内核的DDR容量信息,如下:

Adding bank: 0x00200000 - 0x08400000 (size: 0x08200000) //去除安全内存块后的
Adding bank: 0x09400000 - 0x40000000 (size: 0x36c00000) //去除安全内存块后的
Total: 1182.666 ms
Starting kernel ...
[ 0.000000] Booting Linux on physical CPU 0x0000000000 [0x412fd050]
  1. 理解了上述DDR容量信息的描述⽅法,就可以修改 假设,系统中要增加了⼀块DRAM空间,如下图Region 1的位置,可以做如下修改 image.png

struct memblock *param_parse_ddr_mem(int *out_count)
{
    ......
    /* extend top ram size */
    if (t->u.ddr_mem.flags & DDR_MEM_FLG_EXT_TOP)
        gd->ram_top_ext_size = t->u.ddr_mem.data[0];
    //修改DDR容量信息,开始
    count = 2;
    t->u.ddr_mem.count = count;
    t->u.ddr_mem.bank[0] = 0x0; //Region 0 base address
    t->u.ddr_mem.bank[0 + count] = 0x40000000 - 0x0; //Region 0 size
    t->u.ddr_mem.bank[1] = 0x70000000; //Region 1 base address
    t->u.ddr_mem.bank[1 + count] = 0x80000000 - 0x70000000; //Region 1 size
    t->u.ddr_mem.hash = 0;
    //修改DDR容量信息,结束
    /* normal ram size */
    count = t->u.ddr_mem.count;
    mem = calloc(count + MEM_RESV_COUNT, sizeof(*mem));
    if (!mem) {
        printf("Calloc ddr memory failed\n");
        return 0;
    }
    ......
    for (i = 0, n = 0; i < count; i++, n++) {
        base = t->u.ddr_mem.bank[i];
        size = t->u.ddr_mem.bank[i + count];
        printf("base:0x%llx, size:0x%llx\n", base, size);
    /* 0~4GB */
    if (base < SZ_4GB) {
        mem[n].base = base;
        mem[n].size = ddr_mem_get_usable_size(base, size);
    ......
}

重点分析如下代码:

    count = 2; //表⽰有2块DRAM空间
    t->u.ddr_mem.count = count; //必须这么写
    t->u.ddr_mem.bank[0] = 0x0;
    t->u.ddr_mem.bank[0 + count] = 0x40000000 - 0x0; //数组index必须有+count
    //Region base address加上Region size为⼀组,有多少组,必须跟上⾯的count⼀样
    t->u.ddr_mem.bank[1] = 0x70000000;
    t->u.ddr_mem.bank[1 + count] = 0x80000000 - 0x70000000; //数组index必须有
    +count
    t->u.ddr_mem.hash = 0; //必须这么写

重新编译U-Boot,重新烧录,在新的开机log中,看到修改已⽣效。

......
U-Boot 2017.09-g0236dc3682-220712-dirty #hcy (Aug 04 2022 - 10:46:07 +0800)
Model: Rockchip RK3568 Evaluation Board
PreSerial: 2, raw, 0xfe660000
DRAM: base:0x0, size:0x40000000
base:0x70000000, size:0x10000000 //我们新增的
......
Adding bank: 0x00200000 - 0x08400000 (size: 0x08200000)
Adding bank: 0x09400000 - 0x40000000 (size: 0x36c00000)
Adding bank: 0x70000000 - 0x80000000 (size: 0x10000000) //我们新增的
Total: 1182.666 ms
Starting kernel ...
[ 0.000000] Booting Linux on physical CPU 0x0000000000 [0x412fd050]

如果客⼾还是只有⼀块DRAM,但是size,想改⼤到0x80000000,可以增加如下代码

    count = 1; //表⽰有1块DRAM空间
    t->u.ddr_mem.count = count; //必须这么写
    t->u.ddr_mem.bank[0] = 0x0;
    t->u.ddr_mem.bank[0 + count] = 0x80000000 - 0x0; //数组index必须有+count
    t->u.ddr_mem.hash = 0; //必须这么写

注解

DRAM容量并不能随意改⼤的,因为没有真正的储存器,系统会异常。⼀般都是size改小,⽤于评估。此处举例加Region1,是为了⽅便理解。

1.10 查看DDR频率

有以下几种方式可以查看DDR频率

1.10.1 通过devfreq

有开启DDR变频功能的,可以通过如下节点获取到当前DDR频率 cat /sys/class/devfreq/dmc/cur_freq

console:/ # cat /sys/class/devfreq/dmc/cur_freq
780000000

1.10.2 通过clk_summary

cat /sys/kernel/debug/clk/clk_summary | grep scmi_clk_ddrcat /sys/kernel/debug/clk/clk_summary | grep sclk_ddrccat /sys/kernel/debug/clk/clk_summary | grep sclk_ddr 具体看kernel版本

console:/ # cat /sys/kernel/debug/clk/clk_summary | grep scmi_clk_ddr
scmi_clk_ddr 0 0 0 2736000000 0
0 50000 /*2736000000为单位Hz的DDR频率,即2736MHz*/

1.11 如何修改 DDR 频率

kernel 中改变 DDR 频率的,有 2 种情况,⼀种是场景变频,⼀种是负载变频。对于这 2 种变频策略,kernel 4.4 及以后版本和 kernel 3.10 的实现有些不同。 kernel 4.4及以后版本: 场景变频指:进⼊指定场景,如果此时负载变频功能关闭,则 DDR 频率就变到对应 SYS_STATUS_XXX定义的频率。如果此时负载变频功能开启的,则 SYS_STATUS_XXX 定义的频率作为最低频率,再根据实际 DDR 利⽤率状况结合 upthreshold、downdifferential 定义来提频或降频,但是最低频率始终不会低于SYS_STATUS_XXX 定义的频率。负载变频指:所有场景都会根据负载来变频。但是会保证频率不低于 SYS_STATUS_XXX 场景定义的频 率。唯⼀特例的是 SYS_STATUS_NORMAL,如果负载变频功能开启,SYS_STATUS_NORMAL 完全被负载变频替换,连最低频率都是由 auto-min-freq 决定,而不是 SYS_STATUS_NORMAL 决定。 kernel 3.10: 场景变频指:进⼊指定场景,DDR 频率就变到对应 SYS_STATUS_XXX 定义的频率,不在变化,即使此时负载变频功能是打开的,也不会根据负载来变频。负载变频指:仅仅是⽤来替换 SYS_STATUS_NORMAL 场景的。即只有在 SYS_STATUS_NORMAL 场景下,DDR 频率才会根据负载情况来变频。 要修改 DDR 频率,还是得按分⽀和芯⽚型号来分开处理

  • RK3326S/PX30S,⻅后⾯的"RK3326S/PX30S DDR频率选择"

  • RK356x/RK3588,⻅后⾯的"RK356x/RK3588/RK3528/RK3562 DDR频率选择"

  • 对于 kernel 4.4,需要找到 dts 中 dmc 节点。举例如下,RK3399 EVB 板的 dmc 节点在arch/arm64/boot/dts/rockchip/rk3399-evb.dtsi 和 arch/arm64/boot/dts/rockchip/rk3399.dtsi,

&dmc {
    status = "okay";
    center-supply = <&vdd_center>;
    upthreshold = <40>;
    downdifferential = <20>;
    system-status-freq = <
        /*system status freq(KHz)*/
        SYS_STATUS_NORMAL 800000
        SYS_STATUS_REBOOT 528000
        SYS_STATUS_SUSPEND 200000
        SYS_STATUS_VIDEO_1080P 200000
        SYS_STATUS_VIDEO_4K 600000
        SYS_STATUS_VIDEO_4K_10B 800000
        SYS_STATUS_PERFORMANCE 800000
        SYS_STATUS_BOOST 400000
        SYS_STATUS_DUALVIEW 600000
        SYS_STATUS_ISP 600000
    >;
    /* 每⼀⾏作为⼀组数据,其中的min_bw、max_bw表⽰vop发出的带宽需求,落在min_bw、max_bw范围内,则DDR频率需要再提⾼freq指定的频率,auto-freq-en=1时有效 */
    vop-bw-dmc-freq = <
    /* min_bw(MB/s) max_bw(MB/s) freq(KHz) */
    0 577 200000
    578 1701 300000
    1702 99999 400000
    >;
    auto-min-freq = <200000>;
    };
dmc: dmc {
    compatible = "rockchip,rk3399-dmc";
    devfreq-events = <&dfi>;
    interrupts = <GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH 0>;
    clocks = <&cru SCLK_DDRCLK>;
    clock-names = "dmc_clk";
    ddr_timing = <&ddr_timing>;
    /* DDR利⽤率超过40%,开始升频;auto-freq-en=1时才有效 */
    upthreshold = <40>;
    /* DDR利⽤率低于20%,开始降频;auto-freq-en=1时才有效 */
    downdifferential = <20>;
    system-status-freq = <
    /*system status freq(KHz)*/
    /* 只有auto-freq-en=0时有效。表⽰除了下列场景以外的,都⽤这个场景 */
    SYS_STATUS_NORMAL 800000
    /* reboot前的DDR频率。当auto-freq-en=1时,会以此频率作为min值,根据负载状况往上升频 */
    SYS_STATUS_REBOOT 528000
    /* ⼀级待机时的DDR频率。当auto-freq-en=1时,会以此频率作为min值,根据负载状况往上升频*/
    SYS_STATUS_SUSPEND 200000
    /* 1080P视频时的DDR频率。当auto-freq-en=1时,会以此频率作为min值,根据负载状况往上升频*/
    SYS_STATUS_VIDEO_1080P 300000
    /* 4K视频时的DDR频率。当auto-freq-en=1时,会以此频率作为min值,根据负载状况往上升频 */
    SYS_STATUS_VIDEO_4K 600000
    /* 4K 10bit视频时的DDR频率。当auto-freq-en=1时,会以此频率作为min值,根据负载状况往上升频 */
    SYS_STATUS_VIDEO_4K_10B 800000
    /* 性能模式时的DDR频率。当auto-freq-en=1时,会以此频率作为min值,根据负载状况往上升频*/
    SYS_STATUS_PERFORMANCE 800000
    /* 触屏时的DDR频率,⽤于从低频提⾼上来,改善触屏响应。当auto-freq-en=1时,会以此频率作为min值,根据负载状况往上升频 */
    SYS_STATUS_BOOST 400000
    /* 双屏显⽰时的DDR频率。当auto-freq-en=1时,会以此频率作为min值,根据负载状况往上升频*/
    SYS_STATUS_DUALVIEW 600000
    /* ISP时的DDR频率。当auto-freq-en=1时,会以此频率作为min值,根据负载状况往上升频 */
    SYS_STATUS_ISP 600000
    >;
    /* auto-freq-en=1时有效,表⽰normal场景的最低频率 */
    auto-min-freq = <400000>;
    /* 等于1,表⽰开启负载变频功能;等于0,表⽰关闭负载变频功能。开启负载变频功能后,SYS_STATUS_NORMAL场景将完全被负载负载变频接替,且最低频率以auto-min-freq为准,而不是以SYS_STATUS_NORMAL定义的为准。而且开启负载变频后,其他场景定义的频率将作为最低频率,系统根据DDR带宽的利⽤率状况,依据upthreshold、downdifferential来提⾼、降低DDR频率 */
    auto-freq-en = <1>;
    status = "disabled";
};

注意:

  1. kernel 4.4 的频率电压跟 kernel 3.10 不同,只有频率等于 dmc_opp_table 所列 opp-hz 的,才会按该频率运⾏;小于 opp-hz,频率会向上取;如果频率超过这个表格的最⼤ opp-hz,只会按最⼤ opp-hz⼯作。所以,如果不想频率被向上取,或被限制到最⼤ opp-hz,dmc_opp_table 也是需要关注的。

dmc_opp_table: opp-table3 {
    opp-200000000 {
    /* 当DDR频率等于200MHz时,使⽤这个电压;小于200MHz,就按200MHz运⾏ */
        opp-hz = /bits/ 64 <200000000>;
        opp-microvolt = <825000>; //vdd_center电压
    };
    ......
    opp-800000000 {
    opp-hz = /bits/ 64 <800000000>;
    opp-microvolt = <900000>;
    };
};

明⽩了每个配置的含义后,根据需要修改的场景,来修改对应的频率定义,就可以了。如果 auto-freqen=1,就不好控制频率,这时候降频如果是为了查找定位问题,可以把 auto-freq-en 设置为 0,然后修改各场景定义的频率值来达到⽬的。

  1. RK3399 LPDDR4和RV1126平台,DDR变频⽀持的频率在loader阶段已确定,ddr初始化阶段会通过串口打印出来,dmc_opp_table定义的频率需要跟其对应。以RV1126为例,loader阶段打印的DDR频率为328MHz,528MHz,784MHz,924MHz,则dmc_opp_table也只能定义这4个频率。

change to: 328MHz
change to: 528MHz
change to: 784MHz
change to: 924MHz(final freq)
dmc_opp_table: dmc-opp-table {
    compatible = "operating-points-v2";
    opp-328000000 {
        opp-hz = /bits/ 64 <328000000>;
        opp-microvolt = <800000>;
    };
opp-528000000 {
    opp-hz = /bits/ 64 <528000000>;
    opp-microvolt = <800000>;
    };
opp-784000000 {
    opp-hz = /bits/ 64 <784000000>;
    opp-microvolt = <800000>;
    };
opp-924000000 {
    opp-hz = /bits/ 64 <924000000>;
    opp-microvolt = <800000>;
    };
};

如果dmc_opp_table和loader打印的频率不⼀致,kernel dmc驱动执⾏DDR变频时会出现频率不匹配问题,log 如下:

rockchip-dmc dmc: Get wrong frequency, Request 1056000000, Current 924000000

对于 kernel 3.10,需要找到 dts 中的 clk_ddr_dvfs_table 节点。举例如下,RK3288 SDK 板的最终的clk_ddr_dvfs_table 在 arch/arm/boot/dts/rk3288-tb_8846.dts 中:

&clk_ddr_dvfs_table {
    /* DDR频率对应的logic电压,如果freq-table或bd-freq-table中的频率⽐这⾥的最⼤频率还⼤,就⽆法找到对应电压,因此⽆法切换到对应频率。这时候需要在这⾥增加频率电压表格 */
    operating-points = <
        /* KHz uV */
        200000 1050000
        300000 1050000
        400000 1100000
        533000 1150000
    >;
    freq-table = <
    /*status freq(KHz)*/
    /* 只有auto-freq-en=0时有效。表⽰除了下列场景以外的,都⽤这个场景 */
    SYS_STATUS_NORMAL 400000
    /* ⼀级待机时的DDR频率 */
    SYS_STATUS_SUSPEND 200000
    /* 1080P视频时的DDR频率 */
    SYS_STATUS_VIDEO_1080P 240000
    /* 4K视频时的DDR频率 */
    SYS_STATUS_VIDEO_4K 400000
    /* 4K视频60fps时的DDR频率 */
    SYS_STATUS_VIDEO_4K_60FPS 400000
    /* 性能模式时的DDR频率 */
    SYS_STATUS_PERFORMANCE 528000
    /* 双屏显⽰时的DDR频率 */
    SYS_STATUS_DUALVIEW 400000
    /* 触屏时的DDR频率,⽤于从低频提⾼上来,改善触屏响应 */
    SYS_STATUS_BOOST 324000
    /* ISP时的DDR频率 */
    SYS_STATUS_ISP 400000
    >;
    bd-freq-table = <
    /* bandwidth freq */
        5000 800000
        3500 456000
        2600 396000
        2000 324000
    >;
    /* 负载变频开启后,当处于SYS_STATUS_NORMAL场景时,将按照DDR带宽利⽤率情况,在这个表格的⼏个频率之间切换 */
    auto-freq-table = <
        240000
        324000
        396000
        528000
    >;
    /* 等于1,表⽰开启负载变频功能;等于0,表⽰关闭负载变频功能。开启负载变频功能后,SYS_STATUS_NORMAL场景将完全被负载负载变频接替 */
    auto-freq=<1>;
    /*
    * 0: use standard flow
    * 1: vop dclk never divided
    * 2: vop dclk always divided
    */
    vop-dclk-mode = <0>;
    status="okay";
};

明⽩了每个配置的含义后,根据需要修改的场景,来修改对应的频率定义,就可以了。如果 autofreq=1,就不好控制频率,这时候降频如果是为了查找定位问题,可以把 auto-freq 设置为 0,然后修改各场景定义的频率值来达到⽬的。 ==注意:修改频率,⼀定要注意对应的电压,是否能正常⼯作。==如何修改电压,⻅"如何修改 DDR 某个频率对应的电压"章节

RK356x/RK3588/RK3528/RK3562 DDR频率选择

对于RK3566/RK3568/RK3588/RK3528/RK3562平台loader有变4次频率,保存着相应频率的training结果。 如下的F1,F2,F3,F0就是这4个频率,默认以F0退出。这些平台,虽然DDR变频⽀持的频率也是在loader阶段就确定,但新平台kernel dmc驱动会⾃动获取⽀持的DDR频率值,因此不存在dmc_opp_table频率匹配问题。这些平台的kernel dmc节点如下:

dmc: dmc {
    ...
    system-status-freq = <
    /*system status freq(KHz)*/
    SYS_STATUS_NORMAL DMC_FREQ_LEVEL_MID_HIGH
    SYS_STATUS_REBOOT DMC_FREQ_LEVEL_MID_LOW
    ...
    SYS_STATUS_SUSPEND DMC_FREQ_LEVEL_LOW
    ...
    SYS_STATUS_PERFORMANCE DMC_FREQ_LEVEL_HIGH
    ...
    >;
    ...
};

各个场景对应的频率不再是直接的频率,而是DMC_FREQ_LEVEL_HIGH等定义,这些定义,能⾃动对应到loader的4个频率。如果⽤"修改DDR bin⽂件"的⽅法,修改了4个频率,DMC_FREQ_LEVEL_HIGH这些定义能⾃动变为修改后的4个频率。以上⾯开机loader的4个频率为例,说明对应关系如下:

宏定义

频率

说明

DMC_FREQ_LEVEL_HIGH

F0的 2112MHz

最高频

DMC_FREQ_LEVEL_MID_HIGH

F3的 1560MHz

次高频

DMC_FREQ_LEVEL_MID_LOW

F2的 1068MHz

次低频

DMC_FREQ_LEVEL_LOW

F1的 528MHz

最低频

1.12 如何修改 DDR 某个频率对应的电压

如果是为了定位问题,想通过命令来修改电压,可以采⽤如下⽅式 kernel 4.4以后版本:可以通过 /sys/kernel/debug/regulator/ 节点直接调整某⼀路的电压。要求先对DDR进⾏定频。否则这⾥调整电压,与DDR变频的调整电压,会冲突。DDR定频参考"DDR 如何定频"章节。 以3588调整电压为例:

cat /sys/kernel/debug/regulator/vdd_ddr_s0/voltage
echo 700000 > /sys/kernel/debug/regulator/vdd_ddr_s0/voltage

各平台修改DDR频率对应电压需要调整的regulator如下

平台

需要调整的节点

RK3588

vdd_log_s0和vdd_ddr_s0

RK3308/RK3308B

vdd_core或vdd_log

Other SOC

vdd_center或vdd_logic

内核相关配置与操作

kernel 4.4

需要编译 kernel 时,打开 pm_tests 选项:
make ARCH=arm64 menuconfig -> Device Drivers -> SOC(System On Chip)specific Drivers -> Rockchip pm_test support

kernel 3.10

需要编译 kernel 时,打开 pm_tests 选项:
make menuconfig -> System Type -> /sys/pm_tests/support
调整 DDR 电压的命令:

  • RK3399

    echo set vdd_center 900000 > /sys/pm_tests/clk_volt  
    
  • 其他

    echo set vdd_logic 1200000 > /sys/pm_tests/clk_volt  
    

如果没有pm_tests 或命令不满足需求时 需修改 kernel 固件,步骤如下:

  • 对于 kernel 4.4,需找到 dts 中 dmc_opp_table 节点。举例:

  • RK3399 EVB 板的在 arch/arm64/boot/dts/rockchip/rk3399-opp.dtsi

  • RK3368 在 arch/arm64/boot/dts/rockchip/rk3368.dtsi 以 RK3399 为例:

  /* 只有频率等于dmc_opp_table所列opp-hz的,才会按该频率运⾏;小于opp-hz,频率会向上取;如果频
率超过这个表格的最⼤opp-hz,只会按最⼤opp-hz⼯作;这是跟kernel 3.10不同的地⽅ */
dmc_opp_table: opp-table3 {
    compatible = "operating-points-v2";
        opp-200000000 {
        /* 当DDR频率等于200MHz时,使⽤这个电压;小于200MHz,就按200MHz运⾏ */
        opp-hz = /bits/ 64 <200000000>;
        opp-microvolt = <825000>; //vdd_center电压
    };
    opp-300000000 {
        opp-hz = /bits/ 64 <300000000>;
        opp-microvolt = <850000>;
    };
    opp-400000000 {
        opp-hz = /bits/ 64 <400000000>;
        opp-microvolt = <850000>;
    };
    opp-528000000 {
        opp-hz = /bits/ 64 <528000000>;
        opp-microvolt = <900000>;
    };
    opp-600000000 {
        opp-hz = /bits/ 64 <600000000>;
        opp-microvolt = <900000>;
    };
    opp-800000000 {
        opp-hz = /bits/ 64 <800000000>;
        opp-microvolt = <900000>;
    };
};

1.13 如何关闭 DDR 的负载变频功能,只留场景变频

对于 kernel 4.4及以后版本,需要找到 dts 中 dmc 节点的 auto-freq-en。举例如下,RK3399 EVB 板的在 arch/arm64/boot/dts/rockchip/rk3399.dtsi

dmc: dmc {
    compatible = "rockchip,rk3399-dmc";
    ........
    auto-min-freq = <400000>;
    /* 这个设为0,就关闭DDR负载变频功能,只留场景变频 */
    auto-freq-en = <0>;
    .......
};

以 RK3368 为例:

/* 只有频率等于dmc_opp_table所列opp-hz的,才会按该频率运行;小于opp-hz,频率会向上取;如果频率超过这个表格的最大opp-hz,只会按最大opp-hz工作;这是跟kernel 3.10不同的地方 */  
dmc_opp_table: opp_table2 {  
    compatible = "operating-points-v2";  

    opp-192000000 {  
        /* 当DDR频率等于200MHz时,使用这个电压,小于200MHz,就按200MHz运行 */  
        opp-hz = /bits/ 64 <192000000>;  
        opp-microvolt = <1100000>; //vdd_logic电压  
    };  
    opp-300000000 {  
        opp-hz = /bits/ 64 <300000000>;  
        opp-microvolt = <1100000>;  
    };  
    opp-396000000 {  
        opp-hz = /bits/ 64 <396000000>;  
        opp-microvolt = <1100000>;  
    };  
    opp-528000000 {  
        opp-hz = /bits/ 64 <528000000>;  
        opp-microvolt = <1100000>;  
    };  
    opp-600000000 {  
        opp-hz = /bits/ 64 <600000000>;  
        opp-microvolt = <1100000>;  
    };  
};  

可以修改对应频率对应的电压。因为频率电压表采⽤的是小于等于指定频率,就使⽤相应电压。如果新增的频率超出了这张表格的最⾼频率,使得找不到对应电压,会导致 DDR ⽆法切换到该新增频率。这时候,就需要增加⼀项该频率对应的电压表。对于 kernel 3.10,需要找到 dts 中的clk_ddr_dvfs_table 节点。举例如下,RK3288 SDK 板的最终的clk_ddr_dvfs_table 在 arch/arm/boot/dts/rk3288-tb_8846.dts 中,

&clk_ddr_dvfs_table {  
    /* 频率电压表是这个 */  
    operating-points = <  
        /* KHz    uV */  
        /* 表示DDR频率小于等于200MHz,logic使用1050mV,其他行以此类推 */  
        200000 1050000  
        300000 1050000  
        400000 1100000  
        533000 1150000  
    >;  
    ......  
    status = "okay";  
};  

可以修改对应频率对应的电压。因为频率电压表采用的是“小于等于指定频率,就使用相应电压”。如果新增的频率超出了这张表格的最高频率,使得找不到对应电压,会导致 DDR 无法切换到该新增频率。这时候,就需要增加一项该频率对应的电压表。 对于 kernel 3.0,需要在板级的 borad-**.c 文件中修改 dvfs_ddr_table。举例如下,RK3066 SDK 板的板级文件在 arch/arm/mach-rk30/board-rk30-sdk.c 中:

static struct cpufreq_frequency_table dvfs_ddr_table[] = {  
    {.frequency = 200 * 1000 + DDR_FREQ_SUSPEND, .index = 1050 * 1000},  
    {.frequency = 300 * 1000 + DDR_FREQ_VIDEO, .index = 1050 * 1000},  
    {.frequency = 400 * 1000 + DDR_FREQ_NORMAL, .index = 1125 * 1000},  
    {.frequency = CPUFREQ_TABLE_END},  
};  

dvfs_ddr_table 表格中的 .index 就是对应的电压,单位是 uV

1.14 DDR 如何定频

如果是为了定位问题,想通过命令来定频,kernel 4.4及以后版本,可以采⽤如下⽅式:

获取固件支持的 DDR 频率

cat /sys/class/devfreq/dmc/available_frequencies  

设置频率

echo userspace > /sys/class/devfreq/dmc/governor  
echo 300000000 > /sys/class/devfreq/dmc/min_freq  // 这条是防止要设置的频率低于 min_freq,导致设置失败  
echo 300000000 > /sys/class/devfreq/dmc/userspace/set_freq  

这里的频率单位是 Hz,可用的值,必须在 cat /sys/class/devfreq/dmc/available_frequencies 结果中。

1.15 如何查看 DDR 带宽利用率

kernel 4.4 及以后版本,提供了一个命令,可简单查看整个 DDR 带宽利用率(无详细端口数据量信息)。

命令示例

rk3288: # cat /sys/class/devfreq/dmc/load  
11@396000000Hz  

11 表示 DDR 当前带宽利用率为 11%。

1.16 如何测试 DDR 可靠性

详⻅“DDR 颗粒验证流程说明”章节,此处略。

1.17 如何确定 DDR 能运行的最高频率

1.18 怎么判断 DDR 已经进入自刷新(self-refresh 省电模式)

可以通过测量 CKE 信号来判断,且不需要带宽很高的示波器。

CKE 状态 解释
低电平(时间大于 7.8us) 处于自刷新状态
高电平 处于正常工作状态

如果测到的 CKE 是一会儿高一会儿低,也按上表解释,即一会儿进入自刷新,一会儿退出自刷新,处于正常工作状态。

注意:CKE 为低电平时间一定要大于 7.8us 才算进入自刷新,因为 power-down 状态也是 CKE 为低,但时间小于 7.8us,不要混淆。

1.19 怎么判断 DDR 已经进入 auto power-down 省电模式

可以通过测量 CKE 信号来判断,而且不需要带宽很高的示波器就可以。

CKE 状态 解释
低电平(时间小于 7.8us) 处于 auto power-down 状态
高电平 处于正常工作状态

auto power-down 状态,测到的 CKE 状态,是 CKE 保持低电平将近 7.8us(DDR3、DDR4)或 3.9us(LPDDR2、LPDDR3、LPDDR4),然后拉高一小段时间,再进入低电平近 7.8us 或 3.9us,如此一直往复。

注意:CKE 为低电平时间一定要小于 7.8us(DDR3、DDR4)或 3.9us(LPDDR2、LPDDR3、LPDDR4)才是 auto power-down;若时间大于 7.8us,就是自刷新,不要混淆。

1.20 如何调整 DQ、DQS、CA、CLK 的 de-skew

主要由于硬件 PCB 中 DDR 走线不等⻓,可以通过调整 de-skew,达到类似 DDR 走线等⻓的效果。deskew 功能就是 DDR PHY 内部串联在信号线上的⼀个个延迟单元,可以通过 de-skew 寄存器控制各个信号线上串联的延迟单元的个数来改变每个信号线的延迟。

1.20.1 调整 kernel 中的 de-skew

要调整 kernel 中的 de-skew,目前仅 RK322xh、RK3328 支持,需修改对应的 dts 文件。

  • 芯片:RK322xh、RK3328

  • 代码位置

    arch/arm64/boot/dts/rk322xh-dram-default-timing.dtsi  
    arch/arm64/boot/dts/rk322xh-dram-2layer-timing.dtsi  
    

    若产品覆盖这两个定义,以新定义为准。

  • 修改
    根据“deskew 自动扫描工具”扫描结果,选择 mid 值,修改到对应 dts 定义中。“deskew 自动扫描工具”的使用需参照《328H deskew 自动扫描工具使用说明.pdf》。

1.20.2 调整 loader 中的 de-skew

要调整 loader 中的 de-skew,目前仅 RK3308 支持。

  • 芯片:RK3308

  • 需要的文件

    • deskew 自动扫描工具

    • 3308_deskew.exe

    • RK3308_DDRXPXXXXXX_Template_VXX_de-skew.txt

    • rk3308_ddr_XXXMHz_uartX_mX_vX.XX.bin

  • 修改
    根据“deskew 自动扫描工具”扫描结果,选择 mid 值,修改到 RK3308_DDRXPXXXXXX_Template_VXX_de-skew.txt 对应定义中。通过 3308_deskew.exe,将 RK3308_DDRXPXXXXXX_Template_VXX_de-skew.txt 中定义的 de-skew 修改到 rk3308_ddr_XXXMHz_uartX_mX_vX.XX.bin 中。“deskew 自动扫描工具”的使用需参照《deskew 自动扫描工具使用说明.pdf》。

1.21 U-Boot下运行DDR压力测试

U-Boot下我们移植了2个常⽤DDR压⼒测试程序stressapptest和memtester 主要⽤于:

  • kernel⽆法启动时,在U-Boot下对DDR进⾏压⼒测试,以排除DDR问题

  • kernel不具备运⾏stressapptest或memtester的条件。

  • 如:kernel中有较多模块不完善,会引起系统不稳定,导致stressapptest或memtester⽆法⻓时间运⾏。

  • 测试更⼤的DDR空间

  • kernel由于系统占⽤,只能对剩余DDR空间进⾏测试。在U-Boot下测试,占⽤空间小很多。可以测到更⼤的DDR空间。

  • 指定物理地址测试

1.21.1 stressapptest

开启
make ARCH=arm64 menuconfig -> Command line interface -> DDR Tool -> Enable DDR Tool  
再开启
make ARCH=arm64 menuconfig -> Command line interface -> DDR Tool -> Enable DDR Tool -> Enable stressapptest for ddr  

编译 U-Boot,烧录 uboot.img 后。

重启设备,在 PC 串口终端上,持续按住 CTRL+C 键,直到 U-Boot 进入命令行模式,如下:

pclk_top_root 100000 KHz  
aclk_low_top_root 396000 KHz  
Net:  No ethernet found.  
Hit key to stop autoboot('CTRL+C'): 0  
=> <INTERRUPT>  
=> <INTERRUPT>  

输入 stressapptest -h,按照参数说明来测试。

1.21.2 memtester

开启
make ARCH=arm64 menuconfig -> Command line interface -> DDR Tool -> Enable DDR Tool  
再开启
make ARCH=arm64 menuconfig -> Command line interface -> DDR Tool -> Enable DDR Tool -> Enable memtester for ddr  

编译 U-Boot,烧录 uboot.img 后。

重启设备,在 PC 串口终端上,持续按住 CTRL+C 键,直到 U-Boot 进入命令行模式,如下:

pclk_top_root 100000 KHz  
aclk_low_top_root 396000 KHz  
Net:  No ethernet found.  
Hit key to stop autoboot('CTRL+C'): 0  
=> <INTERRUPT>  
=> <INTERRUPT>  

输入 memtester -h,按照参数说明来测试。

1.22 RK3568 ECC的使能

ECC 指 Error Correcting Code,而 DDR ECC 是对 DDR 数据进行错误检查和纠正的。RK3568 支持的是 SideBand ECC,即在 DDR 数据通道旁增加一个专门存放 ECC 数据的 DDR 通道。 其 ECC 具有纠错 1bit,发现 2bit 错误的能力,即 SEC/DED ECC(Single Error Correction/Double Error Detection)。

  1. 使能 ECC:只要 DDR_ECC_DQ0-7 有贴颗粒,ECC 会自动 enable。

  2. DDR_ECC_DQ0-7 的作用:ECC 由 32bit 的 DQ 数据 + 7bit 的 ECC 数据组成。DDR_ECC_DQ0-7 用于存储 DQ0-DQ31 计算得到的 ECC 数据,因此需多贴一颗用于存放 ECC 数据的颗粒。

  3. 多贴的 ECC 颗粒要求:颗粒类型、Row/Col/Bank 需与 DQ0-31 上所贴的颗粒一致。

  4. 支持的颗粒类型:所有颗粒类型均支持 ECC。

1.23 如何在 kernel 获取 DDR ECC 的信息

DDR ECC 信息在 kernel 的 edac 架构里,对于支持 DDR ECC 功能的平台(如 RK3568,且硬件上支持使能 DDR ECC,详见 “RK3568 ECC 的使能” 章节),需在 dtsi 里开启 edac 模块。若平台 dtsi 无 edac 节点,则表明此平台不支持。

edac: edac {  
    ...  
    status = "okay";  
};  

RK EDAC 系统位置

RK EDAC 注册到系统的位置:

sys/devices/system/edac/mc/mc0  
  
sys/bus/edac/devices/mc/mc0/  

获取 RK EDAC 相关信息

获取 RK EDAC 名称
# cat sys/devices/system/edac/mc/mc0/mc_name  
rk_edac_ecc  
获取单 bit 可检测可纠正性错误(correctable error, ce)数量
# cat sys/devices/system/edac/mc/mc0/ce_count  
0  
获取多 bit 可检测不可纠正性错误(uncorrectable error, ue)数量
# cat sys/devices/system/edac/mc/mc0/ue_count  
0  

当系统重新启动时,内核会重新初始化相关的 EDAC 模块和计数器,之前记录的错误数量会被清零,重新从零开始计数。

1.24 如何查看DDR厂商ID

只有LPDDR类型(如LPDDR2,LPDDR3,LPDDR4,LPDDR4X,LPDDR5,LPDDR5X)才有⼚商的ID,DDR类型(如DDR2,DDR3,DDR4)是没有⼚商ID的。

1.24.1 通过 kernel 的 dmcdbg 节点

以下平台支持通过 proc/dmcdbg/dmcinfo 获取 DDR 厂商 ID:

  • RK3566

  • RK3568

  • RK3326

  • RK3326S

需先在对应平台 dtsi 开启 dmcdbg 节点(若节点不存在,则此平台不支持)。以 RK356X 为例,在 arch/arm64/boot/dts/rockchip/rk3568.dtsi 文件中开启 dmcdbg 节点:

dmcdbg: dmcdbg {  
    compatible = "rockchip,rk3568-dmcdbg";  
    status = "okay";  
};  

重新编译 kernel 固件并烧录,开机后,通过 cat 对应节点 proc/dmcdbg/dmcinfo 获取 DDR 厂商 ID 和其他 DDR 信息:

console:/ # cat proc/dmcdbg/dmcinfo  
DramType:        LPDDR4  
Dram ID:         MR5=0x1,MR6=0x0,MR7=0x1/* 其中MR5是指厂商ID,MR6,MR7是预留给厂商定义的version ID */  

DramCapacity:    
CS Count:        1  
Bus Width:       32 bit  
Column:          10  
Bank:            8  
CS0_Row:         16  
CS1_Row:         0  
DieBusWidth:     16 bit  
TotalSize:       2048 MB  

devfreq/dmc:     Enable  
governor:        dmc_ondemand  

cur_freq:        780000000  

NOTE:  
more information about dmc can get from /sys/class/devfreq/dmc.  

1.24.2 通过 loader 输出信息

RK3588 loader 输出中含有厂商 ID
DDR ... v1.14  
LPDDR5, 2736MHz  
channel[0] BW=16 Col=10 Bk=16 CS0 Row=15 CS1 Row=15 CS=2 Die BW=16 Size=2048MB  
channel[1] BW=16 Col=10 Bk=16 CS0 Row=15 CS1 Row=15 CS=2 Die BW=16 Size=2048MB  
channel[2] BW=16 Col=10 Bk=16 CS0 Row=15 CS1 Row=15 CS=2 Die BW=16 Size=2048MB  
channel[3] BW=16 Col=10 Bk=16 CS0 Row=15 CS1 Row=15 CS=2 Die BW=16 Size=2048MB  
Manufacturer ID:0xff /* 这就是厂商ID */  
...  
change to F0: 2736MHz  
out  
RK3399 loader 输出中含有厂商 ID
DDR Version ...  
In  
channel 0  
...  
MR5=0xFF  /* 这就是厂商ID */  
...  
change freq to 416MHz 0,1  
Channel 0: LPDDR4,416MHz  
Bus Width=32 Col=10 Bank=8 Row=16 CS=1 Die Bus-Width=16 Size=2048MB  
Channel 1: LPDDR4,416MHz  
Bus Width=32 Col=10 Bank=8 Row=16 CS=1 Die Bus-Width=16 Size=2048MB  
...  
OUT  

1.24.3 应当注意如下几点

  1. 只有 LPDDR 类型(如 LPDDR2,LPDDR3,LPDDR4,LPDDR4X,LPDDR5,LPDDR5X)才有厂商的 ID,DDR 类型(如 DDR2,DDR3,DDR4)是没有厂商 ID 的。

  2. 厂商 ID 显示的是晶圆来自哪个厂商,并不是封装后的 DDR 品牌信息。因为很多 DDR 品牌并不具备生产晶圆的能力。

  3. 所有 LPDDR,都无法获得封装丝印上的型号。这跟 Nand Flash 或 eMMC 不同。

  4. 晶圆厂商 ID 的解析,遵循 JEDEC 标准的 JEP166。

1.24.4 厂商ID对照表

如下是截⽌本⽂档完成时,最新的ID对照表。 后续有更新,可以⾃⾏在https://www.jedec.org搜索JEP166,找到最新的ID对照表

LPDDR2 ,LPDDR3 ⼚商 ID 对照表

MR5值 厂商名称
0x1 Samsung
0x2 Qimonda
0x3 Elpida
0x4 Etron
0x5 Nanya
0x6 SK hynix
0x7 Mosel
0x8 Winbond
0x9 ESMT
0xa Zentel
0xb Spansion
0xc SST
0xd ZMOS
0xe Intel
0x12 Being Advanced Memory Corp
0x1a Xi'an UniIC Semiconductors Co., Ltd
0x1b ISSI
0x1c JSC
0xaa Tezzaron
0xc2 Macronix
0xf8 Fidelix
0xfc eveRAM
0xfd AP Memory
0xfe Numonyx
0xff Micron

LPDDR4 ⼚商 ID 对照表:

MR5值 厂商名称
0x1 Samsung
0x5 Nanya
0x6 SK hynix
0x8 Winbond
0x9 ESMT
0x13 CXMT
0x1a Xi'an UniIC Semiconductors Co., Ltd
0x1c JSC
0xf8 Fidelix
0xf9 Ultra Memory
0xfd AP Memory
0xff Micron

LPDDR5 厂商 ID 对照表

MR5值 厂商名称
0x1 Samsung
0x5 Nanya
0x6 SK hynix
0x8 Winbond
0x9 ESMT
0x13 CXMT
0xe5 Dosilicon
0xff Micron

1.25 高温刷新率(refresh rate)问题

当温度高于85°C,需要对DDR加倍刷新。此要求对工规、车规产品尤其需要注意。
RK平台加倍刷新方法
RK平台有如下几个方法可以加倍刷新:

  • LPDDR系列颗粒:LPDDR3、LPDDR4、LPDDR4X、LPDDR5、LPDDR5X颗粒自身带有温度传感器,能根据温度,通过MR4告知需要多少刷新率。RK有些平台实现了一套 Automatic Temperature Derating 功能(简称 derate),可根据颗粒提供的 MR4 信息,自动调整刷新率,实现刷新率和温度匹配。

  • DDR3、DDR4颗粒:因自身不带温度传感器,也没有 MR4 告知刷新率功能,无法使用 derate,这类颗粒采用加倍刷新。

  • 修改DDR bin文件:客户可修改 DDR bin 的 ext_temp_ref 自行加倍刷新。

    ext_temp_ref  0  |  ——  0: 固定1x刷新(3568M/3568J 固定2x刷新), 1 固定2x刷新, 2: 固定4x刷新, 3: 固定1x刷新  
    

    修改方法见 "修改DDR bin文件"。

Note:RK的工规、车规、宽温芯片,均已对刷新率进行修改,无需通过此工具自行加倍。 工具中,固定2x/4x/1x刷新的对应关系如下:

名词 LPDDR3/LPDDR4 LPDDR5 备注
固定2x刷新 MR4描述中Refresh Rate=0x4 MR4描述中Refresh Multiplier=0xB 2倍刷新
固定4x刷新 MR4描述中Refresh Rate=0x5 MR4描述中Refresh Multiplier=0xC 4倍刷新
固定1x刷新 MR4描述中Refresh Rate=0x3 MR4描述中Refresh Multiplier=0x9 正常刷新

目前RK工规、车规、宽温平台处理方法及DDR bin版本要求,如下:

平台 DDR类型 处理方法及DDR bin版本要求
RK3568J/M LPDDR3、LPDDR4、LPDDR4X 使用 rk3568_ddr_XXXXMHz_v1.18.bin 及以后版本
默认开启derate功能
RK3568J/M DDR3、DDR4 使用 rk3568_ddr_XXXXMHz_v1.16.bin 及以后版本
默认2倍刷新(即0.5x tREFI)
RK3588J/M LPDDR4、LPDDR4X、LPDDR5、LPDDR5X 使用 rk3588_ddr_XXXXMHz_v1.13.bin 及以后版本
默认开启derate功能
RK3308J
RK3308K
RK3308GK
所有DDR类型 使用 rk3308_ddr_XXXMHz_uartX_mX_v1.26.bin 及以后版本
默认4倍刷新(即0.25x tREFI)
RV1126K 所有DDR类型 使用 rv1126_ddr_XXXMHz_v1.03.bin 及以后版本
默认2倍刷新(即0.5x tREFI)

普通平台默认DDR bin开启derate功能情况

平台 DDR类型 处理方法及DDR bin版本要求
RK3588 LPDDR4、LPDDR4X、LPDDR5、LPDDR5X 使用 rk3588_ddr_XXXXMHz_v1.13.bin 及以后版本
默认开启derate功能
RK3568 LPDDR3、LPDDR4、LPDDR4X 使用 rk3568_ddr_XXXXMHz_v1.18.bin 及以后版本
默认开启derate功能
RK3528 LPDDR3、LPDDR4、LPDDR4X 使用 rk3528_ddr_XXXXMHz_v1.07.bin
rk3528_ddr_XXXXMHz_2L_PCB_v1.07.bin
rk3528_ddr_XXXXMHz_4BIT_PCB_v1.07.bin
及以后版本
默认开启derate功能
RK3562 LPDDR3、LPDDR4、LPDDR4X 使用 rk3562_ddr_XXXXMHz_v1.05.bin
rk3562_ddr_XXXXMHz_ultra_v1.05.bin
及以后版本
默认开启derate功能

第2章 DDR 问题排查手册

2.1 怎么确认是不是 DDR 问题

查看串口 log

  1. 若串口 log 在 loader 中的 DDR 初始化部分报错,一定是 DDR 问题。

  2. 检查 loader 中 DDR 初始化部分 log 里的 DDR 容量、行列 bank 及颗粒类型位宽信息是否正确,信息错误可能引发 DDR 问题。

  3. 若串口 log 是系统中的 panic log,多次尝试查看 panic 地址一致性:

    • 地址一致,基本非 DDR 问题;

    • 地址不一致,可能是 DDR 问题或电源问题。

看显示是否正常

显示异常时,是 DDR 问题的概率较大。

做排查试验

  1. arm gpu 降频并定频抬压,若有效,非 DDR 问题,大概率是电源问题。

  2. 关闭 DDR 变频功能,若有效,DDR 变频导致问题的概率较高。

  3. 将 DDR 频率降至稳妥值(如 200M),若有效,大概率是 DDR 信号质量问题。

2.2 引起 DDR 问题的几个主要原因

电源问题

  1. layout 上电容不够,电容摆放位置离芯片太远,电容分布不合理。

  2. 电源 feedback 回路没按要求从末端引回到 PMU/DC-DC 端。

  3. 敷铜未按 RK 的 layout 规则处理,导致电源路径过窄。

  4. LQFP 封装芯片正下方的 GND 需堆锡以保证良好接地,否则影响芯片内部电源质量及散热。

信号质量问题

  1. 走线不等长。RK 大部分平台无 eye training,不等长走线会直接影响 DDR 的 setup/hold time。

  2. 线间距过窄,导致严重串扰问题。

  3. T 型拓扑结构分支不等长,恶化信号边沿,使其非单调。

  4. 信号参考层回路不完整。敷铜时间隙过大,过孔隔断参考层,降低信号质量并引发兼容性问题。

颗粒问题

  1. 白牌颗粒未经测试,可能为原厂淘汰品,良率无法保证。

  2. 特殊渠道颗粒可能存在驱动强度偏弱等问题。

  3. hynix 4Gb C die DDR3(如 H5TQ4G63CFR),kernel 3.10 早期代码需打补丁。(后期修复,判断标准:进入 kernel 正常则为已修复,否则需补丁)。

2.3 解决 DDR 问题的一些手段

解决 DDR 问题总的办法是找规律,例如尝试定位死机规律(如特定频率、休眠唤醒时间关联等),通过定频、尝试不同频率、抬压、调整驱动强度等方法排查,缩小问题范围。

2.3.1 对于在 DDR 初始化中报错的问题

  1. 若出现 "rd addr 0x... = 0x..." 报错,基本为焊接问题,可用 “Rockchip 平台 DDR 测试工具” 定位问题点。

  2. 若报 “16bit error!!!“ 或 ”W FF != R“,表明 DDR 基本读写错误,焊接问题概率较大。

  3. 打印 "unknow device" 说明颗粒读写异常,无法探测 dram 类型,需检查焊接问题。

  4. 对非 2 的 n 次幂容量颗粒(如 768MB、1.5GB、3GB 等),若代码兼容性不足,可更新至最新 loader,仍异常则联系 DDR 工程师分析。

  5. DDR loader 中报错,多数为焊接问题,可使用 ddr 测试工具的焊接专项,选择对应容量测试项分析。

2.3.2 查看 loader 中 DDR 初始化部分 log 信息

检查 loader 中 DDR 初始化 log 里的 DDR 容量、行列 bank、颗粒类型位宽信息是否正确,错误信息可能引发 DDR 问题。

示例说明:

  • 第一行为 DDR 版本号,第 3 行为 DDR 频率,第 4 行为 DDR 类型。

  • 第五行从左到右:系统位宽数、列数、bank 数、行数、片选数、颗粒位宽数、总容量。

  • 第 7 行 “OUT” 打印后,表明 DDR 初始化成功退出,后续为 usbplug 或 miniloader 日志。

  • 注意:Die Bus-Width 比实际大无问题,比实际小会引发死机。

DDR Version v1.06 20171026  
In  
300MHz  
DDR3  
Bus Width=32 Col=10 Bank=8 Row=15 CS=2 Die Bus-Width=16 Size=2048MB  
mach:14  
OUT  
Boot1 Release Time: 2017-06-12, version: 2.37  

2.3.3 看显示是否正常

系统死机时,CPU 停止工作,但 vop 仍会重复从 DDR 中读取数据并显示在屏幕上。通过显示情况可初步判断 DDR 状态:

  1. 显示正常:表明此时 DDR 能够被正常访问,但无法完全排除死机与 DDR 无关。

  2. 显示异常

    • 若出现“花屏”,可能是 DDR 变频过程中死机导致 DDR 处于不可访问状态,可尝试通过定频排查;也可能是电源问题引发 DDR 控制器逻辑异常。

    • 若出现“重影”,可能是板⼦参考层不完整,导致 DDR3异常。可尝试提⾼ VCC_DDR 电压到 1.6V 或者将颗粒的 dll bypass 来解决。

2.3.4.排查是否是电源问题

  1. 固定 cpu/gpu 到一个较低的频率,适当提高 arm/logic 电压,查看是否有改善。若有改善,可能是电源问题。

  2. 审核 layout,查看电源上是否存在问题。

  3. 测量电源纹波,检查是否存在问题。

2.3.5 排查是否信号质量问题

  1. 降低 DDR 频率,查看是否有明显改善。若有改善,很可能是信号质量问题。

  2. 让硬件同事审核 layout 和 gerber 文件,检查走线是否合理,参考层是否完整。

  3. 适当加强或减弱驱动强度/odt 强度,查看是否有改善。

  4. 改变 RZQ 的阻值,查看是否有改善。如遇到个别 220ball 的 lpddr3,需将 RZQ 改小或去掉才能恢复正常。

2.3.6 对于白牌颗粒

若排查过电源、信号质量等均无问题,可怀疑存储单元有问题,尝试以下处理方法:

  1. 尝试关闭 pd_idle、sr_idle,查看是否有效果。

  2. 对于死机时屏幕有“重影”的颗粒,尝试 bypass DRAM DLL,查看是否有效果。

  3. 存储单元有问题的颗粒,可通过 DDR 测试工具测试(如 DDR 测试工具 March 专项,检测概率较大)。注意:DDR 测试工具仅是辅助工具,测试 pass 不代表颗粒或板子稳定性一定无问题。