Você está na página 1de 7

FIFO

1.0 Introduction An asynchronous FIFO refers to a FIFO design where data values are written to a FIFO buffer from one clock domain and the data values are read from the same FIFO buffer from another clock domain, where the two clock domains are asynchronous to each other. Asynchronous FIFOs are used to safely pass data from one clock domain to another clock domain. There are many ways to do asynchronous FIFO design, including many wrong ways. Most incorrectly implemented FIFO designs still function properly 90% of the time. Most almost-correct FIFO designs function properly 99%+ of the time. Unfortunately, FIFOs that work properly 99%+ of the time have design flaws that are usually the most difficult to detect and debug (if you are lucky enough to notice the bug before shipping the product), or the most costly to diagnose and recall (if the bug is not discovered until the product is in the hands of a dissatisfied customer). This paper discusses one FIFO design style and important details that must be considered when doing asynchronous FIFO design. 2.0 Passing multiple asynchronous signals Attempting to synchronize multiple changing signals from one clock domain into a new clock domain and insuring that all changing signals are synchronized to the same clock cycle in the new clock domain has been shown to be problematic. FIFOs are used in designs to safely pass multi-bit data words from one clock domain to another. Data words are placed into a FIFO buffer memory array by control signals in one clock domain, and the data words are removed from another port of the same FIFO buffer memory array by control signals from a second clock domain. Conceptually, the task of designing a FIFO with these assumptions seems to be easy. The difficulty associated with doing FIFO design is related to generating the FIFO pointers and finding a reliable way to determine full and empty status on the FIFO. 2.1 Synchronous FIFO pointers For synchronous FIFO design (a FIFO where writes to, and reads from the FIFO buffer are conducted in the same clock domain), one implementation counts the number of writes to, and reads from the FIFO buffer to increment (on FIFO write but no read), decrement (on FIFO read but no write) or hold (no writes and reads, or simultaneous write and read operation) the current fill value of the FIFO buffer. The FIFO is full when the FIFO counter reaches a predetermined full value and the FIFO is empty when the FIFO counter is zero. Unfortunately, for asynchronous FIFO design, the increment-decrement FIFO fill counter cannot be used, because two different and asynchronous clocks would be required to control the counter. To determine full and empty status for an asynchronous FIFO design, the write and read pointers will have to be compared. 2.2 Asynchronous FIFO pointers In order to understand FIFO design, one needs to understand how the FIFO pointers work. The write pointer always points to the next word to be written; therefore, on reset, both pointers are set to zero, which also happens to be the next FIFO word location to be written. On a FIFO-write operation, the memory location that is pointed to by the write pointer is written, and then the write pointer is incremented to point to the next location to be written. Similarly, the read pointer always points to the current FIFO word to be read. Again on

reset, both pointers are reset to zero, the FIFO is empty and the read pointer is pointing to invalid data (because the FIFO is empty and the empty flag is asserted). As soon as the first data word is written to the FIFO, the write pointer increments, the empty flag is cleared, and the read pointer that is still addressing the contents of the first FIFO memory word, immediately drives that first valid word onto the FIFO data output port, to be read by the receiver logic. The fact that the read pointer is always pointing to the next FIFO word to be read means that the receiver logic does not have to use two clock periods to read the data word. If the receiver first had to increment the read pointer before reading a FIFO data word, the receiver would clock once to output the data word from the FIFO, and clock a second time to capture the data word into the receiver. That would be needlessly inefficient. FIFO is empty when the read and write pointers are both equal. This condition happens when both pointers are reset to zero during a reset operation, or when the read pointer catches The up to the write pointer, having read the last word from the FIFO. A FIFO is full when the pointers are again equal, that is, when the write pointer has wrapped around and caught up to the read pointer. This is a problem. The FIFO is either empty or full when the pointers are equal, but which? One design technique used to distinguish between full and empty is to add an extra bit to each pointer. When the write pointer increments past the final FIFO address, the write pointer will increment the unused MSB while setting the rest of the bits back to zero as shown in Figure 1 (the FIFO has wrapped and toggled the pointer MSB). The same is done with the read pointer. If the MSBs of the two pointers are different, it means that the write pointer has wrapped one more time that the read pointer. If the MSBs of the two pointers are the same, it means that both pointers have wrapped the same number of times. Using n-bit pointers where (n-1) is the number of address bits required to access the entire FIFO memory buffer, the FIFO is empty when both pointers, including the MSBs are equal. And the FIFO is full when both pointers, except the MSBs are equal. //----------------------------------------------------// Design Name : syn_fifo // File Name : syn_fifo.v // Function : Synchronous (single clock) FIFO // Coder : Deepak Kumar Tala //----------------------------------------------------module syn_fifo ( clk , // Clock input rst , // Active high reset wr_cs , // Write chip select rd_cs , // Read chipe select data_in , // Data input rd_en , // Read enable wr_en , // Write Enable data_out , // Data Output empty , // FIFO empty full // FIFO full ); // FIFO constants parameter DATA_WIDTH = 8;

