目次

RTOS移植

 i8049は、ROM容量2kバイト、SRAM容量128バイトなので
 RTOS(Real Time OperationSystem)を入れられると判断し
 μITRON互換のリアルタイムモニタを移植しました。

 タスクの状態遷移は、次のように決定。



 システムコールは、4種に限定。

 この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

 次のシーケンスを利用しています。
  1. TCB0CからTCB7Cの値を、全て−1
  2. TCB0CからTCB7Cの中で、0になっていれば、マーキング
  3. WAIQの該当タスクのビット位置に0を格納
  4. 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)

目次

inserted by FC2 system