/****************************************************************************/
/*	Copyright (c) 2009, Bendor Research Pty. Ltd. All rights reserved.		*/
/*	This software is released under the GNU General Public License, version	*/
/*	3, with the exemptions that 1) the header files installed by this 	 	*/
/*	package can be included in closed-source code and 2) the libraries		*/
/*	built by this package can be statically linked into a closed-source 	*/
/*	binary. See the file LICENSE for the details.							*/
/*	This software is provided "AS IS", without any warranty or guarantee.	*/
/*	By using this software you acknowledge that neither Bendor Research nor	*/
/*	any of its personnel or affiliates can be held liable for any damages,	*/
/*	including, but not limited to, loss of business, loss of money, loss of */
/*	life, loss of reputation, arising out of this software being faulty or	*/
/*	not performing as advertised, even if Bendor Research was aware of such	*/
/*	faults in the software. You use this software at your own risk, any and */
/*	all liabilities that arise out of your using this software are strictly	*/
/*	yours.																	*/
/****************************************************************************/

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>

/****************************************************************************/
/*						Definitions											*/
/****************************************************************************/

#define	FLASH_T		50				// FLASH access speed [ns]

/*
*	This structure is used for the CPU clock calculations
*/

typedef struct {

	int		pll_m;					// PLL M value
	int		pll_n;					// PLL N value
	int		cpu_d;					// CPU clock divider
	int		usb_d;					// USB clock divider
	int		rtc_q;					// RTC divisor quotient
	int		rtc_r;					// RTC divisor remainder
	double	rtc_e;					// RTC frequency error
	double	usb_e;					// USB frequency error
	double	vco_f;					// VCO frequency
	double	cpu_f;					// CPU frequency
	double	cpu_e;					// CPU frequency error

} FREQ;

/*
*	This structure is used for the UART calculations
*/

typedef struct {

	int		brate;					// Requested baud rate
	double	final;					// Final baudrate
	double	error;					// Error (percentage)
	int		i_val;					// Integer divisor
	int		a_val;					// Fractional divider A value
	int		m_val;					// Fractional divider M value

} BDIV;

typedef struct chip {

	int		*name;					// Names of the chips
	int		*pdiv;					// Peripheral division values
	int		(*prep)(void);			// Preparation function
	int		(*calc)(void);			// Calculation function
	void	(*dump)(void);			// Output function

} CHIP;

/****************************************************************************/
/*						Prototypes											*/
/****************************************************************************/

static	BDIV	*CommandLine( int argc, char **argv );
static	int		CalculateSio( void );
static	void	CalculateRtc( void );
static	int		CalculateGcd( int a, int b );
static	int		BrateCompare( const void *a, const void *b );

static	int		Prep210x( void );
static	int		Calc210x( void );
static	void	Dump210x( void );
static	int		Prep23xx( void );
static	int		Calc23xx( void );
static	void	Dump23xx( void );

static	void	DumpHdr( void );
static	void	DumpSio( void );
static	void	DumpRtc( void );

/****************************************************************************/
/*						Data												*/
/****************************************************************************/

/*
*	Known chips
*/

static CHIP	chips[] = {

	{
		(int[])
		{
			2101, 2102, 2103, 0
		},

		(int[]) { 4, 1, 2, 0 },
		Prep210x,
		Calc210x,
		Dump210x
	},

	{
		(int[])
		{
			2361, 2352, 2364, 2365, 2366, 2367,
			2368, 2377, 2378, 2387, 2388, 0
		},

		(int[]) { 4, 1, 2, 8, 0 },
		Prep23xx,
		Calc23xx,
		Dump23xx
	}
};

/*
*	Command line options
*/

static	double	xtal	= 0;		// Reference clock				[MHz]
static	double	cclk	= 0;		// Required CPU clock			[MHz]
static	double	cmax	= 60;		// Maximum CPU freq				[MHz]
static	double	cmin	= 0;		// Minimum CPU freq				[MHz]
static	double	usbe	= 0;		// USB error 					[ppm]
static	int		pdiv	= 0;		// UART P clock divider value	[-]
static	int		rdiv	= 0;		// RTC P clock divider value	[-]
static	int		form	= 0;		// Use alternate format			[-]
static	int		chip	= 2378;		// Chip type					[-]
static	char	*stem	= NULL;		// Name stem					[-]

/*
*	These are the low frequency PLL multipliers, as per NXP
*/

