一般是DDR3控制器有,采用fly-by拓扑,CK与DQS的时延难以控制,用ddr traning用于调整两者之间的时延,已达到最佳采样点的目的。窗口值反映出当前CK与DQS的偏移值,窗口越大,窗口越居中越好。

https://blog.csdn.net/hierro_zs/article/details/53472080。

00430: void start_ddr_training(unsigned int base)
00431: {
00432: #ifdef CONFIG_SVB_ENABLE    #define CONFIG_SVB_ENABLE,使能SVB动态调压
00433: start_svb(); //实际上分析没什么意思,实际你又不去改,不外乎设置动态电压!不管他就是!
00434: #endif
00435:
00436: hi3519_ddr_init(); //DDR初始化!这个是重点!
00437:
00438: #ifdef CONFIG_DDR_TRAINING_V2   //再次进行training?
00439:     int ret = 0;
00441:     /* ddr training function */
00442:     ret = ddr_sw_training_if(0);
00443:     if (ret) reset_cpu(0);
00445: #endif
00446: /*the value should config after trainning, or
00447: it will cause chip compatibility problems*/
00448: writel(0x401, DDR0_BASE_REG + 0x28);

#define DDR0_BASE_REG 0x12068000

00450: #ifdef DDR_SCRAMB_ENABLE
00451: /* enable ddr scramb */
00452: ddr_scramb();   //使能DDR的扰码功能。
00453: #endif
00454: } ? end start_ddr_training ?

00400: static void start_svb(void)
00401: {
00402: unsigned int hpm_core;
00403: unsigned int hpm_cpu;
00404: unsigned int hpm_ddr;
00405: unsigned int hpm_mda;
00406: unsigned int otp_hpm_core;
00407: unsigned int otp_hpm_mda;
定义了几个电。如core、cpu、ddr、media等电参数。
00409: /*set start voltage, CORE/MDA0 is 0.9v*/设置core电和media电为0.9V。

#define HI_PMC_CTL_REG   0x120a0000

#define HPM_CORE_VOL_REG    (HI_PMC_CTL_REG + 0x0)

#define HPM_MDA_VOL_REG     (HI_PMC_CTL_REG + 0xc)
00410: writel(0x2f00c7, HPM_CORE_VOL_REG);           0x120a0000
00411: writel(0x2e00c7, HPM_MDA_VOL_REG);      0x120a000c


这两个寄存器都能找到,配置的是占空比和周期。实际的占空比D=0x2f/0xc7=23.6%
00414: start_hpm(&hpm_core, &hpm_cpu, &hpm_ddr, &hpm_mda);//这个是动态调压的使能,且将数值平均值赋值给参数
00416: /* modify hpm value according to otp hpm value */
00417: otp_hpm_core = readl(OTP_CORE_HPM_IDDQ) & 0x3ff;

#define OTP_CORE_HPM_IDDQ 0x12032000 //数据手册找不到这个寄存器,意思就是取出后10位的值。
00418: if (otp_hpm_core && otp_hpm_core < hpm_core)//如果取出的数值不为0,且小于读取的数值,则赋值
00419: hpm_core = otp_hpm_core;    //一个是寄存器读出来的,一个是设置的,一般情况下,读出来的比较真实,读出来的结果肯定小于等于设置的值,我们比较这两个值,取其中较小的。感觉分析起来没什么意义!
00421: otp_hpm_mda = readl(OTP_MDA_HPM_IDDQ) & 0x3ff;
00422: if (otp_hpm_mda && (otp_hpm_mda + 15) < hpm_mda)
00423: hpm_mda = otp_hpm_mda + 15;
功能与hpm_core类似吧!
00425: set_hpm_core_volt(hpm_core);  //设置动态电压!
00426: set_hpm_mda_volt(hpm_mda);
00427: } ? end start_svb ?
00428: #endif

