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标准进入自刷新(SR:SELF 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
- DRA642的DDR自刷新功能
- GridView控件自带的不刷新页面的功能
- 使用android自带的下拉刷新效果实现页面下拉刷新功能
- 详解SDR/DDR/DDR2/SDRAM的功能及异同
- android官方自带下拉刷新功能
- .net自刷新的无刷新页面
- 内存的自动刷新与自刷新
- JS实现浏览器部分自带的前进后退及刷新等功能
- JS实现浏览器部分自带的前进后退及刷新等功能
- Android开发-UI控件:为ListView,GirdView,etc...添加系统自带的下拉刷新功能
- DDR 内存的速度
- DDR的基本原理
- DDR的几个概念
- DDR的PCB设计
- 调试LATTICE 的DDR
- DDR的几个概念
- DDR的一点概念
- 使用google自带包实现下拉刷新功能
- NSSet
- Greenplum配置系统和参数
- 最强日期验证
- Add, Remove, and Search Packages in Python with pip
- activemq: jms api实现queue和topic生产者、消费者demo及spring api 实现queue生产者、消费者
- DRA642的DDR自刷新功能
- Linux下的时间--struct tm, mktime, localtime, strftime
- NET中Barcode Library的应用二
- MYSQL中利用select查询某字段中包含以逗号分隔的字符串的记录方法
- UVa 294 - Divisors
- Linux C编程 使用scanf时清除键盘缓冲区 -- 清除输入缓存
- Android中当数据库需要更新时我们该怎么办
- 求网上书店系统(struts+mysyl)
- Session的生命周期