存储控制器

《嵌入式Linux应用完全开发手册》第2篇第6章总结归纳
总线的使用是嵌入式底层开发的基础,了解它之后,再根据外设的具体特性,就可以驱动该外设了。

本章目标

  1. 了解S3C2410/S3C2440地址空间的布局
  2. 掌握如何通过总线形式访问扩展的外设,比如内存、NOR Flash、网卡等。

使用存储控制器访问外设的原理

S3C2410/S3C2440的地址空间

S3C2410/S3C2440的”存储控制器“提供了访问外部设备所需的信号,它有如下特性:

  1. 支持小字节序、大字节序(通过软件选择);
  2. 每个BANK的地址空间为128MB,共1GB(8BANKs);
  3. 可编程控制的范围总线位宽(8/16/32bit),不过BANK0只能选择两种位宽(16/32bit);
  4. 总共8个BANK,BANK0-BANK5可以支持外接ROM、SRAM等。BANK6-BANK7除了可以支持ROM、SRAM外,还支持SDRAM等;
  5. BANK0-BANK7共7个BANK的起始地址是固定的;
  6. BANK7的起始地址可编程选择;
  7. BANK6、BANK7的地址空间大小是可编程控制的;
  8. 每个BANK的访问周期均可编程控制;
  9. 可以通过外部的”wait“信号延长总线的访问周期;
  10. 在外接SDRAM时,支持自刷新(self-refresh)和省电模式(power down mode)
    S3C2410/S3C2440对外引出的27根地址线ADDR0-ADDR26的访问范围只有128MB,那么如何达到上面所说的1GB的访问空间呢?CPU对外引出了8根片选信号nGCS0-nGCS7,对应于BANK0-BANK7,当访问BANKx的地址空间时,nGCSx的引脚输出低电平,用来选中外接的设备。这样,每个nGCSx对应的128MB空间,8个nGCSx信号总共就对应了1GB的地址空间。这8个BANK的地址空间如图所示:
    img not found
    如图所示,左边对应不使用NAND Flash作为启动设备(单板上不接NAND BOOT跳线)时的地址空间布局,右边对应使用NAND Flash作为启动设备(单板上接NAND BOOT跳线)时的地址空间布局。
    S3C2410/S3C2440作为32位的CPU,可以使用的地址范围理论上达到4GB。除去上述用于连接外设的1GB地址空间外,还有一部分是CPU内部寄存器的地址,剩下的地址空间没有使用。
    S3C2410/S3C2440的寄存器地址范围都处于0x4800000-0x5fffffff,各功能部件的寄存器大体相同。(”-表示相同。”无“表示不含该功能部件。)
    功能部件 S3C2410起始地址 S3C2410结束地址 S3C2440起始地址 S3C2440结束地址
    存储控制器 0x48000000 0x48000030 0x48000000 0x48000030
    USB Host控制器 0x49000000 0x49000058 - -
    中断控制器 0x4A000000 0x4A00001C - -
    DMA 0x4B000000 0x4B0000E0 - -
    时钟和电源管理 0x4C000000 0x4C000014 - 0x4C000018
    LCD控制器 0x4D000000 0x4D000060 - -
    NAND Flash控制器 0x4E000000 0x4E000014 - 0x4E00003C
    摄像头接口 0x4F000000 0x4F0000A0
    UART 0x50000000 0x50008028 - -
    脉宽调制计时器 0x51000000 0x51000040 - -
    USB设备 0x52000140 0x5200026F - -
    看门狗计时器 0x53000000 0x53000008 - -
    IIC控制器 0x54000000 0x5400000C - 0x54000010
    IIS控制器 0x55000000 0x55000012 - -
    I/O端口 0x56000000 0x560000B0 - 0x560000CC
    RTC 0x57000000 0x5700008B - -
    A/D转换器 0x58000000 0x58000010 - 0x58000014
    SPI 0x59000000 0x59000034 - -
    SD接口 0x5A000000 0x5A000040 - 0x5A000043
    AC97音频编码接口 0x5B000000 0x5B00001C

