EDN首页   博客首页

最新日志

发表于:2006/12/30 13:03:42
标签:无标签

6

《嵌入式常用IC芯片索引》

《嵌入式常用IC芯片索引》

×     【目录】
×   1.电源变换IC芯片
×   2.数字式传感器,电位器及精密运放芯片
×   3.电机控制及驱动芯片
×   4.数字通信IC芯片及接口
×   5.AD转换芯片
×   6.DA转换芯片
×     【说明】
×    1.“...”表示未完成待补充,因为采用增量模型,所以没有结束的一天。:-)
×    2.全部一个一个敲的,注意版权,引用时注明出处,呵呵
×
××××××××××××××××××××××××××××××××××××××××
1.电源变换IC芯片
——————————————————————————————
指标:
1.输出电压范围(正/负)
2.输入电压范围
3.输入输出电阻及电流(直流交流)
4.纹波抑制比
5.温漂
6.
7.
...
————————————————————————————————————
7800     三端,固定正电压输出稳压器(块)芯片
7900     三端,固定负电压输出稳压器(块)芯片
AD580    三端,精密电压基准芯片
ADR290/291/292/293  高精度,新型XFET 3端基准电源芯片
D14,D24  DC-DC隔离电源模块
HV-2405E  50mA,5~24V,AC/DC电源IC芯片
HQA-2405E AC/DC电源变换器模块
IMP706  低功耗,uP电源监控IC芯片
LM117/217/317 3端,可调正电压输出稳压芯片
LM137/237/337 3端,可调负电压输出稳压(块)芯片
LM138/238/338 3端,大电流,可调正电压输出稳压(块)芯片
LM150/250/350 3端,大电流,可调正电压输出稳压(块)芯片
LM2930 汽车用3端稳压器芯片
LT108X/SP116XX 3端,低电压,输出可调稳压器芯片
M5236L/37L 灵活方便,低电压差,3端稳压驱动芯片
MAX610  无变压器式,AC/DC电源变换器IC芯片
MAX619  输入2V,输出5V,充电泵DC/DC变换器IC芯片
MAX629  DC/DC转换芯片
MAX638  过低电压检测报警,降压开关型,DC/DC电源变换器IC芯片
MAX639  过低电压检测报警,降压开关型,DC/DC电源变换器IC芯片
MAX682-685  低电压差,微功耗稳压器芯片
MAX706  电压监控芯片
MAX813L  看门狗,电压监控芯片
MAX889   2MHZ稳压型电荷泵,负电压输出,DC/DC变换器芯片
MAX1606  输入5V,输出28V,LCD偏置电源DC/DC芯片
MAX1642/1643  输入电压仅为1V的DC/DC变换器芯片
MAX1692    1.8V,降压型,微型开关,DC/DC芯片
MAX1725/1726  更低功耗,低压差,线性稳压器芯片
MAX1742/1842  内含1A开关,1MHz,降压型DC/DC芯片
MAX1744/1745  36V输入,10W输出,降压型转换器芯片
MAX1730/1759  稳压型,电荷泵,DC/DC芯片
MAX1775    双路,降压型,2A以上,DC/DC芯片
MAX1832/1833/1834/1835   电池反接保护,升压型DC/DC转换器芯片
MAX1864/1865  降压型,DC/DC,5路输出线缆MODEM电源芯片
MAX5130+PIC   精确可编程,8000基准电压值,DC/DC发生器芯片
MAX6125   微封装,微功耗,微漂移,DC/DC芯片
MAX6129   功耗更低,串联型,3端,电压基准芯片
MAX6333   监视电压可低至1.6V的新型单片复位IC芯片
MAX6821-6825   手动复位,“看门狗”定时器,低功耗,UP监控电路芯片
MAX828/829     充电泵,反压型,DC/DC芯片
MAX8880/8881   带有电源好2(POWDWR-OK)输出的DC/DC芯片
MAX8883    双路,低压差,线性稳压器芯片
MC1403    8脚精密电压基准芯片
MIC2141   微功耗,升压型,V0可控,DC/DC变换器芯片
PS0500-5  500mA,超小型,AC/DC电源变换芯片
TOP1xx-2xx  无变压器,5W以上,AC/DC变换式精密开关电源IC芯片
TL499AC     可调线型串联稳压器和升压型开关稳压器(合成稳压器)芯片
TPS7350   5V固定输出,掉电延时复位,低压差稳压器芯片
W431    3端,可调式电压基准芯片
YA-S     AC/DC电源变换器模块

2.数字式传感器,电位器及精密运放芯片
————————————————————————————————————————————
2.1 运放指标:
1.增益大小,是否增益可编程,
2.温漂,低功耗,高精度
3.放大器类型:Rail-Rail放大器,仪表放大器,功率放大器
4.工作频率,增益带宽积,共模抑制比
5.
6.
7.
...
——————————————————————————————————————————————
2.2 数字温度传感器指标:
1.测温范围
2.测温误差
3.转换时间
4.通信方式:并口,IIC,一(二,三)线
5.温度制表达方式:字节数据,脉宽
6.编程告警温度
7.辅助功能:带实时时钟?微处理器核?
8.
9.
...
——————————————————————————————————————————————
AD526  增益可编程运算放大器芯片
AD620  低功耗,高精密度仪器用运放芯片
AD623  单电源Rail-Rail仪表运放芯片
AD625  增益可编程运算放大器芯片
AD626  单电源差分运算放大器芯片
AD7416  带IIC接口,10位低功耗数字温度传感器芯片
AD8571/8572/8574  0温漂,单电源,运算放大器芯片
AD8591/8592/8594  带节能控制端的CMOS,单电源工作,满电源输入输出,运算放大器芯片
DS1620   数字式温度传感器IC芯片
DS1621  数字式温度传感器IC芯片及恒温控制器IC芯片
DS1625  数字温度计和控温器芯片
DS1629  2线接口,带有实时时钟的温度传感器芯片
DS1820  数字式温度传感器IC芯片
ITT2301AF 射频功率放大器芯片
LM76    带数字温度传感器,IIC总线接口,12位信号输出,测温芯片
LM92    数字式温度传感器芯片
MAX54xx 体积更小,256级,数字电位器芯片
MAX4265~4270  超低失真,单+5V,300MHz,运算放大器芯片
MAX4430/4431/4432/4433  高速(280MHz),高精度,宽频带,单/双运算放大器芯片
MAX6627/6628   兼容SPI接口的远端结温检测器芯片
MAX6629/6630/6631/6632  微型SOT封装,+-1摄氏度精度的数字温度传感器芯片
MAX6657/6658/6659    +-1摄氏度的本地和远端结温检测器芯片
OP193/293/493  精密,微功耗,运算放大器芯片
OP177   超精密运算放大器芯片
OP777   精密,微功耗,单电源,运算放大器芯片
MIC91x  高速(100~350MHz)运算放大器芯片
X9241   IIC接口,数字电位器(EEPOT)IC芯片
X9312   数字电位器IC芯片
X9313   数控电位器芯片
X9511   PushPOT按钮控制电位器芯片

3.电机控制及驱动芯片
87C196MC  电机控制专用微处理器芯片
CIPH9803  可编程步进电机控制IC芯片
FR-Z240-7.5K  变频调速器芯片
HEF4752V  PWM大规模集成电路芯片
IR2110    高压浮动MOSFET,栅极步进电机驱动器IC芯片
LM628     直流电机运动控制芯片
LM1542    无刷直流电机控制器芯片
LMD18200  H桥组件电机驱动芯片
MA818     3相PWM,变频调速专用控制器芯片
MAX1749   微型直流电机驱动控制芯片
MC33033   带温度补偿的直流电机控制器芯片
ML4428    无传感器PWM,无刷直流电机控制器芯片
MOC30xx   双向晶闸管电机控制驱动器(双向光电耦合器)IC芯片
MTE1122   智能型电机驱动运放芯片
PA03      大功率(1000w)运放电机驱动芯片
PA21/25/26  双功率电机驱动运放芯片
PA61     大功率运放电机驱动芯片
PA85    高压,高速,大功率,运放驱动芯片
PBL3772/PBM3960   高性能步进电机驱动IC芯片组
PH2083   多模式步进电机控制器IC芯片
PMM8713  步进电机专用控制芯片
SA06     脉宽调制运放,电机驱动芯片
SA60     脉宽调制型功放芯片
SA866    可编程,全数字化,3相PWM,变频调速控制器IC芯片
ST6210   通用电机驱动电路(MCU)IC芯片
TDA1085C  通用电机速度控制器芯片
UCx637XC9536  PWM型直流电机驱动芯片
XC9536  步进电机CPLD控制芯片
 
4.数字通信IC芯片及接口
——————————————————————————————————————
数据通信IC芯片指标:
1.支持的协议类型:无线/有线,蓝牙,FSK,RS232,RS485,RS422,EIA/TIA-232
2,全双工/半双工
3.工作频率,是否ESD保护
4.是否为编解码芯片
5.
6.
7.
...
——————————————————————————————————————
5G16C550
ACM1330E/1550D
ACMTX16/ACMRX18
ADM101E
AM7910
Core 01
DS14C232C/232T
DS26F31
DS26C32
DS3695/3696/3697/3698
DS8921
DS8922
DS9637
DS9638
DS14185
DS75176
DS96172/96174
DS96173/96175
HT9200A
ICL232
KX50xx
LM1893
LMx3162
M303S/303R
M-8888
MAX48x/49x
MAX202
MAX202E/211E/213E/232E/241E
MAX214
MAX220/232/232A
MAX250/251
MAX1480A/1480B
MAX3080E-3089E
MAX3082
MAX3100
MAX3140
MAX3222/3232
MAX3224~3227
MAX3238E/3248E
...
————————————————————————————————————————
AD/DA芯片指标:
1.几位,几通道
2.低/高速
3.接入方式:并行/串行/IIC
4.是/否内含参考电压源,自校准,片内运放
5.是/否带微处理核
6.辅助功能:温度传感,时钟发生
7.工作频率
8.
9.
10.
...
————————————————————————————————————————
5.AD转换芯片
...
6.DA转换芯片
...

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

评论(0) | 阅读(1911)
发表于:2006/12/30 12:50:19
标签:无标签

3

ARM简介及BootLoader介绍

ARM简介及BootLoader介绍
黄大荣 2006-09-29

ARM基本常识(1)
    目前嵌入式处理器常见的有ARM、PowerPC、MIPS、Motorola 68K、ColdFire(冷火)等,但ARM占据了绝对主流(有资料说手机中几乎90%都是ARM处理器)。ARM是一个只卖知识产权的公司,目前获得购买了ARM CPU核授权许可的大公司很多,包括Intel、Samsung、Amstel、Motorola、Philip等,他们都在ARM CPU核的基础上进行了一些外围扩展,形成自己的处理器。
    ARM(Advanced RISC Machines),既可以认为是一个公司的名字,也可以认为是对一类微处理器的通称,还可以认为是一种技术的名字。

ARM基本常识(2)
    1991年ARM公司成立于英国剑桥,主要出售芯片设计技术的授权。目前,采用ARM技术知识产权(IP)核的微处理器,即我们通常所说的ARM微处理器,已遍及工业控制、消费类电子产品、通信系统、网络系统、无线系统等各类产品市场,基于ARM技术的微处理器应用约占据了32位RISC微处理器75%以上的市场份额,ARM技术正在逐步渗入到我们生活的各个方面。
    ARM公司是专门从事基于RISC技术芯片设计开发的公司,作为知识产权供应商,本身不直接从事芯片生产,靠转让设计许可由合作公司生产各具特色的芯片,世界各大半导体生产商从ARM公司购买ARM微处理器核,根据各自不同的应用领域,加入适当的外围电路,从而形成自己的ARM微处理器芯片进入市场。全世界有几十家大的半导体公司都使用ARM公司的授权,因此既使得ARM技术获得更多的第三方工具、制造、软件的支持,又使整个系统成本降低,使产品更容易进入市场被消费者所接受,更具有竞争力。

ARM处理器状态(1)
    ARM微处理器的工作状态一般有两种,并可在两种状态之间切换:
第一种为ARM状态,此时处理器执行32位的字对齐的ARM指令;
第二种为Thumb状态,此时处理器执行16位的、半字对齐的Thumb指令。
在程序的执行过程中,微处理器可以随时在两种工作状态之间切换,并且,处理器工作状态的转变并不影响处理器的工作模式和相应寄存器中的内容。但ARM微处理器在开始执行代码时,应该处于ARM状态。 

ARM处理器状态(2)
    进入Thumb状态:当操作数寄存器的状态位(位0)为1时,可以采用执行BX指令的方法,使微处理器从ARM状态切换到Thumb状态。此外,当处理器处于Thumb状态时发生异常(如IRQ、FIQ、Undef、Abort、SWI等),则异常处理返回时,自动切换到Thumb状态。
    进入ARM状态:当操作数寄存器的状态位为0时,执行BX指令时可以使微处理器从Thumb状态切换到ARM状态。此外,在处理器进行异常处理时,把PC指针放入异常模式链接寄存器中,并从异常向量地址开始执行程序,也可以使处理器切换到ARM状态。

ARM处理器模式(1)
    ARM微处理器支持7种运行模式,分别为:
用户模式(usr):ARM处理器正常的程序执行状态。
快速中断模式(fiq):用于高速数据传输或通道处理。
外部中断模式(irq):用于通用的中断处理。
管理模式(svc):操作系统使用的保护模式。
数据访问终止模式(abt):当数据或指令预取终止时进入该模式,可用于虚拟存储及存储保护。
系统模式(sys):运行具有特权的操作系统任务。
定义指令中止模式(und):当未定义的指令执行时进入该模式,可用于支持硬件协处理器的软件仿真。

