VHDL DSD

Download as doc, pdf, or txt
Download as doc, pdf, or txt
You are on page 1of 280

Basic Language Elements The basic elements of the language includes data objects that store values of a given

type, literals that represent constant values, and operators that operate on data objects. Every data object belongs to a specific type. It is important to understand the notion of data types and objects since VHDL is a strongly-typed language. This means that operations and assignments are legal in the language only if the types of the operands and the result match according to a set of rules; it does not allow objects and literals of different types to be mixed freely in expressions. Examples of illegal operations are adding a real value to an integer value, and assigning a boolean value to an object of type BIT. It is, therefore, important to understand what types are and how they can be correctly used in the language. Identifiers An identifier in VHDL is composed of a sequence of one or more characters. A legal character is an upper-case letter (A... Z), or a lower-case letter (a. .. z), or a digit (0 . . . 9) or the underscore ( _ ) character. The first character in an identifier must be a letter and the last character may not be an underscore. Lower-case and upper-case letters are considered to be identical when used in an identifier; as an example. Count, COUNT, and CouNT, all refer to the same identifier. Also,-two underscore characters cannot appear consecutively. Some more examples of identifiers are DRIVE_BUS SelectSignal RAM_Address SET_CK_HIGH CONST32_59 r2d2 Comments in a description must be preceded by two consecutive hyphens (-); the comment extends to the end of the line. Comments can appear anywhere within a description. Examples are 1 This is a comment; it ends at the end of this line. 2 To continue a comment onto a second line, a separate 3 comment line must be started. entity UART is end; --This comment starts after the entity declaration. The language defines a set of reserved words; these are listed in Appendix A.I. These words, also called keywofds, have a specific meaning in the language, and therefore, cannot be used as identifiers. Data Objects A data object holds a value of a specified type. It is created by means of an object declaration. An example is variable COUNT: INTEGER; This results in the creation of a data object called COUNT which can hold integer values. The object COUNT is also declared to be of variable class. Every data object belongs to one of the following three classes: 1. Constant: An object of constant cla^s can hold a single value of a given type. This value is assigned to the object before simulation starts and the value cannot be changed during the course of the simulation.

2. Variable: An object of variable class can also hold a single value of a given type. However in this case, different values can be assigned to the object at different times using a variable assignment statement. 3. Signal: An object belonging to the signal class has a past history of values, a current value, and a set of future values. Future values can be assigned to a signal object using a signal assignment statement. Signal objects can be regarded as wires in a circuit while variable and constant objects are analogous to their counterparts in a high-level programming language like C or Pascal. Signal objects are typically used to model wires and flip-flops while variable and constant objects are typically used to model the behavior of the circuit. An object declaration is used to declare an object, its type, and its class, and optionally assign it a value. Some examples of object declarations of various types and classes follow. Constant Declarations Examples of constant declarations are constant RISE_TIME: TIME := 10ns; constant BUS_WIDTH: INTEGER := 8: The first declaration declares the object RISE_TIME that can hold a value of type TIME (a predefined type in the language) and the value assigned to the object at the start of simulation is 10 ns. The second constant declaration declares a constant BUS_WIDTH of type INTEGER with a value of 8. An example of another form of constant declaration is constant NO_OF_INPUTS: INTEGER; The value of the constant has not been specified in this case. Such a constant is called a deferred constant and it can appear only inside a package declaration. The complete constant declaration with the associated value must appear in the corresponding package body. Variable Declarations Examples of variable declarations are variable CTRL_STATUS: BIT_VECTOR(10 downto 0); variable SUM: INTEGER range Oto 100 := 10; variable FOUND, DONE: BOOLEAN; The first declaration specifies a variable object CTRL_STATUS as an array of II elements, with each array element of type BIT. In the second declaration, an explicit initial value has been assigned to the variable SUM. When simulation starts, SUM will have an initial value of 10. If no initial value is specified for a variable object, a default value is used as an initial value. This default value is T'LEFT, where T is the object type and LEFT is a predefined attribute of a type that gives the leftmost value in the set of values belonging to type T. In the third declaration, the initial values assigned to FOUND and DONE at start of simulation is FALSE (FALSE is the leftmost value of the predefined type, BOOLEAN). The initial value for all the array elements of CTRL_STATUS is '0'. Signal Declarations Here are some examples of signal declarations. signal CLOCK: BIT; signal DATA_BUS: BIT_VECTOR(0 to 7);

signal GATE_DELAY: TIME := 10 ns; The interpretation for these signal declarations is very similar to that of the variable declarations. The first signal declaration declares the signal object CLOCK of type BIT and gives it an initial value of '0' ('0' being the leftmost value of type BIT). The third signal declaration declares a signal object GATE_DELAY of type TIME that has an initial value of 10 ns. Other Ways to Declare Objects Not all objects in a VHDL description are created using object declarations. These other objects are declared as 1. ports of an entity. All ports are signal objects. 2. generics of an entity. These are constant objects. 3. formal parameters of functions and procedures. Function parameters are constant objects or signal objects while procedure parameters can belong to any object class, 4. a file declared by a file declaration (see file types in next section). There are two other types of objects that are implicitly declared. These are the indices of a for. . . loop statement and the generate statement. An example of such an implicit declaration for the loop index in a for. . . loop statement is shown. for COUNT in 1 to 10 loop SUM := SUM + COUNT; end loop; In this for . . . loop statement, object COUNT has an implicit declaration of type INTEGER with range I to 10, and therefore, need not be explicitly declared. The object COUNT is created when the loop is first entered and ceases to exist after the loop is exited. In the rest of this text, we shall follow the standard practice of referring to signal objects as signals, variable objects as variables, and constant objects as constants. Data Types Every data object in VHDL can hold a value that belongs to a set of values. This set of values is specified by using a type declaration. A type is a name that has associated with it a set of values and a set of operations. Certain types, and operations that can be performed on objects of these types, are predefined in the language. For example, INTEGER is a predefined type with the set of values being integers in a specific range provided by the VHDL system. The minimum range that must be provided is -(2^31 - 1) through +(2^31 - 1). Some of the allowable and frequently used predefined operators are +, for addition, -, for subtraction, /, for division, and *, for multiplication. BOOLEAN is another predefined type that has the values FALSE and TRUE, and some of its predefined operators are and, or, nor, nand, and not. The declarations for the predefined types of the language are contained in package STANDARD (see Appendix A); the operators for these types are predefined in the language. The language also provides the facility to define new types by using type declarations and also to define a set of operations on these types by writing functions that return values of this new type. All the possible types that can exist in the language can be categorized into the following four major categories: 1. Scalar types: Values belonging to these types appear in a sequential order.

2. Composite types: These are composed of elements of a single type (an array type) or elements of different types (a record type). 3. Access types: These provide access to objects of a given type (via pointers). 4. File types: These provides access to objects that contain a sequence of values of a given type. It is possible to derive restricted types, called subtypes, from other predefined or userdefined types. Subtypes A subtype is a type with a constraint. The constraint specifies the subset of values for the type. The type is called the base type of the subtype. An object is said to belong to a subtype if it is of the base type and if it satisfies the constraint. Subtype declarations are used to declare subtypes. An object can be declared to either belong to a type or to a subtype. The set of operations belonging to a subtype is the same as that associated with its base type. Subtypes are useful for range checking and for imposing additional constraints on types. Examples of subtypes are subtype MY_INTEGER is INTEGER range 48 to 156 ; type DIGIT is ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9') ; subtype MIDDLE is DIGIT range '3' to '7' ; In the first example, MYJNTEGER is a subtype of the INTEGER base type and has a range constraint with values ranging from 48 through 156. DIGIT is a user-defined enumeration type. The last subtype declaration declares a new subtype called MIDDLE whose base type is DIGIT and has the values '3', '4', '5', '6' and '7'. A subtype need not impose a constraint. In such a case, the subtype simply gives another name to an already existing type. For example: subtype NUMBER is DIGIT; NUMBER is a subtype with no constraint. Therefore, its set of values are the same as that for type DIGIT. Scalar Types The values belonging to this type are ordered, that is, relational operators can be used on these values. For example, BIT is a scalar type and the expression '0' < 1' is valid and has Hie value TRUE. There are four different kinds of scalar types. These types are 1. enumeration, 2. integer, 3. physical, 4. floating point. Integer types, floating point types, and physical types are classified as numeric types since the values associated with these types are numeric. Further, enumeration and integer types are called discrete types since these types have discrete values associated with them. Every value belonging to an enumeration type, integer type, or a physical type has a position number associated with it. This number is the position of the value in the ordered list of values belonging to that type.

Enumeration Types An enumeration type declaration defines a type that has a set of user-defined values consisting of identifiers and character literals. Examples are Type MVL is ('U','0','1','Z); type MICRO_OP is (LOAD, STORE, ADD, SUB, MUL, DIV); subtype ARITH_OP is MICRO_OP range ADD to DIV; Examples of objects defined for these types are signal CONTROL_A: MVL; signal CLOCK: MVL range '0' to '1'; -- Implicit subtype declaration. variable IC: MICRO_OP := STORE; -- STORE is the initial value for IC. variable ALU: ARITH_OP; MVL is an enumeration type that has the set of ordered values, 'U', '0', '1', and 'Z'. ARITH_OP is a subtype of the base type MICRO_OP and has a range constraint specified to be from ADD to DIV, that is, the values ADD, SUB, MUL, and DIV belong to the subtype ARITH_OP. A range constraint can also be specified in an object declaration as shown in the signal declaration for CLOCK; here the value of signal CLOCK is restricted to '0' or 1'. The order of values appearing in an enumeration type declaration defines the lexical order for the values. That is, when using relational operators, a value is always less than a value that appears to its right in the order. For instance, in the MICRO_OP type declaration, STORE < DIV is true, and SUB > MUL is false. Values of an enumeration type also have a position number associated with them. The position number of the leftmost element is 0. The position number of any particular element is one more than the position number of the element to its left. The values of an enumeration type are called enumeration literals. For example, consider the following enumeration type declaration. type CAR_STATE is (STOP, SLOW, MEDIUM, FAST); The enumeration literals specified in this type declaration are STOP, SLOW, MEDIUM, and FAST; therefore, objects of type CAR_STATE may be assigned only these values. If the same literal is used in two different enumeration type declarations, the literal is said to be overloaded. In such a case, whenever such a literal is used, the type of the literal is determined from its surrounding context. The following example clarifies this. type MVL is ('U', '0', '1 ', 'Z); --line 1 type TWO_STATE is ('0', '1'); --line 2 ... variable CLOCK: TWO_STATE; -- line 3 variable LATCH.CTRL: MVL; -- line 4 ... CLOCK := '0': -- line 5 LATCH.CTRL := LATCH.CTRL xor '0'; -- line 6 ... Here '0' and '1' are two literals that are overloaded since they appear in both the types, MVL and TWO_STATE. The value '0' being assigned to CLOCK in line 5 refers to the literal in the TWO_STATE type since variable CLOCK is of that type. The value '0' in the expression for LATCH.CTRL refers to the literal in type MVL since the first argument of the xor operator is of type MVL.

The predefined enumeration types of the language are CHARACTER, BIT, BOOLEAN, and SEVERITY_LEVEL. Values belonging to the type CHARACTER constitute the 128 characters of the ASCII character set. These values are called character literals and are always written between two single quotes (' '). Examples are 'A', '_', '" (the single quote character itself), '3' (the character literal 3) The predefined type BIT has the literals '0' and 1', while type BOOLEAN has {he literals FALSE and TRUE. Type SEVERITY_LEVEL has the values NOTE, WARNING, ERROR, and FAILURE; this type is typically used in assertion statements. Integer Types An integer type defines a type whose set of values fall within a specified integer range. Examples of integer type declarations are type INDEX is range 0 to 15; type WORD_LENGTH is range 31 downto 0; subtype DATA_WORD is WORD_LENGTH range 15 downto 0; type MY_WORD is range 4 to 6; Some object declarations using these types are constant MUX_ADDRESS: INDEX := 5; signal DATA_BUS: DATA_WORD; INDEX is an integer type that includes the integer values from 0 through 15. DATA_WORD is a subtype of WORD_LENGTH that includes the integer values ranging from 15 through 0. The position of each value of an integer type is the value itself. For example, in the type declaration of WORD_LENGTH, value 31 is at position 31, value 14 is at position 14, and so on. In the declaration of MY_WORD, the position number of values 4,5, and 6, is 4,5, and 6, respectively. Contrast this with the position number of elements of an enumeration type; the position number in case of integer types does not refer to the index of the element in the range, but to its numeric value itself. The bounds of the range for an integer type must be constants or locally static expressions; a locally static expression is an expression that computes to a constant value at compile time (a globally static expression is an expression that computes to a constant value at elaboration time). Values belonging to an integer type are called integer literals. Examples of integer literals are 56349 6E2 0 98_71_28 Literal 6E2 refers to the decimal value 6 * (10^) = 600. The underscore ( _ ) character can be used freely in writing integer literals and has no impact on the value of the literal; 98_71_28 is same as 987128. INTEGER is the only predefined integer type of the language. The range of the INTEGER type is implementation dependent but must at least cover the range -(2^ 31 1) to +(2^31 - 1). Floating Point Types A floating point type has a set of values in a given range of real numbers. Examples of floating point type declarations are type TTL_VOLTAGE is range -5.5 to -1.4;

type REAL_DATA is range 0.0 to 31.9; An example of an object declaration is variable LENGTH: REAL_DATA range 0.0 to 15.9; ... variable LI, L2, L3: REAL_DATA range 0.0 to 15.9; LENGTH is a variable object of type REAL_DATA that has been constrained to take real values in the range 0.0 through 15.9 only. Notice that in this case, the range constraint was specified in the variable declaration itself. Alternately, it is possible to declare a subtype and then use this subtype in the variable declarations as shown. subtype RD16 is REAL_DATA range 0.0 to 15.9; ... variable LENGTH: RD16; ... variable Li, L2, L3: RD16; The range bounds specified in a floating point type declaration must be constants or locally static expressions. Floating -point literals are values of a floating point type. Examples of floating point literals are 16.26 0.0 0.002 3_1.4_2 Floating point literals differ from integer literals by the presence of the dot ( . ) character. Thus 0 is an integer literal while 0.0 is a floating point literal. Floating point literals can also be expressed in an exponential form. The exponent represents a power of ten and the exponent value must be an integer. Examples are 62.3 E-2 5.0 E+2 Integer and floating point literals can also be written in a base other than 10 (decimal). The base can be any value between 2 and 16. Such literals are called based literals. In this case, the exponent represents a power of the specified base. The syntax for a based literal is base # based-value # -- form 1 base # based-value # E exponent -- form 2 Examples are 2#101_101_000# represents (101101000)2 = (360) in decimal, 16#FA# represents (FA)16= (11111010)2 = (250) in decimal, 16#E#E1 represents (E)16* (16^1) = 14* 16= (224) in decimal, 2#110.01 # represents (110.01)2 = (6.25) in decimal. The base and the exponent values in a based literal must be in decimal notation. The only predefined floating point type is REAL. The range of REAL is again implementation dependent but it must at least cover the range -I.OE38 to +I.OE38 and it must allow for at least six decimal digits of precision. Physical Types A physical type contains values that represent measurement of some physical quantity, like time, length, voltage, and current. Values of this type are expressed as integer multiples of a base unit. An example of a physical type declaration is type CURRENT is range 0 to 1 E9 units

nA; -- (base unit) nano-ampere uA = 1000 nA; -- micro-ampere mA = 1000 A; --milli-ampere Amp = 1000 mA; -- ampere end units; subtype FILTER_CURRENT is CURRENT range 10 A to 5 mA; CURRENT is defined to be a physical type that contains values from 0 nA to 10^9 nA. The base unit is a nano-ampere while all others are derived units. The position number of a value is the number of base units represented by that value. For example, 2 A has a position of 2000 while 100 nA has a position of 100. Values of a physical type are called physical literals. Physical literals are written as an integer literal followed by the unit name. For example, "10 nA" is a physical literal (note that a space between 10 and nA is essential) while "Amp" is also a literal that implies I Amp. Other examples are 100ns 10V 50 sec Kohm (implies 1Kohm) The only predefined physical type is TIME and its range of base values, which again is implementation dependent, must at least be -(2^31 - 1) to +(2^31 - 1). The declaration of type TIME appears in package STANDARD (see Appendix A). Composite Types A composite type represents a collection of values. There are two composite types: an array type and a record type. An array type represents a collection of values all belonging to a single type; on the other hand, a record type represents a collection of values that may belong to same or different types. An object belonging to a composite type, therefore, represents a collection of subobjects, one for each element of the composite type. An element of a composite type could have a value belonging to either a scalar type, a composite type, or an access type. For example, a composite type may be defined to represent an array of an array of records. This provides the capability of defining arbitrarily complex composite types. Array Types An object of an array type consists of elements that have the same type. Examples of array type declarations are type ADDRESS_WORD is array (0 to 63) of BIT; type DATA_WORD is array (7 downto 0) of MVL; type ROM is array (0 to 125) of DATA_WORD; type DECODE_MATRIX is array (POSITIVE range 15 downto 1, NATURAL range 3 downto 0) of MVL; --POSITIVE and NATURAL are predefined subtypes; these are: subtype NATURAL is INTEGER range 0 to INTEGER'HIGH; subtype POSITIVE is INTEGER range 1 to INTEGER'HIGH; -- The HIGH attribute gives the highest value belonging to the type. Examples of object declarations using these types are variable ROM_ADDR: ROM; signal ADDRESS.BUS: ADDRESS_WORD;

constant DECODER: DECODE_MATRIX; - A deferred constant. variable DECODE_VALUE: DECODE_MATRIX; ADDRESS_BUS is a one-dimensional array object that consists of 64 elements of type BIT. ROM_ADDR is a one-dimensional array object that consists of 126 elements, each element being another array object consisting of 8 elements of type MVL. We have thus created an array of arrays. Elements of an array can be accessed by specifying the index values into the array. For example, ADDRESS_BUS(26) refers to the 27th element of ADDRESS_BUS array object; ROM_ADDR(10)(5) refers to the value (of type MVL) at index 5 of the ROM_ADDR(10) data object (of type DATA_WORD); DECODER(5,2) refers to the value of the element at the 2nd column and 5th row of the two-dimensional object. Notice the difference in addressing for a two-dimensional array type and an array of a type which is an array of another type. The language allows for an arbitrary number of dimensions to be associated with an array. It also allows an array object to be assigned to another array object of the same type using a single assignment statement. Assignment can be made to an entire array, or to an element of an array, or to a slice of an array. For example, ROM_ADDR(5) := "0100_0100"; - Assign to an element of an array. DECODE_VALUE := DECODER; - An entire array is assigned. ADDRESS_BUS (8 to 15) <= X"FF; -Assign to a slice of an array. These examples of array types are constrained array declarations since the number of elements in the type is explicitly specified. The language also allows array types to be unconstrained, in this case, the number of elements in the array is not specified in the type declaration. Instead, the object declaration for that type declares the number of elements of the array. A subtype declaration may also specify the index constraint for an unconstrained array type. A subprogram parameter may be an object specified to be of an unconstrained type; in this case, the constraint is specified by the actual parameter passed in during the subprogram call. Examples of unconstrained array declarations are -- The "o" symbol is called the "box" symbol in the language. type STACK_TYPE is array (INTEGER range <>) of ADDRESS_WORD; subtype STACK is STACK_TYPE(0 to 63); type OP_TYPE is (ADD, SUB, MUL, DIV); type TIMING is array (OP_TYPE range <>, OP_TYPE range <>) of TIME; Examples of object declarations using these types are variable FAST_STK: STACK_TYPE(-127 to 127); constant ALU_TIMING: TIMING := --ADD, SUB, MUL ((10 ns, 20 ns, 45 ns), -- ADD (20 ns, 15 ns, 40 ns), -- SUB (45 ns, 40 ns, 30 ns)); -- MUL STACK_TYPE is defined to be an unconstrained array type that specifies the index of the array as an integer type and the element types as type ADDRESS_WORD. STACK is a subtype of the base type STACK_TYPE with the specified index constraint. The variable declaration for FAST_STK, defined to be of type STACK_TYPE, also specifies an index

constraint. The constant ALU_TIMING specifies the timing for a two-operator ALU, where the operators could be ADD, SUB, or MUL; for example, an ALU that performs ADD and SUB operations has a delay of 20 ns. The declaration for ALU_TIMING is a special case of a constant declaration where no constraint need be specified for the unconstrained array type, since the size of the constant object is determined from the number of values in the constant. There are two predefined one-dimensional unconstrained array types in the language, STRING and BIT_VECTOR. STRING is an array of characters while BIT_VECTOR is an array of bits. Examples are variable MESSAGE: STRING(1 to 17) := "Hello, VHDL world"; signal RX_BUS: BIT_VECTOR(0 to 5) := O"37"; --O"37" is a bit-string literal representing the octal value 37. constant ADD_CODE: BIT_VECTOR := ('0', '1', '1', '1', '0'): A value representing a one-dimensional array of characters is called a string literal. String literals are written by enclosing the sequence of characters within double quotes (". . ."). Examples of string literals are THIS IS A TEST " SPIKE DETECTED! " "State ""READY"" entered!" -- 2 double quote characters in a sequence -- represents one double quote character. A string literal can be assigned to different types of objects, for example, to a STRING type object or to a BIT_VECTOR type object. The type of a string literal is, therefore, determined from the context in which it appears. Here are some examples. -- Example 1: variable ERROR.MESSAGE: STRING(1 to 19); ERROR_MESSAGE := "Fatal ERROR: abort!"; -- Example 2: variable BUS_VALUE: BIT_VECTOR(0 to 3); BUS_VALUE:= "1101"; In the first example, the string literal is of type STRING while in the second example, the string literal is of type BIT_VECTOR. The type of a string literal can also be explicitly stated by using a qualified expression. A string literal that represents a sequence of bits (values of type BIT) can also be represented as a bit string literal. This sequence of bits, called bit strings, can be represented either as a binary value, or as an octal value, or as a hexadecimal value. The underscore character can again be freely used in bit string literals for clarity. Examples are XFFO" -- X for hexadecimal. B"00_0011_1101 --B for binary. O"327" -- 0 for octal. X"FF_FO_AB" There are many different ways to assign values to an array object. Here are some examples. variable OP_CODES : BIT_VECTOR(1 to 5); OP_CODES := "01001"; -- A string literal is assigned. OP_CODES := ('0', '1', '0', '0', '1'); -- Positional association is implicit;

-- first value is assigned to OP_CODES(0), second value to --OP_CODES(1), and so on. OP_CODES := (2=>'1', 5=>'1', others=>'0'); -- Named association; -- second and fifth element of OP_CODES get the value '1', and -- the rest get a value of '0'. OP_CODES := (others=>'0'); -- All values set to '0'. Notice the use of the keyword others to mean all unassigned values; when used, it must be the last. The expressions used in the last three assignments to OP_CODES are examples of array aggregates. An aggregate is a set of comma separated elements enclosed within parenthesis. Record Types An object of a record type is composed of elements of same or different types. It is analogous to the record data type in Pascal and the struct declaration in C. An example of a record type declaration is type PIN_TYPE is range 0 to 10; type MODULE is record SIZE: INTEGER range 20 to 200; CRITICAL_DLY: TIME; NO_INPUTS: PIN_TYPE: NO_OUTPUTS: PIN_TYPE; end record; Values can be assigned to a record type object using aggregates. For example, variable NAND.COMP: MODULE; -- NAND_COMP is an object of record type MODULE. NAND_COMP := (50, 20 ns, 3,2); -- Implies 50 is assigned to SIZE, 20 ns is assigned -- to CRITICAL_DLY, etc. Values can be assigned to a record type object from another record type object of the same type using a single assignment statement. In the following example, each element of NAND_GENERIC is assigned the value of the corresponding element in NAND_COMP. signal NAND_GENERIC: MODULE; NAND_GENERIC <= NAND_COMP; Each element of a record type object can also be assigned individually by using selected names. For example, NAND_COMP.NOJNPUTS := 2: Aggregate values can be assigned to record type objects using both positional and named associations. Since an aggregate does not specify its type, it can be either an array or a record aggregate, depending on its usage. For example, CAB := (others=>'0'); If CAB is an array type object, the aggregate is treated as an array aggregate; on the other hand, if CAB is a record type object, the aggregate is a record aggregate where others refers to all the elements in the record.

Access Types Values belonging to an access type are pointers to a dynamically allocated object of some other type. They are similar to pointers in Pascal and C languages. Examples of access type declarations are -- MODULE is a record type declared in the previous sub-section. type PTR is access MODULE; type FIFO is array (0 to 63, 0 to 7) of BIT; type FIFO_PTR is access FIFO; PTR is an access type whose values are addresses that point to objects of type MODULE. Every access type may also have the value null, which means that it does not point to any object. Objects of an access type can only belong to the variable class. When an object of an access type is declared, the default value of this object is null. For example, variable MOD1PTR, MOD2PTR: PTR; - Default value is null. Objects that access types point to, can be created using allocators. Allocators provide a mechanism to dynamically create objects of a specific type. MOD1PTR := new MODULE; The new in this assignment causes an object of type MODULE to be created and the pointer to this object is returned. The values of the elements of the MODULE record are the default values of each element; these are 20 (the leftmost value of the implied subtype) for the SIZE element, TIME'LEFT for the CRITICAL_DLY element, and the value 0 (this is PIN_TYPE'LEFT) for the NO_INPUTS and NO_OUTPUTS elements. Initial values can also be assigned to a newly created object by explicitly specifying the values as shown in the following example. MOD2PTR := new MODULE'(25, 10ns, 4, 9); Objects of an access type can be referenced as 1. scalar-obj-ptr-all, if scalar-obj-ptr is a pointer to a scalar type object, 2. array-obj-ptr (element-index), if array-obj-ptr is a pointer to an array object, 3. record-obj-ptr.element-name, if record-obj-ptr is a pointer to a record object. The elements of the object that MOD2PTR points to can be accessed as MOD2PTR.SIZE, MOD2PTR.CRITICAL_DLY, MOD2PTR.NO_INPUTS and MOD2PTR.NO_OUTPUTS, provided the pointer is not null. For every access type, a procedure DEALLOCATE is implicitly declared. This procedure, when called, returns the storage occupied by the object to th.e host environment. For the PTR and FIFO_PTR access types declared earlier, the following DEALLOCATE procedures are implicitly declared: procedure DEALLOCATE (P: inout PTR); procedure DEALLOCATE (P: inout FIPO_PTR); The execution of the following statement: DEALLOCATE(MOD2PTR); causes the storage occupied by the object that MOD2PTR points to, to be deallocated and MOD2PTR is set to null. Pointers can be assigned to other pointer variables of the same access type. Therefore, MOD1PTR := MOD2PTR;

is a legal assignment. Both MOD1PTR and MOD2PTR now point to the same object. Consider the following example. type BITVEC_PTR is access BIT_VECTOR; variable BITVEC1:BITVEC_PTR:= new BIT_VECTOR'("1001'); BITVECI points to an object that is constrained to be of four bits. The bits can be accessed as BITVECI(0) which is 1, BITVECI(1) which is 0, and so on. Here is another example in which values are assigned to a dynamically allocated object using named association. MOD2PTR := new MODULE'(CRITICAL_DLY=>10 ns, NO_INPUTS=>2, NO_OUTPUTS=>3, SIZE=>100); Access types are useful in modeling high-level behavior, especially that of regular structures like RAMs and FIFOs, where pointers can be used to access objects one at a time in a sequence. Incomplete Types It is possible to have an access type that points to an object that has elements which are also access types. This can lead to mutually dependent or recursive access types. Since a type must be declared before it is used, an incomplete type declaration can be used to solve this problem. An incomplete type declaration has the form type type-name; Once an incomplete type has been declared, the type-name can now be used in any mutually dependent or recursive access type. However, a corresponding full type declaration must follow later. An example of a mutually dependent access type is type COMP; -- Record contains component name and list of -- nets its connected to. type NET; -- Record contains net name and list of -- components it is connected to. type COMP_PTR is access COMP; type NET_PTR is access NET; constant MODMAX: INTEGER := 100; constant NETMAX: INTEGER := 2500; type COMPJJST is array (1 to MODMAX) of COMP_PTR; type NETJJST is array (1 to NETMAX) of NET_PTR; type COMPLIST_PTR is access COMP_LIST; type NETUST_PTR is access NET_LIST; -- Full type declarations for COMP and NET follow: type COMP is record COMP_NAME: STRING(1 to 10); NETS: NETLIST_PTR; end record; type NET is record NET_NAME: STRING(1 to 10); COMPONENTS: COMPLIST_PTR:

end record; Here, COMP and NET have elements which access objects of type NET and COMP, respectively. An example of a recursive access type is type DFG; A data flow graph node. type OP_TYPE is (ADD, SUB, MUL, DIV, SHIFT, ROTATE); type PTR is access DFG; type DFG is record OP_CODE: OP_TYPE; SUCC: PTR; -- Successor node list. PRED:PTR; --Predecessor node list. end record; PTR is an access type of DFG. It is also the type of an element in DFG. Here is another example. type MEM_RANGE is range 0 to 16383; type HOLE; -- Represents available memory. type HOLE_PTR is access HOLE; type HOLE is record START-LOC, END_LOC: MEM_RANGE; PREV_HOLE, NEXT_HOLE: HOLE_PTR; end record; File Types Objects of file types represent files in the host environment. They provide a mechanism by which a VHDL design communicates with the host environment. The syntax of a file type declaration is type file-type-name Is file of type-name, The type-name is the type of values contained in the file. Here are two examples. type VECTORS is file of BIT_VECTOR; type NAMES is file of STRING; A file of type VECTORS has a sequence of values of type BIT_VECTOR; a file of type NAMES has a sequence of strings as values in it. A file is declared using a file declaration.The syntax of a file declaration is: file file-name: file-type-name is mode string-expression ', The string-expression is interpreted by the host environment as the physical name of the file. The mode of a file, in or out, specifies whether it is an input or an output file, respectively. Input files can only be read while output files can only be written to. Here are two examples of declaring files. file VEC_FILE: VECTORS is in "/usr/home/jb/uart/div.vec"; file OUTPUT: NAMES is out "stdout"; VEC_FILE is declared to be a file that contains a sequence of bit vectors and it is an input file. It is associated with the file "/usr/home/jb/uart/div.vec" in the host environment. A file belongs to the variable class of objects. However, a file cannot be assigned values using a variable assignment statement. It can be read, written to, or tested

for an end-of-file condition, only by using special procedures and functions that are implicitly declared for every file type; these are procedure READ (F: in file-type-name ; VALUE: out type-name) ; -- Gets the next value in VALUE from file F. procedure WRITE (F: out file-type-name ; VALUE: in type-name) ; -- Appends a given value in VALUE to file F. function ENDFILE (F: in file-type-name) return BOOLEAN; -- Returns false if a read on an input file F will be successful in getting -- another value, otherwise it returns true. If type-name is an unconstrained array type, a different READ procedure is implicitly declared, which is of the form procedure READ (F: in file-type-name, VALUE: out type-namer, LENGTH: out NATURAL); -- LENGTH returns the number of elements of the array that was read. A file cannot be opened or closed explicitly and values within a file can only be accessed sequentially. Here is a complete example of a test bench that reads vectors from a file,. "fadd.vec", applies these vectors to the test component, a one-bit full adder, and then writes back the results into another file, "fadd.out". entity FA_TEST is end; architecture IO_EXAMPLE of FA_TEST is component FULL_ADD port (CIN, A, B: in BIT; COUT, SUM: out BIT); end component; subtype STRING3 is BIT_VECTOR(0 to 2); subtype STRING2 is BIT_VECTOR(0 to 1); type IN_TYPE is file of STRING3; type OUT_TYPE is file of STRING2; file VEC_FILE: IN_TYPE is in "/usr/home/jb/vhdl_ex/fadd.vec"; file RESULT_FILE: OUT_TYPE is out "/usr/home/jb/vhdl_ex/fadd.out"; signal S: STRINGS; signal Q: STRING2: begin FA: FULL_ADD port map (S(0), S(1), S(2), Q(0), Q(1)); process constant PROPAGATION_DELAY: TIME := 25ns; variable IN_STR: STRING3; variable OUT_STR: STRING2; begin while (not ENDFILE (VEC_FILE)) loop READ (VEC_FILE, IN_STR); S <= IN_STR; wait for PROPAGATION_DELAY; OUT_STR := Q; WRITE (RESULT_FILE, OUT_STR);

end loop; assert FALSE report "Completed."; end process; end IO_EXAMPLE; Two files, VEC_FILE and RESULT_FILE, are declared. VEC_FILE is an input file and contains 3-bit strings, and RESULT_FILE is an output file into which 2-bit strings are written. Input vectors are read one at a time until end of file is reached. Each vector is applied and then the process waits for the full-adder circuit to stabilize before sampling the adder output value which is written to the output file. The assertion statement when executed simply stops the simulation run since the assertion condition is always false. Thisstatement is not necessary if the VHDL simulator provides a facility to control the simulation run. One file type, TEXT, is predefined in the language; this file type represents a file consisting of variable length ASCII strings. An access type, LINE, is also provided to point to such strings. Operations to read and write data from a single line are provided. Operations to read and write entire lines are also provided. The definitions for all these types and operations appear in the predefined package, TEXTIO. Operators The predefined operators in the language are classified into the following five categories: 1. Logical operators 2. Relational operators 3. Adding operators 4. Multiplying oper ators 5. Miscellaneous operators The operators have increasing precedence going from category (1) to (5). Operators in the same category have the same precedence and evaluation is done left to right. Parentheses may be used to override the left to right evaluation. Logical Operators The six logical operators are and or nand nor xor not These operators are defined for the predefined types BIT and BOOLEAN. They are also defined for one-dimensional arrays of BIT and BOOLEAN. During evaluation, bit values '0' and 1' are treated as FALSE and TRUE values of the BOOLEAN type, respectively. The result of a logical operation has the same type as its operands. The not operator is a unaiy logical operator and has the same precedence as that of miscellaneous operators. Relational Operators These are = /= < <= > >= The result types for all relational operations is always BOOLEAN. The = (equality) and the /= (inequality) operators are permitted on any type except file types. The remaining four relational operators are permitted on any scalar type (e.g., integer or enumerated types) or discrete array type (i.e., arrays in which element values belong to a discrete

type). When operands are discrete array types, comparison is performed one element at a time from left to right. For example, BIT_VECTOR'('0', '1', '1') < BIT_VECTOR'('1', '0', '1') is true, since the first element in the first array aggregate is less than the first element in the second aggregate. Similarly, if type MVL is ('U', '0', '1', 'Z' ); then, MVL'( 'U' ) < MVL'( 'Z' ) is true since 'U' occurs to the left of 'Z'. Note that it is necessary to qualify the aggregates and literals used in these examples since the literals are overloaded and their type cannot be determined from its use. Adding Operators These are +-& The operands for the + (addition) and - (subtraction) operators must be of the same numeric type with the result being of the same numeric type. The addition and subtraction operators may also be used as unary operators, in which case, the operand and the result type must be the same. The operands for the & (concatenation) operator can be either a I-dirnensional array type or an element type. The result is always an array type. For example, '0' & '1' results in an array of characters "01". 'C' & 'A' & 'T' results in the value "CAT". "BA" & "LL" creates an array of characters "BALL". Multiplying Operators These are * / mod rem The * (multiplication) and / (division) operators are predefined for both operands being of the same integer or floating point type. The result is also of the same type. The multiplication operator is also defined for the case when one of the operands is of a physical type and the second operand is of integer or real type. The result is of physical type. For the division operator, division of an object of a physical type by either an integer or a real type object is allowed and the result type is of the physical type. Division of an object of a physical type by another object of the same physical type is also defined and it yields an integer value as a result. The rem (remainder) and mod (modulus) operators operate on operands of integer types and the result is also of the same type. The result of a rem operation has the sign of its first operand and is defined as A rem B = A - ( A / B ) * B The result of a mod operator has the sign of the second operand and is defined as A mod B = A B * N -For some integer N.

Miscellaneous Operators The miscellaneous operators are abs ** The abs (absolute) operator is defined for any numeric type. The ** (exponentiation) operator is defined for the left operand to be of integer or floating point type and the right operand (i.e., the exponent) to be of integer type only. The not logical operator has the same precedence as the above two operators.

Packages and Libraries


A package provides a convenient mechanism to store and share declarations that are common across many design units. A package is represented by 1. a package declaration, and optionally, 2. a package body. Package Declaration A package declaration contains a set of declarations that may possibly be shared by many design units. It defines the interface to the package, that is, it defines items that can be made visible to other design units, for example, a function declaration. A package body, in contrast, contains the hidden details of a package, for example, a function body. The syntax of a package declaration is package package-name is package-item-declarations "> These may be: - subprogram declarations ~ type declarations - subtype declarations - constant declarations - signal declarations - file declarations - alias declarations - component declarations - attribute declarations - attribute specifications - disconnection specifications - use clauses end [ package-name ] ; An example of a package declaration is given next. package SYNTH_PACK is constant LOW2HIGH: TIME := 20ns: type ALU_OP is (ADD, SUB, MUL, DIV, EQL); attribute PIPELINE: BOOLEAN; type MVL is ('U', '0', '1', 'Z'); type MVL_VECTOR is array (NATURAL range <>) of MVL; subtype MY_ALU_OP is ALU_OP range ADD to DIV; component NAND2 port (A, B: in MVL; C: out MVL); end component;

end SYNTH_PACK; Items declared in a package declaration can be accessed by other design units by using the library and use context clauses. The set of common declarations may also include function and procedure declarations and deferred constant declarations. In this case, the behavior of the subprograms and the values of the deferred constants are specified in a separate design unit called the package body. Since the previous package example did not contain any subprogram declarations and deferred constant declarations, a package body was not necessary. Consider the following package declaration. use WORK.SYNTH_PACK.all: package PROGRAM_PACK is constant PROP_DELAY: TIME; -A deferred constant. function "and" (L, R: MVL) return MVL; procedure LOAD (signal ARRAY_NAME: inout MVL_VECTOR; START_BIT, STOP_BIT, INT_VALUE: in INTEGER); end PROGRAM_PACK; In this case, a package body is required. Package Body A package body primarily contains the behavior of the subprograms and the values of the deferred constants declared in a package declaration. It may contain other declarations as well, as shown by the following syntax of a package body. package body package-name is package-body-item-daclarations "> These are: - subprogram bodies -- complete constant declarations - subprogram declarations - type and subtype declarations - file and alias declarations - use clauses end [ package-name ]; The package name must be the same as the name of its corresponding package declaration. A package body is not necessary if its associated package declaration does not have any subprogram or deferred constant declarations. The associated package body for the package declaration, PROGRAM_PACK, described in the previous section is package body PROGRAM_PACK is constant PROP_DELAY: TIME := 15ns; function "and" (L, R: MVL) return MVL is begin return TABLE_AND(L, R); -- TABLE_AND is a 2-D constant defined elsewhere. end "and"; procedure LOAD (signal ARRAY_NAME: inout MVL_VECTOR; START_BIT, STOP_BIT, INT_VALUE: in INTEGER) is -- Local declarations here.

