////////////////////////////////////////////////////////////////////////////////
//
//     XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS"
//     SOLELY FOR USE IN DEVELOPING PROGRAMS AND SOLUTIONS FOR
//     XILINX DEVICES.  BY PROVIDING THIS DESIGN, CODE, OR INFORMATION
//     AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION
//     OR STANDARD, XILINX IS MAKING NO REPRESENTATION THAT THIS
//     IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT,
//     AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE
//     FOR YOUR IMPLEMENTATION.  XILINX EXPRESSLY DISCLAIMS ANY
//     WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE
//     IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR
//     REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF
//     INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
//     FOR A PARTICULAR PURPOSE.
//     
//     (c) Copyright 2003 Xilinx, Inc.
//     All rights reserved.
// 
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
// Filename:     simon.c
// 
// Description:    
// Simon Game demonstrating LED & LCD interfaces, sound interface and 
// push button interface also includes a software state machine
//
// Design Notes:
//   
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Xilinx APD
// History:
// 
//
//-----------------------------------------------------------------------------


//***************************************************
//
//  Simon -- Power PC Version
//
//*************************************************** 
#include "xgpio_l.h"
#include "xbasic_types.h"
//#include "xtime_l.h"
#include "sleep.h"
#include "xparameters.h"

#define GPIO_0_BASEADDR XPAR_OPB_GPIO_0_BASEADDR
#define LED1_to_LED4 0
#define LED4_to_LED1 1

void tone (int freq, int duration);
void tone_blink (int tone_select, int duration);
int ran_num_gen(int seed);
void example_3( void );

