RTOSコード読み(FreeRTOS)

FreeRTOSの方も気になったので読んでみた。

### ディスパッチャ

xPortPendSVHandler for ARM_CM3

mrs r0, psp

割り込み時点のpspの値をr0にロードしている。pspはプロセス(ユーザ)スタックポインタである。

isb

instruction barrier.

ldr r3, pxCurrentTCBConst
ldr r2, [r3]

現在の実行中のタスクのアドレスをr2に入れる。なお、pxCurrentTCBConst: .word pxCurrentTCBという一行があり、pxCurrentTCBConstは変数pxCurrentTCBへのアドレスを保持する。

stmdb r0!, {r4-r11}

レジスタの保存。stmdbはstore multiple命令。dbはdecrement beforeの略で、pspが入ったr0を減算しながらr4-r11をストアする。push命令はstmdb sp!と同義。pspを使うのでpushが使えないのだろう。

https://developer.arm.com/documentation/dui0489/e/arm-and-thumb-instructions/memory-access-instructions/push-and-pop

str r0, [r2]    // r0 = psp

FreeRTOSのTCB-TaskControlBlockは先頭にアドレス(sp)を保存する領域を確保しているので、そこにr0を登録する。

stmdb sp!, {r3, r14}

スタックポインタにr3とr14(lr)を登録している。理由はあとでわかる。

mov r0, %0
msr basepri, r0
(%0: configMAX_SYSCALL_INTERRUPT_PRIORITY)

割り込みを無効にする。

bl vTaskSwitchContext

C言語の関数をコールしている。

taskSELECT_HIGHEST_PRIORITY_TASK
=> listGET_OWNER_OF_NEXT_ENTRY(
    pxCurrentTCB,
    pxReadyTaskLists[ uxTopPriority]
);

設計思想がまだ読めておらず、defineだらけでコードを追いにくいがpxCurrentTCBが更新される。

mov r0, #0
msr basepri, r0

enable interrupt

ldmia sp!, {r3, r14}

spからr3とr14を復元する。spにr13とr14を保存したのは、単にC言語の関数をコールするからだった。r3は&pxCurrentTCBConstのアドレスが入っている。r14は割り込みから抜けるときの値(EXC_RETURN)が入っている。それ以外は使わないので、保存するのはこの2つだけでよい。

ldr r1, [r3]    // r1 = *(&pxCurrentTCBConst)
ldr r0, [r1]    // r0 = *(TCB*) // get psp

r1に現在のタスクへのポインタを、 r0にその先頭=スタックポインタを格納する。

ldmia r0!, {r4-r11}
msr psp, r0

タスクのスタックポインタからレジスタの値を復元し、pspにセットする。

isb
bx r14

割り込みから抜ける。

T-KernelはMSP/PSPの使い分けをしていない。FreeRTOSは使い分けている。

List

List_tの双方向リスト。レディーキューの実装に使われている。

typedef struct xLIST {
    uint32_t        uxNumberOfItems;
    ListItem_t*     pxIndex;
    MiniListItem_t  xListEnd;
} List_t;

まあまあ歴史があるからなのか、命名則がキモイ。pxIndexは数字かと思いきや、何かの要素を指すらしい。

struct xLIST_ITEM {
    TickType_t          xItemValue;
    struct xLIST_ITEM   *pxNext;
    struct xLIST_ITEM   *pxPrevious;
    void                *pvOwner;        
    struct xLIST        *pxContainer;
};
typedef struct xLIST_ITEM ListItem_t;  

恐れることはない、ただの双方向リストだ。ちなみにMiniListItem_tはownerとcontainerを除いたアイテムだ。

リストにアクセスするマクロ関数が定義されている。

#define listSET_LIST_ITEM_OWNER( pxListItem, pxOwner )    ( ( pxListItem )->pvOwner = ( void * ) ( pxOwner ) )

それくらいDefineしなくていいのに、と思う気も。

気になるものがあるとすればこれ。

#define listGET_HEAD_ENTRY( pxList )                      ( ( ( pxList )->xListEnd ).pxNext )

リストの先頭はxListEndの次にある。

あとこれ。

#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList )

名前の通りなのだが、pxIndexpxIndex->pxNextに更新して、その親をpxTCBに代入する。何に使うんだろう。

あとは普通の挿入とか削除とか。vListInsertxItemValueでソートされた状態で挿入される。vListInsertEndという関数もある。

xTaskCreate(...)
=> prvAddNewTaskToReadyList(pxNewTCB);
=> prvAddTaskToReadyList(pxNewTCB);
=> listINSERT_END(
    &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ),
    &( ( pxTCB )->xStateListItem )
);

こんな感じ。

Timer

xTaskIncrementTick()
=> xTickCount++
=> if( xConstTickCount >= xNextTaskUnblockTime )
=> if( ! listLIST_IS_EMPTY( pxDelayedTaskList ))
=> pxTCB = listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList );
=> listREMOVE_ITEM( &( pxTCB->xStateListItem ) );
=> prvAddTaskToReadyList( pxTCB );