Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 13

FIFO buffer (First In, First Out):

1.Overview of FIFO
FIFO buffer In computing and in systems theory, it is a method for organizing the manipulation of data

structures. Where the oldest (first) entry, or 'head' of the queue, is processed first. Such processing is

similar to serving people in the queue area on a first-come, first-served basis, in the same order in which

they arrived up to the tail of the queue.

FIFO buffers are commonly used in electronic circuits to buffer and control flow between hardware

and software. In hardware form, FIFO mainly consists of a set of read and write pointers, storage and

control logic. The memory can be static random access memory (SRAM), flip-flops, latches, or any other

suitable form of storage. For FIFOs of non-small size, two-port SRAM is often used, where one port is

dedicated for writing and the other for reading.

There are many ways to create a FIFO buffer, but the one used in this simulation exercise is a circular

FIFO (array-base buffer). As its name suggests (array base), this buffer is implemented based on an array.

Accompanied by two pointers Write and Read. Every time a write command is received, the prWrite

pointer will write data to the buffer, then increase by 1 unit. Every time a read command is received, the

prRead pointer will increase by one. Then read the value from the buffer. When a pointer reaches the end

of the array, it scrolls back to the first position.

That's why it's called a ring buffer.

This buffer includes 2 flags: empty and full.

- Full flag: is the state when the write pointer has written data one circle and meets the read pointer in

the second circle. In other words, the read pointer coincides with the write pointer when the write pointer

rotates 1 round larger than the read pointer. The data has not been read out but there has been a signal

written to that memory cell. Then we will not be allowed to write data anymore.
- Empty flag: is the state where the read pointer coincides with the write pointer when both pointers

are in the same cycle. Data that has not been written in has a signal read out, it is considered that the data

is also lost.

A FIFO can be thought of as a one-way tunnel that cars can pass through. At the end of the tunnel is a

toll booth. Once the gate opens, the car can leave the tunnel. If that gate doesn't open and more cars

continue to enter the tunnel, eventually the tunnel will fill up with cars. This is called a FIFO overflow.

The depth of the FIFO can be thought of as the length of the tunnel. The longer the FIFO, the more data

can go in before it overflows. FIFOs also have a width, which represents the width of the data (in terms of

bits) going into the FIFO.

- FIFO depth: corresponds to the maximum number of elements that the FIFO can store

- FIFO bandwidth: equivalent to the size of a data element read/written in one read/write cycle

Designers should never write data so that the FIFO overflows. Therefore, always check the FIFO full

flag to ensure there is room for another data frame, otherwise the old data will be lost.

Designers should also never read from an empty FIFO.

We can recall two extremely important rules of FIFO:

- Never write to a full FIFO (overflow);

- Never read from an empty FIFO (underflow).

The EMPTY state corresponds to when the read pointer Rptr points to the same location as the write

pointer Wptr. When the FIFO is reset, it is set to this state with pointers Rptr and Wptr both pointing to

memory location 0.

FULL state occurs when value Wptr + 1 = Rptr. Although in this case the memory array still has a

memory cell (whose address corresponds to the value of Wptr) that has not yet been written to, we cannot

save data to it. This is because after writing data to this memory cell, the value of Wptr is increased by 1
unit and points to the same memory cell as Rptr, and we cannot distinguish whether this is an EMPTY or

FULL state.

Basically, the transmitter FIFO and the receiver FIFO are the same, the only difference is that the

transmitter FIFO is a read FIFO so you pay more attention to the FULL flag, while the receiver FIFO is a

write FIFO so you pay more attention to the EMPTY flag more.

2.Functional description and block diagram design


The FIFO-buffer block has the function of temporarily storing received and sent data (data_w) when

the RX and TX sets have not yet had time to read or send previously arrived data. To store data, the buffer

block must have a register file. In addition, to perform read and write, we need a read signal (rd), a write

signal (wr) and a pointer signal pointing to the address of the file you want to read or write. The block

outputs a full signal (full) = 1 when the queue is full and an empty signal (empty) = 1 when the queue is

empty and data is read (data_r). Because the block contains memory elements, we need to use clk pulses

and reset signals.

The block diagram of the buffer block is also shown below:

Figure : Block diagram of FIFO Buffer


Next, we will go to the design of the FIFO function of reading and writing in the buffer block.