static const int lowf_m[ 65 ] = {

	 4272,  4395,  4578,  4725,  4807,
	 5127,  5188,  5400,  5493,  5859,
	 6042,  6075,  6104,  6409,  6592,
	 6750,  6836,  6866,  6958,  7050,
	 7324,  7425,  7690,  7813,  7935,
	 8057,  8100,  8545,  8789,  9155,
	 9613, 10254, 10376, 10986, 11719,
	12085, 12207, 12817, 13184, 13672,
	13733, 13916, 14099, 14420, 14648,
	15381, 15564, 15625, 15869, 16113,
	16479, 17578, 18127, 18311, 19226,
	19775, 20508, 20599, 20874, 21149,
	21973, 23071, 23438, 23804, 24170
};

/*
*	Globals used during the calculations. Yes, globals are ugly and shall
*	be banished, but in this small program they can do no harm and make
*	things much simpler.
*/

static	int		vco_min;				// Lowest PLL frequency [MHz]
static	int		vco_max;				// Highest PLL frequency [MHz]
static	CHIP	*devp;					// Pointer to the device
static	FREQ	pll;					// PLL calculation structure
static	BDIV	*btab;					// UART baudrates

/*
*	The help message
*/

static const char *usage =
"\n"
"This program calculates the PLL and UART clock values for LPC2xxx chips.\n"
"It writes the result to the standard output in a format that is a valid\n"
"C header file. All calculated values are #define-s in the file.\n"
"Calculation details are also written as comments.\n"
"\n"
"The following symbols are defined:\n"
"\tCLOCK_XTAL    is the reference clock in Hz\n"
"\tCLOCK_CCLK    is the achieved CPU clock in Hz\n"
"\tCLOCK_PLL_M   is the PLL's 'M' parameter\n"
"\tCLOCK_PLL_N   is the PLL's 'N' parameter\n"
"\tCLOCK_PLL_P   is the PLL's 'P' parameter\n"
"\tCLOCK_CPU_DIV is the CPU clock divider value\n"
"\tCLOCK_USB_DIV is the USB clock divider value (if requested)\n"
"\tCLOCK_MAM_TIM is the value to use in the MAMTIM register\n"
"\tCLOCK_SIO_DIV is the CCLK prescale code for the UART\n"
"\tCLOCK_SIO_CLK is the UART's peripheral clock in Hz\n"
"\tCLOCK_RTC_DIV is the CCLK prescale code for the RTC\n"
"\tCLOCK_RTC_CLK is the peripheral clock for the RTC\n"
"\tCLOCK_RTC_INT is the quotient for the RTC's fractional divider\n"
"\tCLOCK_RTC_INT is the remainder for the RTC's fractional divider\n"