00215: void hi3519_ddr_init(void)
00216: {
00217: unsigned int initctrl_rank0, initctrl_rank1, rank_num;

科普一下:DRAM由于构造简单,高密度,作为电脑内部的主要记忆体非常适合。但由于主存通常放在CPU之外,从工厂出来的颗粒需要封装和组合之后才可以和CPU相连,因此从CPU到DRAM颗粒之间依次按层级由大到小分为channel > DIMM > rank > chip > bank > row/column。

rank指的是连接到同一个cs(Chip Select,片选)的所有内存颗粒chips,内存控制器能够对同一个rank的所有chips同时进行读写操作,而在同一个rank的chip也分享同样的控制信号。

若rank1和rank2共享同一组addr/command信号线,利用cs片选线选择欲读取或是写入的那一组,之后将存储内容经由MUX多路器送出。很多人有错误的理解,常以chip的数量或是以内存模组的单/双面对rank进行判断,但实际上应该以内存控制器和内存颗粒的规格进行判断。目前家用PC的内存控制器通道绝大部分是64bit宽,内存颗粒的位宽是8bit。因此8颗颗粒就可以满足内存控制器的需求,也就是一组rank。但偶尔也有以16bit位宽的内存颗粒制成的内存模组,此时4个颗粒chip就是一组rank。

比如3519v101,DDRC控制器有32bit数据线,4个DQS,每个DQS选通8bit,2个DQS对应一个4Gb的DDR芯片,其实就只有一个rank。
00219: /* Read rank0 HW DDR training value */读取rank0的硬件DDR训练数据!

00220: initctrl_rank0 = readl(REG_BASE_SCTL + REG_SC_GEN32);       //数据手册找不到!

#define SYS_CTRL_REG_BASE   0x12020000

#define REG_BASE_SCTL       SYS_CTRL_REG_BASE

#define REG_SC_GEN32        0xa0

#define REG_SC_GEN33        0xa4
00221: /* Read rank1 HW DDR training value */
00222: initctrl_rank1 = readl(REG_BASE_SCTL + REG_SC_GEN33);
00223: /* 0: one rank, 1: two rank */
00224: rank_num = (readl(REG_DDR_DMC0_CFG_DDRMODE) >> 20) & 0x3;

#define REG_DDR_DMC0_CFG_DDRMODE    (DDR0_BASE_REG + 0x50)

#define DDR0_BASE_REG         0x12068000

主要是读出有几个rank!
00226: if (initctrl_rank0 || (initctrl_rank1 && rank_num))

{
00227:     unsigned int timing;
00228:     unsigned int timing_backup;
00229:     unsigned int rank_ctrl;
00230:
00231: /* Save the Auto-self refresh timing */
00232: timing = readl(REG_DDR_DMC0_CFG_TIMING2); //读

#define REG_DDR_DMC0_CFG_TIMING2 (DDR0_BASE_REG + 0x108)


00233: timing_backup = timing & 0x7FF;    //自动刷新周期读出来,保存
00234:
00235: /* Set Auto-self refresh timing to 0 */
00236: writel((timing & 0xFFFFF800), REG_DDR_DMC0_CFG_TIMING2);  //自动刷新禁止,改写
00238: rank_ctrl = readl(REG_DDR_PHY0_TRAINCTRL);   //找不到这个寄存器,读

#define DDR0_PLL_REG    0x1206c000

#define REG_DDR_PHY0_TRAINCTRL   (DDR0_PLL_REG + 0x48)
00239: if (initctrl_rank0)

{
00240:     /* set bit[0:3] = 0 to training rank0 */
00241:      writel(rank_ctrl & 0xfffffff0, REG_DDR_PHY0_TRAINCTRL);    //改写
00242:     /* Start HW DDR training */
00243:     writel(initctrl_rank0, REG_DDR_PHY0_INITCTRL);
00244:     /* wait HW DDR training finish */
00245:     do {
00246:          initctrl_rank0 = readl(REG_DDR_PHY0_INITCTRL) & 0x7FFFF;
00247:          } while(initctrl_rank0);
00248: }
//这个if条件的意思就是将ddr training数值清零,然后运行一下得到一个数值,怎样操作的呢?

----》个人认为是,DDRC自己进行的一次计数,也就是DDRC启动一次时将某个寄存器清零,然后开始计数,等到DDRC的信号到达DDR时,停止计数然后将某个寄存器设置,计算出时间,记录下来,有点像中断触发。这个只是猜测,有可能是多个数据线,因为海思给的寄存器不全,所以不知道怎么搞得。
00250: if (initctrl_rank1 && rank_num)

{
00251:     /* set bit[0:3] = 1 to training rank1 */
00252:     writel((rank_ctrl & 0xfffffff0) | 0x1, REG_DDR_PHY0_TRAINCTRL);
00253:     /* Start HW DDR training */
00254:     writel(initctrl_rank1, REG_DDR_PHY0_INITCTRL);
00255:      /* wait HW DDR training finish */
00256:     do {
00257:     initctrl_rank1 = readl(REG_DDR_PHY0_INITCTRL) & 0x7FFFF;
00258:     } while(initctrl_rank1);
00259: }

同样的就行training操作,这个training是训练的意思,这个有点意思,海思这个做的还可以,可以结合硬件进行分析,目的是减小总线间的时差。如果寄存器再详细点就更好了!
00260: writel(rank_ctrl, REG_DDR_PHY0_TRAINCTRL);
00260: /* restore */将训练的数据保存起来
00261:
00262: /* Exit Auto-self refresh */
00263: ddr_set_sref(0x2);

00265: /* Restore the Auto-self refresh timing */
00266: timing = readl(REG_DDR_DMC0_CFG_TIMING2);  //读出来
00267: timing = (timing & 0xFFFFF800) | timing_backup ;//与刚开始时间或一下,相当于取最大值
00268: writel(timing, REG_DDR_DMC0_CFG_TIMING2);//写进去,遵循读改写
00269: } ? end if initctrl_rank0||(init... ?

00271: /* Clear fifo */
00272: initctrl_rank0 = readl(REG_DDR_PHY0_INITCTRL);
00273: initctrl_rank0 = initctrl_rank0 | (1<<15);
00274: writel(initctrl_rank0, REG_DDR_PHY0_INITCTRL);
00275: initctrl_rank0 = readl(REG_DDR_PHY0_INITCTRL);
00276: initctrl_rank0 = initctrl_rank0 & ~(1<<15);
00277: writel(initctrl_rank0, REG_DDR_PHY0_INITCTRL);
00278: } ? end hi3519_ddr_init ?

3519DDR初始化这段代码是是什么意思呢?我们来看下:

1)读了rank0和rank1的硬件训练的数值

