/*H***************************************************************************
*
* $Archive:: /boards/dsk5509a_v1/examples/nitest/nitest.c                     $
* $Revision:: 4                                                               $
* $Date:: 12/23/05 8:59a                                                      $
* $Author:: Vss6admin                                                         $
*
* DESCRIPTION: Test program to verify operation of the NI C55xx Power
*              Optimization software. The core of the test function ramps
*              the DSP PLL from 12Mhz to 192MHz in steps of 12Mhz. When
*              the max frequency is reached the ramp is repeated.  This
*              provides a set of well known current values which the NI
*              C55xx software can monitor for demonstration and validation
*              purposes.  The test can also be configured for dip switch
*              mode where user determines the pll frequency.  
*
*   
* (C) Copyright 2005 by Spectrum Digital Incorporated
* All rights reserved
*
*H***************************************************************************/

#include "nitestcfg.h"

/*
 *  The test uses the Chip Support Library for basic definitions as well as
 *  McBSP manipulation.  Programs that use the CSL must include the
 *  appropriate header files.
 */
#include <csl.h>
#include <csl_mcbsp.h>
#include <csl_dma.h>
#include <csl_timer.h>
#include <csl_gpio.h>

/*
 *  The Board Support Library is divided into several modules, each
 *  of which has its own include file.  The file DSK5509.h must be included
 *  in every program that uses the BSL.  This example also includes
 *  BSL header files for each module it uses.
 */
#include "dsk5509.h"
#include "dsk5509_led.h"

// GPIO 6-5 are used to set the DSP core voltage for 1.2, 1.4 and 1.6 volt
// operation.  
#define   VOLT_12   0
#define   VOLT_14   1
#define   VOLT_16   3

// Define the low and meg frequency cut off points.  When setting the PLL
// we optionally set the core voltage per the cut off frequency.
#define   LOW_FREQ  ((72 )+1)
#define   MED_FREQ  ((120)+1)

/*Variables used by the TEST_sleep() funciton */
Uint16 eventId1;
volatile Uint16 CompareError   = 0;
volatile Uint16 sleepCount     = 0;

// If UseDipSwitches is set to 1 then sw will use the value of the dip
// switches to select the pll frequency.  The frequency is defined as
// 12*(DipSwitchValue+1)
volatile int    UseDipSwitches = 0;

// If SetCoreVoltage is set to 1 then sw will set the core voltage when
// it sets the pll voltage.
volatile int    SetCoreVoltage = 0;


TIMER_Handle hTimer1;
TIMER_Config timerCfg1 = {
    TIMER_TCR_RMK(  TIMER_TCR_IDLEEN_DISABLE,
                    TIMER_TCR_FUNC_OF( 1 ),
                    TIMER_TCR_TLB_NORESET,
                    TIMER_TCR_SOFT_BRKPTNOW,
                    TIMER_TCR_FREE_WITHSOFT,
                    TIMER_TCR_PWID_OF( 0 ),
                    TIMER_TCR_ARB_RESET,
                    TIMER_TCR_TSS_STOP,
                    TIMER_TCR_CP_CLOCK,
                    TIMER_TCR_POLAR_LOW,
                    TIMER_TCR_DATOUT_0 ),
    TIMER_PRD_RMK(  TIMER_PRD_PRD_OF( 0x4e1f ) ),
    TIMER_PRSC_RMK( TIMER_PRSC_PSC_OF( 0 ),
                    TIMER_PRSC_TDDR_0F( 9 ) )
};

/*
 *  Interrupt Service Routines
 */

void sleepIsr()
{
    sleepCount++;
}

/*
 *  Accessory functions
 */
void TEST_sleep(Int16 sleeptime)
{
    sleepCount = 0;

    /* Get Event Id associated with Timer 1, for use with */
    /* CSL interrupt enable functions.                    */
    eventId1 = TIMER_getEventId(hTimer1);

    /* Map the logical event to a physical interrupt */
    IRQ_map(eventId1);

    /* Clear any pending Timer interrupts */
    IRQ_clear(eventId1);

    /* Enable timer interrupt */
    IRQ_enable(eventId1);

    /* Make sure global interrupts are enabled */
    IRQ_globalEnable();

    /* Start timer 1 */
    TIMER_start(hTimer1);

    while(sleepCount < sleeptime);

    /* Disable timer interrupt */
    IRQ_disable(eventId1);
}

