// start with delay plugin ... see README

/*
 * Copyright (c) 2008 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: priq.c  
 * Author: Ken Wong
 * Email: kenw@arl.wustl.edu
 * Organization: Applied Research Laboratory
 * 
 * Derived from: pluginFramework/delay.c
 *
 * Date Created: 12/10/2008 
 * 
 * Description: 3 priority queues
 *
 * Modification History:
 * 	2/5/09	version v3	Added debug_on
 * 	1/20/09	version v2	Cleaned up v1 and README
 * 	1/14/09	version v1	Three flow priorities (QID=1,2,3)
 * 	1/5/09	version v0	Only high- and low-priority flows (QID=1 or *)
 *
 * Debugging:
 *
 * 	DEBUG1	display result of onl_api_getQueueParams()
 * 	DEBUG2	nalloc	number of calls to queue_alloc()
 * 		freesz	number of items on the free list
 * 	DEBUG3	check the validity of the qid and buffer handle fields
 * 		  in the ring_in meta-pkt.  Requires DEBUG5.
 * 	DEBUG4	record/display free list state variables in xdata[].
 * 		  Requires DEBUG5.
 * 	DEBUG5	record/display xdata[].
 *
 */


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

#include "scratch_rings_WU.h"

//#ifdef DL_ORDERED
//    SIGNAL dl_sink_packet_sig;
//    SIGNAL dl_source_packet_sig;
//#endif

//-----------------------------------------------------------
// typedefs, unions, enums
//-----------------------------------------------------------
// sizeof(struct item_tag) = 20 ==> 52,428 items in 1 MB
struct item_tag {
    unsigned int	buf_handle;	// meta-packet
    unsigned int	out_port;	// .
    unsigned int	qid;		// .
    unsigned int	l3_pkt_len;	// .
    struct item_tag	*next;
};

struct queue_tag {
    unsigned long	which;		// which subscript
    unsigned long	npkts;		// #pkts in queue
    unsigned long	nbytes;		// #bytes in queue
    unsigned long	maxinq;		// max #pkts in queue
    unsigned long	ndrops;		// #overflows from queue
    unsigned long	nerrs;		// #errors other than drops
    struct item_tag	*hd;		// head ptr
    struct item_tag	*tl;		// tail ptr
};

//-----------------------------------------------------------
// 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

#define	N	2	// #flow types (gold, silver, bronze) - 1
			//   only silver and bronze traffic are queued here
			//   gold traffic is immediately forwarded to Q64

#define	GOLD_FLOW	1	// QID in meta-pkt
#define	SILVER_FLOW	2
#define	BRONZE_FLOW	3
#define SILVERQ		0	// internal queue
#define BRONZEQ		1

const unsigned int SLEEP_CYCLES = 14000;	// cycles between
						//   callbacks (10 usec)
__declspec(gp_reg) unsigned int pluginId;	// plugin id (0...7)
__declspec(shared gp_reg) unsigned int npkts;	// total #pkts
__declspec(shared gp_reg) unsigned int ndrops;	// total #pkts dropped
__declspec(shared gp_reg) unsigned int debug_on;

#ifdef DEBUG2
__declspec(shared gp_reg) unsigned int nalloc = 0;// total #queue_alloc() calls
						// DEBUG2
#endif

#define RESQ		64		// reserved pkt queue

//------
#define UNLOCKED 0
#define LOCKED   1
__declspec(shared gp_reg) unsigned int queue_lock;

#define	MAX_QUEUE_SZ	10
//XXX	#define	MAX_QUEUE_SZ	40000
__declspec(shared sram) struct queue_tag queue[N];
__declspec(sram) struct item_tag * __declspec(shared sram) free_hd;// free list
	// pointer to SRAM that resides in sram and is shared


//------


#include "plugin_helpers.h"

//-----------------------------------------------------------
// Function prototypes
//-----------------------------------------------------------
void handle_pkt_user();
void handle_msg_user();
void plugin_init_user();
int queue_init_free( void );
void queue_init_desc( __declspec(shared sram) struct queue_tag *qptr, int which );

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

