source: azure_iot_hub_f767zi/trunk/asp_baseplatform/arch/arm_m_gcc/common/core_support.S@ 457

Last change on this file since 457 was 457, checked in by coas-nagasima, 4 years ago

ファイルを追加

  • Property svn:eol-style set to native
  • Property svn:mime-type set to text/plain;charset=UTF-8
File size: 33.2 KB
Line 
1/*
2 * TOPPERS/ASP Kernel
3 * Toyohashi Open Platform for Embedded Real-Time Systems/
4 * Advanced Standard Profile Kernel
5 *
6 * Copyright (C) 2008-2015 by Embedded and Real-Time Systems Laboratory
7 * Graduate School of Information Science, Nagoya Univ., JAPAN
8 *
9 * 上記著作権者は,以下の(1)~(4)の条件を満たす場合に限り,本ソフトウェ
10 * ア(本ソフトウェアを改変したものを含む.以下同じ)を使用・複製・改
11 * 変・再配布(以下,利用と呼ぶ)することを無償で許諾する.
12 * (1) 本ソフトウェアをソースコードの形で利用する場合には,上記の著作
13 * 権表示,この利用条件および下記の無保証規定が,そのままの形でソー
14 * スコード中に含まれていること.
15 * (2) 本ソフトウェアを,ライブラリ形式など,他のソフトウェア開発に使
16 * 用できる形で再配布する場合には,再配布に伴うドキュメント(利用
17 * 者マニュアルなど)に,上記の著作権表示,この利用条件および下記
18 * の無保証規定を掲載すること.
19 * (3) 本ソフトウェアを,機器に組み込むなど,他のソフトウェア開発に使
20 * 用できない形で再配布する場合には,次のいずれかの条件を満たすこ
21 * と.
22 * (a) 再配布に伴うドキュメント(利用者マニュアルなど)に,上記の著
23 * 作権表示,この利用条件および下記の無保証規定を掲載すること.
24 * (b) 再配布の形態を,別に定める方法によって,TOPPERSプロジェクトに
25 * 報告すること.
26 * (4) 本ソフトウェアの利用により直接的または間接的に生じるいかなる損
27 * 害からも,上記著作権者およびTOPPERSプロジェクトを免責すること.
28 * また,本ソフトウェアのユーザまたはエンドユーザからのいかなる理
29 * 由に基づく請求からも,上記著作権者およびTOPPERSプロジェクトを
30 * 免責すること.
31 *
32 * 本ソフトウェアは,無保証で提供されているものである.上記著作権者お
33 * よびTOPPERSプロジェクトは,本ソフトウェアに関して,特定の使用目的
34 * に対する適合性も含めて,いかなる保証も行わない.また,本ソフトウェ
35 * アの利用により直接的または間接的に生じたいかなる損害に関しても,そ
36 * の責任を負わない.
37 *
38 * @(#) $Id$
39 */
40
41/*
42 * プロセッサ依存モジュール アセンブリ言語部(ARM-M用)
43 */
44
45#define TOPPERS_MACRO_ONLY
46#define UINT_C(val) (val) /* uint_t型の定数を作るマクロ */
47#define ULONG_C(val) (val) /* ulong_t型の定数を作るマクロ */
48#define CAST(type, val) (val) /* 型キャストを行うマクロ */
49
50#include "kernel_impl.h"
51#include "arm_m.h"
52#include "offset.h"
53#include "target_asm.inc"
54
55/*
56 * タスクディスパッチャ
57 */
58 ATEXT
59 ABALIGN(4)
60 ATHUMB(dispatch)
61 AGLOBAL(dispatch)
62ALABEL(dispatch)
63 /*
64 *
65 * このルーチンは,タスクコンテキスト・CPUロック状態・ディパッチ許可状態
66 * ・(モデル上の)割込み優先度マスク全開状態で呼び出される.
67 */
68 stmfd sp!,{r4-r11,lr} /* レジスタの保存 */
69#ifdef TOPPERS_FPU_CONTEXT
70 /*
71 * ディスパッチ元のタスクFPUを使用した場合はFPUレジスタを保存
72 */
73 mrs r0, control /* CONTROL.FPCA == 0(FPU未使用)ならばスキップ */
74 tst r0, #CONTROL_FPCA
75 beq dispatch_1
76 vpush {s16-s31} /* FPUレジスタの保存 */
77ALABEL(dispatch_1)
78 push {r0} /* 復帰時の判定用にCONTROLを保存 */
79#endif /* TOPPERS_FPU_CONTEXT */
80 ldr r0, =p_runtsk /* p_runtskを読み込む */
81 ldr r1, [r0]
82 str sp, [r1,#TCB_sp] /* タスクスタックを保存 */
83 ldr lr, =dispatch_r /* 実行再開番地を保存 */
84 str lr, [r1,#TCB_pc]
85 b dispatcher
86
87 ATHUMB(dispatch_r)
88 AGLOBAL(dispatch_r)
89ALABEL(dispatch_r)
90#ifdef TOPPERS_FPU_CONTEXT
91 /*
92 * ディスパッチ先のタスクでFPUを使用していた場合はFPUレジスタを復帰
93 * スタックに保存したCONTROL.FPCAをチェック
94 */
95 pop {r3}
96 tst r3, #CONTROL_FPCA /* CONTROL.FPCA == 0(FPU未使用)ならばスキップ */
97 beq dispatch_r_0
98 vpop {s16-s31} /* FPUレジスタの復帰 */
99ALABEL(dispatch_r_0)
100#endif /* TOPPERS_FPU_CONTEXT */
101 ldmfd sp!,{r4 - r11,lr} /* レジスタの復帰 */
102 /*
103 * タスク例外処理ルーチンの起動
104 * dispatcherから呼び出されるため,TCBのアドレスはr1に入っている
105 */
106 ldrb r0,[r1,#TCB_enatex]
107 tst r0,#TCB_enatex_mask
108 beq dispatch_r_1 /* enatex が false ならリターン */
109 ldr r0,[r1,#TCB_texptn] /* texptn が 0 ならリターン */
110 tst r0,r0
111 beq dispatch_r_1
112 ldr r1, =ipmflg /* ipmflgが false ならリターン */
113 ldr r0, [r1]
114 tst r0,r0
115 beq dispatch_r_1
116 ldr r0, =call_texrtn /* タスク例外ルーチンの呼び出し */
117 bx r0
118ALABEL(dispatch_r_1) /* タスクへのcall_textnから戻る */
119 bx lr
120
121/*
122 * CPU例外エントリ
123 *
124 * 割込みエントリと処理の内容は同等だが,ログの種類が異なるため,
125 * 分けている.
126 */
127 ABALIGN(4)
128 ATEXT
129 ATHUMB(core_exc_entry)
130 AGLOBAL(core_exc_entry)
131ALABEL(core_exc_entry)
132 /*
133 * 例外/割込みが発生すると,発生時にアクティブなスタックにスクラ
134 * ッチレジスタ等が保存される.
135 * この内容に加えて,CPU例外ハンドラへの情報として,basepri の値と,
136 * EXC_RETURNの情報を加えて保存する.basepriの値は,CPU例外からの
137 * リターン時に割込み優先度マスクの値を元に戻すためにも用いられる.
138 *
139 * -----------
140 * | basepri |
141 * -----------
142 * | EXC_RETURN|
143 * -----------
144 * | R0 |
145 * -----------
146 * | R1 |
147 * -----------
148 * | R2 |
149 * -----------
150 * | R3 |
151 * -----------
152 * | R12 |
153 * -----------
154 * | LR |
155 * -----------
156 * | PC |
157 * -----------
158 * | xPSR |
159 * -----------
160 *
161 */
162
163 /*
164 * カーネル管理外の例外かチェック
165 * カーネル内のクリティカルセクションの実行中,全割込みロック状態,
166 * CPUロック状態,カーネル管理外の割込みハンドラ実行中のいずれかで
167 * 発生したCPU例外を,カーネル管理外のCPU例外と呼ぶ
168 * 全割込みロック状態はFAULTMASKが'1'の場合
169 * CPUロック状態はbasepriがIIPM_LOCKかで判断する.
170 */
171 mrs r2, FAULTMASK /* 全割込みロック状態ならカーネル管理外例外処理へ */
172 cbnz r2, core_nonkernel_exc_entry
173
174 mrs r0, basepri /* baepriの値を取得 */
175 cmp r0, #IIPM_LOCK /* CPUロック状態ならカーネル管理外例外処理へ */
176 beq core_nonkernel_exc_entry
177
178 /*
179 * スタックを変更する必要があるかチェック
180 * EXC_RETURN(割込み時にLRに設定される値)をチェックして,例外発生時に
181 * アクティブなスタックを特定することで多重割込みか判定する.
182 */
183 tst lr, #EXC_RETURN_PSP /* 割込み元がMSPなら多重割込み */
184 beq core_exc_entry_1 /* 多重割込みならcore_exc_entry_1へ */
185 mrs r3, psp /* 一段目の割込みの場合はPSP上に */
186 stmfd r3!, {r0, lr} /* 割込み発生時の割込み優先度マスク(r0),EXC_RETURN(lr)の順にPSP上に積む */
187 msr psp, r3
188 push {r0,lr} /* 割込み発生時の割込み優先度マスク(r0),EXC_RETURN(lr)の順にMSP上に積む */
189 mov r0, r3 /* CPU例外ハンドラへの引数となる */
190 b core_exc_entry_2
191ALABEL(core_exc_entry_1) /* 多重割込みの場合 */
192 push {r0,lr} /* 割込み発生時の割込み優先度マスク(r0),EXC_RETURN(lr)の順にPSP上に積む */
193 mov r0, sp /* CPU例外ハンドラへの引数となる */
194
195 /*
196 * 共通処理
197 */
198ALABEL(core_exc_entry_2)
199 mrs r3, ipsr /* ハンドラアドレスを取得 */
200 ldr r1, =_kernel_exc_tbl
201 ldr r2, [r1, r3, lsl #2]
202
203#ifdef LOG_EXC_ENTER
204 push {r0,r2,r3}
205 mov r0, r3 /* 例外番号をパラメータに */
206 bl log_exc_enter /* log_exc_enterを呼び出す */
207 pop {r0,r2,r3}
208 push {r3} /* 例外番号をスタックへ */
209#endif /* LOG_EXC_ENTER */
210
211 /*
212 * CPU例外ハンドラの呼び出し
213 */
214 blx r2
215
216#ifdef LOG_EXC_ENTER
217 pop {r0} /* 例外番号を引数に */
218 bl log_exc_leave /* log_exc_leaveを呼び出す */
219#endif /* LOG_EXC_ENTER */
220
221 b ret_exc
222
223/*
224 * カーネル管理外のCPU例外の出入口処理
225 */
226ALABEL(core_nonkernel_exc_entry)
227 tst lr, #EXC_RETURN_PSP /* 割込み元がMSPなら多重割込み */
228 beq core_nonkernel_exc_entry_1 /* 多重割込みなら */
229 mrs r3, psp /* 一段目の割込みの場合はPSP上に */
230 stmfd r3!, {r0, lr} /* 割込み発生時の割込み優先度マスク(r0),EXC_RETURN(lr)の順にPSP上に積む */
231 msr psp, r3
232 push {r0,lr} /* 割込み発生時の割込み優先度マスク(r0),EXC_RETURN(lr)の順にMSP上に積む */
233 mov r0, r3 /* CPU例外ハンドラへの引数となる */
234 b core_nonkernel_exc_entry_2
235ALABEL(core_nonkernel_exc_entry_1) /* 多重割込みの場合 */
236 push {r0,lr} /* 割込み発生時の割込み優先度マスク(r0),EXC_RETURN(lr)の順にPSP上に積む */
237 mov r0, sp /* CPU例外ハンドラへの引数となる */
238
239ALABEL(core_nonkernel_exc_entry_2)
240 mrs r3, ipsr /* CPU例外ハンドラのアドレスを取得 */
241 ldr r1, =_kernel_exc_tbl
242 ldr r2, [r1, r3, lsl #2]
243
244 /*
245 * CPU例外ハンドラの呼び出し
246 */
247 blx r2
248
249 /*
250 * 割込みロック状態とする.
251 */
252 cpsid f
253
254 /*
255 * 戻り先のコンテキストの判定
256 *
257 * 割込みハンドラ実行にLRにセットされるEXC_RETURNをチェックして,戻り
258 * 先でMSPが使われていれば,割込み先が非タスクコンテキストと判定する.
259 */
260 pop {r1,r3} /* 元の割込み優先度マスク(basepri)をr1へ,EXC_RETURNをr3へ */
261 tst r3, #EXC_RETURN_PSP /* 戻り先がPSPなら */
262 bne core_nonkernel_ret_exc_1
263 b core_nonkernel_ret_exc_2 /* の値をMSPから取得 */
264
265ALABEL(core_nonkernel_ret_exc_1)
266 /*
267 * PSP上から,EXC_RETURN(r0)と元の割込み優先度マスク(basepri)分を削除
268 */
269 mrs r2, psp
270 add r2, r2, #(0x04*2)
271 msr psp, r2
272
273ALABEL(core_nonkernel_ret_exc_2)
274 msr basepri, r1 /* 割込み優先度マスクを割込み前に状態へ */
275 bx r3 /* リターン */
276
277/*
278 * 割込みエントリ
279 */
280 ATHUMB(core_int_entry)
281 AGLOBAL(core_int_entry)
282ALABEL(core_int_entry)
283 /*
284 * 割込み発生時の割込み優先度マスクをスタックに保存するため取得
285 */
286 mrs r0, basepri /* baepriの値を取得 */
287
288 /*
289 * 多重割込みかチェック
290 * EXC_RETURN(割込み時にLRに設定される値)をチェックして,例外発生時に
291 * アクティブなスタックを特定することで多重割込みか判定する.
292 */
293 tst lr, #EXC_RETURN_PSP /* 割込み元がMSPなら多重割込み */
294 beq core_int_entry_1 /* 多重割込みならcore_int_entry_1へ */
295 mrs r3, psp /* 一段目の割込みの場合はPSP上に */
296 stmfd r3!, {r0, lr} /* 割込み発生時の割込み優先度マスク(r0),EXC_RETURN(lr)の順にPSP上に積む */
297 msr psp, r3
298 push {r0,lr} /* 割込み発生時の割込み優先度マスク(r0),EXC_RETURN(lr)の順にMSP上に積む */
299 mov r0, r3 /* 未定義の割込みが発生した場合の情報とする */
300 b core_int_entry_2
301ALABEL(core_int_entry_1) /* 多重割込みの場合 */
302 push {r0,lr} /* 割込み発生時の割込み優先度マスク(r0),EXC_RETURN(lr)の順にPSP上に積む */
303 mov r0, sp /* 未定義の割込みが発生した場合の情報とする */
304
305 /*
306 * 共通処理
307 */
308ALABEL(core_int_entry_2)
309 mrs r3, ipsr /* ハンドラアドレスを取得 */
310 ldr r1, =_kernel_exc_tbl
311 ldr r2, [r1, r3, lsl #2]
312
313 /*
314 * basepriの設定
315 * NVIC優先度マスクが自動的に設定されるため優先度マスクの点では必要な
316 * いが,x_get_ipm()がbasepriを参照するため,basepriも更新する.
317 */
318 ldr r1, =_kernel_int_iipm_tbl
319 ldr lr, [r1, r3, lsl #2]
320 msr basepri, lr
321
322#ifdef LOG_INH_ENTER
323 push {r0,r2,r3}
324 mov r0, r3 /* 例外番号をパラメータに */
325 bl log_inh_enter /* log_exc_enterを呼び出す */
326 pop {r0,r2,r3}
327#endif /* LOG_EXC_ENTER */
328
329#ifdef LOG_INH_LEAVE
330 push { r3 } /* 例外番号をスタックへ */
331#endif /* LOG_INT_LEAVE */
332
333 /*
334 * 割込みハンドラの呼び出し
335 */
336 blx r2
337
338#ifdef LOG_INH_LEAVE
339 pop {r0} /* 例外番号を引数に */
340 bl log_exc_leave /* log_exc_leaveを呼び出す */
341#endif /* LOG_INH_LEAVE */
342
343/*
344 * 割込み/例外出口
345 *
346 * ret_exc/ret_intは,CPU例外/割込みハンドラから戻った直後に実行する
347 * ルーチンである.
348 */
349ALABEL(ret_exc)
350ALABEL(ret_int)
351 /*
352 * 割込みロック状態とする.この時点では,CPUロック状態にはならない
353 * (basepriとlock_flagとsaved_iipmは更新しない).
354 *
355 * 割込みロック状態とするのは,戻り先のコンテキストのチェックと,
356 * 戻り先が非タスクコンテキストであった場合のリターンをアトミック
357 * に行うためである.bsepriをCPUロックの値にすることでもアトミッ
358 * クなチェックと復帰は可能であるが,割込みからリターンしても,
359 * basepri の設定内容は元に戻らないため,使用することができない.
360 * 一方,FAULTMASKは,割込みからのリターン処理によって,'0'にクリ
361 * アされる.
362 */
363 cpsid f
364
365 /*
366 * 戻り先のコンテキストの判定
367 *
368 * 割込みハンドラ実行にLRにセットされるEXC_RETURNをチェックして,戻り
369 * 先でMSPが使われていれば,割込み先が非タスクコンテキストと判定する.
370 */
371 pop {r1,r3} /* 元の割込み優先度マスク(basepri)をr1へ,EXC_RETURNをr3へ */
372 tst r3, #EXC_RETURN_PSP /* 戻り先がPSPなら ret_int_1 へ */
373 bne ret_int_1
374 b ret_int_2 /* の値をMSPから取得 */
375
376 /*
377 * 一段目の割込みの出口処理
378 */
379ALABEL(ret_int_1)
380 /*
381 * reqflgをチェックする
382 *
383 * カーネル管理内の割込みは禁止した状態で実行する必要があるため,
384 * FAULTMASKを'1'にした状態で実行する.
385 * reqflgをチェックする前に割込みを禁止するのは,reqflgをチェック
386 * した直後に割込みハンドラが起動され,その中でディスパッチが要求
387 * された場合に,すぐにディスパッチされないという問題が生じるため
388 * である.
389 */
390 ldr r0, =reqflg /* reqflgがfalseならそのまま戻る */
391 ldr r2, [r0]
392 cbnz r2, ret_int_3 /* trueならret_int_3へ */
393
394 /*
395 * PSP上から,EXC_RETURN(r0)と元の割込み優先度マスク(basepri)分を削除
396 */
397 mrs r2, psp
398 add r2, r2, #(0x04*2)
399 msr psp, r2
400
401ALABEL(ret_int_2)
402 /*
403 * ここには割込みロック状態(FAULTMASKがセット)された状態で来る.
404 * Threadモードからのリターンにより自動的に割込みロック解除状態になる.
405 * 割込み優先度マスクは割込み前に状態に戻す.
406 */
407 msr basepri, r1 /* 割込み優先度マスクを割込み前の状態へ */
408 bx r3 /* リターン */
409
410ALABEL(ret_int_3)
411 /*
412 * ここでは,戻り先がタスクであり,PSP上にスクラッチレジスタと割
413 * 込み優先度マスク(basepri)が保存された状態になっている.また,
414 * プロセッサは,Handlerモード・割込みロック状態となっている.
415 * また,r0には,reqflgのアドレス,r3には割込み受付時のlrの値が保
416 * 持されている.
417 */
418 /*
419 * タスク例外ハンドラやディスパッチをする際にThreadモードへ遷移する
420 * ダミーのスタックフレームを作成して,bx命令でHandlerモードからリ
421 * ターンする.また,遅延ディスパッチする場合も,再び割り込んだタス
422 * クに戻る際には,svc命令で,svc_handlerを呼び出す.
423 * スタックフレームは,Configureation and Control Register(CCR)の
424 * STKALIGNが'1'の場合は,8byte境界にアラインされる.
425 * 参考 : DDI0403B_arm_architecture_v7m_reference_manual(P.220)
426 * そのため,この時点のスタックは割込みや例外発生時に作成された
427 * スタックフレームから,8byte境界のサイズにしておくと,svc_handler
428 * 等でスタックフレームのアライメントの有無の確認を省略できる.
429 * ただし,システム起動後は,動的にCCRのSTKALIGNの設定を変更するのは
430 * 禁止とする.
431 * この時点は標準のスタックフレームは,割込み・例外発生時と同等であ
432 * るため,タスクスタック(PSP)は8byte境界になっている.
433 */
434 mov r1, #0 /* reqflgをfalseに */
435 str r1, [r0]
436
437 /*
438 * CPUロック状態に移行する.
439 *
440 * カーネルの管理内の割込みを禁止するようにbasepriを設定し,
441 * lock_flag と saved_iipm を更新する.saved_iipmは,戻り先の割込み
442 * 優先度マスク(の内部表現)に設定する.
443 * この時点でCPUロック状態とするのは,dispatcherへ分岐する時と,
444 * call_texrtnを呼び出す時に,CPUロック状態になっている必要がある
445 * ためである.
446 * なお,この処理の後,Threadモードへの移行処理を行なうため,割込み
447 * ロック状態(FAULTMASKを"1")は保持する.
448 */
449 ldr r1, =IIPM_LOCK /* CPUロック状態 */
450 msr basepri, r1
451 mov r1, #0x01 /* lock_flag を trueに */
452 ldr r0, =lock_flag
453 str r1, [r0]
454
455 /*
456 * 割込み優先度マスクを,全解除状態(TIPM_ENAALL)に設定する
457 * すでにCPUロック状態なので,saved_iipmをIIPM_ENAALLとする.
458 */
459 ldr r1, =IIPM_ENAALL
460 ldr r0, =saved_iipm
461 str r1, [r0]
462
463 /*
464 * Threadモードへ移行する.
465 *
466 * dispatcherやcall_texrnを呼び出す場合は,Threadモードである必
467 * 要があるため,PSPスタック上にダミーの例外フレームを置いて,
468 * 擬似的に割込みハンドラからリターンする.
469 * リターンと同時にFAULTMASKが自動的にクリアされ,カーネル管理外の
470 * 割込みが許可される.
471 */
472 ldr r0, =ret_int_4 /* PC */
473 ldr r1, =EPSR_T /* xPSR(Tビットが'1'である必要がある) */
474 mrs r2, psp
475 stmfd r2!, {r0-r1} /* ダミーフレームをスタック上に積む */
476 subs r2, #(EXC_FRAME_SIZE - (4*2)) /* r0-r3,r12,lrの内容は設定する必要がない */
477 msr psp,r2
478 /* Basicフレーム,THREADモード,PSPのための EXC_RETURN */
479 mov r0, #(EXC_RETURN_TREAD_PSP|EXC_RETURN_OTHER|EXC_RETURN_FP_NONUSED)
480 bx r0 /* Threadモードへ移行 */
481
482ALABEL(ret_int_4)
483 /*
484 * 上記の処理により,Threadモードで実行される.
485 * dspflgがfalseである場合と,p_runtskとp_schedtskが同じ場合には,
486 * ディスパッチを行わない.このチェックが必要なのは,タスク例外処
487 * 理ルーチンの呼出しが必要な場合に,ディスパッチが必要なくても,
488 * reqflgをtrueにするためである.
489 */
490 ldr r0, =p_runtsk /* ディスパッチを行わない場合でも,r1にp_runtsk の値(TCB) */
491 ldr r1, [r0] /* が入っている必要があるので,先に読み込む */
492 ldr r0, =dspflg
493 ldr r2, [r0]
494 cbz r2, ret_int_r_1 /* dspflgがfalseならret_int_r_1へ */
495 ldr r0, =p_schedtsk
496 ldr r2, [r0]
497 cmp r1, r2 /* p_runtskとp_schedtskが同じなら */
498 beq ret_int_r_1 /* ret_int_r_1へ */
499#ifdef TOPPERS_FPU_CONTEXT
500 ldr r3, [sp,4] /* ディスパッチ元のEXC_RETURNを取得 */
501 /*
502 * ディスパッチ元のタスクFPUを使用した場合はFPUレジスタを保存
503 */
504 tst r3, #EXC_RETURN_FP
505 bne ret_int_5 /* EXC_RETURN[4] == 1(FPU未使用)ならばスキップ */
506 vpush {s16-s31} /* FPUレジスタの保存 */
507ALABEL(ret_int_5)
508 push {r3} /* 戻り先のEXC_RETURNを保存 */
509#endif /* TOPPERS_FPU_CONTEXT */
510 stmfd sp!, {r4-r11} /* 残りのレジスタを保存 */
511 str sp, [r1,#TCB_sp] /* タスクスタックを保存 */
512 ldr lr, =ret_int_r /* 実行再開番地を保存 */
513 str lr, [r1,#TCB_pc]
514 b dispatcher /* ディスパッチャへ */
515
516/*
517 * 割込みによりプリエンプトされたタスクへのリターン処理
518 *
519 * Threadモードで,ディスパッチャや割込みの出口処理から呼び出される.
520 * 割込みによりプリエンプトされたタスクへリターンするには,いったん
521 * Handlerモードに移行し,PCに0xfffffffdを代入してリターンする必要
522 * がある.そのため,SVCにより,SVCハンドラを呼び出し,Handlerモー
523 * ドへ移行する.
524 */
525 ATHUMB(ret_int_r)
526 AGLOBAL(ret_int_r)
527ALABEL(ret_int_r)
528 pop {r4-r11} /* レジスタの復帰 */
529#ifdef TOPPERS_FPU_CONTEXT
530 pop {r3} /* 戻り先のEXC_RETURNを取得 */
531 /*
532 * ディスパッチ元のタスクFPUを使用した場合はFPUレジスタを復帰
533 */
534 tst r3, #EXC_RETURN_FP
535 bne ret_int_r_1 /* EXC_RETURN[4] == 1(FPU未使用)ならばスキップ */
536 vpop {s16-s31} /* FPUレジスタの復帰 */
537#endif /* TOPPERS_FPU_CONTEXT */
538ALABEL(ret_int_r_1)
539 /*
540 * enatexがtrueで,texptnが0でなければ,タスク例外処理ルーチンを
541 * 呼び出す.
542 * dispatcherから呼び出されるため,TCBのアドレスはr1に入っている
543 */
544 ldrb r0, [r1,#TCB_enatex]
545 tst r0, #TCB_enatex_mask
546 beq ret_int_r_2 /* enatex が false なら ret_int_r_2へ */
547 ldr r0, [r1,#TCB_texptn] /* texptn が 0 ならリターン */
548 cbz r0, ret_int_r_2
549 ldr r1, =ipmflg /* ipmflgが false ならリターン */
550 ldr r0, [r1]
551 cbz r0, ret_int_r_2
552#ifdef TOPPERS_FPU_LAZYSTACKING
553 /*
554 * Lazy Stacking かつ 割込みの出口処理からの分岐で来た場合は
555 * ここでFPU命令を実行して例外フレームにFPUのコンテキストを書き戻す必要がある
556 * 他の状況では必要ないが,判断が困難なため一律実行する.
557 */
558 vmov r3, s15
559#endif /* TOPPERS_FPU_LAZYSTACKING */
560 bl call_texrtn /* タスク例外ルーチンの呼び出し */
561ALABEL(ret_int_r_2)
562#ifdef TOPPERS_FPU_CONTEXT
563 /*
564 * SVCの実行でFPUコンテキストをスタックに積まないように
565 * CONTROL.FPCAをクリア
566 */
567 mrs r3, control
568 bic r3, r3, #CONTROL_FPCA
569 msr control, r3
570#endif /* TOPPERS_FPU_CONTEXT */
571 svc 0 /* SVCの呼び出し */
572
573/*
574 * SVCハンドラ
575 */
576 ATHUMB(svc_handler)
577 AGLOBAL(svc_handler)
578ALABEL(svc_handler)
579 /*
580 * 割込み処理からのリターンにより,CPUロック解除状態に移行するよ
581 * う準備する.
582 */
583 cpsid f /* 割込みロック状態へ */
584 mrs r0, psp
585 adds r0, #EXC_FRAME_SIZE /* スタックを捨てる */
586 ldmfd r0!,{r1,lr} /* EXC_RETURNは,FPU使用時・未使用時で異なるためスタックから取得 */
587 msr psp, r0
588 mov r0, #0
589 ldr r1, =lock_flag /* CPUロック解除状態へ */
590 str r0, [r1]
591 ldr r1, =IIPM_ENAALL /* 割込み優先度マスクを全解除状態に設定 */
592 msr basepri, r1
593 bx lr /* リターン */
594
595/*
596 * ディスパッチャの動作開始
597 */
598 ATHUMB(start_dispatch)
599 AGLOBAL(start_dispatch)
600ALABEL(start_dispatch)
601 /*
602 * このルーチンは,カーネル起動時に,すべての割込みを禁止した状態
603 * (割込みロック状態と同等)で呼び出される.また,割込みモード(非
604 * タスクコンテキストと同等)で呼び出されることを想定している.
605 *
606 * core_initializeで,lock_flagをtrueに,saved_iipmをIIPM_ENAALLに
607 * 初期化しているため,カーネル管理外の割込みを許可することで,
608 * CPUロック状態・(モデル上の)割込み優先度マスク全解除状態になる.
609 * また,task_initializeでdisdspをfalseに初期化しているため,ディ
610 * スパッチ許可状態になっている.
611 */
612 ldr r0,=istkpt /* MSPを初期化 */
613 ldr r1,[r0] /* start_dispatch呼び出し時に呼び出し用に */
614 msr msp, r1 /* 使用しているため初期化する */
615 ldr r1, =IIPM_LOCK /* カーネル管理内の割込みを禁止 */
616 msr basepri, r1
617 cpsie f /* カーネル管理外の割込みを許可 */
618 mov r0, #CONTROL_PSP /* PSPを有効に */
619 msr control, r0
620 isb /* control の操作後に必要 */
621
622/*
623 * 現在のコンテキストを捨ててディスパッチ
624 */
625 ATHUMB(exit_and_dispatch)
626 AGLOBAL(exit_and_dispatch)
627ALABEL(exit_and_dispatch)
628 /* ディスパッチャ本体(dispatcher)へ */
629
630/*
631 * ディスパッチャ本体
632 */
633ALABEL(dispatcher)
634 /*
635 * このルーチンは,タスクコンテキスト・CPUロック状態・ディスパッチ
636 * 許可状態・(モデル上の)割込み優先度マスク全解除状態で呼び出さ
637 * れる.
638 *
639 * すなわち,Threadモード・lock_flagがtrue・disdspがfalse・dspflg
640 * がtrue・saved_iipmがIIPM_ENAALLとなっている.実行再開番地へもこ
641 * の状態のまま分岐する.
642 */
643#ifdef TOPPERS_FPU_CONTEXT
644 mrs r3, control /* ディスパッチャ実行時はFPCAを一律クリアする */
645 bic r3, r3, #CONTROL_FPCA
646 msr control, r3
647#endif /* TOPPERS_FPU_CONTEXT */
648#ifdef LOG_DSP_ENTER
649 ldr r1, =p_runtsk /* p_runtskをパラメータに */
650 ldr r0, [r1]
651 bl log_dsp_enter
652#endif /* LOG_DSP_ENTER */
653ALABEL(dispatcher_0)
654 ldr r0, =p_schedtsk /* p_schedtskをp_runtskに */
655 ldr r1, [r0]
656 ldr r2, =p_runtsk
657 str r1, [r2]
658 cbz r1, dispatcher_1 /* p_runtskがNULLならdispatcher_1へ */
659 ldr sp, [r1,#TCB_sp] /* タスクスタックを復帰 */
660#ifdef LOG_DSP_LEAVE
661 mov r0, r1 /* p_runtskをパラメータに */
662 mov r4, r1 /* r1はスクラッチレジスタなので保存 */
663 bl log_dsp_leave
664 mov r1, r4
665#endif /* LOG_DSP_LEAVE */
666 ldr pc, [r1,#TCB_pc] /* 実行再開番地を復帰 */
667ALABEL(dispatcher_1)
668 /*
669 * CPUロック状態の解除と,非タスクコンテキスト実行状態への
670 * 準備をする
671 */
672 mov r0, #CONTROL_MSP /* MSPを有効に */
673 msr control, r0
674 isb /* control の操作後に必要 */
675 mov r4, #0 /* r4 <- '0' */
676 ldr r5, =IIPM_LOCK /* r5 <- 割込みロック状態の割込み優先度マスクの値 */
677 ldr r6, =reqflg /* r6 <- reqflg */
678 ldr r7, =lock_flag /* r7 <- lock_flg */
679 str r4, [r7] /* CPUロック解除状態へ */
680ALABEL(dispatcher_2)
681 /*
682 * 割込みを許可し,非タスクコンテキスト実行状態とし割込みを待つ.
683 *
684 * ここで非タスクコンテキスト実行状態に切り換えるのは,ここで発生
685 * する割込み処理にどのスタックを使うかという問題の解決と,割込み
686 * ハンドラ内でのタスクディスパッチの防止という2つの意味がある.
687 *
688 * プロセッサを割込み待ちに移行させる処理と,割込み許可とは,不可
689 * 分に行なう必要がある.
690 * これを不可分に行なわない場合,割込みを許可した直後に割込
691 * みが入り,その中でタスクが実行可能状態になると,実行すべきタス
692 * クがあるにもかかわらずプロセッサが割込み待ちになってしまう.
693 * ARM-Mでは,PRIMASKをセットした状態でWFIを呼び出すことで実現できる.
694 * この状態で割込みが入ると,割込みは実行されず,WFIからリターンす
695 * ることになるので,一旦割込みを許可して割込みハンドラを実行する.
696 *
697 * 割込み待ちの間は,p_runtskをNULL(=0)に設定しなければならな
698 * い.このように設定しないと,割込みハンドラからiget_tidを呼び出
699 * した際の動作が仕様に合致しなくなる.
700 *
701 * ターゲットによっては,省電力モード等に移行するため,標準の方法と
702 * 異なる手順が必要な場合がある.
703 * そのようなターゲットでは,ターゲット依存において,TOPPERS_CUSTOM_IDLE
704 * を定義し,アセンブラマクロとして,toppers_asm_custom_idle を用意
705 * すればよい.
706 *
707 * なお,toppers_asm_custom_idle の記述にあたっては,次のレジスタは
708 * toppers_asm_custom_idleの前後で使用するため,
709 * toppers_asm_custom_idle 内で使用する場合は,前後で保存復帰すること.
710 * これらのレジスタは Calee saved レジスタであるため,
711 * toppers_asm_custom_idle として関数呼び出しをした場合は,呼び出した
712 * 関数で自動的に保存復帰されるため,アセンブラレベルでの保存復帰は必
713 * 要ない.
714 *
715 * レジスタ : 内容
716 * r4 : '0'
717 * r5 : 'IIPM_LOCK'
718 * r6 : reqflgのアドレス
719 * r7 : lock_flgのアドレス
720 * sp : 非タスクコンテキスト用のスタックの先頭アドレス(msp)
721 */
722#ifdef TOPPERS_CUSTOM_IDLE
723 toppers_asm_custom_idle
724#else
725 cpsid i /* PRIMASK をセット */
726 msr basepri, r4 /* 全割込み許可 */
727 wfi
728 cpsie i /* PRIMASK をクリア(割込みを受け付ける) */
729 msr basepri, r5 /* CPUロック状態へ */
730#endif /* TOPPERS_CUSTOM_IDLE */
731
732 ldr r0, [r6] /* reqflgがfalseならdispatcher_2へ */
733 cmp r0, #0
734 beq dispatcher_2
735 str r4, [r6] /* reqflgをfalseに */
736
737 /*
738 * CPUロック状態に戻す.割込み待ちの間に実行した割込みハンドラによ
739 * り,saved_iipmが書き換えられる可能性があるため,元の値に戻す必
740 * 要がある.dispatcherが実行される時は,saved_iipmがIIPM_ENAALL
741 * となっているため,ここではsaved_iipmをIIPM_ENAALL(=0)に戻せ
742 * ばよい.
743 */
744 mov r0, #CONTROL_PSP /* PSPを有効に */
745 msr control, r0
746 isb /* control の操作後に必要 */
747 mov r2, #1 /* lock_flagをtrueへ */
748 str r2, [r7]
749 ldr r0, =saved_iipm /* saved_iipm を0に */
750 str r4, [r0]
751 b dispatcher_0
752
753
754/*
755 * カーネルの終了処理の呼出し
756 *
757 * スタックを非タスクコンテキスト用に切り替え.
758 *
759 */
760 ATHUMB(call_exit_kernel)
761 AGLOBAL(call_exit_kernel)
762ALABEL(call_exit_kernel)
763 mov r0, #CONTROL_MSP
764 msr control, r0 /* MSPを有効に */
765 isb /* control の操作後に必要 */
766 ldr r0, =exit_kernel /* カーネルの終了処理を呼ぶ */
767 bx r0
768
769
770/*
771 * タスク起動処理
772 *
773 * dispatcherから呼び出されるため,TCBのアドレスはr1に入っている
774 *
775 */
776 ATHUMB(start_r)
777 AGLOBAL(start_r)
778ALABEL(start_r)
779 mov r0, #0
780 ldr r4, =lock_flag /* CPUロック解除状態へ */
781 str r0, [r4]
782 msr basepri, r0 /* 割込み許可 */
783 ldr lr, =ext_tsk /* 戻り番地設定 */
784 ldr r2, [r1, #TCB_p_tinib] /* p_runtsk->p_tinibをr2に */
785 ldr r0, [r2, #TINIB_exinf] /* exinfを引数レジスタr0に */
786 ldr r1, [r2, #TINIB_task] /* タスク起動番地にジャンプ */
787 bx r1
788
789/*
790 * 微少時間待ち
791 */
792 ABALIGN(4)
793 ATEXT
794 ATHUMB(sil_dly_nse)
795 AGLOBAL(sil_dly_nse)
796ALABEL(sil_dly_nse)
797 subs r0, r0, #SIL_DLY_TIM1
798 cmp r0, #0
799 bgt sil_dly_nse1
800 bx lr
801ALABEL(sil_dly_nse1)
802 subs r0, r0, #SIL_DLY_TIM2
803 cmp r0, #0
804 bgt sil_dly_nse1
805 bx lr
Note: See TracBrowser for help on using the repository browser.