Vous êtes sur la page 1sur 30

Unit-3 Subprogram and Packages:

Subprogram and Overloading: Functions and Procedures with simple examples, subprogram overloading, Operator Overloading. Electronics Syllabus: Packages and Libraries: Package Declaration, Package Body, Design File, Design Libraries, Order of analysis, implicit visibility, explicit visibility, Library Clause and Use Clause. Advanced Features: Entity Statements, Generate Statements, Attributes, Aggregate targets, ports and their behaviour. 3.1 Subprogram and Overloading: There are two kinds of subprograms: procedures and functions. Both procedures and functions written in VHDL must have a body and may have declarations. Procedures perform sequential computations and return values in global objects or by storing values into formal parameters. Functions perform sequential computations and return a value as the value of the function. Functions do not change their formal parameters. Subprograms may exist as just a procedure body or a function body. Subprograms may also have a procedure declarations or a function declaration. When subprograms are provided in a package, the subprogram declaration is placed in the package declaration and the subprogram body is placed in the package body. 3.1.1 Functions: A function is a subprogram that is passed parameters and returns a single value. Functions are primarily used in expressions. Syntax: function designator [ ( formal_parameter_list ) ] return type_mark Function Designator is the identifier or name of the function. Formal parameter list is optional. Type mark represents the data type in which the function returns its value. Example: -- Convert bit_vector to IEEE std_logic_vector format -- (attributes LENGTH and RANGE are described below) function bv2slv (b:bit_vector) return std_logic_vector is variable result: std_logic_vector(b'LENGTH-1 downto 0); begin for i in result'RANGE loop case b(i) is when '0' => result(i) := '0'; when '1' => result(i) := '1'; end case; end loop;

return result; end; -- Convert bit_vector to unsigned (natural) value function b2n (B: bit_vector) return Natural is variable S: bit_vector(B'Length - 1 downto 0) := B; variable N: Natural := 0; begin for i in S'Right to S'Left loop if S(i) = '1' then N := N + (2**i); end if; end loop; return N; end; 3.1.2 Function Calls: signal databus: vector4(15 downto 0); signal internal: bit_vector (15 downto 0); variable x: integer; .... databus <= bv2slv (internal); A subprogram declaration in this form simply names the subprogram and specifies the parameters required. The body of statements defining the behaviour of the subprogram is deferred. For function subprograms, the declaration also specifies the type of the result returned when the function is called. This form of subprogram declaration is typically used in package specifications, where the subprogram body is given in the package body, or to define mutually recursive procedures. The syntax for specifying the formal parameters of a subprogram is: Syntax: formal_parameter_list ::= parameter_interface_list interface_list ::= interface_element { ; interface_element } interface_element ::= interface_declaration interface_declaration ::= interface_constant_declaration | interface_signal_declaration | interface_variable_declaration interface_constant_declaration ::= [ constant ] identifier_list : [ in ] subtype_indication [ := static_expression ] interface_variable_declaration ::= [ variable ] identifier_list : [ mode ] subtype_indication [ := static_expression ] 3.1.3 Procedure: A procedure is a subprogram that is passed parameters and may return values via a parameter list.

Example: procedure proc_name (signal clk: in vlbit; constant d: in vlbit; signal data: out vlbit) is ... local variable declarations ... begin ... sequence of statements ... end proc_name; Procedure call: proc_name(clk1, d1, dout); The procedure is a form of subprograms. It contains local declarations and a sequence of statements. Procedures can be called in any place of the architecture. The procedure definition consists of two parts: The procedure declaration The procedure body 3.1.3.1 Procedure Declaration: Procedure declaration consists of procedure name and the formal parameter list. It is required when the procedure is called. In the procedure specification, the identifier and the formal parameter list follow the reserved word procedure. Bjects, classes, constants, variables, signals and files can be used as formal parameters. The class of each parameter is specified by the appropriate reserved word, unless the default class can be assumed. In case of constants, variables and signals, the parameter mode determines the direction of the information flow and it decides which formal parameters can be read or written inside the procedure. For parameters of the file type_name, no mode is assigned. There are three modes available: in, out and inout. When in mode is declared and object class is not defined, then by default it is assumed that the object is a constant. In case of inout and out modes, the default class is a variable. When a procedure is called, formal parameters are substituted by actual parameters. If a formal parameter is a constant, then the actual parameter must be an expression. In case of formal parameters such as signal, variable and file, the actual parameters must be objects of the same class. A procedure can also be declared without any parameters. 3.1.3.2 Procedure Body: Procedure body consists of the local declarations and statements required to execute the procedure. Procedure body defines the procedures algorithm composed of sequential statements. When the procedure is called, it starts executing the sequence of statements declared inside the procedure body. The procedure body consists of the subprogram declarative part. After the reserved word is the subprogram statement part is placed between the reserved words begin and end. The keyword procedure and the procedure name may optionally follow the end reserved word. Declarations of a procedure are local to this declaration and can declare subprogram declarations, subprogram bodies, types, subtypes, constants, variable, files, aliases,

attribute declaration, attribute specifications, USE clause, group templates and group declarations. A procedure can contain any sequential statements (including WAIT statement). A WAIT statement, however, cannot be used in procedures, which are called from a process with a sensitivity list or from within a function. For now we will only consider constant and variable parameters, although signals can also be used. Some examples will clarify this syntax. Firstly, a simple example of a procedure with no parameters: Example: procedure reset; This simply defines reset as a procedure with no parameters, whose statement body will be given subsequently in the VHDL program. A procedure call to reset would be: reset; Secondly, here is a declaration of a procedure with some parameters: procedure increment_reg(variable reg : inout word_32; constant incr : in integer := 1); In this example, the procedure increment_reg has two parameters, the first called reg and the second called incr. reg is a variable parameter, which means that in the subprogram body, it is treated as a variable object and may be assigned to. This means that when the procedure is called, the actual parameter associated with reg must itself be a variable. The mode of reg is inout, which means that reg can be both read and assigned to. Other possible modes for subprogram parameters are in, which means that the parameter may only be read, and out, which means that the parameter may only be assigned to. If the mode is inout or out, then the word variable can be omitted and is assumed. The second parameter, incr, is a constant parameter, which means that it is treated as a constant object in the subprogram statement body, and may not be assigned to. The actual parameter associated with incr when the procedure is called must be an expression. Given the mode of the parameter, in, the word constant could be omitted and assumed. The expression after the assignment operator is a default expression, which is used if no actual parameter is associated with incr in a call to the procedure. A call to a subprogram includes a list of actual parameters to be associated with the formal parameters. This association list can be position, named, or a combination of both. (Compare this with the format of aggregates for values of composite types.) A call with positional association lists the actual parameters in the same order as the formals. Example: increment_reg(index_reg, offset2); -- add value to index_reg increment_reg(prog_counter); -- add 1 (default) to prog_counter A call with named association explicitly gives the formal parameter name to be associated with each actual parameter, so the parameters can be in any order.

Example: increment_reg(incr => offset2, reg => index_reg); increment_reg(reg => prog_counter); Note that the second call in each example does not give a value for the formal parameter incr, so the default value is used. Thirdly, here is an example of function subprogram declaration: Example: function byte_to_int(byte : word_8) return integer; The function has one parameter. For functions, the parameter mode must be in, and this is assumed if not explicitly specified. If the parameter class is not specified it is assumed to be constant. The value returned by the body of this function must be an integer. When the body of a subprogram is specified. Syntax: subprogram_body ::= subprogram_specification is subprogram_declarative_part begin subprogram_statement_part end [ designator ] ; subprogram_declarative_part ::= { subprogram_declarative_item } subprogram_statement_part ::= { sequential_statement } subprogram_declarative_item ::= subprogram_declaration | subprogram_body | type_declaration | subtype_declaration | constant_declaration | variable_declaration | alias_declaration The declarative items listed after the subprogram specification declares things which are to be used locally within the subprogram body. The names of these items are not visible outside of the subprogram, but are visible inside locally declared subprograms. Furthermore, these items shadow any things with the same names declared outside the subprogram. When the subprogram is called, the statements in the body are executed until either the end of the statement list is encountered, or a return statement is executed. Syntax: return_statement ::= return [ expression ] ; If a return statement occurs in a procedure body, it must not include an expression. There must be at least one return statement in a function body, it must have an expression, and the function must complete by executing a return statement. The value of the expression is the valued returned to the function call. Another point to note about function subprograms is that they may not have any side-effects. This means that no visible

variable declared outside the function body may be assigned to or altered by the function. This includes passing a non-local variable to a procedure as a variable parameter with mode out or inout. The important result of this rule is that functions can be called without them having any effect on the environment of the call. Example: function byte_to_int(byte : word_8) return integer is variable result : integer := 0; begin for index in 0 to 7 loop result := result*2 + bit'pos(byte(index)); end loop; return result; end byte_to_int; 3.1.2 Subprogram Overloading: Subprogram may be defined as overloaded if two subprograms have identical name, different parameters and works with any kind of subprogram. During compilation/runtime that subprogram is called whose formal parameter matdh the provided actuals. It is especially useful when applied to operators, which can be seen as functions with a special name. This allows, for example, to use the conventional + symbol for the addition of integer values and, likewise, with bit vectors that should be interpreted as numbers. Example Procedure READ ( L : inout line; VALUE : out character; GOOD : Boolean); Procedure READ ( L : inout line; VALUE : out integer); Notes: Function Overloading: The concept of function overloading is that an operator can be used to perform several different functions. For example, the addition operation in basic language 1 +2 =3 but 1 +2 = 3 The actual operation is determined by the types and number of parameters. The same scenario takes place in VHDL: Example: library ieee; use ieee.std_logic_1164. all; package ABC is function F11 (a, b, cin : in std_logic) return std_ logic; function F11 (a, b: integer) return integer; end ABC; package body ABC1 is function F11 (a, b, cin : in std_logic)

return std_ logic is begin return ( a And b) Or (A And cin) Or (b And cin); end F11: function F11 (a, b: in integer) return integer is begin return (a + b); end F11; end ABC; 3.1.3 Operator Overloading: The operator is called overloaded if there is more than one function specifying it for different data and result types. VHDL allows defining operators of the same names as predefined operators, but for different operand types. Both can exist together in one specification, offering greater versatility to the user. Such functions can be invoked both with prefix notation and usual infix notation. Example for Prefix notation: type ABC is (0, 1, z, x); function Nand (left, right : ABC) return ABC; function Or (left, right : ABC) return ABC; Signal S1, S2: ABC; S1 <= Or (1, z); S2 <= Nand (S1, x); Function Or and Nand implement basic logic operations for operands of type ABC overloading the predefined operations of the same names. In the above example, these functions are called using the standard call syntax function (prefix). Example for Infix notation: Signal S3, S4: ABC; S3 <= (S1 Nand S2) Or x; The operators Or and Nand are overloaded through the declarations as in the example for the prefix notation. Here the overloaded operators are used in the infix form, i.e. with the operator name between the operands. In the case when the operator is called and the operand values belong to different types, it is necessary to use the type conversion or the qualified expression in order to select appropriate operator. Example: function Or (left, right : ABC) return ABC; Signal S4: bit; S4 <= ABC (1 ) Or ABC (0);

The Or operator is used here in a 4-value logical expression by connecting 1 and 0 operands with the Or symbol. The qualified expression was used here to indicate type of operands ( which otherwise would be considered to be of the type BIT. All standard VHDL operators can be overloaded but is not allowed to define new operators. Operator declarations are equivalent to function declarations apart from the name which must be placed in quotation marks(). The number of parameters is also fixed and can not be modified. In case of binary operators, i.e. operators with two operands, the left/right operand are mapped to the left/rightmost parameter, respectively. Example Function and(left: L_OVER; right : L_OVER) return L_OVER; Signal A,B,C : L_OVER; A<= Z and 0; Here operator AND is an overloaded operator because the type of left operand is of L_OVER type. 3.2 Packages and Libraries: A package is a collection of types, constants, subprograms and possibly other things, usually intended to implement some particular service or to isolate a group of related items. In particular, the details of constant values and subprogram bodies can be hidden from users of a package, with only their interfaces made visible. A package may be split into two parts: 1 A package declaration, which defines its interface, and 2 A package body, which defines the deferred details. The body part may be omitted if there are no deferred details. 3.2.1 Package Declaration: A package declaration contains a set of declarations that can be shared by many design units. The syntax of a package declaration is: package_declaration ::= package identifier is package_declarative_part end [ package_simple_name ] ; package_declarative_part ::= { package_declarative_item } package_declarative_item ::= subprogram_declaration | type_declaration | subtype_declaration | constant_declaration | alias_declaration | use_clause The declarations define things which are to be visible to users of the package, and which are also visible inside the package body. (There are also other kinds of declarations which can be included, but they are not discussed here.) Package declarations can be accessed by other design units by using LIBRARY and USE clauses. Example: package data_types is subtype address is bit_vector(24 downto 0); subtype data is bit_vector(15 downto 0); constant vector_table_loc : address;

function data_to_int(value : data) return integer; function int_to_data(value : integer) return data; end data_types; In this example, the value of the constant vector_table_loc and the bodies of the two functions are deferred, so a package body needs to be given 3.2.2 Package Body: Package Body basically contains the behavior of the subprogram. Syntax: package_body ::= package.body package_simple_name is package_body_declarative_part end [ package_simple_name ] ; package_body_declarative_part ::= { package_body_declarative_item } package_body_declarative_item ::= subprogram_declaration | subprogram_body | type_declaration | subtype_declaration | constant_declaration | alias_declaration | use_clause Note that subprogram bodies may be included in a package body, whereas only subprogram interface declarations may be included in the package interface declaration. The package name & its corresponding package declaration name must be the same. An item declared in the package body can be used only within that package body & is not visible to other design units. Example: package body data_types is constant vector_table_loc : address := X"FFFF00"; function data_to_int(value : data) return integer is body of data_to_int end data_to_int; function int_to_data(value : integer) return data is body of int_to_data end int_to_data; end data_types; In this package body, the value for the constant is specified, and the function bodies are given. The subtype declarations are not repeated, as those in the package declarations are visible in the package body. Once a package has been declared, items declared within it can be used by prefixing their names with the package name, the items declared might be used as follows: Variable PC : data_types.address; int_vector_loc := data_types.vector_table_loc + 4*int_level; offset := data_types.data_to_int(offset_reg);

3.2.3 Design File: A design is described in VHDL using the concept of a design entity. A design entity is split into two parts, each of which is called a design unit in VHDL. i.e. entity declaration and architecture body. A design file is a source file containing one or design units. A design file is the input to a VHDL compiler. Design units in a design file are compiled in the same order as their textual order in the file. VHDL have five constructs called design unit namely entity declaration, architecture, configuration, package declaration & package body.

VHDL compilers accept one design file at a time and store the output in a design library. VHDL design files are saved with extension .vhd. These design files can be combined with other design files into a hierarchical design, called project. 3.2.4 Design Libraries: A library clause defines logical names for design libraries in the host environment, which are used by the design units. A library is a storage facility for previously analyzed design units. In practice, this relates mostly to packages. All analysed objects as there are packages, package bodies, entities, architectures and configurations can be found in a library. In VHDL, the library is a logical name with which compiled objects can be grouped and referenced. WORK is the default library. When a package is to be used in a design, it has to be made visible to the design. In order to specify it, the library clause (making the library visible) and USE clause (making particular declarations visible) must be used. There are two predefined libraries, which are used implicitly in every design: STD and WORK. The former contains standard packages STANDARD and TEXTIO. The latter is a working library, where all user created and analysed design units are stored. User specified packages are stored in the working library WORK. The logical name can be mapped to another logical library name as: USERPACK USERLIB PROJECT_1 PROJECT_XY

But it has to be mapped to a physical path on a strong device eventually. Usually, every designer operates within his own work library. Yet he can use units from other libraries which might hold data from former projects (PROJECT_1 and PROJECT_XY) or the current project packages (USERPACK). If another library than WORK is to be used, it will have to be made visible to the VHDL compiler. This is done with the library statement that starts with the keyword library, followed by the logical name of the library. For example the library IEEE is commonly used because it contains standardized packages. Example: Library IEEE; Use IEEE.std_logic_1164.all; 3.2.5 Order of Analysis: Analysis is the process of checking the VHDL unit for syntactic and semantic correctness and inserting the VHDL unit into the library. There are two classes of library units: Primary Units Secondary Units Primary units are Entity Declaration, Package Declaration and configuration Declaration. Secondary units are Package Body and Architecture Body. Secondary units must be analyzed into the same library as their corresponding primary units. There are three rules to define the order in which library units may be analyzed. Firstly, a secondary unit is analyzed only after its corresponding primary unit has been analyzed. Second rule is that any library unit, that references a primary unit, can be analyzed only after that primary unit has been analyzed. Thirdly, an architecture body referenced in a configuration declaration must be analyzed before the configuration declaration. One configuration declaration referenced in a second configuration declaration must be analyzed before the second configuration declaration. 3.2.6 Implicit Visibility: Visibility in VHDL refers to those declared constructs that are available to the current design unit. Generally, any information visible to an entity is visible to all the architectures within that entity. Similarly, any information visible to a package is visible to its package body. These are two examples of implicit visibility. In these examples, architecture body is tied to the entity by virtue of the statement i.e. Architecture < architecture_name> of < entity_name> is Similarly, package body inherits all the items declared in package declaration by virtue of its first statement i.e. Package body <package_name> is 3.2.7 Explicit Visibility: Explicit visibility of items declared in other design units can be achieved by using following two clauses: o Library Clause o Use Clause These clauses are discussed in detail in sect ion 3.2.8.

3.2.8 Library Clause & Use Clause: A library clause defines logical names for design libraries in the host environment. Syntax:
library

library_name;

The library clause defines the logical names of design libraries, which are used by the design units. A library is a storage facility for previously analysed design units. In practice, this relates mostly to packages. When a package is to be used in a design, it has to be made visible to the design. In order to specify it, the library clause (making the library visible) and use clause (making particular declarations visible) must be used. See use clause for more details. There are two predefined libraries, which are used implicitly in every design: STD and WORK. The first of them contains standard packages STANDARD and TEXTIO. The other is a working library, where all user-created and analysed design units are stored. User-specified packages are stored in the working library WORK. Use clause makes the name directly visible in the enclosing declarative part and in the corresponding statement part. Generally, Use clause is given in the beginning of the design unit. Examples: Library IEEE; use IEEE.Std_Logic_1164.all; The library and Use clause are defined before entity declaration. A library specified in a library clause of the primary design unit (entity, configuration or package) is visible in each secondary unit (architecture or package body) associated to it. Library STD (containing packages STANDARD and TEXTIO) need not to be specified. Both packages are automatically included in every design unit. Package Std_Logic_1164 is specified in the library IEEE. The library IEEE clause must be used in design units, which will use this package. Library clause may contain more than one library names separated by commas. Another form of design unit used in VHDL, in which we gather a collection of library and use clauses, is known as context declaration. Syntax for Context Declaration:
Context <identifier> is { library clause l use clause l context reference }

end [context] [<identifier>];

Syntax for Context Reference: Context selected_name {}; 3.3 Advanced Features: In this section we will be discussing the more general features of VHDL. These features cannot be characterized as belonging to any specific modeling style. 3.3.1 Entity Statements: As we have discussed earlier, there can be multiple architecture bodies linked to a single entity. Some statements that are common to all the architecture bodies can be inserted into the entity declaration. Common declarations appear in the entity declarative part whereas other common statements appear in the entity statement part. The syntax of entity statement is given below: entity <entity_name> is [generic (); ] [ port (); ] [ entity-item-declaration ] [ begin entity statements ] end [ entity ] [ entity-name ]; The statements before the keyword Begin is the entity declarative part and the statements appearing after the keyword Begin is the entity statement part. Entity statements are passive statements i.e. they do not assign values to any signals. Entity statements are intended to represent checks or assertions. The statements allowed as entity statements are: Concurrent Assertion Statement Concurrent Procedure Call Statement Process Statement All the above three statements must be passive. The following example illustrates use of entity declaration and statements: Entity ff is Port (A, B : in bit; C : out bit); Constant ff_delay : time := 12 ns; Begin Assert not ( A = 1 and B = 1) Report Invalid Inputs Severity Warning; End ff; The items declared within the entity declarations are visible to all architecture bodies associated with the entity.

3.3.2 Generate Statements: Generate statement is a structured concurrent statement which is used to model arrays of components. Generate statement is a kind of loop statement which is executed once at the beginning of the simulation i.e. the elaboration phase. Generate statement consists of a generation scheme and a set of enclosed concurrent statements. They create large, regular blocks of code easily. Any VHDL concurrent statement like process statement, block statement may be enclosed by a generate statement. Syntax of generate statement is: label-identifier : <generation scheme> generate concurrent statements end generate identifier; There are two kinds of generation schemes: If- scheme For- scheme According to the generation adopted, generate statement specifies the set of concurrent statements it contains. 3.3.2.1 For-scheme: For-scheme declares a generate parameter and a discrete range defining the values that the generate parameter will take on. The syntax of a forgeneration scheme is: generate-label: for < identifier> in <discrete range> generate [ block-declarations begin] concurrent statements end generate <generate-label]; The values in the discrete range must be globally static i.e. they must be computable at the time of elaboration. During elaboration, the set of concurrent statements are replicated once for each value in the discrete range. If there are any declarations then the declared items are visible only within the generate statement. Example: Entity A1 is Port ( I : in bit_vector ( 0 to 2); O: out bit_vector (0 to 2)); End A1; Architecture A2 of A1 is Component f1 Port ( I1 : in bit; O1 : out bit); End component; G : for N in 0 to 2 generate V1 : f1 portmap ( I(N), O(N)); End generate; End A2;

The generate parameter N acts as a constant i.e. its value may be read, but it cannot be assigned to and cannot be passed to a parameter of mode out or inout. Its value is not defined outside the generate statement. During elaboration, the generate statement is expanded to three blocks. In each block N will be evaluated for all possible values i.e. 0 to 2. The elaboration is shown below: G: block constant N: integer:= 2; begin V1 : f1 portmap (I(N), O(N)); End block G; Similarly, two other blocks with N=1 and N =2 will be executed. 3.3.2.2 If-scheme: This scheme allows for conditional creation of components based o the value of an expression. The expression must be globally static i.e. the value must be computable at elaboration time. We cannot use Else or Elsif clauses with the If-scheme. The syntax for If-scheme is: name : If (Boolean Expression) Generate Concuerrnt Statements End Generate name; Example: Entity A11 is Port (I11 : in bit; O11 : out bit_vector(0 to 2)); End A11; Architecture A12 of A11 is Component f11 Port (I11 : in bit; O11 : out bit); Begin G1 : for N1 in 0 to 2 generate G2 : if N1 =0 generate V11 : f11 portmap (I11, O11(N1)); End generate G2; G3 : if N1> 0 generate V12 : f12 portmap (O11(N1-1), I11, O11(N1)); End generate G3; End A12; 3.3.3 Attributes: Attributes may be used to communicate information about many different items in VHDL. It is a feature of VHDL that allow to extract additional information about an object (such as a signal, variable or type) that may not be directly

related to the value that the object carries. It can return various types of information. It can be associated with signals and arrays. There are two classes of attributes: predefined and user defined. The syntax of an attribute is some named entity followed by an apostrophe and one of the following attribute names i.e. Nameattribute_identifier
3.3.3.1 Predefined Attributes: The VHDL specification describes five fundamental kinds of attributes. These five kinds of attributes are categorized by the results that are returned when they are used. Value Kind Attributes Function Kind Attributes Signal Kind Attributes Range Kind Attributes Type kind Attributes 3.3.3.1.1 Value kind Attributes: Value kind attributes are used to return a particular value. It can be further subdivided into following three categories: Value Type Attributes Value Array Attributes Value Block Attributes a) Value Type Attributes: The value kind attributes that return an explicit value and are applied to a type or subtype. The predefined value type attributes are: Left: This attribute returns the left-most element index (the bound) of a given type or subtype. Right: This attribute returns the right-most element (the bound)of a given type or subtype. High: This attributes returns the upper bound of a given type or subtype. For this attribute the type must be scalar i.e. the values must be in a sequence Low: This attribute is similar to High attribute as it works on scalar type. It returns the upper bound of a given type or subtype. To make a clear understanding of the above attributes, we consider the following example 1:

Example 1:

Process(X) Type MV is (1 to 5); Variable L, R, H, N, M: integer; Variable L1, R1:boolean; begin L:= MVleft; R:= MVright; -- L has a value of 1 -- R has a value of 5

H:= MVhigh; N := MVlow; End process;

-- H has a value of 5 -- N has a value of 1

The right, left, high and low attributes can be used to return non-numeric values e.g. architecture <name> of <entity> is type MV is (tea, coffee, water, milk, juice); signal L, R,L1,R1: MV; begin L <= MVleft; R <= MVright; L1 <= MVhigh; R1 <= MVLow; end <name>; b) Value Array Attributes: For value array attribute, there is only one predefined attribute : length. For a given array type, this attribute returns the total length of the array range specified. The array type must belong to scalar type. It also accepts multidimensional array of scalar type. To make a clear understanding, we consider the following example 2: -- L has the value of tea -- R has the value of juice -- L1 has the value of juice --R1 has the value of tea

