Summer Training (03rd Aug, 2011 to 23rd Aug, 2011) FPGA Programming using Verilog HDL Language Training Manual 1 Training Coordinator Er. Sidhartha Sankar Rout Lecturer, ECE Department, BIT, Bhubaneswar 2 Dedicated to, ~~~Maa~~~ 3 Contents: i) ii) iii) iv) v) vi) vii) viii) Acknowledgement……………………………. p.5 Course Objective………………………………. p.6 ISE 10.1………………………………………... p.07 to p.22 Verilog Programming Language………............. p.23 to p.61 Function and Task………………………........... p.61 to p.64 Modeling Finite State Machine……………….. p.64 to p.67 Writing Test Benches…………………………. p.67 to p.73 Basics of FPGA……………………………….. p.74 to p.75 4 Acknowledgement We convey our heartiest gratitude to Prof. Rabi N Mahapatra (Chairman, BIT) for his words of inspiration. He willingly extracted time from his busy schedule while helping us technically by providing esteemed tutorials (of his own that he uses to teach undergraduates of Texas A&M Univ., USA) regarding FPGA programming. We are highly thankful to Mr. Arun Prusty (H.O.D, ECE Dept, BIT) and the system support team for their full time support in configuring ISE and ADEPT to all the systems. The successful design of this training manual would not have happened without the full time endeavor of Mr. Prabhas Nanda (Placement & Training Officer). We tried our level best in designing an error free manual for the trainees. We welcome everyone’s feedback regarding improvement of this manual. Sidhartha Sankar Rout Lecturer, ECE Dept, BIT. Dharanidhar Dang Lecturer, ECE Dept, BIT. 5 COURSE OBJECTIVE Create and implement designs by using the ISE software design environment and Basys-2 Spartan3E FPGA board. Verilog code for synthesis Functions and Tasks Creating Finite State Machine (FSM) by using Verilog Verilog test fixtures for simulation Introduction to FPGA Project Work 6 1. ISE 10.1 Before you start using the FPGA board you will have to be familiar with ISE 10.1.It would lead you to create, verify and implement logic on FPGA board. This tutorial contains the following sections: • “Getting Started” • “Create a New Project” • “Create an HDL Source” • “Design Simulation” • “Create Timing Constraints” • “Implement Design and Verify Constraints” • “Reimplement Design and Verify Pin Locations” • “Download Design to the Spartan™-3 Demo Board” For an in-depth explanation of the ISE design tools, see the ISE In-Depth Tutorial on the Xilinx® web site at: http://www.xilinx.com/support/techsup/tutorials/ 1.1 GETTING STARTED Starting the ISE Software To start ISE, double-click the desktop icon, or start ISE from the Start menu by selecting: Start → All Programs →Xilinx ISE Design Suite 10.1→ISE→ Project Navigator Accessing Help At any time you can access online help for additional information about the ISE software and related tools. To open Help, do either of the following: Press F1 to view Help for the specific tool or function that you have selected or highlighted. Launch the ISE Help Contents from the Help menu. It contains information about creating and maintaining your complete design flow in ISE. 7 1.2 CREATE A NEW PROJECT Create a new ISE project which will target the FPGA device. To create a new project: 1. Select File > New Project... The New Project Wizard appears. 2. Type tutorial (any Project name) in the Project Name field. 3. Enter or browse to a location (directory path) for the new project. A tutorial subdirectory is created automatically. 4. Verify that HDL is selected from the Top-Level Source Type list. 5. Click Next to move to the device properties page. 6. Fill in the properties in the table as shown below: Product Category: All Family: Spartan3E Device: XC3S100E Package: CP132 Speed Grade: -5 Top-Level Source Type: HDL Synthesis Tool: XST (VHDL/Verilog) Simulator: ISE Simulator (VHDL/Verilog) Preferred Language: Verilog Verify that Enable Enhanced Design Summary is selected. Leave the default values in the remaining fields. When the table is complete, your project properties will look like the following: 7. Click Next to proceed to the Create New Source window in the New Project Wizard. At the end of the next section, your new project will be complete. 8 1.3 CREATE AN HDL SOURCE In this section, you will create the top-level HDL file for your design. Determine the language as Verilog to use for the tutorial. Create the top-level Verilog source file for the project as follows: Click New Source in the New Project dialog box. Select Verilog Module as the source type in the New Source dialog box. Type in the file name counter. Verify that the Add to Project checkbox is selected. Click Next. Declare the ports for the counter design by filling in the port information as shown below: Click Next, then Finish in the New Source Information dialog box to complete the new source file template. Click Next, then Next, then Finish. The source file containing the counter module displays in the Workspace, and the counter displays in the Sources tab, as shown below: 9 Using Language Templates (Verilog) The next step in creating the new source is to add the behavioral description for counter. Use a simple counter code example from the ISE Language Templates and customize it for the counter design. Place the cursor on the line below the output [3:0] COUNT_OUT; statement. Open the Language Templates by selecting Edit → Language Templates… You can tile the Language Templates and the counter file by selecting Window → Tile Vertically to make them both visible. Using the “+” symbol, browse to the following code example: Verilog → Synthesis Constructs → Coding Examples → Counters → Binary → Up/Down Counters → Simple Counter With Simple Counter selected, select Edit → Use in File. This step copies the template into the counter source file. Close the Language Templates. Final Editing of the Verilog Source To declare and initialize the register that stores the counter value, modify the declaration statement in the first line of the template as follows: replace “reg [<upper>:0] <reg_name>;” with “reg [3:0] count_int = 0;” Customize the template for the counter design by replacing the port and signal name. 10 o replace all occurrences of <clock> with CLOCK o replace all occurrences of <up_down> with DIRECTION o replace all occurrences of <reg_name> with count_int Add the following line just above the endmodule statement to assign the register value to the output port: assign COUNT_OUT = count_int; Save the file by selecting File → Save. When you are finished, the code for the counter will look like the following: module counter(CLOCK, DIRECTION, COUNT_OUT); input CLOCK; input DIRECTION; output [3:0] COUNT_OUT; ); reg [3:0] count_int = 0; always @(posedge CLOCK) if (DIRECTION) count_int <= count_int + 1; else count_int <= count_int - 1; assign COUNT_OUT = count_int; endmodule Checking the Syntax of the New Counter Module When the source files are complete, check the syntax of the design to find errors. Verify that Implementation is selected from the drop-down list in the Sources window. Select the counter design source in the Sources window to display the related processes in the Processes window. Click the “+” next to the Synthesize-XST process to expand the process group. Double-click the Check Syntax process. You must correct any errors found in your source files. You can check for errors in the Console tab of the Transcript window. If you continue without valid syntax, you will not be able to simulate or synthesize your design. Close the HDL file. 1.4 DESIGN SIMULATION Create a test bench waveform containing input stimulus where you can verify the functionality of the counter module. The test bench waveform is a graphical view of a test bench. Create the test bench waveform as follows: Select the counter HDL file in the Sources window. Create a new test bench source by selecting Project → New Source. In the New Source Wizard, select Test Bench Waveform as the source type, and type counter_tbw in the File Name field. Click Next. The Associated Source page shows that you are associating the test bench waveform with the source file counter. Click Next. 11 The Summary page shows that the source will be added to the project, and it displays the source directory, type, and name. Click Finish. You need to set the clock frequency, setup time and output delay times in the Initialize Timing dialog box before the test bench waveform editing window opens. The requirements for this design are the following: The counter must operate correctly with an input clock frequency = 25 MHz. The DIRECTION input will be valid 10 ns before the rising edge of CLOCK. The output (COUNT_OUT) must be valid 10 ns after the rising edge of CLOCK. The design requirements correspond with the values below. Fill in the fields in the Initialize Timing dialog box with the following information: Clock High Time: 20 ns. Clock Low Time: 20 ns. Input Setup Time: 10 ns. Output Valid Delay: 10 ns. Offset: 0 ns. Global Signals: GSR (FPGA) Note: When GSR (FPGA) is enabled, 100 ns. is added to the Offset value automatically. Initial Length of Test Bench: 1500 ns. Leave the default values in the remaining fields. Click Finish to complete the timing initialization. The blue shaded areas that precede the rising edge of the CLOCK correspond to the Input Setup Time in the Initialize Timing dialog box. Toggle the DIRECTION port to define the input stimulus for the counter design as follows: 12 o Click on the blue cell at approximately the 300 ns to assert DIRECTION high so that the counter will count up. o Click on the blue cell at approximately the 900 ns to assert DIRECTION low so that the counter will count down. Save the waveform. In the Sources window, select the Behavioral Simulation view to see that the test bench waveform file is automatically added to your project. Close the test bench waveform. Simulating Design Functionality Verify that the counter design functions as you expect by performing behavior simulation as follows: Verify that Behavioral Simulation and counter_tbw are selected in the Sources window. In the Processes tab, click the “+” to expand the Xilinx ISE Simulator process and double-click the Simulate Behavioral Model process. The ISE Simulator opens and runs the simulation to the end of the test bench. To view your simulation results, select the Simulation tab and zoom in on the transitions. The simulation waveform results will look like the following: 13 Verify that the counter is counting up and down as expected. Close the simulation view. If you are prompted with the following message, “You have an active simulation open. Are you sure you want to close it?” Click Yes to continue. You have now completed simulation of your design using the ISE Simulator. 1.5 CREATE TIMING CONSTRAINTS Specify the timing between the FPGA and its surrounding logic as well as the frequency the design must operate at internal to the FPGA. The timing is specified by entering constraints that guide the placement and routing of the design. It is recommended that you enter global constraints. The clock period constraint specifies the clock frequency at which your design must operate inside the FPGA. The offset constraints specify when to expect valid data at the FPGA inputs and when valid data will be available at the FPGA outputs. Entering Timing Constraints To constrain the design do the following: Select Implementation from the drop-down list in the Sources window. Select the counter HDL source file. Click the “+” sign next to the User Constraints processes group, and double-click the Create Timing Constraints process. ISE runs the Synthesis and Translate steps and automatically creates a User Constraints File (UCF). You will be prompted with the following message: Click Yes to add the UCF file to your project. The counter.ucf file is added to your project and is visible in the Sources window. The Xilinx Constraints Editor opens automatically. Note: You can also create a UCF file for your project by selecting Project → Create New Source. 14 In the Timing Constraints dialog, enter the following in the Period, Pad to Setup, and CLock to Pad fields: ♦ Period: 40 ♦ Pade to Setup: 10 ♦ Clock to Pad: 10 Press Enter. After the information has been entered, the dialog should look like what is shown below. Select Timing Constraints under Constraint Type in the Timing Constraints tab and the newly created timing constraints are displayed as follows: 15 Save the timing constraints. If you are prompted to rerun the TRANSLATE or XST step, click OK to continue. 9. Close the Constraints Editor. 1.6 IMPLEMENT DESIGN AND VERIFY CONSTRAINTS Implement the design and verify that it meets the timing constraints specified in the previous section. Implementing the Design Select the counter source file in the Sources window. Open the Design Summary by double-clicking the View Design Summary process in the Processes tab. Double-click the Implement Design process in the Processes tab. Notice that after Implementation is complete, the Implementation processes have a green check mark next to them indicating that they completed successfully without Errors or Warnings. 16 Post Implementation Design Summary Locate the Performance Summary table near the bottom of the Design Summary. Click the All Constraints Met link in the Timing Constraints field to view the Timing Constraints report. Verify that the design meets the specified timing requirements. All Constraints Met Report Close the Design Summary. Assigning Pin Location Constraints Specify the pin locations for the ports of the design so that they are connected correctly on the Spartan-3E Basys-2 board. To constrain the design ports to package pins, do the following: Verify that counter is selected in the Sources window. Double-click the Floorplan Area/IO/Logic - Post Synthesis process found in the User Constraints process group. The Xilinx Pinout and Area Constraints Editor (PACE) opens. Select the Package View tab. In the Design Object List window, enter a pin location for each pin in the Loc column using the following information: ♦ CLOCK input port connects to FPGA pin C8 (RCCLK signal on board) ♦ COUNT_OUT<0> output port connects to FPGA pin M5 (LD0 signal on board) ♦ COUNT_OUT<1> output port connects to FPGA pin M11 (LD1 signal on board) ♦ COUNT_OUT<2> output port connects to FPGA pin P7 (LD2 signal on board) ♦ COUNT_OUT<3> output port connects to FPGA pin P6 (LD3 signal on board) ♦ DIRECTION input port connects to FPGA pin P11 (SW0 signal on board) 17 Select File → Save. You are prompted to select the bus delimiter type based on the synthesis tool you are using. Select XST Default <> and click OK. Close PACE. Notice that the Implement Design processes have an orange question mark next to them, indicating they are out-of-date with one or more of the design files. This is because the UCF file has been modified. 1.7 REIMPLEMENT DESIGN AND VERIFY PIN LOCATIONS Reimplement the design and verify that the ports of the counter design are routed to the package pins specified in the previous section. First, review the Pinout Report from the previous implementation by doing the following: Open the Design Summary by double-clicking the View Design Summary process in the Processes window. Select the Pinout Report and select the Signal Name column header to sort the signal names. Notice the Pin Numbers assigned to the design ports in the absence of location constraints. 18 Reimplement the design by double-clicking the Implement Design process. Select the Pinout Report again and select the Signal Name column header to sort the signal names. Verify that signals are now being routed to the correct package pins. Close the Design Summary. 19 1.8 DOWNLOAD DESIGN TO THE SPARTA-3E BASYS-2 BOARD This is the last step in the design verification process. This section provides simple instructions for downloading the counter design to the Spartan-3E Basys-2 board. Select Implementation from the drop-down list in the Sources window. Select counter in the Sources window. In the Process window, double-click the Configure Target Device process. Click OK in the following warning. Click finish on the iMPACT window 20 Now connect the Basys-2 board to the PC through the USB cable. Power on the board through the power sliding switch. Then open the Adept software. Then click “Initialize Chain”. To program a device: Click the Browse button next to the device icon in the window. An Open dialog box appears. Select the appropriate configuration file (.bit file) in the Open dialog box and click the Open button. Adept displays a history of configuration files in the drop-down list box next to the device. Click the Program button or right-click on the device icon and select Program Device. 21 The Test Tab Adept can run simple diagnostic tests on boards so that you can verify that the board is functioning properly. To begin a test of the board, click the Start Test button. This automatically loads a diagnostic test configuration to the FPGA. The red button changes to green. You see "PASS" and "128" appear alternately on the 7-seg display. Play with the 8 switches and notice that the 8 singular LEDs on the board follow the switches. The test can be stopped any time by pressing the Stop Test button, switching to a different tab, or changing the connected device. 22 VERILOG PROGRAMMING LANGUAGE 23 1. INTRODUCTION The complexity of hardware design has grown exponentially in the last decade. The exponential growth is fueled by new advances in design technology as well as the advances in fabrication technology. The usage of Hardware Description Language (HDL) to model, simulate, synthesize, analyze, and test the design has been a cornerstone of this rapid development. Verilog HDL is a Hardware Description Language. A Hardware Description Language is a language used to describe a digital system: for example, a network switches, a microprocessor or a memory or a simple flip-flop. HDL allows the design to be simulated earlier in the design cycle in order to correct errors or experiment with different architectures. Designs described in HDL are technology-independent, easy to design and debug, and are usually more readable than schematics, particularly for large circuits. One may describe a digital system at several levels. For example, an HDL might describe the layout of the wires, resistors and transistors on an Integrated Circuit (IC) chip, i.e., the switch level. Or, it might describe the logical gates and flip flops in a digital system, i.e., the gate level. An even higher level describes the registers and the transfers of vectors of information between registers. This is called the Register Transfer Level (RTL). Verilog supports all of these levels. 1.1 WHAT IS VERILOG? Verilog is one of the two major Hardware Description Languages (HDL) used by hardware designers in industry and academia. VHDL is the other one. The industry is currently split on which is better. Many feel that Verilog is easier to learn. Verilog is very C-like and liked by electrical and computer engineers as most learn the C language in college. VHDL is very Adalike and most engineers have no experience with Ada. Verilog was introduced in 1985 by Gateway Design System Corporation, now a part of Cadence Design Systems. Until May, 1990, with the formation of Open Verilog International (OVI), Verilog HDL was a proprietary language of Cadence. Cadence was motivated to open the language to the Public Domain with the expectation that the market for Verilog HDL-related software products would grow more rapidly with broader acceptance of the language. Verilog HDL allows a hardware designer to describe designs at a high level of abstraction such as at the architectural or behavioral level as well as the lower implementation levels (i.e., gate and switch levels) leading to Very Large Scale Integration (VLSI) Integrated Circuits (IC) layouts and chip fabrication. A primary use of HDLs is the simulation of designs before the designer must commit to fabrication. 1.2 WHY USE VERILOG HDL? Digital systems are highly complex. At their most detailed level, they may consist of millions of elements, i.e., transistors or logic gates. Therefore, for large digital systems, gate-level design is dead. For many decades, logic schematics served as the bridge language of logic design, but not anymore. Today, hardware complexity has grown to such a degree that a schematic with logic gates is almost useless as it shows only a web of connectivity and not the functionality of design. 24 Since the 1970s, Computer engineers and electrical engineers have moved toward hardware description languages (HDLs). The most prominent modern HDLs in industry are Verilog and VHDL. Verilog is the top HDL used by over 10,000 designers at such hardware vendors as Sun Microsystems, Apple Computer and Motorola. The Verilog language provides the digital designer with a means of describing a digital system at a wide range of levels of abstraction and at the same time provides access to computer-aided design tools to aid in the design process at these levels. Verilog allows hardware designers to express their design with behavioral constructs, deterring the details of implementation to a later stage of design. An abstract representation helps the designer explore architectural alternatives through simulations and to detect design bottlenecks before detailed design begins. Computer-aided-design tools, i.e., programs, exist which will “compile” programs in the Verilog notation to the level of circuits consisting of logic gates and flip flops. One could then go to the lab and wire up the logical circuits and have a functioning system. And, other tools can “compile” programs in Verilog notation to a description of the integrated circuit masks for very large scale integration (VLSI). Verilog also allows the designer to specific designs at the logical gate level using gate constructs and the transistor level using switch constructs. Verilog allows engineers to optimize the logical circuits and VLSI layouts to maximize speed and minimize area of the VLSI chip. 1.3 HIERARCHICAL MODELING CONCEPTS Before we discuss the details of the Verilog language, we must first understand basic hierarchical modeling concepts in digital design. Design Methodologies There are two basic types of digital design methodologies: a top-down design methodology and a bottom-up design methodology. In a top-down design methodology, we define the top-level block and identify the sub-blocks necessary to build the top-level block. We further subdivide the sub-blocks until we come to leaf cells, which are the cells that cannot further be divided. Figure 1-1 shows the top-down design process. Figure 1-1. Top-down Design Methodology 25 In a bottom-up design methodology, we first identify the building blocks that are available to us. We build bigger cells, using these building blocks. These cells are then used for higher-level blocks until we build the top-level block in the design. Figure 1-2. Bottom-up Design Methodology Typically, a combination of top-down and bottom-up flows is used. Design architects define the specifications of the top-level block. Logic designers decide how the design should be structured by breaking up the functionality into blocks and sub-blocks. At the same time, circuit designers are designing optimized circuits for leaf-level cells. They build higher-level cells by using these leaf cells. The flow meets at an intermediate point where the switch-level circuit designers have created a library of leaf cells by using switches, and the logic level designers have designed from top-down until all modules are defined in terms of leaf cells. 26 2. VERILOG FOR DESCRIPTION 2.1 MODULE DEFINITION Verilog provides the concept of a module. A module is the basic building block in Verilog. A module can be an element or a collection of lower-level design blocks. Typically, elements are grouped into modules to provide common functionality that is used at many places in the design. A module provides the necessary functionality to the higher-level block through its port interface (inputs and outputs), but hides the internal implementation. This allows the designer to modify module internals without affecting the rest of the design. Modules communicate with the outside world through input, output and bi-directional (inout) ports. In Verilog, a module is declared by the keyword module. A corresponding keyword endmodule must appear at the end of the module definition. Each module must have a module_name, which is the identifier for the module, and a port list, which describes the input and output terminals of the module. Design functionality is implemented inside module, after port declaration. The design functionality implementation part is represented as “body” here. Syntax: 27 Example: module MAT (enable, data, all_zero, result, status); input input enable; [3:0] data; output // scalar input // vector input all_zero; // scalar output output [3:0] result; // vector output Inout [1:0] // bi-directional port status …… endmodule To make code easy to read, use self-explanatory port names. For the purpose of conciseness, use short port names. In vector port declaration, MSB can be smaller index. For e.g. output [0:3] result (result[0] is the MSB) 2.1.1 THE MODULE NAME The module name, formally called an identifier serves documentation purpose. Based on this in your module names you should use such noun phrases that best describes what the system is doing. Each identifier in Verilog, including module names must follow these rules: It can be composed of letters, digits, dollar sign ($), and underscore characters (_) only. It must start with a letter or underscore. No spaces are allowed inside an identifier. Upper and lower case characters are distinguished (Verilog is case sensitive) Reserved keywords cannot be used as identifiers. Ex: Counter_4Bit, ALU, Receiver, UART_Transmit 2.1.2 PORTS Ports provide a means for a module to communicate through input and output. Every port in the port list must be declared as input, output or inout, in the module. All ports declared as one of the above is assumed to be a wire by default, to declare it otherwise it is neccessary to declare it again. For example in the D-type flip flop we want the ouput to hold on to its value until the next clock edge so it has to be a register. 28 module d_ff(q, d, reset, clock); output q; // all ports must be declared input d, reset, clock; // as input or output reg q; // the ports can be declared again as required. Note: by convention, outputs of the module are always first in the port list. This convention is also used in the predefined modules in Verilog. Input ports: In an inner module inputs must always be of a net type, since values will be driven into them. In the outer module the input may be a net type or a reg. Output ports: In an inner module outputs can be of a net type or a reg. In an outer module the output must be of a net type since values will be driven into them by the inner module. Inout ports: Inouts must always be of a net type. Port Matching: When calling a module the width of each port must be the same, eg, a 4-bit register cannot be matched to a 2-bit register. However, output ports may remain unconnected, by missing out their name in the port list. This would be useful if some outputs were for debugging purposes or if some outputs of a more general module were not required in a particular context. However input ports cannot be omitted. For example: d_ff dff0( , d, reset, clock); // the output (q) has been omitted // the comma is ESSENTIAL 2.2 ONE LANGUAGE, MANY CODING STYLES Verilog is both a behavioral and a structural language. Internals of each module can be defined at four levels of abstraction, depending on the needs of the design. The module behaves identically with the external environment irrespective of the level of abstraction at which the module is described. The internals of the module are hidden from the environment. Thus, the level of abstraction to describe a module can be changed without any change in the environment. 2.2.1 BEHAVIORAL OR ALGORITHMIC LEVEL This is the highest level of abstraction provided by Verilog HDL. A module can be implemented in terms of the desired design algorithm without concern for the hardware implementation details. Designing at this level is very similar to C programming. 29 2.2.2 DATAFLOW LEVEL At this level, the module is designed by specifying the data flow. The designer is aware of how data flows between hardware registers and how the data is processed in the design. Here a circuit can be specified with assignment statements and can be expressed as a list of outputs and expressions that transform the input values to the desired outputs. The expressions can be based on a broad range of operators such as logical operators, bit-wise, reduction, arithmetic, conditional and concatenation operators. All of them can be applied to the assignment statements making data flow style more universal than logical equations. 30 2.2.3 GATE LEVEL or STRUCTURAL LEVEL The module is implemented in terms of logic gates and interconnections between these gates. Design at this level is similar to describing a design in terms of a gate-level logic diagram. It resembles a schematic drawing with components connected with signals. The functionality of the design is hidden inside the component. The components in structural specification can be either primitives or instantiated modules. The former ones are such simple components as gates and flip-flops (or other small scale blocks), and the latter ones can be of any complexity. A change in the value of any input signal of a component activates the component. If two or more components are activated concurrently, they will perform their actions concurrently as well. 31 2.2.4 SWITCH LEVEL This is the lowest level of abstraction provided by Verilog. A module can be implemented in terms of transistors, switches, storage nodes, and the interconnections between them. Design at this level requires knowledge of switch-level implementation details. Verilog allows the designer to mix and match all four levels of abstractions in a design. In the digital design community, the term register transfer level (RTL) is frequently used for a Verilog description that uses a combination of behavioral and dataflow constructs and is acceptable to 32 logic synthesis tools. If a design contains four modules, Verilog allows each of the modules to be written at a different level of abstraction. As the design matures, most modules are replaced with gate-level implementations. Normally, the higher the level of abstraction, the more flexible and technology-independent the design. As one goes lower toward switch-level design, the design becomes technology-dependent and inflexible. A small modification can cause a significant number of changes in the design. Consider the analogy with C programming and assembly language programming. It is easier to program in a higher-level language such as C. The program can be easily ported to any machine. However, if you design at the assembly level, the program is specific for that machine and cannot be easily ported to another machine. 2.3 MODULE INSTANTIATION A module provides a template from which you can create actual objects. When a module is invoked, Verilog creates a unique object from the template. Each object has its own name, variables, parameters, and I/O interface. The process of creating objects from a module template is called instantiation, and the objects are called instances. A module can be instantiated in another module thus creating hierarchy. Syntax: Module_name Instance_name (Port_Association_List) Module instantiation consists of module_name followed by instance_name and port_association_list. Need of instance_name is, we can have multiple instance of same module in the same program. Instance name should be unique for each instance of the same module. Port_association_list shows how ports are mapped. Port mapping can be done in two different ways i.e. “Port mapping by order” and “Port mapping by name’. Let us take an example of 4-bit adder for explaining module instantiation. Example of 4-bit adder: module fulladder_4bit(sum, cout, a, b, cin); //input output port declarations output [3:0] sum; output cout; input [3:0] a, b; input cin; wire c1, c2, c3; // Instantiate four 1-bit full adders fulladd f0 (sum[0], c1, a[0], b[0], cin); fulladd f1 (sum[1], c2, a[1], b[1], c1); fulladd f2 (sum[2], c3, a[2], b[2], c2); fulladd f3 (sum[3], cout, a[3], b[3], c3); endmodule; 33 Module for 1-bit adder is fulladd. This 1-bit adder is instantiated ‘4’ times to get functionality of 4-bit adder i.e. fulladder_4bit. Each instance of full adder has different instance name and port association list. AN EXAMPLE: SYNCHRO DFF1 DFF2 C1_ASYNC ASYNC D D DFF CLOCK Q DFF CLK Q SYNC CLK Figure shows an example for module instantiation. Figure shows module “SYNCHRO” which consists of 2 ‘D’ flip-flops and are connected in serial fashion. Module “SYNCHRO” has 2 input ports “ASYNC” and “CLOCK” and 1 output port “SYNC”. The first ‘D’ flip-flop has 2 inputs “ASYNC” and “CLOCK” and 1 output “C1_ASYNC”. The second ‘D’ flip-flop 2 inputs “C1_ASYNC” and “CLOCK” and 1 output “SYNC”. 2.3.1 MODULE PORT CONNECTION BY ORDER module SYNCHRO(ASYNC,SYNC,CLOCK); input ASYNC; input CLOCK; output SYNC; wire C1_ASYNC; DFF DFF1 (C1_ASYNC, ASYNC, CLOCK); DFF DFF2 (SYNC, C1_ASYNC, CLOCK); //DFF DFF1 (ASYNC, C1_ASYNC, CLOCK); //DFF DFF2 (SYNC, C1_ASYNC, CLOCK); endmodule Here first instance name of ‘D’ flip-flop is “DFF1” and second instance name is “DFF2”. In this module ports are connected by order. Order of ports in instantiation of DFF1 and DFF2 is 34 same as order of ports in DFF. If the number of ports increased, then it is very difficult to do “module ports connection by order”. 2.3.2 MODULE PORT CONNECTION BY NAME Module SYNCHRO (ASYNC, SYNC, CLOCK); input ASYNC; input CLOCK; output SYNC; wire C1_ASYNC; DFF DFF1 (.D (ASYNC), .CLK (CLOCK), .Q (C1_ASYNC)); DFF DFF2 (.D (C1_ASYNC), .Q (SYNC), .CLK (CLOCK)); endmodule; In this module ports are connected by Name. Order of ports in instantiation of DFF1 and DFF2 is different from order of ports in DFF. In this ‘.’ Is used to represent port name followed by associated port name in small brackets i.e. “()”. Advantage of using “port connection by name” is, it is easy to port map for large number of ports in a design. Note: Always connect ports by name. Inside the same module, instance names of particular module should be different. In the same way, inside the same module, instance name of different modules should be different. For example: module A(); DFF DFF1(); DFFE DFF1(); endmodule IS NOT ALLOWED Above example shows module ‘A’ is top-level module, in that ‘2’ different modules “DFF” and “DFFE” are instantiated with same instance name “DFF1”. Since instance name is same, this is not allowed. 35 NOT ALLOWED ALLOWED Figure shows a Design hierarchy. In this “ABC” is the top-level module. Two scenarios are shown here. In first scenario “ABC” is the top-level module and “SYNCHRO1” and “SYNCHRO2” are instance names. Under “SYNCHRO1”, “DFF1” and “DFF2” are instance names. Under “SYNCHRO2”, “DFF1” and “DFF2” are instance names. In this instance names are different at same hierarchy. Hence this is allowed in scenario-1. In second scenario “ABC” is the top-level module and “SYNCHRO1” and “SYNCHRO1” are instance names. Under “SYNCHRO1”, “DFF1” and “DFF2” are instance names. Under another “SYNCHRO1”, “DFF1” and “DFF2” are instance names. In this instance names “SYNCHRO1” and “SYNCHRO1” are same at the same hierarchy. Hence this is not allowed in scenario2 Note: At the same hierarchy the instance name should not be same. 2.4 LEXICOGRAPHY Verilog, like any high level language has a number of tokens like comments, operators, numbers, strings, identifiers and keywords. 2.4.1 COMMENTS Comments can be inserted in the code for readability and documentation. There are two ways to write comments. A one line comment starts with "//". Verilog skips from that point to the end of line. A multiple-line comment block comment starts with "/*" and ends with "*/". Multiple-line comments cannot be nested. However, one-line comments can be embedded in multiple-line comments. 36 a = b && c; // This is a one-line comment /* This is a multiple line comment */ /* This is /* an illegal */ comment */ /* This is //a legal comment */ 2.4.2 OPERATORS Verilog has three types of operators; they take either one, two or three operands. Unary operators appear on the left of their operand, binary in the middle, and ternary separates its three operands by two operators. clock = ~clock; // ~ is the unary bitwise negation operator, clock is the operand c = a || b; // || is the binary logical or, a and b are the operands r = s ? t : u; // ?: is the ternary conditional operator, which // reads r = [if s is true then t else u] 2.4.3 NUMBERS Integers Integers can be in binary ( b or B ), decimal ( d or D ), hexidecimal ( h or H ) or octal ( o or O ). Numbers are specified by 1. <size>'<base><number> : for a full description 2. <base><number>: this is given a default size which is machine dependant but at least 32 bits. 3. <number> : this is given a default base of decimal The size specifies the exact number of bits used by the number. For example, a 4 bit binary will have 4 as the size specification and a 4 digit hexadecimal will have 16 as the size specification since each hexadecimal digit requires 4 bits. 8'b10100010 // 8 bit number in binary representation 8'hA2 // 8 bit number in hexadecimal representation X and Z values x represents an unknown, and z a high impedance value. An x declares 4 unknown bits in hexadecimal, 3 in octal and 1 in binary. z declares high impedance values similarly. 4'b10x0 // 4 bit binary with 2nd least sig. fig. unknown 4'b101z // 4 bit binary with least sig. fig. of high impedance 12'dz // 12 bit decimal high impedance number 37 Negative numbers A number can be declared to be negative by putting a minus sign in front of the <size>. It must not appear between the <size> and <base>, nor between the <base> and the number. -8'd5 // 2's compliment of 5, held in 8 bits 8'b-5 // illegal syntax Underscore Underscores can be put anywhere in a number, except the beginning, to improve readability. 16'b0001_1010_1000_1111 // use of underscore to improve readability 8'b_0001_1010 // illegal use of underscore Real Real numbers can be in either decimal or scientific format, if expressed in decimal format they must have at least one digit either side of the decimal point. 1.8 3_2387.3398_3047 3.8e10 // e or E for exponent 2.1e-9 3. // illegal 2.4.4 STRINGS Strings are delimited by " ... ", and cannot be on multiple lines. "hello world"; // legal string "good b y e wo rld"; // illegal string 2.4.5 IDENTIFIERS Identifiers are user-defined words for variables, function names, module names, block names and instance names. Identifiers begin with a letter or underscore (Not with a number or $) and can include any number of letters, digits and underscores. Identifiers in Verilog are case-sensitive. _ABC_ /* is not the same as */ _abc_ 38 2.4.6 VERILOG KEYWORDS These are words that have special meaning in Verilog. Some examples are assign, case, while, wire, reg, and, or, nand, module etc. They should not be used as identifiers. All keywords must be in lower case 2.5 DATA TYPES 2.5.1 DATA VALUES Value Level Condition in Hardware Circuits (Logic State) 0 Logic Zero/ False Condition/ Ground 1 Logic One/ True Condition/ Power x Unknown/ Uninitialized z High Impedance/ Floating Values ‘x’ and ‘z’ are case insensitive. 2.5.2 NETS Keywords: wire, tri, supply0, supply1 Default value: z Default size: 1 bit Nets represent the continuous updating of outputs with respect to their changing inputs. For example in the figure below, c is connected to a by a not gate. If c is declared and initialized as shown, it will continuously be driven by the changing value of a, its new value will not have to be explicitly assigned to it. If the drivers of a wire have the same value, the wire assumes this value. If the drivers have different values it chooses the strongest, if the strengths are the same the wire assumes the value of unknown, ‘x’. If no driver is connected it defaults to value of ‘z’. 39 The net types wire and tri shall be identical in their syntax and functions. A wire net can be used for nets that are driven by a single gate or continuous assignment. The tri net type can be used where multiple drivers drive a net. Supply0 and supply1 define wires tied to logic 0 (ground) and logic 1 (power), respectively. supply0 my_gnd; // equivalent to a wire assigned 0 supply1 a, b; 2.5.3 REGISTERS Keywords: reg Default value: x Default size: 1 bit Registers represent data storage elements. Registers retain value until another value is placed onto them. Do not confuse the term registers in Verilog with hardware registers built from edgetriggered flip-flops in real circuits. In Verilog, the term register merely means a variable that can hold a value. Unlike a net, a register does not need a driver. Verilog registers do not need a clock as hardware registers do. Values of registers can be changed anytime in a simulation by assigning a new value to the register. Register data types are commonly declared by the keyword reg. The default value for a reg data type is x. reg a; // single 1-bit register variable reg [7:0] b; // an 8-bit vector; a bank of 8 registers. An example of how registers are used is shown. Any object assigned within a Verilog procedural block (always and initial) must be declared as a reg data type. reg reset; // declare a variable reset that can hold its value initial // this construct will be discussed later begin reset = 1'b1; //initialize reset to 1 to reset the digital circuit. #100 reset = 1'b0; // after 100 time units reset is deasserted. end Comparing reg versus wire: Comparison between reg and wire can be done through an example for multiplexure. module MUX21 (input A, B, SEL, output wire OUT1); assign OUT1 = (A & SEL) | (B & ~SEL); endmodule 40 OUT1 is a wire by default module MUX21 (input A, B, SEL, output reg OUT1); always @ (A, B, SEL) if (SEL) OUT1 = A; else OUT1 = B; endmodule OUT1 must be declared as reg 2.5.4 VECTORS and ARRAYS Nets or reg data types can be declared as vectors (multiple bit widths). If bit width is not specified, the default is scalar (1-bit). wire a; // scalar net variable wire [7:0] bus; // 8-bit bus wire [31:0] busA,busB,busC; // 3 buses of 32-bit width. reg clock; // scalar register reg [0:40] virtual_addr; // Vector register, virtual address 41 bits wide Vectors can be declared at [high# : low#] or [low# : high#], but the left number in the squared brackets is always the most significant bit of the vector. In the example shown above, bit 0 is the most significant bit of vector virtual_addr. Vector Part Select: For the vector declarations shown above, it is possible to address bits or parts of vectors. busA[7] // bit # 7 of vector busA busB[2:0] /* Three least significant bits of vector bus. Using busB[0:2] is illegal because the significant bit should always be on the left of a range specification. */ virtual_addr[0:1] // Two most significant bits of vector virtual_addr Multi-dimensional arrays can also be declared with any number of dimensions. reg [4:0] port_id[0:7]; // Array of 8 port_ids; each port_id is 5 bits wide 41 2.5.5 MEMORIES In digital simulation, one often needs to model register files, RAMs, and ROMs. Memories are modeled in Verilog simply as a one-dimensional array of registers. Each element of the array is known as an element or word and is addressed by a single array index. Each word can be one or more bits. It is important to differentiate between n 1-bit registers and one n-bit register. A particular word in memory is obtained by using the address as a memory array subscript. reg mem1bit[0:1023]; // Memory mem1bit with 1K 1-bit words reg [7:0] membyte[0:1023]; // Memory membyte with 1K 8-bit words(bytes) membyte[511] // Fetches 1 byte word whose address is 511. 2.5.6 “INTEGER”, “REAL”, AND “TIME” REGISTER DATA TYPES Integer, real, and time register data types are supported in Verilog. Integer: An integer is a general purpose register data type used for manipulating quantities. Integers are declared by the keyword integer. Although it is possible to use reg as a general-purpose variable, it is more convenient to declare an integer variable for purposes such as counting. The default width for an integer is the host-machine word size, which is implementation-specific but is at least 32 bits. Registers declared as data type reg store values as unsigned quantities, whereas integers store values as signed quantities. integer counter; // general purpose variable used as a counter. initial counter = -1; // A negative one is stored in the counter Real: Real number constants and real register data types are declared with the keyword real. They can be specified in decimal notation (e.g., 3.14) or in scientific notation (e.g., 3e6, which is 3x106). Real numbers cannot have a range declaration, and their default value is 0. When a real value is assigned to an integer, the real number is rounded off to the nearest integer. real delta; // Define a real variable called delta initial begin delta = 4e10; // delta is assigned in scientific notation delta = 2.13; // delta is assigned a value 2.13 end integer i; // Define an integer i 42 initial i = delta; // i gets the value 2 (rounded value of 2.13) Time: Verilog simulation is done with respect to simulation time. A special time register data type is used in Verilog to store simulation time. A time variable is declared with the keyword time. The width for time register data types is implementation-specific but is at least 64 bits. The system function $time is invoked to get the current simulation time. time save_sim_time; // Define a time variable save_sim_time initial save_sim_time = $time; // Save the current simulation time Simulation time is measured in terms of simulation seconds. The unit is denoted by s, the same as real time. However, the relationship between real time in the digital circuit and simulation time is left to the user. 2.5.7 PARAMETERS Verilog allows constants to be defined in a module by the keyword parameter. Parameters cannot be used as variables. Parameter types and sizes can also be defined. parameter port_id = 5; // Defines a constant port_id parameter signed [15:0] WIDTH; // Fixed sign and range for parameter WIDTH 2.5.8 STRINGS Strings can be stored in reg. The width of the register variables must be large enough to hold the string. Each character in the string takes up 8 bits (1 byte). If the width of the register is greater than the size of the string, Verilog fills bits to the left of the string with zeros. If the register width is smaller than the string width, Verilog truncates the leftmost bits of the string. It is always safe to declare a string that is slightly wider than necessary. reg [8*18:1] string_value; // Declare a variable that is 18 bytes wide initial string_value = "Hello Verilog World"; // String can be stored in variable 2.6 EXPRESSIONS, OPERANDS AND OPERATORS Dataflow modeling describes the design in terms of expressions instead of primitive gates. Expressions, operators, and operands form the basis of dataflow modeling. 43 2.6.1 EXPRESSIONS Expressions are constructs that combine operators and operands to produce a result. // Examples of expressions. Combines operands and operators a^b addr1[20:17] + addr2[20:17] in1 | in2 2.6.2 OPERANDS Operands can be any one of the data types defined in Section 2.5, Data Types. Operands can be constants, integers, real numbers, nets, registers, times, bit-select (one bit of vector net or a vector register), part-select (selected bits of the vector net or register vector), and memories or function calls. integer count, final_count; final_count = count + 1; //count is an integer operand 2.6.3 OPERATORS Operators act on the operands to produce desired results. Verilog provides various types of operators. Operators can be arithmetic, logical, relational, equality, bitwise, reduction, shift, concatenation, or conditional. Operator Types and Symbols: Operator Type Arithmetic Logical Relational Equality Operator Symbol * / + % ** ! && || Operation Performed multiply divide add subtract modulus power (exponent) Number of Operands two two two two two two logical negation logical and logical or one two two > < >= <= == != === !== greater than less than greater than or equal less than or equal equality inequality case equality case inequality two two two two two two two two 44 Bitwise ~ & | ^ ^~ or ~^ Reduction & ~& | ~| ^ ^~ or ~^ Shift >> << >>> <<< bitwise negation bitwise and bitwise or bitwise xor bitwise xnor reduction and reduction nand reduction or reduction nor reduction xor reduction xnor Right shift Left shift Arithmetic right shift Arithmetic left shift {} {{}} Concatenation Replication any number any number Conditional three Concatenation Replication ?: Conditional Operator Order of Precedence: 45 one two two two two one one one one one one two two two two 2.6.3.1 Arithmetic Operators: There are two types of arithmetic operators: binary and unary. Binary operators Binary arithmetic operators are multiply (*), divide (/), add (+), subtract (-), power (**), and modulus (%). Binary operators take two operands. A = 4'b0011; B = 4'b0100; // A and B are register vectors D = 6; E = 4; F=2; // D and E are integers A * B // Multiply A and B. Evaluates to 4'b1100 D / E // Divide D by E. Evaluates to 1. Truncates any fractional part. F = E ** F; //E to the power F, yields 16 If any operand bit has a value x, then the result of the entire expression is x. Modulus operators produce the remainder from the division of two numbers. They operate similarly to the modulus operator in the C programming language. 13 % 3 // Evaluates to 1 16 % 4 // Evaluates to 0 -7 % 2 // Evaluates to -1, takes sign of the first operand 7 % -2 // Evaluates to +1, takes sign of the first operand Unary operators The operators + and - can also work as unary operators. They are used to specify the positive or negative sign of the operand. Unary + or - operators have higher precedence than the binary + or - operators. -4 // Negative 4 +5 // Positive 5 Negative numbers are represented as 2's complement internally in Verilog. 2.6.3.2 Logical Operators: Logical operators are logical-and (&&), logical-or (||) and logical-not (!). Operators && and || are binary operators. Operator ! is a unary operator. Logical operators always evaluate to a 1-bit value, 0 (false), 1 (true), or x (ambiguous). // Logical operations A = 3; B = 0; A && B // Evaluates to 0. Equivalent to (logical-1 && logical-0) A || B // Evaluates to 1. Equivalent to (logical-1 || logical-0) !A // Evaluates to 0. Equivalent to not(logical-1) // Unknowns A = 2'b0x; B = 2'b10; A && B // Evaluates to x. Equivalent to (x && logical 1) // Expressions (a == 2) && (b == 3) // Evaluates to 1 if both a == 2 and b == 3 are true. // Evaluates to 0 if either is false. 46 2.6.3.3 Relational Operators: Relational operators are greater-than (>), less-than (<), greater-than-or-equal-to (>=), and lessthan-or-equal-to (<=). If relational operators are used in an expression, the expression returns a logical value of 1 if the expression is true and 0 if the expression is false. If there are any unknown or z bits in the operands, the expression takes a value x. These operators function exactly as the corresponding operators in the C programming language. // A = 4, B = 3 // X = 4'b1010, Y = 4'b1101, Z = 4'b1xxx A <= B // Evaluates to a logical 0 A > B // Evaluates to a logical 1 Y >= X // Evaluates to a logical 1 Y < Z // Evaluates to an x 2.6.3.4 Equality Operators: Equality operators are logical equality (==), logical inequality (!=), case equality (===), and case inequality (!==). When used in an expression, equality operators return logical value 1 if true, 0 if false. These operators compare the two operands bit by bit, with zero filling if the operands are of unequal length. It is important to note the difference between the logical equality operators (==, !=) and case equality operators (===, !==). The logical equality operators (==, !=) will yield an x if either operand has x or z in its bits. However, the case equality operators (===, !== ) compare both operands bit by bit and compare all bits, including x and z. The result is 1 if the operands match exactly, including x and z bits. The result is 0 if the operands do not match exactly. Case equality operators never result in an x. // A = 4, B = 3 // X = 4'b1010, Y = 4'b1101 // Z = 4'b1xxz, M = 4'b1xxz, N = 4'b1xxx A == B // Results in logical 0 X != Y // Results in logical 1 X == Z // Results in x Z === M // Results in logical 1 (all bits match, including x and z) Z === N // Results in logical 0 (least significant bit does not match) M !== N // Results in logical 1 2.6.3.5 Bitwise Operators: Bitwise operators are negation (~), and (&), or (|), xor (^), xnor (^~, ~^). Bitwise operators perform a bit-by-bit operation on two operands. They take each bit in one operand and perform the operation with the corresponding bit in the other operand. If one operand is shorter than the other, it will be bit-extended with zeros to match the length of the longer operand. A z is treated as an x in a bitwise operation. The exception is the unary negation operator (~), which takes only one operand and operates on the bits of the single operand. 47 // X = 4'b1010, Y = 4'b1101 // Z = 4'b10x1 ~X // Negation. Result is 4'b0101 X | Y // Bitwise or. Result is 4'b1111 X ^~ Y // Bitwise xnor. Result is 4'b1000 X & Z // Result is 4'b10x0 It is important to distinguish bitwise operators ~, &, and | from logical operators !, &&, ||. Logical operators always yield a logical value 0, 1, x, whereas bitwise operators yield a bit-by-bit value. Logical operators perform a logical operation, not a bit-by-bit operation. // X = 4'b1010, Y = 4'b0000 X | Y // bitwise operation. Result is 4'b1010 X || Y // logical operation. Equivalent to 1 || 0. Result is 1. 2.6.3.6 Reduction Operators: Reduction operators are and (&), nand (~&), or (|), nor (~|), xor (^), and xnor (~^, ^~). Reduction operators take only one operand. Reduction operators perform a bitwise operation on a single vector operand and yield a 1-bit result. The logic tables for the operators are the same as shown for Bitwise Operators. The difference is that bitwise operations are on bits from two different operands, whereas reduction operations are on the bits of the same operand. Reduction operators work bit by bit from right to left. Reduction nand, reduction nor, and reduction xnor are computed by inverting the result of the reduction and, reduction or, and reduction xor, respectively. // X = 4'b1010 48 &X //Equivalent to 1 & 0 & 1 & 0. Results in 1'b0 ^X //Equivalent to 1 ^ 0 ^ 1 ^ 0. Results in 1'b0 2.6.3.7 Shift Operators: Shift operators are right shift ( >>), left shift (<<), arithmetic right shift (>>>), and arithmetic left shift (<<<). Regular shift operators shift a vector operand to the right or the left by a specified number of bits. When the bits are shifted, the vacant bit positions are filled with zeros. Arithmetic shift operators use the context of the expression to determine the value with which to fill the vacated bits. // X = 4'b1100 Y = X >> 1; //Y is 4'b0110. Shift right 1 bit. 0 filled in MSB position. Y = X << 2; //Y is 4'b0000. Shift left 2 bits. 2.6.3.8 Concatenation Operator: The concatenation operator ( {, } ) provides a mechanism to append multiple operands. The operands must be sized. Unsized operands are not allowed because the size of each operand must be known for computation of the size of the result. // A = 1'b1, B = 2'b00, C = 2'b10, D = 3'b110 Y = {B , C} // Result Y is 4'b0010 Y = {A , B[0], C[1]} // Result Y is 3'b101 2.6.3.9 Replication Operator: Repetitive concatenation of the same number can be expressed by using a replication constant. A replication constant specifies how many times to replicate the number inside the brackets ( { } ). A = 1'b1; B = 2'b00; C = 2'b10; Y = { 4{A} } // Result Y is 4'b1111 Y = { 4{A} , 2{B} , C } // Result Y is 8'b1111000010 2.6.3.10 Conditional Operator: The conditional operator (?:) takes three operands. Usage: condition_expr ? true_expr : false_expr ; The condition expression (condition_expr) is first evaluated. If the result is true (logical 1), then the true_expr is evaluated. If the result is false (logical 0), then the false_expr is evaluated. 49 i0 i1 s 2-to-1 Multiplexer, Using Logic Equations // Module 2-to-1 multiplexer using data flow. Logic equation module mux2_to_1 (out, i0, i1, s); // Port declarations from the I/O diagram output out; input i0, i1; input s; //Logic equation for out assign out = (~s & i0) | (s & i1); endmodule 2-to-1 Multiplexer, Using Conditional Operators // Module 2-to-1 multiplexer using data flow. Conditional operator. module multiplexer2_to_1 (out, i0, i1, s); // Port declarations from the I/O diagram output out; input i0, i1; input s; assign out = s ? i1 : i0; endmodule 2.7 SYSTEM TASKS AND COMPILER DIRECTIVES In this section, we introduce two special concepts used in Verilog: system tasks and compiler directives. 2.7.1 SYSTEM TASKS Verilog provides standard system tasks for certain routine operations. All system tasks appear in the form $<keyword>. 50 System Task Description Usage $display Prints the formatted message once when the statement is executed during simulation. A new line is automatically added at the end of its output. Example initial $display(“Hello World”); Prints the string between the quotation marks with a new line added at the end of the string. $write Prints the formatted message once when the statement is executed during simulation. No newline is added at the end of its output. Example initial $write(“Hello World”); Prints the string between the quotation marks with no new line added at the end of the string. $strobe It executes after all simulation events in the current time step have executed. Prints the formatted message once when executed. This task guarantees that the printed values for the signals/variables are the final values the signals/ variables can have at that time step. Example initial $strobe(“Current values of A, B and C are A=%b, B=%b, C=%b”, A, B, C); Prints A, B and C and prints their value in binary format after all simulation events in the current time step have executed. $monitor Invokes a background process that continuously monitors the signals listed, and prints the formatted message whenever one of the signals changes. A newline is automatically added at the end of its output. Only one $monitor can be active at a time. Returns the current simulation time as a 64-bit integer. Example initial $monitor(“Current values of A, B and C are A=%b, B=%b, C=%b”, A, B, C); Monitors A, B and C and prints their value in binary format whenever one of the signals (i.e A or B or C) changes its value during simulation. Example initial $monitor(“time = %d, A = %d”, $time, A); Prints the current simulation time as a 64-bit decimal whenever $monitor gets executed. $time $finish Finishes a simulation and exits the Example simulation process. initial $finish; $stop Halts a simulation and enters an Example interactive debug mode. initial $stop; 51 2.7.2 COMPILER DIRECTIVES Compiler Directives direct the pre-processor part of Verilog Parser or Simulator. They are not bound by modules or files. When a Simulator encounters a compiler directive, the directive remains in effect until another compiler directive either modifies it or turns it off. All compiler directives are defined by using the `<keyword> construct. Compiler Directive ‘include Description Usage File inclusion. The contents of another Verilog source file is inserted where the ‘include directive appears. Here the contents of ‘define.h’ are included within the ‘idu’ module. ‘define Allows a text string to be defined Example as a macro name. ‘define gate_regression 1 Allows ‘gate_regression’ to be substituted by 1 where ever it gets used. 2.8 WHAT ARE THE TIMING CONTROL STATEMENTS IN VERILOG Timing Controls Description # It delays execution for a specific amount of time, ‘delay’ may be a number, a variable or an expression. Example of a procedural block using ‘#” is shown below. Here ‘(c & d)’ gets evaluated at time 0 but gets assigned to ‘a’ after 2 time units whereas gets evaluated after 3 time units and gets assigned to ‘e’ immediately. 52 @ It delays execution until there is a transition on any one of the signals in the sensitivity list. ‘edge’ may be either a posedge or negedge. If no edge is specified then any logic transition is used. Signals here may be scalar or vector, and any data type. Example of a procedural block using ‘@’ is shown below. Here the statement within the always block gets evaluated whenever there is a transition on ‘b’ or ‘c’. wait It delays execution until the condition evaluates as true. Example of a procedural block using ‘wait’ is shown below. Here we wait for ‘a’ to be equal to 2 before evaluating ‘e’. 2.9 BASIC BLOCKS 2.9.1 INTRODUCTION TO PROCEDURAL CONSTRUCTS There are two kinds of assignment, continuous and procedural. Continuous assignments can only be made to nets, or a concatenation of nets. The operands can be of any data type. If one of the operands on the right hand side (RHS) of the assignment change, as the name suggests, the net on the left hand side (LHS) of the assignment is updated. In this way values are said to be driven into nets. Continuous assignments can be used to replace gate level descriptions with a higher level of abstraction. Procedural assignments are made to reg, integer, real or time, they need updating constantly to reflect any change in the value of the operands on the RHS. Procedural blocks in Verilog are used to model both combinatorial and sequential logic. They are also used in building a test bench environment for a design. There are two types of Procedural blocks which are ‘initial’ and ‘always’. 2.9.2 INITIAL BLOCK Keywords: initial 53 An initial block consists of a statement or a group of statements enclosed in begin... end which will be executed only once at simulation time 0. If there is more than one block they execute concurrently and independently. The initial block is normally used for initialization, monitoring, generating wave forms (eg, clock pulses) and processes which are executed once in a simulation. An example of initialization and wave generation is given below. initial clock = 1'b0; // variable initialization initial begin // multiple statements have to be grouped alpha = 0; #10 alpha = 1; // waveform generation #20 alpha = 0; #5 alpha = 1; end; 2.9.3 ALWAYS BLOCK Keywords: always An always block is similar to the initial block, but the statements inside an always block will repeated continuously, in a looping fashion, until stopped by $finish or $stop. Note: the $finish command actually terminates the simulation where as $stop merely pauses it and awaits further instructions. Thus $finish is the preferred command unless you are using an interactive version of the simulator. One way to simulate a clock pulse is shown in the example below. module pulse; reg clock; initial clock = 1'b0; // start the clock at 0 always #10 clock = ~clock; // toggle every 10 time units initial #5000 $finish // end the simulation after 5000 time units endmodule 2.10 BEHAVIORAL MODELING The Behavioral or procedural level statements are used to model a design at a higher level of abstraction than the other levels. They provide powerful ways of doing complex designs. However small changes n coding methods can cause large changes in the hardware generated. 2.10.1 PROCEDURAL ASSIGNMENTS Procedural assignments are assignment statements used within Verilog procedures (always and initial blocks). Only reg variables and integers (and their bit/part-selects and concatenations) can be placed left of the “=” in procedures. The right hand side of the assignment is an expression which may use any of the operator types. 54 2.10.2 DELAY IN ASSIGNMENT (not for synthesis) In a delayed assignment Δt time units pass before the statement is executed and the left-hand assignment is made. With intra-assignment delay, the right side is evaluated immediately but there is a delay of Δt before the result is place in the left hand assignment. Delays are not supported by synthesis tools. Syntax for Procedural Assignment variable = expression Delayed assignment #Δt variable = expression; Intra-assignment delay variable = #Δt expression; 2.10.3 BLOCKING ASSIGNMENTS Procedural (blocking) assignments (=) are done sequentially in the order the statements are written. A second assignment is not started until the preceding one is complete. Syntax variable = expression; variable = #Δt expression; grab inputs now, deliver ans. later. #Δt variable = expression; grab inputs later, deliver ans. later Example: For simulation initial begin a=1; b=2; c=3; #5 a = b + c; // wait for 5 units, and execute a= b + c =5. d = a; // Time continues from last line, d=5 = b+c at t=5. 2.10.4 NONBLOCKING (RTL) ASSIGNMENTS RTL (nonblocking) assignments (<=), which follow each other in the code, are started in parallel. The right hand side of nonblocking assignments is evaluated starting from the completion of the last blocking assignment or if none, the start of the procedure. The transfer to the left hand side is made according to the delays. An intra-assignment delay in a non-blocking statement will not delay the start of any subsequent statement blocking or non-blocking. However normal delays will are cumulative and will delay the output. For synthesis One must not mix “<=” or “=” in the same procedure. 55 “<=” best mimics what physical flip-flops do; use it for “always @ (posedge clk ..) type procedures. “=” best corresponds to what c/c++ code would do; use it for combinational procedures. Syntax variable <= expression; variable <= #Δt expression; #Δt variable <= expression; Example: For simulation initial begin #3 b <= a; /* grab a at t=0 Deliver b at t=3. #6 x <= b + c; // grab b+c at t=0, wait and assign x at t=6. x is unaffected by b’s change. */ The following example shows interactions between blocking and non-blocking for simulation. Do not mix the two types in one procedure for synthesis. Example: for simulation only initial begin a=1; b=2; c=3; x=4; #5 a = b + c; // wait for 5 units, then grab b,c and execute a=2+3. d = a; // Time continues from last line, d=5 = b+c at t=5. x <= #6 b + c; // grab b+c now at t=5, don’t stop, make x=5 at t=11. b <= #2 a; /* grab a at t=5 (end of last blocking statement). Deliver b=5 at t=7. Previous x is unaffected by b change. */ y <= #1 b + c; // grab b+c at t=5, don’t stop, make y=5 at t=6. #3 z = b + c; // grab b+c at t=8 (#5+#3), make z=8 (b =5 at t=7 and c = 3) at t=8. w <= x // make w=4 at t=8. Starting at last blocking assignm. 2.10.5 PROCEDURAL ASSIGNMENT GROUPS If a procedure block contains more than one statement, those statements must be enclosed within Sequential begin - end block Parallel fork - join block When using begin-end, we can give name to that group. This is called named blocks. Example: "begin-end" and "fork - join" 56 Begin: clk gets 0 after 1 time unit, reset gets 0 after 6 time units, enable after 11 time units, data after 13 units. All the statements are executed in sequentially. Fork: clk gets value after 1 time unit, reset after 5 time units, enable after 5 time units, data after 2 time units. All the statements are executed in parallel. Sequential Statement Groups The begin - end keywords group several statements together and cause the statements to be evaluated in sequentially (one at a time). Any timing within the sequential groups is relative to the previous statement. Delays in the sequence accumulate (each delay is added to the previous delay). Block finishes after the last statement in the block. Parallel Statement Groups The fork - join keywords group several statements together and cause the statements to be evaluated in parallel (all at the same time). Timing within parallel group is absolute to the beginning of the group. Block finishes after the last statement completes (Statement with high delay, it can be the first statement in the block). 2.10.6 CONTINUOUS ASSIGNMENT STATEMENTS Continuous assignment statements drive nets (wire data type). They represent structural connections. They can be used for modeling combinational logic. They are outside the procedural blocks (always and initial blocks). The left-hand side of a continuous assignment must be net data type. Syntax : assign # delay net = expression; Example: 1-bit Adder module adder (a,b,sum,carry); input a, b; output sum, carry; assign #5 {carry,sum} = a+b; endmodule 57 2.10.7 VARIOUS PROGRAMMING STATEMENTS USED IN VERILOG Progra mming Statem ents if if-else Usage Example It executes the statement or statement_group if the condition evaluates as true. If we need more than one statement (i.e statement_group) then we need to use begin-end or a fork-join block. The condition here can be an expression or a single value. If the condition evaluates to ‘0’ or unknown then the condition is considered false, and if the condition evaluates to ‘1’ or more then the condition is considered true. if (alu_func == 2’b00) aluout = a + b; else if (alu_func == 2’b01) aluout = a - b; else if (alu_func == 2’b10) aluout = a & b; else // alu_func == 2’b11 aluout = a | b; if (a == b) begin x = 1; ot = 4’b1111; It executes the first statement or end statement_group if the condition evaluates as true and executes the second statement or statement_group if the condition evaluates as false. The condition here can be an expression or a single value. If the condition evaluates to ‘0’ or unknown then the condition is considered false, and if the condition evaluates to ‘ 1’ or more then the condition is considered true. 58 case (alu_ctr) 2’b00: aluout = a + b; 2’b01: aluout = a - b; 2’b10: aluout = a & b; default: aluout = 1’bx; /* Treated as don’t cares for minimum logic generation. */ endcase case It compares the expression with each of the case_item’s and executes the statement or statement_group associated with the first matching case_item. It executes the default if none of the case_item’s match. Here the default case is optional. casez case (x, y, z) 2’b00: aluout = a + b; /*case if x or y or z is 2’b00.*/ 2’b01: aluout = a - b; 2’b10: aluout = a & b; default: aluout = a | b; endcase casez (d) 3’b1??: b = 2’b11; /* b = 11 if d = 100 or 101 or 110 or 111 */ 3’b01?: b = 2’b10; // b = 10 if d = 010 or 011 It is special version of case statement where ‘z’ default: b = 2’b00; and ‘?’ are treated as don’t cares. Similar to endcase case statement it compares the expression with each of the case_item’s and executes the statement or statement_group associated with the first matching case_item. It executes the default if none of the case_item’s match. Here the default case is optional. 59 casex forever repeat while for casex (a) 2’b1x: msb = 1; // msb = 1 if a = 10 or a = 11 /* If this were case(a) then only a=1x would match.*/ It is special version of case statement where default: msb = 0; ‘x’, ‘z’ and ‘?’ are treated as don’t cares. endcase Similar to case statement it compares the expression with each of the case_item’s and executes the statement or statement_group associated with the first matching case_item. It executes the default if none of the case_item’s match. Here the default case is optional. forever begin @(posedge clk); // or use a= #9 a+1; a = a + 1; It is an infinite loop that continuously executes end the statement or statement_group. Syntax repeat(expression) repeat (2) begin // after 50, a = 00, statement or statement _group #50 a = 2’b00; // after 100, a = 01, #50 a = 2’b01; // after 150, a = 00, Like forever it is a loop that executes the end// after 200, a = 01 statement or statement _group a fixed number of times based on the expression. Syntax while(condition) while (!overflow) begin statement or statement_group @(posedge clk); a = a + 1; It executes the statement or statement_group as end long as the condition evaluates as true. Syntax for (j = 0; j <= 7; j = j + 1) for(initial_value; condition; step) begin statement or statement_group c[j] = a[j] & b[j]; The for loop here uses three expressions d[j] = a[j] | b[j]; separated by semicolons to control the loop. end The first expression (initial_value) is executed 60 disable once before entering the loop the first time. The second expression (condition) is evaluated to determine if the contents of the loop (i.e statement or statement_group) should be executed. If the loop condition expression is true, the loop is entered. The final expression (step) is evaluated at the end of the loop. begin: accumulate Syntax forever disable group_name; begin It discontinues execution of a named group of @(posedge clk); statements. a = a + 1; if (a == 2’b0111) disable accumulate; end end 3. FUNCTION and TASK Verilog provides Functions and Tasks to allow the behavioral description of a module to be broken into more manageable parts allowing better readability and manageability. Functions and Tasks are useful for several reasons which are, they allow often used behavioral sequences to be written once and called when needed, they allow for a cleaner writing style and finally they allow data to be hidden from other parts of the design. 3.1 FUNCTION Functions are defined in the module in which they are used. It is possible to define function in separate file and use compile directive 'include to include the function in the file which instantiates the function. Function can call other functions, but cannot call task. The following are some of the general rules for functions: Functions must contain at least one input argument and cannot drive more than one output. Functions cannot contain an inout or output declaration. Functions cannot contain time controlled statements (#, @, wait), i.e., it cannot contain delays . Function cannot include timing delays, like posedge, negedge, # delay. Which means that function should be executed in "zero" time delay. Functions must contain a statement that assigns the return value to the implicit function name register. The variables declared within the function are local to that function. The order of declaration within the function defines how the variables passed to the function by the caller are used. 61 Syntax Function begins with keyword function and ends with keyword endfunction. Inputs are declared after the keyword function. function [msb:lsb] function_name; input [msb:lsb] input_arguments; reg [msb:lsb] reg_variable_list; parameter [msb:lsb] parameter_list; integer [msb:lsb] integer_list; ... statements ... endfunction Example function [7:0] my_func; // function return 8-bit value input [7:0] i; reg [3:0] temp; integer n; temp= i[7:4] | ( i[3:0]); my_func = {temp, i[3:0]}; endfunction Example: Simple Function function myfunction; input a, b, c, d; begin myfunction = ((a+b) + (c-d)); end endfunction Calling a Function Let’s assume that function in above example is stored in a file called myfunction.v. Advantage of coding function in separate file is that, it can be used in multiple module's. module func_test(a, b, c, d, e, f); input a, b, c, d, e ; output f; wire f; `include "myfunction.v" assign f = (myfunction (a,b,c,d)) ? e :0; endmodule 62 3.2 TASK Tasks are used in all programming languages, generally known as procedures or sub routines. A task is similar to a function, but unlike a function it has any number input and output ports. Therefore tasks do not return values. Included in the main body of code tasks can be called many times, reducing code repetition. Tasks are defined in the module in which they are used. It is possible to define task in separate file and use compile directive 'include to include the task in the file which instantiates the task. Task can include timing delays, like posedge, negedge, # delay. The variables declared within the task are local to that task. The order of declaration within the task defines how the variables passed to the task by the caller are used. Task can call another task or function. Syntax Task begins with keyword task and end's with keyword endtask Input and output are declared after the keyword task. Local variables are declared after input and output declaration. task task_name; input [msb:lsb] input_port_list; output [msb:lsb] output_port_list; reg [msb:lsb] reg_variable_list; parameter [msb:lsb] parameter_list; integer [msb:lsb] integer_list; ... statements ... endtask Example : Simple Task task convert; input [7:0] temp_in; output [7:0] temp_out; begin temp_out = (9/5) *( temp_in + 32); end endtask Example : Task using Global Variables task convert; begin temp_out = (9/5) *( temp_in + 32); end endtask 63 Calling a Task Let’s assume that task in example “Simple Task” is stored in a file called mytask.v. Advantage of coding task in separate file is that, it can be used in multiple modules. module temp_cal (temp_a, temp_b, temp_c, temp_d); input [7:0] temp_a, temp_c; output [7:0] temp_b, temp_d; reg [7:0] temp_b, temp_d; `include "mytask.v" always @ (temp_a) convert (temp_a, temp_b); always @ (temp_c) convert (temp_c, temp_d); endmodule 4. MODELING FINITE STATE MACHINE (FSM) State machine or FSM are the heart of any digital design. There are two types of state machines classified by the types of outputs generated from each. The first is the Moore State Machine where the outputs are only a function of the present state, the second is the Mealy State Machine where one or more of the outputs are a function of the present state and one or more of the inputs. Mealy Model Moore Model 64 Modeling State machines. One thing that needs to be kept in mind when coding FSM is that, combinational logic and sequence logic should be in two different always blocks. In the above two figures, next state logic is always the combinational logic. State Registers and Output logic are sequential logic. It is very important that any asynchronous signal to the next state logic should be synchronized before feeding to FSM. Always try to keep FSM in separate Verilog file. Using constants declaration like parameter or `define to define states of the FSM, this makes code more readable and easy to manage. State Diagram. Verilog Code FSM code should have three sections, Encoding style. Combinational part. Sequential part. Encoding Style One Hot Encoding parameter [1:0] IDLE = 3'b001, GNT0 = 3'b010, GNT1 = 3'b100; Binary Encoding parameter [1:0] IDLE = 2'b00, GNT0 = 2'b01, GNT1 = 2'b10; 65 Combinational Section This section can be modeled using function, assign statement or using always block with case statement. For time being let’s see always block version. next_state = 3'b000 case(state) IDLE : if (req_0 == 1'b1) next_state = GNT0; else if (req_1 == 1'b1) next_state= GNT1; else next_state = IDLE; GNT0 : if (req_0 == 1'b1) next_state = GNT0; else next_state = IDLE; GNT1 : if (req_1 == 1'b1) begin next_state = GNT1; else next_state =1 IDLE; default : next_state = IDLE endcase end Sequential Section This section has be modeled using only edge sensitive logic such as always block with posedge or negedge of clock. always @ (posedge clock) begin : OUTPUT_LOGIC if (reset == 1'b1) begin gnt_0 <= #1 1'b0; gnt_1 <= #1 1'b0; state <= #1 IDLE; end else begin state <= #1 next_state; case(state) 66 IDLE : begin gnt_0 <= #1 1'b0; gnt_1 <= #1 1'b0; end GNT0 : begin gnt_0 <= #1 1'b1; gnt_1 <= #1 1'b0; end GNT1 : begin gnt_0 <= #1 1'b0; gnt_1 <= #1 1'b1; end default : begin gnt_0 <= #1 1'b0; gnt_1 <= #1 1'b0; end endcase end end 5. WRITING TESTBENCHES Writing a testbench is as complex as writing the RTL code itself. These days ASICs are getting more and more complex and thus verifying these complex ASIC has become a challenge. Typically 60-70% of time needed for any ASIC is spent on verification/validation/testing. For writing testbench it is important to have the design specification of "design under test" or simply DUT. Specs need to be understood clearly and test plan is made, which basically documents the test bench architecture and the test scenarios ( test cases) in detail. Example : Counter Let’s assume that we have to verify a simple 4-bit up counter, which increments its count when ever enable is high and resets to zero, when reset is asserted high. Reset is synchronous to clock. Code for Counter module counter (clk, reset, enable, count); input clk, reset, enable; output [3:0] count; 67 reg [3:0] count; always @ (posedge clk) if (reset == 1'b1) count <= 0; else if ( enable == 1'b1) count <= count + 1; endmodule Test Plan We will write self checking test bench, but we will do this in steps to help you understand the concept of writing automated test benches. Our testbench environment will look something like shown in below figure. DUT is instantiated in testbench, and testbench will contain a clock generator, reset generator, enable logic generator, compare logic, which basically calculate the expected count value of counter and compare the output of counter with calculated value. Test Cases Reset Test : We can start with reset deasserted, followed by asserting reset for few clock ticks and deasserting the reset, See if counter sets its output to zero. Enable Test : Assert/deassert enable after reset is applied. Random Assert/deassert of enable and reset. Writing TestBench First step of any testbench creation is to create a dummy template which basically declares inputs to DUT as reg and outputs from DUT as wire, instantiate the DUT as shown in code below. Note: there is no port list for the test bench. Test Bench module counter_tb; reg clk, reset, enable; wire [3:0] count; 68 counter U0 ( .clk (clk), .reset (reset), .enable (enable), .count (count) ); endmodule Next step would be to add clock generator logic. Before we add clock generator we need to drive all the inputs to DUT to some known state as shown in code below. Test Bench with Clock gen module counter_tb; reg clk, reset, enable; wire [3:0] count; counter U0 ( .clk (clk), .reset (reset), .enable (enable), .count (count) ); initial begin clk = 0; reset = 0; enable = 0; end always #5 clk = !clk; endmodule Initial block in verilog is executed only once, thus simulator sets the value of clk, reset and enable to 0, which by looking at the counter code (of course you will be refering to the the DUT specs) could be found that driving 0 makes all this signals disabled. There are many ways to generate clock, one could use forever loop inside an initial block as an alternate to above code. At this point, you would like test if the testbench is generating the clock correctly. You will see that simulator does not come out, or print anything on screen or does it dump any waveform. Thus we need to add support for all the above as shown in code below. 69 Test Bench continues... module counter_tb; reg clk, reset, enable; wire [3:0] count; counter U0 ( .clk (clk), .reset (reset), .enable (enable), .count (count) ); initial begin clk = 0; reset = 0; enable = 0; end always #5 clk = !clk; initial begin $dumpfile ("counter.vcd"); $dumpvars; end initial begin $display("\t\ttime,\tclk,\treset,\tenable,\tcount"); $monitor("%d,\t%b,\t%b,\t%b,\t%d",$time, clk, reset, enable, count); end initial #100 $finish; //Rest of testbench code after this line endmodule $dumpfile is used for specifying the file that simulator will use to store the waveform, that can be used later to view using waveform viewer. $dumpvars basically instructs the Verilog compiler to start dumping all the signals to "counter.vcd". $display is used for printing text or variables to stdout (screen), \t is for inserting tab. Syntax is same as printf. Second line $monitor is bit different, $monitor keeps track of changes to the variables that are in the list (clk, reset, enable, count). Whenever anyone of them changes, it prints their value, in the respective radix specified. 70 $finish is used for terminating simulation after #100 time units (note, all the initial, always blocks start execution at time 0). Adding Reset Logic Once we have the basic logic to allow us to see what our testbench is doing, we can next add the reset logic, If we look at the testcases, we see that we had added a constraint that it should be possible to activate reset anytime during simulation. To achieve this we have many approaches, but we will follow something that will go long way. There is something called 'events' in Verilog, events can be triggered, and also monitored to see, if an event has occurred. Lets code our reset logic in such a way that it waits for the trigger event "reset_trigger" to happen, when this event happens, reset logic asserts reset at negative edge of clock and de-asserts on next negative edge as shown in code below. Also after de-asserting the reset, reset logic triggers another event called "reset_done_trigger". This trigger event can then be used at somewhere else in test bench to sync up. Code of reset logic event reset_trigger; event reset_done_trigger; initial begin forever begin @ (reset_trigger); @ (negedge clk); reset = 1; @ (negedge clk); reset = 0; -> reset_done_trigger; end end Adding test case logic Moving forward, let’s add logic to generate the test cases, ok we have three testcases as in the first part of this tutorial. Lets list them again. Reset Test : We can start with reset deasserted, followed by asserting reset for few clock ticks and deasserting the reset, See if counter sets its output to zero. Enable Test : Assert/deassert enable after reset is applied. Random Assert/deassert of enable and reset. Test Case # 1 : Asserting/ Deasserting reset In this test case, we will just trigger the event reset_trigger after 10 simulation units. initial begin: TEST_CASE 71 #10 -> reset_trigger; end Test Case # 2 : Asserting/ Deasserting enable after reset is applied. In this test case, we will trigger the reset logic and wait for the reset logic tocomplete its operation, before we start driving enable signal to logic 1. initial begin: TEST_CASE #10 -> reset_trigger; @ (reset_done_trigger); @ (negedge clk); enable = 1; repeat (10) begin @ (negedge clk); end enable = 0; end Test Case # 3 : Asserting/Deasserting enable and reset randomly. In this testcase we assert the reset, and then randomly drive values on to enable and reset signal. initial begin : TEST_CASE #10 -> reset_trigger; @ (reset_done_trigger); fork begin repeat (10) begin @ (negedge clk); enable = $random; repeat (10) begin @ (negedge clk); reset = $random; end end end Well you might ask, are all this three test case exist in same file, well the answer is no. If we try to have all three test cases on one file, then we end up having race condition due to three initial blocks driving reset and enable signal. So normally, once test bench coding is done, test cases are coded separately and included in testbench as `include directive as shown below. If you look closely all the three test cases, you will find that, even through test case execution is not complete, simulation terminates. To have better control, what we can do is, add a event like 72 "terminate_sim" and execute $finish only when this event is triggered. We can trigger this event at the end of test case execution. The code for $finish now could look as below. event terminate_sim; initial begin @ (terminate_sim); #5 $finish; End and the modified test case #2 would like. initial begin: TEST_CASE #10 -> reset_trigger; @ (reset_done_trigger); @ (negedge clk); enable = 1; repeat (10) begin @ (negedge clk); end enable = 0; #5 -> terminate_sim; end 73 BASICS OF FPGA A Hard-Coded World – ASIC ASIC stands for Application Specific Integrated Circuit. ASIC design flow takes huge amount of time and hence costly. It is an IC which is designed only for a particular application; i.e., one time programmability. For Example Pentium chips (P I, P II) are ASIC chips. ASIC involves huge rework cost. Aim should be for right output from first time even in re-work cycle. A Re-programmability World Rehearse your act as many times till perfection. Very less turnaround time involved. Can afford to commit initial mistakes. Proto-type complex systems before going to a hard coded world. Emulation platform can be developed using FPGA’s to validate designs at system level. Designs can be downloaded into an FPGA based system to validate the functionality. If any defects are encountered during the validation process the design can be fixed and verified using the same platform. What Re-programmability means to H/W Same piece of hardware. Different types of functionality. Matter of loading a new bitmap. Easy design changes. Faster development cycle. Suitable for low volume production. The same piece of hardware can be used multiple times to validate either new designs or modified (bug fixes, feature enhancement) designs. The development cycle is faster as this involves only the design and verification cycle followed by synthesis and FPGA P&R instead of the entire backend flow and fabrication of the hardware. NOTE: ASIC is cost effective for mass production & FPGA is effective for low volume production. So What’s an FPGA? Field Programmable Gate Array. Gate Array – a custum VLSI circuit consisting of huge number of unconnected gates. Circuit function determined at the field by the user. Re-programmable. Pre tested for manufacturing defects. It is: 74 An ASIC for FPGA manufacturer. An FPGA for the user. Comes as chip ready to operate. Field Programmable means that the FPGA’s function is defined by a user’s program rather than by the manufacturer of the device. A typical integrated circuit performs a particular function defined at the time of manufacturer. In contrast, the FPGA’s function is defined by a program written by someone other than the device manufacturer. This user programmability gives the user access to complex integrated designs without the high engineering costs associated with application specific integrated circuit. The FPGA is an integrated circuit that contains many identical logic cells that can be viewed as standard components. The individual cells are interconnected by a matrix of wires and programmable switches. A user’s design is implemented by specifying the simple logic function for each cell and selectively closing the switches in the interconnected matrix. The array of logic cells and interconnects from a fabric of basic building blocks for logic circuits. Complex designs are created by combining these basic blocks to create the desired circuit. FPGA Devices Xilinx Devices Vertex Series o Vertex-5 family, Vertex-4 family, Vertex II family, Vertex family o High cost, Highly efficient Spartan Series o Spartan-3A, 3E, 3 family, Spartan IIE, II family, Spartan family o Low cost, Low efficient Altera Devices Stratix Series o Stratix III family, Stratix II (GX) family, Stratix (GX) family Cyclone Series o Cyclone III family, Cyclone II family, Cyclone family 75