/*
 *---------------------------------------------------------------------------
 *
 *                  I N T E L   P R O P R I E T A R Y
 *
 *     COPYRIGHT (c)  2002 BY  INTEL  CORPORATION.  ALL RIGHTS
 *     RESERVED.   NO  PART  OF THIS PROGRAM  OR  PUBLICATION  MAY
 *     BE  REPRODUCED,   TRANSMITTED,   TRANSCRIBED,   STORED  IN  A
 *     RETRIEVAL SYSTEM, OR TRANSLATED INTO ANY LANGUAGE OR COMPUTER
 *     LANGUAGE IN ANY FORM OR BY ANY MEANS, ELECTRONIC, MECHANICAL,
 *     MAGNETIC,  OPTICAL,  CHEMICAL, MANUAL, OR OTHERWISE,  WITHOUT
 *     THE PRIOR WRITTEN PERMISSION OF :
 *
 *                        INTEL  CORPORATION
 *
 *                     2200 MISSION COLLEGE BLVD
 *
 *               SANTA  CLARA,  CALIFORNIA  95052-8119
 *
 *---------------------------------------------------------------------------
 */

#ifndef __DL_SOURCE_C__
#define __DL_SOURCE_C__


#include <dl_system.h>
#include <dl_buf.c>
#include <dl_meta.c>

#define INLINE static __forceinline

/////////////////////////////
//	Some local defines
/////////////////////////////

//	XXX - Move this to dl_system.h 

#if (DL_RING_NUMBER > 11)

#error		"For Ring Number Greater than 11, we cannot use the SCR_Ring#_Full input state. Check DL_RING_NUMBER."

#else

#define		DL_RING_FULL			inp_state_scr_ring4_full	

#endif

#ifndef	DL_NEXT_ME
#define	DL_NEXT_ME	0
#endif

//	How many QUAD words of ipv4 packet header to read/write from DRAM

#define	PKTHDR_CACHE_SIZE			3

/////////////////////////////
// Global variables/Registers
/////////////////////////////

extern int	dlRing;							// Scratch ring number for pos rx ==>ipv4. This can be made a shared variable.
extern dl_buf_handle_t 	dlBufHandle;			// The current buffer handle.
extern dl_buf_handle_t 	dlEopBufHandle;		// For large packets, this is the last buffer in the chain.
extern dl_meta_t		dlMeta;
__declspec(gp_reg) int 	dlNextBlock;			// Next block that should process the buffer/packet.

SIGNAL	dl_sig_prev;							// interthread signal used for dispatch loop
__declspec(gp_reg) unsigned int pkt_hdr[8];		// cache of packet header in registers.
int		dl_exception_code;						// exception code generated by microblock

///////////////////////////////////////////////////////////////////////////////
// dl_init:
//	 	Description: 
//
//	 	Outputs:
//
//		Inputs:
//
INLINE	void dl_init(void)
{
	// intialize the dispatch loop interthread/inter ME signal	
	__implicit_read(&dl_sig_prev);
	__assign_relative_register(&dl_sig_prev,DL_SIG_WAKE);
	
	// Ring number to be used in scratch[put].
	dlRing = POS_RX_TO_IPV4_SCR_RING << 2;

	return;
}


///////////////////////////////////////////////////////////////////////////////
// DlDropPacket:
//	 	Description: 
//
//	 	Outputs:
//
//		Inputs:
//
INLINE DlDropPacket(dl_buf_handle_t bufHandle, dl_buf_handle_t eopBufHandle)
{
	// If dl_eop_buf_handle_arg == IX_NULL
	if (eopBufHandle.lw_offset == IX_NULL)
		// Drop single buffer.
		Dl_BufDrop(bufHandle);
	else
		// Drop buffer chain.
		Dl_BufDropChain(bufHandle, eopBufHandle);
}


///////////////////////////////////////////////////////////////////////////////
// dl_sink:
//	 	Description: 
//			Called by POS RX to handover the packet to the next block (IPv4) in the
//			ingress pipeline. We do not do thread ordering in this function. (It is done
//			by POS RX itself because of tight budget)
//			
//			This function will produce the following on to the scratch ring.
//			dlBufHandle		// The current buffer containing SOP 
//			dlEopBufHandle	// for large packets, the buffer containing EOP. Otherwise NULL
//			meta1				// Long Word 1 from the Meta data (starting from 0)
//			meta2				// Long Word 2 from the Meta Data
//
//			This info will be used by the IPV4 block (by calling dl_source)
//
//			Note:
//			sigMask is a bit mask of signals to wait on. This is passed in a regiter.
//			If there is no need to wait for any signal, pass SIG_NONE. 
//
//	 	Outputs:
//
//		Inputs:
//			req_sig			:	signal to be used in the I/O (scratch put)
//			sigMask		:	What to do with I/O operation - 
//								- wait for signal(s) as specified by sigMask (register)
//								- do not wait for signal, just return (SIG_NONE - constant)
//			The following Global variables will also act as inputs.
//			dlNextBlock			- IX_EXCEPTION, for exception packets
//								- BID_NEXT_BLOCK, block ID of the next block to handle this packet.
//			dlBufHandle		: Buffer handle of the buffer in the start of chain (contianinf SOP)
//			dl_buf_eop_handle	: Buffer handle of the last buffer in the chain
//
INLINE DlSink(SIGNAL_MASK sigMask)
{
	SIGNAL sigScrWr;

	// Put data on scratch only on EOP.
	if (dlNextBlock == PACKET_RX_NEXT1)
	{
		// Check that buffer is not null.
		if (dlBufHandle.lw_offset != IX_NULL)
		{
			int ring_state = inp_state_test(DL_RING_FULL);

			// If scratch ring is full, drop the packet.
			if (ring_state)
				DlDropPacket(dlBufHandle, dlEopBufHandle);

			else
			// Write to scratch ring.
			{

				__declspec(sram_write_reg) int sink_sr_wr[5];

				sink_sr_wr[0] = dlBufHandle.value;
				sink_sr_wr[1] = dlEopBufHandle.value;
				sink_sr_wr[2] = dlMeta.value[1];
				sink_sr_wr[3] = dlMeta.value[2];		// second  long word
				sink_sr_wr[4] = (dlMeta.inputPort << 16) | 0xFF;

				scratch_put_ring(sink_sr_wr, (volatile void __declspec(scratch) *)dlRing, 5, sig_done, &sigScrWr);

				sigMask |= 1 << __signal_number(&sigScrWr);
			}
		}
	}
	// Not EOP.
	else if (dlNextBlock == IX_EXCEPTION)
	{
		// Do exception processing here.
		// For now just drop the packet.
		DlDropPacket(dlBufHandle, dlEopBufHandle);
	}

	//	Wait for the scratch Write (if it had happend) and the RX Event signals.
	wait_for_all(sigMask);

	__implicit_read(&sigScrWr);
}

///////////////////////////////////////////////////////////////////////////////////////
//	compute_sop_buf_cframes:
//		Description:
//		It calculates the number of csix-frames in the sop buffer.
//
//		This recomputation of c-frames is required in sop buffer as some 
//		microblocks after RX block might have updated the buffer size according
//		to the encapsulation the packet has. 
//
//		Note: This algorithm gives count of c-frames one less than actual as required
//		by the hardware.
//
//		c-frames count = (buff_size + 8)/120 where divide is not regular divide
//		but our special divide which yields 1 less for numbers that are integral multiples
//		of 120.
//
//		Eg
//		buf size 			c-frame cnt returned
//		 230 = 112 + 118			1
//		 231 = 112 + 119			1
//		 232 = 112 + 120			1
//		 233 = 112 + 120 + 1	    2
//		 234 = 112 + 120 + 2 		2
// 
//		Input:
//			sop_buf_handle: The buffer handle of the first buffer in the buffer chain.
//
//		Output:
//			N/A
//
//		Size: 12 instr - worst case
///////////////////////////////////////////////////////////////////////////////////////
INLINE dl_buf_handle_t compute_sop_buf_cframes(dl_buf_handle_t handle)
{

	int sop_buf_size,temp1,temp2;

	// check if there is more than one cell. If there is only one cell
	// then we are done.
	if (handle.seg_count == 0)
		return handle;

	sop_buf_size = dlMeta.bufferSize + 8 + (dlMeta.offset & 0x7);

	/* 	now execute divide by 120
	 * 	The algorithm we use to divide by 120 (remember this is integer division).
	 *	(x/120) = ((x<<4+x)<<4+x)>>15
	 */

	temp1 = (sop_buf_size << 4) + sop_buf_size;
	temp1 = (temp1<<4) + sop_buf_size;

	handle.seg_count = temp1 >> 15;

	return handle;

}