// forward reference
void helper_send_from_queue_to_QM(
			__declspec(shared sram) struct queue_tag *qptr );
struct item_tag * queue_alloc( void );
int queue_free( struct item_tag *item );
int queue_enq(
	__declspec(shared sram) struct queue_tag *qptr,
	unsigned int buf_handle, 	unsigned int out_port, 
	unsigned int qid,		unsigned int l3_pkt_len );
int queue_pop( __declspec(shared sram) struct queue_tag *qptr );


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

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

// handle errors
#define	BAD_QUEUE_INIT_ERR	1	// bad queue_init()
#define	BAD_ENQ_ERR		2	// bad queue_enq()
#define	BAD_POP_EMPTY_ERR	3	// bad queue_pop() - empty queue
#define	BAD_POP_FREE_ERR	4	// bad queue_pop() - free() failed
#define	BAD_NXTBLK		5	// bad nextBlk
#define	BAD_OUT_PORT		6	// bad QID in meta-pkt
#define	BAD_QID			7	// bad QID in meta-pkt
#define	BAD_HANDLE_A		98	// bad buffer handle in meta-pkt
#define	BAD_HANDLE_B		99	// bad buffer handle in meta-pkt = 0

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

#ifdef DEBUG3
volatile __declspec(shared sram) unsigned int nerrsA;	// #handle A errs
volatile __declspec(shared sram) unsigned int nerrsB;	// #handle B errs
#endif

#ifdef DEBUG4
#define NX	32
volatile __declspec(shared sram) unsigned int	xdata[NX];	// extra data
volatile __declspec(shared sram) unsigned int	nxdata;		// #xdata valid
volatile __declspec(shared sram) unsigned int	nxget;
#endif

// 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, 0);	// 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;
    ndrops = 0;

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

#ifdef DEBUG3
    nerrsA = 0;		nerrsB = 0;
#endif
#ifdef DEBUG5
    nxdata = 0;		nxget = 0;
#endif

    sleep( SLEEP_CYCLES );		// not sure if I need this
    helper_plugin_cntr_zero( 0 );
    helper_plugin_cntr_zero( 1 );
    helper_plugin_cntr_zero( 2 );
    helper_plugin_cntr_zero( 3 );
}

#ifdef DEBUG5
// record extra data (usually debugging data)
//
static __forceinline void
helper_set_xdata( unsigned int x ) {
    if( nxdata < NX ) {
	xdata[nxdata] = x;
	++nxdata;
    }
}
#endif


#ifdef DEBUG5
// output control msg with xdata[]
//									<<<<<
void
helper_sram_outmsg_xdata( __declspec(local_mem) char *outmsgstr ) {
    __declspec(sram) char	SPACE[2] = " ";
    __declspec(sram) char	sram_msg_buf[28];
    __declspec(sram) char	sram_tmpstr[16];
    __declspec(gp_reg) int	i;

    if( nxget >= nxdata )	return;

    sram_msg_buf[0] = '\0';
    for( i=0; i<2; i++ ) {
    	if( nxget >= nxdata )	break;
    	if( i > 0 )	strcat_sram( sram_msg_buf, SPACE );
	helper_ultoa_sram( xdata[nxget], sram_tmpstr, 16 );
	strcat_sram( sram_msg_buf, sram_tmpstr );
	++nxget;
    }
    memcpy_lmem_sram( outmsgstr, sram_msg_buf, 28 );
}
#endif