"\n"
"The exit code is 0 in case of success and 1 if some error was detected,\n"
"in which case an error message is also sent to stderr.\n"
"\n"
"Usage:\n\n%1$s options [baud_rates]\n\nwhere the options are the following:\n\n"
"-d chip\n"
"    Defines the chip to do the calculations for. The 'chip' parameter is\n"
"    an integer, which is the chip's type without the LPC part.\n"
"    This option should be mandatory but for backward compatibility reasons\n"
"    it defaults to 2378.\n\n"
"-x xtal\n"
"    The reference clock frequency (float)\n"
"    If the number is less than 100, it is taken as MHz, otherwise as Hz.\n"
"    This option is mandatory.\n\n"
"-c cclk\n"
"    The required CPU clock (float).\n"
"    If the number is less than 100, it is taken as MHz, otherwise as Hz.\n"
"    This option is mandatory.\n\n"
"-s stem\n"
"    The name stem used for all definitions generated in the output. It is\n"
"    also the name that is used for the #ifdef protecting the file against\n"
"    multiple inclusions.\n"
"    The default is 'CLOCK'.\n\n"
"-h max_clock\n"
"    Maximum CPU clock (float).\n"
"    If the number is less than 100, it is taken as MHz, otherwise as Hz.\n"
"    The default is 60MHz or the specified clock (whichever is larger).\n"
"    If you specify a value that is less than the requested clock frequency\n"
"    the the requested clock frequency will be used instead.\n"
"    This value is only used if the required CPU clock can not be exactly\n"
"    achieved. A value close to the requested frequency will be accepted if\n"
"    it is not higher than this value.\n\n"
"-l min_clock\n"
"    Minimum CPU clock (float).\n"
"    If the number is less than 100, it is taken as MHz, otherwise as Hz.\n"
"    The default is 0. If you specify a value that is more than the requested\n"
"    clock frequency then the requested clock frequency will be used instead.\n"
"    This value is only used if the required CPU clock can not be exactly\n"
"    achieved. A value close to the requested frequency will be accepted if\n"
"    it is not lower than this value.\n\n"
"-u max_error\n"
"	 Maximum USB error (float).\n"
"    Indicates that USB is needed. In that case the VCO frequency must be\n"
"    set to an exact multiple of 96MHz. The parameter indicates the maximum\n"
"	 frequency error, in ppm. Specifying 0 or a negative value disables the\n"
"    USB calculations. The default is 0.\n\n"
"-p n\n"
"    The peripheral clock divider for the UART's clock (1, 2, 4 or 8)\n"
"    It should be specified if UART calculation are to be performed.\n"
"    If the chip has no individual dividers for its peripherals and the\n"
"    -r option is also used, then the two values must be identical.\n"
"    If this parameter is not specified, but the -r switch was used, then\n"
"    that value is assumed. If neither -p nor -r was used but baud rate\n"
"    values are present, then the default is 4.\n"
"-r n\n"
"    The peripheral clock divider for the RTC's clock (1, 2, 4 or 8)\n"
"    If it is specified, then the RTC's fractional divider values will be\n"
"    calculated, so that the RTC can run from the system clock rather than\n"
"    a dedicated 32.768 kHz crystal.\n"
"    If the chip has no individual dividers for its peripherals and the\n"
"    -p option is also used, then the two values must be identical.\n\n"
"-a\n"
"    Selects the alternate format for UART calculations.\n"
"    The default is to create three definitions for each baudrate:\n"
"      stem_SIO_I_XXXX  is the UART's integer divider for baud rate XXXX\n"
"      stem_SIO_M_XXXX  is the UART's fractional divider's 'M' value\n"
"      stem_SIO_A_XXXX  is the UART's fractional divider's 'A' value\n"
"    The alternative format is that an initialiser is defined for an\n"
"    array of structures. The structure has to have 4 members, the first\n"
"    one is the baud rate, the next one is the intefer divider value,\n"
"    then the fractional divider's M value and finally the fractional\n"
"    divider's A value. The suggested member sizes are int, short\n"
"    char and char, all unsigned. The initialiser is called\n"
"    stem_SIO_TABLE.\n\n"
"[baud_rates]\n"
"    The required baud rates, in baud (integers) separated by space\n"
"    If no values are given, then no UART calculations are performed.\n"
"\n"
"Examples:\n\n"
"%1$s -d 2378 -x 12 -c 72 -u 500 -p 2 115200 38400\n\n"
"     Calculate the PLL settings for an LPC2378 using a 12 MHz external\n"
"     crystal. The desired CPU clock is 72MHz, and clock for the USB is\n"
"     also needed, within 500ppm frequency error. The UART clock will be\n"
"     CCLK/2 and UART settings for for 115200 and 38400 baud are needed.\n\n"
"%1$s -d 2361 -x 32768 -c 48 -h 50 -l 45\n\n"
"     Calculates the settings for an LPC2361 chip that is clocked from a\n"
"     32.768 kHz watch crystal. The desired CPU clock is 48MHz, but if that\n"
"     can not be achieved, any frequency between 45 and 50MHz is accepted.\n"
"     No USB clock is needed and the UART is not in use.\n\n"
"%1$s -d 2103 -x 10 -c 60 -r 4 -p 4 -s MYSTUFF 38400\n\n"
"     Calculate the PLL settings for an LPC2103. The crystal is 10 MHz.\n"
"     The CPU should run at 60 MHz. The RTC will be clocked from the\n"
"     peripheral clock and its fractional divider values are needed. The\n"
"     peripheral clock is 1/4 of the CPU clock. The UART runs from the\n"
"     same peripheral clock (the LPC2103 has only one divider) and the\n"
"     only baudrate we need is 38400. All definitions in teh result file\n"
"     must have the stem of 'MYSTUFF'.\n\n"
"";

/****************************************************************************/
/*						Public functons										*/
/****************************************************************************/

/*
*	Program entry point
*	~~~~~~~~~~~~~~~~~~~
*/

