//////////////////////////// FIFO RAM MEMORY ///////////////////////////////////-- | |
// *********************************************************************** | |
// FileName: FIFO_v.v | |
// FPGA: Lattice ECP2-70E | |
// IDE: Lattice Diamond ver 2.0.1 | |
// | |
// HDL IS PROVIDED "AS IS." DIGI-KEY EXPRESSLY DISCLAIMS ANY | |
// WARRANTY OF ANY KIND, WHETHER EXPRESS OR IMPLIED, INCLUDING BUT NOT | |
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A | |
// PARTICULAR PURPOSE, OR NON-INFRINGEMENT. IN NO EVENT SHALL DIGI-KEY | |
// BE LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR CONSEQUENTIAL | |
// DAMAGES, LOST PROFITS OR LOST DATA, HARM TO YOUR EQUIPMENT, COST OF | |
// PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY OR SERVICES, ANY CLAIMS | |
// BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), | |
// ANY CLAIMS FOR INDEMNITY OR CONTRIBUTION, OR OTHER SIMILAR COSTS. | |
// DIGI-KEY ALSO DISCLAIMS ANY LIABILITY FOR PATENT OR COPYRIGHT | |
// INFRINGEMENT. | |
// | |
// Version History | |
// Version 1.0 28/7/2013 Tony Storey | |
// Initial Public Release | |
`timescale 1ns/ 100 ps | |
module fifo #(parameter ADDR_W = 5, DATA_W = 8, BUFF_L = 32, ALMST_F = 7, ALMST_E = 5) // buffer length must be less than or equal to address space as in BUFF_L <or= 2^(ADDR_W)-1 | |
( | |
input wire clk, | |
input wire n_reset, | |
input wire wr_en, | |
input wire [DATA_W -1 : 0] data_in, | |
input wire rd_en, | |
output reg [DATA_W- 1 : 0] data_out, | |
output reg [ADDR_W : 0] data_count, | |
output reg empty, | |
output reg full, | |
output reg almst_empty, | |
output reg almst_full, | |
output reg err | |
); | |
////--------------- internal variables --------------------------------------------------------- | |
reg [DATA_W-1 : 0] mem_array [0 : (2**ADDR_W)-1]; | |
reg [ADDR_W-1 : 0] rd_ptr, wr_ptr; | |
reg [ADDR_W-1 : 0] rd_ptr_nxt, wr_ptr_nxt; | |
reg full_ff, empty_ff; | |
reg full_ff_nxt, empty_ff_nxt; | |
reg almst_f_ff, almst_e_ff; | |
reg almst_f_ff_nxt, almst_e_ff_nxt; | |
reg [ADDR_W : 0] q_reg, q_nxt; | |
reg q_add, q_sub; | |
//// ------------------------------------------------------------------------------------------------ | |
//// Always block to update the states | |
//// ------------------------------------------------------------------------------------------------ | |
always @ (posedge clk) | |
begin : reg_update | |
if(n_reset == 1'b 0) | |
begin | |
rd_ptr <= {(ADDR_W-1){1'b 0}}; | |
wr_ptr <= {(ADDR_W-1){1'b 0}}; | |
full_ff <= 1'b 0; | |
empty_ff <= 1'b 1; | |
almst_f_ff <= 1'b 0; | |
almst_e_ff <= 1'b 1; | |
q_reg <= {(ADDR_W){1'b 0}}; | |
end | |
else | |
begin | |
rd_ptr <= rd_ptr_nxt; | |
wr_ptr <= wr_ptr_nxt; | |
full_ff <= full_ff_nxt; | |
empty_ff <= empty_ff_nxt; | |
almst_f_ff <= almst_f_ff_nxt; | |
almst_e_ff <= almst_e_ff_nxt; | |
q_reg <= q_nxt; | |
end | |
end // end of always | |
//// Control for almost full and almost emptly flags | |
//// ------------------------------------------------------------------------------------------------ | |
always @ ( almst_e_ff, almst_f_ff, q_reg) | |
begin : Wtr_Mrk_Cont | |
almst_e_ff_nxt = almst_e_ff; | |
almst_f_ff_nxt = almst_f_ff; | |
//// check to see if wr_ptr is ALMST_E away from rd_ptr (aka almost empty) | |
if(q_reg < ALMST_E) | |
almst_e_ff_nxt = 1'b 1; | |
else | |
almst_e_ff_nxt = 1'b 0; | |
if(q_reg > BUFF_L-ALMST_F) | |
almst_f_ff_nxt = 1'b 1; | |
else | |
almst_f_ff_nxt = 1'b 0; | |
end // end of always | |
//// Control for read and write pointers and empty/full flip flops | |
always @ (wr_en, rd_en, wr_ptr, rd_ptr, empty_ff, full_ff, q_reg) | |
begin | |
wr_ptr_nxt = wr_ptr ; //// no change to pointers | |
rd_ptr_nxt = rd_ptr; | |
full_ff_nxt = full_ff; | |
empty_ff_nxt = empty_ff; | |
q_add = 1'b 0; | |
q_sub = 1'b 0; | |
////---------- check if fifo is full during a write attempt, after a write increment counter | |
////---------------------------------------------------- | |
if(wr_en == 1'b 1 & rd_en == 1'b 0) | |
begin | |
if(full_ff == 1'b 0) | |
begin | |
if(wr_ptr < BUFF_L-1) | |
begin | |
q_add = 1'b 1; | |
wr_ptr_nxt = wr_ptr + 1; | |
empty_ff_nxt = 1'b 0; | |
end | |
else | |
begin | |
wr_ptr_nxt = {(ADDR_W-1){1'b 0}}; | |
empty_ff_nxt = 1'b 0; | |
end | |
//// check if fifo is full | |
if( (wr_ptr+1 == rd_ptr) || ((wr_ptr == BUFF_L-1) && (rd_ptr == 1'b 0))) | |
full_ff_nxt = 1'b 1; | |
end | |
end | |
////---------- check to see if fifo is empty during a read attempt, after a read decrement counter | |
////--------------------------------------------------------------- | |
if( (wr_en == 1'b 0) && (rd_en == 1'b 1)) | |
begin | |
if(empty_ff == 1'b 0) | |
begin | |
if(rd_ptr < BUFF_L-1 ) | |
begin | |
if(q_reg > 0) | |
q_sub = 1'b 1; | |
else | |
q_sub = 1'b 0; | |
rd_ptr_nxt = rd_ptr + 1; | |
full_ff_nxt = 1'b 0; | |
end | |
else | |
begin | |
rd_ptr_nxt = {(ADDR_W-1){1'b 0}}; | |
full_ff_nxt = 1'b 0; | |
end | |
//// check if fifo is empty | |
if( (rd_ptr + 1 == wr_ptr) || ((rd_ptr == BUFF_L -1) && (wr_ptr == 1'b 0 ))) | |
empty_ff_nxt = 1'b 1; | |
end | |
end | |
//// ----------------------------------------------------------------- | |
if( (wr_en == 1'b 1) && (rd_en == 1'b 1)) | |
begin | |
if(wr_ptr < BUFF_L -1) | |
wr_ptr_nxt = wr_ptr + 1; | |
else | |
wr_ptr_nxt = {(ADDR_W-1){1'b 0}}; | |
if(rd_ptr < BUFF_L -1) | |
rd_ptr_nxt = rd_ptr + 1; | |
else | |
rd_ptr_nxt = {(ADDR_W-1){1'b 0}}; | |
end | |
end // end of always | |
//// Control for memory array writing and reading | |
//// ---------------------------------------------------------------------- | |
always @ (posedge clk) | |
begin : mem_cont | |
if( n_reset == 1'b 0) | |
begin | |
mem_array[rd_ptr] <= {(DATA_W-1){1'b 0}}; | |
data_out <= {(DATA_W-1){1'b 0}}; | |
err <= 1'b 0; | |
end | |
else | |
begin | |
//// if write enable and not full then latch in data and increment wright pointer | |
if( (wr_en == 1'b 1) && (full_ff == 1'b 0) ) | |
begin | |
mem_array[wr_ptr] <= data_in; | |
err <= 1'b 0; | |
end | |
else if( (wr_en == 1'b 1) && (full_ff == 1'b 1)) //// check if full and trying to write | |
err <= 1'b 1; | |
//// if read enable and fifo not empty then latch data out and increment read pointer | |
if( (rd_en == 1'b 1) && (empty_ff == 1'b 0)) | |
begin | |
data_out <= mem_array[rd_ptr]; | |
err <= 1'b 0; | |
end | |
else if( (rd_en == 1'b 1) && (empty_ff == 1'b 1)) | |
err <= 1'b 1; | |
end // end else | |
end // end always | |
//// Combo Counter with Control Flags | |
//// ------------------------------------------------------------------------------------------------ | |
always @ ( q_sub, q_add, q_reg) | |
begin : Counter | |
case( {q_sub , q_add} ) | |
2'b 01 : | |
q_nxt = q_reg + 1; | |
2'b 10 : | |
q_nxt = q_reg - 1; | |
default : | |
q_nxt = q_reg; | |
endcase | |
end // end of always | |
//// Connect internal regs to ouput ports | |
//// ------------------------------------------------------------------------------------------------ | |
always @ (full_ff, empty_ff, almst_e_ff, almst_f_ff, q_reg) | |
begin | |
full = full_ff; | |
empty = empty_ff; | |
almst_empty = almst_e_ff; | |
almst_full = almst_f_ff; | |
data_count = q_reg; | |
end | |
endmodule |