/*******************************************************************
 Example code to reading the SVS register and program the digi-pot 
 on ADSP-2148x FSV BUB

initial version - RC 02/20/2012

version 1.0 - RC 04/27/2012. Automated reading of SVS register, calculate
VDD_INT, compute digipot step, and program digipot via TWI interface.

version 2.0 - RC 05/17/2012, Added parity, redundancy checking while determining
VDD_INT bit field.

version 2.1 - RC 07/10/2012. Added quad-redundancy check code and error 
functionality.

version 3.0 - RC 03/04/2013. Modified the Algorithm for error checking functionality

Analog Devices, Inc.

*******************************************************************/

/*******************************************************************

Project built for ADSP-21489 EZ-BUB populated with rev 0.0 silicon. Customer
needs to change the project options for different part no/silicon rev.

********************************************************************/

/*******************************************************************
*  include files
*******************************************************************/
#include <cdef21489.h>
#include <stdio.h>
#include <sru.h>
#include <def21489.h>
#include <signal.h>


/*******************************************************************
*  function prototypes
*******************************************************************/
void Reset_TWI(void);
void Delay(int );
void InitSRU(void);
int PROGRAM_DIGIPOT(int );
int CHECK_DIGIPOT(int );
void TWI_MasterMode_Write(unsigned short DeviceAddr, unsigned char *TWI_Data_Pointer, unsigned short Count, unsigned short TWI_Length);
void TWI_MasterMode_Read(unsigned short DeviceAddr, unsigned char *TWI_Data_Pointer, unsigned short Count);
void main(void);
void digipot_ISR(void );
float Calculate_VDD_INT_Value(int  );
int Calculate_digipot_Step_Value(float );
void Bit_error_failure(void );
void Read_SVS_DAT_Register_and_Extract_VDD_INT_bits(void );
int Calculate_CheckSum(int, int );

/*******************************************************************
*  Defines
*******************************************************************/


#define DIGIPOT_ADDRESS 0x18	                        // upper 7 bits = 0x18, write = 0x30, read = 0x31

#define SVS_1   ((volatile unsigned int *)0x30106)      // Fuse Serial Number register[31:0]   
#define SVS_2   ((volatile unsigned int *)0x30107)      // Fuse Serial Number register[63:32]  
#define SVS_3   ((volatile unsigned int *)0x30108)      // Fuse Serial Number register[96:64]  
#define pSVS_CTL  ((volatile unsigned int *)0x100)      // SVS control register
#define SVS_LOAD   0x00000001                           // SVS bit-cell load
#define pSVS_DAT0 ((volatile unsigned int *)0x110)       // SVS data register 0 
#define pSVS_DAT1 ((volatile unsigned int *)0x111)       // SVS data register 1 
#define pSVS_DAT2 ((volatile unsigned int *)0x112)       // SVS data register 2
#define pSVS_DAT3 ((volatile unsigned int *)0x113)       // SVS data register 3
#define pSVS_DAT4 ((volatile unsigned int *)0x114)       // SVS data register 4
#define pSVS_DAT5 ((volatile unsigned int *)0x115)       // SVS data register 5
#define pSVS_DAT6 ((volatile unsigned int *)0x116)       // SVS data register 6
#define pSVS_DAT7 ((volatile unsigned int *)0x117)       // SVS data register 7

#define PRESCALE_VALUE 10		/* PRESCALE = 80/10 = 8 */
#define RESET_TWI 0				/* RESET_TWI value for controller */
#define CLKDIV_HI 0x19			/* SCL high period */
#define CLKDIV_LO 0x19			/* SCL low period */

/*******************************************************************
*  Global variables. Note that these variables are defined globally 
for the sake of clarity in this example. Alternately, a user can choose 
to make them all local and combine the individual functions together, 
in order to save valuable memory space.
*******************************************************************/

int VDD_INT_bits_SVS_DAT[4]; // stores VDD_INT_bits_SVS_DAT4,VDD_INT_bits_SVS_DAT5,VDD_INT_bits_SVS_DAT6 and VDD_INT_bits_SVS_DAT7
int this_pointer1[4][6];
unsigned char WriteRDAC[] = { 0x00, 0x00 };
unsigned char StoreRDAC[] = { 0xc0 };
unsigned char ReadRdacPrefix[] = { 0x00 };
unsigned char ReadEepromPrefix[] = { 0x20 };
float VDD_INT;
int error_code = 0;
int TWISuccess;