存储控制器与外设的关系

本书所用的开发板使用了存储控制器的BANK0-BANK6,分别接如下设备:NOR Flash、IDE接口、10M网卡 CS8900A、100M网卡DM9000、扩展串口芯片16C2550、SDRAM。连线方式如下图所示:
img not found
根据图6.1可以知道各个BANK的起始地址,但是还需要结合图6.2中用到的地址线才能确定相关外设的访问地址。这些地址线所确定的地址值,再加上这个BANK的起始地址,就是这个外设的访问地址。
选择一个复杂的BANK—扩展串口作为例子。

  1. 它使用nGCS5,起始地址作为0x28000000;
  2. nCSA=ADDR24 || nGCS5,nCSB=!ADDR24 || nGCS5。当ADDR24和nGCS5均为低电平时选中扩展串口A;当ADDR24为高电平、nGCS5为低电平时选中扩展串口B。
  3. CPU的ADDR0-ADDR2连接到扩展串口的A0-A2。所以访问空间有8字节。
    综上所述,扩展串口A的访问空间为:0x28000000-0x28000007;扩展串口B的访问空间为:0x29000000-0x29000007。

BANK0-BANK5的连接方式都是相似的,BANK6连接SDRAM时复杂一点,CPU提供了一组用于SDRAM的信号。

  1. SDRAM时钟有效信号SCKE;
  2. SDRAM时钟信号SCLK0/SCLK1;
  3. 数据掩码信号DQM0、DQM1、DQM2、DQM3;
  4. SDRAM片选信号nSCS0(它与nGCS6是同一个引脚的两个功能);
  5. SDRAM行地址选通脉冲信号nSRAS;
  6. SDRAM列地址选通脉冲信号nSCAS;
  7. 写允许信号mWE(它不是专用于SDRAM的)。
    SDRAM的内部是一个存储阵列,阵列就如同表格一样,把数据填进去,和表格的检索原理一样,先指定一个行(Row),再指定一个列(Column),就可以找到所需的单元格,这就是SDRAM的寻址的基本原理。这个单元格称为存储单元,这个表格就是逻辑BANK(L-BANK),SDRAM一般含有4个L-BANK。
    对SDRAM的访问可以分为如下4个步骤:
  8. CPU发出片选信号,nSCS0有效,它选中SDRAM芯片。
  9. SDRAM中有4个L-BANK,需要两个地址线来选择其中一个,从图6.2可知使用ADDR24、ADDR25作为L-BANK的选择信号。
  10. 对被选中的芯片进行统一的行/列(存储单元)寻址。
    根据SDRAM芯片的列地址线数目选择CPU的相关寄存器后,CPU就会从32位的地址中自动分出L-BANK选择信号,行地址信号,列地址信号,然后发出行地址信号、列地址信号。L-BANK选择信号在发出行地址信号的同时发出,并维持到列地址信号结束。
    在图6.2中,行地址、列地址公用地址线ADDR2-ADDR14(BANK6位宽位32,ADDR0/1没有使用),使用nSRAS、nSCAS两个信号来区分它们。比如本开发板中,使用两根地址线ADDR24、ADDR25作为L-BANK的选择信号;SDRAM芯片K4S561632的行地址数位13,列地址数为9,所以当nSRAS信号有效时,ADDR2-ADDR14上发出的是行地址信号,它对应32位地址空间的bit[23::11];当nSCAs信号有效时,ADDR2-ADDR10上发出的是列地址信号,它对应32位地址空间的bit[10:2];由于图6.2中BANK6以32位的宽度外接SDRAM,ADDR0、ADDR1恒为0,不参与译码。
  11. 找到存储单元后,被选中的芯片就要进行统一的数据传输了。
    开发板中使用两片16位的SDRAM芯片并联组成32位的位宽,与CPU的32根数据线(DATA0-DATA31)相连。
    BANK6的起始地址为0x30000000,所以SDRAM的访问地址为0x30000000-0x33ffffff,共64M。
    对6.2图中连接的外设,它们的访问地址(物理地址)如下表所示:
    BANKx 外设名称 起始地址 结束地址 大小(字节) 位宽
    BANK0 NOR Flash 0x00000000 0x001FFFFF 2M 16
    BANK1 IDE 接口命令块寄存器 0x08000000 0x0800000F 16 16
    BANK2 IDE 接口控制块寄存器 0x10000000 0x1000000F 16 16
    BANK3 10M网卡CS8900A 0x19000000 0x190FFFFF 1M 16
    BANK4 10/100M网卡DM9000 只有两个地址 0x20000000 和 0x20000004 2M 16
    BANK5 扩展串口A 0x28000000 0x28000007 8 8
    BANK6 扩展串口B 0x29000000 0x29000007 8 8
    BANK7 SDRAM 0x30000000 0x33FFFFFF 64M 32

