ARM裸机程序之存储管理器控制SDRAM

来源:互联网 发布:怎么做淘宝客链接 编辑:程序博客网 时间:2024/05/21 17:18

http://blog.csdn.net/u011449588/article/details/45021409


本文讲的是s3c2440A芯片的存储管理器,配套的开发板是友善之臂mini2440,首先贴出代码

head.s的代码:

[cpp] view plain copy
  1. .equ    MEM_CTL_BASE,    0x48000000        @定义13个寄存器的首地址  
  2. .equ    SDRAM_BASE,      0x30000000        @定义SDRAM的首地址  
  3.   
  4. .text  
  5. .global _start  
  6. _start:  
  7.     bl disable_watch_dog  
  8.     bl memsetup  
  9.     bl copy_steppingstone_to_sdram         @把代码从片内的SRAM复制到SDRAM里面  
  10.     ldr pc, =on_sdram  
  11. on_sdram:  
  12.     ldr sp, =0x34000000                    @设置栈指针,用来调用C函数  
  13.     bl main  
  14. halt_loop:  
  15.     b halt_loop  
  16. disable_watch_dog:  
  17.     ldr r0, =0x53000000  
  18.     mov r1, #0x0  
  19.     str r1, [r0]  
  20.     mov pc, r14  
  21. copy_steppingstone_to_sdram:  
  22.     mov r0, #0x0  
  23.     mov r1, #SDRAM_BASE  
  24.     mov r2, #4096  
  25. Loop:                                      @用一个循环,从片内SRAM的0地址开始复制到SDRAM的0x30000000处  
  26.     ldr r3, [r0], #4  
  27.     str r3, [r1], #4  
  28.     cmp r0, r2  
  29.     bcc Loop  
  30.     mov pc, r14  
  31. memsetup:                                  @设置存储管理器相关的13个寄存器  
  32.     mov r0, #MEM_CTL_BASE  
  33.     ldr r1, =0x22011110                    @BWSCON  
  34.     str r1, [r0], #4                       @把r1寄存器里面的值放到r0所指的内存单元中,然后r0=r0+4  
  35.     ldr r1, =0x00000700                 
  36.     str r1, [r0], #4                       @BANKCON0  
  37.     str r1, [r0], #4                       @BANKCON1  
  38.     str r1, [r0], #4                       @BANKCON2  
  39.     str r1, [r0], #4                       @BANKCON3  
  40.     str r1, [r0], #4                       @BANKCON4  
  41.     str r1, [r0], #4                       @BANKCON5  
  42.     ldr r1, =0x00018005         
  43.     str r1, [r0], #4                       @BANKCON6  
  44.     str r1, [r0], #4                       @BANKCON7  
  45.     ldr r1, =0x008c07a3  
  46.     str r1, [r0], #4                       @REFRESH  
  47.     ldr r1, =0x000000b1  
  48.     str r1, [r0], #4                       @BANKSIZE  
  49.     ldr r1, =0x00000030  
  50.     str r1, [r0], #4                       @MRSRB6  
  51.     str r1, [r0]                           @MRSRB7  
  52.     mov pc, r14  

led_key.c的代码:

[cpp] view plain copy
  1. #define GPBCON      (*(volatile unsigned long *)0x56000010)  
  2. #define GPBDAT      (*(volatile unsigned long *)0x56000014)  
  3. #define GPBUP       (*(volatile unsigned long *)0x56000018)  
  4.   
  5. #define GPGCON      (*(volatile unsigned long *)0x56000060)  
  6. #define GPGDAT      (*(volatile unsigned long *)0x56000064)  
  7.   
  8. #define GPB5_out    (1<<(5*2))  
  9. #define GPB6_out    (1<<(6*2))  
  10. #define GPB7_out    (1<<(7*2))  
  11. #define GPB8_out    (1<<(8*2))  
  12. #define GPB0_out    (1<<(0*2))  
  13.   
  14. #define GPG0_in     ~(3<<(0*2))  
  15. #define GPG3_in     ~(3<<(3*2))  
  16. #define GPG5_in     ~(3<<(5*2))  
  17. #define GPG6_in     ~(3<<(6*2))  
  18. #define GPG7_in     ~(3<<(7*2))  
  19. #define GPG11_in    ~(3<<(11*2))  
  20.   
  21. int main()  
  22. {  
  23.     unsigned long read_value;  
  24.     GPBCON |= (GPB5_out | GPB6_out | GPB7_out | GPB8_out | GPB0_out);  
  25.     GPBUP &= 0x1e1;  
  26.     GPBDAT |= 0x1e0;  
  27.   
  28.     GPGCON &= (GPG0_in & GPG3_in & GPG5_in & GPG6_in & GPG7_in );  
  29.     while(1)  
  30.     {  
  31.         read_value = GPGDAT;  
  32.         if(read_value & (1<<0))  
  33.         {  
  34.             GPBDAT |= (1<<5);  
  35.         }  
  36.         else  
  37.         {  
  38.             GPBDAT &= (~(1<<5));  
  39.         }  
  40.         if(read_value & (1<<3))  
  41.         {  
  42.             GPBDAT |= (1<<6);  
  43.         }  
  44.         else  
  45.         {  
  46.             GPBDAT &= (~(1<<6));  
  47.         }  
  48.         if(read_value & (1<<5))  
  49.         {  
  50.             GPBDAT |= (1<<7);  
  51.         }  
  52.         else  
  53.         {  
  54.             GPBDAT &= (~(1<<7));  
  55.         }  
  56.         if(read_value & (1<<6))  
  57.         {  
  58.             GPBDAT |= (1<<8);  
  59.         }  
  60.         else  
  61.         {  
  62.             GPBDAT &= (~(1<<8));  
  63.         }  
  64.         if(read_value & (1<<7))  
  65.         {  
  66.             GPBDAT &= (~(1<<0));  
  67.         }  
  68.         else  
  69.         {  
  70.             GPBDAT |= (1<<0);  
  71.         }  
  72.     }  
  73.     return 0;  
  74. }  