Example 2:

Process(X) Type MV is array (1 to 15) of bit; Variable L: integer; begin L:= MVlength; -- L has a value of 16

End process; In the above example, MV is defined as a array with 16 elements ( 1 to 15). Attribute length is used with array MV and returns the length or total number of elements of the array which is 16 in this example. So, the variable L holds the value 16. This attribute can also be used with enumerated type ranges, as illustrated in the following example:

Package X1 is Type MV1 is (A1, B1, C1, D1); Type MV is array (MV1Low to MV1High) of MV1; Type MV2 is array (MV1Low to MV1High, MVLow to MVHigh ) of MV1; -- Multidimensional Array End X1; Process(X) Variable L, L1, L2: integer; begin L:= MVlength; L1: MV2length(1) L2:= MV2length(2) End process; In this example, MV1 is enumerated type array with 4 elements. MV is another array of type MV. It contains values from MV1low i.e. A1 to MV1High i.e. D1. Type MV2 is a multidimensional array. Variable L returns the length of array MV i.e. 4 in this example. c) Value Block Attributes: Value Block Attributes are the attributes that can be used to determine information about blocks or architectures in a design. There are two value block attributes: Structure and Behavior These attributes,structure and behavior, return true or false values depending on whether the block or architecture being referenced includes references to lower-level components. The structure attribute returns true if there are references to lower-level components i.e. if the block or the architecture specified contains component instantiation statements and false if vice versa. The behavior attribute returns true if there are no references to lower-level components i.e. if the block or the architecture specified contains no component instantiation statements and false if vice versa.. To make a clear understanding, we consider the following example 3: -- L has a value of 4 -- L1 has a value of 4 -- L2 has a value of 4