///////////////////////////////////////////////////////////////////////////////
// dl_source:
//	 	Description: 
//
//	 	Outputs:
//
//		Inputs:
//

INLINE void dl_source(unsigned int thread_order, SIGNAL_MASK sigMask)
{

	__declspec(sram_read_reg) unsigned int rx_msg[5];

	SIGNAL	scratch_get;

	// Wait for signal from previous thread to ensure thread ordering
	if (thread_order == DL_THREAD_ORDER)
		wait_for_all(&dl_sig_prev);

	// now get the data from scratch ring
	scratch_get_ring(rx_msg,(volatile __declspec(scratch) void *)dlRing,5,sig_done,&scratch_get);

	/* now signal next thread */
	if (ctx() == 7)
	{	
		// signal NEXT ME
		cap_fast_write(((DL_NEXT_ME << 7) | DL_SIG_WAKE),csr_interthread_sig);
	}
	else
	{
		// signal next context if thread order to be preserved
		if (thread_order == DL_THREAD_ORDER)
			local_csr_write(local_csr_same_me_signal,(0x80 | (DL_SIG_WAKE << 3)));
	}

	/* create signal mask and wait for all signals */
	sigMask |= __signals(&scratch_get);	
	wait_for_all(sigMask);		

	/*
	 * process the message
	 */
	if (rx_msg[0])
	{
		/* the ring is not empty */

		SIGNAL_PAIR	dram_sig;
		__declspec(sdram) unsigned char *p_pkt_hdr; 
		__declspec(dram_read_reg) unsigned int pkthdr_in[8];

		/* set the meta data accordingly */
		dlMeta.value[1]	= rx_msg[2];
		dlMeta.value[2] 	= rx_msg[3];
		dlMeta.value[3] 	= rx_msg[4];
		dlMeta.value[0]		= 0;
		dlMeta.value[4]		= 0;
		dlMeta.nextHopId 	= -1;		// set nexthop ID to invalid value.
		dlMeta.value[5]		= 0;
		dlMeta.value[6]		= 0;
		dlMeta.value[7]		= 0;
	
		dlBufHandle.value 		= rx_msg[0];
		dlEopBufHandle.value 	= rx_msg[1];

		/*
		 * now read IP header into the registers
		 */		
		p_pkt_hdr = (__declspec(dram) unsigned char *) 
				(Dl_BufGetData(dlBufHandle) + dlMeta.offset);
		
		dram_read(pkthdr_in,(volatile void __declspec(sdram) *)p_pkt_hdr, PKTHDR_CACHE_SIZE, sig_done,&dram_sig);

		wait_for_all(&dram_sig);

		/* for now, copy into register buffer */
		pkt_hdr[0] = pkthdr_in[0];
		pkt_hdr[1] = pkthdr_in[1];
		pkt_hdr[2] = pkthdr_in[2];
		pkt_hdr[3] = pkthdr_in[3];
		pkt_hdr[4] = pkthdr_in[4];
		pkt_hdr[5] = pkthdr_in[5];

//	6 & 7 not used. So save instructions. XXX - to be removed.
//		pkt_hdr[6] = pkthdr_in[6];
//		pkt_hdr[7] = pkthdr_in[7];

		dlNextBlock = DL_SOURCE_NEXT1;
	}

	else
	{
		dlNextBlock = IX_NULL;
		dlBufHandle.value = IX_NULL;
	}

	return;

}


///////////////////////////////////////////////////////////////////////////////
// dl_qm_sink:
//	 	Description: 
//
//	 	Outputs:
//
//		Inputs:
//

