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)
|
---|
62 | ALABEL(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レジスタの保存 */
|
---|
77 | ALABEL(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)
|
---|
89 | ALABEL(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レジスタの復帰 */
|
---|
99 | ALABEL(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
|
---|
118 | ALABEL(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)
|
---|
131 | ALABEL(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
|
---|
191 | ALABEL(core_exc_entry_1) /* 多重割込みの場合 */
|
---|
192 | push {r0,lr} /* 割込み発生時の割込み優先度マスク(r0),EXC_RETURN(lr)の順にPSP上に積む */
|
---|
193 | mov r0, sp /* CPU例外ハンドラへの引数となる */
|
---|
194 |
|
---|
195 | /*
|
---|
196 | * 共通処理
|
---|
197 | */
|
---|
198 | ALABEL(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 | */
|
---|
226 | ALABEL(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
|
---|
235 | ALABEL(core_nonkernel_exc_entry_1) /* 多重割込みの場合 */
|
---|
236 | push {r0,lr} /* 割込み発生時の割込み優先度マスク(r0),EXC_RETURN(lr)の順にPSP上に積む */
|
---|
237 | mov r0, sp /* CPU例外ハンドラへの引数となる */
|
---|
238 |
|
---|
239 | ALABEL(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 |
|
---|
265 | ALABEL(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 |
|
---|
273 | ALABEL(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)
|
---|
282 | ALABEL(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
|
---|
301 | ALABEL(core_int_entry_1) /* 多重割込みの場合 */
|
---|
302 | push {r0,lr} /* 割込み発生時の割込み優先度マスク(r0),EXC_RETURN(lr)の順にPSP上に積む */
|
---|
303 | mov r0, sp /* 未定義の割込みが発生した場合の情報とする */
|
---|
304 |
|
---|
305 | /*
|
---|
306 | * 共通処理
|
---|
307 | */
|
---|
308 | ALABEL(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 | */
|
---|
349 | ALABEL(ret_exc)
|
---|
350 | ALABEL(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 | */
|
---|
379 | ALABEL(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 |
|
---|
401 | ALABEL(ret_int_2)
|
---|
402 | /*
|
---|
403 | * ここには割込みロック状態(FAULTMASKがセット)された状態で来る.
|
---|
404 | * Threadモードからのリターンにより自動的に割込みロック解除状態になる.
|
---|
405 | * 割込み優先度マスクは割込み前に状態に戻す.
|
---|
406 | */
|
---|
407 | msr basepri, r1 /* 割込み優先度マスクを割込み前の状態へ */
|
---|
408 | bx r3 /* リターン */
|
---|
409 |
|
---|
410 | ALABEL(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 |
|
---|
482 | ALABEL(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レジスタの保存 */
|
---|
507 | ALABEL(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)
|
---|
527 | ALABEL(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 */
|
---|
538 | ALABEL(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 /* タスク例外ルーチンの呼び出し */
|
---|
561 | ALABEL(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)
|
---|
578 | ALABEL(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)
|
---|
600 | ALABEL(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)
|
---|
627 | ALABEL(exit_and_dispatch)
|
---|
628 | /* ディスパッチャ本体(dispatcher)へ */
|
---|
629 |
|
---|
630 | /*
|
---|
631 | * ディスパッチャ本体
|
---|
632 | */
|
---|
633 | ALABEL(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 */
|
---|
653 | ALABEL(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] /* 実行再開番地を復帰 */
|
---|
667 | ALABEL(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ロック解除状態へ */
|
---|
680 | ALABEL(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)
|
---|
762 | ALABEL(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)
|
---|
778 | ALABEL(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)
|
---|
796 | ALABEL(sil_dly_nse)
|
---|
797 | subs r0, r0, #SIL_DLY_TIM1
|
---|
798 | cmp r0, #0
|
---|
799 | bgt sil_dly_nse1
|
---|
800 | bx lr
|
---|
801 | ALABEL(sil_dly_nse1)
|
---|
802 | subs r0, r0, #SIL_DLY_TIM2
|
---|
803 | cmp r0, #0
|
---|
804 | bgt sil_dly_nse1
|
---|
805 | bx lr
|
---|