2)读了rank的数目

3)不管怎么先对rank0进行操作:读了刷新时间并保存下来,清空刷新时间,进行DDR training训练

4)如果有rank1,则对rank1进行同样的操作

5)然后将训练的数据写到相应的寄存器中

6)设置自动刷新时间

7)清除fifo

看完之后比较麻木,因为有些寄存器,你是看不到的,海思没给你,只有猜。

00200: void ddr_set_sref(unsigned int val)
00201: {
00202: int cnt = 0;
00203:
00204: writel(val, REG_DDR_DMC0_CTRL_SREF);

#define DDR0_BASE_REG         0x12068000

#define REG_DDR_DMC0_CTRL_SREF   DDR0_BASE_REG
00205: while ((!(readl(REG_DDR_DMC0_IN_SREF) & 0x1)) && (cnt < 10000))

//设置之后等待设置完成!如果超时,就cpu重启

{
00206: cnt++;
00207: }
00209: if (cnt >= 10000)

{
00210: reset_cpu(0);
00211: }
00212: }

00313: int ddr_sw_training_if(void *ddrtr_result)
00314: {
00315: return DDR_SW_TRAINING_FUNC(ddrtr_result);
00316: }

00022: int ddr_sw_training_func(void *ddrtr_result)
00023: {
00024: unsigned int base_dmc = DDR_REG_BASE_DMC0;  //DDRC内DMC模块寄存器

#define DDR_REG_BASE_DMC0   0x12068000
00025: unsigned int base_phy = DDR_REG_BASE_PHY0;

#define DDR_REG_BASE_PHY0   0x1206c000
00026: int result = 0;

//寄存器的读取,因为PHY0寄存器找不到,看不到具体内容
00027: unsigned int auto_ref_timing = REG_READ(base_dmc + DDR_DMC_TIMING2);
00028: unsigned int misc_scramb = REG_READ(base_phy +DDR_PHY_MISC);
00029: unsigned int dramcfg_ma2t = REG_READ(base_phy +DDR_PHY_DRAMCFG) & PHY_DRAMCFG_MA2T;
00031: unsigned int acphyctl = REG_READ(base_phy +DDR_PHY_ACPHYCTL4);
00033: DDR_VARIABLE_DECLARE(swapdfibyte_en); //声明下几片DDR
00035: /* check sw ddr training enable */
00036: if (DDR_BYPASS_ALL_MASK == REG_READ(DDR_REG_BASE_SYSCTRL+ SYSCTRL_DDR_TRAINING_CFG))

#define DDR_REG_BASE_SYSCTRL      0x12020000

#define SYSCTRL_DDR_TRAINING_CFG     0x90

这个也就是读取训练的状态,如果训练过了,就跳过,如果没有就继续训练。
00038: return 0;
00039:
00040: ddr_training_start();       //这个就是判断没有训练过,那就开始训练,空函数,不分析了,一般用不到啊!
00041:
00042: #ifdef DDR_TRAINING_STAT_CONFIG
00043: /* clear stat register清除训练的状态寄存器 */
00044: REG_WRITE(0x0, DDR_REG_BASE_SYSCTRL +SYSCTRL_DDR_TRAINING_STAT);
00045: #endif
00046:
00047: /* disable scramb 关闭加扰的地址模式*/
00048: REG_WRITE(misc_scramb & PHY_MISC_SCRAMB_DIS, base_phy + DDR_PHY_MISC);
00051: /* disable rdqs swap关闭读写数据选通扫描:RDQS(为8bit位宽芯片增设的专用DQS读取信号,主要用来简化一个模组中同时使用4与8bit位宽芯片时的控制设计) */
00052: DDR_DQSSWAP_SAVE_FUNC(swapdfibyte_en, base_phy);
00053:
00054: /* check hardware gating 检查硬件的门控,然后标记状态*/
00055: if (REG_READ(base_phy + DDR_PHY_PHYINITSTATUS) & PHY_INITSTATUS_GT_MASK)

{
00057: DDR_FATAL("PHY[%x] hw gating fail.", base_phy) ;
00058: ddr_training_stat(DDR_ERR_HW_GATING, base_phy, -1, -1);
00060: }
00080: #ifdef DDR_WL_TRAINING_CONFIG
00081: /* write leveling */
00082: if (!ddr_training_check_bypass(DDR_BYPASS_WL_MASK) ) 如果没有训练则继续,如果有,则跳过

{
00083: /* disable auto refresh 关闭自动刷新*/
00084: ddr_training_set_timing(base_dmc, auto_ref_timing & DMC_AUTO_TIMING_DIS);
00086: result += ddr_write_leveling(base_dmc, base_phy);
00087: /* enable auto refresh开启自动刷新 ,已经看不懂了,问下高人再来看*/
00088: ddr_training_set_timing(base_dmc, auto_ref_timing);
00089: }
00090: #endif
00092: #ifdef DDR_DATAEYE_TRAINING_CONFIG
00093: /* dataeye */
00094: if (!ddr_training_check_bypass(DDR_BYPASS_DATAEYE_MASK)) {
00095: ddr_ddrt_init(base_dmc, DDR_DDRT_MODE_DATAEYE) ;
00096: result += ddr_dataeye_training(base_dmc, base_phy, ddrtr_result, DDR_DATAEYE_NORMAL_ADJUST);
00098: }
00099: #endif
00100:
00101: #ifdef DDR_HW_TRAINING_CONFIG
00102: /* hardware read */
00103: if (result && !ddr_training_check_bypass(
00103: DDR_BYPASS_HW_MASK)) {
00104: if (!dramcfg_ma2t) /* set 1T */
00105: REG_WRITE(0x0, base_phy +DDR_PHY_ACPHYCTL4);
00107: result = ddr_hw_training(base_dmc, base_phy);
00108: if (!dramcfg_ma2t) /* restore */
00109: REG_WRITE(acphyctl, base_phy + DDR_PHY_ACPHYCTL4);
00111: result += ddr_dataeye_training(base_dmc, base_phy, ddrtr_result, DDR_DATAEYE_ABNORMAL_ADJUST);
00113: }
00114: #endif
00115:
00116: #ifdef DDR_MPR_TRAINING_CONFIG
00117: /* mpr */
00118: if (result && !ddr_training_check_bypass(
00118: DDR_BYPASS_MPR_MASK)) {
00119: result = ddr_mpr_training(base_dmc, base_phy);
00120: result += ddr_dataeye_training(base_dmc, base_phy, ddrtr_result, DDR_DATAEYE_ABNORMAL_ADJUST);
00122: }
00123: #endif
00125: #ifdef DDR_GATE_TRAINING_CONFIG
00126: /* gate */
00127: if (!ddr_training_check_bypass(DDR_BYPASS_GATE_MASK)) {
00128: ddr_ddrt_init(base_dmc, DDR_DDRT_MODE_GATE);
00129: /* disable auto refresh */
00130: ddr_training_set_timing(base_dmc,
00131: auto_ref_timing & DMC_AUTO_TIMING_DIS);
00133: if (!dramcfg_ma2t) /* set 1T */
00134: REG_WRITE(0x0, base_phy +
00134: DDR_PHY_ACPHYCTL4);
00136: result += ddr_gate_training(base_dmc, base_phy);
00138: /* enable auto refresh */
00139: ddr_training_set_timing(base_dmc, auto_ref_timing);
00141: if (!dramcfg_ma2t) /* restore */
00142: REG_WRITE(acphyctl, base_phy +
00142: DDR_PHY_ACPHYCTL4);
00143: }
00144: #endif
00146: #ifdef DDR_VREF_TRAINING_CONFIG
00147: if (!ddr_training_check_bypass(
00147: DDR_BYPASS_VREF_MASK)) {
00148: ddr_ddrt_init(base_dmc, DDR_DDRT_MODE_DATAEYE)
00148: ;
00149: result += ddr_vref_training(base_dmc, base_phy
00149: ,
00150: ddrtr_result);
00151: }
00152: #endif
00154: /* restore scramb */
00155: REG_WRITE(misc_scramb, base_phy + DDR_PHY_MISC);
00157: /* restore rdqs swap */
00158: DDR_DQSSWAP_RESTORE_FUNC(swapdfibyte_en, base_phy);
00160: if (!result)
00161: ddr_training_suc();
00162: return result;
00163: } ? end ddr_sw_training_func ?

