本章目标:
了解S3C2410/S3C2440地址空间的布局
掌握如何通过总线形式访问扩展的外设,比如内存、NOR Flash、网卡等
····································································································
总线的使用方法是嵌入式低层开发的基础,了解它之后,再根据外设的具体特性,就可以驱动外设了。
6.1 使用存储控制器访问外设的原理
6.1.1 S3C2410/S3C2440的地址空间
S3C2410/S3C2440的“存储控制器”提供了访问外部设备所需要的信息,它有如下特性:① 支持小字节序、大字节序(通过软件选择);
② 每个BANK的地址空间为128MB,总共1GB(8 BANKs);
③ 可编程控制的总线位宽(8/16/32-bit),不过BANK0只能选择两种位宽(16/32-bit);
④ 总共8个BANK,BANK0~BANK5可以支持外接ROM、SRAM等,BANK6~BANK7除了可以支持
ROM、SRAM外,还支持SDRAM等;
⑤ BANK0~BANK6共7个BANK的起始地址是固定的;
⑥ BANK7的起始地址可编程选择;
⑦ BANK6、BANK7的地址空间大小是可编程控制的;
⑧ 每个的访问周期均可编程控制;
⑨ 可以通过外部的“wait”信号延长总线的访问周期;
⑩ 在外接SDRAM时,支持自刷新(self-refresh)和省电模式(power down mode)。
S3C2410/S3C2440对外引出的27根地址线ADDR0~ADDR26的访问范围只有128MB,CPU对外
还引出了8根片选信号nGCS0~nGCS7,对应于BANK0~BANK7,当访问BANKx的地址空间时,
nGCSx引脚输出低电平用来选中外接的设备。这8个BANK的地址空间如下图6.1所示:
S3C2410/S3C2440作为32位的CPU,可以使用的地址范围理论上达到4GB。除去上述用于连接外设
的1GB地址空间外,还有一部分是CPU内部寄存器的地址,剩下的地址空间没有使用。
S3C2410/S3C2440的寄存器地址范围都处于0x480 0000~0x5ff ffff,各功能部件的寄存器大体相同,
如下表所示:
6.1.2 存储控制器与外设的关系
本书所用开发板使用了存储控制器的BANK0~BANK6,分别外接了如下设备:
NOR Flash、IDE接口、10M网卡CS8900A、100M网卡DM9000、扩展串口芯片16C2550、
SDRAM,连线方式如下图6.2所示。
外设的访问地址 = 地址线确定的地址 + BANK的起始地址。
比如:扩展串口。
(1)它使用nGCS5,起始地址为0x2800 0000。
(2)nCSA=ADDR24||nGCS5,nCSB=!ADDR24||nGCS5。当ADDR24和nGCS5均为低电平
时选中扩展串口A;当ADDR24为高电平、nGCS5为低电平时,选中扩展串口B。
(3)CPU的ADDR0~ADDR2连接到扩展串口的A0~A2,所以访问空间为8字节。
综上所述,扩展串口A的访问空间为:0x2800 0000~0x2800 0007;扩展串口B的
访问空间为:0x2900 0000~0x2900 0007(bit24为1)。
图4.2:
BANK0~BANK5的连接方式都是类似的,BANK6连接SDRAM时复杂一点,CPU提供了一
组用于SDRAM的信号。
① SDRAM时钟有效信号SCKE;
② SDRAM时钟信号SCLK0/SCLK1;
③ 数据掩码信号DQM0/DQM1/DQM2/DQM3;
④ SDRAM片选信号nSCS0(它与nGCS6是同一引脚的两个功能);
⑤ SDRAM行地址选通脉冲信号nSRAS;
⑥ SDRAM列地址选通脉冲信号nSCAS;
⑦ 写允许信号nWE(它不是专用于SDRAM的)。
SDRAM的内部是一个存储阵列,如同表格一样,将数据填进去。
SDRAM的寻址基本原理:先指定一个行,在指定一个列,就可以准确地找到所需要的单元格。
这个单元格被称为存储单元。这个表格(存储阵列)就是逻辑Bank(简称L-Bank),SDRAM一般含有
4个L-BANK。
SDRAM的逻辑结构如图6.3所示。
可以想象,对SDRAM的访问可以分为如下4个步骤:
(1)CPU发出的片选信号nSCS0有效,它选中SDRAM芯片;
(2)SDRAM中有4个L-Bank,需要两根地址信号来选中其中一个,从图6.2可知使用ADDR24、
ADDR25作为L-Bank的选择信号;
(3)对被选中的芯片进行统一行、列(存储单元)寻址;
根据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,不参与译码。
(4)找到存储单元后,被选中的芯片就要进行统一的数据传输了。
开发板中使用两片16位的SDRAM芯片并联组成32位的位宽,与CPU的32根数据线(DATA0~DATA31)相连。
BANK6的起始地址为0x3000 0000,所以SDRAM的访问地址为0x3000 0000~0x33ff ffff,共64MB。
对图6.2中连接的外设,它们的访问地址(物理地址)如表6.2所示。
6.1.3 存储控制器的寄存器使用方法
存储控制器共有13个寄存器,BANK0~BANK5只需要设置BWSCON和BANKCONx(x为0~5)两个
寄存器:BANK6和BANK7外接SDRAM时,除BWSCON和BANKCONx(x为6、7)外,还要设置REFRESH、
BANKSIZE、MRSRB6、MRSRB7等4个寄存器。下面分类说明:
1.位宽和等待控制寄存器BWSCON
每4位控制一个BANK,最高4位对应BANK7、接下来4位对应BANK6,以此类推。
(1)STx:启动/禁止SDRAM的数据掩码引脚,对于SDRAM, 此位为0;对于SRAM,此位为1;
(2)WSx:是否使用存储器的WAIT信号,通常设为0;
(3)DWx:使用两位来设置相应BANK的位宽:
0b00:8位,0b01:16位,0b10:32位,0b11:保留;
BANK0比较特殊,只支持16和32位两种位宽,它没有ST0和WS0,DW0([2:1])只读——由硬件跳线决定:
0b01:16位,0b10:32位;
对于本开发板,没有使用BANK7,根据表6.1可以确定BWSCON的值为:0x2201 1110。
2.BANK控制寄存器BANKCONx(x为0~5)
这几个寄存器用来控制BANK0~BANK5外接设备的访问时序,使用默认的0x0700即可满足
本开发板所接各外设的要求。
3.BANK控制寄存器BANKCONx(x为6~7)
在8个BANK中,只有BANK6和BANK7可以外接SRAM或SDRAM,所以BANKCON6~
BANKCON7与BANKCON0~BANKCON5有点不同。
(1)MT([16:15]):用于设置本BANK外接的是ROM/SRAM还是SDRAM。
当MT = 0b00:时,接SRAM,此寄存器与BANKCON0~BANKOCN5类似;
当MT = 0b11:时,接SDRAM,此寄存器其他值如下设置。
(2)Trcd([3:2]):RAS to CAS delay,设为推荐值0b01。
(3)SCAN([1:0]):SDRAM的列地址位数,对于本开发板使用的SDRAM K4S561632,
列地址位数为9,所以SCAN = 0b01。如果使用其他型号的SDRAM,需要查看数据手册来
决定SCAN的取值。0b00:8位;0b01:9位;0b10:10位。
综上所述,本开发板中BANKCON6、7均设为0x0001 8005。
4.刷新控制寄存器REFRESH:设为0x008c 0000 + R_CNT
(1)REFEN([23]):0 = 禁止SDRAM的刷新功能,1 = 开启SDRAM的刷新功能。
(2)TREFMD([22]):SDRAM的刷新模式,0 = CBR/Auto Refresh,1 = Self Refresh(一般在系统休眠时使用)。
(3)Trp([21:20]):设为0即可。
(4)Tsrc([19:18]):设为默认值0b11即可。
(5)Refresh Counter([10:0]):即上述的R_CNT,
R_CNT = 2^11 + 1 - SDRAM时钟频率(MHz) * SDRAM刷新周期(us);
其中SDRAM时钟频率就是HCLK,SDRAM的刷新周期在SDRAM数据手册上有标明,
在本开发板所使用的SDRAM 数据手册上,可以看见“ 64ms refresh period(8K Cycle)”。所以,
刷新周期 = 64ms/8192 = 7.8125us。
在未使用PLl时,SDRAM时钟频率等于晶振频率12MHz。
现在可以计算:R_CNT = 2^11 + 1 - 12*7.8125 = 1955。
所以,在未使用PLL时,REFRESH = 0x008c 0000 + 195 = 0x008C 07A3。
5.BANKSIZE寄存器BANKSIZE
(1)BURST_EN([7])。
0 = ARM核禁止突发传输,1 = ARM核支持突发传输。
(2)SCKE_EN([5])。
0 = 不使用SCKE信号令SDRAM进入省电模式,1 = 使用SCKEN信号令SDRAM进入省电模式。
(3)SCLK_EN([4])。
0 = 时刻发出SCLK信号,1 = 仅在访问SDRAM期间发出SCLK信号(推荐)。
(4)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的取值意义如下:
0b010 = 128MB/128MB,0b001 = 64MB/64MB,0b000 = 32M/M,
0b111 = 16M/16M,0b110 = 8M/8M,0b101 = 4M/4M,0b100 = 2M/2M
本开发板BANK6外接64MB的SDRAM,令[2:0] = 0b001,表示BANK6/7的容量都是64MB,
虽然BANK7没使用。
综上所述,开发板的BANKSIZE寄存器的值可算得0xB1。
6.SDRAM模式设置寄存器MRSRBx(x为6~7)
能修改的只有位CL([6:4]),这是SDRAM时序的一个时间参数:
[work] 0b000 = 1 clock,0b010 = 2 clocks,0b011 = 3 clocks
SDRAM K4S561632不支持CL = 1的情况,所以此位取值0b010(CL = 2)或0b011(CL = 3)。
本开发板取最保守值0b011,所以MRSRB6/7的值为0x30。
6.2 存储控制器操作实例:使用SDRAM
6.2.1 代码详解及程序的复制、跳转过程
从NAND Flash启动CPU时,CPU会通过内部的硬件将NAND Flash开始的4KB数据复
制到称为“Steppingstone”的4KB的内部RAM中(起始地址为0),然后跳到地址0开始执行。
本实例先使用汇编语言设置好存储控制器,使外接的SDRAM可用;然后把程序从
Steppingstone复制到SDRAM处;最后跳到SDRAM中执行。
源代码在/work/hardware/sdram目录中,包含两个文件head.S和leds.c。其中leds.c和第5
章中的leds的代码完全一样,也是让3个led从0~7轮流计数。
重点在head.S,它的作用是设置SDRAM,将程序复制到SDRAM,然后跳到SDRAM
继续执行。head.S的代码如下:
1 @**************************************** 2 @ File:head.S 3 @ 功能:设置SDRAM,将程序复制到SDRAM,然后跳到SDRAM继续执行 4 @**************************************** 5 6 .equ MEM_CTL_BASE, 0x48000000 7 .equ SDRAM_BASE, 0x30000000 8 9 .text 10 .global _start 11 _start: 12 bl disable_watch_dog @关闭WATCHDOG 13 bl memsetup @设置存储控制器 14 bl copy_steppingston_to_sdram @复制代码到SDRAM中 15 ldr pc, =on_sdram @跳到SDRAM中继续执行 16 on_sdram: 17 ldr sp, =0x34000000 @设置栈 18 bl main 19 halt_loop: 20 b halt_loop 21 22 disable_watch_dog: 23 @往WATCHDOG寄存器写0即可 24 mov r1, #0x53000000 25 mov r2, #0x0 26 str r2, [r1] 27 mov pc, lr @返回 28 29 copy_steppingstone_to_sdram: 30 @将Steppingstone的4KB数据全部复制到SDRAM中去 31 @Steppingstone起始地址为0x0000 0000,SDRAM中起始地址为0x3000 0000 32 33 mov r1, #0 34 ldr r2, =SDRAM_BASE 35 mov r3, #4*1024 36 1: 37 ldr r4, [r1], #4 @从Steppingstone(0x0000 0000)读取4字节的数据,并让源地址加4 38 str r4, [r2], #4 @将此4字节数据复制到SDRAM中,并让目的地址加4 39 cmp r1, r3 @判断是否完成:源地址是否等于Steppingstone的末地址? 40 bne 1b @若没有复制完,继续 41 mov pc, lr @返回 42 43 memsetup: 44 @设置存储控制器以便使用SDRAM等外设 45 46 mov r1, #MEM_CTL_BASE @存储控制器的13个寄存器的开始地址 47 adr1 r2, mem_cfg_val @这13个值的起始存储地址 48 add r3, r1, #52 @13*4 = 52 49 1: 50 ldr r4, [r2], #4 @读取“待设置值”,并让r2加4 51 str r4, [r1], #4 @将此值写入寄存器,并让r1加4 52 cmp r1, r3 @判断是否设置完所有13个寄存器 53 bne 1b @若没有写完,继续 54 mov pc, lr @返回 55 56 57 .align 4 58 mem_cfg_val: 59 @存储控制器13个寄存器的设置值 60 .long 0x22011110 @BWSCON 61 .long 0x00000700 @BANKCON0 62 .long 0x00000700 @BANKCON1 63 .long 0x00000700 @BANKCON2 64 .long 0x00000700 @BANKCON3 65 .long 0x00000700 @BANKCON4 66 .long 0x00000700 @BANKCON5 67 .long 0x00018005 @BANKCON6 68 .long 0x00018005 @BANKCON7 69 .long 0x008c07a3 @REFRESH 70 .long 0x000000b1 @BANKSIZE 71 .long 0x00000030 @MRSRB6 72 .long 0x00000030 @MRSRB7
程序是如何通过第15行的“ ldr pc, =on_sdram”指令来完成的?
程序标号“on_sdram”这个地址值在连接程序时被确定为0x3000 0010(这个是SDRAM的
地址),执行“ldr pc, =on_sdram”后,程序一下子就跳到SDRAM中去了。
“on_sdram”这个地址值为什么等于0x3000 0010?
Makefile中连接程序的命令为“arm-linux-ld -Ttext 0x30000000 head.o sdram.o -o
sdram_elf”,意思就是代码段的起始地址为0x3000 0000,即程序的第一条指令(第12行)的
连接地址为0x3000 0000,第二条指令(第13行)的连接地址为0x3000 0004,...,第5条指
令(第17行)的连接地址为0x3000 0010,其程序标号“on_sdram”的值就是0x3000 0010。
虽然第12~14行指令的连接地址都在SDRAM中,但是由于他们都是位置无关的相对
跳转指令,所以可以在Steppingstone里执行。
Makefile如下(注意第4行,“-Ttext 0x30000000”指定了代码段的起始地址):
1 sdram.bin : head.S leds.c 2 arm-linux-gcc -c -o head.o head.S 3 arm-linux-gcc -c -o leds.o leds.c 4 arm-linux-ld -Ttext 0x30000000 head.o leds.o -o sdram_elf 5 arm-linux-objcopy -O binary -S sdram_elf sdram.bin 6 arm-linux-objdump -D -m arm sdram_elf > sdram.dis 7 clean: 8 rm -f sdram.dis sdram.bin sdram_elf *.o
为了更形象地了解本程序,下面用图6.4来演示程序的复制、跳转过程。
6.2.2 实例测试
在sdram目录中执行make指令生成可执行文件sdram.bin后,下载到板子上运行。可以发现与leds程序相比,LED闪烁得更慢,原因是外部SDRAM的性能比内部SRAM差一些。
把程序从性能更好的内部SRAM移到外部SDRAM中去,是否多此一举呢?内部SRAM
只有4KB大小,如果程序大于4KB,那么就不能指望完全利用内部SRAM来运行了,得想
办法把存储在NAND Flash中的代码复制到SDRAM中去。对于NAND Flash中的前4KB,
芯片自动把它复制到内部SRAM中,可以很轻松地再把它复制到SDRAM中(实验代码中的
函数copy_steppingston_to_sdram就有此功能),要复制4KB后面的代码需要使用NAND Flash
控制器来读取NAND Flash,这就是第8章的内容。
附:代码:
链接: https://pan.baidu.com/s/1kV24a9L 密码: tfab