基于ATMEL AT91RM9200的嵌入式Linux移植笔记 (2)

2019-07-13 04:25发布

来自: http://hi.baidu.com/xfcylyf/blog/item/10776a95157e6b46d1135ef4.html
一、了解一下存储器的基本分类情况。
存储器的物理实质是一组或多组具备数据输入输出和数据存储功能的集成电路,用于充当设备缓存或保存固定的程序及数据。存储器按存储信息的功能可分为只读存储器ROM(Read Only Memory)和随机存储器RAM(Random Access Memory)。
图1   常见存储器分类



1、   ROM
ROM 中的信息一次写入后只能被读出,而不能被操作者修改或删除,一般由芯片制造商进行掩膜写入信息,价格便宜,适合于大量的应用。一般用于存放固定的程序,如监控程序、汇编程序等,以及存放各种表格。EPROM(Erasable Programmable ROM)和一般的ROM不同点在于它可以用特殊的装置擦除和重写它的内容,一般用于软件的开发过程。
特别介绍:闪存(Flash Memory)
闪速存储器(Flash Memory)又称PEROM(Programmable and Erasable Read Only Memory),是Intel 公司在80 年代末90 年代初推出的,由于它的众多优点而深受用户的青睐。Flash Memory 的两个主要特点是可以按整体/扇区擦除和按字节编程。它是完全非易失的,可以在线写入,并且可以按页连续字节写入,读出速度高。Flash 芯片划分成很多扇区,把一位从0 重置为1 不能通过对该位单独操作来实现,而必须擦除整个扇区。Flash芯片的寿命就用擦除周期来衡量。通常的寿命为每个扇区可擦除100,000 次。为了避免任意一个扇区在其他扇区之前达到这个极限,大多数Flash 芯片用户会尽量保证擦除次数在各扇区之间均匀分布,这一过程称为“磨损均衡”(wear leveling)
2、   RAM
RAM 就是我们平常所说的内存,主要用来存放各种现场的输入、输出数据,中间计算结果,以及与外部存储器交换信息和作堆栈用。它的存储单元根据具体需要可以读出,也可以写入或改写。RAM 只能用于暂时存放程序和数据,一旦关闭电源或发生断电,其中的数据就会丢失。现在的RAM 多为MOS 型半导体电路,它分为静态和动态两种。静态RAM 是靠双稳态触发器来记忆信息的;动态RAM 是靠MOS 电路中的栅极电容来记忆信息的。由于电容上的电荷会泄漏,需要定时给与补充,所以动态RAM 需要设置刷新电路。但动态RAM 比静态RAM 集成度高、功耗低,从而成本也低,适于作大容量存储器。所以主内存通常采用动态RAM,而高速缓冲存储器(Cache)则使用静态RAM。动态RAM 按制造工艺的不同,又可分为动态随机存储器(Dynamic RAM)、扩展数据输出随机存储器(Extended Data Out RAM)和同步动态随机存储器(Synchromized DynamicRAM)。DRAM 需要恒电流以保存信息,一旦断电,信息即丢失。它的刷新频率每秒钟可达几百次,但由于DRAM 使用同一电路来存取数据,所以DRAM 的存取时间有一定的时间间隔,这导致了它的存取速度并不是很快。另外,在DRAM 中,由于存储地址空间是按页排列的,所以当访问某一页面时,切换到另一页面会占用CPU 额外的时钟周期。EDO-RAM同DRAM 相似,但在把数据发送给CPU 的同时可以去访问下一个页面,故而速度要比普通DRAM 快15~30%。SDRAM 同DRAM 有很大区别,它使用同一个CPU 时钟周期即可完成数据的访问和刷新,即以同一个周期、相同的速度、同步的工作,因而可以同系统总线以同频率工作,可大大提高数据传输率,其速度要比DRAM 和EDO-RAM 快很多(比EDO-RAM提高近50%)。
二、AT91RM9200开发板的存储器情况
第一级地址译码由存储控制器执行,即由具有附加功能的高级系统总线(ASB) 执行。译码将4G的地址空间分为16 个256M字节的区域。区域1 ~ 8 对应EBI,和外部片选NC0 ~NCS7相联系。区域0为内部存储器地址,第二级译码提供1M字节内部存储空间。区域15为外设地址,且提供对高级外设总线(APB) 的访问。其它区域未使用,使用它们进行访问时将向发出访问请求的主机发出异常中断。注意,地址的转换都是按照字节为单位的。
1、内部存储器映射
内部ROM:AT91RM9200集成了一个128-K字节的内部ROM。任何时候,ROM均被映射到地址0x10 0000。若复位时BMS 为高,则在复位后到重新映射命令执行前,可访问地址0x0。ROM容量为128KB,即对应0x20000。所以范围为0x100000-0x120000。
内部RAM:AT91RM9200集成了高速,16-K 字节的内部SRAM。复位后到重新映射命令执行前,只可访问SRAM 中0x20 0000的地址空间。重新映射后, SRAM 在地址0x0 同样有效。SRAM容量为16KB,即对应0x4000,所以范围为0x200000-0x204000。
USB 主机端口:AT91RM9200集成了一个USB主机端口开放主机控制器接口(OHCI)。ASB可直接访问该接口寄存器,且同标准内部存储器一样映射到地址0x30 0000。
图2 内部存储器映射