00098: static int ddr_scramb(void)
00099: {
00104: unsigned int reg_val;
00105:
00106: /* open rsa and trng clock */
00107: reg_val = readl(CRG_REG_BASE + REG_PERI_CRG56);
00108: reg_val |= TRNG_CLK_ENABLE;
00109: writel(reg_val, CRG_REG_BASE + REG_PERI_CRG56);
00110:
00111: /* set ddrc to do self-refurbish */
00112: reg_val = 0x1;
00113: writel(reg_val, REG_BASE_DDRC +DDRC_CTRL_SREF_OFST);
00114:
00115: /* wait the status of ddrc to be sef-refurbish */
00116: do {
00117:     reg_val = readl(REG_BASE_DDRC +
00117:      DDRC_CURR_FUNC_OFST);
00118: } while (!(reg_val & 0x1));
00140: /* start ddr scrambling */
00141: ddr_scramb_start(0, 0);
00142:
00143: /* set ddrc to exit self-refurbish */
00144: reg_val = (0x1 << 1);
00145: writel(reg_val, REG_BASE_DDRC +DDRC_CTRL_SREF_OFST);
00146:
00147: /* wait the status of ddrc to be normal */
00148: do {
00149: reg_val = readl(REG_BASE_DDRC +DDRC_CURR_FUNC_OFST);
00150: } while (reg_val & 0x1);
00151:
00152: return 0;
00153: } ? end ddr_scramb ?

