/******************************************************************************

  Copyright (C), 2001-2012, Texas Instrument.

 ******************************************************************************
  File Name     : Hook.c
  Description   : hooks to record the function/task enter and exit time stamp
					On C64x+ or C66x DSP
  History       :
  1.Date        : 2011-2-28
    Author      : Brighton Feng
    Modification: Created file

  2.Date         : 2012/11/18
    Author       : Brighton Feng
    Modification : Add record for TSCH

******************************************************************************/
#include <c6x.h>
#include <string.h>
#include "Hook.h"

/*since the profile data buffer is huge, normally it is allocated in
big external memory. If it is in external memory, 
do NOT make this buffer cacheable because this is write only buffer,
cache is not helpful, and it will cause cache conflict if it is cacheable*/
#pragma DATA_SECTION (ProfileData, ".far:External_NonCache_Data")
Hook_Record ProfileData[MAX_PROFILE_RECORD_NUM];

/*this variables should be located in internal memory for performance.
Initializing it to be 0xFFFFFFFF actually disable hook recording*/
unsigned int uiProfileDataCnt= 0xFFFFFFFF;   //total counter of records

/*call this function where you want to begin record,
recalling this function actually restarts the record.
If profile data buffer is in DDR, this function can only
be called after DDR initialization*/
#pragma NO_HOOKS(hook_init);
void hook_init()
{
	TSCL= 0; 	//enable TSC

	uiProfileDataCnt= 0;
	
	/*0xFFFFFFFF represents a invalid record*/
	memset(ProfileData, 0xFFFFFFFF, sizeof(ProfileData));
}

/*--------------------------------------------------------------
Following hook functions stop recording when record buffer full
thus save the records from the beginning of the program
---------------------------------------------------------------*/
#pragma CODE_SECTION (entry_hook, ".text:hook_funcs")
void entry_hook(void (* func_addr)())
{
	unsigned int uiOldGIE;

	//disable interrupt to make sure every record is atomic
	uiOldGIE= _disable_interrupts(); 	

	if(uiProfileDataCnt<MAX_PROFILE_RECORD_NUM)
	{
		ProfileData[uiProfileDataCnt].address_type= 
			(unsigned int)func_addr;
		ProfileData[uiProfileDataCnt].TSCL= TSCL;
		ProfileData[uiProfileDataCnt].TSCH= TSCH;

		uiProfileDataCnt++;
	}

	_restore_interrupts(uiOldGIE); 	//restore interrupt
}

#pragma CODE_SECTION (exit_hook, ".text:hook_funcs")
void exit_hook(void (* func_addr)())
{
	unsigned int uiOldGIE;

	//disable interrupt to make sure every record is atomic
	uiOldGIE= _disable_interrupts(); 	

	if(uiProfileDataCnt<MAX_PROFILE_RECORD_NUM)
	{
		/*function Address always has last 2 bits == 0.  
		"|HOOK_TYPE_FUNC_EXIT" to signal this is a function exit record
		in the record buffer, make it possible to distinguish record 
		type in the post processing tool*/
		ProfileData[uiProfileDataCnt].address_type= 
			((unsigned int)func_addr)|HOOK_TYPE_FUNC_EXIT;
		ProfileData[uiProfileDataCnt].TSCL= TSCL;
		ProfileData[uiProfileDataCnt].TSCH= TSCH;

		uiProfileDataCnt++;
	}

	_restore_interrupts(uiOldGIE); 	//restore interrupt
}

/*if multiple tasks are used, task switch must be recorded with
task switch hook feature of OS. Without task switch record,
the function executing when task switch happens can not be 
recognized correctly by post processing tool.
function hook should NOT be generated for this function*/
#pragma CODE_SECTION (task_switch_hook, ".text:hook_funcs")
#pragma NO_HOOKS(task_switch_hook);
void task_switch_hook(unsigned int prevTask, 
	unsigned int nextTask)
{
	unsigned int uiOldGIE;
	unsigned int uiTSCL, uiTSCH;

	//disable interrupt to make sure every record is atomic
	uiOldGIE= _disable_interrupts(); 	

	uiTSCL= TSCL;
	uiTSCH= TSCH;

	if(uiProfileDataCnt<MAX_PROFILE_RECORD_NUM)
	{
		/*task handle Address always has last 2 bits == 0.  
		"|HOOK_TYPE_TASK_EXIT" to signal this is an task exit record 
		in the record buffer, make it possible to distinguish record 
		type in the post processing tool*/
		ProfileData[uiProfileDataCnt].address_type= 
			prevTask|HOOK_TYPE_TASK_EXIT;
		ProfileData[uiProfileDataCnt].TSCL= uiTSCL;
		ProfileData[uiProfileDataCnt].TSCH= uiTSCH;

		uiProfileDataCnt++;
	}

	if(uiProfileDataCnt<MAX_PROFILE_RECORD_NUM)
	{
		/*task handle Address always has last 2 bits == 0.  
		"|HOOK_TYPE_TASK_ENTRY" to signal this is a task entry record
		in the record buffer, make it possible to distinguish record 
		type in the post processing tool*/
		ProfileData[uiProfileDataCnt].address_type= 
			nextTask|HOOK_TYPE_TASK_ENTRY;
		ProfileData[uiProfileDataCnt].TSCL= uiTSCL;
		ProfileData[uiProfileDataCnt].TSCH= uiTSCH;

		uiProfileDataCnt++;
	}

	_restore_interrupts(uiOldGIE); 	//restore interrupt
}