ARM处理器模式(2)
    ARM微处理器的运行模式可以通过软件改变,也可以通过外部中断或异常处理改变。大多数的应用程序运行在用户模式下,当处理器运行在用户模式下时,某些被保护的系统资源是不能被访问的。
    除用户模式以外,其余的所有6种模式称之为非用户模式,或特权模式;其中除去用户模式和系统模式以外的5种又称为异常模式,常用于处理中断或异常,以及需要访问受保护的系统资源等情况。

ARM寄存器
    ARM处理器共有37个寄存器。其中包括:31个通用寄存器,包括程序计数器(PC)在内。这些寄存器都是32位寄存器。以及6个32位状态寄存器。
关于寄存器这里就不详细介绍了,有兴趣的人可以上网找找,很多这方面的资料。

异常处理
    当正常的程序执行流程发生暂时的停止时,称之为异常,例如处理一个外部的中断请求。在处理异常之前,当前处理器的状态必须保留,这样当异常处理完成之后,当前程序可以继续执行。处理器允许多个异常同时发生,它们将会按固定的优先级进行处理。当一个异常出现以后,ARM微处理器会执行以下几步操作:

进入异常处理的基本步骤:
将下一条指令的地址存入相应连接寄存器LR,以便程序在处理异常返回时能从正确的位置重新开始执行。将CPSR复制到相应的SPSR中。
根据异常类型,强制设置CPSR的运行模式位。
强制PC从相关的异常向量地址取下一条指令执行,从而跳转到相应的异常处理程序处。

如果异常发生时,处理器处于Thumb状态,则当异常向量地址加载入PC时,处理器自动切换到ARM状态。
ARM微处理器对异常的响应过程用伪码可以描述为:
R14_ = Return Link
SPSR_= CPSR
CPSR[4:0] = Exception Mode Number
CPSR[5] = 0 ;当运行于 ARM 工作状态时
If == Reset or FIQ then;当响应 FIQ 异常时,禁止新的 FIQ 异常
CPSR[6] = 1
PSR[7] = 1
PC = Exception Vector Address

异常处理完毕之后,ARM微处理器会执行以下几步操作从异常返回:
将连接寄存器LR的值减去相应的偏移量后送到PC中。
将SPSR复制回CPSR中。
若在进入异常处理时设置了中断禁止位,要在此清除。


................................................................................
BootLoader简介
    简单地说,Boot Loader 就是在操作系统内核运行之前运行的一段小程序。通过这段小程序,我们可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境。
    Boot Loader 是严重地依赖于硬件而实现的,特别是在嵌入式世界。因此,在嵌入式世界里建立一个通用的 Boot Loader 几乎是不可能的。尽管如此,我们仍然可以对 Boot Loader 归纳出一些通用的概念来,以指导用户特定的 Boot Loader 设计与实现。
   
    基于 ARM7TDMI core 的 CPU 在复位时通常都从地址 0x00000000 取它的第一条指令。在系统加电后,CPU 将首先执行 Boot Loader 程序。
大多数 Boot Loader 都包含两种不同的操作模式:“启动加载”模式和“下载”模式 :
启动加载(Boot loading)模式:Boot Loader 从目标机上的某个固态存储设备上将操作系统加载到 RAM 中运行,整个过程并没有用户的介入。
下载(Downloading)模式:Boot Loader 将通过串口连接或网络连接等通信手段从主机(Host)下载文件,比如:下载内核映像和根文件系统映像等。

BOOT的一般步骤为:

        设置中断向量表
        初始化存储设备
        初始化堆栈
        初始化用户执行环境
        呼叫主应用程序

设置中断向量表
    ARM要求中断向量表必须放置在从0地址开始,连续8X4字节的空间内。
每当一个中断发生以后,ARM处理器便强制把PC指针置为向量表中对应中断类型的地址值。因为每个中断只占据向量表中1个字的存储空间,只能放置一条ARM指令,使程序跳转到存储器的其他地方,再执行中断处理。
中断向量表的程序实现通常如下表示:
AREA Boot ,CODE, READONLY
ENTRY
B??? ResetHandler
B??? UndefHandler
B??? SWIHandler
B??? PreAbortHandler
B??? DataAbortHandler
B
B?? ?IRQHandler
B??? FIQHandler
其中关键字ENTRY是指定编译器保留这段代码,因为编译器可能会认为这是一段亢余代码而加以优化。链接的时候要确保这段代码被链接在0地址处,并且作为整个程序的入口。?

初始化存储设备
    存储器端口的接口时序优化是非常重要的,这会影响到整个系统的性能。因为一般系统运行的速度瓶颈都存在于存储器访问,所以存储器访问时序应尽可能的快;而同时又要考虑到由此带来的稳定性问题。
在不同的板子上处理芯片、存储设备以及其接口差异很大,应根据不同的情况来配置。

初始化堆栈
    因为ARM有7种执行状态,每一种状态的堆栈指针寄存器(SP)都是独立的。因此,对程序中需要用到的每一种模式都要给SP定义一个堆栈地址。方法是改变状态寄存器内的状态位,使处理器切换到不同的状态,然后给SP赋值。注意:不要切换到User模式进行User模式的堆栈设置,因为进入User模式后就不能再操作CPSR回到别的模式了,可能会对接下去的程序执行造成影响。

   这是一段堆栈初始化的代码示例:
mrs     r0,cpsr  ;读取cpsr寄存器的值
bic     r0,r0,#MODEMASK ;把模式位清零
orr     r1,r0,#UNDEFMODE|NOINT
msr     cpsr_cxsf,r1  ;UndefMode
ldr     sp,=UndefStack
其他模式的堆栈的初始化也类似。

堆栈地址的定义一般如下:
 ^ (_ISR_STARTADDRESS-0x1400)
    
UserStack # 1024 ;#=field,定义一个数据域,长度为1024
SVCStack # 1024
UndefStack # 1024 
AbortStack # 1024 
IRQStack # 1024 
FIQStack # 0

初始化用户执行环境
    一个ARM映像文件由RO,RW和ZI三个段组成,其中RO为代码段,RW是已初始化的全局变量,ZI是未初始化的全局变量。
映像一开始总是存储在ROM/Flash里面的,其RO部分即可以在ROM/Flash里面执行,也可以转移到速度更快的RAM中执行;而RW和ZI这两部分是必须转移到可写的RAM里去。所谓应用程序执行环境的初始化,就是完成必要的从ROM到RAM的数据传输和内容清零。
   
    编译器使用下列符号来记录各段的起始和结束地址:
|Image$$RO$$Base| :RO段起始地址
|Image$$RO$$Limit| :RO段结束地址加1
|Image$$RW$$Base| :RW段起始地址
|Image$$RW$$Limit| :ZI段结束地址加1
|Image$$ZI$$Base| :ZI段起始地址
|Image$$ZI$$Limit| :ZI段结束地址加1
这些标号的值是根据链接器中设置的中ro-base和rw-base的设置来计算的。 
    初始化用户执行环境主要是把RO、RW、ZI三段拷贝到指定的位置。

呼叫主应用程序
    当所有的系统初始化工作完成之后,就需要把程序流程转入主应用程序。最简单的一种情况是:
IMPORT main
B????? main
直接从启动代码跳转到应用程序的主函数入口,当然主函数名字可以由用户随便定义。

以上介绍的都只是相关知识点的概要,如果需要详细了解请上网查询。Thanks


系统分类: ARM   |    用户分类: 无分类    |    来源: 转贴

评论(3) | 阅读(6505)
发表于:2006/12/30 12:37:18
标签:无标签

1

嵌入式linux快速入门

一个典型的桌面Linux系统包括3个主要的软件层---linux内核、C库和应用程序代码。
内核是唯一可以完全控制硬件的层,内核驱动程序代表应用程序与硬件之间进行会话。内核之上是C库,负责把POSIX API转换为内核可以识别的形式,然后调用内核,从应用程序向内核传递参数。应用程序依靠驱动内核来完成特定的任务。
在设计嵌入式应用的时候,可以不按照这种层次,应用程序越过C库直接和内核会话,或者把应用和内核捆绑在一起,甚至可以把应用写为内核的一个线程,在内核中运行,虽然这样在移植上带来了困难,但考虑嵌入式系统对尺寸要求小的特点,是完全可行的。不过我们使用三层软件结构的模式来学习嵌入式linux将会是我们认识更清晰,简单可行并使应用具有弹性。

快速入门,最简单的建立嵌入式Linux应用的方法就是从我们使用的桌面Linux入手,安装一个喜爱的版本,把我们的某个应用作为初始化的一部分,框架就算完成了。当然,嵌入式linux应用远比我们的桌面版本功能简单专一,它也许就是一个用于足彩的终端机,或是一个数码音频播放器,这些系统除了使用嵌入式CPU外,仅仅再需要一个串口,网口等少量的输入输出接口就可以完成它们特定的应用了。在软件上,它可以按照三层的概念由内核装载器,定制的内核和较少的为特定任务设计的静态连接的应用程序组成。之所以使用静态连接的应用程序,是因为少量的静态连接程序所要的存储空间,比同样数量的动态连接的程序所占的空间小,这个平衡点需要我们在实际开发中去获取。也许你正在设计的是个PDA,它的应用程序较多,那么你很可能就要使用动态连接程序来减少存储空间。在你的/bin或者/sbin目录下,用厂列表看看bash,ifconfig,vi...,也许只用几十K,当你运行 ldd /bin/bash 时,你会看到它们都和好几个库文件相连。好了,这样看来,我们得把PC想像成一个嵌入式硬件平台,再重新制作一个特定功能的嵌入式linux

再进行实际操作之前,先来搞清楚几个基础知识。
内核装载器Loader,它的作用是把内核从外部存储器,移动到内存中。它只作这个事情,一旦完成了调入内核的工作,Loader就跳转到内核位置开始执行。不同架构有不同的Loader,在x86结构的PC上,通常使用的loaderLILO,GRUB,syslinux,syslinux在嵌入式 linux中也同样工作。其他非x86架构的应用中,你必须使用专门的loader,或者自己编写loader来装入内核。也有不使用loader的情况,系统加电以后,内核直接从烧录有映象的Flash上开始执行。
内核,一旦内核开始执行,它将通过驱动程序初始化所有硬件,这可以从我们的pc机监视器的输出看出来,每个驱动程序都打印一些有关它的信息。初始化完成后,计算机就准备运行嵌入式应用。也许一个,也许是多个应用程序组成了嵌入式应用,但通常首先调用的是init(通过loader 向核心传入init=/program 可以定制首先运行的程序)。桌面linux中,init会读取/etc/inittab文件,来决定执行级别和哪些脚本和命令。嵌入式应用中,可以根据实际的情况决定是否使用标准的init执行方式,也许这个init是个静态程序,它能够完成我们的嵌入应用的特定任务,那完全不用考虑inittab了。
initrd
文件系统,initrd以一种把内核从存储介质装入到内存的相同的机制来装入一个小型文件系统。这个文件系统最好是以压缩的方式存储在介质上的,解压缩到RAM盘上。通过使用initrd,包含有核心驱动和启动脚本的小文件系统,就可以直接从介质上和内核一起启动起来,内核届压缩这个文件系统,并执行这个文件系统上叫做/linuxrc的脚本文件,这个脚本通常会把启动过程中所需要的驱动程序装入。脚本退出以后,initrd文件系统也卸下了,启动过程进入真正初始化过程。对于嵌入式来讲,可以将需要的应用软件都运行在这个initrd文件系统上,只要/linxrc文件不结束,内核启动过程的其他部分就不会继续。
做个试验:
cp /boot/initrd-2.4.20.img /tmp
cd /tmp
mv initrd-2.4.2-.img initrd.img.gz
gunzip initrd.img.gz
mount -o loop initrd.img /mnt
cd /mnt
ls
cat linuxrc
可以看到里面执行了加载了两个模块的操作,你在启动linxu的时候会看见屏幕打印信息。

入门试验,制作一个简单的应用:我们使用一张软盘启动一台假象的只有一个串口,键盘输入,显示输出的x86架构的linux系统,执行的特定应用就是运行 minicom,通过串口拨号。需要软件: minicom-xx.src.tar.gz syslinux-xx.tar.gzxx代表版本号,开始之前,在主目录建立一个目录,来释放这两个软件包:
cd
mkdir -p project/minilinux
cd project/minilinux
tar zxvf minicom-xx.src.tar.gz
tar zxvf syslinux-xx.tar.gz

1、裁减linux内核(需要系统安装内核文件包)

配置内核的时候,我们需要选择这些:摸块编入内核,386处理器、物理内存off、支持ELF、标准PC软盘、支持RAM(4096)、支持 initial RAM disk (initrd)、虚你终端、虚拟终端控制台、标准串口、ext2文件系统、控制台驱动,VGA text consoleDOS FATMSDOS文件系统,其他的都可以不要,这样内核编出来较小。
步骤:
cd /usr/src/linux
make mrproper
make xconfig
make dep && make bzImage
得到 /usr/src/linux/arch/i386/boot/目录的内核文件bzIamge

2、编译一个静态的minicom ,把它作为将来的linuxrc
cd minicom-xx/src
vi Makefile
修改下面这行
minicom: $(minicom_OBJECTS) $(minicom_DEPENDENCIES)
rm -f minicom
下面的行加上 -static,连接为静态程序
(LINK) -static $(minicom_LDFLAGS) $(minicom_OBJECTS) $(minicom_LDADD) $(LIBS)

vi minicom.c
找到 if (real_uid==0 && dosetup==0 ) 删除这个判断条件语句,主要是用于权限判断的,因为这个嵌入应用不关注权限问题,否则会出错。
make
得到可执行程序,用ldd 检查一下是不是静态程序。

