/* 
 * Multi-processor Packet Counting
 * Single-threaded receive
 * Chapter 5 Example code, with modified main()
 * Run this code on microprocessor 0
 */

#include "ixp.h"
#include "CSR.h"
#include "buffer.h"

#define QWS_IN_RFIFO 8
/* 
 * get_mpacket: waits for an mpacket to be available on the given port
 *              then transfers it into the given RFIFO
 */
rcv_cntl_reg_t get_mpacket(unsigned int port_num,
                           unsigned int rfifo_num) {
	unsigned int rcv_ready_bits;
	rcv_req_reg_t rcv_request;
	rcv_cntl_reg_t rcv_control;

	/* Wait for data on the requested port */	
	do {
		csr_read((void *)&rcv_ready_bits, rcv_rdy_lo, 1, ctx_swap);
	} while ( !(rcv_ready_bits & (1 << port_num)) );
	
	/* Build and issue the receive request */
	rcv_request.reg_val     = 0;
	rcv_request.fields.rp   = port_num;
	rcv_request.fields.tid  = ctx();
	rcv_request.fields.e1   = rfifo_num;
	csr_write(rcv_request.reg_val, rcv_req, no_signal);

	/* Wait for the data to get pulled into the RFIFO */
	ctx_wait(start_receive);

	/* Get information about the mpacket */
	csr_read((void *)&rcv_control.reg_val, rcv_cntl, 1, ctx_swap);
	return rcv_control;
}


/* Reassembly state */
typedef struct _rcv_state_t {
	unsigned int discard		: 1; /* Entire packet is bad */
	unsigned int mpkt_count		: 6; /* # mpackets received */
	buffer_handle_t	handle;
} rcv_state_t;


/* 
 * transfer_mpacket_to_buffer: wrapper around sdram_r_fifo_read
 *                             moves the given RFIFO data into 
 *                             SDRAM at the given address
 */
void transfer_mpacket_to_buffer(__declspec(sdram) void *buf_ptr,
								unsigned int rfifo_num) {
	fifo_read_write_ind_t rfifo_read_params = { 0 };

 	rfifo_read_params.qword_addr = rfifo_num * QWS_IN_RFIFO;
	rfifo_read_params.ov_ref_count = 1;
	rfifo_read_params.ref_count = QWS_IN_RFIFO - 1;
	sdram_r_fifo_read_ind(
		buf_ptr,
		4, /* Overridden */
		rfifo_read_params,
		optimize_mem,
		ctx_swap);
}


/*
 * receive_packet: Properly reassembles an entire packet for
 *                 the given port using the given RIFO
 */
buffer_handle_t receive_packet(unsigned int port_num,
							   unsigned int rfifo_num,
							   unsigned int *pkt_length) {
	rcv_state_t rcv_state = { 0 };
	rcv_cntl_reg_t rcv_control;
	__declspec(sdram) void *buf_data_ptr;

	
	do {
		rcv_control = get_mpacket(port_num, rfifo_num);
		/* Check that the packet was properly received */
		if ( rcv_control.fields.rf || rcv_control.fields.rerr ) {
			rcv_state.discard = 1;
			if ( rcv_state.handle != BUFFER_NULL ) {
				buffer_free(rcv_state.handle);
				rcv_state.handle = BUFFER_NULL;
			}
			continue;
		}

		if ( rcv_control.fields.sop ) {
			/* Allocate the buffer handle */
			if ((rcv_state.handle = buffer_allocate()) == BUFFER_NULL) {
				/* No room for packet */
				rcv_state.discard = 1;
				continue;
			}
			
			/* Transfer the mpacket into the buffer */
			buf_data_ptr = buffer_get_data_ptr(rcv_state.handle); 
			transfer_mpacket_to_buffer(buf_data_ptr, rfifo_num);

			/* Update the state */
			rcv_state.discard = 0;
			rcv_state.mpkt_count = 1;
		} else {
			/* Non-SOP */
			if ( rcv_state.discard ) {
				continue;
			}
			/* Transfer the mpacket into the buffer */
			buf_data_ptr = (__declspec(sdram) void *)
			               (((unsigned int)buffer_get_data_ptr(rcv_state.handle)) +
						     (rcv_state.mpkt_count * 64 / sizeof(long long)));
			transfer_mpacket_to_buffer(buf_data_ptr, rfifo_num);
			/* Update the state */
			rcv_state.mpkt_count++;
		}

		if ( rcv_control.fields.eop ) {
			*pkt_length = ((rcv_state.mpkt_count - 1) * 64) + 
						   rcv_control.fields.valid_bytes + 1;
		   	return rcv_state.handle;
		}
			
	} while ( 1 );
}


/*
 * Receive and count packets on a given port
 */
void main(void) {
	int i;

	buffer_handle_t buf_handle;
	unsigned int cur_pkt_len;
	__declspec(sram_write_reg) unsigned int mask;
	__declspec(sram_read_reg) unsigned int temp;
	volatile __declspec(sram) unsigned int * pktadr;

	volatile __declspec(scratch) unsigned int * ready;
	__declspec(sram_read_reg) unsigned int data;
	__declspec(sram_write_reg) unsigned int newdata;

	ready = (__declspec(scratch) unsigned int *) 0x0;
	pktadr = (__declspec(sram) unsigned int *) 0x50;
	*pktadr = BUFFER_NULL; // intializing
	mask = 1;

	/* Initialize our freelist */
	buffer_free(BUFFER_NULL);
	buffer_free(BUFFER_NULL);
    for (i=0; i < 10; ++i) {
    	buffer_free(SRAM_BASE + i*SRAM_ENTRY_SIZE);
	}

	*ready = 1;

    while (1) {
		buf_handle = receive_packet(0, 5, &cur_pkt_len);
		do
		{
			scratch_test_and_clear_bits(&temp, &mask, ready, ctx_swap);		
		}
		while (!temp);
		sram_read_lock(&data, pktadr, 1, queue_default, ctx_swap);
		newdata = buf_handle;
		sram_write_unlock(&newdata, pktadr, 1, queue_default, ctx_swap);
		csr_fast_write(inter_thd_sig, 4); // send signal to thread on microprocessor 1
	}
}


void exit(unsigned int code) {
	/* empty */
}