#ifdef DEBUG3
//
// check meta-packet fields (used for debugging)
// 	ring_out.plugin_qm_data_out.qid = (1+out_port)*8192 + 64
// 					qid = 41024 for out_port = 4
// 	ring_out.plugin_qm_data_out.buf_handle_lo24:
// 		rightmost 3 bits = 0
// 		rightmost 24 bits != 0
// 	out_port in {0:4}
//
// Note:
// 	The calls to helper_sram_dbgmsg_str_1ul() are commented out
// 	because they caused the compile to run out of local memory.
//
void
helper_check_meta( plugin_out_data my_ring_out ) {
    if( my_ring_out.plugin_qm_data_out.out_port != 4 ) {	// output port
//	helper_sram_dbgmsg_str_1ul( tmpstr1,
//					ring_out.plugin_qm_data_out.out_port );
    	helper_set_errno( BAD_OUT_PORT );
    }
    if( my_ring_out.plugin_qm_data_out.qid != 41024 ) {		// QID
//	helper_sram_dbgmsg_str_1ul( tmpstr2,
//					ring_out.plugin_qm_data_out.qid );
    	helper_set_errno( BAD_QID );
    }
    								// buffer handle
    if( (my_ring_out.plugin_qm_data_out.buf_handle_lo24 & 0x7) != 0 ) {
//	helper_sram_dbgmsg_str_1ul( tmpstr3,
//				ring_out.plugin_qm_data_out.buf_handle_lo24 );
	if( debug_on ) {
	    helper_set_xdata( my_ring_out.plugin_qm_data_out.buf_handle_lo24 );
	}
    	helper_set_errno( BAD_HANDLE_A );
    	++nerrsA;
    }
    if( my_ring_out.plugin_qm_data_out.buf_handle_lo24 == 0 ) {
//	helper_sram_dbgmsg_str_1ul( tmpstr3,
//				ring_out.plugin_qm_data_out.buf_handle_lo24 );
	if( debug_on ) {
	    helper_set_xdata( my_ring_out.plugin_qm_data_out.buf_handle_lo24 );
	}
    	helper_set_errno( BAD_HANDLE_B );
    	++nerrsB;
    }
}
#endif						// END DEBUG3


// Same as dl_sink_packet() but don't do any signalling and assume pkt
// goes to QM
//
static __forceinline void
helper_send_from_queue_to_QM( __declspec(shared,sram) struct queue_tag *qptr ) {
    plugin_out_data	my_ring_out;	// ring data to next block
    int		rc;

    my_ring_out.plugin_qm_data_out.out_port		= qptr->hd->out_port;
    my_ring_out.plugin_qm_data_out.qid			= qptr->hd->qid;
    my_ring_out.plugin_qm_data_out.l3_pkt_len		= qptr->hd->l3_pkt_len;
    my_ring_out.plugin_qm_data_out.buf_handle_lo24	= qptr->hd->buf_handle;

    rc = queue_pop( qptr );
    if( rc == -1 ) {
    	helper_set_errno( BAD_POP_EMPTY_ERR );
    } else if( rc == -2 ) {
    	helper_set_errno( BAD_POP_FREE_ERR );
    }

#ifdef DEBUG3
    if( debug_on )	helper_check_meta( my_ring_out );	// DEBUG3
#endif

    dlNextBlock = QM;

    scr_ring_put_buffer_3word( PLUGIN_TO_QM_RING, my_ring_out.i, 0 );
}