3、准备initrd压缩文件image.gz
dd if=/dev/zero of="image" bs="1k" count="4096"
losetup /dev/loop0 image
mke2fs -m 0 /dev/loop0
mounmt -t ext2 /dev/loop0 /mnt/
mkdir -p /mnt/dev
mkdir -p /mnt/usr/share/terminfo/l/
cd /dev
cp -a consle null tty tty0 zero mem /mnt/dev
cp -P /usr/share/terminfo/l/linux /mnt/usr/share/terminfo/l/linux
cp ~/project/minilinux/mincom/src/minicom /mnt/linuxrc
umount /mnt
losetup -d /dev/loop0
sync
gzip -9 image

4、制作软盘引导,并拷贝文件 bzimage image.gz 到软盘

A.使用grub
fdformat /dev/fd0
mke2fs /dev/fd0
mount /mnt/fd0 /mnt/floppy
mkdir -p /mnt/floppy/boot/grub
cp /boot/grub/stage1 /boot/grub/stage2 /mnt/floppy/boot/grub
执行 grub,在软盘上创建引导
grub > root (fd0)
grub > setup (fd0)
grub > quit

cp /usr/src/linux/arch/i386/boot/bzImge /mnt/floppy
cp ~/porject/minilinux/image.gz /mnt/floppy

编辑 /mnt/floppy/boot/grub/grub.conf
default =0
timeout-=10
title minilinux
root (fd0)
kernel /bzImage
initrd /image.gz

卸下软盘
umount /mnt/floppy


B.
使用syslinux
fdformat /dev/fd0
mkfs.msdos /dev/fd0
mount -t msdos /dev/fd0 /mnt/floppy

cp /usr/src/linux/arch/i386/boot/bzImge /mnt/floppy
cp ~/porject/minilinux/image.gz /mnt/floppy

cp syslinux-xx/ldlinxu.sys /mnt/floppy
cat > /mnt/floppy/syslinux.cfg
LABEL linux
KERNEL bzimage
APPEND initrd="image".gz

umont /mnt/floppy
syslinux-xx/syslinux /dev/fd0
sync

5、用软盘启动计算机,如果幸运,minicom的运行画面出现在屏幕上。

到此,我们的单应用嵌入式linux做好了,但它还很简陋,没有什么实际用途,但通过这个实验,可以了解嵌入式系统的大致结构和开发过程。在进行实际的嵌入式开发时,通常要在PC机上借助嵌入式linux开发工具包,如:uclinux,bluecat等,对相应的硬件平台(目标机)进行软件编写编译,调试成功后,将内核及应用程序写入到目标机的存储器中,从而完成整个应用。

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

评论(0) | 阅读(1033)
发表于:2006/12/30 12:36:09
标签:无标签

1

实现一个最简单的嵌入式操作系统

实现一个最简单的嵌入式操作系统(一)精华帖

实现一个什么都不能做的嵌入式操作系统

1.首先确定CPU,在这里为了简单,就选用嵌入式的CPU,比如ARM系列,之所以用RISC(简单指令集)
类型的CPU,其方便之处是没有实模式与保护模式之分,采用线性的统一寻址,也就是不需要进行段
页式内存管理,还有就是芯片内部集成了一些常用外设控制器,比如以太网卡,串口等等,不需要像
在PC机的主板上那么多外设芯片
2.确定要实现的模块和功能,为了简单,只实现多任务调度(但有限制,比如最多不超过10),实
现中断处理(不支持中断优先级),不进行动态SHELL交互,不实现动态模块加载,不实现fork之类
的动态进程派生和加载(也就是说要想在你的操作系统上加入用户程序,只能静态编译进内核中;不
支持文件系统,不支持网络,不支持PCI,USB,磁盘等外设(除了支持串口,呵呵,串口最简单嘛),
不支持虚拟内存管理(也就是说多任务中的每个进程都可以访问到任何地址,这样做的话,一个程序
死了,那么这个操作系统也就玩完了)
3.确定要使用的编译器,这里采用GCC,文件采用ELF格式,当然,最终的文件就是BIN格式,GCC和
LINUX有着紧密的联系,自己的操作系统,需要C库支持和系统调用支持,所以需要自己去裁剪C库,
自己去实现系统调用
4.实现步骤:首先是CPU选型,交叉编译环境的建立,然后就是写BOOTLOADER,写操作系统

实现一个最简单的嵌入式操作系统(二)精华帖

如何实现BOOTLOADER

1.之所以要实现一个专用的BOOTLOADER,一是为了更好的移植和自身的升级,二是为了方便操作系统的调试,当然,你完全可以将这部分所要实现的与操作系统相关的功能集成到操作系统中去
2.确定一个简单的BOOTLOADER所要完成的功能:我们这里只需要完成两个主要功能,一是将操作系统加载到内存中去运行,二是将自己和操作系统内核固化到ROM存储区(这里的ROM可以是很多设备,比如嵌入式芯片中的FLASH,PC机上的软盘,U盘,硬盘等)

3.BOOTLOADER的编写:
第一步:要进行相关硬件的初使化,比如在at91rm9200这块嵌入式板子上(以后都使用这一款芯片,主要是我对这款芯片比较熟悉,嘿嘿),大概要做接下来的几方面的工作,其一:将CPU模式切换进系统模式,关闭系统中断,关闭看门狗,根据具体情况进行内存区域映射,初始化内存控制区,包括所使用的内存条的相关参数,刷新频率等,其二:设定系统运行频率,包括使用外部晶振,设置
CPU频率,设置总线频率,设置外部设备所采用的频率等。其三:设置系统中断相关,包括定时器中断,是否使用FIQ中断,外部中断等,还有就是中断优先级设置,这里只实现两个优先级,只有时钟中断高一级,其它都一样,而中断向量初始化时都将这些中断向量指向0x18处,并关闭这里的所有中断,如果板子还接有诸如FLASH设备的话,还需要设置诸如FLASH相关操制寄存器,其四:需要关闭CACHE,到此为止,芯片相关内容就完成初始化了

第二步:中断向量表,ARM的中断与PC机芯片的中断向量表有一点差异,嵌入式设备为了简单,当发生中断时,由CPU直接跳入由0x0开始的一部分区域(ARM芯片自身决定了它中断时就会跳入0x0开始的一片区域内,具体跳到哪个地址是由中断的模式决定的,一般用到的就是复位中断,FIQ,IRQ中断,SWI中断,指令异常中断,数据异常中断,预取指令异常中断),而当CPU进入相应的由0x0开始的向量表中时,这就需要用户自己编程接管中断处理程序了,这就是需要用户自己编写中断向量表,中断向量表里存放的就是一些跳转指令,比如当CPU发生一个IRQ中断时,就会自动跳入到0x18处,这里就是用户自己编写的一个跳转指令,假如用户在此编写了一条跳转到0x20010000处的指令,那么这个地址就是一个总的IRQ中断处理入口,一个CPU可能有多个IRQ中断,在这个总的入口处如何区分不同的中断呢?就由用户编程来决定了,具体实现请参见以后相关部分,中断向量表的一般用一个vector.S文件,当然,如何命名那是你自己的喜爱,但有一点需要声明,那就是在链接时一定要将它定位在0x0处


第三步:设置堆栈,一般使用三个栈,一个是IRQ栈,一个是系统模式下的栈(系统模式下和用户模式共享寄存器和内存空间,这主要是为了简单),设置栈的目的主要是为了进行函数调用和局部变量的存放,不可能全用汇编,也不可能不用局部变量


第四步:将自己以后的代码段和数据段全部拷贝至内存,并将BSS段清零


第五步:进行串口的初始化(主要是为了与用户交互,进行与PC机的文件传输),FLASH的初始化这里在FLASH中存放BOOT和内核),FLASH驱动的编写(这里的驱动有别于平常所说的驱动,由于FLASH不像SDRAM,只要设定了相关控制器之后就可以直接读写指定地址的数据,对FLASH的写操作是一块一块数据进行,而不是一个字节一个字节地写,具体请查阅相关资料)
第六步:等待一定的秒数,来接收用户进行输入,如果在指定的秒数内用户未输入任何字符,那么
BOOT就开始在FLASH中的指定位置(可以由自己指定,这么做主要是为了简单)读取内核的所有数据到内存中(具体是内存中的什么位置由自己指定,也可以采用LINUX之类的做法,就是在内存的起始位置加上一个0x8000处),将跳转到内核的第一条代码处);如果用户在指定的秒数内键入了字符(这主要是为了方便开发,如果开发定型之后完全可以不要这段代码),那么就在串口与用户进行交互,接受用户在串口输入的命令,比如用户要求下载文件在FLASH中指定的位置等,具体内容可参考U-BOOT之类的开源项目到这里为止,BOOT部分已完成,这个BOOT非常简单,仅仅只是将PC机上传下来的文件固化到FLASH中,然后再将FLASH中的操作系统内核部分加载进内存中,并将CPU的控制权交给操作系统,下一页开始讲解如何写一个最简单的操作系统,呵,到现在才开始切入正题呢!!!!

实现一个最简单的嵌入式操作系统(三)

如何实现一个最简单的操作系统

这里为了简单,就不考虑可移植性开求,不从BOOT部分来接收参数,也不对硬件进行检测,
也不需要进行DATA段,代码段的重定位。我只是读了LINUX内核相关部分,并未自己去实现
一个操作系统,所以我以下所说的只是概念性的东西:


1.接管系统的中断处理,由于BOOT部分的代码决定了那个中断向量表,从而决定了系统中断
之后进入的内存位置,但BOOT并不知道操作系统的中断处理函数位置所在啊,怎么办呢?
有几种方法,其一是:如果你的板子可以重映射地址,也就是可以将内存条所在的位置
重映射成0x0开始,那么在链接内核的时候,就将操作系统自己的中断向量表定位在0x0处
并且在BOOTLOADER引导结束时就完成映射操作,并让CPU跳转到0x0处执行;如果没有重映
射功能,我就不晓得怎么办了,不过我想到一个折衷的办法,就是在BOOTLOADER启动完成
时(也就是将CPU控制权交给操作系统内核时),重新改写FLASH的0x0区域,就是将操作
系统的内核的中断向量表写入FLASH区的0x0处,比如,当一个IRQ发生时,CPU决定了会
跳入0x18(假设这里FLASH占用地址总线0x0至0x0fffffff,内存占用0x20000000至0x2fffffff)
,而BOOTLOADER在最后将0x18处的代码修改成了0x20000000加上0x18的地址处的代码,而这个
地址就是内核的中断向量表中的相关跳转指令,就相当于跳转进了内核所关联的IRQ处理函数
的地址上去执行中断处理函数了,而这样的不好之处在于:当系统重新上电之后,BOOT的
中断向量表已经被修改,除非BOOT本身不使用中断,呵,在这样简单的系统中,BOOT是不
需要中断功能的

2.这里为了简单,所以没有使用分页内存管理,就不需要建立页表等操作,直接进行操作
系统的堆栈设置,同BOOT一样的设置过程一样,接着就进行BSS段清零操作,这里的BSS段
是指操作系统自身的BSS段,与BOOT的BSS段是同一个含义只是用在了不同的地方了,接着
就跳入了MAIN函数

3.为了最大可能的简单,采用静态建立任务结构数组,比如只建立十个任务,那么首先要
为这十个任务结构分配段内存,可以在堆上分配(这个分配的内存直到操作系统结束才会
被释放,当然也可以指定一片操作系统的其它地方都用不到的内存区域,不过这样写的话
就有点外行的味道了,而符务结构数组的指针却是全局变量,存放在BSS段或者DATA段),
由于在上一步中已经分配了一个系统堆栈,那么我们这十个任务就分享这总体的堆栈区域
这里的重点就是如果定义每个任务结构数组里面的结构,可以参照LINUX的相关部分设计

4.中断处理:在第一步中已经确定了CPU进行相关的几类型的中断跳转地址,而相同类型
的中断却只有一个入口地址,这里的中断处理就会完成以几个动作:
其一:入栈操作,包括所有寄存器入栈,至于这个栈,就是在第二步中所设置的IRQ栈,
其二:屏掉所有中断,呵,这里为了简单起见,所以在处理中断时不允许再次发生中断
其三:读取中断相关的寄存器,判别是发生了什么中断,以至于跳进相关的中断处理函
数中去执行(在这里只包括两种中断,一是时钟中断,另一个是SWI中断,也就是所谓
的系统调用时需要用到的)
其四:等待中断处理完成,然后就开启中断并出栈,恢复现场,将CPU控制权交给被中断
的代码处
注意:
其一:在MIAN中必须首先确定整个系统有哪些需要处理的中断,也就是有哪些中断处理
函数,然后才编写这里的中断处理函数
其二:本操作系统不处理虚拟内存,其至连CPU异常都不处理(一切都为了简单),一旦
发生异常,系统就死机

5.对TIMER的实现,首先确定时间片,为了让系统更稳定,而且我们不需要实时功能,尽
可能让时间片设置长一点,比如我们让一个任务运行20个时钟滴答数,然后应根据系统
频率来确定每个系统滴答所占用的毫秒,这里使用5毫秒让系统定时器中断一次,那么就
需要写时钟寄存器,具体参阅芯片资料,计算下来,一个任务最大可能连续运行100毫秒
,注意:我们的操作系统不支持内核抢占,同时只支持两级中断优先级,就是只有时钟
中断的优先级高一点,其它的优先级都低一级,但是在中断处理一节中却屏掉了这个功能
因为一进入中断处理,就禁止中断,所以不管其它中断优先级有多高都没有用的,这样做
优点是简单了,但不好之处显而易见,特别在相关中断处理函数如果进入了死循环,那么
整个系统就死了,而且时间片也变得不准确了,反正都不用实时,也不需要实时钟支持嘛
至于中断优先级设置请参阅芯片资料


