IMX6ULL裸机启动,IMX6ULL运行FreeRTOS,源代码分析


裸机启动部分

imx6ull中根据运行代码位置的不同RAM/flash/SD卡,上电会先运行片上ROM中的代码,搬运的数据大小是不同的,代码也不是存储在0地址处的。如下图,比如从sd中运行代码,代码IVToffset大小1k,initloadSize为4k。 实质上应用程序前红色框中的大小有3k,可以配置启动信息,入口地址,初始化设备,基本的时钟,DDR要先初始化才能使用吧。具体的可以参考其他资料如百问网的imx6ull手册 韦老师的裸机代码中,写好的程序是调用mkimage给app程序加上特有的头部信息,配合其开发的烧录工具100ask_imx6ull_flashing_tool可以下载到ram运行(*.imx文件)。如果想下载sd中,需要在imx文件前面加上1k的偏移(image vector table offset)

1mkimage -n ./tools/imximage.cfg.cfgtmp -T imximage -e 0x80200000 -d led.bin led.imx
2// -e 0x80200000程序会被搬运到的位置

其他的开发板如,原子的imxdownload工具中直接固定了加载地址为0X87800000,链接脚本中也是这个地址,如不地址不相同,就会牵扯到代码重定位的问题。

但也有可能有人设置的就是不一样的地址,照样能跑下去,这就要看start.s中的跳转指令是

1    ldr sp, =main       /* 跳转到main函数 		 */
2    b main              /* 跳转到main函数 		 */

链接地址和加载地址不一样的情况: 如果是第一行的话就无法运行,地址相关代码 如果第二行的话就代码可以部分运行,比如打印定义好的全局变量就不正常了,data区没有搬运到相应的链接地址去。

IMX6ULL运行FreeRTOS

imx6ull有官网的skd,里面已经有一些例子。 第一步: SDK_2.2_MCIM6ULL/boards/evkmcimx6ull/demo_apps/hello_world/armgcc中直接运行 ./build_ddr_debug.sh

1SDK_2.2_MCIM6ULL/boards/evkmcimx6ull/demo_apps/hello_world/armgcc# ./build_ddr_debug.sh

第二步:

1mkimage -n ./tools/imximage.cfg.cfgtmp -T imximage -e 0x80002000 -d led.bin led.imx

注意:-e 地址 为 0x80002000

第三步: 利用100ask_imx6ull_flashing_tool下载到imx6ull上面运行就会看到串口打印的

1hello world.

买到板子之后不久就一直想在imx6ULL上面跑rtos,结果也没找到啥教程,有人说cortexa7的可以参看a9的移植,无奈还是懵,后来想看看FreeRTOS源码之后在动手,结果发现简单看了源码还是菜到没头绪。 中间玩玩裸机程序,突然有两天就想研究一下,imx上程序从编译好到下载到板子上是怎么运行的,为啥我编译的sdk代码不可以用,后面就发现rtos的demo代码可能启动位置不对,根据连接脚本中的信息链接地址为0x80002000 SDK_2.2_MCIM6ULL/boards/evkmcimx6ull/rtos_examples 目录中有些例子,只要编译后,加上imx头部信息,就可以愉快的在imx6ull上面跑FreeRTOS了, 后续看看我的板子怎么能在FreeRTOS基础上跑lwip

代码重定位

关于代码重定位。

API总览

@startmindmap
+ FreeRTOS
++ Task Creation
+++_ xTaskCreate
+++_ xTaskCreateStatic
+++_ vTaskDelete
+++_ xTaskGetStaticBuffers

++ Task Control
+++_ vTaskDelay
+++_ vTaskDelayUntil
+++_ xTaskDelayUntil
+++_ uxTaskPriorityGet
+++_ uxTaskPriorityGetFromISR
+++_ uxTaskBasePriorityGet
+++_ uxTaskBasePriorityGetFromISR
+++_ vTaskPrioritySet
+++_ vTaskSuspend
+++_ vTaskResume
+++_ xTaskResumFromISR
+++_ xTaskAbortDelay

