I have decided to implement a FIFO buffer in verilog (for fun). Here is my primary prototype you can say :

  • It will consist of a register bank or memory.
  • Each register will be of size N and there will be M such registers / shift registers.
  • The registers support serial write and serial read only ie serial-in serial-out topology.
  • Two registers 'read' and 'write' will be used which act as enable signals.
  • A register 'writeLoc' is used which tells where data was last written to. Its size should be M. It is decremented with each write.
  • A register 'readLoc' is used which tells where data was last read from. Its size should be M. It is incremented with each read.

  • If readloc = writeloc,

    • if last operation was read, then buffer is empty.
    • if last operation was write, then buffer is full.
  • A register 'lastOp' is used to signify last operation. It is set whenever last write was sucessful. It is reset whenever last read was sucessful.

  • A register 'isEmpty' is used to signify that buffer is empty. It is set whenever buffer is empty.
  • A register 'isFull' is used to signify that buffer is full. It is set whenever buffer is full.

  • Experimental feature 1 : A register 'lock' can be used to prevent reading data while writing and vice versa. 'lock' is set whenever 'write' is set ie whenever data is being written. 'lock' is reset whenever no data is being written. The reader can read from buffer when 'lock' is reset. Likewise, the writer can write to buffer whenever lock is set.

However I have a few concerns :

1) Firstly, have I left out some critical component ? And is there some flaw so far ? Keep in mind it is meant to be a simple design, with 'essential' functionality.

2) My first concern is whether buffer full and empty conditions are correct or not. I have tried my best, but I'm not sure.

3) Can isEmpty and isFull registers be dispensed with ?

4) At the receiver end, the data can come any time. I feel that if I implement the lock feature, then if at the time when data is coming and lock is held by reader, data will be lost. On the other hand, lock is also necessary to prevent reading while writing and vice versa. Can you point me in the right direction as to how to overcome this problem ?

(Apologies if this question does not belong here. Please be kind enough to suggest appropriate place).

  • \$\begingroup\$ Regarding question #4, why can't you have simultaneous reading and writing? \$\endgroup\$
    – Dave Tweed
    Jun 21, 2015 at 20:14
  • 2
    \$\begingroup\$ The whole point of a FIFO is that one side can write to it as needed (if there is space) and the other side can read from it whenever there is data available. There is nothing needed to stop read and write occurring at the same time with one exception... You need to prevent data being written when isFull is asserted, and prevent data being read when isEmpty is asserted. With those two flags you can be sure that a given memory address will never be read and written at the same time without prevent writes to the FIFO as a whole during read. \$\endgroup\$ Jun 21, 2015 at 20:54
  • \$\begingroup\$ Is this for an asynchronous or synchronous FIFO? \$\endgroup\$ Jun 21, 2015 at 21:21
  • \$\begingroup\$ @TomCarpenter . Yes I think you are correct. Otherwise, have I missed something here ? And is the empty and full logic correct ? \$\endgroup\$ Jun 22, 2015 at 1:27
  • \$\begingroup\$ @alex.forencich . Not sure what you mean by that. But its going to be a part of UART, that much I can say. \$\endgroup\$ Jun 22, 2015 at 1:28

1 Answer 1


Your read pointer and write pointer both need to travel in the same direction. Generally they are incremented, but decrementing also works, so long as you do the same for both. One method for full/empty detection is to use M+1 bit registers for the read and write pointer. If all M+1 bits match, then the FIFO is empty. However, if all bits match except the MSB, then the FIFO is full. Presuming you use a true dual port memory for storage (i.e. an FPGA block RAM), no locking is necessary as dual port RAM supports simultaneous reading and writing. No need for any isEmpty and isFull registers or a lastOp register if you can glean all of that information from the read and write pointers with simple combinatorial logic. However, you will need logic to prevent the write pointer from being changed when the FIFO is full or the read pointer from being changed when the FIFO is empty.

  • \$\begingroup\$ Sorry for late reply. I was trying to understand the (M + 1) bit scheme which took time. Consider a buffer with 8 locations labelled from 000 (being the leftmost register) and 111 being the right most register. Then lets say if we start writing from loc 010 ( ie loc 3) and continue all the way upto loc 001 ( ie loc 2) [ we write in this seq : 3, 4, 5, 6, 7, 8, 1, 2], then next loc to write will be loc 010. I dont understand how (MSB + 1) scheme will help ? In fact after 111 , it will go to 1000, which doesnt exist. I'm sorry if I missed something basic here. \$\endgroup\$ Jun 22, 2015 at 5:45
  • \$\begingroup\$ I think your scheme will work only when we keep some sort of count for read and write operation. I dont think it will work in case of absolute register addresses. \$\endgroup\$ Jun 22, 2015 at 5:55
  • \$\begingroup\$ A buffer with 8 locations has M of 3, and M+1 of 4. Let's make that smaller for now. How about 2 locations, M of 1 and M+1 of 2. Initial read ptr is 00, initial write ptr is 00. Write one value. Write ptr is 01. LSB does not match, so not full and not empty. Write another value. Write pointer is 10. LSB matches and MSB does not, so FIFO is full. Read one value. Read pointer is 01. LSB does not match, so FIFO is not full or empty. Read another value. Read pointer is 10. Exact match, FIFO is empty. \$\endgroup\$ Jun 22, 2015 at 11:27
  • \$\begingroup\$ Also note that this scheme does work as I use it in production code: github.com/alexforencich/verilog-axis/blob/master/rtl/… . Also, see github.com/alexforencich/verilog-axis/blob/master/rtl/… for the async version. \$\endgroup\$ Jun 22, 2015 at 11:29
  • \$\begingroup\$ Oh, I forgot to mention: in the M+1 counter scheme, only the lowest M lines are connected as address lines to the memory. So in your example, 0000 and 1000 refer to the same memory location. \$\endgroup\$ Jun 22, 2015 at 11:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.