void LED_binary(Int16 ledmask)
{
    Int16 i, bit;

    /* Walk through the bits in num setting corresponding LEDs */
    bit = 1;
    for (i = 0; i < 4; i++)
    {
        if (ledmask & bit)
            DSK5509_LED_on(i);
        else
            DSK5509_LED_off(i);
        bit = bit << 1;
    }

}

void LED_blink(Int16 ledmask, Int16 count)
{
    while (count > 0)
    {
        LED_binary(ledmask);
        TEST_sleep(100);
        LED_binary(0);
        TEST_sleep(150);
        count--;
    }
}

void LED_error(Int16 ledmask)
{
    while(1)
        LED_blink(ledmask, 1);
}


Int16 TEST_timer()
{
    /* Wait for 100 timer interrupts */
    TEST_sleep(100);

    return 0;
}

Int16 TEST_null()
{
    return 0;
}

#define PLL_RESV             0x8000
#define PLL_IAI              0x4000
#define PLL_IOB              0x2000
#define PLL_TEST             0x1000
#define PLL_MULT             0x0F80
#define PLL_DIV              0x0060
#define PLL_ENABLE           0x0010
#define PLL_BP_DIV           0x000C
#define PLL_BREAKLN          0x0002
#define PLL_LOCK             0x0001
#define PLL_MULT_MASK        0x0F80
#define PLL_RESET            0x2002

#define C55XX_CLKMD_ADDR     0x1C00
#define C55XX_CLKDIV_ADDR    0x07FD

typedef ioport volatile unsigned short * PIOPUS;  /* Generic pointer to io  */

#define C55XX_GPIO_ADDR         0x3400
void
C55xx_InitCoreVoltage()
{
    volatile unsigned short Reg;    
    PIOPUS pGpio = (PIOPUS)C55XX_GPIO_ADDR;
   
    // GPIO 5/6 output
	pGpio[1] |= 0x0060;
    // GPIO 5/6 output
	pGpio[0] |= 0x0060;
	TEST_sleep(100);	
}

void
C55xx_SetCoreVoltage( Uint16 Voltage, Uint16 Wait )
{
    register Uint16 Reg;    
    PIOPUS pGpio = (PIOPUS)C55XX_GPIO_ADDR;
    // GPIO 5/6 output
	Reg = pGpio[1];

	if( ((Reg>>5)&0x3) != Voltage ) {
	// Do not change the core voltage if already set to 
	// current value. This avoids the sleep.
		Reg &= ~0x0060;
		Reg |= (Voltage & 3)<<5;
		pGpio[1] = Reg;
		TEST_sleep(Wait);
	}	
}

int 
C55xx_PllBypass( void )
{

    volatile Uint16 PllLock;    
    PIOPUS pClkMode = (PIOPUS)C55XX_CLKMD_ADDR;
       
    *pClkMode = PLL_RESET;
   
    PllLock = 10;
    while(PllLock--);
                     
    do
    {
        PllLock = *pClkMode;
    }while( ( PllLock & PLL_LOCK ) == PLL_LOCK );

    return( 0 );
        
}
    
int
C55XX_PllSetFrequency( int InputFrequecy, int OutputFrequency )
{  

    Uint16   CoreVoltage;
    register Uint16 ClkModeSet;
    volatile Uint16 PllLock;    
    PIOPUS pClkMode = (PIOPUS)C55XX_CLKMD_ADDR;

    if( OutputFrequency > 192 ) {
		OutputFrequency = 192;
	}

    C55xx_PllBypass();
    // Optionally set the core voltage for the requested frequency.
	//
    if( SetCoreVoltage != 0 ) {
		if( OutputFrequency < LOW_FREQ ) {
			CoreVoltage = VOLT_12;
		} else if( OutputFrequency < MED_FREQ ) {
			CoreVoltage = VOLT_14;
		} else {
			CoreVoltage = VOLT_16;
		}
		// Each timer tick is ~166us with pll in bypass
    	C55xx_SetCoreVoltage( CoreVoltage, 5 );
	}

    ClkModeSet  =  (unsigned short)(OutputFrequency/InputFrequecy);
    // Only set the pll on if the frequency is greater then the 
    // input i.e. bypass frequency
    //
    if( ClkModeSet > 1 )
    {

		ClkModeSet = ClkModeSet<<7;
        ClkModeSet |= ( PLL_IOB | PLL_ENABLE );

        *pClkMode = ClkModeSet;

	    do
	    {
	        PllLock = *pClkMode;
	    }while( ( PllLock & PLL_LOCK ) != PLL_LOCK );
	}
    return( 0 );
}    

