`timescale 1ns / 1ps module fifo_buffer ( input wire wr_clk, input wire [7:0] wr_data, input wire wr_valid, output wire wr_ready, input wire rd_clk, output wire [7:0] rd_data, output wire rd_valid, input wire rd_ready, input wire rst_n, output wire fifo_empty, output wire fifo_full, output wire [7:0] fifo_level ); parameter DEPTH = 512; parameter ADDR_WIDTH = 9; reg [7:0] mem [0:DEPTH-1]; reg [ADDR_WIDTH:0] wr_ptr; reg [ADDR_WIDTH:0] wr_ptr_gray; reg [ADDR_WIDTH:0] wr_ptr_gray_sync1; reg [ADDR_WIDTH:0] wr_ptr_gray_sync2; reg [ADDR_WIDTH:0] rd_ptr; reg [ADDR_WIDTH:0] rd_ptr_gray; reg [ADDR_WIDTH:0] rd_ptr_gray_sync1; reg [ADDR_WIDTH:0] rd_ptr_gray_sync2; wire [ADDR_WIDTH:0] wr_ptr_bin; wire [ADDR_WIDTH:0] rd_ptr_bin; wire [ADDR_WIDTH:0] fifo_count; reg wr_en; reg rd_en; reg [7:0] rd_data_reg; reg rd_valid_reg; assign wr_ready = ~fifo_full; always @(posedge wr_clk or negedge rst_n) begin if (!rst_n) begin wr_ptr <= 0; wr_ptr_gray <= 0; wr_en <= 1'b0; end else begin wr_en <= wr_valid && wr_ready; if (wr_en) begin mem[wr_ptr[ADDR_WIDTH-1:0]] <= wr_data; wr_ptr <= wr_ptr + 1; end wr_ptr_gray <= binary_to_gray(wr_ptr); end end always @(posedge wr_clk or negedge rst_n) begin if (!rst_n) begin rd_ptr_gray_sync1 <= 0; rd_ptr_gray_sync2 <= 0; end else begin rd_ptr_gray_sync1 <= rd_ptr_gray; rd_ptr_gray_sync2 <= rd_ptr_gray_sync1; end end assign rd_data = rd_data_reg; assign rd_valid = rd_valid_reg; always @(posedge rd_clk or negedge rst_n) begin if (!rst_n) begin rd_ptr <= 0; rd_ptr_gray <= 0; rd_en <= 1'b0; rd_data_reg <= 8'h00; rd_valid_reg <= 1'b0; end else begin rd_en <= rd_ready && ~fifo_empty; if (rd_en) begin rd_data_reg <= mem[rd_ptr[ADDR_WIDTH-1:0]]; rd_valid_reg <= 1'b1; rd_ptr <= rd_ptr + 1; end else if (rd_ready) begin rd_valid_reg <= 1'b0; end rd_ptr_gray <= binary_to_gray(rd_ptr); end end always @(posedge rd_clk or negedge rst_n) begin if (!rst_n) begin wr_ptr_gray_sync1 <= 0; wr_ptr_gray_sync2 <= 0; end else begin wr_ptr_gray_sync1 <= wr_ptr_gray; wr_ptr_gray_sync2 <= wr_ptr_gray_sync1; end end assign wr_ptr_bin = gray_to_binary(wr_ptr_gray_sync2); assign rd_ptr_bin = gray_to_binary(rd_ptr_gray_sync2); wire [ADDR_WIDTH:0] fifo_count_wr; assign fifo_count_wr = wr_ptr - gray_to_binary(rd_ptr_gray_sync2); wire [ADDR_WIDTH:0] fifo_count_rd; assign fifo_count_rd = gray_to_binary(wr_ptr_gray_sync2) - rd_ptr; assign fifo_full = (fifo_count_wr >= DEPTH); assign fifo_empty = (fifo_count_rd == 0); reg [7:0] fifo_level_reg; always @(*) begin if (fifo_count_wr > DEPTH) fifo_level_reg = DEPTH; else fifo_level_reg = fifo_count_wr[7:0]; end assign fifo_level = fifo_level_reg; function [ADDR_WIDTH:0] binary_to_gray; input [ADDR_WIDTH:0] binary; begin binary_to_gray = binary ^ (binary >> 1); end endfunction function [ADDR_WIDTH:0] gray_to_binary; input [ADDR_WIDTH:0] gray; reg [ADDR_WIDTH:0] binary; integer i; begin binary[ADDR_WIDTH] = gray[ADDR_WIDTH]; for (i = ADDR_WIDTH-1; i >= 0; i = i - 1) binary[i] = binary[i+1] ^ gray[i]; gray_to_binary = binary; end endfunction integer i; initial begin for (i = 0; i < DEPTH; i = i + 1) mem[i] = 8'h00; end endmodule