int		main( int argc, char *argv[] )
{
FREQ		freq;
unsigned	i;
int			j;

	// We have to initialise freq, because gcc complains that it might
	// be used uninitialised. Also set the stem.

	memset( &freq, 0, sizeof( FREQ ) );
	stem = (char *) "CLOCK";

	if ( ! ( btab = CommandLine( argc, argv ) ) ) return( 2 );

	// Let's see if we know this chip

	for ( devp = NULL, i = 0 ; i < sizeof( chips ) / sizeof( CHIP ) ; i++ ) {

		for ( j = 0 ; chips[ i ].name[ j ] ; j++ )

			if ( chips[ i ].name[ j ] == chip )

				devp = chips + i;
	}

	if ( ! devp ) {

		fprintf( stderr, "Unknown chip type %d\n", chip );
		return( 2 );
	}

	if ( ! (*devp->prep)() ) return( 1 );
	if ( ! (*devp->calc)() ) return( 1 );
	if ( ! CalculateSio() ) return( 1 );
	CalculateRtc();
	(*devp->dump)();
	return( 0 );
}

/****************************************************************************/
/*						Local functons										*/
/****************************************************************************/

/*
*	Processes the command line
*	~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

static BDIV		*CommandLine( int argc, char **argv )
{
int		next, bcnt, temp;

	if ( argc < 3 ) {

		printf( usage, argv[ 0 ] );
		printf( "Known chip types:\n    " );

		temp = 0;

		for ( next = 0 ; next != sizeof( chips ) / sizeof( CHIP ) ; next++ ) {

			for ( bcnt = 0 ; chips[ next ].name[ bcnt ] ; bcnt++ ) {

				printf( "%d ", chips[ next ].name[ bcnt ] );
				if ( ++temp == 10 ) printf( "\n    " );
			}
		}

		if ( temp ) printf( "\n" );

		return( 0 );
	}

	for ( next = 1 ; next < argc ; next++ ) {

		// Switches start with a '-'

		if ( argv[ next ][ 0 ] != '-' ) break;

		// All switches are a single character

		if ( argv[ next ][ 2 ] ) {

			fprintf( stderr, "Unknown switch \"%s\"\n", argv[ next ] );
			return( 0 );
		}

		// Process switches with no parameter

		switch ( argv[ next ][ 1 ] ) {

			case 'a':

				form = 1;
				continue;

		}

		// All other switches have an additional argument

		if ( next == argc - 1 ) {

			fprintf( stderr, "Option %s needs an argument\n", argv[ next ] );
			return( 0 );
		}

		// Process the switches

		switch ( argv[ next ][ 1 ] ) {

		case 's':

				stem = argv[ ++next ];
				temp = 1;
				break;

			case 'd':

				temp = sscanf( argv[ ++next ], "%d", &chip );
				break;

			case 'u':

				temp = sscanf( argv[ ++next ], "%lf", &usbe );
				break;

			case 'x':

				temp = sscanf( argv[ ++next ], "%lf", &xtal );
				break;

			case 'c':

				temp = sscanf( argv[ ++next ], "%lf", &cclk );
				break;

			case 'p':

				temp = sscanf( argv[ ++next ], "%i", &pdiv );
				break;

			case 'r':

				temp = sscanf( argv[ ++next ], "%i", &rdiv );
				break;

			case 'l':

				temp = sscanf( argv[ ++next ], "%lf", &cmin );
				break;

			case 'h':

				temp = sscanf( argv[ ++next ], "%lf", &cmax );
				break;

			default:

				fprintf( stderr, "Unknown switch \"%s\"\n", argv[ next ] );
				return( 0 );
		}

		if ( temp != 1 ) {

			fprintf( stderr, "Invalid option argument \"%s\"\n", argv[ next ] );
			return( 0 );
		}
	}

	// Sanitise the values

	if ( xtal > 100 ) xtal /= 1E6;
	if ( cclk > 100 ) cclk /= 1E6;
	if ( cmin > 100 ) cmin /= 1E6;
	if ( cmax > 100 ) cmax /= 1E6;

	if ( usbe < 0    ) usbe = 0;
	if ( cmax < cclk ) cmax = cclk;
	if ( cmin > cclk ) cmin = cclk;

	// Test for limits and missing parameters

	if ( ! chip || ! xtal || ! cclk ) {

		fprintf( stderr, "The -d, -x and -c options are mandatory.\n" );
		return( 0 );
	}

	// Work out the legth of the baud rate table and try to allocate it

	bcnt = sizeof( BDIV ) * ( argc - next + 1 );

	if ( ! ( btab = malloc( bcnt ) ) ) {

		fprintf( stderr, "Allocating %d bytes failed\n",  bcnt );
		return( 0 );
	}

	// Read in the requested baud rates

	for ( bcnt = 0 ; next < argc ; next++, bcnt++ ) {

		if ( sscanf( argv[ next ], "%i", &temp ) != 1 ||
				 temp < 1 || temp > 9999999 ) {

			fprintf( stderr, "%s is not a valid baud rate\n", argv[ next ] );
			free( btab );
			return( 0 );
		}

		btab[ bcnt ].brate = temp;
	}

	// Just to be a good citizen we check the table for duplicate
	// values and we also sort it.

	qsort( btab, bcnt, sizeof( BDIV ), BrateCompare );

	for ( next = 0 ; next < bcnt - 1 ; next++ ) {

		if ( btab[ next ].brate == btab[ next + 1 ].brate ) {

			for ( temp = next + 1 ; temp < bcnt - 1 ; temp++ )

				btab[ temp ].brate = btab[ temp + 1 ].brate;

			bcnt--;
			next--;
		}
	}

	btab[ bcnt ].brate = 0;
	return( btab );
}

/*
*	Compare function for quicksort on baud rates
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

static	int		BrateCompare( const void *a, const void *b )
{
	if ( ((BDIV *) a)->brate > ((BDIV *) b)->brate ) return( +1 );
	if ( ((BDIV *) a)->brate < ((BDIV *) b)->brate ) return( -1 );
	return( 0 );
}

/*
*	Finds the greatest common divisor of two positive integers
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*	It uses the divisionless Euclidean algorithm, simplicity at its best.
*	Returns the GCD of a and b.
*/

