/* * Test whether denormalized values as parameters raise an * exception or set the Denorm bit in the control register. * * cc -mcpu=ep9312 -mfpu=maverick and -mfloat-abi=softfp if using EABI. * * Results (tested on Revision E1 silicon): * - cfaddd neither sets Denorm nor raises an exception for * denormalized inputs, silently taking them as zero. * - cfmuld raises IX(Inexact) and UF(underflow) if you multiply two * denorms together, but doesn't set the Denorm bit either. * * Martin Guy 5 Oct 2007 - 22 Mar 2009 */ #include #include #include /* Contents of Maverick Crunch DSPSC register in little-endian mode. * See EP9307 Users Guide, Chapter 2 */ struct DSPSC { #define u unsigned u IO:1; u RSVD1:1; u OF:1; u UF:1; u IX:1; u IOE:1; u RSVD6:1; u OFE:1; u UFE:1; u IXE:1; u RM:2; u Denorm:1; u Invalid:1; u FWDEN:1; u V:1; u FCC:2; u SAT:2; u AEXC:1; u INT:1; u UI:1; u ISAT:1; u RSVD24:2; u HVID:3; u DAID:3; u INST:32; #undef u } dspsc; static void read_dspsc(void); static void write_dspsc(void); static void print_dspsc(void); /* A little union for bit pattern<->float conversion. * We use double, not float, to avoid float->double lossage when values are * passed to printf. */ union u { double d; unsigned long long ull; } u; double one = 1.0; /* Use to test cfmuld behaviour */ main(int argc, char **argv) { u.ull = 0x0000000000000001ULL; /* smallest denormalized value */ puts("Before:"); read_dspsc(); print_dspsc(); printf("u: 0x%016x = %g\n", u.ull, u.d); denorm_add(); /* or denorm_mul() */ puts("After:"); read_dspsc(); print_dspsc(); printf("u: 0x%016x = %g\n", u.ull, u.d); exit(0); } /* Perform Maverick operations on a denormalized value */ denorm_add() { asm("ldr r3,=u"); asm("nop"); /* ward off the evil eye */ asm("cfldrd mvd0,[r3]"); asm("cfaddd mvd1, mvd0, mvd1"); asm("cfstrd mvd1,[r3]"); } denorm_mul() { asm("ldr r1,=one"); asm("ldr r3,=u"); asm("nop"); /* ward off the evil eye */ asm("cfldrd mvd0,[r3]"); asm("cfldrd mvd1,[r1]"); asm("cfmuld mvd2, mvd0, mvd1"); /* try ,mvd0,mvd0 to see IX,UF */ asm("cfstrd mvd2,[r3]"); } /* Print the contents of the structure in human-readable form */ static void print_dspsc() { #define d dspsc printf("INST=0x%08x\n", d.INST); printf("DAID=%d HVID=%d ISAT=%d UI=%d INT=%d AEXC=%d SAT=%d\n", d.DAID, d.HVID, d.ISAT, d.UI, d.INT, d.AEXC, d.SAT); printf("FCC=%d V=%d FWDEN=%d Invalid=%d Denorm=%d RM=%d\n", d.FCC, d.V, d.FWDEN, d.Invalid, d.Denorm, d.RM); printf("IXE=%d UFE=%d OFE=%d IOE=%d IX=%d UF=%d OF=%d IO=%d\n", d.IXE, d.UFE, d.OFE, d.IOE, d.IX, d.UF, d.OF, d.IO); #undef d } /* Copy register contents into our structure */ static void read_dspsc() { /* If you put the ldr after the cfmv32sc, you can * trigger an undocumented Maverick hardware bug, whereby * ldr rN, foo; cfstr64 mvdX, [rN] corrupts memory at random. * (Tested on Rev E1 hardware). */ asm("ldr r3, =dspsc"); /* Get address of dspsc */ asm("cfmv32sc mvdx0, dspsc"); /* Get DSPSC contents */ asm("cfstr64 mvdx0, [r3]"); /* Store dspcc contents in dspsc */ } /* Copy our structure's contents into the register */ static void write_dspsc() { asm("ldr r0, =dspsc"); /* Get address of dspsc */ /* no-op to workaround Maverick timing bug: cfldr64 must not busy-wait. * Without this, rubbish is written into all 64 bits of DSPSC. * (Erratum 3, tested on Rev E1 hardware) */ // asm("mov r0, r0"); asm("cfldr64 mvdx0, [r0]"); /* Read our variable into c0 */ asm("cfmvsc32 dspsc, mvdx0"); /* Write to dspcc */ }