2、外部存储器映射
图3 外部存储器映射


嵌入式存储设备通常主要是RAM 和作为永久存储媒质的Flash。
现在所用的AT91RM9200开发板所用的SDRAM是HY57V281620HCT-H,其容量为4banks×2Mbits×16,即128Mbits=16Mbytes。SDRAM共有两片HY57V281620HCT-H,所以SDRAM容量为32MB。
现在所用的Flash芯片为Intel的28F640J3,容量为8MB,地址映射从0x10000000到0x10800000。现在将Flash分为64个扇区,每个扇区为128KB=0x20000,每个扇区分为两个擦除块,为64KB=0x10000。
-------------------------------------------------------------------
Chip Select 0――Flash(0x1000 0000-0x1FFF FFFF)
0x1000 0000(第0扇区)
       boot.bin          Flash
0x1001 0000(第0扇区)
       u-boot.bin.gz Flash
0x1002 0000(第1扇区)
       uImage       Flash
.
.
.
0x1012 0000(第9扇区)
       ramdisk       Flash
.
.
.
0x107E 0000(第63扇区)
       u-boot环境变量 Flash
-------------------------------------------------------------------
Chip Select 1――SDRAM(0x2000 0000-0x2200 0000)
0x2000 0000
                   SDRAM
.
.
0x2100 0000
       uImage    SDRAM
0x2110 0000
       ramdisk     SDRAM
.
.
-------------------------------------------------------------------
2006-08-17   u-boot移植
1   首先,了解一下bootloader。bootloader是系统加电后运行的第一段代码。它要完成的工作就是初始化硬件设备,建立内存空间的映射图,这样为最终调用操作系统内核做好准备。
2   bootloader的操作模式
(1)启动加载模式(bootloading)
(2)下载模式(downloading)
开发时要用(2),target board上的bootloader将通过串口或者网络等通信手段从host上下载内核映象和根文件系统映象等到ram中。
3   bootloader的启动方式:网络启动、磁盘启动、flash启动。
4   bootloader的种类
区分一下“bootloader”和“monitor”的概念。bootloader只是引导设备并且执行主程
序的固件;而monitor还提供了更多的命令行接口,可以进行调试、读写内存、烧写flash、配置环境变量等。monitor在嵌入式系统开发过程中
还可以提供很好的调试功能,开发完成后,就完全配置成了一个bootloader。所以,习惯上把它们统称为bootloader。我现在要使用的u-
boot就是典型的monitor。
5   bootloader的启动流程
搜集了一些资料,写的比较精彩,放在这里。。。
[color="#999900"]资料一:
系统上电,检测BMS,选择系统的启动方式,如果BMS为高电平,则系统从片
内ROM启动。AT91RM9200的ROM上电后被映射到了0x0和0x100000处,在这两个地址处都可以访问到ROM。由于9200的ROM中固
化了一个BOOTLOAER程序。所以PC从0X0处开始执行这个BOOTLOAER(准确的说应该是一级BOOTLOADER)。这个BOOTLOER
依次完成以下步骤:
1.              PLL SETUP
设置PLLB产生48M时钟频率提供给USB DEVICE。同时DEBUG USART也被初始化为48M的时钟频率。
2.              相应模式下的堆栈设置
3.              检测主时钟源(Main oscillator)
4.              中断控制器(AIC)的设置
5.              C 变量的初始化
6.              跳到主函数
完成以上步骤后,我们可以认为BOOT过程结束,接下来的就是LOADER的过程,或者也可以认为是装载二级BOOTLOER。
[color="#999900"]AT91RM9200

