module depar_do_deparsing #(
	parameter	C_AXIS_DATA_WIDTH = 512,
	parameter	C_AXIS_TUSER_WIDTH = 128,
	parameter	C_PKT_VEC_WIDTH = (6+4+2)*8*8+256,
	parameter	DEPARSER_MOD_ID = 3'b101,
	parameter	C_VLANID_WIDTH = 12
)
(
	input													clk,
	input													aresetn,

	// phv
	input [C_PKT_VEC_WIDTH-1:0]								phv_fifo_out,
	input													phv_fifo_empty,
	output reg												phv_fifo_rd_en,

	//
	input [C_VLANID_WIDTH-1:0]								vlan_id,
	input													vlan_fifo_empty,
	output reg												vlan_fifo_rd_en,
	// 
	input [C_AXIS_DATA_WIDTH-1:0]							fst_half_fifo_tdata,
	input [C_AXIS_TUSER_WIDTH-1:0]							fst_half_fifo_tuser,
	input [C_AXIS_DATA_WIDTH/8-1:0]							fst_half_fifo_tkeep,
	input													fst_half_fifo_tlast,
	input													fst_half_fifo_empty,
	output reg												fst_half_fifo_rd_en,
	// 
	input [C_AXIS_DATA_WIDTH-1:0]							snd_half_fifo_tdata,
	input [C_AXIS_TUSER_WIDTH-1:0]							snd_half_fifo_tuser,
	input [C_AXIS_DATA_WIDTH/8-1:0]							snd_half_fifo_tkeep,
	input													snd_half_fifo_tlast,
	input													snd_half_fifo_empty,
	output reg												snd_half_fifo_rd_en,
	//
	input [C_AXIS_DATA_WIDTH-1:0]							pkt_fifo_tdata,
	input [C_AXIS_TUSER_WIDTH-1:0]							pkt_fifo_tuser,
	input [C_AXIS_DATA_WIDTH/8-1:0]							pkt_fifo_tkeep,
	input													pkt_fifo_tlast,
	input													pkt_fifo_empty,
	output reg												pkt_fifo_rd_en,

	// output
	output reg [C_AXIS_DATA_WIDTH-1:0]						depar_out_tdata,
	output reg [C_AXIS_DATA_WIDTH/8-1:0]					depar_out_tkeep,
	output reg [C_AXIS_TUSER_WIDTH-1:0]						depar_out_tuser,
	output reg												depar_out_tvalid,
	output reg												depar_out_tlast,
	input													depar_out_tready,

	// control path
	input [C_AXIS_DATA_WIDTH-1:0]							ctrl_s_axis_tdata,
	input [C_AXIS_TUSER_WIDTH-1:0]							ctrl_s_axis_tuser,
	input [C_AXIS_DATA_WIDTH/8-1:0]							ctrl_s_axis_tkeep,
	input													ctrl_s_axis_tvalid,
	input													ctrl_s_axis_tlast
);

integer i;

reg [C_AXIS_DATA_WIDTH-1:0]						depar_out_tdata_next;
reg [C_AXIS_DATA_WIDTH/8-1:0]					depar_out_tkeep_next;
reg [C_AXIS_TUSER_WIDTH-1:0]					depar_out_tuser_next;
reg												depar_out_tlast_next;
reg												depar_out_tvalid_next;

wire [159:0] bram_out;
wire [6:0] parse_action_ind [0:9];
wire [9:0] parse_action_ind_10b [0:9];


wire [15:0] parse_action [0:9];		// we have 10 parse action
assign parse_action[9] = bram_out[0+:16];
assign parse_action[8] = bram_out[16+:16];
assign parse_action[7] = bram_out[32+:16];
assign parse_action[6] = bram_out[48+:16];
assign parse_action[5] = bram_out[64+:16];
assign parse_action[4] = bram_out[80+:16];
assign parse_action[3] = bram_out[96+:16];
assign parse_action[2] = bram_out[112+:16];
assign parse_action[1] = bram_out[128+:16];
assign parse_action[0] = bram_out[144+:16];

