// viterbi_module.cpp : Defines the entry point for the console application.


#include "systemc.h"

#define	SCALE	1000
#define INT32	int
#define	BINARY	unsigned char
#define	MAX_LEVEL						3
#define	MAX_STREAM						4674
#define	MAX_BUFFER						4

#define MC_NUM_STATES					64
#define MC_NUM_OUTPUT_COMBINATIONS		16
#define MC_METRIC_INIT_VALUE			(1e10)
#define PP_TYPE_0000					0 /* not used, dummy */
#define PP_TYPE_1111					1
#define PP_TYPE_0111					2
#define PP_TYPE_0011					3
#define PP_TYPE_0001					4
#define PP_TYPE_0101					5

#define _REAL			double
#define _DECISIONTYPE	unsigned char
#define _BINARY			unsigned char

FILE* pLog	=	stdout;

SC_MODULE (bufferfly)
{
	sc_in<INT32> fxp_pOldTrelMetric_prev0;
	sc_in<INT32> fxp_pOldTrelMetric_prev1;
	sc_in<INT32> fxp_vecrMetricSet_met0;
	sc_in<INT32> fxp_vecrMetricSet_met1;

	sc_out<INT32> fxp_pCurTrelMetric_cur;
	sc_out<BINARY> matdecDecisions_cur;
	sc_out<INT32> fxp_pCurTrelMetric_next;
	sc_out<BINARY> matdecDecisions_next;

	SC_CTOR (bufferfly)
	{	
		SC_METHOD (process);
		sensitive	<<fxp_pOldTrelMetric_prev0
					<<fxp_pOldTrelMetric_prev1
					<<fxp_vecrMetricSet_met0
					<<fxp_vecrMetricSet_met1;
		dont_initialize();
	}
	
	void process()
	{
		/* First state in this set ------------------------------------ */ 
		/* Calculate metrics from the two previous states, use the old
		metric from the previous states plus the "transition-metric" */ 
		
		const INT32 fxp_rFiStAccMetricPrev0 =	fxp_pOldTrelMetric_prev0 + fxp_vecrMetricSet_met0; 
		const INT32 fxp_rFiStAccMetricPrev1 =	fxp_pOldTrelMetric_prev1 + fxp_vecrMetricSet_met1; 
		
		/* Take path with smallest metric */ 
		/*printf("diff in comp is %fn",(rFiStAccMetricPrev0 - rFiStAccMetricPrev1));*/ 
		if (fxp_rFiStAccMetricPrev0 < fxp_rFiStAccMetricPrev1) 
		{ 
			/* Save minimum metric for this state and store decision */ 
			fxp_pCurTrelMetric_cur = fxp_rFiStAccMetricPrev0; 
			matdecDecisions_cur = 0; 
		} 
		else 
		{ 
			/* Save minimum metric for this state and store decision */ 
			fxp_pCurTrelMetric_cur = fxp_rFiStAccMetricPrev1; 
			matdecDecisions_cur = 1; 
		} 
		
		/* Second state in this set ----------------------------------- */ 
		/* The only difference is that we swapped the matric sets */ 
		
		const INT32 fxp_rSecStAccMetricPrev0 = fxp_pOldTrelMetric_prev0 + fxp_vecrMetricSet_met1; 
		const INT32 fxp_rSecStAccMetricPrev1 = fxp_pOldTrelMetric_prev1 + fxp_vecrMetricSet_met0; 
		
		/* Take path with smallest metric */ 
		/*printf("diff in comp is %fn",(rSecStAccMetricPrev0 - rSecStAccMetricPrev1));*/ 
		if (fxp_rSecStAccMetricPrev0 < fxp_rSecStAccMetricPrev1) 
		{ 
			/* Save minimum metric for this state and store decision */ 
			fxp_pCurTrelMetric_next = fxp_rSecStAccMetricPrev0; 
			matdecDecisions_next = 0; 
		} 
		else 
		{ 
			/* Save minimum metric for this state and store decision */ 
			fxp_pCurTrelMetric_next = fxp_rSecStAccMetricPrev1; 
			matdecDecisions_next = 1; 
		} 		
	}
};

