module reorder_queue_output #(
	parameter C_PCI_DATA_WIDTH = 9'd128,
	parameter C_NUM_CHNL = 4'd12,
	parameter C_TAG_WIDTH = 5, 						// Number of outstanding requests 
	parameter C_TAG_DW_COUNT_WIDTH = 8,				// Width of max count DWs per packet
	parameter C_DATA_ADDR_STRIDE_WIDTH = 5,			// Width of max num stored data addr positions per tag
	parameter C_DATA_ADDR_WIDTH = 10,				// Width of stored data address
	// Local parameters
	parameter C_PCI_DATA_WORD = C_PCI_DATA_WIDTH/32,
	parameter C_PCI_DATA_WORD_WIDTH = clog2s(C_PCI_DATA_WORD),
	parameter C_PCI_DATA_COUNT_WIDTH = clog2s(C_PCI_DATA_WORD+1),
	parameter C_NUM_TAGS = 2**C_TAG_WIDTH
)
(
	input CLK,														// Clock
	input RST,														// Synchronous reset
	output [C_DATA_ADDR_WIDTH-1:0] DATA_ADDR,						// Address of stored packet data
	input [C_PCI_DATA_WIDTH-1:0] DATA,								// Stored packet data
	input [C_NUM_TAGS-1:0] TAG_FINISHED,							// Bitmap of finished tags
	output [C_NUM_TAGS-1:0] TAG_CLEAR,								// Bitmap of tags to clear
	output [C_TAG_WIDTH-1:0] TAG,									// Tag for which to retrieve packet data
	input [5:0] TAG_MAPPED,											// Mapped tag (i.e. internal tag)
	input [C_TAG_DW_COUNT_WIDTH-1:0] PKT_WORDS,						// Total count of packet payload in DWs
	input PKT_WORDS_LTE1,											// True if total count of packet payload is <= 4 DWs
	input PKT_WORDS_LTE2,											// True if total count of packet payload is <= 8 DWs
	input PKT_DONE,													// Packet done flag
	input PKT_ERR,													// Packet error flag

	output [C_PCI_DATA_WIDTH-1:0] ENG_DATA,							// Engine data 
	output [(C_NUM_CHNL*C_PCI_DATA_COUNT_WIDTH)-1:0] MAIN_DATA_EN,	// Main data enable
	output [C_NUM_CHNL-1:0] MAIN_DONE,								// Main data complete
	output [C_NUM_CHNL-1:0] MAIN_ERR,								// Main data completed with error
	output [(C_NUM_CHNL*C_PCI_DATA_COUNT_WIDTH)-1:0] SG_RX_DATA_EN,	// Scatter gather for RX data enable
	output [C_NUM_CHNL-1:0] SG_RX_DONE,								// Scatter gather for RX data complete
	output [C_NUM_CHNL-1:0] SG_RX_ERR,								// Scatter gather for RX data completed with error
	output [(C_NUM_CHNL*C_PCI_DATA_COUNT_WIDTH)-1:0] SG_TX_DATA_EN,	// Scatter gather for TX data enable
	output [C_NUM_CHNL-1:0] SG_TX_DONE,								// Scatter gather for TX data complete
	output [C_NUM_CHNL-1:0] SG_TX_ERR								// Scatter gather for TX data completed with error
);

