1|Page VHDL PROGRAMMING By Prashant Pacific Module I Fundamental VHDL Units , LIBRARY Declarations, ENTITY, ARCHITECTURE, Introductory Examples, Specification of combinational systems using VHDL, Introduction to VHDL, Basic language element of VHDL, Behavioral Modeling, Data flow modeling, Structural modeling, Subprograms and overloading, VHDL description of gates. What the F*** is VHDL? VHDL is an acronym for VHSlC Hardware Description Language (VHSIC is an acronym for Very High Speed Integrated Circuits). It is a hardware description language that can be used to model a digital system at many levels of abstraction ranging from the algorithmic level to the gate level. The VHDL language can be regarded as an integrated amalgamation of the following languages: sequential language + concurrent language + net-list language + timing specifications + waveform generation language => VHDL Fundamental VHDL Units A standalone piece of VHDL code is composed of at least three fundamental sections: LIBRARY declarations: Contains a list of all libraries to be used in the design. For example: ieee, std, work, etc. ENTITY: Specifies the I/O pins of the circuit. ARCHITECTURE: Contains the VHDL code proper, which describes how the 2|Page LIBRARY Declarations To declare a LIBRARY (that is, to make it visible to the design) two lines of code are needed, one containing the name of the library, and the other a use clause, as shown in the syntax below. LIBRARY library_name; USE library_name.package_name.package_parts; At least three packages, from three di¤erent libraries, are usually needed in a design: ieee.std_logic_1164 (from the ieee library), standard (from the std library), and work (work library). Their declarations are as follows: LIBRARY ieee; USE ieee.std_logic_1164.all; LIBRARY std; USE std.standard.all; -- A semi-colon (;) indicates -- the end of a statement or -- declaration, while a double -- dash (--) indicates a comment. LIBRARY work; USE work.all; The libraries std and work shown above are made visible by default, so there is no need to declare them; only the ieee library must be explicitly written. However, the latter is only necessary when the STD_LOGIC (or STD_ULOGIC) data type is employed in the design (data types will be studied in detail in the next chapter). The purpose of the three packages/libraries mentioned above is the following: the std_logic_1164 package of the ieee library specifies a multi-level logic system; std is a resource library (data types, text i/o, etc.) for the VHDL design environment; and the work library is where we save our design (the .vhd file, plus all files created by the compiler, simulator, etc.). ENTITY An ENTITY is a list with specifications of all input and output pins (PORTS) of the circuit. Its syntax is shown below. ENTITY entity_name IS PORT ( port_name : signal_mode signal_type; port_name : signal_mode signal_type; ...); END entity_name; Example: Let us consider the NAND gate of figure 2.4. Its ENTITY can be specified as: 3|Page The mode of the signal can be IN, OUT, INOUT, or BUFFER. As illustrated in figure 2.3, IN and OUT are truly unidirectional pins, while INOUT is bidirectional. BUFFER, on the other hand, is employed when the output signal must be used (read) internally. The type of the signal can be BIT, STD_LOGIC, INTEGER, etc. Data types will be discussed LATER. Finally, the name of the entity can be basically any name, except VHDL reserved words (VHDL reserved words are listed in appendix E). The meaning of the ENTITY above is the following: the circuit has three I/O pins, being two inputs (a and b, mode IN) and one output (x, mode OUT). All three signals are of type BIT. The name chosen for the entity was nand gate. 4|Page HALF ADDER entity HALF_ADDER is port (A, B: in BIT; SUM, CARRY: out BIT); end HALF_ADDER; FULL ADDER entity FULL_ADDER is port (A, B, CIN: in BIT; SUM, COUT: out BIT); end FULL_ADDER; 2x4 DECODER entity DECODER2x4 is port (A, B, ENABLE: in BIT; Z: out BIT_VECTOR(0 to 3)); end DECODER2x4; MUX 2x1 entity MUX2x1 is Port ( A : in std_logic; B : in std_logic; S : in std_logic; Y : out std_logic); end MUX2x1; DFF entity DFF is port ( D, CLK, RST: in STD_LOGIC; Q: out STD_LOGIC); end DFF; 5|Page ARCHITECTURE The ARCHITECTURE is a description of how the circuit should behave (function). Its syntax is the following: ARCHITECTURE architecture_name OF entity_name IS [declarations] BEGIN (code) END architecture_name; As shown above, an architecture has two parts: a declarative part (optional), where signals and constants (among others) are declared, and the code part (from BEGIN down). Like in the case of an entity, the name of an architecture can be basically any name (except VHDL reserved words), including the same name as the entity’s. The internal details of an entity are specified by an architecture body using any of the following modeling styles: 1. As a set of interconnected components (to represent structure), 2. As a set of concurrent assignment statements (to represent dataflow), 3. As a set of sequential assignment statements (to represent be-hav.ior), 4. Any combination of the above three. Structural modeling In the structural style of modeling, an entity is described as a set of interconnected components. architecture identifier of entity_name is architecture_declarative_part begin architecture_statement_part end [ architecture_simple_name ] ; architecture_declarative_part ::= { block_declarative_item } architecture_statement_part ::= { concurrent_statement } block_declarative_item ::= subprogram_declaration subprogram_body type_declaration subtype_declaration constant_declaration signal_declaration alias_declaration component_declaration 6 | P a ADDER ge HALF architecture HA_STRUCTURE of HALF_ADDER is component XOR2 port (X, Y: in BIT; Z: out BIT); end component; component AND2 port (L, M: in BIT; N: out BIT); end component; begin X1: XOR2 port map (A, B, SUM); A1: AND2 port map (A, B, CARRY); end HA_STRUCTURE; FULL ADDER architecture FA_STRUCT of FULL_ADDER is component XOR1 port (A,B: in BIT; S1: out BIT); end component; component XOR2 port (S1,CIN: in BIT; SUM: out BIT); end component; component AND1 port (CIN,S1: in BIT; N1: out BIT); end component; component AND2 port (A,B: in BIT; N2: out BIT); end component; component OR port (N1,N2: in BIT; COUT: out BIT); end component; begin X1: XOR1 port map (A, B, S1); A1: XOR2 port map (S1,CIN,SUM); A1: AND1 port map (CIN,S1,N1); A2: AND2 port map (A,B,N2); 01: OR1 port map (N1,N2,COUT) end FA_STRUCT; 2x4 DECODER architecture DEC_STR of DECODER2x4 is component INV port (A: in BIT; Z: out BIT); end component; component NAND3 port (A, B, C: in BIT; Z: out BIT); end component; signal ABAR, BBAR: BIT; begin I0: INV port map (A, ABAR); I1: INV port map (B, BBAR); N0: NAND3 port map (ABAR, BBAR, ENABLE, Z(0)); N1: NAND3 port map (ABAR, B, ENABLE, Z(1)); 7|Page N2: NAND3 port map (A, BBAR, ENABLE, Z(2)); N3: NAND3 port map (A, B, ENABLE, Z(3)); end DEC_STR; MUX 2x1 architecture MUX_STRUCT of mux2to1 is component AND1 port (A,SEL: in BIT; S1: out BIT); end component; component INV port (SEL: in BIT; K: out BIT); end component; signal SELBAR: BIT; component AND2 port (B,SELBAR: in BIT; S2: out BIT); end component; component 0R port (S1,S2: in BIT; Z: out BIT); end component; begin I0: INV port map (SEL, SELBAR); A1: AND1 port map (A,SEL); A2: AND2 port map (SELBAR,B); OR: OR port map (S1,S2); end MUX_STRUCT DFF 8|Page Data Flow Modeling In this modeling style, the flow of data through the entity is expressed primarily using concurrent signal assignment statements. The structure of the entity is not explicitly specified in this modeling style, but it can be implicitly deduced. HALF ADDER architecture HA_dataflow of HA begin SUM <= A xor B after 8 ns; CARRY <= A and B after 4 ns; end HA_dataflow; FULL ADDER architecture FA_dataflow OF full_adder IS signal x1, x2, x3, x4, y1 : BIT; begin x1 <= a AND b; x2 <= a AND carry_in; x3 <= b AND carry_in; x4 <= x1 OR x2; Cout <= x3 OR x4; y1 <= a XOR b; S<= y1 XOR carry_in; end FA_dataflow; 2x4 DECODER architecture dec_dataflgw of DECODER2x4 is signal ABAR, BBAR: BIT; begin Z(3) <= not (A and B and ENABLE); Z(0) <= not (ABAR and BBAR and ENABLE); BBAR <= not B; Z(2) <= not (A and BBAR and ENABLE); ABAR <= not A; Z(1 ) <= not (ABAR and B and ENABLE); end DEC_DATAFLOW; 9|Page MUX 2x1 architecture MUX_dataflow OF MUX_2to1 IS signal x1, x2,SELBAR : BIT; begin x1 <= A AND SEL ; x2 <= SELBAR AND B; OUT <= x1 OR x2; end MUX_dataflow; DFF Behavioral Style of Modeling In contrast to the styles of modeling described earlier, the behavioral style of modeling specifies the behavior of an entity as a set of statements that are executed sequentially in the specified order. This set of sequential statements, that are specified inside a process statement, do not explicitly specify the structure of the entity but merely specifies its functionality. A process statement is a concurrent statement that can appear within an architecture body. HALF ADDER 10 | P a g e FULL ADDER 2x4 DECODER Architecture dec_seq of DECODER2x4 is begin process (A, B, ENABLE) variable ABAR, BBAR: BIT; begin ABAR := not A; BBAR := not B; if (ENABLE = '1') then Z(3) <= not (A and B): Z(0) <= not (ABAR and BBAR); Z(2) <= not (A and BBAR); Z(1 ) <= not (ABAR and B); else Z<= "1111"; end if; end process; end; MUX 2x1 11 | P a g e DFF architecture LS_DFF_BEH of LS_DFF is begin process (D, CLK) begin if (CLK = '1') then Q <= D; end if; end process; end LS_DFF_BEH; Basic elements of the VHDL language 1. 2. 3. 4. Data objects Literals Operators Object declarations (how to associate types with objects) 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: 12 | P a g e 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 O to 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. 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; 13 | P a g e 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. 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 — This is a comment; it ends at the end of this line. — To continue a comment onto a second line, a separate — 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 TYPES(We have complete MODULE 2 on this…So We’ll study this later in Detail) 14 | P a g e OPERATORS In the order of Increasing Precedence 1.Logical 2.Relational 3.Adding 4.Multiplying 5.Misc. 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 onedimensional 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. 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. 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. Same goes for mod and rem. 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 Idirnensional array type or an element type. The result is always an array type. For example, '0' & '1' results in an array of characters "01". 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 15 | P a g e LITERALS 1.Comments Comments in VHDL start with two adjacent hyphens (‘--’) and extend to the end of the line. They have no part in the meaning of a VHDL description. 2.Identifiers Identifiers in VHDL are used as reserved words and as programmer defined names. They must conform to the rule: identifier ::= letter { [ underline ] letter_or_digit } Note that case of letters is not considered significant, so the identifiers cat and Cat are the same. Underline characters in identifiers are significant, so This_Name and ThisName are different identifiers. 3.Numbers Literal numbers may be expressed either in decimal or in a base between two and sixteen. If the literal includes a point, it represents a real number, otherwise it represents an integer. Decimal literals are defined by: decimal_literal ::= integer [ . integer ] [ exponent ] integer ::= digit { [ underline ] digit } exponent ::= E [ + ] integer | E - integer Some examples are: 0 1 123_456_789 987E6 -- integer literals 0.0 0.5 2.718_28 12.4E-9 -- real literals Based literal numbers are defined by: based_literal ::= base # based_integer [ . based_integer ] # [ exponent ] base ::= integer based_integer ::= extended_digit { [ underline ] extended_digit } 2-2 The VHDL Cookbook extended_digit ::= digit | letter The base and the exponent are expressed in decimal. The exponent indicates the power of the base by which the literal is multiplied. The letters A to F (upper or lower case) are used as extended digits to represent 10 to 15. Some examples: 2#1100_0100# 16#C4# 4#301#E1 -- the integer 196 2#1.1111_1111_111#E+11 16#F.FF#E2 -- the real number 4095.0 4.Characters Literal characters are formed by enclosing an ASCII character in single-quote marks. For example: 'A' '*' ''' ' ' 5.Strings Literal strings of characters are formed by enclosing the characters in double-quote marks. To include a double-quote mark itself in a string, a pair of double-quote marks must be put together. A string can be used as a value for an object which is an array of characters. 16 | P a g e 6.Bit Strings VHDL provides a convenient way of specifying literal values for arrays of type bit ('0's and '1's, see Section 2.2.5). The syntax is: bit_string_literal ::= base_specifier " bit_value " base_specifier ::= B | O | X bit_value ::= extended_digit { [ underline ] extended_digit } Base specifier B stands for binary, O for octal and X for hexadecimal. Some examples: B"1010110" -- length is 7 O"126" -- length is 9, equivalent to B"001_010_110" SUBPROGRAMS There are two kinds of subprograms: procedures and functions. Both procedures and functions written in VHDL must have a body and may have declarations. Procedures perform sequential computations and return values in global objects or by storing values into formal parameters. Functions perform sequential computations and return a value as the value of the function. Functions do not change their formal parameters. Subprograms may exist as just a procedure body or a function body. Subprograms may also have a procedure declarations or a function declaration. When subprograms are provided in a package, the subprogram declaration is placed in the package declaration and the subprogram body is placed in the package body. OVERLOADING VHDL allows two subprograms to have the same name, provided the number or base types of parameters differs. The subprogram name is then said to be overloaded. When a subprogram call is made using an overloaded name, the number of actual parameters, their order, their base types and the corresponding formal parameter names (if named association is used) are used to determine which subprogram is meant. If the call is a function call, the result type is also used. For example, suppose we declared the two subprograms: function check_limit(value : integer) return boolean; function check_limit(value : word_32) return boolean; Then which of the two functions is called depends on whether a value of type integer or word_8 is used as the actual parameter. 17 | P a g e So test := check_limit(4095) would call the first function, and test := check_limit(X"0000_0FFF") would call the second function. The designator used to define a subprogram can be either an identifier or a string representing any of the operator symbols listed in Section2.3. The latter case allows extra operand types to be defined for those operators. For example, the addition operator might be overloaded to add word_32 operands by declaring a function: function "+" (a, b : word_32) return word_32 is begin return int_to_word_32( word_32_to_int(a) + word_32_to_int(b) ); end "+"; Within the body of this function, the addition operator is used to add integers, since its operands are both integers. However, in the expression: X"1000_0010" + X"0000_FFD0" the newly declared function is called, since the operands to the addition operator are both of type word_32. Note that it is also possible to call operators using the prefix notation used for ordinary subprogram calls, for example: "+" (X"1000_0010", X"0000_FFD0") 18 | P a g e VHDL PROGRAMMING By Prashant Pacific Module II Data Types; Pre-Defined Data Types, User-Defined Data Types, Subtypes, Arrays, Port Array, Records, Signed and Unsigned Data Types, Data Conversion Pre-Defined Data Types VHDL contains a series of pre-defined data types, specified through the IEEE 1076 and IEEE 1164 standards. More specifically, such data type definitions can be found in the following packages / libraries: PACKAGE standard of library std std_logic_1164 of library ieee std_logic_arith of library ieee std_logic_signed and std_logic_unsigned of library ieee DATA TYPES Defines BIT, BOOLEAN, INTEGER, and REAL data types. Defines STD_LOGIC and STD_ULOGIC data types. Defines SIGNED and UNSIGNED data types, plus several data conversion functions, like conv_integer(p), conv_unsigned(p, b), conv_signed(p, b), and conv_std_logic_vector(p, b). Contain functions that allow operations with STD_LOGIC_VECTOR data to be performed as if the data were of type SIGNED or UNSIGNED, respectively. User-Defined Data Types VHDL also allows the user to define his/her own data types. Two categories of userdefined data types are shown below: integer and enumerated. User-defined integer types: TYPE my_integer IS RANGE -32 TO 32; -- A user-defined subset of integers. TYPE student_grade IS RANGE 0 TO 100; -- A user-defined subset of integers or naturals. User-defined enumerated types: TYPE state IS (idle, forward, backward, stop); -- An enumerated data type, typical of finite state machines. TYPE color IS (red, green, blue, white); -- Another enumerated data type. TYPE my_logic IS ('0', '1', 'Z'); -- A user-defined subset of std_logic. 19 | P a g e The encoding of enumerated types is done sequentially and automatically (unless specified otherwise by a user-defined attribute. A SUBTYPE is a TYPE with a constraint. The main reason for using a subtype rather than specifying a new type is that, though operations between data of di¤erent types are not allowed, they are allowed between a subtype and its corresponding base type. Examples: SUBTYPE natural IS INTEGER RANGE 0 TO INTEGER'HIGH; -- As expected, NATURAL is a subtype (subset) of INTEGER. SUBTYPE my_logic IS STD_LOGIC RANGE '0' TO 'Z'; -- Recall that STD_LOGIC=('X','0','1','Z','W','L','H','-'). -- Therefore, my_logic=('0','1','Z'). SUBTYPE my_color IS color RANGE red TO blue; -- Since color=(red, green, blue, white), then -- my_color=(red, green, blue). SUBTYPE small_integer IS INTEGER RANGE -32 TO 32; -- A subtype of INTEGER. Arrays Arrays are collections of objects of the same type. They can be one-dimensional (1D), two-dimensional (2D), or one-dimensional-by-one-dimensional (1Dx1D). They can also be of higher dimensions, but then they are generally not synthesizable. To specify a new array type: TYPE type_name IS ARRAY (specification) OF data_type; To make use of the new array type: SIGNAL signal_name: type_name [:= initial_value]; In the syntax above, a SIGNAL was declared. However, it could also be a CONSTANT or a VARIABLE. Notice that the initial value is optional (for simulation only). 20 | P a g e Port Array As we have seen, there are no pre-defined data types of more than one dimension. However, in the specification of the input or output pins (PORTS) of a circuit (which is made in the ENTITY), we might need to specify the ports as arrays of vectors. Since TYPE declarations are not allowed in an ENTITY, the solution is to declare user-defined data types in a PACKAGE, which will then be visible to the whole design (thus including the ENTITY). Records Records are similar to arrays, with the only di¤erence that they contain objects of di¤erent types. Example: TYPE birthday IS RECORD day: INTEGER RANGE 1 TO 31; month: month_name; END RECORD; Signed and Unsigned Data Types As mentioned earlier, these types are defined in the std_logic_arith package of the ieee library. Their syntax is illustrated in the examples below. Examples: SIGNAL x: SIGNED (7 DOWNTO 0); SIGNAL y: UNSIGNED (0 TO 3); Notice that their syntax is similar to that of STD_LOGIC_VECTOR, not like that of an INTEGER, as one might have expected. To use SIGNED or UNSIGNED data types, the std_logic_arith package, of the ieee library, must be declared. Despite their syntax, SIGNED and UNSIGNED data types are intended mainly for arithmetic operations, that is, contrary to STD_LOGIC_VECTOR, they accept arithmetic operations. On the other hand, logical operations are not allowed. With respect to relational (comparison) operations, there are no restrictions. Example: Legal and illegal operations with signed/unsigned data types. Example: Legal and illegal operations with std_logic_vector. LIBRARY ieee; USE ieee.std_logic_1164.all; USE ieee.std_logic_arith.all; -- extra package necessary ... SIGNAL a: IN SIGNED (7 DOWNTO 0); SIGNAL b: IN SIGNED (7 DOWNTO 0); SIGNAL x: OUT SIGNED (7 DOWNTO 0); ... v <= a + b; -- legal (arithmetic operation OK) w <= a AND b; -- illegal (logical operation not OK) LIBRARY ieee; USE ieee.std_logic_1164.all; -- no extra package required ... SIGNAL a: IN STD_LOGIC_VECTOR (7 DOWNTO 0); SIGNAL b: IN STD_LOGIC_VECTOR (7 DOWNTO 0); SIGNAL x: OUT STD_LOGIC_VECTOR (7 DOWNTO 0); ... v <= a + b; -- illegal (arithmetic operation not OK) w <= a AND b; -- legal (logical operation OK) 21 | P a g e Despite the constraint mentioned above, there is a simple way of allowing data of type STD_LOGIC_VECTOR to participate directly in arithmetic operations. For that, the ieee library provides two packages, std_logic_signed and std_logic_unsigned, which allow operations with STD_LOGIC_VECTOR data to be performed as if the data were of type SIGNED or UNSIGNED, respectively. Example: Arithmetic operations with std_logic_vector. LIBRARY ieee; USE ieee.std_logic_1164.all; USE ieee.std_logic_unsigned.all; -- extra package included SIGNAL a: IN STD_LOGIC_VECTOR (7 DOWNTO 0); SIGNAL b: IN STD_LOGIC_VECTOR (7 DOWNTO 0); SIGNAL x: OUT STD_LOGIC_VECTOR (7 DOWNTO 0); ... v <= a + b; -- legal (arithmetic operation OK), unsigned w <= a AND b; -- legal (logical operation OK) DATA CONVERSIONS VHDL does not allow direct operations (arithmetic, logical, etc.) between data of different types. Therefore, it is often necessary to convert data from one type to another. This can be done in basically two ways: or we write a piece of VHDL code for that, or we invoke a FUNCTION from a pre-defined PACKAGE which is capable of doing it for us. If the data are closely related (that is, both operands have the same base type, despite being declared as belonging to two different type classes), then the std_logic_1164 of the ieee library provides straightforward conversion functions. Example: Legal and illegal operations with subsets. TYPE long IS INTEGER RANGE -100 TO 100; TYPE short IS INTEGER RANGE -10 TO 10; SIGNAL x : short; SIGNAL y : long; ... y <= 2*x + 5; -- error, type mismatch y <= long(2*x + 5); -- OK, result converted into type long Several data conversion functions can be found in the std_logic_arith package of the ieee library. They are: conv_integer(p) : Converts a parameter p of type INTEGER, UNSIGNED, SIGNED, or STD_ULOGIC to an INTEGER value. Notice that STD_LOGIC_ VECTOR is not included. conv_unsigned(p, b): Converts a parameter p of type INTEGER, UNSIGNED, SIGNED, or STD_ULOGIC to an UNSIGNED value with size b bits. conv_signed(p, b): Converts a parameter p of type INTEGER, UNSIGNED, SIGNED, or STD_ULOGIC to a SIGNED value with size b bits. conv_std_logic_vector(p, b): Converts a parameter p of type INTEGER, UNSIGNED, SIGNED, or STD_LOGIC to a STD_LOGIC_VECTOR value with size b bits. 22 | P a g e Example: Data conversion. LIBRARY ieee; USE ieee.std_logic_1164.all; USE ieee.std_logic_arith.all; ... SIGNAL a: IN UNSIGNED (7 DOWNTO 0); SIGNAL b: IN UNSIGNED (7 DOWNTO 0); SIGNAL y: OUT STD_LOGIC_VECTOR (7 DOWNTO 0); ... y <= CONV_STD_LOGIC_VECTOR ((a+b), 8); -- Legal operation: a+b is converted from UNSIGNED to an -- 8-bit STD_LOGIC_VECTOR value, then assigned to y. 23 | P a g e VHDL PROGRAMMING By Prashant Pacific Module III Sequential codes: PROCESS: Signals and Variables, IF, WAIT, CASE, LOOP, CASE versus IF, CASE versus WHEN, Bad Clocking, Using Sequential Code to Design Combinational Circuits Description and design of sequential circuits using VHDL SEQUENTIAL CODES VHDL code is inherently concurrent. PROCESSES, FUNCTIONS, and PROCEDURES are the only sections of code that are executed sequentially. However, as a whole, any of these blocks is still concurrent with any other statements placed outside it. One important aspect of sequential code is that it is not limited to sequential logic. Indeed, with it we can build sequential circuits as well as combinational circuits. Sequential code is also called behavioral code. PROCESS A PROCESS is a sequential section of VHDL code. It is characterized by the presence of IF, WAIT, CASE, or LOOP, and by a sensitivity list (except when WAIT is used). A PROCESS must be installed in the main code, and is executed every time a signal in the sensitivity list changes (or the condition related to WAIT is fulfilled). Its syntax is shown below. [label:] PROCESS (sensitivity list) [VARIABLE name type [range] [:= initial_value;]] BEGIN (sequential code) END PROCESS [label]; VARIABLES are optional. If used, they must be declared in the declarative part of the PROCESS (before the word BEGIN, as indicated in the syntax above). The initial value is not synthesizable, being only taken into consideration in simulations. The use of a label is also optional. Its purpose is to improve code readability. The label can be any word, except VHDL reserved words. SIGNALS AND VARIABLES VHDL has two ways of passing non-static values around: by means of a SIGNAL or by means of a VARIABLE. A SIGNAL can be declared in a PACKAGE, ENTITY or ARCHITECTURE (in its declarative part), while A VARIABLE can only be declared inside a piece of sequential code (in a PROCESS, for example). Therefore, while the value of the former can be global, the latter is always local. The value of a VARIABLE can never be passed out of the PROCESS directly; if necessary, then it must be assigned to a SIGNAL. On the other hand, the update of a VARIABLE is immediate, that is, we can promptly count on its new value in the next line of code. That is not the case with a SIGNAL (when used in a PROCESS), 24 | P a g e for its new value is generally only guaranteed to be available after the conclusion of the present run of the PROCESS. SIGNAL Assignment Statement - signal-object <= expression [ after delay-value ]; VARIABLE Assignment Statement - variable-object := expression; Delta Delay A delta delay is a very small delay (infinitesimally small). It does not correspond to any real delay and actual simulation time does not advance. This delay models hardware where a minimal amount of time is needed for a change to occur, for example, in performing zero delay simulation. Delta delay allows for ordering of events that occur at the same simulation time during a simulation. Each unit of simulation time can be considered to be composed of an infinite number of delta delays. Therefore, an event always occurs at a real simulation time plus an integral multiple of delta delays. For example, events can occur at 15 ns, 15 ns+IA, 15 ns+2A, 15 ns+3A, 22 ns, 22 ns+A, 27 ns, 27 ns+A, and so on. WAIT The wait statement provides an alternate way to suspend the execution of a process. There are three basic forms of the wait statement. wait on sensitivity-list; wait until boolean-expression ; wait for time-expression ; They may also be combined in a single wait statement. For example, 25 | P a g e wait on sensitivity-list until boolean-expression for time-expression; Some examples of wait statements are wait on A, B, C; -- statement 1 wait until (A = B); -- statement 2 wait for 10ns; -- statement 3 wait on CLOCK for 20ns; -- statement 4 wait until (SUM > 100) for 50 ms; -- statement 5 IF An if statement selects a sequence of statements for execution based on the value of a condition. The condition can be any expression that evaluates to a boolean value. The general form of an if statement is if boolean-expressionthen sequential-statements [ elsif boolean-expression then sequential-statements ] [ else sequential-statements ] end if; The if statement is executed by checking each condition sequentially until the first true condition is found; then, the set of sequential statements associated with this condition is executed. The if statement can have zero or more else if clauses and an optional else clause. An if statement is also a sequential statement, and therefore, the previous syntax allows for arbitrary nesting of if statements. CASE CASE is another statement intended exclusively for sequential code (along with IF,LOOP, and WAIT). Its syntax is shown below. CASE identifier IS WHEN value => assignments; WHEN value => assignments; ... END CASE; 26 | P a g e A model for a 4*1 multiplexer using a case statement is shown next. entity MUX is port (A, B, C, D: in BIT; CTRL: in BIT_VECTOR(0 to 1); Z: out BIT); end MUX; architecture MUX_BEHAVIOR of MUX is constant MUX_DELAY: TIME := 10 ns; begin PMUX: process (A, B, C, D, CTRL) variable TEMP: BIT; begin case CTRL is when "00" => TEMP := A: when "01" => TEMP := B; when "10" => TEMP := C; when "11" => TEMP := D; end case; Z <= TEMP after MUX_DELAY; end process PMUX; end MUX_BEHAVIOR; CASE versus WHEN 27 | P a g e CASE versus IF Though in principle the presence of ELSE in the IF/ELSE statement might infer the implementation of a priority decoder (which would never occur with CASE), this will generally not happen. For instance, when IF (a sequential statement) is used to implement a fully combinational circuit, a multiplexer might be inferred instead. Therefore, after optimization, the general tendency is for a circuit synthesized from a VHDL code based on IF not to di¤er from that based on CASE. NULL The statement “null;” is a sequential statement that does not cause any action to take place and execution continues with the next statement. One example of this statement's use is in an if statement or in a case statement where for certain conditions, it may be useful or necessary to explicitly specify that no action needs to be performed. LOOP A loop statement is used to iterate through a set of sequential statements. The syntax of a loop statement is [ loop-label : ] iteration-scheme loop sequential-statements end loop [ loop-label ] ; There are three types of iteration schemes. FOR / LOOP: The loop is repeated a fixed number of times. [label:] FOR identifier IN range LOOP (sequential statements) END LOOP [label]; WHILE / LOOP: The loop is repeated until a condition no longer holds. [label:] WHILE condition LOOP (sequential statements) END LOOP [label]; 28 | P a g e EXIT: Used for ending the loop. [label:] EXIT [label] [WHEN condition]; NEXT: Used for skipping loop steps [label:] NEXT [loop_label] [WHEN condition]; Bad Clocking The compiler will generally not be able to synthesize codes that contain assignments to the same signal at both transitions of the reference (clock) signal (that is, at the rising edge plus at the falling edge). This is particularly true when the target technology contains only single-edge flip-flops (CPLDs, for example—appendix A). In this case, the compiler might display a message of the type ‘‘signal does not hold value after clock edge’’ or similar. Using Sequential Code to Design Combinational Circuits We have already seen that sequential code can be used to implement either sequential or combinational circuits. In the former case, registers are necessary, so will be inferred by the compiler. However, this should not happen in the latter case. Moreover, if the code is intended for a combinational circuit, then the complete truth-table should be clearly specified in the code. In order to satisfy the criteria above, the following rules should be observed: Rule 1: Make sure that all input signals used (read) in the PROCESS appear in its sensitivity list. Rule 2: Make sure that all combinations of the input/output signals are included in the code; that is, make sure that, by looking at the code, the circuit’s complete truthtable can be obtained (indeed, this is true for both sequential as well as concurrent code). Failing to comply with rule 1 will generally cause the compiler to simply issue a warning saying that a given input signal was not included in the sensitivity list, and then proceed as if the signal were included. Even though no damage is caused to the design in this case, it is a good design practice to always take rule 1 into consideration. With respect to rule 2, however, the consequences can be more serious because incomplete specifications of the output signals might cause the synthesizer to infer latches in order to hold their previous values. This fact is illustrated in the example below. 29 | P a g e VHDL PROGRAMMING By Prashant Pacific Module IV Standard combinational modules, Design of a Serial Adder with Accumulator, State Graph for Control Network, design of a Binary Multiplier, Multiplication of a Signed Binary Number, Design of a Binary Divider. 30 | P a g e VHDL PROGRAMMING By Prashant Pacific Module V Micro programmed Controller, Structure of a micro programmed controller, Basic component of a micro system, memory subsystem. Overview of PAL, PLA, FPGA, CPLD. Microprogramming is an orderly method of designing the control unit of a conventional computer (Wilkes 1951). The term microprogramming is based on the analogy between sequence of transfer required to execute a machine instruction and the sequence of individual instructions in conventional user program. Each step is called microinstruction and complete set of steps required to process a machine instruction is called the micro program. Block diagram for a MCU MAR - a-bit address register ROM - memory with 2a wbit words MIR - w- bit instruction register AGP - address generating logic In a microprogrammed control unit, the values of control signals are read from an appropriate address location in a ROM (instead of being generated by combinational logic gates). The contents of each address in the ROM are called a control word. Basic microprogrammed control unit (example) We use two-way branching address generation Each control word contains 64 bits of data The condition select field (CW(63:40)) contains information needed to select the input condition signal that is used to compute the address of the next instruction. In the BMCU we use a “one-hot” code to select the condition signal. 31 | P a g e If Ci is selected and Ci = 1 then address NAD field is the address of the next control word to be fetched. If Ci = 0, then the memory address [MAR] is incremented to compute the next address. Control word Every designer must observe the constraints imposed by the controller. These constraints include such things as the number of control signals that can be generated, the number of status signals that can be handled, and limits imposed by the address generation logic. The maximum number of control signals is 32. ROM size is 256 words. Address generation logic The correct sequence of control signals is obtained by generating the proper sequence of addresses at the ROM inputs. The address generating logic varies considerably from design to design and is a major contributor to the constraints imposed by the controller. Timing signals for the control unit and the data unit must be closely coordinated. 32 | P a g e The controller works best if the sequencing of addresses is relatively simple. Usually, the address generating logic circuit (AG) is a counter with the option to parallel load a new address when one wishes to jump to a new point in the control sequence. If the controller usually goes to the next higher sequential address for the next set of control signal values (increment) with just an occasional need to parallel load a new address (branch), the AG can be a low complexity circuit. Address generation logic organization (example) Address Generation Logic for BMCU can be designed using a vector multiplexer. Synthesis of microprogrammed controllers 1. Prepare a table showing all transitions from each state and the conditions that define each transition by scanning the statements in the VHDL description. 2. Make a list of the conditions from step 1. After the assignment of ROM addresses to states, some of these conditions may not be needed. 3. Identify a reset state. 33 | P a g e 4. Assign a ROM address to each state. 5. Remove the redundant signals from the condition list created in step2 and assign the condition signals to the condition inputs in an arbitrary manner. 6. Create a list of transfers during each state and a list of outputs required during each state. 7. From the list of transfers and outputs created in step 6, generate a list of control signals needed to time the transfers and outputs. 8. Draw a block diagram showing the condition and control signals produced. 9. Determine the ROM program using the information in the lists produced in steps 1-8. This procedure is similar to that performed by an assembler program. BASIC LAYOUT OF A MICROPROGRAMMED CONTROL UNIT Control memory stores a microprogram corresponding to each op-code of a conventional instruction. 34 | P a g e FPGA Key Points 1. ON-CHIP STATIC RAM LOADED WITH CONFIGURATION BIT PATTERNS (sram-fpgas). (volatile) 2. ANTIFUSE-PROGRAMMED DEVICES PROGRAMMED ELECTRICALLY TO PROVIDE CONNECTIONS THAT DEFINE CHIP CONFIGURATION 3. ARRAY-STYLE eprom and eeprom PROGRAMMED DEVICES USING SEVERAL plas AND A SHARED INTERCONNECT MECHANISM A field-programmable gate array (FPGA) is an integrated circuit designed to be configured by the customer or designer after manufacturing—hence "field-programmable". The FPGA configuration is generally specified using a hardware description language (HDL), similar to that used for an application-specific integrated circuit (ASIC) (circuit diagrams were previously used to specify the configuration, as they were for ASICs, but this is increasingly rare). FPGAs can be used to implement any logical function that an ASIC could perform. The ability to update the functionality after shipping, partial re-configuration of the portion of the design and the low non-recurring engineering costs relative to an ASIC design (not withstanding the generally higher unit cost), offer advantages for many applications. FPGAs contain programmable logic components called "logic blocks", and a hierarchy of reconfigurable interconnects that allow the blocks to be "wired together"—somewhat like a 35 | P a g e one-chip programmable breadboard. Logic blocks can be configured to perform complex combinational functions, or merely simple logic gates like AND and XOR. In most FPGAs, the logic blocks also include memory elements, which may be simple flip-flops or more complete blocks of memory. In addition to digital functions, some FPGAs have analog features. The most common analog feature is programmable slew rate and drive strength on each output pin, allowing the engineer to set slow rates on lightly loaded pins that would otherwise ring unacceptably, and to set stronger, faster rates on heavily loaded pins on high-speed channels that would otherwise run too slow. Another relatively common analog feature is differential comparators on input pins designed to be connected to differential signaling channels. A few "mixed signal FPGAs" have integrated peripheral Analog-to-Digital Converters (ADCs) and Digitalto-Analog Converters (DACs) with analog signal conditioning blocks allowing them to operate as a system-on-a-chip. Such devices blur the line between an FPGA, which carries digital ones and zeros on its internal programmable interconnect fabric, and fieldprogrammable analog array (FPAA), which carries analog values on its internal programmable interconnect fabric. Applications of FPGAs include digital signal processing, software-defined radio, aerospace and defense systems, ASIC prototyping, medical imaging, computer vision, speech recognition, cryptography, bioinformatics, computer hardware emulation, radio astronomy, metal detection and a growing range of other areas. FPGAs originally began as competitors to CPLDs and competed in a similar space, that of glue logic for PCBs. As their size, capabilities, and speed increased, they began to take over larger and larger functions to the state where some are now marketed as full systems on chips (SoC). Particularly with the introduction of dedicated multipliers into FPGA architectures in the late 1990s, applications which had traditionally been the sole reserve of DSPs began to incorporate FPGAs instead. FPGAs especially find applications in any area or algorithm that can make use of the massive parallelism offered by their architecture. One such area is code breaking, in particular bruteforce attack, of cryptographic algorithms. FPGAs are increasingly used in conventional high performance computing applications where computational kernels such as FFT or Convolution are performed on the FPGA instead of a microprocessor. 36 | P a g e PAL Programmable AND Plane Fixed OR Plane The term Programmable Array Logic (PAL) is used to describe a family of programmable logic device semiconductors used to implement logic functions in digital circuits introduced by Monolithic Memories, Inc. (MMI) in March 1978.[1] PAL devices consisted of a small PROM (programmable read-only memory) core and additional output logic used to implement particular desired logic functions with few components. Using specialized machines, PAL devices were "field-programmable". Each PAL device was "one-time programmable" (OTP), meaning that it could not be updated and reused after its initial programming. (MMI also offered a similar family called HAL, or "hard array logic", which were like PAL devices except that they were mask-programmed at the factory.) 37 | P a g e PAL Architecture The PAL architecture consists of two main components: a logic plane and output logic macrocells. Programmable logic plane The programmable logic plane is a programmable read-only memory (PROM) array that allows the signals present on the devices pins (or the logical complements of those signals) to be routed to an output logic macrocell. PAL devices have arrays of transistor cells arranged in a "fixed-OR, programmable-AND" plane used to implement "sum-of-products" binary logic equations for each of the outputs in terms of the inputs and either synchronous or asynchronous feedback from the outputs. Output logic The early 20-pin PALs had 10 inputs and 8 outputs. The outputs were active low and could be registered or combinational. Members of the PAL family were available with various output structures called "output logic macrocells" or OLMCs. Prior to the introduction of the "V" (for "variable") series, the types of OLMCs available in each PAL were fixed at the time of manufacture. (The PAL16L8 had 8 combinational outputs and the PAL16R8 had 8 registered outputs. The PAL16R6 had 6 registered and 2 combinational while the PAL16R4 had 4 of each.) Each output could have up to 8 product terms (effectively AND gates), however the combinational outputs used one of the terms to control a bidirectional output buffer. There were other combinations that had fewer outputs with more product term per output and were available with active high outputs. The 16X8 family or registered devices had an XOR gate before the register. There were also similar 24-pin versions of these PALs. PALs were programmed electrically using binary patterns (as JEDEC ASCII/hexadecimal files) and a special electronic programming system available from either the manufacturer or a third-party, such as DATA/IO. In addition to single-unit device programmers, device feeders and gang programmers were often used when more than just a few PALs needed to be programmed. (For large volumes, electrical programming costs could be eliminated by having the manufacturer fabricate a custom metal mask used to program the customers' patterns at the time of manufacture; MMI used the term "hard array logic" (HAL) to refer to devices programmed in this way.) 38 | P a g e PLA 39 | P a g e A programmable logic array (PLA) is a programmable device used to implement combinational logic circuits. The PLA has a set of programmable AND gate planes, which link to a set of programmable OR gate planes, which can then be conditionally complemented to produce an output. This layout allows for a large number of logic functions to be synthesized in the sum of products (and sometimes product of sums) canonical forms. One application of a PLA is to implement the control over a datapath. It defines various states in an instruction set, and produces the next state (by conditional branching). [eg. if the machine is in state 2, and will go to state 4 if the instruction contains an immediate field; then the PLA should define the actions of the control in state 2, will set the next state to be 4 if the instruction contains an immediate field, and will define the actions of the control in state 4]. Programmable Logic Arrays should correspond to a state diagram for the system. Other commonly used programmable logic devices are PAL, CPLD and FPGA. Note that the use of the word "programmable" does not indicate that all PLAs are fieldprogrammable; in fact many are mask-programmed during manufacture in the same manner as a mask ROM. This is particularly true of PLAs that are embedded in more complex and numerous integrated circuits such as microprocessors. PLAs that can be programmed after manufacture are called FPLA (Field-programmable PLA). 40 | P a g e CPLD A complex programmable logic device (CPLD) is a programmable logic device with complexity between that of PALs and FPGAs, and architectural features of both. The building block of a CPLD is the macrocell, which contains logic implementing disjunctive normal form expressions and more specialized logic operations. Features in common with PALs: Non-volatile configuration memory. Unlike many FPGAs, an external configuration ROM isn't required, and the CPLD can function immediately on system start-up. For many legacy CPLD devices, routing constrains most logic blocks to have input and output signals connected to external pins, reducing opportunities for internal state storage and deeply layered logic. This is usually not a factor for larger CPLDs and newer CPLD product families. Features in common with FPGAs: Large number of gates available. CPLDs typically have the equivalent of thousands to tens of thousands of logic gates, allowing implementation of moderately complicated data processing devices. PALs typically have a few hundred gate equivalents at most, while FPGAs typically range from tens of thousands to several million. Some provisions for logic more flexible than sum-of-product expressions, including complicated feedback paths between macro cells, and specialized logic for implementing various commonly-used functions, such as integer arithmetic. The most noticeable difference between a large CPLD and a small FPGA is the presence of on-chip non-volatile memory in the CPLD. This distinction is rapidly becoming less relevant, as several of the latest FPGA products also offer models with embedded configuration memory. The characteristic of non-volatility makes the CPLD the device of choice in modern digital designs to perform 'boot loader' functions before handing over control to other devices not having this capability. A good example is where a CPLD is used to load configuration data for an FPGA from non-volatile memory. CPLDs were an evolutionary step from even smaller devices that preceded them, PLAs (first shipped by Signetics), and PALs. These in turn were preceded by standard logic products, that offered no programmability and were "programmed" by wiring several standard logic chips together. The main distinction between FPGA and CPLD device architectures is that FPGAs are internally based on Look-up tables (LUTs) while CPLDs form the logic functions with seaof-gates (e.g. sum of products).