static	int		CalculateGcd( int a, int b )
{
	if ( ! a ) return( b );

	while ( b ) {

		if ( a > b )

			a -= b;
		else
			b -= a;
	}

	return( a );
}

/*
*	Calculates the baud rate divisors from the peripheral clock
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*	Returns !0 on success, 0 if the required baud rate can't be achieved
*	within 3% error (as per the requirements of the standard).
*/

static	int		CalculateSio( void )
{
int		i, m, a, is, ms, as;
double	b, bs, e, es;
double	pclk;
BDIV	*bdiv;

	// Check if the peripheral clock division was specified

	if ( btab[ 0 ].brate && ! pdiv ) {

		fprintf( stderr, "UART calculations need a clock divider value!\n" );
		return( 0 );
	}

	// Calculate PCLK, in Hz

	pclk = 1E6 * pll.cpu_f / pdiv;

	// Do some unnecessary inits, gcc is not smart enough

	ms = 0;
	as = 0;
	is = 0;
	bs = 0;

	for ( bdiv = btab ; bdiv->brate ; bdiv++ ) {

		es = 1e6;

		// We do a brute-force search for the best solution.
		// We prefer solutions with the larger integer divisor.

		for ( a = 0 ; a < 15 ; a++ ) {

			for ( m = a+1 ; m <= 15 ; m++ ) {

				i = ( pclk / 16.0 / bdiv->brate / ( a + m ) ) * m;

				if ( i >= 65535 ) continue;
				if ( i < 1 ) i = 1;

				b = ( pclk / 16.0 / i / ( a + m ) ) * m;
				e = ( b - bdiv->brate ) / bdiv->brate;

				if ( fabs( e ) < fabs( es ) ||
					 ( fabs( e ) == fabs( es ) && i > is ) ) {

					bs = b;
					es = e;
					is = i;
					as = a;
					ms = m;
				}

				i++;
				b = ( pclk / 16.0 / i / ( a + m ) ) * m;
				e = ( b - bdiv->brate ) / bdiv->brate;

				if ( fabs( e ) < fabs( es ) ||
					 ( fabs( e ) == fabs( es ) && i > is ) ) {

					bs = b;
					es = e;
					is = i;
					as = a;
					ms = m;
				}
			}
		}

		if ( fabs( es ) > 0.03 ) {

			fprintf( stderr, "Can't synthetise %d baud\n", bdiv->brate );
			return( 0 );
		}

		bdiv->final = bs;
		bdiv->error = 100 * es;
		bdiv->i_val = is;
		bdiv->a_val = as;
		bdiv->m_val = ms;
	}

	return( 1 );
}

/*
*	Calculates the RTC fractional divider values
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

static	void	CalculateRtc( void )
{
	if ( rdiv ) {

		pll.rtc_q = 1E6 * pll.cpu_f / rdiv / 32768 - 1;
		pll.rtc_r = 1E6 * pll.cpu_f / rdiv - ( pll.rtc_q + 1 ) * 32768;
		pll.rtc_e = pll.rtc_q + 1 + pll.rtc_r / 32768.0;
		pll.rtc_e = ( 1E6 * pll.cpu_f / rdiv / pll.rtc_e - 32768 ) / 32768;
	}
}

/*
*	Tests whether the peripheral clock divider is a valid value
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*	If yes, it returns the code corresponding to the value, otherwise
*	it returns -1. The value 0 is accepted.
*/