/*--------------------------------------------------------------
Following hook functions use record buffer as a circular buffer
thus save the last records when program stops
---------------------------------------------------------------*/
#pragma CODE_SECTION (entry_hook_circular, ".text:hook_funcs_circular")
void entry_hook_circular(void (* func_addr)())
{
	unsigned int uiOldGIE;
	unsigned int uiProfileDataIndex; //index in the circular buffer

	//return before hook is initialized
	if(0xFFFFFFFF==uiProfileDataCnt)
		return;

	//disable interrupt to make sure every record is atomic
	uiOldGIE= _disable_interrupts(); 	

	/*make data pointer circular*/
	uiProfileDataIndex= uiProfileDataCnt & (MAX_PROFILE_RECORD_NUM-1); 

	ProfileData[uiProfileDataIndex].address_type= 
		(unsigned int)func_addr;
	ProfileData[uiProfileDataIndex].TSCL= TSCL;
	ProfileData[uiProfileDataIndex].TSCH= TSCH;

	uiProfileDataCnt++;

	_restore_interrupts(uiOldGIE); 	//restore interrupt
}

#pragma CODE_SECTION (exit_hook_circular, ".text:hook_funcs_circular")
void exit_hook_circular(void (* func_addr)())
{
	unsigned int uiOldGIE;
	unsigned int uiProfileDataIndex; //index in the circular buffer

	//return before hook is initialized
	if(0xFFFFFFFF==uiProfileDataCnt)
		return;

	//disable interrupt to make sure every record is atomic
	uiOldGIE= _disable_interrupts(); 	

	/*make data pointer circular*/
	uiProfileDataIndex= uiProfileDataCnt & (MAX_PROFILE_RECORD_NUM-1); 

	/*Address always has last 2 bits == 0.  
	So, "| 1" to signal this is an exit record in the buffer. 
	makes it possible to distinguish enter against exit
	in the post processing tool*/
	ProfileData[uiProfileDataIndex].address_type= 
		((unsigned int)func_addr)|HOOK_TYPE_FUNC_EXIT;
	ProfileData[uiProfileDataIndex].TSCL= TSCL;
	ProfileData[uiProfileDataIndex].TSCH= TSCH;

	uiProfileDataCnt++;

	_restore_interrupts(uiOldGIE); 	//restore interrupt
}

/*if multiple tasks are used, task switch must be recorded with
task switch hook feature of OS. Without task switch record,
the function executing when task switch happens can not be 
recognized correctly by post processing tool.
function hook should NOT be generated for this function*/
#pragma CODE_SECTION (task_switch_hook_circular, ".text:hook_funcs_circular")
#pragma NO_HOOKS(task_switch_hook_circular);
void task_switch_hook_circular(unsigned int prevTask, 
	unsigned int nextTask)
{
	unsigned int uiOldGIE;
	unsigned int uiProfileDataIndex; //index in the circular buffer
	unsigned int uiTSCL, uiTSCH;

	//return before hook is initialized
	if(0xFFFFFFFF==uiProfileDataCnt)
		return;

	//disable interrupt to make sure every record is atomic
	uiOldGIE= _disable_interrupts(); 	

	uiTSCL= TSCL;
	uiTSCH= TSCH;

	/*make data pointer circular*/
	uiProfileDataIndex= uiProfileDataCnt & (MAX_PROFILE_RECORD_NUM-1); 

	/*task handle Address always has last 2 bits == 0.  
	"|HOOK_TYPE_TASK_EXIT" to signal this is an task exit record 
	in the record buffer, make it possible to distinguish record 
	type in the post processing tool*/
	ProfileData[uiProfileDataIndex].address_type= 
		prevTask|HOOK_TYPE_TASK_EXIT;
	ProfileData[uiProfileDataIndex].TSCL= uiTSCL;
	ProfileData[uiProfileDataIndex].TSCH= uiTSCH;

	uiProfileDataCnt++;

	/*make data pointer circular*/
	uiProfileDataIndex= uiProfileDataCnt & (MAX_PROFILE_RECORD_NUM-1); 

	/*task handle Address always has last 2 bits == 0.  
	"|HOOK_TYPE_TASK_ENTRY" to signal this is an task entry record 
	in the record buffer, make it possible to distinguish record 
	type in the post processing tool*/
	ProfileData[uiProfileDataIndex].address_type= 
		nextTask|HOOK_TYPE_TASK_ENTRY;
	ProfileData[uiProfileDataIndex].TSCL= uiTSCL;
	ProfileData[uiProfileDataIndex].TSCH= uiTSCH;

	uiProfileDataCnt++;

	_restore_interrupts(uiOldGIE); 	//restore interrupt
}

