/* ------------------------------------------------------------------------------------------------ */
/* FILE :		uDemo_FreeA53.c																		*/
/*																									*/
/* CONTENTS :																						*/
/*				Demo for the free version of uAbassi												*/
/*				Serial port settings:																*/
/*							Baud Rate: 115200														*/
/*							Data bits:      8														*/
/*							Stop bits:      1														*/
/*							Parity:      none														*/
/*							Flow Ctrl:   none														*/
/*							Emulation: VT-100														*/
/*																									*/
/*																									*/
/* Copyright (c) 2017-2019, Code-Time Technologies Inc. All rights reserved.						*/
/*																									*/
/* Code-Time Technologies retains all right, title, and interest in and to this work				*/
/*																									*/
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS							*/
/* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF										*/
/* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL							*/
/* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR								*/
/* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,							*/
/* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR							*/
/* OTHER DEALINGS IN THE SOFTWARE.																	*/
/*																									*/
/*																									*/
/*	$Revision: 1.11 $																				*/
/*	$Date: 2019/02/20 15:32:36 $																	*/
/*																									*/
/* ------------------------------------------------------------------------------------------------ */
/*																									*/
/* DESCRIPTION:																						*/
/*	This demo is a simple example 
*/

#include "uAbassi.h"
#include "Platform.h"								/* Everything about the target processor is here*/
#include "HWinfo.h"									/* Everything about the tagret hardware is here	*/
#include "SysCall.h"								/* System layer stuff							*/

#if (((OS_PLATFORM) & 0x00FFFFFF) == 0x00007753)	/* Xilinx's UltraScale+ A53					*/
  #include "cd_uart.h"
#else
  #error "Un-supported platform"
#endif

/* ------------------------------------------------------------------------------------------------ */
/* App definitions																					*/

/* ------------------------------------------------------------------------------------------------ */
/* App variables																					*/

int G_UartDevIn  = UART_DEV;						/* Needed by the system call layer if used		*/
int G_UartDevOut = UART_DEV;						/* Needed by the system call layer if used		*/
int G_UartDevErr = UART_DEV;						/* Needed by the system call layer if used		*/

													/* Sem are global: timer callback posts them	*/
SEM_t *SemAE;										/* Semaphore used by Adam & Eve					*/
SEM_t *SemT0;										/* Semaphore Task 0 blocks on					*/
SEM_t *SemT2;										/* Semaphore Task 2 blocks on					*/

static const char *MtxNames[] = {
                              "Mtx 0", "Mtx 1", "Mtx 2", "Mtx 3", "Mtx 4", "Mtx 5", "Mtx 6", "Mtx 7",
                              "Mtx 8", "Mtx 9", "Mtx 10", "Mtx 11", "Mtx 12", "Mtx 13", "Mtx 14"
                                };
static const char *SemNames[] = {
                              "Sem 0", "Sem 1", "Sem 2", "Sem 3", "Sem 4", "Sem 5", "Sem 6", "Sem 7",
                              "Sem 8", "Sem 9", "Sem 10", "Sem 11", "Sem 12", "Sem 13", "Sem 14"
                                };
static const char *TskNames[] = {
                              "Tsk 0", "Tsk 1", "Tsk 2", "Tsk 3", "Tsk 4", "Tsk 5", "Tsk 6", "Tsk 7",
                              "Tsk 8", "Tsk 9", "Tsk 10", "Tsk 11", "Tsk 12", "Tsk 13", "Tsk 14"
                                };

static int g_MaxTsk;								/* Maximum number of tasks availeble			*/
static volatile int IsReady;						/* If the timer call back can proceed			*/

/* ------------------------------------------------------------------------------------------------ */
/* App functions																					*/

void Fct(void);										/* All tasks, other than A&E runs this			*/
void GetTime(char *String);							/* Convert RTOS timer tick to elapsed time		*/
void UpdDisp(int Tout);								/* To update the terminal display				*/

/* ------------------------------------------------------------------------------------------------ */
/* ------------------------------------------------------------------------------------------------ */
/* ------------------------------------------------------------------------------------------------ */

