/*
 * Copyright (c) 2009 Washington University in St. Louis.
 * All rights reserved
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *    1. Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *    3. The name of the author or Washington University may not be used 
 *       to endorse or promote products derived from this source code 
 *       without specific prior written permission.
 *    4. Conditions of any other entities that contributed to this are also
 *       met. If a copyright notice is present from another entity, it must
 *       be maintained in redistributions of the source code.
 *
 * THIS INTELLECTUAL PROPERTY (WHICH MAY INCLUDE BUT IS NOT LIMITED TO SOFTWARE,
 * FIRMWARE, VHDL, etc) IS PROVIDED BY THE AUTHOR AND WASHINGTON UNIVERSITY 
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR WASHINGTON UNIVERSITY 
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS INTELLECTUAL PROPERTY, EVEN IF 
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * */
/*
 * File: erd++.c  
 * Author: Ken Wong
 * Email: kenw@arl.wustl.edu
 * Organization: Applied Research Laboratory
 * 
 * Derived from: priq.c and setNxtBlk.c
 *
 * 	Modified control msgs; changed queueing to be more like delay
 * 	plugin queueing.
 *
 * Date Created: 3/10/2009 
 * 
 * Description:  Early random drop
 *
 * Modification History:
 * 	3/18/09	version v0
 *
 */


#include <stdlib.h>
#include <memory.h>
#include "plugin_api.h"
#include "plugin_dl.h"

#include "scratch_rings_WU.h"
#include "sram_rings_WU.h"

// uncomment to use hardware random number generator
#define IXP_RAND

#define N		4	// #queues (queues 64-67)

// plugin counters
#define	PKT_COUNT	0	// #pkts received by handle_pkt_user()
#define DROP_COUNT	1	// #pkts dropped by ERD
#define ERR_COUNT	3	// #errors


//-----------------------------------------------------------
// typedefs, unions, enums
//-----------------------------------------------------------


//-----------------------------------------------------------
// Global variables/Registers
//-----------------------------------------------------------
//
// >> thread-specific globals <<
//
__declspec(gp_reg) int dlNextBlock;  // where to send packets to next
__declspec(gp_reg) int dlFromBlock;  // where to get packets from
__declspec(gp_reg) int msgNextBlock; // where to send control messages to next
__declspec(gp_reg) int msgFromBlock; // where to get control messages from

// see ring_formats.h for struct definitions
volatile __declspec(gp_reg) plc_plugin_data ring_in;	// ring data from PLC
volatile __declspec(gp_reg) plugin_out_data ring_out;	// ring data to nxt blk

const unsigned int SLEEP_CYCLES = 14000;	// cycles between
						//   callbacks (10 usec)
__declspec(gp_reg) unsigned int pluginId;	// plugin id (0...7)

// >> user globals <<
//
__declspec(shared gp_reg) unsigned int npkts;	// total #pkts
__declspec(shared gp_reg) unsigned int nsent;	// total #pkts sent by callback()
__declspec(shared gp_reg) unsigned int ndrops;	// total #pkts dropped
__declspec(shared gp_reg) unsigned int debug_on;
__declspec(shared gp_reg) unsigned int nxt_queue;

__declspec(shared gp_reg) unsigned int qlen[N]; // queue length (bytes)
__declspec(shared gp_reg) unsigned int qthresh[N]; // ERD threshold; i.e.,
					// when to start dropping (bytes)
__declspec(shared gp_reg) unsigned int dropmask[N]; // (2^K - 1)
__declspec(shared gp_reg) unsigned int rawqid[N]; // QIDs


//------


#include "plugin_helpers.h"

//-----------------------------------------------------------
// Function prototypes
//-----------------------------------------------------------
void handle_pkt_user();
void handle_msg_user();
void plugin_init_user();

static void wait_packet_signal(SIGNAL *);
static void send_packet_signal(SIGNAL *);


//-----------------------------------------------------------
// New helper functions 
//-----------------------------------------------------------

#define	FormRawQid(out_port,qid)	((((out_port)+1) << 13) | qid)

// handle errors
#define	BAD_NXTBLK	1	// bad dlNextBlock

__declspec(shared gp_reg) unsigned int nerrs;		// #errors
volatile __declspec(shared sram) unsigned int errno[5];	// 1st 5 errors

// record error number
//									<<<<<
static __forceinline void
helper_set_errno( __declspec(local_mem) unsigned int n ) {
    if( nerrs < 5 )	errno[nerrs] = n;
    ++nerrs;
    onl_api_plugin_cntr_inc(pluginId, 3);	// external error counter
}

