/*******************************************************************************
 *                            Intel Proprietary
 *
 * Copyright (c) 1998-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, CA  95052-8119
 *******************************************************************************/

/*****************************************************************************
 *
 * File:  ether_encap.c
 *
 * Abstract:
 *      Ethernet encap performs the Layer2 encapsulation. It encapsulates the 
 *		Dest Ethernet, Source Ethernet, and Protocol type fields. A check is made  
 *		on the validity of the table entry.
 *	
 *
 * Contents:
 *		Definitions of the following exported functions :=
 *    	ether_encap ():	performs the layer 2 encapsulation.    
 *
 *****************************************************************************/


#ifndef __ETHERNET_ENCAP_C__
#define __ETHERNET_ENCAP_C__


#include "ether_encap.h"
#include "dl_system.h"

/**************************************************************************************
* This a functional pipe-stage microblock which add Ethernet header,
* It gets next hop ID from meta data, if next_hop_id = 0xFFFF, 
* it means Ethernet header is already in DRAM.
* If next_hop_id !=0xFFFF, it check whether there is a valid L2 table entry 
* for this next_hop_id in L2_TABLE in SRAM by the valid bit of this 
* L2 table entry.  If valid, it prepared Ethernet header from this L2 table 
* entry, and aligns with payload and write to DRAM Xfer reg, If this is not a 
* valid L2 table entry, it will issue an exception packet message to core component 
* running in Xscale.  Since there is no core component in simulation now, we just 
* use this invalid L2 table entry to prepare Ethernet header.  
*
*
***************************************************************************************/



INLINE unsigned int 
EtherEncap(
		   __declspec(dram_write_reg) unsigned int *p_pkt,
		   offsetType in_offset
		  )
{

	__declspec(sram) ether_encap_t *p_encapEntry;
	__declspec(sram_read_reg) ether_encap_t etherEncapEntry;

#ifdef OUTPORT_STATISTICS

	__declspec (sram) ix_ether_outport_stats * mpls_outport_stats;
	__declspec (sram) ix_ether_outport_stats * ipv4_outport_stats;
	__declspec (sram) ix_ether_outport_stats * ipv6_outport_stats;

#endif
	SIGNAL sig_sram;

	/* Check if the next block is Ethernet Encap */
//	if (dlNextBlock != BID_ETHER_ENCAP)
//		return;

	  p_encapEntry	= dlMeta.nextHopId + (ether_encap_t*)L2_TABLE_SRAM_BASE;



	sram_read (	
				 (__declspec(sram_read_reg) void *) &etherEncapEntry,
				 (volatile __declspec(sram) void*) p_encapEntry,
				 ETHER_ENCAP_ENTRY_SIZE, sig_done,
				 &sig_sram
			);

	wait_for_all(&sig_sram);
	
	if (etherEncapEntry.valid)
	{
		if(in_offset.bit1) goto two_and_six;
		if(in_offset.bit2) goto four;

	    /* Append the Ethernet header on to the transfer registers for offset 0 */
	    p_pkt[0] = etherEncapEntry.value[0];
	    p_pkt[1] = etherEncapEntry.value[1];
	    p_pkt[2] = etherEncapEntry.value[2];

		goto end;

		two_and_six:
		if(in_offset.bit2) goto six;

	    /* Append the Ethernet header on to the transfer registers for offset 2 */
	    p_pkt[0] = etherEncapEntry.value[0] >> 16;
	    p_pkt[1] = (etherEncapEntry.value[0] << 16) | (etherEncapEntry.value[1] >> 16);
	    p_pkt[2] = (etherEncapEntry.value[1] << 16) | (etherEncapEntry.value[2] >> 16);
	    p_pkt[3] = (etherEncapEntry.value[2] << 16);

		goto end;

		six:
	    /* Append the Ethernet header on to the transfer registers for offset 6 */
	    p_pkt[1] = etherEncapEntry.value[0] >> 16;
	    p_pkt[2] = (etherEncapEntry.value[0] << 16) | (etherEncapEntry.value[1] >> 16);
	    p_pkt[3] = (etherEncapEntry.value[1] << 16) | (etherEncapEntry.value[2] >> 16);
	    p_pkt[4] = (etherEncapEntry.value[2] << 16);

		goto end;

		four:
	    /* Append the Ethernet header on to the transfer registers  for offset 4*/
	    p_pkt[1] = etherEncapEntry.value[0];
	    p_pkt[2] = etherEncapEntry.value[1];
	    p_pkt[3] = etherEncapEntry.value[2];

	end:        
    

#ifdef OUTPORT_STATISTICS
		if ( etherEncapEntry.packetType == 0x8847 )	
		{
	
			mpls_outport_stats = (__declspec (sram) ix_ether_outport_stats *)MPLS_OUTPORT_STATS_BASE;
			mpls_outport_stats = mpls_outport_stats + dlMeta.outputPort;

			/* Increment the output packet transmit count for MPLS */
			sram_incr (&mpls_outport_stats->outPortTxPkts);


		}
		if ( etherEncapEntry.packetType == 0x0800 )	
		{
	
			ipv4_outport_stats = (__declspec (sram) ix_ether_outport_stats *)IPV4_OUTPORT_STATS_BASE;
			ipv4_outport_stats = ipv4_outport_stats + dlMeta.outputPort;

			/* Increment the output packet transmit count for IPv4 */
			sram_incr (&ipv4_outport_stats->outPortTxPkts);

	
		}
		if ( etherEncapEntry.packetType == 0x86DD )	
		{
	
			ipv6_outport_stats = (__declspec (sram) ix_ether_outport_stats *)IPV6_OUTPORT_STATS_BASE;
			ipv6_outport_stats = ipv6_outport_stats + dlMeta.outputPort;
		
			/* Increment the output packet transmit count for IPv6 */
			sram_incr (&mpls_outport_stats->outPortTxPkts);
		

		}
#endif

		return(etherEncapEntry.packetType);
	}
		else return IX_NULL;

}

#endif