void main(void) {
	
	int how_many_checksums_match=0, checksum_true_for_SVS_DAT4=0, checksum_true_for_SVS_DAT5=0, checksum_true_for_SVS_DAT6=0, checksum_true_for_SVS_DAT7=0;
	int digipot_Step_Value;
	float VDD_INT_SVS_DAT4, VDD_INT_SVS_DAT5, VDD_INT_SVS_DAT6, VDD_INT_SVS_DAT7;
		
	/* program SRU */
	
	InitSRU();
	
/*
The following functions read the SVS register, extract the appropriate bits within
the register corresponding to VDD_INT, and calculate the digipot step accordingly. 
*/
        
	Read_SVS_DAT_Register_and_Extract_VDD_INT_bits();
	
/* The checksum_true_for_SVS_DATn variable is set to 1 if the checksum for the corresponding SVS_DATn register is correct.
Otherwise, the checksum_true_for_SVS_DATn variable is a 0.
*/
	checksum_true_for_SVS_DAT4 = Calculate_CheckSum(VDD_INT_bits_SVS_DAT[0],0);
	checksum_true_for_SVS_DAT5 = Calculate_CheckSum(VDD_INT_bits_SVS_DAT[1],1);
	checksum_true_for_SVS_DAT6 = Calculate_CheckSum(VDD_INT_bits_SVS_DAT[2],2);
	checksum_true_for_SVS_DAT7 = Calculate_CheckSum(VDD_INT_bits_SVS_DAT[3],3);

// Uncomment the following line for debug purposes only		
//	printf("%d %d %d %d\n", checksum_true_for_SVS_DAT4, checksum_true_for_SVS_DAT5, checksum_true_for_SVS_DAT6, checksum_true_for_SVS_DAT7);
	
	how_many_checksums_match = checksum_true_for_SVS_DAT4 + checksum_true_for_SVS_DAT5 + checksum_true_for_SVS_DAT6 + checksum_true_for_SVS_DAT7;
	                             
    if (how_many_checksums_match == 0) {
    error_code = 1;
    Bit_error_failure();
    }
 
// Code got here implying that at least one of the chekcums are correct...
       
	VDD_INT_SVS_DAT4 = Calculate_VDD_INT_Value(0);
	VDD_INT_SVS_DAT5 = Calculate_VDD_INT_Value(1);
	VDD_INT_SVS_DAT6 = Calculate_VDD_INT_Value(2);
	VDD_INT_SVS_DAT7 = Calculate_VDD_INT_Value(3);
	
// The code below checks to see that at least two of the VDD_INTs calculated are identical, AND 
// that the corresponding checksum is also correct...

	if ( ((VDD_INT_SVS_DAT4 == VDD_INT_SVS_DAT5) | (VDD_INT_SVS_DAT4 == VDD_INT_SVS_DAT6) |
	 (VDD_INT_SVS_DAT4 == VDD_INT_SVS_DAT7) ) && (checksum_true_for_SVS_DAT4 ==1) )
	 
	 digipot_Step_Value=Calculate_digipot_Step_Value(VDD_INT_SVS_DAT4);
	 
	else if ( ((VDD_INT_SVS_DAT5 == VDD_INT_SVS_DAT4) | (VDD_INT_SVS_DAT5 == VDD_INT_SVS_DAT6) | 
	 (VDD_INT_SVS_DAT5 == VDD_INT_SVS_DAT7) ) && (checksum_true_for_SVS_DAT5 ==1) )
	 
	digipot_Step_Value=Calculate_digipot_Step_Value(VDD_INT_SVS_DAT5);
	
	else if ( ((VDD_INT_SVS_DAT6 == VDD_INT_SVS_DAT4) | (VDD_INT_SVS_DAT6 == VDD_INT_SVS_DAT5) | 
	 (VDD_INT_SVS_DAT6 == VDD_INT_SVS_DAT7) ) && (checksum_true_for_SVS_DAT6 ==1) )
	 
	digipot_Step_Value=Calculate_digipot_Step_Value(VDD_INT_SVS_DAT6);
	
	else if ( ((VDD_INT_SVS_DAT7 == VDD_INT_SVS_DAT4) | (VDD_INT_SVS_DAT7 == VDD_INT_SVS_DAT5) | 
	 (VDD_INT_SVS_DAT7 == VDD_INT_SVS_DAT6) ) && (checksum_true_for_SVS_DAT7 ==1) )
	 
	digipot_Step_Value=Calculate_digipot_Step_Value(VDD_INT_SVS_DAT7);

	else {
	error_code = 2;
	Bit_error_failure();
	}
	
	
	
/* 
IMPORTANT!!!!!!!!!!

Note that if manually entered, the step value that needs to be entered is the value that comes from the spreadsheet
for the AD5258 (i.e., one of the 64 steps). Look for the voltage in the correponding "nominal voltage" columns
Measure voltage on the EZ-Kit board between test point TP28 and GND. 
Some step values from the table:

0x0c	// step value c gives us 1.118V 
0x3  // step value 3 gives us 1.223V 
0x05	// step value 5 gives us 1.198V 
 
*/

/* 
Uncomment following line if you want to program digipot to a different value than SVS contents	
Note that manual programming of the digipot is not necessary and not recommended. So this manual
programming should never be needed. 
*/

// #define MANUAL_PROGRAM	

#ifdef MANUAL_PROGRAM
	digipot_Step_Value = 9;                  // program the required digipot step value here
#endif	


	if (CHECK_DIGIPOT(digipot_Step_Value-2) ==1)  // why the "-2"?  Read below...
/*
Note that the step value that is programmed into the digipot is guardbanded by two step 
values less than what is determined by the equation. This results in a higher guardbanded 
voltage applied to the part. For example, if the equation results in a step value of 11, 
the value programmed into the digipot is 9. This is regulator and board design specific 
and needed to guarantee correct voltage at the DSP under all possible operating and load 
transient conditions.
*/ 	
	 printf("\nDigipot already programmed to correct value. Re-programming not needed\n");
	else  {
	 PROGRAM_DIGIPOT(digipot_Step_Value-2);  
     CHECK_DIGIPOT(digipot_Step_Value-2);	
	}

}  // end of main()