assign parse_action_ind[0] = parse_action[0][12:6];
assign parse_action_ind[1] = parse_action[1][12:6];
assign parse_action_ind[2] = parse_action[2][12:6];
assign parse_action_ind[3] = parse_action[3][12:6];
assign parse_action_ind[4] = parse_action[4][12:6];
assign parse_action_ind[5] = parse_action[5][12:6];
assign parse_action_ind[6] = parse_action[6][12:6];
assign parse_action_ind[7] = parse_action[7][12:6];
assign parse_action_ind[8] = parse_action[8][12:6];
assign parse_action_ind[9] = parse_action[9][12:6];

assign parse_action_ind_10b[0] = parse_action_ind[0];
assign parse_action_ind_10b[1] = parse_action_ind[1];
assign parse_action_ind_10b[2] = parse_action_ind[2];
assign parse_action_ind_10b[3] = parse_action_ind[3];
assign parse_action_ind_10b[4] = parse_action_ind[4];
assign parse_action_ind_10b[5] = parse_action_ind[5];
assign parse_action_ind_10b[6] = parse_action_ind[6];
assign parse_action_ind_10b[7] = parse_action_ind[7];
assign parse_action_ind_10b[8] = parse_action_ind[8];
assign parse_action_ind_10b[9] = parse_action_ind[9];


reg	[9:0]					sub_depar_act_valid;

wire [47:0]					sub_depar_val_out_swapped [0:9];
wire [47:0]					sub_depar_val_out [0:9];
wire [1:0]					sub_depar_val_out_type [0:9];
wire [9:0]					sub_depar_val_out_valid;


reg [47:0]					sub_depar_val_out_d1 [0:9];
reg [1:0]					sub_depar_val_out_type_d1 [0:9];
reg [9:0]					sub_depar_val_out_valid_d1;

always @(posedge clk) begin
	if (~aresetn) begin
		for (i=0; i<10; i=i+1) begin
			sub_depar_val_out_d1[i] <= 0;
			sub_depar_val_out_type_d1[i] <= 0;
		end
		sub_depar_val_out_valid_d1 <= 0;
	end
	else begin
		for (i=0; i<10; i=i+1) begin
			sub_depar_val_out_d1[i] <= sub_depar_val_out[i];
			sub_depar_val_out_type_d1[i] <= sub_depar_val_out_type[i];
		end
		sub_depar_val_out_valid_d1 <= sub_depar_val_out_valid;
	end
end

`SWAP_BYTE_ORDER2(0)
`SWAP_BYTE_ORDER2(1)
`SWAP_BYTE_ORDER2(2)
`SWAP_BYTE_ORDER2(3)
`SWAP_BYTE_ORDER2(4)
`SWAP_BYTE_ORDER2(5)
`SWAP_BYTE_ORDER2(6)
`SWAP_BYTE_ORDER2(7)
`SWAP_BYTE_ORDER2(8)
`SWAP_BYTE_ORDER2(9)

wire discard_signal;
assign discard_signal = phv_fifo_out[128];

localparam		IDLE=0,
				WAIT_1CYCLE_RAM=1,
				START_SUB_DEPARSE=2,
				FINISH_SUB_DEPARSER_0=3,
				FINISH_SUB_DEPARSER_1=4,
				FINISH_SUB_DEPARSER_2=5,
				FLUSH_PKT_0=6,
				FLUSH_PKT_1=7,
				FLUSH_PKT_2=8,
				FLUSH_PKT_3=9,
				FLUSH_PKT=10,
				DROP_PKT=11,
				DROP_PKT_REMAINING=12,
				EMPTY_1=13,
				EMPTY_2=14;


