最新日志

发表于:2007-11-12 17:30:24
标签:S3C2410  nandflash  嵌入式  bootloader  linux  

0

Linux BOOTLOADER全程详解(ARM S3C2410)

网上关于Linux的BOOTLOADER文章不少了,但是大都是vivi,blob等比较庞大的程序,读起来不太方便,编译出的文件也比较大,而且更多的是面向开发用的引导代码,做成产品时还要裁减,这一定程度影响了开发速度,对初学者学习开销也比较大,在此分析一种简单的BOOTLOADER,是在三星公司提供的S3C2410 BOOTLOADER上稍微修改后的结果,编译出来的文件大小不超过4k,希望对大家有所帮助.

1.几个重要的概念

* COMPRESSED KERNEL and DECOMPRESSED KERNEL
压缩后的KERNEL,按照文档资料,现在不提倡使用DECOMPRESSED KERNEL,而要使用COMPRESSED KERNEL,它包括了解压器.因此要在ram分配时给压缩和解压的KERNEL提供足够空间,这样它们不会相互覆盖.
当执行指令跳转到COMPRESSED KERNEL后,解压器就开始工作,如果解压器探测到解压的代码会覆盖掉COMPRESSED KERNEL,那它会直接跳到COMPRESSED KERNEL后存放数据,并且重新定位KERNEL,所以如果没有足够空间,就会出错.
* Jffs2 File System
可以使armlinux应用中产生的数据保存在FLASH上,我的板子还没用到这个.
* RAMDISK
使用RAMDISK可以使ROOT FILE SYSTEM在没有其他设备的情况下启动.一般有两种加载方式,我就介绍最常用的吧,把COMPRESSED RAMDISK IMAGE放到指定地址,然后由BOOTLOADER把这个地址通过启动参数的方式ATAG_INITRD2传递给KERNEL.具体看代码分析.
* 启动参数(摘自IBM developer)
在调用内核之前,应该作一步准备工作,即:设置 Linux 内核的启动参数。Linux 2.4.x 以后的内核都期望以标记列表(tagged list)的形式来传递启动参数。启动参数标记列表以标记 ATAG_CORE 开始,以标记 ATAG_NONE 结束。每个标记由标识被传递参数的 tag_header 结构以及随后的参数值数据结构来组成。数据结构 tag 和 tag_header 定义在 Linux 内核源码的include/asm/setup.h 头文件中.
在嵌入式 Linux 系统中,通常需要由 BOOTLOADER 设置的常见启动参数有:ATAG_CORE、ATAG_MEM、ATAG_CMDLINE、ATAG_RAMDISK、ATAG_INITRD等。
(注)参数也可以用COMMANDLINE来设定,在我的BOOTLOADER里,我两种都用了.

2.开发环境和开发板配置:
CPU:S3C2410,BANK6上有64M的SDRAM(两块),BANK0上有32M NOR FLASH,串口当然是逃不掉的.这样,按照数据手册,地址分配如下:
0x4000_0000开始是4k的片内DRAM.
0x0000_0000开始是32M FLASH 16bit宽度
0x3000_0000开始是64M SDRAM 32bit宽度
注意:控制寄存器中的BANK6和BANK7部分必须相同.
0x4000_0000(片内DRAM)存放4k以内的BOOTLOADER IMAGE
0x3000_0100开始存放启动参数
0x3120_0000 存放COMPRESSED KERNEL IMAGE
0x3200_0000 存放COMPRESSED RAMDISK
0x3000_8000 指定为DECOMPRESSED KERNEL IMAGE ADDRESS
0x3040_0000 指定为DECOMPRESSED RAMDISK IMAGE ADDRESS
开发环境:Redhat Linux,armgcc toolchain, armlinux KERNEL

如何建立armgcc的编译环境:建议使用toolchain,而不要自己去编译armgcc,偶试过好多次,都以失败告终.
先下载arm-gcc 3.3.2 toolchain
将arm-linux-gcc-3.3.2.tar.bz2 解压到 /toolchain
# tar jxvf arm-linux-gcc-3.3.2.tar.bz2
# mv /usr/local/arm/3.3.2 /toolchain
在makefile 中在把arch=arm CROSS_COMPILE设置成toolchain的路径
还有就是INCLUDE = -I ../include -I /root/my/usr/local/arm/3.3.2/include.,否则库函数就不能用了

3.启动方式:
可以放在FLASH里启动,或者用Jtag仿真器. 由于使用NOR FLASH,根据2410的手册,片内的4K DRAM在不需要设置便可以直接使用,而其他存储器必须先初始化,比如告诉memory controller,BANK6里有两块SDRAM,数据宽度是32bit,= =.否则memory control会按照复位后的默认值来处理存储器.这样读写就会产生错误.
所以第一步,通过仿真器把执行代码放到0x4000_0000,(在编译的时候,设定TEXT_BAS
E=0x40000000) 。
第二步,通过 AxD把linux KERNEL IMAGE放到目标地址(SDRAM)中,等待调用
第三步,执行BOOTLOADER代码,从串口得到调试数据,引导armlinux

4.代码分析
讲了那么多执行的步骤,是想让大家对启动有个大概印象,接着就是BOOTLOADER内部的代码分析了,BOOTLOADER文章内容网上很多,我这里精简了下,删除了不必要的功能.
BOOTLOADER一般分为2部分,汇编部分和c语言部分,汇编部分执行简单的硬件初始化,C部分负责复制数据,设置启动参数,串口通信等功能.
BOOTLOADER的生命周期:
1. 初始化硬件,比如设置UART(至少设置一个),检测存储器= =.
2. 设置启动参数,这是为了告诉内核硬件的信息,比如用哪个启动界面,波特率 = =.
3. 跳转到Linux KERNEL的首地址.
4. 消亡

当然,在引导阶段,象vivi等,都用虚地址,如果你嫌烦的话,就用实地址,都一样.
我们来看代码:
2410init.s
.global _start//开始执行处
_start:
//下面是中断向量
b reset @ Supervisor Mode//重新启动后的跳转
……
……
reset:
ldr r0,=WTCON /WTCON地址为53000000,watchdog的控制寄存器 */
ldr r1,=0x0 /*关watchdog*/
str r1,[r0]

ldr r0,=INTMSK
ldr r1,=0xffffffff /*屏蔽所有中断*/
str r1,[r0]

ldr r0,=INTSUBMSK
ldr r1,=0x3ff /*子中断也一样*/
str r1,[r0]
/*Initialize Ports...for display LED.*/
ldr r0, =GPFCON
ldr r1, =0x55aa
str r1, [r0]
ldr r0, =GPFUP
ldr r1, =0xff
str r1, [r0]
ldr r0,=GPFDAT
ldr r1,=POWEROFFLED1
str r1,[r0]
/* Setup clock Divider control register
* you must configure CLKDIVN before LOCKTIME or MPLL UPLL
* because default CLKDIVN 1,1,1 set the SDMRAM Timing Conflict
nop
* FCLK:HCLK:PCLK = 1:2:4 in this case
*/
ldr r0,=CLKDIVN
ldr r1,=0x3
str r1,[r0]

/*To reduce PLL lock time, adjust the LOCKTIME register. */
ldr r0,=LOCKTIME
ldr r1,=0xffffff
str r1,[r0]
/*Configure MPLL */
ldr r0,=MPLLCON
ldr r1,=((M_MDIV<<12)+(M_PDIV<<4)+M_SDIV) //Fin=12MHz,Fout=203MHz
str r1,[r0]
ldr r1,=GSTATUS2
ldr r10,[r1]
tst r10,#OFFRST
更多>>

点击此处查看原文 >>

系统分类: 嵌入式   |    用户分类:    |    来源: 无分类

评论(0) | 阅读(867)
发表于:2007-8-27 14:55:06
标签:nandflash  nand  flash  nand闪存  

0

一种NAND FLASH自启动的新方法

1

随着消费类电子产品包括 PDA , MP3 、智能手机等手持设备的市场需求逐步扩大,产品间的竞争也愈发激烈,降低产品的设计成本,提升产品的市场竞争力成为嵌入式系统开发者所面临的重大挑战。 NAND FLASH 和 NORFLASH 作为两种主要的非易失性存储器,被应用于各种嵌入式系统。其中 NAND FLASH主要优点在于存储密度高、容量大,有更占优势的存储性价比。但是NANDFLASH 由于其独特的页式读写方式,并不适合程序的直接执行。因此,从NAND FLASH启动需要片上存储器作为代码执行的中转区。本文所讨论的一种系统启动方式,是在缺少片上存储器支持的情况下,实现系统直接从NAND FLASH启动。论文中充分考虑了如何实现软、硬件之间的协同工作,以完成 SOC 系统的设计。

2 NAND FLASH 控制器的结构

本文所讨论的NAND FLASH控制器是针对一款基于 ARM7TDMI 的 SoC 芯片,该控制器在芯片中的位置如图 1 所示,作为 AMBA 总线上的一个从设备集成于 AHB 上。主要模块包括总线接口模块、 FIFO 缓冲模块、 ECC 编码模块以及逻辑控制模块。

总线接口模块主要的功能是转换 AMBA 总线上的控制和数据信号:将总线上的数据送入 FIFO 或将数据从 FIFO 读出到总线上,将总线上的控制信号转换时序后送到控制模块。