Example 3:
library ieee ; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; entity reg is port ( I : clock: clear : Q : in bit_vector(0 to 3); in bit; in bit; out bit_vector(0 to 3)

); end reg; architecture str of reg is component dff port( a,b,c: in bit; o: out bit); end component; begin FF0: d_ff port map (I(0), clock, clear, Q(0)); FF1: d_ff port map (I(1), clock, clear, Q(1)); FF2: d_ff port map (I(2), clock, clear, Q(2));; FF3: d_ff port map (I(3), clock, clear, Q(3)); process(clock) begin Assert (clock'event) Report Clock triggers; Severity Warning; end process; end str; The above VHDL code is for a 4- bit register. It uses four D flip flops. The result of the attributes of the above program for the architecture str are shown below: The process(clock) is a passive statement which has no effect on the fact that the architecture is structural. So the attribute structure returns true. Strstructure: true If the process contains signal assignment statements then the process would no more be considered as passive and the attribute structure returns false. If the block or architecture does not contain any component instantiation statements then the attribute behavior returns true. So, for the above program the attribute behavior returns false. Strbehavior: false 3.3.3.1.2 Function Kind Attributes: Function kind attributes are used to return information about types, arrays and signal. It can be subdivided into following three categories: Function Type Attributes Function Array Attributes Function Signal Attributes a) Function Type attributes: Function type attribute return information about a given

type. VHDL defines the following function type attributes:

Pos(value) It returns the position number of a type value

Example: type MV is (tea, coffee, water, milk, juice); variable L: integer := MVpos(water); -- L has the value of 2 In the above example, variable L is a integer which holds the value of the type MV associated with the function type attribute pos. Attribute pos returns the position of the element water in the type MV i.e. 2.

Val(value) It returns the value corresponding to a position number of a type value.

Example: type MV is (tea, coffee, water, milk, juice); variable L: MV := MVval(2); -- L has the value of Water In this example, MVval returns the value corresponding to location 2 in type MV i.e. water. So, L holds the value of water

Succ(value)It returns the value corresponding to position number after a given type value.

Example: type MV is (tea, coffee, water, milk, juice); variable L: MV:= MVsucc(Water); -- L has the value of Milk In this example, MVsucc(water) returns the next succeeding element to water in the type MV i.e. Milk. So, L holds the value of Milk

Pred(value)It returns the value corresponding to position number preceding a given type value -- L has the value of Water

Example: type MV is (tea, coffee, water, milk, juice); variable L: MV := MVpred(Milk);

In this example, MVpred(milk) returns the element preceeding to Milk in the type MV i.e. water. So, L holds the value of water.

Leftof(value)It returns the value corresponding to position number to the left of a given type value -- L has the value of Water

Example: type MV is (tea, coffee, water, milk, juice); variable L: MV := MVleftof(Milk);

In this example, MVleftof(milk) returns the element next on the left hand side of Milk in the type MV i.e. water. So, L holds the value of water.

Rightof(value)It returns the value corresponding to position number to the right of a given type value. Example: type MV is (tea, coffee, water, milk, juice); variable L: MV := MVrightof(Water); -- L has the value of Milk In this example, MVrightof(water) returns the element next on the right hand side of Water in the type MV i.e. milk. So, L holds the value of milk. From the above descriptions, it might appear that the val and succ attributes are equivalent to the attributes leftof and rightof. One case where they would be different is the case where a subtype is defined that changes the ordering of the base type. This is illustrated in the example given below: type MV is (tea, coffee, water, milk, juice); subtype reverse_MV is MV range Juice downto tea; variable V1: reverse_MV := reverse_MVleftof(Water); variable V2: reverse_MV := reverse_MVpred(Water -- V1 has the value of Milk -- V2 has the value of Coffee

In the above example, reverse_MV contains elements in reverse order as in type MV i.e. from juice to tea. The element returned by attribute Leftof(water) will be milk as in the reverse format milk will be on the left hand side of water. So, V1 will hold value as Milk. But in reverse order also, the position remains the same as for example (0 to 7) and (7 downto 0). So, the element preceding water will be the same as in the type MV i.e. coffee, and as a result V2 will hold the value as coffee.
b) Function Array Attributes: The function array attribute are used to return the bounds

of array types. This attribute is used to access an array location. The function array attributes that can be applied to array objects include: Left(value) This attribute returns the index value corresponding to the left bound of a given array range. Example: type MV is array (15 downto 0) of bit; variable L: integer := MVleft(MVrange); -- L has the value of 15

In this example, type MV is an array having left bound as 15 and right bound as 0. variable L is a integer which holds the left bound of the entire range of the type MV which is 15. Here, we have also used a attribute MVrange which is a range kind attribute and will be discussed later in this section. Right(value)This attribute returns the index value corresponding to the right bound of a given array range. Example: type MV is array (15 downto 0) of bit;

variable L: integer := MVright(MVrange);

-- L has the value of 0

In this example, variable L holds the right bound of the entire range of the type MV which is 0 . High(value)This attribute returns the index value corresponding to the upper-most bound of a given array range. Example: type MV is array (15 downto 0) of bit; variable L: integer := MVhigh(MVrange); -- L has the value of 15

In this example, variable L holds the upper-most bound of the entire range of the type MV which is 15 Low(value)returns the index value corresponding to the lower bound of a given array range. Example: type MV is array (15 downto 0) of bit; variable L: integer := Mvlow(MVrange); -- L has the value of 0

c) Function Signal Attributes: Function signal attributes are used to return information