main() 
{
	// State Machine States
	#define	INIT		0
	#define	START		1
	#define	PLAYSYNC	3 
	#define	PLAY		4
	#define	TEST		5
	#define	FAIL		6

	// variables
	int	next_state; 		// state variables for state machine
	int	seed; 			// 16 bit random number seed
	int	current_seed; 		// current random seed
	int	note_count; 		// counter for notes played
	int	score;			// score counter
	int	current_note; 		// counter for current note being played
	unsigned int buttonread; 	// push button status (0=none presed, 1,2,4..=button number)
	int	button_hi; 		// true if a button is pressed
	int	lamp_on;		// light currently on
	int 	i;
	char 	*line1, *line2;	// LCD Output buffers
	Xboolean run_example_3;         // flag to run example_3 function
	                                // run if 1, skip otherwise


  Xboolean button_n, button_e, button_s, button_w, button_c; /* GPIO[13:9] */
  Xboolean led_n, led_e, led_s, led_w, led_c; /* GPIO[8:4] */
  Xboolean gpio_led3, gpio_led2, gpio_led1, gpio_led0; /*  GPIO [3:0] */
  Xboolean dip_sw8, dip_sw7, dip_sw6, dip_sw5, dip_sw4, dip_sw3, dip_sw2, dip_sw1; /* GPIO [21:14] */


// Each example can be run and observed using:
// EP)  Embedded hardware platform (EP)
// MS)  Modelsim simulation (MS)
// CS)  Chipscope on an embedded hardware platform (CS)
// GDB) GDB on an embedded hardware platform (GDB)
// Comments for each type of test scenario are provided for
// the example code. Each example is also provided in a standalone
// source file within its own dedicated directory.
//
// ****************************************************************************
// Example_1a
// GPIO Output loop using GPIO access functions (gpio.c)
// ****************************************************************************
// EP)  On an embedded platform, each LED will be turned on/off
//      in sequence from LED1 to LED4. To change the sequencing
//      direction uncomment the alternative lamp_on assignment
//      statement. Note this LED sequencing will occur too
//      fast for visual observation unless the call to usleep()
//      is included in the loop.
// MS)  For Modelsim simulation include the sim_sleep.c file to
//      eliminate any time delays added by usleep() calls. Compiling
//      the software with sim_sleep.c saves one from having to modify 
//      or remove all the usleep() calls. Modelsim will show GPIO output
//      lines changing as determined by the code in the loop.
// CS)  Chipscope monitoring of GPIO output will show signals on a hardware
//      platform changing state as in the ModelSim simulation. To observe actual
//      signal timing information compile software without sim_sleep.c 
// GDB) LED output can be observed visually on a hardware platform using
//      GDB with a breakpoint set on the lamp_on assignment statement.
//      Delay time between LED writes is controlled by time interval between
//      user issued GDB "continue" commands. Compile without sim_sleep.c

	lamp_on = 0x1;
	for (i=0; i<8; i++)
	{
		//GPIO_writeLED(lamp_on);
		XGpio_mSetDataReg(GPIO_0_BASEADDR, 1,(lamp_on<<4));
		lamp_on = ((lamp_on << 1) | (lamp_on >> 3)) & 0xf;
		//lamp_on = ((lamp_on >> 1) | (lamp_on << 3)) & 0xf;
		//usleep(500000);
	}	
	

// ****************************************************************************
// Example_1b
// GPIO Input and GPIO Output loop using GPIO access functions (gpio.c)
// ****************************************************************************
// This provides a means to observe the turnaround time between
// consecutive GPIO read and GPIO write calls.
// EP)  On an embedded platform, the LED state will reflect the
//      state of the corresponding pushbutton state.
//      ( Eg. PUSH1 Pressed -> LED1 ON )
//      Note this LED code will occur too fast for visual observation
//      unless the usleep() call in included within the loop. To increase
//      the chance capturing a user pressing the pushbutton the maximum
//      count value in the for loop can be increased to a larger number.
//      Compile without sim_sleep.c
// MS)  For Modelsim simulation include the sim_sleep.c file to
//      eliminate any time delays added by usleep() calls. Compiling
//      the software with sim_sleep.c saves one from having to modify 
//      or remove all the usleep() calls. Modelsim will show GPIO output
//      lines changing through loop control.
// CS)  Chipscope monitoring of GPIO output will show signals on a hardware
//      platform changing state as in ModelSim simulation. To observe actual
//      signal timing information compile software without sim_sleep.c 
// GDB) LED output can be observed visually on a hardware platform using
//      GDB with a breakpoint set on the buttonread assignment statement.
//      Delay time between LED write and the next sampling of the pushbutton
//      is controlled by time interval between user issued
//      GDB "continue" commands. Compile without sim_sleep.c

	//for (i=0; i<1000000; i++) 
	for (i=0; i<3; i++) 
	{
		buttonread = XGpio_mGetDataReg(GPIO_0_BASEADDR, 1);//set GDB breakpoint here
		                               //on reaching breakpoint,
					       //press and hold pushbutton
					       //let GDB continue
        buttonread &= 0x00003e00;
		XGpio_mSetDataReg(GPIO_0_BASEADDR, 1,(buttonread>>5));		//LED should reflect pushbutton state
		//usleep(500000);
	}

// ****************************************************************************
// Example_2
// Modify LCD text message
// ****************************************************************************
// EP)  On an embedded platform, the LCD will show the
//      user selected text string. Compile without sim_sleep.c
// MS)  For Modelsim simulation include the sim_sleep.c file to
//      eliminate any time delays added by usleep() calls. Compiling
//      the software with sim_sleep.c saves one from having to modify 
//      or remove all the usleep() calls. Modelsim will show the 
//      selected LCD text message on the appropriate GPIO output lines.
// CS)  Chipscope monitoring of GPIO output will show signals on a hardware
//      platform changing state as in ModelSim simulation. To observe actual
//      signal timing information and proper LCD operation compile software 
//      without sim_sleep.c 
// GDB) Modify the LCD text message via GDB by activating the breakpoint at
//      the location shown below. Open a local variables window in GDB and
//      alter the contents of the "line1" character array as desired. Make
//      sure the last character remains the end of string (Eg. '\0' or 0x00)
//      After issuing the GDB "continue" command the first line of the LCD
//      will display the new user text message.


    
//	LCD_Init();  // ADD LCD FUNCTIONALITY LATER

    XromLCDInit();
    XromLCDOn();
    XromLCDClear();

	(char *)line1 = "Simon";
	(char *)line2 = "for ML401";

    XromLCDPrintString(line1,line2);

	// write message to display
	// select desired line to view on LCD
	// by uncommenting desired strcpy instruction
	// or add your own message by duplicating the
	// strcpy command. Limit message size to 16 characters

//	strcpy (line1, "UltraController");
	//strcpy (line1, "LCD Message 1");
	//strcpy (line1, "LCD Message 2");
	//strcpy (line1, "LCD Message 3");
	(char *)line2 = "Simon's Score: 0";
//	LCD_Write (line1, line2); //set GDB breakpoint here
	
	// Example_3
	// See comments at beginning of example_3 function definition below
	//run_example_3 = XTRUE;
	run_example_3 = XFALSE;
	if( run_example_3 == XTRUE ) //set GDB breakpoint here to modify
        {                            //run_example_3 at run-time
		example_3();         //XTRUE == 1, run example_3
	}                            //XFALSE == 0, skip example_3

	// play a few tones
	for (i=0;i<4;i++) tone_blink(i,500000);
	for (i=0;i<4;i++) tone_blink(i,500000);

	// variable initialization
	next_state = INIT;

// ****************************************************************************
// Example_4
// Simon application code
// ****************************************************************************
// EP)  On an embedded hardware platform, the pushbuttons and LEDs are used
//      to interact with a user and the Simon application. The LCD screen
//      displays the score. Compile without sim_sleep.c
// MS)  For Modelsim simulation include the sim_sleep.c file to
//      eliminate any time delays added by usleep() calls. Compiling
//      the software with sim_sleep.c saves one from having to modify 
//      or remove all the usleep() calls.
// CS)  Chipscope monitoring of GPIO output will show signals on a hardware
//      platform changing state as in ModelSim simulation. To observe actual
//      signal timing information compile software without sim_sleep.c 
// GDB) Compile without sim_sleep.c for EP setup using GDB. Set breakpoints
//      as desired while interacting with simon.

	// begin simon game
	// main routine - loop forever
	while (1) 
	{

		// state machine
		switch (next_state)
		{
		// INIT: Initialize State
		case INIT:
			next_state = START;
			seed = 0;
			current_seed = 0;
			score = 0;
			note_count = 1;
			current_note = 1;
			button_hi = 0;
			lamp_on = 0x1;
			buttonread = 1234;
			break; // INIT	

		// START: Flash lights until any button press, generate random seed
		case START:
			// shift through the lamps
			//GPIO_writeLED(lamp_on);
		    XGpio_mSetDataReg(GPIO_0_BASEADDR, 1,(lamp_on<<4));
			lamp_on = ((lamp_on << 1) | (lamp_on >> 4)) & 0x1f;
			usleep (50000); // delay 50ms for each lamp change
			// increment the random seed
			seed++;
			// if start button press & released then go to play state
			buttonread = XGpio_mGetDataReg(GPIO_0_BASEADDR, 1);
			buttonread &= 0x00003e00;
			if (buttonread != 0) 
				{
					button_hi =1;
					lamp_on = 0; // turn off lamps
					// clear an old score
					line2[14] = 0x30; line2[15] = 0x30;
//					LCD_Write (line1, line2);
                    XromLCDClear();
                    XromLCDPrintString(line1, line2);
					}					
			else // button press -- go to next state
				if (button_hi) next_state = PLAYSYNC;
			break; // START

		// PLAYSYNC: Delay and go to START after .5 sec
		case PLAYSYNC:
			usleep (500000);
			button_hi = 0;
			current_note = 1; // reset to first note for playback
			next_state = PLAY;
			break; // PLAYSYNC

		// PLAY: play a note up to current max note count
		case PLAY:
			// if all notes played for this cycle then go to test state
			if (current_note > note_count)
			{
				current_note = 1; // reset counter and seed to initial values
				current_seed = seed;
				button_hi = 0;
				next_state = TEST;
				} // next state setup
			else
			// otherwise play the note and increment the counters
			{	
				if (current_note == 1)
					current_seed = seed;
				else
					current_seed = ran_num_gen(current_seed);	// generate a new random number
				// plsy the note
				tone_blink(current_seed % 5, 300000); usleep (150000);
				current_note = current_note + 1; // increment note count
				} // increment for next note
			break; // PLAY

		// TEST:  poll the buttons for a correct push
		case TEST:
			buttonread = XGpio_mGetDataReg(GPIO_0_BASEADDR, 1);
			buttonread &= 0x00003e00;
			if (buttonread !=0)
			{
				// play correct note while button is pressed
				button_hi = 1;
				// if correct button is pressed then play the note for 10ms
				if (buttonread == ((0x1 << current_seed % 5)<<9))
					tone_blink(current_seed % 5, 10000); 
				// if not correct button then go to FAIL state
				else
					next_state = FAIL;
				} // test for button push
			// if button release then stop note and prepair for next button press
			else 
				if (button_hi)
				{
					// if have passed current test round then go back to play
					if (current_note >= note_count)
					{
						note_count = note_count + 1; // increment note counter to test with one more note
						score = score + 1;
						// update new score on the display
						line2[14] = (score / 10) + 0x30; line2[15] = (score %10) + 0x30;
//						LCD_Write (line1, line2);
                        XromLCDClear();
                        XromLCDPrintString(line1, line2);
						next_state = PLAYSYNC;
						}
					else
					{
						button_hi = 0;
						current_note = current_note + 1;
						current_seed = ran_num_gen(current_seed);	// generate a new random number
						}
					} // test for button release
				break; // TEST

		// FAIL: play correct note several times if fail
		case FAIL:
			// stutter sound
			for (i=0; i < 10; i++)
			{
				tone_blink(current_seed % 5, 50000); usleep(50000);
				}
			next_state = INIT;
			break; // FAIL

		} // State Machine Switch
	} // } while
	return 0;
}  // } main


