/*
 * Print contents of Maverick Crunch FPU Status/Control register
 * as used in Cirrus Logic EP93xx chips.
 *
 * You can also use this to tickle two different 64-bit load/store Maverick
 * hardware bugs. Hack the read/write functions at the end of the file.
 * See http://www.cirrus.com/en/products/pro/detail/P131.html for HW bug list.
 *
 * Output should be:
 * the initial contents of the DSPSC register with 0xdeadb0d1 in the INST field,
 * then ten numbers ending in infinities (or saturating, depending on SAT)
 * then the DSPSC register contents again with FCC=2 IX=1 and OF=1.
 *
 * cc -mcpu=ep9312 -mfpu=maverick and -mfloat-abi=softfp if using EABI.
 *
 * Martin Guy <martinwguy@yahoo.it> 5 Oct 2007 - 22 Mar 2009
 */
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

/* 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);

main(int argc, char **argv)
{
	if(sizeof(struct DSPSC) != sizeof(double)) {
		fputs("Error: struct DSPSC compiles with wrong size", stderr);
		exit(1);
	}

	/* Fill top 32 bits with garbage to check they are written correctly */
	dspsc.INST=0xdeadb0d1;
	read_dspsc();
	print_dspsc();
	printf("%sperating in serialized mode.\n",
	    ((dspsc.IXE || dspsc.UFE || dspsc.OFE || dspsc.IOE) && !dspsc.AEXC)
	    ? "O" : "Not o");

	exit(0);
}

/* 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 */
}