// set ring_out qid given output port# and external qid
//									<<<<<
static __forceinline void
helper_set_meta_qid(	__declspec(gp_reg) unsigned int out_port,
			__declspec(gp_reg) unsigned int xqid ) {
    ring_out.plugin_qm_data_out.qid = (out_port+1 << 13) | xqid;
}

// reset global counters
//									<<<<<
static __forceinline void
reset_counters( void ) {
    npkts = 0;
    nsent = 0;
    ndrops = 0;

    nerrs = 0;
    errno[0] = 0;	errno[1] = 0;	errno[2] = 0;	errno[3] = 0;
    errno[4] = 0;

    sleep( SLEEP_CYCLES );		// not sure if I need this

    helper_plugin_cntr_zero( PKT_COUNT );
    helper_plugin_cntr_zero( DROP_COUNT );
    helper_plugin_cntr_zero( ERR_COUNT );
}


// do a reset
//
static __forceinline void
do_reset( void ) {
    __declspec( gp_reg ) onl_api_qparams	qparams;

    nxt_queue = 0;

    debug_on = 0;
    rawqid[0] = 16448;    rawqid[1] = 16449;    rawqid[2] = 16450;
    rawqid[3] = 16451;
    reset_counters( );

    // default drop mask = 127(10) ==> drop prob = 1/128
    dropmask[0] = dropmask[1] = dropmask[2] = dropmask[3] = 0x3f;

    // default qthresh[i] = qcapacity[i]/4
    onl_api_getQueueParams( 16448, &qparams );	// 2*8192+64
	qlen[0] = qparams.length;
	qthresh[0] = qparams.threshold>>2;
    onl_api_getQueueParams( 16449, &qparams );	// 2*8192+65
	qlen[1] = qparams.length;
	qthresh[1] = qparams.threshold>>2;
    onl_api_getQueueParams( 16450, &qparams );	// 2*8192+66
	qlen[2] = qparams.length;
	qthresh[2] = qparams.threshold>>2;
    onl_api_getQueueParams( 16451, &qparams );	// 2*8192+67
	qlen[3] = qparams.length;
	qthresh[3] = qparams.threshold>>2;
}


// return 1 if should drop; else 0
//
// >>>>> TRY DROPPING STARTING AT Q/4 instead of Q/2 <<<<<
//
//	Let	Q	be the queue capacity 'qcap' (bytes)
//		L	be the current queue length (bytes)
//		T	be the queue drop threshold (bytes) = Q/2, a constant
//		R	be a random integer in 0..Rmax = 65536 = 2^16
//		p	be the drop probability when L > (Q-T) where p = 1/2^K
//	Then	q	is the drop quanta = pT = T/2^K = T>>K
//		drop	is a boolean value indicating the drop decision
//
//		drop	= True,    L > T and (R & (2^K-1)) = 0
//			= False,   otherwise ( i.e., L <= T )
// rand():
// 
// 	Returns a 16-bit integer between 0 and RAND_MAX = 32767
// 	The IXP's hardware random number generator will be used if you
// 	define the macro IXP_RAND.  'srand(seed)' can be used to set
// 	the random number seed.
//
static __forceinline int
drop(	__declspec(gp_reg) unsigned int qlen,
	__declspec(gp_reg) unsigned int qthresh,
	__declspec(gp_reg) unsigned int dropmask ) {
    int			randint;
    __declspec(gp_reg)	int dropit;

    if( qlen < qthresh )	return 0;

    randint = rand();
    randint = randint & dropmask;	// rightmost K bits

    if( randint == 0 )	dropit = 1;
    else		dropit = 0;

#ifdef DEBUG
helper_sram_dbgmsg_3ul( qlen, randint, dropit );
#endif

    return dropit;
}