about the behavior of the signals (such as whether that signal has changed its value, what the previous value of the signal was or how much time have passed after the last transaction) include: EventThis attribute returns a true value if any event has occurred on the signal in the current simulation delta cycle and if not it returns false. It is useful for determining clock edges.. This can be explained with the example given below:

LIBRARY ieee; USE ieee.std_logic_1164.all; Entity dff is Port ( Clk, d : in bit; q: out bit); End dff; ARCHITECTURE dff1 OF dff IS Begin Process (Clk) Begin if (Clkevent and Clk = 1) then q <= d; End if; End Process; End dff1; This program implements a D flip flop. In a D-flip flop input is transferred to the output at the rising edge of the clock pulse. This is achieved in the above program using attribute

event. The condition if (Clkevent and Clk = 1) then says that when there is an event on the clock signal and its value is i.e. the rising edge of the clock signal. ActiveThis attribute returns true if any transaction (scheduled event) occurred on this signal in the current simulation delta cycle. The attribute event returns true if there is a event (change in the value) on the associated signal whereas the attribute active returns true whenever a transaction occurs on the signal i.e. the signal is operating or active. variable L1,L2: boolean; begin Q <= D after 5 ns; L1 := Qactive; -- A gets a value of True L2 := Qevent; -- E gets a value of False ... In this example, Q and D are presumed as the input and the output and L1 and L2 as the intermediate signals. There is a transaction on signal Q as it holds the value of input D after 5ns ( i.e. if D is assigned a value at Tns then Q is assigned with this value at (T+5)ns.). So, this is a scheduled event or a transaction and hence L1 holds true and and there is no event or change in the value of the signal so L2 holds false. Last_eventThis attribute returns the time elapsed since the previous event occurring on the associated signal. This is useful in timing checks. The attribute event refers that there is an event or change in the signal value whereas last_event returns the time of the last event occurance. This attribute can be explained with the example given below: variable T: time; begin Q <= D after 5ns; T := Qlast_event; ... ; In this example, Q and D are presumed as input and output. It is also assumed that input D is assigned the value at the very initial state and hence Q stores the value of D at 5ns. This shows that last event or transaction held on Q is at 5ns. As a result, T holds the value as 5ns. This is very clear from the above discussion that when attribute event returns true then attribute last_event returns false and vice versa. Last_value This attribute returns the value of the signal prior to the last event occurring on the associated signal. We can also quote that it is the value of the associated signal before the most recent event occurred on the signal. This is useful in timing checks. The attribute event refers that there is a event or change

