完整手机启动过程
因为主流的feature phone都使用的是Nucleus Plus作为手机的操作系统,同时我自己也所接触的feature phone平台也是基于nucleus Plus的操作系统,所以这里我就以基于Nucleus 操作系统的phone的系统启动过程做个剖析.
Nucleus操作系统简述
Nucleus操作系统是一个单进程,多任务的实时操作系统,由美国ATI(Accelerated Technology Inc.)公司出品
关于该操作系统更加详细和深入的分析,请查看相关资料.
Nucleus操作系统启动过程
第一部分,硬件部分
要理解整个操作系统的启动过程,必须先要了解硬件的上电过程、Flash的存储结构、image编译的bootloader、code section、data section 等的分布.
1>Flash的存储结构
Feature phone上的存储器通常是被封装过的.比如:32M bit/16M bit指的是4M byte的ROM和2M byte的RAM. 系统根据片选信号对Flash进行相应的读\写操作.
下面是一个例子:
比如:cs0的时候访问RAM,cs3的时候访问ROM.
Start | End | ||||||
DATA MEMORY (RAM) | 0x00400000 | 0x00600000 | 2M BYTE | CS0: PSRAM => RAM,addr from 0x0040 0000 to 0x01FF FFFF | |||
... | ... | ||||||
Program memory[ROM] | 0x06000000 | 0x063C0000 | 4M BYTE | CS3: Nor flash => ROM,addr from 0x0600 0000 to 0x07FF FFFF | |||
0x063C0000 | 0x06400000 | FMS |
a.0x063C0000~0x06400000这块存储区域对应的是FMS[flash management system],主要用来保存系统中的一些设置选项等内容.FMS可以当成一个内部的文件系统来对待.
b.0x06000000~0x063C0000这块区域,主要是代码段,数据段的存储区域,以及const数据存储区.
关于const存储区,这里再展开讲一下:
做过feature phone开发的兄弟都知道在feature phone上,所有的本地资源:铃音,图片,字库等都是以const类型保存在flash上的.为啥子没有放在RAM中?第一RAM太贵,第二RAM,因为RAM贵所以没有那么多空间给这些资源使用.
在嵌入式系统上代码的执行有三种方式:
a>完全映射(fully shadowed)。嵌入式系统程序运行时,将所有的代码从非易失存储器(Flash、ROM等)复制到RAM中运行;
b>按需分页(demand paging)。只复制部分代码到RAM中。这种方法对RAM中的页进行导入/导出管理,如果访问位于虚存中但不在物理RAM中会产生页错误,这时才将代码和数据映射到RAM中;
c>eXecute In Place (XIP)。在系统启动时,不将代码复制到RAM,而是直接在非易失性存储位置执行;
首先,传统方式都是数据从flash上读取,然后放到RAM中,CPU从RAM中获取指令,然后解码后执行.
但是在Nor flash上可以采用XIP[eXecute in place]芯片内执行:
CPU直接从Flash上取指令,然后解码执行, 但是这样效率是要比从RAM中做同样的操作要效率低一些.但是这个并不代表Nand flash不能实现XIP功能.
更详细的信息,问google.
2>image编译的过程
首先一个image包括两部分:RO data和code data.
RO data就像上面讲的是通过XIP的方式来执行.但是CPU怎么知道去哪里找数据?那么Flash的地址就很重要了,这些都是在make file里面写死的.
Code data也一样,数据,代码的地址都是写死的.保证CPU能找到需要执行的数据.
通过上面的方式,连接器把所有的数据连接成为一个可执行文件.
3>硬件上电过程
下面我们先看看硬件上电后13MHz时钟的产生顺序:
从上图中我们可以看到有四部分:
1>插入电池;
2>按下开机键;
3>ABB;
4>DBB;
a.当手机插入主电池,32KHz始终开始激活之后,feature phone的部分系统就开始启动.
b.当开机键按下之后,ABB的FSM[有限状态机]会根据开机条件,进行开机操作.
c.在Trespwon时间间隔之后,FSM释放系统复位,同时驱动ON_nOFF信号为高电平.最后FMS激活system clock,然后驱动clk_en信号到逻辑高电平当Tclk_en时序复位操作释放之后.
从上图中可以看到有三个平行状态机,为了满足时序之间的合理延迟,系统时钟必须满足下面的条件:DCXO_Clk_en信号要在Sysclk_en信号之前释放, Sysclk_en信号要在Clk13_en信号之前释放.
PS:
在系统中不止有32kHz,13MHz的时钟信号,还有其它的时钟信号,具体要根据硬件的电器特性来分析.不同的硬件模块工作的时钟信号不同.
比如32KHz范围内的有:keyboard/DRP/PWL[pulse width light]/LPG[light pulse generator]
13MHz范围内的有:LCD/PWT[pulse width tones]/I2C/TPU/MCSI/PRRM[protected resource reset management]/GEA[GPRS encryption algorithm]
相关时钟模块介绍:
DCXO:Digitally-Controlled Crystal Oscillator
ULPD: Ultra low-Power Device.
第二部分,软件部分
从软件层面来看,系统的启动过程如下:
start
|
|
板级初始化 # int.s
|
|
操作系统初始化 # int.c
|
|
应用程序初始化
|
|
while(TRUE)
{循环调度}
1>板级初始化, int.s[汇编代码]
首先我们先看看一些汇编指令:
/* Directives that define sections */ .bss symbol, size in bytes [, alignment ] #Reserves size in bytes in the .bss (uninitialized data) section. .data #assebles into the .data(initialized data) section .sect "section name" #assembles into a named(initialized) section .text #assembles into the .text(executable code) section .usect "section name",size in bytes[,alignment] #reserves size in bytes in a named(uninitialized)section /* Directives that change the instruction type */ .state16 #begins assembling 16-bit instructions .state32 #begins assembling 32-bit instructions(default) /* Directives that initialize constants(data and memory) */ .field value[,size] #initializes a field of size bits(1-32)with value. /* Directives that format the output listing */ /* Directives that reference other files */ /* Directives that enable conditional assembly */ /* Directives that define symbols at assembly time */ /* Miscellaneous directives */ 32-bit instruction set summary B[cond] addr #Branch, PC =addr,Branch to the specified address. The assembler calculates the offset.
Boot程序调用 INT_Initialize(void)函数[init.s文件中],该函数处理和硬件相关的初始化操作以及访问处理器中断向量表相关的服务.
那么 INT_Initialize(void)都做了哪些事情呢?
a.针对板子上的external memory[MCP]设置片选,比如:
CS0: PSRAM => RAM,addr from 0x0040 0000 to 0x01FF FFFF
CS3: Nor flash => ROM,addr from 0x0600 0000 to 0x07FF FFFF
b.配置一些特殊的GPIO控制寄存器
; Configure CNTL_ARM_CLK register with reset value: DPLL is used to
; generate ARM clock with division factor of 1.
c.配置ARM时钟相关的控制寄存器
d.配置DPLL控制寄存器
e.配置系统堆栈
f.初始化和系统timer控制HISR相关的数据结构
g.调用INC_Initialize(VOID *first_available_memory)
2>操作系统初始化INC_Initialize(), int.c
这个函数是系统的主要初始化函数,所有的组建都在这里被初始化. 当系统初始化完成后Application_Initialize()函数被调用,当所有的初始化完成后,TCT_Schedule()函数被调用,开始调度Task.
VOID INC_Initialize(VOID *first_available_memory) { /* Indicate that initialization is starting. */ INC_Initialize_State = INC_START_INITIALIZE; /* Call release information function. */ RLC_Release_Information(); /* Call license information function. */ LIC_License_Information(); /* Initialize the Error handling (ER) component. */ ERI_Initialize(); /* Initialize the History (HI) component. */ HII_Initialize(); /* Initialize the Thread Control (TC) component. */ TCI_Initialize(); /* Initialize the Mailbox (MB) component. */ MBI_Initialize(); /* Initialize the Queue (QU) component. */ QUI_Initialize(); /* Initialize the Pipe (PI) component. */ PII_Initialize(); /* Initialize the Semaphore (SM) component. */ SMI_Initialize(); /* Initialize the Event Group (EV) component. */ EVI_Initialize(); /* Initialize the Partition memory (PM) component. */ PMI_Initialize(); /* Initialize the Dynamic memory (DM) component. */ DMI_Initialize(); /* Initialize the Timer (TM) component. */ TMI_Initialize(); /* Initialize the I/O Driver (IO) component. */ IOI_Initialize(); /* Invoke the application-supplied initialization function. */ Application_Initialize(first_available_memory); /* Indicate that initialization is finished. */ INC_Initialize_State = INC_END_INITIALIZE; /* Start scheduling threads of execution. */ TCT_Schedule(); }
3>应用程序初始化
Application_Initialize()函数,主要实现平台业务相关的初始化,当该函数执行完毕后所有的task都进入enable状态.
TCT_Schedule函数紧接着被调用.void Application_Initialize (void *first_available_memory) { //1.configure all DMA channel; //2.arm_power_on(); //3.FMS and FFS initialization gp_init_target(); /* Perform gp_init_external_devices() first than cust_init_layer1(), We need to init uart first because the cust_init_layer1 will output some trace messages to uart during the init process. */ //Initialize UARTs and external devices,This needs to be done after Layer1 has reset the VEGA. //1.initialize the I2C //2.Turn on DRP and make VRMMC[voltage regulator MMC] enable //3.SIM Main Initialization gp_init_external_devices(); //1.platform initialize[create partition pools,create memory pools, initialize the routing table] //2.Create all entities[initialize the lcd\fms\kpd\spi\i2c,initialize the tracer] //3.Start all tasks entities start_frame(); //Load and boot the DSP,Initialize shared memory and L1 data structures cust_init_layer1(); //read abb pin data,check the power on cause l1b_check_power_one(); //reset and enable the interrupt[uart/TDMA/GPIO/DMA/ARM] gp_unmask_interrupts(); }
/* This function waits for a thread to become ready. Once a thread */ /* is ready, this function initiates a transfer of control to that */ /* thread. */ VOID TCT_Schedule()
4>循环调度
到目前为止,整个平台已经随着OS启动起来.task之间的通讯、中断的响应都会通过pf_task_entry进行处理:
pf_task_entry(USHORT TaskHandle, ULONG Value )
{
while (1){ pf_handle_message (TaskHandle, &Msg);}
}