++ Task Utilities
+++_ uxTaskGetSystemState
+++_ vTaskGetInfo
+++_ xTaskGetApplicationTaskTag
+++_ xTaskGetCurrentTaskHandle
+++_ xTaskGetHandle
+++_ xTaskGetIdleTaskHandle
+++_ uxTaskGetStackHighWaterMark
+++_ eTaskGetState
+++_ pcTaskGetName
+++_ xTaskGetTickCount
+++_ xTaskGetTickCountFromISR
+++_ xTaskGetSchedulerState
+++_ uxTaskGetNumberOfTasks
+++_ vTaskList
+++_ vTaskStartTrace
+++_ ulTaskEndTrace
+++_ vTaskGetRunTimeStats
+++_ vTaskSetApplicationTaskTag
+++_ xTaskCallApplicationTaskHook
+++_ vTaskSetThreadLocalStoragePointer
+++_ pvTaskGetThreadLocalStoragePointer
+++_ vTaskSetTimeoutState
+++_ xTaskCheckForTimeOut

++ RTOS Kernel Control
+++_ taskYIELD Colors
+++_ taskENTER_CRITICAL
+++_ taskEXIT_CRITICAL
+++_ taskENTER_CRITICAL_FROM_ISR
+++_ taskEXIT_CRITICAL_FROM_ISR
+++_ taskDISABLE_INTERRUPTS
+++_ taskENABBLE_INTERRUPTS
+++_ vTaskStartScheduler
+++_ vTaskEndScheduler
+++_ vTaskSuspendAll
+++_ vTaskResumeAll
+++_ vTaskStepTick
+++_ xTaskCatchUpTicks

++ Direct To Task Notifications
+++_ xTaskNotifyGive/xTaskNotifyGiveIndexed
+++_ vTaskNotifyGiveFromISR
+++_ ulTaskNotifyTake
+++_ xTaskNotify
+++_ xTaskNotifyAndQuery
+++_ xTaskNotifyAndQueryFromISR
+++_ xTaskNotifyFromISR
+++_ xTaskNotifyWait
+++_ xTaskNotifyStateClear
+++_ ulTaskNotifyValueClear

++ Queues
+++_ xQueueCreate
+++_ xQueueCreateStatic
+++_ xQueueDelete
+++_ xQueueSend
+++_ xQueueSendFromISR
+++_ xQueueSendToBack
+++_ xQueueSendToBackFromISR
+++_ xQueueSendToFront
+++_ xQueueSendToFrontFromISR
+++_ xQueueReceive
+++_ xQueueReceiveFromISR
+++_ uxQueueMessagesWaiting
+++_ uxQueueMessagesWaitingFromISR
+++_ uxQueueSpacesAvailable
+++_ xQueueReset
+++_ xQueueOverwrite
+++_ xQueueOverwriteFromISR
+++_ xQueuePeek
+++_ xQueuePeekFromISR
+++_ vQueueAddToRegistry
+++_ vQueueUnregisterQueue
+++_ pcQueueGetName
+++_ xQueueIsQueueFullFromISR
+++_ xQueueIsQueueEmptyFormISR
+++_ xQueueGetStaticBuffers
+++_ xQueue
+++_ xQueue
+++_ xQueue
+++_ xQueued
+++_ xQueuedd

++ Queue Sets
+++_ xQueueCreateSet
+++_ xQueueAddToSet
+++_ xQueueRemoveFromSet
+++_ xQueueSelectFromSet
+++_ xQueueSelectFromSetFromISR

-- Stream Buffers
---_ xStreamBufferCreate/xStreamBufferCreateWithCallback
---_ xStreamBufferCreateStatic/xStreamBufferCreateStaticWithCallback
---_ xStreamBufferSend
---_ xStreamBufferSendFromISR
---_ xStreamBufferReceive
---_ xStreamBufferReceiveFromISR
---_ xStreamBufferDelete
---_ xStreamBufferBytesAvailable
---_ xStreamBufferSpacesAvailable
---_ xStreamBufferSetTriggerLevel
---_ xStreamBufferReset
---_ xStreamBufferResetFromISR
---_ xStreamBufferIsEmpty
---_ xStreamBufferIsFull
---_ xStreamBufferGetStaticBuffers
---_ uxStreamBufferGetStreamBufferNotificationIndex
---_ xStreamBufferSetStreamBufferNotificationIndex
---_ xStreamBatchingBufferCreate
---_ xStreamBatchingBufferCreateStatic