6.进程调度的实现,也就是do_timer函数(时钟中断处理函数),有一个全局变量指针,
指向的就是当前任务结构数组(或者链表),当时钟中断时,就进入此函数中,首先判断
任务结构体中的时间片是否用完,如未用完,就减一,然后退出中断,让CPU继续运行当
前的任结构,若用完了时间片,就重置时间片,并重新寻找任何结构数组中的下一个等待
运行的任务,若找到了,就切换至新的任务,至于如何切换,请见下一页描述,如果未找
到就切换到IDLE任务(类似于LINUX,呵呵,所有的处理就是模仿LINUX,由于本人水平太
差,所就不能自创一招),注意:为了简单,所以没有实现任务优先级,也未实现任务
休眠等,也就是说只要静态地决定了有十个任务,这十个任务就按先后顺序一个一个执行
而且每个任务都不允许结束,就是说在每个进程中的最后一句代码都必须用死循环,不然
的话系统就跑飞了),还有一点,进程不支持信号,没有休眠与唤醒操作,这个CPU就是
不停地在运行,呵呵,反正CPU又不是人,所以不需要人权的哈!!!这种调度是不是简
单得不能再简单了?????!!!!

7.串口不使用中断,这就是最大可能的降低难度,串口使用论询的方式来实现读写(当
然是阻塞的方式了哦,而且只有写,不允许读,因为读的时候需要涉及到采用中断方式,
因为轮询方式有个不好的地方,那就是正在读的时候,这里有可能当前进程的时间片用
完了,系统切换到另一个进程,这里你在PC机的串口输入的数据就丢弃了,唉,又是为
了简单嘛)

8,最后一步就是MIAN函数的最后一部分,将本进程当作IDLE进程(相当于修改任务结构
数组中的数据),开启中断,将当前进程加入一段死循环,以免它退出去。

9.编译你的BOOTLOADER,KERNEL,并烧写至FLASH,反复调试

10.至此将你的at91rm9200(或者是其它相类似的芯片)的串口接上PC机,打开超级终端,
打开板子电源,说不定你的操作系统就打印出了"hello,world"了!!!一个最简单的操作
系统就出来了

下一页是具体的功能模块实现

任务结构数组(或链表)的实现  
   
  我们的任务结构就采用链表形式吧,但其长度是限定了的,头指针是一个全局指针变量(  
  指针变量是一个无符号整型指针,其指针本身所在的地址是在BSS段,但其指向的内容是分  
  配在堆上的一片内存),分配内核内存的函数就用kmalloc吧,kmalloc函数需要自己编写  
  呵,为了简单,这个函数只接受一个参数,就是所需分配大小,这个函数做得很简单,首先  
  有一个全局针指,它在初始化时指向了整个堆的起始位置,并且固定大小,就是所谓的内核  
  堆栈,在内核堆栈之后就是用户堆栈,由于总共有十个任务,当然不包括内核本身的任务,  
  所以整个堆栈就平均分成十一部分,注意:在所有任务初始化完成之后,还有一个步骤就是  
  将内核这个任务移到用户态,相当于要将自己的任务结构的堆栈指针修改一下就行了),  
  判断大小是否超出了内核堆的可分配范围,还有一点,需要维护内核堆和其它任务的堆,  
  需要进行分块,并且有一个全局的内存使用标识,就用数组吧,简单,0表示相应的内存  
  部分未占用,1就表示占用,对应的kfree就相当于把标志置0),  
  对于内存的维护,比较复杂,为了简单,就定为4K,并且不能进行大于四K的内存申请,因为  
  大于4K之后,由于没有虚拟地址的概念,就不能实现堆上的连续分配地址,当然在栈上分配  
  是可以大于4K的,栈是由编译器和CPU所决定了的  
   
  任务结构包括:  
  1.所剩的时间片  
  2.本任务所指向的代码段内存地址,这里也就是函数入口地址  
  3.本任务所指向的数据段地址,这里的数据段被包含进了整个内核中,所以并没有用,作为保留  
  4.本任务的函数体是否存在,也就是否会被调度  
  5.本任务所使用的栈指针  
  6.本任务所使用的堆指针  
  7.本任务的标识,用0代表是IDLE,1代表是其它进程  
  8.所有寄存器的值  
  9.当前PC值,初始化时被置成了函数入口地址  
   
  首先讲解一下任务数组结构的初始化:  
  将先定义一个全局指针,然后将此指针强制转换为一个任务结构指针,并通过kmalloc函在内核所  
  占用的堆(前而讲过内核的堆的起始就是整个堆的起始)上去分配十个任务结构所占的内存,这里  
  是绝不会超过4K的  
  并且为这十个任务结构赋值,将第一个任务置为IDLE,时间片为20,代码段内存地址为main函数的  
  的地址,数据段地址忽略,函数体存在,可以被调度,栈指针指向的位置根据以下来计算:  
  假定每个给每个任务可使用的堆栈设定为64K,而整个堆的起始位置是0x20030000,那么第一个堆指  
  针所指向的就是0x20030000,栈就是0x20030000+64K的位置,第二个以后就以此类推  
  注意:在初始化任务结构之前,不允许系统使用堆,但可以使用栈,那么内核任务栈部分就分成了  
  两个,在未进行调度之前,栈就是上一页中第二步中所设的栈,那么上一页设置堆栈的时候就得注  
  意必须将堆栈空间设成十个64K再加上在本步骤使用以前的最大可能所需的栈空间  
   
   
  再讲解一下任务切换时所要做的事情:  
  进入整个中断处理入口时,会将所有寄存器推入IRQ栈之中,并把值拷贝到当前任务结构相应的字段  
  当中,并取出被中断的进程的当前PC值存入当前任务结构中的相应字段中,接下就判别中断类型,  
  以进入相应的中断处理函数,这里就会进入do_timer函数中,以下就是进入此函数之后的流程:  
  内核中还有一个全局指针,就是当前任务指针,它本身也是在系统BSS段中,它的定义如上一步中的  
  那个全局指针一样,当由系统时钟中断之后,就取出这个全局指针,上一步初始化完成之后,还会  
  把这个指针指向第一个任务结构所在位置,也就是0x20030000处,那么就取出这个任务结构中的时  
  间片字段,判断其是否为0,若为0,就进行以下的操作:  
   
  保存用户态下的栈指针至当前任务结构,保存堆指针,并将搜索一下可以被调度的任务结构,并将  
  此任务结构赋给当前任务指针,置需要进行任务切换标识,此标识同样是一个全局变量,但它是被  
  赋了初值,会放在整个系统的DATA段中,返回do_timer函数。  
   
  若不为0,就进行以下操作:  
  将时间片减一,返回do_timer函数  
   
   
  接下来判断任务切换标识,若为0,则进行以下操作:  
  不需要进行任务切换,所有寄存器出栈(这里的栈指的是IRQ栈),重新开启中断,切换到用户模式,  
  加载当前任务结构中的当前PC值字段,以退出中断处理程序  
   
  若此标识为1,则执行以下操作:  
  就需要进行任务切换,让所有寄存器出栈(这里的栈指的是IRQ栈),将当前任务结构中的所有寄  
  存器的值恢复到相应寄存器中,将用户态下的栈指针恢复至当前任务结构栈指针,将堆指针恢复至  
  当前任务结构堆指针,并把需要进行任务切换标识恢复为0,重新开启中断,切换到用户模式,任务  
  切换是通过加载PC值来实现的,也就是通过加载当前任务结构中的当前PC值字段,以退出中断处理程序  
   
  系统调用的实现  
   
  本系统是完全可以不实现系统调用的,因为没有实现内核态和用户态的保护,完全可以不实现  
  自己的C库,所有的函数都像kmalloc之类的实现一样,在内核中直接写函数原型,但为了以后  
  扩展,还是说一下系统调用,这里以malloc系统调用来实现  
   
  首先说明还有一个堆指针(前面在kmalloc时有一个堆指针,不过那个堆指针是为内核任务,中  
  断处理所提供),这里这个堆指针是用于用户态的,它在系统初始化完成之前会赋上初值,其初  
  值就是第一个任务结构所使用的堆的起始位置,也就是在内核所使用的堆加上64K的位置  
  函数库中的malloc函数实现步骤如下:  
  1.首先检测申请大小是否超出了4K,若超出4K,就返回错误  
  2.进行系统调用(这里用_syscall1,并只传递一个参数(所需分配大小)  
  系统调用函数_syscall1的实现:  
  1.将寄存器压入堆栈(这里的栈指向就是当前任务的栈)  
  2.将系统调用号1放至R0,参数放入R1  
  3.发出SWI指令以产生SWI中断(就是所说的软中断,陷阱)  
  此时系统发生中断,会进入SWI中断处理入口,下面说一下SWI入口函数的实现  
  1.取出R0的值,判断其值,进入相应的分支处理代码段  
  2.在此进入_malloc处理代码段,取出R1的值,然后再得到前面所说的当前堆指针,并申请对应数  
  据块大小,置用于内存占用标识的相应字段,将当前堆指针放入R0,移动当前堆指针,改变当前任  
  务结构的堆指针,切换到用户态,返回SWI中断系统调用_syscall1的返回处理:  
  为了简单,在从内核态返回用户态时,不再进行任务的重新调度,所以上面的步骤就相对简单  
  1.当从SWI中断返回后,系统就运行在了用户态,此时取出R0的值,并赋值给需要申请内存的指针  
  2.在用户态弹出寄存器,返回到上一层函数  
  malloc函数的返回,此时malloc函数直接返回指针就行了,整个malloc的流程就结束了,其它的系  
  统调用同这个过程类似  
   
   
   
  到此为止,这个操作系统初步实现了,但好像什么事情都不能做,如果让它支持串口中断的话,或许  
  可以做那么一点点事情,比如像单片机那样的功能,整个系统的难点就是中断处理和任务切换,在本  
  例中,由于ARM不支持像0x86那样的CPU级的保护模式,所以进行任务切换的时候,就得自己通过加载  
  PC值的方法来实现,呵,因为我想不到更好的办法,但这个办法有一个不好解决的地方,就是寄存器  
  入栈和出栈的保护,在进入中断时,必须保护寄存器,但如果需要进行重新调度,就得从中断上下文  
  切换到进程上下文中,如何从中断上下文切换到进程上下文呢??我在这里所采用的方法很笨拙:  
  1.首先让寄存器入栈  
  2.让寄存器保存至当前任务结构数组,被中断掉的进程的PC值保存至任务结构  
  3.处理timer中断  
  4.如果进行任务切换,寻找下一个可调度的进程,然后把当前任务结构指下刚搜索到  
  的任务结构,让寄存器出栈,恢复当前任务结构里的值到寄存器,恢复堆栈指针,切换到用户态,通  
  过加载当前任务结构的PC值来恢复被挂起的进程  
   
  这里在中断上下文中使用了任务结构,这在LINUX上好像是不这样用的,中断上下文和进程上下文是两  
  个不同的概念,中断上下文中不能访问进程上下文里的任务结构,我实在想不出有什么办法来实现进程  
  调度了,所以请看到我这则文章的人提出好一点的方法  
   
  欢迎对LINUX有兴趣的人加入群讨论,群号:10074203  

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

评论(0) | 阅读(996)
发表于:2006/12/30 11:58:28
标签:无标签

2

基于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所示,主要的过程如下:

300)this.width=300" align="absMiddle" border="0">
    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的启动代码之后,对系统功能程序设计会起到很大的帮助,是进行下一步程序设计的基础。

五. 参考文献:
1. 田泽.嵌入式系统开发与应用.北京.北京航空航天大学出版社.2005
2. 田泽.嵌入式系统开发与应用实验教程.北京.北京航空航天大学出版社.2004
3. 深圳英蓓特信息技术有限公司.Embest ARM实验教学系统用户手册.Version 2.01.2003
4. SAMSUNG公司.S3C44B0_datasheet.pdf.

系统分类: ARM   |    用户分类: 无分类    |    来源: 转贴

评论(0) | 阅读(3583)
发表于:2006/12/30 11:56:45
标签:无标签

3

ARM启动代码设计参考

基于ARM的芯片多数为复杂的片上系统,这种复杂系统里的多数硬件模块都是可配置的,需要由软件来设置其需要的工作状态。因此在用户的应用程序之前,需要由专门的一段代码来完成对系统的初始化。由于这类代码直接面对处理器内核和硬件控制器进行编程,一般都是用汇编语言。一般通用的内容包括:
中断向量表
初始化存储器系统
初始化堆栈
初始化有特殊要求的断口,设备
初始化用户程序执行环境
改变处理器模式
呼叫主应用程序
1. 中断向量表
ARM要求中断向量表必须放置在从0地址开始,连续8X4字节的空间内。
每当一个中断发生以后,ARM处理器便强制把PC指针置为向量表中对应中断类型的地址值。因为每个中断只占据向量表中1个字的存储空间,只能放置一条ARM指令,使程序跳转到存储器的其他地方,再执行中断处理。
中断向量表的程序实现通常如下表示:
AREA Boot ,CODE, READONLY
ENTRY
B    ResetHandler
B    UndefHandler
B    SWIHandler
B    PreAbortHandler
B    DataAbortHandler
B
B    IRQHandler
B    FIQHandler
其中关键字ENTRY是指定编译器保留这段代码,因为编译器可能会认为这是一段亢余代码而加以优化。链接的时候要确保这段代码被链接在0地址处,并且作为整个程序的入口。
2. 初始化存储器系统
(1)存储器类型和时序配置
通常Flash和SRAM同属于静态存储器类型,可以合用同一个存储器端口;而DRAM因为有动态刷新和地址线复用等特性,通常配有专用的存储器端口。
存储器端口的接口时序优化是非常重要的,这会影响到整个系统的性能。因为一般系统运行的速度瓶颈都存在于存储器访问,所以存储器访问时序应尽可能的快;而同时又要考虑到由此带来的稳定性问题。
(2)存储器地址分布
一种典型的情况是启动ROM的地址重映射。
3. 初始化堆栈
因为ARM有7种执行状态,每一种状态的堆栈指针寄存器(SP)都是独立的。因此,对程序中需要用到的每一种模式都要给SP定义一个堆栈地址。方法是改变状态寄存器内的状态位,使处理器切换到不同的状态,让后给SP赋值。注意:不要切换到User模式进行User模式的堆栈设置,因为进入User模式后就不能再操作CPSR回到别的模式了,可能会对接下去的程序执行造成影响。
这是一段堆栈初始化的代码示例,其中只定义了三种模式的SP指针:
MRS   R0,CPSR
BIC    R0,R0,#MODEMASK  安全起见,屏蔽模式位以外的其他位
ORR   R1,R0,#IRQMODE
MSR   CPSR_cxfs,R1
LDR   SP,=UndefStack