parameter ADDR_WIDTH = 8; parameter RAM_DEPTH = (1 << ADDR_WIDTH); // Port Declarations input clk ; input rst ; input wr_cs ; input rd_cs ; input rd_en ; input wr_en ; input [DATA_WIDTH-1:0] data_in ; output full ; output empty ; output [DATA_WIDTH-1:0] data_out ; //-----------Internal variables------------------reg [ADDR_WIDTH-1:0] wr_pointer; reg [ADDR_WIDTH-1:0] rd_pointer; reg [ADDR_WIDTH :0] status_cnt; reg [DATA_WIDTH-1:0] data_out ; wire [DATA_WIDTH-1:0] data_ram ; //-----------Variable assignments--------------assign full = (status_cnt == (RAM_DEPTH-1)); assign empty = (status_cnt == 0); //-----------Code Start--------------------------always @ (posedge clk or posedge rst) begin : WRITE_POINTER if (rst) begin wr_pointer <= 0; end else if (wr_cs && wr_en ) begin wr_pointer <= wr_pointer + 1; end end always @ (posedge clk or posedge rst) begin : READ_POINTER if (rst) begin rd_pointer <= 0; end else if (rd_cs && rd_en ) begin rd_pointer <= rd_pointer + 1; end end always @ (posedge clk or posedge rst) begin : READ_DATA if (rst) begin data_out <= 0; end else if (rd_cs && rd_en ) begin data_out <= data_ram;

end end always @ (posedge clk or posedge rst) begin : STATUS_COUNTER if (rst) begin status_cnt <= 0; // Read but no write. end else if ((rd_cs && rd_en) && !(wr_cs && wr_en) && (status_cnt != 0)) begin status_cnt <= status_cnt - 1; // Write but no read. end else if ((wr_cs && wr_en) && !(rd_cs && rd_en) && (status_cnt != RAM_DEPTH)) begin status_cnt <= status_cnt + 1; end end ram_dp_ar_aw #(DATA_WIDTH,ADDR_WIDTH)DP_RAM ( .address_0 (wr_pointer) , // address_0 input .data_0 (data_in) , // data_0 bi-directional .cs_0 (wr_cs) , // chip select .we_0 (wr_en) , // write enable .oe_0 (1'b0) , // output enable .address_1 (rd_pointer) , // address_q input .data_1 (data_ram) , // data_1 bi-directional .cs_1 (rd_cs) , // chip select .we_1 (1'b0) , // Read enable .oe_1 (rd_en) // output enable ); endmodule

Name: First In First Out (FIFO) Specification: 1. A FIFO is a list that data is written at the rear of the list but then read from the beginning of the list, with a read is also removing the read data from the list. 2. This FIFO design is an 8x16 register file where the total address line is 8 and each address line is 16 bits wide. 3. For writing operation, the first data will be written into register file is at address waddr (R[waddr]wdata)and then the address will be incremented by one so that the next data coming will be written at waddr+1 (R[waddr+1]wdata) . Since the total address line is 8, so the address will be incremented only up to 8 and then it will wrap around to 0. 4. For reading operation, the first data that will be read is at address raddr (rdata R[raddr]), and then the address will be incremented by one so that the next data that will be read is coming from address raddr+1 (rdata R[raddr+]). Same thing happened to address for reading, it will only incremented up to 8 and then it will wrap around to 0. 5. After writing or reading operation is performed, the counter value for both writing address (waddr) and reading address (raddr) is compared either the counter value is equal or not. If writing operation is performed and then if waddr == raddr means the FIFO is FULL. If reading operation is performed and then waddr == raddr means the FIFO is empty. These two conditions are always updated after each operation is performed for both writing and reading. 6. Noticed that for this FIFO design, write and read operation cannot be handled at the same time even the reading and writing operations are at different addresses. 7. As a result, the conditions detecting the queue being full and the queue being empty are depends on what operation is performed first before checking which is either writing first or reading first.

