4667|1

32

帖子

1

TA的资源

一粒金砂(中级)

楼主
 

Helper2416跳到内存运行 [复制链接]

本帖最后由 微末凡尘 于 2014-7-29 23:37 编辑 Helper2416跳到内存运行 HELPER2416开发板助学计划 在谈这个问题之前,我们先回顾一下helper2416的启动方式,它分为两种启动方式,其一为IROM模式下的SD卡启动,这个我们暂不研究。另一个就是传统的nandflash启动。在使用nandflash启动的时候,地址的0x00000000起始处映射的是8KB的stepping stone,也就是图1最右边那个映射图中绿色的SRAM块。 为什么我们要提这个呢?我上次发了一个帖子,点亮了LED灯。那个程序就是利用helper2416官方提供的烧写工具,从SD卡启动,利用SD卡将nandflash里面的裸机代码放到stepping stone中。然后当我们切换回nandflash启动时,代码就在0x00000000开始的这个8K的stepping stone运行。由于我们代码量比较少,这样完全没有问题。但是一般的启动过程,都会进行一个搬移工作,也就是将代码搬移到内存中运行,所以最近就想着琢磨琢磨内存。Helper2416的板子有两种内存,一种是128M的,一种是64M的。我手上的是64M的DDR2版本的,就以这个为例来谈这个问题啦。 板子上面这块DDR2的芯片型号是HSPS5162GFR-S5C,这个手册在官方压缩包里面并没有给出,我找到一份,贡献给大家啦。 H5PS5162GFRseries(Rev1.2)_PDF_规格书.pdf (2.39 MB, 下载次数: 11) 在这个里面,有这样的描述 “512Mb DDR2 SDRAM” 说明这个芯片是512Mb即 64MB(这里小写的b代表bit-位,大写的B代表Byte-字节)。 接着我们当然是看我们的芯片手册里面的相关部分了。芯片手册中,内存相关章节是第6章,MOBILE DRAM CONTROLLER 。由于大家手上都有英文手册,我这里就不再粘贴出来了。OVERVIEW里面描述,S3C2416支持3种内存,1SDRAM(C传统的2440多采用),2DDR(一般市面上的6410开发板采用这种内存),3DDR2(Helper2416采用的就是这种,市面上一些210开发板也是使用的这种内存。)。2416这个DRAM控制器有2个chip select signal(分别对应两个memory bank)这个如下图1,他们用来使用2个SDRAM,或者2个DDR,或者2个DDR2。但是需要特别注意的是,不能混合使用,仅能使用两种相同的内存。 图1 内存分布 图1中,我们可以看到SDRAM有nsCS0以及nsCS1,这就是我们提到的两个chip select signal。到底使用的是哪一个呢?我们可以从开发板的电路图2中看到,helper2416使用的是nsCS0.并且使用13根地址线,使用16根数据线。 图2 DDR2部分电路图 如何初始化内存呢,在我们芯片手册中给出了详细的说明。我们就根据这个流程来进行我们的初始化工作。 图3,内存初始化流程 我先一次性将代码粘贴出来,然后一段一段解析。
  1. .text
  2. .global init_mem
  3. #define ELFIN_MEMCTL_BASE 0x48000000
  4. #define BANKCFG_OFFSET 0x00
  5. #define BANKCON1_OFFSET 0x04
  6. #define BANKCON2_OFFSET 0x08
  7. #define BANKCON3_OFFSET 0x0c
  8. #define REFRESH_OFFSET 0x10
  9. #define TIMEOUT_OFFSET 0x14
  10. #define INIT_NORMAL 0x0
  11. #define INIT_PALL 0x1
  12. #define INIT_MRS 0x2
  13. #define INIT_EMRS 0x3
  14. #define INIT_MASK 0x3
  15. #define PHYS_SDRAM_1 0x30000000 /* SDRAM Bank #1 */
  16. #define PHYS_SDRAM_1_SIZE 0x04000000 /* 64 MB */
  17. #define CFG_BANK_CFG_VAL_DDR2 0x00049253
  18. #define CFG_BANK_CON2_VAL_DDR2 0x006E0035
  19. init_mem:
  20. ldr r4, =ELFIN_MEMCTL_BASE
  21. /* Step 1: BANKCFG Setting */
  22. ldr r2, =CFG_BANK_CFG_VAL_DDR2
  23. str r2, [r4, #BANKCFG_OFFSET]
  24. ldr r1, =0x44000040
  25. str r1, [r4, #BANKCON1_OFFSET]
  26. /* Step 2: BANKCON2 Setting */
  27. ldr r2, =CFG_BANK_CON2_VAL_DDR2
  28. str r2, [r4, #BANKCON2_OFFSET]
  29. /* Step 3: issue PALL */
  30. orr r2, r1, #INIT_PALL
  31. str r2, [r4, #BANKCON1_OFFSET]
  32. /* Step 4: Issue a EMRS2 command */
  33. ldr r2, =0x80000000
  34. str r2, [r4, #BANKCON3_OFFSET]
  35. orr r2, r1, #INIT_EMRS
  36. str r2, [r4, #BANKCON1_OFFSET]
  37. /* Step 5: Issue a EMRS3 command */
  38. ldr r2, =0xc0000000
  39. str r2, [r4, #BANKCON3_OFFSET]
  40. orr r2, r1, #INIT_EMRS
  41. str r2, [r4, #BANKCON1_OFFSET]
  42. /* Step 6: Issue a EMRS1 command */
  43. ldr r2, =0x44000000
  44. str r2, [r4, #BANKCON3_OFFSET]
  45. orr r2, r1, #INIT_EMRS
  46. str r2, [r4, #BANKCON1_OFFSET]
  47. /* Step 7: issue MRS */
  48. ldr r2, =0x44000130
  49. str r2, [r4, #BANKCON3_OFFSET]
  50. orr r2, r1, #INIT_MRS
  51. str r2, [r4, #BANKCON1_OFFSET]
  52. /* Step 8: issue PALL */
  53. orr r2, r1, #INIT_PALL
  54. str r2, [r4, #BANKCON1_OFFSET]
  55. /* Step 9: write 0xff into the refresh timer */
  56. mov r3, #0xff
  57. str r3, [r4, #REFRESH_OFFSET]
  58. /* Step 10: wait more than 120 clk */
  59. mov r3, #0x200
  60. 10: subs r3, r3, #1
  61. bne 10b
  62. /* Step 11: issue MRS */
  63. ldr r2, =0x44000030
  64. str r2, [r4, #BANKCON3_OFFSET]
  65. orr r2, r1, #INIT_MRS
  66. str r2, [r4, #BANKCON1_OFFSET]
  67. /* Step 12: Issue a EMRS1 command */
  68. ldr r2, =0x47800030
  69. str r2, [r4, #BANKCON3_OFFSET]
  70. orr r2, r1, #INIT_EMRS
  71. str r2, [r4, #BANKCON1_OFFSET]
  72. ldr r2, =0x44000030
  73. str r2, [r4, #BANKCON3_OFFSET]
  74. orr r2, r1, #INIT_EMRS
  75. str r2, [r4, #BANKCON1_OFFSET]
  76. /* Step 13: write 0x412 into the refresh timer */
  77. mov r3, #0x412
  78. str r3, [r4, #REFRESH_OFFSET]
  79. /* Step 14: Normal Mode */
  80. orr r2, r1, #INIT_NORMAL
  81. str r2, [r4, #BANKCON1_OFFSET]
  82. mov pc, lr
复制代码
芯片手册初始化流程(上面有一张图)中 1. Setting the BANKCFG & BANKCON1, 2, 3 ldr r4, =ELFIN_MEMCTL_BASE ,我们首先将内存这块的基地址0x48000000赋值给r4 /* Step 1: BANKCFG Setting */ ldr r2, =0x00049253 str r2, [r4, #BANKCFG_OFFSET] 然后将0x00049253(二进制 0000_0000_0000_0100_1001_0010_0101_0011)写入BANKCON1 ldr r1, =0x44000040 str r1, [r4, #BANKCON1_OFFSET] 我们找到寄存器,得到如图4,信息: 图4-BANKCFG 至于为什么这么配置,我们可以看我们的DDR2内存芯片手册(上面已经给出): 这里面指出,Row Address为13bit,Column Address为10bit。这里我们其实不用配置RASBW1、CASBW1、ADDRCFG1,但是它既然在这里,我们顺便给它一个值也没什么影响。 ldr r1, =0x44000040 str r1, [r4, #BANKCON1_OFFSET] 这一句就是将BANKCON1配置为如下: 图5 BANKCON1 /* Step 2: BANKCON2 Setting */ ldr r2, =0x006E0035 str r2, [r4, #BANKCON2_OFFSET] 将BANKCON2配置如下: 图6 BANKCONF2 至于上面这些参数为什么这么选择,我一时也没有找到有效的参考信息,不过差查看我们的内存的芯片,在后面几项都写的5,应该是表示可以支持的为5,而S3C2416显然没有支持到5,所以我就直接参考uboot的参数选择了。 做完如上这写,我们的第一步已经完成。为什么没有配置BANKCONF3呢?答:这个寄存器里面没有什么需要配置的,对它的描述如下:Mobile DRAM (E)MRS Register,这个寄存器后面会用到。 再回到我们的初始化流程,它如是写道:2. Wait 200us to allow DRAM power and clock stabilize. 它的意思是等待DRAM供电和时钟稳定,在这之前,时钟已经稳定,这里我们就直接忽略这一步。 3. Wait minimum of 400 ns then issue a PALL(pre-charge all) command. Program the INIT[1:0] to ‘01b’. This automatically issues a PALL(pre-charge all) command to the DRAM. 等待什么的,我们继续忽略。就将INIT[1:0]改为01b。由于INIT是在BANKCON1寄存器中的,寄存器的图我们上面已经粘贴出来,这里就不重复粘贴,的操作如下: /* Step 3: issue PALL */ orr r2, r1, #1 str r2, [r4, #BANKCON1_OFFSET] 这一步执行完毕,我们再继续。 4. Issue an EMRS command to EMR(2), provide LOW to BA0, High to BA1. Program the INIT[1:0] of Control Register1 to ‘11b’ & BANKCON3[31]=’1b’ 这句要将EMRS改为EMR2,使BA0为低,BA1为高。那么BA[1:0]值刚好为2.BA就是Bank Address,块地址。我们的DDR2芯片一共有4块。而我们的BA[1:0]G刚好有4个值。然后它说将INIT[1:0]置位‘11b’,这两位我们刚才操作过,设置为11表示执行EMRS命令。然后将BANKCON3[31]置为1,其实就使BA1为1,BA0为0.寄存器如图7,个人觉得这个寄存器有点小问题,这个寄存器有三种模式EMR(1)/EMR(2)/EMR(3).寄存器的模式是根据最高两位来确定的。这里为EMRS(1)是最高两位是01,为EMRS(2)最高两位为10,为EMRS(3)是最高两位为11,而不应该像手册中那样全部都可以是10。从这里,我们还可以看出,在流程中分为两行的,上面一行写的是目的,下面一行写的是具体操作。 图7 EMRS 图7 ,EMRS(2) EMRS(3) 其实,看懂这个之后,再实现代码也不那么纠结: /* Step 4: Issue a EMRS2 command */ ldr r2, =0x80000000 str r2, [r4, #BANKCON3_OFFSET] BANKCON3[31]=’1b’ orr r2, r1, #0X3 str r2, [r4, #BANKCON1_OFFSET] INIT[1:0] 写 ‘11b’ 我们接着瞧 5.Issue an EMRS command to EMR(3), provide High to BA0 and BA1. Program the INIT[1:0] of Control Register1 to ‘11b’ & BANKCON3[31:30]=’11b’ 这一段和上一段写的地址都差不多,就写进去的值不一样。就不多描述。 /* Step 5: Issue a EMRS3 command */ ldr r2, =0xc0000000 str r2, [r4, #BANKCON3_OFFSET] orr r2, r1, #0X3 str r2, [r4, #BANKCON1_OFFSET] 6. Issue an EMRS to enable DLL and RDQS, nDQS, ODT disable. 这个操作其实更改的也是EMRS(1)部分。 /* Step 6: Issue a EMRS1 command */ ldr r2, =0x44000000 str r2, [r4, #BANKCON3_OFFSET] orr r2, r1, #INIT_EMRS str r2, [r4, #BANKCON1_OFFSET] 写入0x44000000后,寄存器如下: 图8 寄存器内容 7. Issue a MRS(Mode Register Set) command for DLL reset.(Toissue DLL Reset command, provide HIGH to A8 and LOW to BA0-BA1, and A13-A15.) Program the INIT[1:0] to ‘10b’. & BANKCON3[8]=’1b’ 到这里,出现了MRS,大意是 模式设置寄存器,那么EMRS就是 扩展模式设置寄存器。 /* Step 7: issue MRS */ ldr r2, =0x44000130 str r2, [r4, #BANKCON3_OFFSET] orr r2, r1, #INIT_MRS str r2, [r4, #BANKCON1_OFFSET] 其实就是操作的这么一位: 图9 MSR 8. Issue a PALL(pre-charge all) command. Program the INIT[1:0] to ‘01b’. This automatically issues a PALL(pre-charge all) command to the DRAM. /* Step 8: issue PALL */ orr r2, r1, #INIT_PALL str r2, [r4, #BANKCON1_OFFSET] 这之后一直都是这样类似的操作,就不做过多的解释。 在内存初始化完成之后,我们就将我们垫脚石里面的代码搬运到内存里面去执行。(本来这个搬运操作应该是搬运nandflash里面的代码到内存的。我们这里还没有初始化nandflash,就搬运垫脚石里面的代码练练手咯,以后会搬运nandflash里面的代码过去。让它的功能更加强大!),搬运代码如下:
  1. copy_to_ram:
  2. ldr r0, =0x00000000 @垫脚石——的开始地址
  3. ldr r1, =0x30000000 @搬运的目的地址
  4. add r3, r0, #1024*8 @垫脚石——的结束地址(我们的2416stepping stone是8K的,就用起始地址加上8K得到结束地址)
  5. copy_loop:
  6. ldr r2, [r0], #4 @将垫脚石里面的内容放入r2,然后地址+4
  7. str r2, [r1], #4 @将r2的内容写入内存,内存地址+4
复制代码
搬运完成后,我们做一些初始化,来运行C语言程序。
  1. init_stack:
  2. ldr sp, =0x34000000 @初始化堆栈,将sp指向64M内存的最后一个地址处
  3. mov pc, lr
  4. clean_bss:
  5. ldr r0, =bss_start @bss段,就是未初始化的变量存放的地方,这里将这片区域全部清0
  6. ldr r1, =bss_end @bss_start和bss_end在连接器脚本文件中定义。
  7. cmp r0,r1 @比较bss段起始和结尾地址
  8. moveq pc,lr @如果为空,则返回。不为空,继续往下执行
  9. clean_loop:
  10. mov r2, #0 @向r2中写0
  11. str r2, [r0], #4 @把r2中的值写入r0指向的地址,r0+4
  12. cmp r0,r1 @比较r0和r1
  13. bne clean_loop @如果不等,跳回clean_loop继续执行清0工作。
复制代码
这些都做完了,我们就要跳到C里面执行啦:
  1. reset:
  2. bl set_svc @带链接相对跳转,执行完成后mov pc,lr返回这里
  3. bl disable_watchdog @带链接相对跳转,执行完成后mov pc,lr返回这里
  4. bl disable_interrupt @带链接相对跳转,执行完成后mov pc,lr返回这里
  5. bl disable_mmu @带链接相对跳转,执行完成后mov pc,lr返回这里
  6. bl init_clock @带链接相对跳转,执行完成后mov pc,lr返回这里
  7. bl init_mem @带链接相对跳转,执行完成后mov pc,lr返回这里
  8. bl copy_to_ram @带链接相对跳转,执行完成后mov pc,lr返回这里
  9. bl init_stack @带链接相对跳转,执行完成后mov pc,lr返回这里
  10. bl clean_bss @带链接相对跳转,执行完成后mov pc,lr返回这里
  11. ldr pc, =main @绝对跳转,跳到main处执行。
复制代码
主函数代码及其简单,如下: 就是让CPB1输出低电平,点亮LED灯。
  1. #define GPBCON (volatile unsigned long*)0x56000010
  2. #define GPBDAT (volatile unsigned long*)0x56000014
  3. int main()
  4. {
  5. *GPBCON = 0XFFFFFFF7;
  6. *GPBDAT = 0XFFFFFFFD;
  7. return 0;
  8. }
复制代码
这样整个工程就完毕了。最下方有整个代码,可以下载下来瞧瞧。 下面补充2个东西: 1,相对跳转和绝对跳转: b指令就是一个相对跳转指令。比如有两端代码a,b。a链接地址在2,b链接地址在7.采用相对跳转,要从a跳到b,它会先计算b-a的值,然后在a上加上这个值,跳过去。 而直接给pc指针赋值,属于绝对跳转。我给pc指针赋值7,它就跳到地址7处执行。 这样说大家可能绝对这两个指令其实也没啥区别。但是,看看我们上面那个程序。在脚本文件中,我定义链接起始地址为0x30000000,这个地址在内存。可是,我上一个帖子中,程序一直不在内存中运行,就是因为我一直使用的b这类指令,都是相对跳转,跳来跳去,一直都在stepping stone里面。
  1. SECTIONS
  2. {
  3. <font color="#ff0000"><b> . = 0x30000000; //链接起始地址</b></font>
  4. . = ALIGN(4);
  5. .text :
  6. {
  7. start.o(.text)
  8. *(.text)
  9. }
  10. ……
  11. }
复制代码
我们现在要让程序从stepping stone跑到内存中去运行,就使用绝对跳转。跳到main函数的链接地址去。这样就跑到内存了。 第2个问题就是,这个工程和第一个工程相比,添加了一个mem.o文件和一个main.c文件。这样Makefile文件就要做一点修改,不然编译会报错。修改的也不多,就是加上两个依赖。如下图,增加的地方为红色。
  1. all : start.o <b><font color="#ff0000">mem.o main.o</font></b>
  2. arm-linux-ld -Tled.lds -o led.elf $^
  3. arm-linux-objcopy -O binary led.elf led.bin
  4. %.o : %.S
  5. arm-linux-gcc -g -c $^
  6. %.o : %.c
  7. arm-linux-gcc -g -c $^
  8. .PHONY:clean
  9. clean :
  10. rm *.o *.elf *.bin
复制代码
至于最后这个奇葩,如下。是由于我用samba共享目录之后用UltraEdit在Windows环境下编辑。每次编辑后,它都会自动加一个.bak备份文件,我很不喜欢。但如果直接在clean后面加上 rm *.bak,我如果一直在linux下编辑,就不会有.bak文件,使用make clean老是有个警告也很不爽,于是乎,我就又加了一条规则咯。希望这个东西不要给大家带来困惑。
  1. .YHONY:clean_bak
  2. clean_bak :
  3. rm *.bak
复制代码
代码如下: led2.zip (3.46 KB, 下载次数: 15) 里面有我编译好的bin文件。使用方法可参考开发板手册,或者我上一个帖子。 其实这个启动代码功能还不完备,我们的MMU还没有使用,缓存也是关闭的,nandflash也还没有用起来。但是我们迈出了最伟大的一步,进入到了C的环境。后面的东西,我们就在C的环境下进行初始化。然后欢快的跑起来。 大家如果发现帖子里面的问题,或是我哪里写的不明白,有歧义,希望大家能够指出。
论坛ID:微末凡尘
提交时间:2014.07.29

最新回复

分析得很仔细,看起来很吃力,我现在基本上是遇到问题才会去仔细分析,有兴趣的可以看一下。。。  详情 回复 发表于 2014-7-30 11:15
点赞 关注(2)

回复
举报

554

帖子

0

TA的资源

版主

沙发
 
分析得很仔细,看起来很吃力,我现在基本上是遇到问题才会去仔细分析,有兴趣的可以看一下。。。
 
个人签名My dreams will go on...
http://www.jyxtec.com
 

回复
您需要登录后才可以回帖 登录 | 注册

查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/8 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表