-- Message Buffers
---_ xMessageBufferCreate
---_ xMessageBufferCreateStatic
---_ xMessageBufferSend
---_ xMessageBufferSendFromISR
---_ xMessageBufferReceive
---_ xMessageBufferReceiveFromISR
---_ xMessageBufferReset
---_ xMessageBufferResetFromISR
---_ xMessageBufferIsEmpty
---_ xMessageBufferIsFull
---_ xMessageBufferGetStaticBuffers

-- Semaphore / Mutexes
---_ xSemaphoreCreateBinary
---_ xSemaphoreCreateBinaryStatic
---_ vSemaphoreCreateBinary
---_ xSemaphoreCreateCounting
---_ xSemaphoreCreateCountingStatic
---_ xSemaphoreCreateMutex
---_ xSemaphoreCreateMutexStatic
---_ xSemaphoreCreateRecursiveMutex
---_ xSemaphoreCreateRecursiveMutexStatic
---_ vSemaphoreDelete
---_ xSemaphoreGetMutexHolder
---_ xSemaphoreGetCount
---_ xSemaphoreTake
---_ xSemaphoreTakeFromISR
---_ xSemaphoreTakeRecursive
---_ xSemaphoreGive
---_ xSemaphoreGiveRecursive
---_ xSemaphoreGiveFromISR
---_ xSemaphoreGetStaticBuffer

-- Software Timers
---_ xTimerCreate
---_ xTimerCreateStatic
---_ xTimerIsTimerActive
---_ xTimerStart
---_ xTimerStop
---_ xTimerChangePeriod
---_ xTimerDelete
---_ xTimerReset
---_ xTimerStartFromISR
---_ xTimerStopFromISR
---_ xTimerChangePeriodFromISR
---_ xTimerResetFromISR
---_ pvTimerGetTimerID
---_ vTimerSetReloadMode
---_ vTimerSetTimerID
---_ xTimerGetTimerDaemonTaskHandle
---_ xTimerPendFunctionCall
---_ xTimerPendFunctionCallFromISR
---_ xTimerGetName
---_ xTimerGetPeriod
---_ xTimerGetExpiryTime
---_ xTimerGetReloadMode

-- Event Groups
---_ xEventGroupCreate
---_ xEventGroupCreatxeStatic
---_ vEventGroupDelte
---_ xEventGroupWaitBits
---_ xEventGroupSetBits
---_ xEventGroupSetBitsFromISR
---_ xEventGroupClearBits
---_ xEventGroupClearBitsFromISR
---_ xEventGroupGetBits
---_ xEventGroupSync
---_ xEventGroupGetStaticBuffer

-- MPU
---_ xTaskCreateRestricted
---_ xTaskCreateRestrictedStatic
---_ vTaskAllocateMPURegions
---_ portSWITCH_TO_USER_MODE
-- Co-routines
---_ xCoRoutineCreate
---_ crDELAY
---_ crQUEUE_SEND
---_ crQUEUE_RECEIVE
---_ crQUEUE_SEND_FROM_ISR
---_ crQUEUE_RECEIVE_FROM_ISR
---_ vCoRoutineSchedule
---_ vCoRoutineResetState
@endmindmap

FreeRTOS源代码分析

任务切换

@startuml
start
fork
    :Irq Handler;

    split
    :other iar func;
    split again
    :timer tick func;
    note right
    FreeRTOS_Tick_Handler
        xTaskIncrementTick(超时任务,从delay,event list删除,加入ready list)
            如果 xTickCount 溢出,累加 xNumOfOverflows
            delay到期的任务高于当前优先级,时间片轮转,xYieldPending=true(其他任务抢占需要调度)
            ulPortYieldRequired = true
    end note
    end split

    if (ulPortYieldRequired == true) then (false)
    :irq return;
    else (yes)
    :save_context_and_switch;
    note right
    bl vTaskSwitchContextConst (vTaskSwitchContext)
        uxSchedulerSuspended != flase 挂起 os 不允许调度
        xYieldPending = 0 一个标志位吧,其他需要调度的时候置1
        设置新的 pxCurrentTCB,(软件,clz方式找到最高优先级)
        就可以恢复当前task上下文,完成任务切换
    end note
    endif
