module tx_engine_formatter_32 #(
	parameter C_PCI_DATA_WIDTH = 9'd32,
	// Local parameters
	parameter C_TRAFFIC_CLASS = 3'b0,
	parameter C_RELAXED_ORDER = 1'b0,
	parameter C_NO_SNOOP = 1'b0
)
(
	input CLK,
	input RST,
	input [15:0] CONFIG_COMPLETER_ID,

	input VALID,		 					// Are input parameters valid?
	input WNR,		 						// Is a write request, not a read?
	input [7:0] TAG,		 				// External tag
	input [3:0] CHNL,		 				// Internal tag (just channel portion)
	input [61:0] ADDR,		 				// Request address
	input ADDR_64,		 					// Request address is 64 bit
	input [9:0] LEN,		 				// Request length
	input LEN_ONE,		 					// Request length equals 1
	input [C_PCI_DATA_WIDTH-1:0] WR_DATA,	// Request data, timed to arrive accordingly

	output [C_PCI_DATA_WIDTH-1:0] OUT_DATA,	// Formatted PCI packet data
	output OUT_DATA_WEN 					// Write enable for formatted packet data
);

reg		[2:0]						rState=`S_TXENGFMTR32_IDLE, _rState=`S_TXENGFMTR32_IDLE;
reg		[61:0]						rAddr=62'd0, _rAddr=62'd0;
reg									rAddr64=0, _rAddr64=0;
reg		[C_PCI_DATA_WIDTH-1:0]		rData={C_PCI_DATA_WIDTH{1'd0}}, _rData={C_PCI_DATA_WIDTH{1'd0}};
reg		[C_PCI_DATA_WIDTH-1:0]		rPrevData={C_PCI_DATA_WIDTH{1'd0}}, _rPrevData={C_PCI_DATA_WIDTH{1'd0}};
reg									rDataWen=0, _rDataWen=0;
reg		[9:0]						rLen=0, _rLen=0;
reg									rLenEQ1=0, _rLenEQ1=0;
reg									rWNR=0, _rWNR=0;
reg		[7:0]						rTag=0, _rTag=0;
reg									rInitDone=0, _rInitDone=0;
reg									rDone=0, _rDone=0;


assign OUT_DATA = rData;
assign OUT_DATA_WEN = rDataWen;


// Format read and write requests into PCIe packets.
wire [31:0] wData = ({rPrevData, WR_DATA}>>(32*rAddr64));
always @ (posedge CLK) begin
	rState <= #1 (RST ? `S_TXENGFMTR32_IDLE : _rState);
	rDataWen <= #1 (RST ? 1'd0 : _rDataWen);
	rData <= #1 _rData;
	rLen <= #1 _rLen;
	rAddr <= #1 _rAddr;
	rAddr64 <= #1 _rAddr64;
	rWNR <= #1 _rWNR;
	rTag <= #1 _rTag;
	rLenEQ1 <= #1 _rLenEQ1;
	rInitDone <= #1 _rInitDone;
	rDone <= #1 _rDone;
	rPrevData <= #1 _rPrevData;
end

always @ (*) begin
	_rState = rState;
	_rLen = rLen;
	_rData = rData;
	_rDataWen = rDataWen;
	_rAddr64 = rAddr64;
	_rAddr = rAddr;
	_rWNR = rWNR;
	_rTag = rTag;
	_rLenEQ1 = rLenEQ1;
	_rInitDone = rInitDone;
	_rDone = rDone;
	
	_rPrevData = WR_DATA;
	
	case (rState) 

	`S_TXENGFMTR32_IDLE : begin
		_rLen = LEN;
		_rAddr64 = ADDR_64;
		_rAddr = ADDR;
		_rWNR = WNR;
		_rTag = TAG;
		_rLenEQ1 = LEN_ONE;
		_rData = {1'b0, {WNR, ADDR_64, 5'd0}, 1'b0, C_TRAFFIC_CLASS, CHNL, 1'b0, 1'b0, // Use the reserved 4 bits before traffic class to hide the internal tag  
					C_RELAXED_ORDER, C_NO_SNOOP, 2'b0, LEN};
		_rDataWen = VALID;
		_rState = (VALID ? `S_TXENGFMTR32_HDR_0 : `S_TXENGFMTR32_IDLE);
	end

	`S_TXENGFMTR32_HDR_0 : begin
		_rData = {CONFIG_COMPLETER_ID[15:3], 3'b0, rTag,
					(rLenEQ1 ? 4'b0 : 4'b1111), 4'b1111};
		_rInitDone = (!rAddr64 & !rWNR);
		_rState = (rAddr64 ? `S_TXENGFMTR32_HDR_1 : `S_TXENGFMTR32_HDR_2);
	end

	`S_TXENGFMTR32_HDR_1 : begin
		_rData = rAddr[61:30];
		_rInitDone = !rWNR;
		_rState = `S_TXENGFMTR32_HDR_2;
	end

	`S_TXENGFMTR32_HDR_2 : begin // FIFO data should be available now (if it's a write)
		_rData = {rAddr[29:0], 2'b00};
		_rDone = rLenEQ1;
		_rState = (rInitDone ? `S_TXENGFMTR32_IDLE : `S_TXENGFMTR32_WR);
	end

	`S_TXENGFMTR32_WR : begin 
		_rLen = rLen - 1'd1;
		_rData = wData;
		_rDone = (rLen == 2'd2);
		_rState = (rDone ? `S_TXENGFMTR32_IDLE : `S_TXENGFMTR32_WR);
	end

	default : begin
		_rState = `S_TXENGFMTR32_IDLE;
	end

	endcase
end

endmodule