Makefile:

[cpp] view plain copy
  1. sdrampp1.bin : head.s led_key.c  
  2.     arm-linux-gcc -g -c -o head.o head.s  
  3.     arm-linux-gcc -g -c -o led_key.o led_key.c  
  4.     arm-linux-ld -Ttext 0x30000000 -g head.o led_key.o -o sdrampp1_elf  
  5.     arm-linux-objcopy -O binary -S sdrampp1_elf sdrampp1.bin  
  6.     arm-linux-objdump -D -m arm sdrampp1_elf > sdrampp1.dis  
  7. clean:  
  8.     rm -rf sdrampp1.* *.o  


首先说明本程序主要实现的功能:本程序最终为了生成一个sdrampp1.bin的可执行文件下载到开发板的NandFlash的0地址处,一旦下载进去,s3c2440A这块芯片就会自动地把NandFlash的前4K代码复制到片内的steppingstone,也就是片内的SRAM里,如果不加处理,一般的程序就从SRAM的0地址处开始运行了,例如上一篇博客“arm裸机程序之LED灯”就是从SRAM的0地址处开始运行的,而本程序并非从SRAM的0地址开始执行,而是把片内的SRAM的4K代码复制到SDRAM的0地址处,最后从SDRAM的0地址处开始执行,所以Makefile中的代码段地址是0x30000000,并非上一篇博客里写的0x00000000,从0x30000000处运行代码,在SDRAM的0x34000000处设置了一个栈指针,至于为什么栈指针是这个,后面的内容会详细解释,最后调用C函数,这里的C函数实现的功能是按下开发板上的k1键,led1亮,松开led1就灭,k2,k3,k4同样道理控制其他3个led,k5控制蜂鸣器的响与不响.

首先看Makefile,注意到Makefile的第4行,要求把代码段放在0x3000000处,解释如上。至于其他的内容和上一篇博客的Makefile没什么不同,Makefile就分析到此。


下面看看head.s的代码,程序一开始一个bl跳转指令,实现关闭看门狗的程序就不解释了。接着是一个Memory Controller(存储管理器)初始化的工作,这里涉及到13个寄存器,分别是BWSCON寄存器,BANKCON0寄存器等等,如果一开始我解释这些寄存器,你肯定不知道我要讲会听的云里雾里,所以我先讲点原理,把握整体再深究细节。

我们先从开发板的硬件原理图开始说起,因为是开发板的原理图是最基本直观的东西了,by the way,我用的开发板是mini2440,所以查看的是mini2440原理图,先上张图先:


卧槽,你TMD给我上张这个图片,先别着急,我是想让你看看着块芯片有多少根地址总线和数据总线,正因为这张图片长得是比较丑,所以我尽量把它讲的生动,美化它,最终让你爱上它。首先我们来明确两个概念,什么是地址总线,什么是数据总线,顾名思义,地址总线是CPU发送一个地址,地址总线决定了CPU所能访问的最大内存空间,这个CPU是中的地址总线是ADDR0~ADDR26,总用27条地址总线吧,所以s3c2440A最大访问的内存地址为2^27=128M,但是如果想让这个CPU实现最大能访问的内存空间达到1GB的空间怎么办呢,cpu还引出8根片选信号nGCS0~nGCS7,所以总的寻址空间就达到1GB了,虽然你在上面这张图看不出来有多少个片选信号,你可以在原理图上搜索一下即可。其实在这里,我就可以引出一张s3c2440 Memery Controller的地址空间分布图了,在上图之前,我讲一句话解释数据总线到底怎么回事:就是决定CPU每次传送数据的大小,这里有DARA0~DATA31,,,每次发送32位的数据,每次发送32位数据,其实就可以引出SDRAM的原理图了,暂且不说,先看Memery Controller的地址空间分布图:



