/*
 * scheduler.c
 *
 *  Created on: Jan 2, 2021
 *      Author: ianst
 */


#include "scheduler.h"
#include "stdio.h"


volatile uint32_t FreeTime=0;
volatile uint32_t Initialised=0;
volatile uint32_t SchedulerShield=0;

void scheduler_setup(void)
{

	 // Stop systick interrupt and make reload 0xffffff
	  SysTick->LOAD  = (uint32_t)(0xffffff);                         /* set reload register */
	  NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */
	  SysTick->VAL   = 0UL;/* Load the SysTick Counter Value */
	  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |
	                   SysTick_CTRL_ENABLE_Msk;


//  Set The Scheduler Going
	  TIM4->PSC = 0;         // Set prescaler  (PSC + 1)
	  TIM4->ARR = SCHEDULER_TIMER_RELOAD_VALUE; //Defined in setup.h
	  TIM4->DIER = TIM_DIER_UIE; // Enable update interrupt (timer level)
	  TIM4->CR1 = TIM_CR1_CEN;   // Enable timer
	  NVIC_SetPriority(TIM4_IRQn, 15); // Lowest Priority
	  NVIC_EnableIRQ(TIM4_IRQn); // Enable interrupt from TIM4 (NVIC level)
//	  TIM11->CR1 &= ~TIM_CR1_CEN;   // Disable timer


	  TIM4->CR1 = TIM_CR1_CEN;   // Enable timer
	  MACInit();
	  StartUpConfig();
	  Initialised=1;
}



void main_loop(void)
{
	  scheduler_setup();
	  while (1)
	  {

		  if (SchedulerShield==0)  PrintTasks();
		  FreeTime++;
	  }
}


char __attribute__((aligned(4))) Screen[SCREEN_ROWS*SCREEN_COLS];
uint8_t __attribute__((aligned(4))) DMA_Buffer_0[DMA_Buffer_0_Size];
char DeviceName[21]=DEVICE_NAME;
volatile uint32_t  T32=0;
volatile uint32_t timtmp;

#define TASK0_DIVISOR 5
#define TASK1_DIVISOR 5
#define TASK2_DIVISOR 2
#define TASK3_DIVISOR 4
#define TASK4_DIVISOR 5  //4ms
#define TASK5_DIVISOR 5  //20ms
#define TASK6_DIVISOR 2  //100ms
#define TASK7_DIVISOR 5  //200ms
#define TASK8_DIVISOR 2  //1S
#define TASK9_DIVISOR 5  //2S


TaskCtl  TaskCtl9={0,0,0,0x7fffffff,0,TASK9_DIVISOR,1,&Task9,NULL,0,0,0}; // 2000 milli
TaskCtl  TaskCtl8={0,0,0,0x7fffffff,0,TASK8_DIVISOR,1,&Task8,&TaskCtl9,0,0,0}; // 1000 milli
TaskCtl  TaskCtl7={0,0,0,0x7fffffff,0,TASK7_DIVISOR,1,&Task7,&TaskCtl8,0,0,0}; // 200 milli
TaskCtl  TaskCtl6={0,0,0,0x7fffffff,0,TASK6_DIVISOR,1,&Task6,&TaskCtl7,0,0,0}; // 100 milli
TaskCtl  TaskCtl5={0,0,0,0x7fffffff,0,TASK5_DIVISOR,1,&Task5,&TaskCtl6,0,0,0}; // 20 milli
TaskCtl  TaskCtl4={0,0,0,0x7fffffff,0,TASK4_DIVISOR,1,&Task4,&TaskCtl5,0,0,0}; // 4 milli   /5
TaskCtl  TaskCtl3={0,0,0,0x7fffffff,0,TASK3_DIVISOR,1,&Task3,&TaskCtl4,0,0,0}; // 1 milli   /2
TaskCtl  TaskCtl2={0,0,0,0x7fffffff,0,TASK2_DIVISOR,1,&Task2,&TaskCtl3,0,0,0}; // 500 micro /5
TaskCtl  TaskCtl1={0,0,0,0x7fffffff,0,TASK1_DIVISOR,1,&Task1,&TaskCtl2,0,0,0}; // 100 micro /2
TaskCtl  TaskCtl0={0,0,0,0x7fffffff,0,TASK0_DIVISOR,1,&Task0,&TaskCtl1,0,0,0}; // 20 micro


