DRA642的DDR自刷新功能

来源:互联网 发布:淘宝店铺一键装修软件 编辑:程序博客网 时间:2024/05/19 16:05

DDR自刷新是一个在移动设备领域非常常用的特性。

网上很难找到TI DM814X系列CPU的DDR自刷新说明,甚至FAE都没有触及过这个特性。

因为这个系列的CPU本身不是为移动设备设计的,所以多数使用者不会涉及这个部分。

这也是我写这篇文档的原因,如果确实需要用到这个特性,可以参考一下。

另外,没有用过这个特性的人,也可以了解一下。

DDR处于自刷新状态时,CPU无法访问DDR数据,DDR数据不丢失,但DDR功耗会比正常使用时低很多。

具体自刷新功耗(待机功耗)可以通过DATASHEET查到。

通常在DDR DATASHEET里有一章:Electrical Characteristics – IDD Specifications

Idd就是 DDR在不同状态下的电流值。

例如,我当前使用的美光DDR3,REV K的IDD说明:


这么多IDD,都是那些状态呢?美光的说明书里没有,我在三星的说明书里找到描述:







所以,IDD6一般就是自刷新电流。

DDR3标准进入自刷新(SRSELF REFRESH)命令:CLK高时,CKE=自动刷新(AR: AOTU REFRESH)命令+CKE低。

退出自刷新命令:正常读命令或自动刷新命令,

进入自刷新后,必须有一定时间不能下退出自刷新命令。

退出自刷新后,必须有一定时间不能有写入操作。

按照文档 的说明:

 dm814xReferenceManual_sprugz8e.pdf  

tms320dm8148.pdf

The memory controller automatically puts the SDRAM into self-refresh  after the  memory  controller  is  idle  for  SR_TIM  number  of  DDR  clock  cycles  and  the  LP_MODE  field is set to 2. The LP_MODE and SR_TIM fields can be programmed in the Power Management Control register(PMCR).

In self-refresh  mode,  the  memory  controller  automatically  stops  the clocks DDR[x]_CLK to the SDRAM. The memory controller maintains DDR[x]_CKE  low to  maintain the self-refresh state. When the SDRAM is in self-refresh, the memory  controller services register accesses  as normal. If the LP_MODE  field is set  not  equal to 2, or an SDRAM access is requested while it is in self-refresh, and T_CKE + 1 cycles have elapsed since the SELF-REFRESH command  was  issued, the  memory  controller will bring the  SDRAM out of self-refresh. The value of T_CKE is taken from SDRAM Timing 2 register.

按字面上理解,进入和退出自刷新的配置贼简单:


SR_TIM: 自刷新前*个时钟,DDR空闲无访问,才可进入自刷新

LP_MODE:设置为2,表示要求进入自刷新状态

T_CKE:退出自刷新后*个时钟,不允许访问DDR(所有访问会阻塞)

事情好像很简单,要进入自刷新把SR_TIM设置为0,LP_MODE设置为2,立即进入自刷新状态!!!


狗屎!要是这么简单,我需要搞一天才搞定这个问题,查那么多资料?去你妹的DATASHEET!


真实的情况是这样的:

一切尝试失败之后,我在GIT上找到了TI提供的DDR自刷新代码,然后,按照我自己的应用方式移植到了UBOOT上,而这段代码远比DATASHEET上说明的复杂。

最终,DDR自刷新成功了。


分析一下源码,其实源码上的说明以及很详细了。先贴上我在UBOOT下的源码。