begin -- Procedure behavior here. end LOAD; end PROGRAM_PACK; An item declared inside a package body has its scope restricted to be within the package body and it cannot be made visible in other design units. This is in contrast to items declared in a package declaration that can be accessed by other design units. Therefore, a package body is used to store private declarations that should not be visible, while a package declaration is used to store public declarations which other design units can access. This is very similar to declarations within an architecture body which are not visible outside of its scope while items declared in an entity declaration can be made visible to other design units. An important difference between a package declaration and an entity declaration is that an entity can have multiple architecture bodies with different names, while a package declaration can have exactly one package body, the names for both being the same. A subprogram written in any other language can be made accessible to design units by specifying a subprogram declaration in a package declaration without a subprogram body in the corresponding package body. The association of this subprogram with its declaration in the package is not defined by the language and is, therefore, tool implementation-specific. Design Libraries A compiled VHDL description is stored in a design library. A design library is an area of storage in the file system of the host environment. The format of this storage is not defined by the language. Typically, a design library is implemented on a host system as a file directory and the compiled descriptions are stored as files in this directory. The management of the design libraries is also not defined by the language and is again tool implementation-specific. An arbitrary number of design libraries may be specified. Each design library has a logical name with which it is referenced inside a VHDL description. The association of the logical names with their physical storage names is maintained by the host environment. There is one design library with the logical name, STD, predefined in the language; this library contains the compiled descriptions for the two predefined packages, STANDARD and TEXTIO. Exactly one design library must be designated as the working library with the logical name, WORK. When a VHDL description is compiled, the compiled description is always stored in the working library. Therefore, before compilation begins, the logi'cal name WORK must point toone of the design libraries. Figure 9.1 shows a typical compilation scenario.

The VHDL source is present in an ASCII file called the design file. This is processed by the VHDL analyzer, which after verifying the syntactic and semantic correctness of the source, compiles it into an intermediate form. The intermediate form is stored in the design library that has been designated as the working library. Design File The design file is an ASCII file containing the VHDL source. It can contain one or more design units, where a design unit is one of the following: entity declaration, architecture body, configuration declaration, package declaration, package body. This means that each design unit can also be compiled separately. A design library consists of a number of compiled design units. Design units are further classified as 1. Primary units: These units allow items to be exported out of the design unit. They are a. entity declaration: The items declared in an entity declaration are implicitly visible within the associated architecture bodies. b. package declaration: Items declared within a package declaration can be exported to other design units using context clauses. c. configuration declaration. 2. Secondary units: These units do not allow items declared within them to be exported out of the design unit, that is, these items cannot be referenced in other design units. These are a. architecture l)ody: A signal declared in an architecture body, for example, cannot be referenced in other design units. b. package body. There can be exactly one primary unit with a given name in a single design library. Secondary units associated with different primary units can have identical names in the same design library; also a secondary unit may have the same name as its

associated primary unit. For example, assume there exists an entity called AND_GATE in a design library. It may have an architecture body with the same name, and another entity, MY_GATE, in the same design library may have an architecture body that also has the name, AND_GATE. Secondary units must coexist with their associated primary units in the same design library, for example, an entity declaration and all of its architecture bodies must reside in the same library. Similarly, a package declaration and its associated package body must reside in a single library. Even though a configuration declaration is a primary unit, it must reside in the same library as the entity declaration to which it is associated. Order of Analysis Since it is possible to export items declared in primary units to other design units, a constraint is imposed in the sequence in which design units must be analyzed. A design unit that references an item declared in another primary unit can be analyzed only after that primary unit has been analyzed. For example, if a configuration declaration references an entity, COUNTER, the entity declaration for COUNTER must be analyzed before the configuration declaration. A primary unit must be analyzed before any of its associated secondary units. For example, an entity declaration must be analyzed before its architecture bodies are analyzed. Implicit Visibility An architecture body implicitly inherits all declarations in the entity since it is tied to that entity by virtue of the statement architecture architecture-name of entity-name is . . . Similarly, a package body implicitly inherits all items declared in the package declaration by virtue of its first statement package body package-name is. . . where the package name is the same as the one in the package declaration. 9.7 Explicit Visibility Explicit visibility of items declared in other design units can be achieved using context clauses. There are two types of context clauses: 1. library clause, 2. use clause. Context clauses are associated with design units and may be written just before the design unit to which visibility is to be made available. Figure 9.2 shows an example. Items specified in the context clause become visible only to the design unit that follows the context clause. This means that if a design file contains three design units, such as in the example shown in Fig. 9.3, then context clauses must be specified for each design unit. For example, the context clauses specified before design unit A makes them visible to design unit A only; context clauses specified before design unit B makes them visible to design unit B only.

Figure 9.2 Context clause associated w'rth following design unit.

Library Clause The library clause makes visible the logical names of design libraries that can be referenced within a design unit. The format of a library clause is library list-of-logical-library-names; The following example of a library clause library TTL, CMOS; makes the logical names, TTL and CMOS, visible in the design unit that follows. Note that the library clause does not make design units or items present in the library visible, it makes only the library name visible (it is like a declaration for a library name). For

example, it would be illegal to use the expression "TTL.SYNTH_PACK.MVL" within a design unit without first declaring the library name using the "library 1TL;" clause. The following library clause library STD, WORK; is implicitly declared for every design unit. 9.7.2 Use Clause There are two main forms of the use clause. use library-name. primafy-unit-name ; --Form 1. use library-name. primafy-unit-name. Item ; --Form 2. The first form of the use clause allows the specified primary unit name from the specified design library to be referenced in a design description. For example, library CMOS; use CMOS.NOR2; configuration... . . . use entity NOR2( . . . ); end; Note that entity NOR2 must be available in compiled form in the design library, CMOS, before attempting to compile the design unit where it is used. The second form of the use clause makes the item declared in the pri- ' mary unit visible and the item can, therefore, be referenced within the following design unit. For example, library ATTLIB; use ATTLIB.SYNTH_PACK.MVL; -- MVL is a type declared in SYNTH_PACK package. -- The package, SYNTH_PACK, is stored in the ATTLIB design library. entity NAND2 is port (A, B: in MVL; ...)... If all items within a primary unit are to be made visible, the keyword all can be used. For example, use ATTLIB.SYNTH_PACK.all; makes all items declared in package SYNTH_PACK in design library ATTLIB visible. Items external to a design unit can be accessed by other means as well. One way is to use a selected name. An example of using a selected name is library ATTLIB; use ATTLIB.SYNTH_PACK; entity NOR2 is port (A, B: in SYNTH_PACK.MVL; ...)... Since only the primary unit name was made visible by the use clause, the complete name of the item, that is, SYNTH_PACK.MVL must be specified. Another example is shown next. The type VALUE_9 is defined in package SIMPACK that has been compiled into the CMOS design library. library CMOS; package P1 is procedure LOAD (A, B: CMOS.SIMPACK.VALUE_9; ...)... ...

end P1; In this case, the primary unit name was specified only at the time of usage. So far, we talked about exporting items across design libraries. What if it is necessary to export items from design units that are in the same library? In this case, there is no need to specify a library clause since every design unit has the following library clause implicitly declared. library WORK; The predefined design library STD contains the package STANDARD. The package STANDARD contains the declarations for the predefined types such as CHARACTER, BOOLEAN, BIT_VECTOR, and INTEGER. The following two clauses are also implicitly declared for every design unit: library STD; use STD.STANDARD.all; Thus all items declared within the package STANDARD are available for use in every VHDL description.

Session VI by Prof.K.R.Shoba:4.4.05 Subprograms Often the algorithmic model becomes so large that it needs to be split into distinct code segments. And many a times a set of statements need to be executed over and over again in different parts of the model. Splitting the model into subprograms is a programming practice that makes understanding of concepts in VHDL to be simpler. Like other programming languages, VHDL provides subprogram facilities in the form of procedures and functions. The features of subprograms are such that they can be written once and called many times. They can be recursive and thus can be repeated from within the scope. The major difference between procedure and function is that the function has a return statement but a procedure does not have a return statement. Types of Subprograms VHDL provides two sub-program constructs: Procedure: generalization for a set of statements. Function: generalization for an expression. Both procedure and function have an interface specification and body specification. Declarations of procedures and function Both procedure and functions can be declared in the declarative parts of: Entity Architecture Process Package interface Other procedure and functions Formal and actual parameters The variables, constants and signals specified in the subprogram declaration are called formal parameters. The variables, constants and signals specified in the subprogram call are called actual parameters. Formal parameters act as placeholders for actual parameters.

Concurrent and sequential programs 26

Both functions and procedures can be either concurrent or sequential Concurrent functions or procedures exists outside process statement or another subprogram Sequential functions or procedures exist only in process statement or another subprogram statement. Functions A function call is the subprogram of the form that returns a value. It can also be defined as a subprogram that either defines a algorithm for computing values or describes a behavior. The important feature of the function is that they are used as expressions that return values of specified type. This is the main difference from another type of subprogram: procedures, which are used as statements. The results return by a function can be either scalar or complex type. Function Syntax <= [ pure | impure ] function id [ ( parameter_interface_list ) ] return return_type is {declarative part } begin { sequential statement } [ label :] return return_value; end [ function ] [ id ] ;

Function declaration

parameter_interface _list <= ( [ constant | signal ] identifier{ , . . . }: [ in ] subtype_indication [ := static_expression ] ) { ; . . . }

Functions can be either pure (default) or impure. Pure functions always return the same value for the same set of actual parameters. Impure functions may return different values for the same set of parameters. Additionally an impure function may have side effects like updating objects outside their scope, which is not allowed in pure function. The function definition consists of two parts: 1) Function declaration: this consists of the name, parameter list and type of values returned by function 2) Function body: this contains local declaration of nested subprograms, types, constants, variables, files, aliases, attributes and groups, as well as sequence of statements specifying the algorithm performed by the function. The function declaration is optional and function body, which contains the copy of it is sufficient for correct specification. However, if a function declaration exists, the function body declaration must exist in the given scope. Functional Declaration:

27

The function declaration can be preceded by an optional reserved word pure or impure, denoting the character of the function. If the reserved word is omitted it is assumed to be pure by default. The function name (id), which appears after the reserved word function can either be an identifier or an operator symbol. Specification of new functions for existing operators is allowed in VHDL and is called OPERATOR OVERLOADING. The parameters of the function are by definition INPUTS and therefore they do not need to have the mode (direction) explicitly specified. Only constants, signals and files can be function parameters .The object class is specified by using the reserved words (constant, signal or file respectively) preceding the parameter name. If no reserved word is used, it is assumed that the parameter is a CONSTANT. In case of signal parameters the attributes of the signal are passed into the function, except for `STABLE, `QUIET, `TRANSACTION and `DELAYED, which may not be accessed within the function. Variable class is NOT allowed since the result of operations could be different when different instantiations are executed. If a file parameter is used, it is necessary to specify the type of data appearing in the opened file. Function Body: Function body contains a sequence of statements that specify the algorithm to be realized within the function. When the function is called, the sequence of statements is executed.. A function body consists of two parts: declarations and sequential statements. At the end of the function body, the reserved word END can be followed by an optional reserved word FUNCTION and the function name. Pure an Impure Functions Pure Functions Function Does Not Refer to Any Variables or Signals Declared by Parent Result of Function Only Depends on Parameters Passed to It Always Returns the Same Value for Same Passed Parameters No Matter When It Is Called If Not Stated Explicitly, a Function Is Assumed to Be Pure

Impure Function Can State Explicitly and Hence Use Parents Variables and/or Signals for Function Computation May Not Always Return the Same Value

28

Function Calling Once Declared, Can Be Used in Any Expression A Function Is Not a Sequential Statement So It Is Called As Part of an Expression

[ label : ] function_name Example 1 [ parameter_association_list ] ;

type int_data is file of natural; function func_1 (a, b, x : real) return real; function * (a, b: integer_new) return integer_new; function add_signals (signal in1, in2: real) return real; function end_of_file (file file_name: int_data) return boolean;

The first function name above is called func_1, it has three parameters A,B and X, all of the REAL types and returns a value also of REAL type. The second function defines a new algorithm for executing multiplication. Note that the operator is enclosed in double quotes and plays the role of the function name. The third is based on the signals as input parameters, which is denoted by the reserved word signal preceding the parameters. The fourth function declaration is a part of the function checking for end of file, consisting of natural numbers. Note that the parameter list uses the Boolean type declaration. Example 2 function transcod_1(value: in std_logic_vector (0 to 7)) return std_logic_vector is begin case value is when 00000000 => return 01010101; when 01010101 => return 00000000; when others => return 11111111; end case; end transcod_1; 29

The case statement has been used to realize the function algorithm. The formal parameter appearing in the declaration part is the value constant, which is a parameter of the std_logic_vector type. This function returns a value of the same type. Example 3 function func_3 (constant A, B, X: real) return real is begin return A*X**2+B; end func_3;

The formal parameters: A, B and X are constants of the real type. The value returned by this function is a result of calculating the A*X**2+B expression and it is also of the real type. Example 4 function func_4(constant A, B, step, leftb, rightb: in real) return real is variable counter, max, temp: real; begin counter: =leftb; max:= func_3(A, B ,counter); l1: while counter<= right loop temp:= func_1 (A,B, counter); if temp>max then max:= temp; end if; counter:=counter+ step; end loop l1; return max; end func_4; The fourth example is much more complicated. It calculates the maximum value of the func_1 function. All the formal parameters are constants of the real type. When the function is called, the A and B values appearing in the function are passed; step is a determinant of calculating correctness. The LeftB and rightB values define the range in which we search for the maximum value of the function. Inside the function body is contained definitions of variables

30

counter, max and temp. They are used in the simple algothim, which calculating all the function values in a given range and storing the maximum values returned by the function. Example 5 variable number: integer: =0; impure function func_5(a: integer) return integer is variable counter: integer begin counter: = a* number; number: = number+1; return counter; end func_5; Func_5 is an impure function its formal parameter A and returned value are constants of the integer type. When the function is invoked, output value depends on the variable number declared outside the function. The number variable is additionally updated after each function call (it increases its value by 1). This variable affects the value calculated by the function, that is why the out function value is different for the same actual parameter value Programs using functions 1. Program to find the largest of three integers using function in architecture library ieee; use ieee.std_logic_1164.all; entity lar is port(z: out integer); end lar; architecture lar of lar is function largest (a,b,c: integer) return integer is variable rval: integer; begin if a>b then rval :=a; else rval :=b; end if; if rval < c then rval :=c; end if; return rval; end largest; begin z<= largest(10,30,20); end lar;

Formal parameters

Actual parameters

31

The function largest computes the largest among the three integer variables a, b, c passed as formal parameters and returns the largest number. Since the function is written in architecture it becomes local to this architecture lar. So this function code cannot be used in any other architecture Output of the program will be 30 which is the largest among the three numbers 10,30 and 20 that is passed as actual parameters to the function largest 2. Program to convert vector to integer using functions

library ieee; use ieee.std_logic_1164.all; entity test is port(a: in std_logic_vector (3 downto 0); ya: out integer range 0 to 15 ); function v2i(a:std_logic_vector) return integer is variable r: integer; begin r:=0; for i in arange loop if a(i)=1 then r:= r+2**i; end if; end loop; return r; end function v2i; end entity test; architecture dataflow of test is begin ya<=v2i(a); end dataflow;
The function v2i computes the integer equivalent of the std_logic_vector, which is passed as an argument to the function. Since the function v2i is written in the entity test the function code is available to any architecture written to the entity test. it is important to note that this function code v2i can be made available to multiple architectures written to the same entity.

32

But in the previous example since the function is written in architecture the code will not be available to any other architecture written to the same entity. If the input a=1010 Then the output ya =10. Procedure A procedure is a subprogram that defined as algorithm for computing values or exhibiting behavior. Procedure call is a statement, which encapsulates a collection of sequential statements into a single statement. It may zero or more values .it may execute in zero or more simulation time. Procedure declarations can be nested o Allows for recursive calls Procedures can call other procedures Procedure must be declared before use. It can be declared in any place where declarations are allowed, however the place of declaration determines the scope Cannot be used on right side of signal assignment expression since doesnt return value Procedure Syntax procedure identifier [ parameter_interface _list ] is { subprogram_declarative_part } begin { sequential_statement } end [ procedure ] [ identifier ] ; parameter_interface _list <= ( [ constant | variable | signal ] identifier { , . . . } Description The procedure is a form of subprogram. it contains local declarations and a sequence of statements. Procedure can be called in the place of architecture. The procedure definition consists of two parts The PROCEDURE DECLARATION, which contains the procedure name and the parameter list required when the procedure is called. The PROCEDURE BODY, which consists of local declarations and statements required to execute the procedure.

33

Procedure Declaration The procedure declaration consists of the procedure name and the formal parameter list. In the procedure specification, the identifier and optional formal parameter list follow the reserved word procedure (example 1) Objects classes CONSTANTS, VARIABLES, SIGNALS, and files can be used as formal parameters. The class of each parameter is specified by the appropriate reserve 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. Parameters of the file type have no mode 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 VARIABLE. When a procedure is called formal parameters are substituted by actual parameters, if a formal parameter is a constant, then actual parameter must be an expression. In case of formal parameters such as signal, variable and file, the actual parameters such as class. Example 2 presents several procedure declarations with parameters of different classes and modes. A procedure can be declared without any parameters. Procedure Body 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 reserve word IS and the subprogram statement part placed between the reserved words BEGIN and END. The key word procedure and the procedure name may optionally follow the END reserve word. Declarations of a procedure are local to this declaration and can declare subprogram declarations, subprogram bodies, types, subtypes, constants, variables, files, aliases, attribute declarations, attribute specifications, use clauses, group templates and group declarations. (example 3) A procedure can contain any sequential statements (including wait statements). A wait statement, however, cannot be used in procedure s which are called from process with a sensitivity list or form within a function. Examples 4 and 5 present two sequential statements specifications. Procedure Parameter List Do not have to pass parameters if the procedure can be executed for its effect on variables and signals in its scope. But this is limited to named variables and signals Class of object(s) that can be passed are 34

Constant (assumed if mode is in) Variable (assumed if mode is out)

Signals are passed by reference ( not value) because if wait statement is executed inside a procedure, the value of a signal may change before the rest of the procedure is calculated. If mode is inout, reference to both signal and driver are passed. In And Out Mode Parameter If the mode of a parameter is not specified it is assumed to be in. A procedure is not allowed to assign a value to a in mode parameter. A procedure can only read the value of an in mode parameter. A procedure can only assign a value to an out mode parameter. In the case of a variable in mode parameter the value of the variable remains constant during execution of the procedure. However, this is not the case for a signal in in mode parameter as the procedure can contain a wait statement, in which case the value of the signal might change. VHDL permits the specification of default values for constant and in mode variable classes only. If a default value is specified, the actual parameter can be replaced by the keyword open in the call.

Default values

Procedure examples Example 1 procedure procedure_1(variable x, y: inout real); The above procedure declaration has two formal parameters: bi-directional X and Y of real type. Example 2 procedure proc_1 (constant in1: in integer; variable o1: out integer); procedure proc_2 (signal sig: inout std_logic);

35

Procedure proc_1 has two formal parameters: the first one is a constant and it is in the mode in and of the integer type, the second one is an output variable of the integer type. Procedure proc_2 has only one parameter, which is a bi-directional signal of type std_logic. Example 3 procedure proc_3(x, y: inout integer) is type word_16 is range 0 to 65536; subtype byte is word_16 range 0 to 255; variable vb1, vb2, vb3: real; constant p1: real: = 3.14; procedure compute (variable v1, v2: real) is begin --subprogram_statement_part end procedure compute; begin --subprogram_statement_part end procedure proc_3;

The example above present different declarations, which may appear in the declarative part of the procedure. Example 4 procedure transcoder_1 (variable value: inout bit_vector (0 to 7)) is begin case value is when 00000000 => value: = 01010101; when 01010101 => value: = 00000000; when others => value: = 11111111; end case; end procedure transcoder_1; The procedure transcoder_1 transforms the value of the signal variable, which is therefore a bi-directional parameter. Example 5 procedure comp_3(in1, r: inn real; step :in integer; w1, w2:out real) is variable counter: integer; begin w1: = 1.43 * in1; w2: =1.0; l1: for counter in 1 to step loop w2: = w2*w1; exit l1 when w2 > r; end loop l1; assert (w2<r) report out of range severity error; end procedure comp_3;

36

The comp_3 procedure calculates two variables of mode out: w1 and w2, both of the real type. The parameters of mode in: in1 and R constants are of real types and step is of integer type. The w2 variable is calculated inside the loop statement. When the value of w2 variable is greater than R, the execution of the loop statement is terminated and the error report appears. Example 6 procedure calculate (w1, w2: in real, signal out1: inout integer); procedure calculate (w1, w2: in integer; signal out1: inout real); --calling of overloaded procedures: calculate(23.76,1.632,sign1); calculate(23,826,sign2);

The procedure calculates is an overloaded procedure as the parameters can be of different types. Only when the procedure is called the simulator determines which version of the procedure should be used, depending on the actual parameters. Important notes The procedure declaration is optional, procedure body can exist without it. however, if a procedure declaration is used, then a procedure body must accompany it. Subprograms (procedures and functions) can be nested. Subprograms can be called recursively. Synthesis tools usually support procedures as long as they do not contain the wait statements.

Procedure Call A procedure call is a sequential or concurrent statement, depending on where it is used. A sequential procedure call is executed whenever control reaches it, while a concurrent procedure call is activated whenever any of its parameters of in or inout mode changes its value.

37

Procedure examples 1.program to add 4 bit vector using procedures.

library ieee; use ieee.std_logic_1164.all; entity test is port(a,b: in std_logic_vector(3 downto 0); cin : in std_logic; sum: out std_logic_vector(3 downto 0); cout: out std_logic); end test; architecture test of test is procedure addvec(constant add1 ,add2: in std_logic_vector; constant cin: in std_logic; signal sum: out std_logic_vector; signal cout : out std_logic; constant n: in natural ) is variable c: std_logic; begin c:= cin; for i in 0 to n-1 loop sum(i) <= add1(i) xor add2(i) xor c; c:= (add1(i) and add2(i)) or (add1(i) and c) or (add2(i) and c); end loop; cout<=c; end addvec; begin addvec(a,b,cin,sum,cout,4); end test;

38

The procedure addvec is used to compute addition of two four bit numbers and implicitly returns the sum and carry after addition. Since the procedure is written in the architecture it cannot be used in any other architecture. The input to the programs are a=0001, b=1110, cin=0.the output for the given inputs are sum =1111 and cout=0; 2. program to convert a given vector of four bits to its equivalent integer using procedure.

library ieee; use ieee.std_logic_1164.all; entity test is port(a: in std_logic_vector (3 downto 0); ya: out integer range 0 to 15); procedure v2i(a:in std_logic_vector; signal r1 : out integer ) is variable r: integer; begin r:=0; for i in arange loop if a(i)=1 then r:= r+2**i; end if; end loop; r1<=r; end procedure v2i; end entity test; architecture dataflow of test is begin v2i(a,ya); end dataflow;
The procedure v2i computes the integer equivalent of the std_logic_vector, which is passed as an in mode argument to the procedure and the computed integer value is assigned to be out mode parameter passed to the procedure. Since the procedure v2i is written in the entity test the procedure code is available to any architecture written to the entity test. it is important to note that this procedure code v2i can be made available to multiple architectures written to the same entity. But in the previous example since the procedure is written in architecture the code will not be available to any other architecture written to the same entity. If the input a=1010 39

Then the output ya =10.

Differences between Functions And Procedures FUNCTIONS PROCEDURES

1) Returns only one argument. 2) Lists parameters are constant by default but can be overridden by using signal. 3) function by itself is not complete statement. 4) function has two parts: function declaration and function call.

1) it can return more than one argument 2) list parameter s can be in/out/inout. it can be only in for constant and can be out/inout for variable. 3) procedure is a complete statement 4) procedure has two parts: procedure declaration and procedure call.

Session VII by Prof.K.R.Shoba:6.4.05 PACKAGES Packages are useful in organizing the data and the subprograms declared in the model VHDL also has predefined packages. These predefined packages include all of the predefined types and operators available in VHDL. In VHDL a package is simply a way of grouping a collections of related declarations that serve a common purpose. This can be a set of subprograms that provide operations on a particular type of data, or it can be a set of declarations that are required to modify the design Packages separate the external view of the items they declare from the implementation of the items. The external view is specified in the package declaration and the implementation is defined in the separate package body. Packages are design unit similar to entity declarations and architecture bodies. They can be put in library and made accessible to other units through use and library clauses Access to members declared in the package is through using its selected name 40

Library_name.package_name.item_name Two Components to Packages Package declaration---The visible part available to other modules Package body --The hidden part Aliases can be used to allow shorter names for accessing declared items

Package declaration The Packages declaration is used to specify the external view of the items. The syntax rule for the package declaration is as follows. The identifier provides the name of the package. This name can be used any where in the model to identify the model The package declarations includes a collection of declaration such as Type Subtypes Constants Signal Subprogram declarations etc Aliases

components The above declarations are available for the user of the packages. The following are the advantages of the usage of packages All the declarations are available to all models that use a package. Many models can share these declarations. Thus, avoiding the need to rewrite these declarations for every model. A package is a separate form of design unit, along with entity and architecture bodies. It is separately analyzed and placed in their working library. Any model can access the items declared in the package by referring the name of the declared item.

The following are the points to be remembered on the package declaration

Package declaration syntax

41

package identifier is { package_declarative_item } end [ package ] [ identifier ] ; Constants in package declaration The external view of a constant declared in the package declaration is just the name of the constant and the type of the constant. The value of the constant need not be declared in the package declaration. Such constants are called are called deferred constants. The actual value of these constants will be specified in the package body. If the package declaration contains deferred constants, then a package body is a must. If the value of the constant is specified in the declaration, then the package body is not required. The constants can be used in the case statement, and then the value of the constant must be logically static. If we have deferred constants in the package declaration then the value of the constant would not be known when the case statement is analyzed. Therefore, it results in an error. In general, the value of the deferred constants is not logically static. Subprograms in the package declaration Procedures and functions can be declared in the package declaration Only the design model that uses the package can access the subprograms declared in the package declarations The subprogram declaration includes only the information contained in the header. This does not specify the body of the subprogram. The package declaration provides information regarding the external view of the subprogram without the implementation details. This is called information hiding. For every subprogram declaration there must be subprogram body in the package body. The subprograms present in the package body but not declared in the package declaration cant be accessed by the design models.

Package body Each package declaration that includes a subprogram or a deferred constant must have package body to fill the missing information. But the package body is not required when the package declaration contains only type, subtype, signal or fully specified constants. It may contain additional declarations which are local to the package body but cannot declare signals in body. Only one package body per package declaration is allowed.

package body identifier is { package_body_declarative_item }


42

Point to remember: The package body starts with the key word package body The identifier of the package body follows the keyword The items declared in the package body must include full declarations of all subprograms declared in the corresponding package declarations. These full declarations must include subprogram headers as it appears in the package declarations. This means that the names, modes typed and the default values of each parameters must be repeated in exactly the same manner. In this regard two variations are allowed: A numerical literal may be written differently for example; in a different base provided it has the same value. A simple name consisting just of an identifier can be replaced by a selected name, provided it refers to the same item. A deferred constant declared in the package declaration must have its value specified in the package body by declaration in the package body A package body may include additional types, subtypes, constants and subprograms. These items are included to implement the subprogram defined in the package declaration. The items declared in the package declaration cant be declared in the package body again An item declared in the package body has its scope restricted to within the package body, and these items are not visible to other design units. Every package declaration can have at most one package body with the name same as that of the package declaration The package body cant include declaration of additional signals. Signals declarations may only be included in the interface declaration of package. Examples for package

library ieee; use ieee.std_logic_1164.all;

package bit_pack is function add4(add1 ,add2:std_logic_vector (3 downto 0); carry: std_logic ) return std_logic_vector; end package bit_pack;

Creating a package bit_pack to add four bit and eight bit numbers.

package body bit_pack is function add4(add1 ,add2: std_logic_vector (3 downto 0); carry: std_logic ) return std_logic_vector is variable cout,cin: std_logic; variable ret_val : std_logic_vector (4 downto 0); begin cin:= carry; ret_val:="00000" ; for i in 0 to 3 loop ret_val(i) := add1(i) xor add2(i) xor cin; cout:= (add1(i) and add2(i)) or (add1(i) and cin) or (add2(i) and cin); cin:= cout; end loop; ret_val(4):=cout; return ret_val; end add4;

43

1. Program to add two four bit vectors using functions written in package bit_pack . library ieee; use ieee.std_logic_1164.all; use work.bit_pack.all; entity addfour is port ( a: in STD_LOGIC_VECTOR (3 downto 0); b: in STD_LOGIC_VECTOR (3 downto 0); cin: in STD_LOGIC; sum: out STD_LOGIC_VECTOR (4 downto 0) ); end addfour; architecture addfour of addfour is begin sum<= add4(a,b,cin); end addfour;

2. Program to implement arithmetic logic unit (ALU) using the function in package bit_pack. CODE 000 001 010 011 100 101 110 111 Z A B A AND B A 0R B A+B A-B L(larger of A&B) S(smaller of A&B) FUNCTION MOV Z,A MOV Z,B AND A,B,Z OR A,B,Z ADD A,B,Z SUB A,B,Z MOV Z,L MOV Z,S CY BR 1(L=A) 0(L=B) 1(S=A) 0(S=B) F(2) F(1) F(0)

44

library ieee; use ieee.std_logic_1164.all; --library programs; use work.bit_pack.all; entity alu is port ( a: in std_logic_vector (3 downto 0); b: in std_logic_vector (3 downto 0); code: in std_logic_vector(2 downto 0); z: out std_logic_vector (3 downto 0); f: inout std_logic_vector (2 downto 0) ); end alu; architecture alu of alu is begin process(a,b,code) variable temp:std_logic_vector(4 downto 0); begin f<=(others=>'0'); if code="000" then z<=a; elsif code="001" then z<=b; elsif code="010" then z<=a and b; elsif code="011" then z<=a or b; elsif code="100" then temp:=add4(a,b,'0'); z<=temp(3 downto 0); f(2)<=temp(4); elsif code="101" then temp:=add4(a,not b,'0'); z<=temp(3 downto 0);

f(2)<=temp(4); elsif code="110" then if a>b then z<=a; f(1)<='1'; elsif b>a then z<=b; f(1)<='0'; end if; elsif code="111" then if a<b then z<=a; f(0)<='1'; elsif b<a then z<=b; f(0)<='0'; end if; end if; end process; end alu;

45

LIBRARY Each design unit entity architecture, configuration, package declaration and package body is analyzed (compiled) and placed in design library. Libraries are generally implemented as directories and are referenced by logical names. In the implementation of VHDL environment, this logical name maps to a physical path to the corresponding directory and this mapping is maintained by the host implementation. However just like variables and signals before we can use a design library we must declare the library we are using by specifying the libraries logical name. This is done in VHDL program using the library clause that has the following syntax library identifier { , . . . } ; In VHDL, the libraries STD and WORK are implicitly declared therefore the user programs do not need to declare these libraries. The STD contains standard package provided with VHDL distributions. The WORK contains the working directory that can be set within the VHDL environment you are using. However if a program were to access functions in a design unit that was stored in a library with a logical name IEEE. Then this library must be declared at the start of the program. Most if not all vendors provide an implementation of the library IEEE with packages such as STD_LOGIC_1164.vhd as well as other mathematics and miscellaneous packages. Once a library has been declared all of the functions procedures and type declarations of a package in this library can be made accessible to a VHDL model through a USE clause. For example, the following statements appear prior to the entity declaration. Library IEEE; USE IEEE.STD_LOGIC_1164.all; 46

When these declarations appear just before the entity design unit they are referred to as the context clause. The second statement in the above context clause makes all of the type definitions functions and procedures defined in the package std_logic_1164.vhd visible to the VHDL model. It is as if all of the declarations had been physically placed within the declarative part of the process that uses them. A second form of the use clause can be used when only a specific item such as a function called my_func in the package is to be made visible. USE IEEE.STD_LOGIC_1164.my_func; The USE clause can appear in the declarative part of any design unit. Collectively the library and the use clauses establish the set of design units that are visible to the VHDL analyzer as it is trying to analyze and compile a specific VHDL design unit. When we first start writing VHDL programs we tend to think of single entity architecture pairs when constructing models. We probably organize our files in the same fashion with one entity description and the associated architecture description in the same file. When this file is analyzed the library and the use clauses determine which libraries and packages within those libraries are candidates for finding functions procedures and user defined types that are referenced within the model being compiled. However these clauses apply only to the immediate entity architecture pair! Visibility must be established for other design units separately. There are three primary design units they are entity package declarations and configuration declarations. The context clause applies to the following primary design unit. If we start having multiple design units within the same physical file then each primary design unit must be preceded by the library and use clauses necessary to establish the visibility to the required packages. for example let us assume that the VHDL model shown in package example are physically in the same file. The statements LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.all; must appear at the beginning of each model. That is prior to the entity descriptions we cannot assume that because we have the statements at the top of the file they are valid for all design units in the same file. In this case if we neglect to precede each model with the preceding statements the VHDL analyzer would return with an error on the use of the type STD_LOGIC in the subsequent models because this is not a predefined type within the language but rather is defined in the package STD_LOGIC_1164. Creating a library Step1: using a text editor it is necessary to create a package with the functions, procedures and types(if necessary a deferred constant). In the example shown we have created a package called bit_pack with two functions int2vec and vec2int which converts integer to vector and vice versa. Step2: analyze and test each of the functions separately before committing them to placement within the package.

47

Step3: it is important to note that packages have two parts package declaration and package body. The function declaration is placed in package declaration and function body is placed in package body. Step4: create a library in name bitlib. This operation of creating a library is simulator specific. Step5: compile the package into the library bitlib. The cad tool documentation provides guidelines on compiling design units into a library. Step6: write any VHDL model to use the library. In the example given below we have specified the model for 74163 ic, which is a counter. Step7: the model must declare library bitlib and provide access to the package via the use clause

library ieee; the model of counter to ensure the functionality properly. Step8: test use ieee.std_logic_1164.all; package bit_pack is function vec2int(a: bit_vector) return integer; function int2vec(a,size: integer) return bit_vector; end bit_pack; Program to be put in bitlib library . package body bit_pack is function vec2int(a : bit_vector) return integer is variable result: integer:=0; begin if a'length = 0 then return result; end if; for i in a'reverse_range loop if a(i)='1' then result:=result+2**i; end if; end loop; return result; end vec2int; function int2vec(a, size: integer) return bit_vector is variable result: bit_vector(size-1 downto 0); variable tmp: integer; begin tmp:=a; for i in 0 to size-1 loop if tmp mod 2 = 1 then result(i):='1'; else result(i):='0'; end if; tmp:=tmp / 2; end loop; return result; end int2vec; end bit_pack;

48

Program to implement the functionality of IC 74163 synchronous counter using the library bitlib.

cout <= q(3). q(2). q(1). q(0). t;

49

Session VIII by Prof.K.R.Shoba:11.4.05 MEALY MACHINES The mealy state machines are sequential machines, which generates outputs based on the present state, and the inputs to the machine. So, it is capable of generating many different patterns of output signals for the same state, depending on the inputs present on the clock cycle.

INPUT VARIABLES INPUT LOGIC

EXCITATION
(E) OUTPUT
CLOCK

MEMEOR

LOGIC

Figure1: Mealy sequential circuit model 50

STATE MACHINE NOTATIONS Input variable: all the variables that originate outside the sequential machine are said to be input variables. Output variables: all the variables that exit the sequential machine are said to be output variables. State variable: the output of the memory (flip flops) defines the state of the sequential machine. Decoded state variables (along with the input variables for a mealy machine ) produce the output variables. Excitation variable: excitation variables are the inputs to the memory (flip flops). The name excitation is used because the variable excites the memory to change. State variables are a function of the excitation variables. Excitation variables are generated by the input combinational logic operating on the state variables and the input variables. State: the state of the sequential machine is defined by the content of the memory. When the memory is realized with flip-flops, the machine state is defined as the q outputs. Each state of the sequential machine is unique and unambiguous. State variables and states are related by the expression2 x = y where x= number of state variables.(Examples flip-flop ) and y = maximum number of states possible (example: 4 state variables can represent a maximum of 16 states) Present state: The status of all state variables, at some time,t, before the next clock edge, represents a condition called present state. The present state or status of sequential circuit memory is reference point with respect to time. Next state: the status of all the state variables. At some time, t+1, represents a condition called next state. The next state of a sequential machine is represented by the memory status after a particular clock, t. State machine represents a system as a set of states, the transitions between them, along with the associated inputs and outputs. So, a state machine is a particular conceptualization of a particular sequential circuit. State machines can be used for many other things beyond logic design and computer architecture Finite State Machine (FSM) is a special case of a sequential machine, which is just a computational machine with memory. In FSM terminology, the state of the machine is reflected in the contents of the memory and is used to determine the output of the machine. In this, finite state machines and other sequential machines differ from simple combinational circuits in which the output depends only on the input at the time with no dependence on history or any information stored in memory. In simple, FSM and all sequential machines have memory and combinational machines do not. Implementation of BCD to EXCESS3 code converter using mealy sequential machine. Excess 3 code is a self-complementing BCD code used in decimal arithmetic units. The truth table above shows that the output code value is three greater than the value of the input code. Truth table
BCD input EXCESS 3 output

51

figure 2: truth table for BCD to EXCESS-3 converter. Procedure to arrive to state diagram
t0 t1 t2