ORR   R1,R0,#FIQMODE
MSR   CPSR_cxsf,R1
LDR   SP,=FIQStack

ORR   R1,R0,#SVCMODE
MSR   CPSR_cxsf,R1
LDR   SP,=SVCStack
4. 初始化有特殊要求的端口,设备
5. 初始化应用程序执行环境
映像一开始总是存储在ROM/Flash里面的,其RO部分即可以在ROM/Flash里面执行,也可以转移到速度更快的RAM中执行;而RW和ZI这两部分是必须转移到可写的RAM里去。所谓应用程序执行环境的初始化,就是完成必要的从ROM到RAM的数据传输和内容清零。
下面是在ADS下,一种常用存储器模型的直接实现:
LDR    r0,=|Image$$RO$$Limit|      得到RW数据源的起始地址
LDR    r1,=|Image$$RW$$Base|      RW区在RAM里的执行区起始地址
LDR    r2,=|Image$$ZI$$Base|        ZI区在RAM里面的起始地址
CMP    r0,r1                      比较它们是否相等
      BEQ    %F1
0     CMP    r1,r3
      LDRCC  r2,[r0],#4
      STRCC  r2,[r1],#4
      BCC    %B0
1     LDR    r1,=|Image$$ZI$$Limit|
      MOV   r2,#0
2     CMP    r3,r1
      STRCC  r2,[r3],#4
      BCC    %B2
程序实现了RW数据的拷贝和ZI区域的清零功能。其中引用到的4个符号是由链接器第一输出的。
|Image$$RO$$Limit|:表示RO区末地址后面的地址,即RW数据源的起始地址
|Image$$RW$$Base|:RW区在RAM里的执行区起始地址,也就是编译器选项RW_Base指定的地址
|Image$$ZI$$Base|:ZI区在RAM里面的起始地址
|Image$$ZI$$Limit|:ZI区在RAM里面的结束地址后面的一个地址
程序先把ROM里|Image$$RO$$Limt|开始的RW初始数据拷贝到RAM里面|Image$$RW$$Base|开始的地址,当RAM这边的目标地址到达|Image$$ZI$$Base|后就表示RW区的结束和ZI区的开始,接下去就对这片ZI区进行清零操作,直到遇到结束地址|Image$$ZI$$Limit|
6. 改变处理器模式
因为在初始化过程中,许多操作需要在特权模式下才能进行(比如对CPSR的修改),所以要特别注意不能过早的进入用户模式。
内核级的中断使能也可以考虑在这一步进行。如果系统中另外存在一个专门的中断控制器,这么做总是安全的。
7. 呼叫主应用程序
当所有的系统初始化工作完成之后,就需要把程序流程转入主应用程序。最简单的一种情况是:
IMPORT main
B      main
直接从启动代码跳转到应用程序的主函数入口,当然主函数名字可以由用户随便定义。
在ARM ADS环境中,还另外提供了一套系统级的呼叫机制。
IMPORT __main
B     __main
__main()是编译系统提供的一个函数,负责完成库函数的初始化和初始化应用程序执行环境,最后自动跳转到main()函数。

系统分类: ARM   |    用户分类: 无分类    |    来源: 转贴

评论(0) | 阅读(1820)
发表于:2006/12/30 11:55:33
标签:无标签

2

arm开发经验(连载)

 前一段时间做了arm的一些开发,主要是编写了arm的启动软件和移植了uCOS-II到arm7。我做事情喜欢深入简出,及从最简单,最原理的方面先做一个框架,然后在这个框架里面进行补充。我还是一个很喜欢和别人讨论的人,希望有人可以给我提出意见和建议。我的这个心得很初级,都是一些基本的东西。现在拿出来和大家分享,希望在我毕业之前能给大家留一些纪念。^_^
    由于这些东西发paper实在是没有价值,但是我感觉可以作为arm开发的入门。由于我的水平和经验有限,错误也是难免的。但是如果不拿出来和大家分享,就算有错误我也发现不了,是么?呵呵。我现试试发连载的第一篇,看看有多少价值,如果大家觉得有价值,我会继续连载的。
连载一:
前言
这个文档是我学习ARM编程的总结和心得。阅读这个文档的人应当首先阅读ADS1.2的帮助文档及相关内容。这个文档不会对编译器及连接器做出详细的说明,在需要的时候会指出具体内容在相关资料的章节。同时阅读这个文档的人需要了解ARM指令集和一些ARM汇编的基本内容以及C和C++的相关编程内容。同时还需要了解ARM的流水线结构及一些基本的编程知识。同时为了方便查阅英文文档,所有的相关术语都使用英文原文

第一章 STARTUP
1 ARM的启动
一般的嵌入式系统在主程序执行之前都需要执行一些初始化的过程以创造嵌入式程序运行的环境,尤其是一些高级的嵌入式系统,由于核心芯片使用内存映射、内存保护等机制以及编程使用高级语言C,C++甚至JAVA语言,都需要先创建一个适合程序运行的硬件环境,然后初始化或者配置或者剪裁run-time library, 这些工作都必须在主程序运行前完成,所以一个startup程序或者程序组对于一个嵌入式系统来说是非常重要的。要编写startup程序,需要对编译器、链接器和汇编器的细节有一定的了解,同时对ARM芯片硬件本身的地址分配以及memory mapping机制也需要有一些了解。
2 ARM 程序的工作过程
首先由各种source file经过编译产生object文件,然后object文件经过链接生成Image文件,然后通过ICE的方法,根据描述文件的指定下载到目标板上的固态存储器指定地址当中,比如flash,EEPROM, ROM等等。在程序执行之前,根据某些描述文件,将需要读写数据的部分读出放入动态存储器比如RAM当中,然后程序从ROM开始执行。或者有时为了提高程序的运行速度,也可以将所有的程序(有一些root的部分除外,以后会提及)通过一个描述文件放入指定的RAM当中,然后程序从RAM开始执行,但是这样会耗费大量的动态存储器,所以大部分程序会取折中的方法,将需要快速运行的部分和要读写的部分放入RAM中(一般读固态存储器的过程和动态存储器的过程是一样的,但是写就不同了,所以读写的部分一定要放到RAM中),而只读的部分和对速度要求不是那么高的部分放入固态存储器。同时ARM结构的异常向量表规定放在地址为0x00000000开始的地址空间上,而一般的CPU为了提高异常相应速度,会将这个向量段remap到其他的RAM当中,所以在描述文件当中必须精确指定异常向量跳转程序的地址到remap的地方。在application程序执行前,还需要由一些文件描述application程序执行的环境。比如系统工作时钟,总线频率。现在一般嵌入式编程语言为C,C++等。如果在使用它们的时候使用的runtime-library,那么在程序执行前还需要为这些库函数初始化heap。然后ARM可能工作在不同的模式,还需要为不同的工作模式设置stack。这样,描述链接地址的文件,以及在application运行前所有的初始化程序就是startup程序组
3 STARTUP分类
这样,将startup程序所完成的功能分类。一类是链接地址描述,一类是各种初始化的程序。根据不同的应用,描述文件和初始化程序的内容以及结构和复杂程度都会不同。但是基本上,它们都必须实现以下功能。
3.1 描述文件实现功能
描述文件可以是链接命令行上简单的几个字符,也可以是一个非常复杂的文件,但是它必须完成如下功能:
; 指定程序下载的地址
; 指定程序执行的地址
3.2 初始化程序实现的功能
初始化程序根据不同的应用,其结构和复杂度也不同,但是它必须完成如下基本功能:
; 异常向量初始化
; 内存环境初始化
; 其他硬件环境初始化

ARM开发经验(二)
发表于 2005-11-27 21:53:44

注:这个连载的版权属于自控所158所有。转载的时候请注明。转载需要通过作者本人同意。

/*
*********************************************************************************************************
*                                               Programming Arm
*
*
*                             (c) Copyright 1992-2008, 西安交通大学
*                                          All Rights Reserved
*
*                                              自控研究所158
*
* 文件      : 连载二
* 版本   : V1.00
* 作者      : 潘自强
*
* 对象      : ARM7
* 模式      : ARM
* 工具      : ADS1.20
*********************************************************************************************************
*/


4 描述文件
要编写描述文件,必须知道ARM Image文件的组成及ARM Image文件执行的机理。
4.1 ARM Image的结构
一个ARM Image structure由linker在以下几个方面定义:
 组成它的regions 和 output sections
 当Image 下载的时候这些regions 和 sections 在内存中的位置
 当Image 执行时这些regions和sections在内存中的位置
4.1.1 ARM Image的组成
一个ARM Image被保存在可执行文件当中,它的层次结构可以包括Image,regions,output sections和input sections。
 一个Image由一个或多个regions组成,每个region包括一个或多个output sections
 每个output section由一个或多个input sections组成
 Input sections是一个object file中的code和data信息。
Image的结构如
下图:
1 附图: tu1.JPG (24684 字节)
NOTE Input section,output section和region的定义见ADS_LinkerGuide 3-3页。
同时Input section 有几种属性,分别为readonly,read-write,zero-initialized。分别称为RO,RW和ZI。属性来源于AREA后的attr属性。
比如CODE是RO,DATA是RW,NOINT默认为ZI,即用0值初始化,但是可以选择不进行0值初始化。ZI属性仅仅来源于SPACE, DCB, DCD, DCDU, DCQ, DCQU, DCW, 或者DCWU。由以上定义,ZI属性的包含于RW属性,它是有初始值的RW数据。又例如在C语言中,代码为RO,静态变量和全局变量是RW,ZI的。

 点击看大图

ARM开发经验(三)
发表于 2005-11-28 9:14:09
注:这个连载的版权属于自控所158所有。转载的时候请注明。转载需要通过作者本人同意。 

/*
*********************************************************************************************************
*                                               Programming Arm
*
*
*                             (c) Copyright 1992-2008, 西安交通大学
*                                          All Rights Reserved
*
*                                              自控研究所158
*
* 文件      : 连载三
* 版本   : V1.00
* 作者      : 潘自强
*
* 对象      : ARM7
* 模式      : ARM
* 工具      : ADS1.20
*********************************************************************************************************
*/

4.1.2 Image 的Load view 和 execution view
在下载的时候Image regions被放置在memory map当中,而在执行Image前,或许你需要将一些regions放置在它们执行时的地址上,并建立起ZI regions。例如,你初始化的RW数据需要从它在下载时的在ROM中的地址处移动到执行时RAM的地址处。
1 附图: tu2.jpg (640566 字节)
点击看大图
NOTE Load view 和execution view的详细定义见ADS_LinkerGuide 3-4
以上的描述包括二个内容,一是要指定各个section在load view和execution view时的地址即memory map,二是要在执行前根据这些地址进行section的初始化。
4.1.3 制定Memory map
制定memory map的方法基本上有二种,一是在link时使用命令行选项,并在程序执行前利用linker pre-define symbol使用汇编语言制定section的段初始化,二是使用scatter file。以上二种方法依应用程序的复杂度而定,一针对简单的情况,二针对复杂的情况。
ARM开发经验(四)精华帖
注:这个连载的版权属于自控所158所有。转载的时候请注明。转载需要通过作者本人同 
意。

/*
******************************************************************************
***************************
*                                               Programming Arm
*
*
*                             (c) Copyright 1992-2008, 西安交通大学
*                                          All Rights Reserved
*
*                                              自控研究所158
*
* 文件      : 连载四
* 版本   : V1.00
* 作者      : 潘自强
*
* 对象      : ARM7
* 模式      : ARM
* 工具      : ADS1.20
******************************************************************************
***************************
*/

4.1.1.1 利用linker pre-define symbol使用汇编程序
这是简单的方法,针对简单的memory map。在link时使用选项-ro, -rw, 等等指定memory map的地址。详细说明参看ADS_LinkerGuide中命令行选项说明。然后利用汇编使用pre-define symbol,来进行各种段的定位。Linker pre-define定义如下:
1 附图: tu1.jpg (22811 字节)
点击看大图
由前面对ZI的说明,Image$$RW$$Limit = Image$$ZI$$Limit。
2 附图: tu2.jpg (30577 字节)
点击看大图
这些都是linker预先定义的外部变量,在使用的时候可以用IMPORT引入。下面给出一个例子。
假设linker 选项为:-ro-base 0x40000000 -rw-base 0x40003000。程序和只读变量(const 变量)大小为0x84,这样RO section的大小为0x84 bytes。Data的大小为0x04 bytes,并且data被初始化,则RW section的大小为0x04,ZI section的大小为0x04。这样程序
在load view,地址是这样的:
0x40000000开始到地址0x40000080,是RO section部分(程序从0x40000000开始),Image$$RO$$Limit = 0x40000084.
0x40000084地址开始到地址0x40000084,是RW section部分。

