summaryrefslogtreecommitdiff
path: root/nuttx/arch/avr/src/avr/excptmacros.h
blob: 730f02acc2ea14747962717b28478d0c58c0721d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
/********************************************************************************************
 * arch/avr/src/avr/excptmacros.h
 *
 *	Copyright (C) 2011 Gregory Nutt. All rights reserved.
 *	Author: Gregory Nutt <gnutt@nuttx.org>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *	notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *	notice, this list of conditions and the following disclaimer in
 *	the documentation and/or other materials provided with the
 *	distribution.
 * 3. Neither the name NuttX nor the names of its contributors may be
 *	used to endorse or promote products derived from this software
 *	without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ********************************************************************************************/

#ifndef __ARCH_AVR_SRC_AVR_EXCPTMACROS_H
#define __ARCH_AVR_SRC_AVR_EXCPTMACROS_H

/********************************************************************************************
 * Included Files
 ********************************************************************************************/

#include <nuttx/config.h>

#ifdef __ASSEMBLY__

#include <arch/irq.h>

#include <avr/io.h>
#include <avr/sfr_defs.h>

/********************************************************************************************
 * Pre-Processor Definitions
 ********************************************************************************************/

/********************************************************************************************
 * Pre-processor Definitions
 ********************************************************************************************/

/********************************************************************************************
 * Global Symbols
 ********************************************************************************************/

#if CONFIG_ARCH_INTERRUPTSTACK > 3
	.global		g_intstackbase
	.global		g_nestlevel
#endif

/********************************************************************************************
 * Assembly Language Macros
 ********************************************************************************************/

/********************************************************************************************
 * General Exception Handling Example:
 *
 *		HANDLER IRQ_X, my_exception
 *		...
 * my_exception:
 *		EXCPT_PROLOGUE				- Save registers on stack
 *		in	r22, _SFR_IO_ADDR(SPL)	- Pass register save structure as the parameter 2
 *		in	r23, _SFR_IO_ADDR(SPH)	- (Careful, push post-decrements)
 *		USE_INTSTACK rx, ry, rz		- Switch to the interrupt stack
 *		call handler				- Handle the exception IN=old regs OUT=new regs
 *		RESTORE_STACK rx, ry		- Undo the operations of USE_INTSTACK
 *		EXCPT_EPILOGUE				- Return to the context returned by handler()
 *		reti						- Return from interrupt
 *
 ********************************************************************************************/

/********************************************************************************************
 * Name: HANDLER
 *
 * Description:
 *	This macro provides the exception entry logic.  It is called with the PC already on the
 *	stack.  It simply saves one register on the  stack (r24) and passes the IRQ number to 
 *	common logic (see EXCPT_PROLOGUE).
 *
 * On Entry:
 *	sp - Points to the top of the stack.  The PC is already on the stack.
 *	Only the stack is available for storage
 *
 *	 PCL
 *	 PCH
 *	 --- <- SP
 *
 * At completion:
 *	Stack pointer is incremented by one, the saved r24 is on the stack, r24 now contains the
 *	IRQ number
 *
 *	 PCL
 *	 PCH
 *	 R0
 *	 --- <- SP
 *
 ********************************************************************************************/

	.macro	HANDLER, label, irqno, common
	.global	\label
\label:
	push	r24
	ldi		r24, \irqno
	rjmp	\common
	.endm