// tone generator -- frequency in Hz and duration in microseconds
void tone (int freq, int duration)
{
	int period, time, i;
	// compute period in microseconds
	period = 500000 / freq;
	// compute number of cycles to be played for duration
	time = duration /(period * 2);
	// play the tone
//	for (i=0; i < time; i++)  // IMPLEMENT LATER
//		{GPIO_writeSound(0x1); usleep(period); GPIO_writeSound(0x0); usleep(period);}
    for (i=0; i < time; i++) usleep(period);
	}

// tone_blink -- play one of three tones and flash matching lamp (0 to 2)
void tone_blink (int tone_select, int duration)
{
	//GPIO_writeLED( 1 << tone_select);
	XGpio_mSetDataReg(GPIO_0_BASEADDR, 1, ((1 << tone_select)<<4));
	tone (200 * (tone_select+1), duration);
	//GPIO_writeLED(0);
	XGpio_mSetDataReg(GPIO_0_BASEADDR, 1, 0);
	}

// random number generator
// this simple random number generator does not assure a uniform random distribution
int ran_num_gen(int seed)
{
	int a,b;
	// generate random number by xnor of the seed with cirular shifts of itself
	seed &= 0xff; // work with 16 bit seed
	a = (seed << 2) | (seed >> 14); // left circular shift 2
	b = (seed << 7) | (seed >> 8) ; // left circular shift 7
	b = b & ~0x001 | (0x001 & ( ~b       ^ ~(b >> 1) ^ ~(b >> 2) ^ ~(b >> 3))); // replace bit 0 with xnor of bits 0-3
	b = b & ~0x400 | (0x004 & (~(b << 3) ^ ~(b << 4) ^ ~(b << 5) ^ ~(b << 6))); // replace bit 10 with xnor of bits 4-7
	a = a & ~0x002 | (0x002 & ((a << 1)  ^  a        ^ (a >> 1)  ^ (a >> 2))); // replace bit 1 with xor of bits 0=3	
	a = a & ~0x1000| (0x1000& ((a << 8)  ^ (a << 7)  ^ (a << 6)  ^ (a << 5))); // replace bit 12 with xor of bits 4-7
	return (~(a ^ b) ^ ~seed); // return random number
	}	