照DATAFLASH、EEPROM、连接在外部总线上的8位并行FLASH的顺序依次来找合法的BOOT程序。所谓合法的指的是在这些存储设备的开始地
址处连续的存放的32个字节,也就是8条指令必须是跳转指令或者装载PC的指令,其实这样规定就是把这8条指令当作是异常向量表来处理。必须注意的是第6
条指令要包含将要装载的映像的大小。关于如何计算和写这条指令可以参考用户手册。一旦合法的映像找到之后,则BOOT程序会把找到的映像搬到SRAM中
去,所以映像的大小是非常有限的,不能超过16K的大小。当BOOT程序完成了把合法的映像搬到SRAM的任务以后,接下来就进行存储器的REMAP,经
过REMAP之后,SRAM从映设前的0X200000地址处被映设到了0X0地址并且程序从0X0处开始执行。而ROM这时只能在0X100000这个
地址处看到了。至此9200就算完成了一种形式的启动过程。如果BOOT程序在以上所列的几种存储设备中找到合法的映像,则自动初始化DEBUG
USART口和USB DEVICE口以准备从外部载入映像。对DEBUG口的初始化包括设置参数115200 8 N
1以及运行XMODEM协议。对USB
DEVICE进行初始化以及运行DFU协议。现在用户可以从外部(假定为PC平台)载入你的映像了。在PC平台下,以WIN2000为例,你可以用超级终
端来完成这个功能,但是还是要注意你的映像的大小不能超过13K。一旦正确从外部装载了映像,接下来的过程就是和前面一样重映设然后执行映像了。我们上面
讲了BMS为高电平,
[color="#999900"]AT91RM9200
[color="#999900"]选择从片内的ROM启动的一个过程。如果BMS为低电平,则
[color="#999900"]AT91RM9200