static	int		ClockDivider( int value )
{
int		i;

	if ( ! value ) return( 0 );

	for ( i = 0 ; devp->pdiv[ i ] ; i++ )

		if ( devp->pdiv[ i ] == value )

			return( i );

	fprintf( stderr,
			 "The clock division factor of %d is not valid for the LPC%d\n",
			 value, chip );

	return( -1 );
}

/****************************************************************************/
/*						LPC210x functons									*/
/****************************************************************************/

/*
*	Preparation for calculations - 210x
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

static	int		Prep210x( void )
{
	vco_min = 156;
	vco_max = 320;
	usbe    = 0;

	if ( ClockDivider( pdiv ) < 0 ) return( 0 );
	if ( ClockDivider( rdiv ) < 0 ) return( 0 );

	if ( cclk < 10 || cclk > 60 ) {

		fprintf( stderr, "Invalid CPU clock value\n" );
		return( 0 );
	}

	if ( xtal < 1 || xtal > 25 ) {

		fprintf( stderr, "Invalid reference clock value\n" );
		return( 0 );
	}

	if ( rdiv && pdiv && rdiv != pdiv ) {

		fprintf( stderr, "For the LPC%d the RTC and the UART ", chip );
		fprintf( stderr, "dividers must be the same!\n" );
		return( 0 );
	}

	if ( ! pdiv ) {

		if ( rdiv )

			pdiv = rdiv;
		else
			pdiv = 4;
	}

	return( 1 );
}

/*
*	Calculate the PLL values
*	~~~~~~~~~~~~~~~~~~~~~~~~
*/

static	int		Calc210x( void )
{
double	vco_f, cpu_f, c_err, c_min;
int		pll_p, pll_m;

	c_min = 1e20;
	pll.vco_f = 0;

	// We do a brute-force search for the best solution (if it exists).

	for ( pll_m = 1 ; pll_m < 33 ; pll_m++ ) {

		for ( pll_p = 0 ; pll_p < 4 ; pll_p++ ) {

			vco_f = pll_m * xtal;
			cpu_f = vco_f / ( 4 << pll_p );
			c_err = fabs( ( cpu_f - cclk ) / cclk );

			if ( vco_f < vco_min ) continue;
			if ( vco_f > vco_max ) continue;
			if ( cpu_f > cmax ) continue;
			if ( cpu_f < cmin ) continue;
			if ( c_err > c_min ) continue;

			// This is a prospective contender. If the error is the same
			// as before, we replace it but only if the VCO frequency is
			// lower than before.

			if ( c_err < c_min || vco_f < pll.vco_f ) {

				// This solution is better than any we had before.

				c_min = fabs( c_err );
				pll.vco_f = vco_f;
				pll.pll_m = pll_m;
				pll.pll_n = pll_p;
				pll.cpu_f = cpu_f;
				pll.cpu_e = c_err;
			}
		}
	}

	if ( ! pll.vco_f ) {

		fprintf( stderr, "Can not synthetise the CPU clock.\n" );
		return( 0 );
	}

	return( 1 );
}

/*
*	Write the results
*	~~~~~~~~~~~~~~~~~
*/

static void		Dump210x( void )
{
	DumpHdr();

	printf( "#define %s_PLL_M   %11d\n", stem, pll.pll_m );
	printf( "#define %s_PLL_P   %11d\n", stem, pll.pll_n );

	printf( "#define %s_MAM_TIM %11d\n\n", stem,
		   (int) ceil( FLASH_T * 1E-3 * pll.cpu_f ) );

	DumpRtc();
	DumpSio();
	printf( "#endif\n" );
}

/****************************************************************************/
/*						LPC23xx functons									*/
/****************************************************************************/