//-----------------------------------------------------------
// Begin Normal Functions
//-----------------------------------------------------------
//									<<<<<
void handle_pkt_user( )  {
    __declspec(gp_reg) unsigned int out_port;
    __declspec(gp_reg) unsigned int qid;
    __declspec(gp_reg) unsigned int rawqid;
    unsigned long	ninq;
    // debug stuff
#ifdef DEBUG
    __declspec(local_mem) char dbgmsg1[8] = "qid=1";
    __declspec(local_mem) char dbgmsg2[8] = "qid>1";
    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

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

#ifdef DEBUG
    if( debug_on )	helper_sram_dbgmsg_str_1ul( dbgmsg3, qid );
#endif

    ++npkts;

    if( qid == GOLD_FLOW ) {
    	onl_api_plugin_cntr_inc(pluginId, 1);
    } else if( qid == SILVER_FLOW ) {
    	onl_api_plugin_cntr_inc(pluginId, 2);
    } else if( qid == BRONZE_FLOW ) {
    	onl_api_plugin_cntr_inc(pluginId, 3);
    } else {			// BAD META-PKT QID
	helper_set_errno( BAD_QID );
	helper_set_out_to_DROP( );
	return;
    }

    if( qid == GOLD_FLOW ) {
#ifdef DEBUG
	if( debug_on )	onl_api_debug_message( msgNextBlock, dbgmsg1 );
#endif
	if ( helper_set_meta_default( QM ) != 0 ) {
    	    helper_set_errno( BAD_NXTBLK );
	}
	qid = RESQ;
	helper_set_meta_qid( out_port, qid );
    } else {
#ifdef DEBUG
	if( debug_on )	onl_api_debug_message( msgNextBlock, dbgmsg2 );
#endif
	rawqid = FormRawQid( out_port, RESQ );
	if( qid == SILVER_FLOW ) {
	    ninq = queue_enq( &queue[SILVERQ],
	    				ring_in.buf_handle_lo24,
					out_port, rawqid, ring_in.l3_pkt_len );
	} else {
	    ninq = queue_enq( &queue[BRONZEQ],
	    				ring_in.buf_handle_lo24,
					out_port, rawqid, ring_in.l3_pkt_len );
	}
#ifdef DEBUG
	if( debug_on )	helper_sram_dbgmsg_str_1ul( dbgmsg4, ninq );
#endif
	if( ninq == -1 ) {		// hard limit
//XXX	    helper_set_errno( BAD_ENQ_ERR );
	    //helper_set_xdata( npkts );
	    //helper_set_xdata( qid );
	    ++ndrops;		// number of drops
	    helper_set_out_to_DROP( );
	} else {
	    helper_set_out_to_DO_NOTHING( );
	}
    }
}

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

//									<<<<<
void plugin_init_user()
{
    if(ctx() == 0)
    {
	reset_counters( );
	debug_on = 1;

	queue_lock = UNLOCKED;
	if( queue_init_free() != 0 ) {
	    helper_set_errno( BAD_QUEUE_INIT_ERR );
	}
	queue_init_desc( &queue[SILVERQ], SILVERQ );
	queue_init_desc( &queue[BRONZEQ], BRONZEQ );

#ifdef DEBUGX
	__set_timestamp( 0 );
#endif
    }
}