void TIM4_IRQHandler( void )
{
  timtmp=0xffffff-SysTick->VAL;
  if ((0xffffff&T32)>timtmp)	   T32+=0x1000000;
  T32&=0xff000000;
  T32|=timtmp;
  TIM4->SR &= ~TIM_SR_UIF; // clear UIF flag

	#if(__CORTEX_M>= 0x04)
 	    asm	("VMOV.F32 S0,S0;\n\t");
	 #endif
 	    	 	    asm	(
 	    	"MRS R0,PSR\n\t"
		   	"PUSH {R0, LR}\n\t"
		   	"SUB SP ,SP, #0x20\n\t"
		   	"LDR R0, =Reentrant_Handler_thread_pt\n\t"
		   	"STR R0,[SP, #24]\n\t"
		   	"LDR R0,=0x01000000\n\t"
		   	"STR R0,[SP,#28]\n\t"
		   	"LDR R0,=0xFFFFFFF9\n\t"
		   	"MOV LR, R0\n\t"
		   	"BX LR\n\t"
		   	"Reentrant_Handler_thread_pt:\n\t"
		   	"BL Reentrant_Interrupt_Handler \n\t"
 	    	 //		   	"LDR R0,=0xE000ED23\n\t"  /Systick Int
 			//"LDR R0,=0xE000E41E\n\t"   // TIM3 INT
 	    	"LDR R0,=0xE000E41E\n\t"   //TIM4 INT
 	    	"LDR R0,[R0]\n\t"
		   	"MSR BASEPRI, R0\n\t"
		   	"SVC 0\n\t"
		   	"B .\n\t");
}








char * OutText(int Row, int Col, int NoChars, char * s)
{
 char * t;
 t=Screen+Col+(Row*SCREEN_COLS);
 while (NoChars>0)
 {
	 if (t<(&Screen[SCREEN_COLS*SCREEN_ROWS]))	  *t++=*s++;
	 NoChars--;
 }
 return t;
}

uint32_t timextnd(void)
{
 uint32_t T32Local;
 uint32_t timt;

 T32Local=T32;
 timt=0xffffff-SysTick->VAL;

 if ((0xffffff&T32Local)>timt)
 {
  T32Local+=0x1000000;
 }
 T32Local&=0xff000000;
 T32Local|=timt;
 return T32Local;
}

void Wait_N_Cycles(uint32_t Cycles)
{
	volatile uint32_t start;
	Cycles&=0x3fffffffl;  // about 6 Seconds at 168MHz
	if (Cycles>0)
	{
	 start=timextnd();
	 while ((int32_t)(Cycles)>(int32_t)(timextnd()-start));
	}
}

void Wait_N_uS(uint32_t uS)
{
	volatile uint32_t start;
	uS*=CYCLES_PER_US;
	start=timextnd();
	while ((uS)>(timextnd()-start));
}

void Wait_N_100nS(uint32_t N)
{
	volatile uint32_t start;
	N=((CYCLES_PER_US*N)/10);
	start=timextnd();
	while ((N)>(timextnd()-start));
}

void DoTask(TaskCtl * T, uint32_t Told)
{
 uint32_t Tnew=0;
 if ((T->InTask)==0)
 {
  T->InTask=1;
  T->CallCount++;
  (T->ThisTask)();
  if (SchedulerShield==0)
  {
   #ifdef SCHEDULER_JITTER
    while ((timextnd()-Told+SCHEDULER_JITTER)<T->MaxTimeInTask) { };
   #endif
   Tnew = timextnd();
   T->TimeInTask=Tnew-Told;
   if (T->MaxTimeInTask<T->TimeInTask) T->MaxTimeInTask=T->TimeInTask;
   T->TaskPeriod=Told-T->LastTime;
  } //scheduler shield for flash program
  T->LastTime=Told;
  T->InTask=0;
  if (--T->DownCounter==0)
  {
   T->DownCounter=T->DownRatio;
   if (T->NextTaskCtl!=NULL)
   {
    DoTask(T->NextTaskCtl,Tnew);
   }
  }
 }
 else
 {
  if (T->ReEntryCount!=255) T->ReEntryCount++; // else NVIC_SystemReset();
 }
}

