]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - cpu/nios/traps.S
Patch by Scott McNutt, 25 Apr 2004:
[karo-tx-uboot.git] / cpu / nios / traps.S
1 /*
2  * (C) Copyright 2003, Psyent Corporation <www.psyent.com>
3  * Scott McNutt <smcnutt@psyent.com>
4  *
5  * See file CREDITS for list of people who contributed to this
6  * project.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of
11  * the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21  * MA 02111-1307 USA
22  */
23
24 #include <config.h>
25
26 /*************************************************************************
27  * Register window underflow
28  *
29  * The register window underflow exception occurs whenever the lowest
30  * valid register window is in use (CWP=LO_LIMIT) and a save instruction
31  * is issued. The save moves CWP below LO_LIMIT, %sp is set as normal,
32  * then the exception is generated prior to executing the instruction
33  * after the save.
34  ************************************************************************/
35         .text
36         .global _cwp_lolimit
37         .align  4
38
39 _cwp_lolimit:
40
41         /* Sixteen words are always allocated by the compiler in every
42          * procedure's stack frame, always starting at %sp, for saving
43          * 'in' and 'local' registers on a window overflow.
44          *
45          * Save the 'global' and 'in' regs on stack. They are restored
46          * at cwp = HI_LIMIT. The 'local' regs aren't in-use at this point.
47          */
48         sts     [%sp,0], %g0            /* Save 'global' regs*/
49         sts     [%sp,1], %g1
50         sts     [%sp,2], %g2
51         sts     [%sp,3], %g3
52         sts     [%sp,4], %g4
53         sts     [%sp,5], %g5
54         sts     [%sp,6], %g6
55         sts     [%sp,7], %g7
56
57         sts     [%sp,8], %i0            /* Save 'in' regs */
58         sts     [%sp,9], %i1
59         sts     [%sp,10], %i2
60         sts     [%sp,11], %i3
61         sts     [%sp,12], %i4
62         sts     [%sp,13], %i5
63         sts     [%sp,14], %i6
64         sts     [%sp,15], %i7
65
66         /* Save current %sp and return address in a global so they are
67          * available at cwp = HI_LIMIT ... where the 'global'/'in' regs
68          * are restored. NOTE: %sp changes with cwp.
69          */
70         mov     %g7, %o7
71         mov     %g6, %sp
72
73         /* Get LO_LIMIT/HI_LIMIT to know where to start & stop. Note: in
74          * the underflow exception, cwp is __NOT__ guaranteed to be zero.
75          * If the OCI debug module is enabled the reset value for LO_LIMIT
76          * is 2, not 1 -- so cwp can be 1 or 0.
77          */
78         pfx     2                       /* WVALID */
79         rdctl   %g1
80         mov     %g2, %g1
81         pfx     0
82         and     %g1, 0x1f               /* g1 <- LO_LIMIT */
83         lsri    %g2, 5
84         pfx     0
85         and     %g2,0x1f                /* g2 <- HI_LIMIT */
86
87         /* Set istatus so cwp = HI_LIMIT after tret
88          */
89         movi    %g5, 0x1f
90         lsli    %g5, 4
91         not     %g5                     /* mask to clr cwp */
92         pfx     1                       /* istatus */
93         rdctl   %g0
94         and     %g0, %g5                /* clear cwp field */
95
96         mov     %g4, %g2
97         lsli    %g4, 4
98         or      %g0, %g4                /* cwp = HI_LIMIT */
99         pfx     1
100         wrctl   %g0                     /* update istatus */
101
102         /* Now move up the register file, saving as we go. When loop
103          * is first entered, %g1 is at LO_LIMIT.
104          */
105 0:
106         restore                         /* cwp++ */
107         sts     [%sp,0], %l0            /* Save "local" regs*/
108         sts     [%sp,1], %l1
109         sts     [%sp,2], %l2
110         sts     [%sp,3], %l3
111         sts     [%sp,4], %l4
112         sts     [%sp,5], %l5
113         sts     [%sp,6], %l6
114         sts     [%sp,7], %l7
115
116         sts     [%sp,8], %i0            /* Save 'in' regs */
117         sts     [%sp,9], %i1
118         sts     [%sp,10], %i2
119         sts     [%sp,11], %i3
120         sts     [%sp,12], %i4
121         sts     [%sp,13], %i5
122         sts     [%sp,14], %i6
123         sts     [%sp,15], %i7
124
125         cmp     %g1, %g2                /* cwp == HI_LIMIT ? */
126         skps    cc_ne                   /* if so, we're done */
127         br      1f
128         nop                             /* delay slot */
129
130         inc     %g1                     /* g1 <- cwp++ */
131         br      0b
132         nop                             /* delay slot */
133
134         /* At this point cwp = HI_LIMIT, so the global/in regs that were
135          * in place when the underflow occurred must be restored using
136          * the original stack pointer (saved in g6).
137          */
138 1:
139         mov     %o7, %g7                /* restore return addr */
140         mov     %sp, %g6                /* Restore original sp */
141
142         lds     %g0, [%sp,0]            /* Restore 'global' regs*/
143         lds     %g1, [%sp,1]
144         lds     %g2, [%sp,2]
145         lds     %g3, [%sp,3]
146         lds     %g4, [%sp,4]
147         lds     %g5, [%sp,5]
148         lds     %g6, [%sp,6]
149         lds     %g7, [%sp,7]
150
151         lds     %i0, [%sp,8]            /* Restore 'in' regs*/
152         lds     %i1, [%sp,9]
153         lds     %i2, [%sp,10]
154         lds     %i3, [%sp,11]
155         lds     %i4, [%sp,12]
156         lds     %i5, [%sp,13]
157         lds     %i6, [%sp,14]
158         lds     %i7, [%sp,15]
159
160         tret    %o7                     /* All done */
161
162 /*************************************************************************
163  * Register window overflow
164  *
165  * The register window overflow exception occurs whenever the highest
166  * valid register window is in use (cwp = HI_LIMIT) and a restore
167  * instruction is issued. Control is transferred to the overflow handler
168  * before the instruction following restore is executed.
169  *
170  * When a register window overflow exception is taken, the exception
171  * handler sees cwp at HI_LIMIT.
172  ************************************************************************/
173         .text
174         .global _cwp_hilimit
175         .align  4
176
177 _cwp_hilimit:
178
179         /* Save 'global'/'in' regs on the stack -- will restore when cwp
180          * is at LO_LIMIT. Locals don't need saving as they are going away.
181          */
182         sts     [%sp,0], %g0            /* Save "global" regs*/
183         sts     [%sp,1], %g1
184         sts     [%sp,2], %g2
185         sts     [%sp,3], %g3
186         sts     [%sp,4], %g4
187         sts     [%sp,5], %g5
188         sts     [%sp,6], %g6
189         sts     [%sp,7], %g7
190
191         sts     [%sp,8], %i0            /* Save 'in' regs */
192         sts     [%sp,9], %i1
193         sts     [%sp,10], %i2
194         sts     [%sp,11], %i3
195         sts     [%sp,12], %i4
196         sts     [%sp,13], %i5
197         sts     [%sp,14], %i6
198         sts     [%sp,15], %i7
199
200         /* The current %sp must be available in global to restore regs
201          * saved on stack. Need return addr as well ;-)
202          */
203         mov     %g7, %o7
204         mov     %g6, %sp
205
206         /* Get HI_LIMIT & LO_LIMIT
207          */
208         pfx     2                       /* WVALID */
209         rdctl   %g1
210         mov     %g2, %g1
211         pfx     0
212         and     %g1, 0x1f               /* g1 <- LO_LIMIT */
213         lsri    %g2, 5
214         pfx     0
215         and     %g2,0x1f                /* g2 <- HI_LIMIT */
216
217         /* Set istatus so cwp = LO_LIMIT after tret
218          */
219         movi    %g5, 0x1f
220         lsli    %g5, 4
221         not     %g5                     /* mask to clr cwp */
222         pfx     1                       /* istatus */
223         rdctl   %g0
224         and     %g0, %g5                /* clear cwp field */
225
226         mov     %g4, %g1                /* g4 <- LO_LIMIT */
227         lsli    %g4, 4
228         or      %g0, %g4                /* cwp = LO_LIMIT */
229         pfx     1
230         wrctl   %g0                     /* update istatus */
231
232         /* Move to cwp = LO_LIMIT-1 and restore 'in' regs.
233          */
234         subi    %g4,(1 << 4)            /* g4 <- LO_LIMIT - 1 */
235         rdctl   %g0
236         and     %g0, %g5                /* clear cwp field */
237         or      %g0, %g4                /* cwp = LO_LIMIT - 1 */
238         wrctl   %g0                     /* update status */
239         nop
240
241         mov     %sp, %g6                /* Restore sp */
242         lds     %i0, [%sp,8]            /* Restore 'in' regs */
243         lds     %i1, [%sp,9]
244         lds     %i2, [%sp,10]
245         lds     %i3, [%sp,11]
246         lds     %i4, [%sp,12]
247         lds     %i5, [%sp,13]
248         lds     %i6, [%sp,14]           /* sp in next window */
249         lds     %i7, [%sp,15]
250
251         /* Starting at LO_LIMIT-1, move up the register file, restoring
252          * along the way.
253          */
254 0:
255         restore                         /* cwp++ */
256         lds     %l0, [%sp,0]            /* Restore 'local' regs*/
257         lds     %l1, [%sp,1]
258         lds     %l2, [%sp,2]
259         lds     %l3, [%sp,3]
260         lds     %l4, [%sp,4]
261         lds     %l5, [%sp,5]
262         lds     %l6, [%sp,6]
263         lds     %l7, [%sp,7]
264
265         lds     %i0, [%sp,8]            /* Restore 'in' regs */
266         lds     %i1, [%sp,9]
267         lds     %i2, [%sp,10]
268         lds     %i3, [%sp,11]
269         lds     %i4, [%sp,12]
270         lds     %i5, [%sp,13]
271         lds     %i6, [%sp,14]           /* sp in next window */
272         lds     %i7, [%sp,15]
273
274         cmp     %g1, %g2                /* cwp == HI_LIMIT ? */
275         skps    cc_ne                   /* if so, we're done */
276         br      1f
277         nop                             /* delay slot */
278
279         inc     %g1                     /* cwp++ */
280         br      0b
281         nop                             /* delay slot */
282
283         /* All windows have been updated at this point, but the globals
284          * still need to be restored. Go to cwp = LO_LIMIT-1 to get
285          * some registers to use.
286          */
287 1:
288         rdctl   %g0
289         and     %g0, %g5                /* clear cwp field */
290         or      %g0, %g4                /* cwp = LO_LIMIT - 1 */
291         wrctl   %g0                     /* update status */
292         nop
293
294         /* Now there are some registers available to use in restoring
295          * the globals.
296          */
297         mov     %sp, %g6
298         mov     %o7, %g7
299
300         lds     %g0, [%sp,0]            /* Restore "global" regs*/
301         lds     %g1, [%sp,1]
302         lds     %g2, [%sp,2]
303         lds     %g3, [%sp,3]
304         lds     %g4, [%sp,4]
305         lds     %g5, [%sp,5]
306         lds     %g6, [%sp,6]
307         lds     %g7, [%sp,7]
308
309         /* The tret moves istatus -> status. istatus was already set for
310          * cwp = LO_LIMIT.
311          */
312
313         tret    %o7                     /* done */
314
315 /*************************************************************************
316  * Default exception handler
317  *
318  * The default handler passes control to external_interrupt(). So trap
319  * or hardware interrupt hanlders can be installed using the familiar
320  * irq_install_handler().
321  *
322  * Here, the stack is fixed-up and cwp is incremented prior to calling
323  * external_interrupt(). This lets the underflow and overflow handlers
324  * operate normally during the exception.
325  ************************************************************************/
326         .text
327         .global _def_xhandler
328         .align  4
329
330 _def_xhandler:
331
332         /* Allocate some stack space: 16 words at %sp to accomodate
333          * a reg window underflow, 8 words to save interrupted task's
334          * 'out' regs (which are now the 'in' regs), 8 words to preserve
335          * the 'global' regs and 3 words to save the return address,
336          * status and istatus. istatus must be saved in the event an
337          * underflow occurs in a dispatched handler. status is saved so
338          * a handler can access it on stack.
339          */
340         pfx     %hi((16+16+3) * 4)
341         subi    %fp, %lo((16+16+3) * 4)
342         mov     %sp, %fp
343
344         /* Save the 'global' regs and the interrupted task's 'out' regs
345          * (our 'in' regs) along with the return addr, status & istatus.
346          * First 16 words are for underflow exception.
347          */
348         rdctl   %l0                     /* status */
349         pfx     1                       /* istatus */
350         rdctl   %l1
351
352         sts     [%sp,16+0], %g0         /* Save 'global' regs*/
353         sts     [%sp,16+1], %g1
354         sts     [%sp,16+2], %g2
355         sts     [%sp,16+3], %g3
356         sts     [%sp,16+4], %g4
357         sts     [%sp,16+5], %g5
358         sts     [%sp,16+6], %g6
359         sts     [%sp,16+7], %g7
360
361         sts     [%sp,16+8], %i0         /* Save 'in' regs */
362         sts     [%sp,16+9], %i1
363         sts     [%sp,16+10], %i2
364         sts     [%sp,16+11], %i3
365         sts     [%sp,16+12], %i4
366         sts     [%sp,16+13], %i5
367         sts     [%sp,16+14], %i6
368         sts     [%sp,16+15], %i7
369
370         sts     [%sp,16+16], %l0        /* status */
371         sts     [%sp,16+17], %l1        /* istatus */
372         sts     [%sp,16+18], %o7        /* return addr */
373
374         /* Move to cwp+1 ... this guarantees cwp is at or above LO_LIMIT.
375          * Need to set IPRI=3 and IE=1 to enable underflow exceptions.
376          * NOTE: only the 'out' regs have been saved ... can't touch
377          * the 'in' or 'local' here.
378          */
379         restore                         /* cwp++ */
380         rdctl   %o0                     /* o0 <- status */
381
382         pfx     %hi(0x7e00)
383         movi    %o1, %lo(0x7e00)
384         not     %o1
385         and     %o0, %o1                /* clear IPRI */
386
387         pfx     %hi(0x8600)
388         movi    %o1, %lo(0x8600)
389         or      %o0, %o1                /* IPRI=3, IE=1 */
390
391         wrctl   %o0                     /* o0 -> status */
392         nop
393
394         /* It's ok to call a C routine now since cwp >= LO_LIMIT,
395          * interrupt task's registers are/will be preserved, and
396          * underflow exceptions can be handled.
397          */
398         pfx     %hi(external_interrupt@h)
399         movi    %o1, %lo(external_interrupt@h)
400         pfx     %xhi(external_interrupt@h)
401         movhi   %o1, %xlo(external_interrupt@h)
402         bgen    %o0, 4+2                /* 16 * 4 */
403         add     %o0, %sp                /* Ptr to regs */
404         call    %o1
405         nop
406
407         /* Move back to the exception register window, restore the 'out'
408          * registers, then return from exception.
409          */
410         rdctl   %o0                     /* o0 <- status */
411         subi    %o0, 16
412         wrctl   %o0                     /* cwp-- */
413         nop
414
415         mov     %sp, %fp
416         lds     %g0, [%sp,16+0]         /* Restore 'global' regs*/
417         lds     %g1, [%sp,16+1]
418         lds     %g2, [%sp,16+2]
419         lds     %g3, [%sp,16+3]
420         lds     %g4, [%sp,16+4]
421         lds     %g5, [%sp,16+5]
422         lds     %g6, [%sp,16+6]
423         lds     %g7, [%sp,16+7]
424
425         lds     %i0, [%sp,16+8]         /* Restore 'in' regs*/
426         lds     %i1, [%sp,16+9]
427         lds     %i2, [%sp,16+10]
428         lds     %i3, [%sp,16+11]
429         lds     %i4, [%sp,16+12]
430         lds     %i5, [%sp,16+13]
431         lds     %i6, [%sp,16+14]
432         lds     %i7, [%sp,16+15]
433
434         lds     %l0, [%sp,16+16]        /* status */
435         lds     %l1, [%sp,16+17]        /* istatus */
436         lds     %o7, [%sp,16+18]        /* return addr */
437
438         pfx     1
439         wrctl   %l1                     /* restore istatus */
440
441         pfx     %hi((16+16+3) * 4)
442         addi    %sp, %lo((16+16+3) * 4)
443         mov     %fp, %sp
444
445         tret    %o7                     /* Done */
446
447
448 /*************************************************************************
449  * Timebase Timer Interrupt -- This has identical structure to above,
450  * but calls timer_interrupt().  Doing it this way keeps things similar
451  * to other architectures (e.g. ppc).
452  ************************************************************************/
453         .text
454         .global _timebase_int
455         .align  4
456
457 _timebase_int:
458
459         /* Allocate  stack space.
460          */
461         pfx     %hi((16+16+3) * 4)
462         subi    %fp, %lo((16+16+3) * 4)
463         mov     %sp, %fp
464
465         /* Save the 'global' regs & 'out' regs (our 'in' regs)
466          */
467         rdctl   %l0                     /* status */
468         pfx     1                       /* istatus */
469         rdctl   %l1
470
471         sts     [%sp,16+0], %g0         /* Save 'global' regs*/
472         sts     [%sp,16+1], %g1
473         sts     [%sp,16+2], %g2
474         sts     [%sp,16+3], %g3
475         sts     [%sp,16+4], %g4
476         sts     [%sp,16+5], %g5
477         sts     [%sp,16+6], %g6
478         sts     [%sp,16+7], %g7
479
480         sts     [%sp,16+8], %i0         /* Save 'in' regs */
481         sts     [%sp,16+9], %i1
482         sts     [%sp,16+10], %i2
483         sts     [%sp,16+11], %i3
484         sts     [%sp,16+12], %i4
485         sts     [%sp,16+13], %i5
486         sts     [%sp,16+14], %i6
487         sts     [%sp,16+15], %i7
488
489         sts     [%sp,16+16], %l0        /* status */
490         sts     [%sp,16+17], %l1        /* istatus */
491         sts     [%sp,16+18], %o7        /* return addr */
492
493         /* Move to cwp+1.
494          */
495         restore                         /* cwp++ */
496         rdctl   %o0                     /* o0 <- status */
497
498         pfx     %hi(0x7e00)
499         movi    %o1, %lo(0x7e00)
500         not     %o1
501         and     %o0, %o1                /* clear IPRI */
502
503         pfx     %hi(0x8600)
504         movi    %o1, %lo(0x8600)
505         or      %o0, %o1                /* IPRI=3, IE=1 */
506
507         wrctl   %o0                     /* o0 -> status */
508         nop
509
510         /* Call timer_interrupt()
511          */
512         pfx     %hi(timer_interrupt@h)
513         movi    %o1, %lo(timer_interrupt@h)
514         pfx     %xhi(timer_interrupt@h)
515         movhi   %o1, %xlo(timer_interrupt@h)
516         bgen    %o0, 4+2                /* 16 * 4 */
517         add     %o0, %sp                /* Ptr to regs */
518         call    %o1
519         nop
520
521         /* Move back to the exception register window, restore the 'out'
522          * registers, then return from exception.
523          */
524         rdctl   %o0                     /* o0 <- status */
525         subi    %o0, 16
526         wrctl   %o0                     /* cwp-- */
527         nop
528
529         mov     %sp, %fp
530         lds     %g0, [%sp,16+0]         /* Restore 'global' regs*/
531         lds     %g1, [%sp,16+1]
532         lds     %g2, [%sp,16+2]
533         lds     %g3, [%sp,16+3]
534         lds     %g4, [%sp,16+4]
535         lds     %g5, [%sp,16+5]
536         lds     %g6, [%sp,16+6]
537         lds     %g7, [%sp,16+7]
538
539         lds     %i0, [%sp,16+8]         /* Restore 'in' regs*/
540         lds     %i1, [%sp,16+9]
541         lds     %i2, [%sp,16+10]
542         lds     %i3, [%sp,16+11]
543         lds     %i4, [%sp,16+12]
544         lds     %i5, [%sp,16+13]
545         lds     %i6, [%sp,16+14]
546         lds     %i7, [%sp,16+15]
547
548         lds     %l0, [%sp,16+16]        /* status */
549         lds     %l1, [%sp,16+17]        /* istatus */
550         lds     %o7, [%sp,16+18]        /* return addr */
551
552         pfx     1
553         wrctl   %l1                     /* restore istatus */
554
555         pfx     %hi((16+16+3) * 4)
556         addi    %sp, %lo((16+16+3) * 4)
557         mov     %fp, %sp
558
559         tret    %o7                     /* Done */
560
561 /*************************************************************************
562  * GDB stubs
563  ************************************************************************/
564         .text
565         .global _brkpt_hw_int, _brkpt_sw_int
566         .align  4
567
568 _brkpt_hw_int:
569         movi    %l1, 9
570         pfx     3
571         wrctl   %l1
572         pfx     4
573         wrctl   %l1
574
575 _brkpt_sw_int:
576         movi    %l1, 9
577         pfx     3
578         wrctl   %l1
579         pfx     4
580         wrctl   %l1
581
582         tret    %o7