/********************************************************************************************
 * Name: EXCPT_PROLOGUE
 *
 * Description:
 *	Provides the common "prologue" logic that should appear at the beginning of the exception
 *	handler.
 *
 * On Entry:
 *	r24 - has already been pushed onto the stack and now holds the IRQ number
 *	sp - Points to the top of the stack
 *	Only the stack is available for storage
 *
 *	 PCL
 *	 PCH
 *	 R24
 *	 --- <- SP
 *
 * At completion:
 *	Register state is saved on the stack; All registers are available for usage except sp and
 *  r24 which still contains the IRQ number as set by the HANDLER macro.
 *
 ********************************************************************************************/

	.macro	EXCPT_PROLOGUE

	/* Save R25  */

	push	r25

	/* Save the status register on the stack */

	in		r25, _SFR_IO_ADDR(SREG)	/* Save the status register */
	cli								/* Disable interrupts (not necessary) */
	ori		r25, (1 << SREG_I)		/* Interrupts re-enabled on restore */
	push	r25

	/* Save R0 -- the scratch register and the zero register (which may not be zero).  R1
	 * must be zero for our purposes
	 */

	push	r0
	push	r1
	clr		r1

	/* Save r2-r17 - Call-saved, "static" registers */

	push	r2
	push	r3
	push	r4
	push	r5
	push	r6
	push	r7
	push	r8
	push	r9
	push	r10
	push	r11
	push	r12
	push	r13
	push	r14
	push	r15
	push	r16
	push	r17

	/* Save r18-r27 - Call-used, "volatile" registers (r24 was saved by
	 * HANDLER, r15 was saved above, and r26-r27 saved later, out of sequence)
	 */

	push	r18
	push	r19
	push	r20
	push	r21
	push	r22
	push	r23

	/* Save r28-r29 - Call-saved, "static" registers */

	push	r28
	push	r29

	/* Save r30-r31 - Call-used, "volatile" registers */

	push	r30
	push	r31

	/* Now save r26-r27 */

	push	r26
	push	r27

	/* Finally, save the stack pointer.  BUT we want the value of the stack pointer as
	 * it was just BEFORE the exception.  We'll have to add to get that value.
	 * The value to add is the size of the register save area including the bytes
	 * pushed by the interrupt handler (2), by the HANDLER macro (1), and the 32
	 * registers pushed above.  That is, the entire size of the register save structure
	 * MINUS two bytes for the stack pointer which has not yet been saved.
	 */

	in		r26, _SFR_IO_ADDR(SPL)
	in		r27, _SFR_IO_ADDR(SPH)
	adiw	r26, XCPTCONTEXT_REGS-2

	push	r26					/* SPL then SPH */
	push	r27
	.endm

/********************************************************************************************
 * Name: EXCPT_EPILOGUE
 *
 * Description:
 *	Provides the "epilogue" logic that should appear at the end of every exception handler.
 *
 * On input:
 *	sp points to the address of the register save area (just as left by EXCPT_PROLOGUE).
 *	All registers are available for use.
 *	Interrupts are disabled.
 *
 * On completion:
 *	All registers restored except the PC which remains on the stack so that a return
 *	via reti can be performed.
 *
 ********************************************************************************************/

	.macro	EXCPT_EPILOGUE, regs

	/* We don't need to restore the stack pointer */

	pop		r27					/* Discard SPH */
	pop		r26					/* Discard SPL */

	/* Restore r26-r27 */

	pop		r27
	pop		r26

	/* Restore r30-r31 - Call-used, "volatile" registers */

	pop		r31
	pop		r30

	/* Restore r28-r29 - Call-saved, "static" registers */

	pop		r29
	pop		r28

	/* Restore r18-r27 - Call-used, "volatile" registers (r26-r27 already
	 * restored, r24 and r25 will be restored later)
	 */

	pop		r23
	pop		r22
	pop		r21
	pop		r20
	pop		r19
	pop		r18

	/* Restore r2-r17 - Call-saved, "static" registers */

	pop		r17
	pop		r16
	pop		r15
	pop		r14
	pop		r13
	pop		r12
	pop		r11
	pop		r10
	pop		r9
	pop		r8
	pop		r7
	pop		r6
	pop		r5
	pop		r4
	pop		r3
	pop		r2

	/* Restore r0 - the scratch register and  r1- the "zero" register (that may not be zero) */

	pop		r1
	pop		r0

	/* Restore the status register (probably enabling interrupts) */

	pop		r24					/* Restore the status register */
	andi	r24, ~(1 << SREG_I)	/* but keeping interrupts disabled until the reti */
	out		_SFR_IO_ADDR(SREG), r24

	/* Finally, restore r24-r25 - the temporary and IRQ number registers */

	pop		r25
	pop		r24
	.endm