//-----------------------------------------------------------
// Begin Normal Functions
//-----------------------------------------------------------
//									<<<<<
//
void handle_pkt_user( )  {
    __declspec(gp_reg) unsigned int out_port;
    __declspec(gp_reg) unsigned int qid;
    unsigned long	ninq;
    int			droppkt;

    // debug stuff
#ifdef DEBUG
    char	dbgmsg[12] = "GOT PKT ";
    char	dbgmsg3[8] = "QID = ";
    char	dbgmsg4[8] = "ninq = ";
#endif

#ifdef DEBUG
    if( debug_on )	helper_sram_dbgmsg_str_1ul( dbgmsg, npkts );
#endif

    ++npkts;

    onl_api_plugin_cntr_inc(pluginId, PKT_COUNT);

    qid = ring_in.qid & 0x1fff;			// external qid
    out_port = (ring_in.uc_mc_bits >> 3) & 0x7;

    droppkt = 0;
    if( qid == 64 ) {
    	droppkt = drop( qlen[0], qthresh[0], dropmask[0] );
    } else if( qid == 65 ) {
    	droppkt = drop( qlen[1], qthresh[1], dropmask[0] );
    } else if( qid == 66 ) {
    	droppkt = drop( qlen[2], qthresh[2], dropmask[0] );
    } else if( qid == 67 ) {
    	droppkt = drop( qlen[3], qthresh[3], dropmask[0] );
    } else {
    	droppkt = 0;	// forward pkt untouched
    }

    if( droppkt ) {
	onl_api_plugin_cntr_inc(pluginId, DROP_COUNT);
	++ndrops;
	if ( helper_set_meta_default( DROP ) != 0 ) {
	    helper_set_errno( BAD_NXTBLK );
	}
    } else {
	if ( helper_set_meta_default( dlNextBlock ) != 0 ) {
	    helper_set_errno( BAD_NXTBLK );
	}
    }

}

//									<<<<<
void handle_msg_user(){}				// NOT USED

//									<<<<<
void plugin_init_user()
{
    if(ctx() == 0)
    {
    	do_reset( );

	// use up a few random integers
	// use srand( )???
	rand( );	rand( );	rand( );	rand( );

#ifdef DEBUGX
	__set_timestamp( 0 );
#endif
    }

    // plugin chain
    if( pluginId == 0 )		dlNextBlock = PACKET_IN_RING_1;
    else if( pluginId == 1 )	dlNextBlock = PACKET_IN_RING_2;
    else if( pluginId == 2 )	dlNextBlock = PACKET_IN_RING_3;
    else if( pluginId == 3 )	dlNextBlock = PACKET_IN_RING_4;
    else			dlNextBlock = QM;
}



/**
	----------------------------------------------------------------
 @User: YOU SHOULD NOT NEED TO MAKE ANY CHANGES TO THE REST OF THIS FILE
	----------------------------------------------------------------
*/


/* handle packets */
//									<<<<<
void handle_pkt()
{
  dl_source_packet(dlFromBlock);
  
  handle_pkt_user( );

  dl_sink_packet(dlNextBlock);	// assumes do nothing if an invalid value
}