/********************************************************************************
*   Function:    Read_SVS_DAT_Register_and_Extract_VDD_INT_bits
*   Description: Function to read SVS_DATn registers and extract VDD_INT bit field
* 
*********************************************************************************/

void Read_SVS_DAT_Register_and_Extract_VDD_INT_bits(void )
{

	int read_SVS_DAT0, read_SVS_DAT1, read_SVS_DAT2,read_SVS_DAT3;
	int read_SVS_DAT4, read_SVS_DAT5, read_SVS_DAT6,read_SVS_DAT7;
	int check1, check2;

	*pSVS_CTL = SVS_LOAD;
	read_SVS_DAT0 = *pSVS_DAT0;
	read_SVS_DAT1 = *pSVS_DAT1;
	read_SVS_DAT2 = *pSVS_DAT2;
	read_SVS_DAT3 = *pSVS_DAT3;
	read_SVS_DAT4 = *pSVS_DAT4;
	read_SVS_DAT5 = *pSVS_DAT5;
	read_SVS_DAT6 = *pSVS_DAT6;
	read_SVS_DAT7 = *pSVS_DAT7;
	
	
	VDD_INT_bits_SVS_DAT[0] = read_SVS_DAT4 >> 25;  // shift down to get bits of interest
	VDD_INT_bits_SVS_DAT[1] = (read_SVS_DAT5 & 0x000000FF) >> 1;  // extract bits of interest
	VDD_INT_bits_SVS_DAT[2] = read_SVS_DAT6 >> 25;  // shift down to get bits of interest
	VDD_INT_bits_SVS_DAT[3] = (read_SVS_DAT7 & 0x000000FF) >> 1;  // extract bits of interest

	
}

/********************************************************************************
*   Function:    Calculate_CheckSum
*   Description: Function to calculate checksum for each VDD_INT bit field
* 
*********************************************************************************/


