VHDL-2008 Just the New Stuff
Peter J. Ashenden Consultant Ashenden Designs
Jim Lewis Director of Training SynthWorks Design, Inc.
Publishing Director Publisher Senior Acquisitions Editor Publishing Services Manager Senior Production Editor Assistant Editor Production Assistant Cover Designer Cover Image Composition Copyeditor Proofreader Indexer Interior printer Cover printer
Joanne Tracy Denise E.M. Penrose Charles Glaser George Morrison Dawnmarie Simpson Matthew Cater Lianne Hong Dennis Schaefer Scott Tysick/Masterfile Peter J. Ashenden JC Publishing Janet Cocker Joan Green Sheridan Books, Inc. Phoenix Color, Inc.
Morgan Kaufmann Publishers is an imprint of Elsevier. 30 Corporate Drive, Suite 400, Burlington, MA 01803, USA This book is printed on acid-free paper. © 2008 by Elsevier Inc. All rights reserved. Designations used by companies to distinguish their products are often claimed as trademarks or registered trademarks. In all instances in which Morgan Kaufmann Publishers is aware of a claim, the product names appear in initial capital or all capital letters. Readers, however, should contact the appropriate companies for more complete information regarding trademarks and registration. No part of this publication may be reproduced, stored in a retrieval system, or transmitted in any form or by any means-electronic, mechanical, photocopying, scanning, or otherwise-without prior written permission of the publisher. Permissions may be sought directly from Elsevier's Science & Technology Rights Department in Oxford, UK: phone: (+44) 1865 843830, fax: (+44) 1865 853333, E-mail:
[email protected]. You may also complete your request online via the Elsevier homepage (http://elsevier.com), by selecting “Support & Contact” then “Copyright and Permission” and then “Obtaining Permissions.” Library of Congress Cataloging-in-Publication Data Ashenden, Peter J. VHDL-2008 : just the new stuff / Peter J. Ashenden, Jim Lewis. p. cm. Includes index. ISBN 978-0-12-374249-0 (pbk. : alk. paper) 1. VHDL (Computer hardware description language) I. Lewis, Jim. II. Title. TK7885.7.A846 2007 621.39'2--dc22 2007039499 ISBN: 978-0-12-374249-0 For information on all Morgan Kaufmann publications, visit our Web site at www.mkp.com or www.books.elsevier.com Printed in the United States. 07 08 09 10 5 4 3 2 1
Contents Preface 1
Enhanced Generics 1.1 1.2 1.3 1.4 1.5 1.6 1.7
2
2.6
3
3.2
53
External Names 53 Force and Release 63 Context Declarations 67 Integrated PSL 70 IP Encryption 77 2.5.1 Key Exchange 96 VHDL Procedural Interface (VHPI) 97 2.6.1 Direct Binding 97 2.6.2 Tabular Registration and Indirect Binding 99 2.6.3 Registration of Applications and Libraries 101
Type System Changes 3.1
1
Generic Types 1 Generic Lists in Packages 6 Local Packages 11 Generic Lists in Subprograms 15 Generic Subprograms 21 1.5.1 Uninstantiated Methods in Protected Types 32 Generic Packages 36 Use Case: Generic Memories 43
Other Major Features 2.1 2.2 2.3 2.4 2.5
ix
103
Unconstrained Element Types 103 3.1.1 Composite Types 103 3.1.2 Subtype Indications and Constraints 107 3.1.3 Use of Composite Subtypes 109 Variable and Signal Declarations 110 Constant Declarations 110 Attribute Specifications 111 Allocated Objects 111 Interface Objects 112 Summary: Determining Array Index Ranges 117 Type Conversions 118 Alias Declarations and Subtype Attributes 119 Resolved Composite Subtypes 122 Resolved Elements 123 v
vi
4
Contents
New and Changed Operations 4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8 4.9
5
5.2 5.3
Conditional and Selected Assignments 143 5.1.1 Sequential Signal Assignments 143 5.1.2 Forcing Assignments 146 5.1.3 Variable Assignments 147 Matching Case Statements 149 5.2.1 Matching Selected Assignments 150 If and Case Generate 151 5.3.1 Configuration of If and Case Generate 155
159
Signal Expressions in Port Maps 159 All Signals in Sensitivity List 161 Reading Out-Mode Ports and Parameters 162 Slices in Aggregates 166 Bit-String Literals 167
Improved I/O 7.1
7.2 7.3 7.4 7.5 7.6
8
143
Modeling Enhancements 6.1 6.2 6.3 6.4 6.5
7
Array/Scalar Logical Operations 127 Array/Scalar Addition Operators 129 Logical Reduction Operators 130 Condition Operator 132 Matching Relational Operators 133 Maximum and Minimum 138 Mod and Rem for Physical Types 140 Shift Operations 141 Strength Reduction and 'X' Detection 142
New and Changed Statements 5.1
6
127
169
The To_string Functions 169 7.1.1 Predefined To_string Functions 170 7.1.2 Overloaded To_string Functions 171 7.1.3 The To_ostring and To_hstring Functions The Justify Function 173 Newline Formatting 173 Read and Write Operations 174 The Tee Procedure 177 The Flush Procedure 178
Standard Packages 8.1 8.2 8.3 8.4 8.5
The The The The The
Std_logic_1164 Package 179 Numeric_bit and Numeric_std Packages Numeric Unsigned Packages 182 Fixed-Point Math Packages 182 Floating-Point Math Packages 186
172
179 180
vii
Contents 8.6 8.7 8.8 8.9 8.10
9
Miscellaneous Changes 9.1 9.2 9.3 9.4 9.5 9.6 9.7 9.8 9.9 9.10 9.11 9.12 9.13 9.14 9.15 9.16 9.17 9.18 9.19 9.20 9.21 9.22 9.23
10
The Standard Package 191 The Env Package 192 Operator Overloading Summary 193 Conversion Function Summary 196 Strength Reduction Function Summary 204
207
Referencing Generics in Generic Lists 207 Function Return Subtype 208 Qualified Expression Subtype 209 Type Conversions 209 Case Expression Subtype 211 Subtypes for Port and Parameter Actuals 212 Static Composite Expressions 213 Static Ranges 214 Use Clauses, Types, and Operations 215 Hiding of Implicit Operations 216 Multidimensional Array Alias 217 Others in Aggregates 217 Attribute Specifications in Package Bodies 219 Attribute Specification for Overloaded Subprograms Integer Expressions in Range Bounds 220 Action on Assertion Violations 221 'Path_Name and 'Instance_Name 221 Non-Nesting of Architecture Region 223 Purity of Now 223 Delimited Comments 224 Tool Directives 225 New Reserved Words 225 Replacement Characters 226
What’s Next 10.1 Object-Oriented Class Types 229 10.1.1 Standard Components Library 10.2 Randomization 232 10.3 Functional Coverage 235 10.4 Alternatives 235 10.5 Getting Involved 235
Index
219
229 232
237
This page intentionally left blank
Preface VHDL is defined by IEEE Standard 1076, IEEE Standard VHDL Language Reference Manual (the VHDL LRM). The original standard was approved in 1987. IEEE procedures require that standards be periodically reviewed and either reaffirmed or revised. The VHDL standard was revised in 1993, 2000, and 2002. In each revision, new language features were added and some existing features enhanced. The aim in each revision was to improve the language as a tool for design and verification of digital systems. Since the 2002 revision, there have two parallel efforts to further develop the language. The first was the VHDL Procedural Interface (VHPI) Task Force, a subcommittee of the IEEE P1076 Working Group. The VHPI Task Force prepared an interim amendment to the standard, formally approved by IEEE in March 2007. The amendment is titled IEEE 1076c, Standard VHDL Language Reference Manual—Amendment 1: Procedural Language Application Interface. In the second effort, during 2004 and 2005, the P1076 Working Group undertook preliminary work toward a new revision of the standard. In June 2005, the board of Accellera approved formation of a Technical Committee (TC) to continue that work, funded jointly by Accellera and TC members directly. The Accellera VHDL-TC worked intensively between September 2005 and June 2006, producing a new draft of the LRM, P1076/D3.0. This draft was a full revision of the VHDL standard, defining numerous new and enhanced language features, incorporating minor clarifications and corrections, and including the VHPI specification from IEEE 1076c. The language defined by this draft is informally called VHDL-2006. The draft was published for trial use by implementers and users during the period from June 2006 to June 2007. Feedback has been rolled into a subsequent draft to be forward to the P1076 Working Group for IEEE standardization. The final version will be informally called VHDL-2008. The aim of this book is to introduce the new and changed features of VHDL-2008 in a way that is more accessible to users than the formal definition in the LRM. We describe the features, illustrate them with examples, and show how they improve the language as a tool for design and verification. We assume you are already familiar with earlier versions of VHDL, specifically VHDL-2002 and VHDL-93. These versions are described comprehensively in The Designer’s Guide to VHDL, Second Edition, by Peter Ashenden, also published by Morgan Kaufmann Publishers. We hope that the present book will be helpful not only to early adopters of the new language version, but also to tool implementers seeking to understand what it is they have to implement. In addition to the information presented in this book, additional reference information is available at the authors’ web sites: • www.ashenden.com.au • www.SynthWorks.com
ix
x
Preface
Acknowledgments We sincerely thank David Bishop of Kodak, Bill Logan of Rockwell Collins, and Chuck Swart of Mentor Graphics for their technical review of various chapters of this book. Their comments led to significant improvement in our explanations and correction of coding errors. Presenting code examples for language features yet to be implemented in tools is a risky business. Having “human compilers” check the code is most valuable. Any remaining errors are, of course, ours. We would also like to thank Chuck Glaser, our editor at Elsevier, for his encouragement to develop this book. Chuck has a keen sense of what the market needs, and we are happy to take his advice. Finally, we would like to thank you, the reader, in advance for any comments and corrections. We would love to hear from you, by email at
[email protected]. We will maintain a list of errata on the web sites mentioned above.
Chapter 1 Enhanced Generics We start our tour of the new features in VHDL-2008 with one of the most significant changes in the language, enhanced generics. All earlier versions of VHDL since VHDL-87 have included generic constants, which are interface constants for design entities and components. They are widely used in models to represent timing parameters and to control the widths of vector ports. When we instantiate an entity or component, we supply values for the generic constants for that instance. The generic constants in the generic list are called the formal generics, and the values we supply in the generic map are called the actual generics. Most of the time, generic constants are referred to just as “generics,” since the only kind of generics are constants. In VHDL-2008, generics are enhanced in several significant ways. First, we can declare generic types, subprograms, and packages, as well as generic constants. Second, we can declare generics on packages and subprograms, as well as on entities and components. The rationale for extending generics in these ways is to increase productivity by allowing us to declare reusable entities, packages, and subprograms that deal with different types of data and that can be specialized to perform different actions. In this chapter, we will describe each of the new kinds of generics and the new places in which we can declare generics.
1.1
Generic Types Generic types allow us to define a type that can be used for ports and internal declarations of an entity, but without specifying the particular type. When we instantiate the entity, we specify the actual type to be used for that instance. As we will see later, generic types can also be specified for packages and subprograms, not just for entities and components. We can declare a formal generic type in a generic list in the following way: type identifier
The identifier is the name of the formal generic type, and can be used within the rest of the entity in the same way as a normally declared type. When we instantiate the entity, we specify a subtype as the actual generic type. This can take the form of a type name, a type name followed by a constraint, or a subtype attribute.
1
2
Chapter 1 — Enhanced Generics EXAMPLE 1.1
A generic multiplexer
A multiplexer selects between two data inputs and copies the value of the selected input to the output. The behavior of the multiplexer is independent of the type of data on the inputs and output. So we can use a formal generic type to represent the type of the data. The entity declaration is: entity generic_mux2 is generic ( type data_type ); port ( sel : in bit; a, b : in data_type; z : out data_type ); end entity generic_mux2;
The name data_type is the formal generic type that stands for some type, as yet unspecified, used for the data inputs a and b and for the data output z. An architecture body for the multiplexer is: architecture rtl of mux2 is begin z bit ) port map ( sel => sel_bit, a => a_bit, b => b_bit, z => z_bit );
Similarly, we can instantiate the same entity to get a multiplexer for signals of other types, including user-defined types. type msg_packet is record src, dst : unsigned(7 downto 0); pkt_type : bit_vector(2 downto 0); length : unsigned(4 downto 0); payload : byte_vector(0 to 31); checksum : unsigned(7 downto 0); end record msg_packet; signal pkt_sel : bit;
3
1.1 Generic Types signal pkt_in1, pkt_in2, pkt_out : msg_pkt; ... pkt_mux : entity work.generic_mux2(rtl) generic map ( data_type => msg_packet ) port map ( sel => pkt_sel, a => pkt_in1, b => pkt_in2, z => pkt_out );
VHDL-2008 defines a number of rules covering formal generic types and the ways they can be used. The formal generic type name can potentially represent any constrained type, except a file type or a protected type. The entity can only assume that operations available for all such types are applicable, namely: assignment; allocation using new; type qualification and type conversion; and equality and inequality operations. The formal generic type cannot be used as the type of a file element or an attribute. Moreover, it can only be used as the type of an explicitly declared constant or a signal (including a port) if the actual type is not an access type and does not contain a subelement of an access type. For signals, the predefined equality operator of the actual type is used for driver update and event detection. If we have a formal generic type T, we can use it to declare signals, variables, and constants within the entity and architecture, and we can write signal and variable assignments for objects of the type. For example, the following shows signals declared using T: signal s1, s2 : T; ... s1 std_ulogic_vector(3 downto 0), init_val => "ZZZZ" );
We can also use this technique to provide values for initializing variables and signals declared to be of the formal generic type. Note that the generic list in this entity makes use of one generic (T) in the declaration of another generic (init_val). This was illegal in previous versions of VHDL, but is now legal in VHDL-2008 (see Section 9.1). One thing that we cannot do with formal generic types is apply operations that are not defined for all types. For example, we cannot use the “+” operator to add to values of a formal generic type, since the actual type in an instance may not be a numeric type. Similarly, we cannot perform array indexing, or apply most attributes. This may at first seem an onerous restriction, but it does mean that a VHDL analyzer can check the entity and architecture for correctness in isolation, independently of any particular instantiation. It also means we don’t get any surprises when we subsequently analyze an instance of the entity. Fortunately, as we will see in Section 1.5, there are ways of providing operations to an instance for use on values of the actual type. EXAMPLE 1.2
Illegal use of formal generic types
Suppose we want to define a generic counter that can be used to count values of types such as integer, unsigned, signed, and so on. We can declare the entity as follows: entity generic_counter is generic ( type count_type; constant reset_value : count_type );
1.1 Generic Types
5
port
( clk, reset : in bit; data : out count_type ); end entity generic_counter;
We might then try to define an architecture as: architecture rtl of generic_counter is begin count : process (clk) is begin if rising_edge(clk) then if reset = '1' then data red ) port map ( ... );
The process in the instance would have to apply the “+” operator to a value of the actual generic type, in this case, traffic_light_color. That application would fail, since there is no such operator defined. We will revise this example in Section 1.5 to show how to supply such an operator to the instance. Note in passing that the process in this example reads the value of the out-mode parameter data in an expression. While this was illegal in earlier versions of VHDL, it is legal in VHDL-2008 (see Section 6.3). When we declare a generic constant in a generic list, we can specify a default value that is used if no actual value is provided in an instance. For generic types, there is no means of specifying a default type. That means that we must always specify an actual type in an instance. Since the type of objects in VHDL is considered to be a very impor-
6
Chapter 1 — Enhanced Generics tant property, the language designers decided to insist on the actual type being explicitly specified.
1.2
Generic Lists in Packages One of the new places in which we can write generic lists in VHDL-2008 is in package declarations. A package with a generic list takes the form: package identifier is generic ( ... ); ...
-- declarations within the package
end package identifier;
The package body corresponding to such a package is unchanged; we don’t repeat the generic list there. Within the generic list, we can declare formal generic constants and formal generic types, just as we can in a generic list of an entity or component. We can then use those formal generics in the declarations within the package. A package with a generic list is called an uninstantiated package. Unlike a simple package with no generic list, we cannot refer to the declarations in an uninstantiated package with selected names or use clauses. Instead, the uninstantiated package serves as a form of template that we must instantiate separately. We make an instance with a package instantiation of the form: package identifier is new uninstantiated_package_name generic map ( ... );
The identifier is the name for the package instance, and the generic map supplies actual generics for the formal generics defined by the uninstantiated package. If all of the formal generics have defaults, we can omit the generic map to imply use of the defaults. (As we mentioned in Section 1.1, if any of the formal generics is a generic type, it cannot have a default. In that case, we could not omit the generic map in the package instance.) Once we have instantiated the package, we can then refer to names declared within it with selected names and use clauses with the instance name as the prefix. For now, we will assume that the uninstantiated package and the package instance are declared as design units and stored in a design library. We will refine this assumption in Section 1.3. EXAMPLE 1.3
A package for stacks of data
We can write a package that defines a data type and operations for fixed-sized stacks of data. A given stack has a specified capacity and stores data of a specified type. The capacity and type are specified as formal generics of the package, as follows: package generic_stacks is generic ( size : positive; type element_type );
7
1.2 Generic Lists in Packages
type stack_array is array (0 to size-1) of element_type; type stack_type is record SP : integer range 0 to size-1; store : stack_array; end record stack_type; procedure push (s : inout stack_type; e : in element_type); procedure pop (s : inout stack_type; e : out element_type); end package generic_stacks;
The corresponding package body is: package body generic_stacks is procedure push (s : inout stack_type; e : in begin s.store(s.SP) := e; s.SP := (s.SP + 1) mod size; end procedure push;
element_type) is
procedure pop (s : inout stack_type; e : out element_type) is begin s.SP := (s.SP - 1) mod size; e := s.store(s.SP); end procedure pop; end package body generic_stacks;
The uninstantiated package defines types stack_array and stack_type for representing stacks, and operations to push and pop elements. The formal generic constant size is used to determine the size of the array for storing elements, and the formal generic type element_type is the type of elements to be stored, pushed and popped. We cannot refer to items in this uninstantiated package directly, since there is no specification of the actual size and element type. Thus, for example, we cannot write the following: use work.generic_stacks.all; -- Illegal ... variable my_stack : work.generic_stacks.stack_type;
-- Illegal
Instead, we must instantiate the package and provide actual generics for that instance. For example, we might declare the following as a design unit for a CPU design:
8
Chapter 1 — Enhanced Generics library IEEE; use IEEE.numeric_std.all; package address_stacks is new work.generic_stacks generic map ( size => 8, element_type => unsigned(23 downto 0) );
If we analyze this instantiation into our working library, we can refer to it in other design units, for example: architecture behavior of CPU is use work.address_stacks.all; ... begin interpret_instructions : process is variable return_address_stack : stack_type; variable PC : unsigned(23 downto 0); ... begin ... case opcode is when jsb => push(return_address_stack, PC); PC pop(return_address_stack, PC); ... end case; ... end process interpret_instructions; end architecture behavior;
This architecture includes a use clause that makes names declared in the package instance address_stacks visible. References to stack_type, push and pop in the architecture thus refer to the declarations in the address_stacks package instance. We can declare multiple instances of a given uninstantiated package, each with different actual generics. The packages instances are distinct, even though they declare similarly named items internally. For example, we might declare two instances of the generic_stacks package from Example 1.3 as follows: package address_stacks is new work.generic_stacks generic map ( size => 8, element_type => unsigned(23 downto 0) ); package operand_stacks is new work.generic_stacks generic map ( size => 16, element_type => real );
If we then wrote a use clause in a design unit: use work.address_stacks.all, work.operand_stacks.all;
1.2 Generic Lists in Packages
9
the names from the two package instances would all be ambiguous. This is an application of the existing rule in VHDL that, if two packages declare the same name and both are “used,” we cannot refer to the simple name, since it is ambiguous. Instead, we need to use selected names to distinguish between the versions declared in the two package instances. So, for example, we could write: use work.address_stacks, work.operand_stacks;
to make the package names visible without prefixing them with the library name work, and then declare variables and use operations as follows: variable return_address_stack : address_stacks.stack; variable PC : unsigned(23 downto 0); variable FP_operand_stack : operand_stacks.stack; variable TOS_operand : real; ... address_stacks.push(return_address_stack, PC); operand_stacks.pop(FP_operand_stack, TOS_operand);
An important aspect of VHDL’s strong-typing philosophy is that two types introduced by two separate type declarations are considered to be distinct, even if they are structurally the same. Thus the two types declared as type T1 is array (1 to 10) of integer; type T2 is array (1 to 10) of integer;
are distinct, and we cannot assign a value of type T1 to an object of type T2. This same principle applies to formal generic types. Within an entity or a package that declares a formal generic type, that type is considered to be distinct from every other type, including other formal generic types. So, for example, we cannot assign a value declared to be of one formal generic type to an object declared to be of another formal generic type. The fact that two formal generic types are distinct can lead to interesting situations when the actual types provided are the same (or are subtypes of the same base type). Ambiguity can arise between overloaded operations declared using the formal generic types. This kind of situation is not likely to happen in common use cases, but it is worth exploring to demonstrate the way overloading works in the presence of formal generic types. Suppose we declare a package with two formal generic types, as follows: package generic_pkg is generic ( type T1; type T2 ); procedure proc ( x : T1 ); procedure proc ( x : T2 ); procedure proc ( x : bit ); end package generic_pkg;
10
Chapter 1 — Enhanced Generics Within the package, T1 and T2 are distinct from each other and from the type bit, so the procedure proc is overloaded three times. The uninstantiated package can be analyzed without error. If we instantiate the package as follows: package integer_boolean_pkg is new work.generic_pkg generic map ( T1 => integer, T2 => boolean );
we can successfully resolve the overloading for the following three calls to procedures in the package instance: work.integer_boolean_pkg.proc(3); work.integer_boolean_pkg.proc(false); work.integer_boolean_pkg.proc('1');
On the other hand, if we instantiate the package as package integer_bit_pkg is new work.generic_pkg generic map ( T1 => integer, T2 => bit );
the following call is ambiguous: work.integer_bit_pkg.proc('1');
It could be a call to the second or third of the three overloaded versions of proc in the package instance. Similarly, if we instantiate the package as package integer_integer_pkg is new work.generic_pkg generic map ( T1 => integer, T2 => integer );
the following call is ambiguous: work.integer_integer_pkg.proc(3);
This could be a call to the first or second of the three overloaded versions of proc. The point to gain from these examples is that overload resolution depends on the actual types denoted by the formal generic types in the instances. Depending on the actual types, calls to overloaded subprograms may be resolvable for some instances and ambiguous for others. The final aspect of packages with generic lists is that we can also include a generic map in a package, following the generic list. Such a package is called a generic-mapped package, and has the form package identifier is generic ( ... ); generic map ( ... ); ...
-- declarations within the package
end package identifier;
11
1.3 Local Packages
The generic list defines the generics, and the generic map aspect provides actual values and type for those generics. While VHDL-2008 allows us to write a generic-mapped package explicitly, we would not normally do so. Rather, the feature is included in the language as a definitional aid. An instantiation of an uninstantiated package is defined in terms of an equivalent generic-mapped package that is a copy of the uninstantiated package, together with the generic map from the instantiation. This is analogous to the way in which an entity instantiation is defined in terms of a block statement that merges the generic and port lists of the entity with the generic map and port map of the instantiation. Since generic-mapped packages are not a feature intended for regular use, we won’t dwell on them further. We simply mention them here to raise awareness, since the occasional error message from an analyzer might hint at them.
1.3
Local Packages In earlier versions of VHDL, packages can only be declared as design units. They are separately analyzed into a design library, and can be referenced by any other design unit that names the library. Thus, they are globally visible. In VHDL-2008, packages can also be declared locally within the declarative region of an entity, architecture, block, process, subprogram, protected type body, or enclosing package. This allows the visibility of the package to be contained to just the enclosing declarative region. Moreover, since declarations written in a package body are not visible outside the package, we can use local packages to provide controlled access to locally declared items. EXAMPLE 1.4
Sequential item numbering
Suppose we need to generate test cases in a design, with each test case having a unique identification number. We can declare a package locally within a stimulusgenerator process. The package encapsulates a variable that tracks the next identification number to be assigned, and provides an operation to yield the next number. The process outline is: stim_gen : process is package ID_manager is impure function get_ID return natural; end package ID_manager; package body ID_manager is variable next_ID : natural := 0; impure function get_ID return natural is variable result : natural; begin result := next_ID; next_ID := next_ID + 1; return result; end function get_ID;
12
Chapter 1 — Enhanced Generics end package body ID_manager; ... begin ... test_case.ID := ID_manager.get_ID; ID_manager.next_ID := 0; -- Illegal ... end process stim_gen;
The variable next_ID is declared in the package body, and so is not visible outside the package. The only way to access it is using the get_ID function provided by the package declaration. This is shown in the first assignment statement within the process body. The package name is used as a prefix in the selected name for the function. The second assignment statement is illegal, since the variable is not visible at that point. The package provides a measure of safety against inadvertent corruption of the data state. We can write use clauses for locally declared packages. Thus, we could follow the package declaration in this example with the use clause use ID_manager.all;
and then rewrite the assignment in the process as test_case.ID := get_ID;
By writing the package locally within the process, it is only available in the process. Thus, we have achieved greater separation of concerns than had we written the package as a design unit, making it globally visible. Moreover, since the package is local to a process, there can be no concurrent access by multiple processes. Thus, the encapsulated variable can be an ordinary non-shared variable. If the package were declared as a global design unit, there could be concurrent calls to the get_ID function. As a consequence, the variable would have to be declared as a shared variable of a protected type. This would significantly complicate the design. As Example 1.4 illustrates, if a package declared within a declarative region requires a body, then the body must come after the package declaration in the same region. If the enclosing region is itself a package, then we write the inner package declaration within the enclosing package declaration, and the inner package body within the outer package body. If the inner package requires a body, then the outer package requires a body as a consequence. A locally declared package need not be just a simple package. It can be an uninstantiated package with a generic list (or, indeed, a generic-mapped package with both generic list and generic map). In that case, we must instantiate the package so that we can refer to items in the instance. The same rules apply to locally declared uninstantiated packages and instances as apply to globally declared packages.
13
1.3 Local Packages EXAMPLE 1.5
Package for wrapping items with item numbers
We can revise the package from Example 1.4 to make it deal with test cases of generic type, and to wrap each test case in a record together with a unique ID number. The numbers are unique across test cases of all types. We achieve this by keeping the previous package as an outer package encapsulating the next_ID variable. Within that package, we declare an uninstantiated package for wrapping test cases. The process outline containing the packages is: stim_gen : process is package ID_manager is package ID_wrappers is generic ( type test_case_type ); type wrapped_test_case is record test_case : test_case_type; ID : natural; end record wrapped_test_case; impure function wrap_test_case ( test_case : test_case_type ) return wrapped_test_case; end package ID_wrappers; end package ID_manager; package body ID_manager is variable next_ID : natural := 0; package body ID_wrappers is impure function wrap_test_case ( test_case : test_case_type ) return wrapped_test_case is variable result : wrapped_test_case; begin result.test_case := test_case; result.ID := next_ID; next_ID := next_ID + 1; return result; end function wrap_test_case; end package body ID_wrappers; end package body ID_manager; use ID_manager.ID_wrappers;
14
Chapter 1 — Enhanced Generics package word_wrappers is new ID_wrappers generic map ( test_case_type => unsigned(32 downto 0) ); package real_wrappers is new ID_wrappers generic map ( test_case_type => real ); variable next_word_test : word_wrappers.wrapped_test_case; variable next_real_test : real_wrappers.wrapped_test_case; begin ... next_word_test := word_wrappers.wrap_test_case(X"0440CF00"); next_real_test := real_wrappers.wrap_test_case(3.14159); ... end process stim_gen;
The process declares two instances of the uninstantiated package ID_wrappers, one for a test-case type of unsigned, and another for a test-case type of real. The process then refers to the wrapped_test_case type and the wrap_test_case function declared in each instance. Example 1.5 exposes a number of important points about packages. First, a package declared within an enclosing region is just another declared item, and is subject to the normal scope and visibility rules. In the example, the ID_wrappers package is declared within an enclosing package, and so can be referred to with a selected name and made visible by a use clause. Second, in the case of package instantiations, any name referenced within the uninstantiated package keeps its meaning in each instance. In the example, the name next_ID referenced within the uninstantiated package ID_wrappers, refers to the variable declared in the ID_manager package. So, within each of the package instances, word_wrappers and real_wrappers, the same variable is referenced. Importantly, had the process also declared an item called next_ID outside the packages but before the instances, that name would not be “captured” by the instances. They still refer to the same variable nested within the ID_manager package. The only exception to this rule for interpreting names is that the name of the uninstantiated package itself, when referenced within the package, is interpreted in an instance as a reference to the instance. This allows us to use an expanded name for an item declared within the uninstantiated package, and to have it interpreted appropriately in the instance. The rules for name interpretation illustrate quite definitely that package instantiation is different in semantics from file inclusion, as is used for C header files. The benefit of the VHDL-2008 approach is that names always retain the meaning they are given at the point of declaration, and so we avoid unwanted surprises. The third point is that local instantiation of an uninstantiated package is a common use case, whether the uninstantiated package be locally declared, as in the example, or globally declared as a design unit. The advantage of local instantiation is that it allows use of a locally declared type as the actual for a formal generic type. Were local instantiation not possible, the actual type would have to be declared in a global package in
1.4 Generic Lists in Subprograms
15
order to use it in a global package instantiation. Thus, local instantiation improves modularity and information hiding in a design. EXAMPLE 1.6
Local stack package instantiation
In Example 1.3, we declared an uninstantiated package for stacks as a design unit. We can instantiate the package to deal with stacks of a type declared locally within a subprogram that performs a depth-first search of a directed acyclic graph (DAG) consisting of vertices and edges, as follows: subprogram analyze_network ( network : network_type ) is type vertex_type is ...; type edge_type is ...; constant max_diameter : positive := 30; package vertex_stacks is new work.generic_stacks generic map ( size => max_diameter, element_type => vertex_type ); use vertext_stacks.all; variable current_vertex : vertex_type; variable pending_vertices : stack_type; begin ... push(pending_stacks, current_vertex); ... end subprogram analyze_network;
The data types used to represent the DAG for analyzing a network are the local concern of the subprogram. By instantiating the generic_stacks package locally, there is no need to expose the data types outside the subprogram.
1.4
Generic Lists in Subprograms The second new place in which we can write generic lists in VHDL-2008 is in subprogram (procedure and function) declarations. A procedure with a generic list takes the form: procedure identifier generic ( ... ) parameter ( ... ) is ... -- declarations begin
16
Chapter 1 — Enhanced Generics ... -- statements end procedure identifier;
Similarly, a function with a generic list takes the form: function identifier generic ( ... ) parameter ( ... ) return result_type is ... -- declarations begin ... -- statements end function identifier;
We use terminology analogous to that for packages to refer to subprograms with generics. Thus, a subprogram with a generic list is called an uninstantiated subprogram. Note that the new keyword parameter is included to make the demarcation between the generic list and the parameter list clear. For backward compatibility, including the keyword is optional. We expect that designers will omit it for subprograms without generics and include it or not as a matter of taste for uninstantiated subprograms. VHDL allows us to declare a subprogram in two parts, one consisting just of the specification, and the other consisting of the specification together with the body. We can separate a subprogram in this way within a given declarative part, for example, in order to declare mutually recursive subprograms. In the case of subprograms declared in packages, we are required to separate the subprogram specification into the package declaration and to repeat the specification together with the subprogram body in the package body. In the case of uninstantiated subprograms, the generic list is part of the subprogram specification. Thus, if we separate the declaration, we must write the generic list and parameter list in the specification, and then repeat both together with the body. Using a text editor to copy and paste the specification into the body makes this easy. We cannot call an uninstantiated subprogram directly. We can think of it as a template that we must instantiate with a subprogram instantiation to get a real subprogram that we can call. For a procedure, the instantiation is of the form: procedure identifier is new uninstantiated_procedure_name generic map ( ... );
and for a function, the instantiation is of the form function identifier is new uninstantiated_function_name generic map ( ... );
In both cases, the identifier is the name for the subprogram instance, and the generic map supplies actual generics for the formal generics defined by the uninstantiated subprogram. If all of the formal generics have defaults, we can omit the generic map to imply use of the defaults. Once we have instantiated the subprogram, we can then use the instance name to call the instance.
1.4 Generic Lists in Subprograms EXAMPLE 1.7
17
Generic swap procedure
The way in which we swap the values of two variables does not depend on the types of the variables. Hence, we can write a swap procedure with the type as a formal generic, as follows: procedure swap generic ( type T ) parameter ( a, b : inout T ) is variable temp : T; begin temp := a; a := b; b := temp; end procedure swap;
We can now instantiate the procedure to get versions for various types: procedure generic procedure generic
int_swap is new swap map ( T => integer ); vec_swap is new swap map ( T => bit_vector(0 to 7) );
and call them to swap values of variables: variable a_int, b_int : integer; variable a_vec, b_vec : bit_vector(0 to 7); ... int_swap(a_int, b_int); vec_swap(a_vec, b_vec);
We can’t just call the swap procedure directly, as follows: swap(a_int, b_int); -- Illegal
since it is an uninstantiated procedure. Note also that we can’t instantiate the swap procedure with an unconstrained type as the actual generic type, since the procedure internally uses the type to declare a variable. Thus, the following would produce an error: procedure string_swap is new swap generic map ( T => string );
since there is no specification of the index bounds for the variable temp declared within swap.
18
Chapter 1 — Enhanced Generics EXAMPLE 1.8
Setup timing check procedure
Suppose we are developing a package of generic operations for timing checks on signals. We include a generic procedure that determines whether a signal meets a setup time constraint. The package declaration is: package timing_pkg is procedure check_setup generic ( type signal_type; type clk_type; clk_active_value : clk_type; T_su : delay_length ) ( signal s : signal_type; signal clk : clk_type ); ... end package timing_pkg;
The package body contains a body for the procedure: package body timing_pkg is procedure check_setup generic ( type signal_type; type clk_type; clk_active_value : clk_type; T_su : delay_length ) ( signal s : signal_type; signal clk : clk_type ) is begin if clk'event and clk = clk_active_value then assert s'last_event >= T_su report "Setup time violation" severity error; end if; end procedure check_setup; ... end package body timing_pkg;
We can now instantiate the procedure to get versions that check the constraint for signals of different types and for different setup time parameters: use work.timing_pkg.all; procedure check_normal_setup is new check_setup generic map ( signal_type => std_ulogic, clk_type => std_ulogic, clk_active_value => '1', T_su => 200ps ); procedure check_normal_setup is new check_setup generic map ( signal_type => std_ulogic_vector, clk_type => std_ulogic, clk_active_value => '1', T_su => 200ps ); procedure check_long_setup is new check_setup generic map ( signal_type => std_ulogic_vector,
19
1.4 Generic Lists in Subprograms clk_type => std_ulogic, clk_active_value => '1', T_su => 300ps );
Note that the procedure check_normal_setup is now overloaded, once for a std_ulogic parameter and once for a std_ulogic_vector parameter. We can apply these functions to signals of std_ulogic and std_ulogic_vector types, as follows: signal status : std_ulogic; signal data_in, result : std_ulogic_vector(23 downto 0); ... check_normal_setup(status, clk); check_normal_setup(result, clk); check_long_setup(data_in, clk); ...
In each case, the active value for the clock signal and the setup time interval value are bound into the definition of the procedure instance. We do not need to provide the values as separate parameters. VHDL-2008 allows us to declare uninstantiated subprograms and to instantiate them in most places where we can currently declare simple subprograms. That includes declaring uninstantiated subprograms as methods of protected types, and declaring instances of subprograms as methods. Since most reasonable use cases for doing this involve use of generic action procedures, we will defer further consideration to Section 1.5, where we introduce generic subprograms. VHDL allows us to overload subprograms, and uses the parameter and result type profiles to distinguish among them based on the types of parameters in a call. Where we need to name a subprogram other than in a call, we can write a signature to indicate which overloaded version we mean. The signature lists the parameter types and, for functions, the return type, all enclosed in square brackets. This information is sufficient to distinguish one version of an overloaded subprogram from other versions. We can use a signature in attribute specifications, attribute names, and alias declarations. Subprogram instantiations, introduced in VHDL-2008, are a further place in which we name a subprogram. If the uninstantiated subprogram is overloaded, we can include a signature in an instantiation to indicate which uninstantiated version we mean. In such cases, the uninstantiated subprograms typically have one or more parameters of a formal generic type. We use the formal generic type name in the signature. For example, if we have two uninstantiated subprograms declared as procedure combine generic ( type T ) parameter ( x : T; value : bit );
20
Chapter 1 — Enhanced Generics procedure combine generic ( type T ) parameter ( x : T; value : integer );
the procedure name combine is overloaded. We can use a signature in an instantiation as follows: procedure combine_vec_with_bit is new combine[T, bit] generic map ( T => bit_vector );
VHDL-2008 specifies that a formal generic type name of an uninstantiated subprogram is made visible within a signature in an instantiation of the subprogram. Thus, in this example, the signature distinguishes between the two uninstantiated subprograms, since only one of them has a profile with T for the first parameter and bit for the second. The T in the signature refers to the formal generic type for that version of the subprogram. As with packages, we can also include a generic map in a subprogram, following the generic list. Such a subprogram is called a generic-mapped subprogram. A genericmapped procedure has the form procedure identifier generic ( ... ) generic map ( ... ) parameter ( ... ) is ... -- declarations begin ... -- statements end procedure identifier;
and a generic-mapped function has the form function identifier generic ( ... ) generic map ( ... ) parameter ( ... ) return result_type is ... -- declarations begin ... -- statements end function identifier;
The generic list defines the generics, and the generic map aspect provides actual values and type for those generics. Like generic-mapped packages, we would not normally write a generic-mapped subprogram explicitly, since the feature is included in the language as a definitional aid. Hence, we won’t dwell on them further, but simply mention them here to raise awareness in case an analyzer produces a seemingly cryptic error message.
21
1.5 Generic Subprograms
1.5
Generic Subprograms As well as generic constants and types, VHDL-2008 allows us to declare generic subprograms. We declare a formal generic subprogram in a generic list, representing some subprogram yet to be specified, and include calls to the formal generic subprogram within the unit that has the generic list. When we instantiate the unit, we supply an actual subprogram for that instance. Each call to the formal generic subprogram represents a call to the actual subprogram in the instance. The way we declare a formal generic subprogram is to write a subprogram specification in the generic list. The specification must be for a simple subprogram; that is, the subprogram must not contain a generic list itself. We will illustrate formal generic subprograms with a number of examples based on typical use cases. One important use case is to supply an operation for use with a formal generic type declared in the same generic list as the subprogram. Recall, from our discussion in Section 1.1, that the only operations we can assume for a formal generic type are those defined for all actual types, such as assignment, equality and inequality. We can use a formal generic subprogram to explicitly provide further operations. EXAMPLE 1.9
Supplying an operator for use with a formal generic type
In Example 1.2, we attempted to define a counter that could count with a variety of types. However, our attempt failed because we could not use the “+” operator to increment the count value. We can rectify this by declaring a formal generic function for incrementing the count value: entity generic_counter is generic ( type count_type; constant reset_value : count_type; function increment ( x : count_type ) return count_type ); port ( clk, reset : in bit; data : out count_type ); end entity generic_counter;
We can then use the increment function in the architecture: architecture rtl of generic_counter is begin count : process (clk) is begin if rising_edge(clk) then if reset = '1' then data (others => '0'), increment => add1 ) -- add1 is the -- actual function port map ( clk => clk, reset => reset, data => count_val );
In the instance, we specify a subtype of unsigned as the actual type for the formal generic type count_type. That subtype is then used as the subtype of the formal generic constant reset_value in the instance, so the actual value is a vector of 16 elements. The subtype is also used for the parameters of the formal generic function increment in the instance, so we must provide an actual function with a matching profile. The add1 function meets that requirement, since it has unsigned as its parameter and result type. Within the instance, whenever the process calls the increment function, the actual function add1 is called. We can instantiate the same entity to create a counter for the traffic_light_colour type defined in Example 1.2. Again, we define a function, next_color, to increment a value of the type, and provide the function as the actual for the increment generic. type traffic_light_color is (red, yellow, green); function next_color ( arg : traffic_light_color ) return traffic_light_color is begin if arg = traffic_light_color'high then return traffic_light_color'low; else return traffic_light_color'succ(arg); end if; end function next_color;
1.5 Generic Subprograms
23
signal east_light : traffic_light_color; ... east_counter : work.generic_counter(rtl) generic map ( count_type => traffic_light_color, reset_value => red, increment => next_color ) -- next_color is the -- actual function port map ( clk => clk, reset => reset, data => east_light );
When we declare a formal generic subprogram in a generic list, we can specify a default subprogram that is to be used in an instance if no actual generic subprogram is provided. The declaration is of the form generic list ( ...; subprogram_specification is subprogram_name; ... );
The subprogram that we name must be visible at that point. It might be declared before the uninstantiated unit, or it can be another formal generic subprogram declared earlier in the same generic list. In the case of an uninstantiated package, we cannot name a subprogram declared in the package as a default subprogram, since items declared within the package are not visible before they are declared. EXAMPLE 1.10 Error reporting in a package Suppose we are developing a package defining operations to be used in a design and need to report errors that arise while performing operations. We can declare a formal generic procedure in the package to allow separate specification of the errorreporting action. We can also declare a default procedure that simply issues a report message. We need to declare the default action procedure separately from the package so that we can name it in the generic list. We will declare it in a utility package: package error_utility_pkg is procedure report_error ( report_string : string; report_severity : severity_level ); end package error_utility_pkg; package body error_utility_pkg is procedure report_error ( report_string : string; report_severity : severity_level ) is begin report report_string severity report_severity; end procedure report_error; end package body error_utility_pkg;
24
Chapter 1 — Enhanced Generics We can now declare the operations package: package operations is generic ( procedure error_action ( report_string : string; report_severity : severity_level ) is work.error_utility_pkg.report_error ); procedure step1 ( ... ); ... end package operations; package body operations is procedure step1 ( ... ) is begin ... if something_is_wrong then error_action("Something is wrong in step1", error); end if; ... end procedure step1; ... end package body operations;
If issuing a report message is sufficient for a given design, it can instantiate the operations package without providing an actual generic subprogram: package reporting_operations is new work.operations; use reporting_operations.all; ... step1 ( ... );
If something goes wrong during execution of step1 in this instance, the call to error_action results in a call to the default generic subprogram report_error defined in the utility package. Another design might need to log error messages to a file. The design can declare a procedure to deal with error messages as follows: use std.textio.all; file log_file : text open write_mode is "error.log"; procedure log_error ( report_string : string; report_severity : severity_level ) is variable L : line; begin write(L, severity_level'image(report_severity);
1.5 Generic Subprograms
25
write(L, string'(": "); write(L, report_string); writeline(log_file, L); end procedure log_error;
The design can then instantiate the operations package with this procedure as the actual generic procedure: package logging_operations is new work.operations generic map ( error_action => log_error ); use logging_operations.all; ... step1 ( ... );
In this instance, when something goes wrong in step1, the call to error_action results in a call to the procedure log_error, which writes the error details to the log file. Since the actual procedure is declared in the context of the instantiating design, it has access to items declared in that context, including the file object log_file. By providing this procedure as the actual generic procedure to the package instance, the instance is able to “import” that context via the actual procedure. In many use cases where an operation is required for a formal generic type, there may be an overloaded version of the operation defined for the actual generic type at the point of instantiation. VHDL-2008 provides a way to indicate that the default for a generic subprogram is a subprogram, directly visible at the point of instantiation, with the same name as the formal generic subprogram and a matching profile. We use the box symbol (“”) in place of a default subprogram name in the generic declaration. For example, we might write the following in a generic list of a package: function minimum ( L, R : T ) return T is
If, when we instantiate the package, we omit an actual generic function, and there is a visible function named minimum with the required profile, then that function is used. Normally, the parameter type T used in the declaration of the formal generic subprogram is itself a formal generic type declared earlier in the generic list. We provide an actual type for T in the instance, and that determines the parameter type expected for the visible default subprogram. If we define the formal generic subprogram with the same name and similar profile to a predefined operation, we can often rely on a predefined operation being visible and appropriate for use as the default subprogram. We will illustrate this with an example. EXAMPLE 1.11 Dictionaries implemented as binary search trees The following package defines an abstract data type for dictionaries implemented as binary search trees. A dictionary contains elements that are each identified by a key value. The formal generic function key_of determines the key for a given element.
26
Chapter 1 — Enhanced Generics No default function is provided, so we must supply an actual function on instantiation of the package. The formal function “ get_vector_for_time ); end protected test_set;
We might declare two shared variables of this protected type, representing two distinct sets of test vectors: shared variable main_test_set, extra_test_set : test_set;
If we invoke the trace_for_time method on one of the shared variables: main_test_set.trace_for_time(100 ns);
the instance of the trace_test_vector procedure invokes the actual subprogram provided for the instance of the protected type. That is, it invokes the get_vector_for_time method associated with the shared variable main_test_set. If, on the other hand, we invoke the trace_for_time method on the other shared variable: extra_test_set.trace_for_time(100 ns);
the instance of the trace_test_vector procedure invokes the get_vector_for_time method associated with the shared variable extra_test_set. What this reveals is that each shared variable of the protected type binds its get_vector_for_time method, which has access to the shared variable’s state, as the actual generic procedure in its instance of the trace_test_vector procedure. That instance, provided as a method of the shared variable, thus has indirect access to the shared variable’s state. The second case to consider is declaration of an uninstantiated subprogram within a protected type. That uninstantiated procedure is not itself a method, since it cannot be called. However, it can be instantiated within the protected type to provide a method. Moreover, each shared variable of the protected type contains a declaration of the uninstantiated subprogram. That subprogram can be instantiated, giving a subprogram that has access to the items encapsulated in the shared variable. We will illustrate these mechanisms with an example. EXAMPLE 1.14 Stimulus list with visitor traversal For a design requiring signed stimulus values, we can declare a procedure for displaying a signed value to the standard output file, as follows: procedure output_signed ( value : in signed ) is use std.textio.all; variable L : line; begin write(L, value);
1.5 Generic Subprograms
35
writeline(output, L); end procedure output_signed;
We also declare a protected type for a list of signed stimulus values: type signed_stimulus_list is protected ... procedure traverse_with_in_parameter generic ( procedure visit ( param : in signed ) ); procedure output_all is new traverse_with_in_parameter generic map ( visit => output_signed ); end protected signed_stimulus_list;
The protected type includes an uninstantiated procedure to apply a visitor procedure to each element in the list of signed values. It instantiates the traversal procedure to provide a method that displays each element. We can use this protected type to declare a shared variable and then invoke the method to display its element values: shared variable list1 : signed_stimulus_list; ... list1.output_all;
Suppose now we want to use the traversal procedure to accumulate the sum of element in a list so that we can calculate the average value. We can provide another action procedure and use it in a further instantiation of the traversal procedure: variable sum, average : signed(31 downto 0); variable count : natural := 0; procedure accumulate_signed ( value : in signed ) is begin sum := sum + value; count := count + 1; end procedure accumulate_signed; procedure accumulate_all_list1 is new list1.traverse_with_in_parameter generic map ( visit => accumulate_signed ); ... accumulate_all_list1; average := sum / count;
36
Chapter 1 — Enhanced Generics In this case, the instance is a procedure declared externally to the protected type. However, since it is an instance of a subprogram defined within the shared variable list1, the instance has access to the encapsulated items within list1. The instance accumulate_all_list1 thus applies the accumulate_signed visitor procedure to each element within list1. If we want to calculate the average value of any list of elements, we need to wrap these declarations up in a procedure that has a shared variable as a parameter. That includes declaring the instance of the traversal procedure within the outer procedure. The complete procedure would be: procedure calculate_average ( variable list : inout signed_stimulus_list variable average : out signed ) is variable sum : signed(average’range); variable count : natural := 0; procedure accumulate_signed ( value : in signed ) is begin sum := sum + value; count := count + 1; end procedure accumulate_signed; procedure accumulate_all is new list.traverse_with_in_parameter generic map ( visit => accumulate_signed ); begin accumulate_all; average := sum / count; end procedure calculate_average;
In this case, the instance of the traversal procedure is also declared externally to the protected type. However, it is an instance of the subprogram defined within the shared variable list provided as a parameter to the calculate_average procedure. Logically, each time the calculate_average procedure is called, a new instance of the traversal procedure is defined particular to the actual shared variable provided as the parameter. The instance thus applies the local accumulate_signed visitor procedure to each element within the actual shared variable.
1.6
Generic Packages One of the common uses of packages is to declare an abstract data type (ADT), consisting of a named type and a collection of operations on values of the type. We have seen in Section 1.2 that we can include a generic list in a package declaration to make the
1.6 Generic Packages
37
package reusable for different actual types and operations. Often, the package for an ADT is reusable in this way. Suppose we have an ADT specified in a package with generics, and we want to provide a further package extending the types and operations of the ADT. To make the extension package reusable, we would have to provide a generic type to specify an instance of the ADT named type, along with generic subprograms for each of the ADT operations. If the ADT has many operations, specifying them as actual generic subprograms in every instance of the extension package would be extremely onerous. To avoid this, VHDL-2008 allows us to specify an instance of the ADT package as a formal generic package of the extension package. Once we’ve instantiated the ADT package, we then provide that instance as the actual generic package of the extension package. There are three forms of formal generic package declaration that we can write in a generic list. The first form is: generic ( ...; package formal_pkg_name is new uninstantiated_pkg_name generic map ( ); ... );
In this case, formal_pkg_name represents an instance of the uninstantiated_pkg_ name package, for use within the enclosing unit containing the generic list. In most use cases, the enclosing unit is itself an uninstantiated package. However, we can also specify formal generic packages in the generic lists of entities and subprograms. When we instantiate the enclosing unit, we provide an actual package corresponding to the formal generic package. The actual package must be an instance of the named uninstantiated packge. The box notation “” written in the generic map of the formal generic package specifies that the actual package is allowed to be any instance of the named uninstantiated package. We use this form when the enclosing unit does not depend on the particular actual generics defined for the actual generic package. No doubt, all of this discussion of packages within packages and generics at different levels can become confusing. The best way to motivate the need for formal generic packages and to sort out the relationships between the pieces is with an example. EXAMPLE 1.15 Fixed-point complex numbers VHDL-2008 defines a new package, fixed_generic_pkg (described in Section 8.4), for fixed-point numbers represented as vectors of std_logic elements. The package is an uninstantiated package, with generic constants specifying how to round results, how to handle overflow, the number of guard bits for maintaining precision, and whether to issue warnings. The package defines types ufixed and sfixed for unsigned and signed fixed-point numbers; and numerous arithmetic, conversion and input/output operations. We can instantiate the package with values for the actual generic constants to get a version with the appropriate behavior for our specific design needs. Now suppose we wish to build upon the fixed-point package to define fixedpoint complex numbers, represented in Cartesian form with fixed-point real and imaginary parts. We want the two parts of a complex number to have the same left and right index bounds, implying the same range and precision for the two parts. We
38
Chapter 1 — Enhanced Generics can achieve that constraint by defining the complex-number type and operations in a package with formal generic constants for the index bounds. The complex-number type is defined using the sfixed type from an instance of the fixed-point package, and the complex-number operations need to use fixed-point operations from that instance. Thus, we include a formal generic package in the generic list of the complex-number package, as follows: library IEEE; package complex_generic_pkg is generic ( left, right : integer; package fixed_pkg_for_complex is new IEEE.fixed_generic_pkg generic map () ); use fixed_pkg_for_complex.all; type complex is record re, im : sfixed(left downto right); end record; function function function function function function
"-" conj "+" "-" "*" "/"
( ( ( ( ( (
z z l l l l
: : : : : :
complex ) complex ) complex; complex; complex; complex;
return complex; return complex; r : complex ) return r : complex ) return r : complex ) return r : complex ) return
complex; complex; complex; complex;
end package complex_generic_pkg;
Within the complex_generic_pkg package, the formal generic package fixed_pkg_for_complex represents an instance of the fixed_generic_pkg package. The
box
notation
in
the
generic
map
indicates
that
any
instance
of
fixed_generic_pkg will be appropriate as an actual package. The use clause makes items defined in the fixed_pkg_for_complex instance visible, so that sfixed can be used in the declaration of type complex. The generic constants left and right are
used to specify the index bounds of the two record elements. The operations defined for sfixed in the fixed_pkg_for_complex instance are also used to implement the complex-number operations in the package body for complex_generic_pkg, as follows: package body fixed_complex_pkg is function "-" ( z : complex ) return complex is begin return ( -z.re, -z.im ); end function "-"; ... end package body fixed_complex_pkg;
1.6 Generic Packages
39
In the “–” operation for type complex, the “–” operation for type sfixed is applied to each of the real and imaginary parts. The other operations use the sfixed operations similarly. In a design, we can instantiate both the fixed-point package and the complexnumber package according to our design needs, for example: package dsp_fixed_pkg is new IEEE.fixed_generic_pkg generic map ( fixed_rounding_style => true, fixed_overflow_style => true, fixed_guard_bits => 3, no_warning => false ); package dsp_complex_pkg is new work.complex_generic_pkg generic map ( left => 3, right => -12, fixed_pkg_for_complex => dsp_fixed_pkg );
The first instantiation defines an instance of the fixed-point package, which provides the type sfixed and operations with the required behavior. The second instantiation defines an instance of the complex-number package with left and right bounds of 3 and –12 for the real and imaginary parts. The type sfixed and the corresponding operations used within this instance of the complex-number package are provided by the actual generic package dsp_fixed_pkg. We can use the packages to declare variables and apply operations as follows: use dsp_fixed_pkg.all, dsp_complex_pkg.all; variable a, b, z : complex variable c : sfixed; ... z := a + conj(b); z := (c * z.re, c * z.im);
The second form of formal generic package that we can write in a generic list is: generic ( ...; package formal_pkg_name is new uninstantiated_pkg_name generic map ( actual_generics ); ... );
Again, formal_pkg_name represents an instance of the uninstantiated_pkg_name package, for use within the enclosing unit containing the generic list. The actual generics provided in the generic map of the formal generic package specify that the actual package must be an instance of the named uninstantiated package with those same actual generics. We generally use this form when the enclosing unit also has another formal generic package defined earlier in its generic list. The latter generic is expected to have a generic package that is the same instance as the actual for the earlier generic package. No doubt that statement is unfathomable due to the packages within packages within
40
Chapter 1 — Enhanced Generics packages. An example, building on Example 1.15, will help to motivate the need for the language feature and show how it may be used. EXAMPLE 1.16 Mathematical operations on fixed-point complex numbers In Example 1.15, we defined a package for complex number that provided a complex-number type and basic arithmetic operations. We can build upon this package to define a further package for more advanced mathematical operations on complex values. We will also use a package of advanced mathematical operations defined for fixed-point values: package fixed_math_ops is generic ( package fixed_pkg_for_math is new IEEE.fixed_generic_pkg generic map () ); use fixed_pkg_for_math.all; function sqrt ( x : sfixed ) return sfixed; function exp ( x : sfixed ) return sfixed; ... end package fixed_math_ops;
This package has a formal generic package for an instance of the fixed_generic_ pkg package, since the operations it applies to the function parameters of type sfixed must be performed using the behavior defined for the sfixed type in the package instance proving the type. This is a similar scenario to that described in Example 1.15. The advanced complex-number operations must be performed using the same sfixed type and basic fixed-point operations used to define the complex-number type and operations. It must also use the advanced fixed-point operations and the complex-number type and operations, with those types and operations being based on the same sfixed type and basic fixed-point operations. Thus, the advance complex-number package must have formal generic packages for the fixed-point package, the fixed-point mathematical operations package, and the complex-number package, as follows: package complex_math_ops is generic ( left, right : integer; package fixed_pkg_for_complex_math is new IEEE.fixed_generic_pkg generic map (); package fixed_math_ops is new work.fixed_math_ops generic map ( fixed_pkg_for_math => fixed_pkg_for_complex_math );
41
1.6 Generic Packages
package complex_pkg is new work.complex_generic_pkg generic map ( left => left, right => right, fixed_pkg_for_complex => fixed_pkg_for_complex_math ) ); use fixed_pkg_for_complex_math.all, fixed_math_ops.all, complex_pkg.all; function "abs" ( z : complex ) return sfixed; function arg ( z : complex ) return sfixed; function sqrt ( z : complex ) return complex; ... end package complex_math_ops;
The package body is package body complex_math_ops is function "abs" ( z : complex ) return sfixed is begin return sqrt(z.re * z.re + z.im * z.im); end function "abs"; ... end package body complex_math_ops;
We can now instantiate the packages for a given design. For example, given the instances dsp_fixed_pkg and dsp_complex_pkg declared in Example 1.15, we can also declare instances of the advanced fixed-point operations package and the advanced complex operations package: package dsp_fixed_math_ops is new work.fixed_math_ops generic map ( fixed_pkg_for_math => dsp_fixed_pkg ); package dsp_complex_math_ops is new work.complex_math_ops generic map ( left => 3, right => -12, fixed_pkg_for_complex_math => dsp_fixed_pkg, fixed_math_ops => dsp_fixed_math_ops, complex_pkg => dsp_complex_pkg );
The third form of formal generic package that we can write in a generic list is: generic ( ...; package formal_pkg_name is new uninstantiated_pkg_name generic map ( default ); ... );
42
Chapter 1 — Enhanced Generics This form is similar in usage to the second form, but replaces the actual generics with the reserved word default. We can use this third form when the named uninstantiated package has defaults for all of its formal generics. The actual package must then be an instance of the named uninstantiated package with all of the actual generics being the same as the defaults. Those actual generics (for the actual generic package) can be either explicitly specified when the actual package is instantiated, or they can be implied by leaving the actual generics unassociated. Thus, this third form is really just a notational convenience, as it saves us writing out the defaults again as actual generics in the generic map of the formal generic package. While generic packages might seem to be rather complex to put into practice, we envisage that most of the time packages using generic packages will be developed by personnel in support of design teams. They would normally provide source code templates for designers to instantiate the packages, including instantiating any dependent packages as actual generics. Thus, the designers would be largely insulated from the complexity. For the developers of such packages, however, there are a number of rules relating to formal and actual generic packages. As we have mentioned, the actual package corresponding to a formal generic package must be an instance of the named uninstantiated package. To summarize the rules relating to the generic map in the formal generic package: • If the generic map of the formal generic package uses the box (“”) symbol, the actual generic package can be any instance of the named uninstantiated package. • If the formal generic package declaration includes a generic map with actual generics, then the actual generics in the actual package’s instantiation must match the actual generics in the formal generic package declaration. • If the formal generic package declaration includes a generic map with the reserved word default, then the actual generics in the actual package’s instantiation must match the default generics in the generic list of the named uninstantiated package. The meaning of the term “match” applied to actual generics depends on what kind of generics are being matched. For generic constants, the actuals must be the same value. It doesn’t matter whether that value is specified as a literal, a named constant, or any other expression. For a generic type, the actuals must denote the same subtype; that is, they must denote the same base type and the same constraints. Constraints on a subtype include range constraints, index ranges and directions, and element subtypes. For generic subprograms, the actuals must refer to the same subprogram, and for generic packages, the actuals must refer to the same instance of a specified uninstantiated package. In the case of a default generic subprogram implied by a box symbol in the generic list of the named uninstantiated package, the actual subprogram must be the subprogram of the same name and conforming profile directly visible at the point where the formal generic package is declared. For example, if an uninstantiated package is declared as package pkg1 is generic ( function " CpuWrite(CpuRec, DMA_WORD_COUNT, DmaWcIn); I0 := 0; -- modify weight with I1 => CpuWrite(CpuRec, DMA_ADDR_HI, DmaAddrHiIn); I1 := 0; -- modify weight with I2 => CpuWrite(CpuRec, DMA_ADDR_LO, DmaAddrLoIn); I2 := 0; -- modify weight end randcase; end loop; CpuWrite(CpuRec, DMA_CTRL, START_DMA or DmaCycle);
10.3
Functional Coverage
235
10.3 Functional Coverage Functional coverage is intended to supplement other forms of coverage. Tool based code coverage provides information about what parts of a design are exercised during a simulation. However, it cannot test whether an aspect of the specification for the design is actually implemented. Functional coverage features, on the other hand, allow us to measure the occurrence of difference categories of data values during a simulation. We can thus determine whether processing of categories of interest has been exercised. To measure functional coverage, we specify a bin (a value or range of values) for each category of a data object. During simulation, for each bin, the tool records the number of transactions that produce values in the bin. We can analyze the result to identify bins for which no transactions occurred, and adjust our stimulus generation or randomization constraints accordingly. The VHDL-TC plans to incorporate functional coverage features into a future extension of VHDL. The details of language features are yet to be determined.
10.4 Alternatives One question that comes up frequently is, why update VHDL? Instead, why not adopt SystemVerilog as the verification language? The answer is much simpler than one would expect. From a language perspective, VHDL already includes many system-level modeling features, such as records, access types (pointers), and protected types. Many of these features can be enhanced with relatively little impact on the language, and new features can be added in a way that integrates cleanly with existing features. From a project perspective, organizations using VHDL already have significant experience using the language. If a verification engineer is needed for a project, the organization has a pool of people familiar with VHDL. A person from that pool can build on their existing knowledge of VHDL, provided the language includes the necessary verification features. If they were to adopt SystemVerilog, they would not only have to learn a language that is quite idiomatically different, but they would also have to manage a multilingual design/verification environment. Both of these issues would adversely affect their productivity.
10.5 Getting Involved Standards development is a volunteer-run effort, and depends on your participation. As you become an experienced VHDL design and/or verification engineer, it is both your right and responsibility to participate. You can participate by submitting enhancement requests, participating in the standards groups, helping with funding, and helping with vendor support. One person can make a difference. No matter how hard the VHDL-TC works, without your ideas, the group may overlook the changes you desire. You can submit your enhancement requests using the web page at http://www.eda.org/vasg. VHDL standards are co-developed by IEEE and Accellera. Currently most of the new technical development is done by the Accellera VHDL-TC. The IEEE VHDL Analysis and Standardization Group (VASG) resolves issues with the current IEEE VHDL standard and
236
Chapter 10 — What’s Next conducts balloting for new IEEE versions of the standard. For more information about the Accellera VHDL-TC, see http://www.accellera.org/vhdl, and for more information about the IEEE VASG, see http://www.eda.org/vasg. Volunteers run these standards groups, and members tend to work on what interests them personally. For a request to become a proposal and then a language feature, someone has to champion it. The best way to make this happen is to participate. Participation is open to anyone who has the background and is willing to invest the time. You can join the technical subcommittees, participate in email reflectors, attend teleconferences, attend in-person meetings, and actively participate in all discussions. Most technical decisions are held at a level where everyone can contribute. When decisions have conflicting choices, the issue is put to a member vote. To have a member vote in the Accellera VHDL-TC, your company must join Accellera. To have a member vote in the IEEE VASG, you need to join the parent group, IEEE Design Automation Standards Committee (DASC, see http://www.dasc.org) and maintain an active history of voting participation. While much of the work is volunteer based, the task of integrating the language change proposals and editing the standard is a time-intensive task and is undertaken by a paid technical editor. This person is a VHDL expert with deep language design knowledge. Currently, this position is funded through Accellera. If your company is able, please encourage them to become an Accellera member and help fund future revisions of the VHDL standard. Finally, ongoing evolution of VHDL requires vendor support. Part of achieving this is to understand why vendors implement standards. For an EDA vendor, supporting a standard is a business decision. In general, this means they support the features their customers request. Hence, you can influence the process by learning the new features and making the vendors aware of the ones that are important to you. The person with the most power is the person who funds your tool licenses. Make sure they are aware of what you need and make sure to forward your requests through them.
Index Page numbers in bold face denote whole sections and subsections that address a topic.
A abs operator, 195, 196
absolute pathname, 56 abstract data type (ADT), 36 access type, 3 allocator, 3 index range, 111, 117 viewport, 92 action procedure, example, 23, 28, 34 active, 64 actual, index range, 115, 118 adder, configuration example, 156 addition, aggregate target example, 166 addition operator (+), 194, 195 array/scalar operands, 129, 181 Advanced Encryption Standard (AES) cipher, 78, 93 Advanced Verification Methodology (AVM), 232 aggregate array, 166, 212, 213 assignment target, 66, 166 index range, 217 others, 212, 217 range, 166 record, 213 slice, 166 alias, 64 array, 217 external name, 54 index range, 119 unconstrained port or parameter, 120 all, sensitivity list, 57, 161 allocator, 3 index range, 111, 117 analysis, 53 and operator, 127, 130, 194, 195 application name, 101 arbiter matching case example, 149 matching selected assignment example, 150
architecture declarative region, 11, 223 foreign, 97, 98, 99 in pathname, 56 array, 103 aggregate, 166, 212, 213 alias, 217 constrained, 104 discrete type, 139 element, 213 index range, 117 reduction operator, 130 resolved element, 124 slice. See slice std_ulogic elements, 193 subtype, 107 to_string result, 170 type, 104, 209 unbounded, 104 unconstrained element type, 103 ascending attribute, 215 assertion message, 180, 182 assertion statement, 169, 221 ambiguity with PSL, 71 condition operator (??), 132 newline, 174 assertion violation, 221 assignment, 3, 218 aggregate target, 66, 166 signal. See signal assignment variable. See variable assignment asymmetric cipher, 78 attribute, 3 index range, 111, 117, 214 attribute specification, 76 overloaded subprogram, 219 package body, 219 author of protected IP, 90, 95
B base type, 104 base64 encoding method, 79, 93, 94 binary bit-string literal, 167 binary search tree, example, 25 binary string conversion, 172 binding, verification unit, 73 bit type, 191, 196 condition operator (??), 132
bit_vector type, 191, 193, 196
bit-string literal, 167 length, 167 signed, 168 unsigned, 168 block configuration, 155, 157, 158 block statement, 11 declarative region, 11 in pathname, 56 Blowfish cipher, 93 boolean type, 191 boolean_vector type, 191 box symbol (), 25, 37, 42 bread alias, 177 buffer mode port, 162 bwrite alias, 177
C carriage return, 174 carry, 129 aggregate target example, 166 case statement choice, 149, 211 expression subtype, 211 matching, 149 case-generate statement, 151 alternative label, 157 configuration, 155 CAST-128 cipher, 93 certification authority (CA), 96 character, replacement, 226 character, to_string result, 170 check_error parameter, 197, 198, 201, 202 choice aggregate others, 217 case statement, 149, 211 matching case statement, 150 cipher, 77, 90, 94 specification, 93 cipher text, 77 class type, 229 combinational logic, 161 comment delimited, 224 IP protection, 93, 95
237
238 communication, interface example, 231 complementary outputs flip-flop example, 163 verification unit example, 74 complex multiplier case-generate statement example, 153 configuration example, 157 complex number, example, 37, 40 component configuration, verification unit binding, 73 component instance, 56, 159 verification unit binding, 73 composite subtype, 109 composite type, 3, 103, 107 static expression, 213 concatenation operator (&), 191 concurrent region, 54, 59 concurrent signal assignment, 143 concurrent statement, 152, 153 condition, 132 matching operator example, 138 condition operator (??), 132, 180, 192 conditional assignment, 143 force, 146 conditional incrementer, example, 129 conditional signal assignment, 143 conditional variable assignment, 147 configuration declaration, 155 verification unit binding, 73 configuration specification verification unit binding, 73 conforming profile, 30, 42 constant declaration, 107 external name, 53, 55 generic See generic constant generic type, 3 index range, 110, 117 initial value, 110, 117 constrained array, 104 constrained subtype, 3, 109 constraint, 103, 107, 116, 117, 121 index, 104, 112 context clause, 67 before context declaration, 70 context declaration, 67 example, 68 standard, 70 context reference, 67 control condition, example, 133 conversion function, 185, 191, 196
Index in actual part, 116 in association, 115, 118 example, 46 in formal part, 117 in port map, 160, 161 result subtype, 209 subtype, 112 type, 3, 209 in actual part, 116 in association, 115, 118 element type, 210 in formal part, 117 implicit, 220 index range, 118, 210 in port map, 161 counter, generic example, 4, 21 coverage, 235
D Data Encryption Standard (DES) cipher, 78 decimal bit-string literal, 168 declaration, 152, 153 PSL, 71 declarative region, 11 architecture, 223 decryption, 77 decryption envelope, 80, 89, 95 decryption license, 92, 95 decryption tool, 79 key exchange, 96 default clock declaration, 71 default generic, 42 default initial value, 3 default, reserved word, 42 default subprogram, 23, 25 default value generic constant, 5, 218 parameter, 30 delay generic example, 207 in signal assignment, 145 delimited comment, 224 delta cycle, 64 delta delay, 160 denormalize parameter, 202 denormalize parameter, 198, 201 denormalize_in parameter, 202 denormalize_out parameter, 202 DES cipher, 93 design unit, 6, 11, 67 verification unit, 73 dictionary, example, 25, 28 digest, 78, 91, 94, 96 digital envelope, 78, 94, 95
example, 82, 83 digital signature, 78, 94, 96 example, 85 direct binding, 97 directive protect, 77, 225 PSL, 71 tool, 89, 225 disconnection, 146 discrete array type, 139 division operator ( / ), 194, 195 don’t care (–), 149 driver, 3 driving value, 65, 147 dump memory, 43 dynamic dispatch, 229
E effective value, 65, 147 elaboration, 53, 55 generate statement, 152 elaboration function, 98, 99 element array, 213 formal, 114 index range, 120 unconstrained type, 103 element attribute, 120 element subtype, 104, 106, 108, 120 element type, type conversion, 210 ElGamal cipher, 78, 93 encoding, 79, 91, 95 specification, 93 encryption, 77 encryption envelope, 80, 89, 94 encryption tool, 79, 90, 95 key exchange, 96 entity declarative region, 11 instantiation, 11 in pathname, 56 enumeration literal, 215 enumeration type to_string result, 170 in use clause, 215 env package, 192 equality operator (=), 2, 3, 133, 191, 194 equality operator, matching (?=), 133, 149, 180, 181, 192, 194 error reporting, example, 23 event, 3, 64 execution function, 98, 99, 100 exit statement, condition operator (??), 132
239
Index expanded name, 14 exponent_width parameter, 201, 202 expression port map, 159 range bounds, 220 static, 159, 213 external name, 53 alias, 54 signal, 64
F falling_edge function, 192 field, 173 FIFO, class type example, 230 file declaration, 107 file type, 3 find_leftmost function, 181 find_rightmost function, 181 finish procedure, 192 finite-state machine combinational logic example, 161 external name example, 57, 60 force example, 64 fixed_float_types package, 183 fixed_generic_pkg package, 37, 40, 182, 193, 196 fixed_guard_bits generic, 183 fixed_overflow_style generic, 183 fixed_overlow_style_type, 183 fixed_pkg package, 182 fixed_round_style generic, 183 fixed_round_style_type, 183 fixed-point math package, 182 flip-flop, example, 163 float type, 188, 193, 201 float_check_error generic, 187 float_denormalized generic, 187 float_exponent_width generic, 187 float_fraction_width generic, 187 float_generic_pkg package, 186, 193, 196 float_guard_bit generic, 187 float_pkg package, 186 float_round_style generic, 187 float128 type, 189 float32 type, 189 float64 type, 189 floating point type, to_string result, 170 floating-point math package, 186 flush procedure, 178 force, 63, 146 aggregate target, 66 default mode, 65 mode, 65, 147
multiple, 67 in subprogram, 67 foreign application, 97 registration, 101 foreign architecture, 97, 98, 99 foreign attribute, 97, 100 foreign model, 97 foreign subprogram, 97, 98, 100 for-generate statement, 151 parameter, 214 formal element, 114 index range, 112, 115, 118 slice, 114 subtype, 118 fraction_width parameter, 201, 202 fully constrained subtype, 104, 108, 110, 112, 117, 121 function conversion, 185, 191, 196 in actual part, 116 in association, 115, 118 in formal part, 117 in port map, 160, 161 generic list, 15 predefined, 213 resolution. See resolution function return subtype, 208 function call, in port map, 160 functional coverage, 235
G generate statement case-generate. See case-generate statement if-generate. See if-generate statement in pathname, 56 generic default, 42 formal, 207 matching, 42 generic constant, 1, 4 actual, 1 aggregate, 218 declaration, 107 default value, 5, 218 external name, 58 formal, 1 index range, 112 matching, 42 generic list, 4, 11, 21, 207 in package, 6 in subprogram, 15 generic map, 6, 11, 16, 39, 42
generic package, 36 actual, 37, 42 formal, 37, 42 matching, 42 generic subprogram, 21, 32 actual, 21 call, 21 formal, 21 matching, 42 generic type, 1 actual, 1, 3 constant, 3 default, 5 distinct, 9 formal, 1, 3 in signature, 19 matching, 42 operation defined by generic subprogram, 21 operations not defined, 4 signal, 3 variable, 3 generic-mapped package, 10, 12 generic-mapped subprogram, 20 getable interface, 231 greater than operator (>), 133, 191, 194 greater than operator, matching (?>), 133, 180, 181, 192, 194 greater than or equal operator (>=), 133, 191, 194 greater than or equal operator, matching (?>=), 133, 180, 181, 192, 194 guard signal, 146 guard_bits parameter, 198 guarded signal assignment, 146
H handshake assertion, example, 71 hash function, 78, 91, 95 specification, 93 hexadecimal bit-string literal, 167 hexadecimal read and write, 175 hexadecimal string conversion, 172 hiding, 216 hread procedure, 175 hwrite procedure, 175
I identification number, example, 11, 13 ieee_bit_context, 70
240 ieee_std_context, 70 if statement, condition operator (??), 132 if-generate statement, 151 alternative label, 155 omitted, 158 condition operator (??), 132 configuration, 155 impure function, 223 incrementer, generic function example, 21 index constraint, 104, 107, 112 index range, 104, 107, 109 actual, 115, 118 aggregate, 217 alias, 119 allocator, 111, 117 array, 117 attribute, 111, 117, 214 constant, 110, 117 element, 120 formal, 112, 115, 118 generic constant, 112 interface object, 112, 118 operator result, 195 parameter, 112 port, 112 signal, 110, 117 static, 214 subtype, 112 type conversion, 118, 210 variable, 110, 117 index subtype, 104, 109 indirect binding, 99 inequality operator (/=), 3, 133, 191, 194 inequality operator, matching (?/=), 133, 180, 181, 192, 194 inertial delay, 145 inertial, in port map, 160 infinity, 190 inheritance, 229 initial value, 4 constant, 110, 117 default initial value, 3 instance_name attribute, 221 instantiation component, 56 package, 6, 14, 37, 39, 42, 56 subprogram, 16, 19 integer type, to_string result, 170 integer_vector type, 191 interface object index range, 112, 118 mode, 116 interface type, 230
Index IP encryption, 77 is_X function, 142, 182, 205
J justify function, 173
K key, 77, 94 exchange, 96 method, 90 name, 90 owner, 90
L label block statement, 56 case-generate alternative, 157 component instance, 56 generate statement, 56 if-generate alternative, 155, 158 latch inference, 161 left attribute, 215 left_index parameter, 198, 202 length attribute, 120, 215 less than operator (