`include "common_functions.v"

reg		[1:0]								rState=0;
reg		[C_DATA_ADDR_WIDTH-1:0]				rDataAddr=0;
reg		[C_PCI_DATA_WIDTH-1:0]				rData=0;

reg											rTagFinished=0;
reg		[C_NUM_TAGS-1:0]					rClear=0;

reg		[C_TAG_WIDTH-1:0]					rTag=0;
reg		[C_TAG_WIDTH-1:0]					rTagCurr=0;
wire	[C_TAG_WIDTH-1:0]					wTagNext = rTag + 1'd1;

reg		[5:0]								rShift;
reg											rDone=0;
reg											rDoneLast=0;
reg											rErr=0;
reg											rErrLast=0;
reg		[C_PCI_DATA_COUNT_WIDTH-1:0]		rDE=0;
reg		[C_TAG_DW_COUNT_WIDTH-1:0]			rWords=0;
reg											rLTE2Pkts=0;

reg		[C_PCI_DATA_WIDTH-1:0]				rDataOut={C_PCI_DATA_WIDTH{1'b0}};
reg		[(3*16*C_PCI_DATA_COUNT_WIDTH)-1:0]	rDEOut={3*16*C_PCI_DATA_COUNT_WIDTH{1'd0}};
reg		[(3*16)-1:0]						rDoneOut={3*16{1'd0}};
reg		[(3*16)-1:0]						rErrOut={3*16{1'd0}};


assign DATA_ADDR = rDataAddr;
assign TAG = rTag;
assign TAG_CLEAR = rClear;

assign ENG_DATA = rDataOut;

assign MAIN_DATA_EN = rDEOut[(0*16*C_PCI_DATA_COUNT_WIDTH) +:(C_NUM_CHNL*C_PCI_DATA_COUNT_WIDTH)];
assign MAIN_DONE = rDoneOut[(0*16) +:C_NUM_CHNL];
assign MAIN_ERR = rErrOut[(0*16) +:C_NUM_CHNL];

assign SG_RX_DATA_EN = rDEOut[(1*16*C_PCI_DATA_COUNT_WIDTH) +:(C_NUM_CHNL*C_PCI_DATA_COUNT_WIDTH)];
assign SG_RX_DONE = rDoneOut[(1*16) +:C_NUM_CHNL];
assign SG_RX_ERR = rErrOut[(1*16) +:C_NUM_CHNL];

assign SG_TX_DATA_EN = rDEOut[(2*16*C_PCI_DATA_COUNT_WIDTH) +:(C_NUM_CHNL*C_PCI_DATA_COUNT_WIDTH)];
assign SG_TX_DONE = rDoneOut[(2*16) +:C_NUM_CHNL];
assign SG_TX_ERR = rErrOut[(2*16) +:C_NUM_CHNL];


// Output completed data in increasing tag order, avoid stalls if possible
always @ (posedge CLK) begin
	if (RST) begin
		rState <= #1 0;
		rTag <= #1 0;
		rDataAddr <= #1 0;
		rDone <= #1 0;
		rErr <= #1 0;
		rDE <= #1 0;
		rClear <= #1 0;
		rTagFinished <= #1 0;
	end
	else begin
		rTagFinished <= #1 TAG_FINISHED[rTag];
		case (rState)
		
		2'd0: begin // Request initial data and final info, output nothing
			rDone <= #1 0;
			rErr <= #1 0;
			rDE <= #1 0;
			rClear <= #1 0;
			if (rTagFinished) begin
				rTag <= #1 wTagNext;
				rTagCurr <= #1 rTag;
				rDataAddr <= #1 rDataAddr + 1'd1;
				rState <= #1 2'd2;
			end
			else begin
				rState <= #1 2'd0;
			end
		end

		2'd1: begin // Request initial data and final info, output last data
			rDone <= #1 rDoneLast;
			rErr <= #1 rErrLast;
			rDE <= #1 rWords[C_PCI_DATA_COUNT_WIDTH-1:0];
			rClear <= #1 1<<rTagCurr; // Clear the tag
			if (rTagFinished) begin
				rTag <= #1 wTagNext;
				rTagCurr <= #1 rTag;
				rDataAddr <= #1 rDataAddr + 1'd1;
				rState <= #1 2'd2;
			end
			else begin
				rState <= #1 2'd0;
			end
		end

		2'd2: begin // Initial data now available, output data
			rShift <= #1 TAG_MAPPED;
			rDoneLast <= #1 PKT_DONE;
			rErrLast <= #1 PKT_ERR;
			rWords <= #1 PKT_WORDS - C_PCI_DATA_WORD[C_PCI_DATA_WORD_WIDTH:0];
			rLTE2Pkts <= #1 (PKT_WORDS <= (C_PCI_DATA_WORD*3));
			if (PKT_WORDS_LTE1) begin // Guessed wrong, no addl data, need to reset
				rDone <= #1 PKT_DONE;
				rErr <= #1 PKT_ERR;
				rDE <= #1 PKT_WORDS[C_PCI_DATA_COUNT_WIDTH-1:0];
				rClear <= #1 1<<rTagCurr; // Clear the tag
				rDataAddr <= #1 rTag<<C_DATA_ADDR_STRIDE_WIDTH; // rTag is already on the next
				rState <= #1 2'd0;
			end
			else if (PKT_WORDS_LTE2) begin // Guessed right, end of data, output last and continue
				rDone <= #1 0;
				rErr <= #1 0;
				rDE <= #1 C_PCI_DATA_WORD[C_PCI_DATA_WORD_WIDTH:0];
				rClear <= #1 0;
				rDataAddr <= #1 rTag<<C_DATA_ADDR_STRIDE_WIDTH; // rTag is already on the next
				rState <= #1 2'd1;
			end
			else begin // Guessed right, more data, output it and continue
				rDone <= #1 0;
				rErr <= #1 0;
				rDE <= #1 C_PCI_DATA_WORD[C_PCI_DATA_WORD_WIDTH:0];
				rClear <= #1 0;
				rDataAddr <= #1 rDataAddr + 1'd1;
				rState <= #1 2'd3;
			end
		end

		2'd3: begin // Next data now available, output data
			rDone <= #1 0;
			rErr <= #1 0;
			rDE <= #1 C_PCI_DATA_WORD[C_PCI_DATA_WORD_WIDTH:0];
			rWords <= #1 rWords - C_PCI_DATA_WORD[C_PCI_DATA_WORD_WIDTH:0];
			rLTE2Pkts <= #1 (rWords <= (C_PCI_DATA_WORD*3));
			if (rLTE2Pkts) begin // End of data, output last and continue
				rDataAddr <= #1 rTag<<C_DATA_ADDR_STRIDE_WIDTH; // rTag is already on the next
				rState <= #1 2'd1;
			end
			else begin // More data, output it and continue
				rDataAddr <= #1 rDataAddr + 1'd1;
				rState <= #1 2'd3;
			end
		end

		endcase
	end
end


// Output the data
always @ (posedge CLK) begin
	rData <= #1 DATA;
	rDataOut <= #1 rData;
	if (RST) begin
		rDEOut <= #1 0;
		rDoneOut <= #1 0;
		rErrOut <= #1 0;
	end
	else begin
		rDEOut <= #1 rDE<<(C_PCI_DATA_COUNT_WIDTH*rShift);
		rDoneOut <= #1 (rDone | rErr)<<rShift;
		rErrOut <= #1 rErr<<rShift;
	end
end


endmodule