NAND 控制器包含一个宽度为 32 b ,深度为 4 的缓冲 FIFO ,用于解决高速总线与低速设备之间数据传输速度的匹配问题。为提高总线的传输效率,以及控制器设计的便利性,NAND FLASH在总线上的数据传输采用 DMA 的方式来完成。譬如在读取 FLASH 一页数据时,数据持续写入控制器 FIFO , FIFO 满时发出 DMA 传输的请求,同时暂停 FLASH 的数据读取,控制信号 nRE 拉高,直至 DMA 响应请求即 FIFO 不满时, FLASH 的数据传输重新开始。当选择应用的 FLASH 位宽为 8 ,页大小为 (512+16)B 时,控制器需要发出 (32+1) 次 4 拍字宽度的 DMA 传输请求来完成数据和校验信息的读取。

控制模块的上作主要是将总线接口转换的控制信号,按照NAND FLASH的接口协议.将片选、地址、命令、读写使能按照所配置的时序要求,发送到NAND FLASH中,并且控制数据的传输个数,以及 DMA 请求、数据传输完成中断、数据错误中断等系统信号。

NAND FLASH可靠性相对较差,存储器芯片中有坏块的存在,会导致存储数据出错。 ECC 校验模块针对 NAND FLASH的可靠性问题,提供了一种查错、纠错的机制。 ECC 校验码在数据读人时,由硬件计算完成后写入到 FLASH 的校验位中,当此页数据读出时,校验码再次生成与存储器校验位中的数据进行比较,若相同则没有损坏位,若不同,则给出出错中断,软件通过检查比较结果,判断出错位的位置进行纠错处理。纠错功能仅针对单 bit 位的出错,当一个以上位同时在一页中出现时, ECC 校验不能给出出错位正确的位置。

3 NAND FLASH 工作的软件流程

按照上节对控制器结构以及传输机理的分析,NAND FLASH的使用需要在 FLASH 控制器模块以及 DMA 控制器模块的协同下完成,工作的软件流程如图 2 所示。

软件驱动的主要工作是配置 DMA 模块以及 FLASH 控制模块,当传输完成,检测到中断后,软件查询状态寄存器,其中的状态位来自 FLASH 。当一次操作完成后,控制器自动向 FLASH 发出查询状态的命令 0x70 ,读出的状态字保存在控制器的状态寄存器中。

4 NAND FLASH 系统启动的传统模式

目前支持从NAND FLASH启动的 SoC 芯片中,一般都内嵌有片内存储器。各个处理器厂商对这块片上存储器定义的容量大小有所不同,但是启动模式都是比较一致的。NAND FLASH按页顺序读取的方式,意味着对当前的存储地址访问后就无法马上再次访问,需在当前页访问完成后,重新对此页访问时,才可对先前的地址单元再次访问,这就导致了一些程序语句无法执行,譬如跳转、循环等语句的使用。因此NAND FLASH仅作为启动代码的存储区,而真正执行的存储器区域是内嵌的片上存储器或者片外的 SDRAM 。

以上文中描述的控制器为例,按照这种启动模式,程序搬运以及执行的过程如下:

点击看大图

作者:蔡浩

1  2

点击此处查看原文 >>

系统分类: 嵌入式   |    用户分类:    |    来源: 整理

评论(0) | 阅读(546)
发表于:2007-8-21 16:59:19
标签:闪速存储器  闪存  nandflash  flash  

0

闪速存储器技术现状及发展趋势

要: 主要介绍闪速存储器的特点、技术分类及其发展趋势 , 其中包括闪速存储器的制造工艺、供电、读写操作、擦除次数、功耗等性能比较。

关键词: 闪速存储器 NOR技术 DINOR技术 NAND技术 UltraNAND技术

一、 闪速存储器的特点

   闪速存储器( Flash Memory )是一类非易失性存储器 NVM ( Non-Volatile Memory )即使在供电电源关闭后仍能保持片内信息;而诸如 DRAM 、 SRAM 这类易失性存储器,当供电电源关闭时片内信息随即丢失。 Flash Memory 集其它类非易失性存储器的特点:与 EPROM 相比较,闪速存储器具有明显的优势 —— 在系统电可擦除和可重复编程,而不需要特殊的高电压(某些第一代闪速存储器也要求高电压来完成擦除和 / 或编程操作);与 EEPROM 相比较,闪速存储器具有成本低、密度大的特点。其独特的性能使其广泛地运用于各个领域,包括嵌入式系统,如 PC 及外设、电信交换机、蜂窝电话、网络互联设备、仪器仪表和汽车器件,同时还包括新兴的语音、图像、数据存储类产品,如数字相机、数字录音机和个人数字助理( PDA )。

二、 闪速存储器的技术分类

   全球闪速存储器的主要供应商有 AMD 、 ATMEL 、 Fujistu 、 Hitachi 、 Hyundai 、 Intel 、 Micron 、 Mitsubishi 、 Samsung 、 SST 、 SHARP 、 TOSHIBA ,由于各自技术架构的不同,分为几大阵营。

1 NOR 技术

NOR

NOR 技术(亦称为 Linear 技术)闪速存储器是最早出现的Flash Memory ,目前仍是多数供应商支持的技术架构。它源于传统的 EPROM 器件,与其它Flash Memory 技术相比,具有可靠性高、随机读取速度快的优势,在擦除和编程操作较少而直接执行代码的场合,尤其是纯代码存储的应用中广泛使用,如 PC 的 BIOS 固件、移动电话、硬盘驱动器的控制存储器等。

NOR 技术Flash Memory 具有以下特点:( 1 ) 程序和数据可存放在同一芯片上,拥有独立的数据总线和地址总线,能快速随机读取,允许系统直接从 Flash 中读取代码执行,而无需先将代码下载至 RAM 中再执行;( 2 ) 可以单字节或单字编程,但不能单字节擦除,必须以块为单位或对整片执行擦除操作,在对存储器进行重新编程之前需要对块或整片进行预编程和擦除操作。由于 NOR 技术 Flash Memory 的擦除和编程速度较慢,而块尺寸又较大,因此擦除和编程操作所花费的时间很长,在纯数据存储和文件存储的应用中, NOR 技术显得力不从心。不过,仍有支持者在以写入为主的应用,如 CompactFlash 卡中继续看好这种技术。

Intel 公司的 StrataFlash 家族中的最新成员 ——28F128J3 ,是迄今为止采用 NOR 技术生产的存储容量最大的闪速存储器件,达到 128Mb (位),对于要求程序和数据存储在同一芯片中的主流应用是一种较理想的选择。该芯片采用 0.25μm 制造工艺,同时采用了支持高存储容量和低成本的 MLC 技术。所谓 MLC 技术(多级单元技术)是指通过向多晶硅浮栅极充电至不同的电平来对应不同的阈电压,代表不同的数据,在每个存储单元中设有 4 个阈电压( 00/01/10 /11 ),因此可以存储 2b 信息;而传统技术中,每个存储单元只有 2 个阈电压( 0/1 ),只能存储 1b 信息。在相同的空间中提供双倍的存储容量,是以降低写性能为代价的。 Intel 通过采用称为 VFM (虚拟小块文件管理器)的软件方法将大存储块视为小扇区来管理和操作,在一定程度上改善了写性能,使之也能应用于数据存储中。

DINOR

DINOR(Divided bit-line NOR) 技术是 Mitsubishi 与 Hitachi 公司发展的专利技术,从一定程度上改善了 NOR 技术在写性能上的不足。 DINOR 技术 Flash Memory 和 NOR 技术一样具有快速随机读取的功能,按字节随机编程的速度略低于 NOR ,而块擦除速度快于 NOR 。这是因为 NOR 技术 Flash Memory 编程时,存储单元内部电荷向晶体管阵列的浮栅极移动,电荷聚集,从而使电位从 1 变为 0 ;擦除时,将浮栅极上聚集的电荷移开,使电位从 0 变为 1 。而 DINOR 技术 Flash Memory 在编程和擦除操作时电荷移动方向与前者相反。 DINOR 技术 Flash Memory 在执行擦除操作时无须对页进行预编程,且编程操作所需电压低于擦除操作所需电压,这与 NOR 技术相反。

   尽管 DINOR 技术具有针对 NOR 技术的优势,但由于自身技术和工艺等因素的限制,在当前闪速存储器市场中,它仍不具备与发展数十年,技术、工艺日趋成熟的 NOR 技术相抗衡的能力。目前 DINOR 技术 Flash Memory 的最大容量达到 64Mb 。 Mitsubishi 公司推出的 DINOR 技术器件 ——M5M29GB/T320 ,采用 Mitsubishi 和 Hitachi 的专利 BGO 技术,将闪速存储器分为四个存储区,在向其中任何一个存储区进行编程或擦除操作的同时,可以对其它三个存储区中的一个进行读操作,用硬件方式实现了在读操作的同时进行编程和擦除操作,而无须外接 EEPROM 。由于有多条存取通道,因而提高了系统速度。该芯片采用 0.25μm 制造工艺,不仅快速读取速度达到 80ns ,而且拥有先进的省电性能。在待机和自动省电模式下仅有 033μW 功耗,当任何地址线或片使能信号 200ns 保持不变时,即进入自动省电模式。对于功耗有严格限制和有快速读取要求的应用,如数字蜂窝电话、汽车导航和全球定位系统、掌上电脑和顶置盒、便携式电脑、个人数字助理、无线通信等领域中可以一展身手。

 