/********************************************************************************************
 * Name: USER_SAVE
 *
 * Description:
 *	Similar to EXPCT_PROLOGUE except that (1) this saves values into a register save
 *	data structure instead of on the stack, (2) the pointer is in r26;r27, and (3)
 *	Call-used registers are not saved.
 *
 * On Entry:
 *	X [r26:r27] - Points to the register save structure.
 *	Return address is already on the stack (due to CALL or RCALL instruction)/.
 *	Interrupts are disabled.
 *
 * At completion:
 *	Register state is saved on the stack; All registers are available for usage except sp.
 *
 ********************************************************************************************/

	.macro	USER_SAVE

	/* Pop the return address from the stack (PCH then PCL).  R18:19 are Call-used */

	pop		r19			/* r19=PCH */
	pop		r18			/* r18=PCL */

	/* Save the current stack pointer  as it would be after the return(SPH then SPL). */

	in		r25, _SFR_IO_ADDR(SPH)
	st		x+, r25
	in		r24, _SFR_IO_ADDR(SPL)
	st		x+, r24

	/* Skip over r26-r27 and r30-r31 - Call-used, "volatile" registers */

	adiw	r26, 4		/* Four registers: r26-r27 and r30-r31*/

	/* Save r28-r29 - Call-saved, "static" registers */

	st		x+, r29
	st		x+, r28

	/* Skip over r18-r27 - Call-used, "volatile" registers (r26-r27 have
	 * already been skipped, r24 and r25 are saved elsewhere)
	 */

	adiw	r26, 6		/* Seven registers: r18-23 */

	/* Save r2-r17 - Call-saved, "static" registers */

	st		x+, r17
	st		x+, r16
	st		x+, r15
	st		x+, r14
	st		x+, r13
	st		x+, r12
	st		x+, r11
	st		x+, r10
	st		x+, r9
	st		x+, r8
	st		x+, r7
	st		x+, r6
	st		x+, r5
	st		x+, r4
	st		x+, r3
	st		x+, r2

	/* Set r1  to zero - Function calls must return with r1=0 */

	clr		r1
	st		x+, r1

	/* Skip over r0 -- the scratch register */

	adiw	r26, 1

	/* Save the status register (probably not necessary since interrupts are disabled) */

	in		r0, _SFR_IO_ADDR(SREG)
	st		x+, r0

	/* Skip r24-r25 - These are scratch register and Call-used, "volatile" registers */

	adiw	r26, 2		/* Two registers: r24-r25 */

	/* Save the return address that we have saved in r18:19*/

	st		x+, r19		/* r19=PCH */
	st		x+, r18		/* r18=PCL */
	.endm