#define UART_LCR_OFFSET             0xC#define UART_TX_FIFO                0x0#define EMIF_CLK_GATE_OFFSET                0x0694#define TI81XX_SCM_BASE                0x48140000#define TI814X_DMM_LISA_MAP_OFFSET          0x0040#define TI814X_DMM_BASE                     0x4E000000#define TI814X_DMM_LISA_MAP_0         (TI814X_DMM_BASE + TI814X_DMM_LISA_MAP_OFFSET)#define TI814X_DMM_LISA_MAP_1         (TI814X_DMM_BASE + TI814X_DMM_LISA_MAP_OFFSET + 0x4)#define TI814X_DMM_LISA_MAP_2         (TI814X_DMM_BASE + TI814X_DMM_LISA_MAP_OFFSET + 0x8)#define TI814X_DMM_LISA_MAP_3         (TI814X_DMM_BASE + TI814X_DMM_LISA_MAP_OFFSET + 0xc)#define TI814X_PLL_BASE                0x481C5000#define ADPLLJ_CLKCTRL0x4#define ADPLLJ_STATUS0x24#define TI814X_DDR_PLL_CONTROL_OFFSET(TI814X_PLL_BASE + ADPLLJ_CLKCTRL)#define TI814X_DDR_PLL_STATUS_OFFSET(TI814X_PLL_BASE + ADPLLJ_STATUS)void do_ddr_self_refresh(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]){    register int count = 0;    register int delay = 0;puts("flush dcache all\n");    flush_dcache_all();puts("disable cache\n");    dcache_disable ();    icache_disable ();__raw_writel(__raw_readl(UART0_BASE+UART_LCR_OFFSET) & 0x7F, UART0_BASE+UART_LCR_OFFSET);    __asm__ __volatile__("": : :"memory");    puts("going halt system, ddr self refresh\n");__raw_writel(0x200, EMIF4_0_SDRAM_PMCR);__raw_writel(0x31, UART0_BASE+UART_TX_FIFO);    while (__raw_readl(TI81XX_SCM_BASE + EMIF_CLK_GATE_OFFSET)&0xc != 0)    {        ;    }    if (__raw_readl(TI81XX_SCM_BASE + EMIF_CLK_GATE_OFFSET)&0xc != 0)    {    //puts("CKE pull down fail,CKE high\n");    __raw_writel(0x32, UART0_BASE+UART_TX_FIFO);    __raw_writel(0x32, UART0_BASE+UART_TX_FIFO);        reset_cpu(0);    }    else    {    __raw_writel(0x32, UART0_BASE+UART_TX_FIFO);        __raw_writel(0, TI814X_DMM_LISA_MAP_0);        __raw_writel(0, TI814X_DMM_LISA_MAP_1);        __raw_writel(0, TI814X_DMM_LISA_MAP_2);        __raw_writel(0, TI814X_DMM_LISA_MAP_3);            __raw_writel(0x3, TI81XX_SCM_BASE + EMIF_CLK_GATE_OFFSET);}        if (__raw_readl(TI81XX_SCM_BASE + EMIF_CLK_GATE_OFFSET) != 0x3)    {    __raw_writel(0x33, UART0_BASE+UART_TX_FIFO);    __raw_writel(0x33, UART0_BASE+UART_TX_FIFO);        reset_cpu(0);    }    else    {    __raw_writel(0x33, UART0_BASE+UART_TX_FIFO);    /*    * DDR PLL bypass, clear bit 0    */        __raw_writel(__raw_readl(TI814X_DDR_PLL_CONTROL_OFFSET)&0xFFFFFFFE, TI814X_DDR_PLL_CONTROL_OFFSET);    }    while (__raw_readl(TI814X_DDR_PLL_STATUS_OFFSET)&0x600 != 0)    {        ;    }    if (__raw_readl(TI814X_DDR_PLL_STATUS_OFFSET)&0x600 != 0)    {    //puts("DDR PLL in Bypass fail\n");    __raw_writel(0x34, UART0_BASE+UART_TX_FIFO);    __raw_writel(0x34, UART0_BASE+UART_TX_FIFO);        reset_cpu(0);    }    else    {    __raw_writel(0x34, UART0_BASE+UART_TX_FIFO);    }        for (count=0;count<20;count++)    {        for (delay=0;delay<10000000;delay++)            __asm__ __volatile__("": : :"memory");    __raw_writel(0x35, UART0_BASE+UART_TX_FIFO);    }    /*    * resume DDR    */    __raw_writel(0x36, UART0_BASE+UART_TX_FIFO);    /*    * DDR PLL exit bypass, set bit 0, clear bit 23    */    __raw_writel(__raw_readl(TI814X_DDR_PLL_CONTROL_OFFSET)&(~(1<<23))|0x1, TI814X_DDR_PLL_CONTROL_OFFSET);    /*    * wait for DDR PLL lock, frequency is +/- 1% close to the target frequency    */    while (__raw_readl(TI814X_DDR_PLL_STATUS_OFFSET)&0x600 != 0x600)    {        ;    }    __raw_writel(0x37, UART0_BASE+UART_TX_FIFO);    /*    * wait for DDR PLL lock to the target frequency, > 1ms    */    for (count=0;count<20000;count++)    {        __asm__ volatile ("nop");    }    __raw_writel(0x2, TI81XX_SCM_BASE + EMIF_CLK_GATE_OFFSET);/* Wait for DLL to lock before configuring dmm  > 10ms*/    for (count=0;count<200000;count++)    {        __asm__ volatile ("nop");    }    /* reconfigure_dmm     */__raw_writel(0x0, TI814X_DMM_LISA_MAP_0);__raw_writel(0x0, TI814X_DMM_LISA_MAP_1);__raw_writel(0x80500100, TI814X_DMM_LISA_MAP_2);//datasheet 1216page__raw_writel(0xA0500100, TI814X_DMM_LISA_MAP_3);while (__raw_readl(TI814X_DMM_LISA_MAP_0) != 0);while (__raw_readl(TI814X_DMM_LISA_MAP_1) != 0);while (__raw_readl(TI814X_DMM_LISA_MAP_2) != 0x80500100);while (__raw_readl(TI814X_DMM_LISA_MAP_3) != 0xA0500100);    __raw_writel(0x38, UART0_BASE+UART_TX_FIFO);/* Prevent ddr going in to self refresh in idle time */__raw_writel(0x0, EMIF4_0_SDRAM_PMCR);    __raw_writel(0x39, UART0_BASE+UART_TX_FIFO);        while (__raw_readl(TI81XX_SCM_BASE + EMIF_CLK_GATE_OFFSET)&0x4 != 0x4)    {        ;    }    __asm__ __volatile__("": : :"memory");    __raw_writel(0x30, UART0_BASE+UART_TX_FIFO);puts("going halt system, ddr self refresh\n");return ;}U_BOOT_CMD(SR,CONFIG_SYS_MAXARGS,0,do_ddr_self_refresh,"ddr self_refresh","ddr self_refresh\n");
页面没法接受中文注释,都是乱码,直接删除中文注释。
代码中只是将DDR配置为自刷新,CPU待机了20秒左右的时间,退出自刷新继续执行UBOOT。

实际应用中,DDR配置自刷新以后,CPU直接掉电,启动时根据DDR自刷新状态执行正常的启动或退出自刷新启动。

这段代码的基本含义是:

回写cache-->配置UART(用来调试,因DDR不能使用,所以不能调用其他函数输出,直接操作FIFO寄存器)

-->写入SR命令-->等待CKE(时钟使能)为低-->禁止CPU对外部存储访问(EMIF)-->关闭EMIF模块

这一段是进入自刷新的步骤,退出自刷新时反向操作。

通常在最基本BIOS的调试,连最小系统都不能正常启动的情况下,用JTAG是最方便的,不过没仿真器就玩完~

所以,这种情况下,一般都是用一个GPIO控制LED看闪灯,或者直接对UART FIFO操作,一样可以看串口打印信息。


TI在LINUX的待机源码:

http://arago-project.org/git/projects/?p=linux-omap3.git;a=commit;h=79ff68a25c0fcc3cc8fb51c15bdf6bbbb8cc7769

0 0
原创粉丝点击