2 * VFPv2 or VFPv3 floating point unit
5 #include "../port/lib.h"
12 /* subarchitecture code in m->havefp */
18 /* fp control regs. most are read-only */
25 Fpinst= 9, /* optional, for exceptions */
32 Fpdex = 1 << 29, /* defined synch exception */
33 // Fp2v = 1 << 28, /* Fpinst2 reg is valid */
34 // Fpvv = 1 << 27, /* if Fpdex, vecitr is valid */
35 // Fptfv = 1 << 26, /* trapped fault is valid */
36 // Fpvecitr = MASK(3) << 8,
37 /* FSR bits appear here */
38 Fpmbc = Fpdex, /* bits exception handler must clear */
40 /* Fpscr bits; see u.h for more */
41 Stride = MASK(2) << 20,
45 /* trap exception enables (not allowed in vfp3) */
46 FPIDNRM = 1 << 15, /* input denormal */
47 Alltraps = FPIDNRM | FPINEX | FPUNFL | FPOVFL | FPZDIV | FPINVAL,
48 /* pending exceptions */
49 FPAIDNRM = 1 << 7, /* input denormal */
50 Allexc = FPAIDNRM | FPAINEX | FPAUNFL | FPAOVFL | FPAZDIV | FPAINVAL,
52 Allcc = MASK(4) << 28,
56 Cpaccnosimd = 1u << 31,
61 subarch(int impl, uint sa)
63 static char *armarchs[] = {
64 "VFPv1 (unsupported)",
66 "VFPv3+ with common VFP subarch v2",
67 "VFPv3+ with null subarch",
68 "VFPv3+ with common VFP subarch v3",
71 if (impl != 'A' || sa >= nelem(armarchs))
96 gotfp = 1 << CpFP | 1 << CpDFP;
97 cpwrsc(0, CpCONTROL, 0, CpCPaccess, MASK(28));
98 acc = cprdsc(0, CpCONTROL, 0, CpCPaccess);
99 if ((acc & (MASK(2) << (2*CpFP))) == 0) {
100 gotfp &= ~(1 << CpFP);
101 print("fpon: no single FP coprocessor\n");
103 if ((acc & (MASK(2) << (2*CpDFP))) == 0) {
104 gotfp &= ~(1 << CpDFP);
105 print("fpon: no double FP coprocessor\n");
108 print("fpon: no FP coprocessors\n");
112 m->fpon = 1; /* don't panic */
115 switch((sid >> 16) & MASK(7)){
122 default: /* VFPv3 or later */
124 m->fpnregs = (acc & Cpaccd16) ? 16 : 32;
128 print("fp: %d registers, %s simd\n", m->fpnregs,
129 (acc & Cpaccnosimd? " no": ""));
135 * these can be called to turn the fpu on or off for user procs,
136 * not just at system start up or shutdown.
151 if (!m->fpon && havefp()) {
152 /* enable fp. must be first operation on the FPUs. */
153 fpwr(Fpexc, Fpenabled);
165 /* clear pending exceptions; no traps in vfp3; all v7 ops are scalar */
166 m->fpscr = Dn | Fz | FPRNR | (FPINVAL | FPZDIV | FPOVFL) & ~Alltraps;
167 fpwr(Fpscr, m->fpscr);
174 print("fp: %s arch %s; rev %ld\n", implement(impl),
175 subarch(impl, (sid >> 16) & MASK(7)), sid & MASK(4));
194 fpwr(Fpscr, (fprd(Fpscr) & Allcc) | m->fpscr);
196 fpcfg(); /* 1st time on this fpu; configure it */
206 // scr = fprd(Fpscr);
207 // m->fpscr = scr & ~Allexc;
208 // fpwr(Fpscr, m->fpscr);
210 fpwr(Fpexc, fprd(Fpexc) & ~Fpmbc);
215 * Called when a note is about to be delivered to a
216 * user process, usually at the end of a system call.
217 * Note handlers are not allowed to use the FPU so
218 * the state is marked (after saving if necessary) and
219 * checked in the Device Not Available handler.
224 if(up->fpstate == FPactive){
226 up->fpstate = FPinactive;
228 up->fpstate |= FPillegal;
232 * Called from sysnoted() via the machine-dependent
234 * Clear the flag set above in fpunotify().
239 up->fpstate &= ~FPillegal;
242 /* should only be called if p->fpstate == FPactive */
249 fps->control = fps->status = fprd(Fpscr);
251 for (n = 0; n < m->fpnregs; n++)
252 fpsavereg(n, (uvlong *)fps->regs[n]);
262 fpwr(Fpscr, p->fpsave->control);
263 m->fpscr = fprd(Fpscr) & ~Allcc;
265 for (n = 0; n < m->fpnregs; n++)
266 fprestreg(n, *(uvlong *)p->fpsave->regs[n]);
270 * Called from sched() and sleep() via the machine-dependent
271 * procsave() routine.
272 * About to go in to the scheduler.
273 * If the process wasn't using the FPU
274 * there's nothing to do.
279 if(p->fpstate == FPactive){
280 if(p->state == Moribund)
284 * Fpsave() stores without handling pending
285 * unmasked exeptions. Postnote() can't be called
286 * here as sleep() already has up->rlock, so
287 * the handling of pending exceptions is delayed
288 * until the process runs again and generates an
289 * emulation fault to activate the FPU.
293 p->fpstate = FPinactive;
298 * The process has been rescheduled and is about to run.
299 * Nothing to do here right now. If the process tries to use
300 * the FPU again it will cause a Device Not Available
301 * exception and the state will then be restored.
304 fpuprocrestore(Proc *)
309 * The current process has been forked,
310 * save and copy neccesary state to child.
318 switch(up->fpstate & ~FPillegal){
321 up->fpstate = FPinactive;
324 memmove(p->fpsave, up->fpsave, sizeof(FPsave));
325 p->fpstate = FPinactive;
332 * Called from sysexec() via sysprocsetup() to
333 * set the FPU for the new process.
336 fpusysprocsetup(Proc *p)
346 char *msg, note[ERRMAX];
348 status = up->fpsave->status;
351 * Some attention should probably be paid here to the
352 * exception masks and error summary.
354 if (status & FPAINEX)
356 else if (status & FPAOVFL)
358 else if (status & FPAUNFL)
360 else if (status & FPAZDIV)
361 msg = "divide by zero";
362 else if (status & FPAINVAL)
363 msg = "bad operation";
366 snprint(note, sizeof note, "sys: fp: %s fppc=%#p status=%#lux",
367 msg, up->fpsave->pc, status);
368 postnote(up, 1, note, NDebug);
374 if(m->havefp == VFPv3 && !(fprd(Fpexc) & (Fpex|Fpdex)))
375 iprint("mathemu: not an FP exception but an unknown FP opcode\n");
378 error("illegal instruction: VFP opcode in emulated mode");
381 up->fpstate = FPactive;
385 * Before restoring the state, check for any pending
386 * exceptions. There's no way to restore the state without
387 * generating an unmasked exception.
388 * More attention should probably be paid here to the
389 * exception masks and error summary.
391 if(up->fpsave->status & (FPAINEX|FPAUNFL|FPAOVFL|FPAZDIV|FPAINVAL)){
396 up->fpstate = FPactive;
399 error("illegal instruction: bad vfp fpu opcode");
408 if (m->fppc == pc && m->fppid == up->pid) {
411 panic("fpuemu: cpu%d stuck at pid %ld %s pc %#p "
412 "instr %#8.8lux", m->machno, up->pid, up->text,
430 condok(int cc, int c)
435 case 1: /* Z clear */
439 case 3: /* C clear */
443 case 5: /* N clear */
447 case 7: /* V clear */
449 case 8: /* C set and Z clear */
450 return cc&C && (cc&Z) == 0;
451 case 9: /* C clear or Z set */
452 return (cc&C) == 0 || cc&Z;
453 case 10: /* N set and V set, or N clear and V clear */
454 return (~cc&(N|V))==0 || (cc&(N|V)) == 0;
455 case 11: /* N set and V clear, or N clear and V set */
456 return (cc&(N|V))==N || (cc&(N|V))==V;
457 case 12: /* Z clear, and either N set and V set or N clear and V clear */
458 return (cc&Z) == 0 && ((~cc&(N|V))==0 || (cc&(N|V))==0);
459 case 13: /* Z set, or N set and V clear or N clear and V set */
460 return (cc&Z) || (cc&(N|V))==N || (cc&(N|V))==V;
461 case 14: /* always */
463 case 15: /* never (reserved) */
466 return 0; /* not reached */
469 /* only called to deal with user-mode instruction faults */
477 postnote(up, 1, up->errstr, NDebug);
481 if(up->fpstate & FPillegal)
482 error("floating point in note handler");
487 if(!condok(ureg->psr, *(ulong*)pc >> 28))
488 iprint("fpuemu: conditional instr shouldn't have got here\n");
489 op = (*(ulong *)pc >> 24) & MASK(4);
490 cop = (*(ulong *)pc >> 8) & MASK(4);
492 fpstuck(pc); /* debugging; could move down 1 line */
493 if (ISFPAOP(cop, op)) { /* old arm 7500 fpa opcode? */
494 // iprint("fpuemu: fpa instr %#8.8lux at %#p\n", *(ulong *)pc, pc);
495 // error("illegal instruction: old arm 7500 fpa opcode");
501 nfp = fpiarm(ureg); /* advances pc past emulated instr(s) */
502 if (nfp > 1) /* could adjust this threshold */
503 m->fppc = m->fpcnt = 0;
506 } else if (ISVFPOP(cop, op)) { /* if vfp, fpu must be off */
507 mathemu(ureg); /* enable fpu & retry */