Academia.eduAcademia.edu

Circuit design with VHDL

2004

TLFeBOOK 'X' Forcing Unknown (synthesizable unknown) '0' Forcing Low (synthesizable logic '1') '1' Forcing High (synthesizable logic '0') 'Z' High impedance (synthesizable tri-state bu¤er) 'W' Weak unknown 'L' Weak low 'H' Weak high '-' Don't care Examples: SIGNAL x: STD_LOGIC;-x is declared as a one-digit (scalar) signal of type STD_LOGIC.

Circuit Design with VHDL Volnei A. Pedroni TLFeBOOK Circuit Design with VHDL TLFeBOOK TLFeBOOK Circuit Design with VHDL Volnei A. Pedroni MIT Press Cambridge, Massachusetts London, England TLFeBOOK 6 2004 Massachusetts Institute of Technology All rights reserved. No part of this book may be reproduced in any form by any electronic or mechanical means (including photocopying, recording, or information storage and retrieval) without permission in writing from the publisher. This book was set in Times New Roman on 3B2 by Asco Typesetters, Hong Kong and was printed and bound in the United States of America. Library of Congress Cataloging-in-Publication Data Pedroni, Volnei A. Circuit design with VHDL/Volnei A. Pedroni. p. cm. Includes bibliographical references and index. ISBN 0-262-16224-5 (alk. paper) 1. VHDL (Computer hardware description language) 2. Electronic circuit design. 3. System design. I. Title. TK7885.7.P43 2004 621.39 0 5—dc22 2004040174 10 9 8 7 6 5 4 3 2 1 TLFeBOOK To Claudia, Patricia, Bruno, and Ricardo TLFeBOOK TLFeBOOK Contents Preface xi I CIRCUIT DESIGN 1 1 Introduction 1.1 About VHDL 1.2 Design Flow 1.3 EDA Tools 1.4 Translation of VHDL Code into a Circuit 1.5 Design Examples 3 3 3 4 5 8 2 Code 2.1 2.2 2.3 2.4 2.5 2.6 Structure Fundamental VHDL Units LIBRARY Declarations ENTITY ARCHITECTURE Introductory Examples Problems 13 13 13 15 17 17 22 3 Data 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 3.10 3.11 Types Pre-Defined Data Types User-Defined Data Types Subtypes Arrays Port Array Records Signed and Unsigned Data Types Data Conversion Summary Additional Examples Problems 25 25 28 29 30 33 35 35 37 38 38 43 4 Operators and Attributes 4.1 Operators 4.2 Attributes 4.3 User-Defined Attributes 4.4 Operator Overloading 47 47 50 52 53 TLFeBOOK viii Contents 4.5 4.6 4.7 4.8 GENERIC Examples Summary Problems 54 55 60 61 65 65 67 69 78 81 84 5 Concurrent Code 5.1 Concurrent versus Sequential 5.2 Using Operators 5.3 WHEN (Simple and Selected) 5.4 GENERATE 5.5 BLOCK 5.6 Problems 6 Sequential Code 6.1 PROCESS 6.2 Signals and Variables 6.3 IF 6.4 WAIT 6.5 CASE 6.6 LOOP 6.7 CASE versus IF 6.8 CASE versus WHEN 6.9 Bad Clocking 6.10 Using Sequential Code to Design Combinational Circuits 6.11 Problems 91 91 93 94 97 100 105 112 113 114 118 121 7 Signals and Variables 7.1 CONSTANT 7.2 SIGNAL 7.3 VARIABLE 7.4 SIGNAL versus VARIABLE 7.5 Number of Registers 7.6 Problems 129 129 130 131 133 140 151 8 State 8.1 8.2 8.3 159 159 160 168 Machines Introduction Design Style #1 Design Style #2 (Stored Output) TLFeBOOK Contents 8.4 8.5 ix Encoding Style: From Binary to OneHot Problems 181 183 9 Additional Circuit Designs 9.1 Barrel Shifter 9.2 Signed and Unsigned Comparators 9.3 Carry Ripple and Carry Look Ahead Adders 9.4 Fixed-Point Division 9.5 Vending-Machine Controller 9.6 Serial Data Receiver 9.7 Parallel-to-Serial Converter 9.8 Playing with a Seven-Segment Display 9.9 Signal Generators 9.10 Memory Design 9.11 Problems 187 187 191 194 198 202 208 211 212 217 220 225 II SYSTEM DESIGN 231 10 Packages and Components 10.1 Introduction 10.2 PACKAGE 10.3 COMPONENT 10.4 PORT MAP 10.5 GENERIC MAP 10.6 Problems 233 233 234 236 244 244 251 11 Functions and Procedures 11.1 FUNCTION 11.2 Function Location 11.3 PROCEDURE 11.4 Procedure Location 11.5 FUNCTION versus PROCEDURE Summary 11.6 ASSERT 11.7 Problems 253 253 256 265 266 270 270 271 12 Additional System Designs 12.1 Serial-Parallel Multiplier 12.2 Parallel Multiplier 275 275 279 TLFeBOOK x Contents 12.3 12.4 12.5 12.6 Multiply-Accumulate Circuits Digital Filters Neural Networks Problems 285 289 294 301 Appendix A: Programmable Logic Devices 305 Appendix B: Xilinx ISE B ModelSim Tutorial 317 Appendix C: Altera MaxPlus II B Advanced Synthesis Software Tutorial 329 Appendix D: Altera Quartus II Tutorial 343 Appendix E: VHDL Reserved Words 355 Bibliography Index 357 359 TLFeBOOK Preface Structure of the Book The book is divided into two parts: Circuit Design and System Design. The first part deals with everything that goes directly inside the main code, while the second deals with units that might be located in a library (for code sharing, reuse, and partitioning). In summary, in Part I we study the entire background and coding techniques of VHDL, which includes the following:  Code structure: libraries, entity, architecture (chapter 2)  Data types (chapter 3)  Operators and attributes (chapter 4)  Concurrent statements and concurrent code (chapter 5)  Sequential statements and sequential code (chapter 6)  Objects: signals, variables, constants (chapter 7)  Design of finite state machines (chapter 8)  And, finally, additional circuit designs are presented (chapter 9). Then, in Part II we simply add new building blocks, which are intended mainly for library allocation, to the material already presented. The structure of Part II is the following:  Packages and components (chapter 10)  Functions and procedures (chapter 11)  Finally, additional system designs are presented (chapter 12). Distinguishing Features The main distinguishing features of the book are the following: It teaches in detail all indispensable features of VHDL synthesis in a concise format.  The sequence is well established. For example, a clear distinction is made between what is at the circuit level (Part I) versus what is at the system level (Part II). The foundations of VHDL are studied in chapters 1 to 4, fundamental coding in chapters 5 to 9, and finally system coding in chapters 10 to 12.  Each chapter is organized in such a way to collect together related information as closely as possible. For instance, concurrent code is treated collectively in one chap-  TLFeBOOK xii Preface ter, while sequential code is treated in another; data types are discussed in one chapter, while operators and attributes are in another; what is at the circuit level is seen in one part of the book, while what is at the system level is in another. While books on VHDL give limited emphasis to digital design concepts, and books on digital design discuss VHDL only briefly, the present work completely integrates them. It is indeed a design-oriented approach.  To achieve the above-mentioned integration between VHDL and digital design, the following steps are taken:  a large number of complete design examples (rather than sketchy or partial solutions) are presented;   illustrative top-level circuit diagrams are always shown;  fundamental design concepts are reviewed;  the solutions are explained and commented;  the circuits are always physically implemented (using programmable logic devices);  simulation results are always included, along with analysis and comments;  finally, appendices on programmable devices and synthesis tools are also included. Audience The book is intended as a text for any of the following EE/CS courses:  VHDL  Automated Digital Design  Programmable Logic Devices  Digital Design (basic or advanced) It is also a supporting text for in-house courses in any of the areas listed above, particularly for vendor-provided courses on VHDL and/or programmable logic devices. Acknowledgments To the anonymous reviewers for their invaluable comments and suggestions. Special thanks also to Ricardo P. Jasinski and Bruno U. Pedroni for their reviews and comments. TLFeBOOK I CIRCUIT DESIGN TLFeBOOK TLFeBOOK 1 1.1 Introduction About VHDL VHDL is a hardware description language. It describes the behavior of an electronic circuit or system, from which the physical circuit or system can then be attained (implemented). VHDL stands for VHSIC Hardware Description Language. VHSIC is itself an abbreviation for Very High Speed Integrated Circuits, an initiative funded by the United States Department of Defense in the 1980s that led to the creation of VHDL. Its first version was VHDL 87, later upgraded to the so-called VHDL 93. VHDL was the original and first hardware description language to be standardized by the Institute of Electrical and Electronics Engineers, through the IEEE 1076 standard. An additional standard, the IEEE 1164, was later added to introduce a multi-valued logic system. VHDL is intended for circuit synthesis as well as circuit simulation. However, though VHDL is fully simulatable, not all constructs are synthesizable. We will give emphasis to those that are. A fundamental motivation to use VHDL (or its competitor, Verilog) is that VHDL is a standard, technology/vendor independent language, and is therefore portable and reusable. The two main immediate applications of VHDL are in the field of Programmable Logic Devices (including CPLDs—Complex Programmable Logic Devices and FPGAs—Field Programmable Gate Arrays) and in the field of ASICs (Application Specific Integrated Circuits). Once the VHDL code has been written, it can be used either to implement the circuit in a programmable device (from Altera, Xilinx, Atmel, etc.) or can be submitted to a foundry for fabrication of an ASIC chip. Currently, many complex commercial chips (microcontrollers, for example) are designed using such an approach. A final note regarding VHDL is that, contrary to regular computer programs which are sequential, its statements are inherently concurrent (parallel). For that reason, VHDL is usually referred to as a code rather than a program. In VHDL, only statements placed inside a PROCESS, FUNCTION, or PROCEDURE are executed sequentially. 1.2 Design Flow As mentioned above, one of the major utilities of VHDL is that it allows the synthesis of a circuit or system in a programmable device (PLD or FPGA) or in an ASIC. The steps followed during such a project are summarized in figure 1.1. We start the design by writing the VHDL code, which is saved in a file with the extension TLFeBOOK 4 Chapter 1 VHDL entry (RTL level) Compilation Netlist (Gate level) Optimization Synthesis Optimized netlist (Gate level) Simulation Place & Route Physical device Simulation Figure 1.1 Summary of VHDL design flow. .vhd and the same name as its ENTITY’s name. The first step in the synthesis process is compilation. Compilation is the conversion of the high-level VHDL language, which describes the circuit at the Register Transfer Level (RTL), into a netlist at the gate level. The second step is optimization, which is performed on the gate-level netlist for speed or for area. At this stage, the design can be simulated. Finally, a placeand-route (fitter) software will generate the physical layout for a PLD/FPGA chip or will generate the masks for an ASIC. 1.3 EDA Tools There are several EDA (Electronic Design Automation) tools available for circuit synthesis, implementation, and simulation using VHDL. Some tools (place and route, for example) are o¤ered as part of a vendor’s design suite (e.g., Altera’s Quartus II, which allows the synthesis of VHDL code onto Altera’s CPLD/FPGA chips, or Xilinx’s ISE suite, for Xilinx’s CPLD/FPGA chips). Other tools (synthe- TLFeBOOK Introduction 5 sizers, for example), besides being o¤ered as part of the design suites, can also be provided by specialized EDA companies (Mentor Graphics, Synopsis, Synplicity, etc.). Examples of the latter group are Leonardo Spectrum (a synthesizer from Mentor Graphics), Synplify (a synthesizer from Synplicity), and ModelSim (a simulator from Model Technology, a Mentor Graphics company). The designs presented in the book were synthesized onto CPLD/FPGA devices (appendix A) either from Altera or Xilinx. The tools used were either ISE combined with ModelSim (for Xilinx chips—appendix B), MaxPlus II combined with Advanced Synthesis Software (for Altera CPLDs—appendix C), or Quartus II (also for Altera devices—appendix D). Leonardo Spectrum was also used occasionally. Although di¤erent EDA tools were used to implement and test the examples presented in the book (see list of tools above), we decided to standardize the visual presentation of all simulation graphs. Due to its clean appearance, the waveform editor of MaxPlus II (appendix C) was employed. However, newer simulators, like ISE þ ModelSim (appendix B) and Quartus II (appendix D), o¤er a much broader set of features, which allow, for example, a more refined timing analysis. For that reason, those tools were adopted when examining the fine details of each design. 1.4 Translation of VHDL Code into a Circuit A full-adder unit is depicted in figure 1.2. In it, a and b represent the input bits to be added, cin is the carry-in bit, s is the sum bit, and cout the carry-out bit. As shown in the truth table, s must be high whenever the number of inputs that are high is odd, while cout must be high when two or more inputs are high. A VHDL code for the full adder of figure 1.2 is shown in figure 1.3. As can be seen, it consists of an ENTITY, which is a description of the pins (PORTS) of the a b cin Full Adder s cout ab 00 01 10 11 00 01 10 11 cin 0 0 0 0 1 1 1 1 s cout 0 0 1 0 1 0 0 1 1 0 0 1 0 1 1 1 Figure 1.2 Full-adder diagram and truth table. TLFeBOOK 6 Chapter 1 ENTITY full_adder IS PORT (a, b, cin: IN BIT; s, cout: OUT BIT); END full_adder; -------------------------------------ARCHITECTURE dataflow OF full_adder IS BEGIN s <= a XOR b XOR cin; cout <= (a AND b) OR (a AND cin) OR (b AND cin); END dataflow; Circuit Figure 1.3 Example of VHDL code for the full-adder unit of figure 1.2. circuit, and of an ARCHITECTURE, which describes how the circuit should function. We see in the latter that the sum bit is computed as s ¼ a a b a cin, while cout is obtained from cout ¼ a.b þ a.cin þ b.cin. From the VHDL code shown on the left-hand side of figure 1.3, a physical circuit is inferred, as indicated on the right-hand side of the figure. However, there are several ways of implementing the equations described in the ARCHITECTURE of figure 1.3, so the actual circuit will depend on the compiler/optimizer being used and, more importantly, on the target technology. A few examples are presented in figure 1.4. For instance, if our target is a programmable logic device (PLD or FPGA— appendix A), then two possible results (among many others) for cout are illustrated in figures 1.4(b)–(c) (in both, of course, cout ¼ a.b þ a.cin þ b.cin). On the other hand, if our target technology is an ASIC, then a possible CMOS implementation, at the transistor level, is that of figure 1.4(d) (which makes use of MOS transistors and clocked domino logic). Moreover, the synthesis tool can be set to optimize the layout for area or for speed, which obviously also a¤ects the final circuitry. Whatever the final circuit inferred from the code is, its operation should always be verified still at the design level (after synthesis), as indicated in figure 1.1. Of course, it must also be tested at the physical level, but then changes in the design might be too costly. When testing, waveforms similar to those depicted in figure 1.5 will be displayed by the simulator. Indeed, figure 1.5 contains the simulation results from the circuit synthesized with the VHDL code of figure 1.3, which implements the full-adder unit of figure 1.2. As can be seen, the input pins (characterized by an inward arrow with an I marked inside) and the output pins (characterized by an outward arrow with an O marked inside) are those listed in the ENTITY of figure 1.3. We can freely estab- TLFeBOOK Introduction 7 a cin a b cin cout b s a cin (a) (b) clk a cout b a cout cin a a b b cin cin b clk cin (c) (d) Figure 1.4 Examples of possible circuits obtained from the full-adder VHDL code of figure 1.3. Figure 1.5 Simulation results from the VHDL design of figure 1.3. TLFeBOOK 8 Chapter 1 lish the values of the input signals (a, b, and cin in this case), and the simulator will compute and plot the output signals (s and cout). As can be observed in figure 1.5, the outputs do behave as expected. 1.5 Design Examples As mentioned in the preface, the book is indeed a design-oriented approach to the task of teaching VHDL. The integration between VHDL and Digital Design is achieved through a long series of well-detailed design examples. A summary of the complete designs presented in the book is shown below.  Adders (examples 3.3 and 6.8 and section 9.3)  ALU (examples 5.5 and 6.10)  Barrel shifters and vector shifters (examples 5.6 and 6.9 and section 9.1)  Comparators (section 9.2)  Controller, tra‰c light (example 8.5)  Controller, vending machine (section 9.5)  Count ones (examples 7.1 and 7.2)  Counters (examples 6.2, 6.5, 6.7, 7.7, and 8.1)  Decoder (example 4.1)  Digital filters (section 12.4)  Dividers, fixed point (section 9.4)  Flip-flops and latches (examples 2.1, 5.7, 5.8, 6.1, 6.4, 6.6, 7.4, and 7.6)  Encoder (example 5.4)  Frequency divider (example 7.5)  Function arith_shift (example 11.7)  Function conv_integer (examples 11.2 and 11.5)  Function multiplier (example 11.8)  Function ‘‘þ’’ overloaded (example 11.6)  Function positive_edge (examples 11.1, 11.3, and 11.4)  Leading zeros counter (example 6.10)  Multiplexers (examples 5.1, 5.2, and 7.3) TLFeBOOK Introduction  Multipliers (example 11.8 and sections 12.1 and 12.2)  MAC circuit (section 12.3)  Neural networks (section 12.5)  Parallel-to-serial converter (section 9.7)  Parity detector (example 4.2)  Parity generator (example 4.3)  Playing with SSD (section 9.8)  Procedure min_max (examples 11.9 and 11.10)  RAM (example 6.11 and section 9.10)  ROM (section 9.10)  Serial data receiver (section 9.6)  Shift registers (examples 6.3, 7.8, and 7.9)  Signal generators (example 8.6 and section 9.9)  String detector (example 8.4)  Tri-state bu¤er/bus (example 5.3) 9 Moreover, several additional designs and experimental verifications are also proposed as exercises:  Adders and subtractors (problems 3.5, 5.4, 5.5, 6.14, 6.16, 10.2, and 10.3)  Arithmetic-logic units (problems 6.13 and 10.1)  Barrel and vector shifters (problems 5.7, 6.12, 9.1, and 12.2)  Binary-to-Gray code converter (problem 5.6)  Comparators (problems 5.8 and 6.15)  Count ones (problem 6.9)  Counters (problems 7.5 and 11.6)  Data delay circuit (problem 7.2)  Decoders (problems 4.4 and 7.6)  DFFs (problems 6.17, 7.3, 7.4, and 7.7)  Digital FIR filter (problem 12.4)  Dividers (problems 5.3 and 9.2)  Event counter (problem 6.1) TLFeBOOK 10  Finite-state machine (problem 8.1)  Frequency divider, generic (problem 6.4)  Frequency multiplier (problem 6.5)  Function conv_std_logic_vector (problem 11.1)  Function ‘‘not’’ overloaded for integers (problem 11.2)  Function shift for integers (problem 11.4)  Function shift for std_logic_vector (problem 11.3)  Function BCD-SSD converter (problem 11.6)  Function ‘‘þ’’ overloaded for std_logic_vector (problem 11.8)  Intensity encoder (problem 6.10)  Keypad debouncer/encoder (problem 8.4)  Multiplexers (problems 2.1, 5.1, and 6.11)  Multipliers (problems 5.3, 11.5, and 12.1)  Multiply-accumulate circuit (problem 12.3)  Neural network (problem 12.5)  Parity detector (problem 6.8)  Playing with a seven-segment display (problem 9.6)  Priority encoder (problems 5.2 and 6.3)  Procedure statistics (problem 11.7)  Random number generator plus SSD (problem 9.8)  ROM (problem 3.4)  Serial data receiver (problem 9.4)  Serial data transmitter (problem 9.5)  Shift register (problem 6.2)  Signal generators (problems 8.2, 8.3, 8.6, and 8.7)  Speed monitor (problem 9.7)  Stop watch (problem 10.4)  Timers (problems 6.6 and 6.7)  Tra‰c-light controller (problem 8.5)  Vending-machine controller (problem 9.3) Chapter 1 TLFeBOOK Introduction 11 Additionally, four appendices on programmable logic devices and synthesis tools are included:  Appendix A: Programmable Logic Devices  Appendix B: Xilinx ISE þ ModelSim Tutorial  Appendix C: Altera MaxPlus II þ Advanced Synthesis Software Tutorial  Appendix D: Altera Quartus II Tutorial TLFeBOOK TLFeBOOK 2 Code Structure In this chapter, we describe the fundamental sections that comprise a piece of VHDL code: LIBRARY declarations, ENTITY, and ARCHITECTURE. 2.1 Fundamental VHDL Units As depicted in figure 2.1, a standalone piece of VHDL code is composed of at least three fundamental sections: LIBRARY declarations: Contains a list of all libraries to be used in the design. For example: ieee, std, work, etc.   ENTITY: Specifies the I/O pins of the circuit. ARCHITECTURE: Contains the VHDL code proper, which describes how the circuit should behave (function).  A LIBRARY is a collection of commonly used pieces of code. Placing such pieces inside a library allows them to be reused or shared by other designs. The typical structure of a library is illustrated in figure 2.2. The code is usually written in the form of FUNCTIONS, PROCEDURES, or COMPONENTS, which are placed inside PACKAGES, and then compiled into the destination library. The fundamental units of VHDL (figure 2.1) will be studied in Part I of the book (up to chapter 9), whereas the library-related sections (figure 2.2) will be seen in Part II (chapters 10–12). 2.2 Library Declarations To declare a LIBRARY (that is, to make it visible to the design) two lines of code are needed, one containing the name of the library, and the other a use clause, as shown in the syntax below. LIBRARY library_name; USE library_name.package_name.package_parts; At least three packages, from three di¤erent libraries, are usually needed in a design:  ieee.std_logic_1164 (from the ieee library),  standard (from the std library), and  work (work library). TLFeBOOK 14 Chapter 2 LIBRARY declarations ENTITY Basic VHDL code ARCHITECTURE Figure 2.1 Fundamental sections of a basic VHDL code. LIBRARY PACKAGE FUNCTIONS PROCEDURES COMPONENTS CONSTANTS TYPES Figure 2.2 Fundamental parts of a LIBRARY. TLFeBOOK Code Structure 15 Their declarations are as follows: LIBRARY ieee; USE ieee.std_logic_1164.all; -- A semi-colon (;) indicates -- the end of a statement or LIBRARY std; USE std.standard.all; -- declaration, while a double -- dash (--) indicates a comment. LIBRARY work; USE work.all; The libraries std and work shown above are made visible by default, so there is no need to declare them; only the ieee library must be explicitly written. However, the latter is only necessary when the STD_LOGIC (or STD_ULOGIC) data type is employed in the design (data types will be studied in detail in the next chapter). The purpose of the three packages/libraries mentioned above is the following: the std_logic_1164 package of the ieee library specifies a multi-level logic system; std is a resource library (data types, text i/o, etc.) for the VHDL design environment; and the work library is where we save our design (the .vhd file, plus all files created by the compiler, simulator, etc.). Indeed, the ieee library contains several packages, including the following: std_logic_1164: Specifies the STD_LOGIC (8 levels) and STD_ULOGIC (9 levels) multi-valued logic systems.  std_logic_arith: Specifies the SIGNED and UNSIGNED data types and related arithmetic and comparison operations. It also contains several data conversion functions, which allow one type to be converted into another: conv_integer(p), conv_unsigned(p, b), conv_signed(p, b), conv_std_logic_vector(p, b).  std_logic_signed: Contains functions that allow operations with STD_LOGIC_ VECTOR data to be performed as if the data were of type SIGNED.  std_logic_unsigned: Contains functions that allow operations with STD_LOGIC_ VECTOR data to be performed as if the data were of type UNSIGNED.  In chapter 3, all these libraries will be further described and used. 2.3 ENTITY An ENTITY is a list with specifications of all input and output pins (PORTS) of the circuit. Its syntax is shown below. TLFeBOOK 16 Chapter 2 ENTITY entity_name IS PORT ( port_name : signal_mode signal_type; port_name : signal_mode signal_type; ...); END entity_name; The mode of the signal can be IN, OUT, INOUT, or BUFFER. As illustrated in figure 2.3, IN and OUT are truly unidirectional pins, while INOUT is bidirectional. BUFFER, on the other hand, is employed when the output signal must be used (read) internally. The type of the signal can be BIT, STD_LOGIC, INTEGER, etc. Data types will be discussed in detail in chapter 3. Finally, the name of the entity can be basically any name, except VHDL reserved words (VHDL reserved words are listed in appendix E). Example: Let us consider the NAND gate of figure 2.4. Its ENTITY can be specified as: ENTITY nand_gate IS PORT (a, b : IN BIT; x : OUT BIT); END nand_gate; OUT IN Circuit INOUT BUFFER Figure 2.3 Signal modes. a b x Figure 2.4 NAND gate. TLFeBOOK Code Structure 17 The meaning of the ENTITY above is the following: the circuit has three I/O pins, being two inputs (a and b, mode IN) and one output (x, mode OUT). All three signals are of type BIT. The name chosen for the entity was nand_gate. 2.4 ARCHITECTURE The ARCHITECTURE is a description of how the circuit should behave (function). Its syntax is the following: ARCHITECTURE architecture_name OF entity_name IS [declarations] BEGIN (code) END architecture_name; As shown above, an architecture has two parts: a declarative part (optional), where signals and constants (among others) are declared, and the code part (from BEGIN down). Like in the case of an entity, the name of an architecture can be basically any name (except VHDL reserved words), including the same name as the entity’s. Example: Let us consider the NAND gate of figure 2.4 once again. ARCHITECTURE myarch OF nand_gate IS BEGIN x <= a NAND b; END myarch; The meaning of the ARCHITECTURE above is the following: the circuit must perform the NAND operation between the two input signals (a, b) and assign (‘‘<¼’’) the result to the output pin (x). The name chosen for this architecture was myarch. In this example, there is no declarative part, and the code contains just a single assignment. 2.5 Introductory Examples In this section, we will present two initial examples of VHDL code. Though we have not yet studied the constructs that appear in the examples, they will help illustrate fundamental aspects regarding the overall code structure. Each example is followed by explanatory comments and simulation results. TLFeBOOK 18 Chapter 2 d q DFF clk rst Figure 2.5 DFF with asynchronous reset. Example 2.1: DFF with Asynchronous Reset Figure 2.5 shows the diagram of a D-type flip-flop (DFF), triggered at the risingedge of the clock signal (clk), and with an asynchronous reset input (rst). When rst ¼ ‘1’, the output must be turned low, regardless of clk. Otherwise, the output must copy the input (that is, q <¼ d) at the moment when clk changes from ‘0’ to ‘1’ (that is, when an upward event occurs on clk). There are several ways of implementing the DFF of figure 2.5, one being the solution presented below. One thing to remember, however, is that VHDL is inherently concurrent (contrary to regular computer programs, which are sequential), so to implement any clocked circuit (flip-flops, for example) we have to ‘‘force’’ VHDL to be sequential. This can be done using a PROCESS, as shown below. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 --------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; --------------------------------------ENTITY dff IS PORT ( d, clk, rst: IN STD_LOGIC; q: OUT STD_LOGIC); END dff; --------------------------------------ARCHITECTURE behavior OF dff IS BEGIN PROCESS (rst, clk) BEGIN IF (rst='1') THEN q <= '0'; ELSIF (clk'EVENT AND clk='1') THEN TLFeBOOK Code Structure 19 17 q <= d; 18 END IF; 19 END PROCESS; 20 END behavior; 21 --------------------------------------- Comments: Lines 2–3: Library declaration (library name and library use clause). Recall that the other two indispensable libraries (std and work) are made visible by default. Lines 5–8: Entity d¤. Lines 10–20: Architecture behavior. Line 6: Input ports (input mode can only be IN). In this example, all input signals are of type STD_LOGIC. Line 7: Output port (output mode can be OUT, INOUT, or BUFFER). Here, the output is also of type STD_LOGIC. Lines 11–19: Code part of the architecture (from word BEGIN on). Lines 12–19: A PROCESS (inside it the code is executed sequentially). Line 12: The PROCESS is executed every time a signal declared in its sensitivity list changes. In this example, every time rst or clk changes the PROCESS is run. Lines 14–15: Every time rst goes to ‘1’ the output is reset, regardless of clk (asynchronous reset). Lines 16–17: If rst is not active, plus clk has changed (an EVENT occurred on clk), plus such event was a rising edge (clk ¼ ‘1’), then the input signal (d) is stored in the flip-flop (q <¼ d). Lines 15 and 17: The ‘‘<¼’’ operator is used to assign a value to a SIGNAL. In contrast, ‘‘:¼’’ would be used for a VARIABLE. All ports in an entity are signals by default. Lines 1, 4, 9, and 21: Commented out (recall that ‘‘- -’’ indicates a comment). Used only to better organize the design. Note: VHDL is not case sensitive. Simulation results: Figure 2.6 presents simulation results regarding example 2.1. The graphs can be easily interpreted. The first column shows the signal names, as defined in the ENTITY. It also shows the mode (direction) of the signals; notice that the arrows associated TLFeBOOK 20 Chapter 2 Figure 2.6 Simulation results of example 2.1. a q b DFF clk Figure 2.7 DFF plus NAND gate. with rst, d, and clk are inward, and contain the letter I (input) inside, while that of q is outward and has an O (output) marked inside. The second column has the value of each signal in the position where the vertical cursor is placed. In the present case, the cursor is at 0ns, where the signals have value 1, 0, 0, 0, respectively. In this example, the values are simply ‘0’ or ‘1’, but when vectors are used, the values can be shown in binary, decimal, or hexadecimal form. The third column shows the simulation proper. The input signals (rst, d, clk) can be chosen freely, and the simulator will determine the corresponding output (q). Comparing the results of figure 2.6 with those expected from the circuit shown previously, we notice that it works properly. As mentioned earlier, the designs presented in the book were synthesized onto CPLD/ FPGA devices (appendix A), either from Altera or Xilinx. The tools used were either ISE combined with ModelSim (for Xilinx chips—appendix B), or MaxPlus II combined with Advanced Synthesis Software (for Altera CPLDs—appendix C), or Quartus II (also for Altera devices—appendix D). Leonardo Spectrum (from Mentor Graphics) was also used occasionally. Example 2.2: DFF plus NAND Gate The circuit of figure 2.4 was purely combinational, while that of figure 2.5 was purely sequential. The circuit of figure 2.7 is a mixture of both (without reset). In the TLFeBOOK Code Structure 21 Figure 2.8 Simulation results of example 2.2. solution that follows, we have purposely introduced an unnecessary signal (temp), just to illustrate how a signal should be declared. Simulation results from the circuit synthesized with the code below are shown in figure 2.8. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 --------------------------------------ENTITY example IS PORT ( a, b, clk: IN BIT; q: OUT BIT); END example; --------------------------------------ARCHITECTURE example OF example IS SIGNAL temp : BIT; BEGIN temp <= a NAND b; PROCESS (clk) BEGIN IF (clk'EVENT AND clk='1') THEN q<=temp; END IF; END PROCESS; END example; --------------------------------------- Comments: Library declarations are not necessary in this case, because the data is of type BIT, which is specified in the library std (recall that the libraries std and work are made visible by default). Lines 2–5: Entity example. Lines 7–16: Architecture example. TLFeBOOK 22 Chapter 2 Line 3: Input ports (all of type BIT). Line 4: Output port (also of type BIT). Line 8: Declarative part of the architecture (optional). The signal temp, of type BIT, was declared. Notice that there is no mode declaration (mode is only used in entities). Lines 9–15: Code part of the architecture (from word BEGIN on). Lines 11–15: A PROCESS (sequential statements executed every time the signal clk changes). Lines 10 and 11–15: Though within a process the execution is sequential, the process, as a whole, is concurrent with the other (external) statements; thus line 10 is executed concurrently with the block 11–15. Line 10: Logical NAND operation. Result is assigned to signal temp. Lines 13–14: IF statement. At the rising edge of clk the value of temp is assigned to q. Lines 10 and 13: The ‘‘<¼’’ operator is used to assign a value to a SIGNAL. In contrast, ‘‘:¼’’ would be used for a VARIABLE. Lines 8 and 10: Can be eliminated, changing ‘‘q <¼ a NAND b’’ in line 13. Lines 1, 6, and 17: Commented out. Used only to better organize the design. 2.6 Problems Problem 2.1: Multiplexer The top-level diagram of a multiplexer is shown in figure P2.1. According to the truth table, the output should be equal to one of the inputs if sel ¼ ‘‘01’’ (c ¼ a) or sel ¼ ‘‘10’’ (c ¼ b), but it should be ‘0’ or Z (high impedance) if sel ¼ ‘‘00’’ or sel ¼ ‘‘11’’, respectively. a (7:0) MUX b (7:0) c (7:0) sel 00 01 10 11 c 0 a b Z sel (1:0) Figure P2.1 TLFeBOOK Code Structure 23 a) Complete the VHDL code below. b) Write relevant comments regarding your solution (as in examples 2.1 and 2.2). c) Compile and simulate your solution, checking whether it works as expected. Note: A solution using IF was employed in the code below, because it is more intuitive. However, as will be seen later, a multiplexer can also be implemented with other statements, like WHEN or CASE. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 --------------------------------------LIBRARY ieee; USE _________________________ ; --------------------------------------ENTITY mux IS PORT ( __ , __ : ___ STD_LOGIC_VECTOR (7 DOWNTO 0); sel : IN ____________________________ ; ___ : OUT STD_LOGIC_VECTOR (7 DOWNTO 0)); END _____ ; --------------------------------------ARCHITECTURE example OF _____ IS BEGIN PROCESS (a, b, ____ ) BEGIN IF (sel = "00") THEN c <= "00000000"; ELSIF (__________) THEN c <= a; _____ (sel = "10") THEN c <= __; ELSE c <= (OTHERS => '__'); END ___ ; END _________ ; END _________ ; --------------------------------------- TLFeBOOK 24 Chapter 2 d a b c Figure P2.2 Problem 2.2: Logic Gates a) Write a VHDL code for the circuit of figure P2.2. Notice that it is purely combinational, so a PROCESS is not necessary. Write an expression for d using only logical operators (AND, OR, NAND, NOT, etc.). b) Synthesize and simulate your circuit. After assuring that it works properly, open the report file and check the actual expression implemented by the compiler. Compare it with your expression. TLFeBOOK 3 Data Types In order to write VHDL code e‰ciently, it is essential to know what data types are allowed, and how to specify and use them. In this chapter, all fundamental data types are described, with special emphasis on those that are synthesizable. Discussions on data compatibility and data conversion are also included. 3.1 Pre-Defined Data Types VHDL contains a series of pre-defined data types, specified through the IEEE 1076 and IEEE 1164 standards. More specifically, such data type definitions can be found in the following packages / libraries: Package standard of library std: Defines BIT, BOOLEAN, INTEGER, and REAL data types.  Package std_logic_1164 of library ieee: Defines STD_LOGIC and STD_ULOGIC data types.  Package std_logic_arith of library ieee: Defines SIGNED and UNSIGNED data types, plus several data conversion functions, like conv_integer(p), conv_unsigned(p, b), conv_signed(p, b), and conv_std_logic_vector(p, b).  Packages std_logic_signed and std_logic_unsigned of library ieee: Contain functions that allow operations with STD_LOGIC_VECTOR data to be performed as if the data were of type SIGNED or UNSIGNED, respectively.  All pre-defined data types (specified in the packages/libraries listed above) are described below.  BIT (and BIT_VECTOR): 2-level logic (‘0’, ‘1’). Examples: SIGNAL x: BIT; -- x is declared as a one-digit signal of type BIT. SIGNAL y: BIT_VECTOR (3 DOWNTO 0); -- y is a 4-bit vector, with the leftmost bit being the MSB. SIGNAL w: BIT_VECTOR (0 TO 7); -- w is an 8-bit vector, with the rightmost bit being the MSB. Based on the signals above, the following assignments would be legal (to assign a value to a signal, the ‘‘<¼’’ operator must be used): TLFeBOOK 26 Chapter 3 x <= '1'; -- x is a single-bit signal (as specified above), whose value is -- '1'. Notice that single quotes (' ') are used for a single bit. y <= "0111"; -- y is a 4-bit signal (as specified above), whose value is "0111" -- (MSB='0'). Notice that double quotes (" ") are used for -- vectors. w <= "01110001"; -- w is an 8-bit signal, whose value is "01110001" (MSB='1'). STD_LOGIC (and STD_LOGIC_VECTOR): 8-valued logic system introduced in the IEEE 1164 standard.  ‘X’ ‘0’ ‘1’ ‘Z’ ‘W’ ‘L’ ‘H’ ‘–’ Forcing Unknown Forcing Low Forcing High High impedance Weak unknown Weak low Weak high Don’t care (synthesizable (synthesizable (synthesizable (synthesizable unknown) logic ‘1’) logic ‘0’) tri-state bu¤er) Examples: SIGNAL x: STD_LOGIC; -- x is declared as a one-digit (scalar) signal of type STD_LOGIC. SIGNAL y: STD_LOGIC_VECTOR (3 DOWNTO 0) := "0001"; -- y is declared as a 4-bit vector, with the leftmost bit being -- the MSB. The initial value (optional) of y is "0001". Notice -- that the ":=" operator is used to establish the initial value. Most of the std_logic levels are intended for simulation only. However, ‘0’, ‘1’, and ‘Z’ are synthesizable with no restrictions. With respect to the ‘‘weak’’ values, they are resolved in favor of the ‘‘forcing’’ values in multiply-driven nodes (see table 3.1). Indeed, if any two std_logic signals are connected to the same node, then conflicting logic levels are automatically resolved according to table 3.1. STD_ULOGIC (STD_ULOGIC_VECTOR): 9-level logic system introduced in the IEEE 1164 standard (‘U’, ‘X’, ‘0’, ‘1’, ‘Z’, ‘W’, ‘L’, ‘H’, ‘–’). Indeed, the  TLFeBOOK Data Types 27 Table 3.1 Resolved logic system (STD_LOGIC). X 0 1 Z W L H - X 0 1 Z W L H - X X X X X X X X X 0 X 0 0 0 0 X X X 1 1 1 1 1 X X 0 1 Z W L H X X 0 1 W W W W X X 0 1 L W L W X X 0 1 H W W H X X X X X X X X X STD_LOGIC system described above is a subtype of STD_ULOGIC. The latter includes an extra logic value, ‘U’, which stands for unresolved. Thus, contrary to STD_LOGIC, conflicting logic levels are not automatically resolved here, so output wires should never be connected together directly. However, if two output wires are never supposed to be connected together, this logic system can be used to detect design errors.  BOOLEAN: True, False.  INTEGER: 32-bit integers (from 2,147,483,647 to þ2,147,483,647).  NATURAL: Non-negative integers (from 0 to þ2,147,483,647).  REAL: Real numbers ranging from 1.0E38 to þ1.0E38. Not synthesizable. Physical literals: Used to inform physical quantities, like time, voltage, etc. Useful in simulations. Not synthesizable.  Character literals: Single ASCII character or a string of such characters. Not synthesizable.  SIGNED and UNSIGNED: data types defined in the std_logic_arith package of the ieee library. They have the appearance of STD_LOGIC_VECTOR, but accept arithmetic operations, which are typical of INTEGER data types (SIGNED and UNSIGNED will be discussed in detail in section 3.6).  Examples: x0 <= '0'; x1 <= "00011111"; x2 <= "0001_1111"; x3 <= "101111" ------ bit, std_logic, or std_ulogic value '0' bit_vector, std_logic_vector, std_ulogic_vector, signed, or unsigned underscore allowed to ease visualization binary representation of decimal 47 TLFeBOOK 28 x4 <= B"101111" x5 <= O"57" x6 <= X"2F" n <= 1200; m <= 1_200; IF ready THEN... y <= 1.2E-5; q <= d after 10 ns; Chapter 3 --------- binary representation of decimal 47 octal representation of decimal 47 hexadecimal representation of decimal 47 integer integer, underscore allowed Boolean, executed if ready=TRUE real, not synthesizable physical, not synthesizable Example: Legal and illegal operations between data of di¤erent types. SIGNAL a: BIT; SIGNAL b: BIT_VECTOR(7 DOWNTO 0); SIGNAL c: STD_LOGIC; SIGNAL d: STD_LOGIC_VECTOR(7 DOWNTO 0); SIGNAL e: INTEGER RANGE 0 TO 255; ... a <= b(5); -- legal (same scalar type: BIT) b(0) <= a; -- legal (same scalar type: BIT) c <= d(5); -- legal (same scalar type: STD_LOGIC) d(0) <= c; -- legal (same scalar type: STD_LOGIC) a <= c; -- illegal (type mismatch: BIT x STD_LOGIC) b <= d; -- illegal (type mismatch: BIT_VECTOR x -- STD_LOGIC_VECTOR) e <= b; -- illegal (type mismatch: INTEGER x BIT_VECTOR) e <= d; -- illegal (type mismatch: INTEGER x -- STD_LOGIC_VECTOR) 3.2 User-Defined Data Types VHDL also allows the user to define his/her own data types. Two categories of userdefined data types are shown below: integer and enumerated.  User-defined integer types: TYPE integer IS RANGE -2147483647 TO +2147483647; -- This is indeed the pre-defined type INTEGER. TYPE natural IS RANGE 0 TO +2147483647; -- This is indeed the pre-defined type NATURAL. TLFeBOOK Data Types 29 TYPE my_integer IS RANGE -32 TO 32; -- A user-defined subset of integers. TYPE student_grade IS RANGE 0 TO 100; -- A user-defined subset of integers or naturals.  User-defined enumerated types: TYPE bit IS ('0', '1'); -- This is indeed the pre-defined type BIT TYPE my_logic IS ('0', '1', 'Z'); -- A user-defined subset of std_logic. TYPE bit_vector IS ARRAY (NATURAL RANGE <>) OF BIT; -- This is indeed the pre-defined type BIT_VECTOR. -- RANGE <> is used to indicate that the range is unconstrained. -- NATURAL RANGE <>, on the other hand, indicates that the only -- restriction is that the range must fall within the NATURAL -- range. TYPE state IS (idle, forward, backward, stop); -- An enumerated data type, typical of finite state machines. TYPE color IS (red, green, blue, white); -- Another enumerated data type. The encoding of enumerated types is done sequentially and automatically (unless specified otherwise by a user-defined attribute, as will be shown in chapter 4). For example, for the type color above, two bits are necessary (there are four states), being ‘‘00’’ assigned to the first state (red), ‘‘01’’ to the second (green), ‘‘10’’ to the next (blue), and finally ‘‘11’’ to the last state (white). 3.3 Subtypes A SUBTYPE is a TYPE with a constraint. The main reason for using a subtype rather than specifying a new type is that, though operations between data of di¤erent types are not allowed, they are allowed between a subtype and its corresponding base type. Examples: The subtypes below were derived from the types presented in the previous examples. TLFeBOOK 30 Chapter 3 SUBTYPE natural IS INTEGER RANGE 0 TO INTEGER'HIGH; -- As expected, NATURAL is a subtype (subset) of INTEGER. SUBTYPE my_logic IS STD_LOGIC RANGE '0' TO 'Z'; -- Recall that STD_LOGIC=('X','0','1','Z','W','L','H','-'). -- Therefore, my_logic=('0','1','Z'). SUBTYPE my_color IS color RANGE red TO blue; -- Since color=(red, green, blue, white), then -- my_color=(red, green, blue). SUBTYPE small_integer IS INTEGER RANGE -32 TO 32; -- A subtype of INTEGER. Example: Legal and illegal operations between types and subtypes. SUBTYPE my_logic IS STD_LOGIC RANGE '0' TO '1'; SIGNAL a: BIT; SIGNAL b: STD_LOGIC; SIGNAL c: my_logic; ... b <= a; -- illegal (type mismatch: BIT versus STD_LOGIC) b <= c; -- legal (same "base" type: STD_LOGIC) 3.4 Arrays Arrays are collections of objects of the same type. They can be one-dimensional (1D), two-dimensional (2D), or one-dimensional-by-one-dimensional (1Dx1D). They can also be of higher dimensions, but then they are generally not synthesizable. Figure 3.1 illustrates the construction of data arrays. A single value (scalar) is shown in (a), a vector (1D array) in (b), an array of vectors (1Dx1D array) in (c), and an array of scalars (2D array) in (d). Indeed, the pre-defined VHDL data types (seen in section 3.1) include only the scalar (single bit) and vector (one-dimensional array of bits) categories. The predefined synthesizable types in each of these categories are the following:  Scalars: BIT, STD_LOGIC, STD_ULOGIC, and BOOLEAN. Vectors: BIT_VECTOR, STD_LOGIC_VECTOR, STD_ULOGIC_VECTOR, INTEGER, SIGNED, and UNSIGNED.  TLFeBOOK Data Types 0 (a) 31 0 1 0 0 0 (b) 0 1 0 0 0 0 1 0 0 0 1 0 0 1 0 1 0 0 1 0 1 1 0 0 1 1 1 0 0 1 (c) (d) Figure 3.1 Illustration of (a) scalar, (b) 1D, (c) 1Dx1D, and (d) 2D data arrays. As can be seen, there are no pre-defined 2D or 1Dx1D arrays, which, when necessary, must be specified by the user. To do so, the new TYPE must first be defined, then the new SIGNAL, VARIABLE, or CONSTANT can be declared using that data type. The syntax below should be used. To specify a new array type: TYPE type_name IS ARRAY (specification) OF data_type; To make use of the new array type: SIGNAL signal_name: type_name [:= initial_value]; In the syntax above, a SIGNAL was declared. However, it could also be a CONSTANT or a VARIABLE. Notice that the initial value is optional (for simulation only). Example: 1Dx1D array. Say that we want to build an array containing four vectors, each of size eight bits. This is then an 1Dx1D array (see figure 3.1). Let us call each vector by row, and the complete array by matrix. Additionally, say that we want the leftmost bit of each vector to be its MSB (most significant bit), and that we want the top row to be row 0. Then the array implementation would be the following (notice that a signal, called x, of type matrix, was declared as an example): TYPE row IS ARRAY (7 DOWNTO 0) OF STD_LOGIC; TYPE matrix IS ARRAY (0 TO 3) OF row; SIGNAL x: matrix; -- 1D array -- 1Dx1D array -- 1Dx1D signal TLFeBOOK 32 Chapter 3 Example: Another 1Dx1D array. Another way of constructing the 1Dx1D array above would be the following: TYPE matrix IS ARRAY (0 TO 3) OF STD_LOGIC_VECTOR(7 DOWNTO 0); From a data-compatibility point of view, the latter might be advantageous over that in the previous example (see example 3.1). Example: 2D array. The array below is truly two-dimensional. Notice that its construction is not based on vectors, but rather entirely on scalars. TYPE matrix2D IS ARRAY (0 TO 3, 7 DOWNTO 0) OF STD_LOGIC; -- 2D array Example: Array initialization. As shown in the syntax above, the initial value of a SIGNAL or VARIABLE is optional. However, when initialization is required, it can be done as in the examples below. ... :="0001"; ... :=('0','0','0','1') ... :=(('0','1','1','1'), ('1','1','1','0')); ----- for 1D array for 1D array for 1Dx1D or 2D array Example: Legal and illegal array assignments. The assignments in this example are based on the following type definitions and signal declarations: TYPE row IS ARRAY (7 DOWNTO 0) OF STD_LOGIC; -- 1D array TYPE array1 IS ARRAY (0 TO 3) OF row; -- 1Dx1D array TYPE array2 IS ARRAY (0 TO 3) OF STD_LOGIC_VECTOR(7 DOWNTO 0); -- 1Dx1D TYPE array3 IS ARRAY (0 TO 3, 7 DOWNTO 0) OF STD_LOGIC; -- 2D array SIGNAL x: row; SIGNAL y: array1; SIGNAL v: array2; SIGNAL w: array3; TLFeBOOK Data Types 33 --------- Legal scalar assignments: ---------------- The scalar (single bit) assignments below are all legal, -- because the "base" (scalar) type is STD_LOGIC for all signals -- (x,y,v,w). x(0) <= y(1)(2); -- notice two pairs of parenthesis -- (y is 1Dx1D) x(1) <= v(2)(3); -- two pairs of parenthesis (v is 1Dx1D) x(2) <= w(2,1); -- a single pair of parenthesis (w is 2D) y(1)(1) <= x(6); y(2)(0) <= v(0)(0); y(0)(0) <= w(3,3); w(1,1) <= x(7); w(3,0) <= v(0)(3); --------- Vector assignments: --------------------x <= y(0); -- legal (same data types: ROW) x <= v(1); -- illegal (type mismatch: ROW x -- STD_LOGIC_VECTOR) x <= w(2); -- illegal (w must have 2D index) x <= w(2, 2 DOWNTO 0); -- illegal (type mismatch: ROW x -- STD_LOGIC) v(0) <= w(2, 2 DOWNTO 0); -- illegal (mismatch: STD_LOGIC_VECTOR -- x STD_LOGIC) v(0) <= w(2); -- illegal (w must have 2D index) y(1) <= v(3); -- illegal (type mismatch: ROW x -- STD_LOGIC_VECTOR) y(1)(7 DOWNTO 3) <= x(4 DOWNTO 0); -- legal (same type, -- same size) v(1)(7 DOWNTO 3) <= v(2)(4 DOWNTO 0); -- legal (same type, -- same size) w(1, 5 DOWNTO 1) <= v(2)(4 DOWNTO 0); -- illegal (type mismatch) 3.5 Port Array As we have seen, there are no pre-defined data types of more than one dimension. However, in the specification of the input or output pins (PORTS) of a circuit (which is made in the ENTITY), we might need to specify the ports as arrays of vectors. Since TYPE declarations are not allowed in an ENTITY, the solution is to declare TLFeBOOK 34 Chapter 3 user-defined data types in a PACKAGE, which will then be visible to the whole design (thus including the ENTITY). An example is shown below. ------- Package: -------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; ---------------------------PACKAGE my_data_types IS TYPE vector_array IS ARRAY (NATURAL RANGE <>) OF STD_LOGIC_VECTOR(7 DOWNTO 0); END my_data_types; -------------------------------------------------- Main code: ------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; USE work.my_data_types.all; -- user-defined package --------------------------ENTITY mux IS PORT (inp: IN VECTOR_ARRAY (0 TO 3); ... ); END mux; ... ; -------------------------------------------- As can be seen in the example above, a user-defined data type, called vector_array, was created, which can contain an indefinite number of vectors of size eight bits each (NATURAL RANGE <> signifies that the range is not fixed, with the only restriction that it must fall within the NATURAL range, which goes from 0 to þ2,147,483,647). The data type was saved in a PACKAGE called my_data_types, and later used in an ENTITY to specify a PORT called inp. Notice in the main code the inclusion of an additional USE clause to make the user-defined package my_data_types visible to the design. Another option for the PACKAGE above would be that shown below, where a CONSTANT declaration is included (a detailed study of PACKAGES will be presented in chapter 10). ------- Package: ------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; TLFeBOOK Data Types 35 ---------------------------PACKAGE my_data_types IS CONSTANT b: INTEGER := 7; TYPE vector_array IS ARRAY (NATURAL RANGE <>) OF STD_LOGIC_VECTOR(b DOWNTO 0); END my_data_types; ------------------------------------------------- 3.6 Records Records are similar to arrays, with the only di¤erence that they contain objects of di¤erent types. Example: TYPE birthday IS RECORD day: INTEGER RANGE 1 TO 31; month: month_name; END RECORD; 3.7 Signed and Unsigned Data Types As mentioned earlier, these types are defined in the std_logic_arith package of the ieee library. Their syntax is illustrated in the examples below. Examples: SIGNAL x: SIGNED (7 DOWNTO 0); SIGNAL y: UNSIGNED (0 TO 3); Notice that their syntax is similar to that of STD_LOGIC_VECTOR, not like that of an INTEGER, as one might have expected. An UNSIGNED value is a number never lower than zero. For example, ‘‘0101’’ represents the decimal 5, while ‘‘1101’’ signifies 13. If type SIGNED is used instead, the value can be positive or negative (in two’s complement format). Therefore, ‘‘0101’’ would represent the decimal 5, while ‘‘1101’’ would mean 3. To use SIGNED or UNSIGNED data types, the std_logic_arith package, of the ieee library, must be declared. Despite their syntax, SIGNED and UNSIGNED data types are intended mainly for arithmetic operations, that is, contrary to TLFeBOOK 36 Chapter 3 STD_LOGIC_VECTOR, they accept arithmetic operations. On the other hand, logical operations are not allowed. With respect to relational (comparison) operations, there are no restrictions. Example: Legal and illegal operations with signed/unsigned data types. LIBRARY ieee; USE ieee.std_logic_1164.all; USE ieee.std_logic_arith.all; -- extra package necessary ... SIGNAL a: IN SIGNED (7 DOWNTO 0); SIGNAL b: IN SIGNED (7 DOWNTO 0); SIGNAL x: OUT SIGNED (7 DOWNTO 0); ... v <= a + b; -- legal (arithmetic operation OK) w <= a AND b; -- illegal (logical operation not OK) Example: Legal and illegal operations with std_logic_vector. LIBRARY ieee; USE ieee.std_logic_1164.all; -- no extra package required ... SIGNAL a: IN STD_LOGIC_VECTOR (7 DOWNTO 0); SIGNAL b: IN STD_LOGIC_VECTOR (7 DOWNTO 0); SIGNAL x: OUT STD_LOGIC_VECTOR (7 DOWNTO 0); ... v <= a + b; -- illegal (arithmetic operation not OK) w <= a AND b; -- legal (logical operation OK) Despite the constraint mentioned above, there is a simple way of allowing data of type STD_LOGIC_VECTOR to participate directly in arithmetic operations. For that, the ieee library provides two packages, std_logic_signed and std_logic_unsigned, which allow operations with STD_LOGIC_VECTOR data to be performed as if the data were of type SIGNED or UNSIGNED, respectively. Example: Arithmetic operations with std_logic_vector. LIBRARY ieee; USE ieee.std_logic_1164.all; USE ieee.std_logic_unsigned.all; ... -- extra package included TLFeBOOK Data Types SIGNAL SIGNAL SIGNAL ... v <= a w <= a 3.8 37 a: IN STD_LOGIC_VECTOR (7 DOWNTO 0); b: IN STD_LOGIC_VECTOR (7 DOWNTO 0); x: OUT STD_LOGIC_VECTOR (7 DOWNTO 0); + b; AND b; -- legal (arithmetic operation OK), unsigned -- legal (logical operation OK) Data Conversion VHDL does not allow direct operations (arithmetic, logical, etc.) between data of di¤erent types. Therefore, it is often necessary to convert data from one type to another. This can be done in basically two ways: or we write a piece of VHDL code for that, or we invoke a FUNCTION from a pre-defined PACKAGE which is capable of doing it for us. If the data are closely related (that is, both operands have the same base type, despite being declared as belonging to two di¤erent type classes), then the std_logic_1164 of the ieee library provides straightforward conversion functions. An example is shown below. Example: Legal and illegal operations with subsets. TYPE long IS INTEGER RANGE -100 TO 100; TYPE short IS INTEGER RANGE -10 TO 10; SIGNAL x : short; SIGNAL y : long; ... y <= 2*x + 5; -- error, type mismatch y <= long(2*x + 5); -- OK, result converted into type long Several data conversion functions can be found in the std_logic_arith package of the ieee library. They are: conv_integer(p) : Converts a parameter p of type INTEGER, UNSIGNED, SIGNED, or STD_ULOGIC to an INTEGER value. Notice that STD_LOGIC_ VECTOR is not included.  conv_unsigned(p, b): Converts a parameter p of type INTEGER, UNSIGNED, SIGNED, or STD_ULOGIC to an UNSIGNED value with size b bits.  conv_signed(p, b): Converts a parameter p of type INTEGER, UNSIGNED, SIGNED, or STD_ULOGIC to a SIGNED value with size b bits.  TLFeBOOK 38 Chapter 3 conv_std_logic_vector(p, b): Converts a parameter p of type INTEGER, UNSIGNED, SIGNED, or STD_LOGIC to a STD_LOGIC_VECTOR value with size b bits.  Example: Data conversion. LIBRARY ieee; USE ieee.std_logic_1164.all; USE ieee.std_logic_arith.all; ... SIGNAL a: IN UNSIGNED (7 DOWNTO 0); SIGNAL b: IN UNSIGNED (7 DOWNTO 0); SIGNAL y: OUT STD_LOGIC_VECTOR (7 DOWNTO 0); ... y <= CONV_STD_LOGIC_VECTOR ((a+b), 8); -- Legal operation: a+b is converted from UNSIGNED to an -- 8-bit STD_LOGIC_VECTOR value, then assigned to y. Another alternative was already mentioned in the previous section. It consists of using the std_logic_signed or the std_logic_unsigned package from the ieee library. Such packages allow operations with STD_LOGIC_VECTOR data to be performed as if the data were of type SIGNED or UNSIGNED, respectively. Besides the data conversion functions described above, several others are often o¤ered by synthesis tool vendors. 3.9 Summary The fundamental synthesizable VHDL data types are summarized in table 3.2. 3.10 Additional Examples We close this chapter with the presentation of additional examples illustrating the specification and use of data types. The development of actual designs from scratch will only be possible after we conclude laying out the basic foundations of VHDL (chapters 1 to 4). Example 3.1: Dealing with Data Types The legal and illegal assignments presented next are based on the following type definitions and signal declarations: TLFeBOOK Data Types 39 Table 3.2 Synthesizable data types. Data types Synthesizable values BIT, BIT_VECTOR STD_LOGIC, STD_LOGIC_VECTOR STD_ULOGIC, STD_ULOGIC_VECTOR BOOLEAN NATURAL INTEGER SIGNED UNSIGNED User-defined integer type User-defined enumerated type SUBTYPE ARRAY RECORD ‘0’, ‘1’ ‘X’, ‘0’, ‘1’, ‘Z’ (resolved) ‘X’, ‘0’, ‘1’, ‘Z’ (unresolved) True, False From 0 to þ2, 147, 483, 647 From 2,147,483,647 to þ2,147,483,647 From 2,147,483,647 to þ2,147,483,647 From 0 to þ2,147,483,647 Subset of INTEGER Collection enumerated by user Subset of any type (pre- or user-defined) Single-type collection of any type above Multiple-type collection of any types above TYPE byte IS ARRAY (7 DOWNTO 0) OF STD_LOGIC; -- 1D -- array TYPE mem1 IS ARRAY (0 TO 3, 7 DOWNTO 0) OF STD_LOGIC; -- 2D -- array TYPE mem2 IS ARRAY (0 TO 3) OF byte; -- 1Dx1D -- array TYPE mem3 IS ARRAY (0 TO 3) OF STD_LOGIC_VECTOR(0 TO 7); -- 1Dx1D -- array SIGNAL a: STD_LOGIC; -- scalar signal SIGNAL b: BIT; -- scalar signal SIGNAL x: byte; -- 1D signal SIGNAL y: STD_LOGIC_VECTOR (7 DOWNTO 0); -- 1D signal SIGNAL v: BIT_VECTOR (3 DOWNTO 0); -- 1D signal SIGNAL z: STD_LOGIC_VECTOR (x'HIGH DOWNTO 0); -- 1D signal SIGNAL w1: mem1; -- 2D signal SIGNAL w2: mem2; -- 1Dx1D signal SIGNAL w3: mem3; -- 1Dx1D signal -------- Legal scalar assignments: --------------------x(2) <= a; -- same types (STD_LOGIC), correct indexing y(0) <= x(0); -- same types (STD_LOGIC), correct indexing z(7) <= x(5); -- same types (STD_LOGIC), correct indexing b <= v(3); -- same types (BIT), correct indexing w1(0,0) <= x(3); -- same types (STD_LOGIC), correct indexing TLFeBOOK 40 Chapter 3 w1(2,5) <= y(7); -- same types (STD_LOGIC), correct indexing w2(0)(0) <= x(2); -- same types (STD_LOGIC), correct indexing w2(2)(5) <= y(7); -- same types (STD_LOGIC), correct indexing w1(2,5) <= w2(3)(7); -- same types (STD_LOGIC), correct indexing ------- Illegal scalar assignments: -------------------b <= a; -- type mismatch (BIT x STD_LOGIC) w1(0)(2) <= x(2); -- index of w1 must be 2D w2(2,0) <= a; -- index of w2 must be 1Dx1D ------- Legal vector assignments: ---------------------x <= "11111110"; y <= ('1','1','1','1','1','1','0','Z'); z <= "11111" & "000"; x <= (OTHERS => '1'); y <= (7 =>'0', 1 =>'0', OTHERS => '1'); z <= y; y(2 DOWNTO 0) <= z(6 DOWNTO 4); w2(0)(7 DOWNTO 0) <= "11110000"; w3(2) <= y; z <= w3(1); z(5 DOWNTO 0) <= w3(1)(2 TO 7); w3(1) <= "00000000"; w3(1) <= (OTHERS => '0'); w2 <= ((OTHERS=>'0'),(OTHERS=>'0'),(OTHERS=>'0'),(OTHERS=>'0')); w3 <= ("11111100", ('0','0','0','0','Z','Z','Z','Z',), (OTHERS=>'0'), (OTHERS=>'0')); w1 <= ((OTHERS=>'Z'), "11110000" ,"11110000", (OTHERS=>'0')); ------ Illegal array assignments: ---------------------x <= y; -- type mismatch y(5 TO 7) <= z(6 DOWNTO 0); -- wrong direction of y w1 <= (OTHERS => '1'); -- w1 is a 2D array w1(0, 7 DOWNTO 0) <="11111111"; -- w1 is a 2D array w2 <= (OTHERS => 'Z'); -- w2 is a 1Dx1D array w2(0, 7 DOWNTO 0) <= "11110000"; -- index should be 1Dx1D -- Example of data type independent array initialization: FOR i IN 0 TO 3 LOOP FOR j IN 7 DOWNTO 0 LOOP x(j) <= '0'; y(j) <= '0' TLFeBOOK Data Types 41 z(j) <= '0'; w1(i,j) <= '0'; w2(i)(j) <= '0'; w3(i)(j) <= '0'; END LOOP; END LOOP; --------------------------------------------------------- Example 3.2: Single Bit Versus Bit Vector This example illustrates the di¤erence between a single bit assignment and a bit vector assignment (that is, BIT versus BIT_VECTOR, STD_LOGIC versus STD_ LOGIC_VECTOR, or STD_ULOGIC versus STD_ULOGIC_VECTOR). Two VHDL codes are presented below. Both perform the AND operation between the input signals and assign the result to the output signal. The only di¤erence between them is the number of bits in the input and output ports (one bit in the first, four bits in the second). The circuits inferred from these codes are shown in figure 3.2. ---------------------------- ----------------------------------- ENTITY and2 IS ENTITY and2 IS PORT (a, b: IN BIT; PORT (a, b: IN BIT_VECTOR (0 TO 3); x: OUT BIT_VECTOR (0 TO 3)); x: OUT BIT); END and2; END and2; ---------------------------- ----------------------------------- ARCHITECTURE and2 OF and2 IS ARCHITECTURE and2 OF and2 IS BEGIN BEGIN x <= a AND b; x <= a AND b; END and2; END and2; ---------------------------- ----------------------------------- Example 3.3: Adder Figure 3.3 shows the top-level diagram of a 4-bit adder. The circuit has two inputs (a, b) and one output (sum). Two solutions are presented. In the first, all signals are of type SIGNED, while in the second the output is of type INTEGER. Notice in solution 2 that a conversion function was used in line 13, for the type of a þ b does not match that of sum. Notice also the inclusion of the std_logic_arith package (line 4 of each solution), which specifies the SIGNED data type. Recall that a SIGNED value is represented like a vector; that is, similar to STD_LOGIC_VECTOR, not like an INTEGER. TLFeBOOK 42 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Chapter 3 ----- Solution 1: in/out=SIGNED ---------LIBRARY ieee; USE ieee.std_logic_1164.all; USE ieee.std_logic_arith.all; -----------------------------------------ENTITY adder1 IS PORT ( a, b : IN SIGNED (3 DOWNTO 0); sum : OUT SIGNED (4 DOWNTO 0)); END adder1; -----------------------------------------ARCHITECTURE adder1 OF adder1 IS BEGIN sum <= a + b; END adder1; ------------------------------------------ a a(0) x b b(0) x(0) a(1) b(1) x(1) a(2) b(2) x(2) a(3) b(3) x(3) Figure 3.2 Circuits inferred from the codes of example 3.2. a (3:0) b (3:0) + sum (4:0) Figure 3.3 4-bit adder of example 3.3. TLFeBOOK Data Types 43 Figure 3.4 Simulation results of example 3.3. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ------ Solution 2: out=INTEGER ----------LIBRARY ieee; USE ieee.std_logic_1164.all; USE ieee.std_logic_arith.all; -----------------------------------------ENTITY adder2 IS PORT ( a, b : IN SIGNED (3 DOWNTO 0); sum : OUT INTEGER RANGE -16 TO 15); END adder2; -----------------------------------------ARCHITECTURE adder2 OF adder2 IS BEGIN sum <= CONV_INTEGER(a + b); END adder2; ------------------------------------------ Simulation results (for either solution) are presented in figure 3.4. Notice that the numbers are represented in hexadecimal 2’s complement form. Since the input range is from 8 to 7, its representation is 7 ! 7, 6 ! 6, . . . , 0 ! 0, 1 ! 15, 2 ! 14, . . . , 8 ! 8. Likewise, the output range is from 16 to 15, so its representation is 15 ! 15, . . . , 0 ! 0, 1 ! 31, . . . , 16 ! 16. Therefore, 2H þ 4H ¼ 06H (that is, 2 þ 4 ¼ 6), 4H þ 8H ¼ 1CH (that is, 4 þ (8) ¼ 4), etc., where H ¼ Hexadecimal. 3.11 Problems The problems below are based on the following TYPE definitions and SIGNAL declarations: TYPE array1 IS ARRAY (7 DOWNTO 0) OF STD_LOGIC; TYPE array2 IS ARRAY (3 DOWNTO 0, 7 DOWNTO 0) OF STD_LOGIC; TYPE array3 IS ARRAY (3 DOWNTO 0) OF array1; TLFeBOOK 44 SIGNAL SIGNAL SIGNAL SIGNAL SIGNAL SIGNAL Chapter 3 a b x y w z : : : : : : BIT; STD_LOGIC;: array1; array2; array3; STD_LOGIC_VECTOR (7 DOWNTO 0); Problem 3.1 Determine the dimensionality (scalar, 1D, 2D, or 1Dx1D) of the signals given. Also, write down a numeric example for each signal. Problem 3.2 Determine which among the assignments in table P3.2 are legal and which are illegal. Briefly justify your answers. Also, determine the dimensionality of each assignment (on both sides). Problem 3.3: Subtypes Consider the pre-defined data types INTEGER and STD_LOGIC_VECTOR. Consider also the user-defined types ARRAY1 and ARRAY2 specified above. For each, write down a possible SUBTYPE. Problem 3.4: ROM Consider the implementation of a ROM (read-only memory). It can be done utilizing a 1Dx1D CONSTANT. Say that the ROM must be organized as a pile of eight words of four bits each. Create an array called rom, then define a signal of type rom capable of solving this problem. Choose the values to be stored in the ROM and declare them along with your CONSTANT, that is, ‘‘CONSTANT my_rom: rom :=(values);’’. Problem 3.5: Simple Adder Rewrite solution 1 of example 3.3, but this time with all input and output signals of type STD_LOGIC_VECTOR. (Suggestion: review section 3.8). TLFeBOOK Data Types 45 Table P3.2 Assignment Dimension (on each side) Legal or illegal (why) a <= x(2); b <= x(2); b <= y(3,5); b <= w(5)(3); y(1)(0) <= z(7); x(0) <= y(0,0); x <= a <= y(1) w(0) w(1) y(1) "1110000"; "0000000"; <= x; <= y; <= (7=>'1', OTHERS=>'0'); <= (0=>'0', OTHERS=>'1'); w(2)(7 DOWNTO 0) <= x; w(0)(7 DOWNTO 6) <= z(5 DOWNTO 4); x(3) <= x(5 DOWNTO 5); b <= x(5 DOWNTO 5); y <= ((OTHERS=>'0'), (OTHERS=>'0'), (OTHERS=>'0'), "10000001"); z(6) <= x(5); z(6 DOWNTO 4) <= x(5 DOWNTO 3); z(6 DOWNTO 4) <= y(5 DOWNTO 3); y(6 DOWNTO 4) <= z(3 TO 5); y(0, 7 DOWNTO 0) <= z; w(2,2) <= '1'; TLFeBOOK TLFeBOOK 4 Operators and Attributes The purpose of this chapter, along with the preceding chapters, is to lay the basic foundations of VHDL, so in the next chapter we can start dealing with actual circuit designs. It is indeed impossible—or little productive, at least—to write any code efficiently without undertaking first the sacrifice of understanding data types, operators, and attributes well. Operators and attributes constitute a relatively long list of general VHDL constructs, which are often examined only sparsely. We have collected them together in a specific chapter in order to provide a complete and more consistent view. At the end of the chapter, a few design examples will be presented. However, due to the fact that this is still a ‘‘foundation’’ chapter, the examples are merely illustrative, like those in the preceding chapters. As mentioned above, we will start dealing with actual designs in chapter 5. 4.1 Operators VHDL provides several kinds of pre-defined operators:  Assignment operators  Logical operators  Arithmetic operators  Relational operators  Shift operators  Concatenation operators Each of these categories is described below. Assignment Operators Are used to assign values to signals, variables, and constants. They are: <¼ Used to assign a value to a SIGNAL. :¼ Used to assign a value to a VARIABLE, CONSTANT, or GENERIC. Used also for establishing initial values. ¼> Used to assign values to individual vector elements or with OTHERS. Example: Consider the following signal and variable declarations: SIGNAL x : STD_LOGIC; VARIABLE y : STD_LOGIC_VECTOR(3 DOWNTO 0); -- Leftmost bit is MSB TLFeBOOK 48 Chapter 4 SIGNAL w: STD_LOGIC_VECTOR(0 TO 7); -- Rightmost bit is -- MSB Then the following assignments are legal: x y w w <= := <= <= '1'; -- '1' is assigned to SIGNAL x using "<=" "0000"; -- "0000" is assigned to VARIABLE y using ":=" "10000000"; -- LSB is '1', the others are '0' (0 =>'1', OTHERS =>'0'); -- LSB is '1', the others are '0' Logical Operators Used to perform logical operations. The data must be of type BIT, STD_LOGIC, or STD_ULOGIC (or, obviously, their respective extensions, BIT_VECTOR, STD_LOGIC_VECTOR, or STD_ULOGIC_VECTOR). The logical operators are:  NOT  AND  OR  NAND  NOR  XOR  XNOR Notes: The NOT operator has precedence over the others. The XNOR operator was introduced in VHDL93. Examples: y <= NOT a AND b; y <= NOT (a AND b); y <= a NAND b; -- (a'.b) -- (a.b)' -- (a.b)' Arithmetic Operators Used to perform arithmetic operations. The data can be of type INTEGER, SIGNED, UNSIGNED, or REAL (recall that the last cannot be synthesized directly). Also, if the std_logic_signed or the std_logic_unsigned package of the ieee library is used, then STD_LOGIC_VECTOR can also be employed directly in addition and subtraction operations (as seen in section 3.6). TLFeBOOK Operators and Attributes þ Addition  Subtraction * Multiplication / Division ** Exponentiation 49 MOD Modulus REM Remainder ABS Absolute value There are no synthesis restrictions regarding addition and subtraction, and the same is generally true for multiplication. For division, only power of two dividers (shift operation) are allowed. For exponentiation, only static values of base and exponent are accepted. Regarding the mod and rem operators, y mod x returns the remainder of y/x with the signal of x, while y rem x returns the remainder of y/x with the signal of y. Finally, abs returns the absolute value. With respect to the last three operators (mod, rem, abs), there generally is little or no synthesis support. Comparison Operators Used for making comparisons. The data can be of any of the types listed above. The relational (comparison) operators are: ¼ Equal to =¼ Not equal to < Less than > Greater than <¼ Less than or equal to >¼ Greater than or equal to Shift Operators Used for shifting data. They were introduced in VHDL93. Their syntax is the following: 3left operand4 3shift operation4 3right operand4. The left operand must be of type BIT_VECTOR, while the right operand must be an INTEGER (þ or  in front of it is accepted). The shift operators are:  sll Shift left logic – positions on the right are filled with ‘0’s  srl Shift right logic – positions on the left are filled with ‘0’s TLFeBOOK Operators and Attributes 51 Data Attributes The pre-defined, synthesizable data attributes are the following:  d’LOW: Returns lower array index  d’HIGH: Returns upper array index  d’LEFT: Returns leftmost array index  d’RIGHT: Returns rightmost array index  d’LENGTH: Returns vector size  d’RANGE: Returns vector range  d’REVERSE_RANGE: Returns vector range in reverse order Example: Consider the following signal: SIGNAL d : STD_LOGIC_VECTOR (7 DOWNTO 0); Then: d'LOW=0, d'HIGH=7, d'LEFT=7, d'RIGHT=0, d'LENGTH=8, d'RANGE=(7 downto 0), d'REVERSE_RANGE=(0 to 7). Example: Consider the following signal: SIGNAL x: STD_LOGIC_VECTOR (0 TO 7); Then all four LOOP statements below are synthesizable and equivalent. FOR FOR FOR FOR i i i i IN IN IN IN RANGE (0 TO 7) LOOP ... x'RANGE LOOP ... RANGE (x'LOW TO x'HIGH) LOOP ... RANGE (0 TO x'LENGTH-1) LOOP ... If the signal is of enumerated type, then:  d’VAL(pos): Returns value in the position specified  d’POS(value): Returns position of the value specified  d’LEFTOF(value): Returns value in the position to the left of the value specified  d’VAL(row, column): Returns value in the position specified; etc. There is little or no synthesis support for enumerated data type attributes. TLFeBOOK 52 Chapter 4 Signal Attributes Let us consider a signal s. Then:  s’EVENT: Returns true when an event occurs on s  s’STABLE: Returns true if no event has occurred on s  s’ACTIVE: Returns true if s ¼ ‘1’  s’QUIET 3time4: Returns true if no event has occurred during the time specified  s’LAST_EVENT: Returns the time elapsed since last event  s’LAST_ACTIVE: Returns the time elapsed since last s ¼ ‘1’  s’LAST_VALUE: Returns the value of s before the last event; etc. Though most signal attributes are for simulation purposes only, the first two in the list above are synthesizable, s’EVENT being the most often used of them all. Example: All four assignments shown below are synthesizable and equivalent. They return TRUE when an event (a change) occurs on clk, AND if such event is upward (in other words, when a rising edge occurs on clk). IF (clk'EVENT AND clk='1')... IF (NOT clk'STABLE AND clk='1')... WAIT UNTIL (clk'EVENT AND clk='1'); IF RISING_EDGE(clk)... -------- EVENT attribute used with IF STABLE attribute used with IF EVENT attribute used with WAIT call to a function 4.3 User-Defined Attributes We saw above attributes of the type HIGH, RANGE, EVENT, etc. Those are all pre-defined in VHDL87. However, VHDL also allows the construction of userdefined attributes. To employ a user-defined attribute, it must be declared and specified. The syntax is the following: Attribute declaration: ATTRIBUTE attribute_name: attribute_type; TLFeBOOK Operators and Attributes 53 Attribute specification: ATTRIBUTE attribute_name OF target_name: class IS value; where: attribute_type: any data type (BIT, INTEGER, STD_LOGIC_VECTOR, etc.) class: TYPE, SIGNAL, FUNCTION, etc. value: ‘0’, 27, ‘‘00 11 10 01’’, etc. Example: ATTRIBUTE number_of_inputs: INTEGER; -- declaration ATTRIBUTE number_of_inputs OF nand3: SIGNAL IS 3; -- specification ... inputs <= nand3'number_of_pins; -- attribute call, returns 3 Example: Enumerated encoding. A popular user-defined attribute, which is provided by synthesis tool vendors, is the enum_encoding attribute. By default, enumerated data types are encoded sequentially. Thus, if we consider the enumerated data type color shown below: TYPE color IS (red, green, blue, white); its states will be encoded as red ¼ ‘‘00’’, green ¼ ‘‘01’’, blue ¼ ‘‘10’’, and white ¼ ‘‘11’’. Enum_encoding allows the default encoding (sequential) to be changed. Thus the following encoding scheme could be employed, for example: ATTRIBUTE enum_encoding OF color: TYPE IS "11 00 10 01"; A user-defined attribute can be declared anywhere, except in a PACKAGE BODY. When not recognized by the synthesis tool, it is simply ignored, or a warning is issued. 4.4 Operator Overloading We have just seen that attributes can be user-defined. The same is true for operators. As an example, let us consider the pre-defined arithmetic operators seen in section 4.1 (þ, , *, /, etc.). They specify arithmetic operations between data of certain types TLFeBOOK 54 Chapter 4 (INTEGER, for example). For instance, the pre-defined ‘‘þ’’ operator does not allow addition between data of type BIT. We can define our own operators, using the same name as the pre-defined ones. For example, we could use ‘‘þ’’ to indicate a new kind of addition, this time between values of type BIT_VECTOR. This technique is called operator overloading. Example: Consider that we want to add an integer to a binary 1-bit number. Then the following FUNCTION could be used (details on how to construct and use a FUNCTION will be seen in chapter 11): -------------------------------------FUNCTION "+" (a: INTEGER, b: BIT) RETURN INTEGER IS BEGIN IF (b='1') THEN RETURN a+1; ELSE RETURN a; END IF; END "+"; -------------------------------------- A call to the function above could thus be the following: -----------------------------SIGNAL inp1, outp: INTEGER RANGE 0 TO 15; SIGNAL inp2: BIT; (...) outp <= 3 + inp1 + inp2; (...) ------------------------------ In ‘‘outp<=3+inp1+inp2;’’, the first ‘‘+’’ is the pre-defined addition operator (adds two integers), while the second is the overloaded user-defined addition operator (adds an integer and a bit). 4.5 GENERIC As the name suggests, GENERIC is a way of specifying a generic parameter (that is, a static parameter that can be easily modified and adapted to di¤erent applications). The purpose is to confer the code more flexibility and reusability. A GENERIC statement, when employed, must be declared in the ENTITY. The specified parameter will then be truly global (that is, visible to the whole design, including the ENTITY itself ). Its syntax is shown below. TLFeBOOK Operators and Attributes 55 GENERIC (parameter_name : parameter_type := parameter_value); Example: The GENERIC statement below specifies a parameter called n, of type INTEGER, whose default value is 8. Therefore, whenever n is found in the ENTITY itself or in the ARCHITECTURE (one or more) that follows, its value will be assumed to be 8. ENTITY my_entity IS GENERIC (n : INTEGER := 8); PORT (...); END my_entity; ARCHITECTURE my_architecture OF my_entity IS ... END my_architecture: More than one GENERIC parameter can be specified in an ENTITY. For example: GENERIC (n: INTEGER := 8; vector: BIT_VECTOR := "00001111"); Complete design examples, further illustrating the use of GENERIC and other attributes and operators, are presented below. 4.6 Examples We show now a few complete design examples, with the purpose of further illustrating the use of operators, attributes and GENERIC. Recall, however, that so far we have just worked on establishing the basic foundations of VHDL, with the formal discussion on coding techniques starting only in the next chapter (chapter 5). Therefore, a first-time VHDL student should not feel discouraged if the constructs in the examples look still unfamiliar. Instead, you may have a look at the examples now, and then, after studying chapters 5 to 7, return and reexamine them. Example 4.1: Generic Decoder Figure 4.1 shows the top-level diagram of a generic m-by-n decoder. The circuit has two inputs, sel (m bits) and ena (single bit), and one output, x (n bits). We assume that n is a power of two, so m ¼ log2 n. If ena ¼ ‘0’, then all bits of x should be high; TLFeBOOK 56 Chapter 4 sel (m-1:0) mxn DECODER ena x(n-1) x(n-2) … x(1) x(0) ena sel x 0 1 00 00 01 10 11 1111 1110 1101 1011 0111 Figure 4.1 Decoder of example 4.1. otherwise, the output bit selected by sel should be low, as illustrated in the truth table of figure 4.1. The ARCHITECTURE below is totally generic, for the only changes needed to operate with di¤erent values of m and n are in the ENTITY (through sel, line 7, and x, line 8, respectively). In this example, we have used m ¼ 3 and n ¼ 8. However, though this works fine, the use of GENERIC would have made it clearer that m and n are indeed generic parameters. That is indeed the procedure that we will adopt in the other examples that follow (please refer to problem 4.4). Notice in the code below the use of the following operators: ‘‘þ’’ (line 22), ‘‘*’’ (lines 22 and 24), ‘‘:¼’’ (lines 17, 18, 22, 24, and 27), ‘‘<¼’’ (line 29), and ‘‘¼>’’ (line 17). Notice also the use of the following attributes: HIGH (lines 14–15) and RANGE (line 20). 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 --------------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; --------------------------------------------ENTITY decoder IS PORT ( ena : IN STD_LOGIC; sel : IN STD_LOGIC_VECTOR (2 DOWNTO 0); x : OUT STD_LOGIC_VECTOR (7 DOWNTO 0)); END decoder; --------------------------------------------ARCHITECTURE generic_decoder OF decoder IS BEGIN PROCESS (ena, sel) VARIABLE temp1 : STD_LOGIC_VECTOR (x'HIGH DOWNTO 0); VARIABLE temp2 : INTEGER RANGE 0 TO x'HIGH; BEGIN TLFeBOOK Operators and Attributes 57 Figure 4.2 Simulation results of example 4.1. 17 temp1 := (OTHERS => '1'); 18 temp2 := 0; 19 IF (ena='1') THEN 20 FOR i IN sel'RANGE LOOP -- sel range is 2 downto 0 21 IF (sel(i)='1') THEN -- Bin-to-Integer conversion 22 temp2:=2*temp2+1; 23 ELSE 24 temp2 := 2*temp2; 25 END IF; 26 END LOOP; 27 temp1(temp2):='0'; 28 END IF; 29 x <= temp1; 30 END PROCESS; 31 END generic_decoder; 32 --------------------------------------------- The functionality of the encoder above can be verified in the simulation results of figure 4.2. As can be seen, all outputs are high, that is, x ¼ ‘‘11111111’’ (decimal 255), when ena ¼ ‘0’. After ena has been asserted, only one output bit (that selected by sel) is turned low. For example, when sel ¼ ‘‘000’’ (decimal 0), x ¼ ‘‘11111110’’ (decimal 254); when sel ¼ ‘‘001’’ (decimal 1), x ¼ ‘‘11111101’’ (decimal 253); when sel ¼ ‘‘010’’ (decimal 2), x ¼ ‘‘11111011’’ (decimal 251); and so on. Example 4.2: Generic Parity Detector Figure 4.3 shows the top-level diagram of a parity detector. The circuit must provide output ¼ ‘0’ when the number of ‘1’s in the input vector is even, or output ¼ ‘1’ otherwise. Notice in the VHDL code below that the ENTITY contains a GENERIC statement (line 3), which defines n as 7. This code would work for any other vector TLFeBOOK 58 Chapter 4 input (n:0) PARITY DETECTOR output Figure 4.3 Generic parity detector of example 4.2. Figure 4.4 Simulation results of example 4.2. size, being only necessary to change the value of n in that line. You are invited to highlight the operators and attributes that appear in this design. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 -------------------------------------------ENTITY parity_det IS GENERIC (n : INTEGER := 7); PORT ( input: IN BIT_VECTOR (n DOWNTO 0); output: OUT BIT); END parity_det; -------------------------------------------ARCHITECTURE parity OF parity_det IS BEGIN PROCESS (input) VARIABLE temp: BIT; BEGIN temp := '0'; FOR i IN input'RANGE LOOP temp := temp XOR input(i); END LOOP; output <= temp; END PROCESS; END parity; -------------------------------------------- TLFeBOOK Operators and Attributes input (n-1:0) PARITY GENERATOR 59 output (n:0) Figure 4.5 Generic parity generator of example 4.3. Simulation results from the circuit synthesized with the code above are shown in figure 4.4. Notice that when input ¼ ‘‘00000000’’ (decimal 0), the output is ‘0’, because the number of ‘1’s is even; when input ¼ ‘‘00000001’’ (decimal 1), the output is ‘1’, because the number of ‘1’s is odd; and so on. Example 4.3: Generic Parity Generator The circuit of figure 4.5 must add one bit to the input vector (on its left). Such bit must be a ‘0’ if the number of ‘1’s in the input vector is even, or a ‘1’ if it is odd, such that the resulting vector will always contain an even number of ‘1’s (even parity). A VHDL code for the parity generator is shown below. Once again, you are invited to highlight the operators and attributes used in the design. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ----------------------------------------------ENTITY parity_gen IS GENERIC (n : INTEGER := 7); PORT ( input: IN BIT_VECTOR (n-1 DOWNTO 0); output: OUT BIT_VECTOR (n DOWNTO 0)); END parity_gen; ----------------------------------------------ARCHITECTURE parity OF parity_gen IS BEGIN PROCESS (input) VARIABLE temp1: BIT; VARIABLE temp2: BIT_VECTOR (output'RANGE); BEGIN temp1 := '0'; FOR i IN input'RANGE LOOP temp1 := temp1 XOR input(i); temp2(i) := input(i); END LOOP; TLFeBOOK 60 Chapter 4 Figure 4.6 Simulation results of example 4.3. Table 4.1 Operators. Operator type Operators Data types Assignment <¼, :¼, ¼> Any Logical NOT, AND, NAND, OR, NOR, XOR, XNOR BIT, BIT_VECTOR, STD_LOGIC, STD_LOGIC_VECTOR, STD_ULOGIC, STD_ULOGIC_VECTOR Arithmetic þ, , *, /, ** (mod, rem, abs)) INTEGER, SIGNED, UNSIGNED Comparison ¼, =¼, <, >, <¼, >¼ All above Shift sll, srl, sla, sra, rol, ror BIT_VECTOR Concatenation &, ( , , , ) Same as for logical operators, plus SIGNED and UNSIGNED 19 temp2(output'HIGH) := temp1; 20 output <= temp2; 21 END PROCESS; 22 END parity; 23 ----------------------------------------------- Simulation results are presented in figure 4.6. As can be seen, when input ¼ ‘‘0000000’’ (decimal 0, with seven bits), output ¼ ‘‘00000000’’ (decimal 0, with eight bits); when input ¼ ‘‘0000001’’ (decimal 1, with seven bits), output ¼ ‘‘10000001’’ (decimal 129, with eight bits); and so on. 4.7 Summary A summary of VHDL operators and attributes is presented in tables 4.1 and 4.2, respectively. The constructs that are not synthesizable (or have little synthesis support) are marked with the ‘‘ ) ’’ symbol. TLFeBOOK Operators and Attributes 61 Table 4.2 Attributes. Application Attributes Return value For regular DATA d’LOW d’HIGH d’LEFT d’RIGHT d’LENGTH d’RANGE d’REVERSE_RANGE Lower array index Upper array index Leftmost array index Rightmost array index Vector size Vector range Reverse vector range For enumerated DATA d’VAL(pos)) d’POS(value)) d’LEFTOF(value)) d’VAL(row, column)) Value in the position specified Position of the value specified Value in the position to the left of the value specified Value in the position specified For a SIGNAL s’EVENT s’STABLE s’ACTIVE ) True when an event occurs on s True if no event has occurred on s True if s is high 4.8 Problems Problems 4.1 to 4.3 are based on the following signal declarations: SIGNAL SIGNAL SIGNAL SIGNAL SIGNAL SIGNAL a b c d e f : : : : : : Problem 4.1: BIT := '1'; BIT_VECTOR (3 BIT_VECTOR (3 BIT_VECTOR (7 INTEGER RANGE INTEGER RANGE DOWNTO 0) := "1100"; DOWNTO 0) := "0010"; DOWNTO 0); 0 TO 255; -128 TO 127; Operators (fill in the blanks) -> x1 <= a & c; x1 <= ________ -> x2 <= c & b; x2 <= ________ -> x3 <= b XOR c; x3 <= ________ x4 <= a NOR b(3); > x4 <= ________ -> x5 <= b sll 2; x5 <= ________ -> x6 <= b sla 2; x6 <= ________ -> x7 <= b rol 2; x7 <= ________ -> x8 <= a AND NOT b(0) AND NOT c(1); -> d <= (5=>'0', OTHERS=>'1'); x8 <= ________ d <= ________ TLFeBOOK 62 Problem 4.2: Chapter 4 Attributes (fill in the blanks) c'LOW d'HIGH c'LEFT d'RIGHT c'RANGE d'LENGTH c'REVERSE_RANGE Problem 4.3: -> -> -> -> -> -> -> ______ ______ ______ ______ ______ ______ ______ Legal and Illegal Operations Verify whether each of the operations below is legal or illegal. Briefly justify your answers. b(0) AND a a + d(7) NOT b XNOR c c + d e - f IF (b<c) ... IF (b>=a) ... IF (f/=e) ... IF (e>d) ... b sra 1 c srl -2 f ror 3 e*3 5**5 f/4 e/3 d <= c d(6 DOWNTO 3) := b e <= d f := 100 Problem 4.4: Generic Decoder The questions below are related to decoder circuit designed in example 4.1. (a) In order for that design to operate with another vector size, two values must be changed: the range of sel (line 7) and the range of x (line 8). We want now to trans- TLFeBOOK Operators and Attributes 63 form that design in a truly generic one. In order to do so, introduce a GENERIC statement in the ENTITY, specifying the number of bits of sel (say, n ¼ 3), then replace the upper range limits of sel and x by an attribute which is a function of n. Synthesize and simulate your circuit in order to verify its functionality. (b) In example 4.1, a binary-to-integer conversion was implemented (lines 20–26). This conversion could be avoided if sel had been declared as an INTEGER. Modify the code, declaring sel as an INTEGER. The code should remain truly generic, so the range of sel must be specified in terms of n. Synthesize and simulate your new code. Problem 4.5 List all operators, attributes and generics employed in examples 4.2 and 4.3. TLFeBOOK TLFeBOOK 5 Concurrent Code Having finished laying out the basic foundations of VHDL (chapters 1 to 4), we can now concentrate on the design (code) itself. VHDL code can be concurrent (parallel) or sequential. The former will be studied in this chapter, while the latter will be seen in chapter 6. This division is very important, for it allows a better understanding of which statements are intended for each kind of code, as well as the consequences of using one or the other. The concurrent statements in VHDL are WHEN and GENERATE. Besides them, assignments using only operators (AND, NOT, þ, *, sll, etc.) can also be used to construct concurrent code. Finally, a special kind of assignment, called BLOCK, can also be employed in this kind of code. 5.1 Concurrent versus Sequential We start this chapter by reviewing the fundamental di¤erences between combinational logic and sequential logic, and by contrasting them with the di¤erences between concurrent code and sequential code. Combinational versus Sequential Logic By definition, combinational logic is that in which the output of the circuit depends solely on the current inputs (figure 5.1(a)). It is then clear that, in principle, the system requires no memory and can be implemented using conventional logic gates. In contrast, sequential logic is defined as that in which the output does depend on previous inputs (figure 5.1(b)). Therefore, storage elements are required, which are connected to the combinational logic block through a feedback loop, such that now the stored states (created by previous inputs) will also a¤ect the output of the circuit. A common mistake is to think that any circuit that possesses storage elements (flip-flops) is sequential. A RAM (Random Access Memory) is an example. A RAM can be modeled as in figure 5.2. Notice that the storage elements appear in a forward path rather than in a feedback loop. The memory-read operation depends only on the address vector presently applied to the RAM input, with the retrieved value having nothing to do with previous memory accesses. Concurrent versus Sequential Code VHDL code is inherently concurrent (parallel). Only statements placed inside a PROCESS, FUNCTION, or PROCEDURE are sequential. Still, though within these blocks the execution is sequential, the block, as a whole, is concurrent with any other (external) statements. Concurrent code is also called dataflow code. TLFeBOOK 66 input Chapter 5 Combinational Logic output input present state (a) Combinational Logic Storage Elements output next state (b) Figure 5.1 Combinational (a) versus sequential (b) logic. input Combinational Logic output Storage Elements Figure 5.2 RAM model. As an example, let us consider a code with three concurrent statements (stat1, stat2, stat3). Then any of the alternatives below will render the same physical circuit: stat1 stat3 stat1 stat2 C stat2 C stat3 C etc. stat3 stat1 stat2 It is then clear that, since the order does not matter, purely concurrent code can not be used to implement synchronous circuits (the only exception is when a GUARDED BLOCK is used). In other words, in general we can only build combinational logic circuits with concurrent code. To obtain sequential logic circuits, sequential code (chapter 6) must be employed. Indeed, with the latter we can implement both, sequential as well as combinational circuits. TLFeBOOK Concurrent Code 67 In this chapter, we will discuss concurrent code, that is, we will study the statements that can only be used outside PROCESSES, FUNCTIONS, or PROCEDURES. They are the WHEN statement and the GENERATE statement. Besides them, assignments using only operators (logical, arithmetic, etc) can obviously also be used to create combinational circuits. Finally, a special kind of statement, called BLOCK, can also be employed. In summary, in concurrent code the following can be used:  Operators;  The WHEN statement (WHEN/ELSE or WITH/SELECT/WHEN);  The GENERATE statement;  The BLOCK statement. Each of these cases is described below. 5.2 Using Operators This is the most basic way of creating concurrent code. Operators (AND, OR, þ, . *, sll, sra, etc.) were discussed in section 4.1, being a summary repeated in table 5.1 below. Operators can be used to implement any combinational circuit. However, as will become apparent later, complex circuits are usually easier to write using sequential code, even if the circuit does not contain sequential logic. In the example that follows, a design using only logical operators is presented. Table 5.1 Operators. Operator type Operators Data types Logical NOT, AND, NAND, OR, NOR, XOR, XNOR Arithmetic Comparison þ, , *, /, ** (mod, rem, abs) ¼, =¼, <, >, <¼, >¼ BIT, BIT_VECTOR, STD_LOGIC, STD_LOGIC_VECTOR, STD_ULOGIC, STD_ULOGIC_VECTOR INTEGER, SIGNED, UNSIGNED All above Shift sll, srl, sla, sra, rol, ror BIT_VECTOR Concatenation &, ( , , , ) Same as for logical operators, plus SIGNED and UNSIGNED TLFeBOOK 68 Chapter 5 a b c y MUX d s1 s0 Figure 5.3 Multiplexer of example 5.1. Example 5.1: Multiplexer #1 Figure 5.3 shows a 4-input, one bit per input multiplexer. The output must be equal to the input selected by the selection bits, s1-s0. Its implementation, using only logical operators, can be done as follows: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 --------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; --------------------------------------ENTITY mux IS PORT ( a, b, c, d, s0, s1: IN STD_LOGIC; y: OUT STD_LOGIC); END mux; --------------------------------------ARCHITECTURE pure_logic OF mux IS BEGIN y <= (a AND NOT s1 AND NOT s0) OR (b AND NOT s1 AND s0) OR (c AND s1 AND NOT s0) OR (d AND s1 AND s0); END pure_logic; --------------------------------------- Simulation results, confirming the functionality of the circuit, are shown in figure 5.4. TLFeBOOK Concurrent Code 69 Figure 5.4 Simulation results of example 5.1. 5.3 WHEN (Simple and Selected) As mentioned above, WHEN is one of the fundamental concurrent statements (along with operators and GENERATE). It appears in two forms: WHEN / ELSE (simple WHEN) and WITH / SELECT / WHEN (selected WHEN). Its syntax is shown below. WHEN / ELSE: assignment WHEN condition ELSE assignment WHEN condition ELSE ...; WITH / SELECT / WHEN: WITH identifier SELECT assignment WHEN value, assignment WHEN value, ...; Whenever WITH / SELECT / WHEN is used, all permutations must be tested, so the keyword OTHERS is often useful. Another important keyword is UNAFFECTED, which should be used when no action is to take place. TLFeBOOK 70 Chapter 5 Example: ------ With WHEN/ELSE ------------------------outp <= "000" WHEN (inp='0' OR reset='1') ELSE "001" WHEN ctl='1' ELSE "010"; ---- With WITH/SELECT/WHEN -------------------WITH control SELECT output <= "000" WHEN reset, "111" WHEN set, UNAFFECTED WHEN OTHERS; ----------------------------------------------- Another important aspect related to the WHEN statement is that the ‘‘WHEN value’’ shown in the syntax above can indeed take up three forms: WHEN value WHEN value1 to value2 WHEN value1 | value2 |... Example 5.2: ----- single value range, for enumerated data types only value1 or value2 or ... Multiplexer #2 This example shows the implementation of the same multiplexer of example 5.1, but with a slightly di¤erent representation for the sel input (figure 5.5). However, in it WHEN was employed instead of logical operators. Two solutions are presented: one using WHEN/ELSE (simple WHEN) and the other with WITH/SELECT/WHEN (selected WHEN). The experimental results are obviously similar to those obtained in example 5.1. a b MUX c y d sel (1:0) Figure 5.5 Multiplexer of example 5.2. TLFeBOOK Concurrent Code 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ------- Solution 1: with WHEN/ELSE -------LIBRARY ieee; USE ieee.std_logic_1164.all; ------------------------------------------ENTITY mux IS PORT ( a, b, c, d: IN STD_LOGIC; sel: IN STD_LOGIC_VECTOR (1 DOWNTO 0); y: OUT STD_LOGIC); END mux; ------------------------------------------ARCHITECTURE mux1 OF mux IS BEGIN y <= a WHEN sel="00" ELSE b WHEN sel="01" ELSE c WHEN sel="10" ELSE d; END mux1; ------------------------------------------- 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 --- Solution 2: with WITH/SELECT/WHEN ----LIBRARY ieee; USE ieee.std_logic_1164.all; ------------------------------------------ENTITY mux IS PORT ( a, b, c, d: IN STD_LOGIC; sel: IN STD_LOGIC_VECTOR (1 DOWNTO 0); y: OUT STD_LOGIC); END mux; ------------------------------------------ARCHITECTURE mux2 OF mux IS BEGIN WITH sel SELECT y <= a WHEN "00", -- notice "," instead of ";" b WHEN "01", c WHEN "10", d WHEN OTHERS; -- cannot be "d WHEN "11" " END mux2; -------------------------------------------- 71 TLFeBOOK 72 Chapter 5 In the solutions above, sel could have been declared as an INTEGER, in which case the code would be the following: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 ---------------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; ---------------------------------------------ENTITY mux IS PORT ( a, b, c, d: IN STD_LOGIC; sel: IN INTEGER RANGE 0 TO 3; y: OUT STD_LOGIC); END mux; ---- Solution 1: with WHEN/ELSE --------------ARCHITECTURE mux1 OF mux IS BEGIN y <= a WHEN sel=0 ELSE b WHEN sel=1 ELSE c WHEN sel=2 ELSE d; END mux1; -- Solution 2: with WITH/SELECT/WHEN -------ARCHITECTURE mux2 OF mux IS BEGIN WITH sel SELECT y <= a WHEN 0, b WHEN 1, c WHEN 2, d WHEN 3; -- here, 3 or OTHERS are equivalent, END mux2; -- for all options are tested anyway ----------------------------------------------- Note: Only one ARCHITECTURE can be synthesized at a time. Therefore, whenever we show more than one solution within the same overall code (like above), it is implicit that all solutions but one must be commented out (with ‘‘- -’’), or a synthesis script must be used, in order to synthesize the remaining solution. In simulations, the CONFIGURATION statement can be used to select a specific architecture. Note: For a generic mux, please refer to problem 5.1. TLFeBOOK Concurrent Code 73 ena input (7:0) output (7:0) Figure 5.6 Tri-state bu¤er of example 5.3. Example 5.3: Tri-state Bu¤er This is another example that illustrates the use of WHEN. The 3-state bu¤er of figure 5.6 must provide output ¼ input when ena (enable) is low, or output ¼ ‘‘ZZZZZZZZ’’ (high impedance) otherwise. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 LIBRARY ieee; USE ieee.std_logic_1164.all; ---------------------------------------------ENTITY tri_state IS PORT ( ena: IN STD_LOGIC; input: IN STD_LOGIC_VECTOR (7 DOWNTO 0); output: OUT STD_LOGIC_VECTOR (7 DOWNTO 0)); END tri_state; ---------------------------------------------ARCHITECTURE tri_state OF tri_state IS BEGIN output <= input WHEN (ena='0') ELSE (OTHERS => 'Z'); END tri_state; ---------------------------------------------- Simulation results from the circuit synthesized with the code above are shown in figure 5.7. As expected, the output stays in the high-impedance state while ena is high, being a copy of the input when ena is turned low. Example 5.4: Encoder The top-level diagram of an n-by-m encoder is shown in figure 5.8. We assume that n is a power of two, so m ¼ log2 n. One and only one input bit is expected to be high at a time, whose address must be encoded at the output. Two solutions are presented, one using WHEN / ELSE, and the other with WITH / SELECT / WHEN. TLFeBOOK 74 Chapter 5 Figure 5.7 Simulation results of example 5.3. x (n-1) x (n-2) … x (1) x (0) nxm ENCODER (m-1:0) Figure 5.8 Encoder of example 5.4. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ---- Solution 1: with WHEN/ELSE ------------LIBRARY ieee; USE ieee.std_logic_1164.all; --------------------------------------------ENTITY encoder IS PORT ( x: IN STD_LOGIC_VECTOR (7 DOWNTO 0); y: OUT STD_LOGIC_VECTOR (2 DOWNTO 0)); END encoder; --------------------------------------------ARCHITECTURE encoder1 OF encoder IS BEGIN y <= "000" WHEN x="00000001" ELSE "001" WHEN x="00000010" ELSE "010" WHEN x="00000100" ELSE "011" WHEN x="00001000" ELSE "100" WHEN x="00010000" ELSE "101" WHEN x="00100000" ELSE "110" WHEN x="01000000" ELSE "111" WHEN x="10000000" ELSE "ZZZ"; TLFeBOOK Concurrent Code 75 21 END encoder1; 22 --------------------------------------------1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 ---- Solution 2: with WITH/SELECT/WHEN -----LIBRARY ieee; USE ieee.std_logic_1164.all; --------------------------------------------ENTITY encoder IS PORT ( x: IN STD_LOGIC_VECTOR (7 DOWNTO 0); y: OUT STD_LOGIC_VECTOR (2 DOWNTO 0)); END encoder; --------------------------------------------ARCHITECTURE encoder2 OF encoder IS BEGIN WITH x SELECT y <= "000" WHEN "00000001", "001" WHEN "00000010", "010" WHEN "00000100", "011" WHEN "00001000", "100" WHEN "00010000", "101" WHEN "00100000", "110" WHEN "01000000", "111" WHEN "10000000", "ZZZ" WHEN OTHERS; END encoder2; --------------------------------------------- Notice that the code above has a long test list (lines 12–20 in solution 1, lines 13– 21 in solution 2). The situation becomes even more cumbersome when the number of selection bits grows. In such a case, the GENERATE statement (section 5.4) or the LOOP statement (section 6.6) can be employed. Simulation results (from either solution) are shown in figure 5.9. Example 5.5: ALU An ALU (Arithmetic Logic Unit) is shown in figure 5.10. As the name says, it is a circuit capable of executing both kinds of operations, arithmetic as well as logical. Its operation is described in the truth table of figure 5.10. The output (arithmetic or logical) is selected by the MSB of sel, while the specific operation is selected by sel’s other three bits. TLFeBOOK 76 Chapter 5 Figure 5.9 Simulation results of example 5.4. a (7:0) b (7:0) Logic Unit y (7:0) Mux Arithmetic Unit cin sel (3) sel (3:0) sel 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111 Operation y <= a y <= a+1 y <= a-1 y <= b y <= b+1 y <= b-1 y <= a+b y <= a+b+cin y <= NOT a y <= NOT b y <= a AND b y <= a OR b y <= a NAND b y <= a NOR b y <= a XOR b y <= a XNOR b Function Transfer a Increment a Decrement a Transfer b Increment b Decrement b Add a and b Add a and b with carry Complement a Complement b AND OR NAND NOR XOR XNOR Unit Arithmetic Logic Figure 5.10 ALU of example 5.5. TLFeBOOK Concurrent Code 77 Figure 5.11 Simulation results of example 5.5. The solution presented below, besides using only concurrent code, also illustrates the use of the same data type to perform both arithmetic and logical operations. That is possible due to the presence of the std_logic_unsigned package of the ieee library (discussed in section 3.6). Two signals, arith and logic, are used to hold the results from the arithmetic and logic units, respectively, being the value passed to the output selected by the multiplexer. Simulation results are shown in figure 5.11. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 ---------------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; USE ieee.std_logic_unsigned.all; ---------------------------------------------ENTITY ALU IS PORT (a, b: IN STD_LOGIC_VECTOR (7 DOWNTO 0); sel: IN STD_LOGIC_VECTOR (3 DOWNTO 0); cin: IN STD_LOGIC; y: OUT STD_LOGIC_VECTOR (7 DOWNTO 0)); END ALU; ---------------------------------------------ARCHITECTURE dataflow OF ALU IS SIGNAL arith, logic: STD_LOGIC_VECTOR (7 DOWNTO 0); BEGIN ----- Arithmetic unit: -----WITH sel(2 DOWNTO 0) SELECT arith <= a WHEN "000", a+1 WHEN "001", a-1 WHEN "010", b WHEN "011", b+1 WHEN "100", TLFeBOOK 78 Chapter 5 23 b-1 WHEN "101", 24 a+b WHEN "110", 25 a+b+cin WHEN OTHERS; 26 ----- Logic unit: ----------27 WITH sel(2 DOWNTO 0) SELECT 28 logic <= NOT a WHEN "000", 29 NOT b WHEN "001", 30 a AND b WHEN "010", 31 a OR b WHEN "011", 32 a NAND b WHEN "100", 33 a NOR b WHEN "101", 34 a XOR b WHEN "110", 35 NOT (a XOR b) WHEN OTHERS; 36 -------- Mux: --------------37 WITH sel(3) SELECT 38 y <= arith WHEN '0', 39 logic WHEN OTHERS; 40 END dataflow; 41 ---------------------------------------------- 5.4 GENERATE GENERATE is another concurrent statement (along with operators and WHEN). It is equivalent to the sequential statement LOOP (chapter 6) in the sense that it allows a section of code to be repeated a number of times, thus creating several instances of the same assignments. Its regular form is the FOR / GENERATE construct, with the syntax shown below. Notice that GENERATE must be labeled. FOR / GENERATE: label: FOR identifier IN range GENERATE (concurrent assignments) END GENERATE; An irregular form is also available, which uses IF/GENERATE (with an IF equivalent; recall that originally IF is a sequential statement). Here ELSE is not allowed. In the same way that IF/GENERATE can be nested inside FOR/ GENERATE (syntax below), the opposite can also be done. TLFeBOOK Concurrent Code 79 IF / GENERATE nested inside FOR / GENERATE: label1: FOR identifier IN range GENERATE ... label2: IF condition GENERATE (concurrent assignments) END GENERATE; ... END GENERATE; Example: SIGNAL x: BIT_VECTOR (7 DOWNTO 0); SIGNAL y: BIT_VECTOR (15 DOWNTO 0); SIGNAL z: BIT_VECTOR (7 DOWNTO 0); ... G1: FOR i IN x'RANGE GENERATE z(i) <= x(i) AND y(i+8); END GENERATE; One important remark about GENERATE (and the same is true for LOOP, which will be seen in chapter 6) is that both limits of the range must be static. As an example, let us consider the code below, where choice is an input (non-static) parameter. This kind of code is generally not synthesizable. NotOK: FOR i IN 0 TO choice GENERATE (concurrent statements) END GENERATE; We also must to be aware of multiply-driven (unresolved) signals. For example, OK: FOR i IN 0 TO 7 GENERATE output(i)<='1' WHEN (a(i) AND b(i))='1' ELSE '0'; END GENERATE; is fine. However, the compiler will complain that accum is multiply driven (and stop compilation) in either of the following two cases: NotOK: FOR i IN 0 TO 7 GENERATE accum <="11111111" WHEN (a(i) AND b(i))='1' ELSE "00000000"; END GENERATE; TLFeBOOK 80 Chapter 5 NotOK: For i IN 0 to 7 GENERATE accum <= accum + 1 WHEN x(i)='1'; END GENERATE; Example 5.6: Vector Shifter This example illustrates the use of GENERATE. In it, the output vector must be a shifted version of the input vector, with twice its width and an amount of shift specified by another input. For example, if the input bus has width 4, and the present value is ‘‘1111’’, then the output should be one of the lines of the following matrix (the original vector is underscored): row(0): 0 0 0 0 1 1 1 1 row(1): 0 0 0 1 1 1 1 0 row(2): 0 0 1 1 1 1 0 0 row(3): 0 1 1 1 1 0 0 0 row(4): 1 1 1 1 0 0 0 0 The first row corresponds to the input itself, with no shift and the most significant bits filled with ‘0’s. Each successive row is equal to the previous row shifted one position to the left. The solution below has input inp, output outp, and shift selection sel. Each row of the array above (called matrix, line 14) is defined as subtype vector (line 12). 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 -----------------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; -----------------------------------------------ENTITY shifter IS PORT ( inp: IN STD_LOGIC_VECTOR (3 DOWNTO 0); sel: IN INTEGER RANGE 0 TO 4; outp: OUT STD_LOGIC_VECTOR (7 DOWNTO 0)); END shifter; -----------------------------------------------ARCHITECTURE shifter OF shifter IS SUBTYPE vector IS STD_LOGIC_VECTOR (7 DOWNTO 0); TYPE matrix IS ARRAY (4 DOWNTO 0) OF vector; SIGNAL row: matrix; BEGIN TLFeBOOK Concurrent Code 81 Figure 5.12 Simulation results of example 5.6. 16 row(0) <= "0000" & inp; 17 G1: FOR i IN 1 TO 4 GENERATE 18 row(i) <= row(i-1)(6 DOWNTO 0) & '0'; 19 END GENERATE; 20 outp <= row(sel); 21 END shifter; 22 ------------------------------------------------ Simulation results are presented in figure 5.12. As can be seen, inp ¼ ‘‘0011’’ (decimal 3) was applied to the circuit. The result was outp ¼ ‘‘00000011’’ (decimal 3) when sel ¼ 0 (no shift), outp ¼ ‘‘00000110’’ (decimal 6) when sel ¼ 1 (one shift to the left), outp ¼ ‘‘00001100’’ (decimal 12) when sel ¼ 2 (two shifts to the left), and so on. 5.5 BLOCK There are two kinds of BLOCK statements: Simple and Guarded. Simple BLOCK The BLOCK statement, in its simple form, represents only a way of locally partitioning the code. It allows a set of concurrent statements to be clustered into a BLOCK, with the purpose of turning the overall code more readable and more manageable (which might be helpful when dealing with long codes). Its syntax is shown below. label: BLOCK [declarative part] BEGIN (concurrent statements) END BLOCK label; TLFeBOOK 82 Chapter 5 Therefore, the overall aspect of a ‘‘blocked’’ code is the following: -----------------------ARCHITECTURE example ... BEGIN ... block1: BLOCK BEGIN ... END BLOCK block1 ... block2: BLOCK BEGIN ... END BLOCK block2; ... END example; ------------------------ Example: b1: BLOCK SIGNAL a: STD_LOGIC; BEGIN a <= input_sig WHEN ena='1' ELSE 'Z'; END BLOCK b1; A BLOCK (simple or guarded) can be nested inside another BLOCK. The corresponding syntax is shown below. label1: BLOCK [declarative part of top block] BEGIN [concurrent statements of top block] label2: BLOCK [declarative part nested block] BEGIN (concurrent statements of nested block) END BLOCK label2; [more concurrent statements of top block] END BLOCK label1; TLFeBOOK Concurrent Code 83 Note: Although code partitioning techniques are the object of Part II of the book, and the BLOCK statement seen above serves exactly to this purpose, BLOCK is described in this section due to the fact that it is self-contained within the main code (that is, it does not invoke any extra PACKAGE, COMPONENT, FUNCTION, or PROCEDURE—these four units are the actual focus of Part II). Guarded BLOCK A guarded BLOCK is a special kind of BLOCK, which includes an additional expression, called guard expression. A guarded statement in a guarded BLOCK is executed only when the guard expression is TRUE. Guarded BLOCK: label: BLOCK (guard expression) [declarative part] BEGIN (concurrent guarded and unguarded statements) END BLOCK label; As the examples below illustrate, even though only concurrent statements can be written within a BLOCK, with a guarded BLOCK even sequential circuits can be constructed. This, however, is not a usual design approach. Example 5.7: Latch Implemented with a Guarded BLOCK The example presented below implements a transparent latch. In it, clk='1' (line 12) is the guard expression, while q<=GUARDED d (line 14) is a guarded statement. Therefore, q<=d will only occur if clk='1'. 1 2 3 4 5 6 7 8 9 10 ------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; ------------------------------ENTITY latch IS PORT (d, clk: IN STD_LOGIC; q: OUT STD_LOGIC); END latch; ------------------------------ARCHITECTURE latch OF latch IS TLFeBOOK 84 Chapter 5 11 BEGIN 12 b1: BLOCK (clk='1') 13 BEGIN 14 q <= GUARDED d; 15 END BLOCK b1; 16 END latch; 17 ------------------------------- Example 5.8: DFF Implemented with a Guarded BLOCK Here, a positive-edge sensitive D-type flip-flop, with synchronous reset, is designed. The interpretation of the code is similar to that in the example above. In it, clk'EVENT AND clk='1' (line 12) is the guard expression, while q <= GUARDED '0' WHEN rst='1' (line 14) is a guarded statement. Therefore, q<='0' will occur when the guard expression is true and rst is ‘1’. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; ------------------------------ENTITY dff IS PORT ( d, clk, rst: IN STD_LOGIC; q: OUT STD_LOGIC); END dff; ------------------------------ARCHITECTURE dff OF dff IS BEGIN b1: BLOCK (clk'EVENT AND clk='1') BEGIN q <= GUARDED '0' WHEN rst='1' ELSE d; END BLOCK b1; END dff; ------------------------------ 5.6 Problems The problems proposed in this section are to be solved using only concurrent code (operators, WHEN, GENERATE). After writing the VHDL code, synthesize and simulate it, to make sure that it works as expected. TLFeBOOK Concurrent Code m x(0) 2 n 85 x(1) m y MUX n x(2 -1) n sel Figure P5.1 ‘0’ ‘1’ ‘0’ ‘0’ ‘1’ ’1’ ‘0’ 7 6 5 4 3 2 1 PRIORITY ENCODER 2 1 0 ‘1’ ’1’ ‘0’ Figure P5.2 Problem 5.1: Generic Multiplexer We have seen the design of a multiplexer in examples 5.1 and 5.2. Those circuits were for a pre-defined number of inputs (4 inputs) and a pre-defined number of bits per input (1 bit). A truly generic mux is depicted in figure P5.1. In it, n represents the number of bits of the selection input (sel), while m indicates the number of bits per input. The circuit has 2n inputs (notice that there is no relationship between m and n). Using a GENERIC statement to specify n, and assuming m ¼ 8, design this circuit. Suggestion: The input should be specified as an array of vectors. Therefore, review section 3.5. Does your solution (ARCHITECTURE) require more than one line of actual code? Problem 5.2: Priority Encoder Figure P5.2 shows the top-level diagram of a 7-level priority encoder. The circuit must encode the address of the input bit of highest order that is active. ‘‘000’’ should indicate that there is no request at the input (no bit active). Write two solutions for this circuit: TLFeBOOK 86 Chapter 5 a * x=a*b b / y=a/2 Figure P5.3 cin (carry in) a (7:0) + b (7:0) sum (7:0) cout (carry out) Figure P5.4 (a) Using only operators; (b) Using WHEN/ELSE (simple WHEN); Problem 5.3: Simple Multiplier/Divider Using only concurrent code, design the multiplier/divider of figure P5.3. The circuit has two 8-bit integer inputs (a, b) and two integer outputs (x, y), where x ¼ a*b and y ¼ a/2. Note: For a generic fixed-point divider, you may consult chapter 9. Problem 5.4: Adder Using only concurrent statements, design the 8-bit unsigned adder of figure P5.4. Problem 5.5: Signed/Unsigned Adder/Subtractor In figure P5.5, we have added an extra 2-bit input (sel) to the circuit of problem 5.4, such that now the circuit can operate as a signed or unsigned adder/subtractor (see truth table). Write a concurrent VHDL code for this circuit. Note: After having solved this problem, you can compare your solution to a corresponding example in chapter 9. TLFeBOOK Concurrent Code 87 cin a (7:0) b (7:0) + sum (7:0) sel 00 01 10 11 operation add unsigned add signed sub unsigned sub signed sel (1:0) cout Figure P5.5 Table P5.6 Binary code Gray code 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111 0000 0001 0011 0010 0110 0111 0101 0100 1100 1101 1111 1110 1010 1011 1001 1000 Problem 5.6: Binary-to-Gray Code Converter Binary code is the most often used of all digital codes. In it, the LSB (least significant bit) has weight 20 , with the weight increasing by a factor of two for each successive bit, up to 2n1 for the MSB (most significant bit), where n is the number of bits in the codeword. The Gray code, on the other hand, is based on minimum Hamming distance between neighboring codewords, that is, only one bit changes when we move from the j-th to the ( j þ 1)-th codeword. Both codes, for n ¼ 4, are listed in table P5.6. Design a circuit capable of converting binary code to Gray code (for generic n). If possible, present more than one solution. TLFeBOOK 88 Chapter 5 inp(7) MUX outp(7) MUX outp(6) MUX outp(5) MUX outp(4) MUX outp(3) MUX outp(2) MUX outp(1) MUX outp(0) inp(6) inp(5) inp(4) inp(3) inp(2) inp(1) inp(0) ‘0’ shift Figure P5.7 Problem 5.7: Simple Barrel Shifter Figure P5.7 shows the diagram of a very simple barrel shifter. In this case, the circuit must shift the input vector (of size 8) either 0 or 1 position to the left. When actually shifted (shift ¼ 1), the LSB bit must be filled with ‘0’ (shown in the bottom left corner of the diagram). If shift ¼ 0, then outp ¼ inp; else, if shift ¼ 1, then outp(0) ¼ ‘0’ and outp(i) ¼ inp(i  1), for 1 a i a 7. Write a concurrent code for this circuit. Note: A complete barrel shifter (with shift ¼ 0 to n  1, where n is the number of bits) will be seen in chapter 9. TLFeBOOK Concurrent Code a (7:0) b (7:0) 89 a>b a=b a<b x1 x2 x3 sel Figure P5.8 Problem 5.8: Comparator Construct a circuit capable of comparing two 8-bit vectors, a and b. A selection pin (sel) should determine whether the comparison is signed (sel ¼ ‘1’) or unsigned (sel ¼ ‘0’). The circuit must have three outputs, x1, x2, and x3, corresponding to a > b, a ¼ b, and a < b, respectively (figure P5.8). Note: After having solved this problem, you can compare your solution to a corresponding example in chapter 9. TLFeBOOK TLFeBOOK 6 Sequential Code As mentioned in chapter 5, VHDL code is inherently concurrent. PROCESSES, FUNCTIONS, and PROCEDURES are the only sections of code that are executed sequentially. However, as a whole, any of these blocks is still concurrent with any other statements placed outside it. One important aspect of sequential code is that it is not limited to sequential logic. Indeed, with it we can build sequential circuits as well as combinational circuits. Sequential code is also called behavioral code. The statements discussed in this section are all sequential, that is, allowed only inside PROCESSES, FUNCTIONS, or PROCEDURES. They are: IF, WAIT, CASE, and LOOP. VARIABLES are also restricted to be used in sequential code only (that is, inside a PROCESS, FUNCTION, or PROCEDURE). Thus, contrary to a SIGNAL, a VARIABLE can never be global, so its value can not be passed out directly. We will concentrate on PROCESSES here. FUNCTIONS and PROCEDURES are very similar, but are intended for system-level design, being therefore seen in Part II of this book. 6.1 PROCESS A PROCESS is a sequential section of VHDL code. It is characterized by the presence of IF, WAIT, CASE, or LOOP, and by a sensitivity list (except when WAIT is used). A PROCESS must be installed in the main code, and is executed every time a signal in the sensitivity list changes (or the condition related to WAIT is fulfilled). Its syntax is shown below. [label:] PROCESS (sensitivity list) [VARIABLE name type [range] [:= initial_value;]] BEGIN (sequential code) END PROCESS [label]; VARIABLES are optional. If used, they must be declared in the declarative part of the PROCESS (before the word BEGIN, as indicated in the syntax above). The initial value is not synthesizable, being only taken into consideration in simulations. The use of a label is also optional. Its purpose is to improve code readability. The label can be any word, except VHDL reserved words (appendix E). TLFeBOOK 92 Chapter 6 d q DFF clk rst Figure 6.1 DFF with asynchronous reset of example 6.1. Figure 6.2 Simulation results of example 6.1. To construct a synchronous circuit, monitoring a signal (clock, for example) is necessary. A common way of detecting a signal change is by means of the EVENT attribute (seen in section 4.2). For instance, if clk is a signal to be monitored, then clk’EVENT returns TRUE when a change on clk occurs (rising or falling edge). An example, illustrating the use of EVENT and PROCESS, is shown next. Example 6.1: DFF with Asynchronous Reset #1 A D-type flip-flop (DFF, figure 6.1) is the most basic building block in sequential logic circuits. In it, the output must copy the input at either the positive or negative transition of the clock signal (rising or falling edge). In the code presented below, we make use of the IF statement (discussed in section 6.3) to design a DFF with asynchronous reset. If rst ¼ ‘1’, then the output must be q ¼ ‘0’ (lines 14–15), regardless of the status of clk. Otherwise, the output must copy the input (that is, q ¼ d) at the positive edge of clk (lines 16–17). The EVENT attribute is used in line 16 to detect a clock transition. The PROCESS (lines 12–19) is run every time any of the signals that appear in its sensitivity list (clk and rst, line 12) changes. Simulation results, confirming the functionality of the synthesized circuit, are presented in figure 6.2. TLFeBOOK Sequential Code 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 6.2 93 -------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; -------------------------------------ENTITY dff IS PORT (d, clk, rst: IN STD_LOGIC; q: OUT STD_LOGIC); END dff; -------------------------------------ARCHITECTURE behavior OF dff IS BEGIN PROCESS (clk, rst) BEGIN IF (rst='1') THEN q <= '0'; ELSIF (clk'EVENT AND clk='1') THEN q <= d; END IF; END PROCESS; END behavior; -------------------------------------- Signals and Variables Signals and variables will be studied in detail in the next chapter. However, it is impossible to discuss sequential code without knowing at least their most basic characteristics. VHDL has two ways of passing non-static values around: by means of a SIGNAL or by means of a VARIABLE. A SIGNAL can be declared in a PACKAGE, ENTITY or ARCHITECTURE (in its declarative part), while a VARIABLE can only be declared inside a piece of sequential code (in a PROCESS, for example). Therefore, while the value of the former can be global, the latter is always local. The value of a VARIABLE can never be passed out of the PROCESS directly; if necessary, then it must be assigned to a SIGNAL. On the other hand, the update of a VARIABLE is immediate, that is, we can promptly count on its new value in the next line of code. That is not the case with a SIGNAL (when used in a PROCESS), for its new value is generally only guaranteed to be available after the conclusion of the present run of the PROCESS. TLFeBOOK 94 Chapter 6 Finally, recall from section 4.1 that the assignment operator for a SIGNAL is ‘‘<¼’’ (ex.: sig <¼ 5), while for a VARIABLE it is ‘‘:¼’’ (ex.: var :¼ 5). 6.3 IF As mentioned earlier, IF, WAIT, CASE, and LOOP are the statements intended for sequential code. Therefore, they can only be used inside a PROCESS, FUNCTION, or PROCEDURE. The natural tendency is for people to use IF more than any other statement. Though this could, in principle, have a negative consequence (because the IF/ELSE statement might infer the construction of an unnecessary priority decoder), the synthesizer will optimize the structure and avoid the extra hardware. The syntax of IF is shown below. IF conditions THEN assignments; ELSIF conditions THEN assignments; ... ELSE assignments; END IF; Example: IF (x<y) THEN temp:="11111111"; ELSIF (x=y AND w='0') THEN temp:="11110000"; ELSE temp:=(OTHERS =>'0'); Example 6.2: One-digit Counter #1 The code below implements a progressive 1-digit decimal counter (0 ! 9 ! 0). A top-level diagram of the circuit is shown in figure 6.3. It contains a single-bit input clk C O U N T E R digit (3:0) Figure 6.3 Counter of example 6.2. TLFeBOOK Sequential Code 95 Figure 6.4 Simulation results of example 6.2. (clk) and a 4-bit output (digit). The IF statement is used in this example. A variable, temp, was employed to create the four flip-flops necessary to store the 4-bit output signal. Simulation results, confirming the correct operation of the synthesized circuit, are shown in figure 6.4. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 --------------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; --------------------------------------------ENTITY counter IS PORT (clk : IN STD_LOGIC; digit : OUT INTEGER RANGE 0 TO 9); END counter; --------------------------------------------ARCHITECTURE counter OF counter IS BEGIN count: PROCESS(clk) VARIABLE temp : INTEGER RANGE 0 TO 10; BEGIN IF (clk'EVENT AND clk='1') THEN temp := temp + 1; IF (temp=10) THEN temp := 0; END IF; END IF; digit <= temp; END PROCESS count; END counter; --------------------------------------------- Comment: Note that the code above has neither a reset input nor any internal initialization scheme for temp (and digit, consequently). Therefore, the initial value of TLFeBOOK 96 Chapter 6 temp in the physical circuit can be any 4-bit value. If such value is below 10 (see line 17), the circuit will count correctly from there. On the other hand, if the value is above 10, a number of clock cycles will be used until temp reaches full count (that is, 15, or ‘‘1111’’), being thus automatically reset to zero, from where the correct operation then starts. The possibility of wasting a few clock cycles in the beginning is generally not a problem. Still, if one does want to avoid that, temp ¼ 10, in line 17, can be changed to temp ¼> 10, but this will increase the hardware. However, if starting exactly from 0 is always necessary, then a reset input should be included (as in example 6.7). Notice in the code above that we increment temp and compare it to 10, with the purpose of resetting temp once 10 is reached. This is a typical approach used in counters. Notice that 10 is a constant, so a comparator to a constant is inferred by the compiler, which is a relatively simple circuit to construct. However, if instead of a constant we were using a programmable parameter, then a full comparator would need to be implemented, which requires substantially more logic than a comparator to a constant. In this case, a better solution would be to load temp with such a parameter, and then decrement it, reloading temp when the 0 value is reached. In this case, our comparator would compare temp to 0 (a constant), thus avoiding the generation of a full comparator. Example 6.3: Shift Register Figure 6.5 shows a 4-bit shift register. The output bit (q) must be four positive clock edges behind the input bit (d). It also contains an asynchronous reset, which must force all flip-flop outputs to ‘0’ when asserted. In this example, the IF statement is again employed. 1 2 3 4 -------------------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; -------------------------------------------------- d q DFF DFF DFF DFF clk rst Figure 6.5 Shift register of example 6.3. TLFeBOOK Sequential Code 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 97 ENTITY shiftreg IS GENERIC (n: INTEGER := 4); -- # of stages PORT (d, clk, rst: IN STD_LOGIC; q: OUT STD_LOGIC); END shiftreg; -------------------------------------------------ARCHITECTURE behavior OF shiftreg IS SIGNAL internal: STD_LOGIC_VECTOR (n-1 DOWNTO 0); BEGIN PROCESS (clk, rst) BEGIN IF (rst='1') THEN internal <= (OTHERS => '0'); ELSIF (clk'EVENT AND clk='1') THEN internal <= d & internal(internal'LEFT DOWNTO 1); END IF; END PROCESS; q <= internal(0); END behavior; -------------------------------------------------- Simulation results are shown in figure 6.6. As can be seen, q is indeed four positive clock edges behind d. 6.4 WAIT The operation of WAIT is sometimes similar to that of IF. However, more than one form of WAIT is available. Moreover, contrary to when IF, CASE, or LOOP are Figure 6.6 Simulation results of example 6.3. TLFeBOOK 98 Chapter 6 used, the PROCESS cannot have a sensitivity list when WAIT is employed. Its syntax (there are three forms of WAIT) is shown below. WAIT UNTIL signal_condition; WAIT ON signal1 [, signal2, ... ]; WAIT FOR time; The WAIT UNTIL statement accepts only one signal, thus being more appropriate for synchronous code than asynchronous. Since the PROCESS has no sensitivity list in this case, WAIT UNTIL must be the first statement in the PROCESS. The PROCESS will be executed every time the condition is met. Example: 8-bit register with synchronous reset. PROCESS -- no sensitivity list BEGIN WAIT UNTIL (clk'EVENT AND clk='1'); IF (rst='1') THEN output <= "00000000"; ELSIF (clk'EVENT AND clk='1') THEN output <= input; END IF; END PROCESS; WAIT ON, on the other hand, accepts multiple signals. The PROCESS is put on hold until any of the signals listed changes. In the example below, the PROCESS will continue execution whenever a change in rst or clk occurs. Example: 8-bit register with asynchronous reset. PROCESS BEGIN WAIT ON clk, rst; IF (rst='1') THEN TLFeBOOK Sequential Code 99 output <= "00000000"; ELSIF (clk'EVENT AND clk='1') THEN output <= input; END IF; END PROCESS; Finally, WAIT FOR is intended for simulation only (waveform generation for testbenches). Example: WAIT FOR 5ns; Example 6.4: DFF with Asynchronous Reset #2 The code below implements the same DFF of example 6.1 (figures 6.1 and 6.2). However, here WAIT ON is used instead of IF only. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 -------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; -------------------------------------ENTITY dff IS PORT (d, clk, rst: IN STD_LOGIC; q: OUT STD_LOGIC); END dff; -------------------------------------ARCHITECTURE dff OF dff IS BEGIN PROCESS BEGIN WAIT ON rst, clk; IF (rst='1') THEN q <= '0'; ELSIF (clk'EVENT AND clk='1') THEN q <= d; END IF; END PROCESS; END dff; -------------------------------------- Example 6.5: One-digit Counter #2 The code below implements the same progressive 1-digit decimal counter of example 6.2 (figures 6.3 and 6.4). However, WAIT UNTIL was used instead of IF only. TLFeBOOK 100 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 Chapter 6 --------------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; --------------------------------------------ENTITY counter IS PORT (clk : IN STD_LOGIC; digit : OUT INTEGER RANGE 0 TO 9); END counter; --------------------------------------------ARCHITECTURE counter OF counter IS BEGIN PROCESS -- no sensitivity list VARIABLE temp : INTEGER RANGE 0 TO 10; BEGIN WAIT UNTIL (clk'EVENT AND clk='1'); temp := temp + 1; IF (temp=10) THEN temp := 0; END IF; digit <= temp; END PROCESS; END counter; --------------------------------------------- 6.5 CASE CASE is another statement intended exclusively for sequential code (along with IF, LOOP, and WAIT). Its syntax is shown below. CASE identifier IS WHEN value => assignments; WHEN value => assignments; ... END CASE; Example: CASE control IS WHEN "00" => x<=a; y<=b; TLFeBOOK Sequential Code 101 WHEN "01" => x<=b; y<=c; WHEN OTHERS => x<="0000"; y<="ZZZZ"; END CASE; The CASE statement (sequential) is very similar to WHEN (combinational). Here too all permutations must be tested, so the keyword OTHERS is often helpful. Another important keyword is NULL (the counterpart of UNAFFECTED), which should be used when no action is to take place. For example, WHEN OTHERS => NULL;. However, CASE allows multiple assignments for each test condition (as shown in the example above), while WHEN allows only one. Like in the case of WHEN (section 5.3), here too ‘‘WHEN value’’ can take up three forms: WHEN value WHEN value1 to value2 WHEN value1 | value2 |... Example 6.6: ----- single value range, for enumerated data types only value1 or value2 or ... DFF with Asynchronous Reset #3 The code below implements the same DFF of example 6.1 (figures 6.1 and 6.2). However, here CASE was used instead of IF only. Notice that a few unnecessary declarations were intentionally included in the code to illustrate their usage. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ---------------------------------------------LIBRARY ieee; -- Unnecessary declaration, -- because USE ieee.std_logic_1164.all; -- BIT was used instead of -- STD_LOGIC ---------------------------------------------ENTITY dff IS PORT (d, clk, rst: IN BIT; q: OUT BIT); END dff; ---------------------------------------------ARCHITECTURE dff3 OF dff IS BEGIN PROCESS (clk, rst) BEGIN CASE rst IS TLFeBOOK 102 Chapter 6 SSD clk a C O U N T E R f digit2 e digit1 reset b g d c x Input: “xabcdefg” Figure 6.7 2-digit counter of example 6.7. 17 WHEN '1' => q<='0'; 18 WHEN '0' => 19 IF (clk'EVENT AND clk='1') THEN 20 q <= d; 21 END IF; 22 WHEN OTHERS => NULL; -- Unnecessary, rst is of type 23 -- BIT 24 END CASE; 25 END PROCESS; 26 END dff3; 27 ---------------------------------------------- Example 6.7: Two-digit Counter with SSD Output The code below implements a progressive 2-digit decimal counter (0 ! 99 ! 0), with external asynchronous reset plus binary-coded decimal (BCD) to seven-segment display (SSD) conversion. Diagrams of the circuit and SSD are shown in figure 6.7. The CASE statement (lines 31–56) was employed to determine the output signals that will feed the SSDs. Notice that we have chosen the following connection between the circuit and the SSD: xabcdefg (that is, the MSB feeds the decimal point, while the LSB feeds segment g). As can be seen, this circuit is a straight extension of that presented in example 6.2, with the di¤erences that now two digits are necessary rather than one, and that the outputs must be connected to SSD displays. The operation of the circuit can be verified in the simulation results of figure 6.8. TLFeBOOK Sequential Code 103 Figure 6.8 Simulation results of example 6.7. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 -------------------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; -------------------------------------------------ENTITY counter IS PORT (clk, reset : IN STD_LOGIC; digit1, digit2 : OUT STD_LOGIC_VECTOR (6 DOWNTO 0)); END counter; -------------------------------------------------ARCHITECTURE counter OF counter IS BEGIN PROCESS(clk, reset) VARIABLE temp1: INTEGER RANGE 0 TO 10; VARIABLE temp2: INTEGER RANGE 0 TO 10; BEGIN ---- counter: ---------------------IF (reset='1') THEN temp1 := 0; temp2 := 0; ELSIF (clk'EVENT AND clk='1') THEN temp1 := temp1 + 1; IF (temp1=10) THEN temp1 := 0; temp2 := temp2 + 1; IF (temp2=10) THEN temp2 := 0; TLFeBOOK 104 Chapter 6 27 END IF; 28 END IF; 29 END IF; 30 ---- BCD to SSD conversion: -------31 CASE temp1 IS 32 WHEN 0 => digit1 <= "1111110"; --7E 33 WHEN 1 => digit1 <= "0110000"; --30 34 WHEN 2 => digit1 <= "1101101"; --6D 35 WHEN 3 => digit1 <= "1111001"; --79 36 WHEN 4 => digit1 <= "0110011"; --33 37 WHEN 5 => digit1 <= "1011011"; --5B 38 WHEN 6 => digit1 <= "1011111"; --5F 39 WHEN 7 => digit1 <= "1110000"; --70 40 WHEN 8 => digit1 <= "1111111"; --7F 41 WHEN 9 => digit1 <= "1111011"; --7B 42 WHEN OTHERS => NULL; 43 END CASE; 44 CASE temp2 IS 45 WHEN 0 => digit2 <= "1111110"; --7E 46 WHEN 1 => digit2 <= "0110000"; --30 47 WHEN 2 => digit2 <= "1101101"; --6D 48 WHEN 3 => digit2 <= "1111001"; --79 49 WHEN 4 => digit2 <= "0110011"; --33 50 WHEN 5 => digit2 <= "1011011"; --5B 51 WHEN 6 => digit2 <= "1011111"; --5F 52 WHEN 7 => digit2 <= "1110000"; --70 53 WHEN 8 => digit2 <= "1111111"; --7F 54 WHEN 9 => digit2 <= "1111011"; --7B 55 WHEN OTHERS => NULL; 56 END CASE; 57 END PROCESS; 58 END counter; 59 -------------------------------------------------- Comment: Notice above that the same routine was repeated twice (using CASE statements). We will learn, in Part II, how to write and compile frequently used pieces of code into user-defined libraries, so that such repetitions can be avoided. TLFeBOOK Sequential Code 6.6 105 LOOP As the name says, LOOP is useful when a piece of code must be instantiated several times. Like IF, WAIT, and CASE, LOOP is intended exclusively for sequential code, so it too can only be used inside a PROCESS, FUNCTION, or PROCEDURE. There are several ways of using LOOP, as shown in the syntaxes below. FOR / LOOP: The loop is repeated a fixed number of times. [label:] FOR identifier IN range LOOP (sequential statements) END LOOP [label]; WHILE / LOOP: The loop is repeated until a condition no longer holds. [label:] WHILE condition LOOP (sequential statements) END LOOP [label]; EXIT: Used for ending the loop. [label:] EXIT [label] [WHEN condition]; NEXT: Used for skipping loop steps. [label:] NEXT [loop_label] [WHEN condition]; Example of FOR / LOOP: FOR i IN 0 TO 5 LOOP x(i) <= enable AND w(i+2); y(0, i) <= w(i); END LOOP; In the code above, the loop will be repeated unconditionally until i reaches 5 (that is, six times). TLFeBOOK 106 Chapter 6 One important remark regarding FOR / LOOP (similar to that made for GENERATE, in chapter 5) is that both limits of the range must be static. Thus a declaration of the type "FOR i IN 0 TO choice LOOP", where choice is an input (nonstatic) parameter, is generally not synthesizable. Example of WHILE / LOOP: In this example, LOOP will keep repeating while i < 10. WHILE (i < 10) LOOP WAIT UNTIL clk'EVENT AND clk='1'; (other statements) END LOOP; Example with EXIT: In the code below, EXIT implies not an escape from the current iteration of the loop, but rather a definite exit (that is, even if i is still within the data range, the LOOP statement will be considered as concluded). In this case, the loop will end as soon as a value di¤erent from ‘0’ is found in the data vector. FOR i IN data'RANGE LOOP CASE data(i) IS WHEN '0' => count:=count+1; WHEN OTHERS => EXIT; END CASE; END LOOP; Example with NEXT: In the example below, NEXT causes LOOP to skip one iteration when i ¼ skip. FOR i IN 0 TO 15 LOOP NEXT WHEN i=skip; (...) END LOOP; -- jumps to next iteration Several complete design examples, illustrating various applications of LOOP, are presented below. Example 6.8: Carry Ripple Adder Figure 6.9 shows an 8-bit unsigned carry ripple adder. The top-level diagram shows the inputs and outputs of the circuit: a and b are the input vectors to be added, cin is the carry-in bit, s is the sum vector, and cout is the carry-out bit. The one-levelbelow-top diagram shows how the carry bits propagate (ripple). TLFeBOOK Sequential Code 107 One level below top: Top level: a b cin a0 b0 a1 b1 a7 b7 s + c0 cout + + c1 (cin s0 + c2 c7 s1 c8 (cout) s7 Figure 6.9 8-bit carry ripple adder of example 6.8 Figure 6.10 Simulation results of example 6.8. Each section of the latter diagram is a full-adder unit (section 1.4). Thus its outputs can be computed by means of: sj ¼ aj XOR bj XOR cj cjþ1 ¼ (aj AND bj ) OR (aj AND cj ) OR (bj AND cj ) Two solutions are presented, being one generic (that is, for any number of bits, based on what we saw in chapter 4) and the other specific for 8-bit numbers. Moreover, we illustrate the use of vectors and FOR/LOOP in the first solution, and of integers and IF in the second. Simulation results from either solution are shown in figure 6.10. Note: We will see more about adders in chapter 9. 1 2 3 ----- Solution 1: Generic, with VECTORS -------LIBRARY ieee; USE ieee.std_logic_1164.all; TLFeBOOK 108 Chapter 6 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 -----------------------------------------------ENTITY adder IS GENERIC (length : INTEGER := 8); PORT ( a, b: IN STD_LOGIC_VECTOR (length-1 DOWNTO 0); cin: IN STD_LOGIC; s: OUT STD_LOGIC_VECTOR (length-1 DOWNTO 0); cout: OUT STD_LOGIC); END adder; -----------------------------------------------ARCHITECTURE adder OF adder IS BEGIN PROCESS (a, b, cin) VARIABLE carry : STD_LOGIC_VECTOR (length DOWNTO 0); BEGIN carry(0) := cin; FOR i IN 0 TO length-1 LOOP s(i) <= a(i) XOR b(i) XOR carry(i); carry(i+1) := (a(i) AND b(i)) OR (a(i) AND carry(i)) OR (b(i) AND carry(i)); END LOOP; cout <= carry(length); END PROCESS; END adder; ------------------------------------------------ 1 2 3 4 5 6 7 8 9 10 11 12 13 ---- Solution 2: non-generic, with INTEGERS ---LIBRARY ieee; USE ieee.std_logic_1164.all; -----------------------------------------------ENTITY adder IS PORT ( a, b: IN INTEGER RANGE 0 TO 255; c0: IN STD_LOGIC; s: OUT INTEGER RANGE 0 TO 255; c8: OUT STD_LOGIC); END adder; -----------------------------------------------ARCHITECTURE adder OF adder IS BEGIN TLFeBOOK Sequential Code 109 14 PROCESS (a, b, c0) 15 VARIABLE temp : INTEGER RANGE 0 TO 511; 16 BEGIN 17 IF (c0='1') THEN temp:=1; 18 ELSE temp:=0; 19 END IF; 20 temp := a + b + temp; 21 IF (temp > 255) THEN 22 c8 <= '1'; 23 temp := temp---256; 24 ELSE c8 <= '0'; 25 END IF; 26 s <= temp; 27 END PROCESS; 28 END adder; 29 ------------------------------------------------ Example 6.9: Simple Barrel Shifter Figure 6.11 shows the diagram of a very simple barrel shifter. In this case, the circuit must shift the input vector (of size 8) either 0 or 1 position to the left. When actually shifted (shift ¼ 1), the LSB bit must be filled with ‘0’ (shown in the botton left corner of the diagram). If shift ¼ 0, then outp ¼ inp; if shift ¼ 1, then outp(0) ¼ ‘0’ and outp(i) ¼ inp(i  1), for 1 a i a 7. A complete VHDL code is presented below, which illustrates the use of FOR/ LOOP. Simulation results appear in figure 6.12. Note: A complete barrel shifter (with shift ¼ 0 to n  1, where n is the size of the input vector) will be seen in chapter 9. 1 2 3 4 5 6 7 8 9 --------------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; --------------------------------------------ENTITY barrel IS GENERIC (n: INTEGER := 8); PORT ( inp: IN STD_LOGIC_VECTOR (n-1 DOWNTO 0); shift: IN INTEGER RANGE 0 TO 1; outp: OUT STD_LOGIC_VECTOR (n-1 DOWNTO 0)); TLFeBOOK 110 Chapter 6 inp(7) MUX outp(7) MUX outp(6) MUX outp(5) MUX outp(4) MUX outp(3) MUX outp(2) MUX outp(1) MUX outp(0) inp(6) inp(5) inp(4) inp(3) inp(2) inp(1) inp(0) ‘0’ shift Figure 6.11 Simple barrel shifter of example 6.9. Figure 6.12 Simulation results of example 6.9. TLFeBOOK Sequential Code 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 111 END barrel; --------------------------------------------ARCHITECTURE RTL OF barrel IS BEGIN PROCESS (inp, shift) BEGIN IF (shift=0) THEN outp <= inp; ELSE outp(0) <= '0'; FOR i IN 1 TO inp'HIGH LOOP outp(i) <= inp(i-1); END LOOP; END IF; END PROCESS; END RTL; --------------------------------------------- Example 6.10: Leading Zeros The design below counts the number of leading zeros in a binary vector, starting from the left end. The solution illustrates the use of LOOP / EXIT. Recall that EXIT implies not a escape from the current iteration of the loop, but rather a definite exit from it (that is, even if i is still within the specified range, the LOOP statement will be considered as concluded). In this example, the loop will end as soon as a ‘1’ is found in the data vector. Therefore, it is appropriate for counting the number of zeros that precedes the first one. 1 2 3 4 5 6 7 8 9 10 -------------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; -------------------------------------------ENTITY LeadingZeros IS PORT ( data: IN STD_LOGIC_VECTOR (7 DOWNTO 0); zeros: OUT INTEGER RANGE 0 TO 8); END LeadingZeros; -------------------------------------------ARCHITECTURE behavior OF LeadingZeros IS TLFeBOOK 112 Chapter 6 Figure 6.13 Simulation results of example 6.10. 11 BEGIN 12 PROCESS (data) 13 VARIABLE count: INTEGER RANGE 0 TO 8; 14 BEGIN 15 count := 0; 16 FOR i IN data'RANGE LOOP 17 CASE data(i) IS 18 WHEN '0' => count := count + 1; 19 WHEN OTHERS => EXIT; 20 END CASE; 21 END LOOP; 22 zeros <= count; 23 END PROCESS; 24 END behavior; 25 -------------------------------------------- Simulation results, verifying the functionality of the circuit, are shown in figure 6.13. With data ¼ ‘‘00000000’’ (decimal 0), eight zeros are detected; when data ¼ ‘‘00000001’’ (decimal 1), seven zeros are encountered; etc. 6.7 CASE versus IF Though in principle the presence of ELSE in the IF/ELSE statement might infer the implementation of a priority decoder (which would never occur with CASE), this will generally not happen. For instance, when IF (a sequential statement) is used to implement a fully combinational circuit, a multiplexer might be inferred instead. Therefore, after optimization, the general tendency is for a circuit synthesized from a VHDL code based on IF not to di¤er from that based on CASE. TLFeBOOK Sequential Code 113 Table 6.1 Comparison between WHEN and CASE. WHEN CASE Statement type Concurrent Sequential Usage Only outside PROCESSES, FUNCTIONS, or PROCEDURES Only inside PROCESSES, FUNCTIONS, or PROCEDURES All permutations must be tested Yes for WITH/SELECT/WHEN Yes Max. # of assignments per test 1 Any No-action keyword UNAFFECTED NULL Example: The codes below implement the same physical multiplexer circuit. ---- With IF: -------------IF (sel="00") THEN x<=a; ELSIF (sel="01") THEN x<=b; ELSIF (sel="10") THEN x<=c; ELSE x<=d; ---- With CASE: -----------CASE sel IS WHEN "00" => x<=a; WHEN "01" => x<=b; WHEN "10" => x<=c; WHEN OTHERS => x<=d; END CASE; ---------------------------- 6.8 CASE versus WHEN CASE and WHEN are very similar. However, while one is concurrent (WHEN), the other is sequential (CASE). Their main similarities and di¤erences are summarized in table 6.1. Example: From a functional point of view, the two codes below are equivalent. ---- With WHEN: ---------------WITH sel SELECT TLFeBOOK 114 Chapter 6 x <= a WHEN "000", b WHEN "001", c WHEN "010", UNAFFECTED WHEN OTHERS; ---- With CASE: ---------------CASE sel IS WHEN "000" => x<=a; WHEN "001" => x<=b; WHEN "010" => x<=c; WHEN OTHERS => NULL; END CASE; -------------------------------- 6.9 Bad Clocking The compiler will generally not be able to synthesize codes that contain assignments to the same signal at both transitions of the reference (clock) signal (that is, at the rising edge plus at the falling edge). This is particularly true when the target technology contains only single-edge flip-flops (CPLDs, for example—appendix A). In this case, the compiler might display a message of the type ‘‘signal does not hold value after clock edge’’ or similar. As an example, let us consider the case of a counter that must be incremented at every clock transition (rising plus falling edge). One alternative could be the following: PROCESS (clk) BEGIN IF(clk'EVENT AND clk='1') THEN counter <= counter + 1; ELSIF(clk'EVENT AND clk='0') THEN counter <= counter + 1; END IF; ... END PROCESS; In this case, besides the messages already described, the compiler might also complain that the signal counter is multiply driven. In any case, compilation will be suspended. TLFeBOOK Sequential Code 115 Another important aspect is that the EVENT attribute must be related to a test condition. For example, the statement IF(clk'EVENT AND clk='1') is correct, but using simply IF(clk'EVENT) will either have the compiler assume a default test value (say ‘‘AND clk='1'’’) or issue a message of the type ‘‘clock not locally stable’’. As an example, let us consider again the case of a counter that must be incremented at both transitions of clk. One could write: PROCESS (clk) BEGIN IF(clk'EVENT) THEN counter := counter + 1; END IF; ... END PROCESS; Since the PROCESS above is supposed to be run every time clk changes, one might expect the counter to be incremented twice per clock cycle. However, for the reason already mentioned, this will not happen. If the compiler assumes a default value, a wrong circuit will be synthesized, because only one edge of clk will be considered; if no default value is assumed, then an error message and no compilation should be expected. Finally, if a signal appears in the sensitivity list, but does not appear in any of the assignments that compose the PROCESS, then it is likely that the compiler will simply ignore it. This fact can be illustrated with the double-edge counter described above once again. Say that the following code is used: PROCESS (clk) BEGIN counter := counter + 1; ... END PROCESS; This code reinforces the desire that the signal counter be incremented whenever an event occurs on clk (rising plus falling edge). However, a message of the type ‘‘ignored unnecessary pin clk’’ might be issued instead. Example: Contrary to the cases described above, the 2-process code shown below will be correctly synthesized by any compiler. However, notice that we have used a di¤erent signal in each process. TLFeBOOK 116 Chapter 6 ---------------------PROCESS (clk) BEGIN IF(clk'EVENT AND clk='1') THEN x <= d; END IF; END PROCESS; ---------------------PROCESS (clk) BEGIN IF(clk'EVENT AND clk='0') THEN y <= d; END IF; END PROCESS; ---------------------- Now that you know what you can and what you should not to do, you are invited to solve problem 6.1. Example 6.11: RAM Below is another example using sequential code, particularly the IF statement. We show the implementation of a RAM (random access memory). As can be seen in figure 6.14(a), the circuit has a data input bus (data_in), a data output bus (data_out), an address bus (addr), plus clock (clk) and write enable RAM wr_ena word 0 data in word 1 word 2 addr … clk data_out wr_ena d q DFF clk wr ena (a) (b) Figure 6.14 RAM circuit of example 6.11. TLFeBOOK Sequential Code 117 (wr_ena) pins. When wr_ena is asserted, at the next rising edge of clk the vector present at data_in must be stored in the position specified by addr. The output, data_out, on the other hand, must constantly display the data selected by addr. From the register point-of-view, the circuit can be summarized as in figure 6.14(b). When wr_ena is low, q is connected to the input of the flip-flop, and terminal d is open, so no new data will be written into the memory. However, when wr_ena is turned high, d is connected to the input of the register, so at the next rising edge of clk d will overwrite its previous value. A VHDL code that implements the circuit of figure 6.14 is shown below. The capacity chosen for the RAM is 16 words of length 8 bits each. Notice that the code is totally generic. Note: Other memory implementations will be presented in section 9.10 of chapter 9. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 --------------------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; --------------------------------------------------ENTITY ram IS GENERIC ( bits: INTEGER := 8; -- # of bits per word words: INTEGER := 16); -- # of words in the memory PORT ( wr_ena, clk: IN STD_LOGIC; addr: IN INTEGER RANGE 0 TO words-1; data_in: IN STD_LOGIC_VECTOR (bits-1 DOWNTO 0); data_out: OUT STD_LOGIC_VECTOR (bits-1 DOWNTO 0)); END ram; --------------------------------------------------ARCHITECTURE ram OF ram IS TYPE vector_array IS ARRAY (0 TO words-1) OF STD_LOGIC_VECTOR (bits-1 DOWNTO 0); SIGNAL memory: vector_array; BEGIN PROCESS (clk, wr_ena) BEGIN IF (wr_ena='1') THEN IF (clk'EVENT AND clk='1') THEN memory(addr) <= data_in; END IF; END IF; TLFeBOOK 118 Chapter 6 Figure 6.15 Simulation results of example 6.11. 26 END PROCESS; 27 data_out <= memory(addr); 28 END ram; 29 --------------------------------------------------- Simulation results from the circuit synthesizad with the code above are shown in figure 6.15. 6.10 Using Sequential Code to Design Combinational Circuits We have already seen that sequential code can be used to implement either sequential or combinational circuits. In the former case, registers are necessary, so will be inferred by the compiler. However, this should not happen in the latter case. Moreover, if the code is intended for a combinational circuit, then the complete truth-table should be clearly specified in the code. In order to satisfy the criteria above, the following rules should be observed: Rule 1: Make sure that all input signals used (read) in the PROCESS appear in its sensitivity list. Rule 2: Make sure that all combinations of the input/output signals are included in the code; that is, make sure that, by looking at the code, the circuit’s complete truthtable can be obtained (indeed, this is true for both sequential as well as concurrent code). Failing to comply with rule 1 will generally cause the compiler to simply issue a warning saying that a given input signal was not included in the sensitivity list, and then proceed as if the signal were included. Even though no damage is caused to the design in this case, it is a good design practice to always take rule 1 into consideration. TLFeBOOK Sequential Code 119 a x b c y d sel x y sel x y sel x y 00 01 10 11 a 0 b 1 c d 00 01 10 11 a 0 b 1 c y d y 00 01 10 11 a 0 b 1 c X d X sel (1:0) (a) (b) (c) (d) Figure 6.16 Circuit of example 6.12: (a) top-level diagram, (b) specifications provided, (c) implemented truth-table, and (d) the right approach. With respect to rule 2, however, the consequences can be more serious because incomplete specifications of the output signals might cause the synthesizer to infer latches in order to hold their previous values. This fact is illustrated in the example below. Example 6.12: Bad Combinational Design Let us consider the circuit of figure 6.16, for which the following specifications have been provided: x should behave as a multiplexer; that is, should be equal to the input selected by sel; y, on the other hand, should be equal to ‘0’ when sel ¼ ‘‘00’’, or ‘1’ if sel ¼ ‘‘01’’. These specifications are summarized in the truth-table of figure 6.16(b). Notice that this is a combinational circuit. However, the specifications provided for y are incomplete, as can be observed in the truth-table of figure 6.16(b). Using just these specifications, the code could be the following: 1 2 3 4 5 6 7 8 9 10 -------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; -------------------------------------ENTITY example IS PORT (a, b, c, d: IN STD_LOGIC; sel: IN INTEGER RANGE 0 TO 3; x, y: OUT STD_LOGIC); END example; -------------------------------------- TLFeBOOK 120 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 Chapter 6 ARCHITECTURE example OF example IS BEGIN PROCESS (a, b, c, d, sel) BEGIN IF (sel=0) THEN x<=a; y<='0'; ELSIF (sel=1) THEN x<=b; y<='1'; ELSIF (sel=2) THEN x<=c; ELSE x<=d; END IF; END PROCESS; END example; -------------------------------------- After compiling this code, the report files show that no flip-flops were inferred (as expected). However, when we look at the simulation results (figure 6.17), we notice something peculiar about y. Observe that, for the same value of the input (sel ¼ 3 ¼ ‘‘11’’), two di¤erent results are obtained for y (when sel ¼ 3 is preceded by sel ¼ 0, y ¼ ‘0’ results, while y ¼ ‘1’ is obtained when sel ¼ 3 is preceded by sel ¼ 1). This signifies that some sort of memory was indeed implemented by the compiler. In fact, if we look at the equations obtained with Quartus II, for example (appendix D), we verify that y was computed as y ¼ (sel(0) AND sel(1)) OR (sel(0) AND y) OR Figure 6.17 Simulation results of example 6.12. TLFeBOOK Sequential Code 121 (sel(1) AND y). Therefore, a latch (using AND/OR gates) was implemented, which renders the truth-table of figure 6.16(c). To avoid the extra logic required by the latch, the specifications of figure 6.16(d) should be used (‘X’ was used for all unknown or ‘‘don’t care’’ values). Thus the line y<='X'; must be included below lines 22 and 24 in the code above. Now, y can be as simple as y ¼ sel(0). 6.11 Problems Like the examples just seen, the purpose of the problems proposed in this section is to further illustrate the construction of sequential code (that is, the use of IF, WAIT, CASE, and LOOP, always inside a PROCESS). However, if you want to know more about SIGNALS and VARIABLES before working on the problems below, you may have a look at chapter 7, and then return to this section. Finally, recall that with sequential code we can implement sequential as well as combinational logic circuits. Though you will be using only sequential code in this section, you are invited to determine whether each circuit in the problems below (and in the examples just seen, for that matter) is actually a combinational or sequential circuit. Problem 6.1: Event Counter Design a circuit capable of counting the number of clock events (number of rising edges þ falling edges, figure P6.1). Problem 6.2: Shift Register Write a VHDL code that implements the 4-stage shift-register of figure P6.2. The solution should be di¤erent from that of example 6.3. Problem 6.3: Priority Encoder Figure P6.3 shows the same priority encoder of problem 5.2. The circuit must encode the address of the input bit of highest order that is active. The output ‘‘000’’ should indicate that there is no request at the input (no bit active). Write a VHDL solution for this circuit using only sequential code. Present two solutions: clk Figure P6.1 TLFeBOOK 122 Chapter 6 din dout DFF DFF DFF DFF clk Figure P6.2 ‘0’ ‘1’ ‘0’ ‘0’ ‘1’ ’1’ ‘0’ 7 6 5 4 3 2 1 PRIORITY ENCODER 2 1 0 ‘1’ ’1’ ‘0’ Figure P6.3 FREQ. DIVIDER fclk fclk/n Figure P6.4 (a) With IF. (b) With CASE. Problem 6.4: Generic Frequency Divider Write a VHDL code for a circuit capable of dividing the frequency of an input clock signal by an integer n (figure P6.4). The code should be generic; that is, n should be defined using the GENERIC statement. Problem 6.5: Frequency Multiplier What about the opposite of problem 6.4, that is, say that we want to multiply the clock frequency by n. Can it be done? TLFeBOOK Sequential Code clk start stop 123 min sec sec min sec sec T I M E R reset Figure P6.6 clk start/ stop/ reset T I M E R Figure P6.7 Problem 6.6: Timer #1 Design a timer capable of running from 0min:00sec to 9min:59sec (figure P6.6). The circuit must have start, stop, and reset buttons. The outputs must be SSD coded. Consider that a reliable 1 Hz clock signal is available. Problem 6.7: Timer #2 Consider the timer of problem 6.6. However, say that now only one button is available, which must perform the start and stop functions alternately, and it also resets the circuit when pressed for more than 2 seconds. Write a VHDL code for such a timer (figure P6.7). Again, consider that a reliable 1 Hz clock is available. Problem 6.8: Parity Detector Figure P6.8 shows the top-level diagram of a parity detector. The input vector has eight bits. The output must be ‘0’ when the number of ‘1’s in the input vector is even, or ‘1’ otherwise. Write a sequential code for this circuit. If possible, write more than one solution. TLFeBOOK 124 input (7:0) Chapter 6 PARITY DETECTOR output Figure P6.8 Table P6.9 Number of ones in din(7:1) count(2:0) 0 1 2 3 4 5 6 7 000 001 010 011 100 101 110 111 Table P6.10 Number of ones in din(7:1) dout(7:0) 0 1 2 3 4 5 6 7 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000 Problem 6.9: Count Ones Say that we want to design a circuit that counts the number of ‘1’s in a given binary vector (table P6.9). Write a VHDL code that implements such a circuit. Then synthesize and test your solution. Problem 6.10: Intensity Encoder Design an encoder that receives as input a 7-bit vector din, and creates from it an output vector dout whose bits are all ‘0’s, except the bit whose index corresponds to the number of ‘1’s in din. All possible situations are summarized in table P6.10. TLFeBOOK Sequential Code Problem 6.11: 125 Multiplexer Write a sequential VHDL code for the circuit of problem 5.1. If possible, present more than one solution. Problem 6.12: Vector Shifter Write a sequential VHDL code for the circuit of example 5.6. If possible, present more than one solution. Problem 6.13: ALU Write a sequential VHDL code for the circuit of example 5.5. If possible, present more than one solution. Problem 6.14: Signed/Unsigned Adder/Subtractor Solve problem 5.5 using sequential code. Make the code as generic as possible. Problem 6.15: Comparator Solve problem 5.8 using sequential code. Problem 6.16: Carry Ripple Adder Consider the carry ripple adder of example 6.8. (a) Why cannot we replace the IF statement of lines 17–19 in solution 2 by simply ‘‘temp:=c0;’’? (b) Notice that the circuit of example 6.8 is fully combinational, so it can also be implemented using only concurrent code (that is, without a PROCESS). Write such a code for it. Then simulate it and analyze the results. Problem 6.17: DFF Consider the DFF with asynchronous reset of figure 6.1. Below are several codes for that circuit. Examine each of them and determine whether they should work properly. Briefly explain your answers. -------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; -------------------------------------ENTITY dff IS TLFeBOOK 126 Chapter 6 PORT ( d, clk, rst: IN BIT; q: OUT BIT); END dff; ----- Solution 1 --------------------ARCHITECTURE arch1 OF dff IS BEGIN PROCESS (clk, rst) BEGIN IF (rst='1') THEN q <= '0'; ELSIF (clk'EVENT AND clk='1') THEN q <= d; END IF; END PROCESS; END arch1; ----- Solution 2 --------------------ARCHITECTURE arch2 OF dff IS BEGIN PROCESS (clk) BEGIN IF (rst='1') THEN q <= '0'; ELSIF (clk'EVENT AND clk='1') THEN q <= d; END IF; END PROCESS; END arch2; ----- Solution 3 --------------------ARCHITECTURE arch3 OF dff IS BEGIN PROCESS (clk) BEGIN IF (rst='1') THEN q <= '0'; ELSIF (clk'EVENT) THEN q <= d; END IF; END PROCESS; END arch3; TLFeBOOK Sequential Code 127 ----- Solution 4 --------------------ARCHITECTURE arch4 OF dff IS BEGIN PROCESS (clk) BEGIN IF (rst='1') THEN q <= '0'; ELSIF (clk='1') THEN q <= d; END IF; END PROCESS; END arch4; ----- Solution 5 --------------------ARCHITECTURE arch5 OF dff IS BEGIN PROCESS (clk, rst, d) BEGIN IF (rst='1') THEN q <= '0'; ELSIF (clk='1') THEN q <= d; END IF; END PROCESS; END arch5; -------------------------------------- TLFeBOOK TLFeBOOK 7 Signals and Variables VHDL provides two objects for dealing with non-static data values: SIGNAL and VARIABLE. It also provides means for establishing default (static) values: CONSTANT and GENERIC. The last of these (the GENERIC attribute) was already seen in chapter 4. SIGNAL, VARIABLE, and CONSTANT will be studied together in this chapter. CONSTANT and SIGNAL can be global (that is, seen by the whole code), and can be used in either type of code, concurrent or sequential. A VARIABLE, on the other hand, is local, for it can only be used inside a piece of sequential code (that is, in a PROCESS, FUNCTION, or PROCEDURE) and its value can never be passed out directly. As will become apparent, the choice between a SIGNAL or a VARIABLE is not always easy, so an entire section and several examples will be devoted to the matter. Moreover, a discussion on the number of registers inferred by the compiler, based on SIGNAL and VARIABLE assignments, will also be presented. 7.1 CONSTANT CONSTANT serves to establish default values. Its syntax is shown below. CONSTANT name : type := value; Examples: CONSTANT set_bit : BIT := '1'; CONSTANT datamemory : memory := (('0','0','0','0'), ('0','0','0','1'), ('0','0','1','1')); A CONSTANT can be declared in a PACKAGE, ENTITY, or ARCHITECTURE. When declared in a package, it is truly global, for the package can be used by several entities. When declared in an entity (after PORT), it is global to all architectures that follow that entity. Finally, when declared in an architecture (in its declarative part), it is global only to that architecture’s code. The most common places to find a CONSTANT declaration is in an ARCHITECTURE or in a PACKAGE. TLFeBOOK 130 7.2 Chapter 7 SIGNAL SIGNAL serves to pass values in and out the circuit, as well as between its internal units. In other words, a signal represents circuit interconnects (wires). For instance, all PORTS of an ENTITY are signals by default. Its syntax is the following: SIGNAL name : type [range] [:= initial_value]; Examples: SIGNAL control: BIT := '0'; SIGNAL count: INTEGER RANGE 0 TO 100; SIGNAL y: STD_LOGIC_VECTOR (7 DOWNTO 0); The declaration of a SIGNAL can be made in the same places as the declaration of a CONSTANT (described above). A very important aspect of a SIGNAL, when used inside a section of sequential code (PROCESS, for example), is that its update is not immediate. In other words, its new value should not be expected to be ready before the conclusion of the corresponding PROCESS, FUNCTION or PROCEDURE. Recall that the assignment operator for a SIGNAL is ‘‘<=’’ (Ex.: count<=35;). Also, the initial value in the syntax above is not synthesizable, being only considered in simulations. Another aspect that might a¤ect the result is when multiple assignments are made to the same SIGNAL. The compiler might complain and quit synthesis, or might infer the wrong circuit (by considering only the last assignment, for example). Therefore, establishing initial values, like in line 15 of the example below, should be done with a VARIABLE. Example 7.1: Count Ones #1 (not OK) Say that we want to design a circuit that counts the number of ‘1’s in a binary vector (problem 6.9). Let us consider the solution below, which uses only signals. This code has multiple assignments to the same signal, temp, in lines 15 (once) and 18 (eight times). Moreover, since the value of a signal is not updated immediately, line 18 conflicts with line 15, for the value assigned in line 15 might not be ready until the conclusion of the PROCESS, in which case a wrong value would be computed in line 18. In this kind of situation, the use of a VARIABLE is recommended (example 7.2). TLFeBOOK Signals and Variables 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 131 --------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; --------------------------------------ENTITY count_ones IS PORT ( din: IN STD_LOGIC_VECTOR (7 DOWNTO 0); ones: OUT INTEGER RANGE 0 TO 8); END count_ones; --------------------------------------ARCHITECTURE not_ok OF count_ones IS SIGNAL temp: INTEGER RANGE 0 TO 8; BEGIN PROCESS (din) BEGIN temp <= 0; FOR i IN 0 TO 7 LOOP IF (din(i)='1') THEN temp <= temp + 1; END IF; END LOOP; ones <= temp; END PROCESS; END not_ok; --------------------------------------- Notice also in the solution above that the internal signal temp (line 11) seems unnecessary, because ones could have been used directly. However, to do so, the mode of ones would need to be changed from OUT to BUFFER (line 7), because ones is assigned a value and is also read (used) internally. Nevertheless, since ones is a genuine unidirectional (OUT) signal, the use of an auxiliary signal (temp) is an adequate design practice. 7.3 VARIABLE Contrary to CONSTANT and SIGNAL, a VARIABLE represents only local information. It can only be used inside a PROCESS, FUNCTION, or PROCEDURE (that is, in sequential code), and its value can not be passed out directly. On the other hand, its update is immediate, so the new value can be promptly used in the next line of code. TLFeBOOK 132 Chapter 7 To declare a VARIABLE, the following syntax should be used: VARIABLE name : type [range] [:= init_value]; Examples: VARIABLE control: BIT := '0'; VARIABLE count: INTEGER RANGE 0 TO 100; VARIABLE y: STD_LOGIC_VECTOR (7 DOWNTO 0) := "10001000"; Since a VARIABLE can only be used in sequential code, its declaration can only be done in the declarative part of a PROCESS, FUNCTION, or PROCEDURE. Recall that the assignment operator for a VARIABLE is ‘‘:=’’ (Ex.: count:=35;). Also, like in the case of a SIGNAL, the initial value in the syntax above is not synthesizable, being only considered in simulations. Example 7.2: Count Ones #2 (OK) Let us consider the problem of example 7.1 once again. The only di¤erence in the solution below is that an internal VARIABLE is employed instead of a SIGNAL. Since the update of a variable is immediate, the initial value is established correctly and no complains regarding multiple assignments will be issued by the compiler. Simulation results can be verified in figure 7.1. 1 2 3 4 5 6 7 --------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; --------------------------------------ENTITY count_ones IS PORT ( din: IN STD_LOGIC_VECTOR (7 DOWNTO 0); ones: OUT INTEGER RANGE 0 TO 8); Figure 7.1 Simulation results of example 7.2. TLFeBOOK Signals and Variables 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 7.4 133 END count_ones; --------------------------------------ARCHITECTURE ok OF count_ones IS BEGIN PROCESS (din) VARIABLE temp: INTEGER RANGE 0 TO 8; BEGIN temp := 0; FOR i IN 0 TO 7 LOOP IF (din(i)='1') THEN temp := temp + 1; END IF; END LOOP; ones <= temp; END PROCESS; END ok; --------------------------------------- SIGNAL versus VARIABLE As already mentioned, choosing between a SIGNAL or a VARIABLE is not always straightforward. Their main di¤erences are summarized in table 7.1. Table 7.1 Comparison between SIGNAL and VARIABLE. SIGNAL VARIABLE Assignment <¼ :¼ Utility Represents circuit interconnects (wires) Represents local information Scope Can be global (seen by entire code) Local (visible only inside the corresponding PROCESS, FUNCTION, or PROCEDURE) Behavior Update is not immediate in sequential code (new value generally only available at the conclusion of the PROCESS, FUNCTION, or PROCEDURE) Updated immediately (new value can be used in the next line of code) Usage In a PACKAGE, ENTITY, or ARCHITECTURE. In an ENTITY, all PORTS are SIGNALS by default Only in sequential code, that is, in a PROCESS, FUNCTION, or PROCEDURE TLFeBOOK 134 Chapter 7 a b c MUX y d sel (1:0) Figure 7.2 Multiplexer of example 7.3. We want to stress again that an assignment to a VARIABLE is immediate, but that is not the case with a SIGNAL. In general, the new value of a SIGNAL will only be available at the conclusion of the current run of the corresponding PROCESS. Though this might not be always the case, it is a safe practice to consider it so. The examples presented below will further illustrate this and other di¤erences between SIGNALS and VARIABLES. Example 7.3: Bad versus Good Multiplexer In this example, we will implement the same multiplexer of example 5.2 (repeated in figure 7.2). This is, indeed, a classical example regarding the choice of a SIGNAL versus a VARIABLE. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 -- Solution 1: using a SIGNAL (not ok) -LIBRARY ieee; USE ieee.std_logic_1164.all; ----------------------------------------ENTITY mux IS PORT ( a, b, c, d, s0, s1: IN STD_LOGIC; y: OUT STD_LOGIC); END mux; ----------------------------------------ARCHITECTURE not_ok OF mux IS SIGNAL sel : INTEGER RANGE 0 TO 3; BEGIN PROCESS (a, b, c, d, s0, s1) BEGIN sel <= 0; IF (s0='1') THEN sel <= sel + 1; TLFeBOOK Signals and Variables 135 17 END IF; 18 IF (s1='1') THEN sel <= sel + 2; 19 END IF; 20 CASE sel IS 21 WHEN 0 => y<=a; 22 WHEN 1 => y<=b; 23 WHEN 2 => y<=c; 24 WHEN 3 => y<=d; 25 END CASE; 26 END PROCESS; 27 END not_ok; 28 ----------------------------------------1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 -- Solution 2: using a VARIABLE (ok) ---LIBRARY ieee; USE ieee.std_logic_1164.all; ----------------------------------------ENTITY mux IS PORT ( a, b, c, d, s0, s1: IN STD_LOGIC; y: OUT STD_LOGIC); END mux; ----------------------------------------ARCHITECTURE ok OF mux IS BEGIN PROCESS (a, b, c, d, s0, s1) VARIABLE sel : INTEGER RANGE 0 TO 3; BEGIN sel := 0; IF (s0='1') THEN sel := sel + 1; END IF; IF (s1='1') THEN sel := sel + 2; END IF; CASE sel IS WHEN 0 => y<=a; WHEN 1 => y<=b; WHEN 2 => y<=c; WHEN 3 => y<=d; END CASE; TLFeBOOK 136 Chapter 7 26 END PROCESS; 27 END ok; 28 --------------------------------------- Comments: A common mistake when using a SIGNAL is not to remember that it might require a certain amount of time to be updated. Therefore, the assignment sel <¼ sel þ 1 in the first solution (line 16) will result in one plus whatever value had been previously propagated to sel, for the assignment sel <¼ 0 (line 15) might not have had time to propagate yet. The same is true for sel <¼ sel þ 2 (line 18). This is not a problem when using a VARIABLE, for its assignment is always immediate. A second aspect that might be a problem in solution 1 is that more than one assignment is being made to the same SIGNAL (sel, lines 15, 16, and 18), which might not be acceptable. Generally, only one assignment to a SIGNAL is allowed within a PROCESS, so the software will either consider only the last one (sel <¼ sel þ 2 in solution 1) or simply issue an error message and stop compilation. Again, this is never a problem when using a VARIABLE. Figure 7.3 Simulation results of example 7.3. TLFeBOOK Signals and Variables 137 Simulation results from both solutions are shown in figure 7.3 (bad mux in the upper graph, good mux in the lower graph). As can be seen, only solution 2 works properly. Example 7.4: DFF with q and qbar #1 We want to implement the DFF of figure 7.4. This circuit di¤ers from that of example 6.1 by the absence of reset and the inclusion of qbar. The presence of qbar will help understand how an assignment to a SIGNAL is made (recall that a PORT is a SIGNAL by default). 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ---- Solution 1: not OK --------------LIBRARY ieee; USE ieee.std_logic_1164.all; --------------------------------------ENTITY dff IS PORT ( d, clk: IN STD_LOGIC; q: BUFFER STD_LOGIC; qbar: OUT STD_LOGIC); END dff; --------------------------------------ARCHITECTURE not_ok OF dff IS BEGIN PROCESS (clk) BEGIN IF (clk'EVENT AND clk='1') THEN q <= d; qbar <= NOT q; END IF; END PROCESS; END not_ok; --------------------------------------- q d DFF clk qbar Figure 7.4 DFF of example 7.4. TLFeBOOK 138 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 Chapter 7 ---- Solution 2: OK ------------------LIBRARY ieee; USE ieee.std_logic_1164.all; --------------------------------------ENTITY dff IS PORT ( d, clk: IN STD_LOGIC; q: BUFFER STD_LOGIC; qbar: OUT STD_LOGIC); END dff; --------------------------------------ARCHITECTURE ok OF dff IS BEGIN PROCESS (clk) BEGIN IF (clk'EVENT AND clk='1') THEN q <= d; END IF; END PROCESS; qbar <= NOT q; END ok; --------------------------------------- Comments: In solution 1, the assignments q<=d (line 16) and qbar<=NOT q (line 17) are both synchronous, so their new values will only be available at the conclusion of the PROCESS. This is a problem for qbar, because the new value of q has not propagated yet. Therefore, qbar will assume the reverse of the old value of q. In other words, the right value of qbar will be one clock cycle delayed, thus causing the circuit not to work correctly. This behavior can be observed in the upper graph of figure 7.5. In solution 2, we have placed qbar<=NOT q (line 30) outside the PROCESS, thus operating as a true concurrent expression. The behavior of the resulting circuit can be observed in the lower graph of figure 7.5. Example 7.5: Frequency Divider In this example, we want to implement a circuit that divides the clock frequency by 6 (figure 7.6). Intentionally, we have implemented two outputs, one based on a SIGNAL (count1) and the other based on a VARIABLE (count2). Knowing that both work properly (see simulation results in figure 7.7), you are invited to fill in the two blanks and to explain your answers. TLFeBOOK Signals and Variables 139 Figure 7.5 Simulation results of example 7.4. fclk FREQ. DIVIDER fclk/6 Figure 7.6 Frequency divider of example 7.5. Figure 7.7 Simulation results of example 7.5. TLFeBOOK 140 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 Chapter 7 ----------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; ----------------------------------------ENTITY freq_divider IS PORT ( clk : IN STD_LOGIC; out1, out2 : BUFFER STD_LOGIC); END freq_divider; ----------------------------------------ARCHITECTURE example OF freq_divider IS SIGNAL count1 : INTEGER RANGE 0 TO 7; BEGIN PROCESS (clk) VARIABLE count2 : INTEGER RANGE 0 TO 7; BEGIN IF (clk'EVENT AND clk='1') THEN count1 <= count1 + 1; count2 := count2 + 1; IF (count1 = ? ) THEN out1 <= NOT out1; count1 <= 0; END IF; IF (count2 = ? ) THEN out2 <= NOT out2; count2 := 0; END IF; END IF; END PROCESS; END example; ----------------------------------------- 7.5 Number of Registers In this section, we will discuss the number of flip-flops inferred from the code by the compiler. The purpose is not only to understand which approaches require less registers, but also to make sure that the code does implement the expected circuit. TLFeBOOK Signals and Variables 141 A SIGNAL generates a flip-flop whenever an assignment is made at the transition of another signal; that is, when a synchronous assignment occurs. Such assignment, being synchronous, can only happen inside a PROCESS, FUNCTION, or PROCEDURE (usually following a declaration of the type ‘‘IF signal’EVENT . . .’’ or ‘‘WAIT UNTIL . . .’’). A VARIABLE, on the other hand, will not necessarily generate flip-flops if its value never leaves the PROCESS (or FUNCTION, or PROCEDURE). However, if a value is assigned to a variable at the transition of another signal, and such value is eventually passed to a signal (which leaves the process), then flip-flops will be inferred. A VARIABLE also generates a register when it is used before a value has been assigned to it. The examples presented below will illustrate these points. Example: In the process shown below, output1 and output2 will both be stored (that is, infer flip-flops), because both are assigned at the transition of another signal (clk). PROCESS (clk) BEGIN IF (clk'EVENT AND clk='1') THEN output1 <= temp; -- output1 stored output2 <= a; -- output2 stored END IF; END PROCESS; Example: In the next process, only output1 will be stored (output2 will make use of logic gates). PROCESS (clk) BEGIN IF (clk'EVENT AND clk='1') THEN output1 <= temp; -- output1 stored END IF; output2 <= a; -- output2 not stored END PROCESS; Example: In the process below, temp (a variable) will cause x (a signal) to be stored. PROCESS (clk) VARIABLE temp: BIT; BEGIN IF (clk'EVENT AND clk='1') THEN TLFeBOOK 142 Chapter 7 temp <= a; END IF; x <= temp; -- temp causes x to be stored END PROCESS; Additional (complete) examples are presented next. The purpose is to further illustrate when and why registers are inferred from SIGNAL and VARIABLE assignments. Example 7.6: DFF with q and qbar #2 Let us consider the DFF of figure 7.4 once again. Both solutions presented below function properly. The di¤erence between them, however, resides in the number of flip-flops needed in each case. Solution 1 has two synchronous SIGNAL assignments (lines 16–17), so 2 flip-flops will be generated. This is not the case in solution 2, where one of the assignments (line 19) is no longer synchronous. The resulting circuits are presented in figures 7.8(a)–(b), respectively. 1 2 3 4 5 6 ---- Solution 1: Two DFFs --------------LIBRARY ieee; USE ieee.std_logic_1164.all; ----------------------------------------ENTITY dff IS PORT ( d, clk: IN STD_LOGIC; q d DFF q d clk DFF qbar qbar clk DFF (a) (b) Figure 7.8 Circuits inferred from the code of example 7.6: (a) solution 1, (b) solution 2. TLFeBOOK Signals and Variables 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 END dff; ----------------------------------------ARCHITECTURE two_dff OF dff IS BEGIN PROCESS (clk) BEGIN IF (clk'EVENT AND clk='1') THEN q <= d; -- generates a register qbar <= NOT d; -- generates a register END IF; END PROCESS; END two_dff; ----------------------------------------- 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ---- Solution 2: One DFF ---------------LIBRARY ieee; USE ieee.std_logic_1164.all; ----------------------------------------ENTITY dff IS PORT ( d, clk: IN STD_LOGIC; q: BUFFER STD_LOGIC; qbar: OUT STD_LOGIC); END dff; ----------------------------------------ARCHITECTURE one_dff OF dff IS BEGIN PROCESS (clk) BEGIN IF (clk'EVENT AND clk='1') THEN q <= d; -- generates a register END IF; END PROCESS; qbar <= NOT q; -- uses logic gate (no register) END one_dff; ----------------------------------------- 143 q: BUFFER STD_LOGIC; qbar: OUT STD_LOGIC); TLFeBOOK 144 Chapter 7 COUNTER clk count (2:0) rst Figure 7.9 0-to-7 counter of example 7.7. Comments: Example 7.6 illustrates a very important situation, in which extra (unnecessary) hardware might be inferred when the code is not assembled carefully. With solution 2, the synthesizer will always infer only one flip-flop. It is interesting to mention, however, that for certain types of CPLD/FPGA devices, when the signals q and qbar are connected directly to chip pins, the fitter (place & route) might still opt for two flip-flops in the physical implementation. This does not mean that two flip-flops were indeed necessary. In fact, though the fitter (place & route) report might mention two registers in such cases, the synthesis report will invariably inform that only one register was indeed required. A further discussion is presented in problem 7.7. Example 7.7: Counter Let us consider the 0-to-7 counter of figure 7.9. Two solutions are presented below. In the first, a synchronous VARIABLE assignment is made (lines 14–15). In the second, a synchronous SIGNAL assignment occurs (lines 13–14). From either solution, three flip-flops are inferred (to hold the 3-bit output signal count). Solution 1 is an example that a VARIABLE can indeed generate registers. The reason is that its assignment (line 15) is at the transition of another signal (clk, line 14) and its value does leave the PROCESS (line 17). Solution 2, on the other hand, uses only SIGNALS. Notice that, since no auxiliary signal was used, count needed to be declared as of mode BUFFER (line 4), because it is assigned a value and is also read (used) internally (line 14). Still regarding line 14 of solution 2, notice that a SIGNAL, like a VARIABLE, can also be incremented when used in a sequential code. Finally, notice that neither in solution 1 nor in solution 2 was the std_logic_1164 package declared, because we are not using std_logic data types in this example. 1 2 ------ Solution 1: With a VARIABLE -------ENTITY counter IS TLFeBOOK Signals and Variables 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 PORT ( clk, rst: IN BIT; count: OUT INTEGER RANGE 0 TO 7); END counter; -------------------------------------------ARCHITECTURE counter OF counter IS BEGIN PROCESS (clk, rst) VARIABLE temp: INTEGER RANGE 0 TO 7; BEGIN IF (rst='1') THEN temp:=0; ELSIF (clk'EVENT AND clk='1') THEN temp := temp+1; END IF; count <= temp; END PROCESS; END counter; -------------------------------------------- 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ------ Solution 2: With SIGNALS only ------ENTITY counter IS PORT ( clk, rst: IN BIT; count: BUFFER INTEGER RANGE 0 TO 7); END counter; -------------------------------------------ARCHITECTURE counter OF counter IS BEGIN PROCESS (clk, rst) BEGIN IF (rst='1') THEN count <= 0; ELSIF (clk'EVENT AND clk='1') THEN count <= count + 1; END IF; END PROCESS; END counter; -------------------------------------------- 145 Simulation results (from either solution above) are shown in figure 7.10. TLFeBOOK 146 Chapter 7 Figure 7.10 Simulation results of example 7.7. din dout DFF DFF DFF DFF clk Figure 7.11 Shift-register of example 7.8. Example 7.8: Shift Register #1 We are now interested in examining what happens to the 4-stage shift register of figure 7.11 when di¤erent VARIABLE and SIGNAL assignments are made. Of course, if the solution is correct, then the output signal (dout) should be four positive clock edges behind the input signal (din). In solution 1, three VARIABLES are used (a, b, and c, line 10). However, the variables are used before values are assigned to them (that is, in reverse order, starting with dout, line 13, and ending with din, line 16). Consequently, flip-flops will be inferred, which store the values from the previous run of the PROCESS. In solution 2, the variables were replaced by SIGNALS (line 8), and the assignments are made in direct order (from din to dout, lines 13–16). Since signal assignments at the transition of another signal do generate registers, here too the right circuit will be inferred. Finally, in solution 3, the same variables of solution 1 were employed, but in direct order (from din to dout, lines 13–16). Recall, however, that an assignment to a variable is immediate, and since the variables are being used in direct order (that is, after values have been assigned to them), lines 13–15 collapse into one line, equivalent to c :¼ din. The value of c does leave the process in the next line (line 16), however, where a signal assignment (dout <¼ c) occurs at the transition of clk. Therefore, one register will be inferred from solution 3, thus not resulting the correct circuit. TLFeBOOK Signals and Variables 147 Note: More conventional solutions to the shift-register problem will be presented in example 7.9. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 -------- Solution 1: ----------------ENTITY shift IS PORT ( din, clk: IN BIT; dout: OUT BIT); END shift; -------------------------------------ARCHITECTURE shift OF shift IS BEGIN PROCESS (clk) VARIABLE a, b, c: BIT; BEGIN IF (clk'EVENT AND clk='1') THEN dout <= c; c := b; b := a; a := din; END IF; END PROCESS; END shift; -------------------------------------- 1 2 3 4 5 6 7 8 9 10 11 12 13 14 -------- Solution 2: ----------------ENTITY shift IS PORT ( din, clk: IN BIT; dout: OUT BIT); END shift; -------------------------------------ARCHITECTURE shift OF shift IS SIGNAL a, b, c: BIT; BEGIN PROCESS (clk) BEGIN IF (clk'EVENT AND clk='1') THEN a <= din; b <= a; TLFeBOOK 148 Chapter 7 15 c <= b; 16 dout <= c; 17 END IF; 18 END PROCESS; 19 END shift; 20 -------------------------------------1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 -------- Solution 3: ----------------ENTITY shift IS PORT ( din, clk: IN BIT; dout: OUT BIT); END shift; -------------------------------------ARCHITECTURE shift OF shift IS BEGIN PROCESS (clk) VARIABLE a, b, c: BIT; BEGIN IF (clk'EVENT AND clk='1') THEN a := din; b := a; c := b; dout <= c; END IF; END PROCESS; END shift; -------------------------------------- Simulation results from solution 1 or 2 are shown in the upper graph of figure 7.12, while the lower graph shows results from solution 3. As expected, dout is four positive clock edges behind din in the former, but only one positive edge behind the input in the latter. Example 7.9: Shift Register #2 In this example, conventional approaches to the design of shift registers are presented. Figure 7.13 shows a 4-bit shift register, similar to that of example 7.8, except for the presence of a reset input (rst). As before, the output bit (q) should be four positive clock edges behind the input bit (d). Reset should be asynchronous, forcing all flipflop outputs to ‘0’ when asserted. TLFeBOOK Signals and Variables 149 Figure 7.12 Simulation results of example 7.8 (solutions 1 and 2). d q DFF DFF DFF DFF clk rst Figure 7.13 Shift register of example 7.9. Two solutions are presented. One uses a SIGNAL to generate the flip-flops, while the other uses a VARIABLE. The synthesized circuits are the same (that is, four flipflops are inferred from either solution). In solution 1, registers are created because an assignment to a signal is made at the transition of another signal (lines 17–18). In solution 2, the assignment at the transition of another signal is made to a variable (lines 17–18), but since its value does leave the process (that is, it is passed to a port in line 20), it too infers registers. 1 2 3 4 5 ---- Solution 1: With an internal SIGNAL --LIBRARY ieee; USE ieee.std_logic_1164.all; -------------------------------------------ENTITY shiftreg IS TLFeBOOK 150 Chapter 7 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 PORT ( d, clk, rst: IN STD_LOGIC; q: OUT STD_LOGIC); END shiftreg; -------------------------------------------ARCHITECTURE behavior OF shiftreg IS SIGNAL internal: STD_LOGIC_VECTOR (3 DOWNTO 0); BEGIN PROCESS (clk, rst) BEGIN IF (rst='1') THEN internal <= (OTHERS => '0'); ELSIF (clk'EVENT AND clk='1') THEN internal <= d & internal(3 DOWNTO 1); END IF; END PROCESS; q <= internal(0); END behavior; -------------------------------------------- 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 -- Solution 2: With an internal VARIABLE --LIBRARY ieee; USE ieee.std_logic_1164.all; -------------------------------------------ENTITY shiftreg IS PORT ( d, clk, rst: IN STD_LOGIC; q: OUT STD_LOGIC); END shiftreg; -------------------------------------------ARCHITECTURE behavior OF shiftreg IS BEGIN PROCESS (clk, rst) VARIABLE internal: STD_LOGIC_VECTOR (3 DOWNTO 0); BEGIN IF (rst='1') THEN internal := (OTHERS => '0'); ELSIF (clk'EVENT AND clk='1') THEN internal := d & internal(3 DOWNTO 1); END IF; q <= internal(0); TLFeBOOK Signals and Variables 151 Figure 7.14 Simulation results of example 7.9. 21 END PROCESS; 22 END behavior; 23 -------------------------------------------- Simulation results (from either solution above) are shown in figure 7.14. As can be seen, q is indeed four positive clock edges behind d. You may now review the usage of SIGNALS and VARIABLES in all examples of chapter 6. Moreover, in chapter 8, a series of design examples will be presented in which the correct understanding of the di¤erences between signals and variables is crucial, or the wrong circuit might be inferred. 7.6 Problems Problem 7.1: VHDL ‘‘Numerical’’ Objects Given the following VHDL objects: CONSTANT max : INTEGER := 10; SIGNAL x: INTEGER RANGE -10 TO 10; SIGNAL y: BIT_VECTOR (15 DOWNTO 0); VARIABLE z: BIT; Determine which among the assignments below are legal (suggestion: review chapter 3). x <= 5; x <= y(5); z <= '1'; z := y(5); WHILE i IN 0 TO max LOOP... TLFeBOOK 152 Chapter 7 q1 q2 q3 q4 d DFF DFF DFF DFe clk MUX q sel Figure P7.2 FOR i IN 0 TO x LOOP... G1: FOR i IN 0 TO max GENERATE... G1: FOR i IN 0 TO x GENERATE... Problem 7.2: Data Delay Figure P7.2 shows the diagram of a programmable data delay circuit. The input (d) and output (q) are 4-bit buses. Depending on the value of sel (select), q should be one, two, three, or four clock cycles delayed with respect to d. (a) Write a VHDL code for this circuit; (b) How many flip-flops do you expect your solution to contain? (c) Synthesize your solution and open the report file. Verify whether the actual number of flip-flops matches your prediction. Problem 7.3: DFF with q and qbar #1 We want to implement the same flip-flop of example 7.4 (figure 7.4). However, we have introduced an auxiliary signal (temp) in our code. You are asked to examine each of the solutions below and determine whether q and qbar will work properly. Briefly explain your answers. --------------------------------------ENTITY dff IS PORT ( d, clk: IN BIT; q, qbar: BUFFER BIT); END dff; TLFeBOOK Signals and Variables 153 -------- Solution 1 ------------------ARCHITECTURE arch1 OF dff IS SIGNAL temp: BIT; BEGIN PROCESS (clk) BEGIN IF (clk'EVENT AND clk='1') THEN temp <= d; q <= temp; qbar <= NOT temp; END IF; END PROCESS; END arch1; -------- Solution 2 ------------------ARCHITECTURE arch2 OF dff IS SIGNAL temp: BIT; BEGIN PROCESS (clk) BEGIN IF (clk'EVENT AND clk='1') THEN temp <= d; END IF; q <= temp; qbar <= NOT temp; END PROCESS; END arch2; -------- Solution 3 ------------------ARCHITECTURE arch3 OF dff IS SIGNAL temp: BIT; BEGIN PROCESS (clk) BEGIN IF (clk'EVENT AND clk='1') THEN temp <= d; END IF; END PROCESS; q <= temp; qbar <= NOT temp; TLFeBOOK 154 Chapter 7 END arch3; --------------------------------------- Problem 7.4: DFF with q and qbar #2 This problem is similar to problem 7.3. However, here we have an auxiliary VARIABLE instead of an auxiliary SIGNAL. You are asked to examine each of the solutions below and determine whether q and qbar will work as expected. Briefly explain your answers. --------------------------------------ENTITY dff IS PORT ( d, clk: IN BIT; q: BUFFER BIT; qbar: OUT BIT); END dff; -------- Solution 1 ------------------ARCHITECTURE arch1 OF dff IS BEGIN PROCESS (clk) VARIABLE temp: BIT; BEGIN IF (clk'EVENT AND clk='1') THEN temp := d; q <= temp; qbar <= NOT temp; END IF; END PROCESS; END arch1; -------- Solution 2 ------------------ARCHITECTURE arch2 OF dff IS BEGIN PROCESS (clk) VARIABLE temp: BIT; BEGIN IF (clk'EVENT AND clk='1') THEN temp := d; q <= temp; qbar <= NOT q; TLFeBOOK Signals and Variables c0 155 c0.c1+c0.c1 c0.c2+c1.c2+c0.c1.c2 c0 c1 DFF DFF ? c2 DFF c3 DFF clk Figure P7.5 END IF; END PROCESS; END arch2; -------- Solution 3 ------------------ARCHITECTURE arch3 OF dff IS BEGIN PROCESS (clk) VARIABLE temp: BIT; BEGIN IF (clk'EVENT AND clk='1') THEN temp := d; q <= temp; END IF; END PROCESS; qbar <= NOT q; END arch3; --------------------------------------- Problem 7.5: Counter Consider the 4-bit counter of example 6.2. However, suppose that now it should count from 0 (‘‘0000’’) to 15 (‘‘1111’’). (a) Write a VHDL code for it, then synthesize and simulate your solution to verify that it works as expected. (b) Open the report file created by your synthesis tool and confirm that four flipflops were inferred. (c) Still using the report file, observe whether the circuit looks like that of figure P7.5. Are the equations implemented at the flip-flop inputs similar or equivalent to those shown in figure P7.5? What is the missing equation (input of fourth flip-flop)? TLFeBOOK 156 Chapter 7 sel (m-1:0) mxn DECODER ena x(n-1) x(n-2) … x(1) x(0) Figure P7.6 Problem 7.6: Generic n-by-m Decoder Let us consider the generic n-by-m decoder presented in example 4.1 (repeated in figure P7.6). The code presented below, though very compact, contains a flaw in the assignment ‘‘x<=(sel=>'0', OTHERS=>'1'’’. The reason is that sel is not a locally stable signal (indeed, it appears in the sensitivity list of the PROCESS). You are asked to correct the code. --------------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; --------------------------------------------ENTITY decoder IS PORT ( ena : IN STD_LOGIC; sel : IN INTEGER RANGE 0 TO 7; x : OUT STD_LOGIC_VECTOR (7 DOWNTO 0)); END decoder; --------------------------------------------ARCHITECTURE not_ok OF decoder IS BEGIN PROCESS (ena, sel) BEGIN IF (ena='0') THEN x <= (OTHERS => '1'); ELSE x <= (sel=>'0', OTHERS => '1'); END IF; END PROCESS; END not_ok; --------------------------------------------- TLFeBOOK Signals and Variables Problem 7.7: 157 DFF with q and qbar #3 Consider the DFF implemented in solution 2 of example 7.6. We are interested in examining the number of registers required in its implementation. We already know that the answer is one. However, as we mentioned in the comments of example 7.6, even though the synthesizer tells us so, the fitter (place & route) might opt for two registers in the final (physical) implementation when q and qbar are connected directly to output pins. This problem deals with this kind of situation. (a) Compile the code of example 7.6 (solution 2) using Quartus II 3.0 (appendix D). Select a device from the MAX3000A or Cyclone family. In the synthesis reports, verify the number of registers inferred and the equations implemented by the synthesizer (confirming the number of flip-flops). Next, repeat these verifications in the fitter reports (number of registers and equations). (b) Repeat the procedure above for another device. Select a chip from the FLEX10K family. (c) Compile now the code of example 7.6 (solution 2) using ISE 6.1 (appendix B). Select a device from the XC9500 or CoolRunner II family. After compilation, make the same verifications described above. (d) Finally, consider the case when one of the outputs of the flip-flop is not connected directly to a pin. In order to do so, we have introduced a signal called test in the code below. Repeat all topics above for this new code. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ---------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; ---------------------------------------ENTITY dff IS PORT ( d, clk, test: IN STD_LOGIC; q: BUFFER STD_LOGIC; qbar: OUT STD_LOGIC); END dff; ---------------------------------------ARCHITECTURE one_dff OF dff IS BEGIN PROCESS (clk) BEGIN TLFeBOOK 158 Chapter 7 15 IF (clk'EVENT AND clk='1') THEN 16 q <= d; 17 END IF; 18 END PROCESS; 19 qbar <= NOT q AND test; 20 END one_dff; 21 ---------------------------------------- TLFeBOOK 8 State Machines Finite state machines (FSM) constitute a special modeling technique for sequential logic circuits. Such a model can be very helpful in the design of certain types of systems, particularly those whose tasks form a well-defined sequence (digital controllers, for example). We start the chapter by reviewing fundamental concepts related to FSM. We then introduce corresponding VHDL coding techniques, followed by complete design examples. 8.1 Introduction Figure 8.1 shows the block diagram of a single-phase state machine. As indicated in the figure, the lower section contains the sequential logic (flip-flops), while the upper section contains the combinational logic. The combinational (upper) section has two inputs, being one pr_state (present state) and the other the external input proper. It has also two outputs, nx_state (next state) and the external output proper. The sequential (lower) section has three inputs (clock, reset, and nx_state), and one output (pr_state). Since all flip-flops are in this part of the system, clock and reset must be connected to it. If the output of the machine depends not only on the present state but also on the current input, then it is called a Mealy machine. Otherwise, if it depends only on the current state, it is called a Moore machine. Examples of both will be shown later. The separation of the circuit into two sections (figure 8.1) allows the design to be broken into two parts as well. From a VHDL perspective, it is clear that the lower part, being sequential, will require a PROCESS, while the upper part, being combinational, will not. However, recall that sequential code can implement both types of logic, combinational as well as sequential. Hence, if desired, the upper part can also be implemented using a PROCESS. The signals clock and reset normally appear in the sensitivity list of the lower section’s PROCESS (unless reset is synchronous or not used, or WAIT is used instead of IF). When reset is asserted, pr_state will be set to the system’s initial state. Otherwise, at the proper clock edge the flip-flops will store nx_state, thus transferring it to the lower section’s output (pr_state). One important aspect related to the FSM approach is that, though any sequential circuit can in principle be modeled as a state machine, this is not always advantageous. The reason is that the code might become longer, more complex, and more error prone than in a conventional approach. This is often the case with simple registered TLFeBOOK 160 Chapter 8 input Combinational logic output nx_state pr_state Sequential logic clock reset Figure 8.1 Mealy (Moore) state machine diagram. circuits, like counters. As a simple rule of thumb, the FSM approach is advisable in systems whose tasks constitute a well-structured list so all states can be easily enumerated. That is, in a typical state machine implementation, we will encounter, at the beginning of the ARCHITECTURE, a user-defined enumerated data type, containing a list of all possible system states. Digital controllers are good examples of such circuits. Another important aspect, which was already emphasized at the beginning of chapter 5, is that not all circuits that possess memory are necessarily sequential. A RAM (Random Access Memory) was given as an example. In it, the memory-read operation depends only on the address bits presently applied to the RAM (current input), with the retrieved value having nothing to do with previous memory accesses (previous inputs). In such cases, the FSM approach is not advisable. 8.2 Design Style #1 Several approaches can be conceived to design a FSM. We will describe in detail one style that is well structured and easily applicable. In it, the design of the lower section of the state machine (figure 8.1) is completely separated from that of the upper section. All states of the machine are always explicitly declared using an enumerated data type. After introducing such a design style, we will examine it from a data TLFeBOOK State Machines 161 storage perspective, in order to further understand and refine its construction, which will lead to design style #2. Design of the Lower (Sequential) Section In figure 8.1, the flip-flops are in the lower section, so clock and reset are connected to it. The other lower section’s input is nx_state (next state), while pr_state (present state) is its only output. Being the circuit of the lower section sequential, a PROCESS is required, in which any of the sequential statements (IF, WAIT, CASE, or LOOP, chapter 6) can be employed. A typical design template for the lower section is the following: PROCESS (reset, clock) BEGIN IF (reset='1') THEN pr_state <= state0; ELSIF (clock'EVENT AND clock='1') THEN pr_state <= nx_state; END IF; END PROCESS; The code shown above is very simple. It consists of an asynchronous reset, which determines the initial state of the system (state0), followed by the synchronous storage of nx_state (at the positive transition of clock), which will produce pr_state at the lower section’s output (figure 8.1). One good thing about this approach is that the design of the lower section is basically standard. Another advantage of this design style is that the number of registers is minimum. From section 7.5, we know that the number of flip-flops inferred from the code above is simply equal to the number of bits needed to encode all states of the FSM (because the only signal to which a value is assigned at the transition of another signal is pr_state). Therefore, if the default (binary) encoding style (section 8.4) is used, just dlog2 ne flip-flops will then be needed, where n is the number of states. Design of the Upper (Combinational) Section In figure 8.1, the upper section is fully combinational, so its code does not need to be sequential; concurrent code can be used as well. Yet, in the design template shown below, sequential code was employed, with the CASE statement playing the central role. In this case, recall that rules 1 and 2 of section 6.10 must be observed. TLFeBOOK 162 Chapter 8 PROCESS (input, pr_state) BEGIN CASE pr_state IS WHEN state0 => IF (input = ...) THEN output <= <value>; nx_state <= state1; ELSE ... END IF; WHEN state1 => IF (input = ...) THEN output <= <value>; nx_state <= state2; ELSE ... END IF; WHEN state2 => IF (input = ...) THEN output <= <value>; nx_state <= state2; ELSE ... END IF; ... END CASE; END PROCESS; As can be seen, this code is also very simple, and does two things: (a) it assigns the output value and (b) it establishes the next state. Notice also that it complies with rules 1 and 2 of section 6.10, relative to the design of combinational circuits using sequential statements, for all input signals are present in the sensitivity list and all input/output combinations are specified. Finally, observe that no signal assignment is made at the transition of another signal, so no flip-flops will be inferred (section 7.5). State Machine Template for Design Style #1 A complete template is shown below. Notice that, in addition to the two processes presented above, it also contains a user-defined enumerated data type (here called state), which lists all possible states of the machine. TLFeBOOK State Machines 163 LIBRARY ieee; USE ieee.std_logic_1164.all; ----------------------------------------------------ENTITY <entity_name> IS PORT ( input: IN <data_type>; reset, clock: IN STD_LOGIC; output: OUT <data_type>); END <entity_name>; ----------------------------------------------------ARCHITECTURE <arch_name> OF <entity_name> IS TYPE state IS (state0, state1, state2, state3, ...); SIGNAL pr_state, nx_state: state; BEGIN ---------- Lower section: -----------------------PROCESS (reset, clock) BEGIN IF (reset='1') THEN pr_state <= state0; ELSIF (clock'EVENT AND clock='1') THEN pr_state <= nx_state; END IF; END PROCESS; ---------- Upper section: -----------------------PROCESS (input, pr_state) BEGIN CASE pr_state IS WHEN state0 => IF (input = ...) THEN output <= <value>; nx_state <= state1; ELSE ... END IF; WHEN state1 => IF (input = ...) THEN output <= <value>; nx_state <= state2; ELSE ... END IF; WHEN state2 => IF (input = ...) THEN output <= <value>; nx_state <= state3; ELSE ... END IF; ... END CASE; END PROCESS; END <arch_name>; TLFeBOOK 164 Chapter 8 one two three (0010) (0011) (0001) rst four (0100) zero five (0000) (0101) nine six (1001) (0110) eight seven (1000) (0111) Figure 8.2 States diagram of example 8.1. Example 8.1: BCD Counter A counter is an example of Moore machine, for the output depends only on the stored (present) state. As a simple registered circuit and as a sequencer, it can be easily implemented in either approach: conventional (as we have already done in previous chapters) or FSM type. The problem with the latter is that when the number of states is large it becomes cumbersome to enumerate them all, a problem easily avoided using the LOOP statement in a conventional approach. The state diagram of a 0-to-9 circular counter is shown in figure 8.2. The states were called zero, one, . . . , nine, each name corresponding to the decimal value of the output. A VHDL code, directly resembling the design style #1 template, is presented below. An enumerated data type (state) appears in lines 11–12. The design of the lower (clocked) section is presented in lines 16–23, and that of the upper (combinational) section, in lines 25–59. In this example, the number of registers is dlog2 10e ¼ 4. Simulation results are shown in figure 8.3. As can be seen, the output (count) grows from 0 to 9, and then restarts from 0 again. 1 2 3 4 5 ------------------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; ------------------------------------------------ENTITY counter IS TLFeBOOK State Machines 165 Figure 8.3 Simulation results of example 8.1. 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 PORT ( clk, rst: IN STD_LOGIC; count: OUT STD_LOGIC_VECTOR (3 DOWNTO 0)); END counter; ------------------------------------------------ARCHITECTURE state_machine OF counter IS TYPE state IS (zero, one, two, three, four, five, six, seven, eight, nine); SIGNAL pr_state, nx_state: state; BEGIN ------------- Lower section: ----------------PROCESS (rst, clk) BEGIN IF (rst='1') THEN pr_state <= zero; ELSIF (clk'EVENT AND clk='1') THEN pr_state <= nx_state; END IF; END PROCESS; ------------- Upper section: ----------------PROCESS (pr_state) BEGIN CASE pr_state IS WHEN zero => count <= "0000"; nx_state <= one; WHEN one => count <= "0001"; nx_state <= two; TLFeBOOK 166 Chapter 8 34 WHEN two => 35 count <= "0010"; 36 nx_state <= three; 37 WHEN three => 38 count <= "0011"; 39 nx_state <= four; 40 WHEN four => 41 count <= "0100"; 42 nx_state <= five; 43 WHEN five => 44 count <= "0101"; 45 nx_state <= six; 46 WHEN six => 47 count <= "0110"; 48 nx_state <= seven; 49 WHEN seven => 50 count <= "0111"; 51 nx_state <= eight; 52 WHEN eight => 53 count <= "1000"; 54 nx_state <= nine; 55 WHEN nine => 56 count <= "1001"; 57 nx_state <= zero; 58 END CASE; 59 END PROCESS; 60 END state_machine; 61 ------------------------------------------------- Example 8.2: Simple FSM #1 Figure 8.4 shows the states diagram of a very simple FSM. The system has two states (stateA and stateB), and must change from one to the other every time d ¼ ‘1’ is received. The desired output is x ¼ a when the machine is in stateA, or x ¼ b when in stateB. The initial (reset) state is stateA. A VHDL code for this circuit, employing design style #1, is shown below. 1 2 ---------------------------------------------ENTITY simple_fsm IS TLFeBOOK State Machines 167 d=1 a b FSM x d=0 stateA stateB (x=a) (x=b) d=0 d clk rst rst d=1 Figure 8.4 State machine of example 8.1. 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 PORT ( a, b, d, clk, rst: IN BIT; x: OUT BIT); END simple_fsm; ---------------------------------------------ARCHITECTURE simple_fsm OF simple_fsm IS TYPE state IS (stateA, stateB); SIGNAL pr_state, nx_state: state; BEGIN ----- Lower section: ---------------------PROCESS (rst, clk) BEGIN IF (rst='1') THEN pr_state <= stateA; ELSIF (clk'EVENT AND clk='1') THEN pr_state <= nx_state; END IF; END PROCESS; ---------- Upper section: ----------------PROCESS (a, b, d, pr_state) BEGIN CASE pr_state IS WHEN stateA => x <= a; IF (d='1') THEN nx_state <= stateB; ELSE nx_state <= stateA; END IF; WHEN stateB => x <= b; TLFeBOOK 168 Chapter 8 Figure 8.5 Simulation results of example 8.2 31 IF (d='1') THEN nx_state <= stateA; 32 ELSE nx_state <= stateB; 33 END IF; 34 END CASE; 35 END PROCESS; 36 END simple_fsm; 37 ---------------------------------------------- Simulation results relative to the code above are shown in figure 8.5. Notice that the circuit works as expected. Indeed, looking at the report files, one will verify that, as expected, only one flip-flop was required to implement this circuit because there are only two states to be encoded. Notice also that the upper section is indeed combinational, for the output (x), which in this case does depend on the inputs (a or b, depending on which state the machine is in), varies when a or b vary, regardless of clk. If a synchronous output were required, then design style #2 should be employed. 8.3 Design Style #2 (Stored Output) As we have seen, in design style #1 only pr_state is stored. Therefore, the overall circuit can be summarized as in figure 8.6(a). Notice that in this case, if it is a Mealy machine (one whose output is dependent on the current input), the output might change when the input changes (asynchronous output). In many applications, the signals are required to be synchronous, so the output should be updated only when the proper clock edge occurs. To make Mealy machines synchronous, the output must be stored as well, as shown in figure 8.6(b). This structure is the object of design style #2. To implement this new structure, very few modifications are needed. For example, we can use an additional signal (say, temp) to compute the output value (upper sec- TLFeBOOK State Machines 169 Logic gates input Logic gates output Flip-flops output input Flip-flops Flip-flops (a) (b) Figure 8.6 Circuit diagrams for (a) Design Style #1 and (b) Design Style #2. tion), but only pass its value to the actual output signal when a clock event occurs (lower section). These modifications can be observed in the template shown below. State Machine Template for Design Style #2 LIBRARY ieee; USE ieee.std_logic_1164.all; ------------------------------------------------------ENTITY <ent_name> IS PORT (input: IN <data_type>; reset, clock: IN STD_LOGIC; output: OUT <data_type>); END <ent_name>; ------------------------------------------------------ARCHITECTURE <arch_name> OF <ent_name> IS TYPE states IS (state0, state1, state2, state3, ...); SIGNAL pr_state, nx_state: states; SIGNAL temp: <data_type>; BEGIN ---------- Lower section: -------------------------PROCESS (reset, clock) BEGIN IF (reset='1') THEN pr_state <= state0; ELSIF (clock'EVENT AND clock='1') THEN output <= temp; pr_state <= nx_state; END IF; END PROCESS; TLFeBOOK 170 Chapter 8 ---------- Upper section: -------------------------PROCESS (pr_state) BEGIN CASE pr_state IS WHEN state0 => temp <= <value>; IF (condition) THEN nx_state <= state1; ... END IF; WHEN state1 => temp <= <value>; IF (condition) THEN nx_state <= state2; ... END IF; WHEN state2 => temp <= <value>; IF (condition) THEN nx_state <= state3; ... END IF; ... END CASE; END PROCESS; END <arch_name>; Comparing the template of design style #2 with that of design style #1, we verify that the only di¤erences are those related to the introduction of the internal signal temp. This signal will cause the output of the state machine to be stored, for its value is passed to the output only when clk’EVENT occurs. Example 8.3: Simple FSM #2 Let us consider the design of example 8.2 once again. However, let us say that now we want the output to be synchronous (to change only when clock rises). Since this is a Mealy machine, design style #2 is required. 1 2 3 4 5 6 7 8 9 10 ---------------------------------------------ENTITY simple_fsm IS PORT ( a, b, d, clk, rst: IN BIT; x: OUT BIT); END simple_fsm; ---------------------------------------------ARCHITECTURE simple_fsm OF simple_fsm IS TYPE state IS (stateA, stateB); SIGNAL pr_state, nx_state: state; SIGNAL temp: BIT; TLFeBOOK State Machines 171 11 BEGIN 12 ----- Lower section: ---------------------13 PROCESS (rst, clk) 14 BEGIN 15 IF (rst='1') THEN 16 pr_state <= stateA; 17 ELSIF (clk'EVENT AND clk='1') THEN 18 x <= temp; 19 pr_state <= nx_state; 20 END IF; 21 END PROCESS; 22 ---------- Upper section: ----------------23 PROCESS (a, b, d, pr_state) 24 BEGIN 25 CASE pr_state IS 26 WHEN stateA => 27 temp <= a; 28 IF (d='1') THEN nx_state <= stateB; 29 ELSE nx_state <= stateA; 30 END IF; 31 WHEN stateB => 32 temp <= b; 33 IF (d='1') THEN nx_state <= stateA; 34 ELSE nx_state <= stateB; 35 END IF; 36 END CASE; 37 END PROCESS; 38 END simple_fsm; 39 ---------------------------------------------- Looking at the report files produced by the compiler, we observe that two flip-flops were now inferred, one to encode the states of the machine, and the other to store the output. Simulation results are shown in figure 8.7. Recall that when a signal is stored, its value will necessarily remain static between two consecutive clock edges. Therefore, if the input (a or b in the example above) changes during this interval, the change might not be observed by the circuit; moreover, when observed, it will be delayed with respect to the input (which is proper of synchronous circuits). TLFeBOOK 172 Chapter 8 Figure 8.7 Simulation results of example 8.3. d=0 d=1 rst zero one (q=0) (q=0) d=0 d=1 d=0 d=0 three two (q=1) (q=0) d=1 d=1 Figure 8.8 States diagram for example 8.4. Example 8.4: String Detector We want to design a circuit that takes as input a serial bit stream and outputs a ‘1’ whenever the sequence ‘‘111’’ occurs. Overlaps must also be considered, that is, if . . . 0111110 . . . occurs, than the output should remain active for three consecutive clock cycles. The state diagram of our machine is shown in figure 8.8. There are four states, which we called zero, one, two, and three, with the name corresponding to the number of consecutive ‘1’s detected. The solution shown below utilizes design style #1. TLFeBOOK State Machines 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 173 -------------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; -------------------------------------------ENTITY string_detector IS PORT ( d, clk, rst: IN BIT; q: OUT BIT); END string_detector; -------------------------------------------ARCHITECTURE my_arch OF string_detector IS TYPE state IS (zero, one, two, three); SIGNAL pr_state, nx_state: state; BEGIN ----- Lower section: -------------------PROCESS (rst, clk) BEGIN IF (rst='1') THEN pr_state <= zero; ELSIF (clk'EVENT AND clk='1') THEN pr_state <= nx_state; END IF; END PROCESS; ---------- Upper section: --------------PROCESS (d, pr_state) BEGIN CASE pr_state IS WHEN zero => q <= '0'; IF (d='1') THEN nx_state <= one; ELSE nx_state <= zero; END IF; WHEN one => q <= '0'; IF (d='1') THEN nx_state <= two; ELSE nx_state <= zero; END IF; WHEN two => q <= '0'; IF (d='1') THEN nx_state <= three; TLFeBOOK 174 Chapter 8 40 ELSE nx_state <= zero; 41 END IF; 42 WHEN three => 43 q <= '1'; 44 IF (d='0') THEN nx_state <= zero; 45 ELSE nx_state <= three; 46 END IF; 47 END CASE; 48 END PROCESS; 49 END my_arch; 50 -------------------------------------------- Notice that in this example the output does not depend on the current input. This fact can be observed in lines 28, 33, 38, and 43 of the code above, which show that all assignments to q are unconditional (that is, do not depend on d). Therefore, the output is automatically synchronous (a Moore machine), so the use of design style #2 is unnecessary. The circuit requires two flip-flops, which encode the four states of the state machine, from which q is computed. Simulation results are shown in figure 8.9. As can be seen, the data sequence d ¼ ‘‘011101100’’ was applied to the circuit, resulting the response q ¼ ‘‘000100000’’ at the output. Example 8.5: Tra‰c Light Controller (TLC) As mentioned earlier, digital controllers are good examples of circuits that can be e‰ciently implemented when modeled as state machines. In the present example, we want to design a TLC with the characteristics summarized in the table of figure 8.10, that is:  Three modes of operation: Regular, Test, and Standby. Regular mode: four states, each with an independent, programmable time, passed to the circuit by means of a CONSTANT.  Test mode: allows all pre-programmed times to be overwritten (by a manual switch) with a small value, such that the system can be easily tested during maintenance (1 second per state). This value should also be programmable and passed to the circuit using a CONSTANT.  Standby mode: if set (by a sensor accusing malfunctioning, for example, or a manual switch) the system should activate the yellow lights in both directions and remain so while the standby signal is active.   Assume that a 60 Hz clock (obtained from the power line itself ) is available. TLFeBOOK State Machines 175 Figure 8.9 Simulation results of example 8.4. R R Y Y G G State RG RY GR YR YY REGULAR Time timeRG (30s) timeRY (5s) timeGR (45s) timeYR (5s) --- Operation Mode TEST Time timeTEST (1s) timeTEST (1s) timeTEST (1s) timeTEST (1s) --- STANDBY Time --------Indefinite timeGR timeRY stby YY RY GR timeRY timeGR YR stby timeYR timeRG RG timeYR timeRG Figure 8.10 Specifications and states diagram (regular mode) for example 8.5. TLFeBOOK 176 Chapter 8 Here, design style #1 can be employed, as shown in the code below. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 ------------------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; ------------------------------------------------ENTITY tlc IS PORT ( clk, stby, test: IN STD_LOGIC; r1, r2, y1, y2, g1, g2: OUT STD_LOGIC); END tlc; ------------------------------------------------ARCHITECTURE behavior OF tlc IS CONSTANT timeMAX : INTEGER := 2700; CONSTANT timeRG : INTEGER := 1800; CONSTANT timeRY : INTEGER := 300; CONSTANT timeGR : INTEGER := 2700; CONSTANT timeYR : INTEGER := 300; CONSTANT timeTEST : INTEGER := 60; TYPE state IS (RG, RY, GR, YR, YY); SIGNAL pr_state, nx_state: state; SIGNAL time : INTEGER RANGE 0 TO timeMAX; BEGIN -------- Lower section of state machine: ---PROCESS (clk, stby) VARIABLE count : INTEGER RANGE 0 TO timeMAX; BEGIN IF (stby='1') THEN pr_state <= YY; count := 0; ELSIF (clk'EVENT AND clk='1') THEN count := count + 1; IF (count = time) THEN pr_state <= nx_state; count := 0; END IF; END IF; END PROCESS; TLFeBOOK State Machines 36 -------- Upper section of state machine: ---37 PROCESS (pr_state, test) 38 BEGIN 39 CASE pr_state IS 40 WHEN RG => 41 r1<='1'; r2<='0'; y1<='0'; y2<='0'; g1<='0'; 42 nx_state <= RY; 43 IF (test='0') THEN time <= timeRG; 44 ELSE time <= timeTEST; 45 END IF; 46 WHEN RY => 47 r1<='1'; r2<='0'; y1<='0'; y2<='1'; g1<='0'; 48 nx_state <= GR; 49 IF (test='0') THEN time <= timeRY; 50 ELSE time <= timeTEST; 51 END IF; 52 WHEN GR => 53 r1<='0'; r2<='1'; y1<='0'; y2<='0'; g1<='1'; 54 nx_state <= YR; 55 IF (test='0') THEN time <= timeGR; 56 ELSE time <= timeTEST; 57 END IF; 58 WHEN YR => 59 r1<='0'; r2<='1'; y1<='1'; y2<='0'; g1<='0'; 60 nx_state <= RG; 61 IF (test='0') THEN time <= timeYR; 62 ELSE time <= timeTEST; 63 END IF; 64 WHEN YY => 65 r1<='0'; r2<='0'; y1<='1'; y2<='1'; g1<='0'; 66 nx_state <= RY; 67 END CASE; 68 END PROCESS; 69 END behavior; 70 ---------------------------------------------------- 177 g2<='1'; g2<='0'; g2<='0'; g2<='0'; g2<='0'; The expected number of flip-flops required to implement this circuit is 15; three to store pr_state (the machine has five states, so three bits are needed to encode TLFeBOOK 178 Chapter 8 them), plus twelve for the counter (it is a 12-bit counter, for it must count up to timeMAX ¼ 2700). Simulation results are shown in figure 8.11. In order for the results to fit properly in the graphs, we adopted small time values, with all CONSTANTS equal to 3 except timeTEST, which was made equal to 1. Therefore, the system is expected to change state every three clock cycles when in Regular operation, or every clock cycle if in Test mode. These two cases can be observed in the first two graphs of figure 8.11, respectively. The third graph shows the Standby mode being activated. As expected, stby is asynchronous and has higher priority than test, causing the system to stay in state YY (state 4) while stby is active. The test signal, on the other hand, is synchronous, but does not need to wait for the current state timing to finish to be activated, as can be observed in the second graph. Example 8.6: Signal Generator We want to design a circuit that, from a clock signal clk, gives origin to the signal outp shown in figure 8.12(a). Notice that the circuit must operate at both edges (rising and falling) of clk. To circumvent the two-edge aspect (section 6.9), one alternative is to implement two machines, one that operates exclusively at the positive transition of clk and another that operates exclusively at the negative edge, thus generating the intermediate signals out1 and out2 presented in figure 8.12(b). These signals can then be ANDed to give origin to the desired signal outp. Notice that this circuit has no external inputs (except for clk, of course), so the output can only change when clk changes (synchronous output). 1 2 3 4 5 6 7 8 9 10 11 12 ----------------------------------------ENTITY signal_gen IS PORT ( clk: IN BIT; outp: OUT BIT); END signal_gen; ----------------------------------------ARCHITECTURE fsm OF signal_gen IS TYPE state IS (one, two, three); SIGNAL pr_state1, nx_state1: state; SIGNAL pr_state2, nx_state2: state; SIGNAL out1, out2: BIT; BEGIN TLFeBOOK State Machines 179 Figure 8.11 Simulation results of example 8.5. TLFeBOOK 180 Chapter 8 clk outp (a) clk out1 st1 st2 st3 out2 st1 st2 st3 outp (b) Figure 8.12 Waveforms of example 8.6: (a) signal outp to be generated from clk and (b) intermediate signals out1 and out2 (outp ¼ out1 AND out2). 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 ----- Lower section of machine #1: --PROCESS(clk) BEGIN IF (clk'EVENT AND clk='1') THEN pr_state1 <= nx_state1; END IF; END PROCESS; ----- Lower section of machine #2: --PROCESS(clk) BEGIN IF (clk'EVENT AND clk='0') THEN pr_state2 <= nx_state2; END IF; END PROCESS; ---- Upper section of machine #1: ----PROCESS (pr_state1) BEGIN TLFeBOOK State Machines 181 30 CASE pr_state1 IS 31 WHEN one => 32 out1 <= '0'; 33 nx_state1 <= two; 34 WHEN two => 35 out1 <= '1'; 36 nx_state1 <= three; 37 WHEN three => 38 out1 <= '1'; 39 nx_state1 <= one; 40 END CASE; 41 END PROCESS; 42 ---- Upper section of machine #2: ----43 PROCESS (pr_state2) 44 BEGIN 45 CASE pr_state2 IS 46 WHEN one => 47 out2 <= '1'; 48 nx_state2 <= two; 49 WHEN two => 50 out2 <= '0'; 51 nx_state2 <= three; 52 WHEN three => 53 out2 <= '1'; 54 nx_state2 <= one; 55 END CASE; 56 END PROCESS; 57 outp <= out1 AND out2; 58 END fsm; 59 ------------------------------------------ Simulation results from the circuit synthesized with the code above are shown in figure 8.13. 8.4 Encoding Style: From Binary to OneHot To encode the states of a state machine, we can select one among several available styles. The default style is binary. Its advantage is that it requires the least number of TLFeBOOK 182 Chapter 8 Figure 8.13 Simulation results of example 8.6. Table 8.1 State encoding of an 8-state FSM. Encoding Style STATE BINARY TWOHOT ONEHOT state0 state1 state2 state3 state4 state5 state6 state7 000 001 010 011 100 101 110 111 00011 00101 01001 10001 00110 01010 10010 01100 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000 flip-flops. In this case, with n flip-flops (n bits), up to 2n states can be encoded. The disadvantage of this encoding scheme is that it requires more logic and is slower than the others. At the other extreme is the onehot encoding style, which uses one flip-flop per state. Therefore, it demands the largest number of flip-flops. In this case, with n flip-flops (n bits), only n states can be encoded. On the other hand, this approach requires the least amount of extra logic and is the fastest. An style that is inbetween the two styles above is the twohot encoding scheme, which presents two bits active per state. Therefore, with n flip-flops (n bits), up to n(n  1)/2 states can be encoded. The onehot style is recommended in applications where flip-flops are abundant, like in FPGAs (Field Programmable Gate Arrays). On the other hand, in ASICs (Application Specific Integrated Circuits) the binary style is generally preferred. As an example, say that our state machine has eight states. Then the encoding would be that shown table 8.1. The number of flip-flops required in each case is three (for binary), five (twohot), or eight (onehot). Other details are also presented in the table. TLFeBOOK State Machines 183 inp=0 rst state1 inp state2 inp=1 (outp=00) (outp=01) inp=0 inp=1 inp=0 inp=1 state4 state3 (outp=11) (outp=10) inp=1 inp=0 Figure P8.1 8.5 Problems Each solution to the problems proposed below should be accompanied by synthesis and simulation results. Verify, at least, the following: number of flip-flops inferred and circuit functionality. Problem 8.1: FSM Write a VHDL code that implements the FSM described by the states diagram of figure P8.1. Problem 8.2: Signal Generator #1 Using the FSM approach, design a circuit capable of generating the two signals depicted in figure P8.2 (out1, out2) from a clock signal clk. The signals are periodic and have the same period. However, while one changes only at the rising edge of clk, the other has changes at both edges. Problem 8.3: Signal Generator #2 Design a finite state machine capable of generating two signals, UP and DOWN, as illustrated in figure P8.3. These signals are controlled by two inputs, GO and STOP. When GO changes from ‘0’ to ‘1’, the output UP must go to ‘1’ too, but T ¼ 10 ms later. If GO returns to ‘0’, then UP must return to ‘0’ immediately. However, the output DOWN must now go to ‘1’, again 10 ms later, returning to ‘0’ immediately if TLFeBOOK 184 Chapter 8 clk out1 out2 1 period Figure P8.2 GO STOP UP Signal Generator DOWN clk STOP GO UP T DOWN T T Figure P8.3 GO changes to ‘1’. If the input STOP is asserted, then both outputs must go to ‘0’ immediately and unconditionally. Assume that a 10 kHz clock is available. Problem 8.4: Keypad Debouncer and Encoder Consider the keypad shown in the diagram of figure P8.4. A common way of reading a key press is by means of a technique called scanning or polling, which reduces the number of wires needed to interconnect the keypad to the main circuit. It consists of sending one column low at a time, while reading each row sequentially. If a key is pressed, then the corresponding row will be low, while the others remain high (due to the pull-up resistors). TLFeBOOK State Machines 185 VDD 1 2 3 inp1 data6 data1 data0 ... 4 5 6 inp2 7 8 9 inp3 * 0 # inp4 new_data outp inp digit 011 0111 1011 1101 1110 0111 1011 1101 1110 0111 1011 1101 1110 1 4 7 * 2 5 8 0 3 6 9 # 101 110 outp2 outp1 outp0 data ASCII 31h 34h 37h 2Ah 32h 35h 38h 30h 33h 36h 39h 23h Figure P8.4 TLFeBOOK 186 Chapter 8 Encoding: Each digit must be encoded using the ASCII code (7 bits, with the corresponding hexadecimal values listed in the table of figure P8.4). When a new reading is available at the output, the new_data bit should be set to ‘1’. This will avoid interpreting a key pressed for a long time as a long series of the same character. Debouncing: A problem inherent to mechanical switches is switch bounces, which occur before a firm contact is finally established. The settling generally takes up to a few milliseconds. Therefore, the choice of the clock frequency is very important. You are asked to choose it such that at least three readings occur in a 5 ms interval. Thus the new_data bit should be turned high only when the same result is obtained in all consecutive readings within a 5 ms interval. Problem 8.5: Tra‰c Light Controller Using your synthesis tool plus a CPLD/FPGA development kit, implement the TLC of example 8.5. Verify, in the report files generated by your software, which pins of the chip were assigned to the inputs (clk, stby, test) and to the outputs (r1, y1, g1, r2, y2, g2). Then make the following physical connections in your board: a 60 Hz square wave signal (from a signal generator), with the appropriate logic levels, to the clk pin.   a VDD/GND switch to pin stby.  a VDD/GND switch to pin test. an LED (red, if possible), with a 330-1kohm series resistor, to pin r1 (resistor connected between r1 and the anode of the LED, and cathode connected to GND).   an LED (yellow, if possible) to pin y1, with a series resistor like above.  an LED (green, if possible) to pin g1, with a series resistor like above.  finally, install other 3 LEDs, like those above, for r2, y2, and g2. Now download the program file from your PC to the development kit and verify the operation of the TLC. Play with the switches in order to test all modes of operation. You can also increase the clock frequency to speed up the transition from red to yellow, etc. Problem 8.6: Signal Generator #3 Solve problem 8.2 without using the finite state machine approach. Problem 8.7: Signal Generator #4 Solve example 8.6 without using the FSM approach. For further work is this area, see problems 9.3, 9.4, and 9.6 of chapter 9. TLFeBOOK 9 Additional Circuit Designs In the preceding chapters, we saw a series of complete design examples utilizing VHDL code. Each design included:  Top-level diagram of the circuit, with description;  Review of basic concepts whenever necessary;  Complete VHDL code;  Simulation results; and  Additional comments when needed. This chapter concludes Part I of the book. In it, a series of additional design examples are presented. These examples, like all the other designs shown so far, are also at the circuit level (that is, self-contained in the main code). In Part II, we will do the same; that is, we will conclude Part II with a chapter containing additional system design examples. The designs presented in this chapter are the following:  Barrel shifter (section 9.1)  Signed and unsigned comparators (section 9.2)  Carry ripple and carry look ahead adders (section 9.3)  Fixed-point division (section 9.4)  Vending machine controller (section 9.5)  Serial data receiver (section 9.6)  Parallel-to-serial converter (section 9.7)  Playing with a SSD (section 9.8)  Signal generators (section 9.9)  Memories (section 9.10)  Finally, a list of problems is also included (section 9.11). Note: A complete list of all designs presented in the book is shown in section 1.5. 9.1 Barrel Shifter The diagram of a barrel shifter is shown in figure 9.1. The input is an 8-bit vector. The output is a shifted version of the input, with the amount of shift defined by the ‘‘shift’’ input (from 0 to 7). The circuit consists of three individual barrel shifters, each similar to that seen in example 6.9. Notice that the first barrel has only one ‘0’ TLFeBOOK 188 Chapter 9 inp(7) MUX MUX inp(6) MUX outp(7) MUX outp(6) MUX outp(5) MUX outp(4) MUX outp(3) MUX outp(2) MUX outp(1) MUX outp(0) MUX MUX inp(5) MUX MUX inp(4) MUX MUX inp(3) MUX MUX inp(2) ‘0’ MUX MUX inp(1) ‘0’ MUX MUX inp(0) ‘0’ ‘0’ MUX MUX ‘0’ shift(0) shift(1) shift(2) ‘0’ ‘0’ Figure 9.1 Barrel shifter. TLFeBOOK Additional Circuit Designs 189 Figure 9.2 Simulation results from barrel shifter of figure 9.1. connected to one of the multiplexers (bottom left corner), while the second has two, and the third has four. For larger vectors, we would just keep doubling the number of ‘0’ inputs. If shift ¼ ‘‘001’’, for example, then only the first barrel should cause a shift; on the other hand, if shift ¼ ‘‘111’’, then all barrels should cause a shift. A VHDL code for the circuit of figure 9.1 is presented below. Simulation results, verifying the functionality of the circuit, are shown in figure 9.2. As can be seen in the latter, the output is equal to the input when shift ¼ 0 (that is, shift ¼ ‘‘000’’). It can also be seen that, as long as no bit of value ‘1’ is shifted out of the barrel, the output is equal to the input multiplied by 2 (1 shift) when shift ¼ 1 (‘‘001’’), multiplied by 4 (2 shifts) when shift ¼ 2 (‘‘010’’), multiplied by 8 (3 shifts) when shift ¼ 3 (‘‘011’’), and so on. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 --------------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; --------------------------------------------ENTITY barrel IS PORT ( inp: IN STD_LOGIC_VECTOR (7 DOWNTO 0); shift: IN STD_LOGIC_VECTOR (2 DOWNTO 0); outp: OUT STD_LOGIC_VECTOR (7 DOWNTO 0)); END barrel; --------------------------------------------ARCHITECTURE behavior OF barrel IS BEGIN PROCESS (inp, shift) VARIABLE temp1: STD_LOGIC_VECTOR (7 DOWNTO 0); VARIABLE temp2: STD_LOGIC_VECTOR (7 DOWNTO 0); BEGIN TLFeBOOK 190 Chapter 9 17 ---- 1st shifter ----18 IF (shift(0)='0') THEN 19 temp1 := inp; 20 ELSE 21 temp1(0) := '0'; 22 FOR i IN 1 TO inp'HIGH LOOP 23 temp1(i) := inp(i-1); 24 END LOOP; 25 END IF; 26 ---- 2nd shifter ----27 IF (shift(1)='0') THEN 28 temp2 := temp1; 29 ELSE 30 FOR i IN 0 TO 1 LOOP 31 temp2(i) := '0'; 32 END LOOP; 33 FOR i IN 2 TO inp'HIGH LOOP 34 temp2(i) := temp1(i-2); 35 END LOOP; 36 END IF; 37 ---- 3rd shifter ----38 IF (shift(2)='0') THEN 39 outp <= temp2; 40 ELSE 41 FOR i IN 0 TO 3 LOOP 42 outp(i) <= '0'; 43 END LOOP; 44 FOR i IN 4 TO inp'HIGH LOOP 45 outp(i) <= temp2(i-4); 46 END LOOP; 47 END IF; 48 END PROCESS; 49 END behavior; 50 --------------------------------------------- TLFeBOOK Additional Circuit Designs a (n:0) b (n:0) a>b a=b a<b 191 x1 x2 x3 Figure 9.3 Comparator. 9.2 Signed and Unsigned Comparators Figure 9.3 shows the top-level diagram of a comparator. The size of the vectors to be compared is generic (n þ 1). Three outputs must be provided: one corresponding to a > b, another to a ¼ b, and finally one relative to a < b. Three solutions are presented: the first considers a and b as signed numbers, while the other two consider them as unsigned values. Simulation results are also included. Signed Comparator Notice the presence of the std_logic_arith package in the code below (line 4), which is necessary to operate with SIGNED (or UNSIGNED) data types (a and b were declared as SIGNED numbers in line 8). 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ---- Signed Comparator: ---------------LIBRARY ieee; USE ieee.std_logic_1164.all; USE ieee.std_logic_arith.all; -- necessary! ---------------------------------------ENTITY comparator IS GENERIC (n: INTEGER := 7); PORT (a, b: IN SIGNED (n DOWNTO 0); x1, x2, x3: OUT STD_LOGIC); END comparator; ---------------------------------------ARCHITECTURE signed OF comparator IS BEGIN x1 <= '1' WHEN a > b ELSE '0'; x2 <= '1' WHEN a = b ELSE '0'; x3 <= '1' WHEN a < b ELSE '0'; END signed; ---------------------------------------- TLFeBOOK 192 Chapter 9 Figure 9.4 Simulation result of signed comparator of figure 9.3. Simulation results are shown in figure 9.4. As can be seen, 127 > 0, but 128 < 0 and also 255 < 0 (because in 2’s complement notation 127 is the decimal 127 itself, but 128 is the decimal 128, and 255 is indeed 1). Unsigned Comparator #1 The VHDL code below is the counterpart of the code just presented (signed comparator). Notice again the presence of the std_logic_arith package (line 4), which is necessary to operate with UNSIGNED (or SIGNED) data types (a and b were declared as UNSIGNED numbers in line 8). 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ---- Unsigned Comparator #1: ----------LIBRARY ieee; USE ieee.std_logic_1164.all; USE ieee.std_logic_arith.all; -- necessary! ---------------------------------------ENTITY comparator IS GENERIC (n: INTEGER := 7); PORT (a, b: IN UNSIGNED (n DOWNTO 0); x1, x2, x3: OUT STD_LOGIC); END comparator; ---------------------------------------ARCHITECTURE unsigned OF comparator IS BEGIN x1 <= '1' WHEN a > b ELSE '0'; x2 <= '1' WHEN a = b ELSE '0'; x3 <= '1' WHEN a < b ELSE '0'; END unsigned; ---------------------------------------- TLFeBOOK Additional Circuit Designs 193 Figure 9.5 Simulation result of unsigned comparator of figure 9.3. Unsigned Comparator #2 Unsigned comparators can also be implemented with STD_LOGIC_VECTORS, in which case there is no need to declare the std_logic_arith package. A solution of this kind is presented below. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ---- Unsigned Comparator #2: ----------LIBRARY ieee; USE ieee.std_logic_1164.all; ---------------------------------------ENTITY comparator IS GENERIC (n: INTEGER := 7); PORT (a, b: IN STD_LOGIC_VECTOR (n DOWNTO 0); x1, x2, x3: OUT STD_LOGIC); END comparator; ---------------------------------------ARCHITECTURE unsigned OF comparator IS BEGIN x1 <= '1' WHEN a > b ELSE '0'; x2 <= '1' WHEN a = b ELSE '0'; x3 <= '1' WHEN a < b ELSE '0'; END unsigned; ---------------------------------------- Simulation results (from either unsigned comparator) are shown in figure 9.5. Contrary to figure 9.4, now 128 and 255 are indeed bigger than zero. TLFeBOOK 194 Chapter 9 a(0) b(0) cin c(0) FAU a(1) b(1) c(1) s(0) FAU s(1) a(2) b(2) c(2) FAU a(3) b(3) c(3) s(2) FAU s(3) cout c(4) ab 00 01 10 11 00 01 10 11 cin 0 0 0 0 1 1 1 1 s cout 0 0 1 0 1 0 0 1 1 0 0 1 0 1 1 1 Figure 9.6 4-bit carry ripple adder and truth table of Full Adder Unit (FAU). 9.3 Carry Ripple and Carry Look Ahead Adders Carry ripple and carry look ahead are two classical approaches to the design of adders. The former has the advantage of requiring less hardware, while the latter is faster. Both approaches are discussed below. Carry Ripple Adder Figure 9.6 shows a 4-bit unsigned carry ripple adder. For each bit, a full adder unit (FAU, section 1.4) is employed. The truth table of the FAU is also shown. In it, a and b represent the input bits, cin is the carry-in bit, s is the sum bit, and cout is the carry-out bit. s must be high whenever the number of inputs that are high is odd (parity function), while cout must be high when two or more inputs are high (majority function). Notice in figure 9.6 that each FAU relies on the carry bit produced by the previous stage. This approach minimizes the size of the circuitry, at the expense of increased propagation delay. Based on the truth table of figure 9.6, a very simple way of computing s and cout is the following: s ¼ a XOR b XOR cin cout ¼ (a AND b) OR (a AND cin) OR (b AND cin) Therefore, a VHDL implementation of the carry ripple adder is straightforward. The solution shown below works for any number (n) of input bits, defined by means of a GENERIC statement in line 5. Simulation results from the circuit synthesized with the code below are shown in figure 9.7. 1 2 LIBRARY ieee; USE ieee.std_logic_1164.all; TLFeBOOK Additional Circuit Designs 195 Figure 9.7 Simulation results from the carry ripple adder of figure 9.6. 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 --------------------------------------------ENTITY adder_cripple IS GENERIC (n: INTEGER := 4); PORT ( a, b: IN STD_LOGIC_VECTOR (n-1 DOWNTO 0); cin: IN STD_LOGIC; s: OUT STD_LOGIC_VECTOR (n-1 DOWNTO 0); cout: OUT STD_LOGIC); END adder_cripple; --------------------------------------------ARCHITECTURE adder OF adder_cripple IS SIGNAL c: STD_LOGIC_VECTOR (n DOWNTO 0); BEGIN c(0) <= cin; G1: FOR i IN 0 TO n-1 GENERATE s(i) <= a(i) XOR b(i) XOR c(i); c(i+1) <= (a(i) AND b(i)) OR (a(i) AND c(i)) OR (b(i) AND c(i)); END GENERATE; cout <= c(n); END adder; --------------------------------------------- Pre-defined ‘‘B’’ Operator We have already seen that an adder can be implemented directly with the ‘‘þ’’ (addition) operator (section 4.1). In this case, a carry ripple type of solution will be normally implemented by the synthesizer. If, however, if we want the solution to be of a certain type (like the one presented next), then an explicit code must be written. TLFeBOOK 196 Chapter 9 s(0) s(2) s(3) a(0) b(0) a(1) b(1) a(2) b(2) a(3) b(3) PGU PGU PGU PGU p(0) g(0) p(1) g(1) p(2) g(2) p(3) g(3) c(1) cin s(1) c(0) c(2) CLAU c(3) c(4) cout Figure 9.8 4-bit carry look ahead adder. Carry Look Ahead Adder A diagram of a 4-bit carry look ahead adder is shown in figure 9.8. Its implementation is based on the generate and propagate concept, which gives the circuit higher speed than its carry ripple adder counterpart (at the expense of more silicon area). Consider two input bits, a and b. The generate (g) and propagate (p) signals are defined as: g ¼ a AND b p ¼ a XOR b Notice that such signals can be computed in advance, because neither depends on the carry bit. If we consider now two input vectors, a ¼ a(n  1) . . . a(1)a(0) and b ¼ b(n  1) . . . b(1)b(0), then the corresponding generate and propagate vectors are g ¼ g(n  1) . . . g(1)g(0) and p ¼ p(n  1) . . . p(1)p(0), where g( j) ¼ a( j) AND b( j) p( j) ¼ a( j) XOR b( j) Let us consider now the carry vector, c ¼ c(n  1) . . . c(1)c(0). The carry bits can be computed from g and p: c(0) C cin c(1) ¼ c(0)p(0) þ g(0) TLFeBOOK Additional Circuit Designs 197 c(2) ¼ c(0)p(0)p(1) þ g(0)p(1) þ g(1) c(3) ¼ c(0)p(0)p(1)p(2) þ g(0)p(1)p(2) þ g(1)p(2) þ g(2), etc. Notice that, contrary to the carry ripple adder, each carry bit above is computed independently; that is, none of the expressions above depends on preceding carry computations, and that is the reason why this circuit is faster. On the other hand, the hardware complexity grows very fast, limiting this approach to just a few bits (typically four). Larger carry look ahead adders can be implemented by associating such 4-bit-or-so units. The implementation of the adder of figure 9.8 is now straightforward. The PGU (Propagate—Generate Unit) computes p and g (four units are required), plus the actual sum (s), while the CLAU (Carry Look Ahead Unit) computes the carry bits. Note: In order to construct bigger carry look ahead adders, the CLAU block of figure 9.8 must posses Group Propagate (GP) and Group Generate (GG) outputs, which were omitted in the figure because this implementation is intended for four bits only. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 --------------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; --------------------------------------------ENTITY CLA_Adder IS PORT ( a, b: IN STD_LOGIC_VECTOR (3 DOWNTO 0); cin: IN STD_LOGIC; s: OUT STD_LOGIC_VECTOR (3 DOWNTO 0); cout: OUT STD_LOGIC); END CLA_Adder; --------------------------------------------ARCHITECTURE CLA_Adder OF CLA_Adder IS SIGNAL c: STD_LOGIC_VECTOR (4 DOWNTO 0); SIGNAL p: STD_LOGIC_VECTOR (3 DOWNTO 0); SIGNAL g: STD_LOGIC_VECTOR (3 DOWNTO 0); BEGIN ---- PGU: --------------------------------G1: FOR i IN 0 TO 3 GENERATE p(i) <= a(i) XOR b(i); g(i) <= a(i) AND b(i); s(i) <= p(i) XOR c(i); TLFeBOOK 198 Chapter 9 22 END GENERATE; 23 ---- CLAU: -------------------------------24 c(0) <= cin; 25 c(1) <= (cin AND p(0)) OR 26 g(0); 27 c(2) <= (cin AND p(0) AND p(1)) OR 28 (g(0) AND p(1)) OR 29 g(1); 30 c(3) <= (cin AND p(0) AND p(1) AND p(2)) OR 31 (g(0) AND p(1) AND p(2)) OR 32 (g(1) AND p(2)) OR 33 g(2); 34 c(4) <= (cin AND p(0) AND p(1) AND p(2) AND p(3)) OR 35 (g(0) AND p(1) AND p(2) AND p(3)) OR 36 (g(1) AND p(2) AND p(3)) OR 37 (g(2) AND p(3)) OR 38 g(3); 39 cout <= c(4); 40 END CLA_Adder; 41 --------------------------------------------- Qualitatively, the simulation results obtained from the circuit synthesized with the code above are similar to those from the carry ripple adder presented in figure 9.7. 9.4 Fixed-Point Division We saw in chapter 4 that the pre-defined ‘‘/’’ (division) operator accepts only power of two divisors, that is, it is indeed a ‘‘shift’’ operator. In this section, we will discuss the implementation of generic division, in which the dividend and divisor can be any integer. We start by describing the division algorithm, then we present two VHDL solutions followed by simulation results. Division Algorithm Say that we want to calculate y ¼ a/b, where a, b, and y have the same number (n þ 1) of bits. The algorithm is illustrated in figure 9.9, for a ¼ ‘‘1011’’ (decimal 11) and b ¼ ‘‘0011’’ (decimal 3), from which we expect y ¼ ‘‘0011’’ (decimal 3) and remainder ‘‘0010’’ (decimal 2). We first create a shifted version of b, whose length is 2n þ 1 bits (shown in the b-related column in figure 9.9). b_inp(i) is simply b shifted to the left by i positions (notice the underscored characters in the b-related column). TLFeBOOK Additional Circuit Designs 199 y (quotient) Operation on 1st column 0011000 0 none < 0001100 0 none > 0000110 1 a_inp(i)-b_inp(i) > 0000011 1 a_inp(i)-b_inp(i) Index a-related Comparison (i) input (a_inp) 3 1011 < 2 1011 1 1011 0 0101 b-related input (b_inp) 0010 (rem) Figure 9.9 Division algorithm. The computation of the quotient is performed as follows. Starting from the top of the table, we compare a_inp(i) with b_inp(i). If the former is bigger than or equal to the latter, than y(i) ¼ ‘1’ and b_inp(i) is subtracted from a_inp(i); otherwise, y(i) ¼ ‘0’ and we simply proceed to the next line. After n þ 1 iterations, the computation is completed and the value left in a_inp is the remainder. Note: It is obvious that, to subtract b_inp from a_inp, the number of bits of a_inp cannot be less than that of b_inp, so the actual length of a_inp must be increased, which is attained by simply filling a_inp with n ‘0’s on its left-hand side (‘0’s not shown in figure 9.9). Another way of presenting the division algorithm is the following. We multiply b by 2**n, where n þ 1 is the number of bits. This, of course, corresponds to shifting b n positions to the left, but without throwing out any of its bits (so the new b-vector must be n bits longer than the original vector). If a is bigger than the new b, then y(n) ¼ ‘1’, and b (the new value) must be subtracted from a; otherwise, y(n) ¼ ‘0’. Now we move to the next iteration. We multiply b (the original value) by 2**(n  1), which is equivalent to shifting the original vector n  1 positions to the left, or shifting the value of b just used in the previous computation back one position to the right. Then we compare it to a, as we did before, to decide whether y(n  1) should be ‘1’ or ‘0’, and so on. VHDL Dividers Below are two solutions for the division problem. Both use sequential code: IF is used in the first, while LOOP plus IF are employed in the second. The first solution is a step-by-step code, so the division algorithm described above can be clearly observed. The second is more compact and is also generic (notice that n was defined TLFeBOOK 200 Chapter 9 Figure 9.10 Simulation results of divider (for 4-bit operands). by means of a GENERIC statement in line 6). The solutions include also a b ¼ 0 check routine. Simulation results are shown in figure 9.10. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 ----- Solution 1: step-by-step ------------------LIBRARY ieee; USE ieee.std_logic_1164.all; -------------------------------------------------ENTITY divider IS PORT ( a, b: IN INTEGER RANGE 0 TO 15; y: OUT STD_LOGIC_VECTOR (3 DOWNTO 0); rest: OUT INTEGER RANGE 0 TO 15; err : OUT STD_LOGIC); END divider; -------------------------------------------------ARCHITECTURE rtl OF divider IS BEGIN PROCESS (a, b) VARIABLE temp1: INTEGER RANGE 0 TO 15; VARIABLE temp2: INTEGER RANGE 0 TO 15; BEGIN ----- Error and initialization: ------temp1 := a; temp2 := b; IF (b=0) THEN err <= '1'; ELSE err <= '0'; END IF; ----- y(3): --------------------------- TLFeBOOK Additional Circuit Designs 201 25 IF (temp1 >= temp2 * 8) THEN 26 y(3) <= '1'; 27 temp1 := temp1 - temp2*8; 28 ELSE y(3) <= '0'; 29 END IF; 30 ----- y(2): --------------------------31 IF (temp1 >= temp2 * 4) THEN 32 y(2) <= '1'; 33 temp1 := temp1 - temp2 * 4; 34 ELSE y(2) <= '0'; 35 END IF; 36 ----- y(1): --------------------------37 IF (temp1 >= temp2 * 2) THEN 38 y(1) <= '1'; 39 temp1 := temp1 - temp2 * 2; 40 ELSE y(1) <= '0'; 41 END IF; 42 ----- y(0): --------------------------43 IF (temp1 >= temp2) THEN 44 y(0) <= '1'; 45 temp1 := temp1 - temp2; 46 ELSE y(0) <= '0'; 47 END IF; 48 ----- Remainder: ---------------------49 rest <= temp1; 50 END PROCESS; 51 END rtl; 52 -------------------------------------------------1 2 3 4 5 6 7 8 9 ------ Solution 2: compact and generic ----------LIBRARY ieee; USE ieee.std_logic_1164.all; -------------------------------------------------ENTITY divider IS GENERIC(n: INTEGER := 3); PORT ( a, b: IN INTEGER RANGE 0 TO 15; y: OUT STD_LOGIC_VECTOR (3 DOWNTO 0); rest: OUT INTEGER RANGE 0 TO 15; TLFeBOOK 202 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 Chapter 9 err : OUT STD_LOGIC); END divider; -------------------------------------------------ARCHITECTURE rtl OF divider IS BEGIN PROCESS (a, b) VARIABLE temp1: INTEGER RANGE 0 TO 15; VARIABLE temp2: INTEGER RANGE 0 TO 15; BEGIN ----- Error and initialization: ------temp1 := a; temp2 := b; IF (b=0) THEN err <= '1'; ELSE err <= '0'; END IF; ----- y: -----------------------------FOR i IN n DOWNTO 0 LOOP IF(temp1 >= temp2 * 2**i) THEN y(i) <= '1'; temp1 := temp1 - temp2 * 2**I; ELSE y(i) <= '0'; END IF; END LOOP; ----- Remainder: ---------------------rest <= temp1; END PROCESS; END rtl; -------------------------------------------------- 9.5 Vending-Machine Controller In this example, we will design a controller for a vending machine, which sells candy bars for twenty-five cents. As seen in chapter 8, this is the type of design where the FSM (finite state machine) model is helpful. The inputs and outputs of the controller are shown in figure 9.11. The input signals nickel_in, dime_in, and quarter_in indicate that a corresponding coin has been deposited. Two additional inputs, clk (clock) and rst (reset), are also necessary. The TLFeBOOK Additional Circuit Designs 203 nickel_in candy_out dime_in Vendingmachine controller quarter_in nickel_out dime_out clk rst di di di di ni 5 10 ni 15 ni 20 ni ni qi 0 qi qi qi qi 25 co no+c 30 35 no 40 45 di do do+co Figure 9.11 Vending-machine controller (top-level and states diagrams). The signals are. ni ¼ nickel_in, di ¼ dime_in, qi ¼ quarter_in, no ¼ nickel_out, do ¼ dime_out, and co ¼ candy_out. TLFeBOOK 204 Chapter 9 controller responds with three outputs: candy_out, to dispense a candy bar, plus nickel_out and dime_out, asserted when change is due. Figure 9.11 also shows the states of the corresponding FSM. The numbers inside the circles represent the total amount deposited by the customer (only nickels, dimes, and quarters are accepted). State 0 is the idle state. From it, if a nickel is deposited, the machine moves to state 5; if a dime, to state 10; or if a quarter, to state 25. Similar situations are repeated for all states, up to state 20. If state 25 is reached, then a candy bar is dispensed, with no change. However, if state 40 is reached, for example, then a nickel is delivered, passing therefore the system to state 35, from which a dime is delivered and a candy bar dispensed. The three states marked with double circles are those from which a candy bar is delivered and the machine returns to state 0. This problem will be divided into two parts: in the first, the fundamental aspects related to the design of the vending machine controller (figure 9.11) are treated; in the second, additional (and indispensable) features are added. The first part is studied in this section, while the second is proposed as a problem (problem 9.3). The introduction of such additional features is necessary for safety reasons; since we are dealing with money, we must assure that none of the parts (machine or customer) will be hurt in the transaction. A VHDL code, treating only the basic features of the problem depicted in figure 9.11, is presented below. We have assumed that the additional features proposed in problem 9.3 will indeed be implemented, in which case glitches are acceptable in the first part of the solution. Therefore, design style #1 (section 8.2) can be employed. The enumerated type state (line 12) contains a list of all states shown in the FSM diagram of figure 9.11. There are ten states, so four bits are necessary to encode them (so four flip-flops will be inferred). Recall that the compiler encodes such states in the order that they are listed, so st0 ¼ ‘‘0000’’ (decimal 0), st5 ¼ ‘‘0001’’ (decimal 1), . . . , st45 ¼ ‘‘1001’’ (decimal 9). Therefore, in the simulations, such numbers are shown instead of the state names. 1 2 3 4 5 6 7 8 9 -----------------------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; -----------------------------------------------------ENTITY vending_machine IS PORT ( clk, rst: IN STD_LOGIC; nickel_in, dime_in, quarter_in: IN BOOLEAN; candy_out, nickel_out, dime_out: OUT STD_LOGIC); END vending_machine; TLFeBOOK Additional Circuit Designs 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 205 -----------------------------------------------------ARCHITECTURE fsm OF vending_machine IS TYPE state IS (st0, st5, st10, st15, st20, st25, st30, st35, st40, st45); SIGNAL present_state, next_state: STATE; BEGIN ---- Lower section of the FSM (Sec. 8.2): --------PROCESS (rst, clk) BEGIN IF (rst='1') THEN present_state <= st0; ELSIF (clk'EVENT AND clk='1') THEN present_state <= next_state; END IF; END PROCESS; ---- Upper section of the FSM (Sec. 8.2): --------PROCESS (present_state, nickel_in, dime_in, quarter_in) BEGIN CASE present_state IS WHEN st0 => candy_out <= '0'; nickel_out <= '0'; dime_out <= '0'; IF (nickel_in) THEN next_state <= st5; ELSIF (dime_in) THEN next_state <= st10; ELSIF (quarter_in) THEN next_state <= st25; ELSE next_state <= st0; END IF; WHEN st5 => candy_out <= '0'; nickel_out <= '0'; dime_out <= '0'; IF (nickel_in) THEN next_state <= st10; ELSIF (dime_in) THEN next_state <= st15; ELSIF (quarter_in) THEN next_state <= st30; ELSE next_state <= st5; END IF; TLFeBOOK 206 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 Chapter 9 WHEN st10 => candy_out <= '0'; nickel_out <= '0'; dime_out <= '0'; IF (nickel_in) THEN next_state <= st15; ELSIF (dime_in) THEN next_state <= st20; ELSIF (quarter_in) THEN next_state <= st35; ELSE next_state <= st10; END IF; WHEN st15 => candy_out <= '0'; nickel_out <= '0'; dime_out <= '0'; IF (nickel_in) THEN next_state <= st20; ELSIF (dime_in) THEN next_state <= st25; ELSIF (quarter_in) THEN next_state <= st40; ELSE next_state <= st15; END IF; WHEN st20 => candy_out <= '0'; nickel_out <= '0'; dime_out <= '0'; IF (nickel_in) THEN next_state <= st25; ELSIF (dime_in) THEN next_state <= st30; ELSIF (quarter_in) THEN next_state <= st45; ELSE next_state <= st20; END IF; WHEN st25 => candy_out <= '1'; nickel_out <= '0'; dime_out <= '0'; next_state <= st0; WHEN st30 => candy_out <= '1'; nickel_out <= '1'; dime_out <= '0'; next_state <= st0; TLFeBOOK Additional Circuit Designs 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 207 WHEN st35 => candy_out <= '1'; nickel_out <= '0'; dime_out <= '1'; next_state <= st0; WHEN st40 => candy_out <= '0'; nickel_out <= '1'; dime_out <= '0'; next_state <= st35; WHEN st45 => candy_out <= '0'; nickel_out <= '0'; dime_out <= '1'; next_state <= st35; END CASE; END PROCESS; END fsm; ------------------------------------------------------ Simulation results are presented in figure 9.12. As can be seen, three nickels and one quarter were deposited. Notice that, at the first positive clock edge after the first nickel was deposited, the FSM moves from state st0 (decimal 0) to st5 (decimal 1); Figure 9.12 Simulation results from the vending-machine controller. TLFeBOOK 208 Chapter 9 after de second nickel, to state st10 (decimal 2); after de third, to state st15 (decimal 3); and, after de quarter has been deposited, to state st40 (decimal 8). After that, a nickel is returned to the customer (nickel_out ¼ ‘1’), causing the FSM to move to state st35 (decimal 7), at which a dime is delivered (dime_out ¼ ‘1’) and a candy bar is dispensed (candy_out ¼ ‘1’). The system returns then to its idle state (st0). As mentioned above, additional features (like handshake) are necessary to increase the security of the transactions. Please refer to problem 9.3 for a continuation of this design. 9.6 Serial Data Receiver The diagram of a serial data receiver is shown in figure 9.13. It contains a serial data input, din, and a parallel data output, data(6:0). A clock signal is also needed at the input. Two supervision signals are generated by the circuit: err (error) and data_valid. The input train consists of ten bits. The first bit is a start bit, which, when high, must cause the circuit to start receiving data. The next seven are the actual data bits. The ninth bit is a parity bit, whose status must be ‘0’ if the number of ones in data is even, or ‘1’ otherwise. Finally, the tenth is a stop bit, which must be high if the transmission is correct. An error is detected when either the parity does not check or the stop bit is not a ‘1’. When reception is concluded and if no error has been detected, then the data stored in the internal registers (reg) is transferred to data(6:0) and the data_valid output is asserted. A VHDL code for this circuit is presented below. A few variables were used: count, to determine the number of bits received; reg, which stores the data; and temp, to compute the error. Notice in line 37 that reg(0) ¼ din was used instead of reg(0) ¼ ‘0’, because we want the time slot immediately after the stop bit to be considered as possibly containing a start bit for the next input train. data start din parity stop reg err data_valid clk data (0) (1) (2) (3) (4) (5) (6) Figure 9.13 Serial data receiver. TLFeBOOK Additional Circuit Designs 209 1 --------------------------------------------2 LIBRARY ieee; 3 USE ieee.std_logic_1164.all; 4 --------------------------------------------5 ENTITY receiver IS 6 PORT ( din, clk, rst: IN BIT; 7 data: OUT BIT_VECTOR (6 DOWNTO 0); 8 err, data_valid: OUT BIT); 9 END receiver; 10 --------------------------------------------11 ARCHITECTURE rtl OF receiver IS 12 BEGIN 13 PROCESS (rst, clk) 14 VARIABLE count: INTEGER RANGE 0 TO 10; 15 VARIABLE reg: BIT_VECTOR (10 DOWNTO 0); 16 VARIABLE temp : BIT; 17 BEGIN 18 IF (rst='1') THEN 19 count:=0; 20 reg := (reg'RANGE => '0'); 21 temp := '0'; 22 err <= '0'; 23 data_valid <= '0'; 24 ELSIF (clk'EVENT AND clk='1') THEN 25 IF (reg(0)='0' AND din='1') THEN 26 reg(0) := '1'; 27 ELSIF (reg(0)='1') THEN 28 count := count + 1; 29 IF (count < 10) THEN 30 reg(count) := din; 31 ELSIF (count = 10) THEN 32 temp := (reg(1) XOR reg(2) XOR reg(3) XOR 33 reg(4) XOR reg(5) XOR reg(6) XOR 34 reg(7) XOR reg(8)) OR NOT reg(9); 35 err <= temp; 36 count := 0; 37 reg(0) := din; 38 IF (temp = '0') THEN TLFeBOOK 210 Chapter 9 39 data_valid <= '1'; 40 data <= reg(7 DOWNTO 1); 41 END IF; 42 END IF; 43 END IF; 44 END IF; 45 END PROCESS; 46 END rtl; 47 ------------------------------------------------- Simulation results are presented in figure 9.14. The input sequence is din ¼ {start ¼ 1, din ¼ 0111001, parity ¼ 0, stop ¼ 1}. As can be seen in the upper graph, no error was detected in this case, because the parity and stop bits are correct. Hence, after count reaches 9, the data is made available, that is, data ¼ 0111001, from data(0) to data(6), which corresponds to the decimal 78, and the data_valid bit is Figure 9.14 Simulation results of serial data receivers. TLFeBOOK Additional Circuit Designs 211 asserted. Notice that the output remains so indefinitely, unless a new input train is received. The only di¤erence in the lower graph is that a start bit appears immediately after the stop bit. As can be seen, the count variable starts then to count and the whole process is repeated. 9.7 Parallel-to-Serial Converter A parallel-to-serial converter is a typical application of shift registers. It consists of sending out a block of data serially. The need for such converters arises, for example, in ASIC chips when there are not enough pins available to output all data bits simultaneously. A diagram of a parallel-to-serial converter is presented in figure 9.15. d(7:0) is the data vector to be sent out, while dout is the actual output. There are also two other inputs: clk and load. When load is asserted, d is synchronously stored in the shift register reg. While load stays high, the MSB, d(7), remains available at the output. Once load is returned to ‘0’, the subsequent bits are presented at the output at each positive edge of clk. After all eight bits have been sent out, the output remains low until the next transmission. 1 ------------------------------------------------2 LIBRARY ieee; 3 USE ieee.std_logic_1164.all; 4 ------------------------------------------------5 ENTITY serial_converter IS 6 PORT ( d: IN STD_LOGIC_VECTOR (7 DOWNTO 0); 7 clk, load: IN STD_LOGIC; 8 dout: OUT STD_LOGIC); 9 END serial_converter; 10 ------------------------------------------------- d(0) d(1) d(2) d(3) d(4) d(5) d(6) d(7) clk load reg dout Figure 9.15 Parallel-to-serial converter. TLFeBOOK 212 Chapter 9 Figure 9.16 Simulation results of parallel-to-serial converter. 11 12 13 14 15 16 17 18 19 20 21 22 23 24 ARCHITECTURE serial_converter OF serial_converter IS SIGNAL reg: STD_LOGIC_VECTOR (7 DOWNTO 0); BEGIN PROCESS (clk) BEGIN IF (clk'EVENT AND clk='1') THEN IF (load='1') THEN reg <= d; ELSE reg <= reg(6 DOWNTO 0) & '0'; END IF; END IF; END PROCESS; dout <= reg(7); END serial_converter; ------------------------------------------------- Simulation results from the circuit synthesized with the code above are shown in figure 9.16. d ¼ ‘‘11011011’’ (decimal 219) was chosen. As can be seen, d(7) ¼ ‘1’ is presented at the output at the first rising edge of clk after load has been asserted, staying there while load remains high (to illustrate this fact, load was kept high during two clock cycles). The other bits follow as soon as load returns to ‘0’. Notice that after all bits have been transmitted, the output stays low. 9.8 Playing with a Seven-Segment Display We want to design a little game with an SSD (seven-segment display). The top-level diagram of the circuit is shown in figure 9.17. It contains two inputs, clk and stop, and one output, dout(6:0), which feeds the SSD. Assume that fclk ¼ 1 kHz. TLFeBOOK Additional Circuit Designs 213 SSD a f clk e Little game stop b g d c x dout (6:0) Input: “xabcdefg” Figure 9.17 Playing with an SSD. Our circuit should cause a continuous clockwise movement of the SSD segments. Also, in order to make the circulatory movement more realistic, we want to momentarily overlap neighboring segments. Consequently, the sequence should be a ! ab ! b ! bc ! c ! cd ! d ! de ! e ! ef ! f ! fa ! a, with the combined states (ab, bc, etc.) lasting only a few milliseconds. If stop is asserted, then the circuit should return to state a and remain so until stop is turned low again. From chapter 8, it is clear that this is a circuit for which the FSM approach is appropriate. The states diagram is presented in figure 9.18. We want the system to remain in states a, b, c, etc. for time1 ¼ 80 ms, and in the combined states, ab, bc, etc., for time2 ¼ 30 ms. Therefore, a counter counting up to 80 (the clock period is 1 ms) or up to 30 can be employed to determine when to move to the next state. A VHDL solution is shown below. Notice that it is a straight implementation of the FSM template seen in section 8.2. In lines 11–12, time1 and time2 were declared as two constants. Small values (4 and 2, respectively) were here used in order for the simulation results to fit well in one plot, but 80 and 30, respectively, were used in the actual physical implementation. A signal called flip was used to switch from time1 to time2, and vice-versa. Notice that the corresponding decimals are marked beside each value of dout, so they can be easily verified in the simulation results. 1 2 3 4 5 6 -------------------------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; -------------------------------------------------------ENTITY ssd_game2 IS PORT ( clk, stop: IN BIT; TLFeBOOK 214 Chapter 9 time1 time2 time2 bc b c time1 ab cd time1 time2 a stop d time2 fa de time1 f e ef time2 time1 time2 time1 Figure 9.18 States diagram for the circuit of figure 9.17. 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 dout: OUT BIT_VECTOR (6 DOWNTO 0)); END ssd_game2; -------------------------------------------------------ARCHITECTURE fsm OF ssd_game2 IS CONSTANT time1: INTEGER := 4; -- actual value is 80 CONSTANT time2: INTEGER := 2; -- actual value is 30 TYPE states IS (a, ab, b, bc, c, cd, d, de, e, ef, f, fa); SIGNAL present_state, next_state: STATES; SIGNAL count: INTEGER RANGE 0 TO 5; SIGNAL flip: BIT; BEGIN ------- Lower section of FSM (Sec. 8.2): -----------PROCESS (clk, stop) BEGIN IF (stop='1') THEN present_state <= a; ELSIF (clk'EVENT AND clk='1') THEN IF ((flip='1' AND count=time1) OR (flip='0' AND count=time2)) THEN count <= 0; TLFeBOOK Additional Circuit Designs 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 215 present_state <= next_state; ELSE count <= count + 1; END IF; END IF; END PROCESS; ------- Upper section of FSM (Sec. 8.2): -----------PROCESS (present_state) BEGIN CASE present_state IS WHEN a => dout <= "1000000"; -- Decimal 64 flip<='1'; next_state <= ab; WHEN ab => dout <= "1100000"; -- Decimal 96 flip<='0'; next_state <= b; WHEN b => dout <= "0100000"; -- Decimal 32 flip<='1'; next_state <= bc; WHEN bc => dout <= "0110000"; -- Decimal 48 flip<='0'; next_state <= c; WHEN c => dout <= "0010000"; -- Decimal 16 flip<='1'; next_state <= cd; WHEN cd => dout <= "0011000"; -- Decimal 24 flip<='0'; next_state <= d; WHEN d => dout <= "0001000"; -- Decimal 8 flip<='1'; next_state <= de; TLFeBOOK 216 Chapter 9 64 WHEN de => 65 dout <= "0001100"; -- Decimal 12 66 flip<='0'; 67 next_state <= e; 68 WHEN e => 69 dout <= "0000100"; -- Decimal 4 70 flip<='1'; 71 next_state <= ef; 72 WHEN ef => 73 dout <= "0000110"; -- Decimal 6 74 flip<='0'; 75 next_state <= f; 76 WHEN f => 77 dout <= "0000010"; -- Decimal 2 78 flip<='1'; 79 next_state <= fa; 80 WHEN fa => 81 dout <= "1000010"; -- Decimal 66 82 flip<='0'; 83 next_state <= a; 84 END CASE; 85 END PROCESS; 86 END fsm; 87 -------------------------------------------------------- Simulation results are presented in figure 9.19. As can be seen, the system stays in the single states, a, b, etc., for four clock cycles (time1 ¼ 4 here) and in the combined states, ab, bc, etc., for two clock cycles (time2 ¼ 2). Observe also that the decimals detected by the simulator match the decimals listed in the VHDL code. Figure 9.19 Simulation results of little SSD game of figure 9.17. TLFeBOOK Additional Circuit Designs 9.9 217 Signal Generators Say that, from a clock signal (clk), we want to obtain the waveform shown in figure 9.20. In this kind of problem, we can use either the FSM approach or a conventional approach. Both kinds of solutions are illustrated below. FSM Approach The signal of figure 9.20 can be modeled as an 8-state FSM. Using a counter from 0 to 7, we can establish that wave ¼ ‘0’ (1st pulse) when count ¼ 0, wave ¼ ‘1’ (2nd pulse) when count ¼ 1, and so on, thus creating the signal shown in the figure. This implementation requires a total of four flip-flops: three to store count (three bits), plus one to store wave (one bit). Recall from chapter 8, sections 8.2–8.3, that the output of a FSM will only be registered if design style #2 is employed, which is necessary here, because glitches are not acceptable in a signal generator. The corresponding VHDL code, using dsign style #2 (section 8.3), is shown below. Simulation results appear in figure 9.21. Checking the report file created by the synthesis tool, we verify that a total of four flip-flops were indeed inferred from this code. 1 2 3 ----------------------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; clk wave 1 period Figure 9.20 Signal generator problem. Figure 9.21 Simulation results of signal generator (FSM approach). TLFeBOOK 218 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 Chapter 9 ----------------------------------------------------ENTITY signal_gen IS PORT (clk: IN STD_LOGIC; wave: OUT STD_LOGIC); END signal_gen; ----------------------------------------------------ARCHITECTURE fsm OF signal_gen IS TYPE states IS (zero, one, two, three, four, five, six, seven); SIGNAL present_state, next_state: STATES; SIGNAL temp: STD_LOGIC; BEGIN --- Lower section of FSM (Sec. 8.3): --PROCESS (clk) BEGIN IF (clk'EVENT AND clk='1') THEN present_state <= next_state; wave <= temp; END IF; END PROCESS; --- Upper section of FSM (Sec. 8.3): --PROCESS (present_state) BEGIN CASE present_state IS WHEN zero => temp<='0'; next_state <= one; WHEN one => temp<='1'; next_state <= two; WHEN two => temp<='0'; next_state <= three; WHEN three => temp<='1'; next_state <= four; WHEN four => temp<='1'; next_state <= five; WHEN five => temp<='1'; next_state <= six; WHEN six => temp<='0'; next_state <= seven; WHEN seven => temp<='0'; next_state <= zero; END CASE; END PROCESS; END fsm; ----------------------------------------------------- TLFeBOOK Additional Circuit Designs 219 Figure 9.22 Simulation results of signal generator (conventional approach). Conventional Approach A conventional design, with the IF statement, is shown next. Notice that count and wave are both assigned at the transition of another signal (clk). Therefore, according to what you saw in section 7.5, both will be stored (that is, four flip-flops will be inferred, three for count and one for wave). Simulation results are shown in figure 9.22. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 --------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; --------------------------------------ENTITY signal_gen1 IS PORT (clk: IN BIT; wave: OUT BIT); END signal_gen1; --------------------------------------ARCHITECTURE arch1 OF signal_gen1 IS BEGIN PROCESS VARIABLE count: INTEGER RANGE 0 TO 7; BEGIN WAIT UNTIL (clk'EVENT AND clk='1'); CASE count IS WHEN 0 => wave <= '0'; WHEN 1 => wave <= '1'; WHEN 2 => wave <= '0'; WHEN 3 => wave <= '1'; WHEN 4 => wave <= '1'; WHEN 5 => wave <= '1'; WHEN 6 => wave <= '0'; TLFeBOOK 220 Chapter 9 ROM word 0 word 1 addr data word 2 … Figure 9.23 ROM diagram. 24 WHEN 7 => wave <= '0'; 25 END CASE; 26 count := count + 1; 27 END PROCESS; 28 END arch1; 29 --------------------------------------- 9.10 Memory Design In this section, the design of the following memory circuits is presented:  ROM  RAM with separate in/out data buses  RAM with bidirectional in/out data bus ROM (Read Only Memory) Figure 9.23 shows the diagram of a ROM. Since it is a read-only memory, no clock signal or write-enable pin is necessary. As can be seen, the circuit contains a pile of pre-stored words, being the one selected by the address input (addr) presented at the output (data). In the code shown below, words (line 7) represents the number of words stored in the memory, while bits (line 6) represents the size of each word. To create a ROM, an array of CONSTANT values can be used (lines 15–22). First, a new TYPE, called vector_array, was defined (lines 13–14), which was then used in the declaration of a CONSTANT named memory (line 15). An 8  8 ROM is illustrated in this example, with the following (decimal) values stored in addresses 0 to 7: 0, 2, 4, 8, 16, 32, 64, and 128 (lines 15–22). Line 24 shows an example of call to the memory; the output (data) is equal to the word stored at address addr. When implementing a ROM, no TLFeBOOK Additional Circuit Designs 221 registers are inferred, because no signal assignment occurs at the transition of another signal. Logical gates, forming an LUT (lookup table), are used instead. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 --------------------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; --------------------------------------------------ENTITY rom IS GENERIC ( bits: INTEGER := 8; -- # of bits per word words: INTEGER := 8); -- # of words in the memory PORT ( addr: IN INTEGER RANGE 0 TO words-1; data: OUT STD_LOGIC_VECTOR (bits-1 DOWNTO 0)); END rom; --------------------------------------------------ARCHITECTURE rom OF rom IS TYPE vector_array IS ARRAY (0 TO words-1) OF STD_LOGIC_VECTOR (bits-1 DOWNTO 0); CONSTANT memory: vector_array := ( "00000000", "00000010", "00000100", "00001000", "00010000", "00100000", "01000000", "10000000"); BEGIN data <= memory(addr); END rom; --------------------------------------------------- Simulation results are shown in figure 9.24. As can be seen, the address changes from 0 to 7, then restarts from 0, with the outputs matching the values listed in the code above. RAM with Separate Input and Output Data Buses A RAM (Random Access Memory), with separate input and output data buses, is illustrated in figure 9.25. Indeed, this circuit was already discussed in example 6.11, but was repeated here to ease the comparison with the other memory circuits presented in this section. TLFeBOOK 222 Chapter 9 Figure 9.24 Simulation results from the 8  8 ROM code shown above. wr_ena RAM word 0 data_in word 1 wr_ena data_out q d word 2 addr … clk DFF clk wr_ena (a) (b) Figure 9.25 RAM with separate in/out data buses. As can be seen in figure 9.25(a), the circuit has a data input bus (data_in), a data output bus (data_out), an address bus (addr), plus clock (clk) and write enable (wr_ena) pins. When wr_enable is asserted, at the next rising edge of clk the vector present at data_in must be stored in the position specified by addr. data_out, on the other hand, must constantly display the data selected by addr. From the register point-of-view, the circuit can be summarized as in figure 9.25(b). When wr_ena is low, q is connected to the input of the flip-flop, and terminal d is open, so no new data will be written into the memory. However, when wr_ena is turned high, d is connected to the input of the register, so at the next rising edge of clk d will be stored. A VHDL code that implements the circuit of figure 9.25 is shown below. The chosen capacity was 16 words of length eight bits each. Notice that the code is totally generic. Simulation results are shown in figure 9.26. 1 2 --------------------------------------------------LIBRARY ieee; TLFeBOOK Additional Circuit Designs 223 Figure 9.26 Simulation results of 16  8 RAM with separate in/out data buses. 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 USE ieee.std_logic_1164.all; --------------------------------------------------ENTITY ram IS GENERIC ( bits: INTEGER := 8; -- # of bits per word words: INTEGER := 16); -- # of words in the -- memory PORT ( wr_ena, clk: IN STD_LOGIC; addr: IN INTEGER RANGE 0 TO words-1; data_in: IN STD_LOGIC_VECTOR (bits-1 DOWNTO 0); data_out: OUT STD_LOGIC_VECTOR (bits-1 DOWNTO 0)); END ram; --------------------------------------------------ARCHITECTURE ram OF ram IS TYPE vector_array IS ARRAY (0 TO words-1) OF STD_LOGIC_VECTOR (bits-1 DOWNTO 0); SIGNAL memory: vector_array; BEGIN PROCESS (clk, wr_ena) BEGIN IF (wr_ena='1') THEN IF (clk'EVENT AND clk='1') THEN memory(addr) <= data_in; END IF; END IF; END PROCESS; data_out <= memory(addr); END ram; --------------------------------------------------- TLFeBOOK 224 Chapter 9 RAM wr_ena word 0 addr word 1 bidir (d) q word 2 … clk DFF clk wr_ena (a) (b) Figure 9.27 RAM with bidirectional in/out data bus. Figure 9.28 Simulation results of 16  8 RAM with bidirectional in/out data bus. RAM with Bidirectional In/Out Data Bus A RAM with bidirectional in/out data bus is illustrated in figure 9.27. The overall structure is similar to that of figure 9.25, except for the fact that now the same bus (bidir) is used to write data into the memory as well to read data from it. From the register point-of-view, the circuit can be summarized as in figure 9.27(b). When wr_ena is low, the output of the register is connected to its input, so no change on the store data will occur. On the other hand, when wr_ena is asserted, q is connected to d, allowing new data to be stored at the next rising edge of clk. A VHDL code that implements the circuit of figure 9.27 is shown below. The chosen capacity was 16 words of length eight bits each. Notice that this code is also totally generic. Simulation results are shown in figure 9.28. TLFeBOOK Additional Circuit Designs 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 225 ------------------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; ------------------------------------------------ENTITY ram4 IS GENERIC ( bits: INTEGER := 8; -- # of bits per word words: INTEGER := 16); -- # of words in the -- memory PORT ( clk, wr_ena: IN STD_LOGIC; addr: IN INTEGER RANGE 0 TO words-1; bidir: INOUT STD_LOGIC_VECTOR (bits-1 DOWNTO 0)); END ram4; ------------------------------------------------ARCHITECTURE ram OF ram4 IS TYPE vector_array IS ARRAY (0 TO words-1) OF STD_LOGIC_VECTOR (bits-1 DOWNTO 0); SIGNAL memory: vector_array; BEGIN PROCESS (clk, wr_ena) BEGIN IF (wr_ena='0') THEN bidir <= memory(addr); ELSE bidir <= (OTHERS => 'Z'); IF (clk'EVENT AND clk='1') THEN memory(addr) <= bidir; END IF; END IF; END PROCESS; END ram; ------------------------------------------------- 9.11 Problems Problem 9.1: Barrel Shifter Why can we not replace the ARCHITECTURE of the barrel shifter presented in section 9.1 by that shown below, which is much shorter? TLFeBOOK 226 Chapter 9 --------------------------------------------ARCHITECTURE barrel OF barrel IS BEGIN PROCESS (inp, shift) BEGIN IF (shift=0) THEN outp <= inp; ELSE FOR i IN 0 TO shift-1 LOOP outp(i) <= '0'; END LOOP; FOR i IN shift TO inp'HIGH LOOP outp(i) <= inp(i-1); END LOOP; END IF; END PROCESS; END barrel; --------------------------------------------- Problem 9.2: Divider In section 9.4, we studied the design of fixed-point dividers. Two solutions were presented, both using sequential statements (IF and LOOP). Moreover, the codes implemented the second description of the division algorithm presented in that section. You are asked to write a concurrent solution for the division problem (with GENERATE). Additionally, your code should resemble the first description of the division algorithm (figure 9.9). In order to do so, we suggest the creation and use of the following types and signal: SUBTYPE long IS STD_LOGIC_VECTOR (2n DOWNTO 0); TYPE vec_array IS ARRAY (n DOWNTO 0) OF long; SIGNAL a_input, b_input: vec_array; where n should be declared as a GENERIC parameter. Problem 9.3: Vending-Machine Controller Consider the vending-machine controller designed in section 9.5. We want to introduce some sophistications in it. TLFeBOOK Additional Circuit Designs 227 (a) In order to provide the necessary security, introduce some kind of handshake between the controller and the external circuitry. As an example, the handshake could include the following: (i) an ‘‘input valid’’ signal (call it coin_valid), from the external circuit to the controller, informing that a new input is ready to be read. This signal should return to ‘0’ as soon as it has been processed by the controller, so a new input will only be considered by the controller at its rising edge. This is important to avoid possible confusion which may occur when nickel_in, dime_in, or quarter_in stays present at the input of the FSM for more than one clock cycle (so it will not be interpreted as a second coin, as in the design of section 9.5) (ii) an ‘‘input accepted’’ signal (call it coin_accepted), from the controller to the external circuit, informing that the present input has already been processed. Upon receiving this signal, the external circuit should cause coin_valid to return to ‘0’. (b) Consider that the nickel or the dime box in the vending machine might run out of coins. Design alternative return paths taking such possibilities into consideration. (Suggestion: simply include new arrows between st45 ! st40 and st35 ! st30 in the FSM diagram of figure 9.11). (c) Finally, consider the situation where a customer might continue depositing coins even when the necessary amount has already been reached. What should be done in such a situation? Problem 9.4: Serial Data Receiver Try to model and design the serial data receiver of section 9.6 utilizing the FSM (finite state machine) approach (chapter 8). Before you start writing you VHDL code, present a clear states diagram of the system. Problem 9.5: Serial Data Transmitter This problem is the counterpart of that treated in section 9.6. Here, the stored data must be transmitted serially. A diagram of the circuit is shown in figure P9.5. The data in start data_ready clk ‘1’ (0) parity stop (1) (2) (3) (4) (5) (6) p ‘1’ dout Figure P9.5 TLFeBOOK 228 Chapter 9 protocol is the same 10-bit structure of section 9.6; that is, a start bit (high), followed by seven bits of actual data, plus a parity bit, computed such that the total number of ‘1’s in positions 2 to 9 is even, and finally a stop bit (also ‘1’). Consider that a data_ready signal is available to inform when the data can be loaded into the registers and sent out. Problem 9.6: Playing with an SSD You are asked to introduce additional features in the little seven-segment display (SSD) game of section 9.8. (a) Add a 2-bit input, called ‘‘speed’’, which should be able to select four di¤erent speeds for the circulatory movement. Keep the overlap time (time2) fixed at 30 ms, changing only time1. Choose four di¤erent circulatory periods and physically verify whether the circuit behaves as expected. (b) Change the functionality of the stop input, such that instead of going to state when a stop is asserted, it freezes in whatever state it was when stop was activated, proceeding from there when stop returns to zero. (c) Finally, add a ‘‘direction’’ pin. When low, the circuit should behave as above, but when high, it should circulate in the opposite direction (counterclockwise). (d) Physical verification: After synthesizing and simulating your design, physically implement it in you PLD/FPGA development kit, following the steps below. (i) First, verify in the report file generated by the compiler which pins of the chip were assigned to the inputs (clock and switches) and to the outputs (SSD). (ii) Next, connect the signal generator (set to 1 kHz, with the appropriate logic levels, but leave it OFF while you make the connection) and the switches (which should provide VDD and GND levels) to the inputs of the circuit (your development kit board is normally equipped with test switches). (iii) Connect the outputs of the chip to the SSD (your development kit board is normally equipped with seven-segment displays). (iv) Finally, download the compiled file from your computer to the development kit, turn ON the signal generator, and verify the operation of your circuit. Play with the switches in order to test all operation modes. Problem 9.7: Speed Monitor Figure P9.7 shows a possible view of a car speed monitor. The specifications of the system are the following: TLFeBOOK Additional Circuit Designs 229 ON/OFF SPEED 35 45 55 60 65 70 75 80 Figure P9.7 Speed selection button (SPEED), which, at each touch, selects the next speed to be monitored (35, 45, 55, 60, 65, 70, 75, or 80 miles/hour).  Set of eight LEDs, one for each speed. The LED corresponding to the selected speed should be ON.  Two SSDs, which show the actual speed of the car. The car’s electronic speedometer provides a clock signal whose frequency is proportional to the speed. You may check the data sheet of the speedometer that you are going to use, or you can start with a simple round number, which you can provide with a signal generator to test your circuit (say, 100 Hz per mile/hour).  Buzzer, which emits alarm signals as the car approaches the selected speed. A 2 Hz signal should be emitted when the speed is three miles/hour or less from the selected speed, or a continuous alarm when at or above the selected speed. Consider a buzzer with internal oscillator, so only a DC signal must be provided in the latter case, or a square wave with frequency 2 Hz in the former case.  Write a VHDL code for such a circuit. Synthesize and simulate it. Finally, physically implement it in your PLD/FPGA development kit, using a signal generator for clock and following steps similar to those in problem 9.6. Problem 9.8: Random Number Generator Design a 1-digit random number generator. The number should be from ‘‘0000’’ (display ¼ 0) to ‘‘1111’’ (display ¼ F). Use the circuit of section 9.8, with a modified function for the stop switch. The SSD should remain in a circular motion until the switch is pressed. When pressed, a random number should be displayed, being the circular movement resumed at the next touch of the switch. After compiling and simulating your circuit, physically implement it in your PDD/FPGA development kit. TLFeBOOK TLFeBOOK II SYSTEM DESIGN TLFeBOOK TLFeBOOK 10 10.1 Packages and Components Introduction In Part I of the book, we studied the entire background and coding techniques of VHDL, which included the following:  Code structure: library declarations, entity, architecture (chapter 2)  Data types (chapter 3)  Operators and attributes (chapter 4)  Concurrent statements and concurrent code (chapter 5)  Sequential statements and sequential code (chapter 6)  Signals, variables, and constants (chapter 7)  Design of finite state machines (chapter 8)  Additional circuit designs (chapter 9) Thus, in terms of figure 10.1, we may say that we have covered in detail all that is needed to construct the type of code depicted on its left-hand side. A good understanding of that material is indispensable, regardless of the design being just a small circuit or a very large system. In Part II, we will simply add new building blocks to the material already presented. These new building blocks are intended mainly for library allocation, being shown on the right-hand side of figure 10.1. They are:  Packages (chapter 10)  Components (chapter 10)  Functions (chapter 11)  Procedures (chapter 11) These new units can be located in the main code itself (that is, on the left-hand side of figure 10.1). However, since their main purpose is to allow common pieces of code to be reused and shared, it is more usual to place them in a LIBRARY. This also leads to code partitioning, which is helpful when dealing with long codes. In summary, frequently used pieces of code can be written in the form of COMPONENTS, FUNCTIONS, or PROCEDURES, then placed in a PACKAGE, which is finally compiled into the destination LIBRARY. We have already seen (chapter 2) that at least three LIBRARIES are generally needed in a design: ieee, std, and work. After studying Part II, we will be able to construct our own libraries, which can then be added to the list above. TLFeBOOK 234 Chapter 10 LIBRARY Main code PACKAGE Library declarations COMPONENT FUNCTION ENTITY PROCEDURE ARCHITECTURE Figure 10.1 Fundamental units of VHDL code. 10.2 PACKAGE As mentioned above, frequently used pieces of VHDL code are usually written in the form of COMPONENTS, FUNCTIONS, or PROCEDURES. Such codes are then placed inside a PACKAGE and compiled into the destination LIBRARY. The importance of this technique is that it allows code partitioning, code sharing, and code reuse. We start by describing the structure of a PACKAGE. Besides COMPONENTS, FUNCTIONS, and PROCEDURES, it can also contain TYPE and CONSTANT definitions, among others. Its syntax is presented below. PACKAGE package_name IS (declarations) END package_name; [PACKAGE BODY package_name IS (FUNCTION and PROCEDURE descriptions) END package_name;] As can be seen, the syntax is composed of two parts: PACKAGE and PACKAGE BODY. The first part is mandatory and contains all declarations, while the second TLFeBOOK Packages and Components 235 part is necessary only when one or more subprograms (FUNCTION or PROCEDURE) are declared in the upper part, in which case it must contain the descriptions (bodies) of the subprograms. PACKAGE and PACKAGE BODY must have the same name. The declarations list can contain the following: COMPONENT, FUNCTION, PROCEDURE, TYPE, CONSTANT, etc. Example 10.1: Simple Package The example below shows a PACKAGE called my_package. It contains only TYPE and CONSTANT declarations, so a PACKAGE BODY is not necessary. 1 2 3 4 5 6 7 8 9 10 -----------------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; -----------------------------------------------PACKAGE my_package IS TYPE state IS (st1, st2, st3, st4); TYPE color IS (red, green, blue); CONSTANT vec: STD_LOGIC_VECTOR(7 DOWNTO 0) := "11111111"; END my_package; ------------------------------------------------ Example 10.2: Package with a Function This example contains, besides TYPE and CONSTANT declarations, a FUNCTION. Therefore, a PACKAGE BODY is now needed (details on how to write a FUNCTION will be seen in chapter 11). This function returns TRUE when a positive edge occurs on clk. 1 2 3 4 5 6 7 8 9 10 ------------------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; ------------------------------------------------PACKAGE my_package IS TYPE state IS (st1, st2, st3, st4); TYPE color IS (red, green, blue); CONSTANT vec: STD_LOGIC_VECTOR(7 DOWNTO 0) := "11111111"; FUNCTION positive_edge(SIGNAL s: STD_LOGIC) RETURN BOOLEAN; END my_package; TLFeBOOK 236 11 12 13 14 15 16 17 18 Chapter 10 ------------------------------------------------PACKAGE BODY my_package IS FUNCTION positive_edge(SIGNAL s: STD_LOGIC) RETURN BOOLEAN IS BEGIN RETURN (s'EVENT AND s='1'); END positive_edge; END my_package; ------------------------------------------------- Any of the PACKAGES above (example 10.1 or example 10.2) can now be compiled, becoming then part of our work LIBRARY (or any other). To make use of it in a VHDL code, we have to add a new USE clause to the main code (USE work.my_package.all), as shown below. -----------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; USE work.my_package.all; -----------------------------------ENTITY... ... ARCHITECTURE... ... ------------------------------------ 10.3 COMPONENT A COMPONENT is simply a piece of conventional code (that is, LIBRARY declarations þ ENTITY þ ARCHITECTURE, as seen in chapter 2). However, by declaring such code as being a COMPONENT, it can then be used within another circuit, thus allowing the construction of hierarchical designs. A COMPONENT is also another way of partitioning a code and providing code sharing and code reuse. For example, commonly used circuits, like flip-flops, multiplexers, adders, basic gates, etc., can be placed in a LIBRARY, so any project can make use of them without having to explicitly rewrite such codes. To use (instantiate) a COMPONENT, it must first be declared. The corresponding syntaxes are shown below. TLFeBOOK Packages and Components 237 COMPONENT declaration: COMPONENT component_name IS PORT ( port_name : signal_mode signal_type; port_name : signal_mode signal_type; ...); END COMPONENT; COMPONENT instantiation: label: component_name PORT MAP (port_list); As can be seen, the syntax of the declaration is similar to that of an ENTITY (section 2.3); that is, the names of the ports must be specified, along with their modes (IN, OUT, BUFFER, or INOUT) and data types (STD_LOGIC_VECTOR, INTEGER, BOOLEAN, etc.). To instantiate a component a label is required, followed by the component’s name and a PORT MAP declaration. Finally, port_list is just a list relating the ports of the actual circuit to the ports of the pre-designed component which is being instantiated. Example: Let us consider an inverter, which has been previously designed (inverter.vhd) and compiled into the work library. We can make use of it by means of the code shown below. The label chosen for this component was U1. The names of the ports in the actual circuit are x and y, which are being assigned to a and b, respectively, of the pre-designed inverter (this is called positional mapping, for the first signal in one corresponds to the first signal in the other, the second in one to the second in the other, and so on). ----- COMPONENT declaration: ----------COMPONENT inverter IS PORT (a: IN STD_LOGIC; b: OUT STD_LOGIC); END COMPONENT; ----- COMPONENT instantiation: ----------U1: inverter PORT MAP (x, y); There are two basic ways to declare a COMPONENT (figure 10.2). Once we have designed it and placed it in the destination LIBRARY, we can declare it in the TLFeBOOK 238 Chapter 10 main code itself, as shown in figure 10.2(a), or we can declare it using a PACKAGE, as in figure 10.2(b). The latter avoids the repetition of the declaration every time the COMPONENT is instantiated. Examples of both approaches are presented below. Example 10.3: Components Declared in the Main Code We want to implement the circuit of figure 10.3 employing only COMPONENTS (inverter, nand_2, and nand_3), but without creating a specific PACKAGE to declare them, thus as in figure 10.2(a). Then four pieces of VHDL code are needed: one for each component, plus one for the project (main code). All four files are shown below. Notice that, since we have not created a PACKAGE, the COMPONENTS must be declared in the main code (in the declarative part of the ARCHITECTURE). Simulation results are presented in figure 10.4. 1 2 3 4 5 6 7 8 9 10 11 12 13 ------ File inverter.vhd: ------------------LIBRARY ieee; USE ieee.std_logic_1164.all; -----------------------------------ENTITY inverter IS PORT (a: IN STD_LOGIC; b: OUT STD_LOGIC); END inverter; -----------------------------------ARCHITECTURE inverter OF inverter IS BEGIN b <= NOT a; END inverter; --------------------------------------------- 1 2 3 4 5 6 7 8 9 ------ File nand_2.vhd: --------------------LIBRARY ieee; USE ieee.std_logic_1164.all; -----------------------------------ENTITY nand_2 IS PORT (a, b: IN STD_LOGIC; c: OUT STD_LOGIC); END nand_2; -----------------------------------ARCHITECTURE nand_2 OF nand_2 IS TLFeBOOK Packages and Components 239 LIBRARY COMPONENT Inverter Main code Component Declarations ------------Component Instantiations COMPONENT Nand_2 COMPONENT Nand_3 LIBRARY COMPONENT Inverter PACKAGE COMPONENT Nand_2 Component Declarations Main code Component Instantiations COMPONENT Nand_3 Figure 10.2 Basic ways of declaring COMPONENTS: (a) declarations in the main code itself, (b) declarations in a PACKAGE. TLFeBOOK 240 Chapter 10 a x b c d y Figure 10.3 Circuit of example 10.3. 10 BEGIN 11 c <= NOT (a AND b); 12 END nand_2; 13 --------------------------------------------1 2 3 4 5 6 7 8 9 10 11 12 13 ----- File nand_3.vhd: ---------------------LIBRARY ieee; USE ieee.std_logic_1164.all; -----------------------------------ENTITY nand_3 IS PORT (a, b, c: IN STD_LOGIC; d: OUT STD_LOGIC); END nand_3; -----------------------------------ARCHITECTURE nand_3 OF nand_3 IS BEGIN d <= NOT (a AND b AND c); END nand_3; --------------------------------------------- 1 2 3 4 5 6 7 8 9 ----- File project.vhd: --------------------LIBRARY ieee; USE ieee.std_logic_1164.all; -----------------------------------ENTITY project IS PORT (a, b, c, d: IN STD_LOGIC; x, y: OUT STD_LOGIC); END project; ------------------------------------ TLFeBOOK Packages and Components 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 241 ARCHITECTURE structural OF project IS ------------COMPONENT inverter IS PORT (a: IN STD_LOGIC; b: OUT STD_LOGIC); END COMPONENT; ------------COMPONENT nand_2 IS PORT (a, b: IN STD_LOGIC; c: OUT STD_LOGIC); END COMPONENT; ------------COMPONENT nand_3 IS PORT (a, b, c: IN STD_LOGIC; d: OUT STD_LOGIC); END COMPONENT; ------------SIGNAL w: STD_LOGIC; BEGIN U1: inverter PORT MAP (b, w); U2: nand_2 PORT MAP (a, b, x); U3: nand_3 PORT MAP (w, c, d, y); END structural; --------------------------------------------- Example 10.4: Components Declared in a Package We want to implement the same project of the previous example (figure 10.3). However, we will now create a PACKAGE where all the COMPONENTS (inverter, nand_2, and nand_3) will be declared, like in figure 10.2(b). Thus now five pieces of VHDL code are needed: one for each component, one for the PACKAGE, and finally one for the project. Despite having an extra file (PACKAGE), such extra file needs to be created only once, thus avoiding the need to declare the components in the main code every time they are instantiated. Notice that an extra USE clause (USE work.my_components.all) is now necessary, in order to make the PACKAGE my_components visible to the design. The simulation results are obviously the same as those of figure 10.4. 1 2 3 4 ------ File inverter.vhd: ------------------LIBRARY ieee; USE ieee.std_logic_1164.all; ------------------------------------ TLFeBOOK 242 Chapter 10 Figure 10.4 Experimental results of example 10.3. 5 6 7 8 9 10 11 12 13 ENTITY inverter IS PORT (a: IN STD_LOGIC; b: OUT STD_LOGIC); END inverter; -----------------------------------ARCHITECTURE inverter OF inverter IS BEGIN b <= NOT a; END inverter; --------------------------------------------- 1 2 3 4 5 6 7 8 9 10 11 12 13 ------ File nand_2.vhd: --------------------LIBRARY ieee; USE ieee.std_logic_1164.all; -----------------------------------ENTITY nand_2 IS PORT (a, b: IN STD_LOGIC; c: OUT STD_LOGIC); END nand_2; -----------------------------------ARCHITECTURE nand_2 OF nand_2 IS BEGIN c <= NOT (a AND b); END nand_2; --------------------------------------------- 1 2 3 4 ----- File nand_3.vhd: ---------------------LIBRARY ieee; USE ieee.std_logic_1164.all; ------------------------------------ TLFeBOOK Packages and Components 5 6 7 8 9 10 11 12 13 ENTITY nand_3 IS PORT (a, b, c: IN STD_LOGIC; d: OUT STD_LOGIC); END nand_3; -----------------------------------ARCHITECTURE nand_3 OF nand_3 IS BEGIN d <= NOT (a AND b AND c); END nand_3; --------------------------------------------- 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ----- File my_components.vhd: --------------LIBRARY ieee; USE ieee.std_logic_1164.all; -----------------------PACKAGE my_components IS ------ inverter: ------COMPONENT inverter IS PORT (a: IN STD_LOGIC; b: OUT STD_LOGIC); END COMPONENT; ------ 2-input nand: --COMPONENT nand_2 IS PORT (a, b: IN STD_LOGIC; c: OUT STD_LOGIC); END COMPONENT; ------ 3-input nand: --COMPONENT nand_3 IS PORT (a, b, c: IN STD_LOGIC; d: OUT STD_LOGIC); END COMPONENT; -----------------------END my_components; --------------------------------------------- 1 2 3 4 5 6 7 8 ----- File project.vhd: --------------------LIBRARY ieee; USE ieee.std_logic_1164.all; USE work.my_components.all; --------------------------------ENTITY project IS PORT ( a, b, c, d: IN STD_LOGIC; x, y: OUT STD_LOGIC); 243 TLFeBOOK 244 9 10 11 12 13 14 15 16 17 18 Chapter 10 END project; --------------------------------ARCHITECTURE structural OF project IS SIGNAL w: STD_LOGIC; BEGIN U1: inverter PORT MAP (b, w); U2: nand_2 PORT MAP (a, b, x); U3: nand_3 PORT MAP (w, c, d, y); END structural; --------------------------------------------- 10.4 PORT MAP There are two ways to map the PORTS of a COMPONENT during its instantiation: positional mapping and nominal mapping. Let us consider the following example: COMPONENT inverter IS PORT (a: IN STD_LOGIC; b: OUT STD_LOGIC); END COMPONENT; ... U1: inverter PORT MAP (x, y); In it, the mapping is positional; that is, PORTS x and y correspond to a and b, respectively. On the other hand, a nominal mapping would be the following: U1: inverter PORT MAP (x=>a, y=>b); Positional mapping is easier to write, but nominal mapping is less error-prone. Ports can also be left unconnected (using the keyword OPEN). For example: U2: my_circuit PORT MAP (x=>a, y=>b, w=>OPEN, z=>d); 10.5 GENERIC MAP GENERIC units (discussed in section 4.5) can also be instantiated. In that case, a GENERIC MAP must be used in the COMPONENT instantiation to pass information to the GENERIC parameters. The new syntax is shown below. TLFeBOOK Packages and Components input (n-1:0) PARITY GENERATOR 245 output (n:0) Figure 10.5 Generic parity generator to be instantiated in example 10.5. label: compon_name GENERIC MAP (param. list) PORT MAP (port list); As can be seen, the only di¤erences from the syntax already presented are the inclusion of the word GENERIC and of a parameter list. The purpose is to inform that those parameters are to be considered as generic. The usage of GENERIC MAP is illustrated in the example below. Example 10.5: Instantiating a Generic Component Let us consider the generic parity generator of example 4.3 (repeated in figure 10.5), which adds one bit to the input vector (on its left-hand side). Such bit must be a ‘0’ if the number of ‘1’s in the input vector is even, or a ‘1’ if it is odd, such that the resulting vector will always contain an even number of ‘1’s. The code presented below is generic (that is, works for any positive integer n). Two files are shown: one relative to the COMPONENT (par_generator, which, indeed, we can assume as previously designed and available in the work library), and one relative to the project itself (main code), where the component par_generator is instantiated. Notice that the default value (n ¼ 7) of GENERIC in the COMPONENT file (parity_gen) will be overwritten by the value n ¼ 2 passed to it by means of the GENERIC MAP statement in the COMPONENT instantiation. Notice also that the GENERIC declaration that appears along with the COMPONENT declaration in the second file is necessary, for it is part of the original (the component’s) ENTITY. However, it is not necessary to declare its default value again. Simulation results from the circuit synthesized with the code below are shown in figure 10.6. 1 2 3 4 ------ File parity_gen.vhd (component): ------------LIBRARY ieee; USE ieee.std_logic_1164.all; ----------------------------------- TLFeBOOK 246 Chapter 10 Figure 10.6 Simulation results of example 10.5. 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 ENTITY parity_gen IS GENERIC (n : INTEGER := 7); -- default is 7 PORT ( input: IN BIT_VECTOR (n DOWNTO 0); output: OUT BIT_VECTOR (n+1 DOWNTO 0)); END parity_gen; ----------------------------------ARCHITECTURE parity OF parity_gen IS BEGIN PROCESS (input) VARIABLE temp1: BIT; VARIABLE temp2: BIT_VECTOR (output'RANGE); BEGIN temp1 := '0'; FOR i IN input'RANGE LOOP temp1 := temp1 XOR input(i); temp2(i) := input(i); END LOOP; temp2(output'HIGH) := temp1; output <= temp2; END PROCESS; END parity; ------------------------------------------------------ 1 2 3 4 5 6 7 ------ File my_code.vhd (actual project): -----------LIBRARY ieee; USE ieee.std_logic_1164.all; ----------------------------------ENTITY my_code IS GENERIC (n : POSITIVE := 2); -- 2 will overwrite 7 PORT ( inp: IN BIT_VECTOR (n DOWNTO 0); TLFeBOOK Packages and Components 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 247 outp: OUT BIT_VECTOR (n+1 DOWNTO 0)); END my_code; ----------------------------------ARCHITECTURE my_arch OF my_code IS -----------------------COMPONENT parity_gen IS GENERIC (n : POSITIVE); PORT (input: IN BIT_VECTOR (n DOWNTO 0); output: OUT BIT_VECTOR (n+1 DOWNTO 0)); END COMPONENT; -----------------------BEGIN C1: parity_gen GENERIC MAP(n) PORT MAP(inp, outp); END my_arch; ------------------------------------------------------ Example 10.6: ALU Made of COMPONENTS In example 5.5, the design of an ALU (Arithmetic Logic Unit) was presented (diagram repeated in figure 10.7). In that example, the code was self-contained (that is, no external COMPONENT, FUNCTION, or PROCEDURE was called). In the present example, however, we will assume that our library contains the three components (logic_unit, arith_unit, and mux) with which the ALU can be constructed. In the code shown below, besides the main code (alu.vhd), we have also included the design of the three components mentioned above. As can be seen, the COMPONENTS were declared in the main code itself. Simulation results are shown in figure 10.8, which are similar to those of example 5.5. 1 2 3 4 5 6 7 8 9 10 11 -------- COMPONENT arith_unit: -------------------LIBRARY ieee; USE ieee.std_logic_1164.all; USE ieee.std_logic_unsigned.all; ----------------------------------------ENTITY arith_unit IS PORT ( a, b: IN STD_LOGIC_VECTOR (7 DOWNTO 0); sel: IN STD_LOGIC_VECTOR (2 DOWNTO 0); cin: IN STD_LOGIC; x: OUT STD_LOGIC_VECTOR (7 DOWNTO 0)); END arith_unit; TLFeBOOK 248 a (7:0) b (7:0) Chapter 10 logic_unit y (7:0) mux arith_unit cin sel (3) sel (3:0) sel 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111 Operation y <= a y <= a+1 y <= a-1 y <= b y <= b+1 y <= b-1 y <= a+b y <= a+b+cin y <= NOT a y <= NOT b y <= a AND b y <= a OR b y <= a NAND b y <= a NOR b y <= a XOR b y <= a XNOR b Function Transfer a Increment a Decrement a Transfer b Increment b Decrement b Add a and b Add a and b with carry Complement a Complement b AND OR NAND NOR XOR XNOR Unit Arithmetic Logic Figure 10.7 ALU constructed from three COMPONENTS. TLFeBOOK Packages and Components 249 Figure 10.8 Simulation results of example 10.6. 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 ----------------------------------------ARCHITECTURE arith_unit OF arith_unit IS SIGNAL arith, logic: STD_LOGIC_VECTOR (7 DOWNTO 0); BEGIN WITH sel SELECT x <= a WHEN "000", a+1 WHEN "001", a-1 WHEN "010", b WHEN "011", b+1 WHEN "100", b-1 WHEN "101", a+b WHEN "110", a+b+cin WHEN OTHERS; END arith_unit; --------------------------------------------------- 1 2 3 4 5 6 7 8 9 10 11 12 -------- COMPONENT logic_unit: -------------------LIBRARY ieee; USE ieee.std_logic_1164.all; ----------------------------------------ENTITY logic_unit IS PORT ( a, b: IN STD_LOGIC_VECTOR (7 DOWNTO 0); sel: IN STD_LOGIC_VECTOR (2 DOWNTO 0); x: OUT STD_LOGIC_VECTOR (7 DOWNTO 0)); END logic_unit; ----------------------------------------ARCHITECTURE logic_unit OF logic_unit IS BEGIN TLFeBOOK 250 Chapter 10 13 WITH sel SELECT 14 x <= NOT a WHEN "000", 15 NOT b WHEN "001", 16 a AND b WHEN "010", 17 a OR b WHEN "011", 18 a NAND b WHEN "100", 19 a NOR b WHEN "101", 20 a XOR b WHEN "110", 21 NOT (a XOR b) WHEN OTHERS; 22 END logic_unit; 23 --------------------------------------------------1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 -------- COMPONENT mux: --------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; ----------------------------------------ENTITY mux IS PORT ( a, b: IN STD_LOGIC_VECTOR (7 DOWNTO 0); sel: IN STD_LOGIC; x: OUT STD_LOGIC_VECTOR (7 DOWNTO 0)); END mux; ----------------------------------------ARCHITECTURE mux OF mux IS BEGIN WITH sel SELECT x <= a WHEN '0', b WHEN OTHERS; END mux; --------------------------------------------------- 1 2 3 4 5 6 7 8 9 -------- Project ALU (main code): ----------------LIBRARY ieee; USE ieee.std_logic_1164.all; ----------------------------------------ENTITY alu IS PORT ( a, b: IN STD_LOGIC_VECTOR(7 DOWNTO 0); cin: IN STD_LOGIC; sel: IN STD_LOGIC_VECTOR(3 DOWNTO 0); y: OUT STD_LOGIC_VECTOR(7 DOWNTO 0)); TLFeBOOK Packages and Components 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 251 END alu; ----------------------------------------ARCHITECTURE alu OF alu IS ----------------------COMPONENT arith_unit IS PORT ( a, b: IN STD_LOGIC_VECTOR(7 DOWNTO 0); cin: IN STD_LOGIC; sel: IN STD_LOGIC_VECTOR(2 DOWNTO 0); x: OUT STD_LOGIC_VECTOR(7 DOWNTO 0)); END COMPONENT; ----------------------COMPONENT logic_unit IS PORT ( a, b: IN STD_LOGIC_VECTOR(7 DOWNTO 0); sel: IN STD_LOGIC_VECTOR(2 DOWNTO 0); x: OUT STD_LOGIC_VECTOR(7 DOWNTO 0)); END COMPONENT; ----------------------COMPONENT mux IS PORT ( a, b: IN STD_LOGIC_VECTOR(7 DOWNTO 0); sel: IN STD_LOGIC; x: OUT STD_LOGIC_VECTOR(7 DOWNTO 0)); END COMPONENT; ----------------------SIGNAL x1, x2: STD_LOGIC_VECTOR(7 DOWNTO 0); ----------------------BEGIN U1: arith_unit PORT MAP (a, b, cin, sel(2 DOWNTO 0), x1); U2: logic_unit PORT MAP (a, b, sel(2 DOWNTO 0), x2); U3: mux PORT MAP (x1, x2, sel(3), y); END alu; --------------------------------------------------- 10.6 Problems Problem 10.1: ALU with Components Declared in a Package Redo example 10.6. This time, create a PACKAGE containing all COMPONENT declarations. Then make the changes needed in the main code and recompile it. Synthesize and simulate your solution to fully verify its functionality. TLFeBOOK 252 Chapter 10 Problem 10.2: Carry Ripple Adder Constructed From Components Consider the carry ripple adder discussed in section 9.3 (figure 9.6). Design a FAU (full adder unit), to be used as a COMPONENT. Compile it into the work LIBRARY. Then write a code for the complete carry ripple adder containing instantiations of FAU. Compile your project and simulate the synthesized circuit, comparing the results with those obtained in section 9.3. Problem 10.3: Carry Look Ahead Adder Constructed from Components Consider now the carry look ahead adder of section 9.3 (figure 9.8). Design a PGU (propagate-generate unit) and a CLAU (carry look ahead unit), to be used as COMPONENTS. Compile them into the work LIBRARY. Then write a code for the complete carry look ahead adder containing instantiations of PGU and CLAU. You can choose whether to declare the COMPONENTS in a specific PACKAGE or in the main code itself (in the declarative part of the ARCHITECTURE). Compile your project and simulate the synthesized circuit, comparing the results with those obtained in section 9.3. Problem 10.4: Registered Counter Figure P10.4 illustrates the construction of a hierarchical design. Two sub-circuits (that is, ‘‘components’’), called counter and register, are used to construct a higherlevel circuit, called stop_watch. The system consists of a free-running counter, which is reset every time the stop input is asserted. The status of the counter must be stored in the sub-circuit register just before reset occurs. Once stop returns to ‘0’, the counter resumes counting (from zero), while the register holds the previous count. Design the two components of figure P10.4, then instantiate them in the main code to produce the complete stop_watch circuit. STOP_WATCH clk COUNTER REGISTER inp outp inp rst reg store stop Figure P10.4 TLFeBOOK 11 Functions and Procedures FUNCTIONS and PROCEDURES are collectively called subprograms. From a construction point of view, they are very similar to a PROCESS (studied in chapter 6), for they are the only pieces of sequential VHDL code, and thus employ the same sequential statements seen there (IF, CASE, and LOOP; WAIT is not allowed). However, from the applications point of view, there is a fundamental di¤erence between a PROCESS and a FUNCTION or PROCEDURE. While the first is intended for immediate use in the main code, the others are intended mainly for LIBRARY allocation, that is, their purpose is to store commonly used pieces of code, so they can be reused or shared by other projects. Nevertheless, if desired, a FUNCTION or PROCEDURE can also be installed in the main code itself. 11.1 FUNCTION A FUNCTION is a section of sequential code. Its purpose is to create new functions to deal with commonly encountered problems, like data type conversions, logical operations, arithmetic computations, and new operators and attributes. By writing such code as a FUNCTION, it can be shared and reused, also propitiating the main code to be shorter and easier to understand. As already mentioned, a FUNCTION is very similar to a PROCESS (section 6.1). The same statements that can be used in a process (IF, WAIT, CASE, and LOOP) can also be used in a function, with the exception of WAIT. Other two prohibitions in a function are SIGNAL declarations and COMPONENT instantiations. To construct and use a function, two parts are necessary: the function itself (function body) and a call to the function. Their syntaxes are shown below. Function Body FUNCTION function_name [<parameter list>] RETURN data_type IS [declarations] BEGIN (sequential statements) END function_name; In the syntax above, 3parameter list4 specifies the function’s input parameters, that is: 3parameter list4 ¼ [CONSTANT] constant_name: constant_type; or 3parameter list4 ¼ SIGNAL signal_name: signal_type; TLFeBOOK 254 Chapter 11 There can be any number of such parameters (even zero), which, as shown above, can only be CONSTANT (default) or SIGNAL (VARIABLES are not allowed). Their types can be any of the synthesizable data types studied in chapter 3 (BOOLEAN, STD_LOGIC, INTEGER, etc.). However, no range specification should be included (for example, do not enter RANGE when using INTEGER, or TO/ DOWNTO when using STD_LOGIC_VECTOR). On the other hand, there is only one return value, whose type is specified by data_type. Example: The function below, named f1, receives three parameters (a, b, and c). a and b are CONSTANTS (notice that the word CONSTANT can be omitted, for it is the default object), while c is a SIGNAL. a and b are of type INTEGER, while c is of type STD_LOGIC_VECTOR. Notice that neither RANGE nor DOWNTO was specified. The output parameter (there can be only one) is of type BOOLEAN. FUNCTION f1 (a, b: INTEGER; SIGNAL c: STD_LOGIC_VECTOR) RETURN BOOLEAN IS BEGIN (sequential statements) END f1; Function Call A function is called as part of an expression. The expression can obviously appear by itself or associated to a statement (either concurrent or sequential). Examples of function calls: x <= conv_integer(a); y <= maximum(a, b); IF x > maximum(a, b) ... Example 11.1: -------- converts a to an integer (expression appears by itself) returns the largest of a and b (expression appears by itself) compares x to the largest of a, b (expression associated to a statement) Function positive_edge( ) The FUNCTION below detects a positive (rising) clock edge. It is similar to the IF(clk’EVENT and clk ¼ ‘1’) statement. This function could be used, for example, in the implementation of a DFF. TLFeBOOK Functions and Procedures 255 ------ Function body: ------------------------------FUNCTION positive_edge(SIGNAL s: STD_LOGIC) RETURN BOOLEAN IS BEGIN RETURN (s'EVENT AND s='1'); END positive_edge; ------ Function call: ------------------------------... IF positive_edge(clk) THEN... ... ----------------------------------------------------- Example 11.2: Function conv_integer( ) The FUNCTION presented next converts a parameter of type STD_LOGIC_ VECTOR into an INTEGER. Notice that the code is generic, that is, it works for any range or order (TO/DOWNTO) of the input STD_LOGIC_VECTOR parameter. A typical call to the function is also shown. ------ Function body: ------------------------------FUNCTION conv_integer (SIGNAL vector: STD_LOGIC_VECTOR) RETURN INTEGER IS VARIABLE result: INTEGER RANGE 0 TO 2**vector'LENGTH-1; BEGIN IF (vector(vector'HIGH)='1') THEN result:=1; ELSE result:=0; END IF; FOR i IN (vector'HIGH-1) DOWNTO (vector'LOW) LOOP result:=result*2; IF(vector(i)='1') THEN result:=result+1; END IF; END LOOP; RETURN result; END conv_integer; ------ Function call: ------------------------------... y <= conv_integer(a); ... ----------------------------------------------------- TLFeBOOK 256 Chapter 11 PACKAGE LIBRARY (+ PACKAGE BODY) FUNCTION / PROCEDURE location ARCHITECTURE (declarative part) Main code ENTITY Figure 11.1 Typical locations of a FUNCTION or PROCEDURE. 11.2 Function Location The typical locations of a FUNCTION (or PROCEDURE) are depicted in figure 11.1. Though a FUNCTION is usually placed in a PACKAGE (for code partitioning, code reuse, and code sharing purposes), it can also be located in the main code (either inside the ARCHITECTURE or inside the ENTITY). When placed in a PACKAGE, then a PACKAGE BODY is necessary, which must contain the body of each FUNCTION (or PROCEDURE) declared in the declarative part of the PACKAGE. Examples of both cases are presented below. Example 11.3: FUNCTION Located in the Main Code Let us consider the positive_edge( ) function of example 11.1 As mentioned above, when installed in the main code itself, the function can be located either in the ENTITY or in the declarative part of the ARCHITECTURE. In the present example, the function appears in the latter, and is used to construct a DFF. 1 2 3 4 5 6 7 8 9 10 11 --------------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; --------------------------------------------ENTITY dff IS PORT ( d, clk, rst: IN STD_LOGIC; q: OUT STD_LOGIC); END dff; --------------------------------------------ARCHITECTURE my_arch OF dff IS ------------------------------------------ TLFeBOOK Functions and Procedures 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 257 FUNCTION positive_edge(SIGNAL s: STD_LOGIC) RETURN BOOLEAN IS BEGIN RETURN s'EVENT AND s='1'; END positive_edge; -----------------------------------------BEGIN PROCESS (clk, rst) BEGIN IF (rst='1') THEN q <= '0'; ELSIF positive_edge(clk) THEN q <= d; END IF; END PROCESS; END my_arch; --------------------------------------------- Example 11.4: FUNCTION Located in a PACKAGE This example is similar to example 11.3, with the only di¤erence being that the FUNCTION located in a PACKAGE can now be reused and shared by other projects. Notice that, when placed in a PACKAGE, the function is indeed declared in the PACKAGE, but described in the PACKAGE BODY. Below two VHDL codes are presented, being one relative to the construction of the FUNCTION / PACKAGE, while the other is an example where a call to the FUNCTION is made. The two codes can be compiled as two separate files, or can be compiled as a single file (saved as d¤.vhd, which is the ENTITY’s name). Notice the inclusion of ‘‘USE work.my_package.all;’’ in the main code (line 4). 1 2 3 4 5 6 7 8 9 10 11 ------- Package: ----------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; ---------------------------------------------PACKAGE my_package IS FUNCTION positive_edge(SIGNAL s: STD_LOGIC) RETURN BOOLEAN; END my_package; ---------------------------------------------PACKAGE BODY my_package IS FUNCTION positive_edge(SIGNAL s: STD_LOGIC) RETURN BOOLEAN IS TLFeBOOK 258 Chapter 11 12 BEGIN 13 RETURN s'EVENT AND s='1'; 14 END positive_edge; 15 END my_package; 16 ---------------------------------------------1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ------ Main code: ---------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; USE work.my_package.all; ---------------------------------------------ENTITY dff IS PORT ( d, clk, rst: IN STD_LOGIC; q: OUT STD_LOGIC); END dff; ---------------------------------------------ARCHITECTURE my_arch OF dff IS BEGIN PROCESS (clk, rst) BEGIN IF (rst='1') THEN q <= '0'; ELSIF positive_edge(clk) THEN q <= d; END IF; END PROCESS; END my_arch; ---------------------------------------------- Example 11.5: Function conv_integer( ) The conv_integer( ) function shown below was already seen in example 11.2; it converts a STD_LOGIC_VECTOR value into an INTEGER value. Below, the function was placed in a PACKAGE (plus PACKAGE BODY). A call to this function appears in the main code that follows the function implementation. 1 2 3 4 5 --------- Package: --------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; ---------------------------------------------PACKAGE my_package IS TLFeBOOK Functions and Procedures 259 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 FUNCTION conv_integer (SIGNAL vector: STD_LOGIC_VECTOR) RETURN INTEGER; END my_package; ---------------------------------------------PACKAGE BODY my_package IS FUNCTION conv_integer (SIGNAL vector: STD_LOGIC_VECTOR) RETURN INTEGER IS VARIABLE result: INTEGER RANGE 0 TO 2**vector'LENGTH-1; BEGIN IF (vector(vector'HIGH)='1') THEN result:=1; ELSE result:=0; END IF; FOR i IN (vector'HIGH-1) DOWNTO (vector'LOW) LOOP result:=result*2; IF(vector(i)='1') THEN result:=result+1; END IF; END LOOP; RETURN result; END conv_integer; END my_package; ---------------------------------------------- 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 -------- Main code: -------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; USE work.my_package.all; ---------------------------------------------ENTITY conv_int2 IS PORT ( a: IN STD_LOGIC_VECTOR(0 TO 3); y: OUT INTEGER RANGE 0 TO 15); END conv_int2; ---------------------------------------------ARCHITECTURE my_arch OF conv_int2 IS BEGIN y <= conv_integer(a); END my_arch; ---------------------------------------------- TLFeBOOK 260 Chapter 11 Example 11.6: Overloaded ‘‘B’’ Operator The function shown below, called ‘‘þ’’, overloads the pre-defined ‘‘þ’’ (addition) operator (section 4.1 and section 4.4). Recall that the latter accepts only INTEGER, SIGNED, or UNSIGNED values. However, we are interested in writing a function which should allow the sum of STD_LOGIC_VECTOR values as well (thus overloading the ‘‘þ’’ operator). The function shown below was placed in a PACKAGE (plus PACKAGE BODY). An example utilizing this function is also presented in the main code that follows the function implementation. Notice that the two parameters passed to the function, as well as the return value, are all of type STD_LOGIC_VECTOR. We assume that they all have the same number of bits (an extension to this example is presented in problem 11.8). 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 -------- Package: ---------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; ---------------------------------------------PACKAGE my_package IS FUNCTION "+" (a, b: STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR; END my_package; ---------------------------------------------PACKAGE BODY my_package IS FUNCTION "+" (a, b: STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS VARIABLE result: STD_LOGIC_VECTOR; VARIABLE carry: STD_LOGIC; BEGIN carry := '0'; FOR i IN a'REVERSE_RANGE LOOP result(i) := a(i) XOR b(i) XOR carry; carry := (a(i) AND b(i)) OR (a(i) AND carry) OR (b(i) AND carry); END LOOP; RETURN result; END "+"; END my_package; ---------------------------------------------- TLFeBOOK Functions and Procedures 261 Figure 11.2 Simulation results of example 11.6. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 --------- Main code: ------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; USE work.my_package.all; ---------------------------------------------ENTITY add_bit IS PORT ( a: IN STD_LOGIC_VECTOR(3 DOWNTO 0); y: OUT STD_LOGIC_VECTOR(3 DOWNTO 0)); END add_bit; ---------------------------------------------ARCHITECTURE my_arch OF add_bit IS CONSTANT b: STD_LOGIC_VECTOR(3 DOWNTO 0) := "0011"; CONSTANT c: STD_LOGIC_VECTOR(3 DOWNTO 0) := "0110"; BEGIN y <= a + b + c; -- overloaded "+" operator END my_arch; ---------------------------------------------- Simulation results, for 4-bit numbers, are presented in figure 11.2. We have entered b ¼ 3 and c ¼ 6 as two constants, which are added to the input signal a. The expected results is then y ¼ a þ 9. Example 11.7: Arithmetic Shift Function The function shown below arithmetically shifts a STD_LOGIC_VECTOR value to the left. Two arguments are passed to the function: arg1 and arg2. The first is the vector to be shifted, while the second specifies the amount of shift. Notice that the function (lines 13–26) is totally generic; that is, it works for any size (number of bits) or order (TO/DOWNTO) of the input vector. In this example, the function was located in the main code instead of in a package. TLFeBOOK 262 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 Chapter 11 -------------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; --------------------------------------------ENTITY shift_left IS GENERIC (size: INTEGER := 4); PORT ( a: IN STD_LOGIC_VECTOR(size-1 DOWNTO 0); x, y, z: OUT STD_LOGIC_VECTOR(size-1 DOWNTO 0)); END shift_left; --------------------------------------------ARCHITECTURE behavior OF shift_left IS -----------------------------------------FUNCTION slar (arg1: STD_LOGIC_VECTOR; arg2: NATURAL) RETURN STD_LOGIC_VECTOR IS VARIABLE input: STD_LOGIC_VECTOR(size-1 DOWNTO 0) := arg1; CONSTANT size : INTEGER := arg1'LENGTH; VARIABLE copy: STD_LOGIC_VECTOR(size-1 DOWNTO 0) := (OTHERS => arg1(arg1'RIGHT)); VARIABLE result: STD_LOGIC_VECTOR(size-1 DOWNTO 0); BEGIN IF (arg2 >= size-1) THEN result := copy; ELSE result := input(size-1-arg2 DOWNTO 1) & copy(arg2 DOWNTO 0); END IF; RETURN result; END slar; -----------------------------------------BEGIN x <= slar(a, 0); y <= slar(a, 1); z <= slar(a, 2); END behavior; ------------------------------------------ Simulation results are shown in figure 11.3 (for y only). The upper set of curves corresponds to the a(size-1 DOWNTO 0) specification, as shown above in line 7 (that is, a(3) is the MSB), while the second set refers to the reverse order, that is, a(0 TO 3), in which case a(0) is the MSB. TLFeBOOK Functions and Procedures 263 Figure 11.3 Simulation results of example 11.7. Example 11.8: Multiplier In this example, a function called mult( ) is presented. It multiplies two UNSIGNED values, returning their UNSIGNED product. The parameters passed to the function do not need to have the same number of bits, and their order (TO/DOWNTO) can be any. The function was installed in a package called pack. An application example (main code) is also presented. Simulation results are shown in figure 11.4. 1 2 3 4 5 6 7 --------- Package: ----------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; USE ieee.std_logic_arith.all; --------------------------------------------PACKAGE pack IS FUNCTION mult(a, b: UNSIGNED) RETURN UNSIGNED; TLFeBOOK 264 Chapter 11 Figure 11.4 Simulation results of example 11.8. 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 END pack; --------------------------------------------PACKAGE BODY pack IS FUNCTION mult(a, b: UNSIGNED) RETURN UNSIGNED IS CONSTANT max: INTEGER := a'LENGTH + b'LENGTH - 1; VARIABLE aa: UNSIGNED(max DOWNTO 0) := (max DOWNTO a'LENGTH => '0') & a(a'LENGTH-1 DOWNTO 0); VARIABLE prod: UNSIGNED(max DOWNTO 0) := (OTHERS => '0'); BEGIN FOR i IN 0 TO a'LENGTH-1 LOOP IF (b(i)='1') THEN prod := prod + aa; END IF; aa := aa(max-1 DOWNTO 0) & '0'; END LOOP; RETURN prod; END mult; END pack; -------------------------------------------------------- 1 2 3 4 5 6 7 8 9 -------- Main code: -----------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; USE ieee.std_logic_arith.all; USE work.my_package.all; --------------------------------------------ENTITY multiplier IS GENERIC (size: INTEGER := 4); PORT ( a, b: IN UNSIGNED(size-1 DOWNTO 0); TLFeBOOK Functions and Procedures 10 11 12 13 14 15 16 17 265 y: OUT UNSIGNED(2*size-1 DOWNTO 0)); END multiplier; --------------------------------------------ARCHITECTURE behavior OF multiplier IS BEGIN y <= mult(a,b); END behavior; --------------------------------------------------------- 11.3 PROCEDURE A PROCEDURE is very similar to a FUNCTION and has the same basic purposes. However, a procedure can return more than one value. Like a FUNCTION, two parts are necessary to construct and use a PROCEDURE: the procedure itself (procedure body) and a procedure call. Procedure Body PROCEDURE procedure_name [<parameter list>] IS [declarations] BEGIN (sequential statements) END procedure_name; In the syntax above, <parameter list> specifies the procedure’s input and output parameters; that is: 3parameter list4 ¼ [CONSTANT] constant_name: mode type; 3parameter list4 ¼ SIGNAL signal_name: mode type; or 3parameter list4 ¼ VARIABLE variable_name: mode type; A PROCEDURE can have any number of IN, OUT, or INOUT parameters, which can be SIGNALS, VARIABLES, or CONSTANTS. For input signals (mode IN), the default is CONSTANT, whereas for output signals (mode OUT or INOUT) the default is VARIABLE. As seen before, WAIT, SIGNAL declarations, and COMPONENTS are not synthesizable when used in a FUNCTION. The same is true for a PROCEDURE, with TLFeBOOK 266 Chapter 11 the exception that a SIGNAL can be declared, but then the PROCEDURE must be declared in a PROCESS. Moreover, besides WAIT, any other edge detection is also not synthesizable with a PROCEDURE (that is, contrary to a function, a synthesizable procedure should not infer registers). In section 11.5, a summary comparing FUNCTIONS and PROCEDURES will be presented. Example: The PROCEDURE below has three inputs, a, b, and c (mode IN). a is a CONSTANT of type BIT, while b and c are SIGNALS, also of type BIT. Notice that the word CONSTANT can be omitted for input parameters, for it is the default object (recall, however, that for outputs the default object is VARIABLE). There are also two return signals, x (mode OUT, type BIT_VECTOR) and y (mode INOUT, type INTEGER). PROCEDURE my_procedure ( a: IN BIT; SIGNAL b, c: IN BIT; SIGNAL x: OUT BIT_VECTOR(7 DOWNTO 0); SIGNAL y: INOUT INTEGER RANGE 0 TO 99) IS BEGIN ... END my_procedure; Procedure Call Contrary to a FUNCTION, which is called as part of an expression, a PROCEDURE call is a statement on its own. It can appear by itself or associated to a statement (either concurrent or sequential). Examples of procedure calls: compute_min_max(in1, in2, 1n3, out1, out2); -- statement by itself divide(dividend, divisor, quotient, remainder); -- statement by itself IF (a>b) THEN compute_min_max(in1, in2, 1n3, out1, out2); -- procedure call associated to another statement 11.4 Procedure Location The typical locations of a PROCEDURE are the same as those of a FUNCTION (see figure 11.1). Again, though it is usually placed in a PACKAGE (for code parti- TLFeBOOK Functions and Procedures 267 min_out inp1 min_max max_out inp2 ena Figure 11.5 min_max circuit of example 11.9. Figure 11.6 Simulation results of example 11.9. tioning, code reuse, and code sharing purposes), it can also be located in the main code (either in the ENTITY or in the declarative part of the ARCHITECTURE). When placed in a PACKAGE, a PACKAGE BODY is then necessary, which must contain the body of each PROCEDURE declared in the declarative part of the PACKAGE. Examples of both cases are shown below. Example 11.9: PROCEDURE Located in the Main Code The min_max code below makes use of a PROCEDURE called sort. It takes two 8-bit unsigned integers as inputs (inp1, inp2), sorts them, then outputs the smaller value at min_out and the higher value at max_out (figure 11.5). The PROCEDURE is located in the declarative part of the ARCHITECTURE (main code). Notice that the PROCEDURE call, sort(inp1,inp2,min_out,max_out), is a statement on its own. Simulation results are shown in figure 11.6. 1 2 3 -----------------------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; TLFeBOOK 268 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 Chapter 11 -----------------------------------------------------ENTITY min_max IS GENERIC (limit : INTEGER := 255); PORT ( ena: IN BIT; inp1, inp2: IN INTEGER RANGE 0 TO limit; min_out, max_out: OUT INTEGER RANGE 0 TO limit); END min_max; -----------------------------------------------------ARCHITECTURE my_architecture OF min_max IS -------------------------PROCEDURE sort (SIGNAL in1, in2: IN INTEGER RANGE 0 TO limit; SIGNAL min, max: OUT INTEGER RANGE 0 TO limit) IS BEGIN IF (in1 > in2) THEN max <= in1; min <= in2; ELSE max <= in2; min <= in1; END IF; END sort; -------------------------BEGIN PROCESS (ena) BEGIN IF (ena='1') THEN sort (inp1, inp2, min_out, max_out); END IF; END PROCESS; END my_architecture; ------------------------------------------------------ Example 11.10: PROCEDURE Located in a PACKAGE This example is similar to example 11.9, with the only di¤erence being that now the PROCEDURE (called sort) is placed in a PACKAGE (called my_ package). Thus the PROCEDURE can now be reused and shared with other designs. The code below can be compiled as two separate files, or can be compiled as a single file (called min_max.vhd, which is the ENTITY’s name). TLFeBOOK Functions and Procedures 269 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 ------------ Package: --------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; ------------------------------------PACKAGE my_package IS CONSTANT limit: INTEGER := 255; PROCEDURE sort (SIGNAL in1, in2: IN INTEGER RANGE 0 TO limit; SIGNAL min, max: OUT INTEGER RANGE 0 TO limit); END my_package; ------------------------------------PACKAGE BODY my_package IS PROCEDURE sort (SIGNAL in1, in2: IN INTEGER RANGE 0 TO limit; SIGNAL min, max: OUT INTEGER RANGE 0 TO limit) IS BEGIN IF (in1 > in2) THEN max <= in1; min <= in2; ELSE max <= in2; min <= in1; END IF; END sort; END my_package; ------------------------------------------------- 1 2 3 4 5 6 7 8 9 10 11 12 --------- Main code: ---------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; USE work.my_package.all; ------------------------------------ENTITY min_max IS GENERIC (limit: INTEGER := 255); PORT ( ena: IN BIT; inp1, inp2: IN INTEGER RANGE 0 TO limit; min_out, max_out: OUT INTEGER RANGE 0 TO limit); END min_max; ------------------------------------- TLFeBOOK 270 13 14 15 16 17 18 19 20 21 Chapter 11 ARCHITECTURE my_architecture OF min_max IS BEGIN PROCESS (ena) BEGIN IF (ena='1') THEN sort (inp1, inp2, min_out, max_out); END IF; END PROCESS; END my_architecture; ---------------------------------------------- The simulation results are obviously the same as those of example 11.9 (figure 11.6). 11.5 FUNCTION versus PROCEDURE Summary A FUNCTION has zero or more input parameters and a single return value. The input parameters can only be CONSTANTS (default) or SIGNALS (VARIABLES are not allowed).  A PROCEDURE can have any number of IN, OUT, and INOUT parameters, which can be SIGNALS, VARIABLES, or CONSTANTS. For input parameters (mode IN) the default is CONSTANT, whereas for output parameters (mode OUT or INOUT) the default is VARIABLE.  A FUNCTION is called as part of an expression, while a PROCEDURE is a statement on its own.   In both, WAIT and COMPONENTS are not synthesizable. The possible locations of FUNCTIONS and PROCEDURES are the same (figure 11.1). Though they are usually placed in PACKAGES (for code partitioning, code sharing, and code reuse purposes), they can also be located in the main code (either inside the ARCHITECTURE or inside the ENTITY). When placed in a PACKAGE, then a PACKAGE BODY is necessary, which should contain the body of each FUNCTION and/or PROCEDURE declared in the PACKAGE.  11.6 ASSERT ASSERT is a non-synthesizable statement whose purpose is to write out messages (on the screen, for example) when problems are found during simulation. Depending TLFeBOOK Functions and Procedures 271 on the severity of the problem, the simulator is instructed to halt. Its syntax is the following: ASSERT condition [REPORT "message"] [SEVERITY severity_level]; The severity level can be: Note, Warning, Error (default), or Failure. The message is written when the condition is FALSE. Example: Say that we have written a function to add two binary numbers (like in example 11.6), where it was assumed that the input parameters must have the same number of bits. In order to check such an assumption, the following ASSERT statement could be included in the function body: ASSERT a'LENGTH = b'LENGTH REPORT "Error: vectors do not have same length!" SEVERITY failure; Again, ASSERT does not generate hardware. Synthesis tools will simply ignore it or give a warning. 11.7 Problems The purpose of the problems proposed in this section is to reinforce the main aspects related to the construction and use of subprograms (FUNCTIONS and PROCEDURES). Problem 11.1: Conversion to std_logic_vector Write a function capable of converting an INTEGER to a STD_LOGIC_VECTOR value. Call it conv_std_logic( ). Then write an application example, containing a call to your function, in order to test it. Construct two solutions: one with the function in the main code itself, and one with it in a package. Problem 11.2: Overloaded ‘‘not’’ Operator The NOT operator allows the inversion of binary values. For example, if x ¼ ‘‘1000’’ is a STD_LOGIC_VECTOR value, then NOT x could be used, producing ‘‘0111’’. TLFeBOOK 272 Chapter 11 However, if x had been declared as an INTEGER, such operation would not be allowed. Write a ‘‘not’’ function capable of inverting integers. (Suggestion: See section 4.4 and example 11.6.) Problem 11.3: Logic Shift of std_logic_vector The pre-defined shift operators (specified in VHDL93, section 4.1) work only with type BIT_VECTOR. Write a function capable of logically shifting a STD_LOGC_ VECTOR signal to the left by a specified amount. Two arguments must be passed to the function: the value to be shifted (STD_LOGIC_VECTOR), plus a NATURAL value specifying the amount of shift. Place your function in a package. Then write an application with a call to your function in order to test it (suggestion: review example 11.7). Problem 11.4: Logic Shift of an Integer This problem is an extension of problem 11.3. Write a function capable of shifting an INTEGER value to the left by an specified amount. Place your function in a package. Then write an application with a call to your function in order to test it. Problem 11.5: Signed Multiplier Write a function similar to that of example 11.8. However, it should now operate with SIGNED input and output values. Problem 11.6: Two-digit Counter with SSD Output In example 6.7, a progressive 2-digit decimal counter (0 ! 99 ! 0), with external asynchronous reset plus binary-coded decimal (BCD) to seven-segment display (SSD) conversion, was designed. In it, a routine to convert a signal from BCD to SSD format was used twice. This kind of repetition can be avoided with a FUNCTION. Write a function (call it bcd_to_ssd) capable of making such a conversion and place it in a PACKAGE. Then redo the design of example 6.7, using a call to your function whenever such conversion is needed. Then synthesize and test your solution. Problem 11.7: Statistical Procedure Write a PROCEDURE that receives eight signed values and returns their average, the largest value, and the lowest value. Call the return values ave, max, and min. Place your procedure in a package. Then write an application with a call to it in order to test its functionality. TLFeBOOK Functions and Procedures Problem 11.8: 273 Overloaded ‘‘B’’ Operator In example 11.6, a function that overloads the ‘‘þ’’ (addition) operator was presented. Its purpose was to allow the direct addition of STD_LOGIC_VECTOR values. In that example, the return parameter had the same number of bits as the two input parameters. Write a similar function, but with the return vector having one extra bit corresponding to the carry out bit such that overflow can then be easily detected. TLFeBOOK TLFeBOOK 12 Additional System Designs In this chapter, additional designs are presented, with the purpose of further illustrating the usage of the VHDL units that are intended for system-level design: PACKAGES, COMPONENTS, FUNCTIONS, and PROCEDURES. 12.1 Serial-Parallel Multiplier Figure 12.1 shows the RTL diagram of a serial-parallel multiplier. One of the input vectors (a) is applied serially to the circuit (one bit at a time, starting from the LSB), while the other (b) is applied in parallel (all bits simultaneously). Say that a has M bits, while b has N. Then, after all M bits of a have been presented to the system, a string of M ‘0’s must follow, in order to complete the (M þ N)-bit output product. As can be seen in figure 12.1, the system is pipelined, and is constructed using AND gates, full-adder units, plus registers (flip-flops). Each unit of the pipeline (except the leftmost one) requires one adder and two registers, plus an AND gate to compute one of the inputs. Thus for an M  N multiplier, O(N) of such units are required. The solution presented below is of structural type (only COMPONENTS were used). Notice that there is more than one level of instantiation (the unit called pipe instantiates other components, while in the final code, pipe is instantiated as well (besides other components). The design of each component is shown below, along with the PACKAGE containing all COMPONENT declarations, followed by the project proper (main code). Simulation results were also included. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ------ and_2.vhd (component): --------LIBRARY ieee; USE ieee.std_logic_1164.all; --------------------------------------ENTITY and_2 IS PORT ( a, b: IN STD_LOGIC; y: OUT STD_LOGIC); END and_2; --------------------------------------ARCHITECTURE and_2 OF and_2 IS BEGIN y <= a AND b; END and_2; --------------------------------------- TLFeBOOK 276 Chapter 12 b(3) b(2) b(1) b(0) a a(0) a(1) a(2) a(3) D + D D + D D + D prod D Figure 12.1 Serial-parallel multiplier. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ------ reg.vhd (component): ----------LIBRARY ieee; USE ieee.std_logic_1164.all; --------------------------------------ENTITY reg IS PORT ( d, clk, rst: IN STD_LOGIC; q: OUT STD_LOGIC); END reg; --------------------------------------ARCHITECTURE reg OF reg IS BEGIN PROCESS (clk, rst) BEGIN IF (rst='1') THEN q<='0'; ELSIF (clk'EVENT AND clk='1') THEN q<=d; END IF; END PROCESS; END reg; --------------------------------------- 1 2 3 4 5 ------ fau.vhd (component): ----------LIBRARY ieee; USE ieee.std_logic_1164.all; --------------------------------------ENTITY fau IS TLFeBOOK Additional System Designs 6 7 8 9 10 11 12 13 14 15 PORT ( a, b, cin: IN STD_LOGIC; s, cout: OUT STD_LOGIC); END fau; --------------------------------------ARCHITECTURE fau OF fau IS BEGIN s <= a XOR b XOR cin; cout <= (a AND b) OR (a AND cin) OR (b AND cin); END fau; --------------------------------------- 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ------ pipe.vhd (component): ---------LIBRARY ieee; USE ieee.std_logic_1164.all; USE work.my_components.all; --------------------------------------ENTITY pipe IS PORT ( a, b, clk, rst: IN STD_LOGIC; q: OUT STD_LOGIC); END pipe; --------------------------------------ARCHITECTURE structural OF pipe IS SIGNAL s, cin, cout: STD_LOGIC; BEGIN U1: COMPONENT fau PORT MAP (a, b, cin, s, cout); U2: COMPONENT reg PORT MAP (cout, clk, rst, cin); U3: COMPONENT reg PORT MAP (s, clk, rst, q); END structural; --------------------------------------- 1 2 3 4 5 6 7 8 ----- my_components.vhd (package):----LIBRARY ieee; USE ieee.std_logic_1164.all; --------------------------------------PACKAGE my_components IS -------------------------COMPONENT and_2 IS PORT (a, b: IN STD_LOGIC; y: OUT STD_LOGIC); 277 TLFeBOOK 278 Chapter 12 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 END COMPONENT; -------------------------COMPONENT fau IS PORT (a, b, cin: IN STD_LOGIC; s, cout: OUT STD_LOGIC); END COMPONENT; -------------------------COMPONENT reg IS PORT (d, clk, rst: IN STD_LOGIC; q: OUT STD_LOGIC); END COMPONENT; -------------------------COMPONENT pipe IS PORT (a, b, clk, rst: IN STD_LOGIC; q: OUT STD_LOGIC); END COMPONENT; -------------------------END my_components; --------------------------------------- 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ----- multiplier.vhd (project): ------LIBRARY ieee; USE ieee.std_logic_1164.all; USE work.my_components.all; --------------------------------------ENTITY multiplier IS PORT ( a, clk, rst: IN STD_LOGIC; b: IN STD_LOGIC_VECTOR (3 DOWNTO 0); prod: OUT STD_LOGIC); END multiplier; --------------------------------------ARCHITECTURE structural OF multiplier IS SIGNAL and_out, reg_out: STD_LOGIC_VECTOR (3 DOWNTO 0); BEGIN U1: COMPONENT and_2 PORT MAP (a, b(3), and_out(3)); U2: COMPONENT and_2 PORT MAP (a, b(2), and_out(2)); U3: COMPONENT and_2 PORT MAP (a, b(1), and_out(1)); U4: COMPONENT and_2 PORT MAP (a, b(0), and_out(0)); U5: COMPONENT reg PORT MAP (and_out(3), clk, rst, reg_out(3)); TLFeBOOK Additional System Designs 279 Figure 12.2 Simulation results of serial-parallel multiplier. 21 U6: COMPONENT pipe PORT MAP (and_out(2), reg_out(3), 22 clk, rst, reg_out(2)); 23 U7: COMPONENT pipe PORT MAP (and_out(1), reg_out(2), 24 clk, rst, reg_out(1)); 25 U8: COMPONENT pipe PORT MAP (and_out(0), reg_out(1), 26 clk, rst, reg_out(0)); 27 prod <= reg_out(0); 28 END structural; 29 --------------------------------------- Simulation results are shown in figure 12.2. a ¼ ‘‘1100’’ (decimal 12) was applied to the serial input. Notice that this input must start with the LSB (a(0) ¼ ‘0’), which appears in the time slot 100 ns–200 ns, while the MSB (a(3) ¼ ‘1’) is situated in 400 ns–500 ns. Recall that four zeros must then follow. On the other hand, at the parallel input, b ¼ ‘‘1101’’ (decimal 13) was applied. The expected result, prod ¼ ‘‘10011100’’ (decimal 156), can be observed in the lower plot. Recall that the first bit out is the LSB; that is, prod(0) ¼ ‘0’, which appears in the time slot immediately after the first rising edge of clock; (that is, 150 ns–250 ns), while the last bit (MSB) of prod is situated in 850 ns–950 ns. 12.2 Parallel Multiplier Figure 12.3 shows the diagram of a 4-bit parallel multiplier. Contrary to the case of figure 12.1, here all input bits are applied to the system simultaneously. Therefore, registers are not required. Notice in figure 12.3 that only AND gates and FAU (full adder units) are necessary to construct a parallel multiplier. The operands are a and b (each of four bits), and the resulting product is prod (eight bits). TLFeBOOK 280 Chapter 12 b(3) b(2) b(1) b(0) a(0) a(1) p(0) + + carry + sum a(2) p(1) + + + a(3) p(2) + + + p(3) + p(7) p(6) + + p(5) p(4) Figure 12.3 Parallel multiplier. TLFeBOOK Additional System Designs 281 Figure 12.4 Simulation results of parallel multiplier. The VHDL code shown below was based on COMPONENT instantiation. Notice that two basic components, AND_2 and FAU, were first specified (shown in section 12.1). These components were then instantiated to construct higher-level components, top_row, mid_row, and lower_row. All of these components were then declared in a PACKAGE called my_components, and finally used in the project called multiplier to implement the circuit of figure 12.3. Simulation results are shown in figure 12.4. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ------- top_row.vhd (component): ------------LIBRARY ieee; USE ieee.std_logic_1164.all; USE work.my_components.all; --------------------------------------ENTITY top_row IS PORT ( a: IN STD_LOGIC; b: IN STD_LOGIC_VECTOR (3 DOWNTO 0); sout, cout: OUT STD_LOGIC_VECTOR (2 DOWNTO 0); p: OUT STD_LOGIC); END top_row; --------------------------------------ARCHITECTURE structural OF top_row IS BEGIN U1: COMPONENT and_2 PORT MAP (a, b(3), sout(2)); U2: COMPONENT and_2 PORT MAP (a, b(2), sout(1)); U3: COMPONENT and_2 PORT MAP (a, b(1), sout(0)); U4: COMPONENT and_2 PORT MAP (a, b(0), p); cout(2)<='0'; cout(1)<='0'; cout(0)<='0'; END structural; ---------------------------------------------- TLFeBOOK 282 Chapter 12 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 ------- mid_row.vhd (component): ------------LIBRARY ieee; USE ieee.std_logic_1164.all; USE work.my_components.all; --------------------------------------ENTITY mid_row IS PORT ( a: IN STD_LOGIC; b: IN STD_LOGIC_VECTOR (3 DOWNTO 0); sin, cin: IN STD_LOGIC_VECTOR (2 DOWNTO 0); sout, cout: OUT STD_LOGIC_VECTOR (2 DOWNTO 0); p: OUT STD_LOGIC); END mid_row; --------------------------------------ARCHITECTURE structural OF mid_row IS SIGNAL and_out: STD_LOGIC_VECTOR (2 DOWNTO 0); BEGIN U1: COMPONENT and_2 PORT MAP (a, b(3), sout(2)); U2: COMPONENT and_2 PORT MAP (a, b(2), and_out(2)); U3: COMPONENT and_2 PORT MAP (a, b(1), and_out(1)); U4: COMPONENT and_2 PORT MAP (a, b(0), and_out(0)); U5: COMPONENT fau PORT MAP (sin(2), cin(2), and_out(2), sout(1), cout(2)); U6: COMPONENT fau PORT MAP (sin(1), cin(1), and_out(1), sout(0), cout(1)); U7: COMPONENT fau PORT MAP (sin(0), cin(0), and_out(0), p, cout(0)); END structural; ---------------------------------------------- 1 2 3 4 5 6 7 8 9 10 ------- lower_row.vhd (component): ----------LIBRARY ieee; USE ieee.std_logic_1164.all; USE work.my_components.all; --------------------------------------ENTITY lower_row IS PORT ( sin, cin: IN STD_LOGIC_VECTOR (2 DOWNTO 0); p: OUT STD_LOGIC_VECTOR (3 DOWNTO 0); END lower_row; --------------------------------------- TLFeBOOK Additional System Designs 283 11 12 13 14 15 16 17 18 19 20 21 22 ARCHITECTURE structural OF lower_row IS SIGNAL local: STD_LOGIC_VECTOR (2 DOWNTO 0); BEGIN local(0)<='0'; U1: COMPONENT fau PORT MAP (sin(0), cin(0), local(0), p(0), local(1)); U2: COMPONENT fau PORT MAP (sin(1), cin(1), local(1), p(1), local(2)); U3: COMPONENT fau PORT MAP (sin(2), cin(2), local(2), p(2), p(3)); END structural; ---------------------------------------------- 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 ----- my_components.vhd (package): ----------LIBRARY ieee; USE ieee.std_logic_1164.all; -----------------------------------PACKAGE my_components IS ----------------------COMPONENT and_2 IS PORT ( a, b: IN STD_LOGIC; y: OUT STD_LOGIC); END COMPONENT; ----------------------COMPONENT fau IS -- full adder unit PORT ( a, b, cin: IN STD_LOGIC; s, cout: OUT STD_LOGIC); END COMPONENT; ----------------------COMPONENT top_row IS PORT ( a: IN STD_LOGIC; b: IN STD_LOGIC_VECTOR (3 DOWNTO 0); sout, cout: OUT STD_LOGIC_VECTOR (2 DOWNTO 0); p: OUT STD_LOGIC); END COMPONENT; ----------------------COMPONENT mid_row IS PORT ( a: IN STD_LOGIC; b: IN STD_LOGIC_VECTOR (3 DOWNTO 0); sin, cin: IN STD_LOGIC_VECTOR (2 DOWNTO 0); sout, cout: OUT STD_LOGIC_VECTOR (2 DOWNTO 0); TLFeBOOK 284 Chapter 12 27 p: OUT STD_LOGIC); 28 END COMPONENT; 29 ----------------------30 COMPONENT lower_row IS 31 PORT ( sin, cin: IN STD_LOGIC_VECTOR (2 DOWNTO 0); 32 p: OUT STD_LOGIC_VECTOR (3 DOWNTO 0); 33 END COMPONENT; 34 ----------------------35 END my_components; 36 ---------------------------------------------1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 ------- multiplier.vhd (project): -----------LIBRARY ieee; USE ieee.std_logic_1164.all; USE work.my_components.all; --------------------------------------ENTITY multiplier IS PORT ( a, b: IN STD_LOGIC_VECTOR (3 DOWNTO 0); prod: OUT STD_LOGIC_VECTOR (7 DOWNTO 0)); END multiplier; --------------------------------------ARCHITECTURE structural OF multiplier IS TYPE matrix IS ARRAY (0 TO 3) OF STD_LOGIC_VECTOR (2 DOWNTO 0); SIGNAL s, c: matrix; BEGIN U1: COMPONENT top_row PORT MAP (a(0), b, s(0), c(0), prod(0)); U2: COMPONENT mid_row PORT MAP (a(1), b, s(0), c(0), s(1), c(1), prod(1)); U3: COMPONENT mid_row PORT MAP (a(2), b, s(1), c(1), s(2), c(2), prod(2)); U4: COMPONENT mid_row PORT MAP (a(3), b, s(2), c(2), s(3), c(3), prod(3)); U5: COMPONENT lower_row PORT MAP (s(3), c(3), prod(7 DOWNTO 4)); END structural; ---------------------------------------------- TLFeBOOK Additional System Designs 285 Figure 12.5 Parallel multiplier inferred from the pre-defined ‘‘*’’ operator. A Simpler Approach The example above had the purpose of exploring several aspects related to system design using VHDL. However, for the particular case of a parallel multiplier, it can be immediately inferred by means of the pre-defined ‘‘*’’ (multiplication) operator. Therefore, the circuit above can be represented using the compact form of figure 12.5, and the whole code above can be replaced by the following code: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 --------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; USE ieee.std_logic_arith.all; --------------------------------------ENTITY multiplier3 IS PORT ( a, b: IN SIGNED(3 DOWNTO 0); prod: OUT SIGNED(7 DOWNTO 0)); END multiplier3; --------------------------------------ARCHITECTURE behavior OF multiplier3 IS BEGIN prod <= a * b; END behavior; --------------------------------------- 12.3 Multiply-Accumulate Circuits Multiplication followed by accumulation is a common operation in many digital systems, particularly those highly interconnected, like digital filters, neural networks, data quantizers, etc. TLFeBOOK 286 Chapter 12 Figure 12.6 MAC circuit. One typical MAC (multiply-accumulate) architecture is illustrated in figure 12.6. It consists of multiplying two values, then adding the result to the previously accumulated value, which must then be re-stored in the registers for future accumulations. Another feature of a MAC circuit is that it must check for overflow, which might happen when the number of MAC operations is large. This design can be done using COMPONENTS, because we have already designed each of the units shown in figure 12.6. However, since it is a relatively simple circuit, it can also be designed directly. The latter approach is illustrated below, while the former is treated in problem 12.2. In any case, the MAC circuit, as a whole, can be used as a COMPONENT in applications like digital filters and neural networks (next sections). Overflow: In the implementation (code) shown below, a FUNCTION was written to detect overflow and truncate the result in case overflow happens. Overflow in a signed adder occurs when two operands with the same signal (leftmost bit) produce a result with a di¤erent signal from them. If it occurs, the largest value (positive or negative) should be assigned to the result. For example, if eight bits are used to encode the values, the addition of two positive numbers must fall in the interval from 0 to 127, while the addition of two negative numbers must fall between 128 (that is, þ128 in unsigned representation) and 1 (255 in unsigned representation). For example, 65 þ 65 ¼ 130, which is indeed 126 (overflow), so the result should be truncated to the largest positive value (127). Likewise, (70) þ (70) ¼ 140, which is, indeed, 116 (overflow), so the result should be truncated to the most negative value (128). On the other hand, when the operands have di¤erent signals, overflow cannot happen. The add_truncate( ) function was placed in a PACKAGE (chapter 10) called my_functions. The function receives two signals, adds them, then checks for overflow TLFeBOOK Additional System Designs 287 and truncates the result if necessary, returning the processed result to the main code. Notice that the function is generic, for the number of bits of the operands is passed to it by means of a parameter called size. Notice also in the main code that the parameters passed to the function were declared as signals (line 14), because variables are not allowed (chapter 11). 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 ------- PACKAGE my_functions: ----------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; USE ieee.std_logic_arith.all; ---------------------------------------------------------PACKAGE my_functions IS FUNCTION add_truncate (SIGNAL a, b: SIGNED; size: INTEGER) RETURN SIGNED; END my_functions; ---------------------------------------------------------PACKAGE BODY my_functions IS FUNCTION add_truncate (SIGNAL a, b: SIGNED; size: INTEGER) RETURN SIGNED IS VARIABLE result: SIGNED (7 DOWNTO 0); BEGIN result := a + b; IF (a(a'left)=b(b'left)) AND (result(result'LEFT)/=a(a'left)) THEN result := (result'LEFT => a(a'LEFT), OTHERS => NOT a(a'left)); END IF; RETURN result; END add_truncate; END my_functions; ---------------------------------------------------------- 1 2 3 4 5 6 ------- Main code: ----------------------LIBRARY ieee; USE ieee.std_logic_1164.all; USE ieee.std_logic_arith.all; USE work.my_functions.all; ------------------------------------------ TLFeBOOK 288 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 Chapter 12 ENTITY mac IS PORT ( a, b: IN SIGNED(3 DOWNTO 0); clk, rst: IN STD_LOGIC; acc: OUT SIGNED(7 DOWNTO 0)); END mac; -----------------------------------------ARCHITECTURE rtl OF mac IS SIGNAL prod, reg: SIGNED(7 DOWNTO 0); BEGIN PROCESS (rst, clk) VARIABLE sum: SIGNED(7 DOWNTO 0); BEGIN prod <= a * b; IF (rst='1') THEN reg <= (OTHERS=>'0'); ELSIF (clk'EVENT AND clk='1') THEN sum := add_truncate (prod, reg, 8); reg <= sum; END IF; acc <= reg; END PROCESS; END rtl; ------------------------------------------ Simulation results are presented in figure 12.7. Notice that the following sequence of signals was presented to the MAC circuit: a ¼ (0, 2, 4, 6, 8, 6, 4, 2), b ¼ (0, 3, 6, 7, 8, 8, 8). Therefore, the expected output sequence is acc ¼ (0, 6, 30, 12, 52, 100, 148) (recall that 12 is represented in the graph as 256  12 ¼ 244). Figure 12.7 Simulation results of MAC circuit. TLFeBOOK Additional System Designs 289 All the values are OK, except the last one, for it is above the maximum positive value allowed for 8-bit signed numbers (127). Therefore, this result was kept at 127. 12.4 Digital Filters Digital signal processing (DSP) finds innumerable applications in the fields of audio, video, and communications, among others. Such applications are generally based on LTI (linear time invariant) systems, which can be implemented with digital circuitry. Any LTI system be represented by the following equation: N X ak y[n  k] ¼ k¼0 M X bk x[n  k] k¼0 where ak and bk are the filter coe‰cients, and x[n  k], y[n  k] are the current (for k ¼ 0) and earlier (for k > 0) input and output values, respectively. To implement this expression, registers are necessary to store x[n  k] and/or y[n  k] (for k > 0), besides multipliers and adders, which are well-known building blocks in the digital domain. The impulse response of a digital filter can be divided into two categories: IIR (infinite impulse response) and FIR (finite impulse response). The former corresponds to the general case described by the equation above, while the latter occurs when N ¼ 0. Only FIR filters can exhibit linear phase, so they are indispensable when linear phase is required, like in many telecom applications. With N ¼ 0, the equation above becomes y[n] ¼ M X ck x[n  k] k¼0 where ck ¼ bk /a0 are the coe‰cients of the FIR filter. This equation can be implemented by the system of figure 12.8, where D (delay) represents a register (flip-flops), a triangle is a multiplier, and a circle means an adder. An equivalent RTL representation is shown in figure 12.9. As shown, the values of x are stored in a shift register, whose outputs are connected to multipliers and then to adders. The coe‰cients must also be stored on chip. However, if the coe‰cients are always the same (that is, if it is a dedicated filter), their values can be implemented by means of logic gates rather than registers (we just need to store CONSTANTS). On the other hand, if it is a general purpose filter, then registers are required for the coe‰cients. In the architecture of figure 12.9, the output vector (y) was also stored, in order to provide a clean, synchronous output. TLFeBOOK 290 Chapter 12 x[n] + y[n] co D x[n-1] + c1 D x[n-2] + c2 D x[n-3] c3 Figure 12.8 FIR filter diagram (with 4 coe‰cients). Figure 12.9 RTL representation of a FIR filter. The circuit of figure 12.9 can be constructed in several ways. However, if it is intended for future reuse or sharing, than it should be as generic as possible. In the code presented below, two GENERIC parameters are specified (line 7): n defines the number of filter coe‰cients, while m specifies the number of bits used to represent the input and coe‰cients. For the output, 2 m bits were used. Thus, for example, 16 bits could be used for x, coef, and reg, while 32 bits could be used for all other signals (from the outputs of the multipliers all the way to y). Notice that the lower section of the filter contains a MAC (multiply-accumulate) pipeline. This circuit is closely related to the MAC circuit discussed in section 12.3. Here too, overflow can happen, so an add/truncate procedure must be included in the design. TLFeBOOK Additional System Designs 291 In the solution below, the coe‰cients were considered as CONSTANTS (line 19), thus inferring no flip-flops. The values chosen were coef(0) ¼ 4, coef(1) ¼ 3, coef(2) ¼ 2, and coef(3) ¼ 1. Small values were chosen for n and m (4 for both) in order to make the simulation results easy to visualize. With n ¼ m ¼ 4, the synthesized circuit required 20 flip-flops (four for each stage of the shift register, plus eight for the output). As described in chapter 7, flip-flops are inferred when a signal assignment is made on the transition of another signal, which occurs in lines 33–45 of the code below (notice that indeed VARIABLE assignments are made in lines 33–38, but since their values are then passed to a SIGNAL (y), registers are inferred). 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 ----------------------------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; USE ieee.std_logic_arith.all; -- package needed for SIGNED ----------------------------------------------------------ENTITY fir2 IS GENERIC (n: INTEGER := 4; m: INTEGER := 4); -- n = # of coef., m = # of bits of input and coef. -- Besides n and m, CONSTANT (line 19) also need adjust PORT ( x: IN SIGNED(m-1 DOWNTO 0); clk, rst: IN STD_LOGIC; y: OUT SIGNED(2*m-1 DOWNTO 0)); END fir2; ----------------------------------------------------------ARCHITECTURE rtl OF fir2 IS TYPE registers IS ARRAY (n-2 DOWNTO 0) OF SIGNED(m-1 DOWNTO 0); TYPE coefficients IS ARRAY (n-1 DOWNTO 0) OF SIGNED(m-1 DOWNTO 0); SIGNAL reg: registers; CONSTANT coef: coefficients := ("0001", "0010", "0011", "0100"); BEGIN PROCESS (clk, rst) VARIABLE acc, prod: SIGNED(2*m-1 DOWNTO 0) := (OTHERS=>'0'); VARIABLE sign: STD_LOGIC; BEGIN ----- reset: -------------------------- TLFeBOOK 292 Chapter 12 30 IF (rst='1') THEN 31 FOR i IN n-2 DOWNTO 0 LOOP 32 FOR j IN m-1 DOWNTO 0 LOOP 33 reg(i)(j) <= '0'; 34 END LOOP; 35 END LOOP; 36 ----- register inference + MAC: ------37 ELSIF (clk'EVENT AND clk='1') THEN 38 acc := coef(0)*x; 39 FOR i IN 1 TO n-1 LOOP 40 sign := acc(2*m-1); 41 prod := coef(i)*reg(n-1-i); 42 acc := acc + prod; 43 ---- overflow check: -----------44 IF (sign=prod(prod'left)) AND 45 (acc(acc'left) /= sign) 46 THEN 47 acc := (acc'LEFT => sign, OTHERS => NOT sign); 48 END IF; 49 END LOOP; 50 reg <= x & reg(n-2 DOWNTO 1); 51 END IF; 52 y <= acc; 53 END PROCESS; 54 END rtl; 55 ----------------------------------------------------------- Simulation results are shown in figure 12.10. Recall that the coe‰cients are coef(0) ¼ 4, coef(1) ¼ 3, coef(2) ¼ 2, and coef(3) ¼ 1, and that the numbers are Figure 12.10 Simulation results of FIR filter of figure 12.9. TLFeBOOK Additional System Designs 293 SIGNED (therefore, with 4-bit values, the range is from 8 to þ7). The sequence applied to the input was x[0] ¼ 0, x[1] ¼ 5, x[2] ¼ 6 (16  6 ¼ 10 in the graph), x[3] ¼ 1 (16  1 ¼ 15 in the graph), x[4] ¼ 4, x[5] ¼ 7 (16  7 ¼ 9 in the graph), and x[6] ¼ 2 (16  2 ¼ 14 in the graph). Therefore, with all flip-flops previously reset, at the first positive edge of clk the expected output is y[0] ¼ coef(0)*x[0] ¼ 0, which coincides with the first result for y in figure 12.10. At the next upward transition of clk, the expected value is y[1] ¼ coef(0)*x[1] þ coef(1)*x[0] ¼ 20. And one clock cycle later, y[1] ¼ coef(0)*x[2] þ coef(1)*x[1] þ coef(2)*x[0] ¼ 9 (256  9 ¼ 247 in the graph), and so on. General Purpose FIR Filter The design presented above contained fixed coe‰cients, and is therefore adequate for an ASIC with a dedicated filter. For a general purpose implementation (that is, with programmable coe‰cients), the architecture of figure 12.11 can be used instead. As can be seen, this structure is modular and allows several chips to be cascaded, which might be helpful in some applications, because FIR filters tend to have many taps (coe‰cients). In this structure, there are two shift registers, one for storing the inputs (x) and the other for the coe‰cients (coef ). The structure is divided into n equal modules, called TAP1, . . . , TAPn. Each module (TAP) contains a slice of the shift registers, plus a multiplier and an adder. It also contains an output register, but this is optional (could be used at the last TAP only). This would, however, increase the ripple propagation Figure 12.11 General purpose FIR filter. TLFeBOOK 294 Chapter 12 w11 x1 1 y1 x2 2 y2 x3 3 y3 w12 w13 Input Hidden layers (a) Output layer (b) Figure 12.12 Feedforward neural network. between the adders. Of course, all coe‰cients must be loaded before the computation starts. This FIR architecture will be object of problem 12.4. 12.5 Neural Networks Neural Networks (NN) are highly parallel, highly interconnected systems. Such characteristics make their implementation very challenging, and also very costly, due to the large amount of hardware required. A feedforward NN is shown in figure 12.12(a). In this example, the circuit has three layers, with three 3-input neurons in each layer. Internal details of each layer are depicted in figure 12.12(b). xi represents the ith input, wij is the weight between input i and neuron j, and yj is the jth output. Therefore, y1 ¼ f(x1.w11 þ x2.w21 þ x3.w31), y2 ¼ f(x1.w12 þ x2.w22 þ x3.w32), and y3 ¼ f(x1.w13 þ x2.w23 þ x3.w33), where f( ) is the activation function (linear threshold, sigmoid, etc.). A ‘‘ring’’ architecture for the NN of figure 12.12 is presented in figure 12.13, which implements one layer of the NN. Each box represents one neuron. As shown, there are several circular shift registers, one for each neuron (vertical shifters) plus one for the whole set (horizontal shifter). The vertical shifters hold the weights, while the horizontal one holds the inputs (shift registers with ‘data_load’ capability). Notice TLFeBOOK Additional System Designs 295 Figure 12.13 Ring architecture for NN implementation. that the relative position of the weights in their respective registers must match that of the input values. At the output of a vertical shifter there is a MAC circuit (section 12.3), which accumulates the product between the weights and the inputs. All shifters use the same clock signal. Therefore, after one complete circulation, the following values will be available at the output of the MAC circuits: x1.w11 þ x2.w21 þ x3.w31, x1.w12 þ x2.w22 þ x3.w32, and x1.w13 þ x2.w23 þ x3.w33. These values are then applied to a LUT (lookup table), which implements the activation function (sigmoid, for example), thus producing the actual outputs, yi, of the NN. In this kind of circuit, truncation must be considered. Say that the inputs and weights are 16 bits long. Then at the output of the MAC cells 32-bit numbers would be the natural choice. However, since the actual outputs (after the LUT) might be connected to another layer of neurons, truncation to 16 bits is required. This can be done in the LUT or in the MAC circuit. Another approach is presented in figure 12.14, which is appropriate for generalpurpose NNs (that is, with programmable weights). It employs only one input to load all weights (thus saving on chip pins). In figure 12.14, the weights are shifted in sequentially until each register is loaded with its respective weight. The weights are then multiplied by the inputs and accumulated to produce the desired outputs. TLFeBOOK 296 Chapter 12 Figure 12.14 NN implementation with only one input for the weights. Two VHDL codes are presented below, both implementing the architecture of figure 12.14. However, in both solutions the LUT was not included (this will be treated in problem 12.5). The main di¤erence between these two solutions is that the first code is not as generic, and is therefore adequate for specific, small designs. The second solution, being generic, is reusable and easily adaptable to any NN size. Solution 1: For Small Neural Networks The solution below has the advantage of being simple, easily understandable, and self-contained in the main code. Its only limitation is that the inputs (x) and outputs (y) are specified one by one rather than using some kind of two-dimensional array, thus making it inappropriate for large NNs. Everything else is generic. 1 2 3 4 5 ----------------------------------------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; USE ieee.std_logic_arith.all; -- package needed for SIGNED ----------------------------------------------------------- TLFeBOOK Additional System Designs 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 297 ENTITY nn IS GENERIC ( n: INTEGER := 3; -- # of neurons m: INTEGER := 3; -- # of inputs or weights per neuron b: INTEGER := 4); -- # of bits per input or weight PORT ( x1: IN SIGNED(b-1 DOWNTO 0); x2: IN SIGNED(b-1 DOWNTO 0); x3: IN SIGNED(b-1 DOWNTO 0); w: IN SIGNED(b-1 DOWNTO 0); clk: IN STD_LOGIC; test: OUT SIGNED(b-1 DOWNTO 0); -- register test output y1: OUT SIGNED(2*b-1 DOWNTO 0); y2: OUT SIGNED(2*b-1 DOWNTO 0); y3: OUT SIGNED(2*b-1 DOWNTO 0)); END nn; ----------------------------------------------------------ARCHITECTURE neural OF nn IS TYPE weights IS ARRAY (1 TO n*m) OF SIGNED(b-1 DOWNTO 0); TYPE inputs IS ARRAY (1 TO m) OF SIGNED(b-1 DOWNTO 0); TYPE outputs IS ARRAY (1 TO m) OF SIGNED(2*b-1 DOWNTO 0); BEGIN PROCESS (clk, w, x1, x2, x3) VARIABLE weight: weights; VARIABLE input: inputs; VARIABLE output: outputs; VARIABLE prod, acc: SIGNED(2*b-1 DOWNTO 0); VARIABLE sign: STD_LOGIC; BEGIN ----- shift register inference: ------------IF (clk'EVENT AND clk='1') THEN weight := w & weight(1 TO n*m-1); END IF; --------- initialization: ------------------input(1) := x1; input(2) := x2; input(3) := x3; ------ multiply-accumulate: ----------------L1: FOR i IN 1 TO n LOOP acc := (OTHERS => '0'); TLFeBOOK 298 Chapter 12 44 L2: FOR j IN 1 TO m LOOP 45 prod := input(j)*weigth(m*(i-1)+j); 46 sign := acc(acc'LEFT); 47 acc := acc + prod; 48 ---- overflow check: ----------------49 IF (sign=prod(prod'left)) AND 50 (acc(acc'left) /= sign) THEN 51 acc := (acc'LEFT => sign, OTHERS => NOT sign); 52 END IF; 53 END LOOP L2; 54 output(i) := acc; 55 END LOOP L1; 56 --------- outputs: -------------------------57 test <= weight(n*m); 58 y1 <= output(1); 59 y2 <= output(2); 60 y3 <= output(3); 61 END PROCESS; 62 END neural; 63 ----------------------------------------------------------------- Simulation results are shown in figure 12.15. Notice that a small number of bits and a small quantity of neurons were used in order to ease the visualization of the simulation results. As can be seen in lines 7–9 of the code above, the NN has three neurons with three 4-bit inputs each. Since type SIGNED was employed, the range Figure 12.15 Simulation results of NN implemented in solution 1. TLFeBOOK Additional System Designs 299 of the input values and weights runs from 8 to 7, and the range of the outputs (8 bits) runs from 128 to 127. The inputs were kept fixed at x1 ¼ 3, x2 ¼ 4, and x3 ¼ 5. Since there are nine weights, nine clock cycles are needed to shift them in, as shown in figure 12.5. The values chosen for the weights were w9 ¼ 1, w8 ¼ 2, . . . , w1 ¼ 9 (notice that the first weight in is indeed w9, for it is shifted nine positions over). Recall, however, that 9 is indeed 7, and 8 is 8, because our data type is SIGNED. Therefore, after the weights have been all loaded, the system immediately furnishes its first set of outputs; that is: y1 ¼ x1.w1 þ x2.w2 þ x3.w3 ¼ (3)(7) þ (4)(8) þ (5)(7) ¼ 18 (represented as 256  18 ¼ 238); y2 ¼ x1.w4 þ x2.w5 þ x3.w6 ¼ (3)(6) þ (4)(5) þ (5)(4) ¼ 58; and y3 ¼ x1.w7 þ x2.w8 þ x3.w9 ¼ (3)(3) þ (4)(2) þ (5)(1) ¼ 22. These values (238, 58, and 22) can be seen at the right end of figure 12.15. Solution 2: For Large Neural Networks The code below is generic. Moreover, the inputs and outputs were declared as twodimensional arrays (section 3.5), thus easily allowing the construction of NNs of any size. To specify the arrays needed in the design, a PACKAGE named my_data_types was employed. As can be seen, it contains two user-defined data types, vector_ array_in and vector_array_out. The PACKAGE was then made visible to the design by means of a USE clause (line 5 of the main code). In this way, the new data types are truly global, and so can be used even in the ENTITY of the main code (that is, in the specification of PORT). These data types were used to specify the inputs and outputs of the systems (lines 11 and 15, respectively). Therefore, all parameters are now generic and easily modifiable, regardless of the size of the NN to be constructed. Notice in the code below that this solution was divided into two very short parts: sequential logic (shift register implementation) in lines 26–28, followed by combinational logic (MAC) implementation. A test output (for checking the last register) was also included, which is obviously optional. As in all our previous MAC circuit implementations, a routine to check for overflow was also included (lines 39–41). 1 2 3 4 5 6 7 -------- Package my_data_types: ---------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; USE ieee.std_logic_arith.all; -- package needed for SIGNED ---------------------------PACKAGE my_data_types IS CONSTANT b: INTEGER := 3; -- # of bits per input or weight TLFeBOOK 300 Chapter 12 8 TYPE vector_array_in IS ARRAY (NATURAL RANGE <>) OF 9 SIGNED(b-1 DOWNTO 0); 10 TYPE vector_array_out IS ARRAY (NATURAL RANGE <>) OF 11 SIGNED(2*b-1 DOWNTO 0); 12 END my_data_types; 13 -----------------------------------------------------------1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 --------- Project nn (main code): -------------------------LIBRARY ieee; USE ieee.std_logic_1164.all; USE ieee.std_logic_arith.all; -- package needed for SIGNED USE work.my_data_types.all; -- package of user-defined types -----------------------------------------------------------ENTITY nn3 IS GENERIC ( n: INTEGER := 3; -- # of neurons m: INTEGER := 3; -- # of inputs or weights per -- neuron b: INTEGER := 3); -- # of bits per input or -- weight PORT ( x: IN VECTOR_ARRAY_IN (1 TO m); w: IN SIGNED(b-1 DOWNTO 0); clk: IN STD_LOGIC; test: OUT SIGNED(b-1 DOWNTO 0); -- register test -- output y: OUT VECTOR_ARRAY_OUT(1 TO n)); END nn3; ---------------------------------------------------------------ARCHITECTURE neural OF nn3 IS BEGIN PROCESS (clk, w, x) VARIABLE weight: VECTOR_ARRAY_IN (1 TO m*n); VARIABLE prod, acc: SIGNED(2*b-1 DOWNTO 0); VARIABLE sign: STD_LOGIC; BEGIN ----- shift register inference: -------------IF (clk'EVENT AND clk='1') THEN weight := w & weight(1 TO n*m-1); END IF; TLFeBOOK Additional System Designs 301 32 test <= weight(n*m); 33 ---- initialization: ------------------------34 acc := (OTHERS => '0'); 35 ------ multiply-accumulate: -----------------36 L1: FOR i IN 1 TO n LOOP 37 L2: FOR j IN 1 TO m LOOP 38 prod := x(j)*weight(m*(i-1)+j); 39 sign := acc(acc'LEFT); 40 acc := acc + prod; 41 ---- overflow check: -----------------42 IF (sign=prod(prod'LEFT)) AND 43 (acc(acc'LEFT)/=sign) THEN 44 acc := (acc’LEFT => sign, OTHERS => NOT sign); 45 END IF; 46 END LOOP L2; 47 ------ output: --------------------------48 y(i) <= acc; 49 acc := (OTHERS => ’0’); 50 END LOOP L1; 51 END PROCESS; 52 END neural; 53 ----------------------------------------------------------------- Other aspects related to the design of NNs will be treated in problem 12.5. 12.6 Problems This section contains a series of problems regarding the use of system-level VHDL units (PACKAGES, COMPONENTS, FUNCTIONS, and PROCEDURES). Problem 12.1: Parallel Multiplier We have seen, in section 12.2, the implementation of a parallel multiplier from scratch. It was also mentioned that the pre-defined ‘‘*’’ (multiplication) operator implements a parallel multiplier too. Though there are several architectures for such a circuit (one was shown in figure 12.3), it is reasonable to assume that the amount of hardware necessary to implement either solution presented in section 12.2 (from scratch or using ‘‘*’’) should not di¤er substantially. You are asked to synthesize TLFeBOOK 302 Chapter 12 q3 q2 q1 q0 d DFF DFF DFF DFF clk MUX q sel Figure P12.2. both solutions and compare the resulting report files. Choose several PLD/FPGA target chips. What is the number of product terms and logic cells required in each case? Are their quantities of the same order? Problem 12.2: Shifter Consider the 4-stage shift register of figure P12.2, whose actual output (q) is selected by means of a multiplexer. Say that the data bus is eight-bit wide (thus each register is composed of eight D-type flip-flops). (a) Create two COMPONENTS, reg and mux, and then make use of them to construct the complete circuit of figure P12.2. (b) Assume now that we want to implement only the shift register, without the multiplexer, but that all registered values (q0, q1, q2, and q3) must be available at the output. Write a VHDL code for such a circuit. (c) Let us consider the same situation of (b) above. However, we now want the design to be generic (that is, to have n stages, and b bits per stage, with such parameters specified by means of a GENERIC statement). In this case, an user-defined array will be necessary to specify the outputs (call the outputs qout). Write such a code. (Suggestion: review section 3.5 and/or examine the second design of section 12.5). (d) Finally, in continuation to the design of (c) above, assume that we want to add ‘data load’ capability to the shift register. Add an extra input (call it x) to each register and an extra pin to (call it load ), such that when load is asserted all registers are overwritten with the values presented at the inputs. For x, the same user-defined TYPE created for qout can (and should) be used. TLFeBOOK Additional System Designs Problem 12.3: 303 MAC Circuit In section 12.3, we studied the implementation of a MAC (multiply-accumulate) circuit (figure 12.6). In the implementation shown there, a FUNCTION was employed, but COMPONENTS were not. Write another solution, this time using COMPONENTS (multiplier, adder, and register). Create the components, then instantiate them in the main code. Compile and simulate your project, comparing your results with those obtained in figure 12.7 Problem 12.4: General Purpose FIR Filter In section 12.4, we discussed the implementation of FIR filters. One complete design was presented, in which the coe‰cients of the filter were fixed (figure 12.9). For a general purpose filter (programmable coe‰cients), a modular architecture was suggested in figure 12.11. You are asked to write a VHDL code for that filter. As a suggestion, review first sections 12.3 and 12.4. Do not forget to include overflow check in your design. Consider that the number of bits of all signals from the input (x and coef ) up to the multiplier inputs is m, and 2m from there on (that is, from the multiplier outputs up to y). Consider also that the number of taps (stages) is n. Write a code as generic as possible. Then synthesize and simulate your circuit. Problem 12.5: Neural Network In section 12.5, we discussed the implementation of a highly interconnected system: a neural network. Two architectures were presented, and two VHDL codes were written regarding the second architecture. However, the LUT was not included in those solutions. In this problem, the following is asked: (a) Write a VHDL code that implements a LUT (you can choose the function to be implemented, because what we want to practice here is how to implement a LUT). Recall that a lookup table is simply a ROM (section 9.10). (b) Write a VHDL code that implements the neural architecture depicted in figure 12.13. Then synthesize and simulate your solution to verify whether it works as expected. (c) There certainly are other ways of implementing a NN besides the two approaches presented in section 12.5. Can you suggest another one? Can you suggest improvements on the architectures and solutions presented there? TLFeBOOK TLFeBOOK Appendix A: Programmable Logic Devices A1. Introduction Programmable Logic Devices (PLDs) were introduced in the mid 1970s. The idea was to construct combinational logic circuits that were programmable. However, contrary to microprocessors, which can run a program but posses a fixed hardware, the programmability of PLDs was intended at the hardware level. In other words, a PLD is a general purpose chip whose hardware can be reconfigured to meat particular specifications. The first PLDs were called PAL (Programmable Array Logic) or PLA (Programmable Logic Array), depending on the programming scheme (discussed later). They used only logic gates (no flip-flops), thus allowing only the implementation of combinational circuits. To circumvent this problem, registered PLDs were launched soon after, which included one flip-flop at each output of the circuit. With them, simple sequential functions could then be implemented as well. In the beginning of the 1980s, additional logic circuitry was added to each PLD output. The new output cell, called Macrocell, contained (besides the flip-flop) logic gates and multiplexers. Moreover, the cell itself was programmable, allowing several modes of operation. Additionally, it provided a ‘return’ (feedback) signal from the output of the circuit to the programmable array, which gave the PLD greater flexibility. This new PLD structure was called generic PAL (GAL). A similar architecture was known as PALCE (PAL CMOS Electrically erasable/programmable) device. All these chips (PAL, PLA, registered PLD, and GAL/PALCE) are now collectively referred to as SPLDs (Simple PLDs). The GAL/PALCE device is the only still manufactured in a standalone package. Later, several GAL devices were fabricated on the same chip, using a more sophisticated routing scheme, more advanced silicon technology, and several additional features (like JTAG support and interface to several logic standards). This approach became known as CPLD (Complex PLD). CPLDs are currently very popular due to their high density, high performance, and low cost (CPLDs under a dollar can be found). Finally, in the mid 1980s, FPGAs (Field Programmable Gate Arrays) were introduced. FPGAs di¤er from CPLDs in architecture, technology, built-in features, and cost. They are aimed mainly at the implementation of large size, high-performance circuits. TLFeBOOK 306 Appendix A A summary of the evolution of PLDs is presented in the table below. Simple PLD (SPLD) PLDs PAL PLA Registered PAL/PLA GAL Complex PLD (CPLD) FPGA A final remark: all PLDs (simple or complex) are non-volatile. They can be OTP (one-time programmable), in which case fuses or antifuses are used, or can be reprogrammable, with EEPROM or Flash memory (Flash is the technology of choice in most new devices). FPGAs, on the other hand, are mostly volatile, for they make use of SRAM to store the connections, in which case a configuration ROM is necessary to load the interconnects at power up. There are, however, non-volatile options, like the use of antifuse. Examples of each alternative will be shown later. A2. SPLDs (Simple PLDs) As mentioned above, PAL, PLA, and GAL devices are collectively called Simple PLDs (SPLDs). A description of each of these architectures follows. PAL Devices PAL (Programmable Array Logic) chips were introduced by Monolithic Memories in the mid 1970s. Its basic architecture is illustrated symbolically in figure A1, where the little circles represent programmable connections. As can be seen, the circuit is composed of a programmable array of AND gates, followed by a fixed array of OR gates. The implementation of figure A1 was based on the fact that any combinational function can be represented by a Sum-of-Products (SOP); that is, if a1 , a2 , . . . , aN are the logic inputs, then any combinational output x can be computed as x ¼ m1 þ m2 þ    þ mM ; where mi ¼ fi (a1 , a2 , . . . , aN ) are the minterms of the function x. For example x ¼ a1 a2 þ a2 a3 a4 þ a1 a2 a3 a4 a5 : TLFeBOOK Programmable Logic Devices 307 inputs programmable interconnects outputs Figure A1 Illustration of PAL architecture. Hence, the products (minterms) can be obtained by means of AND gates, whose outputs are then connected to an OR gate to compute their sum, thus implementing the SOP equation described above. The main limitation of this approach was the fact that it allowed only the implementation of combinational functions. To circumvent this problem, registered PALs were launched toward the end of the 1970s. These included a flip-flop at each output (after the OR gates in figure A1), thus allowing the implementation of sequential functions as well (though only very simple ones). An example of a then popular PAL chip is the PAL16L8 device, which contained 16 inputs and 8 outputs (though only 18 I/O pins were indeed available, because it was a 20-pin DIP package; there were ten IN pins, two OUT pins, and six IN/OUT TLFeBOOK 308 Appendix A inputs programmable interconnects programmable interconnects outputs Figure A2 Illustration of PLA architecture. pins (bidirectional), plus VCC and GND). Its registered counterpart was the 16R8 chip (where R stands for Registered). The early technology employed in the fabrication of PAL devices was bipolar, with 5 V supply and current consumption (with open outputs) around 200 mA. The maximum frequency was of the order of 100 MHz, and the programmable cells were of PROM (fuse links) or EPROM (20min UV erase time) type. PLA Devices PLA (Programmable Logic Array) chips were also introduced in the mid 1970s (by Signetics). The basic architecture of a PLA is illustrated symbolically in figure A2. Comparing it with figure A1, we observe that the only fundamental di¤erence between them is that while a PAL has programmable AND connections and fixed OR TLFeBOOK Programmable Logic Devices 309 connections, both are programmable in a PLA. The obvious advantage was greater flexibility. However, higher time constants at the internal nodes lowered the circuit speed. An example of a then popular PLA chip is the Signetics PLS161 device. It contained 12 inputs and 8 outputs, being the AND inputs and the OR inputs all programmable. A total of 48 12-input AND gates were available, followed by a total of 8 48-input OR gates. At the outputs, additional programmable XOR gates were also available. The technology then employed in the fabrication of PLAs was the same as that of PALs. Though PLAs are also obsolete now, they reappeared recently as a building block in the first family of low power CPLDs, the CoolRunner family (from Xilinx—to be described later). GAL Devices The GAL (Generic PAL) architecture was introduced by Lattice in the beginning of the 1980s. It contained several important improvements over the first PAL devices: first, a more sophisticated output cell (Macrocell) was constructed, which included, besides the flip-flop, several gates and multiplexers; second, the Macrocell itself was programmable, allowing several modes of operation; third, a ‘return’ signal from the output of the Macrocell to the programmable array was also included, conferring the circuit more versatility; fourth, EEPROM was employed instead of PROM or EPROM. An electronic signature for identification was also included. As mentioned earlier, GAL is the only SPLD (Simple PLD) still manufactured in a standalone package. Additionally, it also serves as the basic building block in the construction of most CPLDs (there are exceptions, however, like the CoolRunner CPLD mentioned above, which employs PLAs instead). Figure A3 shows an example of GAL device, the GAL16V8 (where V stands for Versatile). It is a 16-input, 8-output circuit in a 20-pin package. As can be seen, the actual configuration is eight IN pins (pis 2–9) and eight IN/OUT pins (pins 12–19), plus CLK (pin 1), /OE (–Output Enable, pin 11), VDD (pin 20), and GND (pin 10). At each output there is a Macrocell (after the OR gate), which contains, besides the flip-flop, logic gates and multiplexers. A feedback signal from the Macrocell to the programmable array can also be observed. The programmable interconnections are represented by small circles. Notice that this architecture directly resembles that of a PAL (figure A1), except for the presence of a macrocell at each output and the feedback signal. Current GAL devices use CMOS technology, 3.3 V supply, EEPROM or Flash technology, and maximum frequency around 250 MHz. Several companies manufacture them (Lattice, Atmel, TI, etc.). TLFeBOOK Figure A3 GAL 16V8 chip. TLFeBOOK Programmable Logic Devices 311 Figure A4 CPLD architecture. A3. CPLD (Complex PLD) The basic approach in the construction of a CPLD is illustrated in figure A4. As shown, it consists of several PLDs (in general of GAL type) fabricated on a single chip, with a programmable switch matrix used to connect them together and to the I/O pins. Moreover, CPLDs normally contain a few additional features, like JTAG support and interface to other logic standards (1.8 V, 2.5 V, 5 V, etc.). Regarding figure A4, as an example we can mention the Xilinx XC9500 CPLD. It consists of n PLDs, each resembling a 36V18 GAL device (therefore similar to the 16V8 architecture of figure A3, but with 36 inputs and 18 outputs, instead of 16 inputs and 8 outputs, thus with 18 Macrocells each), where n ¼ 2, 4, 6, 8, 12, or 16. Several companies manufacture CPLDs, like Altera, Xilinx, Lattice, Atmel, Cypress, etc. Examples from two companies (Altera and Xilinx) are illustrated in tables A1 and A2. As can be seen, over 500 macrocells and over 10,000 gates can be found in these devices. A4. FPGA Field Programmable Gate Array (FPGA) devices were introduced by Xilinx in the mid 1980s. They di¤er from CPLDs in architecture, storage technology, number of built-in features, and cost, and are aimed at the implementation of high performance, large-size circuits. TLFeBOOK 312 Appendix A Table A1 Altera CPLDs. Family Max7000 (B, AE, S) MAX3000 (A) MAX II (G) Macrocells/ LUTs 32–512 macrocells 32–512 macrocells 240–2,210 LUTs (192–1,700 equiv. macrocells) System gates 600–10,000 600–10,000 I/O pins 32–512 34–208 80–272 Max. internal clock freq. 303 MHz 227 MHz 304 MHz (I/O limited) Supply voltage 2.5 V (B), 3.3 V (AE), 5 V (S) 3.3 V 1.8 V (G), 2.5 V, 3.3 V Interconnects EEPROM EEPROM Flash þ SRAM Static current 9 mA–450 mA 9 mA–150 mA 2 mA–50 mA Technology 0.22 u CMOS EEPROM 4-layer metal (7000 B) 0.3 u, 4-layer metal 0.18 u, 6-layer metal Table A2 Xilinx CPLDs. Family XC9500 (XV, XL, ) CoolRunner XPLA3 CoolRunner II Macrocells 36–288 32–512 32–512 System gates 800–6,400 750–12,000 750–12,000 I/O pins 34–192 36–260 33–270 Max. internal clock frequency 222 MHz 213 MHz 385 MHz Building block GAL 54V18 (XV, XL) GAL 36V18 () PLA block PLA block Supply voltage 2.5 V (XV), 3.3 V (XL), 5 V 3.3 V 1.8 V Interconnects Flash EEPROM Technology 0.35 u CMOS 0.35 u CMOS 0.18 u CMOS Static current 11–500 mA <0.1 mA 22 uA–1 mA TLFeBOOK Programmable Logic Devices 313 Figure A5 FPGA architecture. The basic architecture of an FPGA is illustrated in figure A5. It consists of a matrix of CLBs (Configurable Logic Blocks), interconnected by an array of switch matrices. The internal architecture of a CLB (figure A5) is di¤erent from that of a PLD (figure A4). First, instead of implementing SOP expressions with AND gates followed by OR gates (like in SPLDs), its operation is normally based on a LUT (lookup table). Moreover, in an FPGA the number of flip-flops is much more abundant than in a CPLD, thus allowing the construction of more sophisticated sequential circuits. Besides JTAG support and interface to diverse logic levels, other additional features are also included in FPGA chips, like SRAM memory, clock multiplication (PLL or DLL), PCI interface, etc. Some chips also include dedicated blocks, like multipliers, DSPs, and microprocessors. Another fundamental di¤erence between an FPGA and a CPLD refers to the storage of the interconnects. While CPLDs are non-volatile (that is, they make use of antifuse, EEPROM, Flash, etc.), most FPGAs use SRAM, and are therefore volatile. This approach saves space and lowers the cost of the chip because FPGAs present a very large number of programmable interconnections, but requires an external ROM. There are, however, non-volatile FPGAs (with antifuse), which might be advantageous when reprogramming is not necessary. TLFeBOOK 314 Appendix A Figure A6 Examples of FPGA packages. Table A3 Xilinx FPGAs. Family Virtex II Pro (X) Virtex II Virtex E Virtex Spartan 3 Spartan IIE Spartan II Logic blocks (CLBs) 352– 11,024 64– 11,648 384– 16,224 384– 6,144 192– 8,320 384– 3,456 96– 1,176 Logic cells 3,168– 125,136 576– 104,882 1,728– 73,008 1,728– 27,648 1,728– 74,880 1,728– 15,552 432– 5,292 40 k– 8M 72 k– 4M 58 k– 1.1 M 50 k– 5M 23 k– 600 k 15 k– 200 k System gates I/O pins 204– 1,200 88–1108 176–804 180–512 124–784 182–514 86–284 Flip-flops 2,816– 88,192 512– 93,184 1,392– 64,896 1,392– 24,576 1,536– 66,560 1,536– 13,824 384– 4,704 Max. internal frequency 547 MHz 420 MHz 240 MHz 200 MHz 326 MHz 200 MHz 200 MHz Supply voltage 1.5 V 1.5 V 1.8 V 2.5 V 1.2 V 1.8 V 2.5 V Interconnects SRAM SRAM SRAM SRAM SRAM SRAM SRAM Technology 0.13 u 9-layer copper CMOS 0.15 u 8-layer metal CMOS 0.18 u 6-layer metal CMOS 0.22 u 5-layer metal CMOS 0.09 u 8-layer metal CMOS SRAM bits (Block RAM) 216 k– 8M 72 k– 3M 64 k– 832 k 32 k– 128 k 72 k– 1.8 M 32 k– 288 k 16 k– 56 k TLFeBOOK Programmable Logic Devices 315 Table A4 Actel FPGAs. Family Accelerator ProASIC MX SX eX Logic modules 2,016–32,256 5,376–56,320 295–2,438 768–6,036 192–768 System gates 125 k–2 M 75 k–1 M 3 k–54 k 12 k–108 k 3 k–12 k I/O pins 168–684 204–712 57–202 130–360 84–132 Flip-flops 1,344–21,504 5,376–26,880 147–1,822 512–4,024 128–512 Max. internal frequency 500 MHz 250 MHz 250 MHz 350 MHz 350 MHz Supply voltage 1.5 V 2.5 V, 3.3 V 3.3 V, 5 V 2.5 V, 3.3 V, 5V 2.5 V, 3.3 V, 5V Interconnects Antifuse Flash Antifuse Antifuse Antifuse Technology 0.15 u 7-layer metal CMOS 0.22 u 4-layer metal CMOS 0.45 um 3-layer metal CMOS 0.22 u CMOS 0.22 u CMOS SRAM bits 29 k–339 k 14 k–198 k 2.56 k n.a. n.a. FPGAs can be very sophisticated. Chips manufactured with state-of-the-art 0.09 mm CMOS technology, with nine copper layers and over 1,000 I/O pins, are currently available. A few examples of FPGA packages are illustrated in figure A6, which shows one of the smallest FPGA packages on the left (64 pins), a medium-size package in the middle (324 pins), and a large package (1,152 pins) on the right. Several companies manufacture FPGAs, like Xilinx, Actel, Altera, QuickLogic, Atmel, etc. Examples from two companies (Xilinx and Actel) are illustrated in tables A3 and A4. As can be seen, they can contain thousands of flip-flops and several million gates. Notice that all Xilinx FPGAs use SRAM to store the interconnects, so are reprogrammable, but volatile (thus requiring external ROM). On the other hand, Actel FPGAs are non-volatile (they use antifuse), but are non-reprogrammable (except one family, which uses Flash memory). Since each approach has its own advantages and disadvantages, the actual application will dictate which chip architecture is most appropriate. TLFeBOOK TLFeBOOK Appendix B: Xilinx ISE B ModelSim Tutorial The following synthesis, placement, and simulation tools are described in the tutorials presented in the Appendices: Tools Application Appendix ISE 6.1 þ ModelSim 5.7c Xilinx CPLDs and FPGAs B MaxPlus II 10.2 þ Advanced Synthesis Software Altera CPLDs and some FPGAs C Quartus II 3.0 Altera CPLDs and FPGAs D XiIinx ISE 6.1 is a comprehensive synthesis and implementation environment for Xilinx programmable devices. ModelSim XE 5.7c (from Model Technology) is also provided as part of the package. The former is employed for circuit synthesis and design implementation, while the latter is used for simulation. Xilinx ISE 6.1 WebPack, along with ModelSim XE II 5.7c Starter, can be downloaded cost-free from www.xilinx.com. This is a very brief tutorial, which is divided into five parts: B1. Entering VHDL Code B2. Synthesis and Implementation B3. Creating Testbenches B4. Simulation (with ModelSim) B5. Physical Realization B1.  Entering VHDL Code Launch ISE 6.1 Project Navigator. A screen like that of figure B1 will be displayed. Start a new project (File ! New Project). The dialog box of figure B2 will be shown. In the Project Name field, type the name of the ENTITY of the VHDL code to be entered (flipflop, in this example). In the Project Location field, choose the working directory. Finally, select HDL as the top level module type. Click on Next.  In the dialog box of figure B3, select the device (Spartan 3, for example). Then select XST (Xilinx Synthesis Technology) as the synthesis tool, ModelSim as the simulator, and VHDL as the language. Click on Next.  TLFeBOOK 318 Appendix B Figure B1 Figure B2 TLFeBOOK Xilinx ISE þ ModelSim Tutorial 319 Figure B3 In the dialog box of figure B4, select VHDL Module, then type the file name (flipflop.vhd, in this example), and choose its location. Click on Next and Finish until the text editor is displayed, as in figure B.5.  Enter your VHDL code (figure B5) and save it. The project is now ready to be synthesized.  B2. Synthesis and Implementation In the Processes for Source window, select Synthesize-XST. Then go to Process ! Properties. The box of figure B6 will be shown. Select Optimization Goal ¼ Area and Optimization E¤ort ¼ Normal, then click on OK.  To synthesize the design, select Process ! Run, or click on , or double-click on Synthesize-XST. However, if desired, the syntax can be checked before synthesis is invoked. Just click on the ‘‘þ’’ sign before the word Synthesize-XST to expand it (see figure B7) and double-click on Check Syntax.  After synthesis is concluded, view the synthesis report. Double-click on View Synthesis Report, under Synthesize-XST, in the Processes for Source window (figure B7). To better view the report, you can use the toggle tool . A section of such a report is presented in figure B8. Check, for example, the number of flip-flops inferred by the compiler.  Check also the RTL diagram. Double-click on View RTL Schematic, under the Synthesize-XST directory. The diagram of figure B9 will be presented.  TLFeBOOK 320 Appendix B Figure B4 Figure B5 TLFeBOOK Xilinx ISE þ ModelSim Tutorial 321 Figure B6 Figure B7 Now the design can be implemented. Double-click on the Implement Design option in the Processes for Source window (figure B7).  After the implementation is concluded, expand the Implement Design option and check the several reports produced, particularly the Pad Report (under the Place & Route directory). Check which pin was assigned to each signal.  Play with the Floorplanner. Double-click on View/Edit Placed Design (Floorplanner), under the Place & Route directory. Select View ! Hierarchy, View ! Floorplan, View ! Placement, View ! Package Pins. Now examine each one of windows created. Move the cursor over the pins of the chip to see their descriptions.  TLFeBOOK 322 Appendix B Release 6.1i - xst G.23 =============================== Input File Name: flipflop.prj Output File Name : flipflop Output Format: NGC Target Device: xc3s50-4-pq208 Optimization Goal: Area Optimization Effort: 1 Keep Hierarchy: NO Global Optimization: AllClockNets RTL Output: Yes =============================== Synthesizing Unit <flipflop>. Related source file is c:/xilinx6.1/my_projects/flipflop.vhd. Found 1-bit register for signal <q>. Summary: inferred 1 D-type flip-flop(s). Unit <flipflop> synthesized. HDL Synthesis Report Macro Statistics # Registers: 1 1-bit register: 1 =============================== Cell Usage : # FlipFlops/Latches: 1 # FDC: 1 # Clock Buffers: 1 # BUFGP: 1 # IO Buffers: 3 # IBUF: 2 # OBUF: 1 =============================== Device utilization summary: Selected Device : 3s50pq208-4 Number of Slices: 1 out of 768 0% Figure B8 Figure B9 Note: Had a CPLD (CoolRunner, for example) been chosen instead of an FPGA (Spartan 3, in this example), the list of options in the Processes for Source window would be a little di¤erent. Try, for example, to double-click on the device description (xc3-s50. . .) in the Sources in Project window. This will bring back the dialog box of figure B3. Change the device to CoolRunner 2. Press OK and then observe the new list of options displayed in the Processes for Source window. B3. Creating Testbenches (with HDL Bencher) HDL Bencher allows the creation of testbenches (waveforms). Then ModelSim can be invoked to perform the actual simulation (ModelSim XE II 5.7c Starter is one of the cost-free third-party softwares provided along with Xilinx ISE 6.1 WebPack). TLFeBOOK Xilinx ISE þ ModelSim Tutorial 323 Figure B10 Select Project ! New Source. The dialog box of figure B10 will be displayed. Select Test Bench Waveform, then type the desired file name (flipflop_tbw, for example). Finally, check whether the project location is correct and click on Next until HDL Bencher is launched (figure B11).  When HDL Bencher starts, a screen like that of figure B11 is displayed, which allows the clock signal to be set. Notice that the input signal clk was chosen as the master clock. Type in its parameters and then click on OK. The waveforms screen shown in figure B12 is then displayed.  The position of any signal in figure B12 can be changed by just dragging it up or down. Also, if the clk waveform must be changed, click on or click the right mouse button in the area under the waveforms, which will cause the dialog box of figure B11 to be presented again.  We must now set up the values of the other signals in figure B12 (rst and d). To do so, just click on the vertical grid line after which you want the value of the signal to be changed. An example, after all input signals have been set up, is shown in figure B13.  Define the end time of the testbenches. To do so, click the right mouse button in the area under the curves and select Set End of Testbench, then drag the blue line to the desired position.  TLFeBOOK 324 Appendix B Figure B11 Figure B12 Figure B13 TLFeBOOK Xilinx ISE þ ModelSim Tutorial 325 Figure B14 Save the testbenches file. Observe that a new file (flipflop_tbw.tbw) is then added to the Sources in Project window.  B4. Simulation (with ModelSim) Having finished creating the testbenches, ModelSim can now be invoked to perform the simulation. Indeed, several levels of simulation are available, including the following (see the complete list in the lower part of figure B14, under ModelSim Simulator):  Expected simulation results: Logical verification.  Behavioral simulation: Logical and timing verification.  Post-place & route simulation: Logical and timing verification after placement. TLFeBOOK 326 Appendix B Figure B15 Figure B16 Two of these simulation levels will be employed in the steps below. In the Sources in Project window, select the testbench file (flipflop_tbw.tbw). Notice then the several simulation options available under ModelSim Simulator in the Processes for Source window (figure B14).  Double-click on Generate Expected Simulation Results. This will run a background logical simulator, which will compute the output signals and then automatically launch HDL Bencher with the computed signals included in it. An example is shown in figure B15. Examine whether your project works as expected (from a logical point of view). Then exit HDL Bencher without saving the waveforms.  Now double-click on Simulate Post-Place & Route VHDL Model. ModelSim is launched and a detailed simulation is performed. Maximize the waveforms window and select Zoom ! Zoom Full. Examine again the results (figure B16).  TLFeBOOK Xilinx ISE þ ModelSim Tutorial B5. 327 Physical Realization To physically implement the design in a CPLD or FPGA chip, a development kit is necessary. Inexpensive alternatives are generally available through manufacturer’s university programs, which o¤er design kits at low prices. Xilinx Digilab XC2, for example, is a development kit for Xilinx CoolRunner II devices. The development kit must be connected to a PC running ISE in order for the chip to be programmed. Since the overall procedure of programming a chip is relatively similar from one manufacturer to another, a detailed description will be presented in only two of the appendices (C and D). TLFeBOOK TLFeBOOK Appendix C: Altera MaxPlus II B Advanced Synthesis Software Tutorial The following synthesis, placement, and simulation tools are described in the tutorials presented in the Appendices: Tools Application Appendix ISE 6.1 þ ModelSim 5.7c Xilinx CPLDs and FPGAs B MaxPlus II 10.2 þ Advanced Synthesis Software Altera CPLDs and some FPGAs C Quartus II 3.0 Altera CPLDs and FPGAs D MaxPlus II 10.2 Baseline from Altera is a very simple, user-friendly synthesis and simulation tool. Its main drawback is that it does not support several VHDL constructs, so only relatively simple code can be synthesized without the help of an external synthesis tool (like Leonardo Spectrum or Advanced Synthesis Software). Additionally, it only covers Altera’s basic devices (its successor, Quartus II, described in appendix D, covers all current devices). Still, due to its simplicity, it may be an adequate starting point for first-time VHDL users. Moreover, with the recent release of Advanced Synthesis Software, also a cost-free synthesis tool from Altera, using MaxPlus II became more e¤ective because Advanced Synthesis Software does support most VHDL constructs. It can be used to synthesize the VHDL code, generating an EDIF (.edf ) file which can then be imported by MaxPlus II for design implementation and simulation. MaxPlus II 10.2 Baseline and Advanced Synthesis Software can be downloaded cost-free from www.altera.com. This is a very brief tutorial, which is divided into five parts: C1. Entering VHDL Code C2. Compilation C3. Simulation C4. Synthesis with Advanced Synthesis Software C5. Physical Implementation TLFeBOOK 330 Appendix C Figure C1 C1.  Entering VHDL Code Launch MaxPlus II 10.2 Baseline. Open the text editor (MaxPlus II ! Text Editor), or open an existing project (File !Open). A blank screen (like that of figure C1, but without the text) will be displayed.  Enter your VHDL code (a D-type flip-flop is shown in figure C1). Save it with the extension .vhd and using the same name as the ENTITY’s (flipflop.vhd, in this example).  C2.  Compilation Set the project to the current file: File ! Project ! Set Project to Current File. Choose the target device (Assign ! Device). A pull down menu will be displayed (figure C2). Select the desired device (say, Family ¼ MAX3000A, Device ¼ AUTO).  TLFeBOOK Altera MaxPlus II þ Advanced Synthesis Software Tutorial 331 Figure C2 Figure C3 Set up the optimizer. The implementation can be optimized for speed or for area. Select Assign ! Global Project Logic Synthesis and move the Optimize cursor all the way to the left (value ¼ 0) to optimize for area, or all the way to the right (value ¼ 10) to optimize for speed. Values in between can also be used.   Click on the Compiler icon , then on Start, in order to execute the compilation. If no errors are detected, a screen like that of figure C3 is shown. It displays the files created during the compilation in the upper part (notice, for example, the report ‘‘rpt’’ file icon), and information regarding the chip and fitter in the lower part.  Open the report (.rpt) file (double-click on its icon, shown in figure C3). Verify at least the following: pin assignments and number of logic cells and flip-flops used to  TLFeBOOK 332 Appendix C #TDI RESERVED RESERVED GND RESERVED RESERVED #TMS RESERVED VCCIO RESERVED GND R R E E S V S E C E R C R V r I G G G c G V E s N N N N l N E D t d T D D D k D q D -----------------------------------_ / 6 5 4 3 2 1 44 43 42 41 40 | | 7 39 | | 8 38 | | 9 37 | | 10 36 | | 11 35 | | 12 EPM3032ALC44-4 34 | | 13 33 | | 14 32 | | 15 31 | | 16 30 | | 17 29 | |_ 18 19 20 21 22 23 24 25 26 27 28 _| -----------------------------------R R R R G V R R R R R E E E E N C E E E E E S S S S D C S S S S S E E E E I E E E E E R R R R N R R R R R V V V V T V V V V V E E E E E E E E E D D D D D D D D D Total Total Total Total Total Total Total RESERVED #TDO RESERVED GND VCCIO RESERVED RESERVED #TCK RESERVED GND RESERVED bidirectional pins required: reserved pins required logic cells required: flipflops required: product terms required: logic cells lending parallel expanders: shareable expanders in database: Synthesized logic cells: 0/32 0 4 1 1 2 0 0 (0%) Figure C4 TLFeBOOK Altera MaxPlus II þ Advanced Synthesis Software Tutorial 333 Figure C5 Figure C6 construct the circuit. A little section of the report file from the design of figure C1 is shown in figure C4. C3. Simulation Open the waveform editor (MaxPlus II ! Waveform Editor). A blank screen like that of figure C5 will be displayed (without the box in the center).  With the cursor inside the window of figure C5, press the right mouse button. A pull down menu like that in the center of figure C5 will be shown. Select Enter Nodes from SNF. The dialog box of figure C6 will then be presented. Click on List, then  TLFeBOOK 334 Appendix C Figure C7 Figure C8 on ¼>, and finally on OK. All signals listed in the ENTITY of the VHDL code will appear in the waveform window (see figure C7). Notice that the default value for the input signals is 0, while for the outputs it is X (unknown). Before establishing the values of the signals, define the length of the waveforms and the grid size. To set the length, select File ! End Time and type 1us. To set the grid, select Options ! Grid Size and type 50 ns. Finally, select View ! Fit in Window. You can also change the order of the signals by just dragging them up or down. For example, to have clk as the first signal, just place the cursor on the arrow that precedes the word clk, then press and hold the left mouse button and drag clk to the desired position. The window will then look like that of figure C8.  We must now define the input signals, so the tools of figure C9 can be used. The clock icon is used for pulse generators, to set the logic value 0, for logic  TLFeBOOK Altera MaxPlus II þ Advanced Synthesis Software Tutorial 335 Figure C9 Figure C10 value 1, for counters (incremental bus values), and a fixed value). for a group value (bus with Start with clk. Select the corresponding line (click the left mouse button on the word clk), then click on (figure C9), which will cause the dialog box of figure C10 to be displayed. Type Starting Value 0 and Multiplied By 1, then click on OK (Multiplied by 1 means that the period corresponds to one pair of time slots, with each time slot corresponding to one grid space; in this case, period ¼ 100 ns).  Set up the other input signals. For rst, select the first two time slots (0 to 100 ns). to change its value to 1 in this interval. Next, select the entire line Then click on of d (click the left mouse button on the word d) and click on again. Type Multiplied By 4 and click on OK. The waveforms should then look like those in figure C11.   Save your waveforms with the extension .scf (flipflop.scf ). Now the design is ready to be simulated. Click on the simulator icon and on Start. The simulator will automatically fill in all output signals in the waveform editor (q, in this example). The result is shown in figure C12.  TLFeBOOK 336 Appendix C Figure C11 Figure C12 C4. Synthesis with Advanced Synthesis Software To overcome the limitations of MaxPlus II, which does not support several VHDL constructs, Advanced Synthesis Software was recently released. It can be used to synthesize the VHDL code, giving origin to an EDIF (.edf ) file, which can then be imported by MaxPlus II to finish the design (fitting, simulation, programming). As mentioned earlier, Advanced Synthesis Software can also be downloaded cost-free from www.altera.com. Using a text editor, type your VHDL code. Suggestion: Since MaxPlus II will be used for fitting and simulation anyway, launch it and type the VHDL code using  TLFeBOOK Altera MaxPlus II þ Advanced Synthesis Software Tutorial 337 Figure C13 MaxPlus II’s own text editor, as described in section C1 above. Save the file with the extension .vhd and the same name as the ENTITY’s (flipflop.vhd). Launch Advanced Synthesis Software. A screen like that of figure C13 will be displayed.  Open a new project (File ! New Project). In the dialog box, type the name of the project (same as the ENTITY’s). The project will be saved with the extension .max2syn (flipflop.max2syn)  Assign the VHDL file to the project (Assign ! Add/remove HDL files). The box of figure C14 will be displayed. Click on Add, select the file, then click on Open and OK.  Click on the synthesis settings icon . The dialog box of figure C15 will be presented. Choose the target device (MAX3000A, for example) and VHDL93.  Click on the synthesis icon . If no syntax errors are detected, an EDIF file will be generated, with the extension .edf and the same name as the project’s (flipflop.edf ).  Return to MaxPlus II and import the EDIF file just created by Advanced Synthesis Software (File ! Open). Then start from the beginning of section C2 above, in order to compile the new design.  TLFeBOOK 338 Appendix C Figure C14 Figure C15 C5. Physical Implementation In this section, we will describe the process of physically implementing a circuit on a CPLD. In this description, Altera’s UP1 development kit will be utilized, which is furnished as part of their University Program. Other options are also available, either from Altera or other companies. Indeed, most CPLD/FPGA manufacturers o¤er low-cost development kits as part of their university programs. The Altera UP1 Board A view of the Altera UP1 kit is shown in figure C16. As can be seen, it contains two devices: TLFeBOOK Altera MaxPlus II þ Advanced Synthesis Software Tutorial 339 Figure C16 EPM7128SLC84-7 (from the MAX7000S family): This is a CPLD (appendix A) in an 84-pin package. It contains 128 macrocells, each having a PAL-type architecture and one flip-flop.  EPF10K20RC240-4 (from the FLEK10K family): This is an FPGA (appendix A) in a 240-pin package. It consists of 1,152 LEs (logic elements), each with a 4-bit LUT (lookup table) and one flip-flop.  For testing the CPLD, the board contains eight LEDs (light emitting diodes), two SSDs (seven-segment displays), and two eight-bit dip switches (figure C16). And, for testing the FPGA, 2 more SSDs and another eight-bit dip switch. The LEDs and the segments of the SSDs use negative logic, thus being turned on when 0 V is applied. The switches, on the other hand, provide 5 V signals when moved up or 0 V when moved down. The LEDs and switches are not connected to any of the chip pins, so they can be freely wired to the devices to satisfy any particular setup. However, the segments of the SSDs are already connected, thus requiring the implemented circuit to have specific pin assignments. In the case of the CPLD, the pins to which the SSDs are connected are those listed in figure C17. The board also contains a 25.175 MHz clock, which is connected to the devices (the global clock pin of the CPLD is pin 83). TLFeBOOK 340 Appendix C Figure C17 Table 2. JTAG Jumper Settings Desired Action Program EPM7128S device only Configure FLEX 10k device only Program/configure both devices Connect multiple boards together TDI TDO DEVICE BOARD C1 & C2 C1 & C2 C1 & C2 C1 & C2 C2 & C3 C2 & C3 C1 & C2 C1 & C2 C2 & C3 C1 & C2 C2 & C3 C1 & C2 C2 & C3 OPEN C2 & C3 C2 & C3 Figure C18 The complete specifications of the board are available at www.altera.com/ literature/univ/upds.pdf. Setting up the UP1 Board In the description presented below, the CPLD (EPM7128SLC84-7) will be used as the target device. Therefore, the jumpers in the TDO, TDI, DEVICE, and BOARD columns (see figure C16, right above the EPM7128S device) should all be installed in the upper position (that is, between the upper two pins, C1 and C2, of each column of pins, as indicated in the table of figure C18).  Connect the ByteBlaster cable provided with the kit between the board and the parallel port of the PC.  Connect the DC supply (9 V) to the board. Notice that the Power LED and two SSDs are lit.  TLFeBOOK Altera MaxPlus II þ Advanced Synthesis Software Tutorial 341 Figure C19 Implementing the Design We will assume that MaxPlus II 10.2 Baseline is open and that the VHDL code has already been entered and debugged, following the steps described in the previous sections of this appendix. Assign the target device by selecting Assign ! Device and choosing Family ¼ MAX7000S and Device ¼ EPM7128SLC84-7 (do not check the Select Only Fastest Speed Grade box).   Compile the circuit as before (click on ). Open the report (rpt) file and check which pin was assigned to each signal. If no changes are required, proceed to the next section. To change pins, proceed in the paragraph below.  To choose a pin for clock di¤erent from the automatic global clock assignment (pin 83), first go to Assign ! Global Project Logic Synthesis and unmark the box Clock under Automatic Global.  To choose the pins, select Assign ! Pin/Location/Chip ! Search ! List. A dialog box like that on the left of figure C19 will be displayed. Select a signal and click on OK, thus displaying the box on the right of figure C19. Choose the pin number and the pin type (input, output, etc.), then click on OK if that is the only pin to be changed, or on Add to continue the procedure.  Upon returning to the main window of MaxPlus II, recompile your design. Then open the report (rpt) file (by clicking on the ‘rpt’ icon) and confirm that the pins were indeed assigned as expected.  TLFeBOOK 342 Appendix C Figure C20 Downloading the Design Your design is now ready to be downloaded onto the chip. Double-click on the pof (program object file) icon (shown at the end of compilation, figure C3). A box like that of figure C20 will be displayed.  Select, in the main menu, Options ! Hardware Setup ! ByteBlaster(MV), then click on OK.  Finally, in the screen of figure C20, click on Program to program the device. After a few moments, the chip will be ready to be physically tested and/or used.  TLFeBOOK Appendix D: Altera Quartus II Tutorial The following synthesis, placement, and simulation tools are described in the tutorials presented in the Appendices: Tools Application Appendix ISE 6.1 þ ModelSim 5.7c Xilinx CPLDs and FPGAs B MaxPlus II 10.2 þ Advanced Synthesis Software Altera CPLDs and some FPGAs C Quartus II 3.0 Altera CPLDs and FPGAs D Quartus II 3.0 from Altera is a comprehensive integrated compiler, placement, and simulation tool. It allows the complete design, from VHDL code to physical implementation, of projects using any of Altera’s FPGA or CPLD devices. Quartus II is the successor of MaxPlus II (Appendix C). Quartus II 3.0 Web Edition can be downloaded cost-free from www.altera.com. This is a very brief tutorial, which is divided into four parts: D1. Entering VHDL Code D2. Compilation D3. Simulation D4. Physical Implementation D1.  Entering VHDL Code Launch Quartus II 3.0. A window like that of figure D1 will be displayed. Create a new project (File ! New Project Wizard). The dialog box of figure D2 will appear. Select the working directory in the first field, and the project name (same as the ENTITY’s) in the second. The last field will be automatically filled with the project name (you may change it if you want). In the example below, the working directory is d:\altera\my_circuits, and the project name is flipflop. A new project, called flipflop.quartus, is then created in the working directory, which will contain the flipflop.vhd file to be created.  Open the text editor (File ! New, or click on ). The menu of figure D3 will then be displayed. Select VHDL File. A blank screen will be presented.  TLFeBOOK 344 Appendix D Figure D1 Figure D2 TLFeBOOK Altera Quartus II Tutorial 345 Figure D3 Enter your VHDL code (as in figure D4). Save it with the extension .vhd (the same name as the ENTITY’s will be automatically assigned to the file, that is, flipflop.vhd in this example).  Check for syntax errors. Select Processing ! Analyze Current File, or simply click on the analysis icon . Any error detected by the compiler will be described in the bottom window.  D2. Compilation Select the target device (Assignments ! Devices). A menu like that of figure D5 will be displayed. Choose the desired device Family (MAX3000A, for example). In the Target device option, you may select Auto device. In the Package, Pin count, and Speed grade options, select Any.   To compile your VHDL code, select Processing ! Start Compilation, or click on . If successful, a window like that of figure D6 will be displayed. Examine the compilation reports (listed on the left of figure D6). Check at least the following:  (a) Flow Summary: This report is displayed automatically at the end of compilation, as shown in figure D6. It contains the part number of the device, the number of pins used, and the usage of the device (number of logic cells used / total number of logic cells). TLFeBOOK 346 Appendix D Figure D4 Figure D5 TLFeBOOK Altera Quartus II Tutorial 347 Figure D6 (b) Resource Usage Summary (Fitter ! Resource Section ! Resource Usage Summary): This report (figure D7) shows details regarding the number of registers inferred from the code, logic cells used, I/O pins, etc. (c) Input and Output Pins (Fitter ! Resource Section ! Input Pins, Fitter ! Resource Section ! Output Pins): These two reports show the I/O pin assignments. (d) Floorplan View (Fitter ! Floorplan View): Shows a layout of the logic cells, which logic cells were used and how, etc. (see figure D8). (e) Analysis and Synthesis Equations (Analysis and Synthesis ! Analysis and Synthesis Equations): Contains the logical equations implemented by the compiler (logical operations þ registers). D3. Simulation Open the Waveform Editor. To do so, select File ! New ! Other File ! Vector Waveform File, or simply click on . A screen like that of figure D9 will be displayed.  In order to define the size of the waveforms (figure D9), do: Edit ! End Time (select 500 ns, for example). Edit ! Grid Size (select Period ¼ 50 ns, Duty Cycle ¼ 50%). Finally, select View ! Fit in Window. Note: To change the default values, go to Tools ! Options ! Waveform Editor ! General.  TLFeBOOK 348 Appendix D Figure D7 Figure D8 TLFeBOOK Altera Quartus II Tutorial 349 Figure D9 Figure D10 Add the input and output signals to the waveform window. To do so, click the right mouse button inside the white area under Name (figure D9) and select Insert Node or Bus. In the next box, select Node Finder. A screen like that of figure D10 will then be shown. Make sure that Filter is set to Pins: all. Click on Start, then on X, and finally on OK. The waveforms window will now contain a list of all signals described in the ENTITY of the VHDL code, as shown in figure D11. Notice that the input signals (clk, rst, d) are indicated by an inward arrow with an ‘‘I’’ inside, while the output signal (q) is represented by an outward arrow with an ‘‘O’’ inside. The position of the signals can be rearranged by simply dragging them up or down (for example, one might want rst to come right below clk).  TLFeBOOK 350 Appendix D Figure D11 Figure D12 We have to set now the values of the input signals (clk, rst, and d in figure D11). The easiest way is by using the waveform menu (shown on the left-hand side of figure D11). To set up the clock signal, select the entire clk line (by clicking on the arrow with an I inside beside the word clk) and then click on . A setup box will be displayed. Choose Period ¼ 100 ns.  For rst, select only its first portion (from 0 to 25 ns), then click on cause the selected portion to change its logic level from 0 to 1.  , which will Finally, we have to set up the value of d. Select the entire d line, then click on . Choose Period ¼ 200 ns and Phase ¼ 75 ns. The result is shown in figure D12.  TLFeBOOK Altera Quartus II Tutorial 351 Figure D13 Notice that q is not available yet, for it will be determined by the simulator. Save the waveform as flipflop.vwf. The system is now ready for simulation. Select Processing ! Start Simulation, or just click on . The result should look like that in figure D13.  D4. Physical Implementation Development kit: To perform the physical implementation, we will assume that an Altera UP1 (or UP2) kit is available (this development kit was described in section C5 of appendix C). The kit must be connected to the parallel port of the PC by means of a ByteBlaster cable (provided with the kit).  Device selection: The kit (Altera UP1 or UP2) contains two devices, EPM7128SLC84-7 (a CPLD from the MAX7000S family) and EPF10K70RC240-4 (an FPGA from the FLEX10K family). Therefore, in the Assignments ! Devices step of section D2, one of these two devices must be selected.  Changing pin assignments: The I/O pins are automatically assigned during compilation. However, if desired, the assignments can be changed. Select Assignments ! Assign Pins, which will cause the window of figure D14 to be opened. Say that we want rst to be connected to pin 4, for example. Select pin 4, then click on , which will open the window of figure D10. Click on Start, select rst on the left column, then click on > and OK. Upon returning to the window of figure D14, click on Add. Repeat this process for any other changes of pin assignments.  TLFeBOOK 352 Appendix D Figure D14 Figure D15 TLFeBOOK Altera Quartus II Tutorial 353 Setting up the Programmer: To download the program to the kit (device), first select Tools ! Programmer, or click on . The window of figure D15 will be shown. In the Hardware option, ByteBlasterMV (LPT1) should appear. If not, click on Hardware, then on Select Hardware, select ByteBlasterMV, and finally click on Add Hardware. Returning to the window of figure D15, in the File column verify that the design file, with the extension .pof (program object file), is present. Then check the box under Program/Configure.  Programming the device: Finally, the device can be programmed. Just select Processing ! Start Programming. After a few moments, programming will be concluded and the chip ready to be physically tested and/or used.  TLFeBOOK TLFeBOOK Appendix E: VHDL Reserved Words From VHDL 87: ABS ACCESS AFTER ALIAS ALL AND ARCHITECTURE ARRAY ASSERT ATTRIBUTE BEGIN BLOCK BODY BUFFER BUS CASE COMPONENT CONFIGURATION CONSTANT DISCONNECT DOWNTO ELSE ELSIF END ENTITY EXIT FILE FOR FUNCTION GENERATE GENERIC GUARDED IF IN INOUT IS LABEL LIBRARY LINKAGE LOOP MAP MOD NAND NEW NEXT NOR NOT NULL OF ON OPEN OR OTHERS OUT PACKAGE PORT PROCEDURE PROCESS RANGE RECORD REGISTER REM REPORT RETURN SELECT SEVERITY SIGNAL SUBTYPE THEN TO TRANSPORT TYPE UNITS UNTIL USE VARIABLE WAIT WHEN WHILE WITH XOR From VHDL 93: GROUP IMPURE INERTIAL LITERAL POSTPONED PURE REJECT ROL ROR SHARED SLA SLL SRA SRL UNAFFECTED XNOR TLFeBOOK TLFeBOOK Bibliography Armstrong J. R. and F. G. Gray, VHDL Design Representation and Synthesis, Englewood Cli¤s, NJ: Prentice Hall, 2nd Edition, 2000. Bhasker J., VHDL Primer, Englewood Cli¤s, NJ: Prentice Hall, 3rd Edition, 1999. Chang K. C., Digital Systems Design with VHDL and Synthesis—An Integrated Approach, Los Alamitos, CA: IEEE Computer Society Press, 1999. Hamblen J. and M. Furman, Rapid Prototyping of Digital Systems, Boston: Kluwer Academic Publisher, 2nd Edition, 2001. Naylor D. and S. Jones, VHDL: A Logic Synthesis Approach, London: Chapman & Hall, 1997. Navabi Z., VHDL Analysis and Modeling of Digital Systems, New York: McGraw-Hill, 1993. Pellerin D. and D. Taylor, VHDL Made Easy, Englewood Cli¤s, NJ: Prentice Hall, 1997. Perry D. L., VHDL, New York: McGraw-Hill, 2nd Edition, 1994. Yalamanchili S., Introductory VHDL from Simulation to Synthesis, Englewood Cli¤s, NJ: Prentice Hall, 2001. Yalamanchili S., VHDL Starter’s Guide, Englewood Cli¤s, NJ: Prentice Hall, 1998. TLFeBOOK TLFeBOOK Index þ ¼> <¼ :¼ Addition operator, 49, 54, 60, 195 Assignment operator for OTHERS, 47, 60 Assignment operator for SIGNAL, 47, 60 Assignment operator for VARIABLE, CONSTANT, or GENERIC, 47, 60 = Division operator, 49, 60 ¼ Equal-to operator, 49, 60  Exponentiation operator, 49, 60 > Greater-than operator, 49, 60 >¼ Greater-than-or-equal-to operator, 49, 60 < Less-than operator, 49, 60 <¼ Less-than-or-equal-to operator, 49, 60  Multiplication operator, 49, 60 =¼ Not-equal-to operator, 49, 60  Subtraction operator, 49, 60 ABS operator, 49, 60 Absolute value operator, 49, 60 Accumulate (multiply-and-), 285–288, 290, 292, 295, 299 Actel FPGAs, 315 ACTIVE attribute, 52, 61 Adder circuits, 5–7, 42–43, 106–109, 194–198 Basic adder, 42–43 Carry-look-ahead adder, 196–198 Carry-ripple adder, 106–109, 194–196 Full-adder, 5–7, 279, 282–283 MAC (multiply-and-accumulate), 285–288, 290, 292, 295, 299 Overflow, 286 Addition operator, 49, 54, 60, 195 Advanced Synthesis Software, 329, 336–338 AFTER clause, 28 Altera Advanced Synthesis Software, 329, 336–338 CPLDs, 312 MaxPlus II software, 5, 20, 329–342 Quartus II software, 4–5, 20, 343–353 ALU, 75–78, 247–251 AND operator, 36–37, 41–42, 48, 60 Antifuse, 306, 313, 315 Application Specific Integrated Circuit. See ASIC ARCHITECTURE Description, 17 Introductory examples, 6, 17–22 Usage, 13–14 Arithmetic logic unit. See ALU Arithmetic operators, 48–49, 60 ARRAY, 30–33, 39 Array indexes, 51 ASIC, 3, 4, 6, 211, 293 ASSERT statement, 270–271 Assignment operators, 47–48, 60 ATTRIBUTE statement, 52–53 Attributes, 50–53, 61 Data attributes, 51, 61 Signal attributes, 52, 61 Summary, 61 User-defined attributes, 52–53 Barrel/vector shifter circuits, 80–81, 109–111, 187–190 Base type, 29–30 BEGIN keyword with ARCHITECTURE, 17 with BLOCK, 81–83 with FUNCTION, 253 with PROCEDURE, 265 with PROCESS, 91 Behavioral description, 91 Binary versus one-hot and two-hot encoding, 181–182 Binary-to-Gray-code converter, 87 BIT. See Data types BIT_VECTOR. See Data types BIT versus BIT_VECTOR, 41–42 BLOCK statement Guarded, 83–84 Simple, 81–83 BODY. See PACKAGE BODY BOOLEAN. See Data types Bu¤er circuit, 73 BUFFER mode, 16, 37–138, 140, 143, 145, 152, 154, 157 Carry-look-ahead adder circuit. See Adder circuits Carry-ripple adder circuits. See Adder circuits CASE statement, 91, 100–104 CASE versus IF, 112–113 CASE versus WHEN, 113–114 Combinational versus sequential circuits, 65–66 Comparator circuits, 191–194 Comparison operators, 49, 60 Complex programmable logic devices. See CPLDs COMPONENT, 234, 236–244 Concatenation operators, 50, 60 Concurrent code, 65–84 Concurrent statements BLOCK, 65, 81–84 GENERATE, 65, 78–81, 195, 197–198 WHEN, 65, 69–78 WHEN versus CASE, 113–114 Concurrent versus sequential code, 65–67 CONFIGURATION statement, 72 CONSTANT, 31, 47, 129–131, 174–176, 220–221, 234–235, 270 Controller circuit for tra‰c light, 174–178, 186 Controller circuit for vending machine, 202–208, 226–227 Conv_integer function, 25, 37, 43, 255, 258–259 TLFeBOOK 360 Conv_signed function, 25, 37 Conv_std_logic_vector function, 25, 38 Conv_unsigned function, 25, 37 Conversion functions. See Data conversion functions Count ones circuits, 130–133 Counter circuits, 94–96, 99–100, 102–104, 144– 146, 155, 164–166, 272 CPLDs, 3–4, 305–306, 311–313, 317, 338–340 Data attributes, 51, 61 Data conversion functions, 25, 37–38 Data objects. See Objects Data types, 25–43 ARRAY, 30–33, 39 BIT, 16–17, 21–22, 25–26, 39, 48–49, 54–55 BIT_VECTOR, 25–26, 28–30, 39, 48–49, 54– 55 BIT versus BIT_VECTOR, 41–42 BOOLEAN, 25, 27–28, 30, 39, 204, 235–237, 254–257 Enumerated data types, 28–29, 39, 51, 53, 61, 70, 101, 160, 162, 164, 204 INTEGER, 25, 27–28, 30, 35, 37–39, 48–49 NATURAL, 27, 29–30, 34–35, 262, 300 Physical data types, 27 Port array, 33–34 REAL, 25, 27, 39 RECORD, 35, 39 SIGNED, 25, 27, 30, 35–39, 42–43, 48, 191, 285, 291, 297 STD_LOGIC, 25–27, 39 STD_LOGIC_VECTOR, 25–27, 39 STD_ULOGIC, 25–27, 39 STD_ULOGIC_VECTOR, 26, 39 SUBTYPE, 29–30, 39, 80, 226 UNSIGNED, 25, 27, 30, 35–39, 48, 191, 263– 265 User-defined, 28–29, 34, 39, 299 Decoder circuits, 55–57, 62–63, 156 Delay circuit, 152 DFF, 18–22, 83–84, 92–93, 99, 101–102, 125–127, 137–138, 142–143, 152–155, 157–158, 254–255 Digital filter circuit, 289–294, 303 Divider circuit, fixed-point, 198–202 Division operator, 49, 60 Don’t care, 26 D-type flip-flop. See DFF EDIF, 329, 336, 337 EEPROM, 306, 309, 312–313 ELSE with WHEN, 69 with IF, 94 Index ELSIF, 94 Encoder circuit, 73–75 END keyword with ARCHITECTURE, 17 with BLOCK, 81–83 with CASE, 100 with FUNCTION, 253 with GENERATE, 78–79 with IF, 94 with LOOP, 105 with PROCEDURE, 265 with PROCESS, 91 ENTITY Description, 15–17 Introductory examples, 6, 17–22 Usage, 13–14 Enum_encoding attribute, 53 Enumerated data types. See Data types EPROM, 308–309 Equal-to operator, 49, 60 Error message. See ASSERT statement EVENT attribute, 52, 61 Event counter circuit, 121 Exclusive-NOR operator, 48, 60 Exclusive-OR operator, 48, 60 EXIT statement, 105–106, 111–112 Exponentiation operator, 49, 60 Field Programmable Gate Arrays. See FPGAs Finite State Machine. See FSM FIR filter. See Digital filter circuit Flip-flop. See DFF FOR statement with GENERATE, 78–81 with LOOP, 105–112 with WAIT, 98–99 FPGAs, 3–4, 305–306, 311–315, 317, 338–339 Frequency divider circuit, 122, 138–140 FSM, 159–182, 202–208, 213–218 Full-adder. See Adder circuits FUNCTION, 253–265 Arithmetic shift function, 261–262 Convert-to-integer function, 37, 43, 255, 258–259 Definition and syntax, 253–254 Function location, 256–258 Multiplication function, 263–265 Multiplier function, 263–265 Overloaded ‘‘þ’’ operator function, 260–261 Positive_edge function, 254–258 FUNCTION versus PROCEDURE, 270 GAL devices, 305–306, 309–312 GENERATE statement, 65, 78–81, 195, 197–198 GENERIC MAP, 244–247 TLFeBOOK Index GENERIC statement, 54–60, 97, 108–109, 117, 191–195, 201, 221, 223, 225 Gray code, 87 Greater-than operator, 49, 60 Greater-than-or-equal-to operator, 49, 60 GUARDED BLOCK, 83–84 Hexadecimal, 28, 43 HIGH attribute, 51, 61 High-impedance, 26, 73 IEEE library, 13–15, 25–27, 35–38 IEEE standards for VHDL, 25 IF statement, 91, 94–97 IF versus CASE, 112–113 IF-GENERATE, 78–79 IN mode, 16–17 Inferred registers, number of, 140–151 INOUT mode, 16, 225 INTEGER. See Data types Intensity encoder circuit, 124 IS keyword with ARCHITECTURE, 17 with ATTRIBUTE, 53 with CASE, 100 with COMPONENT, 237 with ENTITY, 16 with FUNCTION, 253 with PACKAGE, 234 with PACKAGE BODY, 234 with PROCEDURE, 265 with SUBTYPE, 29–30 with TYPE, 28–29 ISE software, 4–5, 20, 317–327 Keypad debouncer/encoder circuit, 184–186 LAST_ACTIVE attribute, 52 LAST_EVENT attribute, 52 LAST_VALUE attribute, 52 Latch, 83–84, 119, 121 (see also DFF) Leading zeros counter circuit, 111–112 LEFT attribute, 51, 61 LEFTOF attribute, 51, 61 LENGTH attribute, 51, 61 Less-than operator, 49, 60 Less-than-or-equal-to operator, 49, 60 Library Declaration, 13–15 IEEE library, 13, 15, 25–27, 35–38 Introductory examples, 18–22 Standard library, 13, 15, 25 Std_logic_1164 package, 13, 15, 25–27 Std_ulogic_1164 package, 25–27 361 Std_logic_arith package, 15, 25, 27, 35–38, 42– 43, 191, 263, 285, 287, 291, 296, 299 Std_logic_signed package, 15, 25, 27, 36, 38, 48 Std_logic_unsigned package, 15, 25, 27, 36, 38, 48 Work library, 13, 15 Logic systems Binary (std library), 25 STD_LOGIC, 25–27 STD_ULOGIC, 25–27 Logical operators, 48, 60 LOOP statement, 91, 105–112 LOW attribute, 51, 61 MAC circuits, 285–288, 290, 292, 295, 299 MAP GENERIC MAP, 244–247 PORT MAP, 237, 241, 244–245, 251, 277–279, 281–284 MaxPlus II software, 5, 20, 329–342 Min_max procedure, 267–270 MOD operator, 49, 60 Mode BUFFER, 16, 137–138, 140, 143, 145, 152, 154, 157 IN, 16–22 INOUT, 16, 225, 266 OUT, 16–22 ModelSim software, 5, 20, 317, 325–326 Modulus operator, 49, 60 Multiplexer circuits, 68, 70–72, 85, 134–137 Multiplication operator, 49, 60 Multiplier circuits, 263–265, 275–285 Multiply-and-accumulate circuit. See MAC circuits Multivalued logic systems STD_LOGIC, 25–27 STD_ULOGIC, 25–27 MUX. See Multiplexer circuits NAND operator, 48, 60 NATURAL. See Data types Neural networks, 294–301, 303 NEXT statement, 105–106 NOR operator, 48, 60 NOT operator, 48, 60 Not-equal-to operator, 49, 60 NULL statement, 101–102, 104, 113–114 Number of registers inferred, 140–151 Numeric data types. See Data types Objects CONSTANT, 31, 47, 129–131, 174–176, 220– 221, 234–235, 270 TLFeBOOK 362 Objects (cont.) SIGNAL, 19, 21–22, 129–132 SIGNAL versus VARIABLE, 133–140 VARIABLE, 129–133 ON keyword, 98–99 One-hot encoding, 181–182 Operator overloading, 53–54, 260–261 Operators Arithmetic, 48–49, 60 Assignment, 47–48, 60 Comparison, 49, 60 Concatenation, 50, 60 Logical, 48, 60 Shift, 49–50, 60 Summary, 60 OR operator, 48, 60 OTHERS clause, 40, 47–48, 69–73, 94, 101–102, 112–114 OUT mode, 16–22 Overloading, 53–54, 260–261 PACKAGE Description, 13–14, 93, 133, 233–236, 256, 266–270 Examples, 34–35, 235–236, 239, 241–244, 257– 260, 263–264, 268–270, 275–284, 287, 299 PACKAGE BODY, 234–236, 256–260, 269–270, 287 PAL devices, 305–308, 339 PALCE devices, 305 Parity detector circuit, 57–59, 123 Parity generator circuit, 59–60 Physical data types, 27 PLA devices, 305–306, 308–309, 312 Playing with a seven-segment display, 212–216, 228 PLDs CPLD, 3–4, 305–306, 311–313, 317, 338–340 FPGA, 3–4, 305–306, 311–315, 317, 338–339 GAL, 305–306, 309–312 PAL, 305–308, 339 PLA, 305–306, 308–309, 312 PORT Introductory examples, 5–6, 18–22 Modes. See Mode Port array, 33–35 PORT MAP, 237, 241, 244, 251, 277–279, 281–284 Pre-defined data attributes, 51, 61 Pre-defined data types, 25–28, 39 Pre-defined operators, 47–50, 60 Pre-defined signal attributes, 52, 61 Priority encoder circuit, 85, 121–122 PROCEDURE, 13–14, 91, 94, 105, 113, 130–133, 233–235, 265–270 Index PROCEDURE versus FUNCTION, 270 PROCESS Description, 91–94 Introductory examples, 18–22, 56–60, 92–120 Programmable array logic. See PAL Programmable logic array. See PLA Programmable logic devices. See PLDs Quartus II software, 4–5, 20, 343–353 QUIET attribute, 52 RAM circuits, 116–118, 221–225 Random access memory. See RAM circuits RANGE attribute, 28–30, 34–35, 37, 51, 61 Read-only memory. See ROM circuits REAL. See Data types RECORD, 35, 39 Registers. See DFF Registers inferred, number of, 140–151 Relational operators. See Operators REM operator, 49, 60 Remainder operator, 49, 60 REPORT statement, 271 Reserved words, 355 Resolved data type, 26–27, 39 REVERSE_RANGE attribute, 51, 61 RIGHT attribute, 51, 61, 262 ROL operator, 50, 60 ROM circuits, 44, 220–221 ROR operator, 50, 60 Rotate left logic operator, 50, 60 Rotate right logic operator, 50, 60 RTL, 4 SELECT statement, 67, 69–73, 113 Sequential code, 65, 91–121 Sequential statements CASE, 91, 100–104, 112–114 IF, 91, 94–97, 112–113 LOOP, 91, 105–112 WAIT, 91, 97–100 Serial data receiver circuit, 208–211, 227 Seven-segment display, 212–216, 228 Shift left arithmetic operator, 50, 60 Shift left logic operator, 49–50, 60 Shift operators, 49–50, 60 Shift register circuits, 96–97, 121, 146–151 Shift right arithmetic operator, 50, 60 Shift right logic operator, 49–50, 60 SIGNAL, 19, 21–22, 129–132 Signal attributes, 52, 61 Signal generator circuits, 178–181, 183–184, 186, 217–220 SIGNAL versus VARIABLE, 133–140 SIGNED. See Data types TLFeBOOK Index 363 SLA operator, 50, 60 SLL operator, 49–50, 60 Speed monitor circuit, 228–229 SRA operator, 50, 60 SRL operator, 49–50, 60 STD_LOGIC, 25–27, 39 STD_LOGIC_VECTOR, 25–27, 39 STD_ULOGIC, 25–27, 39 STD_ULOGIC_VECTOR, 26–27, 39 Stop-watch circuit, 252 String detector circuit, 172–174 Subtype, 29–30, 39 Timer circuits, 123 Tra‰c light controller circuit, 174–178, 186 Tri-state bu¤er circuit, 73 Two-hot encoding, 181–182 Types. See Data types UNAFFECTED statement, 69–70, 101, 113–114 Unresolved data type, 27, 39 UNSIGNED. See Data types UNTIL, 52, 98–100 USE clause, 13, 15 User-defined attributes, 52–53 User-defined data types, 28–29, 34, 39, 299 VARIABLE, 129–133 VARIABLE versus SIGNAL, 133–140 Vending machine controller circuit, 202–208, 226–227 VHDL acronym, 3 VHDL reserved words, 355 WAIT statement, 91, 97–100 WHEN statement, 65, 69–78 WHEN versus CASE, 113–114 WHILE statement, 105–106 WITH, 67, 69–73, 113 Work library, 13, 15 Xilinx CPLDs, 312 FPGAs, 314 ISE software, 4–5, 20, 317–327 XNOR operator, 48, 60 XOR operator, 48, 60 ‘‘Z’’ logic state, 26–27, 39, 73 TLFeBOOK TLFeBOOK