1  2  3 

作者:湖南计算机股份有限公司 李力

点击此处查看原文 >>

系统分类: 嵌入式   |    用户分类:    |    来源: 整理

评论(0) | 阅读(532)
发表于:2007-8-17 15:50:51
标签:nandflash  nand闪存  arm  

0

NAND闪存深入解析

对于许多消费类音视频产品而言, NAND闪存 是一种比硬盘驱动器更好的存储方案,这在不超过 4GB 的低容量应用中表现得犹为明显。随着人们持续追求功耗更低、重量更轻和性能更佳的产品, NAND 正被证明极具吸引力。

NAND闪存阵列分为一系列 128kB 的区块 (block) ,这些区块是 NAND 器件中最小的可擦除实体。擦除一个区块就是把所有的位 (bit) 设置为 “ 1” ( 而所有字节 (byte) 设置为 FFh) 。有必要通过编程,将已擦除的位从 “ 1” 变为 “ 0” 。最小的编程实体是字节 (byte) 。一些 NOR 闪存能同时执行读写操作 ( 见图 1) 。虽然 NAND闪存不能同时执行读写操作,它可以采用称为 “ 映射 (shadowing)” 的方法,在系统级实现这一点。这种方法在个人电脑上已经沿用多年,即将 BIOS 从速率较低的 ROM 加载到速率较高的 RAM 上。

NANDflash的效率较高,是因为 NANDflash 串中没有金属触点。 NAND闪存单元的大小比 NOR 要小 ( 4F 2 : 10F 2) 的原因,是 NOR 的每一个单元都需要独立的金属触点。 NAND闪存与硬盘驱动器类似,基于扇区 ( 页 ) ,适合于存储连续的数据,如图片、音频或个人电脑数据。虽然通过把数据映射到 RAM 上,能在系统级实现随机存取,但是,这样做需要额外的 RAM 存储空间。此外,跟硬盘一样, NAND器件存在坏的扇区,需要纠错码 (ECC) 来维持数据的完整性。

存储单元面积越小,裸片的面积也就越小。在这种情况下, NAND闪存就能够为当今的低成本消费市场提供存储容量更大的闪存产品。 NAND闪存用于几乎所有可擦除的存储卡。 NAND闪存的复用接口为所有最新的器件和密度都提供了一种相似的引脚输出。这种引脚输出使得设计工程师无须改变电路板的硬件设计,就能从更小的密度移植到更大密度的设计上。

NANDNOR闪存比较

NAND闪存的优点在于写 ( 编程 ) 和擦除操作的速率快,而 NOR 的优点是具有随机存取和对字节执行写 ( 编程 ) 操作的能力 ( 见图 2) 。 NOR 的随机存取能力支持直接代码执行 (XiP) ,而这是嵌入式应用经常需要的一个功能。 NAND 的缺点是随机存取的速率慢, NOR 的缺点是受到读和擦除速度慢的性能制约。 NAND 较适合于存储文件。如今,越来越多的处理器具备直接 NAND 接口,并能直接从 NAND( 没有 NOR) 导入数据。

NAND闪存的真正好处是编程速度快、擦除时间短。NAND闪存支持速率超过 5Mbps 的持续写操作,其区块擦除时间短至 2ms ,而 NOR 是 750ms 。显然, NAND闪存在某些方面具有绝对优势。然而,它不太适合于直接随机存取。

对于 16 位的器件, NOR 闪存大约需要 41 个 I/O 引脚;相对而言, NAND器件仅需 24 个引脚。 NAND器件能够复用指令、地址和数据总线,从而节省了引脚数量。复用接口的一项好处,就在于能够利用同样的硬件设计和电路板,支持较大的 NAND器件。由于普通的 TSOP-1 封装已经沿用多年,该功能让客户能够把较高密度的 NAND器件移植到相同的电路板上。 NAND 器件的另外一个好处显然是其封装选项: NAND 提供一种厚膜的 2Gb 裸片或能够支持最多四颗堆叠裸片,容许在相同的 TSOP-1 封装中堆叠一个 8Gb 的器件。这就使得一种封装和接口能够在将来支持较高的密度。

点击看大图
图 1 不同闪存单元的对比

点击看大图
图2 NOR闪存随机存取时间0.12ms,而NAND闪存的第一字节随机存取速度要慢得多

1  2  3  4 

作者 : Jim Cooke

点击此处查看原文 >>

系统分类: 嵌入式   |    用户分类:    |    来源: 整理

评论(0) | 阅读(491)
发表于:2007-5-28 16:44:40
标签:ARM  嵌入式  BSP  

0

基于ARM体系的嵌入式系统BSP的程序设计

摘要: 在介绍基于 ARM 体系的嵌入式系统启动流程的基础上,结合编程实例,详细、系统地叙述了 BSP (板级支持包)程序的各个组成部分及其具体设计方案,并就实际程序设计中的几个难点问题做了说明。

关键词: ARM BSP 嵌入式系统 微处理器

ARM 公司在32 位RISC 的CPU 开发领域不断取得突破,其结构已经从 V3 发展到 V6 。

BSP ( Board Support Package )板级支持包介于主板硬件和操作系统之间,其功能与 PC 机上的 BIOS 相类似,主要完成硬件初始化并切换到相应的操作系统。 BSP 是相对于操作系统而言的,不同的操作系统对应于不同定义形式的 BSP ,例如 VxWorks 的 BSP 和 Linux 的 BSP 相对于某一 CPU 来说,尽管实现的功能一样,可是写法和接口定义是完全不同的。另外,仔细研究所用的芯片资料也十分重要,例如尽管 ARM在内核上兼容,但每家芯片都有自己的特色。所以这就要求 BSP 程序员对硬件、软件和操作系统都要有一定的了解。

本文介绍基于 ARM体系的嵌入式应用系统初始化部分 BSP 的程序设计。本文引用的源码全部是基于 HMS320C7202 芯片设计,并已成功运行。

1 初始化过程

尽管各种嵌入式应用系统的结构及功能差别很大,但其系统初始化部分完成的操作有很大一部分是相似的。嵌入式系统的启动流程如图 1 所示。

1.1 设置入口指针

启动程序首先必须定义指针,而且整个应用程序只有一个入口指针。一般地,程序在编译链接时将异常中断向量表链接在 0 地址处,并且作为整个程序入口点。入口点代码如下:

ENTRY ( _start ) ;开始

1.2 设置异常中断向量表

ARM要求中断向量表必须放置在从 0 开始、连续 8×4 字节的空间内。各异常中断向量地址以及中断的算是优先级如表 1 :

表 1 各异常中断的中断向量地址以及中断的处理优先级

中断向量地址

异常中断类型

异常中断模式

优先级( 6 最低)

0x0

复位

特权模式( SVC )

1

0x4

未定义中断

未定义指令中止模式( Undef)

6

0x8

软件中断( SWI )

特权模式( SVC )

6

0x 0c

指令预取中止

中止模式

5

0x10

数据访问中止

中止模式

2

0x14

保留

未使用

未使用

0x18

外部中断请求( IRQ )

外部中断( IRQ )模式

4

0x 1c

快速中断请求( FIQ )

快速中断( FIQ )模式

3

每当一个中断发生后, ARM处理器便强制把程序计数器( PC )指针置为向量表中对应中断类型的地址值。因为每个中断向量仅占据放置 1 条 ARM 指令的空间,所以通常放置 1 条跳转指令或向程序计数器( PC )寄存器赋值的数据访问指令,使程序跳转到相应的异常中断处理程序执行。如果异常中断处理程序起始地址小于32MB,使用B跳转指令;如果跳转范围大于32MB,使用LDR指令。
另外,对于各未用中断,可使其指向一个只含返回指令的哑函数,以防止错误中断引起系统的混乱。

1.3 初始化存储系统
初始化存储系统的编程对象是系统的存储器控制器,一个系统可能存在多种存储器类型的接口,不同的存储系统的设计不尽相同。Flash和SRAM同属于静态存储器类型,可以合用一个存储器端口;而DRAM因为有动态刷新和地址线复用等特性,通常配有专用的存储器端口。其中,SDRAM必须在初始化阶段进行设置,因为大部分的程序代码和数据都要在SDRAM中运行。

在HMS30C7202中,与SDRAM配置有关的寄存器有4个:配置寄存器、刷新定时寄存器、写缓冲写回寄存器和等待驱动寄存器,需要根据实际的系统设计对此分别加以正确配置。
SDRAM的初始化过程如下:加电→延迟10ms(各具体SDRAM器件延时时间可能不同)→设置配置寄存器参数→延时→写刷新定时寄存器,设置刷新周期→延时→使能自动刷新→延时→设置模式寄存器(位于SDRAM内部)。