// Simple log function to trap errors if desired.
void LogCompareError( Uint16 Expected, Uint16 Acutal )
{
	CompareError++;
}

Uint16 TEST_ni( long Loop, long Decrement )
{
	volatile Uint16 value;
	Uint16  Table[16];
	Uint16  Compare;
	Uint16  Wait;
	Int16   i;
	Int16   TabSize = sizeof(Table)/sizeof(Uint16);
	Int16   Timeout;
	Int16   Reg;
	int     Gie;
	Int16   DipSwitch;
	Int16   CurrentDipSwitch;

 	PIOPUS pClkDiv = (PIOPUS)C55XX_CLKDIV_ADDR;

	// Set clockout to div 4 mode
	value = *pClkDiv;
	value &= 0xFFF0;
	value += 2;
	*pClkDiv = value;
	
	for(i=0; i<TabSize; i++ )
	{
		Table[i] = 0;
	}

	CurrentDipSwitch = -1;
	CompareError = 0;
	do
	{
		for(i=0; i<TabSize; i++ )
		{
			DipSwitch = (Int16)( DSK5509_rget(DSK5509_USER_REG) >> 4);
			DipSwitch &= 0x000F;

			if( UseDipSwitches == 0 ){
				// Just keep processing
			} else {
				if( DipSwitch == CurrentDipSwitch ) {
					continue;
				} else {
					// We have a new dip switch setting so process.
					CurrentDipSwitch = DipSwitch;
					i = DipSwitch;
				}
			}

			// Set the pll freq and wait for a while
			Wait = 1920/(12*(TabSize-i));
			DSK5509_rset(DSK5509_USER_REG, i);

			// Turn off capture while setting the pll  
            Reg = DSK5509_rget(DSK5509_MISC);
			Reg &= ~MISC_NITRIG;
			DSK5509_rset(DSK5509_MISC,Reg);
			// Set frequency
			C55XX_PllSetFrequency(12, (i+1)*12 );
            // Turn on capture
			Reg |= (MISC_NITRIG | MISC_NITEST); 
			DSK5509_rset(DSK5509_MISC,Reg);

			TEST_sleep(Wait);

			// Include a timeout to prevent hanging forever.  In theory
			// the only time this should hang is if one of the clocks
			// is turned off.
			//
			Timeout = 10000;
			Gie = IRQ_globalDisable();
			do{
				value = DSK5509_rget(DSK5509_NISTAT);
			}while(((value & NISTAT_VALID) == 0) && (Timeout-- > 0) );
			IRQ_globalRestore(Gie);

			// Store the code
			Table[i] = value & NISTAT_FREQ_MASK;		
			TEST_sleep(Wait);
		}
		
		if( UseDipSwitches == 0 ) 
		{
			// Now verify the codes are correct.
			for(i=0; i<TabSize; i++ )
			{	// 208 if sample period, times pll div 4
				Compare = 208 * (i+1) * (12/4);
				// Divide the fx clock
				Compare = Compare / 24; 
				// Toss out the 4 lsbs
				Compare = Compare >> 4;
				// When sample clock and DSP divide by 4 clock are equal
				// there is some accuracy in the count because we are
				// throwing out the 4 lsbs in the CPLD.  In this case we
				// are looking for a count of 13 but will sometimes read
				// a count of 12.
				if( Table[i] != Compare ) {
				    if( i != 7 ) {
						LogCompareError( Compare, Table[i]);
					} else {
						if( Table[i] != 12 ) {
							LogCompareError( Compare, Table[i]);
						}
					}
				}
			}
		} // End of UseDipSwitches

	}while( (Loop - Decrement ) > 0 );

	DSK5509_rset(DSK5509_MISC,0);
	return(CompareError);

}

void TEST_stop( Int16 Error )
{
	if( Error == 0 ) {
		LED_blink(0x05, 10);
	} else {
		LED_error( 1 );
	}
	while(1){}
}

main()
{
	int Error;
    /* Call BSL init */
    DSK5509_init();

    hTimer1 = TIMER_open(TIMER_DEV1, TIMER_OPEN_RESET);
    TIMER_config(hTimer1, &timerCfg1);
    IRQ_globalEnable();

    /* Initialize modules */
    DSK5509_LED_init();
	C55xx_InitCoreVoltage();
 
	Error = TEST_ni(1,0);

    /* Disable interrupts */
    IRQ_globalDisable();

	TEST_stop( Error );
}
