1 |
158 |
chris |
/* cpu_asm.c ===> cpu_asm.S or cpu_asm.s
|
2 |
|
|
*
|
3 |
|
|
* This file contains the basic algorithms for all assembly code used
|
4 |
|
|
* in an specific CPU port of RTEMS. These algorithms must be implemented
|
5 |
|
|
* in assembly language
|
6 |
|
|
*
|
7 |
|
|
* NOTE: This is supposed to be a .S or .s file NOT a C file.
|
8 |
|
|
*
|
9 |
|
|
* COPYRIGHT (c) 1989-1999.
|
10 |
|
|
* On-Line Applications Research Corporation (OAR).
|
11 |
|
|
*
|
12 |
|
|
* The license and distribution terms for this file may be
|
13 |
|
|
* found in the file LICENSE in this distribution or at
|
14 |
|
|
* http://www.OARcorp.com/rtems/license.html.
|
15 |
|
|
*
|
16 |
208 |
chris |
* This file adapted from no_bsp board library of the RTEMS distribution.
|
17 |
|
|
* The body has been modified for the Bender Or1k implementation by
|
18 |
|
|
* Chris Ziomkowski. <chris@asics.ws>
|
19 |
158 |
chris |
*/
|
20 |
|
|
|
21 |
|
|
/*
|
22 |
|
|
* This is supposed to be an assembly file. This means that system.h
|
23 |
|
|
* and cpu.h should not be included in a "real" cpu_asm file. An
|
24 |
|
|
* implementation in assembly should include "cpu_asm.h>
|
25 |
|
|
*/
|
26 |
|
|
|
27 |
|
|
#include <rtems/system.h>
|
28 |
|
|
#include <rtems/score/cpu.h>
|
29 |
|
|
/* #include "cpu_asm.h> */
|
30 |
|
|
|
31 |
|
|
/*
|
32 |
|
|
* _CPU_Context_save_fp_context
|
33 |
|
|
*
|
34 |
|
|
* This routine is responsible for saving the FP context
|
35 |
|
|
* at *fp_context_ptr. If the point to load the FP context
|
36 |
|
|
* from is changed then the pointer is modified by this routine.
|
37 |
|
|
*
|
38 |
|
|
* Sometimes a macro implementation of this is in cpu.h which dereferences
|
39 |
|
|
* the ** and a similarly named routine in this file is passed something
|
40 |
|
|
* like a (Context_Control_fp *). The general rule on making this decision
|
41 |
|
|
* is to avoid writing assembly language.
|
42 |
|
|
*
|
43 |
|
|
* or1k specific Information:
|
44 |
|
|
*
|
45 |
|
|
* This implementation of RTEMS considers the concept of
|
46 |
|
|
* "fast context switching", as defined in the or1k architecture
|
47 |
|
|
* specification. Whether or not this makes a significant
|
48 |
|
|
* impact on speed is dubious, however it is not a significant
|
49 |
|
|
* impediment to include it. It probably wastes a few cycles on
|
50 |
|
|
* every floating point context switch.
|
51 |
|
|
*
|
52 |
|
|
* This implementation will currently not work on a processor where
|
53 |
|
|
* the integer unit and floating point unit are not the same size. I
|
54 |
|
|
* am waiting on an architecture change to make this feasible. It
|
55 |
|
|
* should work fine on 64 bit architectures, except for the fact that
|
56 |
|
|
* the variables are declared as 32 bits. This shouldn't really make
|
57 |
|
|
* a difference, as the fact that they must be registers should force
|
58 |
|
|
* them into a 64 bit word anyway.
|
59 |
|
|
*
|
60 |
|
|
* The decision as to whether to do 32 or 64 bit saves is performed
|
61 |
|
|
* at run time based on the configuration of the CPUCFGR register. This
|
62 |
|
|
* takes a performance hit of a few cycles, but this should be a very
|
63 |
|
|
* small percentage of the total number of cycles necessary to do the
|
64 |
|
|
* save, and doesn't require special code for 32 or 64 bit versions.
|
65 |
|
|
*
|
66 |
|
|
* ADDITIONAL INFORMATION:
|
67 |
|
|
*
|
68 |
|
|
* It has been unanimously agreed that floating point will not be
|
69 |
|
|
* included in the initial releases of the Or1k chips, and that
|
70 |
|
|
* significant changes to the floating point architecture may
|
71 |
|
|
* occur before any such release will ever be implemented. The code
|
72 |
|
|
* below is therefore never called and never used.
|
73 |
|
|
*/
|
74 |
|
|
|
75 |
|
|
void _CPU_Context_save_fp(
|
76 |
|
|
void **fp_context_ptr
|
77 |
|
|
)
|
78 |
|
|
{
|
79 |
|
|
register unsigned32 temp;
|
80 |
|
|
register unsigned32 address = (unsigned32)(*fp_context_ptr);
|
81 |
|
|
register unsigned32 xfer;
|
82 |
|
|
register unsigned32 loop;
|
83 |
|
|
|
84 |
|
|
/* %0 is a temporary register which is used for several
|
85 |
208 |
chris |
values throughout the code. %3 contains the address
|
86 |
158 |
chris |
to save the context, and is modified during the course
|
87 |
208 |
chris |
of the context save. %1 is a second dummy register
|
88 |
158 |
chris |
which is used during transfer of the floating point
|
89 |
208 |
chris |
value to memory. %2 is an end of loop marker which
|
90 |
|
|
is compared against the pointer %3. */
|
91 |
158 |
chris |
|
92 |
208 |
chris |
asm volatile ("l.mfspr %0,r0,0x02 \n\t" /* CPUCFGR */
|
93 |
158 |
chris |
"l.andi %0,%0,0x380 \n\t" /* OF32S or OV64S or OF64S */
|
94 |
|
|
"l.sfnei %0,0x0 \n\t"
|
95 |
208 |
chris |
"l.bf _L_nofps \n\t" /* exit if no floating point */
|
96 |
|
|
"l.sfeqi %0,0x080 \n\t" /* (DELAY) single precision? */
|
97 |
158 |
chris |
"l.mfspr %0,r0,0x11 \n\t" /* Load Status Register */
|
98 |
|
|
"l.srli %0,%0,58 \n\t" /* Move CID into low byte*32 */
|
99 |
208 |
chris |
"l.bnf _L_spfp_loops \n\t" /* Branch on single precision */
|
100 |
|
|
"l.addi %2,%0,0x20 \n" /* Terminating condition */
|
101 |
158 |
chris |
/**** Double Precision Floating Point Section ****/
|
102 |
208 |
chris |
"_L_dpfp_loops: \n\t"
|
103 |
|
|
"l.mfspr %1,%0,0x600 \n\t" /* Load VFRx */
|
104 |
|
|
"l.sd 0(%3),%1 \n\t" /* Save VFRx */
|
105 |
158 |
chris |
"l.addi %0,%0,0x01 \n\t" /* Increment counter */
|
106 |
208 |
chris |
"l.sfeq %0,%2 \n\t" /* Branch if incomplete */
|
107 |
|
|
"l.bf _L_dpfp_loops \n\t"
|
108 |
|
|
"l.addi %3,%3,0x08 \n\t" /* (DELAY) update pointer */
|
109 |
|
|
"l.bnf _L_nofps \n\t" /* exit */
|
110 |
158 |
chris |
"l.nop \n"
|
111 |
|
|
/**** Single Precision Floating Point Section ****/
|
112 |
208 |
chris |
"_L_spfp_loops: \n\t"
|
113 |
|
|
"l.mfspr %1,%0,0x600 \n\t" /* Load VFRx */
|
114 |
|
|
"l.sw 0(%3),%1 \n\t" /* Save VFRx */
|
115 |
158 |
chris |
"l.addi %0,%0,0x01 \n\t" /* Increment counter */
|
116 |
208 |
chris |
"l.sfeq %0,%2 \n\t" /* Branch if incomplete */
|
117 |
|
|
"l.bf _L_spfp_loops \n\t"
|
118 |
|
|
"l.addi %3,%3,0x04 \n" /* (DELAY) update pointer */
|
119 |
|
|
"_L_nofps: \n\t" /* End of context save */
|
120 |
|
|
: "=&r" (temp), "=r" (xfer), "=&r" (loop), "+r" (address));
|
121 |
158 |
chris |
}
|
122 |
|
|
|
123 |
|
|
/*
|
124 |
|
|
* _CPU_Context_restore_fp_context
|
125 |
|
|
*
|
126 |
|
|
* This routine is responsible for restoring the FP context
|
127 |
|
|
* at *fp_context_ptr. If the point to load the FP context
|
128 |
|
|
* from is changed then the pointer is modified by this routine.
|
129 |
|
|
*
|
130 |
|
|
*
|
131 |
|
|
*/
|
132 |
|
|
|
133 |
|
|
void _CPU_Context_restore_fp(
|
134 |
|
|
void **fp_context_ptr
|
135 |
|
|
)
|
136 |
|
|
{
|
137 |
|
|
register unsigned32 temp;
|
138 |
|
|
register unsigned32 address = (unsigned32)(*fp_context_ptr);
|
139 |
|
|
register unsigned32 xfer;
|
140 |
|
|
register unsigned32 loop;
|
141 |
|
|
|
142 |
|
|
/* The reverse of Context_save_fp */
|
143 |
|
|
/* %0 is a temporary register which is used for several
|
144 |
|
|
values throughout the code. %1 contains the address
|
145 |
|
|
to save the context, and is modified during the course
|
146 |
|
|
of the context save. %2 is a second dummy register
|
147 |
|
|
which is used during transfer of the floating point
|
148 |
|
|
value to memory. %3 is an end of loop marker which
|
149 |
|
|
is compared against the pointer %1. */
|
150 |
|
|
|
151 |
208 |
chris |
asm volatile ("l.mfspr %0,r0,0x02 \n\t" /* CPUCFGR */
|
152 |
158 |
chris |
"l.andi %0,%0,0x380 \n\t" /* OF32S or OV64S or OF64S */
|
153 |
|
|
"l.sfnei %0,0x0 \n\t"
|
154 |
208 |
chris |
"l.bf _L_nofpr \n\t" /* exit if no floating point */
|
155 |
|
|
"l.sfeqi %0,0x080 \n\t" /* (DELAY) single precision? */
|
156 |
158 |
chris |
"l.mfspr %0,r0,0x11 \n\t" /* Load Status Register */
|
157 |
|
|
"l.srli %0,%0,58 \n\t" /* Move CID into low byte*32 */
|
158 |
208 |
chris |
"l.bnf _L_spfp_loopr \n\t" /* Branch on single precision */
|
159 |
158 |
chris |
"l.addi %3,%0,0x20 \n" /* Terminating condition */
|
160 |
|
|
/**** Double Precision Floating Point Section ****/
|
161 |
208 |
chris |
"_L_dpfp_loopr: \n\t"
|
162 |
158 |
chris |
"l.mfspr %2,%0,0x600 \n\t" /* Load VFRx */
|
163 |
|
|
"l.sd 0(%1),%2 \n\t" /* Save VFRx */
|
164 |
|
|
"l.addi %0,%0,0x01 \n\t" /* Increment counter */
|
165 |
|
|
"l.sfeq %0,%3 \n\t" /* Branch if incomplete */
|
166 |
208 |
chris |
"l.bf _L_dpfp_loopr \n\t"
|
167 |
158 |
chris |
"l.addi %1,%1,0x08 \n\t" /* (DELAY) update pointer */
|
168 |
208 |
chris |
"l.bnf _L_nofpr \n\t" /* exit */
|
169 |
158 |
chris |
"l.nop \n"
|
170 |
|
|
/**** Single Precision Floating Point Section ****/
|
171 |
208 |
chris |
"_L_spfp_loopr: \n\t"
|
172 |
158 |
chris |
"l.mfspr %2,%0,0x600 \n\t" /* Load VFRx */
|
173 |
|
|
"l.sw 0(%1),%2 \n\t" /* Save VFRx */
|
174 |
|
|
"l.addi %0,%0,0x01 \n\t" /* Increment counter */
|
175 |
|
|
"l.sfeq %0,%3 \n\t" /* Branch if incomplete */
|
176 |
208 |
chris |
"l.bf _L_spfp_loopr \n\t"
|
177 |
158 |
chris |
"l.addi %1,%1,0x04 \n" /* (DELAY) update pointer */
|
178 |
208 |
chris |
"_L_nofpr: \n\t" /* End of context save */
|
179 |
158 |
chris |
: "=&r" (temp), "+r" (address), "=r" (xfer), "=&r" (loop));
|
180 |
|
|
}
|
181 |
|
|
|
182 |
|
|
/* _CPU_Context_switch
|
183 |
|
|
*
|
184 |
|
|
* This routine performs a normal non-FP context switch.
|
185 |
|
|
*
|
186 |
|
|
* NO_CPU Specific Information:
|
187 |
|
|
*
|
188 |
|
|
* XXX document implementation including references if appropriate
|
189 |
|
|
*/
|
190 |
|
|
|
191 |
|
|
void _CPU_Context_switch(
|
192 |
|
|
Context_Control *run,
|
193 |
|
|
Context_Control *heir
|
194 |
|
|
)
|
195 |
|
|
{
|
196 |
208 |
chris |
register unsigned32 temp1 = 0;
|
197 |
|
|
register unsigned32 temp2 = 0;
|
198 |
158 |
chris |
|
199 |
|
|
/* This function is really tricky. When this function is called,
|
200 |
|
|
we should save our state as we need it, and then grab the
|
201 |
|
|
new state from the pointer. We then do a longjump to this
|
202 |
|
|
code, replacing the current stack pointer with the new
|
203 |
|
|
environment. This function never returns. Instead, at some
|
204 |
|
|
later time, another person will call context switch with
|
205 |
|
|
our pointer in the heir variable, and they will longjump
|
206 |
|
|
to us. We will then continue. Let's see how this works... */
|
207 |
|
|
|
208 |
|
|
/* Technically, we could probably not worry about saving r3
|
209 |
|
|
and r4, since these are parameters guaranteed to be saved
|
210 |
|
|
by the calling function. We could also probably get away
|
211 |
|
|
without saving r11, as that is filled in by the return
|
212 |
|
|
statement. But as a first cut I'm in favor of just saving
|
213 |
|
|
everything.... */
|
214 |
|
|
|
215 |
|
|
/* We could be more efficient and use compile time directives
|
216 |
|
|
for 32 or 64 bit, but this will allow the code to run on
|
217 |
|
|
everything without modification. Feel free to comment the
|
218 |
|
|
relevant sections out if you don't need it. */
|
219 |
|
|
|
220 |
|
|
/* We should probably write this whole routine in assembly
|
221 |
|
|
so that we can have seperate entry points for self restore
|
222 |
|
|
or context switch. You can't jump to local labels from
|
223 |
|
|
inline assembly across function calls, and I don't feel
|
224 |
|
|
like embedding all the .global directives here...it really
|
225 |
|
|
screws up the debugger. Oh well, what's 2 more instructions
|
226 |
|
|
and a branch really cost... */
|
227 |
|
|
|
228 |
|
|
/* One thing which we should do is check for 32 or 64 bit models
|
229 |
|
|
first, and then do one branch to the appropriate code section.
|
230 |
|
|
Currently, we check the architecture bit in CPUCFGR twice. Once
|
231 |
|
|
during the load section and again during restore. That is inefficient,
|
232 |
|
|
and considering this code is huge anyway, saving the few bytes
|
233 |
|
|
simply doesn't make any practical sense. FIX THIS LATER. */
|
234 |
|
|
|
235 |
|
|
/* Note that this routine assumes software context switches are
|
236 |
|
|
done with the same CID. In other words, it will not manage
|
237 |
|
|
the CIDs and assign a new one as necessary. If you tell it
|
238 |
|
|
to restore a context at CID 2, and the current one is at CID
|
239 |
|
|
4, it will do what it is told. It will overwrite the registers
|
240 |
|
|
for context ID 2, meaning they are irretrievably lost. I hope
|
241 |
|
|
you saved them earlier.... */
|
242 |
|
|
|
243 |
|
|
/* Note that you can have a context jump anywhere you want, although
|
244 |
|
|
by default we will jump to the L_restore label. If you then modify
|
245 |
|
|
the location in the Context_Control structure, it will continue
|
246 |
208 |
chris |
whereever you told it to go. Note however that you had better
|
247 |
158 |
chris |
also have cleaned up the stack and frame pointers though, because
|
248 |
|
|
they are probably still set with the values obtained from
|
249 |
|
|
entering this function... */
|
250 |
|
|
|
251 |
208 |
chris |
asm volatile ("l.sfeqi %3,0x0 \n\t" /* Is this a self restore? */
|
252 |
|
|
"l.bf _L_restore \n\t" /* Yes it is...go there */
|
253 |
|
|
"l.nop \n\t"
|
254 |
|
|
|
255 |
|
|
"l.lwz %0,0(%3) \n\t" /* Prefetch new context */
|
256 |
|
|
"l.mfspr %2,r0,0x11 \n\t" /* Status Register */
|
257 |
|
|
"l.sw 0(%1),%2 \n\t" /* Save it */
|
258 |
|
|
"l.srli %2,%2,28 \n\t" /* Move CID into low byte */
|
259 |
|
|
"l.mfspr %0,%2,0x20 \n\t" /* Offset from EPCR */
|
260 |
158 |
chris |
"l.sw 4(%1),%0 \n\t" /* Store it */
|
261 |
208 |
chris |
"l.mfspr %0,%2,0x30 \n\t" /* Offset from EEAR */
|
262 |
158 |
chris |
"l.sw 8(%1),%0 \n\t" /* Store it */
|
263 |
208 |
chris |
"l.mfspr %0,%2,0x40 \n\t" /* Offset from ESR */
|
264 |
|
|
"l.sw 12(%1),%0 \n\t" /* Store it */
|
265 |
158 |
chris |
"l.mfspr %0,r0,0x02 \n\t" /* CPUCFGR */
|
266 |
|
|
"l.andi %0,%0,0x40 \n\t" /* OB64S */
|
267 |
|
|
"l.sfnei %0,0x0 \n\t"
|
268 |
208 |
chris |
"l.bf _L_64bit \n\t" /* 64 bit architecture */
|
269 |
|
|
"l.movhi %0,hi(_L_restore)\n\t"
|
270 |
158 |
chris |
|
271 |
|
|
/**** 32 bit implementation ****/
|
272 |
208 |
chris |
"l.ori %0,%0,lo(_L_restore)\n\t"
|
273 |
158 |
chris |
"l.sw 140(%1),%0 \n\t" /* Save the PC */
|
274 |
208 |
chris |
"l.lwz %0,140(%3) \n\t" /* New PC. Expect cache miss */
|
275 |
158 |
chris |
"l.sw 16(%1),r1 \n\t"
|
276 |
|
|
"l.sw 20(%1),r2 \n\t"
|
277 |
|
|
"l.sw 24(%1),r3 \n\t"
|
278 |
|
|
"l.sw 28(%1),r4 \n\t"
|
279 |
|
|
"l.sw 32(%1),r5 \n\t"
|
280 |
|
|
"l.sw 36(%1),r6 \n\t"
|
281 |
|
|
"l.sw 40(%1),r7 \n\t"
|
282 |
|
|
"l.sw 44(%1),r8 \n\t"
|
283 |
|
|
"l.sw 48(%1),r9 \n\t"
|
284 |
|
|
"l.sw 52(%1),r10 \n\t"
|
285 |
|
|
"l.sw 56(%1),r11 \n\t"
|
286 |
|
|
"l.sw 60(%1),r12 \n\t"
|
287 |
|
|
"l.sw 64(%1),r13 \n\t"
|
288 |
|
|
"l.sw 68(%1),r14 \n\t"
|
289 |
|
|
"l.sw 72(%1),r15 \n\t"
|
290 |
|
|
"l.sw 76(%1),r16 \n\t"
|
291 |
|
|
"l.sw 80(%1),r17 \n\t"
|
292 |
|
|
"l.sw 84(%1),r18 \n\t"
|
293 |
|
|
"l.sw 88(%1),r19 \n\t"
|
294 |
|
|
"l.sw 92(%1),r20 \n\t"
|
295 |
|
|
"l.sw 96(%1),r21 \n\t"
|
296 |
|
|
"l.sw 100(%1),r22 \n\t"
|
297 |
|
|
"l.sw 104(%1),r23 \n\t"
|
298 |
|
|
"l.sw 108(%1),r24 \n\t"
|
299 |
|
|
"l.sw 112(%1),r25 \n\t"
|
300 |
|
|
"l.sw 116(%1),r26 \n\t"
|
301 |
|
|
"l.sw 120(%1),r27 \n\t"
|
302 |
|
|
"l.sw 124(%1),r28 \n\t"
|
303 |
|
|
"l.sw 128(%1),r29 \n\t"
|
304 |
|
|
"l.sw 132(%1),r30 \n\t"
|
305 |
|
|
"l.jr %0 \n\t" /* Go there */
|
306 |
|
|
"l.sw 136(%1),r31 \n" /* Store the last reg */
|
307 |
|
|
|
308 |
|
|
/**** 64 bit implementation ****/
|
309 |
208 |
chris |
"_L_64bit: \n\t"
|
310 |
|
|
"l.ori %0,%0,lo(_L_restore)\n\t"
|
311 |
158 |
chris |
"l.sw 264(%1),%0 \n\t"
|
312 |
|
|
"l.sd 16(%1),r1 \n\t"
|
313 |
|
|
"l.sd 24(%1),r2 \n\t"
|
314 |
|
|
"l.sd 32(%1),r3 \n\t"
|
315 |
|
|
"l.sd 40(%1),r4 \n\t"
|
316 |
|
|
"l.sd 48(%1),r5 \n\t"
|
317 |
|
|
"l.sd 56(%1),r6 \n\t"
|
318 |
|
|
"l.sd 64(%1),r7 \n\t"
|
319 |
|
|
"l.sd 72(%1),r8 \n\t"
|
320 |
|
|
"l.sd 80(%1),r9 \n\t"
|
321 |
|
|
"l.sd 88(%1),r10 \n\t"
|
322 |
|
|
"l.sd 96(%1),r11 \n\t"
|
323 |
|
|
"l.sd 104(%1),r12 \n\t"
|
324 |
|
|
"l.sd 112(%1),r13 \n\t"
|
325 |
|
|
"l.sd 120(%1),r14 \n\t"
|
326 |
|
|
"l.sd 128(%1),r15 \n\t"
|
327 |
|
|
"l.sd 136(%1),r16 \n\t"
|
328 |
|
|
"l.sd 144(%1),r17 \n\t"
|
329 |
|
|
"l.sd 152(%1),r18 \n\t"
|
330 |
|
|
"l.sd 160(%1),r19 \n\t"
|
331 |
|
|
"l.sd 168(%1),r20 \n\t"
|
332 |
|
|
"l.sd 176(%1),r21 \n\t"
|
333 |
|
|
"l.sd 184(%1),r22 \n\t"
|
334 |
|
|
"l.sd 192(%1),r23 \n\t"
|
335 |
|
|
"l.sd 200(%1),r24 \n\t"
|
336 |
|
|
"l.sd 208(%1),r25 \n\t"
|
337 |
|
|
"l.sd 216(%1),r26 \n\t"
|
338 |
|
|
"l.sd 224(%1),r27 \n\t"
|
339 |
|
|
"l.sd 232(%1),r28 \n\t"
|
340 |
|
|
"l.sd 240(%1),r29 \n\t"
|
341 |
|
|
"l.sd 248(%1),r30 \n\t"
|
342 |
|
|
"l.jr %0 \n\t" /* Go to the new PC */
|
343 |
|
|
"l.sd 256(%1),r31 \n" /* Store the last reg */
|
344 |
|
|
|
345 |
|
|
/**** The restoration routine. ****/
|
346 |
|
|
|
347 |
|
|
/* Note that when we return from this function,
|
348 |
|
|
we will actually be returning to a different
|
349 |
|
|
context than when we left. The debugger might
|
350 |
|
|
have conniptions over this, but we'll have to
|
351 |
|
|
reengineer that later. The stack and status
|
352 |
|
|
registers will all be changed, however we
|
353 |
|
|
will not touch the global interrupt mask. */
|
354 |
|
|
|
355 |
|
|
/* Also note, when doing any restore, the most
|
356 |
|
|
important registers are r1, r2, and r9. These
|
357 |
|
|
will be accessed immediately upon exiting the
|
358 |
|
|
routine, and so we want to make sure we load
|
359 |
|
|
them as early as possible in case they are
|
360 |
|
|
not in cache */
|
361 |
|
|
|
362 |
208 |
chris |
"_L_restore: \n\t" /* Restore "heir" */
|
363 |
|
|
"l.mfspr %2,r0,0x11 \n\t" /* Status Register */
|
364 |
158 |
chris |
"l.movhi %0,0x07FF \n\t" /* ~SR mask */
|
365 |
208 |
chris |
"l.ori %0,%0,0xD1FF \n\t"
|
366 |
|
|
"l.and %2,%0,%2 \n\t" /* save the global bits */
|
367 |
158 |
chris |
"l.movhi %0,0xF800 \n\t" /* SR mask */
|
368 |
208 |
chris |
"l.ori %0,%0,0x2E00 \n\t"
|
369 |
|
|
"l.lwz %1,0(%3) \n\t" /* Get the previous SR */
|
370 |
158 |
chris |
"l.and %0,%1,%0 \n\t" /* Mask out the global bits */
|
371 |
208 |
chris |
"l.or %2,%2,%0 \n\t" /* Combine local/global */
|
372 |
|
|
"l.mtspr r0,%2,0x11 \n\t" /* Restore the status register */
|
373 |
158 |
chris |
|
374 |
|
|
"l.mfspr %0,r0,0x02 \n\t" /* CPUCFGR */
|
375 |
|
|
"l.andi %0,%0,0x40 \n\t" /* OB64S */
|
376 |
|
|
"l.sfnei %0,0x0 \n\t" /* Save the 64 bit flag */
|
377 |
|
|
|
378 |
208 |
chris |
"l.srli %2,%2,28 \n\t" /* Move CID into low byte */
|
379 |
|
|
"l.lwz %0,4(%3) \n\t"
|
380 |
|
|
"l.mtspr %2,%0,0x20 \n\t" /* Offset from EPCR */
|
381 |
|
|
"l.lwz %0,8(%3) \n\t"
|
382 |
|
|
"l.mtspr %2,%0,0x30 \n\t" /* Offset from EEAR */
|
383 |
|
|
"l.lwz %0,12(%3) \n\t"
|
384 |
158 |
chris |
|
385 |
208 |
chris |
"l.bf _L_r64bit \n\t" /* 64 bit architecture */
|
386 |
|
|
"l.mtspr %2,%0,0x30 \n\t" /* Offset from EEAR (DELAY) */
|
387 |
158 |
chris |
|
388 |
|
|
/**** 32 bit restore ****/
|
389 |
208 |
chris |
"l.lwz r1,16(%3) \n\t"
|
390 |
|
|
"l.lwz r2,20(%3) \n\t"
|
391 |
|
|
"l.lwz r9,48(%3) \n\t"
|
392 |
|
|
"l.lwz r3,24(%3) \n\t"
|
393 |
|
|
"l.lwz r4,28(%3) \n\t"
|
394 |
|
|
"l.lwz r5,32(%3) \n\t"
|
395 |
|
|
"l.lwz r6,36(%3) \n\t"
|
396 |
|
|
"l.lwz r7,40(%3) \n\t"
|
397 |
|
|
"l.lwz r8,44(%3) \n\t"
|
398 |
|
|
"l.lwz r10,52(%3) \n\t"
|
399 |
|
|
"l.lwz r11,56(%3) \n\t"
|
400 |
|
|
"l.lwz r12,60(%3) \n\t"
|
401 |
|
|
"l.lwz r13,64(%3) \n\t"
|
402 |
|
|
"l.lwz r14,68(%3) \n\t"
|
403 |
|
|
"l.lwz r15,72(%3) \n\t"
|
404 |
|
|
"l.lwz r16,76(%3) \n\t"
|
405 |
|
|
"l.lwz r17,80(%3) \n\t"
|
406 |
|
|
"l.lwz r18,84(%3) \n\t"
|
407 |
|
|
"l.lwz r19,88(%3) \n\t"
|
408 |
|
|
"l.lwz r20,92(%3) \n\t"
|
409 |
|
|
"l.lwz r21,96(%3) \n\t"
|
410 |
|
|
"l.lwz r22,100(%3) \n\t"
|
411 |
|
|
"l.lwz r23,104(%3) \n\t"
|
412 |
|
|
"l.lwz r24,108(%3) \n\t"
|
413 |
|
|
"l.lwz r25,112(%3) \n\t"
|
414 |
|
|
"l.lwz r26,116(%3) \n\t"
|
415 |
|
|
"l.lwz r27,120(%3) \n\t"
|
416 |
|
|
"l.lwz r28,124(%3) \n\t"
|
417 |
|
|
"l.lwz r29,128(%3) \n\t"
|
418 |
|
|
"l.lwz r30,132(%3) \n\t"
|
419 |
|
|
"l.j _L_return \n\t"
|
420 |
|
|
"l.lwz r31,136(%3) \n"
|
421 |
158 |
chris |
|
422 |
|
|
/**** 64 bit restore ****/
|
423 |
208 |
chris |
"_L_r64bit: \n\t"
|
424 |
|
|
"l.ld r1,16(%3) \n\t"
|
425 |
|
|
"l.ld r2,24(%3) \n\t"
|
426 |
|
|
"l.ld r9,80(%3) \n\t"
|
427 |
|
|
"l.ld r3,32(%3) \n\t"
|
428 |
|
|
"l.ld r4,40(%3) \n\t"
|
429 |
|
|
"l.ld r5,48(%3) \n\t"
|
430 |
|
|
"l.ld r6,56(%3) \n\t"
|
431 |
|
|
"l.ld r7,64(%3) \n\t"
|
432 |
|
|
"l.ld r8,72(%3) \n\t"
|
433 |
|
|
"l.ld r10,88(%3) \n\t"
|
434 |
|
|
"l.ld r11,96(%3) \n\t"
|
435 |
|
|
"l.ld r12,104(%3) \n\t"
|
436 |
|
|
"l.ld r13,112(%3) \n\t"
|
437 |
|
|
"l.ld r14,120(%3) \n\t"
|
438 |
|
|
"l.ld r15,128(%3) \n\t"
|
439 |
|
|
"l.ld r16,136(%3) \n\t"
|
440 |
|
|
"l.ld r17,144(%3) \n\t"
|
441 |
|
|
"l.ld r18,152(%3) \n\t"
|
442 |
|
|
"l.ld r19,160(%3) \n\t"
|
443 |
|
|
"l.ld r20,168(%3) \n\t"
|
444 |
|
|
"l.ld r21,176(%3) \n\t"
|
445 |
|
|
"l.ld r22,184(%3) \n\t"
|
446 |
|
|
"l.ld r23,192(%3) \n\t"
|
447 |
|
|
"l.ld r24,200(%3) \n\t"
|
448 |
|
|
"l.ld r25,208(%3) \n\t"
|
449 |
|
|
"l.ld r26,216(%3) \n\t"
|
450 |
|
|
"l.ld r27,224(%3) \n\t"
|
451 |
|
|
"l.ld r28,232(%3) \n\t"
|
452 |
|
|
"l.ld r29,240(%3) \n\t"
|
453 |
|
|
"l.ld r30,248(%3) \n\t"
|
454 |
|
|
"l.ld r31,256(%3) \n"
|
455 |
158 |
chris |
|
456 |
208 |
chris |
"_L_return: \n\t" /* End of routine */
|
457 |
158 |
chris |
|
458 |
208 |
chris |
: "=&r" (temp1), "+r" (run), "=&r" (temp2)
|
459 |
|
|
: "r" (heir));
|
460 |
158 |
chris |
|
461 |
|
|
/* Note that some registers were used for parameter passing and
|
462 |
|
|
temporary registeres (temp1 and temp2). These values were
|
463 |
|
|
saved and restored across context calls, but the values that
|
464 |
|
|
the caller needs should have been stored on the stack. The
|
465 |
|
|
C code should now restore these from the stack, since r1 and
|
466 |
|
|
r2 have been restored, and return to the location specified
|
467 |
|
|
by r9. Then, all should be happy in the world. */
|
468 |
|
|
}
|
469 |
|
|
|
470 |
|
|
/*
|
471 |
|
|
* _CPU_Context_restore
|
472 |
|
|
*
|
473 |
|
|
* This routine is generally used only to restart self in an
|
474 |
|
|
* efficient manner. It may simply be a label in _CPU_Context_switch.
|
475 |
|
|
*
|
476 |
|
|
* NOTE: May be unnecessary to reload some registers.
|
477 |
|
|
*
|
478 |
|
|
* Or1k Specific Information:
|
479 |
|
|
*
|
480 |
|
|
* In our implementation, this simply redirects to swich context
|
481 |
|
|
*/
|
482 |
|
|
|
483 |
208 |
chris |
void _CPU_Context_restore(
|
484 |
|
|
Context_Control *run
|
485 |
|
|
)
|
486 |
|
|
{
|
487 |
|
|
_CPU_Context_switch(run,NULL);
|
488 |
|
|
}
|
489 |
158 |
chris |
|
490 |
|
|
|
491 |
|
|
/* void __ISR_Handler()
|
492 |
|
|
*
|
493 |
|
|
* This routine provides the RTEMS interrupt management.
|
494 |
|
|
*
|
495 |
208 |
chris |
* Or1k Specific Information:
|
496 |
158 |
chris |
*
|
497 |
208 |
chris |
* Based on the Or1k interrupt architecture described in chapter 16
|
498 |
|
|
* and the exception architecture described in chapter 9
|
499 |
158 |
chris |
*/
|
500 |
|
|
|
501 |
208 |
chris |
void _ISR_Handler(unsigned32 vector,unsigned32 ProgramCounter,
|
502 |
|
|
unsigned32 EffectiveAddress,unsigned32 StatusRegister)
|
503 |
158 |
chris |
{
|
504 |
|
|
/*
|
505 |
|
|
* This discussion ignores a lot of the ugly details in a real
|
506 |
|
|
* implementation such as saving enough registers/state to be
|
507 |
|
|
* able to do something real. Keep in mind that the goal is
|
508 |
|
|
* to invoke a user's ISR handler which is written in C and
|
509 |
|
|
* uses a certain set of registers.
|
510 |
|
|
*
|
511 |
|
|
* Also note that the exact order is to a large extent flexible.
|
512 |
|
|
* Hardware will dictate a sequence for a certain subset of
|
513 |
|
|
* _ISR_Handler while requirements for setting
|
514 |
|
|
*/
|
515 |
|
|
|
516 |
|
|
/*
|
517 |
|
|
* At entry to "common" _ISR_Handler, the vector number must be
|
518 |
|
|
* available. On some CPUs the hardware puts either the vector
|
519 |
|
|
* number or the offset into the vector table for this ISR in a
|
520 |
|
|
* known place. If the hardware does not give us this information,
|
521 |
|
|
* then the assembly portion of RTEMS for this port will contain
|
522 |
|
|
* a set of distinct interrupt entry points which somehow place
|
523 |
|
|
* the vector number in a known place (which is safe if another
|
524 |
|
|
* interrupt nests this one) and branches to _ISR_Handler.
|
525 |
|
|
*
|
526 |
|
|
* save some or all context on stack
|
527 |
|
|
* may need to save some special interrupt information for exit
|
528 |
|
|
*
|
529 |
|
|
* #if ( CPU_HAS_SOFTWARE_INTERRUPT_STACK == TRUE )
|
530 |
|
|
* if ( _ISR_Nest_level == 0 )
|
531 |
|
|
* switch to software interrupt stack
|
532 |
|
|
* #endif
|
533 |
|
|
*
|
534 |
|
|
* _ISR_Nest_level++;
|
535 |
|
|
*
|
536 |
|
|
* _Thread_Dispatch_disable_level++;
|
537 |
|
|
*
|
538 |
|
|
* (*_ISR_Vector_table[ vector ])( vector );
|
539 |
|
|
*
|
540 |
|
|
* --_ISR_Nest_level;
|
541 |
|
|
*
|
542 |
|
|
* if ( _ISR_Nest_level )
|
543 |
|
|
* goto the label "exit interrupt (simple case)"
|
544 |
|
|
*
|
545 |
|
|
* #if ( CPU_HAS_SOFTWARE_INTERRUPT_STACK == TRUE )
|
546 |
|
|
* restore stack
|
547 |
|
|
* #endif
|
548 |
|
|
*
|
549 |
|
|
* if ( !_Context_Switch_necessary )
|
550 |
|
|
* goto the label "exit interrupt (simple case)"
|
551 |
|
|
*
|
552 |
|
|
* if ( !_ISR_Signals_to_thread_executing )
|
553 |
|
|
* _ISR_Signals_to_thread_executing = FALSE;
|
554 |
|
|
* goto the label "exit interrupt (simple case)"
|
555 |
|
|
*
|
556 |
|
|
* call _Thread_Dispatch() or prepare to return to _ISR_Dispatch
|
557 |
|
|
*
|
558 |
|
|
* prepare to get out of interrupt
|
559 |
|
|
* return from interrupt (maybe to _ISR_Dispatch)
|
560 |
|
|
*
|
561 |
|
|
* LABEL "exit interrupt (simple case):
|
562 |
|
|
* prepare to get out of interrupt
|
563 |
|
|
* return from interrupt
|
564 |
|
|
*/
|
565 |
208 |
chris |
|
566 |
|
|
/* In the Or1k architecture, exceptions are handled in the
|
567 |
|
|
startup code of the board support package. Thus, this
|
568 |
|
|
routine is never called. Or1k exception routines are called
|
569 |
|
|
with the following prototype:
|
570 |
|
|
|
571 |
|
|
function(int vector#, int PC, int Address, int StatusRegister);
|
572 |
|
|
|
573 |
|
|
These parameters are snapshots of the system when the exception
|
574 |
|
|
was encountered. If virtual memory is active, things like the
|
575 |
|
|
PC and Address may have little meaning, as they are referenced
|
576 |
|
|
in physical space, not the virtual space of the process.
|
577 |
|
|
*/
|
578 |
158 |
chris |
}
|
579 |
|
|
|