time_event.c

説明を見る。
00001 /*
00002  *  TOPPERS/ASP Kernel
00003  *      Toyohashi Open Platform for Embedded Real-Time Systems/
00004  *      Advanced Standard Profile Kernel
00005  * 
00006  *  Copyright (C) 2000-2003 by Embedded and Real-Time Systems Laboratory
00007  *                              Toyohashi Univ. of Technology, JAPAN
00008  *  Copyright (C) 2005-2008 by Embedded and Real-Time Systems Laboratory
00009  *              Graduate School of Information Science, Nagoya Univ., JAPAN
00010  * 
00011  *  上記著作権者は,以下の(1)〜(4)の条件を満たす場合に限り,本ソフトウェ
00012  *  ア(本ソフトウェアを改変したものを含む.以下同じ)を使用・複製・改
00013  *  変・再配布(以下,利用と呼ぶ)することを無償で許諾する.
00014  *  (1) 本ソフトウェアをソースコードの形で利用する場合には,上記の著作
00015  *      権表示,この利用条件および下記の無保証規定が,そのままの形でソー
00016  *      スコード中に含まれていること.
00017  *  (2) 本ソフトウェアを,ライブラリ形式など,他のソフトウェア開発に使
00018  *      用できる形で再配布する場合には,再配布に伴うドキュメント(利用
00019  *      者マニュアルなど)に,上記の著作権表示,この利用条件および下記
00020  *      の無保証規定を掲載すること.
00021  *  (3) 本ソフトウェアを,機器に組み込むなど,他のソフトウェア開発に使
00022  *      用できない形で再配布する場合には,次のいずれかの条件を満たすこ
00023  *      と.
00024  *    (a) 再配布に伴うドキュメント(利用者マニュアルなど)に,上記の著
00025  *        作権表示,この利用条件および下記の無保証規定を掲載すること.
00026  *    (b) 再配布の形態を,別に定める方法によって,TOPPERSプロジェクトに
00027  *        報告すること.
00028  *  (4) 本ソフトウェアの利用により直接的または間接的に生じるいかなる損
00029  *      害からも,上記著作権者およびTOPPERSプロジェクトを免責すること.
00030  *      また,本ソフトウェアのユーザまたはエンドユーザからのいかなる理
00031  *      由に基づく請求からも,上記著作権者およびTOPPERSプロジェクトを
00032  *      免責すること.
00033  * 
00034  *  本ソフトウェアは,無保証で提供されているものである.上記著作権者お
00035  *  よびTOPPERSプロジェクトは,本ソフトウェアに関して,特定の使用目的
00036  *  に対する適合性も含めて,いかなる保証も行わない.また,本ソフトウェ
00037  *  アの利用により直接的または間接的に生じたいかなる損害に関しても,そ
00038  *  の責任を負わない.
00039  * 
00040  *  @(#) $Id: time_event.c 874 2008-04-11 10:33:17Z hiro $
00041  */
00042 
00043 /*
00044  *      タイムイベント管理モジュール
00045  */
00046 
00047 #include "kernel_impl.h"
00048 #include "check.h"
00049 #include "time_event.h"
00050 
00051 /*
00052  *  タイムイベントヒープ操作マクロ
00053  */
00054 #define PARENT(index)       ((index) >> 1)      /* 親ノードを求める */
00055 #define LCHILD(index)       ((index) << 1)      /* 右の子ノードを求める */
00056 #define TMEVT_NODE(index)   (tmevt_heap[(index) - 1])
00057 
00058 /*
00059  *  イベント発生時刻比較マクロ
00060  *
00061  *  イベント発生時刻は,current_timeからの相対値で比較する.すなわち,
00062  *  current_timeを最小値(最も近い時刻),current_time-1が最大値(最も
00063  *  遠い時刻)とみなして比較する.
00064  */
00065 #define EVTTIM_LT(t1, t2) (((t1) - current_time) < ((t2) - current_time))
00066 #define EVTTIM_LE(t1, t2) (((t1) - current_time) <= ((t2) - current_time))
00067 
00068 #ifdef TOPPERS_tmeini
00069 
00070 /*
00071  *  現在のシステム時刻(単位: ミリ秒)
00072  *
00073  *  厳密には,前のタイムティックのシステム時刻.
00074  */
00075 EVTTIM  current_time;
00076 
00077 /*
00078  *  次のタイムティックのシステム時刻(単位: 1ミリ秒)
00079  */
00080 EVTTIM  next_time;
00081 
00082 /*
00083  *  システム時刻積算用変数(単位: 1/TIM_DENOミリ秒)
00084  */
00085 #if TIC_DENO != 1U
00086 uint_t  next_subtime;
00087 #endif /* TIC_DENO != 1U */
00088 
00089 /*
00090  *  タイムイベントヒープの最後の使用領域のインデックス
00091  */
00092 uint_t  last_index;
00093 
00094 /*
00095  *  タイマモジュールの初期化
00096  */
00097 void
00098 initialize_tmevt(void)
00099 {
00100     current_time = 0U;
00101     next_time = current_time + TIC_NUME / TIC_DENO;
00102 #if TIC_DENO != 1U
00103     next_subtime = TIC_NUME % TIC_DENO;
00104 #endif /* TIC_DENO != 1U */
00105     last_index = 0U;
00106 }
00107 
00108 #endif /* TOPPERS_tmeini */
00109 
00110 /*
00111  *  タイムイベントの挿入位置を上向きに探索
00112  *
00113  *  時刻timeに発生するタイムイベントを挿入するノードを空けるために,
00114  *  ヒープの上に向かって空ノードを移動させる.移動前の空ノードの位置を
00115  *  indexに渡すと,移動後の空ノードの位置(すなわち挿入位置)を返す.
00116  */
00117 #ifdef TOPPERS_tmeup
00118 
00119 uint_t
00120 tmevt_up(uint_t index, EVTTIM time)
00121 {
00122     uint_t  parent;
00123 
00124     while (index > 1) {
00125         /*
00126          *  親ノードのイベント発生時刻の方が早い(または同じ)
00127          *  ならば,index が挿入位置なのでループを抜ける.
00128          */
00129         parent = PARENT(index);
00130         if (EVTTIM_LE(TMEVT_NODE(parent).time, time)) {
00131             break;
00132         }
00133 
00134         /*
00135          *  親ノードを index の位置に移動させる.
00136          */
00137         TMEVT_NODE(index) = TMEVT_NODE(parent);
00138         TMEVT_NODE(index).p_tmevtb->index = index;
00139 
00140         /*
00141          *  index を親ノードの位置に更新.
00142          */
00143         index = parent;
00144     }
00145     return(index);
00146 }
00147 
00148 #endif /* TOPPERS_tmeup */
00149 
00150 /*
00151  *  タイムイベントの挿入位置を下向きに探索
00152  *
00153  *  時刻timeに発生するタイムイベントを挿入するノードを空けるために,
00154  *  ヒープの下に向かって空ノードを移動させる.移動前の空ノードの位置を 
00155  *  indexに渡すと,移動後の空ノードの位置(すなわち挿入位置)を返す.
00156  */
00157 #ifdef TOPPERS_tmedown
00158 
00159 uint_t
00160 tmevt_down(uint_t index, EVTTIM time)
00161 {
00162     uint_t  child;
00163 
00164     while ((child = LCHILD(index)) <= last_index) {
00165         /*
00166          *  左右の子ノードのイベント発生時刻を比較し,早い方の
00167          *  子ノードの位置を child に設定する.以下の子ノード
00168          *  は,ここで選ばれた方の子ノードのこと.
00169          */
00170         if (child + 1 <= last_index
00171                         && EVTTIM_LT(TMEVT_NODE(child + 1).time,
00172                                         TMEVT_NODE(child).time)) {
00173             child = child + 1;
00174         }
00175 
00176         /*
00177          *  子ノードのイベント発生時刻の方が遅い(または同じ)
00178          *  ならば,index が挿入位置なのでループを抜ける.
00179          */
00180         if (EVTTIM_LE(time, TMEVT_NODE(child).time)) {
00181             break;
00182         }
00183 
00184         /*
00185          *  子ノードを index の位置に移動させる.
00186          */
00187         TMEVT_NODE(index) = TMEVT_NODE(child);
00188         TMEVT_NODE(index).p_tmevtb->index = index;
00189 
00190         /*
00191          *  index を子ノードの位置に更新.
00192          */
00193         index = child;
00194     }
00195     return(index);
00196 }
00197 
00198 #endif /* TOPPERS_tmedown */
00199 
00200 /*
00201  *  タイムイベントヒープへの登録
00202  *
00203  *  p_tmevtbで指定したタイムイベントブロックを,timeで指定した時間が経
00204  *  過後にイベントが発生するように,タイムイベントヒープに登録する.
00205  */
00206 #ifdef TOPPERS_tmeins
00207 
00208 void
00209 tmevtb_insert(TMEVTB *p_tmevtb, EVTTIM time)
00210 {
00211     uint_t  index;
00212 
00213     /*
00214      *  last_index をインクリメントし,そこから上に挿入位置を探す.
00215      */
00216     index = tmevt_up(++last_index, time);
00217 
00218     /*
00219      *  タイムイベントを index の位置に挿入する.
00220      */ 
00221     TMEVT_NODE(index).time = time;
00222     TMEVT_NODE(index).p_tmevtb = p_tmevtb;
00223     p_tmevtb->index = index;
00224 }
00225 
00226 #endif /* TOPPERS_tmeins */
00227 
00228 /*
00229  *  タイムイベントヒープからの削除
00230  */
00231 #ifdef TOPPERS_tmedel
00232 
00233 void
00234 tmevtb_delete(TMEVTB *p_tmevtb)
00235 {
00236     uint_t  index = p_tmevtb->index;
00237     uint_t  parent;
00238     EVTTIM  event_time = TMEVT_NODE(last_index).time;
00239 
00240     /*
00241      *  削除によりタイムイベントヒープが空になる場合は何もしない.
00242      */
00243     if (--last_index == 0) {
00244         return;
00245     }
00246 
00247     /*
00248      *  削除したノードの位置に最後のノード(last_index+1の位置のノード)
00249      *  を挿入し,それを適切な位置へ移動させる.実際には,最後のノード
00250      *  を実際に挿入するのではなく,削除したノードの位置が空ノードにな
00251      *  るので,最後のノードを挿入すべき位置へ向けて空ノードを移動させ
00252      *  る.
00253      *  最後のノードのイベント発生時刻が,削除したノードの親ノードのイ
00254      *  ベント発生時刻より前の場合には,上に向かって挿入位置を探す.そ
00255      *  うでない場合には,下に向かって探す.
00256      */
00257     if (index > 1 && EVTTIM_LT(event_time,
00258                                 TMEVT_NODE(parent = PARENT(index)).time)) {
00259         /*
00260          *  親ノードをindexの位置に移動させる.
00261          */
00262         TMEVT_NODE(index) = TMEVT_NODE(parent);
00263         TMEVT_NODE(index).p_tmevtb->index = index;
00264 
00265         /*
00266          *  削除したノードの親ノードから上に向かって挿入位置を探す.
00267          */
00268         index = tmevt_up(parent, event_time);
00269     }
00270     else {
00271         /*
00272          *  削除したノードから下に向かって挿入位置を探す.
00273          */
00274         index = tmevt_down(index, event_time);
00275     }
00276 
00277     /*
00278      *  最後のノードをindexの位置に挿入する.
00279      */ 
00280     TMEVT_NODE(index) = TMEVT_NODE(last_index + 1);
00281     TMEVT_NODE(index).p_tmevtb->index = index;
00282 }
00283 
00284 #endif /* TOPPERS_tmedel */
00285 
00286 /*
00287  *  タイムイベントヒープの先頭のノードの削除
00288  */
00289 Inline void
00290 tmevtb_delete_top(void)
00291 {
00292     uint_t  index;
00293     EVTTIM  event_time = TMEVT_NODE(last_index).time;
00294 
00295     /*
00296      *  削除によりタイムイベントヒープが空になる場合は何もしない.
00297      */
00298     if (--last_index == 0) {
00299         return;
00300     }
00301 
00302     /*
00303      *  ルートノードに最後のノード(last_index + 1 の位置のノード)を
00304      *  挿入し,それを適切な位置へ移動させる.実際には,最後のノードを
00305      *  実際に挿入するのではなく,ルートノードが空ノードになるので,最
00306      *  後のノードを挿入すべき位置へ向けて空ノードを移動させる.
00307      */
00308     index = tmevt_down(1, event_time);
00309 
00310     /*
00311      *  最後のノードをindexの位置に挿入する.
00312      */ 
00313     TMEVT_NODE(index) = TMEVT_NODE(last_index + 1);
00314     TMEVT_NODE(index).p_tmevtb->index = index;
00315 }
00316 
00317 /*
00318  *  タイムイベントまでの残り時間の計算
00319  */
00320 #ifdef TOPPERS_tmeltim
00321 
00322 RELTIM
00323 tmevt_lefttim(TMEVTB *p_tmevtb)
00324 {
00325     EVTTIM  time;
00326 
00327     time = TMEVT_NODE(p_tmevtb->index).time;
00328     if (EVTTIM_LE(time, next_time)) {
00329         /*
00330          *  次のタイムティックで処理される場合には0を返す.
00331          */
00332         return(0U);
00333     }
00334     else {
00335         return((RELTIM)(time - base_time));
00336     }
00337 }
00338 
00339 #endif /* TOPPERS_tmeltim */
00340 
00341 /*
00342  *  タイムティックの供給
00343  */
00344 #ifdef TOPPERS_sigtim
00345 
00346 void
00347 signal_time(void)
00348 {
00349     TMEVTB  *p_tmevtb;
00350 
00351     assert(sense_context());
00352     assert(!i_sense_lock());
00353     i_lock_cpu();
00354 
00355     /*
00356      *  next_timeよりイベント発生時刻の早い(または同じ)タイムイベン
00357      *  トを,タイムイベントヒープから削除し,コールバック関数を呼び出
00358      *  す.
00359      */
00360     while (last_index > 0 && EVTTIM_LE(TMEVT_NODE(1).time, next_time)) {
00361         p_tmevtb = TMEVT_NODE(1).p_tmevtb;
00362         tmevtb_delete_top();
00363         (*(p_tmevtb->callback))(p_tmevtb->arg);
00364     }
00365 
00366     /*
00367      *  current_timeを更新する.
00368      */
00369     current_time = next_time;
00370 
00371     /*
00372      *  next_time,next_subtimeを更新する.
00373      *
00374      *  TIC_NUME < TIC_DENOの時は,除算を使わずに時刻の更新ができるが,
00375      *  ソースコードを読みやすくにするために#ifの多用を避けている.
00376      */
00377 #if TIC_DENO == 1U
00378     next_time = current_time + TIC_NUME;
00379 #else /* TIC_DENO == 1U */
00380     next_subtime += TIC_NUME % TIC_DENO;
00381     next_time = current_time + TIC_NUME / TIC_DENO;
00382     if (next_subtime >= TIC_DENO) {
00383         next_subtime -= TIC_DENO;
00384         next_time += 1U;
00385     }
00386 #endif /* TIC_DENO == 1U */
00387 
00388     i_unlock_cpu();
00389 }
00390 
00391 #endif /* TOPPERS_sigtim */

Copyright © 2008 by Kijineko Inc.

ホームページ制作