看到这么一张大图,先别慌,我比你更慌,不知道怎么解释,先验证一下上面说的那段话,总共8个片选信号,为了满足1GB的地址空间的寻址,其实在这里每个片选对应一个bank,nGCS0对应的是Bank0,nGCS7对应Bank7,依次类推,只要选择了一个片选信号,例如我这里假设悬着nGCS6的片选信号,CPU的27根地址总线就能对0x30000000~0x38000000之间的地址进行寻址了,不过这里还要注意,并不是每个之间的每个地址都能进行存储数据,寻址什么的,因为s3c2440的每个Bank其实都是外接设备的,例如Bank0外接NorFlash,Bank4外接网卡DM9000,Bank6和Bank7外接SDRAM等,至于其他的我们暂时不用管,还可以涉及到芯片的2中启动方式我这里就不介绍了。这里Bank6和Bank7都外接SDRAM是怎么回事呢,这里就和上面的数据总线联系在一起了,因为开发板外接了两块SDRAM,每个SDRAM有16根数据总线,为了和cup的数据总线的宽度保持一致,所以要接两块SDRAM,SDRAM的原理图请看下图:




要知道SDRAM的工作原理,首先看看下面几个问题,下面是我其他博客上看到的:

(1) 地址线为什么从A2开始?

      因为2440数据宽度为32位,按4字节对齐,即地址只会是0x...0,0x...4,0x..C,0x...E,每次地址增加都是四个字节,所以A0和A1没什么用。

 

(2) SDRM BANK 选择输入BA0/BA1为什么连接的是A24,A25

     因为系统内存容量为64M,32bit,由两片64M 16bit的SDRM组成。表示64M的空间需要26根线,所以地址最高两位为A25和A24。

(3) 64M需要26根线,为什么实际只用到了A2~A14,A24,A25?

      理论上应该将A2~A25直接连接到SDRAM来寻址64M(之所以不是A0~A25,是因为每次访问的是32bit),而实际上只把A2~A14这13根线连接到SDRAM的A0~A12,这是因为SDRAM访问时地址是分两次给的,即行地址和列地址,不需要一次输入,行地址和列地址复用了A2~A14这13根线,这个SDRAM理论上可寻址的最大范围为2^13 * 2^13。

 

(4)为什么板子上SDRAM的空间为0x30000000 ~ 0x34000000

     根据2440 SPEC,SDRAM只能放在BANK6 或 BANK7 (nGCS6或nGCS7),起始地址分别为0x30000000和0x38000000,一个BANK的大小为128M,现在选择BANK放SDRAM,而SDRAM的容量为64M(0x4000000),所以SDRAM的范围就是0x30000000~0x34000000,为什么是0x3....呢?因为你把nGCS6片选接到SDRAM芯片上了;当然后你也可以接nGCS7,不过地址就要变了,[A29,A28,A27]=3,即从0x38000000开始.


看了上述的问题与回答,应该能大致理解SDRAM的工作原理吧。

最后我分析一下,Memery Controller的13个寄存器:

1.BWSCON寄存器:中文名字叫做位宽和等待控制器。

STx位:启动或禁止SDRAM的数据掩码引脚,对于SDRAM,此位为0,对于SRAM,此位为1;

WSx:是否使用存储器的WAIT信号,通常设置为0.

DWx:用来设置相应的Bank数据总线的位宽。

所以这里设置BWSCON寄存器的设置的值为0x22011110,bank7和bank6的位宽都为32位,其他的为16位,因外接的外设不同。还要注意一点的是Bank0,没有WS0和DW0

2.BANKCON0和BANKCON1,BANKCON2,BANKCON3,BANKCON4,BANKCON5属于同一类,用于设置外接设备的访问时序,具体要看芯片手册上的时序图,这里默认设置为0x0700即可。

3.BANKCON6和BANKCON7寄存器:因为Bank6和Bank7可以接SDRAM,还可以接SRAM,SROM等等,所以在这个寄存器里相应的位要指明接的是什么,可以根据MT([16:15])来控制,还有就是想BANKCON1等寄存器一样要设置时序了。这里根据芯片手册,设置为0x00018005即可。

4.REFRESH寄存器:中文名叫做刷新控制寄存器,可以设置SDRAM的刷新功能,要不要使能,刷新模式,刷新周期等等,这里可以设置0x008c007a3,具体对照芯片手册。

5.BANKSIZE寄存器:记住一点就是当多个SDRAM一起工作的时候,设置SDRAM在内存中的映射图

6.MRSRB6和MRSRB7寄存器,就是设置Bank6和Bank7的一些特征,知道一个设置值即可0x30,具体细节,我觉得没有必要太深入了,暂时对SDRAM的研究不需要太深,毕竟最终目标是自己定制一个U-Boot。


原创粉丝点击