注:10M网卡CS8900A使用nIOR、nIOW作为读/写使能信号时,ADDR24必须为1。

存储控制器的寄存器使用方法

存储控制器共有13个寄存器,BANK0-BANK5只需要设置BWSCON和BANKCONx(x为0-5)两个寄存器,BANK6、BANK7外接SDRAM时,除BWSCON和BANKCONx(x为6、7)外,还要设置REFRESH、BANKSIZE、MRSRB6、MRSRB7等4个寄存器。

  1. 位宽和等待控制寄存器BWSCON(BUS WIDTH & WAIT CONTROL REGISTER)
    BWSCON中每4位控制一个BANK。最高4位对应BANK7、接下来4位对应BANK6、依此类推。
    STx:启动/禁止SDRAM的数据掩码引脚,对于SDRAM,此位为0;对于SRAM此位为1。
    DWx:使用两位来设置相应BANK的位宽。0b00对应8位,0b01对应16位,0b10对应32位,0b11保留。
    WSx:是否使用存储器的WAIT信号,通常设为0。
    比较特殊的是BANK0,他没有ST0和WS0,DW0([2:1])只读,0b01表示16位,0b10表示32位,BANK0只支持16、32两种位宽。
  2. BANK控制寄存器BANKCONx(BANK CONTROL REGISTER x为0-5)
    这几个寄存器控制BANK0-BANK5外接设备的访问时序。
  3. BANK控制寄存器BANKCONx(BANK CONTROL REGISTER x为6-7)
    在8个BANK中,只有BANK6和BANK7可以外接SRAM或者SDRAM,所以BANKCON6-BANKCON7与BANKCON0-BANKCON5有点不同。
    MT[16:15]:用于设置本BANK外接的是ROM/SRAM还是SDRAM。SRAM-0b00,SDRAM-0b11。当MT=0b00时,此寄存器与BANKCON0-BANKCON5类似,当MT=0b11时,此寄存器其他值如下设置:
    Trcd[3:2]:RAS to CAS delay,设为推荐值,0b01。
    SCAN[1:0]:SDRAM的列地址位数,对于本开发板使用的SDRAM K4S561632,列地址位数为9,所以SCAN=0b01。如果使用其他型号的SDRAM,需要查看其数据手册来决定SCAN的取值。0b00表示8位,0b01表示9位,0b10表示10位。
  4. 刷新控制寄存器REFRESH(REFRESH CONTROL RFEGISTER):设为0x008C0000 + R_CNT
    REFEN[23]:0=禁止SDRAM的刷新功能,1=开启SDRAM的刷新功能。
    TREFMD[22]:SDRAM的刷新模式,0=CBR/ATUO Refresh,1=Self Refresh(一般在系统休眠时使用)。
    Trp[21:20]设为0即可。
    Tsrc[19:18]:设为默认值0b11即可。
    Refresh Counter[10:0]:即上述的R_CNT。可如下计算(SDRFAM的时钟频率就是HCLK):
    1
    R_CNT = 2^11 + 1 - SDRAM时钟频率(MHZ) + SDRAM刷新周期(uS)
    SDRAM的刷新周期在SDRAM的数据手册上有标明,在本开发板上使用的SDRAM K4S561632的数据手册上,可以看见这么一行”64ms refresh period (8K Cycle)“。所以,刷新周期=64ms/8192 = 7.8125 us。
    在未使用PLL时,SDRAM的时钟频率等于晶振频率,12MHz。现在可以计算:
    1
    R_CNT = 2^11 + 1 - 12 * 7.8125 = 1955
    所以在未使用PLL时,REFRESH = 0x008C0000 + 1955 = 0x008C07A3。
  5. BANKSIZE寄存器REFRESH (BANKSIZE REGISTER)
    BURST_EN[7]:0= ARM核禁止突发传输,1=ARM核支持突发传输。
    SCKE_EN[5]:0=不使用SCKE信号令SDRAM进入省电模式,1=使用SCKE信号令SDRAM进入省电模式。
    SCLK_EN[4]:0=时刻发出SCLK信号,1=仅在访问SDRAM期间发出SCLK信号(推荐)。
    BK76MAP[2:0]:设置BANK6/7的大小。
    BANK6/7对应的地址空间与BANK0-5不同,BANK0-5的地址空间大小都是固定的128MB,地址范围是(x * 128M)到(x + 1)* 128M-1,x表示0到5。BANK6/7的大小是可变的,以保持这两个空间的地址连续,即BANK7的起始地址会随他们的大小而变化。 BK76MAP的取值意义如下:
    1
    2
    0b010 = 128MB/128MB,0b001 = 64MB/64MB,0b000 = 32MB/32MB,
    0b111 = 16MB/16MB,0b110 = 8MB/8MB,0b101 = 4M/4M,0b100 = 2M/2M
    本开发板BANK6外接64MB的SDRAM,令[2:0]=0b001,表示BANK6/7的容量都是64MB,虽然BANK7未使用。
  6. SDRAM模式设置寄存器MRSRBx(SDRAM MODE REGISTER SET REGISTER,x为6-7)
    能修改的只有为CL[6:4],这是SDRAM时序的一个时间参数:
    1
    [work]0b000 = 1 clock,0b010  = 2 clocks,0b011 = 3 clocks
    SDRAM K4S561632不支持CL=1的情况,所以位[6:4]取值为0b010(CL = 2)或0b011(CL = 3)。