1.4 存储器地址分布重映射(remap)和MMU
系统一上电,程序将自动从0地址处开始执行。因此,必须保证在0地址处存在正确的代码,即要求0地址开始入是非易失性的ROM或Flash等。但是因为ROM或Flash的访问速度相对较慢,每次中断响应发生后,都要从读取ROM或Flash上面的向量表开始,影响了中断响应速度。一般程序执行后将SDRAM映射为地址0,并把系统程序加载到SDRAM中运行,其具体步骤可以采用以下的方案:
(1)上电后,从0地址的ROM开始往下执行;
(2)根据映射前的地址,对SDRAM进行必要的代码和数据拷贝;
(3)拷贝完成后,进行重映射操作;
(4)因为RAM在重映射前准备好了内容,使得PC指针能继续在RAM里取得正确的指令。
在这种地址映射的变化过程中,程序员需要仔细考虑的是:程序的执行流程不能被这种变化所打断,注意保证程序流程在重映射前后的承接关系。

存储器的地址分配是很灵活的,可以将I/O操作映射成内存操作,也可以通过映射对某些不可访问的地址空间进行保护等。进行存储器初始化设计时,一定要根据应用程序的具体要求来完成地址分配。对地址管理通过MMU即存储器管理单元实现。

ARM系统中,MMU通过页式虚拟存储管理,将虚拟空间和物理空间分别分成一个个固定大小的页,并建立两者之间的映射关系,从而实现虚拟地址到物理地址的转换。MMU还可完成存储器访问权限的控制和虚拟存储器空间缓冲特性的设置。
以下是实现MMU的部分代码:
for=(i=1;i<0x1000;i++){
pagetable[i]=(i<<20)|MMU_SECDESC;
} //建立页表,每页大小为1MB,页表偏移序号是物理地址的高12位;
for(addr=SDRAM_BASE;addr<(SDRAM_BASE+SDRAM_SIZE/2);addr+=SIZE_1M)
pagetable[addr>>20]=addr|MMU_SECDESE|
MMU_CACHEABLE|MMU_BUFFERABLE;
//将SDRAM_BASE至(SDRAM_BASE+SDRAM_SIZE/2)空间的设置为不可CACHE和不可BUFFER的
for(addr=SDRAM_BASE+SDRAM_SIZE/2;addr<(SDRAM_BASE+SDRAM_SIZE);addr+=SIZE_1M)
pagetable[addr>>20]=(addr+0x1000000)|
MMU_SECDESC|MMU_CACHEABLE|MMU_BUFFERABLE;
//将这段空间的地址映射关系设置为VA(虚拟地址)=PA(物理地址)+0x1000000
pagetable[0]=(0x42f00000)|MMU_SECDESC|MMU_CACHEABLE|MMU_BUFFERABLE;
//将SDRAM的虚拟地址0x42f00000映射到0处

1.5初始化各模式下的堆栈指针
因为ARM处理器有7种执行状态,每一种状态的堆栈指针寄存器(SP)都是独立的(System和User三项式使用相同SP寄存器)。因此,对程序中需要用到的每一种模式都要给SP寄存器定义一个堆栈地址。方法是改变状态寄存器(CPSR)内的状态位,使处理器切换到不同的状态,然后给SP赋值。这里列出的代码定义了三种模式的SP指针,其中,I_Bit表示IRQ的中断禁止位;F_Bit表示FIQ的中断禁止位:
@;Set up SVC stack to be 4K on top of zero-init data
LDR r1,=installStack
ADDsp,r1,#2048
@;Set up IRQ and FIQ stacks
MOV r0,#(Mode_IRQ32|I_Bit)
MSRcpsr,r0
MOV r0,r0
ADDsp,r1,#2048*2
MOV r0,#(Mode_FIQ32|I_Bit |F_Bit)
MSR cpsr,r0
MOV r0,r0
ADDsp,r1,#2048*3
一般堆栈的大小要根据需要而定,但是要尽可能给堆栈分配快速和高带宽的存储器。堆栈性能的提高对系统性能的影响是非常明显的。

1.6 初始化有特殊要求的端口、设备
有些关键的I/O部件必须在使能IRQ和FIQ之前进行初始化。因为如果在使能IRQ和FIQ之前没有进行初始化,可以产生假的异常中断信号。程序中初始化了HMS30C7202的串口1用来调试程序与其它设备通信。串口1是一个通用全双工异步接收/发送器(UART),它支持16C550的大部分功能。UART有接收缓冲/发送保持寄存器、波特率除数锁存器、中断允许寄存器等9个寄存器。对串口1的初始化主要是对各寄存器的设置,其实现代码如下所示:
_outb(ser_base+0x30,1);
_outw(0x8002301c,0xffff9f9f) ;GPIO PORT A Enable
Register
_outw(0x800230A4,0x6060) ;GPIO PORT A MultiFunction elect-Register
serial_outb(SERIAL_LCR,0x80);
serial_outb(SERIAL_LCR,0x80);
serial_outb(SERIAL_DLL,baud_data[cur_baud]);
serial_outb(SERIAL_DLM,0x0);
serial_outb(SERIAL_LCR,0x03);
seial_outb(SERIAL_FCR,0x01);
serial_outb(SERIAL_IER,0x00);
serial_outb(SERIAL_MCR,0x03);

1.7切换处理器模式,开中断
最后转换到应用程序运行所需的最终模式,一般是User模式。不要过早切换到User模式进行User模式的堆栈设备。因为进入User模式后就不能再操作CPRS回到别的模式了,可能会对接下去的程序执行造成影响。

这时才使能异常中断,通过清除CPRS寄存器中的中断禁止位实现。如果过早地开中断,在系统初始化之前就触发了有效中断,会导致系统的死机。

1.8 呼叫主应用程序
当所有的系统初始化工作完成后,就需要把程序流程转入主应用程序。

2 技术难点分析
2.1 多种语言的混合编程
ARM有两种汇编指令集:16位THUMB指令集和32位ARM指令集。使用16位的寄存器可以降低成本,而且16位THUMB指令集整体执行速度比ARM 32位指令集快,提高了代码密度。为了满足ARM子程序和Thumb子程序互相调用,必须保证编写的代码遵循ATPCS。ATPCS规定了子程序调用的基本规则。

ARM系统结构也支持C、C++以及汇编语言的混合编程。汇编语言和C/C++语言的混合编程,在一个追求效率的程序中比较常见。许多人认为像BSP这样底层的程序应该用纯汇编语言编写,其实不然。用汇编语言编写的程序可读性不高,而且不宜维护,不便于向其它类型的CPU移植,而这些方面却是C语言程序的优势。BSP能否用纯C语言去写呢?也不行。因为某些操作是用C实现不了的。例如操作特殊寄存器的指令、CP15寄存器的指令、中断使能及堆栈地址的设定等。在汇编和C/C++之间的函数调用时,也要遵循ATPCS的定义,还要注意的是用C语言编写嵌入式程序时,要避免使用不能被固化到ROM中的库函数。

混合编程情况下的程序编译及链接后的输出代码与没有混合编程时是不同的。所以当多个源文件如果使用了不同的设置进行编译,相互之间的调用可能产生兼容性问题,对此一定要加以仔细考虑。编译时,要告诉编译器和链接器足够的信息,一方面,让编译器能够使用正确的指令码进行编译;另一方面,在不同的状态之间发生函数调用时,链接器将插入一段链接代码(veneers)来实现状态转换。

2.2 MMU的实现过程
页表是实现MMU的重要手段。页表存放在内存中,从虚拟地址到物理地址的变换过程其实就是查询页表的过程。大小为1MB的存储块通常被称为段,图2说明了如何查表进行段式寻址的全过程:32位的虚拟地址可分为12位的一级页表序号和20位的段内地址偏移。12位的一级页表序号和CP15寄存器的C2中的18位变换表基址合并成一级描述符地址查表找出相应的一级描述符;然后,段对应的物理基地址与段内地址偏移量合并成为真正的存储器存取地址即物理地址,读出相应数据。

本文介绍的BSP程序已经在以HMS30C7202为主芯片的开发系统上运行并测试通过,并且成功地引导了Linux内核,文中引用代码可以直接使用。今后可以在此基础上添加命令行解释程序,在引导操作系统前进行存存储器的读写等,扩展开发系统的功能。

点击此处查看原文 >>

系统分类: 嵌入式   |    用户分类:    |    来源: 转贴

评论(0) | 阅读(808)
发表于:2007-5-17 16:06:58
标签:S3C2410  快速启动  MMU  嵌入式  

0

S3C2410快速启动的实现

摘 要:介绍 S3C2410 处理器的快速启动技术,重点对系统硬件的初始化,二级中断向量表的复制,代码段的复制以及 MMU 管理进行了探讨,并且对具体的实现代码进行了分析,实现了系统从 RAM 中的高速启动运行。

关键词: S3C2410 ;快速启动; MMU

嵌入式系统对功能、可靠性、成本、体积、功耗等均有严格要求,以 ARM 体系结构为基础的各种 RISC 微处理器具有灵活的特性和强大的性能,在嵌入式系统中得到了广泛的应用。
S3C2410 是三星公司基于 ARM920T 设计的一款处理器,在开发基于S3C2410 的系统的过程中,如何让系统快速稳定地启动是一个重要问题。嵌入式系统的资源有限,程序通常都是固化在 ROM 中运行。但在实际应用中,为提高系统的实时性,加快代码的执行速度,系统启动后程序往往要被搬移到 RAM 中,因为 RAM 的存取速度要比 ROM 快得多,这样大大提升系统的性能。启动程序要完成的任务包括:硬件初始化,系统存储系统的配置,复制二级中断向量表。