-- T gets a value of 5ns

in the signal value whereas last_event returns the time of the last event occurrence. This attribute can be explained with the example given below: variable V: bit; Q <= 1; wait 10 ns; Q <= 0; wait 10 ns; V := Qlast_value; . . .;

-- V gets a value of 1

In this example, Q is presumed as the output. The last event occurred on Q is when its value goes to 0 from 1 after 10ns. As attribute last_event returns the value of the associated signal prior to the last event occurred on it. So, Qlast_value returns the value as 1 which is stored in variable V. Last_activeThis attribute returns the time elapsed since the last transaction (scheduled event) of the signal. It returns the amount of time since the last transaction occurred on the signal. The attribute active returns true if any transaction (scheduled event) occurred on this signal in the current simulation delta cycle. It shows that the associated signal is currently active or the transaction is occurring on the signal currently, whereas attribute last_active returns the time when the signal was last active i.e. the amount of time after the last transaction occurred on the associated signal. variable T: time; begin Q <= D after 30 ns; wait 10 ns; T := Qlast_active; -- T gets a value of 10 ns . . .; In the above example, Q is presumed as the output and D as the input. After the value of D is assigned to Q, the operation waits for 10ns. As attribute last_active returns the amount of time since the last transaction occurred on the signal, so Qlast_active returns the value as 10ns and this value is stored in variable T. This is very clear from the above discussion that when attribute active returns true then attribute last_active returns false and vice versa. 3.3.3.1.3 Signal Kind Attributes: The signal kind attributes are attributes that, when invoked, create special signals that have values and types based on other signals. These special signals returns the information about the functionality of the associated signal. These special signals can be used anywhere in the design description that a normally declared signal could be used. These attributes cant be used inside a subprogram. There are four types of signal kind attributes:

