summaryrefslogtreecommitdiff
path: root/nuttx/arch/avr/src/avr32/up_exceptions.S
blob: d7686e3d0a0521ca8417f6acc8493b2c4be53d3e (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
/****************************************************************************
 * arch/avr32/src/avr32/up_exceptions.S
 *
 *   Copyright (C) 2010 Gregory Nutt. All rights reserved.
 *   Author: Gregory Nutt <spudmonkey@racsa.co.cr>
 *
 * 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.
 *
 ****************************************************************************/

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

#include <nuttx/config.h>

#include <arch/avr32/avr32.h>
#include <arch/irq.h>

/****************************************************************************
 * External Symbols
 ****************************************************************************/

	.global		avr32_int0irqno			/* Returns IRQ number of INT0 event */
	.global		avr32_int1irqno			/* Returns IRQ number of INT1 event */
	.global		avr32_int2irqno			/* Returns IRQ number of INT2 event */
	.global		avr32_int3irqno			/* Returns IRQ number of INT3 event */
	.global		up_doirq				/* Dispatch an IRQ                  */
	.global		up_fullcontextrestore	/* Restore new task contex          */

/****************************************************************************
 * Macros
 ****************************************************************************/

/* Exception entry logic.  On entry, thee context save area looks like:     */
/*     xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx SR PC                   */
/*                                                  ^     ^+2*4             */
/* Upon joining common logic, the context save are will look like:          */
/*     xx xx xx xx xx xx xx xx xx xx xx xx xx xx 10 SR PC                   */
/*                                               ^        ^+3*4             */
/* and r10 will hold the exception's IRQ number                             */

	.macro  HANDLER, label, irqno
\label:
	st.w	--sp, r10
	mov		r10, \irqno
	rjmp	avr32_xcptcommon	/* FIXME!!! Need IRQ in a register          */
	.endm

/****************************************************************************
 * Exception Vector Table
 ****************************************************************************/

/* The Exception Vector Base Address (EVBA) register will contain "a        */
/* pointer to the exception routines. All exception routines start at this  */
/* address, or at a defined offset relative to the address. Special         */
/* alignment requirements may apply for EVBA, depending on the              */
/* implementation of the interrupt controller."                             */

/* REVISIT:  This alignment requirement may be different on other AVR32s    */

	.text
	.balign 0x200

	.global	vectortab
	.type	vectortab, @function
vectortab:
	lda.w	pc, avr32_unrec			/* EVBA+0x00 Unrecoverable exception    */
	lda.w	pc, avr32_tlbmult		/* EVBA+0x04 TLB multiple hit           */
	lda.w	pc, avr32_busdata		/* EVBA+0x08 Bus error data fetch       */
	lda.w	pc, avr32_businst		/* EVBA+0x0c Bus error instr fetch      */
	lda.w	pc, avr32_nmi			/* EVBA+0x10 NMI                        */
	lda.w	pc, avr32_instaddr		/* EVBA+0x14 Instruction Address        */
	lda.w	pc, avr32_itlbrot		/* EVBA+0x18 ITLB Protection            */
	lda.w	pc, avr32_bp			/* EVBA+0x1c Breakpoint                 */
	lda.w	pc, avr32_invinst		/* EVBA+0x20 Illegal Opcode             */
	lda.w	pc, avr32_unimpinst		/* EVBA+0x24 Unimplemented instruction  */
	lda.w	pc, avr32_priv			/* EVBA+0x28 Privilege violation        */
	lda.w	pc, avr32_fp			/* EVBA+0x2c Floating-point             */
	lda.w	pc, avr32_cop			/* EVBA+0x30 Coprocessor absent         */
	lda.w	pc, avr32_rddata		/* EVBA+0x34 Data Address (Read)        */
	lda.w	pc, avr32_wrdata		/* EVBA+0x38 Data Address (Write)       */
	lda.w	pc, avr32_tddtlbprot	/* EVBA+0x3c DTLB Protection (Read)     */
	lda.w	pc, avr32_wrdtlbprot	/* EVBA+0x40 DTLB Protection (Write)    */
	lda.w	pc, avr32_dltbmod		/* EVBA+0x44 DTLB Modified              */
	.rept	2
	lda.w	pc,	avr32_badvector		/* EVBA+0x48-0x4c No such vector        */
	.endr
	lda.w	pc, avr32_itlbmiss		/* EVBA+0x50 ITLB Miss                  */
	.rept	3
	lda.w	pc,	avr32_badvector		/* EVBA+0x54-0x5c No such vector        */
	.endr
	lda.w	pc, avr32_rddtlb		/* EVBA+0x60 DTLB Miss (Read)           */
	.rept	3
	lda.w	pc,	avr32_badvector		/* EVBA+0x64-0x6c No such vector        */
	.endr
	lda.w	pc, avr32_wrdtlb		/* EVBA+0x70 DTLB Miss (Write)          */
	.rept	(3+4*8)
	lda.w	pc,	avr32_badvector		/* EVBA+0x74-0xfc No such vector        */
	.endr
	lda.w	pc, avr32_super			/* EVBA+0x100 Supervisor call           */

/****************************************************************************
 * Interrupts
 ****************************************************************************/

/* The interrupt controller must provide an address that is relative to the */
/* the EVBA so it is natural to define these interrupt vectors just after   */
/* the exception table. On entry to each interrupt handler, R8-R12, LR, PC  */
/* and SR have already been pushed onto the system stack by the MCU.        */
/*                                                                          */
/* An interrupt may disappear while it is being fetched by the CPU and,     */
/* hence spurious interrupt may result.                                     */

	.balign 4
	.global	avr32_int0
avr32_int0:
	mov		r12, 0				/* r12=interrupt level 0                    */
	rjmp	avr32_intcommon		/* Jump to common interrupt handling logic  */

	.balign	4
	.global	avr32_int1
avr32_int1:
	mov		r12, 1				/* r12=interrupt level 1                    */
	rjmp	avr32_intcommon		/* Jump to common interrupt handling logic  */

	.balign	4
	.global	avr32_int2
avr32_int2:
	mov		r12, 2				/* r12=interrupt level 2                    */
	rjmp	avr32_intcommon		/* Jump to common interrupt handling logic  */

	.balign	4
	.global	avr32_int3
avr32_int3:
	mov		r12, 3				/* r12=interrupt level 3                    */

/* Common interrupt handling logic.  R12 holds the interrupt level index    */

avr32_intcommon:
	mcall	.Lavr32_intirqno	/* Get the IRQ number of the int0 event     */
	cp.w	r12, 0				/* Negative returned if spurious interrupt  */
	brge	avr32_common		/* Jump to the common xcptn handling logic  */
	rete						/* Ignore spurious interrupt                */

.Lavr32_intirqno:
	.word	avr32_intirqno

/****************************************************************************
 * Exception Vector Handlers
 ****************************************************************************/

/* Exception Handlers:                                                      */
/* On entry to each, the context save area looks like this:                 */
/*     xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx SR PC                   */
/*                                                  ^     ^+2*4             */
 
	HANDLER	avr32_unrec, AVR32_IRQ_UNREC			/* Unrecoverable xcptn  */
	HANDLER	avr32_tlbmult, AVR32_IRQ_TLBMULT		/* TLB multiple hit     */
	HANDLER	avr32_busdata, AVR32_IRQ_BUSDATA		/* Bus error data fetch */
	HANDLER	avr32_businst, AVR32_IRQ_BUSINST		/* Bus err instr fetch  */
	HANDLER	avr32_nmi, AVR32_IRQ_NMI				/* NMI                  */
	HANDLER	avr32_instaddr, AVR32_IRQ_INSTADDR		/* Instruction Address  */
	HANDLER	avr32_itlbrot, AVR32_IRQ_ITLBPROT		/* ITLB Protection      */
	HANDLER	avr32_bp, AVR32_IRQ_BP					/* Breakpoint           */
	HANDLER	avr32_invinst, AVR32_IRQ_INVINST		/* Illegal Opcode       */
	HANDLER	avr32_unimpinst, AVR32_IRQ_UNIMPINST	/* Unimplemented intsr  */
	HANDLER	avr32_priv, AVR32_IRQ_PRIV 				/* Privilege violation  */
	HANDLER	avr32_fp, AVR32_IRQ_FP					/* Floating-point       */
	HANDLER	avr32_cop, AVR32_IRQ_COP				/* Coprocessor absent   */
	HANDLER	avr32_rddata, AVR32_IRQ_RDDATA			/* Data Address (RD)    */
	HANDLER	avr32_wrdata, AVR32_IRQ_WRDATA 			/* Data Address (WR)    */
	HANDLER	avr32_tddtlbprot, AVR32_IRQ_RDDTLBPROT	/* DTLB Protection (RD) */
	HANDLER	avr32_wrdtlbprot, AVR32_IRQ_WRDTLBPROT	/* DTLB Protection (WR) */
	HANDLER	avr32_dltbmod, AVR32_IRQ_DLTBMOD		/* DTLB Modified        */
	HANDLER	avr32_itlbmiss, AVR32_IRQ_ITLBMISS		/* ITLB Miss            */
	HANDLER	avr32_rddtlb, AVR32_IRQ_RDDTLB 			/* DTLB Miss (RD)       */
	HANDLER	avr32_wrdtlb, AVR32_IRQ_WRDTLB			/* DTLB Miss (WR)       */
	HANDLER	avr32_super, AVR32_IRQ_SUPER			/* Supervisor call      */
	HANDLER	avr32_badvector, AVR32_IRQ_BADVECTOR	/* No such vector       */

/* Common exception handling logic.  Unlike the interrupt handlers, the     */
/* exception handlers do not save R8-R12, and LR on the stack.  Only the PC */
/* and SR have been pushed onto the system stack by the MCU.  The following */
/* logic creates a common stack frame for exception handlers prior to       */
/* joining to the common interrupt/exception logic below.                   */
/*                                                                          */
/* The context save area looks like this on entry to the HANDLER above.     */
/*     xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx SR PC                   */
/*                                                  ^     ^+2*4             */
/* Upon joining common logic here, the context save are will loke:          */
/*     xx xx xx xx xx xx xx xx xx xx xx xx xx xx 10 SR PC                   */
/*                                               ^        ^+3*4             */
/* and r10 will hold the exception's IRQ number                             */
/*                                                                          */
/* either (1) non-time-critical, or (2) fatal.  Obvious, that would not be  */
/* the case if TLB missing handling is required.  Such time-critical vector */
/* handling should be handled differently.                                  */

avr32_xcptcommon:
/* Save r10-r12, lr on the stack:                                           */
/*     xx xx xx xx xx xx xx xx xx xx xx LR 12 11 10 SR PC                   */
/*                                      ^           ^+4*4 ^+6*4             */

	stm		--sp, r11-r12, lr

/* Move SR and PC into the expected position:                               */
/*     xx xx xx xx xx xx xx xx xx SR PC LI 12 11 10 SR PC                   */
/*                                ^                       ^+8*4             */

	ld.w	r11, sp[4*4]
	ld.w	r12, sp[5*4]
	stm		--sp, r11-r12

/* Save r8 and r8:                                                          */
/*     xx xx xx xx xx xx xx xx xx SR PC LI 12 11 10 SR PC                   */
/*                                ^                 ^+6*4 ^+8*4             */
 
	st.w	sp[6*4], r9
	st.w	sp[7*4], r8

/* Move the IRQ number in r12 and fall through to the common event handling */
/* logic.                                                                   */

	mov		r12, r10

/****************************************************************************
 * Common Event Handling Logic
 ****************************************************************************/

/* After this point, logic to manage interrupts and exceptions is           */
/* equivalent. Here we have:                                                */
/*                                                                          */
/*   R8-R12, LR, SR, and the PC on the stack.                               */
/*   R12 holds the IRQ number to be dispatched.                             */
/*                                                                          */
/* The context save area looks like this:                                   */
/*     xx xx xx xx xx xx xx xx xx SR PC LR 12 11 10 09 08                   */
/*                                ^                       ^+8*4             */
/* This function will finish construction of the register save structure    */
/* and call the IRQ dispatching logic.                                      */

avr32_common:
    /* Disable interrupts in the current SR.  This is necessary because the */
	/* AVR32 permits nested interrupts (if they are of higher priority).    */
	/* We can support nested interrupts without some effort because:        */
	/* - The global variable current_regs permits only one interrupt,       */
	/* - If CONFIG_ARCH_INTERRUPTSTACK is defined, then there is a single   */
	/*   interrupt stack, and                                               */
	/* - Probably other things.                                             */

	ssrf	AVR32_SR_GM_SHIFT

	/* Save the SP (as it was before the interrupt) in the conext save      */
	/* structure.                                                           */
	/*     xx xx xx xx xx xx xx xx SP SR PC LR 12 11 10 09 08               */
	/*                             ^sp                                      */

	mov		r8, sp
	sub		r8, -8*4
	st.w	--sp, r8

	/* Saving R0-R7 is all that is left to complete the context save.       */
	/*     07 06 05 04 03 02 01 00 SP SR PC LR 12 11 10 09 08               */
	/*     ^sp                                                              */

	stm		--sp, r0-r7

	/* Now call up_doirq passing the IRQ number in r12 and the base address */
	/* of the register context save area in r11.                            */

	mov		r11, sp

	/* Switch to the interrrupt stack if so configured.  Move the current   */
	/* stack pointer into a preserved register (r7) and set the interrupt   */
	/* stack pointer.                                                       */

#if CONFIG_ARCH_INTERRUPTSTACK > 3
	mov		r7, sp
	lddpc	sp, .Linstackbaseptr
#endif

	/* Call up_doirq with r12=IRQ number and r11=register save area         */

	mcall	.Ldoirqptr

	/* Restore the user stack pointer.                                      */
	/*     07 06 05 04 03 02 01 00 SP SR PC LR 12 11 10 09 08               */
	/*     ^sp                                                              */

#if CONFIG_ARCH_INTERRUPTSTACK > 3
	mov		sp, r7
#endif

	/* On return, r12 will hold the new address of the register context     */
	/* save area.  On an interrupt contex switch, this will (1) not be the  */
	/* same as the value of r12 passed to up_doirq(), and (2) may not       */
	/* reside on a stack.                                                   */

	cp.w	sp, r12
	brne	1f

	/* No context switch... do the simple return.  First, restore r0-r7.    */
	/*     xx xx xx xx xx xx xx xx SP SR PC LR 12 11 10 09 08               */
	/*                             ^sp                                      */

	ldm		sp++, r0-r7

	/* Skip over the saved stack pointer and return from the interrupt.     */
	/*     xx xx xx xx xx xx xx xx xx SR PC LR 12 11 10 09 08               */
	/*                                ^sp                                   */

	sub		sp, -4
	rete

	/* Context switch... jump to up_fullcontestrestor with r12=address of   */
	/* the task context to restore.                                         */

1:
	lddpc	pc, .Lfullcontextrestoreptr

.Ldoirqptr:
	.word	up_doirq
.Lfullcontextrestoreptr:
	.word	up_fullcontextrestore

#if CONFIG_ARCH_INTERRUPTSTACK > 3
.Linstackbaseptr:
	.word	.Lintstackbase
#endif
	.size	vectortab, .-vectortab

/************************************************************************************
 *  Name: up_interruptstack
 ************************************************************************************/

#if CONFIG_ARCH_INTERRUPTSTACK > 3
	.bss
	.align	4
	.globl	up_interruptstack
	.type	up_interruptstack, object
up_interruptstack:
	.skip	(CONFIG_ARCH_INTERRUPTSTACK & ~3)
.Lintstackbase:
	.size	up_interruptstack, .-up_interruptstack
#endif
	.end