2021-04-27 16:26:52 -05:00
|
|
|
// FIFO buffer implemented with synchronous dual-port block ram
|
|
|
|
// Reference:
|
|
|
|
// https://embeddedthoughts.com/2016/07/13/fifo-buffer-using-block-ram-on-a-xilinx-spartan-3-fpga/
|
|
|
|
module fifo
|
2021-04-27 23:09:10 -05:00
|
|
|
#( parameter ADDRESS_WIDTH = 4, // number of words in ram
|
|
|
|
DATA_WIDTH = 4 // number of bits in word
|
2021-04-27 16:26:52 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
// IO ports
|
|
|
|
(
|
|
|
|
input wire clk, reset,
|
|
|
|
input wire read, write,
|
|
|
|
input wire [DATA_WIDTH-1:0] write_data,
|
|
|
|
output wire empty, full,
|
|
|
|
output wire [DATA_WIDTH-1:0] read_data
|
|
|
|
);
|
|
|
|
|
|
|
|
// internal signal declarations
|
|
|
|
reg [ADDRESS_WIDTH-1:0] write_address_reg, write_address_next, write_address_after;
|
|
|
|
reg [ADDRESS_WIDTH-1:0] read_address_reg, read_address_next, read_address_after;
|
|
|
|
reg full_reg, empty_reg, full_next, empty_next;
|
|
|
|
wire write_en;
|
|
|
|
|
|
|
|
// write enable is asserted when write input is asserted and FIFO isn't full
|
|
|
|
assign write_en = write & ~full_reg;
|
|
|
|
|
|
|
|
// instantiate synchronous block ram
|
|
|
|
sync_dual_port_ram #(.ADDRESS_WIDTH(ADDRESS_WIDTH), .DATA_WIDTH(DATA_WIDTH)) ram
|
|
|
|
(.clk(clk), .write_en(write_en), .write_address(write_address_reg),
|
|
|
|
.read_address(read_address_reg), .write_data_in(write_data),
|
|
|
|
.write_data_out(), .read_data_out(read_data));
|
|
|
|
|
|
|
|
// register for address pointers, full/empty status
|
|
|
|
always @(posedge clk, posedge reset)
|
|
|
|
if (reset)
|
|
|
|
begin
|
|
|
|
write_address_reg <= 0;
|
|
|
|
read_address_reg <= 0;
|
|
|
|
full_reg <= 1'b0;
|
|
|
|
empty_reg <= 1'b1;
|
|
|
|
end
|
|
|
|
else
|
|
|
|
begin
|
|
|
|
write_address_reg <= write_address_next;
|
|
|
|
read_address_reg <= read_address_next;
|
|
|
|
full_reg <= full_next;
|
|
|
|
empty_reg <= empty_next;
|
|
|
|
end
|
|
|
|
|
|
|
|
// next-state logic for address index values after read/write operations
|
|
|
|
always @*
|
|
|
|
begin
|
|
|
|
write_address_after = write_address_reg + 1;
|
|
|
|
read_address_after = read_address_reg + 1;
|
|
|
|
end
|
|
|
|
|
|
|
|
// next-state logic for address pointers
|
|
|
|
always @*
|
|
|
|
begin
|
|
|
|
// defaults
|
|
|
|
write_address_next = write_address_reg;
|
|
|
|
read_address_next = read_address_reg;
|
|
|
|
full_next = full_reg;
|
|
|
|
empty_next = empty_reg;
|
|
|
|
|
|
|
|
// if read input asserted and FIFO isn't empty
|
|
|
|
if(read && ~empty_reg && ~write)
|
|
|
|
begin
|
|
|
|
read_address_next = read_address_after; // read address moves forward
|
|
|
|
full_next = 1'b0; // FIFO isn't full if a read occured
|
|
|
|
|
|
|
|
if (read_address_after == write_address_reg) // if read address caught up with write address,
|
|
|
|
empty_next = 1'b1; // FIFO is empty
|
|
|
|
end
|
|
|
|
|
|
|
|
// if write input asserted and FIFO isn't full
|
|
|
|
else if(write && ~full_reg && ~read)
|
|
|
|
begin
|
|
|
|
write_address_next = write_address_after; // write address moves forward
|
|
|
|
empty_next = 1'b0; // FIFO isn't empty if write occured
|
|
|
|
|
|
|
|
if (write_address_after == read_address_reg) // if write address caught up with read address
|
|
|
|
full_next = 1'b1; // FIFO is full
|
|
|
|
end
|
|
|
|
|
|
|
|
// if write and read are asserted
|
|
|
|
else if(write && read)
|
|
|
|
begin
|
|
|
|
write_address_next = write_address_after; // write address moves forward
|
|
|
|
read_address_next = read_address_after; // read address moves forward
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
// assign full/empty status to output ports
|
|
|
|
assign full = full_reg;
|
|
|
|
assign empty = empty_reg;
|
2021-04-27 23:09:10 -05:00
|
|
|
endmodule
|