Because UART transmit and receive data is 8 bits, each register row will have 8 bits. Suppose that the

queue length is 8 corresponding to 3 bit addresses numbered from 0 to 7 as shown:

Figure: Operating mechanism of FIFO

We call the read and write pointers rd ptr and wr ptr respectively. Initially the queue is empty, signal

empty = 1. wr ptr and rd ptr will carry the address value at row 0 of the register file – Figure a. To

perform writing, signal wr = 1, we will write the value at each positive edge of the clk cycle. After each
write, the address of pointer wr ptr will be added by 1. Figures b and c depict the process of writing four

values to the register file. This takes place over 4 clock cycles. To perform a read, let the signal rd = 1.

The read will not depend on the clock cycle and the rd pointer will be added by 1 at each positive edge of

the clock cycle. Figure d depicts this process. For the full output signal, it signals full when we set wr = 1,

rd = 0 and wr ptr increases until it catches up with rd ptr as shown in figure f. For the empty output signal,

it will be reported as empty when we set wr = 0, rd = 1 and rd ptr will increase until it catches up with wr

ptr. In case we both read and write, the buffer will never be empty or full. The above process will be

described by the following summary table. We define signal_next as the value of that signal in the next

positive edge of the clock cycle.

Table : Description of pointer status in FIFO

wr rd wr_ptr_next rd_ptr_next full_next empty_next


0 0 Keep stable Keep stable Keep stable Keep stable

0 1 Keep stable rd_ptr + 1 0 =1 when rd_ptr_ next


catches up with wr ptr
1 0 wr_ptr + 1 Keep stable = 1 when wr_ptr_next 0
catches up with rd_ptr
1 1 wr_ptr + 1 rd_ptr + 1 0 0

All signals used are listed in the table below. We call W the number of bits of input data written to the

queue, N the number of address bits assigned to the queue


Signal value table

signal Signal type SIZE EXplain


array Internal [W-1:0] x This is a file record with [2^N -1 :0]
signal [2**N-1:0] address and each address contains [W-
(8 bit x 1:0] bit
address
[3:0])
r_ptr_reg Internal [N-1:0] Present value address of pointer reader
signal
r_ptr_next Internal [N-1:0] The pointer's address value is read at the
signal next positive edge

w_ptr_reg Internal [N-1:0] Present value address of pointer reader


signal
w_ptr_next Internal [N-1:0] The pointer's address value is read at the
signal next positive edge

wr_en Internal 1 bit


signal Record enable signal.

r_en Internal 1 bit Read enable signal


signal
empty_reg Internal 1 bit Empty state in the present
signal
empty_nextInternal 1 bit Empty state at the next positive edge
signal
full_reg Internal 1 bit Full state in the present
signal
full_next Internal 1 bit Full state in the next positive slope
signal
rd Input 1 bit Read signal
wr Input 1 bit Recording signal
clk Input 1 bit Pulse clk

reset Input 1 bit


System reset signal
data_w Input [W-1:0] Data written to buffer

full Output 1 bit Buffer full signal

empty Output 1 bit Signal indicating the buffer is empty

data_r Output [W-1:0] Data read out

To facilitate simulation, we let the buffer have 2 address bits (N = 2) equivalent to 4 register rows.

The number of bits we transmit into the queue is 8 (W = 8). So the register file here will be 4x8bit.
3. SOURCE CODE DESIGN FIFO-BUFFER BLOCK

timescale 1ns / 1ps

module Fifo_buffer
#(
parameter W = 8, // number of bits per row
N = 2 // number of address bits
)
(
input wire wr, rd, clk, reset,
input wire [W-1:0] data_w,
output wire full, empty,
output wire [W-1:0] data_r
);
//signal declaration
reg [7:0] array [2**N - 1 : 0];
reg [N-1:0] r_ptr_reg, r_ptr_next, r_ptr_succ;
reg [N-1:0] w_ptr_reg, w_ptr_next, w_ptr_succ;
wire wr_en, r_en;
reg empty_next, full_next;
reg empty_reg, full_reg;
// body
// write operation
always@(posedge clk)
if(wr_en)
array[w_ptr_reg] <= data_w;
// read operation
/* if(r_en)
data_r <= array[r_ptr_reg]; */
assign data_r = array[r_ptr_reg];
// note
assign wr_en = ~full_reg & wr;
/*assign r_en = ~empty_reg & rd; */ // cannot be used in case 11