存储控制器操作实例:使用SDRAM

代码详解及程序的复制、跳转过程

从NAND Flash启动CPU时,CPU会通过内部的硬件将NAND Flash开始的4KB数据复制到称为”Steppingstone“的4KB的内部RAM中(起始地址为0),然后跳到地址0开始执行。
本实例先使用汇编语言设置好存储控制器,使外接的SDRAM可用:然后把程序本身从Steppingstone复制到SDRAM处,最后跳到SDRAM中执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/*
File:head.S
功能:设置SDRAM,将程序复制到SDRAM,然后跳到SDARM执行
*/

.equ MEM_CTL_BASE, 0x48000000
.equ SDRAM_BASE, 0x30000000

.text
.global _start
_start:
bl disable_watch_dog @关闭看门狗,否则CPU会一直重启
bl memsetup @设置存储控制器
bl copy_steppingstone_to_sdram @复制代码到SDRAM中
ldr pc,=on_sdram @跳到SDRAM中执行
on_sdram:
ldr sp,=0x34000000 @设置栈
bl main

halt_loop:
b halt_loop

disable_watch_dog:
mov r1,#0x53000000 @看门狗寄存器
mov r2,#0x0 @往里写0
str r2,[r1]
mov pc,lr @返回

copy_steppingstone_to_sdram:
@将Steppingstone的4KB数据全部复制到SDRAM中去。
@Steppingstone起始地址为0x00000000,SDRAM中起始地址为0x30000000
mov r1,#0
ldr r2,=SDARM_BASE
mov r3,#4*1024