SC_MODULE (metric_comp)
{
	sc_in<INT32> iMetricType;	//thyeros: only need to be 3bits [9/4/2005]
	sc_in<INT32> fxp_vecNewDistance_0_0;
	sc_in<INT32> fxp_vecNewDistance_0_1;
	sc_in<INT32> fxp_vecNewDistance_0_2;
	sc_in<INT32> fxp_vecNewDistance_0_3;
	sc_in<INT32> fxp_vecNewDistance_1_0;
	sc_in<INT32> fxp_vecNewDistance_1_1;
	sc_in<INT32> fxp_vecNewDistance_1_2;
	sc_in<INT32> fxp_vecNewDistance_1_3;

	sc_out<INT32> fxp_vecrMetricSet_0;
	sc_out<INT32> fxp_vecrMetricSet_1;
	sc_out<INT32> fxp_vecrMetricSet_2;
	sc_out<INT32> fxp_vecrMetricSet_3;
	sc_out<INT32> fxp_vecrMetricSet_4;
	sc_out<INT32> fxp_vecrMetricSet_5;
	sc_out<INT32> fxp_vecrMetricSet_6;
	sc_out<INT32> fxp_vecrMetricSet_7;
	
	SC_CTOR (metric_comp)
	{	
		SC_METHOD (process);
		sensitive	<<fxp_vecrMetricSet_0
			<<fxp_vecrMetricSet_1
			<<fxp_vecrMetricSet_2
			<<fxp_vecrMetricSet_3
			<<fxp_vecrMetricSet_4
			<<fxp_vecrMetricSet_5
			<<fxp_vecrMetricSet_6
			<<fxp_vecrMetricSet_7
			<<iMetricType;
		dont_initialize();
	}
	
	void process()
	{
		/* Calculate all possible metrics ----------------------------------- */
		/* There are only a small set of possible puncturing patterns used for
		DRM: 0001, 0101, 0011, 0111, 1111. These need different numbers of
		input bits (increment of "iDistCnt" is dependent on pattern!). To
		optimize the calculation of the metrics, a "subset" of bits are first
		calculated which are used to get the final result. In this case,
		redundancy can be avoided.
		Note, that not all possible bit-combinations are used in the coder,
		only a subset of numbers: 0, 2, 4, 6, 9, 11, 13, 15 (compare numbers
		in the BUTTERFLY() calls) */
		
		int fxp_rIRxx00 =	fxp_vecNewDistance_0_1+ fxp_vecNewDistance_0_0;
		int fxp_rIRxx10 =	fxp_vecNewDistance_1_1+ fxp_vecNewDistance_0_0;
		int fxp_rIRxx01 =	fxp_vecNewDistance_0_1+ fxp_vecNewDistance_1_0;
		int fxp_rIRxx11 =	fxp_vecNewDistance_1_1+ fxp_vecNewDistance_1_0;

		int fxp_rIR00xx =	fxp_vecNewDistance_0_3+ fxp_vecNewDistance_0_2;
		int fxp_rIR10xx =	fxp_vecNewDistance_1_3+ fxp_vecNewDistance_0_2;
		int fxp_rIR01xx =	fxp_vecNewDistance_0_3+ fxp_vecNewDistance_1_2;
		int fxp_rIR11xx =	fxp_vecNewDistance_1_3+ fxp_vecNewDistance_1_2;

		switch(iMetricType) {
		case PP_TYPE_0001:	
			fxp_vecrMetricSet_0 = fxp_vecNewDistance_0_0;
			fxp_vecrMetricSet_1 = fxp_vecNewDistance_0_0;
			fxp_vecrMetricSet_2 = fxp_vecNewDistance_0_0;
			fxp_vecrMetricSet_3 = fxp_vecNewDistance_0_0;
			fxp_vecrMetricSet_4 = fxp_vecNewDistance_1_0;
			fxp_vecrMetricSet_5 = fxp_vecNewDistance_1_0;
			fxp_vecrMetricSet_6 = fxp_vecNewDistance_1_0;
			fxp_vecrMetricSet_7 = fxp_vecNewDistance_1_0;
			break;
		case PP_TYPE_0101:	
			fxp_vecrMetricSet_0 = fxp_rIRxx00;
			fxp_vecrMetricSet_1 = fxp_rIRxx00;
			fxp_vecrMetricSet_2 = fxp_rIRxx10;
			fxp_vecrMetricSet_3 = fxp_rIRxx10;
			fxp_vecrMetricSet_4 = fxp_rIRxx01;
			fxp_vecrMetricSet_5 = fxp_rIRxx01;
			fxp_vecrMetricSet_6 = fxp_rIRxx11;
			fxp_vecrMetricSet_7 = fxp_rIRxx11;
			break;
		case PP_TYPE_0011:
			fxp_vecrMetricSet_0 =  fxp_rIRxx00;
			fxp_vecrMetricSet_1 =  fxp_rIRxx10;
			fxp_vecrMetricSet_2 =  fxp_rIRxx00;
			fxp_vecrMetricSet_3 =  fxp_rIRxx10;
			fxp_vecrMetricSet_4 =  fxp_rIRxx01;
			fxp_vecrMetricSet_5 =  fxp_rIRxx11;
			fxp_vecrMetricSet_6 =  fxp_rIRxx01;
			fxp_vecrMetricSet_7 =  fxp_rIRxx11;
			break;
		case PP_TYPE_0111:
			fxp_vecrMetricSet_0 = fxp_vecNewDistance_0_2+ fxp_rIRxx00;
			fxp_vecrMetricSet_1 = fxp_vecNewDistance_0_2+ fxp_rIRxx10;
			fxp_vecrMetricSet_2 = fxp_vecNewDistance_1_2+ fxp_rIRxx00;
			fxp_vecrMetricSet_3 = fxp_vecNewDistance_1_2+ fxp_rIRxx10;
			fxp_vecrMetricSet_4 = fxp_vecNewDistance_0_2+ fxp_rIRxx01;
			fxp_vecrMetricSet_5 = fxp_vecNewDistance_0_2+ fxp_rIRxx11;
			fxp_vecrMetricSet_6 = fxp_vecNewDistance_1_2+ fxp_rIRxx01;
			fxp_vecrMetricSet_7 = fxp_vecNewDistance_1_2+ fxp_rIRxx11;
			break;
		case PP_TYPE_1111:
			fxp_vecrMetricSet_0 = fxp_rIR00xx + fxp_rIRxx00; /* 0 */
			fxp_vecrMetricSet_1 = fxp_rIR00xx + fxp_rIRxx10; /* 2 */
			fxp_vecrMetricSet_2 = fxp_rIR01xx + fxp_rIRxx00; /* 4 */
			fxp_vecrMetricSet_3 = fxp_rIR01xx + fxp_rIRxx10; /* 6 */
			fxp_vecrMetricSet_4 = fxp_rIR10xx + fxp_rIRxx01; /* 9 */
			fxp_vecrMetricSet_5 = fxp_rIR10xx + fxp_rIRxx11; /* 11 */
			fxp_vecrMetricSet_6 = fxp_rIR11xx + fxp_rIRxx01; /* 13 */
			fxp_vecrMetricSet_7 = fxp_rIR11xx + fxp_rIRxx11; /* 15 */
			break;			
		}	
	}
};