启动程序过程

  • 系统硬件初始化
    系统上电或复位后,程序从位于地址 0x0 的 Reset Exception Vector 处开始执行,因此需要在这里放置 Bootloader 的第一条指令: b ResetHandler ,跳转到标号为 ResetHandler 处进行第一阶段的硬件初始化,主要内容为:关看门狗定时器,关中断,初始化 PLL 和时钟,初始化存储器系统。执行完以上程序后,系统进行堆栈和存储器的初始化。系统堆栈初始化取决于用户使用了哪些中断,以及系统需要处理哪些错误类型。一般情况下,管理者堆栈必须设置,如果使用了 IRQ 中断,则 IRQ 堆栈也必须设置。如果系统使用了外设,则需要设置相关的寄存器,以确定其刷新频率、总线宽度等信息。
  • 代码段复制到 RAM 中运行
    因为嵌入式系统的代码通常都是固化在 ROM 或者 Flash 中,上电后开始运行。由于 ROM 和 Flash 的读取速度相对较慢,这样无疑会降低代码的执行速度和系统的运行效率。为此,需要把系统的代码复制到 RAM 中运行。使用 SDT 链接器 ARMLink 产生的定位信息,把 RO 的有效代码和数据段到 RAM 中。 ARMLink 将编译后的程序链接成 ELF 文件。映像文件内部共有三种输出段: RO 段、 RW 段和 ZI 段。这三种输出段分别包含了只读代码及包含在代码段中的少量数据、可读写的数据、初始化为 0 的数据, ARMLink 同时还产生了这三种输出段的起始和终止定位信息: Image$$RO$$Base 、 Image$$RO$$Limit 、 Image$$RW$$Base 、 Image$$Limit 、 Image$$Linit 和 Image$$ZI$$Limit 。可以在程序中使用这些定位信息。将 ROM 中的代码和数据搬移到 RAM 中,具体程序如下。
    LDR r0, =|Image$$RO$$Base| /*RO 段起始地址 */
    LDR r1, =|Image$$RO$$Limit| /*RO 段结束地址 */
    LDR r2, =|Image$$RW$$Base|
    LDR r3, =|Image$$RW$$Limit|
    /* 分别求出需要映像的代码和数据的长度并累加,放到寄存器 R1 中 */
    SUB r1, r1, r0
    SUB r3, r3, r2
    ADD r1, r1, r3
    /* 将需要映象的代码和数据复制到 RAM 中去 */
    0 /* 标示符 */
    LDR r3, [r0], #4
    STR r3, [r2], #4
    SUBS r1, r1, #4
    BNE %B0 /* 如果没有复制完,跳转到 0 标示符处的汇编语句,继续复制,参见 ARM 指令帮助手册 */
  • 建立二级中断向量表
    在 ARM 系统中,中断向量表位于 0X0 开始的地址处,意味着无论运行什么样的上层软件,一旦发生中断,程序就得到 Flash 存储器中的中断向量表里去,降低系统的运行效率。因此在 RAM 中建立自己的二级中断向量表,当中断发生后,程序直接从 RAM 中取中断向量进入中断子程序。尤其是在中断频繁发生的系统里,这种方法可以大大提高系统的运行效率,具体的实现代码如下。
    b ResetHandler
    b HandlerUndef /* 未定义模式句柄 */
    b HandlerSWI /*SWI 中断句柄 */
    b HandlerPabort /*PAbort 中断句柄 */
    b HandlerDabort /*Dabort 中断句柄 */
    b. /* 保留 */
    b HandlerIRQ /*IRQ 中断句柄 */
    b HandlerFIQ /*FIQ 中断句柄 */
    HandlerFIQ HANDLER HandleFIQ
    HandlerIRQ HANDLER HandleIRQ
    HandlerUndef HANDLER HandleUndef
    HandlerSWI HANDLER HandleSWI
    HandlerDabort HANDLER HandleDabort
    HandlerPabort HANDLER HandlePabort
    其中 HANDLER 是一个宏,用于查找中断处理程序的入口地址。这些地址存放在由 HandleXXX 指向的表项中,该表定位在 RAM 高端,基地址为 _ISR_STARTADDRESS 。
    ^ _ISR_STARTADDRESS
    HandleReset # 4
    HandleUndef # 4
    HandleSWI # 4
    HandlePabort # 4
    HandleDabort # 4
    HandleReserved # 4
    HandleIRQ # 4
    HandleFIQ # 4
  • MMU 的应用
    MMU 是存储器管理单元的缩写,是用来管理虚拟内存系统的器件。 MMU 通常是 CPU 的一部分,本身有少量存储空间存放从虚拟地址到物理地址的匹配表,此表称作 TLB( 转换旁置缓冲区 ) 。所有数据请求都送往 MMU ,由 MMU 决定数据是在 RAM 内还是在大容量外部存储器设备内。如果数据不在存储空间内, MMU 将产生页面错误中断。 MMU 存储器系统的结构允许对存储器系统的精细控制,大部分的控制细节由存在存储器中的转换表提供。这些表的入口定义了从 1KB ~ 1MB 的各种存储器区域的属性。 MMU 完成的两个主要功能是:将虚地址转换成物理地址,控制存储器存取允许。 MMU 关掉时,虚地址直接输出到物理地址总线。
    经过以上的分析可以发现,系统启动程序主要是完成了硬件的初始化,以及克服 Flash 或 ROM 读取速度慢的弱点,提高指令和数据的读取速度,实现系统的高速运行,并且通过 MMU 的应用,减少RAM 的使用,降低系统成本。
    摘自:http:www.guangdongdz.com

点击此处查看原文 >>

系统分类: 嵌入式   |    用户分类:    |    来源: 转贴

评论(0) | 阅读(556)
发表于:2007-5-17 16:03:26
标签:ARM  嵌入式系统  Bootloader  

0

基于ARM核的Bootloader代码的分析与设计

摘要: 讲述了基于 ARM 处理器的嵌入式系统在上电启动后应用程序或操作系统运行前,对处理器及其内部功能模块进行初始化的过程,并结合经过实际验证的代码详细的分析了S3C44B0 Bootloader的运行过程。
关键字: ARM  嵌入式系统  Bootloader

一 .  引言:
    对于 PC 机,其开机后的初始化处理器配置、硬件初始化等操作是由 BIOS ( Basic Input /Output System )完成的,但对于嵌入式系统来说,出于经济性、价格方面的考虑一般不配置 BIOS ,因此我们必须自行编写完成这些工作的程序,这就是所需要的开机程序。而在嵌入式系统中,通常并没有像 BIOS 那样的固件程序,启动时用于完成初始化操作的这段代码被称为 Bootloader 程序,因此整个系统的加载启动任务就完全由 Bootloader 来完成。简单地说,通过这段程序,可以初始化硬件设备、建立内存空间的映射图(有的 CPU 没有内存映射功能如 S3C44B0 ),从而将系统的软硬件环境设定在一个合适的状态,以便为最终调用操作系统内核、运行用户应用程序准备好正确的环境。 Bootloader 依赖于实际的硬件和应用环境,因此要为嵌入式系统建立一个通用、标准的 Bootloader 是非常困难的。 Bootloader 也依赖于具体的嵌入式板级设备的配置,这也就是说,对于两块不同的嵌入式主板而言,即使它们是基于同一 CPU 而构建,要想让运行在一块板子上的 Bootloader 程序也能运行在另一块板子上,通常都需要修改 Bootloader 的源程序。

