module fifo_push (
	pushflags,
	gcout,
	ff_waddr,
	rst_n,
	wclk,
	wen,
	rmode,
	wmode,
	gcin,
	upaf
);
	parameter ADDR_WIDTH = 11;
	parameter DEPTH = 6;
	output wire [3:0] pushflags;
	output wire [ADDR_WIDTH:0] gcout;
	output wire [ADDR_WIDTH - 1:0] ff_waddr;
	input rst_n;
	(* clkbuf_sink *)
	input wclk;
	input wen;
	input [1:0] rmode;
	input [1:0] wmode;
	input [ADDR_WIDTH:0] gcin;
	input [ADDR_WIDTH - 1:0] upaf;
	localparam ADDR_PLUS_ONE = ADDR_WIDTH + 1;
	reg full_next;
	reg full;
	reg paf_next;
	reg paf;
	reg fmo;
	reg fmo_next;
	reg overflow;
	reg p1;
	reg p2;
	reg f1;
	reg f2;
	reg q1;
	reg q2;
	reg [1:0] gmode;
	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] raddr_next;
	reg [ADDR_WIDTH - 1:0] paf_thresh;
	wire overflow_next;
	wire [ADDR_WIDTH:0] waddr_next;
	wire [ADDR_WIDTH:0] gc8out_next;
	wire [ADDR_WIDTH - 1:0] gc16out_next;
	wire [ADDR_WIDTH - 2:0] gc32out_next;
	wire [ADDR_WIDTH:0] tmp;
	wire [ADDR_WIDTH:0] next_count;
	wire [ADDR_WIDTH:0] count;
	wire [ADDR_WIDTH:0] fbytes;
	genvar i;
	assign next_count = fbytes - (waddr_next >= raddr_next ? waddr_next - raddr_next : (~raddr_next + waddr_next) + 1);
	assign count = fbytes - (waddr >= raddr ? waddr - raddr : (~raddr + waddr) + 1);
	assign fbytes = 1 << (DEPTH + 5);
	always @(*) begin
		paf_thresh = wmode[1] ? upaf : (wmode[0] ? upaf << 1 : upaf << 2);
	end
	always @(*)
		case (wmode)
			2'h0, 2'h1, 2'h2: begin
				full_next = (wen ? f1 : f2);
				fmo_next = (wen ? p1 : p2);
				paf_next = (wen ? q1 : q2);
			end
			default: begin
				full_next = 1'b0;
				fmo_next = 1'b0;
				paf_next = 1'b0;
			end
		endcase
	always @(*) begin : PUSH_FULL_FLAGS
		f1 = 1'b0;
		f2 = 1'b0;
		p1 = 1'b0;
		p2 = 1'b0;
		q1 = next_count < {1'b0, paf_thresh};
		q2 = count < {1'b0, paf_thresh};
		case (wmode)
			2'h0:
				case (DEPTH)
					3'h6: begin
						f1 = {~waddr_next[11], waddr_next[10:2]} == raddr_next[11:2];
						f2 = {~waddr[11], waddr[10:2]} == raddr_next[11:2];
						p1 = ((waddr_next[10:2] + 1) & 9'h1ff) == raddr_next[10:2];
						p2 = ((waddr[10:2] + 1) & 9'h1ff) == raddr_next[10:2];
					end
					3'h5: begin
						f1 = {~waddr_next[10], waddr_next[9:2]} == raddr_next[10:2];
						f2 = {~waddr[10], waddr[9:2]} == raddr_next[10:2];
						p1 = ((waddr_next[9:2] + 1) & 8'hff) == raddr_next[9:2];
						p2 = ((waddr[9:2] + 1) & 8'hff) == raddr_next[9:2];
					end
					3'h4: begin
						f1 = {~waddr_next[9], waddr_next[8:2]} == raddr_next[9:2];
						f2 = {~waddr[9], waddr[8:2]} == raddr_next[9:2];
						p1 = ((waddr_next[8:2] + 1) & 7'h7f) == raddr_next[8:2];
						p2 = ((waddr[8:2] + 1) & 7'h7f) == raddr_next[8:2];
					end
					3'h3: begin
						f1 = {~waddr_next[8], waddr_next[7:2]} == raddr_next[8:2];
						f2 = {~waddr[8], waddr[7:2]} == raddr_next[8:2];
						p1 = ((waddr_next[7:2] + 1) & 6'h3f) == raddr_next[7:2];
						p2 = ((waddr[7:2] + 1) & 6'h3f) == raddr_next[7:2];
					end
					3'h2: begin
						f1 = {~waddr_next[7], waddr_next[6:2]} == raddr_next[7:2];
						f2 = {~waddr[7], waddr[6:2]} == raddr_next[7:2];
						p1 = ((waddr_next[6:2] + 1) & 5'h1f) == raddr_next[6:2];
						p2 = ((waddr[6:2] + 1) & 5'h1f) == raddr_next[6:2];
					end
					3'h1: begin
						f1 = {~waddr_next[6], waddr_next[5:2]} == raddr_next[6:2];
						f2 = {~waddr[6], waddr[5:2]} == raddr_next[6:2];
						p1 = ((waddr_next[5:2] + 1) & 4'hf) == raddr_next[5:2];
						p2 = ((waddr[5:2] + 1) & 4'hf) == raddr_next[5:2];
					end
					3'h0: begin
						f1 = {~waddr_next[5], waddr_next[4:2]} == raddr_next[5:2];
						f2 = {~waddr[5], waddr[4:2]} == raddr_next[5:2];
						p1 = ((waddr_next[4:2] + 1) & 3'h7) == raddr_next[4:2];
						p2 = ((waddr[4:2] + 1) & 3'h7) == raddr_next[4:2];
					end
					3'h7: begin
						f1 = {~waddr_next[ADDR_WIDTH], waddr_next[ADDR_WIDTH - 1:2]} == raddr_next[ADDR_WIDTH:2];
						f2 = {~waddr[ADDR_WIDTH], waddr[ADDR_WIDTH - 1:2]} == raddr_next[ADDR_WIDTH:2];
						p1 = ((waddr_next[ADDR_WIDTH - 1:2] + 1) & {ADDR_WIDTH - 2 {1'b1}}) == raddr_next[ADDR_WIDTH - 1:2];
						p2 = ((waddr[ADDR_WIDTH - 1:2] + 1) & {ADDR_WIDTH - 2 {1'b1}}) == raddr_next[ADDR_WIDTH - 1:2];
					end
				endcase
			2'h1:
				case (DEPTH)
					3'h6: begin
						f1 = {~waddr_next[11], waddr_next[10:1]} == raddr_next[11:1];
						f2 = {~waddr[11], waddr[10:1]} == raddr_next[11:1];
						p1 = ((waddr_next[10:1] + 1) & 10'h3ff) == raddr_next[10:1];
						p2 = ((waddr[10:1] + 1) & 10'h3ff) == raddr_next[10:1];
					end
					3'h5: begin
						f1 = {~waddr_next[10], waddr_next[9:1]} == raddr_next[10:1];
						f2 = {~waddr[10], waddr[9:1]} == raddr_next[10:1];
						p1 = ((waddr_next[9:1] + 1) & 9'h1ff) == raddr_next[9:1];
						p2 = ((waddr[9:1] + 1) & 9'h1ff) == raddr_next[9:1];
					end
					3'h4: begin
						f1 = {~waddr_next[9], waddr_next[8:1]} == raddr_next[9:1];
						f2 = {~waddr[9], waddr[8:1]} == raddr_next[9:1];
						p1 = ((waddr_next[8:1] + 1) & 8'hff) == raddr_next[8:1];
						p2 = ((waddr[8:1] + 1) & 8'hff) == raddr_next[8:1];
					end
					3'h3: begin
						f1 = {~waddr_next[8], waddr_next[7:1]} == raddr_next[8:1];
						f2 = {~waddr[8], waddr[7:1]} == raddr_next[8:1];
						p1 = ((waddr_next[7:1] + 1) & 7'h7f) == raddr_next[7:1];
						p2 = ((waddr[7:1] + 1) & 7'h7f) == raddr_next[7:1];
					end
					3'h2: begin
						f1 = {~waddr_next[7], waddr_next[6:1]} == raddr_next[7:1];
						f2 = {~waddr[7], waddr[6:1]} == raddr_next[7:1];
						p1 = ((waddr_next[6:1] + 1) & 6'h3f) == raddr_next[6:1];
						p2 = ((waddr[6:1] + 1) & 6'h3f) == raddr_next[6:1];
					end
					3'h1: begin
						f1 = {~waddr_next[6], waddr_next[5:1]} == raddr_next[6:1];
						f2 = {~waddr[6], waddr[5:1]} == raddr_next[6:1];
						p1 = ((waddr_next[5:1] + 1) & 5'h1f) == raddr_next[5:1];
						p2 = ((waddr[5:1] + 1) & 5'h1f) == raddr_next[5:1];
					end
					3'h0: begin
						f1 = {~waddr_next[5], waddr_next[4:1]} == raddr_next[5:1];
						f2 = {~waddr[5], waddr[4:1]} == raddr_next[5:1];
						p1 = ((waddr_next[4:1] + 1) & 4'hf) == raddr_next[4:1];
						p2 = ((waddr[4:1] + 1) & 4'hf) == raddr_next[4:1];
					end
					3'h7: begin
						f1 = {~waddr_next[ADDR_WIDTH], waddr_next[ADDR_WIDTH - 1:1]} == raddr_next[ADDR_WIDTH:1];
						f2 = {~waddr[ADDR_WIDTH], waddr[ADDR_WIDTH - 1:1]} == raddr_next[ADDR_WIDTH:1];
						p1 = ((waddr_next[ADDR_WIDTH - 1:1] + 1) & {ADDR_WIDTH - 1 {1'b1}}) == raddr_next[ADDR_WIDTH - 1:1];
						p2 = ((waddr[ADDR_WIDTH - 1:1] + 1) & {ADDR_WIDTH - 1 {1'b1}}) == raddr_next[ADDR_WIDTH - 1:1];
					end
				endcase
			2'h2:
				case (DEPTH)
					3'h6: begin
						f1 = {~waddr_next[11], waddr_next[10:0]} == raddr_next[11:0];
						f2 = {~waddr[11], waddr[10:0]} == raddr_next[11:0];
						p1 = ((waddr_next[10:0] + 1) & 11'h7ff) == raddr_next[10:0];
						p2 = ((waddr[10:0] + 1) & 11'h7ff) == raddr_next[10:0];
					end
					3'h5: begin
						f1 = {~waddr_next[10], waddr_next[9:0]} == raddr_next[10:0];
						f2 = {~waddr[10], waddr[9:0]} == raddr_next[10:0];
						p1 = ((waddr_next[9:0] + 1) & 10'h3ff) == raddr_next[9:0];
						p2 = ((waddr[9:0] + 1) & 10'h3ff) == raddr_next[9:0];
					end
					3'h4: begin
						f1 = {~waddr_next[9], waddr_next[8:0]} == raddr_next[9:0];
						f2 = {~waddr[9], waddr[8:0]} == raddr_next[9:0];
						p1 = ((waddr_next[8:0] + 1) & 9'h1ff) == raddr_next[8:0];
						p2 = ((waddr[8:0] + 1) & 9'h1ff) == raddr_next[8:0];
					end
					3'h3: begin
						f1 = {~waddr_next[8], waddr_next[7:0]} == raddr_next[8:0];
						f2 = {~waddr[8], waddr[7:0]} == raddr_next[8:0];
						p1 = ((waddr_next[7:0] + 1) & 8'hff) == raddr_next[7:0];
						p2 = ((waddr[7:0] + 1) & 8'hff) == raddr_next[7:0];
					end
					3'h2: begin
						f1 = {~waddr_next[7], waddr_next[6:0]} == raddr_next[7:0];
						f2 = {~waddr[7], waddr[6:0]} == raddr_next[7:0];
						p1 = ((waddr_next[6:0] + 1) & 7'h7f) == raddr_next[6:0];
						p2 = ((waddr[6:0] + 1) & 7'h7f) == raddr_next[6:0];
					end
					3'h1: begin
						f1 = {~waddr_next[6], waddr_next[5:0]} == raddr_next[6:0];
						f2 = {~waddr[6], waddr[5:0]} == raddr_next[6:0];
						p1 = ((waddr_next[5:0] + 1) & 6'h3f) == raddr_next[5:0];
						p2 = ((waddr[5:0] + 1) & 6'h3f) == raddr_next[5:0];
					end
					3'h0: begin
						f1 = {~waddr_next[5], waddr_next[4:0]} == raddr_next[5:0];
						f2 = {~waddr[5], waddr[4:0]} == raddr_next[5:0];
						p1 = ((waddr_next[4:0] + 1) & 5'h1f) == raddr_next[4:0];
						p2 = ((waddr[4:0] + 1) & 5'h1f) == raddr_next[4:0];
					end
					3'h7: begin
						f1 = {~waddr_next[ADDR_WIDTH], waddr_next[ADDR_WIDTH - 1:0]} == raddr_next[ADDR_WIDTH:0];
						f2 = {~waddr[ADDR_WIDTH], waddr[ADDR_WIDTH - 1:0]} == raddr_next[ADDR_WIDTH:0];
						p1 = ((waddr_next[ADDR_WIDTH - 1:0] + 1) & {ADDR_WIDTH {1'b1}}) == raddr_next[ADDR_WIDTH - 1:0];
						p2 = ((waddr[ADDR_WIDTH - 1:0] + 1) & {ADDR_WIDTH {1'b1}}) == raddr_next[ADDR_WIDTH - 1:0];
					end
				endcase
			2'h3: begin
				f1 = 1'b0;
				f2 = 1'b0;
				p1 = 1'b0;
				p2 = 1'b0;
			end
		endcase
	end
	always @(*)
		case (wmode)
			2'h0: gmode = 2'h0;
			2'h1: gmode = (rmode == 2'h0 ? 2'h0 : 2'h1);
			2'h2: gmode = (rmode == 2'h2 ? 2'h2 : rmode);
			2'h3: gmode = 2'h3;
		endcase
	assign gc8out_next = (waddr_next >> 1) ^ waddr_next;
	assign gc16out_next = (waddr_next >> 2) ^ (waddr_next >> 1);
	assign gc32out_next = (waddr_next >> 3) ^ (waddr_next >> 2);
	always @(*)
		if (wen)
			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 = {ADDR_PLUS_ONE {1'b0}};
			endcase
		else
			gcout_next = {ADDR_PLUS_ONE {1'b0}};
	always @(posedge wclk or negedge rst_n)
		if (~rst_n) begin
			full <= 1'b0;
			fmo <= 1'b0;
			paf <= 1'b0;
			raddr <= {ADDR_PLUS_ONE {1'b0}};
		end
		else begin
			full <= full_next;
			fmo <= fmo_next;
			paf <= paf_next;
			case (gmode)
				0: raddr <= raddr_next & {{ADDR_WIDTH - 1 {1'b1}}, 2'b00};
				1: raddr <= raddr_next & {{ADDR_WIDTH {1'b1}}, 1'b0};
				2: raddr <= raddr_next & {ADDR_WIDTH + 1 {1'b1}};
				3: raddr <= 12'h000;
			endcase
		end
	assign overflow_next = full & wen;
	always @(posedge wclk or negedge rst_n)
		if (~rst_n)
			overflow <= 1'b0;
		else if (wen == 1'b1)
			overflow <= overflow_next;
	always @(posedge wclk or negedge rst_n)
		if (~rst_n) begin
			waddr <= {ADDR_WIDTH + 1 {1'b0}};
			gcout_reg <= {ADDR_WIDTH + 1 {1'b0}};
		end
		else if (wen == 1'b1) begin
			waddr <= waddr_next;
			gcout_reg <= gcout_next;
		end
	assign gcout = gcout_reg;
	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: raddr_next = {tmp[ADDR_WIDTH - 2:0], 2'b00} & {{ADDR_WIDTH - 1 {1'b1}}, 2'b00};
			2'h1: raddr_next = {tmp[ADDR_WIDTH - 1:0], 1'b0} & {{ADDR_WIDTH {1'b1}}, 1'b0};
			2'h2: raddr_next = {tmp[ADDR_WIDTH:0]} & {ADDR_WIDTH + 1 {1'b1}};
			default: raddr_next = {ADDR_WIDTH + 1 {1'b0}};
		endcase
	assign ff_waddr = waddr[ADDR_WIDTH - 1:0];
	assign pushflags = {full, fmo, paf, overflow};
	assign waddr_next = waddr + (wmode == 2'h0 ? 'h4 : (wmode == 2'h1 ? 'h2 : 'h1));
endmodule