Delayed(time) This attribute creates a delayed signal i.e the associated signal is delayed by the time defined and the resulting signal is a copy of the original signal and is identical to it in waveform. In this the time parameter is optional, and may be omitted. If no time is given then the associated signal is delayed by delta time. Example1: Entity exm is Port (a,b: in bit; c: out bit); End exm; Architecture exm11 of exm is Signal d: bit; begin process(a,b) begin d <= adelayed(5ns); c <= b and d; end process; end exm11;

The output waveform of this example is shown in fig 1.11. Fig 1.11: Output waveform for example1 The same result can be obtained by using transport delay in the signal assignment statement. But transport delay signal assignment is somewhat tedious as we have to declare a new signal. In the above example, however, we have declared a new signal d but only for the sake of clarity. A more concise code will be: process(a,b) begin c <= b and adelayed(5ns);; end process; But using transport delay assignment statement, we cannot omit the declaration of new signal d. The same code using transport delay assignment statement is shown below: Entity exm is Port (a,b: in bit; c: out bit); End exm; Architecture exm11 of exm is Signal d: bit; begin process(a,b) begin d <= transport a after 5ns;

c <= b and d; end process; end exm11; Stable (time)This attribute creates a signal of type boolean that becomes true when the signal has no event for some given period of time. This attribute determines the relative activity level of signal. If no time parameter or 0ns is given then this attribute returns true if there is no change in the signal value in the current simulation time and false as soon as there is a change.