在execution view,由linker的选项,各个section的地址是这样的:
RO section的地址不变。
RW section的起始地址应当为0x40003000,则Image$$RW$$Base = 0x40003000。
因为全部的0x04 bytes data被初始化,所以Image$$RW$$Limit = Image$$ZI$$Limt = 0x40003004。
现在要做的就是将RW section移到以0x40003000开始的地方,并且创造一个ZI section。
一个更通用的做法是:
首先比较Image$$RO$$Limit和mage$$RW$$Base,如果相等,说明execution view下RW section的地址和load view 下RW section的地址相同,这样,不需要移动RW section;如果不等,说明需要移动RW section 到它在execution view中的地方。然后将Image$$ZI$$Base地址到Image$$ZI$$Limt地址的内容清零。
示例代码如下:
;读入linker pre-define symbols

IMPORT |Image$$RO$$Limit|
IMPORT |Image$$RW$$Base|
IMPORT |Image$$ZI$$Base|
IMPORT |Image$$ZI$$Limit|

; .......一些其他的代码或伪指令

;R0读入section load address
LDR R0,=|Image$$RO$$Limit|
;R1读入section execution address
LDR R1,=|Image$$RW$$Base|
;R2读入execution section 后的紧跟的word address
LDR R2,=|Image$$ZI$$Base|
;检查RW section的地址在load view和execution view下
;是否相等,如果相等,就不移动RW section,直接建立
;ZI scetion
CMP R0,R1
BEQ do_zi_init

;否则就copy RW section到execution view下指定的地址
BL copy

; ......
; ......

;copy 是一个用于copy的子函数,它把从R0中的地址开始的
;section copy到R1中的地址开始的section,这个section的
;上限地址后紧跟的word address保存在R2中
copy
CMP R1,R2
LDRCC R3,[R0],#4
STRCC R3,[R1],#4
BCC copy
MOV PC,LR

; ......
; ......
;do_zi_int子函数是为创建ZI section做一些准备工作
do_zi_int
;将ZI section开始的地址装入R1
LDR R1,=|Image$$ZI$$Base|
;将ZI section结束后紧跟的word address装入R2
LDR R2,=|Image$$ZI$$Limit|
;将ZI section 需要的初始化量装入R3
MOV R3,#0
BL zi_int


; ......
; ......
;zi_int子函数用于建立并初始化ZI section,ZI section的
;开始地址储存在R1,ZI section结束后紧跟的word address
;地址储存在R2

zi_int
CMP R1,R2
STRCC R3,[R1],#4
BCC zi_int
MOV PC,LR

; ......
; ......
这个方法针对比较简单的应用,如果需要进行一个比较复杂的memory map,如下图,那么这个方法就不适用了。为了解决复杂memory map的问题
需要用到scatter load 机制。
3 附图: tu3.jpg (32473 字节)
点击看大图 

系统分类: ARM   |    用户分类: 无分类    |    来源: 转贴

评论(1) | 阅读(1141)
发表于:2006/12/30 11:54:00
标签:无标签

2

使用ADS1.2进行嵌入式软件开发(来自ARM公司)

使用ADS1.2进行嵌入式软件开发(上)

作者:ARM公司   来源:eepw.com.cn   

概述
      嵌入式应用程序通常都是在样机环境下调试与开发的,这种环境与最终产品之间并不完全相同。因此,在系统调试阶段就考虑应用程序在最终目标硬件中的运行情况是非常重要的。
     本文旨在讨论如何将一个开发/调试环境下的嵌入式应用程序转移到最终独立运行的目标系统中去,并提到了ARM ADS1.2开发工具包的一些功能特性及其在这个过程中所起到的作用。


    使用ADS开发嵌入式程序时,需要着重考虑以下几个问题:
1.与硬件相关的C语言库函数的使用;
2.某些C语言库函数使用了调试环境中的资源,要把这些使用的资源重定向到目标系统中的硬件上来;
3.可执行映象文件的存储器映射必须根据目标硬件的存储器分布进行裁剪;
4.在主程序执行前,嵌入式应用程序必须先完成系统的初始化。一个完整的初始化包括用户的启动执行代码和ADS中C库函数的初始化过程。 



图1 Semihosting的实现举例



图2 C语言库函数结构



图3 缺省的存储器映射



图4 连接器布局规则

缺省的工程项目设置
刚开始一个嵌入式应用软件开发时,ADS用户可能并不完全清楚目标硬件的一些参数指标。比如有关外设、存储器地址分布,甚至处理器类型等一些细节,可能还没有最终确定。为了在所有这些细节全部就绪前就能进行软件开发,ADS工具有一套程序构建和调试的缺省设置。了解这套缺省的工程项目设置方法,对于掌握最终的移植步骤非常有好处。


ADS1.2C语言函数库
Semihosting
在ADS的C语言函数库中,某些ANSIC的功能是由主机的调试环境来提供的,这套机制有一个专门术语叫Semihosting。Semihosting通过一组软件中断(SWI)指令来实现。如图1所示,当一个Semihosting软中断被执行时,调试系统先识别这个SWI请求,然后挂起正在运行的程序,调用Semihosting的服务,完成后再恢复原来的程序执行。因此,主机执行的任务对于程序来说是透明的。


C语言库函数结构
从概念上来讲,C语言库函数可以被分成两部分,一是ANSIC语言规范本身的一部分,一是只受某一特定ANSIC层次支持的函数,如图2所示。
其中一些ANSIC的功能是由主机调试环境调用驱动程序级的函数完成的。例如,ADS的库函数printf()把输出信息输出到调试器的控制台窗口,这个功能通过调用__sys_write()实现,__sys_write()执行了一个把字符串输出到主机控制台的Semihosting软中断服务程序。


缺省的存储器映射
如果用户在程序编译时没有指定映象的存储器映射分布,ADS将为生成的目标代码和数据分配一个缺省的存储器映射图,如图3所示。
目标印象被连接至地址0x8000,存储和执行区域都位于该地址开始的空间。RO(只读)部分放在前面,接着是RW(读写)部分,最后是ZI(零初始化)部分。
在ZI部分之上紧跟着HEAP,所以HEAP的确切地址要在连接时才能确定。
STACK的基地址是在应用程序启动时由一个Semihosting操作提供。这项Semihosting操作返回的地址值视不同调试环境而定:
ARMulator返回配置文件peripherals.ami中的设置值;缺省为0x08000000。
Multi-ICE返回的是调试器内部变量$top_of_memory的值;缺省为0x00080000。


连接器布局规则
连接器对代码和数据在存储器系统中的分配,遵循一套规则,如图4所示。
映象首先按照属性以RO-RW-ZI的次序进行排列,在同一种属性里面代码先于数据。然后连接器将输入段根据名字的字母顺序进行排列,输入段的名字与汇编代码里面的块名字指示一致(在汇编程序中用AREA关键字)。在输入段中,来自不同对象的代码和数据放置次序与在连接器命令行中指定的对象文件次序一致。
在需要灵活分配代码和数据放置位置的情况下,建议用户不要简单地依靠这些规则。后面会介绍一种如何控制代码和数据布局的机制Scatterloading。



图5 缺省的ADS初始化过程



图6 C库函数重定向



图7 scatter文件语法



图8 分散加载的简单样例

启动应用程序
大多数嵌入式系统在进入应用主程序之前有一个初始化的过程,该过程完成系统的启动和初始化功能。缺省的ADS初始化过程如图5所示。
总体上,初始化过程可以分成两部分来看:
_main负责设置运行映像存储器映射;
_rt_entry负责库函数的初始化。
_main完成代码和数据的复制,并把ZI数据区清零。这一步只有当代码和数据区在存储和运行时处于不同的存储器位置时才有意义。接着_main跳进_rt_entry,进行STACK和HEAP等的初始化。最后_rt_entry跳进应用程序的入口main()。当应用程序执行完时,_rt_entry又将控制权交还给调试器。
函数main()在ADS中有特殊的意义。当一个程序工程项目中存在main()时,连接器会把_main和_rt_entry中的初始化代码连接进来;如果没有main()函数,初始化过程就不会被连接,结果就会导致一些标准的C库函数无效。