/* handle control messages */
//									<<<<<
// op codes:
//   set:
//   	params= xqid xqthresh xdropmask
//   		set drop parameters (qid, qthresh[], dropmask[])
//   		  where qid = 64, 65, 66 or 67.
//   	oport= oport
//   		set the output port to 'oport' where oport = 0..4. The
//   		  default is 1.  This changes only rawqid[].
//   	seed= seed
//   		set the random number seed
//   	test= n
//   		generate n random integers between 0 and 255 and return
//   		  the average, the number less than or equal to 127, and
//   		  the number greater than 127.
//   get:
//   	=vers	display version number
//   	=params	qid
//   		display parameters (qid, qthresh[], dropmask[])
//   		  where qid = 64, 65, 66 or 67.
//	=counts	display counts (npkts, ndrops, nerrs)
//	=errno	display errno[0], ... , errno[4]
//   miscellaneous:
//	reset	reset parameters, npkts[], ndrops[], errno[] counters, etc.
//	debug	toggle debug_on
//
void handle_msg()
{
    // assume messages are at most 8 words for now
    __declspec(gp_reg) unsigned int i;
    __declspec(gp_reg) unsigned int message[8];
    __declspec(gp_reg) onl_api_ctrl_msg_hdr hdr;
    __declspec(local_mem) char inmsgstr[28];			// inbound
    __declspec(local_mem) char outmsgstr[28];			// outbound
    __declspec(local_mem) char lmem_tmpstr[8];
    __declspec(sram) char sram_inmsgstr[28];
    __declspec(sram) char vers[4] = "1.0";

    char SET_params[8]	= "params=";
    char SET_oport[8]	= "oport=";
    char SET_seed[8]	= "seed=";
    char SET_test[8]	= "test=";
    char GET_params[8]	= "=params";
    char GET_vers[8]	= "=vers";
    char GET_counts[8]	= "=counts";
    char GET_errno[8]	= "=errno";
    char RESET[8]	= "reset";
    char DEBUG_op[8]	= "debug";

    char OK_msg[4]	= "OK";
    char BAD_OP_msg[8]	= "BAD OP";
    char BAD_NARGS_msg[12]= "BAD #ARGS =";
    char BAD_QID_msg[8]	= "BAD QID";
    char BAD_ARG_msg[12]= "BAD ARG =";

    // expand for-loop to get rid of the compiler error:
    //					"Incorrect use of register variable
    message[0] = 0;
    message[1] = 0;
    message[2] = 0;
    message[3] = 0;
    message[4] = 0;
    message[5] = 0;
    message[6] = 0;
    message[7] = 0;

    dl_source_message(msgFromBlock, message);

    hdr.value = message[0];
    if( hdr.type != CM_CONTROLMSG )	return;
    if( hdr.response_requested != 1 )	return;

    onl_api_intarr2str( &message[1], inmsgstr );

    outmsgstr[0] = '\0';
    memcpy_sram_lmem( sram_inmsgstr, inmsgstr, 28 );

    if( strncmp_sram(sram_inmsgstr, GET_vers, 5) == 0 ) {
	memcpy_lmem_sram( outmsgstr, (void *)vers, 4 );
    } else if( strncmp_sram(sram_inmsgstr, GET_params, 7) == 0 ) {
    	char	*cmnd_word;		// points to input command field
    	char	*qid_word;		// points to input qid field
	unsigned int	xqid;
	unsigned int	nwords;

	nwords = helper_count_words( sram_inmsgstr );
	if( nwords != 2 ) {
	    helper_sram_outmsg_str_1ul( BAD_NARGS_msg, nwords, outmsgstr );
	} else {
	    cmnd_word = helper_tokenize( sram_inmsgstr );	// get command
	    qid_word = helper_tokenize( cmnd_word+strlen(cmnd_word)+1 );
	    xqid = helper_atou_sram( qid_word );
	    switch( xqid ) {
	    	case 64:
	    	    helper_sram_outmsg_3ul( 64,
		    			qthresh[0], dropmask[0], outmsgstr );
		    break;
	    	case 65:
	    	    helper_sram_outmsg_3ul( 65,
		    			qthresh[1], dropmask[1], outmsgstr );
		    break;
	    	case 66:
	    	    helper_sram_outmsg_3ul( 66,
		    			qthresh[2], dropmask[2], outmsgstr );
		    break;
	    	case 67:
	    	    helper_sram_outmsg_3ul( 67,
		    			qthresh[3], dropmask[3], outmsgstr );
		    break;
	    	default:
	    	    helper_sram_outmsg_3ul( 64,
		    			qthresh[0], dropmask[0], outmsgstr );
		    break;
	    }
	}
    } else if( strncmp_sram(sram_inmsgstr, GET_counts, 7) == 0 ) {
	    helper_sram_outmsg_3ul( npkts, ndrops, nerrs, outmsgstr );
    } else if( strncmp_sram(sram_inmsgstr, GET_errno, 6) == 0 ) {
	helper_sram_outmsg_5ul( errno[0], errno[1], errno[2], errno[3],
							errno[4], outmsgstr );
    } else if( strncmp_sram(sram_inmsgstr, SET_params, 7) == 0 ) {
    	char	*cmnd_word;		// points to input command field
    	char	*qid_word;		// points to input qid field
    	char	*qthresh_word;		// points to input qthresh field
    	char	*dropmask_word;		// points to input dropmask field
	unsigned int	xqid	  = 0;
	unsigned int	xqthresh  = 0;
	unsigned int	xdropmask = 0;
	unsigned int	nwords;

	nwords = helper_count_words( sram_inmsgstr );
	if( nwords != 4 ) {
	    helper_sram_outmsg_str_1ul( BAD_NARGS_msg, nwords, outmsgstr );
	} else {
	    cmnd_word = helper_tokenize( sram_inmsgstr );	// get command
	    qid_word = helper_tokenize( cmnd_word+strlen(cmnd_word)+1 );
	    qthresh_word = helper_tokenize( qid_word+strlen(qid_word)+1 );
	    dropmask_word =
	    		helper_tokenize( qthresh_word+strlen(qthresh_word)+1 );

	    xqid = helper_atou_sram( qid_word );
	    xqthresh = helper_atou_sram( qthresh_word );
	    xdropmask = helper_atou_sram( dropmask_word );
	    switch( xqid ) {
	    case 64:	qthresh[0] = xqthresh;
	    		dropmask[0] = xdropmask;
			break;
	    case 65:	qthresh[1] = xqthresh;
		    	dropmask[1] = xdropmask;
			break;
	    case 66:	qthresh[2] = xqthresh;
		    	dropmask[2] = xdropmask;
			break;
	    case 67:	qthresh[3] = xqthresh;
		    	dropmask[3] = xdropmask;
			break;
	    default:
			memcpy_lmem_sram( outmsgstr, BAD_QID_msg, 8 );
			break;
	    }
	    helper_sram_outmsg_3ul( xqid, xqthresh, xdropmask, outmsgstr );
	}
    } else if( strncmp_sram(sram_inmsgstr, SET_oport, 6) == 0 ) {
    	char	*cmnd_word;		// points to input command field
    	char	*oport_word;		// points to input oport field
	unsigned int	nwords;
	unsigned int	oport;		// output port

	nwords = helper_count_words( sram_inmsgstr );
	if( nwords != 2 ) {
	    helper_sram_outmsg_str_1ul( BAD_NARGS_msg, nwords, outmsgstr );
	} else {
	    cmnd_word = helper_tokenize( sram_inmsgstr );	// get command
	    oport_word = helper_tokenize( cmnd_word+strlen(cmnd_word)+1 );
	    oport = helper_atou_sram( oport_word );
	    if( oport > 4 ) {
	    	helper_sram_outmsg_str_1ul( BAD_ARG_msg, oport, outmsgstr );
	    } else {
		rawqid[0] = (1+oport)*8192 + 64;
		rawqid[1] = rawqid[0] + 1;
		rawqid[2] = rawqid[1] + 1;
		rawqid[3] = rawqid[2] + 1;
	    	helper_sram_outmsg_1ul( oport, outmsgstr );
	    }
	}
    } else if( strncmp_sram(sram_inmsgstr, SET_seed, 5) == 0 ) {
    	char	*cmnd_word;		// points to input command field
    	char	*seed_word;		// points to input seed field
	unsigned int	nwords;
	unsigned int	seed;

	nwords = helper_count_words( sram_inmsgstr );
	if( nwords != 2 ) {
	    helper_sram_outmsg_str_1ul( BAD_NARGS_msg, nwords, outmsgstr );
	} else {
	    cmnd_word = helper_tokenize( sram_inmsgstr );	// get command
	    seed_word = helper_tokenize( cmnd_word+strlen(cmnd_word)+1 );
	    seed = helper_atou_sram( seed_word );
	    srand( seed );
	    helper_sram_outmsg_1ul( seed, outmsgstr );
	}
    } else if( strncmp_sram(sram_inmsgstr, SET_test, 5) == 0 ) {
    	char	*cmnd_word;		// points to input command field
    	char	*n_word;		// points to input n field
	unsigned int	nwords;
	unsigned int	xn;
	unsigned int	mask;

	nwords = helper_count_words( sram_inmsgstr );
	if( nwords != 2 ) {
	    helper_sram_outmsg_str_1ul( BAD_NARGS_msg, nwords, outmsgstr );
	} else {
	    __declspec(gp_reg) int	i;
	    unsigned int		acc;
	    unsigned int		r;
	    unsigned int		nlo;
	    unsigned int		nhi;
	    unsigned int		int_part;
	    unsigned int		frac_part;
	    char			myreply[28];

	    cmnd_word = helper_tokenize( sram_inmsgstr );	// get command
	    n_word = helper_tokenize( cmnd_word+strlen(cmnd_word)+1 );
	    xn = helper_atou_sram( n_word );

	    acc = 0;        nlo = 0;        nhi = 0;
	    for( i=0; i<xn; i++ ) {
	    	r = rand() & 0xff;
		acc += r;
		if( r <= 127 )	++nlo;
		else		++nhi;
	    }
	    int_part = acc/xn;
	    frac_part = ((10*acc)/xn)%10;

	    myreply[0] = '\0';
	    helper_ultoa_sram( int_part, myreply, 28 );
	    strcat( myreply, "." );
	    helper_ultoa_sram( frac_part, myreply+strlen(myreply),
	    						28-strlen(myreply) );
	    strcat( myreply, " " );
	    helper_ultoa_sram( nlo, myreply+strlen(myreply),
	    						28-strlen(myreply) );
	    strcat( myreply, " " );
	    helper_ultoa_sram( nhi, myreply+strlen(myreply),
	    						28-strlen(myreply) );
	    memcpy_lmem_sram( outmsgstr, myreply, 28 );
	}
    } else if( strncmp_sram(sram_inmsgstr, RESET, 5) == 0 ) {
	do_reset( );
    } else if( strncmp_sram(sram_inmsgstr, DEBUG_op, 5) == 0 ) {
    	debug_on = (debug_on+1) & 0x1;
	helper_sram_outmsg_1ul( debug_on, outmsgstr );
    } else {
	memcpy_lmem_sram( outmsgstr, BAD_OP_msg, 8 );
    }

    if( onl_api_str2intarr(outmsgstr, &message[1]) < 0 )	return;

    hdr.type = CM_CONTROLMSGRSP;
    hdr.response_requested = 0;
    hdr.num_words = 7;
    message[0] = hdr.value;

    dl_sink_message(msgNextBlock, message);
}