// ****************************************************************************
// Example_3
// Changing LED update delay and sequencing direction 
// ****************************************************************************
// EP)  On an embedded hardware platform, the LEDs will sequence from LED4
//      to LED1 with an LED on-time determined by a delay. This sequence will
//      be repeated twice. The sequence direction and LED on-time are set
//      by local variables. To change code at compile time:
//      1) uncomment desired lines of code below to introduce new settings
//      2) save file
//      3) Tools > Compile Program Sources to generate new executable ELF file
//      Compile without sim_sleep.c
// MS)  For Modelsim simulation include the sim_sleep.c file to
//      eliminate any time delays added by usleep() calls. Compiling
//      the software with sim_sleep.c saves one from having to modify 
//      or remove all the usleep() calls. Modelsim will show GPIO output
//      lines changing through loop control.
// CS)  Chipscope monitoring of GPIO output will show signals on a hardware
//      platform changing state as in ModelSim simulation. To observe actual
//      signal timing information compile software without sim_sleep.c 
// GDB) Compile without sim_sleep.c for EP setup using GDB. By setting a
//      breakpoint just before entering the inner for loop one can alter
//      the execution flow by modifying:
//      1) usleep_delay to change time interval between LED updates 
//      2) led_direction to alter LED sequencing direction
//

void example_3()
{
	volatile unsigned int usleep_delay;
	volatile unsigned int led_direction;
	int i,j;
	int lamp_on;

        lamp_on = 1;
	for (i=0; i<2; i++)
	{
	  //usleep_delay = (unsigned int)500000;
	  usleep_delay = (unsigned int)20000000;
	
	  //led_direction = (unsigned int)LED1_to_LED4;
	  led_direction = (unsigned int)LED4_to_LED1;

	  // set breakpoint here to modify usleep_delay
	  // or led_direction before entering LED output loop
	  for (j=0; j<8; j++)
	  {
		//GPIO_writeLED(lamp_on);
		XGpio_mSetDataReg(GPIO_0_BASEADDR, 1,(lamp_on<<4));
		usleep(usleep_delay); //delay usleep_delay microseconds
                // Select direction of LED sequencing 
		// LED1 to LED4
		if (led_direction == LED1_to_LED4 )
		{
			lamp_on = ((lamp_on << 1) | (lamp_on >> 3)) & 0xf;
		}
		else
		{
		// LED4 to LED1
			lamp_on = ((lamp_on >> 1) | (lamp_on << 3)) & 0xf;
		}
	  }
	}
}