根据目标环境裁减C库函数
缺省状态下C库函数利用Semihotsting机制来实现设备驱动的功能。但一个真正的嵌入式系统,要使用到具体的外设或硬件独立于主机环境运行。
C库函数重定向
用户可以定义自己的C语言库函数,连接器在连接时自动使用这些新的功能函数。这个过程叫做重定向C语言库函数,如图6所示。
举例来说,用户有一个I/O设备(如UART)。本来库函数fputc()是把字符输出到调试器控制窗口中去的,但用户把输出设备改成了UART端口,这样一来,所有基于fputc()函数的printf()系列函数输出都被重定向到UART端口上去了。
下面是实现fputc()重定向的一个例子:
externvoidsendchar(char*ch);
intfputc(intch,FILE*f)
{/*e.g.writeacharactertoanUART*/
chartempch=ch;
sendchar(&tempch);
returnch;

这个例子简单地将输入字符重新定向到另一个函数sendchar(),sendchar()假定是一个另外定义的串口输出函数。在这里,fputc()就好像目标硬件和标准C库函数之间的一个抽象层。


在C语言库函数中禁用Semihosting
在一个独立的嵌入式应用程序中,应该不存在SemihostingSWI操作。因此,用户必须确定在所有调用到的库函数中没有使用Semihosting。为了保证这一点,在程序中可以引进一个符号关键字_use_no_semihosting:
在C代码中,使用#prgrama #pragmaimport〈_use_no_semihosting_swi〉
在汇编程序中,使用IMPORT
IMPORT_use_no_semihosting_swi
这样,当有使用SWI机制的库函数被连接时,连接器会进行报错:
Error:Symbol_semihosting_swi_guardmultiplydefined
为了确定具体是哪一个函数,连接时打开-verbose选项。这样在结果信息输出时,该库函数上将有一个_I_use_semihosting_swi的标记。
Loadingmembersys_wxit.ofromc_a_un.1.
Definition:_sys_exit
Reference:_I_use_semihosting_swi
用户必须要把这些函数定义成自己的执行内容。
有一点需要注意,连接器只能报告库函数中被调用的Semihosting,对用户自定义函数中使用的Semihosting则不会报错。

根据目标硬件定制存储器映射
分散装载(Scatlerloading)
在实际的嵌入式系统中,ADS提供的缺省存储器映射是不能满足要求的。用户的目标硬件通常有多个存储器设备位于不同的位置,并且这些存储器设备在程序装载和运行时可能还有不同的配置。
Scattertoading可以通过一个文本文件来指定一段代码或数据在加载和运行时在存储器中的不同位置。这个文本文件scatterfile在命令行中由-scatter开关指定,例如:
armlink_scatterscat.scffilel.ofile2.0
在scatterfile中可以为每一个代码或数据区在装载和执行时指定不同的存储区域地址,Scatlertoading的存储区块可以分成二种类型:
装载区:当系统启动或加载时应用程序的存放区。
执行区:系统启动后,应用程序进行执行和数据访问的存储器区域,系统在实时运行时可以有一个或多个执行块。
映像中所有的代码和数据都有一个装载地址和运行地址(二者可能相同也可能不同,视具体情况而定)。在系统启动时,C函数库中的__main初始化代码会执行必要的复制及清零操作,使应用程序的相应代码和数据段从装载状态转入执行状态。
1.scatter文件语法
scatter文件是一个简单的文本文件,包含一些简单的语法。
My_Region0x00000x1000
{
thecontextofregion
}
每个块由一个头标题开始定义,头中至少包含块的名字和起始地址,另外还有最大长度和其他一些属性选项。块定义的内容包括在紧接的一对花括号内,依赖于具体的系统情况。
一个加载块必须至少含有一个执行块;实践中通常有多个执行块。
一个执行块必须至少含有一个代码或数据段;这些通常来自源文件或库函数等的目标文件;通配符号*可以匹配指定属性项中所有没有在文件中定义的余下部分。
2.简单分散加载样例
图8所示样例中,只有一个加载块,包含了所有的代码和数据,起始地址为0。这个加载块一共对应两个执行块。一个包含所有的RO代码和数据,执行地址与装载地址相同;同时另一个起始地址为0x10000的执行块,包含所有的RW和ZI数据。这样当系统开始启动时,从第一个执行块开始运行(执行地址等于装载地址),在执行过程中,有一段初始化代码会把装载块中的一部分代码转移到另外的执行块中。
下面是这个scatter描述文件,该文件描述了上述存储器映射方式。
LOAD_ROM0x4000

EXE_ROM0x00000x4000;Rootregion

*〈+RO〉;Allcodeandconstantdata

RAM0x100000x8000

*〈+RW,+ZI〉;Allnon-constantdata


3.在分散文件中放置对象
在大多数应用中,并不是像前例那样,简单地把所有属性都放在一起,用户需要控制特定代码和数据段的放置位置。这可以通过在scatter文件中对单个目标文件进行定义实现,而不是只简单地依靠通配符。
为了覆盖标准的连接器布局规则,我们可以使用+FIRST和+LAST分散加载指令。典型的例子是在执行块的开始处放置中断向量表格:
LOAD_ROM0x00000x4000

EXEC_ROM0x00000x4000

vectors.o〈Vect,+FIRST〉
*〈+RO〉

;moreexecregions...

在这个scatter文件中,保证了vextors.o中的Vect域被放置于地址0x0000。
4.RootRegion(根区)
根区是一个执行块,它的加载地址与执行地址是一致的。每个scatter文件至少有一个根区。分散加载有一个限制:创建执行块的代码和数据(即完成复制和清零的代码和数据)无法自行复制到另一个位置。因此,在根区中必须含有下面的部分:
_main.o,包含复制代码/数据的代码;
连接器输出变量$$Table和ZISection$$Table,包含被复制代码/数据的地址。
由于上面两个部分的属性是只读的,因此他们被*〈+RO〉通配符语法匹配。如果*〈+RO〉被用在了非根区中,则在根区中必须显式地指明另一个RO区域。
下面是一个例子:
LOAD_ROM0x00000x4000

EXE_ROM0x00000x4000;rootregion

_main.o〈+RO〉;copyingcode
*〈Region$$Tabl0e〉;RO/RWaddressestocopy
*〈ZISection$$Table〉;ZIaddressestozero

RAM0x100000x8000

*〈+RO〉;allotherROsections
*〈+RW,+ZI〉;allRWandZIsections



(待续)       在下期中将更详细介绍利用scatter文件进行存储器配置的方法,以及系统启动和初始化的过程和存储器映射变化关系。
 
上期主要介绍了基于ARM的嵌入式系统软件开发中,怎样来对必要的C库函数进行移植和重定向,以及如何根据不同的目标存储器系统进行程序编译和连接设置。本期介绍程序中的存储器分配和如何根据设置正确初始化系统。


放置堆栈和heap
Scatterloading机制提供了一种指定代码和静态数据布局的方法。下面介绍如何放置应用程序的堆栈和heap。
* _user_initial_stackheap重定向
应用程序的堆栈和heap是在C库函数初始化过程中建立起来的。可以通过重定向对应的子程序来改变堆栈和heap的位置,在ADS的库函数中,即_user_initial_stackheap()函数。
_user_initial_stackheap()可以用C或汇编来实现,它必须返回如下参数:
r0:heap基地址;
r1:堆栈基地址;
r2:heap长度限制值(需要的话);
r3:堆栈长度限制值。
当用户使用分散装载功能的时候,必须重调用_user_initial_stackheap(),否则连接器会报错:
Error: L6218E: Undefined symbol Image$$ZI$$Limit (referred from sys_stackheap.o)


*存储器模型
ADS提供了两种实时存储器模型。缺省时为one-region,应用程序的堆栈和heap位于同一个存储器区块,使用的时候相向生长,当在heap区分配一块存储器空间时需要检查堆栈指针。另一种情况是堆栈和heap使用两块独立的存储器区域。对于速度特别快的RAM,可选择只用来作堆栈使用。为了使用这种two-region模型,用户需要导入符号use_two_region_memory,heap使用需要检查heap的长度限制值。
对这两种模型来说,缺省情况下对堆栈的生长都不进行检查。用户可以在程序编译时使用 -apcs/swst 编译器选项来进行软件堆栈检查。如果使用two-region模型,必须得在执行_user_initial_stackheap时指定一个堆栈限制值。 



图9 重定向_user_initial_stackheap()



图10 基本初始化过程



图11 ROM/RAM重定向和映射



表1

系统复位和初始化
目前情况,一般假设程序从C库函数的初始化入口_main开始执行。实际上,所有的嵌入式程序在启动时都要执行一些系统级的初始化操作。在此讨论这方面的内容。
初始化过程
图10中显示了一个基于ARM的嵌入式系统的基本初始化过程。可以看到,在_main之前加入了一个复位处理模块reset handler,它在系统上电复位时立即启动。标识为$sub$$main的新代码块在进入主程序之前执行。
复位处理模块reset handler通常是一小段汇编代码,在系统复位时执行。它至少完成应用程序中使用到的所有处理器模式的堆栈初始化工作。对于含有本地存储器系统的内核(比如含cache的ARM内核),配置工作也必须在这一段初始化过程中完成。当完成系统初始化之后,通常程序会跳向_main,开始C库函数的初始化过程。
系统初始化过程一般还包括另外一些内容,中断使能等,这些大多安排在C库函数的初始化完成之后执行。$sub$$main()完成这部分功能。
向量表(vector table)
所有的ARM系统都有一张中断向量表当出现异常需要处理时,必须调用向量表。向量表一般要位于0地址处。



表2



表3



表4



表5



表6



表7



表8



表9



表10

存储器配置
*ROM/RAM重定向
当系统启动的时候,为了保证0地址处有正确的启动代码存在,需要非易失性的存储器。
一种简单的方法,就是把系统0x0000开始的一块地址分配给ROM。其缺点是,由于ROM的访问速度比RAM慢很多,当执行中断响应需要从中断向量表跳转时,会给系统性能带来损失;同时,在ROM中的向量表内容也不能被用户程序动态修改。
另外一种可行的方案如图11所示。ROM位于地址0x1000开始的地方,但是在系统复位时又被存储器控制器映射到0x0000地址处。这样当系统启动之后,在地址0x0000看到的是ROM,系统执行这块ROM中的启动代码,启动代码跳转到真正的ROM的地址,并让存储器控制器移除对ROM的地址映射。这时0x0000地址处的存储器又恢复回了RAM。__main中的代码把向量表copy到0x0000处的RAM中去,使得异常时能被正确响应。
表1为ARM汇编中执行ROM/RAM重定向和映射的一个例子。它以ARM公司的Integrator平台为基础的,该方法适用于类似ROM/RAM重定向方法的所有平台。第一条指令完成从ROM的映射地址(0x00000)到真实地址的跳转。地址标号instruct_2是ROM的真实地址(0x180004)。然后通过设置Integrator平台上的相应控制寄存器,移除ROM的地址映射。代码在系统一启动就被执行。所有关于地址重定向/映射的操作必须在C库函数初始化之前完成。


*本地存储器配置
许多ARM处理器都有片上存储器系统,如cache和紧密耦合存储器(TCM)、存储器管理单元(MMU)或存储器保护单元(MPU)。这些设备都要在系统初始化过程中正确配置,并且有一些特殊的要求需要考虑。
由前文可知,_main中的C库函数初始化代码负责程序运行时的存储器系统设置。因此,整个存储器系统本身必须得在__main之前完成初始化工作,如MMU或MPU必须在reset handler里面完成配置。
紧密耦合存储器(TCM)的初始化同样须在_main之前完成(通常在MMU/MPU之前),因为一般程序都需要把代码和数据分散装入TCM。需要注意的是当TCM被使能后,不再访问被TCM屏蔽的存储器。
关于cache的一致性问题,如果cache在_main之前使能的话,那么当_main里面进行从装载区到执行区的代码和数据拷贝时(因为在拷贝过程中指令和数据在本质上都是被当作数据处理),指令会出现在数据缓冲区。避免此问题的方法是在C库函数初始化完成后再使能cache。
*Scatter loading与存储器配置
无论是通过ROM/RAM重定向还是MMU配置的方法,如果系统在启动和运行时存储器分布不一致,scatterloading文件中的定义就要按照系统重定向后的存储器分布情况进行。
以上文ROM/RAM重定向为例:
LOAD_ROM 0x10000 0x8000
{
EXE_ROM 0x10000 0x8000
{
reset_handler.o (+RO, +FIRST)
...
}
RAM 0x0000 0x4000
{
vectors.o (+RO, +FIRST)
...
}
}
装载区LOAD_ROM被放置在0x10000处,代表了重定向之后代码和数据的装载地址。

堆栈的初始化
程序中可能用到的处理器模式,都需要定义一个堆栈指针。
在表2中,堆栈位于stack_base标识的地址中。这个符号可以是存储器系统中的一个直接地址,也可以在另外的汇编文件中定义,由scatter文件来定义分配地址。表2代码为FIQ和IRQ模式各分配了一个256字节的堆栈,用户可以用同样的方法为其他模式也分配堆栈。最简单的方法就是进入相应的模式,然后为SP寄存器指定相应的值。如果想使用软件堆栈检查,还必须指定一个堆栈长度限制值。
堆栈指针和堆栈限制的数值会作为参数自动传递到C库函数的初始化代码__user_initial_stackheap中,在__user_initial_stackheap中不应该修改这些值。
硬件初始化 $sub$$main()
一般来说,应该把所有的系统初始化代码与主应用程序分离开来,但是有几个例外,比如cache和中断的使能,需要在C库函数初始化之后执行。
表3代码显示了如何使用 $sub和 $supper 。连接器把呼叫main()的函数替换成呼叫$sub$$main(),完成cache和中断的使能,并最终跳向main()。


执行模式考虑
为主应用程序选择一个处理器执行模式非常重要,这取决于系统的初始化代码。
许多在启动过程中使用到的功能,如MMU/MPU的配置、中断的使能等,只能在特权级模式下进行。如果需要在特权极模式下运行自己的应用程序,只要在退出初始化过程之前改变到相应的模式就行了,没有其他任何问题。
如果使用user模式,必须保证所有只能在特权模式下执行的功能完成之后,才能进入user模式。因为system模式和user模式使用相同的寄存器组,reset handler应该从system模式退出,_user_initial_stackheap在system模式下完成应用程序堆栈的初始化。这样在处理器进入user模式后,所有的堆栈空间都已经被正确设置好了。

对存储器布局的进一步考虑
在scatter文件中分配硬件地址
虽然可以在一个scatter文件中描述代码和数据的分散布局,但是目标硬件中的外设寄存器,堆栈和heap配置仍然直接采用硬件地址在程序源代码中进行设置。如果把所有存储器地址相关的信息都在scatter文件中进行定义,避免在源文件中引用绝对硬件地址,对程序的工程化管理是有大好处的。
*在scatter文件中定义目标外设地址
通常外设寄存器的地址在程序文件或头文件中定义,也可以声明一个结构类型指向外设寄存器,结构的地址定位在scatter文件中完成。
举例来说,目标定时器上有2个32位的寄存器,可以用表4来映射这些寄存器。为了把结构放置在指定的存储器地址上面,创建一个新的执行区(见表5)。scatter文件便把timer_regs结构定位在了地址0x40000000。
注意,在启动过程当中这些寄存器的内容不需要清零,改变寄存器的内容可能影响系统状态。在执行区上加UNINIT属性可以防止ZI数据在初始化过程中被清零。
在scatter文件中分配堆栈和heap
在许多情况下,用scatter文件来定义堆栈和heap的地址会带来一些好处,主要有:所有的存储器分配信息集中在一个文件里;改变堆栈和heap的地址只要重新连接就行了,不需要重新编译。
*显式地放置符号
在ADS1.2环境下,这是最简单的方法。在前文中引用过2个符号stack_base和heap_base,这2个符号在汇编模块中创建,在scatter文件中各自的执行区里定位(见表6)。
表7文件中,heap基地址定位在0x20000上,堆栈基地址位于0x40000。现在heap和堆栈的位置就可以非常方便地进行编辑了。
*使用连接器产生的符号
这种方法需要在目标文件中指定好heap和堆栈的长度。这在一定程度上减弱了本节开头描述的两个优点。
首先在汇编源程序中定义heap和堆栈的长度。关键词SPACE用来保留一块存储器空间,NOINT则可以阻止清零操作(见表8)。注意在这里的源文件中并不需要地址标号。
然后这些部分就可以在scatter文件中对应的执行区里定位了(见表9)。连接器产生的符号指向每一个执行区的基地址和长度限制,这些符号可以被_user_initial_stackheap调用的重定向代码使用。在代码中使用DCD来给这些值定义更有意义的名字,可以增强代码的可读性(见表10)。
文件把heap基地址定位在0x15000,堆栈地址定位在0x4000。Heap和堆栈的位置可以通过编辑对应执行区的地址方便地改变

系统分类: ARM   |    用户分类: 无分类    |    来源: 整理

评论(2) | 阅读(3086)
发表于:2006/12/30 11:52:19
标签:无标签

1

ARM概述

ARM微处理器支持7种运行模式,分别为:
1.用户模式(usr):ARM处理器正常的程序执行状态;
2.   快速中断模式(fiq):用于高速数据传输或通道管理;
3.   外部中断模式(irq):用于通用的中断处理;
4.    管理模式(svc):操作系统使用的保护模式;
5.    数据访问终止模式(abt):当数据或指令预取终止时进入该模式,用于虚拟存储及存储保护;
6.   系统模式(sys):运行具有特权的操作系统任务;
7.   未定义指令中止模式(und):当未定义指令执行时进入该模式,可用于支持硬件协处理器的软件仿真

ARM体系结构的存储器格式有如下两种:
 大端格式:字数据的高字节存储在低地址中,字数据的低字节存放在高地址中;
 小端格式:与大端存储格式相反,高地址存放数据的高字节,低地址存放数据的低字节

 ARM寄存器总结:
  ARM有16个32位的寄存器(r0到r15)。
 r15充当程序寄存器PC,r14(link register)存储子程序的返回地址,r13存储的是堆栈地址。
ARM有一个当前程序状态寄存器:CPSR。
一些寄存器(r13,r14)在异常发生时会产生新的instances,比如IRQ处理器模式,这时处理器使用r13_irq和r14_irq
 ARM的子程序调用是很快的,因为子程序的返回地址不需要存放在堆栈中。
1、 ARM处理器共有37个寄存器,其中包括:

i. 31个通用寄存器,包括程序计数器(PC)在内。都是32位寄存器

ii. 6个状态寄存器,都是32位寄存器,但目前只使用了其中12位

2、 ARM处理器有7种不同的处理器模式,在每一种处理器模式中有一组相应的寄存器组

。任意时刻(也就是任意的处理器模式下),可见的寄存器包括15个通用寄存器(R0~

R14)、一个或两个状态寄存器及程序计数器(PC)。在所有的寄存器中,有些是各模式

共用的同一个物理寄存器,有些是各模式自己拥有的独立的物理寄存器。

3、 通用寄存器可以分为3类:未备份寄存器(R0~R7)、备份寄存器(R8~R14)和程序

计数器PC(R15)。对于每一个未备份寄存器来说,在所有的处理器模式下指的都是同一

个物理寄存器。对应备份寄存器R8~R12来说,每个寄存器对应两个不同的物理寄存器,

这使得中断处理非常简单。例如,仅仅使用R8~R14寄存器时,FIQ处理程序可以不必执行

保存和恢复中断现场的指令,从而使中断处理过程非常迅速。对于备份寄存器R13和R14

来说,每个寄存器对应6个不同的物理寄存器,其中的一个是用户模式和系统模式共用的

,另外的5个对应于其他5种处理器模式。

4、 每一种异常模式拥有自己的物理的R13。应用程序初始化该R13,使其指向该异常模

式专用的栈地址。当进入异常模式时,可以将需要使用的寄存器保存在R13所指的栈中;

当退出异常处理程序时,将保存在R13所指的栈中的寄存器值弹出。这样就使异常处理程

序不会破坏被其中断程序的运行现场。

5、 寄存器R14又被称为连接寄存器(Link Register,LR),在ARM体系中具有下面两种

特殊的作用:

i. 每一种处理器模式自己的物理R14中存放当前子程序的返回地址。当通过BL或BLX指令

调用子程序时,R14被设置成该子程序的返回地址。在子程序中,当把R14的值复制到程

序计数器PC中时,子程序即返回。

ii. 当异常中断发生时,该异常模式特定的物理R14被设置成该异常模式将要返回的地址

,对于有些异常模式,R14的值可能与将返回的地址有一个常数的偏移量。具体的返回方

式与子程序返回方式基本相同。

6、 由于ARM采用了流水线机制,当正确读取了PC的值时,该值为当前指令地址值加8个

字节。也就是说,对于ARM指令集来说,PC指向当前指令的下两条指令的地址,由于ARM

指令是字节对齐的,PC值得第0位和第1位总为0。

7、 对于ARM版本3以及更低的版本,写入R15的地址值的bits[1:0]被忽略,对于ARM版本

4以及更高的版本,程序必须保证写入R15寄存器的地址值的bits[1:0]为0b00;否则会产

生不可预知的结果。对于Thumb指令集来说,指令是半字对齐的。处理器将忽略bit[0]。

还有一些指令对于R15的用法有一些特殊的要求。比如,指令BX利用bit[0]来确定是ARM

指令,还是Thumb指令。

8、 指令mov pc, pc将程序跳转到当前指令下面第2条指令处执行。类似的指令还有

add pc, pc, #0

9、 每一种处理器模式下都有一个专用的物理状态寄存器,称为SPSR(备份程序状态寄

存器)。当特定的异常中断发生时,这个寄存器用于存放当前程序状态寄存器的内容。

在异常中断程序退出时,可以用SPSR中保存的值来恢复CPSR。

10、 由于用户模式和系统模式不是异常中断模式,所以它们没有SPSR。当在用户模式或

系统模式中 访问SPSR,将会产生不可预知的结果。

系统分类: ARM   |    用户分类: 无分类    |    来源: 转贴

评论(0) | 阅读(669)
发表于:2006/12/30 11:51:07
标签:无标签

1

ARM开发软件及实用工具介绍

ARM 开发软件及实用工具介绍

更新日期:2006.07.16  www.mcu123.net
   

以下软件均可在www.mcu123.com/down下载中心下载(HTTP高速)



一、编译器介绍
1、ADS1.2

    ADS是ARM公司的集成开发环境软件,他的功能非常强大。他的前身是SDT,SDT是ARM公司几年前的开发环境软件,目前SDT早已经不再升级。ADS包括了四个模块分别是:SIMULATOR;C 编译器;实时调试器;应用函数库。
ADS的编译器调试器较SDT都有了非常大的改观, ADS1.2提供完整的WINDOWS界面开发环境。C编译器效率极高,支持c 以及c++,使工程师可以很方便的使用C语言进行开发。提供软件模拟仿真功能,使没有Emulators的学习者也能够熟悉ARM的指令系统。配合FFT-ICE使用,ADS1.2提供强大的实时调试跟踪功能,片内运行情况尽在掌握。ADS1.2需要硬件支持才能发挥强大功能。目前支持的硬件调试器有Multi-ICE以及兼容Multi-ICE的调试工具如FFT-ICE。而简易下载电缆不能支持ADS1.2

版本:ADS1.2

软件大小:130M

本站下载地址:  http://www.mcu123.com/down/view.asp?id=39

2、ARM REALVIEW DEVELOPER SUITE
    RealView Developer Suite工具是ARM公司是推出的新一代ARM集成开发工具。支持所有ARM 系列核,并与众多第三方实时操作系统及工具商合作简化开发流程。开发工具包含以下组件:

· 完全优化的ISO C/C++编译器
· C++ 标准模板库
· 强大的宏编译器
· 支持代码和数据复杂存储器布局的连接器
· 可选 GUI调试器
· 基于命令行的符号调试器(armsd)
· 指令集仿真器
· 生成无格式二进制工具、Intel 32位和Motorola 32位ROM映像代码的指令集模拟工具
· 库创建工具
· 内容丰富的在线文档

官方网址:

版本:ARM.RealView.Developer2.2

软件大小:500M

本站下载地址:  http://www.mcu123.com/down/view.asp?id=52

3、IAR EWARM

    Embedded Workbench for ARM 是IAR Systems 公司为ARM 微处理器开发的一个集成开发环境(下面简称IAR EWARM)。比较其他的ARM 开发环境,IAR EWARM 具有入门容易、使用方便和代码紧凑等特点。

IAR Systems 公司目前推出的最新版本是IAR Embedded Workbench for ARM version 4.30。这里提供的是32k 代码限制、但没有时间限制的Kickstart版。

EWARM 中包含一个全软件的模拟程序(simulator)。用户不需要任何硬件支持就可以模拟各种ARM 内核、外部设备甚至中断的软件运行环境。从中可以了解和评估IAR EWARM 的功能和使用方法。

IAR EWARM 的主要特点如下:
1、高度优化的IAR ARM C/C++ Compiler
2、IAR ARM Assembler
3、一个通用的IAR XLINK Linker
4、IAR XAR 和XLIB 建库程序和IAR DLIB C/C++运行库
5、功能强大的编辑器
6、项目管理器
7、命令行实用程序
8、IAR C-SPY 调试器(先进的高级语言调试器

版本:IAR EWARM 4.40a

软件大小:93M

本站下载地址:  http://www.mcu123.com/down/view.asp?id=53

4、KEIL ARM-MDKARM

   
  
Keil uVision调试器可以帮助用户准确地调试ARM器件的片内外围功能(I2C、CAN、UART、SPI、中断、I/O口、A/D转换器、D/A转换器和PWM模块等功能)。ULINK USB-JTAG转换器将PC机的USB端口与用户的目标硬件相连(通过JTAG或OCD),使用户可在目标硬件上调试代码。通过使用Keil uVision IDE/调试器和ULINK USB-JTAG转换器,用户可以很方便地编辑、下载和在实际的目标硬件上测试嵌入的程序。

    支持PhilipsSamsungAtmelAnalog DevicesSharpST等众多厂商ARM7内核的ARM微控制器。

高效工程管理的uVision3集成开发环境
* Project/Target/Group/File的重叠管理模式,并可逐级设置;
* 高度智能彩色语法显示;
* 支持编辑状态的断点设置,并在仿真状态下有效。
高速ARM指令/外设模拟器
* 高效模拟算法缩短大型软件的模拟时间;
* 软件模拟进程中允许建立外部输入信号;
* 独特的工具窗口,可快速查看寄存器和方便配置外设;
* 支持C调试描述语言,可建立与实际硬件高度吻合的仿真平台;
* 支持简单/条件/逻辑表达式/存储区读写/地址范围等断点。
多种流行编译工具选择
* Keil高效率C编译器;
* ARM公司的ADS/RealView 编译器;
* GNU GCC 编译器;
* 后续厂商的编译器。

官方网址:www.keil.com

版本: MDKARM V3.10A

软件大小:53M

本站下载地址:  http://www.mcu123.com/down/view.asp?id=38

5、WINARM (GCCARM)
  WINARM  是一个免费的开发工具。
  WinARM 可以在 http://www.siwawi.arubi.uni-kl.de/avr_projects/arm_projects/ 下载得到,里面除了包含 C/C++ 编译器——GCC,汇编、连接器——Binutils,调试器——GDB等工具,也包括了通过 GDB 使用 Wiggler JTAG 的软件——OCDRemote。所以,所需要的工具都包括在了这个 WinARM 发行版中,就可以比较省心了。

官方发布网址:http://www.siwawi.arubi.uni-kl.de/avr_projects/arm_projects/

软件大小:90M

下载地址:WinARM 20060606 zip-Archive here
  
          http://www.siwawi.arubi.uni-kl.de/avr_projects/arm_projects/WinARM-20060606.zip

 WINARM简易使用说明:http:/www.mcu123.com/product/lpc214x/winarm_user_cn.pdf

 


更多软件添加中。。。。敬请关注!

二、相关开发工具介绍

1、简易的JTAG制作:
  
原理图及PCB(protel99)下载

本站下载: http://www.mcu123.com/software/wiggler/wiggler.rar



2、H-JTAG调试代理
 
   H-JTAG是一个免费的ARM调试代理,程序没有任何限制,希望能给ARM的爱好者提供一
个简单实用的学习工具。欢迎访问作者的BLOG: http://twentyone.blogchina.com
blog提供了安装程序的下载,同时,也提供了常见的Q&A.

  0. 支持ARM7/ARM9,支持自动检测和手动指定内核
  1. 使用RDI接口,支持SDT,ADS,REALVIEW和IAR
  2. 支持ADS1.2和SDT2.51和RREAL VIEW
  3. 支持个硬件断点或者数量不限的软件断点
  4. 支持ARM/THUMB模式
  5. 支持LITTLEENDIAN & BIGENDIAN
  6. 支持SEMIHOSTING
  7. 支持WIGGLER SDTJTAG和自定义接口

3、LPC2000系列ISP工具
   a、飞利普官方网站下载工具
   b、

4、Macraigor Systems Wiggler 调试工具

5、J-LINK

    IAR公司的J-LINK是一款小巧的ARM JTAG硬件调试器,它是通过USB口与PC机相连。
IAR的J-LINK与该公司的嵌入式开发平台紧密结合,且完全支持即插即用。

主要特征:

1)支持所有ARM7和ARM9 
2)下载速度高达600KB/sec
3)  无需电源供电,可直接通过USB取电
4)JTAG速度是8MHz
5)自动辨速
6)监控所有的JTAG管脚信号,测量电压
7)20pin标准JTAG连接器
8)配带USB口和20pin插槽
9)  支持Windows 2000 和Windows XP