/*
*	Preparation for calculations
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

static int	Prep23xx( void )
{
	// Set the family specific values

	vco_min	= 275;				// Lowest PLL frequency [MHz]
	vco_max	= 550;				// Highest PLL frequency [MHz]

	if ( ClockDivider( pdiv ) < 0 ) return( 0 );
	if ( ClockDivider( rdiv ) < 0 ) return( 0 );

	if ( cclk <= vco_min / 256.0 || cclk > 72 ) {

		fprintf( stderr, "Invalid CPU clock value\n" );
		return( 0 );
	}

	if ( xtal < 0.032768 || xtal > 40 ) {

		fprintf( stderr, "Invalid reference clock value\n" );
		return( 0 );
	}

	if ( ! pdiv ) {

		if ( rdiv )

			pdiv = rdiv;
		else
			pdiv = 4;
	}

	return( 1 );
}

/*
*	Calculate the PLL values
*	~~~~~~~~~~~~~~~~~~~~~~~~
*/

static	int		Calc23xx( void )
{
double	vco_f, c_err, u_err, c_min;
int		pll_n, pll_m, div_f, usb_d;
int		usb_e, c_sol, multi, gcd_v;

	c_min = 1e20;
	div_f = 0;
	usb_d = 0;
	u_err = 0;			// Just to keep older gcc versions happy
	pll.vco_f = 0;

	// We do a brute-force search for the best solution (if it exists).
	// We examine all valid PLL M,N combinations and select the solution
	// with the smallest error and lowest PLL frequency.

	for ( c_sol = 0, pll_n = 1 ; pll_n <= 32 ; pll_n++ ) {

		for ( multi = 0 ; multi <= 512-6+65 ; multi++ ) {

			usb_e = 0;
			pll_m = multi < 65 ? lowf_m[ multi ] : multi - 6;
			vco_f = ( xtal * 2 * pll_m ) / pll_n;

			if ( vco_f < vco_min || vco_f > vco_max ) continue;

			// If USB is required, we only accept the result if
			// it can be divided down to 96MHz with an error less
			// than the allowed error.

			if ( usbe ) {

				for ( div_f = 2 ; div_f <= 256 ; div_f += 2 ) {

					if ( vco_f / div_f > 48 * ( 1 - 1e-6 * usbe ) &&
						 vco_f / div_f < 48 * ( 1 + 1e-6 * usbe ) ) {

						u_err = ( vco_f / div_f ) / 48 - 1;
						usb_d = div_f;
						break;
					}
				}

				if ( div_f > 256 ) usb_e = 1;
			}

			// We should use the lowest N value. Thus, if M and N
			// has a common divisor, we should simplify the ratio.

			if ( pll_m <= 512 )

				gcd_v = CalculateGcd( pll_m, pll_n );
			else
				gcd_v = 1;

			// Run through the CPU dividers and find the division
			// that is closest to the required value

			for ( div_f = 2 ; div_f <= 256 ; div_f += 2 ) {

				if ( vco_f / div_f > cmax ) continue;
				if ( vco_f / div_f < cmin ) continue;

				c_err = ( vco_f / div_f - cclk ) / cclk;

				if ( fabs( c_err ) > c_min ) {

					// This is worse than what we've found before

					continue;
				}

				// This is a prospective contender. If the error is the same
				// as before, we replace it but only if the VCO frequency is
				// lower than before.

				if ( c_min > fabs( c_err ) || vco_f < pll.vco_f ) {

					// This solution is better than any we had before.

					if ( ! usb_e ) {

						c_min = fabs( c_err );
						pll.vco_f = vco_f;
						pll.pll_m = pll_m / gcd_v;
						pll.pll_n = pll_n / gcd_v;
						pll.usb_d = usb_d;
						pll.cpu_f = vco_f / div_f;
						pll.cpu_d = div_f;
						pll.cpu_e = c_err;
						pll.usb_e = u_err;
					}

					// Make note that we had a CPU solution

					c_sol = 1;
				}
			}
		}
	}

	if ( ! pll.vco_f ) {

		if ( c_sol )

			fprintf( stderr, "Can not synthetise the USB clock.\n" );
		else
			fprintf( stderr, "Can not synthetise the CPU clock.\n" );

		return( 0 );
	}

	return( 1 );
}

/*
*	Write the results
*	~~~~~~~~~~~~~~~~~
*/

static void		Dump23xx( void )
{
	DumpHdr();

	printf( "#define %s_PLL_M   %11d\n", stem, pll.pll_m );
	printf( "#define %s_PLL_N   %11d\n", stem, pll.pll_n );
	printf( "#define %s_CPU_DIV %11d\n", stem, pll.cpu_d );

	if ( usbe )

		printf( "#define %s_USB_DIV %11d\n", stem, pll.usb_d );

	printf( "#define %s_MAM_TIM %11d\n\n", stem,
		   (int) ceil( FLASH_T * 1E-3 * pll.cpu_f ) );

	DumpRtc();
	DumpSio();
	printf( "#endif\n" );
}

