Avatar

该项目平台是TI的locosto,ARM7处理器,nor boot,128+32
DM技术方案提供商为bitfone公司。

在没有加入DM方案以前,系统的划分如下

name            origin    length
———————-  ——–  ———  ——–  —-  ——–
BOOT_MEM                00000000   00100000
D_MEM0                  00400000   00300000
P_MEM0                  06000000   00210000
P_MEM1                  06210000   001f0000
P_MEM2                  06400000   00100000
P_MEM3                  06500000   00180000
R_MEM                   06680000   00380000
FFS_MEM                 06a00000   00600000
S_MEM                   08000000   00037560
S_MEM_JPEG_JUMPTABLE    0804fb60   00000250
S_MEM_JUMPTABLE         0804fdb0   00000250
S_ROM                   08050000   00030000

根据FOTA的实现原理,我们需要划分出Bootloader部分,存放差分包和FOTA标志部分,最后修改后的系统情况如下:
name            origin    length
———————-  ——–  ———  ——–  —-  ——–
BOOT_MEM                00000000   00100000
D_MEM0                  00400000   00300000
P_DMBOOT                06000000   00020000
P_MEM0                  06020000   00210000
P_MEM1                  06230000   001d0000
P_MEM2                  06400000   00030000
P_MEM3                  06430000   00370000
P_DMBIN                 067a0000   00060000
R_MEM                   06800000   00200000
FFS_MEM                 06a00000   00600000
S_MEM                   08000000   00037560
S_MEM_JPEG_JUMPTABLE    0804fb60   00000250
S_MEM_JUMPTABLE         0804fdb0   00000250
S_ROM                   08050000   00030000
FOTA需要的空间是以前空余的代码段,文件系统部分是没有改变的。FOTA的标志部分因为空间原因放在差分包的最后一个block中。
#define DM_FLAG_ADDR        (0×067a0000+0×60000-5)

#define DM_NEED_UPDATE_FLAG     (DM_FLAG_ADDR)
#define DM_REPORT_FLAG     (DM_NEED_UPDATE_FLAG+1)
#define DM_SUCCESSED_FLAG     (DM_REPORT_FLAG+1)
#define DM_VALIDATION_PHASE_FLAG     (DM_SUCCESSED_FLAG+1)
#define DM_UPDATE_PHASE_FLAG     (DM_VALIDATION_PHASE_FLAG+1)
Read the rest of this post »

Tagged with: , , .
Avatar

一般一个ARM系统的boot过程是这样的。首先ARM在上电后直接跳转到0地址,在这个地址上存放的是系统的启动代码。系统开始执行时一般会先设置DPLL和clock,然后进行片选,接着将需要放到ram中代码手动copy到ram中,设置EMI等系统必要参数,设置ARM各个模式的系统堆栈,切回SVC模式进行操作系统初始化。
以下是一个实际的基于ARM平台的一个bootloader示例。
编译环境:ADSv1.2
Read the rest of this post »

Tagged with: , .
Avatar

在Nucleus中timer的创建函数是NU_Create_Timer,其定义是

#define         NU_Create_Timer                 TMS_Create_Timer

通过其参数我们就可以看出如何设置一个timer。

STATUS  TMS_Create_Timer(NU_TIMER *timer_ptr, CHAR *name,
VOID (*expiration_routine)(UNSIGNED), UNSIGNED id,
UNSIGNED initial_time, UNSIGNED reschedule_time, OPTION enable)

timer -> tm_actual_timer.tm_timer_type =    TM_APPL_TIMER;
CSC_Place_On_List(&TMD_Created_Timers_List, &(timer -> tm_created));
TMD_Total_Timers++;

创建一个timer是很容易看懂的,我们主要分析timer时间到了之后的系统流程。

在TI平台中使用一个硬件晶振产生中断来作为系统的定时器,每次中断间隔时间作为操作系统的一个tick,也就是一个时间片。当中断发生时也就是产生了一个irq,在以前的irq流程中分析中我们可以知道每个irq都有一个自己的处理函数。在ti的平台的这个函数中我们可以发现如下代码