l:
ldr r4,[r1],#4 @从Steppingstone读取4字节的数据,并让源地址加4
str r4,[r2],#4 @将此4字节的数据复制到SDRAM中,并让目的地址加4
cmp r1,r3 @判断是否完成:源地址等于Steppingstone的末地址
bne lb @若没有复制完,继续
mov pc, lr @返回

memsetup:
@设置存储控制器以便使用SDRAM外设
mov r1,#MEM_CTL_BASE @存储控制器的13个寄存器的开始地址
adrl r2,mem_cfg_val @这13个值的起始存储地址
add r3,r1,#52 @13*4 = 52

l:
ldr r4,[r2],#4 @读取设置值,并让r2加4
str r4,[r1],#4 @将此值写入寄存器,并让r1加4
cmp r1,r3 @判断是否设置完所有13个寄存器
bne lb @若没有完成,继续
mov pc,lr @返回


.align 4
mem_cfg_val:
@存储控制器13个寄存器的设置值
.long 0x22011110
.long 0x00000700
.long 0x00000700
.long 0x00000700
.long 0x00000700
.long 0x00000700
.long 0x00000700
.long 0x00018005
.long 0x00018005
.long 0x008C07A3
.long 0x000000B1
.long 0x00000030
.long 0x00000030

12-18行是程序的主题,为了使得程序结构明了,主要使用了函数调用的方式。
第12行禁止看门狗,否则看门狗会不断重启系统,往看门狗寄存器(0x53000000)里写0即可禁止看门狗。
第13行设置存储控制器的13个寄存器,以便使用SDRAM。
第14行将Steppingstone中的代码复制到SDRAM中(起始地址为0x30000000)。
第15行向pc寄存器直接赋值跳到SDRAM中执行下一条指令”ldr sp,=0x34000000“。、
第17行设置栈,调用c函数之前必须设置好栈。
第18行调用C函数main。
程序是如何从Steppingstone跳到SDRAM中去执行的呢?
这是通过第15行的”ldr pc,=on_sdram“指令完成的。程序标号”on_stream“这个地址值在连接程序时被确定为0x30000010(这是SDRAM的地址),执行”ldr pc,=on_sdram“后,程序一下子就跳到SDRAM中去了。
”on_sdram“这个地址值为什么等于0x30000010?
Makefile中连接程序的命令为”arm-linux-ld -Ttext 0x30000000 head.o sdram.o -o sdram_elf“,意思就是代码段的起始地址为0x30000000,即程序的第一条指令(第12行)的连接地址为0x30000000,第二条指令(第13行)的连接地址是0x30000004,…,第五条指令(第17行)的连接地址为0x300000010,其程序标号”on_sdram“的值即为0x300000010。
虽然第12-14行指令的连接地址都在SDRAM中,但是由于它们都是位置无关的相对跳转指令,所以可以在Steppingstone里执行。

1
2
3
4
5
arm-linux-gcc -g -c -o head.o head.S
arm-linux-gcc -g -c -o leds.o leds.c
arm-linux-ld -Ttext 0x30000000 -g head.o leds.o -o sdram_elf
arm-linux-objcopy -O binary -S sdram_elf sdram_elf.bin
arm-linux-objdump -D -m arm sdram_elf > sdram_elf.dis

下图所示程序从Steppingstone 到SDRAM 的执行过程。
img not found
img not found
img not found
img not found

实例测试

生成的sdram.bin下载到板子上运行之后,可以发现与leds程序相比,LED灯闪烁的更慢,原因是外部的SDRAM的性能比内部SRAM差一些。
把程序从性能更好的内部SRAM移到外部SDRAM中去,是否多此一举?内部的SRAM只有4KB大小,如果程序大于4KB,那么就不指望完全利用内部SRAM来运行了,就得想办法把存储在NAND Flash中的代码复制到SDRAM中去。对于NAND Flash的前4KB,芯片自动把它复制到内部SRAM中,可以很轻松的再把它复制到SDRAM中(实验代码中的函数copy_steppingstone_to_sdram就有此功能),要复制4KB后面的代码需要使用NAND Flash控制器来读取NAND Flash。