//fifo control logic


//register for read and write pointers
always@(posedge clk, posedge reset)
begin
if(reset)
begin
r_ptr_reg <= 0;
w_ptr_reg <= 0;
full_reg <= 1'b0;
empty_reg <= 1'b1;
end
else
begin
r_ptr_reg <= r_ptr_next;
w_ptr_reg <= w_ptr_next;
full_reg <= full_next;
empty_reg <= empty_next;
end
end
//register for read and write
//next state logic
always @*
begin
// keep default value
r_ptr_next = r_ptr_reg;
w_ptr_next = w_ptr_reg;
full_next = full_reg;
empty_next = empty_reg;
// increment the pointers
r_ptr_succ = r_ptr_reg + 1;
w_ptr_succ = w_ptr_reg + 1;

// read and write case


case({rd, wr})
/* 2'b00: begin
r_ptr_next = r_ptr_reg;
w_ptr_next = w_ptr_reg;
full_next = full_reg;
empty_next = empty_reg;
end */
2'b01:
if(~full_reg) // not full
begin
w_ptr_next = w_ptr_succ;
empty_next = 1'b0;
if(w_ptr_next == r_ptr_reg)
full_next = 1'b1;
/* else
full_next = 1'b0; */ // not necessary
as full_reg will keep its previous value
end

2'b10:
if(~empty_reg)
begin
r_ptr_next = r_ptr_succ;
full_next = 1'b0;
if(r_ptr_next == w_ptr_reg)
empty_next = 1'b1;
/* else
empty_next = 1'b1; */
end

2'b11: begin
r_ptr_next = r_ptr_succ;
w_ptr_next = w_ptr_succ;
end
endcase
end
//output
assign empty = empty_reg;
assign full = full_reg;
endmodule

4. FIFO-BUFFER BLOCK TESTBENCH


`timescale 1ns / 1ps
module Fifo_buffer_test();
localparam T = 20;
reg wr, rd, clk, reset;
reg [7:0] data_w;
wire full, empty;
wire [7:0] data_r;
Fifo_buffer #(.W(8), .N(2))
DUT(.wr(wr), .rd(rd), .clk(clk), .reset(reset), .data_w(data_w), .full
(full),
.empty(empty), .data_r(data_r));
always
begin
clk = 1'b1;
#(T/2);
clk = 1'B0;
#(T/2);
end

// ==== initialization ====


initial
begin
reset = 1'b1;
#(T/2);
reset = 1'b0;
#(T/2);
end
// ==== test vector ====
initial
begin
wait(reset == 1'b0);
@(negedge clk);
data_w = 4'b1001;
// write first
wr = 1'b1;
rd = 1'b0;
repeat(3) @(negedge clk);
// read
wr = 1'b0;
rd = 1'b1;
repeat(3) @(negedge clk);
// full test
rd = 1'b0;
wr = 1'b1;
repeat(5) @(negedge clk);
// empty test
rd = 1'b1;
wr = 1'b0;
repeat(6) @(negedge clk);
// apply both rd and wr
wr = 1'b1;
repeat(10) @(negedge clk);
$finish;
end

endmodule

5. Test script
Checking the buffer block is carried out as follows:

• Step1: Pass the input value equal to 9. Because we are only interested in the read and write process, the

address movement of the read and write pointers in the block, the input value is not too important.

• Step 2: We set the signal wr = 1 to check the recording process

• Step 3: We set wr = 0 and set signal rd = 1 to check the reading process

• Step 4: Perform full function test - set wr = 1, rd = 0 and write until the block is full

• Step 5: Check the empty function - set rd = 1, wr = 0 and read until the block is empty

• Step 6: We set rd = 1, wr = 1 and check the operation

Test results and evaluation


FIFO block simulation results

At step 2, we see that the block has performed the write function: The array register file has stored the

value 9. In step 3, we also see that the read pointer (r_ptr_reg) has been added 1 at each positive edge

after when rd = 1. Reading out data is also always performed during buffer operations. Step 4 and step 5

also show that the system gave full and empty = 1 signals at the full and empty times in the register file,

respectively. At step5, the read and write pointers worked as expected.

You might also like