TMT_Timer_Interrupt();

这个函数就是Nucleus timer的接口函数,在这个函数中会检查是否有timer的时间到了,如果有timer active需要进行对应的操作,该函数会激活timer的HISR,这个HISR是在Nucleus初始化时TMI_Initialize函数中创建的。

status =  TCCE_Create_HISR((NU_HISR *) &TMD_HISR, "SYSTEM H",
TMC_Timer_HISR, (OPTION) TMD_HISR_Priority,
TMD_HISR_Stack_Ptr, TMD_HISR_Stack_Size);

在这个HISR的处理函数中,使用timer函数创建的timer的最主要的代码是

/* Determine if the task timer has expired.  */
if (TMD_Timer_State == TM_EXPIRED)

/* Resume the timer task.  */
TMC_Timer_Expiration();

在TMC_Timer_Expiration函数中

id =                  app_timer -> tm_expiration_id;
expiration_routine =  app_timer -> tm_expiration_routine;
//app_timer的tm_expiration_id和tm_expiration_routine就是创建函数TMS_Create_Timer传入的参数。
if (!done)
{

/* Determine which type of timer has expired.  */
if (type == TM_APPL_TIMER)
//类型为TM_APPL_TIMER,也就是task调用timer函数创建的类型。
//调用创建传入的timer超时时需要调用的函数。
/* Call application timer's expiration function.  */
(*(expiration_routine)) (id);
else
//这里是Nucleus进行task的时间片处理的部分。
/* Call the task timeout function in the thread control
function.  */
TCC_Task_Timeout((NU_TASK *) pointer);
}

基本上Nucleus的timer主要流程就是这些了。

Avatar

在Nucleus的等待事件函数EVC_Retrieve_Events中有这么一段函数

if (suspend)
{

/* Suspension is selected.  */

/* Increment the number of tasks waiting.  */
event_group -> ev_tasks_waiting++;

/* Setup the suspend block and suspend the calling task.  */
suspend_ptr =  &suspend_block;
suspend_ptr -> ev_event_group =              event_group;
suspend_ptr -> ev_suspend_link.cs_next =     NU_NULL;
suspend_ptr -> ev_suspend_link.cs_previous = NU_NULL;
task =                            (TC_TCB *) TCT_Current_Thread();
suspend_ptr -> ev_suspended_task =           task;
suspend_ptr -> ev_requested_events =         requested_events;
suspend_ptr -> ev_operation =                operation;

/* Link the suspend block into the list of suspended tasks on this
event group.  */
CSC_Place_On_List((CS_NODE **)
&(event_group -> ev_suspension_list),
&(suspend_ptr -> ev_suspend_link));

/* Finally, suspend the calling task. Note that the suspension call
automatically clears the protection on the event group.  */
TCC_Suspend_Task((NU_TASK *) task, NU_EVENT_SUSPEND,
EVC_Cleanup, suspend_ptr, suspend);

/* Pickup the return status and the actual retrieved events.  */
status =             suspend_ptr -> ev_return_status;
*retrieved_events =  suspend_ptr -> ev_actual_events;

}
else
..........

我们看到当task因为该事件而阻塞的代码

/* Finally, suspend the calling task. Note that the suspension call
automatically clears the protection on the event group.  */
TCC_Suspend_Task((NU_TASK *) task, NU_EVENT_SUSPEND,
EVC_Cleanup, suspend_ptr, suspend);

执行后紧接着的就是将等待事件的状态和接受到消息的变量进行了赋值,这是为什么呢,这样正确吗?
原来是这样的。当TCC_Suspend_Task被调用后当前task被挂起,这个函数以后的代码已经不会再接着执行了。等到这个task恢复时说明这个事件已经等到了,这个事件的值是在EVC_Set_Events函数中被设置的。