int Calculate_CheckSum(int VDD_INT_bits, int SVS_DATn)
{
	int i,j;
	int parity_bit, parity_sum=0;
	float VDD_INT_Value;
	int extracted_value1=0;
	
	extracted_value1 = VDD_INT_bits;

	parity_bit= (extracted_value1 & 1) ?  1:0;
	this_pointer1[SVS_DATn][0]= (extracted_value1 & 2) ?  1:0;
	parity_sum^=this_pointer1[SVS_DATn][0];
	this_pointer1[SVS_DATn][1]= (extracted_value1 & 4) ?  1:0;
	parity_sum^=this_pointer1[SVS_DATn][1];
	this_pointer1[SVS_DATn][2]= (extracted_value1 & 8) ?  1:0;
	parity_sum^=this_pointer1[SVS_DATn][2];
	this_pointer1[SVS_DATn][3]= (extracted_value1 & 16) ?  1:0;
	parity_sum^=this_pointer1[SVS_DATn][3];
	this_pointer1[SVS_DATn][4]= (extracted_value1 & 32) ?  1:0;
	parity_sum^=this_pointer1[SVS_DATn][4];
	this_pointer1[SVS_DATn][5]= (extracted_value1 & 64) ?  1:0;
	parity_sum^=this_pointer1[SVS_DATn][5];
	
// If checksum matched
	if (parity_bit != parity_sum) 
	   return 0;
	
	   else return 1;
	
}




/********************************************************************************
*   Function:    Calculate_VDD_INT_Value
*   Description: Function to calculate VDD_INT value.
* 
*   Following function calculates VDD_INT value from the binary VDD_INT
*   representation.
*********************************************************************************/


float Calculate_VDD_INT_Value(int SVS_DATn)
{
	int i;
	float VDD_INT_Value;


// Convert to decimal	  	
	i = this_pointer1[SVS_DATn][0]*32 + this_pointer1[SVS_DATn][1]*16 + this_pointer1[SVS_DATn][2]*8 + this_pointer1[SVS_DATn][3]*4 + this_pointer1[SVS_DATn][4]*2 + this_pointer1[SVS_DATn][5]*1;
	VDD_INT_Value = 0.65 + (0.0125* (float)i); 

// Dumping out the calculated VDD_INT for debug, comment out if not needed.
	printf("%d   %f\n",i, VDD_INT_Value);
	return VDD_INT_Value;
	
	
}

/**************************************************************************
*   Function:    Calculate_digipot_Step_Value
*   Description: Function to calculate digipot step value.
* 
*   Following function calculates the digipot step value that needs to be
*   programmed, based on the VDD_INT requirement of the voltage regulator. 
*   The equation used is the following:
*
*   For 64-bit AD5258 ONLY, and WITH the following Resistor values:
*   R6=59.00
*   R9=100.00
*   R8=143.00
*
*   Vout_nominal= (0.6 + ((0.6)/(143+(X*1.55))) * (59+(1.55*(64-X)))) 
*
*   X= Digitpot Step value 0 to 64.
*
*   If any of the values change above, the equation changes!
*
***************************************************************************/


int Calculate_digipot_Step_Value(float VDD_INT_Value)
{
	float digipot_Step_Value;
	
	digipot_Step_Value = (180.72-(143.0*VDD_INT_Value))/(1.55*VDD_INT_Value);
	
	return (int) digipot_Step_Value;
	
} 


/**************************************************************************
*   Function:    Bit_error_failure
*   Description: Error function to indicate failure in SVS register.
***************************************************************************/


void Bit_error_failure(void )
{
	printf("Error, error_code = %d\n", error_code);
	while(1) asm("idle;");
}


/*******************************************************************
*   Function:    InitSRU
*   Description: This function will setup the SRU Registers.
*******************************************************************/


void InitSRU(void)
{
	//Generating Code for connecting : DPI_PIN7 to TWI_DATA
	SRU (LOW, DPI_PB07_I); 
	SRU (TWI_DATA_PBEN_O, DPI_PBEN07_I); 
	SRU (DPI_PB07_O, TWI_DATA_I); 

	//Generating Code for connecting : DPI_PIN8 to TWI_CLK
	SRU (LOW, DPI_PB08_I); 
	SRU (TWI_CLK_PBEN_O, DPI_PBEN08_I); 
	SRU (DPI_PB08_O, TWI_CLK_I); 


}


