30.2440内存的知识
30.2440内存的知识
首先看2440的地址线:mini2440原理图.pdf
2440的芯片提供了27根地址线=128M。
S3c2440芯片对外提供的引脚上,只给出了27根地址线addr[0:26].这27根引脚地址线,只能访问128M的外设空间。
为了扩大外设的访问范围,S3c2440芯片又提供了8个片选信号nGCS0~nGCS7。当某个片选信号nGCSx有效时,则可以通过27根地址线去访问对应这个片选的128MB空间。由于有8个片选,所以2440芯片能访问的外设空间总共为8*128MB=1GB.而1G(0x40000000)以上的空间,则安排给了2440内部的寄存器,访问这些内部的寄存器,则是通过32位的处理器内部总线来完成的。
8个片选信号引脚:
接下来看看芯片手册的信息:
S3C2440.pdf的5. Memory Controller
片选6和7一般是安排内存的位置,2440也是,在片选6和7是SROM和SDRAM。可以看到2440内存的起始地址是0x30000000.
存储控制器:
由上图可知,要去访问内存,NorFlash,网卡芯片等。是通过存储控制器来访问的。例如访问内存地址,我们知道要访问内存的地址需要提供L-Bank,行地址和列地址,可我们在访问的时候只给出一个例如0x30006000的详细地址。那处理器怎么知道所谓的L-Bank,行地址和列地址呢?这就是需要存储控制器,他会把详细的地址单元分解为L-Bank,行地址和列地址供CPU访问。
要让存储控制器完成对外设的访问,是通过它提供的一系列寄存器来进行的。
第一个寄存器是:BWSCON寄存器的功能四控制总线宽度和等待状态。
从寄存器的各个位看到,它的32位被分为8组,31-28位是控制Bank7的,以此类推。
31-28是控制bank7的,设置为0010,首先31没有使用,所以是0,30位暂时不使用也设置为0,然后bank7内存芯片是通过两个16位的内存芯片合并成一个32位的内存芯片,所以是32位的,所28-29是10。Bank6也是一样的,都是链接SDRAM的。剩下的暂时不用都是默认值0.值为0x22000000.
然后对bank0到bank5对应的六个寄存器,由于暂时不用,也是保持默认值:0x07000000
接下来是Bank6和Bank7的控制寄存器:
从上面知道由于这里使用的是SDRAM,所以[16:15]的值为11;然后这里的Memory Type,我们2440是SDRAM是下面的参数值,所以[14:4]=00000000000;
然后末四位,[3:2]是控制行列信号的延时,下图可以看到是两个时钟周期,所以是00.
前面知道,内存地址很像一张表格,里面是由行地址和列地址来确定单元格的位置的。那么列地址有多少位呢?就是通过[1:0]位指定的。
在:HY57V561620.pdf文档看到:列地址是CA 0~CA8是9位的,所以[1:0]=01.最后该寄存器的值为:11000000000000001=0x18001
所以Bank6和Bank7的值为0x00018001.
接下来的寄存器是:REFRESH寄存器:
在前面的笔记知道,SDRAM里面存储信息是通过电容的电荷来记录信息的,就需要定期充电的操作,否则信号失真,所以需要定期刷新。
从上面知道,[23]位是控制是否要刷新的,这里是要刷新的。所以REFEN[23]是为1的。TREFMD[22]位控制的是自动刷新还是自主刷新,这里选的是自动刷新,所以该位为0. Trp[21:20]两位控制的是准备充电的时间,下面可以看到准备时间是2个时钟,所以是00.
接着是Tsrc是,一行进行刷新的持续时间,一般是7个时钟,所以是11.[17:16]是不使用的是00.[15:11]五位也是一样的00000.最后是[10:0]Refresh Counter域的大小11位,是控制多就刷新一次的,后面的公式里HCLK是提供给内存的时钟,在前面知道它是ARMCLK的四分之一,ARMCLK选的是533MHz,它的四分之一是133MHz。
然而下面它给出了一个例子:如果HCLK是100MHz,那么:
Refresh count=+1-100*7.8=1269.
而我们的是133MHz,所以是:
Refresh count=+1-133*7.8= 1011.6=1011=b01111110011
最后该寄存器的值是:100011000000001111110011=0x8C03F3=0x008C03F3
接下来是BANKSIZE寄存器,这个寄存器:
这个寄存器只有前面加个为:[7]是BURST_EN突发模式,所谓的突发模式就是访问内存数据的时候可以使用批量的数据,提高访问效率,打开是设置1。[6]位是保留位。[5]位是节电模式,设置为1.[4]位建议设置为1,就选1好了。[3]保留。[2:0]是bank6.7的大小的,知道是两片32M的,组成64M的,所以是001.最后的值是:10110001=0x000000b1
最后一组寄存器是控制bank6和bank7的模式的,两个是一样的:
在这组寄存器中,只有几个位,而且很多都是Fixed固定的,看到只需要设置[CL]位。所谓的CL就是CAS的潜伏期,什么叫CAS信号的潜伏期?看一段时序图:
潜伏期就是从RAS信号变成低电平,到CAS变为低电平,这段之间的时钟,从上图知道是3,上图知道3个时钟对应的是011。所以这两个寄存器的值是0x00000030.
到这里内存控制的寄存器都在上面的信息,接下来是2440存储控制器代码的编写:
上面为了初始化储存控制器,需要设置13寄存器,如果每一个寄存器都需要ldr指令取出,然后再置数,第三在str指令存入,这样需要39条指令。这样感觉很是没效率。而且代码是差不多的,在别的编程语言,会有循环来处理。接下来也用循环来给寄存器置数。
最后实现的代码是:
#define mem_contrl 0x48000000 //13个寄存器的第一个寄存器的地址
init_sdram:
ldr r0, =mem_contrl //把第一个寄存器的地址存入r0寄存器
add r3, r0, #4*13 //把最后一个寄存器的地址存入r3,总共13个寄存器。
adrl r1, data //adrl指令,把data标号的基地址传给r1
0: //
ldr r2, [r1], #4 //把r1寄存器指向数据表的第一个数据取出,放到r2寄存器里,r1+4指
//向数据表的下一个数据的地址。
str r2, [r0], #4 //把r2寄存器里的值传给r0寄存器所指向的地址,然后r0+4指向寄存器
//表的下一个寄存器。
cmp r0, r3 //比较r0和r3的地址,看是否到了寄存器表的末尾。如果相等结束程序。
bne 0b //如果不相等,则bne,b是跳转,条件ne=no equal,标识是0,b是before往
//前跳转
mov pc, lr //返回
data:
.long 0x22000000 //.long 伪指令,指明长度。
.long 0x00000700
.long 0x00000700
.long 0x00000700
.long 0x00000700
.long 0x00000700
.long 0x00000700
.long 0x00018001
.long 0x00018001
.long 0x008c04f5
.long 0x000000b1
.long 0x00000030
.long 0x00000030
上面代码的过程可以用下图解释:
整个过程:把要写入寄存器的值:可以创建一个数据表的存储结构把它们存起来。这里定义了Data数据表,然后知道寄存器的地址是递增的。所以只要定义一个标识来存起来,标识的地址就是数据的开始地址,寄存器的开始地址是知道的。然后就可以吧D1里的值传给P1所指向的地址,递增寄存器地址。循环上述操作。直到寄存器的地址表末尾就结束。这样就在汇编里,使用了循环结构,往连续地址的寄存器里存入了指定的数据。