reg [C_AXIS_DATA_WIDTH-1:0]			pkts_tdata_stored_1p, pkts_tdata_stored_2p;
reg [C_AXIS_TUSER_WIDTH-1:0]		pkts_tuser_stored_1p, pkts_tuser_stored_2p;
reg [(C_AXIS_DATA_WIDTH/8)-1:0]		pkts_tkeep_stored_1p, pkts_tkeep_stored_2p;
reg									pkts_tlast_stored_1p, pkts_tlast_stored_2p;
reg [C_AXIS_DATA_WIDTH-1:0]			pkts_tdata_stored_1p_next, pkts_tdata_stored_2p_next;
reg [C_AXIS_TUSER_WIDTH-1:0]		pkts_tuser_stored_1p_next, pkts_tuser_stored_2p_next;
reg [(C_AXIS_DATA_WIDTH/8)-1:0]		pkts_tkeep_stored_1p_next, pkts_tkeep_stored_2p_next;
reg									pkts_tlast_stored_1p_next, pkts_tlast_stored_2p_next;

reg [4:0] state, state_next;

always @(*) begin

	phv_fifo_rd_en = 0;
	vlan_fifo_rd_en = 0;
	fst_half_fifo_rd_en = 0;
	snd_half_fifo_rd_en = 0;
	pkt_fifo_rd_en = 0;
	// output
	depar_out_tdata_next = depar_out_tdata;
	depar_out_tuser_next = depar_out_tuser;
	depar_out_tkeep_next = depar_out_tkeep;
	depar_out_tlast_next = depar_out_tlast;
	depar_out_tvalid_next = 0;

	sub_depar_act_valid = 10'b0;

	state_next = state;
	//
	pkts_tdata_stored_1p_next = pkts_tdata_stored_1p;
	pkts_tuser_stored_1p_next = pkts_tuser_stored_1p;
	pkts_tkeep_stored_1p_next = pkts_tkeep_stored_1p;
	pkts_tlast_stored_1p_next = pkts_tlast_stored_1p;
	//
	pkts_tdata_stored_2p_next = pkts_tdata_stored_2p;
	pkts_tuser_stored_2p_next = pkts_tuser_stored_2p;
	pkts_tkeep_stored_2p_next = pkts_tkeep_stored_2p;
	pkts_tlast_stored_2p_next = pkts_tlast_stored_2p;

	case (state) 
		IDLE: begin
			if (!vlan_fifo_empty) begin
				state_next = WAIT_1CYCLE_RAM;
			end
		end
		WAIT_1CYCLE_RAM: begin
			state_next = START_SUB_DEPARSE;
		end
		START_SUB_DEPARSE: begin
			if (!fst_half_fifo_empty 
					&& !snd_half_fifo_empty 
					&& !phv_fifo_empty) begin

				if (discard_signal == 1) begin
					state_next = DROP_PKT;
					phv_fifo_rd_en = 1;
				end
				else begin
					sub_depar_act_valid = 10'b1111111111;

					state_next = EMPTY_2;
					pkts_tdata_stored_1p_next = fst_half_fifo_tdata;
					pkts_tuser_stored_1p_next = phv_fifo_out[0+:128];
					pkts_tkeep_stored_1p_next = fst_half_fifo_tkeep;
					pkts_tlast_stored_1p_next = fst_half_fifo_tlast;
					//
					pkts_tdata_stored_2p_next = snd_half_fifo_tdata;
					pkts_tuser_stored_2p_next = snd_half_fifo_tuser;
					pkts_tkeep_stored_2p_next = snd_half_fifo_tkeep;
					pkts_tlast_stored_2p_next = snd_half_fifo_tlast;
				end
			end
		end
		EMPTY_1: begin
			// sub_depar_act_valid = 10'b1111111111;
			state_next = EMPTY_2;
		end
		EMPTY_2: begin
			state_next = FINISH_SUB_DEPARSER_0;
		end
		FINISH_SUB_DEPARSER_0: begin
			`SUB_DEPARSE_1P(0)
			`SUB_DEPARSE_1P(1)
			`SUB_DEPARSE_2P(5)

			state_next = FINISH_SUB_DEPARSER_1;
		end
		FINISH_SUB_DEPARSER_1: begin
			`SUB_DEPARSE_1P(2)
			`SUB_DEPARSE_2P(6)
			`SUB_DEPARSE_2P(7)

			state_next = FINISH_SUB_DEPARSER_2;
		end
		FINISH_SUB_DEPARSER_2: begin
			`SUB_DEPARSE_1P(3)
			`SUB_DEPARSE_1P(4)
			`SUB_DEPARSE_2P(8)
			`SUB_DEPARSE_2P(9)

			state_next = FLUSH_PKT_0;
		end
		FLUSH_PKT_0: begin
			phv_fifo_rd_en = 1;
			vlan_fifo_rd_en = 1;
			fst_half_fifo_rd_en = 1;
			snd_half_fifo_rd_en = 1;

			depar_out_tdata_next = pkts_tdata_stored_1p;
			depar_out_tuser_next = pkts_tuser_stored_1p;
			depar_out_tkeep_next = pkts_tkeep_stored_1p;
			depar_out_tlast_next = pkts_tlast_stored_1p;

			if (depar_out_tready) begin
				depar_out_tvalid_next = 1;
				if (pkts_tlast_stored_1p) begin
					state_next = IDLE;
				end
				else begin
					state_next = FLUSH_PKT_1;
				end
			end
		end
		FLUSH_PKT_1: begin
			depar_out_tdata_next = pkts_tdata_stored_2p;
			depar_out_tuser_next = pkts_tuser_stored_2p;
			depar_out_tkeep_next = pkts_tkeep_stored_2p;
			depar_out_tlast_next = pkts_tlast_stored_2p;

			if (depar_out_tready) begin
				depar_out_tvalid_next = 1;
				if (pkts_tlast_stored_2p) begin
					state_next = IDLE;
				end
				else begin
					state_next = FLUSH_PKT;
				end
			end
		end
		FLUSH_PKT: begin
			if (!pkt_fifo_empty) begin
				depar_out_tdata_next = pkt_fifo_tdata;
				depar_out_tuser_next =  pkt_fifo_tuser;
				depar_out_tkeep_next =  pkt_fifo_tkeep;
				depar_out_tlast_next =  pkt_fifo_tlast;
				if (depar_out_tready) begin
					pkt_fifo_rd_en = 1;
					depar_out_tvalid_next = 1;
					if (pkt_fifo_tlast) begin
						state_next = IDLE;
					end
				end
			end
		end
		DROP_PKT: begin
			if (fst_half_fifo_tlast==1 
				|| snd_half_fifo_tlast==1) begin
				fst_half_fifo_rd_en = 1;
				snd_half_fifo_rd_en = 1;
				vlan_fifo_rd_en = 1;

				state_next = IDLE;
			end
			else begin
				fst_half_fifo_rd_en = 1;
				snd_half_fifo_rd_en = 1;
				vlan_fifo_rd_en = 1;

				state_next = DROP_PKT_REMAINING;
			end
		end
		DROP_PKT_REMAINING: begin
			pkt_fifo_rd_en = 1;
			if (pkt_fifo_tlast) begin
				state_next = IDLE;
			end
		end
	endcase