/*******************************************************************
*   Function:    PROGRAM_DIGIPOT
*   Description: This programs the digipot on the EZ-Board.
*******************************************************************/

int PROGRAM_DIGIPOT(int digipot_Step_Value)
{
	printf( "\nProgram Digipot" );

    // reset the TWI interface
	Reset_TWI();	
	
    // write the RDAC register
	WriteRDAC[1]= digipot_Step_Value;
	printf( "\nProgramming RDAC with 0x%x", (unsigned int)WriteRDAC[1] );
	TWI_MasterMode_Write( DIGIPOT_ADDRESS, WriteRDAC, 1, 2);
	asm("nop;");
	asm("nop;");
	asm("nop;");
	asm("nop;");

    // store the RDAC value in digipot EEPROM
	printf( "\nStoring RDAC value in digipot EEPROM" );
	TWI_MasterMode_Write( DIGIPOT_ADDRESS, StoreRDAC, 1, 1);

		printf("\nTEST_PASS");
	return 1;
}

/*****************************************************************************
*   Function:    CHECK_DIGIPOT
*   Description: This verifies the digipot has been programmed on the EZ-Board.
******************************************************************************/

int CHECK_DIGIPOT(int digipot_Step_Value)
{
	unsigned char rdac = 0x00;		// stored RDAC value 
	unsigned char eeprom = 0x00;	// stored EEPROM value 

	printf( "\nCheck Digipot" );

	Reset_TWI();		// reset the TWI interface

	// read RDAC and EEPROM
	TWI_MasterMode_Write( DIGIPOT_ADDRESS, ReadRdacPrefix, 1, 1);
	TWI_MasterMode_Read( DIGIPOT_ADDRESS, &rdac, 1);

	TWI_MasterMode_Write( DIGIPOT_ADDRESS, ReadEepromPrefix, 1, 1);
	TWI_MasterMode_Read( DIGIPOT_ADDRESS, &eeprom, 1);

	printf( "\nExpected value of 0x%x", digipot_Step_Value );
	printf( "\nDetected RDAC value of 0x%x and EEPROM value of 0x%x",
	(unsigned int)rdac, (unsigned int)eeprom );

	// both values should match the RDAC value that we program
	if ( (digipot_Step_Value == rdac) && (digipot_Step_Value == eeprom) )
	{
			printf("\nTEST_PASS");
		return 1;
	}
	else
	{
		printf("\nTEST_FAIL");
		return 0;
	}
}




/*******************************************************************
*   Function:    Reset_TWI
*   Description: Function to reset the TWI interface
*******************************************************************/

void Reset_TWI(void)
{
    
	*pTWIMCTL = RESET_TWI;   // reset TWI controller
	
	// clear errors before enabling TWI
	*pTWIMSTAT = TWIRERR | TWIWERR | TWILOST | TWIANAK | TWIDNAK;

	// clear interrupts before enabling TWI
	*pTWIIRPTL = TWISINIT | TWISCOMP | TWISERR | TWISOVF | TWIMCOM | TWIMERR | TWITXINT | TWIRXINT;
	
	*pTWIFIFOCTL = TWITXFLUSH | TWIRXFLUSH;   // flush rx and tx fifos
	                          
	TWISuccess = 1;          // set this to 1 so that we go into the write sequence the very first time
}

/*******************************************************************
*   Function:    ClrSclSda
*   Description: Function to restore control of the TWI interface
*******************************************************************/

bool ClrSclSda(void)
{

	int cnt = 0;

    do {

    	*pTWIMCTL = TWISCLOVR;

        Delay(6);

        *pTWIMCTL = 0;                                                                     

        Delay(6);

	} while ((*pTWIMSTAT & TWISDASEN) && (cnt++ < 8));

    *pTWIMCTL = TWISDAOVR | TWISCLOVR;

    Delay(6);

    *pTWIMCTL = TWISDAOVR;
    
    Delay(6);

    *pTWIMCTL = 0;
    
    return ((*pTWIMSTAT & TWISDASEN) || (cnt == 8)) ? false : true;
}