二 .  启动流程
    系统加电复位后,几乎所有的 CPU 都从由复位地址上取指令。比如,基于 ARM7TDMI内核的CPU 在复位时通常都从地址 0x00000000 处取它的第一条指令。而以微处理器为核心的嵌入式系统通常都有某种类型的固态存储设备(比如 EEPROM 、 FLASH 等)被映射到这个预先设置好的地址上。因此在系统加电复位后,处理器将首先执行存放在复位地址处的程序。通过集成开发环境可以将 Bootloader 定位在复位地址开始的存储空间内,因此 Bootloader 是系统加电后、操作系统内核或用户应用程序运行之前,首先必须运行的一段程序代码。对于嵌入式系统来说,有的使用操作系统,也有的不使用操作系统,比如功能简单仅包括应用程序的系统,但在系统启动时都必须执行 Bootloader ,为系统运行准备好软硬件运行环境。
     系统的启动通常有两种方式,一种是可以直接从 Flash 启动,另一种是可以将压缩的内存映像文件从 Flash (为节省 Flash 资源、提高速度)中复制、解压到 RAM ,再从 RAM 启动。当电源打开时,一般的系统会去执行 ROM (应用较多的是 Flash )里面的启动代码。这些代码是用汇编语言编写的,其主要作用在于初始化 CPU 和板上的必备硬件如内存、中断控制器等。有时候用户还必须根据自己板子的硬件资源情况做适当的调整与修改。
    系统启动代码完成基本软硬件环境初始化后,对于有操作系统的情况下,启动操作系统、启动内存管理、任务调度、加载驱动程序等,最后执行应用程序或等待用户命令;对于没有操作系统的系统直接执行应用程序或等待用户命令。
    启动代码是用来初始化电路以及用来为高级语言写的软件做好运行前准备的一小段汇编语言,在商业实时操作系统中,启动代码部分一般被称为板级支持包,英文缩写为 BSP 。它的主要功能就是:电路初始化和为高级语言编写的软件运行做准备。系统启动流程如图 1 所示,主要的过程如下:

   1.  启动代码的第一步是设置中断和异常向量。
    2.  完成系统启动所必须的最小配置,某些处理器芯片包含一个或几个全局寄存器,这些寄存器必须在系统启动的最初进行配置。
    3.  设置看门狗,用户设计的部分外围电路如果必须在系统启动时初始化,就可以放在这一步。
    4.  配置系统所使用的存储器,包括 Flash , SRAM 和 DRAM 等,并为他们分配地址空间。如果系统使用了 DRAM 或其它外设,就需要设置相关的寄存器,以确定其刷新频率,数据总线宽度等信息,初始化存储器系统。有些芯片可通过寄存器编程初始化存储器系统,而对于较复杂系统通常集成有 MMU 来管理内存空间。
    5.  为处理器的每个工作模式设置栈指针, ARM 处理器有多种工作模式,每种工作模式都需要设置单独的栈空间。
    6.  变量初始化,这里的变量指的是在软件中定义的已经赋好初值的全局变量,启动过程中需要将这部分变量从只读区域,也就是 Flash 拷贝到读写区域中,因为这部分变量的值在软件运行时有可能重新赋值。还有一种变量不需要处理,就是已经赋好初值的静态全局变量,这部分变量在软件运行过程中不会改变,因此可以直接固化在只读的 Flash 或 EEPROM 中。
    7.  数据区准备,对于软件中所有未赋初值的全局变量,启动过程中需要将这部分变量所在区域全部清零。
    8.  最后一步是调用高级语言入口函数,比如 main 函数等。

三 .  程序分析
    下面根据实际经过测试的代码详细讲述系统的启动过程。
    .text      /* 将此操作符开始的代码编译到代码段或代码段子段中 */
    /* 集成开发环境( IDE )可以通过链接脚本文件将下面的语句定位在零起始地址,系统上电后 CPU 从此处开始执行 */
    ENTRY:
        b ResetHandler   /* 跳至 ResetHandler ,此句被定位在零起始地址 */
/* 除用户模式外的其他 6 种模式称为特权模式。特权操作模式主要处理异常和监控调用(有时称为软件中断),它们可以自由的访问系统资源和改变模式。特权模式中除系统模式以外的 5 种模式又称为异常模式,下面的代码用于出现异常时 CPU 就会根据以下的语句自动跳转到对应的异常处理程序处 */
    b HandlerUndef        /* handlerUndef         */
    b HandlerSWI         /* SWI interrupt handler  */
    b HandlerPabort       /* handlerPAbort        */
    b HandlerDabort       /* handlerDAbort       */
    b .                   /* handlerReserved      */
    b HandlerIRQ
    b HandlerFIQ
...
...
ResetHandler:  /* 上电后跳转到此处开始执行 */
    Ldr r0,=WTCON  /* 禁止看门狗 */
    ldr     r1,=0x0
    str     r1,[r0]
    ldr     r0,=INTMSK    /* 屏蔽所有中断请求 */
    ldr     r1,=0x07ffffff
    str     r1,[r0]
  /* 设置时钟控制寄存器 */
    ldr  r0,=LOCKTIME
    ldr  r1,=0xfff
    str  r1,[r0]
.if PLLONSTART
 ldr  r0,=PLLCON   /* 设置 PLL */
 ldr  r1,=((M_DIV<<12)+(P_DIV<<4)+S_DIV) /*Fin=8MHz,Fout=64MHz*/
 str  r1,[r0]
.endif
    ldr     r0,=CLKCON 
    ldr     r1,=0x7ff8      /* 所有单元时钟允许 */
    str     r1,[r0]
/* 为 BDMA 设置复位值 */
    ldr     r0,=BDIDES0
    ldr     r1,=0x40000000     /* BDIDESn 复位值应为 0x40000000 */
    str     r1,[r0]
    ldr     r0,=BDIDES1
    ldr     r1,=0x40000000     /* BDIDESn 复位值应为 0x40000000 */
str     r1,[r0]
    /* 设置存储器控制寄存器,存储器的配置数据都存储在 SMRDATA 为起始地址的数据表中,下面的代码可以一次将预先配置好的初始化数据存入与存储器控制器相关的 13 个寄存器,这些寄存器则是以 0x01c80000 为起始地址的 13 个连续的 32 位寄存器 */
    ldr     r0,=SMRDATA
    ldmia   r0,{r1-r13}
    ldr     r0,=0x01c80000    /* BWSCON 存储控制寄存器地址 */
    stmia   r0,{r1-r13}
/* 初始化堆栈 */
/* CPU 复位后是处于管理模式下的,所以首先要初始化管理模式下的堆栈寄存器 */
    ldr     sp, =SVCStack
    /* 由于处理器的每种运行模式都要有自己独立的物理堆栈寄存器 R13 ,在用户应用程序的初始化部分,一般都要初始化每种模式下的 R13 ,使其指向该运行模式的栈空间,这样,当程序的运行进入异常模式时,可以将需要保护的寄存器放入 R13 所指向的堆栈,而当程序从异常模式返回时,则从对应的堆栈中恢复,采用这种方式可以保证异常发生后程序的正常执行 */
     bl     InitStacks   /* 跳转至其它堆栈初始化程序并返回 */
 /* 设置 IRQ 中断处理 */
/*44B0 有两种中断模式:一种是没有中断向量表;一种是使用了中断向量表,使用中断向量表只能是 IRQ 方式。当使用中断向量表的时候,中断发生时由 S3C44B0 的中断控制器根据中断向量表,利用硬件方式自动跳转到相应的中断处理服务程序所在的位置;不使用中断向量表时按下面的代码,利用软件方式跳转而进行中断处理,因为 S3C44B0 有 30 个中断源,所以需要程序判断以确定调用那个中断服务程序 */
    ldr     r0,=HandleIRQ /* 如果在 0x18 和 0x1c 地址处无 “subs pc,lr,#4”*/
    ldr     r1,=IsrIRQ  /* 为了中断正常返回这些语句是必须的 */
    str     r1,[r0]
 /* 拷贝读写区域数据 / 数据区准备,将系统需要读写的数据和变量从 ROM 拷贝到 RAM 里。  Image_RO_Limit 、 Image_RW_Base 、 Image_ZI_Base 等这些符号还会在另外的链接脚本文件中出现,这些符号是用来定位程序各个段的参考信息。集成开发环境在编译链接的时候会根据我们编写的程序,把它们转换成用来对各个段定位的地址信息 */
    LDR     r0, =Image_RO_Limit /* 取只读数据区域地址指针 */
    LDR     r1, =Image_RW_Base /* 准备执行拷贝操作 */
    LDR     r3, =Image_ZI_Base
     CMP     r0, r1        /* 检查是否相同 */
    BEQ     F1         /* 相同则跳过拷贝操作 */
F0:
    CMP     r1, r3    /* 执行拷贝操作 */
    LDRCC   r2, [r0], #4
    STRCC   r2, [r1], #4
    BCC     F0
F1:
    LDR     r1, =Image_ZI_Base /* 零数据准备区起始地址 */
    MOV     r2, #0
F2:
    CMP     r3, r1       /* 执行数据区清零 */
    STRCC   r2, [r3], #4
    BCC     F2

 MRS r0, CPSR
 BIC r0, r0, #NOINT     /* 中断请求允许 */
 MSR CPSR_cxsf, r0
/* 跳转到 C 入口程序 */
    BL Main
    B .

四 .  总结:
    启动过程中的初始化程序就是初始化 CPU 内部各个关键的寄存器、配置外围硬件电路相关寄存器、建立中断向量表等,然后跳转到一般由高级语言编写的主函数的应用程序代码去执行,这样就可以利用高级语言来编写完成系统设计所要求的各种功能。初始化的过程对大多数初学者来说,比较难理解的是中断的处理和一些少见的操作符号,这些符号多是一些宏定义或系统用于在内存空间中对各个段的定位标识符号。掌握了 S3C44B0的启动代码之后,对系统功能程序设计会起到很大的帮助,是进行下一步程序设计的基础。

点击此处查看原文 >>

系统分类: 嵌入式   |    用户分类:    |    来源: 转贴

评论(0) | 阅读(750)
发表于:2007-4-10 16:18:53
标签:嵌入式  BootLoader  ARM  

0

嵌入式BootLoader技术内幕

本文详细地介绍了基于嵌入式系统中的OS启动加载程序 ―― Boot Loader的概念、软件设计的主要任务以及结构框架等内容。
一、引言
在专用的嵌入式板子运行 GNU/Linux 系统已经变得越来越流行。一个嵌入式 Linux 系统从软件的角度看通常可以分为四个层次:

