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
が使えないのだろう。
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 )
名前の通りなのだが、pxIndex
をpxIndex->pxNext
に更新して、その親をpxTCB
に代入する。何に使うんだろう。
あとは普通の挿入とか削除とか。vListInsert
はxItemValue
でソートされた状態で挿入される。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 );