更强的几点:
1、支持ADS,KEIL,IAR,WINARM,RV等几乎所有开发环境;并且可以和IAR无缝连接
2、支持FLASH软件断点,可以设置2个以上断点(无限个断点),极大的提高调试效率;
3、带J-Link TCP/IP server,允许通过TCP/ IP 网络使用J-Link
4、支持几乎所有ARM7,ARM9,暂时不支持XSCALE;
* ARM7TDMI(Rev 1)
* ARM7TDMI(Rev 3)
* ARM7TDMI-S(Rev 4)
* ARM720T
* ARM920T
* ARM926EJ-S
* ARM946E-S

官方网站:http://www.segger.com/

6、U-LINK
  
概述
    ARM7 TDMI结构的Keil开发套件采用最新设计的超豪华uVision3集成开发环境,内嵌C编译器/汇编器/工程管理器/调试器等功能功能模块,是一款稳定/可靠/高效的开发工具,适用于不同层次的的用户,完全满足从专业的应用开发工程师到初学嵌入式软件开发的学生的所有使用要求。类似于8051的智能平台将大幅度缩短您的开发周期,各大半导体厂商的所有ARM型号将逐一得到全面支持。

    Keil uVision调试器可以帮助用户准确地调试ARM器件的片内外围功能(I2C、CAN、UART、SPI、中断、I/O口、A/D转换器、D/A转换器和PWM模块等功能)。ULINK USB-JTAG转换器将PC机的USB端口与用户的目标硬件相连(通过JTAG或OCD),使用户可在目标硬件上调试代码。通过使用Keil uVision IDE/调试器和ULINK USB-JTAG转换器,用户可以很方便地编辑、下载和在实际的目标硬件上测试嵌入的程序。

    支持PhilipsSamsungAtmelAnalog DevicesSharpST等众多厂商ARM7内核的ARM微控制器。
    特点
 高效工程管理的uVision3集成开发环境
* Project/Target/Group/File的重叠管理模式,并可逐级设置;
* 高度智能彩色语法显示;
* 支持编辑状态的断点设置,并在仿真状态下有效。
 高速ARM指令/外设模拟器
* 高效模拟算法缩短大型软件的模拟时间;
* 软件模拟进程中允许建立外部输入信号;
* 独特的工具窗口,可快速查看寄存器和方便配置外设;
* 支持C调试描述语言,可建立与实际硬件高度吻合的仿真平台;
* 支持简单/条件/逻辑表达式/存储区读写/地址范围等断点。
 多种流行编译工具选择
* Keil高效率C编译器;
* ARM公司的ADS/RealView 编译器;
* GNU GCC 编译器;
* 后续厂商的编译器。
 JTAG仿真器ULINK
* USB通讯接口高速下载用户代码;
* 存储区域/寄存器查看;
* 快速单步程序运行;
* 多种程序断点;
* 片内Flash编程 。
 支持器件

官方网站:http://www.keil.com/ulink

系统分类: ARM   |    用户分类: 无分类    |    来源: 转贴

评论(2) | 阅读(1556)
Total , Page /