SC_MODULE (viterbi_dec2)
{	
	sc_in<INT32>	stream;
	sc_in<bool>		clk;

	INT32	veciTablePuncPat_Size[MAX_LEVEL];
	INT32	vecOutputBits_Size[MAX_LEVEL];	
	INT32	veciTablePuncPat[MAX_LEVEL][MAX_STREAM];
	BINARY	vecOutputBits[MAX_STREAM/2];
	INT32*	fxp_pCurTrelMetric; 
	INT32*	fxp_pOldTrelMetric;
	INT32	fxp_vecTrelMetric1[MAX_LEVEL][MC_NUM_STATES];
	INT32	fxp_vecTrelMetric2[MAX_LEVEL][MC_NUM_STATES];
	INT32	fxp_vecrMetricSet[MC_NUM_OUTPUT_COMBINATIONS/2];	
	BINARY	matdecDecisions[MAX_STREAM][MC_NUM_STATES];
	
	INT32	fxp_vecNewDistance_0[MAX_BUFFER];
	INT32	fxp_vecNewDistance_1[MAX_BUFFER];
	
	int iBufIndex;
	int	iBufCount;
	int	iPuncPatIndex;
	int	iLevel;

	FILE*	pGoldOutput;

	SC_CTOR (viterbi_dec2)
	{
		//thyeros: initialize  [9/2/2005]
		iBufIndex	=	0;
		iBufCount	=	0;
		iLevel		=	0;
		iPuncPatIndex	=	0;
		pGoldOutput	=	fopen("vrt_output.gold","rb");

		//thyeros: load constant [9/2/2005]
		veciTablePuncPat_Size[0]	=	1560;
		veciTablePuncPat_Size[1]	=	3114;
		veciTablePuncPat_Size[2]	=	3734;
			
		vecOutputBits_Size[0]	=	1554;
		vecOutputBits_Size[1]	=	3108;
		vecOutputBits_Size[2]	=	3728;
	
		//thyeros: load veciTablePuncPat [9/2/2005]
		FILE*	pTable	=	fopen("vrt_puncpat","rb");
		
		int iLevel;
		for(iLevel=0;iLevel<MAX_LEVEL;iLevel++)
		{
			for(int i=0;i<veciTablePuncPat_Size[iLevel];i++)
			{
				fread(&(veciTablePuncPat[iLevel][i]),sizeof(veciTablePuncPat[iLevel][i]),1,pTable);  			
			}
		}

		fclose(pTable);

		SC_METHOD (process2);
		sensitive<<clk;
		dont_initialize();
	}  
	
	int	iRemainCount;

	void process2()
	{
		if(iBufCount==0)
		{			
			fxp_pCurTrelMetric = fxp_vecTrelMetric1[iLevel];
			fxp_pOldTrelMetric = fxp_vecTrelMetric2[iLevel];
			
			/* Reset all metrics in the trellis. We initialize all states except of
			the zero state with a high metric, because we KNOW that the state "0"
			is the transmitted state */
			
			fxp_pOldTrelMetric[0] = (int) 0;
			for (int i=1;i<MC_NUM_STATES;i++)
			{
				fxp_pOldTrelMetric[i] = (int) MC_METRIC_INIT_VALUE;
			}

			iPuncPatIndex	=	0;
			iRemainCount	=	0;
			iBufIndex		=	0;
		}

		fflush(pLog);

		if(iBufCount%2==0)
		{
			fxp_vecNewDistance_0[iBufIndex/2]	=	stream.read();
		}
		else
		{
			fxp_vecNewDistance_1[iBufIndex/2]	=	stream.read();

			//thyeros: buffer scheme [9/3/2005]
			// 0		2		4		6
			// 1		3		5		7
			//0001  0101,0011 0111    1111
			
			switch(veciTablePuncPat[iLevel][iPuncPatIndex]) {
			case PP_TYPE_0001:	
				if(iBufIndex==1)
				{
					PartialViterbiDecoder2(iPuncPatIndex,veciTablePuncPat[iLevel][iPuncPatIndex]);
					iPuncPatIndex++;
					iBufIndex	=	-1;
				}
				break;
			case PP_TYPE_0101:	
			case PP_TYPE_0011:
				if(iBufIndex==3)
				{
					PartialViterbiDecoder2(iPuncPatIndex,veciTablePuncPat[iLevel][iPuncPatIndex]);
					iPuncPatIndex++;
					iBufIndex	=	-1;
				}
				break;
			case PP_TYPE_0111:
				if(iBufIndex==5)
				{
					PartialViterbiDecoder2(iPuncPatIndex,veciTablePuncPat[iLevel][iPuncPatIndex]);
					iPuncPatIndex++;
					iBufIndex	=	-1;
				}
				break;
			case PP_TYPE_1111:		
				if(iBufIndex==7)
				{
					PartialViterbiDecoder2(iPuncPatIndex,veciTablePuncPat[iLevel][iPuncPatIndex]);
					iPuncPatIndex++;
					iBufIndex	=	-1;
				}
				break;
			}
		}

		iBufIndex++;
		iBufCount++;

		if(iBufCount==MAX_STREAM*2)
		{
			double	dReturn	=	Output(fxp_pOldTrelMetric[0]);
			
			for(int i=0;i<vecOutputBits_Size[iLevel];i++)
			{
				BINARY	temp_char;
				fread(&(temp_char),sizeof(vecOutputBits[i]),1,pGoldOutput);
				if(vecOutputBits[i]-temp_char) 
					printf("<%d>values are %x %x %x \n",i, vecOutputBits[i]-temp_char,vecOutputBits[i],temp_char);
			}

			double	temp_double;
			fread(&temp_double,sizeof(dReturn),1,pGoldOutput);

			static int a = 0;
			printf("%d]return diff is %g - %g = %g \n",a++,dReturn/SCALE,temp_double,dReturn/SCALE - temp_double);
			
			iBufCount	=	0;
			iLevel		=	(++iLevel)%3;
		}
	}

	void GetMetricSet(
		INT32& fxp_vecrMetricSet_0,
		INT32& fxp_vecrMetricSet_1,
		INT32& fxp_vecrMetricSet_2,
		INT32& fxp_vecrMetricSet_3,
		INT32& fxp_vecrMetricSet_4,
		INT32& fxp_vecrMetricSet_5,
		INT32& fxp_vecrMetricSet_6,
		INT32& fxp_vecrMetricSet_7,
		INT32 iMetricType,
		INT32 fxp_vecNewDistance_0_0,INT32 fxp_vecNewDistance_0_1,INT32 fxp_vecNewDistance_0_2,INT32 fxp_vecNewDistance_0_3,
		INT32 fxp_vecNewDistance_1_0,INT32 fxp_vecNewDistance_1_1,INT32 fxp_vecNewDistance_1_2,INT32 fxp_vecNewDistance_1_3
		)
	{
		/* Calculate all possible metrics ----------------------------------- */
		/* There are only a small set of possible puncturing patterns used for
		DRM: 0001, 0101, 0011, 0111, 1111. These need different numbers of
		input bits (increment of "iDistCnt" is dependent on pattern!). To
		optimize the calculation of the metrics, a "subset" of bits are first
		calculated which are used to get the final result. In this case,
		redundancy can be avoided.
		Note, that not all possible bit-combinations are used in the coder,
		only a subset of numbers: 0, 2, 4, 6, 9, 11, 13, 15 (compare numbers
		in the BUTTERFLY() calls) */
		
		int fxp_rIRxx00 =	fxp_vecNewDistance_0_1+ fxp_vecNewDistance_0_0;
		int fxp_rIRxx10 =	fxp_vecNewDistance_1_1+ fxp_vecNewDistance_0_0;
		int fxp_rIRxx01 =	fxp_vecNewDistance_0_1+ fxp_vecNewDistance_1_0;
		int fxp_rIRxx11 =	fxp_vecNewDistance_1_1+ fxp_vecNewDistance_1_0;

		int fxp_rIR00xx =	fxp_vecNewDistance_0_3+ fxp_vecNewDistance_0_2;
		int fxp_rIR10xx =	fxp_vecNewDistance_1_3+ fxp_vecNewDistance_0_2;
		int fxp_rIR01xx =	fxp_vecNewDistance_0_3+ fxp_vecNewDistance_1_2;
		int fxp_rIR11xx =	fxp_vecNewDistance_1_3+ fxp_vecNewDistance_1_2;

		switch(iMetricType) {
		case PP_TYPE_0001:	
			fxp_vecrMetricSet_0 = fxp_vecNewDistance_0_0;
			fxp_vecrMetricSet_1 = fxp_vecNewDistance_0_0;
			fxp_vecrMetricSet_2 = fxp_vecNewDistance_0_0;
			fxp_vecrMetricSet_3 = fxp_vecNewDistance_0_0;
			fxp_vecrMetricSet_4 = fxp_vecNewDistance_1_0;
			fxp_vecrMetricSet_5 = fxp_vecNewDistance_1_0;
			fxp_vecrMetricSet_6 = fxp_vecNewDistance_1_0;
			fxp_vecrMetricSet_7 = fxp_vecNewDistance_1_0;
			break;
		case PP_TYPE_0101:	
			fxp_vecrMetricSet_0 = fxp_rIRxx00;
			fxp_vecrMetricSet_1 = fxp_rIRxx00;
			fxp_vecrMetricSet_2 = fxp_rIRxx10;
			fxp_vecrMetricSet_3 = fxp_rIRxx10;
			fxp_vecrMetricSet_4 = fxp_rIRxx01;
			fxp_vecrMetricSet_5 = fxp_rIRxx01;
			fxp_vecrMetricSet_6 = fxp_rIRxx11;
			fxp_vecrMetricSet_7 = fxp_rIRxx11;
			break;
		case PP_TYPE_0011:
			fxp_vecrMetricSet_0 =  fxp_rIRxx00;
			fxp_vecrMetricSet_1 =  fxp_rIRxx10;
			fxp_vecrMetricSet_2 =  fxp_rIRxx00;
			fxp_vecrMetricSet_3 =  fxp_rIRxx10;
			fxp_vecrMetricSet_4 =  fxp_rIRxx01;
			fxp_vecrMetricSet_5 =  fxp_rIRxx11;
			fxp_vecrMetricSet_6 =  fxp_rIRxx01;
			fxp_vecrMetricSet_7 =  fxp_rIRxx11;
			break;
		case PP_TYPE_0111:
			fxp_vecrMetricSet_0 = fxp_vecNewDistance_0_2+ fxp_rIRxx00;
			fxp_vecrMetricSet_1 = fxp_vecNewDistance_0_2+ fxp_rIRxx10;
			fxp_vecrMetricSet_2 = fxp_vecNewDistance_1_2+ fxp_rIRxx00;
			fxp_vecrMetricSet_3 = fxp_vecNewDistance_1_2+ fxp_rIRxx10;
			fxp_vecrMetricSet_4 = fxp_vecNewDistance_0_2+ fxp_rIRxx01;
			fxp_vecrMetricSet_5 = fxp_vecNewDistance_0_2+ fxp_rIRxx11;
			fxp_vecrMetricSet_6 = fxp_vecNewDistance_1_2+ fxp_rIRxx01;
			fxp_vecrMetricSet_7 = fxp_vecNewDistance_1_2+ fxp_rIRxx11;
			break;
		case PP_TYPE_1111:
			fxp_vecrMetricSet_0 = fxp_rIR00xx + fxp_rIRxx00; /* 0 */
			fxp_vecrMetricSet_1 = fxp_rIR00xx + fxp_rIRxx10; /* 2 */
			fxp_vecrMetricSet_2 = fxp_rIR01xx + fxp_rIRxx00; /* 4 */
			fxp_vecrMetricSet_3 = fxp_rIR01xx + fxp_rIRxx10; /* 6 */
			fxp_vecrMetricSet_4 = fxp_rIR10xx + fxp_rIRxx01; /* 9 */
			fxp_vecrMetricSet_5 = fxp_rIR10xx + fxp_rIRxx11; /* 11 */
			fxp_vecrMetricSet_6 = fxp_rIR11xx + fxp_rIRxx01; /* 13 */
			fxp_vecrMetricSet_7 = fxp_rIR11xx + fxp_rIRxx11; /* 15 */
			break;			
		default:	fprintf(pLog,"UNKNOWN CODE!!!!\n");
			break;
		}
		
	}

	void PartialViterbiDecoder2(int iPuncPatIndex,int iMetricType)
	{
		GetMetricSet(
			fxp_vecrMetricSet[ 0],
			fxp_vecrMetricSet[ 1],
			fxp_vecrMetricSet[ 2],
			fxp_vecrMetricSet[ 3],
			fxp_vecrMetricSet[ 4],
			fxp_vecrMetricSet[ 5],
			fxp_vecrMetricSet[ 6],
			fxp_vecrMetricSet[ 7],
			iMetricType,
			fxp_vecNewDistance_0[0],fxp_vecNewDistance_0[1],fxp_vecNewDistance_0[2],fxp_vecNewDistance_0[3],
			fxp_vecNewDistance_1[0],fxp_vecNewDistance_1[1],fxp_vecNewDistance_1[2],fxp_vecNewDistance_1[3]);

		ButterFly(
			fxp_pCurTrelMetric[0],
			matdecDecisions[iPuncPatIndex][0],
			fxp_pCurTrelMetric[1],
			matdecDecisions[iPuncPatIndex][1],fxp_pOldTrelMetric[0],fxp_pOldTrelMetric[32],fxp_vecrMetricSet[0],fxp_vecrMetricSet[7]);
		ButterFly(
			fxp_pCurTrelMetric[2],
			matdecDecisions[iPuncPatIndex][2],
			fxp_pCurTrelMetric[3],
			matdecDecisions[iPuncPatIndex][3],fxp_pOldTrelMetric[1],fxp_pOldTrelMetric[33],fxp_vecrMetricSet[3],fxp_vecrMetricSet[4]);
		ButterFly(
			fxp_pCurTrelMetric[4],
			matdecDecisions[iPuncPatIndex][4],
			fxp_pCurTrelMetric[5],
			matdecDecisions[iPuncPatIndex][5],fxp_pOldTrelMetric[2],fxp_pOldTrelMetric[34],fxp_vecrMetricSet[5],fxp_vecrMetricSet[2]);
		ButterFly(
			fxp_pCurTrelMetric[6],
			matdecDecisions[iPuncPatIndex][6],
			fxp_pCurTrelMetric[7],
			matdecDecisions[iPuncPatIndex][7],fxp_pOldTrelMetric[3],fxp_pOldTrelMetric[35],fxp_vecrMetricSet[6],fxp_vecrMetricSet[1]);
		ButterFly(
			fxp_pCurTrelMetric[8],
			matdecDecisions[iPuncPatIndex][8],
			fxp_pCurTrelMetric[9],
			matdecDecisions[iPuncPatIndex][9],fxp_pOldTrelMetric[4],fxp_pOldTrelMetric[36],fxp_vecrMetricSet[5],fxp_vecrMetricSet[2]);
		ButterFly(
			fxp_pCurTrelMetric[10],
			matdecDecisions[iPuncPatIndex][10],
			fxp_pCurTrelMetric[11],
			matdecDecisions[iPuncPatIndex][11],fxp_pOldTrelMetric[5],fxp_pOldTrelMetric[37],fxp_vecrMetricSet[6],fxp_vecrMetricSet[1]);
		ButterFly(
			fxp_pCurTrelMetric[12],
			matdecDecisions[iPuncPatIndex][12],
			fxp_pCurTrelMetric[13],
			matdecDecisions[iPuncPatIndex][13],fxp_pOldTrelMetric[6],fxp_pOldTrelMetric[38],fxp_vecrMetricSet[0],fxp_vecrMetricSet[7]);
		ButterFly(
			fxp_pCurTrelMetric[14],
			matdecDecisions[iPuncPatIndex][14],
			fxp_pCurTrelMetric[15],
			matdecDecisions[iPuncPatIndex][15],fxp_pOldTrelMetric[7],fxp_pOldTrelMetric[39],fxp_vecrMetricSet[3],fxp_vecrMetricSet[4]);
		ButterFly(
			fxp_pCurTrelMetric[16],
			matdecDecisions[iPuncPatIndex][16],
			fxp_pCurTrelMetric[17],
			matdecDecisions[iPuncPatIndex][17],fxp_pOldTrelMetric[8],fxp_pOldTrelMetric[40],fxp_vecrMetricSet[2],fxp_vecrMetricSet[5]);		
		ButterFly(
			fxp_pCurTrelMetric[18],
			matdecDecisions[iPuncPatIndex][18],
			fxp_pCurTrelMetric[19],
			matdecDecisions[iPuncPatIndex][19],fxp_pOldTrelMetric[9],fxp_pOldTrelMetric[41],fxp_vecrMetricSet[1],fxp_vecrMetricSet[6]);
		ButterFly(
			fxp_pCurTrelMetric[20],
			matdecDecisions[iPuncPatIndex][20],
			fxp_pCurTrelMetric[21],
			matdecDecisions[iPuncPatIndex][21],fxp_pOldTrelMetric[10],fxp_pOldTrelMetric[42],fxp_vecrMetricSet[7],fxp_vecrMetricSet[0]);
		ButterFly(
			fxp_pCurTrelMetric[22],
			matdecDecisions[iPuncPatIndex][22],
			fxp_pCurTrelMetric[23],
			matdecDecisions[iPuncPatIndex][23],fxp_pOldTrelMetric[11],fxp_pOldTrelMetric[43],fxp_vecrMetricSet[4],fxp_vecrMetricSet[3]);
		ButterFly(
			fxp_pCurTrelMetric[24],
			matdecDecisions[iPuncPatIndex][24],
			fxp_pCurTrelMetric[25],
			matdecDecisions[iPuncPatIndex][25],fxp_pOldTrelMetric[12],fxp_pOldTrelMetric[44],fxp_vecrMetricSet[7],fxp_vecrMetricSet[0]);
		ButterFly(
			fxp_pCurTrelMetric[26],
			matdecDecisions[iPuncPatIndex][26],
			fxp_pCurTrelMetric[27],
			matdecDecisions[iPuncPatIndex][27],fxp_pOldTrelMetric[13],fxp_pOldTrelMetric[45],fxp_vecrMetricSet[4],fxp_vecrMetricSet[3]);
		ButterFly(
			fxp_pCurTrelMetric[28],
			matdecDecisions[iPuncPatIndex][28],
			fxp_pCurTrelMetric[29],
			matdecDecisions[iPuncPatIndex][29],fxp_pOldTrelMetric[14],fxp_pOldTrelMetric[46],fxp_vecrMetricSet[2],fxp_vecrMetricSet[5]);
		ButterFly(
			fxp_pCurTrelMetric[30],
			matdecDecisions[iPuncPatIndex][30],
			fxp_pCurTrelMetric[31],
			matdecDecisions[iPuncPatIndex][31],fxp_pOldTrelMetric[15],fxp_pOldTrelMetric[47],fxp_vecrMetricSet[1],fxp_vecrMetricSet[6]);
		ButterFly(
			fxp_pCurTrelMetric[32],
			matdecDecisions[iPuncPatIndex][32],
			fxp_pCurTrelMetric[33],
			matdecDecisions[iPuncPatIndex][33],fxp_pOldTrelMetric[16],fxp_pOldTrelMetric[48],fxp_vecrMetricSet[4],fxp_vecrMetricSet[3]);
		ButterFly(
			fxp_pCurTrelMetric[34],
			matdecDecisions[iPuncPatIndex][34],
			fxp_pCurTrelMetric[35],
			matdecDecisions[iPuncPatIndex][35],fxp_pOldTrelMetric[17],fxp_pOldTrelMetric[49],fxp_vecrMetricSet[7],fxp_vecrMetricSet[0]);
		ButterFly(
			fxp_pCurTrelMetric[36],
			matdecDecisions[iPuncPatIndex][36],
			fxp_pCurTrelMetric[37],
			matdecDecisions[iPuncPatIndex][37],fxp_pOldTrelMetric[18],fxp_pOldTrelMetric[50],fxp_vecrMetricSet[1],fxp_vecrMetricSet[6]);
		ButterFly(
			fxp_pCurTrelMetric[38],
			matdecDecisions[iPuncPatIndex][38],
			fxp_pCurTrelMetric[39],
			matdecDecisions[iPuncPatIndex][39],fxp_pOldTrelMetric[19],fxp_pOldTrelMetric[51],fxp_vecrMetricSet[2],fxp_vecrMetricSet[5]);
		ButterFly(
			fxp_pCurTrelMetric[40],
			matdecDecisions[iPuncPatIndex][40],
			fxp_pCurTrelMetric[41],
			matdecDecisions[iPuncPatIndex][41],fxp_pOldTrelMetric[20],fxp_pOldTrelMetric[52],fxp_vecrMetricSet[1],fxp_vecrMetricSet[6]);
		ButterFly(
			fxp_pCurTrelMetric[42],
			matdecDecisions[iPuncPatIndex][42],
			fxp_pCurTrelMetric[43],
			matdecDecisions[iPuncPatIndex][43],fxp_pOldTrelMetric[21],fxp_pOldTrelMetric[53],fxp_vecrMetricSet[2],fxp_vecrMetricSet[5]);
		ButterFly(
			fxp_pCurTrelMetric[44],
			matdecDecisions[iPuncPatIndex][44],
			fxp_pCurTrelMetric[45],
			matdecDecisions[iPuncPatIndex][45],fxp_pOldTrelMetric[22],fxp_pOldTrelMetric[54],fxp_vecrMetricSet[4],fxp_vecrMetricSet[3]);
		ButterFly(
			fxp_pCurTrelMetric[46],
			matdecDecisions[iPuncPatIndex][46],
			fxp_pCurTrelMetric[47],
			matdecDecisions[iPuncPatIndex][47],fxp_pOldTrelMetric[23],fxp_pOldTrelMetric[55],fxp_vecrMetricSet[7],fxp_vecrMetricSet[0]);
		ButterFly(
			fxp_pCurTrelMetric[48],
			matdecDecisions[iPuncPatIndex][48],
			fxp_pCurTrelMetric[49],
			matdecDecisions[iPuncPatIndex][49],fxp_pOldTrelMetric[24],fxp_pOldTrelMetric[56],fxp_vecrMetricSet[6],fxp_vecrMetricSet[1]);
		ButterFly(
			fxp_pCurTrelMetric[50],
			matdecDecisions[iPuncPatIndex][50],
			fxp_pCurTrelMetric[51],
			matdecDecisions[iPuncPatIndex][51],fxp_pOldTrelMetric[25],fxp_pOldTrelMetric[57],fxp_vecrMetricSet[5],fxp_vecrMetricSet[2]);
		ButterFly(
			fxp_pCurTrelMetric[52],
			matdecDecisions[iPuncPatIndex][52],
			fxp_pCurTrelMetric[53],
			matdecDecisions[iPuncPatIndex][53],fxp_pOldTrelMetric[26],fxp_pOldTrelMetric[58],fxp_vecrMetricSet[3],fxp_vecrMetricSet[4]);
		ButterFly(
			fxp_pCurTrelMetric[54],
			matdecDecisions[iPuncPatIndex][54],
			fxp_pCurTrelMetric[55],
			matdecDecisions[iPuncPatIndex][55],fxp_pOldTrelMetric[27],fxp_pOldTrelMetric[59],fxp_vecrMetricSet[0],fxp_vecrMetricSet[7]);
		ButterFly(
			fxp_pCurTrelMetric[56],
			matdecDecisions[iPuncPatIndex][56],
			fxp_pCurTrelMetric[57],
			matdecDecisions[iPuncPatIndex][57],fxp_pOldTrelMetric[28],fxp_pOldTrelMetric[60],fxp_vecrMetricSet[3],fxp_vecrMetricSet[4]);
		ButterFly(
			fxp_pCurTrelMetric[58],
			matdecDecisions[iPuncPatIndex][58],
			fxp_pCurTrelMetric[59],
			matdecDecisions[iPuncPatIndex][59],fxp_pOldTrelMetric[29],fxp_pOldTrelMetric[61],fxp_vecrMetricSet[0],fxp_vecrMetricSet[7]);
		ButterFly(
			fxp_pCurTrelMetric[60],
			matdecDecisions[iPuncPatIndex][60],
			fxp_pCurTrelMetric[61],
			matdecDecisions[iPuncPatIndex][61],fxp_pOldTrelMetric[30],fxp_pOldTrelMetric[62],fxp_vecrMetricSet[6],fxp_vecrMetricSet[1]);
		ButterFly(
			fxp_pCurTrelMetric[62],
			matdecDecisions[iPuncPatIndex][62],
			fxp_pCurTrelMetric[63],
			matdecDecisions[iPuncPatIndex][63],fxp_pOldTrelMetric[31],fxp_pOldTrelMetric[63],fxp_vecrMetricSet[5],fxp_vecrMetricSet[2]);
		
		/* Swap trellis data pointers (old -> new, new -> old) */			
		int* fxp_pTMPTrelMetric = fxp_pCurTrelMetric;
		fxp_pCurTrelMetric = fxp_pOldTrelMetric;
		fxp_pOldTrelMetric = fxp_pTMPTrelMetric;
		
	}

	void ButterFly(
		INT32& fxp_pCurTrelMetric_cur,
		BINARY& matdecDecisions_cur,
		int& fxp_pCurTrelMetric_next,
		BINARY& matdecDecisions_next,
		INT32 fxp_pOldTrelMetric_prev0,
		INT32 fxp_pOldTrelMetric_prev1,
		INT32 fxp_vecrMetricSet_met0,
		INT32 fxp_vecrMetricSet_met1
		)
	{ 
		/* First state in this set ------------------------------------ */ 
		/* Calculate metrics from the two previous states, use the old
		metric from the previous states plus the "transition-metric" */ 
		
		const INT32 fxp_rFiStAccMetricPrev0 =	fxp_pOldTrelMetric_prev0 + fxp_vecrMetricSet_met0; 
		const INT32 fxp_rFiStAccMetricPrev1 =	fxp_pOldTrelMetric_prev1 + fxp_vecrMetricSet_met1; 
		
		/* Take path with smallest metric */ 
		/*printf("diff in comp is %fn",(rFiStAccMetricPrev0 - rFiStAccMetricPrev1));*/ 
		if (fxp_rFiStAccMetricPrev0 < fxp_rFiStAccMetricPrev1) 
		{ 
			/* Save minimum metric for this state and store decision */ 
			fxp_pCurTrelMetric_cur = fxp_rFiStAccMetricPrev0; 
			matdecDecisions_cur = 0; 
		} 
		else 
		{ 
			/* Save minimum metric for this state and store decision */ 
			fxp_pCurTrelMetric_cur = fxp_rFiStAccMetricPrev1; 
			matdecDecisions_cur = 1; 
		} 
		
		/* Second state in this set ----------------------------------- */ 
		/* The only difference is that we swapped the matric sets */ 
		
		const INT32 fxp_rSecStAccMetricPrev0 = fxp_pOldTrelMetric_prev0 + fxp_vecrMetricSet_met1; 
		const INT32 fxp_rSecStAccMetricPrev1 = fxp_pOldTrelMetric_prev1 + fxp_vecrMetricSet_met0; 
		
		/* Take path with smallest metric */ 
		/*printf("diff in comp is %fn",(rSecStAccMetricPrev0 - rSecStAccMetricPrev1));*/ 
		if (fxp_rSecStAccMetricPrev0 < fxp_rSecStAccMetricPrev1) 
		{ 
			/* Save minimum metric for this state and store decision */ 
			fxp_pCurTrelMetric_next = fxp_rSecStAccMetricPrev0; 
			matdecDecisions_next = 0; 
		} 
		else 
		{ 
			/* Save minimum metric for this state and store decision */ 
			fxp_pCurTrelMetric_next = fxp_rSecStAccMetricPrev1; 
			matdecDecisions_next = 1; 
		} 
	}

	double Output(int fxp_pOldTrelMetric_0)
	{
		/* Chainback the decoded bits from trellis (only for MLSE) -------------- */
		/* The end-state is defined by the DRM standard as all-zeros (shift register
		in the encoder is padded with zeros at the end */
		int iCurDecState = 0;
		
		for (int i = 0; i < vecOutputBits_Size[iLevel]; i++)
		{
		/* Read out decisions "backwards". Mask only first bit, because in MMX
			implementation, all 8 bits of a "char" are set to the decision */
			const _DECISIONTYPE decCurBit =
				matdecDecisions[veciTablePuncPat_Size[iLevel] - i - 1][iCurDecState] & 1;
			
				/* Calculate next state from previous decoded bit -> shift old data
			and add new bit */
			iCurDecState = (iCurDecState >> 1) | (decCurBit << 5);
			
			/* Set decisions "backwards" in actual result vector */
			vecOutputBits[vecOutputBits_Size[iLevel] - i - 1] = (_BINARY) decCurBit;
		}
		
		/* Return normalized accumulated minimum metric */
		//return_num = fxp_pOldTrelMetric[0];
		//return_den = iDistCnt;
		
		return	(double)fxp_pOldTrelMetric_0/MAX_STREAM;
	}
};   //Don't forget the semicolon!


SC_MODULE (test_bench)
{
	int		iIter;
	FILE*	pStream;

	sc_out<INT32>	stream;
	sc_out<bool>	clk_port;
	bool			clk;

	SC_CTOR (test_bench) 
	{
		clk		=	false;
		iIter	=	0;
		pStream	=	fopen("vrt_stream","rb");

		SC_THREAD  (process);	
	}

	//Define the functionality of the "process" thread.
	void process()
	{
		for(iIter=0;iIter<96;iIter++)
		{
			for(int i=0;i<MAX_STREAM*2;i++)
			{
				double	dStream	=	-1;

				fread(&(dStream),sizeof(dStream),1,pStream);  			
				stream.write((int)(dStream*SCALE));
				clk	=	!clk;
				clk_port.write(clk);
				wait (1, SC_NS);
			}
		}

//		sc_stop();							//End Simulation
	}
};

int sc_main(int argc, char* argv[])
{

	pLog	=	fopen("log.txt","wt");
	sc_signal<bool>		clk;
	sc_signal<INT32>	stream;

	test_bench test_module("test_bench1");
	test_module<<stream<<clk;

	viterbi_dec2	viterbi_module("viterbi_dec1");
	viterbi_module<<stream<<clk;

	
	sc_start();

	return(0);
}