litex/verilog/generic/asfifo.v

104 lines
2.1 KiB
Verilog

/*
* This file is based on "Asynchronous FIFO" by Alex Claros F.,
* itself based on the article "Asynchronous FIFO in Virtex-II FPGAs"
* by Peter Alfke.
*/
module asfifo #(
parameter data_width = 8,
parameter address_width = 4,
parameter fifo_depth = (1 << address_width)
) (
/* Read port */
output reg [data_width-1:0] data_out,
output reg empty,
input read_en,
input clk_read,
/* Write port */
input [data_width-1:0] data_in,
output reg full,
input write_en,
input clk_write,
/* Asynchronous reset */
input rst
);
reg [data_width-1:0] mem[fifo_depth-1:0];
wire [address_width-1:0] write_index, read_index;
wire equal_addresses;
wire write_en_safe, read_en_safe;
wire set_status, clear_status;
reg status;
wire preset_full, preset_empty;
reg [data_width-1:0] data_out0;
always @(posedge clk_read) begin
data_out0 <= mem[read_index];
data_out <= data_out0;
end
always @(posedge clk_write) begin
if(write_en & !full)
mem[write_index] <= data_in;
end
assign write_en_safe = write_en & ~full;
assign read_en_safe = read_en & ~empty;
asfifo_graycounter #(
.width(address_width)
) counter_write (
.gray_count(write_index),
.ce(write_en_safe),
.rst(rst),
.clk(clk_write)
);
asfifo_graycounter #(
.width(address_width)
) counter_read (
.gray_count(read_index),
.ce(read_en_safe),
.rst(rst),
.clk(clk_read)
);
assign equal_addresses = (write_index == read_index);
assign set_status = (write_index[address_width-2] ~^ read_index[address_width-1]) &
(write_index[address_width-1] ^ read_index[address_width-2]);
assign clear_status = ((write_index[address_width-2] ^ read_index[address_width-1]) &
(write_index[address_width-1] ~^ read_index[address_width-2]))
| rst;
always @(posedge clear_status, posedge set_status) begin
if(clear_status)
status <= 1'b0;
else
status <= 1'b1;
end
assign preset_full = status & equal_addresses;
always @(posedge clk_write, posedge preset_full) begin
if(preset_full)
full <= 1'b1;
else
full <= 1'b0;
end
assign preset_empty = ~status & equal_addresses;
always @(posedge clk_read, posedge preset_empty) begin
if(preset_empty)
empty <= 1'b1;
else
empty <= 1'b0;
end
endmodule