end

always @(posedge clk) begin
	if (~aresetn) begin
		state <= IDLE;
		//
		pkts_tdata_stored_1p <= 0;
		pkts_tuser_stored_1p <= 0;
		pkts_tkeep_stored_1p <= 0;
		pkts_tlast_stored_1p <= 0;
		//
		pkts_tdata_stored_2p <= 0;
		pkts_tuser_stored_2p <= 0;
		pkts_tkeep_stored_2p <= 0;
		pkts_tlast_stored_2p <= 0;
		//
		depar_out_tdata <= 0;
		depar_out_tkeep <= 0;
		depar_out_tuser <= 0;
		depar_out_tlast <= 0;
		depar_out_tvalid <= 0;
	end
	else begin
		state <= state_next;
		//
		pkts_tdata_stored_1p <= pkts_tdata_stored_1p_next;
		pkts_tuser_stored_1p <= pkts_tuser_stored_1p_next;
		pkts_tkeep_stored_1p <= pkts_tkeep_stored_1p_next;
		pkts_tlast_stored_1p <= pkts_tlast_stored_1p_next;
		//
		pkts_tdata_stored_2p <= pkts_tdata_stored_2p_next;
		pkts_tuser_stored_2p <= pkts_tuser_stored_2p_next;
		pkts_tkeep_stored_2p <= pkts_tkeep_stored_2p_next;
		pkts_tlast_stored_2p <= pkts_tlast_stored_2p_next;
		//
		depar_out_tdata <= depar_out_tdata_next;
		depar_out_tkeep <= depar_out_tkeep_next;
		depar_out_tuser <= depar_out_tuser_next;
		depar_out_tlast <= depar_out_tlast_next;
		depar_out_tvalid <= depar_out_tvalid_next;
	end
