目次
前
RTOS移植
i8049は、ROM容量2kバイト、SRAM容量128バイトなので
RTOS(Real Time OperationSystem)を入れられると判断し
μITRON互換のリアルタイムモニタを移植しました。
タスクの状態遷移は、次のように決定。
システムコールは、4種に限定。
- RSM_TSK
- SUS_TSK
- SLP_TSK
- WAI_TSK
このRTOSは、自作RTOSのUSO(ユーソ)を手本にしました。
USOでは、READYであるタスクをスキャンし、順次
CPUを使わせるラウンドロビン型を採用。
タスクの状態遷移は、RTOSに一任しています。
ただし、タスクが無限ループを構成しないという
条件を追加して、ラウンドロビンでタスク状態を
スキャンします。
USOでの時間待ちは、10msの整数倍をシステムコールで
指定。無限ループなしという制約を入れたので、時間
待ちで無限ループの代用としています。
各タスクの状態は、3バイトの領域で管理します。
図で、着色ビットには、1が格納されています。
RDYQのビット位置で、1になっていれば、そのタスクは
READY状態にあります。
SUSQのビット位置で、1になっていれば、そのタスクは
SUSPEND状態にあります。
WAIQのビット位置で、1になっていれば、そのタスクは
WAIT状態にあります。
1バイトで各状態を管理するので、最大タスク数は8。
また、タスクIDは、次のように割り当てます。
TSK0 equ 01h
TSK1 equ 02h
TSK2 equ 04h
TSK3 equ 08h
TSK4 equ 10h
TSK5 equ 20h
TSK6 equ 40h
TSK7 equ 80h
WAIT状態では、待ち時間を管理するためにカウント値が必要。
各タスクには、1バイトのカウント値用領域に割り当てます。
TCB0C equ 70h
TCB1C equ 71h
TCB2C equ 72h
TCB3C equ 73h
TCB4C equ 74h
TCB5C equ 75h
TCB6C equ 76h
TCB7C equ 77h
待ち行列とカウント値を格納する領域を確保したので
タスクの初期状態を、サブルーチンで次のように指定
できるようになりました。
INIT_MON:
; clear each que
clr a
mov R0,#RDYQ
mov @R0,a
mov R0,#SUSQ
mov @R0,a
mov R0,#WAIQ
mov @R0,a
; set READY que
mov R0,#RDYQ
mov a,@R0
orl a,#TSK0 ; TASK0 READY
orl a,#TSK2 ; TASK2 READY
orl a,#TSK3 ; TASK3 READY
mov @R0,a
; set SUSPEND que
mov R1,#SUSQ
mov a,@R1
orl a,#TSK1 ; TASK1 SUSPEND
mov @R1,a
; set WAIT que
mov a,@R0
orl a,#TSK4 ; TASK4 WAIT
mov @R0,a
; store WAIT counter
mov R1,#TCB4C
mov @R1,#128
; exit
ret
どのタスクにCPUを割当てして、使わせるのかは
次のループで実現。
MAIN:
; timer handling
mov R0,#TFLAG
mov a,@R0
jz MAINE
MAINT:
; clear flag
mov @R0,#0
; check WAIT task
call MTIM
MAINE:
; set pointer
mov R0,#RDYQ
mov R1,#TSKID
MAIN0:
; set TASK ID
mov @R1,#TSK0
; get READY que
mov a,@R0
; judge
xrl a,@R1
jb0 MAIN1
; do TASK0
call PTSK0
MAIN1:
; set TASK ID
mov @R1,#TSK1
; get READY que
mov a,@R0
; judge
xrl a,@R1
jb1 MAIN2
; do TASK1
call PTSK1
MAIN2:
; set TASK ID
mov @R1,#TSK2
; get READY que
mov a,@R0
; judge
xrl a,@R1
jb2 MAIN3
; do TASK2
call PTSK2
MAIN3:
; set TASK ID
mov @R1,#TSK3
; get READY que
mov a,@R0
; judge
xrl a,@R1
jb3 MAIN4
; do TASK3
call PTSK3
MAIN4:
; set TASK ID
mov @R1,#TSK4
; get READY que
mov a,@R0
; judge
xrl a,@R1
jb4 MAIN5
; do TASK4
call PTSK4
MAIN5:
; set TASK ID
mov @R1,#TSK5
; get READY que
mov a,@R0
; judge
xrl a,@R1
jb5 MAIN6
; do TASK5
call PTSK5
MAIN6:
; set TASK ID
mov @R1,#TSK6
; get READY que
mov a,@R0
; judge
xrl a,@R1
jb6 MAIN7
; do TASK6
call PTSK6
MAIN7:
; set TASK ID
mov @R1,#TSK7
; get READY que
mov a,@R0
; judge
xrl a,@R1
jb7 MAIN8
; do TASK7
call PTSK7
MAIN8:
jmp MAIN
RDYQとTSKIDのビットごとの排他的論理和を
求めて、該当ビットが'0'になっていれば
そのタスクはREADY状態なので、CPUを使う
権利があると判定します。
CPUを使う権利をもっていれば、READYからRUN
の状態にします。
RTOSは、タスクをサブルーチンとしてコールする
ので、タスクがCPUを使って仕事するのと等価です。
RDYQとTSKIDのビットごとの排他的論理和を
求めると、次のようになります。
この場合は、ビット2がセットされないので
jb2命令の分岐はなく、サブルーチンコールと
なり、タスク2がCPUを使い、仕事をします。
タスク0からタスク7までの状態をスキャンし
READYであれば、該当タスクにCPUを使わせます。
各タスクには、優先順位はなく、ラウンドロビン
で、タスクがCPUを使い仕事をこなします。
WAIT状態のタスクは、指定時間経過後、READYへと
遷移させます。
時間待ちしているタスクの状態遷移は、サブルーチン
MTIMを使います。
サブルーチンMTIMの内容は、以下。
MTIM:
; set pointer
mov R0,#TCB0C
; set counter
mov R2,#8
MTIM0:
; decrement
mov a,@R0
dec a
mov @R0,a
; update pointer
inc R0
; loop
djnz R2,MTIM0
; search zero
; set pointer
mov R0,#TCB0C
; set loop counter
mov R2,#8
; set LSB
mov R3,#1
; clear bit location
mov R4,#0
MTIM1:
; get TCBxC counter
mov a,@R0
; judge
jnz MTIM2
; set location with bit format
mov a,R4
orl a,R3
mov R4,a
MTIM2:
; update pointer
inc R0
; shift 1 bit
mov a,R3
rl a
mov R3,a
;
djnz R2,MTIM1
; deque
mov R0,#WAIQ
mov a,R4
xrl a,@R0
mov R0,a
; enque
mov R0,#RDYQ
mov a,R4
orl a,@R0
mov R0,a
; exit
ret
次のシーケンスを利用しています。
- TCB0CからTCB7Cの値を、全て−1
- TCB0CからTCB7Cの中で、0になっていれば、マーキング
- WAIQの該当タスクのビット位置に0を格納
- RDYQの該当タスクのビット位置に1を格納
ディスパッチを定義したので、システムコールの
コーリングシーケンスを考えます。
RSM_TSK
タスクIDを指定して、SUSPENDからREADYに状態遷移
させます。
タスクIDは、TSKIDに格納してから、サブルーチン
コールして実現します。
例)
mov R0,#TSKID
mov @R0,#TSK4
call RSM_TSK
SUS_TSK
タスクIDを指定して、READYからSUSPENDに状態遷移
させます。
タスクIDは、TSKIDに格納してから、サブルーチン
コールして実現します。
例)
mov R0,#TSKID
mov @R0,#TSK5
call SUS_TSK
SLP_TSK
タスク自身が、READYからSUSPENDに状態遷移します。
例)
call SLP_TSK
WAI_TSK
タスク自身が、READYからWAITに状態遷移します。
待ち時間は、最小分解能の整数倍をWCNTに格納後
サブルーチンコールします。
例)
mov R0,#WCNT
mov @R0,#100
call WAI_TSK
コーリングシーケンスを定義したので、RTOSが使う
メモリ領域を確保しておきます。
TSKID equ 78h
WCNT equ 79h
RUNQ equ 7Ah
RDYQ equ 7Bh
SUSQ equ 7Ch
WAIQ equ 7Dh
これらは、システムコール実行時に使う
一時領域になります。
システムコールに対応するルーチンを定義していきます。
システムコールは、タスク状態を変更か待ち時間の値を
変更かのどちらかの処理を、担当しています。
RSM_TSKは、SUSPENDのタスクを、READYに更新します。
TSKIDとRDYQの間で論理演算をして、READYに変更。
さらに、TSKIDとSUSQの間で論理演算をしてSUSPEND
状態でなくなるようにすれば、うまくいきそう。
RSM_TSK:
; enque
; set pointer
mov R0,#TSKID
mov R1,#RDYQ
; get TASK number
mov a,@R0
; set RDYQ bit
orl a,@R1
; store RDYQ
mov @R1,a
; deque
; set pointer
mov R1,#SUSQ
; get TASK number
mov a,@R0
; reset SUSQ bit
xrl a,@R1
; store SUSQ
mov @R1,a
;
ret
SUS_TSKは、READYのタスクを、SUSPENDに更新します。
TSKID、RDYQとSUSQの間で論理演算をすれば、状態遷移
が完了します。
SUS_TSK:
; enque
; set pointer
mov R0,#TSKID
mov R1,#SUSQ
; get TASK number
mov a,@R0
; set SUSQ bit
orl a,@R1
; store SUSQ
mov @R1,a
; deque
; set pointer
mov R1,#RDYQ
; get TASK number
mov a,@R0
; reset RDYQ bit
xrl a,@R1
; store RDYQ
mov @R1,a
;
ret
SLP_TSKは、サブルーチンをコールする時点で
TSKIDにサブルーチンを使うタスクのタスクID
が格納されています。
これに注意すれば、SUS_TSKをコールするだけ
で実現できると、わかります。
SLP_TSK:
; subroutine
call SUS_TSK
;
ret
WAI_TSKは、サブルーチンをコールする時点で
TSKIDにサブルーチンを使うタスクのタスクID
が格納されています。また、WCNTに値が格納
済みなので、状態遷移と値設定に分けて操作
すればよいはず。
WAI_TSK
; set pointer
mov R0,#TSKID
mov R1,#WAIQ
; get TASK number
mov a,@R0
; set WAIQ bit
orl a,@R1
; store WAIQ
mov @R1,a
; set pointer
mov R1,#RDYQ
; get TASK number
mov a,@R0
; reset RDYQ bit
xrl a,@R1
; store RDYQ
mov @R1,a
; set pointer
mov R0,#WCNT
; get TCBxC value
mov a,@R0
mov R2,a
; set pointer
mov R0,#WAIQ
mov R1,#TSKID
; judge
mov a,@R0
anl a,@R1
jb0 WAI_TSK0
jb1 WAI_TSK1
jb2 WAI_TSK2
jb3 WAI_TSK3
jb4 WAI_TSK4
jb5 WAI_TSK5
jb6 WAI_TSK6
jb7 WAI_TSK7
jmp WAI_TSK8
WAI_TSK0:
; set pointer
mov R1,#TCB0C
; store counter
mov a,R2
mov @R1,a
jmp WAI_TSK8
WAI_TSK1:
; set pointer
mov R1,#TCB1C
; store counter
mov a,R2
mov @R1,a
jmp WAI_TSK8
WAI_TSK2:
; set pointer
mov R1,#TCB2C
; store counter
mov a,R2
mov @R1,a
jmp WAI_TSK8
WAI_TSK3:
; set pointer
mov R1,#TCB3C
; store counter
mov a,R2
mov @R1,a
jmp WAI_TSK8
WAI_TSK4:
; set pointer
mov R1,#TCB4C
; store counter
mov a,R2
mov @R1,a
jmp WAI_TSK8
WAI_TSK5:
; set pointer
mov R1,#TCB5C
; store counter
mov a,R2
mov @R1,a
jmp WAI_TSK8
WAI_TSK6:
; set pointer
mov R1,#TCB6C
; store counter
mov a,R2
mov @R1,a
jmp WAI_TSK8
WAI_TSK7:
; set pointer
mov R1,#TCB7C
; store counter
mov a,R2
mov @R1,a
WAI_TSK8:
;
ret
すべてのシステムコールを定義したので、時間待ちに
関係する内容を考えていきます。
時間待ちで動作するタスクの場合、タイマー割込みで
時間を刻んでいきます。
タイマー割込みは、システムクロック6MHzを480分周し
12.5kHzをタイマーカウンタに与えるクロックとして
使います。
12.5kHzを125分周すると、100Hzで10msの周期を生成
できます。250分周すれば、50Hzで20msの周期を生成。
今回のRTOSでは、10msを最小分解能として利用。
TCBxCの設定値は、10msの整数倍(最大255倍)
とします。
タイマーカウンタの初期化は、次のように定義して対応。
INIT_TIM:
dis tcnti
;
stop tcnt
mov a,#131
mov t,a
strt t
;
en tcnti
;
ret
割込みハンドラは、以下とします。
E_TIMX:
; clear flag
jtf E_TIMX0
; exit
jmp E_TIMXE
E_TIMX0:
; stop counter
stop tcnt
; set flag
mov r0,#TFLAG
mov @r0,#1
; initialize
mov a,#131
mov t,a
; start timer
strt t
E_TIMXE:
;
retr
オーバーフローのフラグがセットされてないなら
そのまま抜けます。
セットされていれば、イベント通知フラグをセット
して、タイマーカウンタを初期化。
各タスクは、サブルーチンのひとつと考えて
次のように中味が空のサブルーチンとして
おきます。
PTSK0:
;
ret
PTSK1:
;
ret
PTSK2:
;
ret
PTSK3:
;
ret
PTSK4:
;
ret
PTSK5:
;
ret
PTSK6:
;
ret
PTSK7:
;
ret
タスクは最大8個あるので、各タスクで使う領域を
8バイトに限定してメモリを確保します。
TMEM0 equ 30h
TMEM1 equ 38h
TMEM2 equ 40h
TMEM3 equ 48h
TMEM4 equ 50h
TMEM5 equ 58h
TMEM6 equ 60h
TMEM7 equ 68h
この仕様で、全体を定義してみます。
;
; TEST program for 8049 ( PROASM-II )
; Copyright (C) 2019 Kensuke Ooyu
;
INCLUDE 8048.LIB
;****************
; define symbols
;****************
ENTRY equ 0h
E_INT equ 3h
E_TIM equ 7h
TSK0 equ 01h
TSK1 equ 02h
TSK2 equ 04h
TSK3 equ 08h
TSK4 equ 10h
TSK5 equ 20h
TSK6 equ 40h
TSK7 equ 80h
TMEM0 equ 30h
TMEM1 equ 38h
TMEM2 equ 40h
TMEM3 equ 48h
TMEM4 equ 50h
TMEM5 equ 58h
TMEM6 equ 60h
TMEM7 equ 68h
;*********************
; TCBxC wait counter
;*********************
TCB0C equ 70h
TCB1C equ 71h
TCB2C equ 72h
TCB3C equ 73h
TCB4C equ 74h
TCB5C equ 75h
TCB6C equ 76h
TCB7C equ 77h
TSKID equ 78h
WCNT equ 79h
RUNQ equ 7Ah
RDYQ equ 7Bh
SUSQ equ 7Ch
WAIQ equ 7Dh
TFLAG equ 7Eh
;*******************
; interrupt vectors
;*******************
org ENTRY
jmp START
; external interrupt
org E_INT
retr
; timer interrupt
org E_TIM
jmp E_TIMX
org 0Ah
;**************
; sub routines
;**************
E_TIMX:
; clear flag
jtf E_TIMX0
; exit
jmp E_TIMXE
E_TIMX0:
; stop counter
stop tcnt
; set flag
mov r0,#TFLAG
mov @r0,#1
; initialize
mov a,#131
mov t,a
; start timer
strt t
E_TIMXE:
;
retr
INIT:
;
anl P1,#0FFh
orl P2,#0
;
ret
INIT_TIM:
dis tcnti
;
stop tcnt
mov a,#131
mov t,a
strt t
;
en tcnti
;
ret
INIT_MON:
; clear each que
clr a
mov R0,#RDYQ
mov @R0,a
mov R0,#SUSQ
mov @R0,a
mov R0,#WAIQ
mov @R0,a
; set READY que
mov R0,#RDYQ
mov a,@R0
orl a,#TSK0 ; TASK0 READY
orl a,#TSK2 ; TASK2 READY
orl a,#TSK3 ; TASK3 READY
mov @R0,a
; set SUSPEND que
mov R1,#SUSQ
mov a,@R1
orl a,#TSK1 ; TASK1 SUSPEND
mov @R1,a
; set WAIT que
mov a,@R0
orl a,#TSK4 ; TASK4 WAIT
mov @R0,a
; store WAIT counter
mov R1,#TCB4C
mov @R1,#128
; exit
ret
RSM_TSK:
; enque
; set pointer
mov R0,#TSKID
mov R1,#RDYQ
; get TASK number
mov a,@R0
; set RDYQ bit
orl a,@R1
; store RDYQ
mov @R1,a
; deque
; set pointer
mov R1,#SUSQ
; get TASK number
mov a,@R0
; reset SUSQ bit
xrl a,@R1
; store SUSQ
mov @R1,a
;
ret
SUS_TSK:
; enque
; set pointer
mov R0,#TSKID
mov R1,#SUSQ
; get TASK number
mov a,@R0
; set SUSQ bit
orl a,@R1
; store SUSQ
mov @R1,a
; deque
; set pointer
mov R1,#RDYQ
; get TASK number
mov a,@R0
; reset RDYQ bit
xrl a,@R1
; store RDYQ
mov @R1,a
;
ret
SLP_TSK:
; subroutine
call SUS_TSK
;
ret
WAI_TSK
; set pointer
mov R0,#TSKID
mov R1,#WAIQ
; get TASK number
mov a,@R0
; set WAIQ bit
orl a,@R1
; store WAIQ
mov @R1,a
; set pointer
mov R1,#RDYQ
; get TASK number
mov a,@R0
; reset RDYQ bit
xrl a,@R1
; store RDYQ
mov @R1,a
; set pointer
mov R0,#WCNT
; get TCBxC value
mov a,@R0
mov R2,a
; set pointer
mov R0,#WAIQ
mov R1,#TSKID
; judge
mov a,@R0
anl a,@R1
jb0 WAI_TSK0
jb1 WAI_TSK1
jb2 WAI_TSK2
jb3 WAI_TSK3
jb4 WAI_TSK4
jb5 WAI_TSK5
jb6 WAI_TSK6
jb7 WAI_TSK7
jmp WAI_TSK8
WAI_TSK0:
; set pointer
mov R1,#TCB0C
; store counter
mov a,R2
mov @R1,a
jmp WAI_TSK8
WAI_TSK1:
; set pointer
mov R1,#TCB1C
; store counter
mov a,R2
mov @R1,a
jmp WAI_TSK8
WAI_TSK2:
; set pointer
mov R1,#TCB2C
; store counter
mov a,R2
mov @R1,a
jmp WAI_TSK8
WAI_TSK3:
; set pointer
mov R1,#TCB3C
; store counter
mov a,R2
mov @R1,a
jmp WAI_TSK8
WAI_TSK4:
; set pointer
mov R1,#TCB4C
; store counter
mov a,R2
mov @R1,a
jmp WAI_TSK8
WAI_TSK5:
; set pointer
mov R1,#TCB5C
; store counter
mov a,R2
mov @R1,a
jmp WAI_TSK8
WAI_TSK6:
; set pointer
mov R1,#TCB6C
; store counter
mov a,R2
mov @R1,a
jmp WAI_TSK8
WAI_TSK7:
; set pointer
mov R1,#TCB7C
; store counter
mov a,R2
mov @R1,a
WAI_TSK8:
;
ret
MTIM:
; set pointer
mov R0,#TCB0C
; set counter
mov R2,#8
MTIM0:
; decrement
mov a,@R0
dec a
mov @R0,a
; update pointer
inc R0
; loop
djnz R2,MTIM0
; search zero
; set pointer
mov R0,#TCB0C
; set loop counter
mov R2,#8
; set LSB
mov R3,#1
; clear bit location
mov R4,#0
MTIM1:
; get TCBxC counter
mov a,@R0
; judge
jnz MTIM2
; set location with bit format
mov a,R4
orl a,R3
mov R4,a
MTIM2:
; update pointer
inc R0
; shift 1 bit
mov a,R3
rl a
mov R3,a
;
djnz R2,MTIM1
; deque
mov R0,#WAIQ
mov a,R4
xrl a,@R0
mov R0,a
; enque
mov R0,#RDYQ
mov a,R4
orl a,@R0
mov R0,a
; exit
ret
;**************
; main routine
;**************
org 100h
START:
call INIT
call INIT_TIM
call INIT_MON
;
MAIN:
; timer handling
mov R0,#TFLAG
mov a,@R0
jz MAINE
MAINT:
; clear flag
mov @R0,#0
; check WAIT task
call MTIM
MAINE:
; set pointer
mov R0,#RDYQ
mov R1,#TSKID
MAIN0:
; set TASK ID
mov @R1,#TSK0
; get READY que
mov a,@R0
; judge
xrl a,@R1
jb0 MAIN1
; do TASK0
call PTSK0
MAIN1:
; set TASK ID
mov @R1,#TSK1
; get READY que
mov a,@R0
; judge
xrl a,@R1
jb1 MAIN2
; do TASK1
call PTSK1
MAIN2:
; set TASK ID
mov @R1,#TSK2
; get READY que
mov a,@R0
; judge
xrl a,@R1
jb2 MAIN3
; do TASK2
call PTSK2
MAIN3:
; set TASK ID
mov @R1,#TSK3
; get READY que
mov a,@R0
; judge
xrl a,@R1
jb3 MAIN4
; do TASK3
call PTSK3
MAIN4:
; set TASK ID
mov @R1,#TSK4
; get READY que
mov a,@R0
; judge
xrl a,@R1
jb4 MAIN5
; do TASK4
call PTSK4
MAIN5:
; set TASK ID
mov @R1,#TSK5
; get READY que
mov a,@R0
; judge
xrl a,@R1
jb5 MAIN6
; do TASK5
call PTSK5
MAIN6:
; set TASK ID
mov @R1,#TSK6
; get READY que
mov a,@R0
; judge
xrl a,@R1
jb6 MAIN7
; do TASK6
call PTSK6
MAIN7:
; set TASK ID
mov @R1,#TSK7
; get READY que
mov a,@R0
; judge
xrl a,@R1
jb7 MAIN8
; do TASK7
call PTSK7
MAIN8:
jmp MAIN
;**************
; TASK routine
;**************
org 160h
;+++++++++
; TASK 0
;+++++++++
PTSK0:
; set pointer
mov R0,#TMEM0
; increment
inc @R0
; judge
mov a,@R0
jb4 PTSK0X
jmp PTSK0E
PTSK0X:
mov R1,#TSKID
mov @R1,#TSK1
call RSM_TSK
PTSK0E:
;
ret
;+++++++++
; TASK 1
;+++++++++
PTSK1:
in a,P1
xrl a,#15
outl P1,a
; system call
call SLP_TSK
;
ret
;+++++++++
; TASK 2
;+++++++++
PTSK2:
; set pointer
mov R0,#TMEM2
; increment
inc @R0
; judge
mov a,@R0
jb6 PTSK2X
jmp PTSK2E
PTSK2X:
mov R1,#TSKID
mov @R1,#TSK1
call SUS_TSK
PTSK2E:
;
ret
;+++++++++
; TASK 3
;+++++++++
PTSK3:
; set pointer
mov R0,#TMEM3
; increment
inc @R0
; get value
mov a,@R0
; mask
anl a,#15
;
swap a
; impress
outl P2,a
;
ret
;+++++++++
; TASK 4
;+++++++++
PTSK4:
; set pointer
mov R0,#TMEM4
; get value
mov a,@R0
;
xrl a,#50h
; store
mov @R0,a
; impress
outl P1,a
; system call
mov R1,#WCNT
mov @R1,#128
call WAI_TSK
;
ret
;+++++++++
; TASK 5
;+++++++++
PTSK5:
;
ret
;+++++++++
; TASK 6
;+++++++++
PTSK6:
;
ret
;+++++++++
; TASK 7
;+++++++++
PTSK7:
;
ret
end
(under construction)
目次
前