/********************************************************************************************
 * Name: TCB_RESTORE
 *
 * Description:
 *	Functionally equivalent to EXCPT_EPILOGUE excetp that register save area is not on the
 *	stack but is held in a data structure.
 *
 * On input:
 *	X [r26:r27] points to the data structure.
 *	All registers are available for use.
 *	Interrupts are disabled.
 *
 * On completion:
 *	All registers restored except for the PC with now resides at the top of the new stack
 *	so that ret can be used to switch to the new context. (ret, not reti, becaue ret
 * 	will preserve the restored interrupt state).
 *
 ********************************************************************************************/

	.macro	TCB_RESTORE, regs

	/* X [r36:27] points to the register save block.  Get an offset pointer to the PC in
	 * Y [r28:29]
	 */

	movw	r28, r26			/* Get a pointer to the PCH/PCL storage location */
	adiw	r28, REG_PCH

	/* Fetch and set the new stack pointer */

	ld		r25, x+				/* Fetch stack pointer (post-incrementing) */
	out		_SFR_IO_ADDR(SPH), r25		/* (SPH then SPL) */
	ld		r24, x+
	out		_SFR_IO_ADDR(SPL), r24
 
	/* Fetch the return address and save it at the bottom of the new stack so
	 * that we can iret to switch contexts.  The new stack is now:
	 *
	 *  PCL
	 *  PCH
	 *  --- <- SP
	 */

	ld		r25, y+				/* Load PCH (r25) then PCL (r24) */
	ld		r24, y+
	push	r24					/* Push PCH and PCL on the stack (PCL then PCH) */
	push	r25

	/* Then get value of X [r26:r27].  Save X on the new stack where we can
	 * recover it later.  The new stack is now:
	 *
	 *  PCL
	 *  PCH
	 *  R26
	 *  R27
	 *  --- <- SP
	 */

	ld		r25, x+				/* Fetch r26-r27 and save to the new stack */
	ld		r24, x+
	push	r24					/* r26 then r27 */
	push	r25

	/* Restore r30-r31 - Call-used, "volatile" registers */

	ld		r31, x+
	ld		r30, x+

	/* Restore r28-r29 - Call-saved, "static" registers */

	ld		r29, x+
	ld		r28, x+

	/* Restore r18-r27 - Call-used, "volatile" registers (r26-r27 have been
	 * moved and r24-r25 will be restore later)
	 */

	ld		r23, x+
	ld		r22, x+
	ld		r21, x+
	ld		r20, x+
	ld		r19, x+
	ld		r18, x+

	/* Restore r2-r17 - Call-saved, "static" registers */

	ld		r17, x+
	ld		r16, x+
	ld		r15, x+
	ld		r14, x+
	ld		r13, x+
	ld		r12, x+
	ld		r11, x+
	ld		r10, x+
	ld		r9, x+
	ld		r8, x+
	ld		r7, x+
	ld		r6, x+
	ld		r5, x+
	ld		r4, x+
	ld		r3, x+
	ld		r2, x+

	/* Restore r1 - zero register (which may not be zero) */

	ld		r1, x+

	/* Restore r0 - the scratch register */

	ld		r0, x+

	/* Restore the status register (probably re-enabling interrupts) */

	ld		r24, x+
	out		_SFR_IO_ADDR(SREG), r24

	/* Restore r24-r25 - The temporary and IRQ number registers */

	ld		r25, x+
	ld		r24, x+

	/* Finally, recover X [r26-r27] from the new stack.  The PC remains on the new
	 * stack so that the user of this macro can return with ret (not reti, ret will
	 * preserve the restored interrupt state).
	 */

	pop		r27					/* R27 then R26 */
	pop		r26
	.endm

/********************************************************************************************
 * Name: USE_INTSTACK
 *
 * Description:
 *	Switch to the interrupt stack (if enabled in the configuration) and if the nesting level
 *	is equal to 0.  Increment the nesting level in any event.
 *
 * On Entry:
 *	sp - Current value of the user stack pointer
 *	tmp1, tmp2, and tmp3 are registers that can be used temporarily.
 *	All interrupts should still be disabled.
 *
 * At completion:
 *	If the nesting level is 0, then (1) the user stack pointer is saved at the base of the
 *	interrupt stack and sp points to the interrupt stack.
 *	The values of tmp1, tmp2, tmp3, and sp have been altered
 *
 ********************************************************************************************/

	.macro	USE_INTSTACK, tmp1, tmp2, tmp3
#if CONFIG_ARCH_INTERRUPTSTACK > 0
# warning "Not implemented"
#endif
	.endm

/********************************************************************************************
 * Name: RESTORE_STACK
 *
 * Description:
 *	Restore the user stack.  Not really.. actually only decrements the nesting level.  We
 *	always get the new stack pointer for the register save array.
 *
 * On Entry:
 *	tmp1 and tmp2 are registers that can be used temporarily.
 *	All interrupts must be disabled.
 *
 * At completion:
 *	Current nesting level is decremented
 *	The values of tmp1 and  tmp2 have been altered
 *
 ********************************************************************************************/

	.macro		RESTORE_STACK, tmp1, tmp2
#if CONFIG_ARCH_INTERRUPTSTACK > 0
#  warning "Not implemented"
#endif
	.endm

#endif /* __ASSEMBLY__ */
#endif /* __ARCH_AVR_SRC_AVR_EXCPTMACROS_H */