module axis_arbiter #(
	parameter CHAN_NUM = 4,
	parameter DATA_WIDTH = 8,
	parameter DEST_INDEX = 0,
	parameter DEST_WIDTH = 4,
	parameter USER_WIDTH = 2
)
(
	input wire aclk,
	input wire aresetn,
	input wire [DATA_WIDTH*CHAN_NUM-1:0]s_axis_tdata,
	input wire [USER_WIDTH*CHAN_NUM-1:0]s_axis_tuser,
	input wire [CHAN_NUM*DEST_WIDTH-1:0]s_axis_tdest,
	input wire [CHAN_NUM-1:0]s_axis_tvalid,
	output wire [CHAN_NUM-1:0]s_axis_tready,
	input wire [CHAN_NUM-1:0]s_axis_tlast,
	output wire [DATA_WIDTH-1:0]m_axis_tdata,
	output wire [USER_WIDTH-1:0]m_axis_tuser,
	output wire m_axis_tvalid,
	input wire m_axis_tready,
	output wire m_axis_tlast
);

localparam PTR_WIDTH = $clog2(CHAN_NUM);

reg [PTR_WIDTH-1:0]curr;
reg curr_wrap;
reg [PTR_WIDTH-1:0]next;
reg act;
wire [CHAN_NUM-1:0]axis_tvalid;

integer i,j;
genvar g;

generate for (g = 0; g < CHAN_NUM; g = g + 1) begin
	if (DEST_WIDTH > 0) begin
		wire dest_valid = (s_axis_tdest[DEST_WIDTH*(g+1)-1-:DEST_WIDTH] == DEST_INDEX);
		assign s_axis_tready[g] = (dest_valid ? (curr == g ? m_axis_tready : 1'b0) : 1'b0) & aresetn;
		assign axis_tvalid[g] = (dest_valid ? s_axis_tvalid[g] : 1'b0) & aresetn;
	end else begin
		assign s_axis_tready[g] = (curr == g ? m_axis_tready : 1'b0) & aresetn;
		assign axis_tvalid[g] = s_axis_tvalid[g] & aresetn;
	end
end endgenerate

assign m_axis_tdata = axis_tvalid[curr] ? s_axis_tdata[DATA_WIDTH*(curr+1)-1-:DATA_WIDTH] : 0;
assign m_axis_tuser = axis_tvalid[curr] ? s_axis_tuser[USER_WIDTH*(curr+1)-1-:USER_WIDTH] : 0;
assign m_axis_tvalid = aresetn ? axis_tvalid[curr] : 1'b0;
assign m_axis_tlast = s_axis_tlast[curr] & axis_tvalid[curr];

always @(posedge aclk) begin
	if (aresetn == 1'b0) begin
		act <= 1'b0;
		next <= 0;
	end else begin
		if (act == 1'b0) begin
			if (m_axis_tvalid) begin
				if (m_axis_tready & m_axis_tlast) begin
					act <= 1'b0;
					if (curr == (CHAN_NUM - 1)) begin
						next <= 0;
					end else begin
						next <= curr + 1;
					end
				end else begin
					act <= 1'b1;
					next <= curr;
				end
			end
		end else begin
			if (m_axis_tvalid & m_axis_tready & m_axis_tlast) begin
				act <= 1'b0;
				if (curr == (CHAN_NUM - 1)) begin
					next <= 0;
				end else begin
					next <= curr + 1;
				end
			end
		end
	end
end

always @(*) begin
	curr = next;
	curr_wrap = 1'b1;
	if (act == 1'b0) begin
		for (i = 0; i < CHAN_NUM; i = i + 1) begin
			if (i == next) begin
				for (j = (CHAN_NUM - 1); j >= i; j = j - 1) begin
					if (axis_tvalid[j]) begin
						curr = j;
						curr_wrap = 1'b0;
					end
				end
				if (curr_wrap == 1'b1) begin
					for (j = i - 1; j >= 0; j = j - 1) begin
						if (axis_tvalid[j]) begin
							curr = j;
						end
					end
				end
			end
		end
	end
end

endmodule