end


//===================== sub deparser
generate
	genvar index;
	for (index=0; index<10; index=index+1) 
	begin: sub_op
		sub_deparser #(
			.C_PKT_VEC_WIDTH(),
			.C_PARSE_ACT_LEN()
		)
		sub_deparser (
			.clk				(clk),
			.aresetn			(aresetn),
			.parse_act_valid	(sub_depar_act_valid[index]),
			.parse_act			(parse_action[index][5:0]),
			.phv_in				(phv_fifo_out),
			.val_out_valid		(sub_depar_val_out_valid[index]),
			.val_out			(sub_depar_val_out[index]),
			.val_out_type		(sub_depar_val_out_type[index])
		);
	end
endgenerate


/*================Control Path====================*/
wire [7:0]          mod_id; //module ID
wire [15:0]         control_flag; //dst udp port num
reg  [7:0]          c_index; //table index(addr)
reg                 c_wr_en; //enable table write(wen)

reg [159:0]         entry_reg;

reg [2:0]           c_state;

localparam IDLE_C = 1,
           WRITE_C = 2,
           SU_WRITE_C = 3;

assign mod_id = ctrl_s_axis_tdata[368+:8];
assign control_flag = ctrl_s_axis_tdata[335:320];

//LE to BE switching
wire[C_AXIS_DATA_WIDTH-1:0] ctrl_s_axis_tdata_swapped;
assign ctrl_s_axis_tdata_swapped = {ctrl_s_axis_tdata[0+:8],
									ctrl_s_axis_tdata[8+:8],
									ctrl_s_axis_tdata[16+:8],
									ctrl_s_axis_tdata[24+:8],
									ctrl_s_axis_tdata[32+:8],
									ctrl_s_axis_tdata[40+:8],
									ctrl_s_axis_tdata[48+:8],
									ctrl_s_axis_tdata[56+:8],
									ctrl_s_axis_tdata[64+:8],
									ctrl_s_axis_tdata[72+:8],
									ctrl_s_axis_tdata[80+:8],
									ctrl_s_axis_tdata[88+:8],
									ctrl_s_axis_tdata[96+:8],
									ctrl_s_axis_tdata[104+:8],
									ctrl_s_axis_tdata[112+:8],
									ctrl_s_axis_tdata[120+:8],
									ctrl_s_axis_tdata[128+:8],
									ctrl_s_axis_tdata[136+:8],
									ctrl_s_axis_tdata[144+:8],
									ctrl_s_axis_tdata[152+:8],
									ctrl_s_axis_tdata[160+:8],
									ctrl_s_axis_tdata[168+:8],
									ctrl_s_axis_tdata[176+:8],
									ctrl_s_axis_tdata[184+:8],
									ctrl_s_axis_tdata[192+:8],
									ctrl_s_axis_tdata[200+:8],
									ctrl_s_axis_tdata[208+:8],
									ctrl_s_axis_tdata[216+:8],
									ctrl_s_axis_tdata[224+:8],
									ctrl_s_axis_tdata[232+:8],
									ctrl_s_axis_tdata[240+:8],
									ctrl_s_axis_tdata[248+:8],
                                    ctrl_s_axis_tdata[256+:8],
                                    ctrl_s_axis_tdata[264+:8],
                                    ctrl_s_axis_tdata[272+:8],
                                    ctrl_s_axis_tdata[280+:8],
                                    ctrl_s_axis_tdata[288+:8],
                                    ctrl_s_axis_tdata[296+:8],
                                    ctrl_s_axis_tdata[304+:8],
                                    ctrl_s_axis_tdata[312+:8],
                                    ctrl_s_axis_tdata[320+:8],
                                    ctrl_s_axis_tdata[328+:8],
                                    ctrl_s_axis_tdata[336+:8],
                                    ctrl_s_axis_tdata[344+:8],
                                    ctrl_s_axis_tdata[352+:8],
                                    ctrl_s_axis_tdata[360+:8],
                                    ctrl_s_axis_tdata[368+:8],
                                    ctrl_s_axis_tdata[376+:8],
                                    ctrl_s_axis_tdata[384+:8],
                                    ctrl_s_axis_tdata[392+:8],
                                    ctrl_s_axis_tdata[400+:8],
                                    ctrl_s_axis_tdata[408+:8],
                                    ctrl_s_axis_tdata[416+:8],
                                    ctrl_s_axis_tdata[424+:8],
                                    ctrl_s_axis_tdata[432+:8],
                                    ctrl_s_axis_tdata[440+:8],
                                    ctrl_s_axis_tdata[448+:8],
                                    ctrl_s_axis_tdata[456+:8],
                                    ctrl_s_axis_tdata[464+:8],
                                    ctrl_s_axis_tdata[472+:8],
                                    ctrl_s_axis_tdata[480+:8],
                                    ctrl_s_axis_tdata[488+:8],
                                    ctrl_s_axis_tdata[496+:8],
                                    ctrl_s_axis_tdata[504+:8]
                                };

