// Avalon ST DATA splitter Serial to x2 parallel (on original clock rate)
// even buffered, odd through type (low latency, partly combinational)
// Packet size assumed even (natural number x 2)
// NEW version fot packing signals

module splitter_s2p
#(
	parameter DATAWIDTH = 32
)
(
	input clk,
	input reset_n,

	// Input side (sink:snk) AVST port
	input snk_valid,
	output snk_ready, // combinational
	input [DATAWIDTH+3:0] snk_pack, // snk_error, snk_sop, snk_eop, snk_data,
	
	// I/O for single src side (on half rate clock)
	output src_valid,
	input src_ready,
	output [DATAWIDTH*2+3:0] src_pack //, DATAWIDTH doubled, src_error, src_sop, src_eop, src_data

);
 // packing port signals

	wire [1:0] snk_error;
	wire snk_sop;
	wire snk_eop;
	wire [DATAWIDTH-1:0] snk_data;
	
	wire [1:0] src_error;
	wire src_sop;
	wire src_eop;
	wire [DATAWIDTH*2-1:0] src_data; // DATAWIDTH doubled
	
	// data & stream control capture reg (1stage FIFO)
	reg [1:0] buf0_error;
	reg buf0_sop;
	reg buf0_eop;
	reg [DATAWIDTH-1:0] buf0_data;

	// 1 stage FIFO filled register
	reg	filled_buf0;
	
	// packet check flag
	reg inpack;
	wire errmissop, errmiseop; // detect error missing SOP/EOP
	
	wire write_buf, read_buf; // logic function for FIFO state control

// I/O wire fragment
assign { snk_error, snk_sop, snk_eop, snk_data } = snk_pack;
assign src_pack = { src_error, src_sop, src_eop, src_data };

// transfer control signals

assign src_valid = filled_buf0 & snk_valid ; // even registered, odd valid
assign snk_ready = ( filled_buf0 ) ? src_ready : 1'b1 ; // odd ready is pass through (combinational)

assign write_buf = snk_valid & snk_ready & (~filled_buf0) ;
assign read_buf = src_valid & src_ready;

// flow state controlled by FIFO filled register (and valid/ready handshake signals)
always @(posedge clk or negedge reset_n)
	if( ~reset_n ) filled_buf0 <= 1'b0; // initial empty
	else case( {write_buf, read_buf} )
		2'b01: filled_buf0 <= 1'b0; // read only => empty
		2'b10: filled_buf0 <= 1'b1; // write only => filled
		// default: ; // keep the filled register
			// NO read , NO write => keep status, read and write => keep status (of filled)
	endcase
// end of always

always @(posedge clk or negedge reset_n)
	if( ~reset_n ) { buf0_error, buf0_sop, buf0_eop, buf0_data } <= {(4+DATAWIDTH){1'b0}};
	else if( write_buf )
		{ buf0_error, buf0_sop, buf0_eop, buf0_data } <= { snk_error, snk_sop, snk_eop, snk_data };

always @(posedge clk or negedge reset_n)
	if( ~reset_n ) inpack <= 1'b0;
	else if( snk_valid & snk_ready & snk_sop ) inpack <= 1'b1; // detect valid sop
	else if( snk_valid & snk_ready & snk_eop ) inpack <= 1'b0; // detect valid eop
	// else keep (if sop and eop both asserted, sop is regarded

assign	errmissop = ( {snk_valid, snk_ready, snk_sop, inpack} == 4'b1100 ) ? 1'b1 : 1'b0;
assign	errmiseop = ( {snk_valid, snk_ready, snk_sop, inpack} == 4'b1111 ) ? 1'b1 : 1'b0;

assign	src_error = ( read_buf ) ? 
			( buf0_error | snk_error | 			// error through
				{2{snk_sop}} | {2{buf0_eop}} |  // sop only bufferd, eop only thru (alignment check)
				{1'b0, errmissop} | {errmiseop, 1'b0} ) : // detect missing SOP/EOP
			2'b00 ;
			
assign	src_sop = ( read_buf ) ? buf0_sop : 1'b0 ;
assign	src_eop = ( read_buf ) ? snk_eop : 1'b0 ;
assign	src_data = { snk_data, buf0_data }; // x2 parallel data

endmodule