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