Professional Documents
Culture Documents
TABLE OF CONTENTS:
ACRONYMS ...3 ACKNOWLEDGEMENT ...4 HISTORY 5 ABSTRACT .6 INTRODUCTION ...7 BLACK-BOX VIEW OF SYNCHRONOUS FIFO ..10 PORT LIST ........10 FUNCTIONAL DESCRIPTION ...12 VERIFICATION OF THE MODULE ...22 SYNTHESIS OF THE MODULE .....27 CONCLUSION ..32 APPENDIX ....33 o A1: RTL DESCRIPTION OF SYNCHRONOUS FIFO USING
VERILOG HDL ......33
ACRONYMS:
EDA : Electronic Design Automation
FIFO : First In First Out CAD CAE ICs RTL : Computer Aided Design : Computer Aided Engineering : Integrated Circuits : Register Transistor Level : Testbench : Hardware Description Language : Most Significant Bit : Random Access Memory
WCLK : Write Clock RCLK : Read Clock LFSR : Linear Feedback Shift Registers VHDL : Very High Speed Integrated Circuit Hardware Description Language
HOD MIT
ABSTRACT
FIFO is a First-In-First-Out memory queue with control logic that manages the read and write operations, generates status flags, and provides optional handshake signals for interfacing with the user logic. It is often used to control the flow of data between source and destination. FIFO can be classified as synchronous or asynchronous depending on whether same clock or different (asynchronous) clocks control the read and write operations. In this project the objective is to design, verify and synthesize a synchronous FIFO using binary coded read and write pointers to address the memory array. FFIO full and empty flags are generated and passed on to source and destination logics, respectively, to pre-empt any overflow or underflow of data. In this way data integrity between source and destination is maintained . The RTL description for the FIFO is written using Verilog HDL, and design is simulated and synthesized using NC-SIM and RTL compiler provided by Cadence Design Systems, Inc. respectively.
INTRODUCTION
WHAT IS A FIFO (first in first out)?
In computer programming, FIFO (first-in, first-out) is an approach to handling program work requests from queues or stacks so that the oldest request is handled first. In hardware it is either an array of flops or Read/Write memory that store data given from one clock domain and on request supplies with the same data to other clock domain following the first in first out logic. The clock domain that supplies data to FIFO is often referred as WRITE OR INPUT LOGIC and the clock domain that reads data from the FIFO is often referred as READ OR OUTPUT LOGIC. FIFOs are used in designs to safely pass multi-bit data words from one clock domain to another or to control the flow of data between source and destination side sitting in the same clock domain. If read and write clock domains are governed by same clock signal the FIFO is said to be SYNCHRONOUS and if read and write clock domains are governed by different (asynchronous) clock signals FIFO is said to be ASYNCHRONOUS. FIFO full and FIFO empty flags are of great concern as no data should be written in full condition and no data should be read in empty condition, as it can lead to loss of data or generation of non relevant data. The full and empty conditions of FIFO are controlled using binary or gray pointers. In this report we deal with binary pointers only since we are designing SYNCHRONOUS FIFO. The gray pointers are used for generating full and
empty conditions for ASYNCHRONOUS FIFO. The reason why they are used is beyond the scope of this document.
APPLICATIONS
FIFOs are used to safely pass data between two asynchronous clock domains. In System-on-Chip designs there are components which often run on different clocks. So, to pass data from component to another we need ASYNCHRONOUS FIFO Some times even if the Source and Requestor sides are controlled by same clock signal a FIFO is needed. This is to match the throughputs of the Source and the Requestor. For example in one case source may be supplying data at rate which the requestor can not handle or in other case requestor may be placing requests for data at a rate at which source can not supply. So, to bridge this gap between source and requestor capacities to supply and consume data a SYNCHRONOUS FIFO is used which acts as an elastic buffer. one such
SYNCHRONOUS FIFO
PORT LIST
COMMON PORTS
Name clk reset_n flush I/O I I I Width Description 1 Clock input to the FIFO. This is common input to both read and write sides of FIFO 1 Active-low asynchronous reset input to FIFO read and write logics 1 Active-high synchronous flush input to FIFO. A clock-wide pulse resets the FIFO read and write pointers
fifo_full fifo_afull
O O
write_ack
fifo_empty fifo_aempty
O O
FUNCTIONAL DESCRIPTION
Figure 2 depicts the basic building blocks of a synchronous FIFO which are: memory array, write control logic and read control logic. The memory array can be implemented either with array of flip-flops or with a dual-port read/write memory. Both of these implementations allow simultaneous read and write accesses. This simultaneous access gives the FIFO its inherent synchronization property. There are no restrictions regarding timing between accesses of the two ports. This means simply, that while one port is writing to the memory at one rate, the other port can be reading at another rate totally independent of one another. The only restriction placed is that the simultaneous read and write access should not be from/to the same memory location. The Synchronous FIFO has a single clock port clk for both data-read and data-write operations. Data presented at the module's data-input port write_data is written into the next available empty memory location on a rising clock edge when the write-enable input write_enable is high. The full status output fifo_full indicates that no more empty locations remain in the module's internal memory. Data can be read out of the FIFO via the module's data-output port read_data in the order in which it was written by asserting read-enable signal read_enable prior to a rising clock edge. The memory-empty status output fifo_empty indicates that no more data resides in the module's internal memory. There are almost empty and almost full flags too viz. fifo_aempty and fifo_afull which can be used to control the read and write speeds of the requestor and the source.
10
11
Figure 3: Write Control Logic Black-box View PORT LIST Name clk reset_n flush wdata_valid rd_ptr write_enable write_ptr write_ack fifo_full fifo_afull I/O I I I I I O O O O O Width Description 1 Clock input 1 Active-low reset input 1 Active-high synchronous flush input to FIFO. A clock-wide pulse resets the FIFO read and write pointers 1 Qualifies write data in. A logic high indicates the data on write_data bus is valid 5 Read pointer from Read Control Logic. This along with write pointer is used to find FIFO full and almost full condition 1 Write enable to FIFOs internal memory 5 Write pointer value. This serves as a write address to FIFOs internal memory 1 Acknowledgement to source that write operation is done. 1 Indicates to the source that FIFOs internal memory has no space left to take in new data 1 Indicates to the source that FIFOs internal memory has only few spaces left for new data. Upon seeing this source may decide to slow down or stall the write operation
12
13
PORT LIST Name clk reset_n flush read_req write_ptr I/O I I I I I Width Description 1 Clock input 1 Active-low reset input 1 Active-high synchronous flush input to FIFO. A clock-wide pulse resets the FIFO read and write pointers 1 Read request from the requestor. 5 Write pointer from Write Control Logic. This along with read pointer is used to find FIFO empty and almost empty conditions 1 Read enable to FIFOs internal memory 5 Read pointer value. This serves as a read address to FIFO internal memory 1 Acknowledgement to source that write operation is done. 1 Indicates to the source that FIFOs internal memory has no space left to take in new data 1 Indicates to the source that FIFOs internal memory has only few spaces left for new data. Upon seeing this source may decide to slow down or stall the write operation
O O O O O
MEMORY ARRAY
Memory Array is an array of flip-flops which stores data. Number of data words that the memory array can store is often referred as DEPTH of the FIFO. Length of the data word is referred as WIDTH of the FIFO. Besides flop-array it comprises read and write address decoding logic.
14
The functionality of Memory Array is relatively straight forward as described below: 1. If write_enable signal is high DATA present on write_data is written into the row pointed by write_addr on the next rising edge of the clock signal clk. Note that write_enable is asserted only when wdata_valid is high and FIFO is not full to avoid any data corruption 2. If read_enable signal is high the DATA present in the row pointed by read_addr is sent onto the read_data bus on the next rising edge of the clock signal clk. Note that read_enable is asserted only when read_req is high and FIFO is not empty to avoid any spurious data being sent to the requestor 3. It can handle simultaneous read and write enables as long as their addresses do not match Figure 6 below shows the black-box view of Memory Array.
MEMORY ARRAY
PORT LIST Name clk write_addr write_enable write_data read_addr read_enable read_data I/O I I I I I I O Width Description 1 Clock input 4 Write address to the memory. It is derived from write pointer by knocking-off its MSB 1 Active-high write enable input to the memory 16 Data Input to the memory 4 Read address to the memory. It is derived from read pointer by knocking-off its MSB 1 Active-high read enable to memory 16 Data read out from the memory
will be of 9-bits. When their lower 8-bits point to same memory location their MSBs are used to ascertain whether it is a full condition or empty condition. In empty conditions the MSBs are equal whereas in full condition MSBs are different. The verilog code shown below depicts the same: assign fifo_full = ( (write_ptr[7 : 0] == read_addr[7 : 0]) && (write_ptr[8] ^ read_ptr[8]) ); assign fifo_empty = (read_ptr[8 : 0] == write_ptr[8 : 0]); Following piece of verilog code shows logic almost full generation: // Generating fifo almost full status always @* begin if ( write_ptr[8] == read_ptr[8] ) fifo_afull = ((write_ptr[7:0] - read_ptr[7:0]) >= (DEPTH - AFULL)); else fifo_afull = ((read_ptr[8:0] - write_ptr[8:0]) <= AFULL); end Here AFULL signifies the almost full-level. User can set it to 4, 8 or whatever value depending on how soon it wants to intimate the source about impending full condition. (AFULL=4) means almost full flag will get asserted when at most 4 locations are left for new data.
17
Following piece of verilog code shows logic almost empty generation: //assigning fifo almost empty always @* begin if (read_ptr[8] == write_ptr[8]) fifo_aempty = ( (write_ptr[7:0] - read_ptr[7:0]) <= AEMPTY ); else fifo_aempty = ((read_ptr[8:0] - write_addr[8:0]) >= (DEPTH - AEMPTY)); end Here AEMPTY signifies the almost empty-level. User can set it to 4, 8 or whatever value depending on how soon it wants to intimate the requestor about impending empty condition. (AEMPTY=4) means almost empty flag will get asserted when at most 4 data locations are left to be read.
18
In this way read keeps following write until the FIFO gets empty again If write operations are not matched by read soon FIFO will get full and any further write will get stalled until fifo_full is pulled down by a read With the help of FIFO full and empty flags data integrity is maintained between the source and the requestor
19
20
21
SIMULATION
We used Cadence NC-SIM simulator to simulate our Synchronous FIFO TB. The results are shown below: FIFO FULL & ALMOST FULL FLAGS GENERATION Yellow circle shows FIFO full and almost full flags getting asserted and deasserted depending on the values of write and read pointers. Note that when FIFO full is asserted except for the MSBs the pointer values are same.
FIFO EMPTY & ALMOST EMPTY FLAG GENERATION You can see the yellow circle which shows how FIFO empty signals are getting asserted and de-asserted depending on the read and write pointer values. The red circle shows almost empty flag assertion and de-assertion. This snapshot is depicting two scenarios: firstly full and almost full both are asserted and secondly almost full is asserted but full is not.
22
FIFO FLUSH OPERATION The yellow circle shows how FIFO is being flushed? Notice that upon seeing the flush signal both the pointers are reset to zero and FIFO empty flag is pulled high.
23
TB CHECKER TB checker compares all the data which were sent to FIFO with all the data received at the output of FIFO. They should match. The result of this comparison is the match signal shown in the snapshot. If this signal is high all the time it means the test has passed the checking.
24
25
EXAMPLE RTL assign z = a && b; Netlist AND2X1 u0 (.A(a), .B(b), .Z(z)); Where AND2X1 is a standard cell for AND gate in the library. CONSTRAINTS The constraints are the goals that the synthesis tool tries to meet. For example, we may have a design that is targeted for several different applications with different requirements: Timing is most important; area is not a concern. Power and area are more important; and timing is less so. Timing, area, and power are all important, and we have specific measures for each. STANDARD CELL LIBRARY Standard Cell Library is a collection of basic building blocks or standard cells targeted to a specific semiconductor process technology.
26
o TSMC library targeted to 65nm technology node is chosen Choosing the Synthesis Tool o Cadence RTL compiler is chosen for this CONSTRAINT FILE FOR SYNCHRONOUS FIFO File name: constraints.sdc ************************************************************* set sdc_version 1.7 set_max_fanout 32 /designs/sync_fifo/ set_max_transition 0.5 /designs/sync_fifo/ set_max_leakage_power 0 set_driving_cell -lib_cell BUFFD4 -library tcbn65lpwc -pin Z [find / -port */ports_in/*] set_load -pin_load 0.05 [get_ports *] create_clock [find / -port clk] -name clk -period 5 -waveform {0 2.5} set_clock_uncertainty 2 clk set_input_delay -clock clk -min 2 [get_ports *] set_output_delay -clock clk -min 2 [get_ports *] *************************************************************
27
SYNTHESIS RUN SCRICPT These are commands given to RTL compiler to perform various synthesis tasks. Below is the synthesis and run script for sync_fifo ************************************************************* rc:/>include load_etc.tcl rc:/>set_attribute library tcbn65lpwc.lib" rc:/>read_hdl -v2001 "../RTL/SYNC_FIFO/mem_array.v \ ../RTL/SYNC_FIFO/read_control_logic.v \ ../RTL/SYNC_FIFO/sync_fifo.v \ ../RTL/SYNC_FIFO/write_control_logic.v" rc:/>elaborate rc:/>check_design > ../REPORTS/check_design.rpt rc:/>read_sdc ../SCRIPTS/constraints.sdc rc:/>report timing -lint > ../REPORTS/timing_lint.rpt synthesize -to_mapped rc:/>write_hdl > ../NETLIST/sync_fifo_netlist.v rc:/>report timing -num_paths 100 > ../REPORTS/timing.rpt *************************************************************
28
RESULTS
It can be observed that there is a positive slack of 776ps (see the yellow circle) for the worst case path. So the synthesis comfortably meets the timing constraints set by us.
29
CONCLUSION
The objective of this training was to learn basics of digital logic design. And during the course of this I learned the following aspects of digital design: How to write RTL codes using Verilog HDL How to write behavioral codes using Verilog HDL to develop testbenches Design of basic logic elements like half adders, full adders, counters, Finite State Machines, sequence detectors etc. Design and application of Synchronous FIFO Basic difference between Synchronous & Asynchronous FIFO How to develop testbench and run simulations to verify the correctness of your design (using Cadence NC-SIM) How to synthesize an RTL design to a gate-level design subject to user defined constraints (using Cadence RTL Compiler) Given the short duration of this training, I believe, my set objectives are more than fulfilled.
30
APPENDIX
A1: RTL DESCRIPTION OF SYNCHRONOUS FIFO USING VERILOG HDL
TOP LEVEL MODULE //***********************************************************// // File Name: sync_fifo.v // Module Name: sync_fifo // Description: Synchronous FIFO // Place: Cadence Design Systems, Inc. // Date: July 10, 2008 //***********************************************************// module sync_fifo #( parameter ADDR_WIDTH = 4, parameter DATA_WIDTH = 16, parameter DEPTH = 16, parameter AEMPTY = 3, parameter AFULL = 3 ) ( // Inputs input clk, input reset_n, input flush, input read_req, input [DATA_WIDTH-1:0] write_data, input wdata_valid, // Outputs output [DATA_WIDTH-1:0] output output output output output output ); read_data, rdata_valid, fifo_empty, fifo_aempty, fifo_full, fifo_afull, write_ack
31
wire [ADDR_WIDTH:0] read_ptr; wire [ADDR_WIDTH:0] write_ptr; wire read_enable; wire write_enable; write_control_logic U_WRITE_CTRL ( .read_ptr(read_ptr), .flush(flush), .reset_n(reset_n), .clk(clk), .wdata_valid(wdata_valid), .write_ack(write_ack), .write_enable(write_enable), .write_ptr(write_ptr), .fifo_full(fifo_full), .fifo_afull(fifo_afull) ); read_control_logic U_READ_CTRL ( .write_ptr(write_ptr), .clk(clk), .reset_n(reset_n), .flush(flush), .read_req(read_req), .read_enable(read_enable), .rdata_valid(rdata_valid), .fifo_empty(fifo_empty), .read_ptr(read_ptr), .fifo_aempty(fifo_aempty) ); mem_array U_MEM_ARRAY ( .write_addr(write_ptr[ADDR_WIDTH-1:0]), .read_addr(read_ptr[ADDR_WIDTH-1:0]), .write_enable(write_enable), .read_enable(read_enable), .clk(clk), //.reset_n(reset_n), .write_data(write_data),
32
.read_data(read_data) ); endmodule //***********************************************************// READ CONTROL LOGIC //***********************************************************// // File Name: read_control_logic.v // Module Name: read_control_logic // Description: Controls the read operation of sync_fifo. Generates // FIFO empty & almost empty flags. // Place: Cadence Design Systems, Inc. // Date: July 8, 2008 //***********************************************************// module read_control_logic #( parameter ADDR_WIDTH = 4, parameter AEMPTY = 3, parameter DEPTH = 16 ) ( // Inputs input [ADDR_WIDTH:0] write_ptr, input clk, input reset_n, input flush, input read_req, // Outputs output output reg output output reg output reg [ADDR_WIDTH:0] ); wire [ADDR_WIDTH-1:0] read_addr; wire [ADDR_WIDTH-1:0] write_addr; read_enable, rdata_valid, fifo_empty, fifo_aempty, read_ptr
33
// Extracting read and write address from corrosponding pointers assign read_addr = read_ptr [ADDR_WIDTH-1:0]; assign write_addr = write_ptr [ADDR_WIDTH-1:0]; //FIFO is empty when read pointer is same as write pointer assign fifo_empty = (read_ptr == write_ptr); // No read when FIFO is empty assign read_enable = read_req && (~fifo_empty); // Logic to generate almost empty flag always @* begin if (read_ptr[ADDR_WIDTH] == write_ptr[ADDR_WIDTH]) fifo_aempty = ((write_addr - read_addr) <= AEMPTY); else fifo_aempty = ((read_addr - write_addr) >= (DEPTH - AEMPTY)); end // Read pointer and read data valid generation logic always @(posedge clk or negedge reset_n) begin if (~reset_n) read_ptr <= {(ADDR_WIDTH+1){1'b0}}; else if (flush) read_ptr <= {(ADDR_WIDTH+1){1'b0}}; else if (read_enable) begin read_ptr <= read_ptr + {{ADDR_WIDTH{1'b0}},1'b1}; rdata_valid <= 1'b1; end else rdata_valid <= 1'b0; end endmodule //***********************************************************//
34
WRITE CONTROL LOGIC //***********************************************************// // File Name: write_control_logic.v // Module Name: write_control_logic // Description: Controls the write operation of sync_fifo. Generates // FIFO full & almost full flags. // Place: Cadence Design Systems, Inc. // Date: July 10, 2008 //***********************************************************// module write_control_logic #( parameter ADDR_WIDTH = 4, parameter AFULL = 3, parameter DEPTH = 16 ) ( // Inputs input [ADDR_WIDTH:0] read_ptr, input wdata_valid, input flush, input reset_n, input clk, // Outputs output reg write_ack, output write_enable, output reg [ADDR_WIDTH:0] write_ptr, output fifo_full, output reg fifo_afull ); wire [ADDR_WIDTH-1:0] write_addr; wire [ADDR_WIDTH-1:0] read_addr; // Extracting read and write addresses assign read_addr = read_ptr[ADDR_WIDTH-1:0]; assign write_addr = write_ptr[ADDR_WIDTH-1:0];
35
// Generating write enable // No write when FIFO is full assign write_enable = wdata_valid && (~fifo_full); // Generating fifo full status // FIFO full is asserted when both pointers point to same address but their // MSBs are different assign fifo_full = ( (write_addr == read_addr) && (write_ptr[ADDR_WIDTH] ^ read_ptr[ADDR_WIDTH]) ); // Generating fifo almost full status always @* begin if (write_ptr[ADDR_WIDTH] == read_ptr[ADDR_WIDTH]) fifo_afull = ((write_addr - read_addr) >= (DEPTH - AFULL)); else fifo_afull = ((read_addr - write_addr) <= AFULL); end // Write pointer generation logic always @(posedge clk or negedge reset_n) begin if (~reset_n) begin write_ptr <= {(ADDR_WIDTH+1){1'b0}}; write_ack <= 1'b0; end else if (flush) begin write_ptr <= {(ADDR_WIDTH+1){1'b0}}; write_ack <= 1'b0; end else if (write_enable) begin write_ptr <= write_ptr + {{ADDR_WIDTH{1'b0}},1'b1}; write_ack<= 1'b1; end else
36
begin write_ack <= 1'b0; end end endmodule //*********************************************************// MEMORY ARRAY //***********************************************************// // File Name: mem_array.v // Module Name: mem_array // Description: FIFO internal memory to store incoming data. It is // implemented as flop-array // Place: Cadence Design Systems, Inc. // Date: July 10, 2008 //***********************************************************// module mem_array #( parameter ADDR_WIDTH = 4, parameter DEPTH = 16, parameter DATA_WIDTH = 16 ) ( input [ADDR_WIDTH-1:0] write_addr, input [ADDR_WIDTH-1:0] read_addr, input write_enable, input read_enable, input clk, input [DATA_WIDTH-1:0] write_data, output reg [DATA_WIDTH-1:0] read_data ); reg [DATA_WIDTH-1:0] mem [0:DEPTH-1]; // Mem write always @(posedge clk) begin if (write_enable)
37
mem[write_addr] <= write_data; end // Mem Read always @(posedge clk) begin if (read_enable) begin read_data <= mem[read_addr]; end end endmodule //***********************************************************//
38
initial begin reset_n=1'b0; #550; reset_n=1'b1; end // LFSR Counter always @(posedge clk or negedge reset_n) begin if(~reset_n) lfsr_count <= 16'h70f0; else lfsr_count <= {lfsr_count[14:0], ~(lfsr_count[15] ^ lfsr_count[14] ^ lfsr_count[12] ^ lfsr_count[3])}; end // generating ready, count_enable and read request signal assign ready = lfsr_count[0] | lfsr_count[3]; assign read_req = lfsr_count[7] | lfsr_count[13]; assign count_enable = (ready) && (~fifo_full); //generating 16 bit input data always @(posedge clk or negedge reset_n) begin if(~reset_n) data_in<={{15{1'b0}},1'b1}; else if(count_enable) data_in[15:0]<={data_in[14:0],data_in[15]^data_in[14]}; else data_in<=data_in; end //destination logic.... clk,reset_n,flush are same as in source logic wire [15:0] data_out; wire rdata_valid; wire fifo_empty; wire fifo_aempty;
40
//instantiating DUT sync_fifo #( .ADDR_WIDTH(4), .DATA_WIDTH(16), .DEPTH(16), .AEMPTY(3), .AFULL(3) ) DUT ( .clk(clk), .reset_n(reset_n), .flush(flush), .read_req(read_req), .write_data(data_in), .wdata_valid(data_valid), .read_data(data_out), .fifo_empty(fifo_empty), .fifo_aempty(fifo_aempty), .fifo_full(fifo_full), .fifo_afull(fifo_afull), .write_ack(write_ack), .rdata_valid(rdata_valid) ); //source array reg [15:0] source_array[0:2047]; reg [10:0] counter; always @(posedge clk or negedge reset_n) begin if(~reset_n) counter <= {11{1'b0}}; else if(data_valid && (~fifo_full)) begin source_array[counter] <= data_in; counter <= counter + {{10{1'b0}},1'b1}; end end //destination array
41
reg [15:0] destination_array[0:2047]; reg [10:0] counter1; always @(posedge clk or negedge reset_n) begin if(~reset_n) counter1 <= {11{1'b0}}; else if(rdata_valid) begin destination_array[counter1] <= data_out; counter1 <= counter1 + {{10{1'b0}},1'b1}; end end integer i; //reg [2047:0] match; reg match; always @(counter1) begin match = 1'b1; for (i=0; i < counter1; i=i+1) //comparing source and destination array match = match && (source_array[i] == destination_array[i]); end // FSM to generate data valid signal reg cs; reg ns; localparam idle = 1'b0; localparam active = 1'b1; always @* begin ns = cs; data_valid = 1'b0; case(cs) idle: begin
42
if(ready) begin ns = active; data_valid = 1'b1 ; end end active: begin if((~fifo_full) && (~ready)) begin ns = idle; data_valid = 1'b0; end else data_valid = 1'b1; end default: cs = idle; endcase end always @(posedge clk or negedge reset_n) begin if (~reset_n) cs <= idle; else cs <= ns; end endmodule //***********************************************************//
43