int main(void)
{
int    ii;											/* General purpose								*/
int    LastTick;									/* Reference RTOS tick count for spinning loop	*/
int    MaxMtx;										/* Maximum number of mutexes available			*/
int    MaxSem;										/* Maximum number of semaphore available		*/
int    MaxTsk;										/* Maximum number of task available				*/
MTX_t *Mtx;											/* To count # of available mutexes				*/
MTX_t *MtxDisp;										/* To give exclusive access to stdout			*/
int    Random;										/* Random seed									*/
SEM_t *Sem;											/* To count # of available semaphores			*/
TSK_t *Task;										/* General purpose								*/

	setvbuf(stdin,  NULL, _IONBF, 0);				/* By default, Newlib library flush the I/O		*/
	setvbuf(stdout, NULL, _IONBF, 0);				/* buffer when full or when new-line			*/
	setvbuf(stderr, NULL, _IONBF, 0);

/* ------------------------------------------------ */
/* Start uAbassi									*/

	IsReady = 0;

	OSstart();										/* Start uAbassi								*/

	MaxMtx = 1;										/* 1 mutex was used for G_OSmutex				*/
				
	SysCallInit();									/* Initialize the System Call layer				*/

	OSintOn();										/* Enable the interrupts						*/


/* ------------------------------------------------ */
/* SysTick Set-up									*/
													/* Timer tick needed for Sleep() & timeouts		*/
	OSisrInstall(OS_TICK_INT, &TIMtick);			/* Install the RTOS timer tick function			*/
	TIMERinit(((((OS_TICK_SRC_FREQ/OS_TICK_PRESCL)/100000)*(OX_TIMER_US))/(10)));
	GICenable(-OS_TICK_INT, 128, 0);				/* Level trig, Prio=Mid, all cores interrupted	*/

/* ------------------------------------------------ */
/* UART set-up										*/
/* 8 bits, No Parity, 1 Stop						*/

   #if (0U == ((UART_LIST_DEVICE) & (1U << (UART_DEV))))
	#error "Selected UART device is not in UART_LIST_DEVICE"
   #endif
													/* UART_Q_SIZE are defined in the makefile		*/
													/* See ??_uart.h for the UART_FILT etc defs		*/
	uart_init(UART_DEV, BAUDRATE, 8, 0, 10, (UART_Q_SIZE_RX), (UART_Q_SIZE_TX), UART_FILT_OUT_LF_CRLF
	                                                                          | UART_FILT_IN_CR_LF
	                                                                          | UART_ECHO
	                                                                          | UART_ECHO_BS_EXPAND);

   #if ((UART_DEV) == 0)							/* If the stdio UART_DEV is UART #0				*/
	OSisrInstall(UART0_INT, &UARTintHndl_0);		/* Install the UART interrupt handler			*/
	GICenable(-UART0_INT, 128, 0);					/* UART Must use level and not edge trigger		*/
   #endif											/* Int # -ve: the interrupt targets all cores	*/

   #if ((UART_DEV) == 1)							/* If the stdio UART_DEV is UART #1				*/
	OSisrInstall(UART1_INT, &UARTintHndl_1);		/* Install the UART interrupt handler			*/
	GICenable(-UART1_INT, 128, 0);					/* UART Must use level and not edge trigger		*/
   #endif											/* Int # -ve: the interrupt targets all cores	*/

   #if ((UART_DEV) == 2)							/* If the stdio UART_DEV is UART #2				*/
	OSisrInstall(UART2_INT, &UARTintHndl_2);		/* Install the UART interrupt handler			*/
	GICenable(-UART2_INT, 128, 0);					/* UART Must use level and not edge trigger		*/
   #endif											/* Int # -ve: the interrupt targets all cores	*/

   #if ((UART_DEV) == 3)							/* If the stdio UART_DEV is UART #3				*/
	OSisrInstall(UART3_INT, &UARTintHndl_3);		/* Install the UART interrupt handler			*/
	GICenable(-UART3_INT, 128, 0);					/* UART Must use level and not edge trigger		*/
   #endif											/* Int # -ve: the interrupt targets all cores	*/

  #if ((UART_SINGLE_MUTEX) >= 0)
	MaxMtx++;										/* Another mutex has been used by the UART		*/
  #endif

/* ------------------------------------------------ */
/* Application set-up								*/

	MaxTsk = 0;										/* Create all available tasks					*/
	do {											/* Create the task in suspended state to set	*/
		Task = TSKcreate(TskNames[g_MaxTsk], 0, 16384, &Fct, 0);	/* the task argument			*/
		if (Task != (TSK_t *)NULL) {				/* Is one of the available tasks				*/
			TSKsetArg(Task, MaxTsk);				/* Task argument now set, resume the task		*/
			TSKresume(Task);
			MaxTsk++;
			g_MaxTsk++;
		}
		if (g_MaxTsk >= (sizeof(TskNames)/sizeof(TskNames[0]))) {
			g_MaxTsk = -1;
			break;
		}
	} while (Task != (TSK_t *)NULL);

	MtxDisp = MTXopen("Mtx Disp");					/* Open / create the mutex for stdout			*/
	MaxMtx++;
	Mtx     = MTXopen("Sprintf");					/* Using sprintf to create RTC string			*/
	MaxMtx++;
	if ((Mtx     == (MTX_t *)NULL)
	||  (MtxDisp == (MTX_t *)NULL)) {
		puts("Error: not enough mutexes to run the test");
		OSintOff();
		for(;;);
	}
	do {											/* Keep opening mutexes to show the max #		*/
		Mtx = MTXopen(MtxNames[MaxMtx-2]);			/* Open anonymous semaphores					*/
		if (Mtx != (MTX_t *)NULL) {
			MaxMtx++;
		}
		if ((MaxMtx-2) >= (sizeof(MtxNames)/sizeof(MtxNames[0]))) {
			MaxMtx = -1;
			break;
		}
	} while (Mtx != (MTX_t *)NULL);

	SemAE   = SEMopen("SemAE");						/* Open / create the semaphore used by A & E	*/
	for (MaxSem=0 ; MaxSem<MaxTsk ; MaxSem++) {		/* And the ones used by the functions			*/
		Sem = SEMopen(SemNames[MaxSem]);
		if (Sem == (SEM_t *)NULL) {
			printf("Error: not enough semaphores (%d) to run the test\n", MaxSem);
			OSintOff();
			for(;;);
		}
	}

	do {											/* Keep opening semaphores to show the max #	*/
		Sem = SEMopen(SemNames[MaxSem]);			/* Open anonymous semaphores					*/
		if (Sem != (SEM_t *)NULL) {
			MaxSem++;
		}
		if (MaxSem >= (sizeof(SemNames)/sizeof(SemNames[0]))) {
			MaxSem = -1;
			break;
		}
	} while (Sem != (SEM_t *)NULL);

  #if ((UART_Q_SIZE_RX) != 0)						/* Take into account the UART semaphores		*/
	MaxSem++;
  #endif

  #if ((UART_Q_SIZE_TX) != 0)
	MaxSem++;
  #endif

	printf("\033[H\033[J");							/* Clear the terminal screen					*/

	printf("uAbassi is limited to:\n"); 
	printf("\n");
	if (g_MaxTsk > 0) {								/* g_MaxTsk+1 to include A&E					*/
		printf("Max # of tasks:      %2d (including Adam & Eve)\n", g_MaxTsk+1);
	}
	else {
		printf("Max # of tasks:      unlimited\n");
	}
	if (MaxSem > 0) {								/* MaxSem+1 to include SemAE					*/
	 #ifdef OS_UART_ISR
	  #if ((OS_UART_ISR) != 0)
	   #ifdef  __CD_UART_H
		MaxSem += 2;								/* 2 semaphores used by the UART				*/
	   #else
		MaxSem += 2;								/* 1 semaphore used by the UART					*/
	   #endif
	  #endif
	 #endif
		printf("Max # of semaphores: %2d\n", MaxSem+1);
	}
	else {
		printf("Max # of semaphores: unlimited\n");
	}
	if (MaxMtx > 0) {
		printf("Max # of mutexes:    %2d (including G_OSmutex)\n", MaxMtx);
	}
	else {
		printf("Max # of mutexes:    unlimited\n");
	}

	printf("\n");
	printf("This is a simple application where all the tasks are executing the same code.\n");
	printf("Each task blocks on its own semaphore, posted by the RTOS timer tick callback.\n"); 
	printf("The display shows if the timer didn\'t post the semaphore before blocking expiry.\n");
	printf("Normal processing is simulated with a do nothing loop.\n");
	printf("\n");
	printf("Info per core:\n");
	printf("    Task  : task running on the core\n");
	printf("            ----- indicates no tasks running\n");
	printf("    status: no display is running\n");
	printf("            (Timeout) timed out waiting for its semaphore\n"); 
	printf("\n-----------------------\n\n");

	IsReady = 1;
	Random  = 0;
	ii      = 0;
	do {
		MTXlock(MtxDisp, -1);						/* Using stdout, get exclusive access			*/
		UpdDisp(ii);
		MTXunlock(MtxDisp);							/* Done accessing stdout, release the lock		*/

		LastTick = G_OStimCnt;						/* Loop for a while to simulate processing		*/
		while((G_OStimCnt-LastTick) < (1+(OS_TICK_PER_SEC)/2));

		Random *=  214013;							/* Each task has it's own local variables		*/
		Random += 2531011;							/* So the random generation is independant		*/
													/* Wait to be unblocked by the RTOS timer		*/
		ii = SEMwait(SemAE, (OS_TICK_PER_SEC+(Random & 63)));

	} while(1);
}