t0

t0t1

52

Figure 3: step by step analysis of the truth table We see from the above truth table that the values t0 can take is either 0 or 1. and the corresponding value t0 of Z at the output is complementing i.e. 1 or 0 respectively. When t1(which can be either 0 or 1) is fed as input. The previous input t0 could be either 0 or 1. so from the truth table we check the combination for t0 t1 which could be 00,01,10,11 respectively. And the corresponding output t1 of Z is written down as shown in the above figure 3. The same procedure is repeated for input t2 and t3 as shown the above figure 3.

State diagram

Figure 4: state diagram for BCD to EXCESS-3 converter A state diagram is a graphical representation of a sequential circuit, where individual states are represented by circles with identifying symbol located inside. Changes from state to state is indicated by directed arcs. Input conditions that causes the state changes to occur and resulting output signals are written adjacent to the directed arc. Above figure 4 illustrates the symbolic notation for a BCD to excess3 converter. 53

State A is represented with a circle with the letter A inside it. A directed arc connects state A with state B in this example, the next state can be B or it could change to C, depending on the input variable. Similarly the other states can be derived by seeing the procedure indicated in the previous page figure 3.

54

Figure 5: State table for BCD to EXCESS-3 converter State tables are the tabular forms of the state diagram. The present state (P.S.) column lists all of the possible states in the machine. The series of next state columns exist, one for each input combination. The purpose of the state table is to indicate the state transition. The fifteen states, one input variable state diagram shown in the above figure 4 is converted to state table shown in figure 5.

Simplified state table.

S0 S1 S2 S3 S4 S5 S6
Figure 6: Simplified state table for BCD to EXCESS-3 converter Simplified state diagram.

55

Figure 7: Simplified state diagram for BCD to EXCESS-3 converter

Alternate state table

Figure 8: Alternate form of state table using sequential states

Guidelines for reducing the amount of logic required and deriving the assignment map. Assignment map: This table assigns binary values to different states depending on the number of flip flops that are used for implementation. Rule I. States which have the same next state (NS) for a given input should be given adjacent assignments (look at the columns of the state table). 56

Rule II. States which are the next states of the same state should be given adjacent assignments (look at the rows). Rule III. States which have the same output for a given input should be given adjacent assignments

Implementation of rules with respect to the example considered. I. (1,2) (3,4) (5,6) (in the X=1 column, S1 and S2 both have NS S4;in the X=0 column, S3 & S4 have NS S5, and S5 & S6 have NS S0) II. (1,2) (3,4) (5,6) (S1 & S2 are NS of S0; S3 & S4 are NS of S1; and S5 & S6 are NS of S4) III. (0,1,4,6) (2,3,5)

Figure 9: Assignment map

Transition table

Figure 10: Transition table A transition table takes the state table once step further. The state diagram and state using symbols or names. Creation of a transition table requires the specific state variable values being assigned to each state. The assignment of values to state variables is called making the 57

state assignment. The state assignment links the abstract state symbols to actual state variable binary values as shown in the above figure 10. The transition table indicates changes that occur in the state variables as a state machine sequences from one state to the next. At each new clock pulse the input variable x is evaluated. If a change of state is required then appropriate flip-flop output must change, indicating the state transition. Excitation table Once the changes in the flip-flop outputs are known, the next step is to determine the excitation inputs needed to cause the desired flip-flop output changes. This requires deciding on the type of flip-flop to be used in realizing the state machine. T and D F/F require only a single input. But JK and RS flip-flop requires two inputs. In this example we have implemented the code converter using D F/F whose characteristic table is as shown below in figure 11. D 0 1 0 1 Present Q 0 0 1 1 Next Q+ 0 1 0 1

Figure 11 : Truth table of D-F/F

D1 Q2 Q3 X Q1

D2

D3

58

Simplification of the excitation table using K-MAPs

Figure 11 : Excitation table using D-F/F

Realization of the code converter using D-F/F

59

VHDL code for BCD to excess-3 converter

library ieee ; use ieee.std_logic_1164.all ; entity SM1_2 is port(X, CLK: in bit; Z: out bit); end SM1_2; architecture Table of SM1_2 is signal State, Nextstate: integer := 0; begin process(State,X) --Combinational Network begin case State is when 0 => if X='0' then Z<='1'; Nextstate<=1; end if; if X='1' then Z<='0'; Nextstate<=2; end if; when 1 => if X='0' then Z<='1'; Nextstate<=3; end if; if X='1' then Z<='0'; Nextstate<=4; end if; when 2 => if X='0' then Z<='0'; Nextstate<=4; end if; if X='1' then Z<='1'; Nextstate<=4; end if; when 3 => if X='0' then Z<='0'; Nextstate<=5; end if; if X='1' then Z<='1'; Nextstate<=5; end if; when 4 => if X='0' then Z<='1'; Nextstate<=5; end if; if X='1' then Z<='0'; Nextstate<=6; end if; when 5 => if X='0' then Z<='0'; Nextstate<=0; end if; if X='1' then Z<='1'; Nextstate<=0; end if; when 6 => if X='0' then Z<='1'; Nextstate<=0; end if; when others => null; -- should not occur end case; end process; process(CLK) -- State Register begin if CLK='1' then -- rising edge of clock State <= Nextstate; end if; end process; end Table;

60

61

Output waveform for BCD to excess-3 code converter

Session IX by Prof.K.R.Shoba:13.4.05 PROGRAMMABLE LOGIC DEVICES

Till now digital circuits were designed using small and medium scale integrated circuits like gates, flip-flops, counters, registers etc, which are called random logic ICs. Now Large-scale integrated circuits, which include memory, microprocessor and programmable logic devices (PLDs) are used for designing logic circuits. PLDs come in a variety of types, but they all have common characteristics; that is, they all can be custom configure by the user to perform specific functions. PLDs consists of an array of identical functional cells. The cells array usually consists of an AND_OR network and often includes a flip-flop. Some PLDs can perform only combinational logic functions, others can perform both combinational logic and sequential logic functions. PLDs give improved performance over random logic and significant cost saving over Very Large-scale integrated circuits. They consume less power, take fewer ICs and are more reliable than random logic designs. The trade off is in speed performance. Random logic still has an upper hand at very high speeds in the Emitter Coupled Logic (ECL) family. The real advantage of PLD designs over random logic is in the ease of design and the resulting design timesaving. The different types of PLDs available are: ROM and EPROM. Programmable Logic Arrays (PLA) Programmable Array Logic (PAL) Field Programmable Gate Arrays (FPGA) 62

ROM A read only memory (ROM) is essentially a device in which permanent binary information is stored. The information must be specified by the designer and is then embedded into the ROM to form the required interconnection or electronic device pattern. Once the pattern is established, it stays within the ROM even when the power is turned off and on again; that is ROM is nonvolatile. A block diagram of the ROM is shown in the figure 1 below. There are n inputs and m outputs. The inputs provide the address for the memory, and the outputs give the data bits of the stored word that is selected from the address. The number of words in the ROM device is determined from the fact that n address input lines can specify 2 n words. Note that ROM doesnt have data inputs, because it doesnt have write operation. Integrated circuit ROM chips have one or more enable inputs and come with the three-state outputs to facilitate the construction of large arrays of ROM. n inputs 2 n x m ROM (address) m outputs

Fig 1 Block Diagram of ROM Conceptually, a ROM consists of a decoder and a memory array. When a pattern of n 0s and 1s is applied to the decoder inputs, exactly one of the 2n-decoder outputs is 1. This decoder output line selects one of the words in the memory array, and the bit pattern stored in this word is transferred to the memory output lines. 2n x m ROM can realize m functions of n variables , since it can store a truth table with 2n rows and m columns.

Fig 2 Internal Logic Diagram of ROM

63

Four technologies are used for ROM programming. If mask programming is used, then the data array is permanently stored at the time of manufacture. Preparation of the mask is expensive, so mask programmable ROMs are economically feasible if large quantity are required within the same data array. If a small quantity of ROMs are required within a given data array, then EPROMs may be used. EPROMs allow the modification of the data stored as they use a special charge storage mechanism to enable or disable the switching elements in the memory array. The data stored in the EPROM is generally permanent until erased using ultraviolet light. The electrically erasable PROM (EEPROM) is similar to EPROM except that the erasure of data is accomplished using electrical pulses instead of ultraviolet light. An EEPROM can be erased and reprogrammed only a limited number of times. Flash memories are similar to EEPROMs except that they use a different charge storage mechanism. They also have built in programming and erase capability so that the data can be written to the flash memory while it is in place in a circuit without the need for a separate programmer. A sequential network can easily be designed using a ROM and flip-flops. The combinational part of the sequential network can be realized using a ROM. The ROM can be used to realize the output functions and the next state functions. The state of the network can then be stored in a register of D flip-flops and fed back to the input of the ROM. Use of D flip flops is preferable to J-K flip flops, since use of 2 input flip flops would require increasing the number of outputs. The fact that the D flip flop input equations would generally require more gates than the J-K equations is of no consequence, since the size of the ROM depends only on the number of inputs and outputs and not on the complexity of the equations being realized. For this reason, the state assignment used is also of little importance, and generally a state assignment in straight binary order is as good as any. Implementation of BCD to Excess3 Converter using ROM for Combinational Logic Figure 3 shows the implementation of BCD to Excess-3 converter using combinational logic and D flip-flops

Fig 3 Realization of MEALY Sequential Network for BCD to Excess3 with gates and flipflops

64

We can realize the same sequential machine for a BCD to excess-3 code converter as shown in fig-3 using a ROM and three D flip flops, which is as shown in Fig.4. Table shown in figures 5 gives the truth table for the ROM, which implements the transition of fig.6 with the dont cares replaced by 0s. Since the ROM has four inputs, it contains 24 = 16 words. In general, a mealy sequential network with i inputs, j outputs and k state variables can be realized using k D flip-flops and a ROM with i+k inputs (2i+k words) and j+k outputs.

Fig 4 Realization of MEALY Sequential Network for BCD to Excess3 with a ROM

65

Fig 5 Truth Table of ROM

Fig 6 State table of BCD Excess

Reorganizing state table and arranging all the input values in increasing order from zero gets truth table of ROM. From the truth table we can see that, if ROM is in state S0 and if input X=0, then output Q1+ Q2+ Q3+ Z is 1001. This indicates that ROM is changing its state to S1 whose binary pattern is 100 which is equivalent to decimal value 4and the output Z is 1. Similarly if ROM is in state S0 and if input X=1 then output Q1+ Q2+ Q3+ Z is 1010 which indicates that ROM is changing its state to S2 whose binary pattern is 101 which is equivalent to decimal value 5 and output Z is 0. Proceeding in the same way we can draw the state diagram as shown in figure 7.

Fig 7 State Diagram for BCD to Excess-3

66

VHDL Code for BCD to Excess-3 Code Converter using ROM

library bitlib; use bitlib.bit_pack.all; entity rom1_2 is port (x , clk: in bit; z: out bit); end rom1_2; architecture rom1 of rom1_2 is signal q, qplus: bit_vector(1 to 3) := "000"; type rom is array (0 to 15) of bit_vector(3 downto 0); constant fsm_rom: rom := ("1001","1010","0000","0000","0001","0000","0000","0001", "1111","1100","1100","1101","0111","0100","0110","0111"); begin process(q,x) -- determines the next state and output variable romvalue: bit_vector(3 downto 0); begin romvalue := fsm_rom(vec2int(q & x)); -- read rom output qplus <= romvalue(3 downto 1); z <= romvalue(0); end process; process(clk) begin if clk='1' then q <= qplus; end if; -- update state register end process; end rom1;

67

The state register is represented by Q, which is a 3-bit vector (Q1 , Q2, Q3 )and the next state of this register is Qplus. In VHDL, a ROM can be represented by a constant one-dimensional array of bit vectors. In this example, a type statement is used to declare type ROM as an array of 16 words of 4 bit vectors. A constant declaration specifies the contents of the ROM named FSM_ROM. The input to the FSM_ROM is Q concatenated with X. Since the index of an array must be an integer, the vec2int function is called to convert Q&X to an integer. The variable ROMvalue is set equal to the ROM output, and then ROMValue is split into Qplus and Z. The state register Q is updated after the rising edge of the clock. Note: This program uses the user defined library bitlib for converting vector to integer, So this library should be created before this program is run. Procedure for creating this library is as explained in the presentation on packages and libraries.
Output waveform for BCD to excess-3 using ROM

From the waveform we can see that Q1Q2Q3X ARE 0000 output Qplus is 100 and is Z is 1 . for each positive edge trigger of the clock the Qplus which is the input to D f/f is transferred to Q. .since the first process in the program gets evaluated for change in X or Q this process also gets evaluated as soon as Q changes.. We can verify the waveform by seeing the state diagram given in figure 7.

68

Programmable Logic Array A programmable logic array (PLA) performs the same basic function as a ROM. It is the most flexible device in the family of PLDs. The internal organization of a PLA is different from that of the ROM. The decoder of the ROM is replaced with an AND array that realizes elected product terms of the input variables. The AND array is followed by an OR array which Ors together the product terms needed to form the output functions. Both the AND and the OR arrays are programmable giving a lot of flexibility for implementing logic design.

Fig 8 Internal Logic Diagram of a PLA Internally the PLA uses NOR-NOR logic but the added input and output inverting buffers make it equivalent to AND-OR logic. Logic gates are formed in the array by connecting NMOS switching transistors between the column line and row line. The transistors act as switches, so if the gate input is a logic zero, the transistor is turned off whereas if the gate input is a logic one, the transistor provides a conducting path to ground.

69

Fig 9 PLA with 3 inputs , 5 product terms and 4 outputs F0 = m(0,1,4,6) = AB+AC F1 = m(2,3,4,6,7) = B+AC F2 = m(0,1,2,6) =AB+BC F3 = m(2,3,5,6,7) =AC+B The above set of formulas are implemented using NOR-NOR logic of PLA as shown in Fig. 9 by placing the NMOS switching transistors wherever the connection has to be established. The same set of equations can be implemented using an AND-OR array equivalent as shown in Fig. 10 on the next page.

70

Fig 10 AND-OR array equivalent of Fig. 9 The contents of a PLA can be specified by a modified truth table as shown in Fig.11. The input side of the table specifies the product terms . The symbols 0,1 and indicate whether a variable is complemented, not complemented or not present in the corresponding product term. The output side of the table specifies which product terms appear in which output function. A 1 or 0 in the output terms indicate whether a given product term is present or not present in the corresponding output function.

fig 11 PLA Table for Fig. 10 The first row of the table indicates that the term AB is present in output functions F0 and F2. The second row indicates that AC is present in F0 and F1. This PLA table can be written directly using the given set of equations to be realized using PLA logic. Realization of a given function using min number of rows in the PLA F1 = m(2,3,5,7,8,9,10,11,13,15) .(1) F2 = m(2,3,5,6,7,10,11,14,15) . (2) F3 = m(6,7,8,9,13,14,15) . (3)

71

Fig 12 Multiple Output Karnaugh Map F1 = BD+BC+AB F2 = C+ABD F3 = BC+ABC+ABD .(4) .(5) .(6)

Equations 1,2 and 3 can be reduced to equations 4, 5 and 6 respectively using Karnaugh map as shown in Fig.12. If we implement these reduced equations 4,5 and 6 in a PLA then a total of 8 different product terms (including C) are required. So instead of minimizing each function separately, we have to minimize the total number of rows in the PLA table. When we are trying to design a logic using PLA, the number of terms in each equation is not important since the size of the PLA does not depend on the number of terms. The term ABC is already present in function F3. So we can use it in F1 instead of AB by writing AB as AB(C+C) . F1 can be now written as F1 = BD + BC + AB(C+C) = BD + BC + ABC + ABC = BD + BC (1+ A) + ABC = BD + BC + ABC This simplification of F1 eliminates the need to use a separate row for the original term AB present in F1 of equation (4). Since the terms ABD and ABD are needed in F2 and F3 respectively, we can replace the term BD in F1 with ABD + ABD. This eliminates the need for a row to implement the term BD in PLA. Similarly, since BC and BC are used in F1 and F3 respectively, w can replace C in F3 with BC + BC. Now the equations for F1, F2 and F3 with the above said changes can be written as : F1 = BD(A+A) + BC + AB(C+C) 72

= ABD + ABD + BC +ABC F2 = C(B+B) +ABD = BC + BC + ABD F3 = BC+ABC+ABD

.(7) .(8) .(9)

The current equations for F1, F2 and F3 as shown in equations 7,8 and 9 respectively have only 5 different product terms. So the PLA table can now be written with only 5 rows. This is a significant improvement over the equations 4, 5 and 6, which resulted in 8 product terms. The reduced PLA table corresponding to equations 7, 8 and 9 is as shown in the figure 13.

Fig 13 Reduced PLA table PLA table is significantly different from that of ROM truth table. In a truth table, each row represents a minterm ; therefore, one row will be exactly selected by each combination of input values. The 0s and 1s of the output portion of the selected row determine the corresponding output values. On the other hand, each row in a PLA table represents a general product term. Therefore, 0, 1 or more rows may be selected by each combination of input values. To determine the value of F for a given input combination, the values of F in the selected rows of the PLA table must be ORed together. For example, if abcd=0001 is given as input , no rows are selected as this combination does not exist in the PLA table; and all the F outputs are 0. If abcd = 1001, only the third row is selected resulting in F1F2F3 = 101. If abcd = 0111, the first and the fifth rows are selected. Therefore, to get the values for F1, F2 and F3 is got by ORing the respective values of F1, F2 and F3 in the corresponding rows resulting in F1 = 1 + 0 = 1, F2 = 1+1 = 1 and F3 = 0 + 1 = 1.

73

Fig 14 shows the PLA structure, which has four inputs, five product terms and three outputs as shown in equation 7,8 and 9. A dot at the intersection of word line and an input or output line indicates the presence of a switching element in the array. Implementation of BCD to Excess-3 using PLA

Fig 15 PLA Table for BCD to Excess-3 Converter

Fig16 Reduced Equations for BCD to excess-3 74

We can realize the sequential machine for BCD to excess 3 using a PLA and three D f/f. The network structure is same as that realized with ROM, except that the ROM is replaced by PLA. The required PLA table is as shown in figure 15 is derived from equations shown in figure 16 for BCD to excess-3. (Deriving these equations is chown in mealy sequential machine presentation) Reading the output of the PLA in VHDL is somewhat more difficult than reading the ROM output. Since the input to the PLA can match several rows, and the output from those rows must be ORed together. The realization of the equations shown in fig 16 is as shown in Fig 17. Fig : 17

Realization of BCD to Excess-3 using PLA.

75

VHDL CODE TO IMPLEMENT A BCD TO EXCESS-3 USING PLAs

VHDL CODE TO IMPLEMENT MVLIB USER DEFINED LIBRARY VHDL CODE TO IMPLEMENT A BCD TO EXCESS-3 USING PLAs

library ieee; use ieee.std_logic_1164.all; package mvl_pack is type plamtrx is array(integer range<>, integer range<>) of std_logic; function plaout(pla:plamtrx; input:std_logic_vector) return std_logic_vector ; end package mvl_pack; package body mvl_pack is function plaout(pla:plamtrx; input:std_logic_vector) return std_logic_vector is variable match:std_logic; variable placol, step: integer; variable plarow: std_logic_vector((plalength(2)-1) downto 0); variable plainp: std_logic_vector((inputlength-1) downto 0); variable output: std_logic_vector((plalength(2)-inputlength-1) downto 0); begin output := (others=> 0); if plaleft(2) > plaright (2) then step := -1; else step:= 1; end if; lp1: for row in plarange loop --scan each row of pla match := 1; placol:=plaleft(2); lp2: for col in plarowrange loop -- copy row of pla table plarow(col) := pla(row, placol); placol := placol+step; end loop lp2; plainp := plarow(plarowhigh downto plarowhigh-inputlength+1); lp3: for col in inputrange loop if input(col) /= plainp(col) and plainp(col) /= X then match :=0; exit; end if; end loop lp3; if match = 1 then output := output or plarow(outputrange); end if; end loop lp1; return output; end plaout; end package body;
76

library ieee; use ieee.std_logic_1164.all; library mvllib; use mvllib.mvl_pack.all;

-- ieee standard logic package -- includes plamtrx type and -- plaout function

entity pla1_2 is port ( x , clk : in std_logic; z: out std_logic); end pla1_2; architecture pla of pla1_2 is signal q, qplus : std_logic_vector ( 1 to 3) := "000"; constant fsm_pla : plamtrx (0 to 6 , 7 downto 0) := ("x0xx1000","1xxx0100","111x0010","1x000010", "00x10010","xx000001","xx110001"); begin process (q,x) variable plavalue : std_logic_vector(3 downto 0); begin plavalue := plaout(fsm_pla,q & x); -- read pla output qplus <= plavalue (3 downto 1); z <= plavalue(0); end process; process(clk) begin if clk='1' then q <= qplus; end if; end process; end pla;

-- update state register

The above program shows how the BCD to Excess-3 converter can be implemented using VHDL.code. This program uses plamtrx as an data type to hold two 77

dimensional; array of size 7X8 i.e. 7 rows and 8 columns as the PLA table has 7 rows and each row has eight values to be put in the table. This data type is declared in the package mvl_pack in mvlib library. A constant of this data type FSM_PLA is declred in the program to store the PLA table. function plaout is also defined in the library which accepts the PLA table as one parameter and the concatenated values of q and X as the other parameter. The binary pattern for q concatenated with X is searched in the table. If it finds a match for it in the input values part of the table the it extracts the output for the combination found from the table and ORs it with the value in the variable output. So this function reads each row of PLA table extracts the input part of the values and compares it with the passed inputs. If a match is found it ORs the outputs of the respective rows which have a match to the input. The value returned by the function has both Qplus and Z so their respective values are separated and assigned to Qplus and Z. .

78

Session X by Prof.K.R.Shoba:13.4.05 PROGRAMMABLE ARRAY LOGIC The PAL device is a special case of PLA which has a programmable AND array and a fixed OR array. The basic structure of Rom is same as PLA. It is cheap compared to PLA as only the AND array is programmable. It is also easy to program a PAL compared to PLA as only AND must be programmed. The figure 1 below shows a segment of an un programmed PAL. The input buffer with non inverted and inverted outputs is used, since each PAL must drive many AND Gates inputs. When the PAL is programmed, the fusible links (F1, F2, F3F8) are selectively blown to leave the desired connections to the AND Gate inputs. Connections to the AND Gate inputs in a PAL are represented by Xs, as shown here:

Input buffer

Figure 1: segment of an unprogrammed and programmed PAL. As an example, we will use the PAL segment of figure 1 to realize the function I1I2+I1I2. the Xs indicate that the I1 and I2 lines are connected to the first AND Gate, and the I1 and I2 lines are connected to the other Gate. Typical combinational PAL have 10 to 20 inputs and from 2 to 10 outputs with 2 to 8 AND gates driving each OR gate. PALs are also available which contain D flip-flops with inputs driven from the programming array logic. Such PAL provides a convenient way of realizing sequential networks. Figure 2 below shows a segment of a sequential PAL. The D flip-flop is driven from the OR gate, which is fed by two AND gates. The flip-flop output is fed back to the programmable AND array through a buffer. Thus the AND gate inputs can be connected to A, A, B, B, Q, or Q. The Xs on the diagram show the realization of the next-state equation. 79

Q+ = D = ABQ + ABQ The flip-flop output is connected to an inverting tristate buffer, which is enabled when EN =1

Figure 2 Segment of a Sequential PAL Figure 3 below shows a logic diagram for a typical sequential PAL, the 16R4. This PAL has an AND gate array with 16 input variables, and it has 4 D flip-flops. Each flip-flop output goes through a tristate-inverting buffer (output pins 14-17). One input (pin 11) is used to enable these buffers. The rising edge of a common clock (pin 1) causes the flip-flops to change the state. Each D flip-flop input is driven from an OR gate, and each OR gate is fed from 8 AND gates. The AND gate inputs can come from the external PAL inputs (pins2-9) or from the flip-flop outputs, which are fed back internally. In addition there are four input/output (i/o) terminals (pins 12,13,18 and 19), which can be used as either network outputs or as inputs to the AND gates. Thus each AND gate can have a maximum of 16 inputs (8 external inputs, 4 inputs fed back from the flip-flop outputs, and 4 inputs from the i/o terminals). When used as an output, each I/O terminal is driven from an inverting tristate buffer. Each of these buffers is fed from an OR gate and each OR gate is fed from 7 AND gates. An eighth AND gate is used to enable the buffer.

Figure 3: logic diagram for 16R4 pal 80

Fig 14PLA Realization of Equations 7, 8 and 9. When the 16R4 PAL is used to realize a sequential network, the I/O terminals are normally used for the z outputs. Thus, a single 16R4 with no additional logic could realize a sequential network with up to 8 inputs, 4 outputs, and 16 states. Each next state equation could contain up to 8 terms, and each output equation could contain up to 7 terms. As an example, we will realize the BCD to Excess-3 code converter using three flip-flops to store Q1,Q2 and Q3, and the array logic that drives these flip-flops is programmed to realize D1, D2 and D3, as shown in figure 3 .The Xs on the diagram indicate the connections to the AND-gate inputs. An X inside an AND gate indicates that the gate is not used. For D3, three AND gates are used, and the function realized is D3 = Q1Q2Q3 + XQ1Q3 + XQ1Q2 The flip-flop outputs are not used externally, so the output buffers are disabled. Since the Z output comes through the inverting buffer, the array logic must realize Z = (X + Q3)(X + Q3) = XQ3 + XQ3 The z output buffer is permanently enabled in this example, so there are no connections to the AND gate that drives the enable input, in which case the AND gate output is logic1. When designing with PALS, we must simplify our logic equations and try to fit them in one or more PALs. Unlike the more general PLA, the AND terms cannot be shared among two or more OR gates; therefore, each function to be realized can be simplified by itself without regard to common terms. For a given type of PAL the number of AND terms that feed each output OR gate is fixed and limited. If the number of AND terms in a simplified function is too large, we may be forced to choose a PAL with more OR-gate inputs and fewer outputs. 81

Computer aided design programs for PAL s are widely available. Such programs accept logic equations, truth tables, state graphs, or state tables as inputs and automatically generate the required fused patterns. These patterns can then be downloaded into a PLD programmer, which will blow the required, fuses and verify the operation of the PAL. Other sequential programmable logic devices (PLDs) The 16R4 is an example of a simple sequential PLD. As the integrated technology has improved, a wide variety of other PLDs have become available. Some of these are based on extensions of PAL concept, and others have based on gate arrays. The 22CEV10 is a CMOS electrically erasable PLD that can be used to realize both combinational and sequential networks. It has 12 dedicated input pins and 10 pins that can be programmed as either inputs or outputs. It contains 10 d flip-flops and 10 OR gates. The number of AND gates that feed each OR gate ranges from 8 to 16. Each OR gate drives an output logic macrocell. Each macro cell contains one of the 10 D flip-flops. The flip-flops have the common clock, a common asynchronous reset (AR) input and a common synchronous preset (SP) input. The name 22V10 indicates a versatile PAL with a total of 22 input and output pins, 10 of which are bi-directional I/O (input/output) pins.

Figure 4: block diagram for 22CEV10 Figure 4 shows the details of a 22CEV10 output macrocell. The connections to the output pins are controlled by programming the macrocell. The output mux controls inputs S1 and S0 select one of the data inputs. For example, S1S0 = 10 selects data input 2. each macrocell has two programmable interconnect bits. S1 or S0 is connected to ground (logic 0) 82

when the corresponding bit is programmed. Erasing a bit disconnects the control line (S1 or S0) from ground and allows it to float to Vcc (logic 1). When S1 = 1, the flip flop is bypassed, and the output is from the OR gate. The OR gate output is connected to the I/O pin through the multiplexer and the output buffer. The OR gate is also fed back so that it can be used as an input to the AND gate array. If S1 = 0, then the flip-flop output is connected to the output pin, and it is also fed back so that it can be used for AND gate inputs. when S0 = 1, the output is not inverted, so it is active high. When S0 = 0 ,the output is inverted, so it is active low. The output pin is driven from the tristate-inverting buffer. When the buffer output is in the high impedance state, the OR gates and the flip-flops are disconnected from the output pin, and the pin can be used as input. The dashed lines on figure 5 show the path when both S0 and S1 are 1. Note that in the first case the flip flop Q output is inverted by the output buffer, and in the second case the OR gate output is inverted twice so there is no net inversion.