/****************************************************************************/
/*				Common parts of the output functions						*/
/****************************************************************************/

/*
*	Write the header and the most basic results
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

static	void	DumpHdr( void )
{
int		res;

	printf( "#ifndef _%s_H\n", stem );
	printf( "#define _%s_H\n\n", stem );

	printf( "/*\n* PLL calculations for LPC%d:\n*\n", chip );

	if ( xtal < 1 )

		printf( "*\tReference clock: %14.6f kHz\n", xtal * 1e3 );
	else
		printf( "*\tReference clock: %14.6f MHz\n", xtal );

	printf( "*\tRequired CPU clock: %11.6f MHz\n", cclk );
	printf( "*\tMax. CPU clock:     %11.6f MHz.\n", cmax );
	printf( "*\tMin. CPU clock:     %11.6f MHz.\n", cmin );

	if ( usbe )

		printf( "*\tUSB error limit:    %11.6f %%\n", usbe * 1E-4 );

	printf( "*\n" );
	printf( "*\tVCO frequency:      %11.6f MHz\n", pll.vco_f );
	printf( "*\tAchieved CPU clock: %11.6f MHz\n", pll.cpu_f );
	printf( "*\tCPU clock error:    %+11.6f %%\n", pll.cpu_e * 100 );

	if ( usbe )

		printf( "*\tUSB clock error:    %+11.6f %%\n", pll.usb_e * 100 );

	if ( rdiv )

		printf( "*\tRTC clock error:    %+11.6f %%\n", pll.rtc_e * 100 );

	if ( btab[ 0 ].brate ) {

		printf( "*\n" );
		printf( "* UART baudrate results:\n*\n" );
		printf( "*\tNominal       Achieved    Error\n" );
		printf( "*\t-------------------------------\n" );

		for ( res = 0 ; btab[ res ].brate ; res++ ) {

			printf( "*\t%7d     ", btab[ res ].brate );
			printf( "%10.2f   ", btab[ res ].final );
			printf( "%+4.2f%%\n", btab[ res ].error );
		}
	}

	printf( "*/\n\n" );
	printf( "#define %s_XTAL %14d\n", stem, (int) round( 1E6 * xtal ) );
	printf( "#define %s_CCLK %14d\n", stem, (int) round( 1E6 * pll.cpu_f ) );
}

/*
*	Write the RTC results
*	~~~~~~~~~~~~~~~~~~~~~
*/

static	void	DumpRtc( void )
{
	if ( rdiv ) {

		printf( "#define %s_RTC_DIV %11d\n", stem, ClockDivider( rdiv ) );
		printf( "#define %s_RTC_CLK %11d\n", stem,
			    (int) round( 1E6 * pll.cpu_f / rdiv ) );
		printf( "#define %s_RTC_INT %11d\n", stem, pll.rtc_q );
		printf( "#define %s_RTC_FRA %11d\n", stem, pll.rtc_r );
		printf( "\n" );
	}
}

/*
*	Write the UART results
*	~~~~~~~~~~~~~~~~~~~~~~
*/

static	void	DumpSio( void )
{
int		res;

	if ( ! btab[ 0 ].brate ) return;

	printf( "#define %s_SIO_DIV %11d\n", stem, ClockDivider( pdiv ) );
	printf( "#define %s_SIO_CLK %11d\n", stem,
			    (int) round( 1E6 * pll.cpu_f / pdiv ) );

	if ( ! form ) {

		printf( "\n" );

		for ( res = 0 ; btab[ res ].brate ; res++ ) {

			printf( "#define %s_SIO_I_%-7d %5d\n", stem,
				    btab[ res ].brate, btab[ res ].i_val );
			printf( "#define %s_SIO_M_%-7d %5d\n", stem,
				    btab[ res ].brate, btab[ res ].m_val );
			printf( "#define %s_SIO_A_%-7d %5d\n", stem,
				    btab[ res ].brate, btab[ res ].a_val );
			printf( "\n" );
		}
	}
	else {

		printf( "#define %s_SIO_TABLE \\\n", stem );

		for ( res = 0 ; btab[ res ].brate ; ) {

			printf( "\t{ %8d, %5d, %2d, %2d }",
				    btab[ res ].brate, btab[ res ].i_val,
				    btab[ res ].m_val, btab[ res ].a_val );

			if ( btab[ ++res ].brate ) printf( ", \\" );
			printf( "\n" );
		}
	}

	printf( "\n" );
}