/* ------------------------------------------------------------------------------------------------ */
/*																									*/
/* Tasks function																					*/
/*																									*/
/* All tasks (non Adam & Eve) use the same function.												*/
/*																									*/
/* ------------------------------------------------------------------------------------------------ */

void Fct(void)
{
int    jj;											/* General purpose								*/
int    LastTick;									/* Reference tick count for spinning loop		*/
MTX_t *MtxDisp;										/* To give exclusive access to stdout			*/
int    MyArg;										/* My task's argument							*/
SEM_t *MySem;										/* If Task 0 or 2, semaphore to block on		*/
int    Random;										/* Random seed									*/


	MyArg   = (int)TSKgetArg();						/* 0:Task 0 ; 1:Task 1 ; ...					*/
	Random  = 265657*(MyArg+1);						/* Init the random generator					*/

	for( ; IsReady==0 ; );							/* Wait for all max # to be checked				*/

	MtxDisp = MTXopen("Mtx Disp");					/* Open / create the mutex for stdout			*/
	MySem   = SEMopen(SemNames[MyArg]);				/* Create my own semaphore						*/

	do {
		Random *=  214013;							/* Each task has it's own local variables		*/
		Random += 2531011;							/* So the random generation is independant		*/

		if (MyArg == 0) {							/* Task #0 blocks on a semaphore posted by A&E	*/
			jj = SEMwait(MySem, (OS_TICK_PER_SEC+(Random & 63)));
		}
		else if (MyArg == 1) {						/* Task #1 blocks on itself, waiting for a bit	*/
			TSKsleep(7+(OS_TICK_PER_SEC));
			jj = 0;
		}
		else if (MyArg == 2) {						/* Task #2 blocks on a semaphore posted by A&E	*/
			jj = SEMwait(MySem, (OS_TICK_PER_SEC+(Random & 63)));	/* Only when number of cores>2	*/
		}
		else {										/* All others block on itself, waiting a bit	*/
			jj = SEMwait(MySem, (OS_TICK_PER_SEC+(Random & 63)));	/* Only when number of cores>3	*/
			TSKsleep(7+((MyArg*OS_TICK_PER_SEC)/2));
			jj = 0;
		}

		MTXlock(MtxDisp, -1);						/* Using stdout, get exclusive access			*/
		UpdDisp(jj);
		MTXunlock(MtxDisp);							/* Done accessing stdout, release the lock		*/

		LastTick = G_OStimCnt;						/* Loop for a while to simulate processing		*/
		while((G_OStimCnt-LastTick) < (1+(OS_TICK_PER_SEC/2)));

	} while(1);
}