Figure 5: internal diagram of macro cell of 22CEV10 For any further details regarding 22CEV10 please visit the site (http://www.anachip.com/downloads/datasheets/pld/PEEL22CV10AZ.pdf)s 83

Traffic light controller As an example of using 22v10, we design a sequential traffic-light controller for the intersection of A street and B street. Each street has traffic sensors, which detect the presence of vehicles approaching or stopped at the intersection. Sa =1 means a vehicle is approaching on A street, and Sb=1 means a vehicle is approaching on B street. A street is a main street and has a green light until the car approaches on B. Then the light changes and B has a green light. At the end of 50 seconds, the light changes back unless there is a car on B street and none on A, in which case the B cycle is extended to another 10 more seconds. When A is green, it remains green at least 60 seconds, and then the lights change only when the car approaches on B. Figure 6 shows the external connections to the controller. Three outputs (Ga, Ya, Ra) drives the green, yellow and red lights on the A street. The other three (Gb, Yb, Rb) drives the green, yellow and red lights on the B street. Figure 7 shows the Moore state graph for the controller. For timing purposes, the sequential network is driven by a clock with a 10 second period. Thus, a state change can occur at most once every 10 seconds. The following notation is used: GaRb in a state means that Ga=Rb=1 and all other output variables are 0. SaSb on an arc implies that Sa=0 and sb=1 will cause a transition along that arc. An arc without a label implies that a state transition will occur when clock occurs, independent of the input variables. Thus, the green A light will stay on for 6 clock cycles (60 seconds) and then changes to yellow if a car is waiting on B street.

Figure 6 : block diagram of traffic light controller

Figure 7: state diagram of traffic light controller

84

The vhdl code for the traffic light controller given below represents the state machine by two processes. Whenever the state , Sa, or Sb changes, the first processes updates the outputs and next state. Whenever the rising edge of the clock occurs, the second process updates the state register. The case statement illustrates use of a when clause with a range.since state s0 through s4 have the same outputs, and the next states are in numeric sequence, we use a when clause with a range instead of five separate when clauses: When 0 to 4 => Ga<= 1; Rb <= 1; nextstate <= state +1; For each state, only the signals that are 1 are listed within the case statement. Since in VHDL a signal will hold its value until it is changed, we should turn off each signal when the next state is reached. In state 6 we should set Ga to 0, in state 7 we should set Ya to 0, etc..This could be accomplished by inserting appropriate statements in the when clauses. for example, we could insert Ga < = 0 in the when 6 => clause. An easier way to turn off three outputs is to set them all to zero before the case statement. At first, it seems that a glitch might occur in the output when we set as signal to zero that should remain 1. however, this is not a problem because sequential statement within the process execute instantaneously. For example, suppose that the time = 20 a state change from S2 to S3 occurs. Ga and Rb are 1, but as soon as the process starts executing, the first liner of the code is executed and Ga and RB are scheduled to change to 0at time 20 + delta. The case statement then executes, and Ga and Rb are scheduled to change to 1 at time 20 + delta. Since this is the same time as before the new value (1) preempts the previously scheduled value zero and the signal never changes to 0. To make it easier to interpret the simulator output, we define a type name light with values R, Y, G and two signals lightA and lightB which can assume these values. Then we add code to set lightA to R when the light is red, to Y when the light is yellow and to G when the light is green. The test results after simulation are as shown in the figure 8.

Figure 8:test results for traffic controller

85

VHDL CODE FOR TRAFFIC CONTROLLER

library ieee; use ieee.std_logic_1164.all; entity traffic_light is port (clk, sa, sb: in bit; ra, rb, ga, gb, ya, yb: inout bit); end traffic_light; architecture behave of traffic_light is signal state, nextstate: integer range 0 to 12; type light is (r, y, g); signal lighta, lightb: light; begin process(state, sa, sb) begin ra <= '0'; rb <= '0'; ga <= '0'; gb <= '0'; ya <= '0'; yb <= '0'; case state is when 0 to 4 => ga <= '1'; rb <= '1'; nextstate <= state+1; when 5 => ga <= '1'; rb <= '1'; if sb = '1' then nextstate <= 6; end if; when 6 => ya <= '1'; rb <= '1'; nextstate <= 7; when 7 to 10 => ra <= '1'; gb <= '1'; nextstate <= state+1; when 11 => ra <= '1'; gb <= '1'; if (sa='1' or sb='0') then nextstate <= 12; end if; when 12 => ra <= '1'; yb <= '1'; nextstate <= 0; end case; end process; process(clk) begin if clk = '1' then state <= nextstate; end if; end process; lighta <= r when ra='1' else y when ya='1' else g when ga='1'; lightb <= r when rb='1' else y when yb='1' else g when gb='1'; end behave;

86

Figure 9: state table for traffic controller

Binary Value Assignment: S0 = 0000, S1 = 0001, S2 = 0010, S3 = 0011, S4 = 0100 S5 = 0101, S6 = 0110, S7 = 0111 , S8 = 1000, S9 = 1001 S10 = 1010, S11 = 1011, S12 = 1100 S13 = XXXX, S14 = XXXX, S15 = XXXX

Figure 10: excitation table

87

To get the excitation table from the state table of the traffic controller we replace all the state names with its equivalent binary values as shown in the figure 10. To get the equations for the O/Ps Ra, Ga, Ya, Rb, Gb, Yb. we have to draw a k-map for 4 variables Q1Q2Q3Q4. For the different combinations of these variables Q1Q2Q3Q4 the value of Ga is read from the state table and written into the K-map as shown in figure 12. For states S13 to S15 which are not present in the excitation table we can write the values for Ga as X inputs in the K-map (dont cares). We then group two 1s or four 1s or eight 1s and simplify the K-map as shown in figure 12 to get the simplified expression for Ga. Following the same procedure the equations for Ra, Ya, Rb, Gb, Yb can be obtained. The equations for D1, D2, D3 and D4 have six variables Q1, Q2, Q3, Q4, Sa and Sb. So for different combinations of Sa Sb. The four variable K-map with Q1Q2Q3and Q4 are solved the resulting four equations are ANDed with their respective Sa Sb values. These expressions are then Ored together to get the desired expressions for D1 D2 D3 D4. the detailed procedure for D1 is shown in the figure 11.

AT SaSb=00 D1= Q1Q2 + Q2Q3Q4

AT SaSb=01

D1= Q1Q2 + Q2Q3Q4

88

AT SaSb=10

D1= Q1Q2 + Q2Q3Q4 D1= Q1Q2 + Q2Q3Q4 at SaSb (i.e Sa=0 and Sb=0) Figure 11: D1= Q1Q2 + Q2Q3Q4 K-map for simplification of D1 Sb=1) at SaSb (i.e Sa=0 and D1= Q1Q2 + Q2Q3Q4 at SaSb (i.e Sa=1 and Sb=0) D1= Q1Q2 + Q2Q3Q4 at SaSb (i.e Sa=1 and Sb=1)

AT SaSb=11 D1= Q1Q2 + Q2Q3Q4

Solving for the first case i.e. D1

D1 = (Q1Q2 + Q2Q3Q4) SaSb +(Q1Q2 + Q2Q3Q4) SaSb + (Q1Q2 + Q2Q3Q4) SaSb + (Q1Q2 + Q2Q3Q4) SaSb
Simplifying further D1= (Q1Q2 + Q2Q3Q4) (SaSb + SaSb + SaSb + SaSb) D1 = (Q1Q2 + Q2Q3Q4) (Sa (Sb + Sb) + Sa(Sb + Sb) ) we know that Sa + Sa =1 and Sb + Sb =1 Thus we see the final value of D1 = Q1Q2 + Q2Q3Q4

Figure 12: K-map for simplification of Ga

Similarly the below equations can be solved and implemented using PAL.

Parallel Adder with Accumulator 89

The following is the implementation of a parallel binary adder that adds a n bit number B = bn.b2b1 to the accumulator A = an.a2a1 to give an an bit sum and carry. The full adder equations are given by equations 1-2 and 1-3. First, the number A must be loaded into the accumulator and then the number B is applied to the full adder inputs. After the carry has propagated through the adders, the sum appears at the adder outputs. Then a clock pulse (Clk) transfers the adder outputs to the accumulator. One way to load A into the accumulator is to first clear the accumulator using the clear inputs of the flip-flops and then put the A data on the B inputs and add.

Figure 1: Parallel adder with accumulator Now we will modify the design so that the addition occurs only when an add signal (Ad) is 1. One-way to do this is to gate the clock so that the flip flop clock inputs are clocked only when Ad = 1. A better way which does not require gating the clock , is to modify the accumulator flip-flop input equations to include Ad : ai+ = Si = Ad(ai bi ci + ai bi ci + ai bi ci + ai bi ci ) + Adai (1) so that ai does not change when Ad = 0. No change is required for ci+1 . How many bits of the adder and accumulator will fit into a 22V10 ? We must consider several limits. Let the number of flip-flops equal to F and the number of additional combinational functions equal C. Since the number of macrocells is 10, F+C <= 10 ..(2) Since there are 12 dedicated inputs and any of the unused macrocells can be used as inputs, the number of external inputs (I) is I <= 12 + [ 10 (F + C) ] = 22 F C ..(3) In addition we must make sure that the number of AND terms requird for the D flip-flop input functions and the combinational functions does not exceed the number of available AND gates. Each bit of the adder requires one flip-flop , and the D input to this flip-flop is Si . In addition we must generate the carry for each bit , which uses up another macrocell. The 90

ci+1 function must be fed back to the AND array through the macrocell even if an external carry output is not required. For an N-bit adder, F = C = N ; thus from Equation 3-6 , 2N<=10 . The number of inputs is N plus one each for the clock , clear , carry in (c1) and Ad signals ; thus from Equation 3 , I = N + 4 <= 22 2N ..(4) Solving these inequalities gives N < = 5. The operation of an adder implemented in this manner will be rather slow because of the time it takes for the carry to propagate through the five AND-OR sections of the 22V10. One way to speed up the operation of the adder and at the same time increase the number of bits that can be implemented in one 22V10 is to implement the adder in blocks of 2 bits at a time with no intermediate carry generated. The partial truth table and equations for such 2 bit adder are :

Figure 2: truth table and equations for a two bit adder Since the longest equation requires 13 AND terms and the maximum number of AND terms for the 22V10 is 16, these equations will easily fit in a 22V10. We can fit three 2-bit adders in a 22V10 since F + C = 6 + 3 = 9 <= 10 and I = 6 + 4 <= 22 9 . With this implementation, the carry must propagate through only three AND-OR sections, so this 6-bit adder is faster than the 5-bit adder previously designed. Design of a keypad scanner In this section, we design a scanner for a telephone keypad using a PLD. Figure3 shows a block diagram for the system. The keypad is wired in matrix form with a switch at the 91

intersection of each row and column. Pressing a key establishes a connection between a row and a column. The purpose of the scanner is to determine which key has been pressed and output the binary number N = N3 N2 N1 N0 , which corresponds to the key number. For example, pressing key 5 will output 0101, pressing the # key will output 1011. When a valid key has been detected, the scanner should output a signal V for one clock time. We will assume that only one key is pressed at time. Resistors to ground are connected to each row of the keyboard, so that R1 = R2 = R3 = R4 = 0 when no key is pressed.

Figure 3: block diagram of a keypad scanner We will use the following procedure to scan the keyboard : First apply logic 1s to columns C0 , C1 and C2 and wait. If any key is pressed , a 1 will appear on R0 , R1 , R2 or R3 . Then apply a 1 to column C0 only. If any of the Ris is 1, a valid key is detected, so set V = 1 and output the corresponding N. If no key is detected in the first column, apply a 1 to C 1 and repeat. If no key is detected in the second column, repeat for C2. When a valid key is detected, apply 1s to C0, C1 and C2 and wait until no key is pressed. This last step is necessary so that only one valid signal is generated each time a key is pressed. In the process of scanning the keyboard to determine which key is pressed, the scanner must take contact bounce into account. When a mechanical switch is closed or opened, the switch contact will bounce , causing noise in the switch output as shown in Figure. The contact may bounce for several milliseconds before it settles down to its final position. After a switch closure has been detected, we must wait for bounce to settle down before reading the key. The signal that indicates a key has been pressed also should be synchronized with the clock , since it will be used as an input to a synchronous sequential network

92

Figure 4 shows a proposed debouncing and synchronizing circuit. The clock period must be greater than the bounce time. If C0 = C1 = C2 =1 , when any key is pressed, K will become 1 after the bounce is settled . If the rising edge of the clock occurs during the bounce , either a 0 or 1 will be clocked into the flip-flop at t1. If a 0 was clocked in , a 1 will be clocked in at the next active clock edge (t2) . So it appears that QA will be debounced and synchronized version of K. However, a possibility of failure exists if K changes very close to the clock edge such that the setup or hold time is violated. In this case the flip-flop output Q A may oscillate or otherwise malfunction. Although this situation will occur very infrequently, it is best to guard against it by adding a second flip-flop. We will choose the clock period so that any oscillation at the output of QA will have died out before the next active edge of the clock so that the input DB will always be stable at the active clock edge . The debounced signal Kd will always be clean and synchronized with the clock, although it may be delayed up to two clock cycles after the key is pressed. We will divide the keypad scanner into three modules, as shown in fig 5 . The debounce module generates a signal K when a key has been pressed and a signal Kd after it has been debounced. The keyscan module generates the column signals to scan the keyboard. When a valid key is detected, the decoder determines the key number from the row and column numbers. Fig shows the keyscan state graph. Keyscan waits in S1 wit outputs C1 = C2 = C3 = 1 until a key is pressed. In S2, C0 = 1 , so if the key that was pressed is in column 0, K=1 ,and the network outputs a valid signal and goes to S5 . If no key press is found in Column 0, column 1 is checked in S3 , and if necessary , column 2 is checked in S4 . In S5 , the network waits until all keys are released and Kd goes to zero before resetting.

93

Figure 4: Debouncing and synchronizing circuit

Figure 5: Scanner modules

The decoder determines the key number from the row and column numbers using the truth table given in Table 7. The truth table has one row for each of the 12 keys. The remaining rows have dont care outputs since we have assumed that only one key is pressed at a time. Since the decoder is a combinational network, its output will change as the keypad is scanned. At the time a valid key is detected ( K=1 and V=1) , its output will have the correct value and this value can be saved in a register at the same time the network goes to S5.

Figure 6:state graph for scanner

94

Figure 7:truth table for decoder

We will try to implement the debounce, keyscan and decoder modules with a single 22V10 and as little hardware as possible. The 22V10 would require the following inputs : R0 , R1 , R2 , R3 , clock and reset. The outputs would be C0 , C1 , C2 , N0 , N1 , N2 , N3 and V. This uses up 8 out of the 10 macrocells. If the state graph was implemented using three flip-flops, 11 macrocells would be required and it would not fit. One solution would be to use two PALs and put the decoder in a separate PAL. A better solution is to use four flip-flops to implement the state graph and encode the states so that the outputs C0 , C1 , C2 can be read directly from the flip-flops Q2 , Q3 , Q4 . The following state assignment can be used for Q1Q2 Q3 Q4 : S1 0111 ; S2 0100 ; S3 0010 ; S4 0001 ; S5 1111 . The first three flip-flops produce the C outputs and flip-flop Q1 distinguishes between states S1 and S5. With this state encoding, a total of 9 macrocells are required to implement the keyscan and the decoder modules. This leaves one flip-flop available for debouncing, so that one external flip-flop is required for Kd. If the 22V10 is reset , the flip-flop states will be 0000, so we have added S 0 to the state graph with a next value of S1. The equation derived from the state graph by writing a state table and then replacing the states with their equivalent binary values to get the excitation table . the excitation table will be for six input variables Q1,Q2,Q3,Q4,K,Kd. And out variable is V. the processes to be followed to derive the equations fro Q1+,Q2+,Q3+,Q4+ is same as shown in mealy sequential machine presentation.

To avoid generating K, which would use up a macro cell, we substitute R0+R1+R2+R3 for K in the preceding equations. The resulting equation with the replaced values is Q1+ = Q1Kd+Q2Q3(R0+R1+R2+R3) + Q2Q3(R0+R1+R2+R3)+Q2Q4 The maximum number of terms in equation is 10, and all these equations, as well as the decoder equations, will easily fit in 22V10.so the expressions fro scanner and debouncer circuit are implemented using PAL. If key pressed is in column 0 or 1 the design works fine but a problem occurs when the key pressed is in column 2 as K would be forced to zero when column C0 and C1 are scanned , so Kd goes low when S5 is reached , even if the key is still pressed. To ovoid this problem, the next state equation for Qa IS Qa+ = k+ QaQ1

95

The added terms assures that once Qa is set to 1. it will remain 1 until S5 is reached and Q1 which indicates S5 state goes high. The revise circuit for debouncer is a s shown in Fig 4c. In the program the decoder equations for K and v are implemented using concurrent statements. The process implements the next state equations for the key scan and debounce flip-flops. It is very difficult to test the code by giving values to R0,R1,R2,R3, since the outputs depend on the column outputs(C1,C1,C2). A much better way to test the scanner is to write a test program where scanner program will be treated as an entity. Scantest program simulates a key press by supplying the appropriate R signals in response to the C signals from the scanner, When scantest receives V=1 from the scanner, it checks to see if the value of N corresponds to the key that was pressed.

library ieee; use ieee.std_logic_1164.all; entity scanner is port (r0,r1,r2,r3,clk: in bit; c0,c1,c2: inout bit; n0,n1,n2,n3,v: out bit); end scanner; architecture scan1 of scanner is signal q1,qa, k, kd: bit; alias q2: bit is c0; -- column outputs will be the same alias q3: bit is c1; -- as the state variables because VHDL assignment alias q4: bit is c2; -- of stateCODE FOR IMPLEMENTING A KEYPAD SCANNER begin k <= r0 or r1 or r2 or r3; -- this is the decoder section n3 <= (r2 and not c0) or (r3 and not c1); n2 <= r1 or (r2 and c0); n1 <= (r0 and not c0) or (not r2 and c2) or (not r1 and not r0 and c0); n0 <= (r1 and c1) or (not r1 and c2) or (not r3 and not r1 and not c1); v <= (q2 and not q3 and k) or (not q2 and q3 and k) or (not q2 and q4); process(clk) -- process to update flip-flops begin if clk = '1' then q1 <= (q1 and kd) or (q2 and not q3 and k) or (not q2 and q3 and k) or (not q2 and q4); q2 <= (not q2 and not q3) or k or q4; q3 <= not q3 or q1 or (q4 and not kd) or (not q2 and k); q4 <= not q2 or q1 or (q3 and not kd) or (not q3 and k); qa <= k or (qa and not q1); -- first debounce flip-flop kd <= qa; -- second debounce flip-flop end if; end process; end scan1;

96

library bitlib; use bitlib.bit_pack.all; entity scantest is end scantest; architecture test1 of scantest is component scanner port (r0,r1,r2,r3,clk: in bit; c0,c1,c2: inout bit; n0,n1,n2,n3,v: out bit); end component; type arr is array(0 to 11) of integer; -- array of keys to test constant karray:arr := (2,5,8,0,3,6,9,11,1,4,7,10); signal c0,c1,c2,v,clk,r0,r1,r2,r3: bit; -- interface signals signal n: bit_vector(3 downto 0); signal kn: integer; begin clk <= not clk after 20 ns; -- generate clock signal r0 <= '1' when (c0='1' and kn=1) or (c1='1' and kn=2) or (c2='1' and kn=3) else '0'; r1 <= '1' when (c0='1' and kn=4) or (c1='1' and for scantest Fig VHDL CODE FOR SCANTEST and kn=6) else '0'; 8: Interface kn=5) or (c2='1' r2 <= '1' when (c0='1' and kn=7) or (c1='1' and kn=8) or (c2='1' and kn=9) else '0'; r3 <= '1' when (c0='1' and kn=10) or (c1='1' and kn=0) or (c2='1' and kn=11) else '0'; process -- this section tests scanner begin for i in 0 to 11 loop -- test every number in key array kn <= karray(i); -- simulates keypress wait until (v='1' and rising_edge(clk)); assert (vec2int(n) = kn) -- check if output matches report "numbers don't match" severity error; kn <= 15; -- equivalent to no key pressed wait until rising_edge(clk); -- wait for scanner to reset wait until rising_edge(clk); wait until rising_edge(clk); end loop; report "test complete."; end process; scanner1: scanner -- connect test1 to scanner port map(r0,r1,r2,r3,clk,c0,c1,c2,n(0),n(1),n(2),n(3),v); end test1;

97

98

VHDL Models for Memories and Buses In this chapter we first describe the operation of a static AM memory and develop VHDL models that represent the operation and timing characteristics of that memory. Then we describe the operation of a microprocessor bus interface and develop a VHDL timing model for it. Finally we design an interface between memory and the microprocessor bus. We will use the VHDL memory and bus models to verify that the timing specifications for the memory and bus interface have been satisfied. Static Ram Memory Read memories were discussed in chapter 3, and now we discuss read-write memories. Such memories are usually referred to as RAMs. Ram stands for random access memory. Which means that any word memory can be accessed in the same amount of the time as any other word. Strictly speaking, ROM memories are also random access, but the term RAM is normally applied only to read write memories. Figure 1, shows the block diagram of a static RAM with n address lines. M data lines, abd three control lines. This memory can store each 2n words, each m bits wide. The data lines are bi-directional in order to reduce the required number of pins and the package size of the memory chip. When reading from the RAM, the data lines are output; when writing to the RAM, the data lines serve as inputs the three control lines function as follows: When asserted low, chip select selects the memory chip so that memory read and write operations are possible. When asserted low, output enable enables the memory output onto an external bus. When asserted low, write enable allows data to be written to the RAM. (We say that a signal is asserted when it is in its active state. An active-low signal is asserted when it is low, and an active-high signal is asserted when it is high.)

Figure 1

99

The term static RAM means that once data is stored in the RAM, the data remains there until the power is turned off. This is in contrast with a dynamic RAM, which requires that the memory be refreshed on a periodic basis to prevent data loss. A detailed discussion of dynamic RAMs is beyond the scope of this book, The RAM contains address decoders and a memory array. The address inputs to the RAM are decoded to select cells within the RAM. Figure 2 shows the functional equivalent of a static RAM cell that stores one bit of data. The cell wantains a transparent D latch, which stores the data. When SEL is asserted low and WR is high, G = 0, the cell is in the read mode, and Data Out = Q. When SEL> is asserted low and WR is high, G = 1 and data can enter the transparent latch. When either SEL and WR goes high, the data is stored in the latch. When SEL is high, Data Out is high-Z

Figure 2

Static RAMs are available that can store up to several million bytes of data. For purposes of illustration, we describe a 6116 static CMOS RAM that can store 2K bytes of data, but the principles illustrated here also apply to large static RAMs. Figure 3 shows the block diagram of a 6116 static RAM, which can store 2048 8-bit words of data This memory has 16,384 cells, arranged in a 128 x 128 memory matrix. The 11 address lines, which are needed to address the 211 bytes of data, are divided into two groups. Lines A 10 through A4 select one of the 128 rows in the matrix. Lines A3 through A0 select 8 columns in the matrix at a time, since there are 8 data lines. The data outputs from the matrix go through tristate buffers before connecting to the data I/O lines. These buffers are disabled except when reading from the memory. The truth table for the RAM describes its basic operation. High-Z in the I/O column means that the output buffers have high-Z outputs, and the data inputs are not used. In the read mode, the address lines are decoded to select eight of the memory cells, and the data comes out on the I/O lines after the memory access time has elapsed. In the write mode. Input data is routed to the latch inputs in the selected memory cells when WE is low, but writing to the latches in the memory cells is not completed until either WE goes high or the chip is deselected. The truth table does not take memory timing into account.

100

Figure 3

Memory timing diagrams and specifications must be consider when designing an interface to the memory. Figure 4(a) shows the read cycle timing for the case where CS and OE are both low before the address changes. In this case, after the address changes, the old data remains at the memory output for a time then there is transition period during which the data may change (as indicated by the cross-hatching). The new data is stable at the memory output after the address access time The address must be stable for the read cycle time. 101

Figure 4

Figure 4(b) shows the timing for the case where the OE is low and the address is stable before CS goes low. When CS is high, Dout is in the high-Z state, as indicated by a line halfway between 0 and 1 when CS goes low, Dout leaves high-z after time there is a transition period during which the data may change, and the new data is stable lat time after CS changes. Dout returns to High-Z at time after CS goes high. The timing parameters for CMOS static RAMs are defined in Table below for both read and write cycles. Specifications are given for the 6116-2 RAM, which has a 120-ns access time, and also for the 43258A-25 RAM, which has an access time of 25 ns. A dash in the table indicates that either the specification.

102

Figure 5 shows the write cycle timing for the case where OE is low during the entire cycle and where writing to memory is controlled by WE. In this case, it is assumed that CS goes low before or at the same time as WE goes low, and WE goes high before or at the same time as CS does. The cross-hatching on DS indicates the interval in which it can go from high to low (or from low to high). The address must be stable for the address setup time, t AS, before WE goes low After time tWJZ, the date out form the tristate buffers go the high Z state and input date may be placed on the I/O lines. The date into the memory must be stable for the setup time tDH. The address must be stable for tWR after WE goes high. When WE goes high, the memory switches back to the read mode. After tOW (min) and during region (a), Dout goes through a transition periods and then becomes the same as the date just stored in t eh memory. Further change in Dout may occur if the address changes or if CS goes high. To avoid bus conflicts during region (a), Din should either high-Z or the same as Dout.

103

Figure 5
Figure 6 shows the write cycle timing for the case where OE is low during the entire Cycle and where writing to memory is co0ntrolled by CS. In this case, it is assumed that WE goes low before or at the same time as CS goes low, and CS goes high before or at the same time as WE does. The address must be stable for the address setup time, t AS, before CS goes low. The data into the memory must be stable for the hold time tDH. The address must be stable for the address setup time, t AS before before CS goes low. The date into the memory must be stable for the setup time tDW before CS goes high, and then it must be kept stable for the hold time tDH. The address must be stable for tWR after CS goes high. Note that this write cycle is very similar to the WE-controlled cycle. In both cases, writing to memory occurs when both CS and WE are low, and writing is completed when either one goes high.

104

Figure 6

We now write a simple VHDL model for the memory that does not take timing consideration into account. Then we add timing information to obtain a more accurate model. And we write a process to check some of the more important timing specifications. We assume that OE is permanently tied low, so it will not appear in the models. We also assume that timing is such that writing to memory is controlled by We. To further simplify the model, we have reduced the number of address lines to 8 and the size of the memory to 256 bytes. We will model only the external behavior of the memory and make no attempt to model the internal behavior. In the VHDL code we use WE_b to represent WE (WE-bar). In Figure 7, the RAM memory array is represented by an array of standard logic vectors (RAM1). Since Address is typed as a bit-vector, it must be converted to an integer in order to index the memory array. The RAM process sets the I/O lines to high-Z if the chip is not selected. Otherwise, the data on the I/O lines is stored in RAM1 on the rising edge of We_b. If address and we_b change simultaneously, lthe old value of Address should be used. Address delayed is used as the array index to delay Address by one delta to make sure that the old address is used. The wait for 0 ns is needed so that the data will be stored in the RAM before it is read back out. If We_b = 0, the memory is in the write mode, and the I/O lines are driven to high-Z so external data can be supplied to the RAM.

105

Figure 7

To test the RAM model, we implement the system shown in Figure 8. This system has a memory address register (MAR) that holds the memory address and a data register to store data read from the memory. The system reads a word from the RAM, loads it into the data register, increments the data register, stores the result back in the RAM, and then increments the memory address. This process continues until the memory address equals 8. Required control signals are ld_data (load data register from Data Bus), en_data (enable data register output onto Data Bus), inc_data (increment Data Register), and inc_addr(increment MAR). Figure 9 Shows the SM chart for the system. The memory data is loaded in Data register during the transition to S1. Data Register is incremented during the transition to S2. WE is an active low signal, which is asserted low only I S2, so that WE is high in lthe other states. Thus, writing to the RAM is initiated in S2 and completed on the rising edge of WE, which occurs during the transition from S2 to S3.

106

Figure 8

107

Figure 9

Figure 10 shows the VHDL code for the RAM system. The first process represents the SM Chart, and the second process is used to update the registers on the rising edge of the clock. A short delay is added when the address is incremented to make sure the write to memory is completed before the address changes. A concurrent statement is used to simulate the tristate buffer, which enables the data register output onto the I/O lines.

108

Figure 10

109

Figure 11

110

111

Next, we revise the RAM model to include timing information based on the read and write cycles shown in Figures 4 , 5, and 6 we still assume that OE = 0. The VHDL RAM timing model in Figure 11 uses a generic declaration to define default value for the important timing parameters. Transport delays are used throughout to avoid cancellation problems, which can occur with inertial delays. The RAM process waits for a change in CS_b = 0, the RAM switches to write mode and the data output goes to high-Z 112

If a rising edge of CS_b has occurred, the RAM is deselected, and the data output goes to high-Z after the specified delay. Otherwise, if a falling edge of CS_b has occurred and WE_b is 1, the RAM is in the read mode. The data bus can leave the high-Z state after time t CLZ (min), but it is not guarantee to have valid data out until time t ACS (max), The region in between is a transitional region where the bus state is unknown, so we model this region by outputting X on the I/O lines. If an address change has jus occurred and the RAM is in lthe read mode (Figure 4(a)), the old data holds its value for time t OH. Then the output is in an unknown transitional state until valid data has been read from the RAM after time tAA The check process, which runs concurrently with the RAM process, tests to see if some of the memory timing specifications are satisfied. NOW is a predefined variable that equals the current time. To avoid false error messages, checking is not done when NOW 0 or when the chip is not selected. Whene the address changes, the process checks to see if the address has been stable for the write cycle time (twc+) and outputs a warning message if it is not. Since an address event has just occurred when this test is made, Address stable (t WC) would always return FALSE. Therefore, Address; delayed must be used instead of Address so that Address is delayed one delta and the stability test is made just before Address changes. Next the timing specifications for write are checked. First, we verify that the address has been stable for tAW Then we check to see that WE_b has been low for T WP Finally, we check the setup and hold times for the data.

Figure 12

113

Figure 13

VHDL code for a partial test of the RAM timing model is shown in Figure 12. This code runs a lwrite cycle followed by two read cycles. The Ram is deselected between cycles. Figure 13 shows the test results. We also tested the model for cases where simultaneous input changes occur and cases where timing specification are violated, but these test results are not included here.

114

A Simplified 486 Model Memories and input-output devices are usually interfaced to microprocessor by means of a tristate bus. To assure proper transfer of data on this bus, the timing characteristics of both the microprocessor bus interface and the memory must be carefully considered. We have already developed a VHDL timing model for a RAM memory, and next we will develop a timing model for microprocessor bus interface. Then we will simulate the operation of a system containing a microprocessor and memory to determine whether the timing specifications are satisfied. Figure 14 shows a typical bus interface to a memory system. The normal sequence of events for writing to memory is: (1) The microprocessor outputs an address on the address bus and asserts Ads (address strobe) to indicate a valid address on the bus; (2) the processor places data on the data bus and asserts W/R (write/read) to initiate writing the data to memory. The memory system asserts Rdyb (ready) to indicate that the data transfer is complete. For reading from memory, step (1) is the same, but in step (2) the memory places data on the data bus and these data are stored inside the processor when the memory system asserts Rdyb.

Figure 14 As an example, we develop a simplified model for a 486 microprocessor bus interface. The actual 486 bus interface is very complex and supports many different types of bus cycles. Figures 15 and 16 illustrate two of these bus cycles. In Figure 15, one word of data is transferred between the CPU and the bus every two clock cycles. These clock cycles are labeled T1 and T2, and they correspond to states of the internal bus controller. In addition, the bus has an idle state, Ti. During Ti and between data transfers on the bus, the data bus is in a high-impedance state (indicated on the diagram by DATA being halfway between 0 and 1 ). The bus remains in the idle state until the bus interface receives a bus request from the CPU. In T1, the interface outputs a new address on the bus and asserts Ads low. For a read 115

cycle, the read-write signal (W/R) os a;sp asserted low during T1 and T2. During T2 of the read cycle, the read cycle, the memory responds to the new address and places data on the data bus (labeled to CPU on the diagram). The memory system also asserts Rdyb low to indicate that valid data is on the bus. At the rising edge of the clock that ends T2, the bus interface senses that Rdyb is low and the data is stored inside the CPU.

Figure 15 The next bus cycle I Figure 15 is a write cycle. As before, the new address is output during T1 and Ads goes low, but W/ R remains high. During T2, the CPU places data on the bus. Near the end of T2, the memory system assert Rdyb low to indicate completion of the write cycle, and the data is stored in the memory at the end of T2 (rising edge of the clock). This is followed by another read and another write cycle. Figure 16 show 486 read and write bus cycles for the case where the memory is show and reading one word from memory or writing one word from memory or writing one word to memory requires three clock cycles. The read operation is similar to that in Figure 15, except at the end of the first T2 cycle, the bus interface senses that Rdyb is high, and another T2 cycle is inserted. At the end of Second T2 cycle( called wait state), Rdyb is low and read is completed. The write operation is similar.

116

Figure 16 We do not attempt to develop an accurate model for the 486 CPU. The internal bus interface in Figure 17 shows only those signals needed for transferring data between the bus interface unit and the CPU. If the CPU needs to write data to a memory attached to the external bus interface, it requests a write cycle by setting br (bus request) to 1 and wr = 0. If the CPU needs to read data, it requests a read cycle by setting br (bus request) to 1 and wr = 0. When the write or read cycle is complete, the bus interface unit returns done = 1 to the CPU.

Figure 17 117

The 486 bus interface unit contains a state machine to control the bus operation. Figure 18 is a modified SM chart that represents a simplified version of this state machine. In state Ti, the bus interface is idle, and the data bus is driven to high-Z. When a bus requires (br) is received from the CPU. The controller goes to state T1. In T`, the new address is driven onto the address bus, and Ads is set to 0 to indicate a valid address on the bus. The write-read signal (W/R) is set low for a read cycle or high for a write cycle, and the controller goes to state T2. In T2, Ads returns to 1. For a read cycle, wr = 0 and the controller waits for Rdyb = 0, which indicates valid data is available from the memory, and then std (store data) is asserted to indicate that the data should be stored in the CPU. For a write cycle, wr = 1 and data from the CPU is placed on the data bus. The controller then waits for Rdyb = 0 to indicate that the data has been stored in memory. For both read and write, the done signal is turned on when Rdyb = 0 to indicate completion of the bus cycle. After read or write is complete, the controller goes to Ti if no bus request is pending, otherwise it goes to state T1 to initiate another read or write cycle. The done signal remains on in Ti. The 486 processor bus is synchronous, so all timing specification are measured with respect to the rising edge of the clock. When the bus interface senses Rdyb, Rdyb must be stable for the setup time before the rising edge of the clock and for the hold time after the rising edge. These setup and hold times are designated as t16 and t17 in Figure 19. During a read cycle, the data to the CPU must also satisfy setup and hold time requirements the data must be stable t22 before and t23 after the active clock edge. When the output signals from the bus interface change, a delay occurs between the active clock edge and the time at which the signals are stable. When the address changes from one valid address to the next, after the clock edge, there is a minimum time (t 5 min) and a maximum time (t6 max) during which the address can change, as shown in Figure 20. The crosshatched region indicates the region in which the address can change. Similarly, when the data is changing from one value to the next, the change can occur any time between t10 min and t10 min and the change to high-Z occurs some time between t10min and t 10max When the data bus is changing from valid data to high-Z, the data remains valid for at least t 10 min and the change to high-Z occurs some time between t10min and t 11 after the clock edge. All the clock cycles in the figure are labeled Tx, but the address and data transitions can only occur between certain clock cycles. Address changes are always initiated by the rising clock edge at the transition between Ti and T1 or between T2 and T1, whereas write data transitions are initiated at the end of a T1 cycle. The transition from valid data out to high Z is initiated at the end of the write cycle at the transition from T2 to T1 or Ti.

118

Figure 18

Figure 19

119

Figure 20 The VHDL model for the 486 bus interface, based on the SM chart of Figure 18 and the block diagram of Figure 17, is given in Figure 21. The 486 comes in several different speeds, and the default values of the generic timing specifications are for the 50- MHz version of the 486. The bus state machine is modeled by two processes, and a third process is used to check some of the critical timing parameters. The bus state is initialized to Ti and dbus (data bus) is driven to high-Z after time t10min When the bus interface state machine enters state T1, ads_b (Ads) is driven low, w_rb (W/R) is driven to the appropriate value, and the address is driven onto the address bus (abus). In each case, the appropriate delay time is included. In state T2, for a write cycle (wr = 0) the data from dbus is sent to the CPU as r-data (read data), and std is asserted to indicate that the data should be stored in the CPU on the next rising edge of the clock. The check process checks to see that the setup times for reading data and for Rdyb_b are satisfied whenever the rising edge of the clock occurs. To avoid false error messages, checking is done only when the chip is selected and now # 0. The first assert statement checks the data setup time if rdy_b= 0 and reports and error if (now dbus event) < minimum setup time where now is the time at which the clock changed and dbus; event is the time at which the data changed. The process also checks to see if lthe read data hold time is satisfied whenever the data changes and reports an error if (now clock_last_rise) < minimum hold time 120

Where now is the time when the data changed, and clock_last_rise is the time at which the last rising edge of clk occurred. The process also checks to see if the rdy_b hold time is satisfied whenever rdy_b changes.

Figure 21

121

122

123

124

Interfacing Memory To a Microprocessor Bus In this section we discuss the timing aspects of interfacing memory to a microprocessor bus. In order to design the interface, the timing specifications fro both the memory and microprocessor must be satisfied. When writing to memory, the setup and hold time specifications for the memory must be satisfied, and when reading from memory, the setup and hold time specifications for the microprocessor bus must be satisfied. If the memory is slow, it may be necessary to insert wait states in the bus cycle. We design the interface between a 486 bus and a small static RAM memory, and then we write VHDL code to test the interface timing. We use the static RAM and 486 bus interface models that we have already developed, Figure 22 shows how the bus interface is connected to the static RAM memory. The memory consists of four static RAM chips, and each RAM chip contains 2 15 = 32,768 8-bit words. The four chips operate in parallel to give a memory that is 32 bits wide. Data bits 31-24 are connected to the first chip. Bits 23-16 to the second chip, etc. The lower 15 bits of the address bus are connected in parallel to all four chips. The system includes a memory controller that generates We and CS signals for the memory and returns a Rdyb signal to the bus interface to indicate completion of a read or write bus cycle. We will assign the address range 0 through 32, 767 to the static RAM. In general, a complete 486 system would have a large dynamic RAM memory and 1/O interface cards connected to the address and data busses. To allow for expansion to the system, we use an address decoder, so the static RAM is selected only for the specified address range. When an address in the range 0 to 32,767 is detected, the decoder outputs CSI = 1, which activates the memory controller.

125

Figure 22 Table shown earlier gives the timing characteristics of the 43258A-25 which is a fast static CMOS RAM, with a 25-ns access time. We use this RAM in our design so we can illustrate the insertion of wait states in the bus cycle. (If we use a faster RAM, we can eliminate the need fro wait states and run 2-2 bus cycles). In general, a detailed timing analysis is necessary in order to determine how many wait states (if any) must be inserted so that both the 486 bus and the RAM timing specifications are met. Figure 23 shows the signal paths for reading data from the RAM. We want to do a worst case analysis, so we must find the path with the longest delay. Sincet , the total propagation delay along the CS path will be longer than for a the address path. Also, since Ads and the address become stable at the same time, 126

the longest path includes the address decoder. Using the start of T1as a reference point, the delays along the longest path are Time from clock high to address valid = 12ns Propagation delay through address decoder = 5ns Propagation delay through memory controller = 5ns Memory access time = 25ns Data setup time for 486 bus = 5ns Total delay = 52ns Propagation delays of 5 ns were assumed for both the address decoder and memory controller. These values may have to be changed when to design of these components is completed. If the 486 is operated at its maximum clock rate of 50 MHz, the clock period is 20 ns. And three clock periods are required to complete a read cycle.

127

Figure 23 The SM chart in Figure 24 shows one possible design of the memory controller for read cycles. The controller waits in state S0 until Ads is asserted to indicate a valid address in bus state T1 and until CS1 is asserted by the address decoder to initiate that the address is in the range for the static RAM. Then CS is asserted to select the RAM, and the controller goes to S1. In S2, the controller also assert Rdyb to indicate that this is the last T2 the timing requirements will still be satisfied if CS is not asserted until S1, In this case, the propagation delays measured with respect to the end of T1 are : Propagation delay through memory controller = t = 5ns Memory access time = 25ns Data setup time for 486 bus = 5ns Total delay = 35ns Since the clock period is 20 ns, valid data is available at 2 x 20 ns 5 = 35 ns, which is 5 ns before the end of the second T2 state.

128

Figure 24 Next, we check the data setup time for writing to RAM. Using the start of T2 as a reference point, the worst-case delays along the data path are Time from clock high to data valid (486) = 12ns Data Valid to end of write (RAM) = 12ns Total time = 24ns Since the total time from the start of T2 to completion of write must be at least 24 ns, one T2 cycle (20 ns) is not sufficient, and a second T2 cycle is required. This means that three clocks (T1 and two T2s) are required to complete the write cycle. The data hold time for the 486 read cycle is t ns minimum. After Cs goes high, the data out of the RAM is valid for at least minimum, which is 0 ns. Therefore, t will be satisfied if is at least 3 ns, so CS goes high and the RAM is deselected at least 3 ns after the rising clock edge. The write to RAM can be controlled by either CS or We. Both Cs and We must go low, and the first one to go high completes the write operation. We will set We =(W/R) and use Cs to control the write cycle. Since CS must be low for at least 15 ns. This requirement is satisfied if CS goes low for one clock period. Since the memory data hold time is t ns, we must make sure that the data remains valid until CS goes high. The 486 bus spec indicates that the write data may go invalid as soon as 3 ns after the end of the last 2. This presents a problem is CS goes high at the end of the second T2, and the propagation delay in the memory controller is greater than 3 ns . In this case, as illustrated by Cs waveform (a) in Figure 25, CS goes high after the data to the RAM becomes invalid. One solution is to use father logic in the memory controller. Another solution is to have Cs go high at the end of the first T2, as shown by CS waveform (b). As long as ns, the data setup time will be satisfied.

129

Figure 25 The SM chart for a memory controller that meets the timing requirement for both read and write bus cycles is shown in figure 26. The write timing is based on Figure 25, CS (b). The VHDL code for the memory controller (Figure 27) is based on the SM chart and follows the standard pattern for a state machine, except that delays have been added to the output signals, CS, WE, and Rdyb

130

Figure 26

131

132

Figure 27

In order to test the 486 bus interface to the static RAM system, we write a tester module to take the place of the CPU in Figure 22 the tester needs to verify that the memory controller works correctly in conjunction with the 486 bus interface and the RAM memory. Since both the bus model and memory model have built-in timing checks, the tester needs to verify only that the system is fully functional, that data written to RAM is read back correctly, and that no bus conflicts occur. Each time a bus cycle. To make it easy to change the test data, we place the test data in a text file and have the tester read the test data from the file. Each line of the test file contains values for the following with each field separated by one more spaces. We have chosen integer format for the address and data, since integer fields are compact and easy to read using the standard TEXT1O routines The tester (Figure 28) interacts with the bus interface state machine (Figure 18). Since the bus interface senses Rdyb just before the end of

133

The T2 bus state, the tester should check Done just before the rising clock edge that ends T2. To facilitate this, we have defined a test clock ( testClk)with the tester. The bus clock (Clk) is the same as testclk , except that it is delayed by a short time, In this way, we can check the value of Done just after the rising edge of testclk, which occurs just before the rising edge of Clk. The process read_test_file waits for the rising edge of tesclk and then tests Done. If Done is I. In this case, the tester verifies that the data read from memory (r_data) is the same as the date previously read from the test file (dataint). Then the tester reads a line from the test file and reads the values of br, wr, data, and address form the read buffer. These values define the next bus cycle. If the next cycle is a write, the data from the test file is output as w_data. The value of br is checked by the bus state machine to determine if the next bus state should be Ti or T1.

134

Figure 28

135

136

The VHDL for the complete 486 bus system with static RAM is shown in Figure 29. This model uses the tester, 486 bus interface unit, memory controller, and static RAM as components and instanitiates these components within the architecture. A generate statement is used to instatiate four copies of the static RAM. In addition to the port map for the RAM, a generic map is used to specify the timing parameters for the 43258A-25 CMOS static RAM. Since our RAM model uses only 8 address lines, we have reduced the number of address lines form 15 to 8. The address decoder is implemented by a single concurrent statement.

Figure 29

137

138

Table below shows a data file that was used to test various sequences of bus cycles, including idle followed by read or write, two successive writes, two successive reads, read followed by write, and write followed by read. The last line test data contains an address that is outside of the RAM address range. When this bus cycle is executed, the memory controller should remain inactive, no Rdyb should be generated, and the 486 bus interface should continue to insert wait states until the simulation is terminated.

139

The simulator output is shown in Figure-30. The performance of the memory, 486 bus, and memory controller are as expected. Since r_data is represented by an iteger signal, r_data is 0 when Dbus is in the high-Z state. When interpreting the simulation result, we should keep in mind that the results are only as good as the models we have used. Although both the memory model and the 486 model are adequate for simulating these components, the models are not complete. Not all the timing specifications have been checked in the VHDL code. In many cases, only the maximum or minimum delay was selected to correspond to a worstcase condition. Under different conditions, the other limit on the delay may become significant. When simulating the memory controller, we used only a nominal delay. Before completing the memory controller delays that are acceptable and make sure that the design conforms to this requirement. In this chapter, we developed a simple VHDL model for a static RAM memory. Then we developed a more complex model, which included timing parameters and built-in-checks to verify that setup and hold times and other timing specifications are met. Next we developed a timing model for a microprocessor bus interface, including checks to verify that timing model for a microprocessor bus interface, including checks to verify that timing specifications are met. Then we interfaced the bus to static RAMs and designed a memory controller to meet the timing requirements. We simulated the entire system to verify that the timing specs were satisfied for both the memory and the bus the memory and the bus interface. In this example , we demonstrated the principles for designing an interface to meet worst-case timing specifications, and we demonstrated how to use VHDL to verify that the design is correct. 140

Figure 30

141

I Session by Prof.B.V.Uma:21.3.05 (a) VHDL VS software languages (b) Entity declaration (c) Types of Architecture (d) Behavioral Architecture example of Full adder and SR flipflop design. Introduction to VHDL VHDL stands for VHSIC (Very High Speed Integrated Circuits) Hardware Description Language. In the mid-1980s the U.S. Department of Defense and the IEEE sponsored the development of this hardware description language with the goal to develop very high-speed integrated circuit. It has become now one of industrys standard languages used to describe digital systems. The other widely used hardware description language is Verilog. Both are powerful languages that allow you to describe and simulate complex digital systems. A third HDL language is ABEL (Advanced Boolean Equation Language) which was specifically designed for Programmable Logic Devices (PLD). ABEL is less powerful than the other two languages and is less popular in industry VHDL versus conventional programming languages (1) A hardware description language is inherently parallel, i.e. commands, which correspond to logic gates, are executed (computed) in parallel, as soon as a new input arrives. (2) A HDL program mimics the behavior of a physical, usually digital, system. (3) It also allows incorporation of timing specifications (gate delays) as well as to describe a system as an interconnection of different components. Levels of representation and abstraction A digital system can be represented at different levels of abstraction [1]. This keeps the description and design of complex systems manageable. Figure 1 shows different levels of abstraction.

Figure 1: Levels of abstraction: Behavioral, Structural and Physical

142

The highest level of abstraction is the behavioral level that describes a system in terms of what it does (or how it behaves) rather than in terms of its components and interconnection between them. A behavioral description specifies the relationship between the input and output signals. This could be a Boolean expression or a more abstract description such as the Register Transfer or Algorithmic level. As an example, let us consider a simple circuit that warns car passengers when the door is open or the seatbelt is not used whenever the car key is inserted in the ignition lock At the behavioral level this could be expressed as, Warning = Ignition_on AND ( Door_open OR Seatbelt_off) The structural level, on the other hand, describes a system as a collection of gates and components that are interconnected to perform a desired function. A structural description could be compared to a schematic of interconnected logic gates. It is a representation that is usually closer to the physical realization of a system. For the example above, the structural representation is shown in Figure 2 below.

Figure 2: Structural representation of a buzzer circuit. VHDL allows to describe a digital system at the structural or the behavioral level. The behavioral level can be further divided into two kinds of styles: Data flow and Sequential. The dataflow representation describes how data moves through the system. This is typically done in terms of data flow between registers (Register Transfer level). The data flow model makes use of concurrent statements that are executed in parallel as soon as data arrives at the input. On the other hand, sequential statements are executed in the sequence that they are specified. VHDL allows both concurrent and sequential signal assignments that will determine the manner in which they are executed. Mixed level design consists both behavioral and structural design in one block diagram. Basic Structure of a VHDL file (a) Entity A digital system in VHDL consists of a design entity that can contain other entities that are then considered components of the top-level entity. Each entity is modeled by an entity declaration and an architecture body. One can consider the entity declaration as the interface to the outside world that defines the input and output signals, while the architecture body contains the description of the entity and is composed of interconnected entities, processes and components, all operating concurrently, as schematically shown in Figure 3 below. In a typical design there will be many such entities connected together to perform the desired function.

143

A VHDL entity consisting of an interface (entity declaration) and a body (architectural description). a. Entity Declaration The entity declaration defines the NAME of the entity and lists the input and output ports. The general form is as follows, entity NAME_OF_ENTITY is [ generic generic_declarations);] port (signal_names: mode type; signal_names: mode type; : signal_names: mode type); end [NAME_OF_ENTITY] ; An entity always starts with the keyword entity, followed by its name and the keyword is. Next are the port declarations using the keyword port. An entity declaration always ends with the keyword end, optionally [] followed by the name of the entity.

x y Ci Full Adder

S CO

Figure 3: Block diagram of Full Adder Example 1: entity FULLADDER is -- (After a double minus sign (-) the rest of -- the line is treated as a comment) --- Interface description of FULLADDER port ( x, y, Ci: in bit; S, CO: out bit); end FULLADDER; The module FULLADDER has five interface ports. Three of them are the input ports x, y and Ci indicated by the VHDL keyword in. The remaining two are the output ports S and CO indicated by out. The signals going through these ports are chosen to be of the type bit. The

144

type bit consists of the two characters '0' and '1' and represents the binary logic values of the signals. The NAME_OF_ENTITY is a user-selected identifier signal_names consists of a comma separated list of one or more user-selected identifiers that specify external interface signals. mode: is one of the reserved words to indicate the signal direction: in indicates that the signal is an input out indicates that the signal is an output of the entity whose value can only be read by other entities that use it. buffer indicates that the signal is an output of the entity whose value can be read inside the entitys architecture inout the signal can be an input or an output. type: a built-in or user-defined signal type. Examples of types are bit, bit_vector, Boolean, character, std_logic, and stc_ulogic. bit can have the value 0 and 1 bit_vector is a vector of bit values (e.g. bit_vector (0 to 7) std_logic, std_ulogic, std_logic_vector, std_ulogic_vector: can have 9 values to indicate the value and strength of a signal. Std_ulogic and std_logic are preferred over the bit or bit_vector types. boolean can have the value TRUE and FALSE integer can have a range of integer values real can have a range of real values character any printing character time to indicate time generic: generic declarations are optional

145

Example 2:

in1 in2 in3 AND Gate Out1

Figure 4: Block diagram of AND Gate entity AND3 is port (in1, in2, in3: in std_logic; out1: out std_logic); end AND3; The entity is called AND3 and has 3 input ports, in1, in2, in3 and one output port, out1 The name AND3 is an identifier. Inputs are denoted by the keyword in, and outputs by the keyword out. Since VHDL is a strongly typed language, each port has a defined type. In this case, we specified the std_logic type. This is the preferred type of digital signals. In contrast to the bit type that can only have the values 1 and 0, the std_logic and std_ulogic types can have nine values. This is important to describe a digital system accurately including the binary values 0 and 1, as well as the unknown value X, the uninitialized value U, - for dont care, Z for high impedance, and several symbols to indicate the signal strength (e.g. L for weak 0, H for weak 1, W for weak unknown - see section on Enumerated Types). The std_logic type is defined in the std_logic_1164 package of the IEEE library. The type defines the set of values an object can have. This has the advantage that it helps with the creation of models and helps reduce errors. For instance, if one tries to assign an illegal value to an object, the compiler will flag the error. Example 3:

146

S0

S1

I0 I1 I2 I3 4:1 MUX y

Figure 5: Block Diagram of 4:1 Multiplexer entity mux4_to_1 is port (I0,I1,I2,I3: in std_logic; S: in std_logic_vector(1downto 0); y: out std_logic); end mux4_to_1; Example 4: D Flip-Flop:

D CLK D FF

Q Qb

Figure 6: Block Diagram of D Flip Flop entity dff_sr is port (D,CLK,S,R: in std_logic; Q,Qb: out std_logic); end dff_sr; 147

Architecture body The architecture body specifies how the circuit operates and how it is implemented. As discussed earlier, an entity or circuit can be specified in a variety of ways, such as behavioral, structural (interconnected components), or a combination of the above. The architecture body looks as follows, architecture architecture_name of NAME_OF_ENTITY is -- Declarations -- components declarations -- signal declarations -- constant declarations -- function declarations -- procedure declarations -- type declarations : begin -- Statements : end architecture_name; The types of Architecture are: (a) The behavioral Model (b) Structure Model (c) Mixed Model (a) Behavioral model The architecture body for the example of Figure 2, described at the behavioral level, is given below, Example 1: architecture behavioral of BUZZER is begin WARNING <= (not DOOR and IGNITION) or (not SBELT and IGNITION); end behavioral; The header line of the architecture body defines the architecture name, e.g. behavioral, and associates it with the entity, BUZZER. The architecture name can be any legal identifier. The main body of the architecture starts with the keyword begin and gives the Boolean expression of the function. We will see later that a behavioral model can be described in several other ways. The <= symbol represents an assignment operator and assigns the 148

value of the expression on the right to the signal on the left. The architecture body ends with an end keyword followed by the architecture name. Example 2: The behavioral description of a 3 input AND gate is shown below. entity AND3 is port (in1, in2, in3: in std_logic; out1: out std_logic); end AND3; architecture behavioral_2 of AND3 is begin out1 <= in1 and in2 and in3; end behavioral_2; Example 3: entity XNOR2 is port (A, B: in std_logic; Z: out std_logic); end XNOR2; architecture behavioral_xnor of XNOR2 is -- signal declaration (of internal signals X, Y) signal X, Y: std_logic; begin X <= A and B; Y <= (not A) and (not B); Z <= X or Y; End behavioral_xnor; Example 4: SR Flip Flop: entity SRFF is port (S, R: in std_logic; Q, Qb: out std_logic); end SRFF; architecture behavioral_2 of SRFF is 149

begin Q <= NOT (S and Qb); Qb <= NOT ( R and Q); end behavioral_2; The statements in the body of the architecture make use of logic operators. In addition, other types of operators including relational, shift, arithmetic are allowed as well. Concurrency The signal assignments in the above examples are concurrent statements. This implies that the statements are executed when one or more of the signals on the right hand side change their value (i.e. an event occurs on one of the signals). In general, a change of the current value of a signal is called an event. For instance, when the input S (in SR FF) changes, the first expression gets evaluated, which changes the value of Q, change in Q in turn triggers second expression and evaluates Qb. Thus Q and Qb are updated concurrently. There may be a propagation delay associated with this change. Digital systems are basically data-driven and an event which occurs on one signal will lead to an event on another signal, etc. Hence, the execution of the statements is determined by the flow of signal values. As a result, the order in which these statements are given does not matter (i.e., moving the statement for the output Z ahead of that for X and Y does not change the outcome). This is in contrast to conventional, software programs that execute the statements in a sequential or procedural manner. Example 5 architecture CONCURRENT of FULLADDER is begin S <= x xor y xor Ci after 5 ns; CO <= (x and y) or (y and Ci) or (x and Ci) after 3 ns; end CONCURRENT; Two concurrent signal assignment statements describe the model of the entity FULLADDER. The symbol <= indicates the signal assignment. This means that the value on the right side of the symbol is calculated and subsequently assigned to the signal on the left side. A concurrent signal assignment is executed whenever the value of a signal in the expression on the right side changes. Due to the fact that all signals used in this example are declared as ports in the entity declaration the arch_declarative_part remains empty Event Scheduling: The mechanism of delaying the new value is called scheduling an event. In the above example, assignment to signals S and CO does not happen instantly. The after (keyword) clause delays the assignment of the new value to S and CO by 3 ns. Example2: architecture CONCURRENT_VERSION2 of FULLADDER is 150

signal PROD1, PROD2, PROD3 : bit; begin SUM <= A xor B xor C; -- statement 1 CARRY <= PROD1 or PROD2 or PROD3; -- statement 2 PROD1 <= A and B; -- statement 3 PROD2 <= B and C; -- statement 4 PROD3 <= A and C; -- statement 5 end CONCURRENT_VERSION2 Session II by Prof.B.V.Uma:23.3.05 (a)Concurrent statement: In VHDL With select and When else statements are called as concurrent statements and they do not require Process statement Example 1: VHDL code for 4:1 multiplexor library ieee; use ieee.std_logic_1164.all; entity Mux is port( I: S: y: end Mux; -- architecture using logic expression architecture behv1 of Mux is begin y<= (not(s(0)) and not(s(1)) and I(0)) or(s(0) and not(s(1)) and I(1)) or (not(s(0)) and s(1) and I(2))or (s(0) and s(1) and I(3)); end behv1; -- Architecture using when..else: architecture behv2 of Mux is begin y <= I(0) when S="00" else I(1) when S="01" else I(2) when S="10" else I(3) when S="11" else Z ; end behv2; 151 in std_logic_vector(3 downto 0); in std_logic_vector(1 downto 0); out std_logic);

-- architecture using with select statement architecture behv3 of Mux is begin with s select y<=i(0) when 00, i(1) when 01, i(2) when 10, i(3) when 11, Z when others; end behv3; Note: Z high impedence state should be entered in capital Z Example 2: SR flipflop using when else statement entity SRFF is port ( S, R: in bit; Q, QB: inout bit); end RSFF; architecture beh of RSFF is begin Q <= Q when S= 0 and R = 0 else 0 when S = 0 and R = 1 else 1 when S = 1 and R = 0 else Z; QB <= not(Q); end beh; The statement WHEN..ELSE conditions are executed one at a time in sequential order until the conditions of a statement are met. The first statement that matches the conditions required assigns the value to the target signal. The target signal for this example is the local signal Q. Depending on the values of signals S and R, the values Q,1,0 and Z are assigned to Q. If more than one statements conditions match, the first statement that matches does the assign, and the other matching state. In with select statement all the alternatives arte checked simultaneously to find a matching pattern. Therefore the with select must cover all possible values of the selector Structural Descriptions A description style where different components of an architecture and their interconnections are specified is known as a VHDL structural description. Initially, these components are 152

declared and then components' instances are generated or instantiated. At the same time, signals are mapped to the components' ports in order to connect them like wires in hardware. VHDL simulator handles component instantiations as concurrent assignments. Syntax: Component declaration: component component_name [generic (generic_list: type_name [:= expression] {; generic_list: type_name [:= expression]} );] [port (signal_list: in|out|inout|buffer type_name {; signal_list: in|out|inout|buffer type_name} );] end component; component instantiation: component_label: component name port map (signal_mapping); The mapping of ports to the connecting signals during the instantiation can be done through the positional notation. Alternatively, it may be done by using the named notation. If one of the ports has no signal connected to it (this happens, for example, when there are unused outputs), a reserved word open may be used. Example 1: signal_mapping: declaration_name => signal_name. entity RSFF is port ( SET, RESET: in bit; Q, QBAR: inout bit); end RSFF; architecture NETLIST of RSFF is component NAND2 port (A, B: in bit; C: out bit); end component; begin U1: NAND2 port map (SET, QBAR, Q); U2: NAND2 port map (Q, RESET, QBAR); end NETLIST; --- named notation instantiation: --U1: NAND2 port map (A => SET, C => Q, B => QBAR);

153

Figure 1: Schematic of SR FF using NAND Gate The lines between the first and the keyword begin are a component declaration. It describes the interface of the entity nand_gate that we would like to use as a component in (or part of) this design. Between the begin and end keywords, the statements define component instances. There is an important distinction between an entity, a component, and a component instance in VHDL. The entity describes a design interface, the component describes the interface of an entity that will be used as an instance (or a sub-block), and the component instance is a distinct copy of the component that has been connected to other parts and signals. In this example the component nand_gate has two inputs (A and B) and an output . There are two instances of the nand_gate component in this architecture corresponding to the two nand symbols in the schematic. The first instance refers to the top nand gate in the schematic and the statement is called the component instantiation statement. The first word of the component instantiation statement (u1:nand2) gives instance a name, u1, and specifies that it is an instance of the component nand_gate. The next words describes how the component is connected to the set of the design using the port map clause. The port map clause specifies what signals of the design should be connected to the interface of the component in the same order as they are listed in the component declaration. The interface is specified in order as A, B and then C, so this instance connects set to A, QBAR to B and Q to C. This corresponds to the way the top gate in the schematic is connected. The second instance, named n2, connects RESET to A, Q to A, and QBAR to C of a different instance of the same nand_gate component in the same manner as shown in the schematic. The structural description of a design is simply a textual description of a schematic. A list of components and there connections in any language is also called a netlist. The structural description of a design in VHDL is one of many means of specifying netlists. Example 2: Four Bit Adder Illustrating a structural VHDL model:

154

s(3)

s(2)

s(1)

s(0)

c(4) Full Adder

c(3)

Full Adder

c(2)

Full Adder

c(1)

Full Adder

ci

b(3) a(3)

b(2) a(2)

b(1) a(1)

b(0)

a(0)

Figure 2: 4-bit Adder using four Full Adders. -- Example of a four bit adder library ieee; use ieee.std_logic_1164.all; -- definition of a full adder entity FULLADDER is port (x, y, ci: in std_logic; s, co: out std_logic); end FULLADDER; architecture fulladder_behav of FULLADDER is begin s <= x xor y xor ci ; co <= (x and y) or (x and ci)or(y and ci)); end fulladder_behav; -- 4-bit adder library ieee; use ieee.std_logic_1164.all; entity FOURBITADD is port (a, b: in std_logic_vector(3 downto 0); Cin : in std_logic; sum: out std_logic_vector (3 downto 0); 155

Cout: out std_logic); end FOURBITADD; architecture fouradder_structure of FOURBITADD is signal c: std_logic_vector (4 downto 0); component FULLADDER port(x, y, ci: in std_logic; s, co: out std_logic); end component; begin FA0: FULLADDER port map (a(0), b(0), Cin, sum(0), c(1)); FA1: FULLADDER port map (a(1), b(1), C(1), sum(1), c(2)); FA2: FULLADDER port map (a(2), b(2), C(2), sum(2), c(3)); FA3: FULLADDER port map (a(3), b(3), C(3), sum(3), c(4)); Cout <= c(4); end fouradder_structure; We needed to define the internal signals c (4 downto 0) to indicate the nets that connect the output carry to the input carry of the next full adder. For the first input we used the input signal Cin. For the last carry we defined c (4) as an internal signal. We could not use the output signal Cout since VHDL does not allow the use of outputs as internal signals! For this reason we had to define the internal carry c(4) and assign c(4) to the output carry signal Cout. Generation of Instances Some repetitive structure descriptions, such as elements corresponding to the bus width, memory size, etc., benefit from the array-like arrangement of components. Descriptions of this type may be done with the generate statements, which allow: 1. repetition of structures corresponding to the for...loop. 2. selection of specific instantiations through the if...then conditions. Syntax: generate_label: for variable in range generate concurrent_statement general instantiations end generate [generate_label]; generate_label: if (condition) generate 156

concurrent_statement end generate [generate_label];

Figure 3: Shift Register using D FF Example: entity SHIFT is port ( SIN, CLK: in bit; SOUT: out bit); end SHIFT; --- iterative construction of a shift register --architecture stru1 of SHIFT is component DFF port (D, CLK: in bit; Q: out bit); end component; signal Z: bit_vector (0 to 4); begin Z(0) <= SIN; GF: for I in 0 to 3 generate UI: DFF port map (Z(I), CLK, Z(I+1)); end generate; SOUT <= Z(4); end stru1; --- separate handling of input and output --architecture stru2 of SHIFT is component DFF port (D, CLK: in bit; Q: out bit); end component; signal Z: bit_vector (1 to 3); begin GF: for I in 0 to 3 generate GI1: if (I = 0) generate U0: DFF port map (SIN, CLK, Z(I+1)); end generate; GI2: if ((I > 0) and (I < 3)) generate UI: DFF port map (Z(I), CLK, Z(I+1)); end generate; GI3: if (I = 3) generate U3: DFF port map (Z(I), CLK, SOUT); end generate; 157

end generate; end stru2; III Session by Prof.B.V.Uma:28.3.05 (a) Data Objects: Signals, Variables and constants (b) Process statement (c) Brief of Sequential statements (d) If then Else statement D flipflop, JK flipflop using If then Else statement (e) Case statement 4:1 MUX using Case statement Data Objects: Signals, Variables and Constants A data object is created by an object declaration and has a value and type associated with it. An object can be a Constant, Variable or a Signal. Signals can be considered wires in a schematic that can have a current value and future values, and that are a function of the signal assignment statements. Variables and Constants are used to model the behavior of a circuit and are used in processes, procedures and functions. Signal Signals are declared with the following statement: signal list_of_signal_names: type [ := initial value] ; signal SUM, CARRY: std_logic; signal CLOCK: bit; signal TRIGGER: integer :=0; signal DATA_BUS: bit_vector (0 to 7); signal VALUE: integer range 0 to 100; Signals are updated when their signal assignment statement is executed, after a certain delay, as illustrated below, SUM <= (A xor B); The result of A xor B is transferred to SUM after a delay called simulation Delta which is a infinitesimal small amount of time. One can also specify multiple waveforms using multiple events as illustrated below, signal wavefrm : std_logic; wavefrm <= 0, 1 after 5ns, 0 after 10ns, 1 after 20 ns; Constant A constant can have a single value of a given type and cannot be changed during the simulation. A constant is declared as follows, 158

constant list_of_name_of_constant: type [ := initial value] ; where the initial value is optional. Constants can be declared at the start of an architecture and can then be used anywhere within the architecture. Constants declared within a process can only be used inside that specific process. constant RISE_FALL_TME: time := 2 ns; constant DELAY1: time := 4 ns; constant RISE_TIME, FALL_TIME: time:= 1 ns; constant DATA_BUS: integer:= 16; Variable A variable can have a single value, as with a constant, but a variable can be updated using a variable assignment statement. (1) The variable is updated without any delay as soon as the statement is executed. (2) Variables must be declared inside a process. The variable declaration is as follows: variable list_of_variable_names: type [ := initial value] ; A few examples follow: variable CNTR_BIT: bit :=0; variable VAR1: boolean :=FALSE; variable SUM: integer range 0 to 256 :=16; variable STS_BIT: bit_vector (7 downto 0); The variable SUM, in the example above, is an integer that has a range from 0 to 256 with initial value of 16 at the start of the simulation. A variable can be updated using a variable assignment statement such as Variable_name := expression; Example of a process using Variables: architecture VAR of EXAMPLE is signal TRIGGER, RESULT: integer := 0; begin process variable x1: integer :=1; variable x2: integer :=2; variable x3: integer :=3; begin wait on TRIGGER; 159

x1 := x2; x2 := x1 + x3; x3 := x2; RESULT <= x1 + x2 + x3; end process; end VAR; Example of a process using Signals: architecture SIGN of EXAMPLE is signal TRIGGER, RESULT: integer := 0; signal s1: integer :=1; signal s2: integer :=2; signal s3: integer :=3; begin process begin wait on TRIGGER; s1 <= s2; s2 <= s1 + s3; s3 <= s2; RESULT <= s1 + s2 + s3; end process; end SIGN; In the first case, the variables x1, x2 and x3 are computed sequentially and their values updated instantaneously after the TRIGGER signal arrives. Next, the RESULT is computed using the new values of these variables. This results in the following values (after a time TRIGGER): x1 = 2, x2 = 5 (ie2+3), x3= 5. Since RESULT is a signal it will be computed at the time TRIGGER and updated at the time TRIGGER + Delta. Its value will be RESULT=12. On the other hand, in the second example, the signals will be computed at the time TRIGGER. All of these signals are computed at the same time, using the old values of s1, s2 and s3. All the signals will be updated at Delta time after the TRIGGER has arrived. Thus the signals will have these values: s1= 2, s2= 4 (ie 1(old value of s1) +3), s3=2(old value of s2) and RESULT=6 ie (1+2+3) Comparison between Signal and Variable: (1) Signal is updated after a certain delay when its signal assignment statement is executed. 160

The variable is updated as soon as the statement is executed without any delay. (2) Variables take less memory while signals need more memory as they need more information to allow for scheduling and signal attributes. (3) Signals are declared in entity or architecture using <= symbol where as variables are declared inside process or functions using := symbol. (4) Signals have attributes and variables do not have attributes. Sequential Statements There are several statements that may only be used in the body of a process. These statements are called sequential statements because they are executed sequentially. That is, one after the other as they appear in the design from the top of the process body to the bottom. Sequential behavioral descriptions are based on the process environment To ensure that simulation time can move forward every process must provide a means to get suspended. Thus, a process is constantly switching between the two states: the execution phase in which the process is active and the statements within this process are executed, and the suspended state. The change of state is controlled by two mutually exclusive implementations:

With a list of signals in such a manner that an event on one of these signals invokes a process. This can be compared with the mechanism used in conjunction with concurrent signal assignment statements. There, the statement is executed whenever a signal on the right side of the assignment operator <= changes its value. In case of a process, it becomes active by an event on one or more signal belonging to the sensitivity list. All statements between the keywords begin and end process are then executed sequentially. Syntax: process (sensitivity_list) [proc_declarativ_part] begin [sequential_statement_part] end process [proc_label]; The sensitivity_list is a list of signal names within round brackets, for example (A, B, C).

Process without sensitivity list must contain wait statement. With wait statements, the process is executed until it reaches a wait statement. At this instance it gets explicitly suspended. The statements within the process are handled like an endless loop which is suspended for some time by a wait statement. Syntax: process [proc_declarativ_part] begin [sequential_statements] 161

wait ...; -- at least one wait statement [sequential_statements] end process [proc_label]; The structure of a process statement is similar to the structure of an architecture. In the proc_declarativ_part various types, constants and variables can be declared; functions and procedures can be defined. The sequential_statement_part contains the description of the process functionality with ordered sequential statements. Sensitivity: The process statement can have an explicit sensitivity list. The list defines the signal that cause the statements inside the process statement to execute whenever one or more elements of the list change value. When the program flow reaches the last sequential statement, the process becomes suspended, until another event occurs on a signal that is sensitive. Following are the sequential statements: if-elsif-else statement: This branching statement is equivalent to the ones found in other programming languages Syntax: if condition then sequential_statements {elsif condition then sequential_statements} [else sequential_statements] end if; Example1: if statement(without else) and a common use of the VHDL attribute. count: process (x) variable cnt : integer :=0 ; begin if (x='1' and x'last_value='0') then cnt:=cnt+1; end if; end process; This if statement has two main parts, the condition and the statement body. A condition is any boolean expression (an expression that evaluates to TRUE and FALSE, such as expressions using relational operators). The condition in the example uses the attribute last_value, which is used to determine the last value that a signal had. Attributes can be used to obtain a lot of auxiliary information about signals. 162

The execution of the if statement begins by evaluating the condition. If the condition evaluates to the value TRUE then the statements in the statement body will be executed. Otherwise, execution will continue after the end if and the statement body of the if statement is skipped. Thus, the assignment statement in this example is executed every time there is a rising edge on the signal x, counting the number of rising edges. Example 2: D flip flop model library ieee ; use ieee.std_logic_1164.all; entity dff is port( data_in: in std_logic; clock: ); end dff; architecture behv of dff is begin process(clock) begin -- clock rising edge if (clock='1' and clock'event) then data_out <= data_in; end if; end process; end behv; clock='1' and clock'event This condition becomes true, when there is a event on the clock and clock state is equal to one i.e. rising edge of the clock. clock'event Event is an attribute on the signal to check whether any change in the signal is present. VHDL Code for nested if elsif in std_logic; data_out: out std_logic

163

T S1;S2;

C 1

C 2 T S5;S6;

S3;S4;

C 3

F S7;S8;

if (C1) then S1;S2; Elsif(C2) then S3;S4; Elsif(C3) then S5;S6; Else S7;S8; End if; Example 3: VHDL code for four bit comparator (example for ifelsif statement) library ieee; use ieee.std_logic_1164.all; entity compr4 is Port ( a : in std_logic_vector(3 downto 0); b : in std_logic_vector(3 downto 0); less : out std_logic; equ : out std_logic; grt : out std_logic); end compr4; architecture Behavioral of compr4 is begin process(a,b) begin less<='0';equ<='0'; grt<='0'; 164

if (a=b) then equ<='1'; elsif (a>b) then grt<='1'; else less<='1'; end if ; end process; end Behavioral; S

J Q CLK K JK FlipFlop Qb

R Example 4: VHDL code for JK Flip Flop (using nested elsif) (a) Using characteristic equations for J and K library ieee; use ieee.std_logic_1164.all; entity jkff is Port ( CLK, S, R, J, K, : in std_logic; Q, Qb : inout std_logic); end jkff; architecture jkff1 of jkff is Begin process(S,R,CLK) begin if (R=0) then Q<=0; elsif(S=0) then Q<=1; 165

elsif(CLK=0 and CLKevent) then Q<=(J and not Q) or (not K and Q); end if; End process; Qb<= not Q; End jkff1; (b) VHDL code for JK flip flop using truth table (Example for nested if statement) library ieee; use ieee.std_logic_1164.all; entity jkff is Port ( clk, pr, cr, j, k : in std_logic; q, qb : out std_logic); end jkff; architecture Behavioral of jkff is signal t: std_logic; begin process(clk,pr,cr) begin if(pr='0' and cr='1') then t<='1'; else if(pr='1' and cr='0') then t<='0'; else if (pr='1'and cr='1') then if(clk'event and clk='1') then if(j='0' and k='0') then t<=t; elsif(j='1' and k='0') then t<='1'; elsif(j='0' and k='1') then t<='0'; elsif(j='1' and k='1') then t<=not(t); end if; end if; end if; end if; end if; end process; q<=t; qb<=not(t); end Behavioral; case statement: 166

This statement is also identical to switch statement found in C programming language. Syntax: case expression is {when choices => sequential_statements} [when others => sequential_statements] end case; The case statement selects one of the branches of execution based on the value of expression. Choices may be expressed as single value or as a range of values. Either all possible values of expression must be covered with choices or the case statement has to be completed with an others branch. Example1: VHDL code for 4:1 MUX (using case statement) library ieee; use ieee.std_logic_1164.all; entity mux is Port ( i : in std_logic_vector(3 downto 0); s : in std_logic_vector(1 downto 0); y : out std_logic); end mux; architecture Behavioral of mux is begin process(s,i) begin case s is when "00"=> y<=i(0); when "01"=> y<=i(1); when "10"=> y<=i(2); when "11"=> y<=i(3); when others =>y<='Z'; end case ; end process; end Behavioral;

167

IV Session by Prof.B.V.Uma:30.3.05 (a) Loop statement examples (b) Brief on Next, Exit, Wait and Assert statements (c) VHDL data types (a) Loop statement examples Loop statement is a conventional loop structure found in other programming languages. Syntax for while loop: while condition loop | --controlled by condition Example: X<=1; sum<=1; While (x<10) loop sum <=sum*2; x<=x+1; end loop; Syntax for for loop: for identifier in value1 to|downto value2 loop | --with counter In the for loop the counter identifier is automatically declared. It is handled as a local variable within the loop statement. Assigning a value to identifier or reading it outside the loop is not possible. The for statement is used to execute a list of statements several times. Example 1: four bit parallel adder using for loop library ieee; use ieee.std_logic_1164.all; entity FOURBITADD is port (a, b: in std_logic_vector(3 downto 0); Cin : in std_logic; sum: out std_logic_vector (3 downto 0); Cout: out std_logic); end FOURBITADD; architecture fouradder_loop of FOURBITADD is signal ct: std_logic_vector(4 downto 0); begin process(a,b,cin) 168

begin ct(0)<= cin; for i in 0 to 3 loop s(i)<=a(i) xor b(i) xor ct(i); ct(i+1)<= (a(i) and b(i)) or (a(i) and ct(i)) or (b(i) and ct(i)); end loop; cout<= ct(4); end process; end fouradder_loop; Syntax for unconditional loop: loop sequential_statements exit when (condition); end loop [loop_label]; Exit statement allows the user to terminate the loop. (b) Brief on Next, Exit, Wait and Assert statements next and exit statement: With these two statements a loop iteration can be terminated before reaching the keyword end loop. With next the remaining sequential statements of the loop are skipped and the next iteration is started at the beginning of the loop. The exit directive skips the remaining statements and all remaining loop iterations. Syntax: next [loop_label][when condition]; exit [loop_label][when condition]; Example for next statement: Process (a, b) Begin for i in 0 to 15 loop if (i = 7 ) then next; else q(i)<=a(i) AND b(i); end if; 169

end loop; end process; The loop statement logically ands array of a and b bits. And transfers result to q. This behavior continues except for 7th element. When i=7 the execution starts from next iteration. The statements after next are not executed for current iteration. Example for Exit statement: Sum:=1; L3: Loop Sum:=sum*10; If sum>100 then Exit L3; End if; End loop L3; Exit statement provides termination for unconditional loop depending on condition. wait statement: This statements may only be used in processes without a sensitivity_list. The purpose of the wait statement is to control activation and suspension of the process. Syntax: wait on signal_names wait until condition wait for time_expression]; The arguments of the wait statement have the following interpretations:
1.

wait on signal_names: The process gets suspended at this line until there is an event on at least one signal in the list signal_names. The signal_names are separated by commas; brackets are not used. It can be compared to the sensitivity_list of the process statement. Example 1: D flip flop model using wait statement library ieee ; use ieee.std_logic_1164.all; entity dff is port(reset, clock: q: ); end dff; architecture behv of dff is 170 d: in std_logic; in std_logic; out std_logic

begin process begin --asynchronous reset input if (reset=0) then q<=0; -- clock rising edge elsif (clock='1' and clock'event) then q <= d; end if; wait on reset,clock; end process; end behv; The statements within the process body are executed only when there is an event on reset or event on clock.
2.

Wait until condition: The process gets suspended until the condition becomes true. Example (synchronous reset input) Process Begin Wait until clock=1 and clockevent If (reset=0) then Q<=0; Else q<=d; End if; End process; When the rising edge of the clock occurs, the Reset signal is tested first. If Reset is 0, d is assigned to q output.

3.

Wait for time expression: The process becomes suspended for the time specified by time expression. Process Begin A<=0; Wait for 5ns; A<=1; Wait for 5ns; End process; 171

In the above statement, it generates a clock for 5ns low state and 5ns high state.
4.

wait without any argument: The process gets suspended until the end of the simulation.

assertion statement: Generating error or warning messages is possible also within the process environment. Syntax: assert condition [report string_expr] [severity failure|error|warning|note]; Example: In JK or D Flip flop, if both asynchronous inputs Set and Reset are at logical 0 state, changes output q an qb both to be at 1 and 1 which is the violation of Boolean law. This condition can be verified by assert statement. Assert (Set=1 or Reset = 1) Report Set and Reset both are 0 Severity ERROR; If we wish to check D input has stabilized before the clock input changes, then assert statement can be used as shown. Assert (Clk = 1 and ClkEvent and DSTABLE(3 ns)) Report Setup time violation Severity WARNING; If the condition inside the assert statement is false, the statement outputs a user specified text string(that is written after report) to the standard console and the severity terminates the program compilation depending on severity level. The four levels of severity are: (1). Note (2) Warning (3) Error (4) Failure Similarly hold time of the D Flipfop is defined as the time after a clock edge for which data must be stable. Assert (Clk=1 and DEVENT and ClkSTABLE(5 ns)) Report Hold time violation Severity WARNING; The assert statement is passive, meaning that there is no signal assignment. (c) VHDL data types To define new type user must create a type declaration. A type declaration defines the name of the type and the range of the type. Type declarations are allowed in (i) Package declaration (ii) Entity Declaration (iii) Architecture Declaration (iv)Subprogram Declaration (v) Process Declaration 172

Data Types

File

Access

Scalar

Composite

Enumerated Enumerated Types:

Real

Integer

Physical

Array

Record

An Enumerated type is a very powerful tool for abstract modeling. All of the values of an enumerated type are user defined. These values can be identifiers or single character literals. An identifier is like a name, for examples: day, black, x Character literals are single characters enclosed in quotes, for example: x, I, o Type Fourval is (x, o, I, z); Type color is (red, yello, blue, green, orange); Type Instruction is (add, sub, lda, ldb, sta, stb, outa, xfr); Real type example: Type input level is range -10.0 to +10.0 Type probability is range 0.0 to 1.0; Type W_Day is (MON, TUE, WED, THU, FRI, SAT, SUN); type dollars is range 0 to 10; variable day: W_Day; variable Pkt_money:Dollars; Case Day is When TUE => pkt_money:=6; When MON OR WED=> Pkt_money:=2; When others => Pkt_money:=7; End case; Example for enumerated type - Simple Microprocessor model: Package instr is Type instruction is (add, sub, lda, ldb, sta, stb, outa, xfr); End instr; Use work.instr.all; 173

Entity mp is PORT (instr: in Instruction; Addr: in Integer; Data: inout integer); End mp; Architecture mp of mp is Begin Process (instr) type reg is array(0 to 255) of integer; variable a,b: integer; variable reg: reg; begin case instr is when lda => a:=data; when ldb => b:=data; when add => a:=a+b; when sub => a:=a-b; when sta => reg(addr) := a; when stb => reg(addr):= b; when outa => data := a; when xfr => a:=b; end case; end process; end mp; Physical types: These are used to represent real world physical qualities such as length, mass, time and current. Type _____ is range _____ to _____ Units identifier; {(identifier=physical literal;)} end units identifier; Examples: (1) Type resistance is range 0 to 1E9 174

units ohms; kohms = 1000ohms; Mohms = 1000kohms; end units; (2) Type current is range 0 to 1E9 units na; ua = 1000na; ma = 1000ua; a = 1000ma; end units; Composite Types: Composite types consist of array and record types. Array types are groups of elements of same type Record allow the grouping of elements of different types Arrays are used for modeling linear structures such as ROM, RAM Records are useful for modeling data packets, instruction etc. A composite type can have a value belonging to either a scalar type, composite type or an access type.

Array Type: Array type groups are one or more elements of the same type together as a single object. Each element of the array can be accessed by one or more array indices. Type data-bus is array (0to 31) of BIT; Variable x: data-bus; Variable y: bit; Y := x(0); Y := x(15); Type address_word is array(0 to 63) of BIT; Type data_word is array(7 downto 0) of std_logic; Type ROM is array(0 to 255) of data_word; We can declare array objects of type mentioned above as follows: 175

Variable ROM_data: ROM; Signal Address_bus: Address_word; Signal word: data_word; Elements of an array can be accessed by specifying the index values into the array. X<= Address_bus(25); transfers 26th element of array Address_bus to X. Y := ROM_data(10)(5); transfers the value of 5th element in 10th row. Multi dimentional array types may also be defined with two or more dimensions. The following example defines a two-dimensional array variable, which is a matrix of integers with four rows and three columns: Type matrix4x3 is array (1 to 4, 1 to 3) of integer; Variable matrixA: matrix4x3 := ((1,2,3), (4,5,6), (7,8,9), (10,11,12)); Variable m:integer; The viable matrixA, will be initialized to 1 2 3 4 5 6 7 8 9 10 11 12 The array element matrixA(3,2) references the element in the third row and second column, which has a value of 8. m := matrixA(3,2); m gets the value 8 Record Type: Record Types group objects of many types together as a single object. Each element of the record can be accessed by its field name. Record elements can include elements of any type including arrays and records. Elements of a record can be of the same type or different types. Example: Type optype is (add, sub, mpy, div, cmp); Type instruction is Record Opcode : optype; Src : integer; Dst : integer; End record; 176

COMPILATION AND SIMULATION OF VHDL CODE After designing a digital system in VHDL, simulation of the VHDL code is important for two reasons. (1) we need to verify the VHDL code correctly implements the intended design iefunctional verification. (2) we need to verify that the design meets its specifications such as speed,power area. Before the VHDL model of a digital system can be simulated, the VHDL code must first be compiled. The VHDL compiler, also called an analyzer, first checks the VHDL source code to see that it conforms to the syntax and semantic rules of VHDL. If there is a syntax error such as a missing semicolon, or if there is a semantic error such as trying to add two signals of incompatible types, the compiler will output an appropriate error message. The compiler also checks to see that references to libraries are correct. If the VHDL code confirms to all the rules, the compiler generates intermediate code, which can be used by a simulator or by a synthesizer.
Simulator Commands

Resource Libraries

VHDL Source Code

Compiler (Analyzer)

Intermediate Code

Working library

Simulation Elaborator Data Structure Simulator

Simulator Output

In preparation for simulation, the VHDL intermediate code must be converted to a form that can be used be the simulator. This step is referred to as Elaboration, during elaboration, ports are created for each instance of a component, memory storage is allocated for the required signals, the interconnections among the port signals are specified, and a mechanism is established for executing the VHDL processes in the proper sequence. The resulting data structure represents the digital system being simulated. After an initialization phase, the simulator enters the execution phase. The simulator accepts simulation commands, which control the simulation of the digital system and specify the desired simulator output. VHDL simulators use event driven simulation.

177

Session V by Prof.B.V.Uma:1.4.05 a) VHDL operators (b) Generics, block, Guard statements (c) VHDL model for Encoder, Decoder and counter (a) VHDL Operators VHDL supports different classes of operators that operate on signals, variables and constants. The different classes of operators are summarized below. Class 1. Logical operators 2. Relational operators 3. Shift operators 4.Addition operators 5. Unary operators 6. Multiplying op. 7. Miscellaneous op. and = sll + + * ** or /= srl = / abs mod not rem nand < sla & nor <= sra xor > rol xnor >= ror

The order of precedence is the highest for the operators of class 7, followed by class 6 with the lowest precedence for class 1. Unless parentheses are used, the operators with the highest precedence are applied first. Operators of the same class have the same precedence and are applied from left to right in an expression. As an example, consider the following std_ulogic_vectors, X (=010), Y(=10), and Z (10101). The expression not X & Y xor Z rol 1 is equivalent to ((not X) & Y) xor (Z rol 1) = ((101) & 10) xor (01011) =(10110) xor (01011) = 11101. The xor is executed on a bit-per-bit basis. 1. Logic operators The logic operators (and, or, nand, nor, xor and xnor) are defined for the bit, boolean, std_logic and std_ulogic types and their vectors. They are used to define Boolean logic expression or to perform bit-per-bit operations on arrays of bits. They give a result of the same type as the operand (Bit or Boolean). These operators can be applied to signals, variables and constants. Notice that the nand and nor operators are not associative. One should use parentheses in a sequence of nand or nor operators to prevent a syntax error: X nand Y nand Z will give a syntax error and should be written as (X nand Y) nand Z.

178

2. Relational operators The relational operators test the relative values of two scalar types and give as result a Boolean output of TRUE or FALSE. Operator = /= < <= > >= Description Equality Inequality Smaller than Smaller than or equal Greater than Greater than or equal Operand Types any type any type scalar or discrete array types scalar or discrete array types scalar or discrete array types scalar or discrete array types Result Type Boolean Boolean Boolean Boolean Boolean Boolean

Notice that symbol of the operator <= (smaller or equal to) is the same one as the assignment operator used to assign a value to a signal or variable. In the following examples the first <= symbol is the assignment operator. Some examples of relational operations are: variable STS constant A constant C : Boolean; : integer :=24; : integer :=14;

constant B_COUNT : integer :=32; STS <= (A < B_COUNT) ; -- will assign the value TRUE to STS STS <= ((A >= B_COUNT) or (A > C)); -- will result in TRUE STS <= (std_logic (1, 0, 1) < std_logic(0, 1,1));--makes STS FALSE type new_std_logic is (0, 1, Z, -); variable A1: new_std_logic :=1; variable A2: new_std_logic :=Z; STS <= (A1 < A2); will result in TRUE since 1 occurs to the left of Z. For discrete array types, the comparison is done on an element-per-element basis, starting from the left towards the right, as illustrated by the last two examples. 3. Shift operators 179

These operators perform a bit-wise shift or rotate operation on a one-dimensional array of elements of the type bit (or std_logic) or Boolean. Operator Description sll Shift left logical (fill right vacated bits with the 0) Operand Type Result Type Left: Any one-dimensional Same as left array type with elements of type type bit or Boolean; Right: integer same as above Same as left type Same as left type Same as left type Same as left type Same as left type

srl sla

Shift right logical (fill left vacated bits with 0)

Shift left arithmetic (fill right same as above vacated bits with rightmost bit) Shift right arithmetic (fill left same as above vacated bits with leftmost bit) Rotate left (circular) Rotate right (circular) same as above same as above

sra

rol ror

The operand is on the left of the operator and the number (integer) of shifts is on the right side of the operator. As an example, variable NUM1 NUM1 srl 2; will result in the number 00100101. When a negative integer is given, the opposite action occurs, i.e. a shift to the left will be a shift to the right. As an example NUM1 srl 2 would be equivalent to NUM1 sll 2 and give the result 01011000. Other examples of shift operations are for the bit_vector A = 101001 variable A: bit_vector :=101001; A sll 2 results in 100100 A srl 2 results in A sla 2 results in A sra 2 results in A rol 2 results in 001010 100111 111010 100110 180 :bit_vector := 10010110;

A ror 2 results in 4. Addition operators

011010

The addition operators are used to perform arithmetic operation (addition and subtraction) on operands of any numeric type. The concatenation (&) operator is used to concatenate two vectors together to make a longer one. In order to use these operators one has to specify the ieee.std_logic_unsigned.all or std_logic_arith package package in addition to the ieee.std_logic_1164 package. Operator Description + & Addition Subtraction Left Operand Type Numeric type Numeric type Right Operand Type Same as left operand Same as left operand Same as left operand Result Type Same type Same type Same array type

Concatenation Array or element type

An example of concatenation is the grouping of signals into a single bus [4]. signal MYBUS signal STATUS signal RW, CS1, CS2 signal MDATA Other examples are MYARRAY (15 downto 0) <= 1111_1111 & MDATA (2 to 9); NEWWORD <= VHDL & 93; The first example results in filling up the first 8 leftmost bits of MYARRAY with 1s and the rest with the 8 rightmost bits of MDATA. The last example results in an array of characters VHDL93. Example: Signal a: std_logic_vector (3 downto 0); Signal b: std_logic_vector (3 downto 0); Signal y:std_logic_vector (7 downto 0); Y<=a & b; :std_logic_vector (15 downto 0); :std_logic_vector (2 downto 0); :std_logic; :std_logic_vector ( 0 to 9);

MYBUS <= STATUS & RW & CS1 & CS2 & MDATA;

181

5. Unary operators The unary operators + and - are used to specify the sign of a numeric type. Operator + Description Identity Negation Operand Type Any numeric type Any numeric type Result Type Same type Same type

6. Multiplying operators The multiplying operators are used to perform mathematical functions on numeric types (integer or floating point). Operator Description Left Operand Type Any integer or floating point Any physical type Any integer or real type / Division Any integer or floating point Any physical type Any physical type mod rem Modulus Remainder Any integer type Any integer type Right Operand Result Type Type Same type Integer or real type Any physical type Any integer or floating point Any integer or real t ype Same type Same type Same as left Same as right Same type Same as left Integer Same type Same type

Multiplication

The multiplication operator is also defined when one of the operands is a physical type and the other an integer or real type. The remainder (rem) and modulus (mod) are defined as follows: A rem B = A (A/B)*B A mod B = A B * N (in which A/B in an integer) (in which N is an integer)

The result of the rem operator has the sign of its first operand while the result of the mod operators has the sign of the second operand. Some examples of these operators are given below. 182

11 rem 4 (-11) rem 4 9 mod 4 7 mod (-4) 7. Miscellaneous operators

results in 3 results in -3 results in 1 results in 1 (7 4*2 = -1).

These are the absolute value and exponentation operators that can be applied to numeric types. The logical negation (not) results in the inverse polarity but the same type. Operator ** abs not Description Exponentiation Absolute value Logical negation Left Operand Type Integer type Floating point Any numeric type Any bit or Boolean type Right Operand Result Type Type Integer type Integer type Same as left Same as left Same type Same type

(b) Generics, block, Guard statements Generics Generics are a general mechanism used to pass information to an instance entity. The data passed to an instance is static data. After the model has been linked to simulator, the data does not change during simulation. The information contained in generics passed into a component instance or a block, can be used to alter simulation results, but results can not modify generics. Generics can not be assigned information as part of a simulation run. Generics are a means of communicating non hardware and non signal information between designs.

Example: Entity test_Gen is Generic (nise, fall: Time); Port(a,b: in std_logic; c: out std_logic); End test; Architecture test_arch of test_Gen is Component AND2 Generic (rise, fall: Time); 183

Port(a,b: in std_logic; c: out std_logic); End component; Begin U1: AND2 Generic Map (10ns, 12ns); Port map (ia, ib, oc); End test_arch; Example: entity nand2 is generic (tplh: Time := 6ns; tphl: Time := 4ns); port(i1, i2: in BIT; 01: out BIT); end nand2; architecture avge_delay of nand2 is begin 01<=i1 NAND i2 After (tplh + tphl); end avge_delay; Block Statements: Blocks are a partitioning mechanism within VHDL that allow the designer to logically group areas of model. The statement area in an architecture can be broken into a number of separate logical areas. For example, in design of CPU, one block for ALU, another for register, another for shifter. Each block contains a defined area of model. Example: Entity cpu is Port(clk, interrupt: in std_logic; Addr: out tw32; data: inout tw32); End cpu; architecture cpu_blk of cpu is signal ibus, dbus: tw32; begin ALU: block; Signal qbus: tw32; Begin 184

--- ALU behaviour statements. End BLOCK ALU; REG8: BLOCK Signal zbus : tw32; Begin Reg1: block Signal qbus: tw32; Begin --- Reg1 behaviour statements. End block Reg1; --- REG8 statements end block REG8; end cpu_blk; Signal ibus, dbus are local to architecture cpu_blk and can not be referenced outside of the architecture. Any block inside of the architecture can reference these signals. Any lower level block can reference signals for a level above, but upper level blocks can not reference lower level local signals. Signal qbus is declared in the block ALU. This signal is local to block ALU, and can not be referenced outside of the block ALU. i.e. All statements of ALU block can reference qbus but statements outside of block ALU cannot use qbus. The signal qbus is declared in the blocks REG1 and ALU. To the compiler these two signals are separate.

Guarded blocks: A guarded block contains a guard expression that can enable and disable drivers inside the block. The guard expression is a Boolean expression when true, drivers contained in the block are enabled, and when false, the drivers are disabled.

Example (1): Entity latch is Port (d, clk: in std_logic; q, qb: out std_logic); end latch; 185

architecture latch_guard of latch is begin G1: Block(clk = 1) Begin q <= guarded d after 5ns; qb<=guarded NOT(d) after 7ns; end block G1; end latch_guard; The guard expression in this example is (clk=1). When clk=1, this return true and false when clk=0. When the guard expression is true all of the drivers of guarded signal assignment statements are enabled or turned ON. When the guard expression is false all of the drivers of guarded signal assignment statements are disabled or turned OFF. Signal assignment can be guarded by using the keyword guarded Guarded block statements allow the capability of turning off drivers within a block. (c) VHDL model for Encoder, Decoder and counter (i)VHDL code to implement the functionality of 8:3 Encoder without Priority library ieee; use ieee.std_logic_1164.all; entity penc83 is Port ( d : in std_logic_vector(7 downto 0); b : out std_logic_vector(2 downto 0)); end penc83; architecture Behavioral of penc83 is begin process(d) begin case d is when "00000001" => b <= "000"; when "00000010" => b <= "001"; when "00000100" => b <= "010"; when "00001000" => b <= "011"; when "00010000" => b <= "100"; 186

when "00100000" => b <= "101"; when "01000000" => b <= "110"; when "10000000" => b <= "111"; when others=>NULL; end case; end process; end Behavioral; (ii) VHDL code to implement the functionality of 8:3 Encoder with Priority library ieee; use ieee.std_logic_1164.all; entity penc83 is Port ( d : in std_logic_vector(7 downto 0); b : out std_logic_vector(2 downto 0)); end penc83; architecture Behavioral of penc83 is begin process(d) begin case d is when "00000001" => b <= "000"; when "0000001X" => b <= "001"; when "000001XX" => b <= "010"; when "00001XXX" => b <= "011"; when "0001XXXX" => b <= "100"; when "001XXXXX" => b <= "101"; when "01XXXXXX" => b <= "110"; when "1XXXXXXX" => b <= "111"; when others=>NULL; end case; end process; end Behavioral; (iii) VHDL code to implement the functionality of 2:4 decoder with active low enable input. library ieee; 187

use ieee.std_logic_1164.all; entity DEC24 is Port ( i : in std_logic_vector(1 downto 0); e: in std_logic; y : out std_logic_vector(3 downto 0)); end DEC24; architecture Behavioral of DEC24 is begin process(i,e) begin if (e=1) then y<=0000; else case i is when "00"=>y<="0001"; when "01"=>y<="0010"; when "10"=>y<="0100"; when "11"=>y<="1000"; when others=>NULL; end case; end if; end process; end Behavioral; (iv) 2:4 Decoder using when..else statement library ieee; use ieee.std_logic_1164.all; entity DECODER is port( ); end DECODER; architecture when_else of DECODER is begin O <= "0001" when I = "00" else 188 I: O: in std_logic_vector(1 downto 0); out std_logic_vector(3 downto 0)

"0010" when I = "01" else "0100" when I = "10" else "1000" when I = "11" else "XXXX"; end when_else; (v) n-bit Shift Register with parallel load input library ieee ; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; entity reg is generic(n: natural :=2); port( I: clock: load: clear: Q: ); end reg; architecture behv of reg is signal Q_tmp: std_logic_vector(n-1 downto 0); begin process(I, clock, load, clear) begin if clear = '0' then -- use 'range in signal assigment Q_tmp <= (Q_tmp'range => '0'); elsif (clock='1' and clock'event) then if load = '1' then Q_tmp <= I; end if; end if; 189 in std_logic_vector(n-1 downto 0); in std_logic; in std_logic; in std_logic; out std_logic_vector(n-1 downto 0)

end process; -- concurrent statement Q <= Q_tmp; end behv; (vi) VHDL code for n-bit counter library ieee ; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; entity counter is generic(n: natural :=8); port( clock: clear: count: Q: ); end counter; architecture behv of counter is signal Pre_Q: std_logic_vector(n-1 downto 0); begin -- behavior describe the counter process(clock, count, clear) begin if clear = '1' then Pre_Q <= Pre_Q - Pre_Q; elsif (clock='1' and clock'event) then if count = '1' then Pre_Q <= Pre_Q + 1; end if; end if; end process; 190 in std_logic; in std_logic; in std_logic; out std_logic_vector(n-1 downto 0)

Q <= Pre_Q; end behv; SessionVI by Prof.B.V.Uma:21.3.05 Attributes Predefined Attributes have number of applications such as detection of edges, perform timing checks and determine range in arrays and so on. Attributes can be applied to arrays, types, and signals. Broad classification of attributes is 1) Array attributes 2) Type attributes 3) Signal attributes Array attributes Array attributes are used to find the range, length or boundaries of arrays. Type ROM is array (0 to 15, 7 downto 0) of bit; Signal ROM1: ROM; Attribute ALEFT(N) ARIGHT(N) AHIGH(N) ALOW(N) ARANGE(N) Returns Left bound of Nth index range RIGHT bound of Nth index range Largest bound of Nth index range Smallest bound of Nth index range Nth index range reversed Examples ROM1 LEFT(1) = 0 ROM1 LEFT(2) = 7 ROM1 RIGHT(1) = 15 ROM1 RIGHT(2) = 0 ROM1 HIGH(1) = 15 ROM1 HIGH(2) = 7 ROM1 LOW(1) = 0 ROM1 LOW(2) = 0 ROM1 RANGE(1)= 0 to 15 ROM1 RANGE(2)=7 downto AREVERSE_RANGE(N) Nth index range 0 ROM1 REVERSE_RANGE(1) = 15 downto 0 ROM1 REVERSE_RANGE(2) = 0 to ALENGTH(N) Size of Nth index range 7 ROM1 LENGTH(1) = 16 191

True if ascending A ASCENDING

ROM1 LENGTH(2) = 8 ROM1 ASENDING(1) returns True ROM1 ASENDING(2) returns false

Type Attributes: Type attributes are used for accessing elements of defined Types and are valid for non array types. Type ncolor is (red, yellow, blue, white, black, green, brown); Signal color: ncolor Attributes LEFT RIGHT HIGH LOW POS(V) VAL(P) SUCC(V) PRED(V) LEFTOF IMAGE(V) VALUE(S) Signal Attributes: Attribute EVENT T/E EV Example S1EVENT Kind VALUE Type BOOLEAN Description Left bound OF type RIGHT bound of type Largest bound type Smallest bound type Position of value V in type Value at Position of V in type Value after V in type Value before V in type Value left to (before) V in type Converts value V of type to string Converts string S of type to value Example Color LEFT ColorRIGHT ColorHIGH ColorLOW ColorPOS(blue) ColorVAL(4) ColorSUCC(blue) ColorPRED(green) ColorLEFTOF(green) ColorIMAGE(red) ColorVALUE(red) Result Returns red Returns brown Returns brown Returns red Returns 2 Returns black Returns white Returns black Returns black Returns red Returns red

Attribute description for the specified example In a simulation cycle, if s1 changes, this attribute becomes TRUE. 192

LAST_EVENT

EV

s1LAST_VALUE

VALUE

TIME

The amount of time since the last value change on s1. If s1EVENT is TRUE, the value of s1LAST_VALUE is 0. LAST_VALUE ACTIVE EV TR s1LAST_VALUE s1ACTIVE VALUE VALUE As s1 BOOLEAN The value of s1 before the most recent event occurred on this signal. If s1 has had a transaction in the current simulation cycle, s1ACTIVE will be TRUE for this simulation cycle, for delta time. LAST_ACTIVE TR s1LAST_ACTIVE VALUE TIME The amount of time since the last transaction occurred on s1. If s1ACTIVE is TRUE, s1LAST_ACTIVE is 0. DRIVING DRIVING_VALUE DELAYED s1DRIVING s1DRIVING_VALUE s1DELAYED (5 NS) VALUE VALUE SIGNAL BOOLEAN As s1 As s1 If s1 is being driven in a process, s1DRIVING is TRUE in the same process. The driving value of s1 from within the process this attribute is being applied. A copy of s1, but delayed by 5 NS. If no parameter or 0, delayed by delta. Equivalent to TRANSPORT delay of s1. STABLE EV s1STABLE (5 NS) SIGNAL BOOLEAN A signal that is TRUE if s1 has not changed in the last 5 NS. If no parameter or 0, the resulting signal is TRUE if s1 has not changed in the current simulation time. QUIET TR s1QUIET (5 NS) SIGNAL BOOLEAN A signal that is TRUE if no transaction has been placed on s1 in the last 5 NS. If no parameter or 0, the current simulation cycle is assumed. TRANSACTION TR s1TRANSACTION SIGNAL BIT A signal that toggles each time a transaction occurs on s1. Initial value of this attribute is not defined. A transaction occurs on a signal every time it is evaluated. Regardless of whether the signal changes or not. Eg. Consider the concurrent VHDL statement A<=B and C. If B=0, then a transaction occurs on A every time C changes, since A is recomputed every time C changes. If B=1, then an event and a transaction occur on A every time C changes. SACTIVE returns true if S has just been reevaluated, even if S does not change. Figure 1: Examples of signal attributes 193

Entity attr_ex is Port(B, C: in bit); End attr_ex; Architecture test of attr_ex is Signal A, C_delayed5, A_trans:bit; Signal A_stable5, A_quiet5: Boolean; Begin A <= B and C; C_delayed5 <= Cdelayed(5 ns); A_trans <= Atransaction; A_stable5 <= Astable(5 ns); A_quiet5 <= Aquiet (5 ns); End test; (a) VHDL code for attribute test

B C A C_delayed5 A-trans A_stable5 A_quiet5 0 10 20 30 40 50

(b) Waveforms for attribute test

194

Attributes are often used together with assert statements for error checking. The assert statement checks to see if a certain condition is true and, if not, causes an error message to be displayed. As an example of using the assert statement together with an attribute, consider the following process, which checks to see if the setup and hold times are satisfied for a D flip-flop: Figure 2: Attributes in assert statements Check: process Begin Wait until (Clk=1 AND ClkEVENT); Assert (Dstable(setup_time)) Report (Setup time violation) Severity error; Wait for hold_time; Assert (Dstable(hold_time)) Report (Hold time violation) Severity error; End process check; In the check process, after the active edge of the clock occurs, the D input is checked to see if has been stable for the specified setup_time. If not, a setup-time violation is reported as an error. Then, after waiting for the hold time, D is checked to see if it has been stable during the hold-time period. If not, a hold-time violation is reported as an error. Example for Array Attributes: The procedure for parallel adder requires that the two bit-vectors to be added both be dimensioned N-1 downto 0 and that N be included in the procedure call. By using attributes, we can write a similar procedure that places no restrictions on the range of the vectors other than the lengths must be the same. When procedure Addvec2 (Figure 3) is executed, it creates a temporary variable, C, for the internal carry and initializes it to the input carry, Cin. Then it creates aleases n1, n2 and S, which have the same length as Add1, Add2, and Sum, respectively. These aleases are dimensioned from their length minus 1 downto 0. Even though the ranges of Add1, Add2, and Sum might be downto or to and might not include 0, the ranges for the aliases are defined in a uniform manner to facilitate further computation. If the input vectors and Sum are not the same length, an error message is reported. The sum and carry are computed bit by bit in a loop, as in parallel ladder. Since this loop must start with i=0, the range if i is the reverse of the range for S. Finally, the carry output, Cout, is set equal to the corresponding temporary variable, C. Figure 3: Procedure for Adding Bit-Vectors procedure Addvec2 (Add1,Add2: in bit_vector; 195

Cin: in bit; signal Sum: out bit_vector; signal Cout: out bit) is variable C: bit := Cin; alias n1 : bit_vector(Add1'length-1 downto 0) is Add1; alias n2 : bit_vector(Add2'length-1 downto 0) is Add2; alias S : bit_vector(Sum'length-1 downto 0) is Sum; begin assert ((n1'length = n2'length) and (n1'length = S'length)) report "Vector lengths must be equal!" severity error; for i in s'reverse_range loop S(i) <= n1(i) xor n2(i) xor C; C := (n1(i) and n2(i)) or (n1(i) and C) or (n2(i) and C); end loop; Cout <= C; end Addvec2; Transport and Inertial Delays VHDL provides for two types of delays - transport delays and inertial delays. The transport delay, which is used to model the delay introduced by wiring. With transport delay any pulse of small width is propagated to the output. The transport delay is especially useful for modeling delay line drivers, wire delays on PC poard, and on path delays on ASIC. The inertial delay, which is the default delay type for VHDL, is used to model propagation delay of gates and other devices. Inertial delay do not propagate short pulses from the input to the output i.e. if a gate has an ideal inertial delay T, the input signal is delayed by time T, but any pulse with a width less than T is rejected. For example, if a gate has an inertial delay of 10ns, a pulse of width 10ns would pass through, but a pulse of width 9.999 ns would be rejected. In general, the maximum width of a pulse rejected by a device will be less than the delay time. To reject a pulse of pre-specified width VHDL can model devices by adding a reject clause to the assignment statement. A statement of the form signal_name <= reject pulse-width expression after delay-time

196

Evaluates the expression, rejects any pulses whose width is less than pulse width, and then sets the signal equal to the result after a delay of delay-time. In statements of this type, the rejection pulse width must be less than the delay time. Figure 4 illustrates the difference between transport and inertial delays. Consider the following VHDL statements: Z1 <= transport X after 10 ns; Z2 <= X after 10 ns; Z3 <= reject 4 ns X after 10 ns; -- transport delay -- delay with specified -- rejection pulse width Z1 is the same as X, except that it is shifted 10 ns in time. Z2 is similar to Z1, except the pulse in X shorter than 10 ns are filtered out and do not appear in Z2. Z3 is the same as Z2, except that only the pulses of width less than 4 ns have been rejected. In general, using reject is equivalent to using a combination of an inertial delay and transport delay. Figure 4: Transport and inertial delays 10ns x 10ns
2ns 3ns

-- inertial delay

5ns

Z1 Z2 Z3

10

20

30

40

50

If a signal is scheduled to change at a given time and then a second change is scheduled to occur at an earlier time, the first change is deleted from the queue. For example, suppose that the following sequential statements are executed at time T: A <= transport B after 2 ns; A <= transport C after 1 ns;

197

First A is scheduled to change to B at time T + 2 ns. Then A is scheduled to change to C at time T + 1 ns, and the previous change to B is removed from the queue. Exercise Problems from Roth: 1. Write a VHDL function that will take two integer vectors, A and B, and find the dot product C= ai * bi. The function call should be of the form DOT (A, B), where A and B are integer vector signals. Use attributes inside the function to determine the length and ranges of the vectors. Make no assumptions about the high and low values of the ranges. For example: A(3 downto 1) = (1,2,3), B(3 downto 1) = (4,5,6), C=3 * 6 + 2 * 5 + 1 * 4 = 32 Output a warning if the ranges are not the same. Type intarray is array (natural range <>) of integer; Function DOT (A, B: intarray) return integer is Alias A1: intarray (Alength-1 downto 0) is A; Alias B1: intarray (Blength-1 downto 0) is B; Variable Sum: integer:=0; Begin Assert (ARange = BRange) Report Vector ranges are not the same! Severity warning; If (Alength /= Blength) then Report Vector length must be equal! Severity error; Return 0; End if; For i in A1range loop Sum := Sum + (A1(i) * B1(i)); End loop; Return sum; End DOT; 2. A VHDL entity has inputs A and B, and outputs C and D. A and B are initially high. Whenever A goes low, C will go high 5 ns later, and if A changes again, C will change 5 ns later. D will change if A has not had any transactions for 5 ns. Write the VHDL architecture with a process that determines the outputs C and D. Entity dm is 198

Port (A, B: in bit:=1; C, D: inout bit); End dm; Architecture dm of dm is Begin Process (A, Aquiet (5ns)) Begin If (Aevent) then C<= transport not A after 5ns; end if; If (Aquiet (5ns)event) then D <= not D; end if; End process; End dm; SessionVII by Prof.B.V.Uma Operator Overloading Signal Resolution IEEE 1164 Standard Logic GENERICS

Operator Overloading The need for operator overloading arises because the operator supplied in VHDL only work with specific types. The VHDL arithmetic operators, + and -, are defined to operate on integer, but not on bit-vectors. if a designer wants to use a particular operator on a userdefined type, by using operator overloading, user can extend the definition of + so that using the + operator will perform addition of two bit vectors/integer and bit vectors/ bitvectors and integers etc. operator overload functions implicitly call an appropriate addition function, which eliminates the need for an explicit function or procedure call. When the compiler encounters a function declaration in which the function name is an operator enclosed in double quotes, the compiler treats this function as an operator overloading function. The package shown in figure 1 defines two + functions. The first one adds two bit-vectors and returns a bit-vector. The second function in the package adds an integer to a bit-vector and returns a bit-vector. When a + operator is encountered, the compiler automatically checks the types of the operands and calls the appropriate functions. Consider the statement A <= B + C + 3; And assume that the bit_overload package of Figure 1 is being used. If A, B and C are of type integer, integer arithmetic is used. If A, B and C are of type bit_vector, the first function 199

in the package is called to add B and C, then the second function is called to add 3 to the sum of B and C. The statement A <= 3 + B + C would cause a compile time error, since we have not defined an overloading function for + when the first operand is an integer and the second operand is a bit-vector. Figure 1: VHDL Package with Overloaded Operators for Bit-Vectors library BITLIB; use BITLIB.bit_pack.all; package bit_overload is function "+" (Add1, Add2: bit_vector) return bit_vector; function "+" (Add1: bit_vector; Add2: integer) return bit_vector; end bit_overload; package body bit_overload is function "+" (Add1, Add2: bit_vector) return bit_vector is variable sum: bit_vector(Add1'length-1 downto 0); variable c: bit := '0'; -- no carry in alias n1: bit_vector(Add1'length-1 downto 0) is Add1; alias n2: bit_vector(Add2'length-1 downto 0) is Add2; begin for i in sum'reverse_range loop sum(i) := n1(i) xor n2(i) xor c; c := (n1(i) and n2(i)) or (n1(i) and c) or (n2(i) and c); end loop; return (sum); end "+"; function "+" (Add1: bit_vector; Add2: integer) return bit_vector is begin return (Add1 + int2vec(Add2 , Add1'length)); end "+"; end bit_overload; Overloading can also be applied to procedures and functions. Several procedures can have the same name, and type of the actual parameters in the procedure call determines which version of the procedure is called. 200

Multivalued Logic Figure 2: Tristate buffers with Active high Output Enable b a

f d c

Figure 2 shows two tristate buffers with their outputs tied together, and the inputs a, b, c and d has four values: X, 0, 1, and Z. The tristate buffers have an active high output enable, so that when b=1 and d=0, f=a; when b=0 and d=1, f=c; and when b=d=0, the f output assumes the high-Z state. If b=d=1, an output conflict can occur. Simple VHDL architecture is as shown. architecture t_buff_conc of t_buff_exmpl is begin f <= a when b = '1' else 'Z'; f <= c when d = '1' else 'Z'; end t_buff_conc; The above architecture uses two concurrent statements. f is driven from two different sources, and VHDL uses a resolution function to determine the actual output. For example, if a=c=d=1 and b=0, f is driven to Z by one concurrent statement or process, and f is driven to 1 by the other concurrent statement or process. The resolution function is automatically called to determine the proper value of f. The resolution function will supply a value of X (unknown) if f is driven to both 0 and 1 at the same time. VHDL signal may either be resolved or unresolved. Resolved signals have an associated resolution function, and unresolved signals do not. signals of type bit are unresolved. i.e. if we drive a bit signal B to two different values in two concurrent statements (or in two processes), the compiler will flag an error, since there is no way determine the proper value of B.

201

Signal Resolution Resolved signals have resolution function either written in standard package or by the user. A resolution function is used to return the value of a signal when the signal is driven by multiple drivers. It is illegal in VHDL to have a signal with multiple drivers without resolution function attached to that signal. A resolution function consists of a function that is called whenever one of the drivers for the signal has an event occur on it. The resolution function is executed and returns a single value from all of the driver values; this value is the new value of the signal. In typical simulators, resolution functions are built in, or fixed. With VHDL, the designer has the ability to define any type of resolution function desired, wired-or, wired-and, average signal value, and so on. A resolution function has a single input argument consists of an unconstrained array of driver values for the signal that the resolution function attached to. The resolution function, which is based on the operation of a tristate bus, is specified by the following table: X 0 1 Z X X X X X 0 X 0 X 0 1 X X 1 1 Z X 0 1 Z Listed by order of strength with the weakest at the top, the values are as follows: Z - Weakest, 1, 0 or X can override 1, 0 Medium Strength, only X can override. X Strong, no override The value X represents an unknown condition in which the value can represent 0 or 1, but not sure. This condition can occur when two drivers are driving a signal, one driver driving with 1 and the other driving with 0. The function resolve4 has an argument, s, which represents a vector of one or more signal values to be resolved. If the vector is of length 1, then the first (and only) element of the vector is returned. Otherwise, the return value (the resolved signal) is computed iteratively by starting with result=Z and recomputing result by using a lookup table with each element of the s vector in turn. Figure 3: Resolution function for X01Z Logic package fourpack is type u_x01z is ('X','0','1','Z'); -- u_x01z is unresolved type u_x01z_vector is array (natural range <>) of u_x01z; function resolve4 (s:u_x01z_vector) return u_x01z; subtype x01z is resolve4 u_x01z; 202

-- x01z is a resolved subtype which uses the resolution function resolve4 type x01z_vector is array (natural range <>) of x01z; end fourpack; package body fourpack is type x01z_table is array (u_x01z,u_x01z) of u_x01z; constant resolution_table : x01z_table := ( ('X','X','X','X'), ('X','0','X','0'), ('X','X','1','1'), ('X','0','1','Z')); function resolve4 (s:u_x01z_vector) return u_x01z is variable result : u_x01z := 'Z'; begin if (s'length = 1) then return s(s'low); else for i in s'range loop result := resolution_table(result,s(i)); end loop; end if; return result; end resolve4; end fourpack; Figure 3 shows how the resolution function for X01Z logic is defined in a package called fourpack. First, an unresolved logic type u_X01Z is defined, along with the corresponding unconstrained array type, u_X01Z_vector. Then a resolution function, named resolve4, is declared. Resolved X01Z logic is defined as a subype of u_X01Z. The subtype declaration contains the function name resolve4. This implies that whenever a signal of type X01Z is computed, function resolve4 is called to compute the correct value. Consider the following three concurrent statements, where R is a resolved signal of type X01Z: R <= transport 0 after 2 ns, Z after 6 ns; R <= transport 1 after 4 ns; R <= transport 1 after 8 ns, 0 after 10 ns; 203

Assuming that R is initialized to Z, three drivers would be created for R, as shown in Figure 3. Each time one of the unresolved signals s(0), s(1), or s(2) changes, the resolution function is automatically called to determine the value of the unresolved signal, R. In the example of Figure 4, the s vector has three elements, and resolve4 would be called at 0, 2, 4, 6, 8, and 10 ns to compute R. the following table shows the result: Time s(0) s(1) s(2) 0 Z Z Z 2 0 Z Z 4 0 1 Z 6 Z 1 Z 8 Z 1 1 10 Z 1 0 Figure 4: Resolution of Signal Drivers R Z 0 X 1 1 X

Z @ 6 ns

0 @ 2 ns

s(0) resolution function resolve4

driver 0 1 @ 4 ns driver 1 0 @ 10 ns 1 @ 8 ns Z s(2) Z s(1)

Resolved signal R

driver 2

204

In order to write VHDL code using X01Z logic, we need to define the required operations for this type of logic. For example, AND and OR may be defined using the following tables: AND X 0 1 Z X X 0 X X 0 0 0 0 0 1 X 0 1 X Z X 0 X X OR X 0 1 Z X X X 1 X 0 X 0 1 X 1 1 1 1 1 Z X X 1 X

The first table corresponds to the way an AND gate with 4-valued inputs would work. If one of the AND gate inputs is 0, the output is always 0. If both inputs are 1, the output is 1. In all other cases, the output is unknown (X), since a high Z gate input may act like either a 0 or 1. For an OR gate, if one of the inputs is 1, the output is always 1. If both inputs are 0, the output is 0. In all other cases, the output is X. AND and OR functions based on these tables can be included in the package fourpack to overload the AND and OR operators. IEEE 1164 Standard Logic The IEEE-1164 Standard specifies a 9-valued logic system for use with VHDL. The nine logic values defined in this standard are U X 0 1 Z W L H _ Uninitialized Forcing unknown Forcing 0 Forcing 1 High impedance Weak unknown Weak 0 Weak 1 Dont Care

The unknown, 0 and 1 values come in two strengths forcing and weak. If a forcing signal and a weak signal are tied together, the forcing signal dominates. For example if 0 and H are tied together, the result is 0. The output of a pull-up resistor could be represented by a value of H. the nine-valued logic is useful in modeling the internal operation of certain types of ICs. Normally use only a subset of the IEEE values X, 0, 1, and Z. The IEEE 1164 standard defines the AND, OR, NOT, XOR, and other functions for 9valued logic. It also specifies a number of subtypes of the 9-valued logic, such as the X01Z subtype, which we have already been using. Table 1 shows the resolution function table for the IEEE 9-valued logic. The row index values have been listed as comments to the right of the table. The resolution function table for X01Z logic is a subset of this table, as indicated by the black rectangle. Table 1: Resolution function Table for IEEE 9-valued Logic 205

Constant resolution_table: stdlogic_table : = ( ---------------------------------------------------------------------------------------------------------| U X 0 1 Z W L H | | ------------------------------------------------------------------------------------------------------( U U U U U U U U U ), -- | U | ( U X X X X X X X X ), -- | X | ( U X 0 X 0 0 0 0 X ), -- | 0 | ( U X X 1 1 1 1 1 X ), -- | 1 | ( U X 0 1 Z W L H X ), -- | Z | ( U X 0 1 W W W W X ), -- | W | ( U X 0 1 L W L W X ), -- | L | ( U X 0 1 H W W H X ), -- | H | ( U X X X X X X X X ), -- | - |

); The table for the standard logic and operation is shown in Table 2. The and functions given in Figure 5 use this table. These functions provide for operator overloading. This means that if we write an expression that uses the and operator, the compiler will automatically call the appropriate and function to evaluate the and operation depending on the type of the operands. If and is used with bit variables, the ordinary and function is used, but if and is used with std_logic variables, the std_logic and function is called. Operator overloading also automatically applies the appropriate and function to vectors. When and is used with bit vectors, the ordinary bit-by-bit and is performed, but when and is applied to std_logic vectors, the std_logic vectors, the std_logic and is applied on a bit-by-bit basis.

206

Table 2: And Table for IEEE 9-valued LogicCONSTANT and_table : stdlogic_table := ( ---------------------------------------------------------------------------------------------------------| U X 0 1 Z W L H | | ------------------------------------------------------------------------------------------------------( U U 0 U U U 0 U U ), -- | U | ( U X 0 X X X 0 X X ), -- | X | ( 0 0 0 0 0 0 0 0 0 ), -- | 0 | ( U X 0 1 X X 0 1 X ), -- | 1 | ( U X 0 X X X 0 X X ), -- | Z | ( U X 0 X X X 0 X X ), -- | W | ( 0 0 0 0 0 0 0 0 0 ), -- | L | ( U X 0 1 X X 0 1 X ), -- | H | ( U X 0 X X X 0 X X ), -- | - | Figure 5: And Function for std_logic_vectors

function "and" ( l : std_ulogic; r : std_ulogic ) return UX01 is begin return (and_table(l, r)); end "and"; function "and" ( l,r : std_logic_vector ) return std_logic_vector is alias lv : std_logic_vector ( 1 to l'LENGTH ) is l; alias rv : std_logic_vector ( 1 to r'LENGTH ) is r; variable result : std_logic_vector ( 1 to l'LENGTH ); begin if ( l'LENGTH /= r'LENGTH ) then assert FALSE report "arguments of overloaded 'and' operator are not of the same length" severity FAILURE; else for i in result'RANGE loop result(i) := and_table (lv(i), rv(i)); end loop; end if; return result; end "and"; 207

Aliases are used to make sure the index range is the same direction for both operands. If the vectors are not the same length, the assert false always causes the message to be displayed. Otherwise, each bit in the result vector is computed by table lookup. GENERICS Generics are means of communicating non hardware and non signal information between designs. Generics are commonly used to specify parameters for a component in such a way that the parameter values may be specified when the component is instantiated. For example, the rise and fall times for a gate could be specified as generics, and different numeric values for these generics could be assigned for each instance of the gate. The example of Figure 6 describes a two input nand gate whose rise and fall delay times depend on the number of loads on the gate. In the entity declaration, Trise, Tfall, and load are generics that specify the no-load rise time, the no-load fall time, and the number of loads. In the architecture, an internal nand_value is computed whenever a or b changes. If nand_value has just changed to a 1, a rising output has occurred, and the gate delay time is computed as Trise + 3 ns * load where 3 ns is the added delay for each load. Otherwise, a falling output has just occurred and the gate delay is computed as Tfall + 2 ns * load where 2 ns is the added delay for a each load. Figure 6: Rise / Fall Time Modeling Using Generic Statement entity NAND2 is generic (Trise, Tfall: time; load: natural); port (a,b : in bit; c: out bit); end NAND2; architecture behavior of NAND2 is signal nand_value : bit; begin nand_value <= a nand b; c <= nand_value after (Trise + 3 ns * load) when nand_value = '1' else nand_value after (Tfall + 2 ns * load); 208

end behavior; entity NAND2_test is port (in1, in2, in3, in4 : in bit; out1, out2 : out bit); end NAND2_test; architecture behavior of NAND2_test is component NAND2 is generic (Trise: time := 3 ns; Tfall: time := 2 ns; load: natural := 1); port (a,b : in bit; c: out bit); end component; begin U1: NAND2 generic map (2 ns, 1 ns, 2) port map (in1, in2, out1); U2: NAND2 port map (in3, in4, out2); end behavior; Exercise Problems from Roth: 1. Write a VHDL function to compare two IEEE std_logic_vectors to see if they are equal. Report an error if any bit in either vector is not 0, 1, or - (dont care), or if the lengths of the vectors are not the same. The function call should pass only the vectors. The function should return TRUE if vectors are equal, else FALSE. When comparing the vectors, consider that 0=-, and 1=-. Make no assumptions about the index range of the two vectors (for example, one could be 1 to 7 and the other 8 downto 0). Function compVec (A1, B1: std_logic_vector) return Boolean is Alias A: std_logic_vector(A1length-1 downto 0) is A1; Alias B: std_logic_vector (B1length-1 downto 0) is B1; Begin Assert (Alength = Blength) Report Vector lengths are not same! Severity ERROR; Return FALSE; For I in Arange loop 209

Assert ((A(i)=0 or A(i)=1 or A(i)=-) and (B(i)=0 or B(i)=-)) Report Illegal bit value Severity ERROR; Return FALSE; If (not (A(i)=B(i) or A(i)=- or B(i)=-) then Return FALSE; end if; End loop; Return TRUE; End compVec; SessionVIII and IX by Prof.B.V.Uma Serial adder with Accumulator Binary 4X4 multiplier Design of a serial adder with Accumulator

B(i)=1or

A control circuit for a digital system is a sequential network that outputs a sequence of control signals. These signals cause operations such a addition and shifting to take place at appropriate times. Figure 1 shows the block diagram for serial adder with accumulator. Two shift registers are used to hold the 4-bit numbers to be added, X and Y. The box at the left end of each shift register shows the inputs: Sh (shift), SI(serial input), and clock. When Sh=1 and the clock is pulsed, SI is entered into x3(or y3) as the contents of register are shifted right one position.

Figure 1: Serial Adder with Accumulator

210

Accumulator
SI Sh

x3

x2

x1

x0

xi

sumi

N (Start Signal)
SI Sh

Control Circuit

Sh

y3

y2

y1

y0

yi ci

Full Adder

ci+1

Addend Register
Q D

Clock

Serial Adder Sh Clock

Q CK

The X-register serves as the accumulator, and after four shifts, the number X is replaced with the sum of X and Y. The addend register is connected as a cyclic shift register, so after four shifts it is back to its original state and the number Y is not lost. The serial adder consists of a full adder and a carry flip flop. At each clock time, one pair of bits is added. When Sh=1, the falling edge of the clock shifts the sum bit into the accumulator, stores the carry bit in the flip-flop, and causes the addend register to rotate left. Additional connections needed for initially loading the X and Y registers and clearing the carry flip-flop are not shown in the block diagram. Table 1 illustrates the operation of the serial adder Initially, at time t 0 the accumulator contains X, the addend register contains Y, and the carry flip-flop is clear. Since the full adder is a combinational network, x0 = 1, y0 = 1, and c0 = 0 are added after a short propagation delay to give 10, so sum0 = 0 and carry c1 = 1. when the first clock occurs, sum0 is shifted into the accumulator, and the remaining accumulator digits are shifted right one position. The same shift pulse stores c1 in the carry flip-flop and cycles the addend register right one position. The next pair of bits, x1=0 and y1 = 1, are now at the full adder input, and the adder generates the sum and carry, sum1=0 and c2 = 1. The second clock pulse shifts sum1 into the accumulator, stores c2 in the carry flip-flop and cycles the addend register right. Bits x2 and y2 are now at the adder input, and the process continues until all bit pairs have been added. After four clocks (time t4), the sum of X and Y is in the accumulator, and the addend register is back to its original state. Table 1: Operation of Serial Adder 211

t0 t1 t2 t3 t4

X 0101 0010 0001 1000 1100

Y 0111 1011 1101 1110 0111

ci 0 1 1 1 0

sumi 0 0 1 1 (1)

ci+1 1 1 1 0 (0)

The control circuit for the adder must now be designed so that after receiving a start signal, the control circuit will output Sh=1 for four clocks and then stop. Figure 2 shows the state graph and table for the control circuit. The network remains in S0 until a start signal (N) is received, at which time the network outputs Sh = 1 and goes to S1. then Sh=1 for three more clock times, and the network returns to S0. It will be assumed that the start signal is terminated before the network returns to state S0, so no further action occurs until another start signal is received. Dashes (dont cares) on the graph indicate the once S1 is reached, the network operation continues regardless of the value of N. Figure 2 Control state Graph and Table for Serial Adder 0/0 N / Sh
S0 -/1 S3 1/1 S1 -/1 S2

-/1

Present State S0 S1 S2 S3

Next State N=0 S0 S2 S3 S0 N=1 S1 S2 S3 S0

Present Output (Sh) N=0 0 1 1 1 N=1 1 1 1 1

Design of a Binary Multiplier Binary multiplication requires only shifting and adding. For example, multiplication 1310 by 0510 in binary: Multiplicand 1 1 0 1 (13) Multiplier 0 1 0 1 (05) 212

Partial Product

0000 01101 1101 1 000001 0 0 00

1101

0 1 0 0 0 0 0 1 (65) Note that each partial product is either the multiplicand (1101) shifted over by the appropriate number of places or zero. Multiplication of two 4-bit numbers requires a 4-bit multiplicand register, a 4-bit multiplier register, a 4-bit full adder, and an 8-bit register for the product. The product register serves as an accumulator to accumulate the sum of the partial products. If the multiplicand were shifted left each time before it was added to the accumulator, as was done in the previous example, an 8-bit adder would be needed. So it is better to shift the contents of the product register to the right each time, as shown in the block diagram of figure 3. This type of multiplier is sometimes referred to as a serial parallel multiplier, since the multiplier bits are processed serially, but the addition takes place in parallel. As indicated by the arrows on the diagram, 4 bits from the accumulator (ACC) and 4 bits from the multiplicand register are connected to the adder inputs; the 4 sum bits and the carry output from the adder are connected back to the accumulator. When an add signal (Ad) occurs, the adder outputs are transferred to the accumulator by the next clock pulse, thus causing the multiplicand to be added to the accumulator. An extra bit at the left end of the product register temporarily stores any carry that is generated when the multiplicand is added to the accumulator. When a shift signal (Sh) occurs, all 9 bits of ACC are shifted right by the next clock pulse.

Figure 3: Block Diagram for Binary Multiplier

213

Since the lower 4 bits of the product register are initially unused, we will store the multiplier in this location instead of in a separate register. As each multiplier bit is used, it is shifted out the right end of the register to make room for additional product bits. A shift signal (Sh) causes the contents of the product register (including the multiplier) to be shifted right one place when the next clock pulse occurs. The control circuit puts out the proper sequence of add and shift signals after a start signal (St=1) has been received. If the current multiplier bit (M) is 1, the multiplicand is added to the accumulator followed by a right shift; if the multiplier bit is 0, the addition is skipped, and only the right shift occurs. The multiplication example (13X05) is reworked below showing the location of the bits in the registers at each clock time. Multiplication Steps: Initial contents of product register (add multiplicand since M=1) after addition after shift (skip addition since M=0) after addition after shift (add multiplicand since M = 1) after addition after shift (skip addition since M=0) 214 0 0 0 0 0 0 1 0 1 M (5) 1101 011010101 0 0 1 1 0 1 0 1 0M 0000 00110 1010 0 0 0 1 1 0 1 0 1 M 1101 1 00000101 0 1 0000010

after shift (final answer)

0 0 1 000001

The control circuit must be designed to output the proper sequence of add and shift signals. Figure 4 shows a state graph for the control circuit. In Figure 4, S0 is the reset state, and the network stays in S0 until a start signal (St=1) is received. This generates a load signal, which causes the multiplier to be loaded into the lower 4 bits of the Accumulator (ACC) and the upper 5 bits of the accumulator to be cleared. In state S1, the low-order bit of the multiplier (M) is tested. If M=1, an add signal is generated, and if M=0, a shift signal is generated. Similarly, in states S 3, S5 and S7, the current multiplier bit (M) is teted to determine whether to generate an add or shift signal. A shift signal is always generated at the next clock time following an add signal (states S2, S4, S6, and S8). After four shifts have been generated, the control network goes to S9, and a done signal is generated before returning to S0. Figure 4: State Graph for Binary Multiplier Control St/0

215

9 -/Sh
S 8

-/Done

0 St/Load
S

M/Sh

1 M/Ad

M/Ad
S 7 S 2

M/Sh

-/Sh
S 6

-/Sh M/Sh M/Sh


S 3

M/Ad

M/Ad
S 5

-/Sh

S 4

The behavioral VHDL model (Figure 5) corresponds directly to the state graph. Since there are 10 states, we have declared an integer range 0 to 9 for the state signal. The signal ACC represents the 9-bit accumulator output. The statement alias M: bit is ACC(0); allows us to use the name M in place of ACC(0) The done signal needs to be turned on only in state 9. If we had used the statement when 9 => State <= 0; Done <= 1, Done would be turned on at the same time the State changed to 0. This is too late, since we want Done to turn on when the state becomes 9. Therefore, we used a separate concurrent assignment statement. This statement is placed outside the process so that Done will be updated whenever State changes. Figure 5 Behavioral Model for 4 X 4 Binary Multiplier -- This is a behavioral model of a multiplier for unsigned -- binary numbers. It multiplies a 4-bit multiplicand -- by a 4-bit multiplier to give an 8-bit product. -- The maximum number of clock cycles needed for a -- multiply is 10. library BITLIB; use BITLIB.bit_pack.all; entity mult4X4 is port (Clk, St: in bit; 216

Mplier,Mcand : in bit_vector(3 downto 0); Done: out bit); end mult4X4; architecture behave1 of mult4X4 is signal State: integer range 0 to 9; signal ACC: bit_vector(8 downto 0); alias M: bit is ACC(0); begin process begin wait until Clk = '1'; case State is when 0=> if St='1' then ACC(8 downto 4) <= "00000"; ACC(3 downto 0) <= Mplier; State <= 1; end if; when 1 | 3 | 5 | 7 => if M = '1' then State <= State+1; else ACC <= '0' & ACC(8 downto 1);--Shift accumulator right State <= State + 2; end if; when 2 | 4 | 6 | 8 => State <= State + 1; when 9 => State <= 0; end case; end process; 217 --End of cycle --"shift" State --Right shift ACC <= '0' & ACC(8 downto 1); --"add/shift" State --Add multiplicand --Begin cycle --load the multiplier --initial State --executes on rising edge of clock --accumulator --M is bit 0 of ACC

ACC(8 downto 4) <=add4(ACC(7 downto 4),Mcand,'0');

Done <= '1' when State = 9 else '0'; end behave1; As the state graph for the multiplier indicates, the control performs two functions generating add or shift signals as needed and counting the number of shifts. If the number of bits is large, it is convenient to divide the control network into a counter and an add-shift control, as shown in Figure 6(a). First, we will derive a state graph for the add-shift control that tests St and M and outputs the proper sequence of add and shift signals (Figure 6(b)). Then we will add a completion signal (K) from the counter that stops the multiplier after the proper number of shifts have been completed. Starting in S0 in Figure 6(b), when a start signal St=1 is received, a load signal is generated and the network goes to state S2; if M=0, a shift signal is generated and the network stays in S1. In S2, a shift signal is generated since a shift always follows an add. The graph of Figure 6(b) will generate the proper sequence of add and shift signals, but it has no provision for stopping the multiplier.

Figure 6 Multiplier Control with Counter

St/0 M/Sh

St M Add-shift control K Counter (a) Multiplier control St/0

Done Load Ad Sh

S 0

St/Load

S 1

-/Sh

M/Ad

S 2

(b) State-graph for add-shift control

218

S 0

St/Load

KM/Sh S 1

KM/Sh -/Done S 3

K/Sh S 2

M/Ad

K/Sh

(c) Final state graph for add shift control In order to determine when the multiplication is completed, the counter is incremented each time a shift signal is generated. If the multiplier is n bits, n shifts are required. We will design the counter so that a completion signal (K) is generated after n-1 shifts have occurred. When K=1, the network should perform one more addition id necessary and then do the final shift. The control operation in Figure 6(c) is the same as Figure 6(b) as long as K=0. In state S1, if K=1, we test M as usual. If M =0, we output the final shift signal and go to the done state (S3); however if M=1, we add before shifting and go to state S2. In state S2, if K=1, we output one more shift signal and then go to S3. The last shift signal will increment the counter to 0 at the same time the add-shift control goes to the done state. As an example, consider the multiplier of figure 3, but replace the control network with Figure 6(a). Since n=4, a 2-bit counter is needed to count the 4 shifts, and K=1 when the counter is in state 3(112). Table 2 shows the operation of the multiplier when 1101 is multiplied by 1011. S0, S1, S2, and S3 represent states of the control circuit (Figure 6(c)). The contents of the product register at each step is the same as multiplication steps written in previous section. Table 2: Operation of Multiplier using a Counter Time t0 t1 t2 t3 t4 State S0 S0 S1 S2 S1 Counter 00 00 00 00 01 Product Register 00000000 0 00000000 0 00000101 1 01101101 1 00110110 St 0 1 0 0 0 M 0 0 1 1 1 K 0 0 0 0 0 Load 0 1 0 0 0 Ad 0 0 1 0 1 Sh 0 0 0 1 0 Done 0 0 0 0 0 219

t5 t6 t7 t8 t9

S2 S1 S1 S2 S3

01 10 11 11 00

1 10011110 1 01001111 0 00100111 1 10001111 1 01000111 1

0 0 0 0 0

1 0 1 1 1

0 0 1 1 0

0 0 0 0 0

0 0 1 0 0

1 1 0 1 0

0 0 0 0 1

At time t0 the control is reset and waiting for a start signal. At time t1, the start signal St = 1, and a Load signal is generated. At time t2, M=1, so an Ad signal is generated. When the next clock occurs, the output of the adder is loaded into the accumulator and the control goes to S2. At t3, an Sh signal is generated, so at the next clock shifting occurs and the counter is incremented. At t4, M=1 so Ad=1, and the Adder output is loaded into the accumulator at the next clock. At t5 and t6, shifting and counting occur. At t7, three shifts have occurred and the counter state is 11, so K=1. Since M = 1, addition occurs and control goes to S2. At t 8, Sh = K = 1, so at the next clock the final shift occurs and the counter is incremented back to state 00. At t9, a Done signal is generated. Behavioral Model for Multiplier Control with Counter library BITLIB; use BITLIB.bit_pack.all; entity mult4X4 is port (Clk, St: in bit; Mplier,Mcand : in bit_vector(3 downto 0); Done: out bit); end mult4X4; architecture behave2 of mult4X4 is signal ACC: bit_vector(8 downto 0); alias M: bit is ACC(0); signal cnt: integer range 0 to 4; begin process (st) begin cnt<=0 220 --accumulator --M is bit 0 of ACC

if St='1' then ACC(8 downto 4) <= "00000"; ACC(3 downto 0) <= Mplier; end if; end process; process (clk) begin while(cnt < 4) loop if (clk=1 and clkevent) then if M = '1' then end if; ACC <= '0' & ACC(8 downto 1);--Shift accumulator right Cnt<=cnt+1; end if; end loop; end process; Done <=1; end behave2; The multiplier design given here can easily be expanded to 8, 16, or more bits simply by increasing the register size and the number of bits in the counter. The add-shift control would remain unchanged. Next, we design a multiplier that consists of an array of AND gates and adders. This multiplier will have an iterative structure with no sequential logic or registers required. Table 3 illustrates multiplication of two 4-bit unsigned numbers, X3 X2 X1 X0 times Y3 Y2 Y1 Y0. Each of the XiYj product bits can be generated by an AND gate. Each partial product can be added to the previous sum of partial products using a row of adders. The sum output of the first row of adders, which adds the first two partial products, is S 13 S12 S11 S10, and the carry output is C13 C12 C11 C10. Similar results occur for the other two rows of adders. (We have used to notation Sij and Cij to represent the sums and carries from the ith row of adders.) Figure 7 shows the corresponding array of AND gates and adders. If an adder has three inputs, a full adder (FA) is used, but if an adder has only two inputs, a half adder (HA) is used. A half adder is the same as full adder with one of the inputs set to 0. This multiplier requires 16 AND gates, 8 full adders, and 4 half-adders. After the X and Y inputs have been applied, the carry must propagate along each row of cells, and the sum must propagate from row to row. The time required to complete the multiplication depends primarily on the propagation delay in the adders. The longest path from input to output goes through 8 adders. If tad is the worst221 --Add multiplicand ACC(8 downto 4) <=add4(ACC(7 downto 4),Mcand,'0'); --Begin cycle --load the multiplier

case (longest possible) delay through an adder, and t8 is the longest AND gate delay, then the worst case time to complete the multiplication is 8 tad + t8. Table 3: 4-bit Multiplier Partial Products X3 Y3 X2 Y2 X1 Y1 X0 Y0 Multiplicand Multiplier Partial Product 1 1st row carries S10 1st row sums partial product 2 2nd row carries S20 2nd row sums partial product 3 3rd row carries S31 P4 S30 P3 P2 P1 P0 3rd row sums Final Product

X3Y0 X2Y0 X1Y0 X0Y0 Partial product 0 X3Y1 X2Y1 X1Y1 X0Y1 C12 C13 C22 C23 C32 C33 P7 S23 C31 S33 P6 S13 C21 S22 C30 S32 P5 C11 S12 C20 S21 C10 S11

X3Y2 X2Y2 X1Y2 X0Y2

X3Y3 X2Y3 X1Y3 X0Y3

In general, an n-bit-by-n-bit array multiplier would require n2 AND gates, n(n-2) full adders, and n half-adders. So the number of components required increases quadratically. For the serial parallel multiplier previously designed, the amount of hardware required in addition to the control circuit increases linearly with n.

Figure 7: Block diagram of 4X4 Array Multiplier

222

For an nXn array multiplier, the longest path from input to output goes through 2n adders, and the corresponding worst-case multiply time is 2ntad + t8. The serial-parallel multiplier of the type previously designed requires 2n clocks to complete the multiply in the worst case, although this can be reduced to n clocks using a technique discussed in the next section. The minimum clock period depends on the propagation delay through the n-bit adder as well a the propagation delay and setup time for the accumulator flip-flops. Session X by Prof.B.V.Uma Design of a Binary Divider We will consider the design of a parallel divider for positive binary numbers. As an example, we will design a network to divide an 8-bit dividend by a 4-bit divisor to obtain a 4-bit quotient. The following example illustrates the division process: Dividend Divisor 1101 0111 0000 1111 1101 223 Quotient

1101) 10000111 (1010

0101 0000 0101 (135 13=10 with a remainder of 5) Just as binary multiplication can be carried out as a series of add and shift operation, division can be carried out by a series of subtract and shift operations. To construct the divider, we will use a 9-bit dividend register and a 4-bit divisor register, as shown in Figure 7. During the division process, instead of shifting the divisor right before each subtraction, we will shift the dividend to the left. Note that an extra bit is required on the left end of the dividend register so that a bit is not lost when the dividend is shifted left. Instead of using a separate register to store the quotient, we will enter the quotient bit-by-bit into the right end of the dividend register as the dividend is shifted left. Figure 7 Block Diagram for Parallel Binary Divider Dividend Register X4 X3 X2 Sh St (Start Signal) Su Subtractor and comparator V C Control (Overflow Indicator) remainder

X8

X7

X6

X5

X1

X0

Sh Ld

Y3

Y2

Y1

Y0

Clock

The preceding division example (135 divided by 13) is reworked next, showing the location of the bits in the registers at each clock time. Initially, the dividend and divisor are entered as follows: 0 1 1 0 1 0 0 0 1 0 1 1 1

Subtraction cannot be carried out without a negative result, so we will shift before we subtract. Instead of shifting the divisor one place to the right, we will shift the dividend one place to the left: 1 0 0 0 0 1 1 1 0 Note that after the shift, the rightmost position 224

in the dividend register is empty.

Subtraction is now carried out and the first quotient digit of 1 is stored in the unused position of the dividend register: 0 0 0 0 1 0 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 First quotient digit 0 Next we shift the dividend one place to the left:

Since subtraction would yield a negative result, we shift the dividend to the left again, and the second quotient bit remains zero: 0 1 1 1 1 1 1 0 1 1 1 0 0

Subtraction is now carried out, and the third quotient digit of 1 is stored in the unused position of the dividend register: 0 0 0 0 0 1 1 0 0 1 1 1 1 0 0 1 1 Third quotient digit 0 A final shift is carried out and the fourth quotient bit is set to 0: remainder quotient

The final result agrees with that obtained in the first example. If, as a result of a division operation, the quotient contains more bits than are available for storing the quotient, we say that an overflow has occurred. For the divider of Figure 7, an overflow would occur if the quotient is greater than 15, since only 4 bits are provided to store the quotient. It is not actually necessary to carry out the division to determine if an overflow condition exists, since an initial comparison of the dividend and divisor will tell if the quotient will be too large. For example, if we attempt to divide 135 by 7, the initial contents of the registers are: 0 1 0 0 1 0 1 0 1 0 1 1 1

Since subtraction can be carried out with a nonnegative result, we should subtract the divisor from the dividend and enter a quotient bit of 1 in the rightmost place in the dividend register. However, we cannot do this because the rightmost place contains the least significant bit of the dividend, and entering a quotient bit here would destroy that dividend bit. Therefore, the quotient would be too large to store in the 4 bits we have allocated for it, and we have detected an overflow condition. In general, for Figure 7, if initially X8X7X6X5X4Y3Y2Y1Y0 (i.e. if the left 5 bits of the dividend register exceed or equal the divisor), the quotient will be greater than 15 bits and an overflow occurs. Note that if X8X7X6X5X4Y3Y2Y1Y0, the quotient is

225

X8X7X6X5X4X3X2X1X0 Y3Y2Y1Y0

X8X7X6X5X40000 Y3Y2Y1Y0

X8X7X6X5X4 X 16 Y3Y2Y1Y0

16

The operation of the divider can be explained in terms of the block diagram of Figure 7. A shift signal (Sh) will shift the dividend one place to the left. A subtract signal (Su) will subtract the divisor from the 5 leftmost bits in the dividend register and set the quotient bit (the rightmost bit in the dividend register) to 1. If the divisor is greater than the 4 leftmost dividend bits, the comparator output is C=0; otherwise, C=1. the control circuit generates the required sequence of shift and subtract signals. Whenever C=0, subtraction can not occur without a negative result, so a shift signal is generated. Whenever C=1, a subtraction signal is generated, and the quotient bit is set to 1. Figure 8 shows the state diagram for the control circuit. When a start signal (St) occurs, the 8-bit dividend and 4-bit divisor are loaded into the appropriate registers, If C is 1, the quotient would require five or more bits. Since space is only provided for 4-bit quotient, this condition constitutes an overflow, so the divider is stopped and the overflow indicator is set by the V output. Normally, the initial value of C is 0, so a shift will occur first, and the control circuit will go to state S2. Then, if C=1, subtraction occurs. After the subtraction is completed, C will always be 0, so the next clock pulse will produce a shift. This process continues until four shifts have occurred and the control is in state S5. Then a final subtraction occurs if necessary, and the control returns to the stop state. For this example, we will assume that when the start signal (St) occurs, it will be 1for one clock time, and then it will remain 0 until the control network is back in state S0. Therefore, St will always be 0 in states S1through S5. Figure 8 State Diagram for Divider Control Circuit

St/0

S0
(sto p)

St/Load S1 C/V

C/Sh

S2

C/Su

C/Su C/0 S5 C/Sh S4 C/Sh S3

C/Sh

C/Su

C/Su

226

Table 1 gives the state table for the control circuit. Since we assumed that St=0 in states S1, S2, S3, and S4, the next states and outputs are dont cares for these states when St=1. The entries in the output table indicate which outputs are 1. For example, the entry Sh means Sh=1 and the other outputs are 0. Table 1 state Table for divider Control Circuit State S0 S1 S2 S3 S4 S5 StC 00 S0 S2 S3 S4 S5 S0 01 S0 S0 S2 S3 S4 S0 11 S1 _ _ _ _ _ 10 S1 _ _ _ _ _ StC 00 0 Sh Sh Sh Sh 0 01 0 V Su Su Su Su 11 Load _ _ _ _ _ 10 Load _ _ _ _ _

This example illustrates a general method for designing a divider for unsigned binary numbers, and the design can easily be extended to larger numbers such as 16 bits divided by 8 bits or 32 bits divided by 16 bits. We now design a divider for signed (2s complement) binary numbers that divides a 32-bit dividend by a 16-bit divisor to give a 16-bit quotient. Exercise Problem: 4.5 (a) Draw the block diagram for divider for unsigned binary number that divides an 8 bit dividend by a 3 bit divisor to give a 5 bit quotient (b) Draw state graph for the control circuit, assume that the start signal (st) is present for 1 clock period. (c) Write VHDL description of the divider. (a) Block diagram for divider for unsigned binary number X8 X7 X6 X5 Dividend Register X4 X3 X2 Load X1 X0 Sh Subtractor and comparator Su C 0 Y2 Y1 Y0 Control Clock V St

(b) Refer Figure 8 (c) VHDL description of the divider: 227

library ieee; use ieee.std_logic_1164.all; entity divider is port (St, Clk: in std_logic; dend: in std_logic_vector(7 downto 0); dsor: in std_logic_vector(2 downto 0); v: out std_logic; qent: out std_logic_vector(4 downto 0)); end divider; architecture beh of divider is signal C, Sh, su, Ld: std_logic; signal DendR: std_logic_vector(8 downto 0); signal DsorR: std_logic_vector(2 downto 0); signal Sub: std_logic_vector(4 downto 0); signal State, nxState: integer range 0 to 6; begin Sub <= Add4 (DendR(8 downto 5), not(0 & DsorR), 1); C<=sub(4); Qent<=DendR(4 downto 0); Process (state, st, C) Begin V<= 0; Sh<= 0; Su<=0; Ld<=0; Case state is When 0=> if (St=1) then Ld<=1; nxState<=1; Else nxstate<=0; end if; When 1=> if(C=1) then V<=1; nxstate<=0; Else Sh<=1; nxState<=2; end if; When 2|3|4|5 => if (C=1) then Su<=1; nxstate<=State; Else Sh<=1; nxstate<=state + 1; end if; When 6 => if (C<=1) then Su<=1; end if; nxState<=0; end case; end process; 228

process(Clk) begin if (Clk=1 and Clkevent) then state<=nxState; if (Ld=1) then DendR<=0 & dend; DsorR<=dsor; end if; If (Sh=1) then DendR<= DendR (7 downto 0) & 0; end if; If (Su=1) then DendR(8 downto 5) <=sub(3 downto 0); DendR(0)<=1; end if; End if; End process; End divider; Although algorithms exist to divide the signed numbers directly, such algorithms are rather complex. So we take the easy way out and complement the dividend and divisor if they are negative; when division is complete, we complement the quotient if it should be negative. Figure 9 shows a block diagram for the divider. We use a 16-bit bus to load the registers. Since the dividend is 32 bits, two clocks are required to load the upper and lower halves of the dividend register, and one clock is needed to load the divisor. An extra sign flip-flop is used to store the sign of the dividend. We will use a dividend register with a built-in 2s complementer. The subtracter consists of an adder and a complementer, so subtraction can be accomplished by adding the 2s complement of the divisor to the dividend register. If the divisor is negative, using a separate step to complement it is unnecessary; we can simply disable the complementer and add the negative divisor instead of subtracting its complement. The control network is divided into two parts a main control, which determines the sequence of shifts and subtracts, and a counter, which counts the number of shifts. The counter outputs a signal K=1 when 15 shifts have occurred. Control signals are defined as follows:

LdU LdL

Load upper half of dividend from bus. Load lower half of dividend from bus. 229

Lds S Cml Ldd Su Cm2 Sh C St V Qneg

Load sign of dividend into sign flip-flop. Sign of dividend. Complement dividend register(2s complement) Load divisor from bus Enable adder output onto bus (Ena) and load upper half of dividend from bus. Enable complementer. (Cm2 equals the complement of the sign bit of the divisor, so a positive divisor is complemented and a negative divisor is not.) Shift the dividend register left one place and increment the counter Carry output from adder. (If C=1, the divisor can be cubtracted from the upper dividend.) Start. Overflow. Quotient will be negative. (Qneg = 1 when the sign of the dividend and divisor are different.) Figure 9 Block diagram for Signed Divider

230

Dbus Data in 16 16 Cm1


Sh Ld1 Ldu

Dividend Acc(Remainder) Q(Quotient) 16 16 16-bit Full Adder Cin Compout Cm2 16 Ena Cm2 4-bit Counter K

St

Cout

Main Control

16-bit Complementor 16 Divisor 16

Ldd

S Sign

Lds

The procedure for carrying out the signed division is as follows: 1. Load the upper half of the dividend from the bus, and copy the sign of the dividend into the sign flip-flop. 2. Load the lower half of the dividend from the bus. 3. Load the divisor from the bus. 4. Complement the dividend if it is negative. 5. If an overflow condition is present, go to the done state. 6. Else carry out the division by a series of shifts and subtracts. 7. When division is complete, complement the quotient if necessary, and go to the done state. Testing for overflow is slightly more complicated than for the case of unsigned division. First, consider the case of all positive numbers. Since the divisor and quotient are each 15

231

bits plus sign, their maximum value is 7FFFh. Since the remainder must be less than the divisor, its maximum value is 7FFEh. Therefore, the maximum dividend for no overflow is Divisor X quotient + remainder = 7FFFh X 7FFFh + 7FFEh = 3FFF7FFFh If the dividend is 1 large (3FFF8000h), division by 7FFFh (or anything smaller) will give an overflow. We can test for the overflow condition by shifting left one place and then comparing the upper half of the dividend (divu) with the divisor. If divu divisor, the quotient would be greater than the maximum value, which is an overflow condition. For the preceding example, shifting 3FFF8000h left once gives 7FFF0000h. Since 7FFFh equals the divisor, there is an overflow. On the other hand, shifting 3FFF7FFFh left gives 7FFEFFFEh, and since 7FFEh<7FFFh, no overflow occurs when dividing by 7FFFh. Another way of verifying that we must shift the dividend left before testing for overflow is as follows. If we shift the dividend left one place and then divu divisor, we could subtract and generate a quotient bit of 1. However, this bit would have to go in the sign bit position of the quotient. This would make the quotient negative, which is incorrect. After testing for overflow, we must shift the dividend left again, which gives a place to store the first quotient bit after the sign bit. Since we work with the complement of negative dividend or a negative divisor, this method for detecting overflow will work for negative numbers except for the special case where the dividend is 8000000h (the largest negative value). Modifying the design to detect overflow in this case is left as an exercise. Figure 10 shows the state graph for the control network. When St=1, the registers are loaded. In S2, if the sign of the dividend (S) is 1, the dividend is complemented. In S3, we shift the dividend left one place and then we test for overflow in S4. If C=1, subtraction is possible, which implies an overflow, and the network goes to he done state. Otherwise, the dividend is shifted left. In S5, C is tested. If C=1, then Su=1, which implies Ldu and Ena, so the adder output is enabled onto the bus and loaded into the upper dividend register to accomplish the subtraction. Otherwise, Sh=1 and the dividend register is shifted. This continues until K=1 at Figure 10 State Graph for Signed Divider Control Network

232

St/0

S0 Rd y

St/Ldu Lds

S1

-/Ld1

S2

S/Col Ldd

S3

S/Ldd

C/V

-/Sh

CQneg/0 CQneg/Col
KC/S h

S3 KC/Sh C/Sh S4

S5

C/Su

C/Su

233

which time the last shift occurs if C=0, and the network goes to S6. Then if the sign of the divisor and the saved sign of the dividend are different, the dividend register is complemented so that the quotient will have the correct sign. The VHDL code for the signed divider is shown in Figure 11. Since the 1s complementer and adder are combinational networks, we have represented their operation by concurrent statements. ADDVEC is executed any time ACC or compout changes, so Sum and carry are immediately recomputed. All the signals that represent register outputs are updated on the rising edge of the clock, so these signals are updated in the process after waiting for CLK to change to 1. For example, ADDVEC is called in states 2 and 6 to store the 2s complement of the dividend back into the dividend register. The counter is simulated by an integer signal, count. For convenience in listing the simulator output, we have added a ready signal (Rdy), which is turned on in S0 to indicate that the division is completed. Figure 11 VHDL Model of 32-bit Signed Divider library BITLIB; use BITLIB.bit_pack.all; entity sdiv is port(Clk,St: in bit; Dbus: in bit_vector(15 downto 0); Quotient: out bit_vector(15 downto 0); V, Rdy: out bit); end sdiv; architecture Signdiv of Sdiv is constant zero_vector: bit_vector(31 downto 0):=(others=>'0'); signal State: integer range 0 to 6; signal Count : integer range 0 to 15; signal Sign,C,NC: bit; signal Divisor,Sum,Compout: bit_vector(15 downto 0); signal Dividend: bit_vector(31 downto 0); alias Q: bit_vector(15 downto 0) is Dividend(15 downto 0); alias Acc: bit_vector(15 downto 0) is Dividend(31 downto 16); begin compout <= divisor when divisor(15) = '1' else not divisor; 234 -- concurrent statements -- 1's complementer

Addvec(Acc,compout,not divisor(15),Sum,C,16); -- 16-bit adder Quotient <= Q; Rdy <= '1' when State=0 else '0'; process begin wait until Clk = '1'; case State is when 0=> if St = '1' then Acc <= Dbus; Sign <= Dbus(15); State <= 1; V <= '0'; Count <= 0; end if; when 1=> Q <= Dbus; State <= 2; when 2=> Divisor <= Dbus; if Sign ='1'then end if; State <= 3; when 3=> Dividend <= Dividend(30 downto 0) & '0'; Count <= Count+1; State <= 4; when 4 => if C ='1' then v <= '1'; State <= 0; else 235 -- left shift -- two's complement Dividend if necessary addvec(not Dividend,zero_vector,'1',Dividend,NC,32); -- load lower dividend -- initialize overflow -- initialize counter -- load upper dividend -- wait for rising edge of clock

Dividend <= Dividend(30 downto 0) & '0'; Count <= Count+1; State <= 5; end if; when 5 => if C = '1' then ACC <= Sum; Q(0)<= '1'; else Dividend <= Dividend(30 downto 0) & '0'; if Count = 15 then count<= 0; State <= 6; else Count <= Count+1; end if; end if; when 6=> if C = '1' then Acc <= Sum; Q(0) <= '1'; else if (Sign xor Divisor(15))='1' then end if; state <= 0; end if; end case; end process; end signdiv;

-- left shift

-- subtract

-- left shift -- KC'

-- C -- subtract -- C'Qneg -- 2's complement Dividend

addvec(not Dividend,zero_vector,'1',Dividend,NC,32);

We are now ready to test the divider design by using the VHDL simulator. We will need a comprehensive set of test examples that will test all the different special cases that can arise in the division process. To start with, we need to test the basic operation of the divider for all the different combinations of signs for the divisor and dividend (+ +, + -, - +, and - -). We also need to test the overflow detection for these four cases. Limiting cases must also be tested, including largest quotient, zero quotient, etc. Use of a VHDL test bench is convenient because the test data must be supplied in sequence at certain times, and the length of time to complete the division is dependent on the test data. Figure 12 shows a test bench for the 236

divisor. The test bench contains a dividend array and a divisor array for the test data. The notation X 07FF00BB is the hexadecimal representation of a bit string. The process in testsdiv first puts the upper dividend on Dbus and supplies a start signal. After waiting for the clock, it puts the lower dividend on Dbus. After the next clock, it puts the divisor on the Dbus. It then waits until the Rdy signal indicates that that division is complete before continuing. Count is set equal to the loop-index, so that the change in Count can be used to trigger the listing output. Figure 12 Test Bench for Signed Divider library BITLIB; use BITLIB.bit_pack.all; entity testsdiv is end testsdiv; architecture test1 of testsdiv is component sdiv port(Clk,St: in bit; Dbus: in bit_vector(15 downto 0); Quotient: out bit_vector(15 downto 0); V, Rdy: out bit); end component; constant N: integer := 12; type arr1 is array(1 to N) of bit_vector(31 downto 0); type arr2 is array(1 to N) of bit_vector(15 downto 0); constant dividendarr: arr1 := (X"0000006F", X"07FF00BB", X"FFFFFE08", X"FF80030A", X"3FFF8000", X"3FFF7FFF", X"C0008000", X"C0008000", X"C0008001", X"00000000", X"FFFFFFFF", X"FFFFFFFF"); constant divisorarr: arr2 := (X"0007", X"E005", X"001E", X"EFFA", X"7FFF", X"7FFF", X"7FFF", X"8000", X"7FFF", X"0001", X"7FFF", X"0000"); signal CLK, St, V, Rdy: bit; signal Dbus, Quotient, divisor: bit_vector(15 downto 0); signal Dividend: bit_vector(31 downto 0); signal count: integer range 0 to N; -- test sdiv1 N times

237

begin CLK <= not CLK after 10 ns; process begin for i in 1 to N loop St <= '1'; Dbus <= dividendarr(i)(31 downto 16); wait until rising_edge(CLK); Dbus <= dividendarr(i)(15 downto 0); wait until rising_edge(CLK); Dbus <= divisorarr(i); St <= '0'; dividend <= dividendarr(i)(31 downto 0); --save dividend for listing divisor <= divisorarr(i); wait until (Rdy = '1'); count <= i; end loop; end process; sdiv1: sdiv port map(Clk, St, Dbus, Quotient, V, Rdy); end test1; --save index for triggering --save divisor for listing

238

DESIGNING WITH POROGRAMMABLE GATE ARRAYS AND CPLDs By: Dr K S Gurumurthy, UVCE, Bangalore

e-Notes for the lectures on VTU EDUSAT Programme

239

2.0 DESIGNING WITH POROGRAMMABLE GATE ARRAYS AND CPLDs We are familiar with the digital design using ICs. The designs and the circuits realized using the SSI, MSI or LSI ICs are far better than the circuits built using discrete devices for more than one reasons. These designs are fixed and the design will be of a moderate complexity. In order to vary the design one has to go for ICs, which can be configured. And also in order to attack a complex design you need ICs with more component density in the range of a VLSI IC. This chapter addresses these issues. The programmable ICs like ROMs, PLAs, PALs, and PLDs have been discussed in the earlier chapter. PLDs are the small component density ICs. Using these ICs a sequential network can be implemented but not the entire digital system. This is where the utility of Programmable Gate Arrays (PGAs) and Complex Programmable Logic Devices (CPLDs) will be appreciated. These are more flexible and versatile. A single IC can be used to implement a complete digital system. Even a small microprocessor could be implemented. A typical PGA is an IC that contains an array of identical logic cells with programmable inter connections. The user can program the functions to be realized by each logic cell and the connections between the cells. Hence these are called F (field) PGAs. FPGAs are readily available VLSI chips. There are a number of companies, which make these FPGAs. This chapter describes the internal structure of some typical XILINX (one of the companies) FPGAs. Methods for programming these ICs to implement digital logic are discussed. As an example, the implementation of the electronic Dice game is discussed. The chapter concludes with a brief explanation to the CPLDs made by ALTERA Company. 2.2 Programmable ICs Programmable ICs are the ones, which can be programmed unlike an Application Specific IC (ASIC). There are two types of programmable ICs.i) Programmable Logic Devices (PLDs) and ii) Field Programmable Gate Arrays (FPGAs). There is hardly any distinction between the two. PLDs have fewer gates compared to FPGAs. So FPGAs are used for complex digital system design. 2.3 FPGAs These are the ICs, which can be programmed as per the requirements of a user. These are off-the shelves available VLSI chips. These use a regular CMOS technology. Therefore they dissipate less power and occupy less silicon area. A system designer can program this IC as per the needs of his or her specifications. A VLSI foundry fabricates FPGAs with some connections missing on the silicon inside the IC package. The designer performs the design entry and simulation. Next, special software creates a string of bits describing the connections required to complete the design. This is called the configuration file. This has to be down loaded to the IC from a PC to program the chip. FPGAs are ideal for prototyping systems or for low-volume productions. FPGA vendors do not need an IC foundry. Instead they supply their design data (in GDS-II format) to the silicon foundry. This type of design usually called a FAB less design. Being FAB less relieves the FPGA vendors of huge investment on their production units in the foundry. A new sub micron silicon foundry costs hundreds of crores. Because of this advantage FPGA companies put their entire effort into the FPGA architecture (new designs) and the software. All FPGAs have certain key elements in general. They have regular array of logic cells, which can be reconfigured. There are two levels of programmability in FPGAs. The structure of each of the logic cell can be changed. This is INTRA cell programming. 240

The programmed logic cells can be interconnected as per the design specifications. This is INTER logic cell programming. The chip inputs and outputs use special INPUT and OUTPUT (I/Os) logic cells. The third key element of any FPGA is a programmable interconnect scheme. This provides the wiring between the two programmed logic cells. The custom software is an important component of FPGA design. The programming technology in a FPGA determines the type of basic logic cell and the interconnect scheme. The program may or may not be permanent. Thus one has one-time programmable and re-programmable technologies. There are 3 main programming technologies, proposed by 3 major companies. 1) Antifuse from ACTEL company 2) SRAM from Xilinx company 3) EPROM from ALTERA company 2.3.1The Antifuse: This is the opposite of a regular fuse that is normally used in .The regular fuse is blown to open the existing contact. An antifuse is programmed to make the connection out of a normally open circuit by blowing it. The antifuse comprises of ONO (Oxide- Nitride- Oxide) dielectric layers sandwiched between the two semiconductor layers. The structure is shown in the Fig 2.1.

Fig 2.1 Actels Antifuse structure This antifuse will offer high resistance before programming and its resistance comes down to around 100 Ohms after the programming. So this is called Programmable Low Impedance Element (PLICE). Any two logic blocks can be connected with this element. The designer simulates the design, after entering the design. The iteration of redesign and the simulation goes on until the designer achieves the specifications. He stores the configuration file in a PC. Now the chip is plugged into a socket on a special programming box, called an Activator. Configuration file is down loaded from the PC to the Activator. Activator generates the programming voltages, which will blow the necessary antifuses on the chip. This is permanent programming and chip is taken out of the Activator. Then it is assembled into the system.

241

The disadvantage of this procedure is to put the IC into activator and take it out. This might damage thin metal leads. The other technologies have a feature known as In- SystemProgramming (ISP). ISP can be done after the chip is assembled to the system. 2.3.2 Static RAM: In this technology, each connection point on the FPGA has an associated configuration memory cell. The cell is shown in Fig2.2

Fig.2.2Configuration Memory Cell Fig 2.2 Configuration memory cell

The configuration memory cells are programmed after power has been applied to the FPGA. The programmed logic functions and inter connections are retained until the power is turned off. During configuration, each memory cell is selected in turn. When a WRITE signal is applied to the transistor, DATA (1/0) is stored in the cell. The configuration control drives the gates of the other transistors on the chip- either turning pass transistors or transmission gates on to MAKE or BREAK a connection as shown in Fig 2.3. From Configuration Control

CLB1 Fig.2.3 Configuration control driving a pass transistor This programming technology is adopted by XILINX Company. The designers can reuse the chips during the prototyping and system can be built using ISP. The disadvantage is that the SRAM cell is volatile. This can be overcome by storing the configuration file in a PROM, which is part of the system. The total size of an SRAM cell plus the transistor switch that the cell drives is larger than the antifuse used by ACTEL Company. 242

2.3.3 EPROM and EEPROM technology: The programmable devices manufactured by ALTERA (MAX 5000 EPLDs) and XILINX (EPLDs) use this technology. EPROM and EEPROM transistors are based on hot-electron injection into the floating gate (Floating gate Avalanche MOS-FAMOS). With the injection of the electrons, the threshold voltage of the transistor will be increased. This puts the programmed transistor in the OFF state. This state can be brought back to original state by flashing UV light on the gate. This takes away the electrons trapped in the floating gate and the transistor threshold voltage falls back to its original value. The EPROM transistor is shown in Fig 2.4. +VGS +VGS +VGS +VGS +Vds
0 0000

+Vpp

00000

+Vds

00 0

+Vds
00

Id s
a) Vtn1 b) Hot electron Injection

Ids=0

Ids

c) Vtn2>Vtn1

d) Vtn1

Fig2.4 EPROM transistor Programming In Fig2.4 (a) the device has a threshold Vtn1 and the device is ON. In Fig 2.4 (b) the device is being programmed by applying Vpp on the drain, and VGS on the control gate. Hot electrons move towards the floating gate. Fig 2.4 (c) shows that the trapped electrons in the floating gate which increases the threshold to Vtn2 (Vtn1). The transistor enters the CUT OFF state. By shining UV light on the gate, the trapped electrons leave the floating gate as shown in Fig2.4 (d). The device comes back to the original state with a threshold voltage Vtn1. 2.4 XILINX-3000 SERIES FPGAs As an example, Xilinx XC 3020 IC is taken up. The architecture of this IC is shown in Fig 2.5. It comprises of 64 configuration logic blocks (CLBs) arranged in an 8 x 8 array, surrounded by a ring of 64 input-output (I/O) interface blocks. The interconnections between these blocks can be programmed by storing data in a RAM cell. Each CLB contains some combinational logic and two D flip-flops. CLB can be programmed to perform a variety of logic functions.

243

FIG 2.5 Layout part of a XC3000 series FPGA 2.4.1 Xilinx 3000 series CLB: Fig 2.6 shows a CLB of XC3000 series FPGA. It has 5 logic inputs (A, B, C, D, E), a data input (DI), a clock input (K), a clock enable (EC), a direct reset (DR) and two outputs (X and Y). The trapezoidal blocks are programmable multiplexers. These can be programmed using configuration memory cell to select the inputs. Each block with letter M attached to the multiplexer represents 1 bit configuration memory cell. The combinational function block contains RAM memory cells and can be programmed to realize any function of 5 variables or 2 functions of 4 variables. The functions are stored in the truth table form. This block can be operated in 3 different mode namely, FG, F and FGM modes. These modes are shown in the Fig 2.7.the FG mode generates two functions of four variables each. One variable (A) must be common to both functions. The next two variables can be chosen from B, C, QX, and QY. The remaining variable can be either D or E. For example, one can generate F= AB + QX E and G= AC + QYD. If QX and QY are not used, then the two 4 variable functions must have A, B, and C in common, and the fourth variable can be D or E. The F mode can generate one function of 5 variables (A, D, E and 2 variables to be selected from B, C, Qx and Qy). Functions ranging in complexity from a simple 5 input AND gate to parity function F = G = A B C D E (this has 16 SOPs).

244

Fig2.6 XC3000 Series CLB The FGM mode uses a multiplexer at the output with E as a select line to select one of 2 functions of 4 variables. As an example, F = G = E (AB+QxD) + E (AB+QxD). The D input on each flip-flop can be programmed to come from F, G or the DI data input. The 2 flip-flops have a common clock. The D flip-flop and MUX combination is equivalent to a D flip-flop with an enable clock (EC) input as shown in Fig.2.8. DI DI 0

D Q
EC CLK

D Q
CE D-FF

EC CLK

Fig.2.8 Flip-flop with Clock Enable Since Q can change only when EC = 1, the characteristic equation described by the flipflops behavior is Q+ = ECD + ECQ. These flip-flops have an active high synchronous reset (RD). The direct reset input will clear both flip-flops when it is set to 1. The global reset will clear the flip-flop in all of the cells in the array. 2.4.2 Input Output Blocks Fig 2.9 shows a configurable input- output block (IOB). The I/O pad connects to one of the pins on the IC package so that external signals can be input to or output from the array of logic cells. To use the cell as an input, the 3-state control must be set to place the tristate buffer, which drives the output pin, in the high-impedance state. To use the cell as an output, 245

the tristate buffer must be enabled. Flip-flops are provided so that input and output values can be stored within the IOB.The flip-flops are bypassed when direct input or output is desired. Two clock lines (CK1 and CK2) can be programmed to connect to either flip-flop. The input flip-flop can be programmed to act as an edge triggered D flip-flop or as a transparent latch. Even if the I/O pin is not used, I/O flip-flops can still be used to store data. An OUT signal coming from the logic array, first goes through an ex-or gate, where it is either complimented or not, depending on how the OUT-INVERT bit is programmed. The OUT signal can be stored in the flip-flop if desired. Depending on how the OUTPUTSELECT bit is programmed, either the OUT signal or the flip-flop out put goes to the output buffer. If the 3-STATE signal is 1 and the 3 STATE INVERT bit is 0, the output buffer has a high impedance output. Otherwise, the buffer drives the output signal to the I/O pad. When the I/O pad is used as an input, the output buffer must be in the high impedance state. An external signal coming into the I/O pad goes through a buffer and then to the input of a D flip-flop. The buffer output provides a DIRECT IN signal to the logic array. Alternatively, the input signal can be stored in the D flip-flop, which provides the REGISTRERD IN signal to the logic array. Each IOB has a number of I/O options, which can be selected by configuration memory cells. The input threshold can be programmed to respond to either TTL or CMOS signal levels. The SLEW RATE bit controls the rate at which the output signal can change. When the output drives an external device, reduction of the slew rate is desirable to reduce the induced noise that can occur when the output changes rapidly. When the PASSIVE PULL-UP bit is set, the pull-up resistor is connected to the I/O pad. This internal pull up resistor can be used to avoid floating inputs.

Fig 2.9 Xilinx 3000 series I/O Block 246

2.4.3 Programmable Interconnects The programmable interconnections between the CLBs and I/O blocks can be made several ways. 1) General-purpose interconnect 2) Direct interconnect 3) Long lines. Fig 2.10 shows the general purpose interconnect system.

in

Fig 2.7 CLBs Operating modes

247

Signal between CLBs or between CLBs and IOBs can routed through switch matrices as they travel along the horizontal and vertical interconnect lines.

Fig 2.10 General-purpose interconnects Direct interconnection of adjacent CLBs is possible as shown in Fig.2.11.

Fig.2.11 Direct Interconnects between adjacent CLBs Long lines are provided to connect CLBs that are far apart. All the interconnections are programmed by storing bits in internal configuration memory cells within the LCA (logic cell array). Long lines provide for high fan-out, low-skew distribution of signals that must 248

travel a relatively long distance. These long line interconnects are as shown in the Fig 2.12. There are 4 vertical long lines between each pair of adjacent columns of CLBs, and 2 of these can be used for clocks. There are 2 horizontal long lines between each pair of adjacent rows of CLBs. The long lines span the entire length or width of the interconnection area. Each logic cell has 2 adjacent tristate buffers that connect to the horizontal long lines. Designers can use these long lines and buffers to implement tristate busses.

Fig.2.12 Vertical and Horizontal long lines *************************************************************

249

2.6.2ALTERA FLEX 10K SERIES CPLDs This embedded programmable logic family provides high-density logic along with RAM memory in each device. The logic and interconnections are programmed using configuration RAM cells in a manner similar to the Xilinx FPGAs. Figure 2.20 shows the block diagram for a FLEX 10K device. Each row of the logic array contains several logic array blocks (LABs) and an embedded array block (EAB). Each LAB contains 8 logic elements and a local interconnect channel. The EAB contains 2048 bits of RAM memory. The LABs and EABs can be interconnected through fast row and column interconnect channels. These are referred to as Fast Track Interconnects. Each input output element (IOE) can be used as an input, output, or bi-directional pin. Each IOE contains a bi-directional buffer and a flip-flop that can be used to store either input or output data. A single FLEX 10K device provides from 72 to 624 LABs, 3 to 12 EABs, and up to 406 IOEs. It can utilize from 10,000 to 100,000 equivalent gates in a typical application.

250

251

Fig.2.20 Block Diagram of FLEX 10K Device

Figure 2.21 shows a block diagram for a FLEX 10K lab. This contains 8 logic elements (LEs). The local interconnect channel has 22 or more inputs from the row interconnects and 8 inputs fed back from the LE outputs. Each LE has 4 data inputs from the local interconnect channel as well as the additional control inputs. The LE outputs can be routed to the row or column interconnects. Connection can also be made between the row and the column interconnects.

Each logic element as shown in Figure 2.22 contains a function generator that can implement any function of 4variables using a look up table (LUT). A cascade chain provides connections to adjacent LEs. Thus, functions of more than 4 variables can be implemented. The cascade chain can be used in an AND or in an OR configuration.

252

Fig 2.21 FLEX 10K Logic Array Block

253

Fig2.22 Flex 10K Logic Element

The Figure2.23 shows an embedded array block.

The inputs from the row interconnect go through the EAB local interconnect and can be utilized as data inputs or address inputs to the EAB. The internal memory array can be used as a RAM or ROM of size 256X8, 512X4, 1024X2, or 2048X1. Several EABs can be used together to form a larger memory. The memory data outputs can be routed to either the row or column interconnects. All memory inputs and outputs are connected

254

to registers so that the memory can be operated in a synchronous mode. Alternatively, the registers can be bypassed and the memory operated asynchronously.

255

Fig.2.23 FLEX 10K Embedded Array Block

The FLEX 10K series is a high density IC. This can be used to implement a complex digital system such as a micro controller.

2.7 SUMMARY

In this chapter, the following topics have been discussed:

1. 2. 3. 4. 5.

The meaning of FPGA and the types based on programming technology. The architecture of Xilinx 3000 and 4000 series FPGAs. The flow of FPGA design. The basics of CPLDs The architecture of Altera 7000 series and FLEX 10K CPLDs. 256

Questions and Answers

Q.1 What is VHDL? A: VHDL- Very high speed IC Hardware Description Language. This is a high level language (like C) used to model the digital circuits as simple as a 2 I/P AND gate to a complex complete system.

Q.2 What is the difference between alternate clock buffer and the global clock buffer? A: Alternate clock buffer drives a horizontal long line. This in turn can be used to drive vertical long lines and the clock (K) inputs to the logic blocks. The Global clock buffer drives a global Network. This provides a high fan-out, synchronized clock to all the IOBs and logic blocks (CLBs).

Q.3 Can we set a pin in a FPGA as input and output pin? A: In Xilinx 3000 and 4000 series any pin can be configured as input or output pin. This is done by supplying the correct output enable signal to the output buffer. In case of ALTERA CPLDs any pin can be configured as in or out or bi-directional pin with the help of I/O control block.

257

REFERENCES 1.www.xilinx.com 2.www.altera.com 3.Digital System Design Using VHDL By Charles H Roth, Jr. Thomson Brooks/Cole, 2004 4.Application Specific Integrated Circuits, By Michael John Sebastian Smith. Pearson Education Asia, 2001 *********************************************************************

258

DIGITAL DESIGN WITH SM CHARTS

By: Dr K S Gurumurthy, UVCE, Bangalore

e-Notes for the lectures VTU EDUSAT Programme

259

DIGITAL DESIGN WITH SM CHARTS The utility of these graphs to define state machines for controlling adders, multipliers and dividers has been shown in earlier chapters. As an alternative to state graphs, state machine chart (SM) may be used to describe the behavior of a state machine. This is a special type of flow chart, called state machine flow chart. SM charts are often used to design control units for digital systems. In this chapter we first start with a brief introduction to state machines. Then we describe the properties of SM charts and how these are used in the design of state machines. Then two examples are discussed. One for a multiplier and the second for a dice game controller. Then construction of VHDL descriptions of these systems directly from the respective SM charts is dealt with. We then proceed with the design and show how PLA tables and logic equations can be derived from SM charts. Showing how alternative designs can be obtained by transforming the SM charts concludes the chapter. 1.1Logic circuits: The classification of logic circuits is given below Logic circuits Combinational circuits sequential circuits (State machines) asynchronous mode.

Synchronous or clock

Fundamental mode

pulse mode

A combinational circuit is a circuit whose outputs are determined totally by its external inputs, on other hand a circuit is sequential if its output are determined not only by its external inputs but also by the past history of the circuit. 1.1.1State machines: Another name for a sequential logic circuit is a state machine. Since the storage capacity or number of bistable devices is not infinite in a sequential logic circuit, the term finite state machine is commonly used. There are three types of circuit models for synchronous state machines. Each model is different because of its output method. The circuit model in fig 1.1 is called a synchronous state machine with Moore outputs. Moore type external outputs are dependant on only the present state of the circuit. They are independent of the external inputs to the circuit.

260

Input combinational logic.

Bistable memory device

Output combinatio nal logic.

System clock Fig 1.1 Moore type machine The external outputs in the synchronous state machine circuit model shown in fig 1.2 are dependant on both the external inputs and the present state of the circuit. These are called Mealy state machines.

Input Combinationa l logic.

Bistable memory device

Output combinatio nal logic.

System clock Fig 1.2 Mealy type machine. The circuit model illustrated in fig 1.3 is a synchronous state machine with both Moore and Mealy type external outputs is called a mixed type machine.

Input combinational logic.

Bistable memory device

Output combinatio nal logic.

System clock Fig 1.3 Mixed type machine. 261

1.2 State machine charts Flow charts are useful not only in software design but also in the hardware design of digital systems. In this section the usefulness of MS charts in hardware design of digital system is discussed. SM chart is also called an algorithmic state machine (ASM) chart. It is easier to understand the operation of digital system by inspection of the SM chart compared to a state graph. In order to have completely specified proper state graph in which the next state is always uniquely defined for every input combination, the following constraints on the input labels for every state Sk are placed. 1. If Ii and Ij are any pair of input labels on arcs exiting state Sk, then IiIj = 0 if I is not equal to j. With respect to the following figure the conditions are: (X1) (X1 X2) = 0 (X1) (X1 X2) = 0 (X1 X2) (X1 X2) = 0

2. If n arcs exit state Sk and the n arcs have input labels I1, I2,In, respectively, then I1 + I2 +. + In =1 The conditions for a proper state graph are automatically satisfied for an SM chart. A given SM chart can be converted into several equivalent forms, and each form leads directly to a hardware realization. An SM chart is different from an ordinary flow chart. Certain specific rules must be followed in constructing the SM chart. When these rules are followed, the SM chart is equivalent to a state graph, and it directly leads to a hardware realization.

262

1.3 Components of an SM chart: The fig 1.4 shows the 3 principal components of an SM chart, namely state box, decision box and the conditional output box. (Optional state code) xxx State_ name/ Output list c) (true branch) 1 Con diti on (false branch) 0 Conditional output list

a) State Box

b) Decision Box

c) Conditional Output Box

Fig.1.4 Components of an SM chart 1.3.1 State Box: A state box represents the state of the system. The state box contains a state name followed by a slash (/) and an optional output list. After a state assignment has been made, a state code may be placed outside the box at the top. 1.3.2 Decision Box: This box will have two branches. The condition placed in the box is a Boolean expression that is evaluated to determine which branch to select. 1.3.3 The conditional output box: It contains a conditional output list. The conditional outputs depend on both the state of the system and the inputs. 1.4 Construction of SM charts: SM chart is constructed from SM blocks. Each SM block contains exactly one state box, together with the decision boxes and conditional output boxes associated with that state. An SM block has one entrance path and one or more exit paths. Each SM block describes the machine operation during the time the machine is in one state. When a digital system enters the state associated with a given SM block, the outputs on the output list in the state box become true. The conditions in the decision boxes are evaluated to determine which path is followed through SM block. When a conditional output box is encountered along such a path, the corresponding outputs become true. If an output is not encountered along a path, that output is false by default. A path through an SM block from entrance to exit is referred to as a link path.

263

1.5 Example of a SM block: The example of SM block is given in fig.1.5. 0ne entrance path One-s S1/Z1, Z2, 0 Link path a X1 One- state 1 Link path b

Z3,Z4 0 1
X 2

0
Z5

1 X 3 1 To Fig1.5

2 3 next states S1-State name, Z1, Z2.List of outputs, X1, X2Inputs.

When the machine enters the stat S1, outputs Z1 and Z2 become 1. If input X1=0, Z3 and Z4 become 1. If X1 = X2 = 0, at the end of the state time the machine goes to the next via exit path1. On the other hand, if X1 = 1 and X3 = 0, the output Z5 = 1, and exit to the next state will occur via exit path 3. Since Z3 and Z4 are not encountered along this link path, Z3 = Z4 =0 by default.

264

Equivalent SM block: A given SM block can generally be drawn in several different formats. The following two figures are equivalent SM blocks.

S1/Z1

S1/Z1

0 0 X1 1

X 2

Z2

X1 0 Z2

1 X1 0
Z2

X2 S3/

S2/

S2/ Fig 1.6 Equivalent SM Blocks

S3/

265

1.5 Example of an ASM chart: A S-R latch built by NOR gates is taken as an example. Fig 1.7 shows the latch, the state diagram and the ASM chart for the SR latch. S R 0 1 1 1 S 00 Q 00 10 10
a/ 0
b / 0

00 10

R a) S-R Latch

a/ S
b/0

. R

10 b) State diagram

c) ASM Chart

Fig.1.7 S-R Latch

1.4 Derivation of SM charts: In this section, method used to design and construct an SM chart for a given sequential control network is discussed. Two examples of SM charts are taken up. They are i) Binary multiplier ii) An electronic dice game. Conversion of an SM chart to a VHDL code process is discussed at the end of this section. 1.4.1 Construction of SM charts: The construction of an SM chart for a sequential control network is similar to that used to derive a state graph. Steps are: i) First draw a block diagram of the system that we are controlling. ii) Define the required input and output signals to the control network. iii) Then construct the chart that tests the input signals and generates the proper sequence of output signals.

266

1.5 Binary Multiplier: The first example that is discussed is Binary multiplier. As per the procedure, the block diagram of the 4-bit * 4-bit multiplier is shown in fig 1.8 Product Accumulator C O N T R O L Load SH ADD Multiplier Done M Multiplicand Fig 1.8 Block diagram for the Multiplier 1.7.1Design: In this section a multiplier for unsigned binary numbers is discussed and designed. If the product aimed at is A*B, the first operand A is called multiplicand and the second operand is called multiplier. Binary multiplication requires shifting and adding operations. Illustration: Let us take an example where in A = 12 (multiplicand) and B =11(multiplier) 1 1 0 0 *1 0 1 1 1 1 0 0 (multiply by M = 1) 1 1 0 0 - (multiply by M = 1 and shift) 100100 0000-(add) (multiply by M = 0 and shift) DONE ST Clk Cm 4-bit Adder 8 7 6 5 4 3 2 1 0

1 0 0 1 0 0 (add) 1 1 0 0 - - - (multiply by M = 1 and shift) 10000100 This is equivalent to 132 in decimal.

267

Multiplication of two 4 bit numbers requires 1. a 4-bit multiplicand register, 2. a 4-bit multiplier register, 3. a 4-bit full adder, and 4. an 8-bit register for the product. The product register serves as an accumulator to accumulate the sum of partial products. If the multiplicand were shifted left each time before it was added to the accumulator, as was done in the example, an 8-bit adder would be needed. So it is better to shift the contents of the product register to the right each time, as shown in the block diagram of the Figure 2.3.This type of multiplier is sometimes called a serial-parallel as the multiplier bits are processed serially and the addition takes place in parallel. 4 bits from the ACC and 4 bits from the multiplicand register are connected to the adder inputs. The 4 sum bits from the multiplicand register are connected back to the ACC. When an add signal (Ad) occurs, the adder outputs transferred to the ACC by the next clock pulse, thus causing the multiplicand to be added to the accumulator. The carry output from Cm is temporarily stored at the extra bit (8th) at the left end of the product register. When a shift signal (Sh) occurs, all 9 bits of ACC are shifted right by the next clock pulse. Since the lower 4 bits of the product register are initially unused, the multiplier is stored in this location instead of storing in a separate register. As each multiplier bit is used, it is shifted out to the right end of the register to make room for additional product bits. A shift signal (SH) is used to shift the contents of the product register. Shifting occurs at the every clock pulse. The control circuit puts out the proper sequence of add and shift signals after a start signal (ST=1) has been received. If the current multiplier bit (M) is 1, the multiplicand is added to the accumulator followed by a right shift. If the multiplier bit is 0, the addition is skipped, and only the right shift occurs. The multiplication example (of 12*11) is reworked below showing the location of the bits in the register at each clock time. Initial contents of register Add multiplicand(12)since M=1 After addition After shift Add multiplicand since M=1 After addition After shift Skip addition since M=0 After shift Add multiplicand since M=1 After addition After shift (final answer) 132 8 7 6 5 product 0 0 0 0 0 0 1 0 0 1 0 1 1 0 1 0 1 0 1 0 1 1 1 1 1 0 0 1 1 0 0 0 0 1 0 1 0 0 0 0 0 4 3 2 1 0 0 1 0 1 1 0 0 0 0 0 1 0 0 0 0 1 0 0 0 1 1 0 0 1 1 0 0 0 1 1 0 0 1 0 0 0 1 1 1 0 1 1 0 M(11)

Fig 1.9 Illustration Binary Multiplier. The control circuit must be designed to output the proper sequence of add and shift signals. 268

The fig 1.10 shows a state graph for the control circuit. In this figure S0is the reset state, and the network stays in S0 until a start signal (ST=1) received. This generates a load signal, which causes the multiplier to be loaded into the lower 4 bits of the accumulator (4<<) and the upper 5 bits of the accumulator to be cleared. In state S1, the low order bits of the multiplier (M) are tested. If M=1, an add signal is generated, and if M=0, a shift signal is generated. Similarly, in states S3, S5 andS7, the current multiplier bit (M) is tested to determine whether to generate an add or shift signal. A shift signal is always generated at the next clock time following an add signal (states S2, S4, & S8). After 4 shifts have been affected, the control network goes to S9, and a done signal is generated returning to S0. 1.7.2 SM chart for control of the binary multiplier: Fig1.10 shows the multiplier control St M Done Load Ad Sh

Add or shift control


K

Counter
Fig 1.10 Multiplier control.

269

The add-shift control generates the required sequence of add and shift signals. The counter counts the number of shift and outputs K=1 just before the last shift occurs. The SM chart for the multiplier control is shown in fig 1.12.

S0/
0

St

1 Load S1/ 0 1

M
Sh 0 K 1 S3/Done 1 Ad S2/sh 0 K

Fig 1.11 SM chart for binary multiplier In state S0, when the start signal St is 1, the registers are loaded. In S1, the multiplier bit M is tested. If M=1, an add signal is generated and the machine goes to state S2. if M=0, a shift signal is generated and K is tested. If K=1, this will be last shift and the next state is S3. in S2, a shift signal is generated as a shift must always follow an add. If K=1, the network goes to S3 at the time of the last shift; otherwise, the network goes break to S1. In S3 the done signal is turned ON. Conversion of an SM chart to a VHDL process is straightforward. A case statement can be used to specify what happens in each state. Each condition box corresponds directly to an if statement. The VHDL code is given in Fig 1.13

270

1.8 EXAMPLE NO.2: ELECTRONIC DICE GAME: This is a game based on a dice, which will have 6 faces with numbers 1,2,3,4,5,6. In this game two dices will be thrown, and depending on the sum of the numbers seen on the faces of the dices, the result is decided. One can come out with many conditions to decide whether a player has won or not. The purpose of this example is to design a circuit that will simulate the above said idea. Fig.1.14 shows the block diagram for the dice game. Two counters are used to simulate the ROLL of DICE. Each counter counts in the sequence 1,2,3,4,5,6,1,Thus after the ROLL of the DICE the SUM of the values in the two counters will be in the range 2 through 12.The rules of the game are as follows: 1. After the first ROLL of the DICE, the player wins if the SUM is 7 or 11.Th player LOSES if the SUM is 2,3 or 12. Otherwise the SUM the player obtained on the first ROLL is referred to as a POINT, and the player must ROLL the DICE again. 2. On the second or subsequent ROLL of the DICE, the player WINS if the SUM is 7. Otherwise, the player must ROLL again until he or she finally WINS or LOSES or RESETS (starts a new game).

The inputs to the DICE game come from push buttons RB (Roll Button) and RESET. RESET is used to initiate a new game. When the ROLL button is pushed, the DICE counters count at high speed. So the values cannot be read on the display. When the Roll Button is released the values in the two counters are displayed and the game can proceed. If the WIN light is not on, the player must push the Roll Button again.

271

VHDL CODE for Binary Multiplier SM Chart: entity Multi is port (CLK, St, K, M: in bit; Load, Sh.Ad, Done: out bit); end Mult; architecture SMbehave of Mult is signal state, Nextstate: integer range 0 to 3; begin process (St, K, M, State) --start if state or inputs change begin Load<=0; Sh<= 0; Ad<=0; case State is When 0 => if St = 1 then --St (state 0) Load <= 1; Nextstate <= 1; else Nextstate <=0; --St end if; When 1=> if M = 1 then --M (state 1) Ad<= 1 Next state <=2; else Sh<= 1 if K = 1 then Nextstate <=3; --K else Nextstate <= 1 --K end if; end if; When 2 => Sh <= 1 (-- state 2) if K = 1 then Nextstate <=3; --K else Nextstate <=1; --K end if; When 3 => Done <= 1; -- (stat3) Nextstate <= 0; end case; end process; Process (CLK) begin if CLK = 1 then State <= Nextstate; --update state on rising edge end if; end process; end SMbehave;

272

Fig1.12 VHDL code for binary multiplier SM chart

Display

Display ROLL

RB

1-6 counter

1-6 counter

Adder Sum Test Logic

D7

D7, 11 D2, 3,12 Eq Sp

C O N T R O L

RESET

WIN

POINT Register

Comparator

LOSE

Fig 1.13 Block diagram of the Electronic Dice Game

273

Flow chart for DICE game: It is given in fig 1.14. After rolling the dice, the sum is tested. If it is 7 or 11, the player wins, if it is 2,3 or 12 the player loses. Otherwise the sum is saved in the point register, and the player wins. If it is 7, he or she loses. Otherwise the player rolls again. After winning or giving it up the player must push reset button again to begin a new game. The components of the dice game are: Adder-ads the output of two counters Register-stores the POINT Test Logic- determines the condition for a WIN or LOSE Control Network- Controls the dice game Roll dice S Y = 7/ 11 Store sum in point Roll Dice S = Y N P oi nt Win S u m Lose R Y R es Y et N N e s Y Y N N N S= 2,3 ,12 Y

e Fig 1.14 Flow chart for Dice game t

274

Input signals to the control network are defined as follows: D7 = 1 If the sum of the dice (output of the counters) is 7 D7, 11 = 1 If the sum of the dice is 7 or 11 D2, 3,12 = 1 If the sum of the dice is 2,3 or 12 Eq = 1 If the sum of the dice equals the number stored in the Point Register. RB = 1 When the reset button is pressed Outputs from the control network are defined as follows: ROLL = 1 Enables the dice counters Sp = 1 Causes the sum to be stored in the Point Register Win = 1 turns on the win light Lose = 1 turns on the Lose light Using the control signals defined above, SM chart is derived and the same is shown in Fig 1.15.The control network waits in state S0 until the roll button is pressed (RB = 1). Then it goes to state S1, and the ROLL counters are enabled as long as RB = 1. As soon as the ROLL button is released (RB = 0), D7, 11 is tested. If the sum is 7 or 11, the network goes to states S2 and turns on the Win light; otherwise, D2, 3,12 is tested. If the sum is 2,3,or 12, the network goes to state S3 and turns on the Lose light; otherwise, the signal Sp becomes 1 and the sum is stored in the Point register. It then enters S4 and waits for the player to ROLL the dice again. In S3, after the ROLL button is released, if Eq = 1, the sum equals the Point and state S2 is entered to indicate a Win. If D7 = 1, the Sum is 7 and S3 is entered to indicate a Lose. Otherwise, control returns to S4 so that the player can roll again. When in S2 or S3, the game is reset to S0 when the reset button is pressed. End of section 1.8 Fig 1.15 SM chart for Dice game Please refer to the annexure -1 1.9 Realization of SM charts: The realization consists of a combinational sub network, together with Flip-flop storing the state of the network. As an example SM chart for binary multiplier is taken up. This SM chart is given in Fig1.12.The controller for the multiplier can be realized using a PLA and D flip-flops. The controller chart has 4 states and you need 2 (2 = 4) D flip-flops. The PLA table is given table1.1.

275

Table1.1 PLA table for Multiplier Controller. So A 0 0 0 0 0 1 S2 S3 1 1 0 1 1 1 0 1 0 0 0 1 0 0 0 0 1 B 0 0 1 1 1 0 St 0 1 M 0 0 1 K 0 1 0 A+ 0 0 0 1 1 0 B+ 0 1 1 1 0 1 Load Sh 0 0 1 0 0 0 0 0 1 1 0 1 Ad 0 0 0 0 1 0 Done 0 0 0 0 0 0

S 1

The PLA has 5 inputs and 6 outputs. Each row in the table corresponds to one of the link paths in the SM chart. Since So has two exit paths the table has two rows for present state So. The first row corresponds to the St = 0 exit path. So the next state and output are 0. In the second row, St = 1, so the next state is a dont care in the corresponding rows. The outputs for each row can be filled in by tracing the corresponding link paths on the SM chart. For example, the link path from S1 to S2 passes through conditional output Ad, so Ad = 1 in this row. Since S2 has a Moore output Sh, Sh = 1 in both of the rows for which AB = 10. By inspection of the PLA table, the logic equations for the multiplier control are: A+ = ABMK + ABM + ABK; B+ = ABSt + ABM + AB; Load = ABSt P Sh = ABSt = AB; Ad = ABM; Done = AB St

L A

Load Sp 276

M K

Ad Done D Q

Clock Fig. 1.19 PLA realization of Multiplier control 1.10 Linked State Machines When a sequential machine becomes large and complex, it is desirable to divide the machine up into several smaller machines that are linked together. Each of the smaller machines is easier to design and implement. Also, one of the submachines may be called in several different places by the main machine. This is analogous to dividing a large software program into procedures that are called by the main program. Figure 1.20 shows the SM charts for two serially linked state machines. The machine (Machine A) executes a sequence of some states until it is ready to call the submachine (machine B). When state SA is reached, the output signal ZA activates machine B. Machine B then leaves its idle state and executes a sequence of other states. When it is finished, it outputs ZB before returning to the idle state. When machine A receives ZB, it continues to execute other states. These two machines are assumed to have a common clock. Machine A Machine B

SOME STATES

IDLE 0
Z A

SA/ZA 0 1 OTHER STATES


Z B

1 OTHER STATES

SB/ZB 277

Figure 1.20 SM charts for serially linked State Machines

278

1.11 Conclusions In this Chapter the following topics have been discussed. i) An introduction to SM charts and basic of SM charts. ii) Procedure to design digital system based on the constructed SM chart. a) Draw the block diagram of the digital system. b) Represent the control unit by an SM chart. c) Write the behavioral VHDL code based on this chart. iii) Hardware implementation using PLAs. a) PLA tables and equations are derived by tracing link paths on an SM chart. b) Using the next state equations D flip-flops are used with PLA to arrive at Moore/Mealy machines for the control circuit designs. iv) PLA size can be reduced by transforming the SM chart into a form in which only one input is tested in each state. v) However this generally increases the number of states and slows down the operation of the system. vi) For complex systems, we can split the control unit into several sections by using linked state machines. References: 1. Charles H ROTH, Jr.: Digital Systems Design Using VHDL; Thomson, Books/Cole, 2004 2. Richard S. Sandige: Modern Digital Design; McGRAW- HILL International Editions; 1990 3. J. Bhaskar: VHDL Primer; Pearson Education Asia; III Edition.

Annexure -1

S0/

0 1 Roll S1/

R b

R b
D E R R

279

S2/WIN Roll

7 q S5/ b b

Sp

1 0 1 D7 11 D 23 12 0 S3/Lose
S4/

0 1

Res et

1 0 1 0 0 0 1

R e 1 s

Fig 1.15 SM chart foreDice game This is to accompany the eNotes on SM charts by Dr. K S Gurumurth, UVCE Bangalore t

280

You might also like