/*******************************************************************
*   Function:    TWI_MasterMode_Write
*   Description: Function to do a master mode write
*******************************************************************/

void TWI_MasterMode_Write(unsigned short DeviceAddr, unsigned char *TWI_Data_Pointer, unsigned short Count, unsigned short TWI_Length)
{
	int i, j, timeout;

	if (*pTWIMSTAT & TWISDASEN)
	{
		if(!ClrSclSda())
			return;
	}
	
                                                                   // make sure no previous errors occured
	if (TWISuccess == 1)
	{			

	    *pTWIFIFOCTL = 0;									      // clear the bit manually
		*pTWIMITR = TWIEN | PRESCALE_VALUE;			              // PRESCALE = fsclk/10MHz
		*pTWIDIV = ((CLKDIV_HI) << 8) | (CLKDIV_LO);	          // CLKDIV = (1/SCL)/(1/10MHz)
		*pTWIMADDR = DeviceAddr;						          // target address (7-bits plus the read/write bit)

		for (i = 0; i < Count; i++)
		{
		                                                         // # of configurations to send
			*pTXTWI8 = *TWI_Data_Pointer++;	                     // pointer to data
			
	
			*pTWIMCTL = (TWI_Length<<6) | TWIMEN;                // start transmission

			timeout = 0x1000;
		
			for (j = 0; j < (TWI_Length-1); j++)
			{
			                                                     // # of transfers before stop condition
				while ((*pTWIFIFOSTAT == TWITXS) && --timeout)	// wait to load the next sample into the TX FIFO
				{
					asm("nop;");
				}
				if(!timeout)
					return;

				*pTXTWI8 = *TWI_Data_Pointer++;             // load the next sample into the TX FIFO
			 asm("nop;");
			}
			
			timeout = 0x1000;
			
			while (!(*pTWIIRPTL & TWIMCOM) && --timeout)	// wait until transmission complete and MCOMP is set
			{
				asm("nop;"); 
			}
			if(!timeout)
				return;
	
			/* check if an error occured */
			if ((*pTWIIRPTL & TWIMERR) == TWIMERR)
			{
				*pTWIIRPTL |= TWIMERR;
				TWISuccess = 0;		
			}
		
			*pTWIIRPTL = TWITXINT | TWIMCOM;				// service TWI for next transmission
		}
	}

	
}


/*******************************************************************
*   Function:    TWI_MasterMode_Read
*   Description: do a master mode read
*******************************************************************/

void TWI_MasterMode_Read(unsigned short DeviceAddr, unsigned char *TWI_Data_Pointer, unsigned short Count)
{
	int i, j;
	
    *pTWIFIFOCTL = 0;									// clear the bit manually
	*pTWIMITR = TWIEN | PRESCALE_VALUE;			        // PRESCALE = fsclk/10MHz
	*pTWIDIV = ((CLKDIV_HI) << 8) | (CLKDIV_LO);	    // CLKDIV = (1/SCL)/(1/10MHz)
	*pTWIMADDR = DeviceAddr;						    // target address (7-bits plus the read/write bit)
		
	*pTWIMCTL = (Count<<6) | TWIMEN | TWIMDIR;          // start transmission
		
	                                                     // for each item
	for (i = 0; i < Count; i++)
	{ 
		while (*pTWIFIFOSTAT == TWIRXS)		            // wait for data to be in FIFO
		asm("nop;");

		*TWI_Data_Pointer++ = *pRXTWI8;		             // read the data
	Delay(6);
	}

	
	while ((*pTWIIRPTL & TWIMCOM) == 0)			       // wait until transmission complete and MCOMP is set
	 asm("nop;");

		*pTWIIRPTL = TWIRXINT | TWIMCOM;			   // service TWI for next transmission


}


/************************************************************************
*   Function:    Delay
*   Description: Provide a dely in terms of numbers of core clock cycles
*************************************************************************/

void Delay(int x)
{
	
	int i,j, NUM_BER;
	NUM_BER=x*100;
	for(i=0;i<NUM_BER;i++) {
		for(j=0;j<NUM_BER;j++) {
			asm("nop;");
		}
	}
	
}