always @(posedge clk or negedge aresetn) begin
    if(~aresetn) begin
        c_wr_en <= 1'b0;
        c_index <= 4'b0;
        entry_reg <= 0;

        c_state <= IDLE_C;
    end
    else begin
        case(c_state)
            IDLE_C: begin
                if(ctrl_s_axis_tvalid && mod_id[2:0] == DEPARSER_MOD_ID && control_flag == 16'hf2f1)begin
                    c_wr_en <= 1'b0;
                    c_index <= ctrl_s_axis_tdata[384+:8];

                    c_state <= WRITE_C;

                end
                else begin
                    c_wr_en <= 1'b0;
                    c_index <= 4'b0; 
                    entry_reg <= 0;

                    c_state <= IDLE_C;
                end
            end  
//support full table flush
            WRITE_C: begin
                if(ctrl_s_axis_tvalid) begin
                    c_wr_en <= 1'b1;
                    entry_reg <= ctrl_s_axis_tdata_swapped[511 -: 160];
                    if(ctrl_s_axis_tlast) begin
                        c_state <= IDLE_C;
                    end
                    else begin
                        c_state <= SU_WRITE_C;
                    end
                end
                else begin
                    c_wr_en <= 1'b0;
                end
            end

            SU_WRITE_C: begin
                if(ctrl_s_axis_tvalid) begin
                    entry_reg <= ctrl_s_axis_tdata_swapped[511 -: 160];
                    c_wr_en <= 1'b1;
                    c_index <= c_index + 1'b1;
                    if(ctrl_s_axis_tlast) begin
                        c_state <= IDLE_C;
                    end
                    else begin
                        c_state <= SU_WRITE_C;
                    end
                end
                else begin
                    c_wr_en <= 1'b0;
                end
            end
        endcase

    end
end


parse_act_ram_ip
parse_act_ram
(
	// write port
	.clka		(clk),
	.addra		(c_index[4:0]),
	.dina		(entry_reg),
	.ena		(1'b1),
	.wea		(c_wr_en),

	//
	.clkb		(clk),
	.addrb		(vlan_id[8:4]), // TODO: note that we may change due to little or big endian
	.doutb		(bram_out),
	.enb		(1'b1) // always set to 1
);


endmodule