1. 引导加载程序。包括固化在固件(firmware)中的 boot 代码(可选),和 Boot Loader 两大部分。
2. Linux 内核。特定于嵌入式板子的定制内核以及内核的启动参数。
3. 文件系统。包括根文件系统和建立于 Flash 内存设备之上文件系统。通常用 ram disk 来作为 root fs。
4. 用户应用程序。特定于用户的应用程序。有时在用户应用程序和内核层之间可能还会包括一个嵌入式图形用户界面。常用的嵌入式 GUI 有:MicroWindows 和 MiniGUI 懂。
引导加载程序是系统加电后运行的第一段软件代码。回忆一下 PC 的体系结构我们可以知道,PC 机中的引导加载程序由 BIOS(其本质就是一段固件程序)和位于硬盘 MBR 中的 OS Boot Loader(比如,LILO 和 GRUB 等)一起组成。BIOS 在完成硬件检测和资源分配后,将硬盘 MBR 中的 Boot Loader 读到系统的 RAM 中,然后将控制权交给 OS Boot Loader。Boot Loader 的主要运行任务就是将内核映象从硬盘上读到 RAM 中,然后跳转到内核的入口点去运行,也即开始启动操作系统。
而在嵌入式系统中,通常并没有像 BIOS 那样的固件程序(注,有的嵌入式 CPU 也会内嵌一段短小的启动程序),因此整个系统的加载启动任务就完全由 Boot Loader 来完成。比如在一个基于 ARM7TDMI core 的嵌入式系统中,系统在上电或复位时通常都从地址 0x00000000 处开始执行,而在这个地址处安排的通常就是系统的 Boot Loader 程序。
本文将从 Boot Loader 的概念、Boot Loader 的主要任务、Boot Loader 的框架结构以及 Boot Loader 的安装等四个方面来讨论嵌入式系统的 Boot Loader。

二、 Boot Loader的概念
简单地说,Boot Loader 就是在操作系统内核运行之前运行的一段小程序。通过这段小程序,我们可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境。
通常,Boot Loader 是严重地依赖于硬件而实现的,特别是在嵌入式世界。因此,在嵌入式世界里建立一个通用的 Boot Loader 几乎是不可能的。尽管如此,我们仍然可以对 Boot Loader 归纳出一些通用的概念来,以指导用户特定的 Boot Loader 设计与实现。
1. Boot Loader 所支持的 CPU 和嵌入式板
每种不同的 CPU 体系结构都有不同的 Boot Loader。有些 Boot Loader 也支持多种体系结构的 CPU,比如 U-Boot 就同时支持 ARM 体系结构和MIPS 体系结构。除了依赖于 CPU 的体系结构外,Boot Loader 实际上也依赖于具体的嵌入式板级设备的配置。这也就是说,对于两块不同的嵌入式板而言,即使它们是基于同一种 CPU 而构建的,要想让运行在一块板子上的 Boot Loader 程序也能运行在另一块板子上,通常也都需要修改 Boot Loader 的源程序。

2. Boot Loader 的安装媒介(Installation Medium)
系统加电或复位后,所有的 CPU 通常都从某个由 CPU 制造商预先安排的地址上取指令。比如,基于 ARM7TDMI core 的 CPU 在复位时通常都从地址 0x00000000 取它的第一条指令。而基于 CPU 构建的嵌入式系统通常都有某种类型的固态存储设备(比如:ROM、EEPROM 或 FLASH 等)被映射到这个预先安排的地址上。因此在系统加电后,CPU 将首先执行 Boot Loader 程序。
下图1就是一个同时装有 Boot Loader、内核的启动参数、内核映像和根文件系统映像的固态存储设备的典型空间分配结构图。


图1 固态存储设备的典型空间分配结构

3. 用来控制Boot Loader的设备或机制
主机和目标机之间一般通过串口建立连接,Boot Loader 软件在执行时通常会通过串口来进行 I/O,比如:输出打印信息到串口,从串口读取用户控制字符等。

4. Boot Loader的启动过程是单阶段(Single Stage)还是多阶段(Multi-Stage)
通常多阶段的 Boot Loader 能提供更为复杂的功能,以及更好的可移植性。从固态存储设备上启动的 Boot Loader 大多都是 2 阶段的启动过程,也即启动过程可以分为 stage 1 和 stage 2 两部分。而至于在 stage 1 和 stage 2 具体完成哪些任务将在下面几篇讨论。

5. Boot Loader的操作模式 (Operation Mode)
大多数 Boot Loader 都包含两种不同的操作模式:"启动加载"模式和"下载"模式,这种区别仅对于开发人员才有意义。但从最终用户的角度看,Boot Loader 的作用就是用来加载操作系统,而并不存在所谓的启动加载模式与下载工作模式的区别。
启动加载(Boot loading)模式:这种模式也称为"自主"(Autonomous)模式。也即 Boot Loader 从目标机上的某个固态存储设备上将操作系统加载到 RAM 中运行,整个过程并没有用户的介入。这种模式是 Boot Loader 的正常工作模式,因此在嵌入式产品发布的时侯,Boot Loader 显然必须工作在这种模式下。
下载(Downloading)模式:在这种模式下,目标机上的 Boot Loader 将通过串口连接或网络连接等通信手段从主机(Host)下载文件,比如:下载内核映像和根文件系统映像等。从主机下载的文件通常首先被 Boot Loader 保存到目标机的 RAM 中,然后再被 Boot Loader 写到目标机上的FLASH 类固态存储设备中。Boot Loader 的这种模式通常在第一次安装内核与根文件系统时被使用;此外,以后的系统更新也会使用 Boot Loader 的这种工作模式。工作于这种模式下的 Boot Loader 通常都会向它的终端用户提供一个简单的命令行接口。
像 Blob 或 U-Boot 等这样功能强大的 Boot Loader 通常同时支持这两种工作模式,而且允许用户在这两种工作模式之间进行切换。比如,Blob 在启动时处于正常的启动加载模式,但是它会延时 10 秒等待终端用户按下任意键而将 blob 切换到下载模式。如果在 10 秒内没有用户按键,则 blob 继续启动 Linux 内核。

6. BootLoader 与主机之间进行文件传输所用的通信设备及协议
最常见的情况就是,目标机上的 Boot Loader 通过串口与主机之间进行文件传输,传输协议通常是 xmodem/ymodem/zmodem 协议中的一种。但是,串口传输的速度是有限的,因此通过以太网连接并借助 TFTP 协议来下载文件是个更好的选择。
此外,在论及这个话题时,主机方所用的软件也要考虑。比如,在通过以太网连接和 TFTP 协议来下载文件时,主机方必须有一个软件用来的提供 TFTP 服务。在讨论了BootLoader的上述概念后,下面我们来具体看看 BootLoader 的应该完成哪些任务。

三、Boot Loader 的主要任务与典型结构框架
在继续本节的讨论之前,首先我们做一个假定,那就是:假定内核映像与根文件系统映像都被加载到 RAM 中运行。之所以提出这样一个假设前提是因为,在嵌入式系统中内核映像与根文件系统映像也可以直接在 ROM 或 Flash 这样的固态存储设备中直接运行。但这种做法无疑是以运行速度的牺牲为代价的。从操作系统的角度看,Boot Loader 的总目标就是正确地调用内核来执行。
另外,由于 Boot Loader 的实现依赖于 CPU 的体系结构,因此大多数 Boot Loader 都分为 stage1 和 stage2 两大部分。依赖于 CPU 体系结构的代码,比如设备初始化代码等,通常都放在 stage1 中,而且通常都用汇编语言来实现,以达到短小精悍的目的。而stage2 则通常用 C 语言来实现,这样可以实现给复杂的功能,而且代码会具有更好的可读性和可移植性。--作者:詹荣开

详情>>

点击此处查看原文 >>

系统分类: 嵌入式   |    用户分类:    |    来源: 转贴

评论(0) | 阅读(913)
发表于:2007-4-10 16:12:05
标签:ARM  Bootloader  ASM  编程  

0

ARMBootloader的实现---C和ASM混合编程

Cirrus Logic 的 clps7111~Ep9312 系列ARM core 的 CPU 内置 128 字节的boot 程序。这个 boot 程序为把操作系统下载到裸机提供了极大的方便。这样再焊接电路板之前不用把操作系统预先写入 Flash ,而且日后升级操作系统也非常方便。 这个 boot 程序的功能是:

1. 设置串行口的参数为: 9600 , 8N1 , No FlowControl 。
2. 然后送出一个 < 字符
3. 开始接收 2K 字节程序( Bootloader )
4. 送出一个 > 字符
5. 跳转去执行这 2K 的程序。

烧写操作系统的过程是 :
1. 连接 ARM target 的产性口和 PC 的串行口
ARM PC
RX ------------------- TX
TX ------------------- RX
GND ---------------- GND

2. 从 BOOT 程序引导 ARM target
3. 在 Windows NT4.0 的 console 中 , 设置串行口的参数 9600 8N1

C:>mode COM2: baud="9600" data="8" parity="n" stop="1"

4. 在 console 中把 bootloader 送到串行口。 /b 表示以二进制方式

C:>copy /b bootldr.bin COM1:

5. 在 console 中 , 根据 bootloader 的设置来调整串行口的参数 115200 8N1

C:>mode COM2: baud="115200" data="8" parity="n" stop="1"

6. 在 console 中把 vxworks image 送到串行口。 /b 表示以二进制方式

C:>copy /b vxworks COM1:

