////////////////////////////////////////////////////////////////////////////////////////
//
//
//                  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
//
////////////////////////////////////////////////////////////////////////////////////////
//
//
//      File Name: qm.c
//
//      Purpose: main file for queue manager 
//
//		
////////////////////////////////////////////////////////////////////////////////////////


#include "qm_packet.h"
#include "qm_internal.c"


/////////////////////////////////////////////////////////////////////////////////////////
//
// 		Qm()
//
//	 	Description: 
//
//		This function executes an infinite loop and calls enqueue and dequeue
//		routines. 
//
////////////////////////////////////////////////////////////////////////////////////////

void Qm(void) 
{

	qm_enq_response_t						schedulerMessageEnq;			// message sent to scheduler on enqueue
	qm_deq_response_t				  		schedulerMessageDeq;			// message sent to scheduler on dequeue 
	dl_buf_handle_t							bufferHandle; 					// buffer handle dequeued from QArray
	__declspec(sram_read_reg) qm_enq_msg_t  enqueueMessage;					// enqueue request  
	__declspec(sram_read_reg) uint32_t 		dequeueMessage;					// dequeue request from scheduler 


	// signal the next thread

	QmSignalNextThread();

	// To kick start the algorithm we do a read from the enqueue scratch ring and 
	// swap out

	QmReadEnqueueRequest(&enqueueMessage); 

	// Swap out waiting for scratch read and previous thread to signal.

	wait_for_all( &sig_enqReadDone, &sig_prevThread) ;

	// The code runs through infinite loop

	while (1)
	{

		// Initialize the message register

		schedulerMessageEnq.value = schedulerMessageDeq.value = bufferHandle.value  = 0;

		// Read dequeue message

		QmReadDequeueRequest(&dequeueMessage);

		// Check if enqueue ring is empty. The read from enqueue scratch ring
		// was issued in the previous phase. If the ring is empty, then 0 is 
		// returned. To save instructions (bit_clr operation) we simply check
		// if the sop bit in the first word is set. 

		if (enqueueMessage.sopHandle.sop != 0)
		{
		
			// This check is for verifying if the enqueue request is for a buffer chain
			// to be dropped. If it matches the drop queue ring number then the special
			// drop queue processsing will be done.	

			if (enqueueMessage.drop == 0)
				schedulerMessageEnq = QmHandleEnqueue(enqueueMessage);
			else  
			{
		
				// enqueue a packet buffer chain to the drop queue 

				QmDropQueueEnqueue(enqueueMessage);

			}

		}
		else // no enqueue
		{
			// signal the next thread

			QmSignalNextThread();

			// dequeue a buffer from the drop queue and free it (if any)

			QmDropQueueDequeue();

		}

		// Read the enqueue request 

		QmReadEnqueueRequest(&enqueueMessage);
                                           
		// Checking for valid dequeue message. The read for dequeueMessage was issued 
		// in the previous phase

		if (dequeueMessage != 0) 
		{

			// Handle the dequeue and return two values. One that the 
			// h/w returns and the other is the transition/scheduler 
			// message

			bufferHandle = QmHandleDequeue(dequeueMessage, &schedulerMessageDeq);

		}
		else // no dequeue
		{

			// signal the next thread

			QmSignalNextThread();

			// Since there is no enqueue just swap out waiting for scratch read 
			// and previous thread to signal.

			wait_for_all( &sig_enqReadDone, &sig_prevThread);
		
				
		}

		// If there was a valid dequeue then write the buffer handle and
		// queue number to the transmit scratch ring. The format for cell
		// based and packet based queue manager is different.

		if(bufferHandle.value != 0)
			QmSendTransmitMessage(bufferHandle, dequeueMessage);

					
		// check and send message to scheduler on NN ring

		QmSendSchedulerMessage(schedulerMessageEnq, schedulerMessageDeq, bufferHandle);	
	
	}

	
}



/////////////////////////////////////////////////////////////////////////////////////////
//
// Qm_Init()
//
//
/////////////////////////////////////////////////////////////////////////////////////////

void Qm_Init(void)
{

	SIGNAL									sig_systemInitDone;
	SIGNAL									sig_sramCSR;
	__declspec(sram_read_reg)  uint32_t		csrReadValue;
	__declspec(sram_write_reg) uint32_t 	csrWriteValue;
	uint32_t					 			sramAddress ;

	// Set up the signal for system initialization 

	__assign_relative_register(&sig_systemInitDone, ME_INIT_SIGNAL);

	// Do some initialization in thread 0. All others simply wait for a previous
	// thread signal 

	if	(ctx() == 0) 
	{

		// Clear the CAM 

		cam_clear();

		// Initialize the Next Neighbor Rings 

		QmNextNeighborRingInit();

#ifdef PACKET_MODE

		// Set the SRAM Channel CSR to ignore cellcount and sop bit in the 
		// buffer handle. This implies that even though we could have chained
		// buffers h/w will enq/deq only one buffer. Therefore we enqueue and
		// dequeue a packet everytime.

		// We are setting one bit that will	indicate to ignore cellcount but 
		// not the eop bit. Therefore all handles that are being enqueue should have 
		// the eop bit set.

		// Read from CSR 

		sramAddress = OFFSET_SRAMCONTROL_CSR +  SRAM_CHANNEL_BASE_ADDRESS ;
		sram_csr_read(&csrReadValue, sramAddress, ctx_swap, &sig_sramCSR) ;
	
		// write back to CSR 

 		csrWriteValue = csrReadValue | (1 << IGNORE_SOP_CELLCOUNT_BIT);
		sram_csr_write(&csrWriteValue, sramAddress, ctx_swap, &sig_sramCSR) ;	

#endif

		// initialize the Q_Array 

		QmQArrayInit();

		// This is the signal that all blocks are to wait on to indicate that system 
		// initialization is done

		wait_for_all(&sig_systemInitDone) ;
	}
	else	
		// All other threads wait for a signal from thread 0 
		wait_for_all(&sig_prevThread) ;

}


/////////////////////////////////////////////////////////////////////////////////////////
//
// main()
//
//	 	Description: 
//
//		Main function which performs queue manager in a infinite loop.
//
////////////////////////////////////////////////////////////////////////////////////////

void main()
{
	

	// Initialize the Queue Manager

	Qm_Init();

	// Call the main routine that loops infinitely

	Qm();

}

/////////////////////////////////////////////////////////////////////////////////////////