/* ------------------------------------------------------------------------------------------------ */
/*																									*/
/* Display update																					*/
/*																									*/
/* ------------------------------------------------------------------------------------------------ */

void UpdDisp(int Tout)
{
int  ii;
int  MyCoreID;
char Time[9];										/* String to display the elkapsed time			*/

	printf("\033[20;0H");							/* Bring the cursor to the correct location		*/
	MyCoreID = COREgetID();							/* Get the core I am running on					*/

	for (ii=0 ; ii<OX_N_CORE ; ii++) {				/* Print the task names on all cores			*/
		printf("Core %d: %s %s\n", ii, (G_OStaskNow[ii]->Prio > OX_PRIO_MIN)
		                              ? "-----"		/* No task running on this core					*/
		                              : G_OStaskNow[ii]->TskName,
		                               ((Tout==0) || (MyCoreID != ii))
		                              ? "             "
		                              : "(timeout)    ");
	}

	GetTime(&Time[0]);
	printf("\n\n  %s\n", Time);						/* Print the elapsed time						*/

	return;
}

/* ------------------------------------------------------------------------------------------------ */
/*																									*/
/* RTOS Timer call back function																	*/
/*																									*/
/* ------------------------------------------------------------------------------------------------ */

void TIMcallBack(void)
{
static int Count  = 0;
static int Toggle = 0;								/* To select which semaphore to post			*/

	if (IsReady != 0) {
		if (++Count >= 2*OS_TICK_PER_SEC) {
			Count = 0;
			Toggle++;
			if (Toggle >= g_MaxTsk) {
				Toggle = 0;
			}
			if (Toggle == 0) {
				SEMpost(SemAE);						/* Time to wake up A&E							*/
			}
			else {
				SEMpost(SEMopen(SemNames[Toggle]));	/* We can use SEMopen() in the ISR because the	*/
			}										/* sempahore exists and is not created			*/
		}
	}

	return;
}

/* ------------------------------------------------------------------------------------------------ */
/* Use the RTOS tikmer tick to obtain a RTC															*/
/* Need a mutex as sprintf is not multi-thread safe													*/
/* ------------------------------------------------------------------------------------------------ */

void GetTime(char *String)
{
int    Hour;
int    ii;
int    Min;
MTX_t *MtxPrintf;
int    Sec;


	MtxPrintf = MTXopen("Sprintf");

	MTXlock(MtxPrintf, -1);

	ii  = G_OStimCnt								/* Take a snapshot of the RTIS time tick		*/
	    / OS_TICK_PER_SEC;
	Sec = ii % 60;
	ii /= 60;
	Min = ii % 60;
	ii /= 60;
	Hour = ii % 100;

	sprintf(&String[0], "%02d:%02d:%02d", Hour, Min, Sec);

	MTXunlock(MtxPrintf);

	return;
}

/* EOF */
