`default_nettype none // Single-clock, registered-output FIFO. No asynchronous reset of data storage. // Full + simultaneous pop/push replaces the removed element in the same cycle. // Empty + simultaneous pop/push accepts only the push (no fall-through). module sync_fifo #( parameter integer DATA_WIDTH = 8, parameter integer DEPTH = 4 ) ( input wire clk, input wire rst_n, input wire wr_en, input wire [DATA_WIDTH-1:0] wr_data, input wire rd_en, output reg [DATA_WIDTH-1:0] rd_data, output wire full, output wire empty, output reg [$clog2(DEPTH+1)-1:0] count ); localparam integer PTR_WIDTH = (DEPTH > 1) ? $clog2(DEPTH) : 1; reg [DATA_WIDTH-1:0] storage [0:DEPTH-1]; reg [PTR_WIDTH-1:0] wr_ptr, rd_ptr; wire take_read = rd_en && !empty; wire take_write = wr_en && (!full || take_read); assign empty = (count == 0); assign full = (count == DEPTH); function automatic [PTR_WIDTH-1:0] advance(input [PTR_WIDTH-1:0] ptr); if (ptr == DEPTH-1) advance = {PTR_WIDTH{1'b0}}; else advance = ptr + 1'b1; endfunction initial begin if (DATA_WIDTH < 1 || DEPTH < 1) $fatal(1, "sync_fifo parameters require DATA_WIDTH >= 1 and DEPTH >= 1"); end always @(posedge clk or negedge rst_n) begin if (!rst_n) begin wr_ptr <= {PTR_WIDTH{1'b0}}; rd_ptr <= {PTR_WIDTH{1'b0}}; count <= 0; rd_data <= {DATA_WIDTH{1'b0}}; end else begin if (take_write) begin storage[wr_ptr] <= wr_data; wr_ptr <= advance(wr_ptr); end if (take_read) begin rd_data <= storage[rd_ptr]; rd_ptr <= advance(rd_ptr); end case ({take_write, take_read}) 2'b10: count <= count + 1'b1; 2'b01: count <= count - 1'b1; default: count <= count; endcase end end endmodule `default_nettype wire