7. Power off ARM target ,设置其从Flash启动。

8. reboot ,进入 VxWorks

这 2K 字节的程序就是我们说的 ARM Bootloader ,它的任务一般是:

1. 必要的硬件初始化
2. 从串行口接收VxWorks的二进制文件,并写入Flash
3. 在这过程中,显示一些提示信息。
像 Bootloader 这样底层的程序一般认为是要用纯汇编来写的。但是用汇编写的程序可读性肯定没有用C写的程序好。汇编程序不宜维护,没办法向其它类型的 CPU 去移植。这些方面 C 的程序是没有问题的! _

那么Bootloader能否用纯C语言去写呢?不可能。因为有些操作特殊寄存器的指令也是特殊指令,用C是实现不了的。有些功能用C也是不能实现的。用C不能作的有:
1. 操作 CP15 寄存器的指令
2. 中断使能
3. 堆栈地址的设定

所以,只要知道这几条汇编指令可以了,不必学习所有的汇编指令。是不是上手很快呀。下面来看看我们在Bootloader 中所用到的汇编部分:
asm ("_my_start:
mov r14, #0x70
mcr p15, 0, r14, c1, c0, 0 /* MMU disabled, 32 Bit mode, Little endian */
mrs r14, cpsr
bic r14, r14, #0x 1f /* Mask */
orr r14, r14, #0xc0 + 0x13 /* Diasble IRQ and FIQ, SVC32 Mode */
msr cpsr, r14
ldr r13, =0xc0020000 /* Setup Stack */
");

简单吧,比看几十K的汇编程序感觉好得多吧。也许你会问:硬件的初始化怎么办?那是要操作寄存器的。我说:看看这段 C 的代码:
*((DWORD*)(dwHardwareBase + HW1_SYSCON1)) = SYSCON1_VALUE;

明白了吧, ARM 中把寄存器映射在内存中了,就跟读写内存没有区别。现在编写程序的问题已经全部解决了,但是否就没有问题了呢?不是。你的程序应该写成什么样呢?怎么编译生成二进制文件呢?让我们先写一个程序试一下吧:
#define DWORD unsigned int
int main(void)
{
register DWORD dwHardwareBase;
asm ("_my_start:
mov r14, #0x70
mcr p15, 0, r14, c1, c0, 0 /* MMU disabled, 32 Bit mode, Little endian */
mrs r14, cpsr
bic r14, r14, #0x 1f /* Mask */
orr r14, r14, #0xc0 + 0x13 /* Diasble IRQ and FIQ, SVC32 Mode */
msr cpsr, r14
ldr r13, =0xc0020000 /* Setup Stack */
");
dwHardwareBase = (DWORD)0x80000000;
return 0;
}

详情>>

点击此处查看原文 >>

系统分类: 嵌入式   |    用户分类:    |    来源: 转贴

评论(0) | 阅读(780)
发表于:2007-3-30 16:01:38
标签:ARM  启动代码  bootloader  

0

ARM处理器启动代码的分析与设计

ARM体系结构

       目前,ARM系列的通用32位RISC微处理器有ARM 7、 ARM 9、 ARM 9E、 ARM 10等多个产品,这些处理器可以工作于 7 种模式下。除 User 模式以外的其它模式都叫做特权模式,除 User 和 System 以外的其它 5 种模式叫做异常模式。大部分应用程序都在 User 模式下运行,当处理器处于 User 模式下时,执行的程序无法访问一些被保护的系统资源,以利于操作系统控制系统资源的使用,也不能改变模式,否则就会导致一次异常。对于 System 模式,任何异常都不会导致进入这一模式,而且它使用的寄存器和 User 模式下基本相同,主要是用于有访问系统资源请求而又避免使用额外的寄存器的操作系统任务。在特权模式下,它们可以完全访问系统资源,可以自由地改变模式。在处理特定的异常时,系统进入对应的异常模式下。这5 种异常模式都有各自额外的寄存器,用于避免在发生异常的时候与用户模式下的程序发生冲突。

       在任意一种处理器模式中,都使用同一个寄存器来标识当前处理器的工作模式,这个寄存器叫做CPSR( 当前程序状态寄存器 ), 它的 0~4 位用来表示CPU 模式,而且在每一种处理器异常模式下,都有一个对应的 SPSR( 缓存程序状态寄存器 ) ,用来保存进入异常模式前的CPSR 的值。SPSR的作用就是当CPU 从异常模式退出时,通过一条简单的汇编指令就能够恢复进入异常模式前的 CPSR ,该值保存在当前异常模式的 SPSR 中。

启动代码的设计

       启动代码类似于电脑中的BIOS ,它从系统上电开始接管CPU ,依次需要负责初始化CPU 在各种模式下的堆栈空间、设定CPU 的内存映射、对系统的各种控制寄存器做初始化、对CPU 的外部存储器进行初始化、设定各外围设备的基地址、创建正确的中断向量表、为C 代码执行创建ZI( 零创建 ) 区,然后进入到C 代码。 在C 代码中继续对时钟、RS232 端口进行初始化,然后打开系统中断允许位。最后进入到应用代码中执行,执行期间响应各种不同的中断信号并调用预先设置好的中断服务程序处理这些中断。整个过程的流程图如图 1 所示。

                                                               图 1  启动代码流程图

       堆栈初始化

       堆栈的初始化要处理的事情是为处理器的 7 个处理器模式分配堆栈空间。以下以 FIQ 模式下的堆栈设置为例说明:

       ORR r1, r0, #LOCKOUT | FIQ_MODE ;把模式放在 r1 中, LOCKOUT 用来屏蔽中断位;

       MSR cpsr, r1  ;改变 CPU 的 CPSR 寄存器,进入到指定的 FIQ 模式;

       MSR spsr, r2   ;保存前一模式;

       LDR sp, =FIQ_STACK   ;把 FIQ 模式下的堆栈起始值赋给当前的 SP , FIQ_STACK 是分配给 FIQ 模式堆栈空间 ( 比如说 1K 字节 ) 的起始地址。按这种方式设置其它模式下的堆栈。

       DRAM 的初始化根据系统配置信息来决定,因为系统不一定会用到DRAM ,但是一定要做SDRAM 的初始化。主要的处理内容是 ROM 和 RAM 基址的设定、数据总线的宽度、 SDRAM 的刷新时间等等,这些可以参照 S3C4510B 芯片的用户手册。特殊寄存器的设置主要是针对 I/O 口,比方说设定几个 I/O 位用做系统状态指示灯 LED 。寄存器的设定主要根据硬件的配置情况而定,值得注意的是由于这段启动代码是烧录到ROM 中的,而中断向量必须位于零地址,所以在存储单元没有重新映射之前ROM 基址的设定应该为零地址。

       零初始化区。内存的初始化处理的内容是:当只读区截止地址等于可读可写区基址时,把零初始化区各字节清零;当只读区截止地址不等于可读可写区基址时,如果可读可写区基址小于零初始化基址,就从只读区截止地址处开始把数据拷贝到可读可写区基址处,直到到达零初始化基址,然后把零初始化区各字节清零,否则也只用把零初始化区各字节清零。

       中断向量表是用于处理异常情况的,当发生异常时,首先要保存当前程序的返回地址和 CPSR 寄存器的值,然后进入到相应的异常向量地址,一般来说在异常向量地址是一个跳转指令,使程序进入相应的异常处理过程。由于中断向量表要位于系统的零地址,当把启动代码烧录到 EEPROM 中运行时就需要把 ROM 的地址定义到零地址,所以程序的入口处如下:

点击看大图

       系统重新映射当你为了提高运行速度而把ROM 的Image 拷贝到RAM 后,中断向量表就不是在零地址处,因此要重新映射存储单元,把RAM 的地址重新设定为零地址。映射就是把启动代码从ROM(EEPROM 或者 Flash) 拷贝到SDRAM 运行,同时再拷贝完毕以后进行内存的重新映射,把SDRAM 映射到原来的ROM 地址 (0x0000) 中,这样就可以用SDRAM中的代码写Flash ,使得程序代码得以更新。但是需要注意的是,如果程序进行了映射,这样就对在线调试带来了困难,使得在线调试不可以在RAM 中进行 ( 如果写入 EEPROM 的代码是映射了的,则在调试器启动的时候必然也会对程序进行映射,使得程序在调试器中不可以定位到原来的地方 , 使得调试失败 ) 。一个折中的方法是,不进行映射,就是说在调试的代码中不可以使用下载,这样就可以像普通的代码一样进行调试了。

        

       做完这些初始化后,让CPU 切换到用户模式 下,并把堆栈指针SP 指定到用户堆栈区 , 就可以进入到C 代码区运行。在C 代码中继续对时钟、 RS232 端口进行初始化,然后打开系统中断允许位,进入到应用代码中执行。此程序加载到处理器S3C4510B 中经过调试,CPU 可以正常启动,能够对中断请求做出及时的响应,上层应用的主代码可以加载到Flash 中,移植实时操作系统RTXC 后对多任务的调度控制正常。

作者:武汉理工大学 甘泉 杨健 陈永泰

更多技术文章

点击此处查看原文 >>

系统分类: 嵌入式   |    用户分类:    |    来源: 转贴

评论(0) | 阅读(810)
总共 , 当前 /,23下一页