///////////////////////////////////////////////////////////////////////////////////////////
//                                                                      
//                  I N T E L   P R O P R I E T A R Y                   
//                                                                      
//     COPYRIGHT (c)  2001 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                  
//                                                                      
///////////////////////////////////////////////////////////////////////////////////////////
//
// Functions for Ingress QM message handling 
//
///////////////////////////////////////////////////////////////////////////////////////////
//
//		
//		Change History
// 		--------------
//
// Date			Description											Whom
// ------------------------------------------------------------------------------------
//
// 11/11/01    	Ingress Scheduler 			  					Uday Naik      
//
///////////////////////////////////////////////////////////////////////////////////////////
//
//
// The QM sends messages on a NN ring. Each message is two words 
//
// First word is the enqueue message
//
//						--	Bit 30 	   : Enqueue Transition
//						--  Bits 9..0  : Enqueue Queue Id 
//
// Second word is the dequeue message
//
//						-- Bit 30		: Dequeue Transition
//						-- Bit 29		: Invalid Dequeue 
//						-- Bits 9..0	: Dequeue queue Id 
//
// For each beat, there may be an enqueue transition AND a dequeue transition 
// in the
// worst case. 
//
//
///////////////////////////////////////////////////////////////////////////////////////////

#include <ixp.h>

// header file with constants for algorithm

#include "scheduler.h"

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

// Bit number for detecting enqueue/dequeue transition messages 

#define QM_TRANSITION_BIT           	30

// Bit number for invalid dequeues

#define QM_INVALID_DEQUEUE_BIT			29

///////////////////////////////////////////////////////////////////////////////////////////
//
// Get the queue group number from the Queue Manager Message, bits 5..9
//
///////////////////////////////////////////////////////////////////////////////////////////

#define QmGetGroupNumber(message)	((message >> 5) & 0x1f)

///////////////////////////////////////////////////////////////////////////////////////////
//
// Get the queue number from the Queue Manager Message, bits 0..4
//
///////////////////////////////////////////////////////////////////////////////////////////

#define QmGetQueueNumber(message) (message & 0x1f)

///////////////////////////////////////////////////////////////////////////////////////////
//
// Handle an enq transition message
//
/////////////////////////////////////////////////////////////////////////////////////////////


INLINE static void SchedulerQmHandleEnqueueTransition(uint32_t message)
{

	uint32_t					queueGroupNumber;		// queue group number 	
	uint32_t					queueNumber;			// queue number within a group 

	// declare a pointer to the queue group structure 

	__declspec(local_mem shared aligned(16)) queue_group_t* pQueueGroup;

	// get the queue group number

	queueGroupNumber = QmGetGroupNumber(message);

	// get a pointer to the queue group structure 

	pQueueGroup = &globalQueueGroups[queueGroupNumber];

	// get the queue number

	queueNumber = QmGetQueueNumber(message);

	// set the bit in the empty vector

	pQueueGroup->emptyVector |= (1 << queueNumber);

	// Set the bit in the parent. we only need to do this if it is not already set
	// but it is more expensive to check. So we will just set it
	
	globalRootEmptyVector |= (1 << queueGroupNumber);

}

/////////////////////////////////////////////////////////////////////////////////////////////
//
// Handle Dequeue Transition
//
// 13 instructions in worst case
//
/////////////////////////////////////////////////////////////////////////////////////////////

INLINE void SchedulerQmHandleDequeueTransition(uint32_t message)
{

	uint32_t	queueGroupNumber;	// queue group number 
	uint32_t	queueNumber;		// queue number within a group 

	// declare a pointer to the queue group structure 

	__declspec(local_mem shared aligned(16)) 	queue_group_t* pQueueGroup;

	// get the queue group number

	queueGroupNumber = QmGetGroupNumber(message);

	// get a pointer to the queue group structure 

	pQueueGroup = &globalQueueGroups[queueGroupNumber];

	// get the queue number

	queueNumber = QmGetQueueNumber(message);

	// clear the bit in the empty vector

	pQueueGroup->emptyVector &= ~(1 << queueNumber);

	// if the bit vector is now zero then clear the bit in the parent
	
	if (pQueueGroup->emptyVector == 0) 
		globalRootEmptyVector &= ~(1 << queueGroupNumber);

}
		
///////////////////////////////////////////////////////////////////////////////////////////// 
//
// QM Message Handler. This loops is infinitely running in a thread of its own
//						
// 
/////////////////////////////////////////////////////////////////////////////////////////////

INLINE void SchedulerQmMessageHandler(void)
{

	uint32_t  	message;					// QM  message read from ring

	// wait for a signal from the scheduler thread 

	wait_for_all(&sig_initDone);

	while (1)
	{

		// loop until there is a message on the NN ring

		do 
		{

			// swap out. The first time the thread runs it will swap out immediately

			ctx_swap();

		}  while (inp_state_test(inp_state_nn_empty));

		// Read the message

		message = nn_ring_dequeue_incr();

		// Check if it is an enqueue transition.   

		if (message & (1 << QM_TRANSITION_BIT))
			SchedulerQmHandleEnqueueTransition(message);

		// Read the next word of the message

		message = nn_ring_dequeue_incr();

		// Check if it is a dequeue transition

		if (message & (1 << QM_TRANSITION_BIT)) 
		{

			// handle the dequeue transition

			SchedulerQmHandleDequeueTransition(message);

			// if a dequeue transition happens an invalid dequeue cannot happen
			// so we are done

			continue;

		}
		else  
		{

			// check if there is an invalid dequeue 

			if (message & (1 << QM_INVALID_DEQUEUE_BIT)) 
			{

				// decrement packets scheduled and packets in flight 

				globalPacketsScheduled--;
				globalPacketsInFlight--;

			}

		}  

	}	// end while (1) 

}
	
////////////////////////////////////////////////////////////////////////////////////////////

	