if (compare)
{

/* Decrement the number of tasks waiting counter.  */
event_group -> ev_tasks_waiting--;

/* Determine if consumption is requested.  */
if (suspend_ptr -> ev_operation & EV_CONSUME)

/* Keep track of the event flags to consume.  */
consume =  consume | suspend_ptr -> ev_requested_events;

/* Remove the first suspended block from the list.  */
CSC_Remove_From_List((CS_NODE **)
&(event_group -> ev_suspension_list),
&(suspend_ptr -> ev_suspend_link));

/* Setup the appropriate return value.  */
suspend_ptr -> ev_return_status =  NU_SUCCESS;
suspend_ptr -> ev_actual_events =
event_group -> ev_current_events;

/* Resume the suspended task.  */
preempt = preempt |
TCC_Resume_Task((NU_TASK *) suspend_ptr -> ev_suspended_task,
NU_EVENT_SUSPEND);

}

可以看到设置的返回值就是EVC_Retrieve_Events函数中的返回值。

Avatar

Nucleus Task的中断与恢复

以下是以TI的源代码进行的分析
一般来说最有可能中断Task的就是irq中断了。在ARM下到有中断发生时,ARM会自动切换到IRQ模式,此时ARM的寄存器已经是IRQ模式下的寄存器了。

INT_IRQ
STMDB   sp!,{r0-r5}                 ; Save a1-a4 on temporary IRQ stack
MRS     a1,spsr                     ; check for the IRQ bug:
TST     a1,#080h                    ; if the I - flag is set,
BNE     IRQBUG                      ; then postpone execution of this IRQ
SUB     r4,lr,#4                    ; Save IRQ's lr (return address)
注意r4的内容lr-4(被中断的PC,-4是因为ARM放入的是下一条指令+4)
BL      _TCT_Interrupt_Context_Save ; Call context save routine
BL      _IQ_IRQ_isr             ; Call  int. service routine
B       _TCT_Interrupt_Context_Restore

;BUG correction 2nd part  ------------------
IRQBUG: LDMFD  sp!,{r0-r5}                  ; return from interrup. Fix for locosto reset problem. We have pushed 6 regs and popping 4.
SUBS   pc,r14,#4
;BUG correction 2nd part end  --------------

以上就是一个irq的完整的处理过程了,我们慢慢来分析。 Read the rest of this post »

Avatar

Task的创建与调度

Nucleus的系统入口函数是

VOID  INC_Initialize(VOID  *first_available_memory)
{

。。。。。。。。
/* 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();
}

Application_Initialize(first_available_memory);这个函数是留给平台用来初始化系统框架和创建task用的。
Task的创建一般如下

if ( NU_Create_Task (&TaskTable[Handle].TaskCB.TCB, Name, os_TaskEntry,
Handle, (VOID *)NULL,
(void*)(TaskTable[Handle].Stack+1), (ULONG) StackSize, (UCHAR) Priority,
#ifndef _TARGET_
10,
#else
0,
#endif
NU_PREEMPT, NU_NO_START) != NU_SUCCESS )
#define         NU_Resume_Task                  TCCE_Create_Task

最关键的实现是 Read the rest of this post »

Avatar

Linux 中的 barrier

#define preempt_disable() \
do { \
inc_preempt_count(); \
barrier(); \
} while (0)

#define barrier() __asm__ __volatile__("": : :"memory")

#ifndef barrier
# define barrier() __memory_barrier()
#endif

memory_barrier 存在的原因是因为CPU的乱序执行技术。在操作系统层面上,我们有多任务,其中一个好处就是在等待磁盘等比较慢的设备反应的时候可以切换到其他任务执行, 而不是一直在那里干等浪费时间,同样,在 CPU 级别,从内存取一个数据也是非常慢的,为了避免空闲等待,可以在这个时候去分派其他指令的执行,就有可能造成乱序了,不过 CPU 最后会把得到的结果按照原来的顺序排列再返回回来,所以如果没有副作用的话,从外面是看不出来 CPU 有没有乱序执行的。 Read the rest of this post »

Tagged with: , .
Next Page »