void Task0(void) //
{
	if (Initialised==1)
	{
	}
}

void Task1(void) //
{
	 if (Initialised==1)
	 {

	 }
}

void Task2(void) //
{
	if (Initialised==1)
	{
		 IPDo();
	}
}

void Task3(void) //
{
	HAL_IncTick();  // 1ms Tick inc for HAL
	if (Initialised==1)
	{

	}
}

void Task4(void) // 4ms
{


	if (Initialised==1)
	{
	}

}

void Task5(void) //
{
}


void Task6(void) //
{
}

void Task7(void) //
{
}

void Task8(void) //
{
	FLASH_THE_LED;
}

void Task9(void) //
{
	SchedulerShield=0;// to protect the whole task list from flash programme badness
}





void TaskPrint(TaskCtl * T,uint32_t n)
{
 char c[SCREEN_COLS+1];
 int i;
 for (i=0;i<SCREEN_COLS;i++) c[i]=' ';
 sprintf(c,"   T%1ld:%9ld:%10lu:%5.1f: %5.1f:",n,T->ReEntryCount,T->TaskPeriod,100.0*(double)T->MaxTimeInTask/((double)T->TaskPeriod+0.1),100.0*(double)T->TimeInTask/((double)T->TaskPeriod+0.1));
 OutText(n+1, 0, SCREEN_COLS, c);
}

void PrintTasks(void)
{
 char c[SCREEN_COLS+1];
 int i;
 for (i=0;i<SCREEN_COLS;i++) c[i]=' ';
 sprintf(&c[0],"Task:      O/R:   Cycles : Max%%: Inst%%:         ");
 OutText(0, 1, SCREEN_COLS, c);

 TaskPrint(&TaskCtl0,0);
 TaskPrint(&TaskCtl1,1);
 TaskPrint(&TaskCtl2,2);
 TaskPrint(&TaskCtl3,3);
 TaskPrint(&TaskCtl4,4);
 TaskPrint(&TaskCtl5,5);
 TaskPrint(&TaskCtl6,6);
 TaskPrint(&TaskCtl7,7);
 TaskPrint(&TaskCtl8,8);
 TaskPrint(&TaskCtl9,9);


 for (i=0;i<SCREEN_COLS;i++)  c[i]=' ';
 sprintf(&c[0],"ETH_Dropped_Packets   :%10lu     ",ETH_Dropped_Packets);
 OutText(13, 1, SCREEN_COLS, c);
 sprintf(&c[0],"ETH_ARP_Responses     :%10lu     ",ETH_ARP_Responses);
 OutText(14, 1, SCREEN_COLS, c);
 sprintf(&c[0],"ETH_ICMP_Responses    :%10lu     ",ETH_ICMP_Responses);
 OutText(15, 1, SCREEN_COLS, c);
 sprintf(&c[0],"ETH_UDP_Xmits         :%10lu     ",ETH_UDP_Xmits);
 OutText(16, 1, SCREEN_COLS, c);
 sprintf(&c[0],"ETH_ENC_Resets        :%10lu     ",ETH_ENC_Resets);
 OutText(17, 1, SCREEN_COLS, c);
 sprintf(&c[0],"ETH_ENC_Rx_Resets     :%10lu     ",ETH_ENC_Tx_Resets);
 OutText(18, 1, SCREEN_COLS, c);
 sprintf(&c[0],"ETH_ENC_Tx_Resets     :%10lu     ",ETH_ENC_Tx_Resets);
 OutText(19, 1, SCREEN_COLS, c);

 sprintf(&c[0],"SystemCoreClock       :%10lu (Hz)",SystemCoreClock );
 OutText(23, 1, SCREEN_COLS, c);

 for (i=0;i<SCREEN_COLS;i++)  c[i]=' ';
 sprintf(&c[0],"DeviceName            : %s",DeviceName );
 OutText(24, 1, SCREEN_COLS, c);

}