从片外的FLASH启动,这时片外的FLASH的起始地址就是0X0了,接下来的过程和片内启动的过程是一样的,只不过这时就需要自己写启动代码了,至于
怎么写,大致的内容和ROM的BOOT差不多,不同的硬件设计可能有不一样的地方,但基本的都是一样的。由于片外FLASH可以设计的大,所以这里编写的
BOOTLOADER可以一步到位,也就是说不用像片内启动可能需要BOOT好几级了,目前
[color="#999900"]AT91RM9200
[color="#999900"]上使用较多的bootloer是u-boot,这是一个开放源代码的软件,用户可以自由下载并根据自己的应用配置。
[color="#ff9933"]资料2 
[color="#ff9933"]loader.bin, boot.bin, u-boot.bin代码执行流分析.
以上三个文件时at91rm9200启动所需要的三个bin,他们的实现代码并不难。
如果是你是采用at91rm9200的评估版,应该能得到其源码。
loader.bin 执行流程,这个文件主要在片内启动从串口下载代码时会用到
loader/entry.S init cpu
b main ---> crt0.S
--> copydata --> clearbss --> b boot
main.c --> boot -->
/*Get internel rom service address*/
/* Init of ROM services structure */ 
pAT91 = AT91C_ROM_BOOT_ADDRESS;
/* Xmodem Initialization */
--> pAT91->OpenSBuffer
--> pAT91->OpenSvcXmodem
/* System Timer initialization */
---> AT91F_AIC_ConfigureIt
/* Enable ST interrupt */
AT91F_AIC_EnableIt
AT91F_DBGU_Printk("XMODEM: Download U-BOOT ");
Jump.S
// Jump to Uboot BaseAddr exec
Jump((unsigned int)AT91C_UBOOT_BASE_ADDRESS) 
boot.bin执行流程 该文件会在从片内启动时被下载到板子上,以后还会被烧写到片外Flash中,以便在片外启动时
用它来引导并解压u-boot.bin.gz,并跳转到u-boot来执行。
boot/entry.S
b main --> crt0.S --> copydata --> clearbss --> b boot
AT91F_DBGU_Printk(" ");
AT91F_DBGU_Printk("************************************** ");
AT91F_DBGU_Printk("** Welcome to at91rm9200 ** ");
AT91F_DBGU_Printk("************************************** ");
boot/misc.s /* unzip uboot.bin.gz */
----> decompress_image(SRC,DST,LEN) --> gunzip 
//jump to ubootBaseAddr exec 这里跳转到解压u-boot.bin.gz的地址处直接开始执行u-boot
asm("mov pc,%0" : : "r" (DST));
u-boot.bin执行流程
u-boot/cpu/at91rm9200/start.S 
start --->reset 
---> copyex ---> cpu_init_crit 
---> /* set up the stack */ --> start_armboot
u-boot/lib_arm/board.c
init_fnc_t *init_sequence[] = {
cpu_init, /* basic cpu dependent setup */
board_init, /* basic board dependent setup */
interrupt_init, /* set up exceptions */
env_init, /* initialize environment */
init_baudrate, /* initialze baudrate settings */
serial_init, /* serial communications setup */
console_init_f, /* stage 1 init of console */
display_banner, /* say that we are here */
dram_init, /* configure available RAM banks */
display_dram_config,
checkboard,
NULL,
};
---> start_armboot ---> call init_sequence
---> flash_init --> display_flash_config 
---> nand_init ---> AT91F_DataflashInit 
---> dataflash_print_info --> env_relocate
---> drv_vfd_init --> devices_init --> jumptable_init
---> console_init_r --> misc_init_r --> enable_interrupts
---> cs8900_get_enetaddr --> board_post_init --> 
u-boot/common/main.c
for (;;) 
{ /* shell parser */
main_loop () --> u_boot_hush_start --> readline
--> abortboot 
-->printf("Hit any key to stop autoboot: %2d ", bootdelay);
}
以上是at91rm9200启动并进入u-boot的执行流分析。后面u-boot还会将uImage解压到特定的位置并开始执行内核代码。
6   u-boot-1.1.1的移植
自己先用u-boot-1.1.1,主要步骤如下:
(1)下载u-boot-1.1.1
http://sourceforge.net/projects/u-boot
(2)解压
用户:armlinux
$mkdir bootloader
$cd bootloader
$tar jxvf ../source/u-boot-1.1.1.tar.bz2
$cd u-boot-1.1.1
(3)修改
首先看一下结构
$ tree -L 1 -d
.
[color="#0000ff"]|-- board   平台依赖,存放电路板相关的目录文件
|-- common   通用多功能函数的实现
[color="#0000ff"]|-- cpu   平台依赖,存放cpu相关的目录文件
[color="#0000ff"]|-- disk   通用。硬盘接口程序
|-- doc   文档
|-- drivers   通用的设备驱动程序,如以太网接口驱动
|-- dtt
|-- examples   应用例子
|-- fs   通用存放文件系统的程序
|-- include   头文件和开发板配置文件,所有开发板配置文件放在其configs里
|-- lib_arm   平台依赖,存放arm架构通用文件
|-- lib_generic   通用的库函数
|-- lib_i386   平台依赖,存放x86架构通用文件
|-- lib_m68k   平台依赖
[color="#0000ff"]|-- lib_microblaze   平台依赖
|-- lib_mips   平台依赖
[color="#0000ff"]|-- lib_nios 平台依赖
|-- lib_ppc平台依赖,存放ppc架构通用文件
|-- net   存放网络的程序
|-- post   存放上电自检程序
|-- rtc   rtc的驱动程序
`-- tools   工具
然后具体步骤为:
(一)在board文件夹下面建立自己的开发板的文件夹。一般的,要选取与自己的开发板硬件设置最为接近的型
号。在u-boot-1.1.1中,已经支持at91rm9200,所以可以选取at91rm9200dk作为模板进行修改。设置你的开发板的名字,随意
即可,我的设置为:myboard。
[armlinux@lqm u-boot-1.1.1]$ cd board
[armlinux@lqm board]$ cp -R at91rm9200dk/ myboard/
[armlinux@lqm board]$ cd myboard
[armlinux@lqm myboard]$ ls
at91rm9200dk.c   config.mk   flash.c   Makefile   u-boot.lds
(二)可以看到,这里共有5个文件。首先,要修改主文件的名字,即要把at91rm9200dk.c更改为
myboard.c。其次,要更改config.mk中TEXT_BASE的数值,与loader等一级bootloader的要一致。接下来,因为在
at91rm9200dk用的是AMD的flash,而我的开发板上用的是Intel的28F640J3,那么需要另外找Intel的flash.C,以
减少工作量。在这里,推荐用source
insight这个查看代码的工具。我是在win下面使用的,它可以很方便的读代码,并且查找调用函数等等的工作。在strong
ARM构架里有xm250,它的flash是Intel的,修改的东西并不是很多。需要注意的是,xm250的flash位宽是32,而我的位宽是16,
要根据这个进行相应的修改。最后,修改Makefile,主要是修改生成文件的名字。具体操作如下:
[armlinux@lqm myboard]$ mv at91rm9200dk.c myboard.c
[armlinux@lqm myboard]$ cat config.mk
[color="#ff0000"]TEXT_BASE = 0x21f80000
[armlinux@lqm myboard]$ vi config.mk
修改成:TE[color="#0000ff"]XT_BASE = 0x21f00000,然后保存退出。
[armlinux@lqm myboard]$ vi Makefile
include $(TOPDIR)/config.mk
LIB     = lib$(BOARD).a
OBJS := [color="#0000ff"]myboard.o flash.o
SOBJS :=
$(LIB): $(OBJS) $(SOBJS)
       $(AR) crv $@ $(OBJS) $(SOBJS)
clean:
       rm -f $(SOBJS) $(OBJS)
[armlinux@lqm myboard]$ rm flash.c
[armlinux@lqm myboard]$ cp ../xm250/flash.c ./
[armlinux@lqm myboard]$ ls
config.mk   flash.c   Makefile   myboard.c   u-boot.lds
[armlinux@lqm myboard]$ vi flash.c

    34 [color="#0000ff"]#undef FLASH_PORT_WIDTH32 /*不定义位宽32*/
    35 [color="#0000ff"]#define FLASH_PORT_WIDTH16   /*定义位宽16*/
216       switch (value) {
217 
218       case (FPW) INTEL_ID_28F128J3A:
219                 info->flash_id += FLASH_28F128J3A;
220                 info->sector_count = 128;
221                 [color="#0000ff"]info->size = 0x01000000;
222                 break;                          [color="#0000ff"]/* => 16 MB     */
223 
224       case (FPW) [color="#ff0000"]INTEL_ID_28F640J3A:     [color="#ff0000"]/*[color="#ff0000"]就是这个芯片*/
225                 info->flash_id += FLASH_28F640J3A;
226                 info->sector_count = 64;
227                 [color="#0000ff"]info->size = 0x00800000;
228                 break;                          [color="#0000ff"]/* => 8 MB     */

[armlinux@lqm myboard]$ cd ../..
[armlinux@lqm u-boot-1.1.1]$ vi Makefile
#########################################################################
## AT91RM9200 Systems
#########################################################################
at91rm9200dk_config     :    unconfig
       @./mkconfig $(@:_config=) arm at91rm9200 at91rm9200dk
[color="#0000ff"]myboard_config   :    unconfig
       @./mkconfig $(@:_config=) arm at91rm9200 myboard
#########################################################################
在这里,可以在命令模式下输入“/at91rm9200”快速查找
at91rm9200dk,仿照它的例子,写出自己板子的配置。注意的是,第二行开头要用TAB键,不是空格,否则报错。选项arm表示目标板架构,
at91rm9200表是片上系统,myboard是你自己的开发板名字。
[armlinux@lqm u-boot-1.1.1]$ vi MAKEALL 
LIST_ARM9="    
       at91rm920