INLINE void	dl_qm_sink(SIGNAL_MASK *p_mask, SIGNAL *p_sig_scratch)
{

	/* recalculate cell count */
	dlBufHandle = compute_sop_buf_cframes(dlBufHandle);


	if (dlNextBlock == BID_NEXT_BLOCK)
	{
		__declspec(dram_write_reg) unsigned int pkthdr_out[8];

		__declspec(sram) unsigned char 	*p_meta;
		__declspec(sdram) unsigned char *p_pkt_hdr;

		SIGNAL		sig_meta;
		SIGNAL_PAIR	dram_sig;

		__declspec(sram_write_reg) unsigned int qm_msg[3];
	
		/* now flush meta data 
		 *	Always skip the first long word in Meta data (buffer_next pointer) 
		 */	

		Dl_MetaFlushCacheSkip0(dlBufHandle, &sig_meta, SIG_NONE, META_CACHE_SIZE - 1);

	
		/* now flush IP header */
		p_pkt_hdr = (__declspec(sdram) unsigned char *)	
				(Dl_BufGetData(dlBufHandle) + dlMeta.offset);

		pkthdr_out[0] = pkt_hdr[0];
		pkthdr_out[1] = pkt_hdr[1];

		pkthdr_out[2] = pkt_hdr[2];
		pkthdr_out[3] = pkt_hdr[3];

		pkthdr_out[4] = pkt_hdr[4];
		pkthdr_out[5] = pkt_hdr[5];

//	6 & 7 are used. So save instructions. XXX - To be removed.
//		pkthdr_out[6] = pkt_hdr[6];
//		pkthdr_out[7] = pkt_hdr[7];

		dram_write(pkthdr_out, p_pkt_hdr, PKTHDR_CACHE_SIZE, sig_done, &dram_sig);

		/* now wait for all signals. This preserves thread order */
		wait_for_all(&dram_sig,&sig_meta,&dl_sig_prev);

		if (ctx() != 7)
		{
			// signal next ctx
			local_csr_write(local_csr_same_me_signal,(0x80 | (DL_SIG_WAKE << 3)));
		}			

		/*
		 * now send message to next block 
		 */
		while(inp_state_test(IPV4_TO_QM_SCR_RING_FULL_CSR));	

		// ring has some space
		qm_msg[0] = dlBufHandle.value;
		qm_msg[1] = dlEopBufHandle.value;
		qm_msg[2] = (dlMeta.fabricPort << 4) | (0x1 << 31);

		scratch_put_ring(&qm_msg,(volatile __declspec(scratch) void *)(IPV4_TO_QM_SCR_RING << 2),	
			3, sig_done,p_sig_scratch);

		*p_mask |= __signals(p_sig_scratch);
	}

	else
	{
		// wait for signal from prev thread
		wait_for_all(&dl_sig_prev);

		if (ctx() != 7)
		{
			// signal next thread
			local_csr_write(local_csr_same_me_signal,(0x80 | (DL_SIG_WAKE << 3)));
		}

		/* exception processing */
		if (dlNextBlock == IX_EXCEPTION)
		{
			/* for now drop packet */
			if (dlEopBufHandle.value == IX_NULL)
				Dl_BufDrop(dlBufHandle);
			else
				Dl_BufDropChain(dlBufHandle,dlEopBufHandle);

		}
		else if(dlNextBlock != IX_NULL)
		{
			/* drop the packet */
			if (dlEopBufHandle.value == IX_NULL)
				Dl_BufDrop(dlBufHandle);
			else
				Dl_BufDropChain(dlBufHandle,dlEopBufHandle);

		}

	}
	return;

}

///////////////////////////////////////////////////////////////////////////////
// dl_set_exception:
//	 	Description: 
//			Set exception code. Not used for Now. Place Holder.
//
//	 	Outputs:
//
//		Inputs:
//		
//		Size:   				:
//	
//
INLINE void	dl_set_exception(int block_id, int exception_code)
{
	dl_exception_code = exception_code;

}


#endif	// __DL_SOURCE_C__

