module fifo_pop (
	ren_o,
	popflags,
	out_raddr,
	gcout,
	rst_n,
	rclk,
	ren_in,
	rmode,
	wmode,
	gcin,
	upae
);
	parameter ADDR_WIDTH = 11;
	parameter FIFO_WIDTH = 3'd2;
	parameter DEPTH = 6;
	output wire ren_o;
	output wire [3:0] popflags;
	output reg [ADDR_WIDTH - 1:0] out_raddr;
	output wire [ADDR_WIDTH:0] gcout;
	input rst_n;
	(* clkbuf_sink *)
	input rclk;
	input ren_in;
	input [1:0] rmode;
	input [1:0] wmode;
	input [ADDR_WIDTH:0] gcin;
	input [ADDR_WIDTH - 1:0] upae;
	localparam ADDR_PLUS_ONE = ADDR_WIDTH + 1;
	reg empty;
	reg epo;
	reg pae;
	reg underflow;
	reg e1;
	reg e2;
	reg o1;
	reg o2;
	reg q1;
	reg q2;
	reg [1:0] bwl_sel;
	reg [1:0] gmode;
	reg [ADDR_WIDTH - 1:0] ff_raddr;
	reg [ADDR_WIDTH:0] waddr;
	reg [ADDR_WIDTH:0] raddr;
	reg [ADDR_WIDTH:0] gcout_reg;
	reg [ADDR_WIDTH:0] gcout_next;
	reg [ADDR_WIDTH:0] waddr_next;
	reg [ADDR_WIDTH - 1:0] pae_thresh;
	wire ren_out;
	wire empty_next;
	wire pae_next;
	wire epo_next;
	wire [ADDR_WIDTH - 2:0] gc32out_next;
	wire [ADDR_WIDTH - 1:0] gc16out_next;
	wire [ADDR_WIDTH:0] gc8out_next;
	wire [ADDR_WIDTH:0] raddr_next;
	wire [ADDR_WIDTH - 1:0] ff_raddr_next;
	wire [ADDR_WIDTH:0] tmp;
	wire [ADDR_PLUS_ONE:0] next_count;
	wire [ADDR_PLUS_ONE:0] count;
	wire [ADDR_PLUS_ONE:0] fbytes;
	genvar i;
	assign next_count = waddr - raddr_next;
	assign count = waddr - raddr;
	assign fbytes = 1 << (DEPTH + 5);
	always @(*) pae_thresh = rmode[1] ? upae : (rmode[0] ? upae << 1 : upae << 2);
	assign ren_out = (empty ? 1'b1 : ren_in);
	always @(*)
		case (rmode)
			2'h0: gmode = 2'h0;
			2'h1: gmode = (wmode == 2'h0 ? 2'h0 : 2'h1);
			2'h2: gmode = (wmode == 2'h2 ? 2'h2 : wmode);
			2'h3: gmode = 2'h3;
		endcase
	always @(*) begin
		e1 = 1'b0;
		e2 = 1'b0;
		o1 = 1'b0;
		o2 = 1'b0;
		q1 = next_count < {1'b0, pae_thresh};
		q2 = count < {1'b0, pae_thresh};
		case (rmode)
			2'h0: begin
				e1 = raddr_next[ADDR_WIDTH:2] == waddr_next[ADDR_WIDTH:2];
				e2 = raddr[ADDR_WIDTH:2] == waddr_next[ADDR_WIDTH:2];
				o1 = (raddr_next[ADDR_WIDTH:2] + 1) == waddr_next[ADDR_WIDTH:2];
				o2 = (raddr[ADDR_WIDTH:2] + 1) == waddr_next[ADDR_WIDTH:2];
			end
			2'h1: begin
				e1 = raddr_next[ADDR_WIDTH:1] == waddr_next[ADDR_WIDTH:1];
				e2 = raddr[ADDR_WIDTH:1] == waddr_next[ADDR_WIDTH:1];
				o1 = (raddr_next[ADDR_WIDTH:1] + 1) == waddr_next[ADDR_WIDTH:1];
				o2 = (raddr[ADDR_WIDTH:1] + 1) == waddr_next[ADDR_WIDTH:1];
			end
			2'h2: begin
				e1 = raddr_next[ADDR_WIDTH:0] == waddr_next[ADDR_WIDTH:0];
				e2 = raddr[ADDR_WIDTH:0] == waddr_next[ADDR_WIDTH:0];
				o1 = (raddr_next[ADDR_WIDTH:0] + 1) == waddr_next[ADDR_WIDTH:0];
				o2 = (raddr[ADDR_WIDTH:0] + 1) == waddr_next[11:0];
			end
			2'h3: begin
				e1 = 1'b0;
				e2 = 1'b0;
				o1 = 1'b0;
				o2 = 1'b0;
			end
		endcase
	end
	assign empty_next = (ren_in & !empty ? e1 : e2);
	assign epo_next = (ren_in & !empty ? o1 : o2);
	assign pae_next = (ren_in & !empty ? q1 : q2);
	always @(posedge rclk or negedge rst_n)
		if (~rst_n) begin
			empty <= 1'b1;
			pae <= 1'b1;
			epo <= 1'b0;
		end
		else begin
			empty <= empty_next;
			pae <= pae_next;
			epo <= epo_next;
		end
	assign gc8out_next = (raddr_next >> 1) ^ raddr_next;
	assign gc16out_next = (raddr_next >> 2) ^ (raddr_next >> 1);
	assign gc32out_next = (raddr_next >> 3) ^ (raddr_next >> 2);
	always @(*)
		if (ren_in)
			case (gmode)
				2'h2: gcout_next = gc8out_next;
				2'h1: gcout_next = {1'b0, gc16out_next};
				2'h0: gcout_next = {2'b00, gc32out_next};
				default: gcout_next = 'h0;
			endcase
		else
			gcout_next = 'h0;
	always @(posedge rclk or negedge rst_n)
		if (~rst_n)
			waddr <= 12'h000;
		else
			waddr <= waddr_next;
	always @(posedge rclk or negedge rst_n)
		if (~rst_n) begin
			underflow <= 1'b0;
			bwl_sel <= 2'h0;
			gcout_reg <= 12'h000;
		end
		else if (ren_in) begin
			underflow <= empty;
			if (!empty) begin
				bwl_sel <= raddr_next[1:0];
				gcout_reg <= gcout_next;
			end
		end
	generate
		for (i = 0; i < (ADDR_WIDTH + 1); i = i + 1) begin : genblk1
			assign tmp[i] = ^(gcin >> i);
		end
	endgenerate
	always @(*)
		case (gmode)
			2'h0: waddr_next = {tmp[ADDR_WIDTH - 2:0], 2'b00} & {{ADDR_WIDTH - 1 {1'b1}}, 2'b00};
			2'h1: waddr_next = {tmp[ADDR_WIDTH - 1:0], 1'b0} & {{ADDR_WIDTH {1'b1}}, 1'b0};
			2'h2: waddr_next = {tmp[ADDR_WIDTH:0]} & {ADDR_PLUS_ONE {1'b1}};
			default: waddr_next = {ADDR_PLUS_ONE {1'b0}};
		endcase
	assign ff_raddr_next = ff_raddr + (rmode == 2'h0 ? 'h4 : (rmode == 2'h1 ? 'h2 : 'h1));
	assign raddr_next = raddr + (rmode == 2'h0 ? 'h4 : (rmode == 2'h1 ? 'h2 : 'h1));
	always @(posedge rclk or negedge rst_n)
		if (~rst_n)
			ff_raddr <= 1'sb0;
		else if (empty & ~empty_next)
			ff_raddr <= raddr_next[ADDR_WIDTH - 1:0];
		else if ((ren_in & !empty) & ~empty_next)
			ff_raddr <= ff_raddr_next;
	always @(posedge rclk or negedge rst_n)
		if (~rst_n)
			raddr <= 12'h000;
		else if (ren_in & !empty)
			raddr <= raddr_next;
	always @(*)
		case (FIFO_WIDTH)
			3'h2: out_raddr = {ff_raddr[ADDR_WIDTH - 1:1], bwl_sel[0]};
			3'h4: out_raddr = {ff_raddr[ADDR_WIDTH - 1:2], bwl_sel};
			default: out_raddr = ff_raddr[ADDR_WIDTH - 1:0];
		endcase
	assign ren_o = ren_out;
	assign gcout = gcout_reg;
	assign popflags = {empty, epo, pae, underflow};
endmodule