Mecanismul FIFO RTAI


1. Obiective
2. Prezentarea mecansimului de comunicare intre procese prin cozi de tip FIFO.

3. Noiuni teoretice
O coad FIFO (First In First Out) este un canal de comunicaie utilizat pentru transferul n mod asincron a datelor ntre procese. Datele sunt extrase din coada FIFO n ordinea n care acestea au fost adugate. Cozile FIFO implementate n cadru RTAI permit comunicarea ntre procese utilizator Linux i proces de timp real RTAI dar pot fi folosite i pentru comunicare ntre procese de acelai tip. O coad FIFO poate fi deschis n mod scriere sau n mod citire de ctre un proces. Datele pot fi scrise n coad n mod continuu pn la atingerea capacitii maxime de stocare a acesteia fr a fi nevoie ca procesul scriitor s atepte ca datele s fie citite. RTAI ofer dou implementri ale cozilor FIFO. Prima implementare numit oldfifos se bazeaz pe implementarea Real-TimeLinux FIFO. A dou implementare numit newfifos ofer o nou implementare dar pstrnd compatibilitile cu vechea implementare. n cadrul acestei seciuni va fi prezentat implementarea newfifo avnd n vedere ca aceasta va deveni standardul de referin pentru cozile FIFO pentru viitoarele versiuni RTAI. Cum se creeaz o coad FIFO? Din cadrul unui modul de nucleu RTAI o coad de mesaje este construit apelnd funcia:
int rtf_create (unsigned int fifo, int size);

Argumentul fifo reprezint un numr ntreg n intervalul 0, RTF_NO (63) ce identific coada de mesaje acest argument se poate referi i la o coad existen deja. Argumentul size specific dimensiunea cozii. Pentru a accesa o coad din cadrul unui proces Linux standard se folosete funcia standard open():
file_descriptor = open("/dev/rtf0", O_RDONLY);

n cadrul Linux numerele de identificare a cozilor FIFO (de la 0 la 63) sunt asociate cu dispozitive orientate pe caracter reprezentate de fiierele /dev/rtf0 pn la /dev/rtf63. Aceste fiiere sunt create n momentul instalrii RTAI. Argumentul file_descriptor returnat de ctre funcia open este descriptorul de fiier folosit n cadrul funciilor read i write. Cum se scriu i se citesc date ntr-o / dintr-o coad FIFO? Pentru a accesa (a scrie sau citit date) o coad FIFO din cadrul unui modul de nucleu RTAI se folosesc funciile:
num_read = rtf_get(0, &buffer_in, sizeof(buffer_in)); num_written = rtf_put(1, &buffer_out, sizeof(buffer_out));

Cele dou funcii returneaz numrul de octei citii sau scrii un numr negativ semnificnd eroare.

Pentru a accesa (a scrie sau citi date) o coad FIFO din cadrul unui proces utilizator Linux se folosesc funciile standard:
num_read = read(read_descriptor, &buffer_in, sizeof(buffer_in)); num_written = write(write_descriptor, &buffer_out, sizeof(buffer_out));

Unde descriptorii read_descriptor i write_descriptor sunt cei returnai de funcia open. RTAI ofer de asemenea posibilitatea de instalare a unor handlere (gestionar prelucrtor) care s fie automat apelate n momentul n care o coad de mesaje este scris sau citit. Pentru a instala un astfel de handler se folosete funcia:
int rtf_create_handler handler ) ( unsigned int minor, int(*)(unsigned int fifo)

Argumentul minor reprezin identificatorul cozii FIFO. Argumentul handler este un pointer ctre o funcie care va fi apelat n mod automat atunci cnd coada va fi citit sau scris.