Example 2: Entity exm is Port (A: in bit; C: out bit); End exm; Architecture exm11 of exm is begin process(A) begin C <= Astable (10 ns); end process; end exm11; The output waveform for example 2 is shown in fig 1.12 Fig 1.12: Output waveform for example 2. In the waveform shown in fig 1.12, the output C is High when the attribute Astable returns true. The value of input A remains unchanged for the first 20ns. So, output C is high. Now the value of A changes i.e. it is not stable so C goes low for 10ns. Again the value of A changes at 40 ns and output c goes low for next 10ns. Now, the value of A again changes 55ns and the output C again goes low. But at this time, input signal A is not stable long enough to allow output C to go to true value and while C is low and the input again changes and C remains low. After 10ns of the last change in input A, it is stable enough to allow the output to again go high (or go true). Quiet (time)This attribute creates a signal of type Boolean that becomes true when the signal has no transactions (scheduled events) or actual events for some given period of time. It is similar to attribute stable but the main difference is that attribute quite is triggered by both an event or an transaction on the associated signal. Example: process variable L: Boolean; begin C <= A after 30 ns; wait 10 ns;

L := Cquiet(20 ns); wait 30 ns; L := Cquiet(20 ns); ... end process;

-- Lgets a value of false (10 ns -- since transaction) -- L finally gets a value of true -- (20 ns since event)

