Skip to content

Instantly share code, notes, and snippets.

@neilzheng
Last active November 29, 2025 05:35
Show Gist options
  • Select an option

  • Save neilzheng/a985fb96bfed084e2ffff720fbb07f66 to your computer and use it in GitHub Desktop.

Select an option

Save neilzheng/a985fb96bfed084e2ffff720fbb07f66 to your computer and use it in GitHub Desktop.
module fifo #(
parameter DATA_WIDTH = 8,
parameter BUFFER_DEPTH = 4, // must greater than 4
parameter ADDR_WIDTH = $clog2(BUFFER_DEPTH)
) (
input logic clk,
input logic rst_n,
// Input side (write port)
input logic i_valid,
input logic [DATA_WIDTH-1:0] i_data,
output logic i_ready, // Registered
// Output side (read port)
output logic o_valid, // Registered
output logic [DATA_WIDTH-1:0] o_data, // Registered
input logic o_ready
);
localparam [ADDR_WIDTH-1:0] PTR_MAX = ADDR_WIDTH'(BUFFER_DEPTH - 1);
// Internal buffer and pointers
logic [DATA_WIDTH-1:0] buffer[BUFFER_DEPTH-1:0];
logic [ADDR_WIDTH:0] count_reg;
logic [ADDR_WIDTH:0] next_count;
logic [ADDR_WIDTH-1:0] read_ptr_reg;
logic [ADDR_WIDTH-1:0] write_ptr_reg;
logic [ADDR_WIDTH-1:0] next_read_ptr;
logic [ADDR_WIDTH-1:0] next_write_ptr;
// Transaction signals
logic write_en;
logic read_en;
logic net_read_en;
logic net_write_en;
logic i_ready_reg;
logic o_valid_reg;
logic [DATA_WIDTH-1:0] o_data_reg;
logic i_ready_next;
logic o_valid_next;
logic [DATA_WIDTH-1:0] o_data_next;
//----------------------------------------------------------------
// Combinational Logic
//----------------------------------------------------------------
assign write_en = i_valid && i_ready;
assign read_en = o_ready && o_valid;
assign net_write_en = write_en & ~read_en;
assign net_read_en = read_en & ~write_en;
// state management
always @* begin
next_count = count_reg;
if (net_read_en) begin
next_count = count_reg - 1;
end
if (net_write_en) begin
next_count = count_reg + 1;
end
next_read_ptr = read_ptr_reg;
next_write_ptr = write_ptr_reg;
if (read_en) begin
next_read_ptr = read_ptr_reg == PTR_MAX ? 0 : read_ptr_reg + 1;
end
if (write_en) begin
next_write_ptr = write_ptr_reg == PTR_MAX ? 0 : write_ptr_reg + 1;
end
end
// output generation
always @* begin
i_ready_next = i_ready_reg;
o_valid_next = o_valid_reg;
o_data_next = buffer[next_read_ptr];
if (next_count == 0) begin
// empty
o_valid_next = 0;
o_data_next = 0;
end else if (next_count == BUFFER_DEPTH) begin
// full
i_ready_next = 0;
end else begin
i_ready_next = 1;
o_valid_next = 1;
if (next_count == (ADDR_WIDTH+1)'(1)
& count_reg != (ADDR_WIDTH+1)'(2)) begin
// almost empty filled
o_data_next = i_data;
end
end
end
//----------------------------------------------------------------
// Sequential Logic
//----------------------------------------------------------------
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
count_reg = 0;
read_ptr_reg = 0;
write_ptr_reg = 0;
i_ready_reg = 1;
o_valid_reg = 0;
o_data_reg = 0;
end else begin
count_reg <= next_count;
read_ptr_reg <= next_read_ptr;
write_ptr_reg <= next_write_ptr;
i_ready_reg <= i_ready_next;
o_valid_reg <= o_valid_next;
o_data_reg <= o_data_next;
if (write_en) begin
buffer[write_ptr_reg] <= i_data;
end
end
end
assign i_ready = i_ready_reg;
assign o_valid = o_valid_reg;
assign o_data = o_data_reg;
endmodule
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment