Q1: Should I set Size to power of 2?If so, why?
Power of 2 usually works better, one way or another.
For example, in the code I posted, it allows to skip wrapping tests. "tail" and "head" are free-running. They hold the number of elements from the beginning of the operation. Therefore, the tests for free/empty are very easy. (head - tail) returns a number of elements currently in the buffer.
In addition, the free-running tail and head allows you to use the whole buffer. If you constrict head and tail to always point to inside the buffer, one of the positions is lost. Not abig deal if your buffer is 1000 elements, but may be quite substantial if it is 4 or 8.
Further. Pointers are free running, but the buffer is circular. Therefore the real position of the byte at head is (head%BUFFER_SIZE) and the position of the byte at tail is (tail%BUFFER_SIZE). Division is usually a long operation which is better avoided. If BUFFER_SIZE is a power of two, it can be replaced with "&(BUFFER_SIZE-1)" which is a very fast operation on any processor. This will come very handy when you want to push your baud rate to the limit.
What happens if you don't want free-running tail and head, and would rather restrict them so that they always point to a valid place in the buffer. Setting BUFFER_SIZE to a power of 2 is not of that much help any more. However this makes checks for empty/full more complicated. For example, instead of simple subtraction, you do
if( (fifo.head + 1 == fifo.tail) || ( (fifo.head + 1 == MAX_ITEMS) && (fifo.tail == 0) ))
And you have to do it for every characters!
What if you want to detect the exact number of characters you can read then read them all at once. With free-running tail and head it is still a simple subtraction. With the restrited head and tail, the check is quite complicated.
Now imagine what you would need to do to implement the peek() function with the constricted tail and head.
Another thing which gets more complicated are increments, such as:
fifo.tail++; //increment the tail
if( fifo.tail == MAX_ITEMS ) {
//check for wrap-around
fifo.tail = 0;
}
instead of a simple increment.
If you want to make it thread-safe (say when you write from the ISR then read from the main), it is even more work:
int nexttail = fifo.tail + 1;
if( nexttail == MAX_ITEMS ) {
//check for wrap-around
nexttail = 0;
}
fifo.tail = nexttail; // this last assignment operation must be atomic to ensure thread safety.
Or, if FIFO_SIZE is a power of 2 you can use the following code for the increment:
fifo.tail = (fifo.tail+1)&(FIFO_SIZE-1); // the assignment must be atomic for thread safety.
So, it is entirely up to you. Either
- you are prepared to use more complicated code to avoid FIFO_SIZE being power of two.
or
- you are prepared to increase the FIFO_SIZE to the next power of two in favour of better and more efficient code.