In this example, C and A are presumed as output and input respectively. In the first statement C holds the value of A after 30ns of its assignment. Then the process waits for 10ns. The next statement L := Cquiet(20 ns); returns false as after C was assigned its value it went into wait statement for 10ns. So, the signal was not quite for 20ns but for 10ns. Again after being in wait state for 30ns the statement is repeated. This time the statement holds true as the signal is quite for 30ns. TransactionThis attribute creates a signal of type bit that toggles its value between 0 and 1 whenever a transaction or actual event occurs on the signal the attribute is applied to. This attribute is used to provoke a process when a transaction occurs. The initial value of this attribute is not defined. Example: process variable L: Boolean; begin C <= A after 30 ns; wait on Ctransaction; ... end process; The main difference between an event and an transaction is that in a event there is a active transaction on the signal itself whereas in a transaction the event can be on its internal elements e.g. in a statement C <= A and B; event is when the value of C changes. But if C remains unchanged and its inputs changes we say there is a transaction on C in the above example, the process waits till there is a transaction on C. 3.3.3.1.4 Range Kind Attributes: The range kind attributes return a special value that is a range and is generally used in constrained array types which return index range specified. There are two types of range kind attributes: Range (n)This attribute returns the range value for a constrained array. The parameter n specify the range. It returns the range in the specified order. This parameter n is optional and we omit it also. Example: type MV is array (15 downto 0) of bit;

variable L: integer := MVleft(MVrange);

-- L has the value of 15

We have discussed this program earlier in context to function array attributes. Now, we consider the same example to make a clear concept of range kind attribute. Here, L holds the value low value of type within the range which is specified by using attribute MVrange. Reverse_rangeThis attribute returns the reverse of the range value for a constrained array. This can be understood from the following example: -- L has the value of 0

Example: type MV is array (15 downto 0) of bit; variable L: integer := MVleft(MVreverse_range);

In this example, MV reverse_range reverses the order of MV and as a result L holds the value as 0 instead of 15. 3.3.3.1.5 Type kind attribute: This attribute returns values of kind type. In VHDL only one type kind attribute available i.e base. This attribute returns the base type of a type or a subtype. This can be explained with the help of following example: Example: type MV is (tea, coffee, water, milk, juice); subtype MV1 is MV range tea to milk; variable V1: MV1 := MV1right; -- V1 has the value of Milk variable V2: MV := MV1baseright; -- V2 has the value of juice In the above example, we have a base type MV and its subtype MV1 which consists of elements ranging from tea to Milk. Variable V1 holds the value of MV1right which returns the right bound of the array range defined by MV1 i.e. Milk. Now, variable V2 holds the value of MV1baseright. Here, the attribute baseright returns the right bound of the array range defined by the base of the subtype MV1 i. e. juice 3.3.4 Aggregate Targets: Aggregates are a set of quantities separated by commas and enclosed in parenthesis. An aggregate target represents a combination of one or more names i.e. of an array type or a record type. Target assignment is shown in the example given below: Signal P, Q, R, S : bit; (P, Q, R, S) <= bit_vector (1010); The left hand side is an aggregate target consisting of four individual signals. This signal assignment statement indicates that signals P, Q, R, S are assigned the value 1, 0, 1, 0 respectively. An assignment to an aggregate target causes subelement values in the right hand side expression to be assigned to the corresponding subelements in the left hand side target.

3.3.5 Ports and their Behavior: Ports are signals through which the entity communicates with the other models in its external environment. Each interface port can have one of the following modes: In Out Inout Buffer Port behavior differs on how they behave internally and externally to the entity specifically what other kind of ports can be connected. Various port behaviors internal and external to an entity are: 1. Internal to Entity: In port: This port can only read a value. We can never assign a value to the signal defined in this mode. Out port: This port cannot read a value. It can only be assigned a value and can have multiple drivers. Inout port: This port is a combination of In and Out port. This port can read a value and can be assigned a value. It can have multiple drivers. Buffer port: It is similar ti inout port except the fact that it can have atmost one driver on the entire net connected to the buffer port either internal or external to the entity. 2. External to Entity: For Net Connection In, Out and Inout port: These ports can connected to any net with or without multiple drivers. Buffer port: This port can only be connected to a net that has at most one driver. The driver can be either internal or external to the entity. For Port Connection: In, Out, Inout and Buffer have to follow certain rules with respect to Connection to a signal in parent entity. Usage inside the entity and Direct connection to parents port The parent child entity is shown in fig. 3.1

1. 2. 3.

fig 3.1: Port Connection Rules In port: This port can be connected to a parent entitys port of mode in, inout or buffer. Out port: This port can be connected only to parent entitys port of mode out and inout. Inout port: This port can only be connected to inout port of parent entity. Buffer port: This port can only be connected to buffer port of parent entity.

In brief, an in port can be connected to an inout port but an out port cannot be connected to a buffer port of the parent entity.

Vous aimerez peut-être aussi