fork again
    :FreeRTOS_SWI_Handler;
    :save_context
    bl vTaskSwitchContextConst
    restroe_context;
end merge

stop
@enduml

vTaskDelay

@startuml
start
:vTaskDelay;
:vTaskSuspendAll;
  note right
  ++uxSchedulerSuspended
  end note
if (xTicksToDelay > 0) then (yes)
  :prvAddCurrentTaskToDelayedList;
  note right
  设置delay到期时间,更新 xNextTaskUnblockTime
  end note
  :xTaskResumeAll;
  note right
  --uxSchedulerSuspended, 为0往下继续处理
  xPendingReadyList 处理pending list中task,加入ready list
  当有任务大于当前优先级,需要任务切换 
  设置 xYieldPending=true,更新 xNextTaskUnblockTime
  补偿系统tick,uxPendedTicks
    xAlreadyYielded = xTaskIncrementTick()
    如果已经有其他task请求切换了,当前就无需再请求切换了
    xYieldPending 会在 vTaskSwitchContext 设置 false
  end note
else
  :xAlreadyYielded = false;
endif

if (xAlreadyYielded = false) then (yes)
  :portYIELD_WITHIN_API;
  note right
  delay参数为0,强制执行调度
  portYIELD
    __asm volatile ( "SWI 0" )
  end note
else (no)
endif

stop
@enduml

xQueueGenericReceive


@startuml
start

:xQueueGenericReceive;

repeat
:taskENTER_CRITICAL();
if (uxMessagesWaiting >0) then(yes)
    note right
    队列里的元素数量
    end note
    :记录读数据的位置,receive数据会出队,peek只是读数据;
    :prvCopyDataFromQueue();
    note right
    读的索引后移一个uxItemSize,如果到tail,再指向head
    copy一个uxItemSize的数据到pvBuffer中
    end note
    if (xJustPeeking == pdFALSE) then(yes)
        :当前为receive模式,读了数据之后就更新读指针,相当于出队操作;
        :当前queue有互斥锁,指向当前的运行task pxCurrentTCB->uxMutexesHeld++;
        :判断当前queue的 xTasksWaitingToSend 不为空的话;
        :xTaskRemoveFromEventList();
        note left
        移除发送队列,os未挂起就加入readylist,挂起加加入pendingreadlist
        优先级大的话,设置task调度标志
        end note
    else
        :当前模式可以一直读队首的数据,不会出队;
        :恢复上面记录的数据读位置;
        :判断当前queue的 xTasksWaitingToReceive 不为空的话;
        :xTaskRemoveFromEventList(), task是否加入readlist的处理,是否切换task等;
    endif
    :taskEXIT_CRITICAL();
    :return pdPASS;
    stop
else (队列为空)
    if (xTicksToWait==0) then (等待时间为0)
        :taskEXIT_CRITICAL();
        :return errQUEUE_EMPTY;
        stop
    elseif (xEntryTimeSet == pdFALSE) then(等待时间不为0)
        :vTaskSetTimeOutState();
        note right
        xEntryTimeSet = true
        记录 xNumOfOverflows xTickCount 为后面判断delay做准备
        end note
    endif
endif
:taskEXIT_CRITICAL();
:;
:vTaskSuspendAll();
:prvLockQueue(), rx tx加互斥锁;
if ( xTaskCheckForTimeOut()== pdFALSE) then (yes)
    note right
    根据xTimeOut pxTicksToWait判断是否延时时间到
    延时时间到返回true,否则返回false,并更新vTaskSetTimeOutState( pxTimeOut );
    end note
    :当前wait时间没有超时;
    if (队列是否为空) then (yes)
        :互斥锁,进入临界区,修改task优先级,优先级继承机制;
        note left
        vTaskPriorityInherit()
        持有锁的taskA优先级低于当前task优先级,调整taskA的优先级
        锁的持有者可能为null,意味中当前在isr上下文
        end note
        :vTaskPlaceOnEventList;
        note right
            insert event list
            加入delay list中
        end note
        :prvUnlockQueue();
        if (xTaskResumeAll() == false) then (yes)
            :调用 svc 切换task;
        endif
    else
        :prvUnlockQueue();
        :xTaskResumeAll();
    endif