3.3519v101-uboot-start.S分析DDR初始化相关推荐

  1. linux内核ddr初始化,X-007-UBOOT-DDR的初始化(Bubblegum-96平台)

    X-007-UBOOT-DDR的初始化(Bubblegum-96平台) 作者:wowo 发布于:2016-7-21 22:47 分类:X Project 1. 前言 到目前为止,"X Pro ...

  2. 2014.4新版uboot启动流程分析

    原文 http://blog.csdn.net/skyflying2012/article/details/25804209 此处转载有稍作修改 最近开始接触uboot,现在需要将2014.4版本ub ...

  3. MT7621_移植篇(3) uboot编译+配置项分析

    U-Boot("通用引导加载程序",通常简称为U-Boot)是一种开源的主引导加载程序,用于嵌入式设备中打包引导设备操作系统内核的指令.它可用于多种计算机架构,包括68k.ARM. ...

  4. 基于IMX6Q的uboot启动流程分析(3):_main函数之relocate_code与board_init_r

    基于IMX6Q的uboot启动流程分析(1):uboot入口函数 基于IMX6Q的uboot启动流程分析(2):_main函数之board_init_f 基于IMX6Q的uboot启动流程分析(3): ...

  5. 嵌入式之uboot源码分析-启动第一阶段学习笔记

    注: 以下的内容来自朱老师物联网大讲堂uboot部分课件 Uboot启动第一阶段start.S执行步骤 1.头文件包含 <config.h>(x210的各种宏定义) <version ...

  6. 嵌入式之uboot源码分析-启动第二阶段学习笔记(下篇)

    接上部分---->嵌入式之uboot源码分析-启动第二阶段学习笔记(上篇) 注:如下内容来自朱老师物联网大讲堂uboot课件 3.2.14 CFG_NO_FLASH (1)虽然NandFlash ...

  7. uboot源码分析-启动第一阶段

    注:基于九鼎x210 uboot 在SourceInsight软件下 一.start.S引入 1.u-boot.lds中找到start.S入口 (1)在C语言中整个项目的入口就是main函数(这是C语 ...

  8. uboot源码分析(基于S5PV210)之启动第一阶段

    目录 一.start.S引入 1.u-boot.lds中找到start.S入口 2.SourceInsight中如何找到文件 3.SI中找文件技巧 二.start.S解析 1.不简单的头文件包含 2. ...

  9. u-boot启动流程分析(1)_平台相关部分

    转自:http://www.wowotech.net/u-boot/boot_flow_1.html 1. 前言 本文将结合u-boot的"board->machine->arc ...

最新文章

  1. vue中的minix
  2. Java集合(六):专用集合和遗留类
  3. zabbix触发器表达式
  4. oracle12c没有有sqlnet文件,Oracle的sqlnet.ora文件配置
  5. iOS 开发线程 gcd
  6. gzp解压命令 linux_Linux下最常用的压缩及解压缩命令
  7. 软齿面主要失效形式_齿轮4种常见故障原因,如何采取预防措施,避免齿轮失效...
  8. P图,还是阿逗比厉害(大师加持)
  9. 2018青岛ICPC ZOJ 4061: Magic Multiplication(模拟)
  10. 博弈的意思_身处博弈时代,我们更要读些历史
  11. 常用脚本语言Perl,Python,Ruby,Javascript一 Perl,Python,Ruby,Javascript
  12. 成功解决NavigationDuplicated: Avoided redundant navigation to current location:
  13. win7原版镜像_i3-8100装Win7没有集显驱动?驱动人生告诉你为什么
  14. 数组排序之冒泡法和选择法
  15. 2020年国家扶贫日“三产联动扶贫论坛”在京召开,杭州复杂美科技有限公
  16. 初始Vue响应式原理~~
  17. 网易相册助手--批量上传下载好帮手
  18. 学计算机应用用画画吗,宝宝学画画
  19. 设置cookies过期时间的几种方法(cookies随浏览器关闭而失效的方法)
  20. FSK非相干解调的原理

热门文章

  1. 数学(二)三角求导公式总结
  2. 《破茧成蝶——用户体验设计师的成长之路》一导读
  3. layui树形表格(treetable)列数据合计
  4. 文件夹加密软件推荐-文件夹加密超级大师
  5. 【Android入门】7、多媒体:用 NotificationChannel 和 NotificationManager 实现系统通知、播放音频和视频
  6. 中国针织行业营销动态及销售趋势预测报告(2022-2027年)
  7. Oracle数据库ORA-01589错误
  8. Silverlight技术应用,打造我的动感相册
  9. 彩票走势分析+大乐透 双色球复式中奖计算器
  10. doxygen常用代码注释标记示例说明