// handle periodic functionality
//									<<<<<
// Called every 10 usec
// Get queue length of queues 64-67
//
void callback()
{
    __declspec( gp_reg ) onl_api_qparams	qparams;
    int		rc;

    if( nxt_queue >= 4 )	nxt_queue = 0;

    if( nxt_queue == 0 ) {
	onl_api_getQueueParams( rawqid[0], &qparams );	// (1+oport)*8192+64
	qlen[0] = qparams.length;
    } else if( nxt_queue == 1 ) {
	onl_api_getQueueParams( rawqid[1], &qparams );	// (1+oport)*8192+65
	qlen[1] = qparams.length;
    } else if( nxt_queue == 2 ) {
	onl_api_getQueueParams( rawqid[2], &qparams );	// (1+oport)*8192+66
	qlen[2] = qparams.length;
    } else {
	onl_api_getQueueParams( rawqid[3], &qparams );	// (1+oport)*8192+67
	qlen[3] = qparams.length;
    }
    ++nxt_queue;

#ifdef DEBUG1
    if( debug_on ) {
	helper_sram_dbgmsg_3ul( qparams.length, qparams.quantum,
							qparams.threshold );
    }
#endif

    sleep( SLEEP_CYCLES );

}


/* take care of any setup that needs to be done before processing begins */
//									<<<<<
void plugin_init()
{
  /* set the default next block to be the Queue Manager */
  dlNextBlock = QM;

  /* by default, get packets and get and put control messages from input rings
   * based on which microengine we are currently running on; this assumes a
   * default one to one mapping */
  switch(__ME())
  {
    case 0x7:
      pluginId = 0;
      dlFromBlock  = PACKET_IN_RING_0;
      msgFromBlock = MESSAGE_IN_RING_0;
      msgNextBlock = MESSAGE_OUT_RING_0;
      break;
    case 0x10:
      pluginId = 1;
      dlFromBlock  = PACKET_IN_RING_1;
      msgFromBlock = MESSAGE_IN_RING_1;
      msgNextBlock = MESSAGE_OUT_RING_1;

      break;
    case 0x11:
      pluginId = 2;
      dlFromBlock  = PACKET_IN_RING_2;
      msgFromBlock = MESSAGE_IN_RING_2;
      msgNextBlock = MESSAGE_OUT_RING_2;  
      break;
    case 0x12:
      pluginId = 3;
      dlFromBlock  = PACKET_IN_RING_3;
      msgFromBlock = MESSAGE_IN_RING_3;
      msgNextBlock = MESSAGE_OUT_RING_3;    
      break;
    case 0x13:
      pluginId = 4;
      dlFromBlock  = PACKET_IN_RING_4;
      msgFromBlock = MESSAGE_IN_RING_4;
      msgNextBlock = MESSAGE_OUT_RING_4;
      break;
    default:  // keep the compiler happy
      pluginId = 0;
      dlFromBlock  = PACKET_IN_RING_0;
      msgFromBlock = MESSAGE_IN_RING_0;
      msgNextBlock = MESSAGE_OUT_RING_0;
      break;
  }

  plugin_init_user(); // user hook
}


/* entry point */
//									<<<<<
void main()
{
  int c;

  /* do initialization */
  plugin_init();
  dl_sink_init();
  dl_source_init();

  /* get the current thread's context number (0-7) */
  c = ctx();

  if(c >= FIRST_PACKET_THREAD && c <= LAST_PACKET_THREAD)
  {
    while(1)
    {
      handle_pkt();
    }
  }
#ifdef MESSAGE_THREAD
  else if(c == MESSAGE_THREAD)
  {
    while(1)
    {
      handle_msg();
    }
  }
#endif
#ifdef CALLBACK_THREAD
  else if(c == CALLBACK_THREAD)
  {
    while(1)
    {
      callback();
    }
  }
#endif
}