/**
	----------------------------------------------------------------
 @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:
//   get:
//   	=vers	display version number
//	=npkts	display npkts, queue[0].npkts, queue[1].npkts
//	=ndrops	display ndrops, queue[0].ndrops, queue[1].ndrops
//	=nerrs	display nerrs, queue[0].nerrs, queue[1].nerrs
//	=maxinq	display 0, queue[0].maxinq, queue[1].maxinq
//	=qstats0 display queue[0].npkts, queue[0].nbytes, queue[0].maxinq,
//						queue[0].ndrops, queue[0].nerrs
//	=qstats1 display queue[1].npkts, queue[1].nbytes, queue[1].maxinq,
//						queue[1].ndrops, queue[1].nerrs
//	=errno	display errno[0], ... , errno[4]
//	=nerrsAB display nerrsA and nerrsB		DEBUG3
//	=nalloc	display #times queue_alloc() called	DEBUG2
//	=freesz	display #items in free list		DEBUG2
//	=xdata	display xdata[]				DEBUG5
//		  (see plugin.data2 file for example output)
//   miscellaneous:
//	reset	reset 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] = "3.5";
#ifdef DEBUG2
    __declspec(sram) unsigned int freesz;		// DEBUG2
    volatile __declspec(sram) struct item_tag *p;	// DEBUG2
#endif

//XXX
__declspec( gp_reg ) onl_api_qparams	qparams;
char GET_spcl[8]	= "=spcl";
    char GET_vers[8]	= "=vers";
    char GET_npkts[8]	= "=npkts";
    char GET_ndrops[8]	= "=ndrops";
    char GET_nerrs[8]	= "=nerrs";
    char GET_maxinq[8]	= "=maxinq";
    char GET_qstats0[8]	= "=qstats0";
    char GET_qstats1[8]	= "=qstats1";
    char GET_errno[8]	= "=errno";
    char RESET[8]	= "reset";
#ifdef DEBUG2
    char GET_nalloc[8]	= "=nalloc";		// DEBUG2
    char GET_freesz[8]	= "=freesz";		// DEBUG2
#endif
#ifdef DEBUG3
    char GET_nerrsAB[8]	= "=nerrsAB";		// DEBUG3
#endif
#ifdef DEBUG5
    char GET_xdata[8]	= "=xdata";
#endif
    char DEBUG_op[8]	= "debug";

    char BAD_op_err[8]	= "BAD OP";		// error msgs

//XXX    for(i=0; i<8; ++i) { message[i] = 0; }
    // to get rid of the compiler error:  "Incorrect use of register variable
    // message - check for & operator or non-constant buffer index"
    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, 8) == 0 ) {
	memcpy_lmem_sram( outmsgstr, (void *)vers, 4 );
//XXX
} else if( strncmp_sram(sram_inmsgstr, GET_spcl, 8) == 0 ) {
	onl_api_getQueueParams( 41024, &qparams );	// 5*8192+64
	helper_sram_outmsg_3ul( (unsigned long)free_hd,
				qparams.length, queue_lock, outmsgstr );
    } else if( strncmp_sram(sram_inmsgstr, GET_npkts, 8) == 0 ) {
	helper_sram_outmsg_3ul( npkts, queue[0].npkts, queue[1].npkts,
								outmsgstr );
    } else if( strncmp_sram(sram_inmsgstr, GET_maxinq, 8) == 0 ) {
	helper_sram_outmsg_3ul( 0, queue[0].maxinq, queue[1].maxinq,
								outmsgstr );
    } else if( strncmp_sram(sram_inmsgstr, GET_ndrops, 8) == 0 ) {
	helper_sram_outmsg_3ul( ndrops, queue[0].ndrops, queue[1].ndrops,
								outmsgstr );
    } else if( strncmp_sram(sram_inmsgstr, GET_qstats0, 8) == 0 ) {
	helper_sram_outmsg_5ul( queue[0].npkts, queue[0].nbytes,
		queue[0].maxinq, queue[0].ndrops, queue[0].nerrs, outmsgstr );
    } else if( strncmp_sram(sram_inmsgstr, GET_qstats1, 8) == 0 ) {
	helper_sram_outmsg_5ul( queue[1].npkts, queue[1].nbytes,
		queue[1].maxinq, queue[1].ndrops, queue[1].nerrs, outmsgstr );
    } else if( strncmp_sram(sram_inmsgstr, GET_nerrs, 8) == 0 ) {
	helper_sram_outmsg_3ul( nerrs, queue[0].nerrs, queue[1].nerrs,
								outmsgstr );
    } else if( strncmp_sram(sram_inmsgstr, GET_errno, 8) == 0 ) {
	helper_sram_outmsg_5ul( errno[0], errno[1], errno[2], errno[3],
							errno[4], outmsgstr );
    } else if( strncmp_sram(sram_inmsgstr, RESET, 5) == 0 ) {
	reset_counters( );
    } else if( strncmp_sram(sram_inmsgstr, DEBUG_op, 5) == 0 ) {
    	debug_on = (debug_on+1) & 0x1;
	helper_sram_outmsg_1ul( debug_on, outmsgstr );
#ifdef DEBUG2
    } else if( strncmp_sram(sram_inmsgstr, GET_nalloc, 8) == 0 ) {
	helper_sram_outmsg_3ul( nalloc, 0, 0, outmsgstr );	// DEBUG2
    } else if( strncmp_sram(sram_inmsgstr, GET_freesz, 8) == 0 ) {
    	freesz = 0;						// DEBUG2
	p = free_hd;
	while( p != 0 ) {
	    ++freesz;
	    p = p->next;
	}
	helper_sram_outmsg_3ul( freesz, (unsigned int) p, 0, outmsgstr );
#endif
#ifdef DEBUG3
    } else if( strncmp_sram(sram_inmsgstr, GET_nerrsAB, 8) == 0 ) {
	helper_sram_outmsg_3ul( nerrsA, nerrsB, 0, outmsgstr );
#endif
#ifdef DEBUG5
    } else if( strncmp_sram(sram_inmsgstr, GET_xdata, 8) == 0 ) {
	helper_sram_outmsg_xdata( outmsgstr );
#endif
    } else {
	memcpy_lmem_sram( outmsgstr, BAD_op_err, 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
//									<<<<<
//o handle_pkt_user()
//
//	if( QID == GOLD_FLOW )	Forward pkt to QM;
//	else {
//	    Enqueue onto qi where i = K-2, K = qid from ring_in;
//	}
//
//o callback()
//
//	n = Get Q64;
//	if( n== 0 ) {
//	    if( q0 > 0 )
//	    { Forward first pkt from q0 to QM;	return; }
//	    else if( ( q1 > 0 )
//	    { Forward first pkt from q2 to QM;	return; }
//	} else Error;
//

void callback()
{
    __declspec( gp_reg ) onl_api_qparams	qparams;

#ifdef DEBUG1
    char	dbgmsg2[8] = "qlen =";
    char	dbgmsg3[8] = "quan =";
    char	dbgmsg4[8] = "thre =";
#endif

#ifdef DEBUG
    __declspec(local_mem) char	dbgmsg1[8] = "CB a";
#endif

    onl_api_getQueueParams( 41024, &qparams );	// 5*8192+64

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

    if( qparams.length == 0 ) {		// empty high-priority queue
	if( queue[SILVERQ].npkts > 0 ) {
#ifdef DEBUG
	    if( debug_on )	onl_api_debug_message( msgNextBlock, dbgmsg1 );
#endif
	    helper_send_from_queue_to_QM( &queue[SILVERQ] );
	} else if( queue[BRONZEQ].npkts > 0 ) {
#ifdef DEBUG
	    if( debug_on )	onl_api_debug_message( msgNextBlock, dbgmsg1 );
#endif
	    helper_send_from_queue_to_QM( &queue[BRONZEQ] );
	}
    }

    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
}

// --------------------------------------------------------------------------
// queueing functions
//
//	queue_init_free		initialize free list
//	queue_init_desc		initialize queue descriptor
//	queue_enq		enqueue an item onto a queue
//	queue_pop		pop an item from a queue
//	queue_alloc		allocate space for an item from the free list
//	queue_free		put an item back onto the free list
//
// --------------------------------------------------------------------------

// initialize queue free list
//									<<<<<
// 	This free list is separated out from the queue descriptor so that
// 	it can be shared by multiple queues.
// 	An example usage for two queues, is:
// 		queue_init_free();
// 		queue_init_desc( &queue[0], 0 );
// 		queue_init_desc( &queue[1], 1 );
//
// 	e.g., see plugin_init_user().
//
int
queue_init_free( void ) {
    int		i;
    int		K = MAX_QUEUE_SZ-1;
    __declspec(sram) struct item_tag *item_ptr;

    if ( pluginId == 0)		item_ptr = (struct item_tag *) 0xC0100000;
    else if ( pluginId == 1)	item_ptr = (struct item_tag *) 0xC0200000;
    else if ( pluginId == 2)	item_ptr = (struct item_tag *) 0xC0300000;
    else if ( pluginId == 3)	item_ptr = (struct item_tag *) 0xC0400000;
    else if ( pluginId == 4)	item_ptr = (struct item_tag *) 0xC0500000;
    else			return -1;

    free_hd = item_ptr;
    (item_ptr+K)->next = 0;

    for (i=0; i<K; i++) {
    	item_ptr->next = item_ptr+1;
	++item_ptr;
    }

    return 0;
}


// initialize a queue descriptor
//									<<<<<
void
queue_init_desc( __declspec(shared sram) struct queue_tag *qptr, int which ) {
    qptr->which = which;
    qptr->hd = qptr->tl = 0;
    qptr->npkts = 0;
    qptr->nbytes = 0;
    qptr->maxinq = 0;
    qptr->ndrops = 0;
}

// atomically insert item at end of queue
// 	return number of items in the queue if OK; else -1
//									<<<<<
int
queue_enq(	__declspec(shared sram) struct queue_tag *qptr,
		unsigned int buf_handle, 
		unsigned int out_port, 
		unsigned int qid, 
		unsigned int l3_pkt_len ) {
    struct item_tag	*item;

    while( queue_lock == LOCKED )	{ ctx_swap(); }
    queue_lock = LOCKED;

	item = queue_alloc( );
	if( item == 0 )	{
	    qptr->ndrops = qptr->ndrops + 1;
	    queue_lock = UNLOCKED;
	    return -1;
	}

	item->buf_handle = buf_handle;
	item->out_port = out_port;
	item->qid = qid;
	item->l3_pkt_len = l3_pkt_len;
	item->next = 0;

	if( qptr->npkts == 0 )	qptr->hd = item;
	else			qptr->tl->next = item;
	qptr->tl = item;

	++(qptr->npkts);
	if( qptr->npkts > qptr->maxinq )	qptr->maxinq = qptr->npkts;

    queue_lock = UNLOCKED;

    return qptr->npkts;
}

// atomically pop the front of the queue
// 	return 0 if OK; -1 otherwise.
// 	A return of -1 means the free list is empty.
//									<<<<<
int
queue_pop( __declspec(shared sram) struct queue_tag *qptr ) {
    struct item_tag	*item;

    while( queue_lock == LOCKED )	ctx_swap();
    queue_lock = LOCKED;

	if( qptr->npkts <= 0 ) {
	    qptr->nerrs = qptr->nerrs + 1;
	    queue_lock = UNLOCKED;
	    return -1;
	}

	item = qptr->hd;
	qptr->hd = item->next;
	--(qptr->npkts);
	if( qptr->npkts == 0 )	qptr->tl = 0;
	if( queue_free( item ) == -1 ) {
	    qptr->nerrs = qptr->nerrs + 1;
	    queue_lock = UNLOCKED;
	    return -2;
	}

    queue_lock = UNLOCKED;

    return 0;
}

// allocate space for an item
//									<<<<<
// 	Get space for an item from the free list
//
// CAVEAT:  should only be called while queue_lock == LOCKED
//
struct item_tag *
queue_alloc( void ) {
    struct item_tag *item;

#ifdef DEBUG2
    ++nalloc;		// DEBUG2
#endif

    if( free_hd == 0 )	return 0;

#ifdef DEBUG4
    if( debug_on ) {
			helper_set_xdata( (unsigned int) free_hd );
    }
#endif

    item = free_hd;
    free_hd = item->next;

#ifdef DEBUG4
    if( debug_on ) {
			helper_set_xdata( (unsigned int) free_hd );
			helper_set_xdata( (unsigned int) item );
			helper_set_xdata( (unsigned int) &free_hd );
    }
#endif

    return item;
}

// free an item
//									<<<<<
// 	Put an item back onto the front of the free list
//
// CAVEAT:  should only be called while queue_lock == LOCKED
//
int
queue_free( struct item_tag *item ) {
    if( item == 0 )	return -1;

#ifdef DEBUG4
    if( debug_on ) {
			helper_set_xdata( (unsigned int) free_hd );
			helper_set_xdata( (unsigned int) item );
    }
#endif

    item->next = free_hd;
    free_hd = item;

#ifdef DEBUG4
    if( debug_on ) {
			helper_set_xdata( (unsigned int) free_hd );
			helper_set_xdata( (unsigned int) &free_hd );
    }
#endif

    return 0;
}