else
    :当前时间已经超时,等待结束了,有数据就返回数据,没有就返回空;
    :prvUnlockQueue();
    :xTaskResumeAll();
    if (队列 uxMessagesWaiting 是否为空) then (yes)
        :return errQUEUE_EMPTY;
        stop
    else
        :非空的话,继续下一个循环;
    endif
endif

repeat while ()


@enduml

xQueueGenericSend

@startuml
start

:xQueueGenericSend;

repeat
:taskENTER_CRITICAL();
if (队列有空闲 || 覆盖写) then(yes)
    note right
    队列里的元素数量
    end note
    :记录读数据的位置,receive数据会出队,peek只是读数据;
    :xYieldRequired = prvCopyDataToQueue();
    note left
        1. uxItemSize = 0可能表示互斥锁,需要调整恢复task优先级 xTaskPriorityDisinherit
        2. queueSEND_TO_BACK,pcWriteTo+uxItemSize
        3. queueSEND_TO_FRONT 先写后调整位置pcReadFrom-uxItemSize pcReadFrom指向的是空
        4. queueOVERWRITE,有点坑,只能用在队列长度为1的场景中(无法执行,否则队列中就会存在无效的值)
    end note
    if (configUSE_QUEUE_SETS) then (yes)
        :prvNotifyQueueSetContainer;
        note left
        1. prvCopyDataToQueue,这里把当前queue放入set中
        队列集上有等待接收的task,xTaskRemoveFromEventList
        唤醒的task高于当前task优先级,则抢占运行
        2. set为null场景,判读是否有等待接收task,逻辑同上      
        end note
    else
        :没有配置队列集;
        if (xTasksWaitingToReceive 非空) then (yes)
            :xTaskRemoveFromEventList;
            :如果高优先级task,则抢占运行;
        else if (xYieldRequired = true) then
            note left
                只有互斥锁的时候才有可能到达这里,因为优先级继承
                发送数据的时候会恢复优先级
            end note
            :抢占;
        endif
    endif
    :taskEXIT_CRITICAL();
    :return pdPASS;
    stop
else (队列已满)
    if (xTicksToWait==0) then (等待时间为0)
        :taskEXIT_CRITICAL();
        :return errQUEUE_EMPTY;
        stop
    elseif (xEntryTimeSet == pdFALSE) then(等待时间不为0)
        :vTaskSetTimeOutState();
        note right
        xEntryTimeSet = true
        记录时间戳 xNumOfOverflows xTickCount 为后面判断delay做准备
        end note
    endif
endif
:taskEXIT_CRITICAL();
:;


:vTaskSuspendAll();
:prvLockQueue(), rx tx加互斥锁;
if ( xTaskCheckForTimeOut()== pdFALSE) then (yes)
    note right
    根据xTimeOut pxTicksToWait判断是否延时时间到
    延时时间到返回true,否则返回false,并更新vTaskSetTimeOutState( pxTimeOut );
    end note
    :当前wait时间没有超时;
    if (队列是否已满) then (yes)
        :互斥锁,进入临界区,修改task优先级,优先级继承机制;
        
        :vTaskPlaceOnEventList;
        note left
        加入待发送队列 xTasksWaitingToSend,xEventListItem
        从当前优先级队列移除,加入 delaylist, xStateListItem
        end note

        :prvUnlockQueue();
        if (xTaskResumeAll() == false) then (yes)
            :调用 svc 切换task;
        endif
    else
        :prvUnlockQueue();
        :xTaskResumeAll();
    endif
else
    :当前时间已经超时,等待结束了;
    :prvUnlockQueue();
    :xTaskResumeAll();
    :return errQUEUE_FULL;
    note right #yellow
    为啥这里直接返回,不判断队列是否已满
    有空闲的话,进行下一次循环呢??
    end note
    stop
endif

repeat while ()


@enduml

参考

PlantUML 语言参考指引
PlantUML 快速入门指南
FreeRTOS开发者文档