4

Hardware Description Languages

image

4.1 Introduction

Thus far, we have focused on designing combinational and sequential digital circuits at the schematic level. The process of finding an efficient set of logic gates to perform a given function is labor intensive and error prone, requiring manual simplification of truth tables or Boolean equations and manual translation of finite state machines (FSMs) into gates. In the 1990s, designers discovered that they were far more productive if they worked at a higher level of abstraction, specifying just the logical function and allowing a computer-aided design (CAD) tool to produce the optimized gates. The specifications are generally given in a hardware description language (HDL). The two leading hardware description languages are SystemVerilog and VHDL.

SystemVerilog and VHDL are built on similar principles but have different syntax. Discussion of these languages in this chapter is divided into two columns for literal side-by-side comparison, with SystemVerilog on the left and VHDL on the right. When you read the chapter for the first time, focus on one language or the other. Once you know one, you’ll quickly master the other if you need it.

Subsequent chapters show hardware in both schematic and HDL form. If you choose to skip this chapter and not learn one of the HDLs, you will still be able to master the principles of computer organization from the schematics. However, the vast majority of commercial systems are now built using HDLs rather than schematics. If you expect to do digital design at any point in your professional life, we urge you to learn one of the HDLs.

4.1.1 Modules

A block of hardware with inputs and outputs is called a module. An AND gate, a multiplexer, and a priority circuit are all examples of hardware modules. The two general styles for describing module functionality are behavioral and structural. Behavioral models describe what a module does. Structural models describe how a module is built from simpler pieces; it is an application of hierarchy. The SystemVerilog and VHDL code in HDL Example 4.1 illustrate behavioral descriptions of a module that computes the Boolean function from Example 2.6, image. In both languages, the module is named sillyfunction and has three inputs, a, b, and c, and one output, y.

HDL Example 4.1 Combinational Logic

SystemVerilog

module sillyfunction(input logic a, b, c, output logic y);

  assign y = ~a & ~b & ~c |

         a & ~b & ~c  |

         a & ~b &  c;

endmodule

A SystemVerilog module begins with the module name and a listing of the inputs and outputs. The assign statement describes combinational logic. ~ indicates NOT, & indicates AND, and | indicates OR.

logic signals such as the inputs and outputs are Boolean variables (0 or 1). They may also have floating and undefined values, as discussed in Section 4.2.8.

The logic type was introduced in SystemVerilog. It supersedes the reg type, which was a perennial source of confusion in Verilog. logic should be used everywhere except on signals with multiple drivers. Signals with multiple drivers are called nets and will be explained in Section 4.7.

VHDL

library IEEE; use IEEE.STD_LOGIC_1164.all;

entity sillyfunction is

  port(a, b, c: in  STD_LOGIC;

     y:    out STD_LOGIC);

end;

architecture synth of sillyfunction is

begin

  y <= (not a and not b and not c) or

    (a and not b and not c) or

    (a and not b and c);

end;

VHDL code has three parts: the library use clause, the entity declaration, and the architecture body. The library use clause will be discussed in Section 4.7.2. The entity declaration lists the module name and its inputs and outputs. The architecture body defines what the module does.

VHDL signals, such as inputs and outputs, must have a type declaration. Digital signals should be declared to be STD_LOGIC type. STD_LOGIC signals can have a value of ‘0’ or ‘1’, as well as floating and undefined values that will be described in Section 4.2.8. The STD_LOGIC type is defined in the IEEE.STD_LOGIC_1164 library, which is why the library must be used.

VHDL lacks a good default order of operations between AND and OR, so Boolean equations should be parenthesized.

A module, as you might expect, is a good application of modularity. It has a well defined interface, consisting of its inputs and outputs, and it performs a specific function. The particular way in which it is coded is unimportant to others that might use the module, as long as it performs its function.

4.1.2 Language Origins

Universities are almost evenly split on which of these languages is taught in a first course. Industry is trending toward SystemVerilog, but many companies still use VHDL and many designers need to be fluent in both. Compared to SystemVerilog, VHDL is more verbose and cumbersome, as you might expect of a language developed by committee.

SystemVerilog

Verilog was developed by Gateway Design Automation as a proprietary language for logic simulation in 1984. Gateway was acquired by Cadence in 1989 and Verilog was made an open standard in 1990 under the control of Open Verilog International. The language became an IEEE standard1 in 1995. The language was extended in 2005 to streamline idiosyncracies and to better support modeling and verification of systems. These extensions have been merged into a single language standard, which is now called SystemVerilog (IEEE STD 1800-2009). SystemVerilog file names normally end in .sv.

VHDL

VHDL is an acronym for the VHSIC Hardware Description Language. VHSIC is in turn an acronym for the Very High Speed Integrated Circuits program of the US Department of Defense.

VHDL was originally developed in 1981 by the Department of Defense to describe the structure and function of hardware. Its roots draw from the Ada programming language. The language was first envisioned for documentation but was quickly adopted for simulation and synthesis. The IEEE standardized it in 1987 and has updated the standard several times since. This chapter is based on the 2008 revision of the VHDL standard (IEEE STD 1076-2008), which streamlines the language in a variety of ways. At the time of this writing, not all of the VHDL 2008 features are supported by CAD tools; this chapter only uses those understood by Synplicity, Altera Quartus, and ModelSim. VHDL file names normally end in .vhd.

Both languages are fully capable of describing any hardware system, and both have their quirks. The best language to use is the one that is already being used at your site or the one that your customers demand. Most CAD tools today allow the two languages to be mixed, so that different modules can be described in different languages.

The term “bug” predates the invention of the computer. Thomas Edison called the “little faults and difficulties” with his inventions “bugs” in 1878.

The first real computer bug was a moth, which got caught between the relays of the Harvard Mark II electromechanical computer in 1947. It was found by Grace Hopper, who logged the incident, along with the moth itself and the comment “first actual case of bug being found.”

image

Source: Notebook entry courtesy Naval Historical Center, US Navy; photo No. NII 96566-KN

4.1.3 Simulation and Synthesis

The two major purposes of HDLs are logic simulation and synthesis. During simulation, inputs are applied to a module, and the outputs are checked to verify that the module operates correctly. During synthesis, the textual description of a module is transformed into logic gates.

Simulation

Humans routinely make mistakes. Such errors in hardware designs are called bugs. Eliminating the bugs from a digital system is obviously important, especially when customers are paying money and lives depend on the correct operation. Testing a system in the laboratory is time-consuming. Discovering the cause of errors in the lab can be extremely difficult, because only signals routed to the chip pins can be observed. There is no way to directly observe what is happening inside a chip. Correcting errors after the system is built can be devastatingly expensive. For example, correcting a mistake in a cutting-edge integrated circuit costs more than a million dollars and takes several months. Intel’s infamous FDIV (floating point division) bug in the Pentium processor forced the company to recall chips after they had shipped, at a total cost of $475 million. Logic simulation is essential to test a system before it is built.

Figure 4.1 shows waveforms from a simulation2 of the previous sillyfunction module demonstrating that the module works correctly. y is TRUE when a, b, and c are 000, 100, or 101, as specified by the Boolean equation.

image

Figure 4.1 Simulation waveforms

Synthesis

Logic synthesis transforms HDL code into a netlist describing the hardware (e.g., the logic gates and the wires connecting them). The logic synthesizer might perform optimizations to reduce the amount of hardware required. The netlist may be a text file, or it may be drawn as a schematic to help visualize the circuit. Figure 4.2 shows the results of synthesizing the sillyfunction module.3 Notice how the three three-input AND gates are simplified into two two-input AND gates, as we discovered in Example 2.6 using Boolean algebra.

image

Figure 4.2 Synthesized circuit

Circuit descriptions in HDL resemble code in a programming language. However, you must remember that the code is intended to represent hardware. SystemVerilog and VHDL are rich languages with many commands. Not all of these commands can be synthesized into hardware. For example, a command to print results on the screen during simulation does not translate into hardware. Because our primary interest is to build hardware, we will emphasize a synthesizable subset of the languages. Specifically, we will divide HDL code into synthesizable modules and a testbench. The synthesizable modules describe the hardware. The testbench contains code to apply inputs to a module, check whether the output results are correct, and print discrepancies between expected and actual outputs. Testbench code is intended only for simulation and cannot be synthesized.

One of the most common mistakes for beginners is to think of HDL as a computer program rather than as a shorthand for describing digital hardware. If you don’t know approximately what hardware your HDL should synthesize into, you probably won’t like what you get. You might create far more hardware than is necessary, or you might write code that simulates correctly but cannot be implemented in hardware. Instead, think of your system in terms of blocks of combinational logic, registers, and finite state machines. Sketch these blocks on paper and show how they are connected before you start writing code.

In our experience, the best way to learn an HDL is by example. HDLs have specific ways of describing various classes of logic; these ways are called idioms. This chapter will teach you how to write the proper HDL idioms for each type of block and then how to put the blocks together to produce a working system. When you need to describe a particular kind of hardware, look for a similar example and adapt it to your purpose. We do not attempt to rigorously define all the syntax of the HDLs, because that is deathly boring and because it tends to encourage thinking of HDLs as programming languages, not shorthand for hardware. The IEEE SystemVerilog and VHDL specifications, and numerous dry but exhaustive textbooks, contain all of the details, should you find yourself needing more information on a particular topic. (See the Further Readings section at the back of the book.)

4.2 Combinational Logic

Recall that we are disciplining ourselves to design synchronous sequential circuits, which consist of combinational logic and registers. The outputs of combinational logic depend only on the current inputs. This section describes how to write behavioral models of combinational logic with HDLs.

4.2.1 Bitwise Operators

Bitwise operators act on single-bit signals or on multi-bit busses. For example, the inv module in HDL Example 4.2 describes four inverters connected to 4-bit busses.

HDL Example 4.2 Inverters

SystemVerilog

module inv(input logic [3:0] a,

       output logic [3:0] y);

  assign y = ~a;

endmodule

a[3:0] represents a 4-bit bus. The bits, from most significant to least significant, are a[3], a[2], a[1], and a[0]. This is called little-endian order, because the least significant bit has the smallest bit number. We could have named the bus a[4:1], in which case a[4] would have been the most significant. Or we could have used a[0:3], in which case the bits, from most significant to least significant, would be a[0], a[1], a[2], and a[3]. This is called big-endian order.

VHDL

library IEEE; use IEEE.STD_LOGIC_1164.all;

entity inv is

  port(a: in STD_LOGIC_VECTOR(3 downto 0);

     y: out STD_LOGIC_VECTOR(3 downto 0));

end;

architecture synth of inv is

begin

  y <= not a;

end;

VHDL uses STD_LOGIC_VECTOR to indicate busses of STD_LOGIC. STD_LOGIC_VECTOR(3 downto 0) represents a 4-bit bus. The bits, from most significant to least significant, are a(3), a(2), a(1), and a(0). This is called little-endian order, because the least significant bit has the smallest bit number. We could have declared the bus to be STD_LOGIC_VECTOR(4 downto 1), in which case bit 4 would have been the most significant. Or we could have written STD_LOGIC_VECTOR(0 to 3), in which case the bits, from most significant to least significant, would be a(0), a(1), a(2), and a(3). This is called big-endian order.

The endianness of a bus is purely arbitrary. (See the sidebar in Section 6.2.2 for the origin of the term.) Indeed, endianness is also irrelevant to this example, because a bank of inverters doesn’t care what the order of the bits are. Endianness matters only for operators, such as addition, where the sum of one column carries over into the next. Either ordering is acceptable, as long as it is used consistently. We will consistently use the little-endian order, [N − 1:0] in SystemVerilog and (N − 1 downto 0) in VHDL, for an N-bit bus.

After each code example in this chapter is a schematic produced from the SystemVerilog code by the Synplify Premier synthesis tool. Figure 4.3 shows that the inv module synthesizes to a bank of four inverters, indicated by the inverter symbol labeled y[3:0]. The bank of inverters connects to 4-bit input and output busses. Similar hardware is produced from the synthesized VHDL code.

image

Figure 4.3 inv synthesized circuit

The gates module in HDL Example 4.3 demonstrates bitwise operations acting on 4-bit busses for other basic logic functions.

image

Figure 4.4 gates synthesized circuit

HDL Example 4.3 Logic Gates

SystemVerilog

module gates(input  logic [3:0] a, b,

        output logic [3:0] y1, y2, y3, y4, y5);

  /* five different two-input logic

   gates acting on 4-bit busses */

  assign y1 = a & b;   // AND

  assign y2 = a | b;   // OR

  assign y3 = a ^ b;   // XOR

  assign y4 = ~(a & b); // NAND

  assign y5 = ~(a | b);  // NOR

endmodule

~, ^, and | are examples of SystemVerilog operators, whereas a, b, and y1 are operands. A combination of operators and operands, such as a & b, or ~(a | b), is called an expression. A complete command such as assign y4 = ~(a & b); is called a statement.

assign out = in1 op in2; is called a continuous assignment statement. Continuous assignment statements end with a semicolon. Anytime the inputs on the right side of the = in a continuous assignment statement change, the output on the left side is recomputed. Thus, continuous assignment statements describe combinational logic.

VHDL

library IEEE; use IEEE.STD_LOGIC_1164.all;

entity gates is

port(a, b: in  STD_LOGIC_VECTOR(3 downto 0);

   y1, y2, y3, y4,

   y5:   out STD_LOGIC_VECTOR(3 downto 0));

end;

architecture synth of gates is

begin

 –– five different two-input logic gates

 –– acting on 4-bit busses

 y1 <= a and b;

 y2 <= a or b;

 y3 <= a xor b;

 y4 <= a nand b;

 y5 <= a nor b;

end;

not, xor, and or are examples of VHDL operators, whereas a, b, and y1 are operands. A combination of operators and operands, such as a and b, or a nor b, is called an expression. A complete command such as y4 <= a nand b; is called a statement.

out <= in1 op in2; is called a concurrent signal assignment statement. VHDL assignment statements end with a semicolon. Anytime the inputs on the right side of the <= in a concurrent signal assignment statement change, the output on the left side is recomputed. Thus, concurrent signal assignment statements describe combinational logic.

4.2.2 Comments and White Space

The gates example showed how to format comments. SystemVerilog and VHDL are not picky about the use of white space (i.e., spaces, tabs, and line breaks). Nevertheless, proper indenting and use of blank lines is helpful to make nontrivial designs readable. Be consistent in your use of capitalization and underscores in signal and module names. This text uses all lower case. Module and signal names must not begin with a digit.

SystemVerilog

SystemVerilog comments are just like those in C or Java. Comments beginning with /* continue, possibly across multiple lines, to the next */. Comments beginning with // continue to the end of the line.

SystemVerilog is case-sensitive. y1 and Y1 are different signals in SystemVerilog. However, it is confusing to use multiple signals that differ only in case.

VHDL

Comments beginning with /* continue, possibly across multiple lines, to the next */. Comments beginning with –– continue to the end of the line.

VHDL is not case-sensitive. y1 and Y1 are the same signal in VHDL. However, other tools that may read your file might be case sensitive, leading to nasty bugs if you blithely mix upper and lower case.

4.2.3 Reduction Operators

Reduction operators imply a multiple-input gate acting on a single bus. HDL Example 4.4 describes an eight-input AND gate with inputs a7, a6, … , a0. Analogous reduction operators exist for OR, XOR, NAND, NOR, and XNOR gates. Recall that a multiple-input XOR performs parity, returning TRUE if an odd number of inputs are TRUE.

HDL Example 4.4 Eight-Input AND

SystemVerilog

module and8(input  logic [7:0] a,

        output logic    y);

  assign y = &a;

  // &a is much easier to write than

  // assign y = a[7] & a[6] & a[5] & a[4] &

  //       a[3] & a[2] & a[1] & a[0];

endmodule

VHDL

library IEEE; use IEEE.STD_LOGIC_1164.all;

entity and8 is

  port(a: in  STD_LOGIC_VECTOR(7 downto 0);

     y:  out STD_LOGIC);

end;

architecture synth of and8 is

begin

  y <= and a;

 –– and a is much easier to write than

 –– y <= a(7) and a(6) and a(5) and a(4) and

 ––    a(3) and a(2) and a(1) and a(0);

end;

image

Figure 4.5 and8 synthesized circuit

4.2.4 Conditional Assignment

Conditional assignments select the output from among alternatives based on an input called the condition. HDL Example 4.5 illustrates a 2:1 multiplexer using conditional assignment.

HDL Example 4.5 2:1 Multiplexer

SystemVerilog

The conditional operator ?: chooses, based on a first expression, between a second and third expression. The first expression is called the condition. If the condition is 1, the operator chooses the second expression. If the condition is 0, the operator chooses the third expression.

?: is especially useful for describing a multiplexer because, based on the first input, it selects between two others. The following code demonstrates the idiom for a 2:1 multiplexer with 4-bit inputs and outputs using the conditional operator.

module mux2(input  logic [3:0] d0, d1,

       input  logic s,

        output logic [3:0] y);

  assign y = s ? d1 : d0;

endmodule

If s is 1, then y = d1. If s is 0, then y = d0.

?: is also called a ternary operator, because it takes three inputs. It is used for the same purpose in the C and Java programming languages.

VHDL

Conditional signal assignments perform different operations depending on some condition. They are especially useful for describing a multiplexer. For example, a 2:1 multiplexer can use conditional signal assignment to select one of two 4-bit inputs.

library IEEE; use IEEE.STD_LOGIC_1164.all;

entity mux2 is

 port(d0, d1: in  STD_LOGIC_VECTOR(3 downto 0);

    s:    in  STD_LOGIC;

    y:    out STD_LOGIC_VECTOR(3 downto 0));

end;

architecture synth of mux2 is

begin

  y <= d1 when s else d0;

end;

The conditional signal assignment sets y to d1 if s is 1. Otherwise it sets y to d0. Note that prior to the 2008 revision of VHDL, one had to write when s = ‘1’ rather than when s.

image

Figure 4.6 mux2 synthesized circuit

HDL Example 4.6 shows a 4:1 multiplexer based on the same principle as the 2:1 multiplexer in HDL Example 4.5. Figure 4.7 shows the schematic for the 4:1 multiplexer produced by Synplify Premier. The software uses a different multiplexer symbol than this text has shown so far. The multiplexer has multiple data (d) and one-hot enable (e) inputs. When one of the enables is asserted, the associated data is passed to the output. For example, when s[1] = s[0] = 0, the bottom AND gate, un1_s_5, produces a 1, enabling the bottom input of the multiplexer and causing it to select d0[3:0].

HDL Example 4.6 4:1 Multiplexer

SystemVerilog

A 4:1 multiplexer can select one of four inputs using nested conditional operators.

module mux4(input  logic [3:0] d0, d1, d2, d3,

       input  logic [1:0] s,

        output logic [3:0] y);

  assign y = s[1] ? (s[0] ? d3 : d2)

          : (s[0] ? d1 : d0);

endmodule

If s[1] is 1, then the multiplexer chooses the first expression, (s[0] ? d3 : d2). This expression in turn chooses either d3 or d2 based on s[0] (y = d3 if s[0] is 1 and d2 if s[0] is 0). If s[1] is 0, then the multiplexer similarly chooses the second expression, which gives either d1 or d0 based on s[0].

VHDL

A 4:1 multiplexer can select one of four inputs using multiple else clauses in the conditional signal assignment.

library IEEE; use IEEE.STD_LOGIC_1164.all;

entity mux4 is

  port(d0, d1,

     d2, d3: in  STD_LOGIC_VECTOR(3 downto 0);

     s:    in  STD_LOGIC_VECTOR(1 downto 0);

     y:    out STD_LOGIC_VECTOR(3 downto 0));

end;

architecture synth1 of mux4 is

begin

  y <= d0 when s = “00” else

    d1 when s = “01” else

    d2 when s = “10” else

    d3;

end;

VHDL also supports selected signal assignment statements to provide a shorthand when selecting from one of several possibilities. This is analogous to using a switch/case statement in place of multiple if/else statements in some programming languages. The 4:1 multiplexer can be rewritten with selected signal assignment as follows:

architecture synth2 of mux4 is

begin

  with s select y <=

   d0  when "00",

   d1  when "01",

   d2  when "10",

   d3  when others;

end;

image

Figure 4.7 mux4 synthesized circuit

4.2.5 Internal Variables

Often it is convenient to break a complex function into intermediate steps. For example, a full adder, which will be described in Section 5.2.1, is a circuit with three inputs and two outputs defined by the following equations:

image (4.1)

If we define intermediate signals, P and G,

image (4.2)

we can rewrite the full adder as follows:

image (4.3)

P and G are called internal variables, because they are neither inputs nor outputs but are used only internal to the module. They are similar to local variables in programming languages. HDL Example 4.7 shows how they are used in HDLs.

Check this by filling out the truth table to convince yourself it is correct.

HDL Example 4.7 Full Adder

SystemVerilog

In SystemVerilog, internal signals are usually declared as logic.

module fulladder(input  logic a, b, cin,

           output logic s, cout);

  logic p, g;

  assign p = a ^ b;

  assign g = a & b;

  assign s = p ^ cin;

  assign cout = g | (p & cin);

endmodule

VHDL

In VHDL, signals are used to represent internal variables whose values are defined by concurrent signal assignment statements such as p <= a xor b;

library IEEE; use IEEE.STD_LOGIC_1164.all;

entity fulladder is

  port(a, b, cin: in  STD_LOGIC;

     s, cout:   out STD_LOGIC);

end;

architecture synth of fulladder is

  signal p, g: STD_LOGIC;

begin

  p <= a xor b;

  g <= a and b;

  s <= p xor cin;

  cout <= g or (p and cin);

end;

image

Figure 4.8 fulladder synthesized circuit

HDL assignment statements (assign in SystemVerilog and <= in VHDL) take place concurrently. This is different from conventional programming languages such as C or Java, in which statements are evaluated in the order in which they are written. In a conventional language, it is important that S = PCin comes after P = AB, because statements are executed sequentially. In an HDL, the order does not matter. Like hardware, HDL assignment statements are evaluated any time the inputs, signals on the right hand side, change their value, regardless of the order in which the assignment statements appear in a module.

4.2.6 Precedence

Notice that we parenthesized the cout computation in HDL Example 4.7 to define the order of operations as Cout = G + (P · Cin), rather than Cout = (G + P) · Cin. If we had not used parentheses, the default operation order is defined by the language. HDL Example 4.8 specifies operator precedence from highest to lowest for each language. The tables include arithmetic, shift, and comparison operators that will be defined in Chapter 5.

HDL Example 4.8 Operator Precedence

SystemVerilog

Table 4.1 SystemVerilog operator precedence

Image

The operator precedence for SystemVerilog is much like you would expect in other programming languages. In particular, AND has precedence over OR. We could take advantage of this precedence to eliminate the parentheses.

assign cout = g | p & cin;

VHDL

Table 4.2 VHDL operator precedence

Image

Multiplication has precedence over addition in VHDL, as you would expect. However, unlike SystemVerilog, all of the logical operations (and, or, etc.) have equal precedence, unlike what one might expect in Boolean algebra. Thus, parentheses are necessary; otherwise cout <= g or p and cin would be interpreted from left to right as cout <= (g or p) and cin.

4.2.7 Numbers

Numbers can be specified in binary, octal, decimal, or hexadecimal (bases 2, 8, 10, and 16, respectively). The size, i.e., the number of bits, may optionally be given, and leading zeros are inserted to reach this size. Underscores in numbers are ignored and can be helpful in breaking long numbers into more readable chunks. HDL Example 4.9 explains how numbers are written in each language.

HDL Example 4.9 Numbers

SystemVerilog

The format for declaring constants is N’Bvalue, where N is the size in bits, B is a letter indicating the base, and value gives the value. For example, 9’h25 indicates a 9-bit number with a value of 2516 = 3710 = 0001001012. SystemVerilog supports ’b for binary, ’o for octal, ’d for decimal, and ’h for hexadecimal. If the base is omitted, it defaults to decimal.

If the size is not given, the number is assumed to have as many bits as the expression in which it is being used. Zeros are automatically padded on the front of the number to bring it up to full size. For example, if w is a 6-bit bus, assign w = ’b11 gives w the value 000011. It is better practice to explicitly give the size. An exception is that ’0 and ’1 are SystemVerilog idioms for filling a bus with all 0s and all 1s, respectively.

Table 4.3 SystemVerilog numbers

Image

VHDL

In VHDL, STD_LOGIC numbers are written in binary and enclosed in single quotes: ‘0’ and ‘1’ indicate logic 0 and 1. The format for declaring STD_LOGIC_VECTOR constants is NB"value", where N is the size in bits, B is a letter indicating the base, and value gives the value. For example, 9X"25" indicates a 9-bit number with a value of 2516 = 3710 = 0001001012. VHDL 2008 supports B for binary, O for octal, D for decimal, and X for hexadecimal.

If the base is omitted, it defaults to binary. If the size is not given, the number is assumed to have a size matching the number of bits specified in the value. As of October 2011, Synplify Premier from Synopsys does not yet support specifying the size.

others => ’0’ and others => ’1’ are VHDL idioms to fill all of the bits with 0 and 1, respectively.

Table 4.4 VHDL numbers

Image

4.2.8 Z’s and X’s

HDLs use z to indicate a floating value, z is particularly useful for describing a tristate buffer, whose output floats when the enable is 0. Recall from Section 2.6.2 that a bus can be driven by several tristate buffers, exactly one of which should be enabled. HDL Example 4.10 shows the idiom for a tristate buffer. If the buffer is enabled, the output is the same as the input. If the buffer is disabled, the output is assigned a floating value (z).

Similarly, HDLs use x to indicate an invalid logic level. If a bus is simultaneously driven to 0 and 1 by two enabled tristate buffers (or other gates), the result is x, indicating contention. If all the tristate buffers driving a bus are simultaneously OFF, the bus will float, indicated by z.

At the start of simulation, state nodes such as flip-flop outputs are initialized to an unknown state (x in SystemVerilog and u in VHDL). This is helpful to track errors caused by forgetting to reset a flip-flop before its output is used.

HDL Example 4.10 Tristate Buffer

SystemVerilog

module tristate(input  logic [3:0] a,

          input  logic     en,

          output tri   [3:0] y);

  assign y = en ? a : 4’bz;

endmodule

Notice that y is declared as tri rather than logic. logic signals can only have a single driver. Tristate busses can have multiple drivers, so they should be declared as a net. Two types of nets in SystemVerilog are called tri and trireg. Typically, exactly one driver on a net is active at a time, and the net takes on that value. If no driver is active, a tri floats (z), while a trireg retains the previous value. If no type is specified for an input or output, tri is assumed. Also note that a tri output from a module can be used as a logic input to another module. Section 4.7 further discusses nets with multiple drivers.

VHDL

library IEEE; use IEEE.STD_LOGIC_1164.all;

entity tristate is

  port(a:  in  STD_LOGIC_VECTOR(3 downto 0);

     en: in  STD_LOGIC;

     y:  out  STD_LOGIC_VECTOR(3 downto 0));

end;

architecture synth of tristate is

begin

  y <= a when en else “ZZZZ”;

end;

image

Figure 4.9 tristate synthesized circuit

If a gate receives a floating input, it may produce an x output when it can’t determine the correct output value. Similarly, if it receives an illegal or uninitialized input, it may produce an x output. HDL Example 4.11 shows how SystemVerilog and VHDL combine these different signal values in logic gates.

HDL Example 4.11 Truth Tables with Undefined and Floating Inputs

SystemVerilog

SystemVerilog signal values are 0, 1, z, and x. SystemVerilog constants starting with z or x are padded with leading z’s or x’s (instead of 0’s) to reach their full length when necessary.

Table 4.5 shows a truth table for an AND gate using all four possible signal values. Note that the gate can sometimes determine the output despite some inputs being unknown. For example 0 & z returns 0 because the output of an AND gate is always 0 if either input is 0. Otherwise, floating or invalid inputs cause invalid outputs, displayed as x in SystemVerilog.

Table 4.5 SystemVerilog AND gate truth table with z and x

Image

VHDL

VHDL STD_LOGIC signals are ‘0’, ‘1’, ‘z’, ‘x’, and ‘u’.

Table 4.6 shows a truth table for an AND gate using all five possible signal values. Notice that the gate can sometimes determine the output despite some inputs being unknown. For example, ‘0’ and ‘z’ returns ‘0’ because the output of an AND gate is always ‘0’ if either input is ‘0’. Otherwise, floating or invalid inputs cause invalid outputs, displayed as ‘x’ in VHDL. Uninitialized inputs cause uninitialized outputs, displayed as ‘u’ in VHDL.

Table 4.6 VHDL AND gate truth table with z, x and u

Image

HDL Example 4.12 Bit Swizzling

SystemVerilog

assign y = {c[2:1], {3{d[0]}}, c[0], 3’b101};

The {} operator is used to concatenate busses. {3{d[0]}} indicates three copies of d[0].

Don’t confuse the 3-bit binary constant 3‘b101 with a bus named b. Note that it was critical to specify the length of 3 bits in the constant; otherwise, it would have had an unknown number of leading zeros that might appear in the middle of y.

If y were wider than 9 bits, zeros would be placed in the most significant bits.

VHDL

y <=(c(2 downto 1), d(0), d(0), d(0), c(0), 3B“101”);

The () aggregate operator is used to concatenate busses. y must be a 9-bit STD_LOGIC_VECTOR.

Another example demonstrates the power of VHDL aggregations. Assuming z is an 8-bit STD_LOGIC_VECTOR, z is given the value 10010110 using the following command aggregation.

z <= (“10”, 4 => ‘1’, 2 downto 1 =>‘1’, others =>‘0’)

The “10” goes in the leading pair of bits. 1s are also placed into bit 4 and bits 2 and 1. The other bits are 0.

Seeing x or u values in simulation is almost always an indication of a bug or bad coding practice. In the synthesized circuit, this corresponds to a floating gate input, uninitialized state, or contention. The x or u may be interpreted randomly by the circuit as 0 or 1, leading to unpredictable behavior.

4.2.9 Bit Swizzling

Often it is necessary to operate on a subset of a bus or to concatenate (join together) signals to form busses. These operations are collectively known as bit swizzling. In HDL Example 4.12, y is given the 9-bit value c2c1d0d0d0c0101 using bit swizzling operations.

4.2.10 Delays

HDL statements may be associated with delays specified in arbitrary units. They are helpful during simulation to predict how fast a circuit will work (if you specify meaningful delays) and also for debugging purposes to understand cause and effect (deducing the source of a bad output is tricky if all signals change simultaneously in the simulation results). These delays are ignored during synthesis; the delay of a gate produced by the synthesizer depends on its tpd and tcd specifications, not on numbers in HDL code.

HDL Example 4.13 adds delays to the original function from HDL Example 4.1, image. It assumes that inverters have a delay of 1 ns, three-input AND gates have a delay of 2 ns, and three-input OR gates have a delay of 4 ns. Figure 4.10 shows the simulation waveforms, with y lagging 7 ns after the inputs. Note that y is initially unknown at the beginning of the simulation.

image

Figure 4.10 Example simulation waveforms with delays (from the ModelSim simulator)

HDL Example 4.13 Logic Gates with Delays

SystemVerilog

‘timescale 1ns/1ps

module example(input  logic a, b, c,

         output logic y);

 logic ab, bb, cb, n1, n2, n3;

 assign #1 {ab, bb, cb} = ~{a, b, c};

 assign #2  n1 = ab & bb & cb;

 assign #2  n2 = a & bb & cb;

 assign #2  n3 = a & bb & c;

 assign #4  y = n1 | n2 | n3;

endmodule

SystemVerilog files can include a timescale directive that indicates the value of each time unit. The statement is of the form ‘timescale unit/precision. In this file, each unit is 1 ns, and the simulation has 1 ps precision. If no timescale directive is given in the file, a default unit and precision (usually 1 ns for both) are used. In SystemVerilog, a # symbol is used to indicate the number of units of delay. It can be placed in assign statements, as well as non-blocking (<=) and blocking (=) assignments, which will be discussed in Section 4.5.4.

VHDL

library IEEE; use IEEE.STD_LOGIC_1164.all;

entity example is

  port(a, b, c: in  STD_LOGIC;

     y:     out STD_LOGIC);

end;

architecture synth of example is

  signal ab, bb, cb, n1, n2, n3: STD_LOGIC;

begin

  ab <= not a after 1 ns;

  bb <= not b after 1 ns;

  cb <= not c after 1 ns;

  n1 <= ab and bb and cb after 2 ns;

  n2 <= a and bb and cb after 2 ns;

  n3 <= a and bb and c after 2 ns;

  y  <= n1 or n2 or n3 after 4 ns;

end;

In VHDL, the after clause is used to indicate delay. The units, in this case, are specified as nanoseconds.

4.3 Structural Modeling

The previous section discussed behavioral modeling, describing a module in terms of the relationships between inputs and outputs. This section examines structural modeling, describing a module in terms of how it is composed of simpler modules.

HDL Example 4.14 Structural Model of 4:1 Multiplexer

SystemVerilog

module mux4(input  logic [3:0] d0, d1, d2, d3,

       input  logic [1:0] s,

        output logic [3:0] y);

 logic [3:0] low, high;

 mux2 lowmux(d0, d1, s[0], low);

 mux2 highmux(d2, d3, s[0], high);

 mux2 finalmux(low, high, s[1], y);

endmodule

The three mux2 instances are called lowmux, highmux, and finalmux. The mux2 module must be defined elsewhere in the SystemVerilog code — see HDL Example 4.5, 4.15, or 4.34.

VHDL

library IEEE; use IEEE.STD_LOGIC_1164.all;

entity mux4 is

  port(d0, d1,

     d2, d3: in  STD_LOGIC_VECTOR(3 downto 0);

     s:    in  STD_LOGIC_VECTOR(1 downto 0);

     y:    out STD_LOGIC_VECTOR(3 downto 0));

end;

architecture struct of mux4 is

  component mux2

   port(d0,

      d1: in  STD_LOGIC_VECTOR(3 downto 0);

      s: in  STD_LOGIC;

      y:  out STD_LOGIC_VECTOR(3 downto 0));

  end component;

  signal low, high: STD_LOGIC_VECTOR(3 downto 0);

begin

  lowmux:   mux2  port  map(d0, d1, s(0), low);

  highmux:   mux2  port  map(d2, d3, s(0), high);

  finalmux:  mux2  port  map(low, high, s(1), y);

end;

The architecture must first declare the mux2 ports using the component declaration statement. This allows VHDL tools to check that the component you wish to use has the same ports as the entity that was declared somewhere else in another entity statement, preventing errors caused by changing the entity but not the instance. However, component declaration makes VHDL code rather cumbersome.

Note that this architecture of mux4 was named struct, whereas architectures of modules with behavioral descriptions from Section 4.2 were named synth. VHDL allows multiple architectures (implementations) for the same entity; the architectures are distinguished by name. The names themselves have no significance to the CAD tools, but struct and synth are common. Synthesizable VHDL code generally contains only one architecture for each entity, so we will not discuss the VHDL syntax to configure which architecture is used when multiple architectures are defined.

For example, HDL Example 4.14 shows how to assemble a 4:1 multiplexer from three 2:1 multiplexers. Each copy of the 2:1 multiplexer is called an instance. Multiple instances of the same module are distinguished by distinct names, in this case lowmux, highmux, and finalmux. This is an example of regularity, in which the 2:1 multiplexer is reused many times.

image

Figure 4.11 mux4 synthesized circuit

HDL Example 4.15 uses structural modeling to construct a 2:1 multiplexer from a pair of tristate buffers. Building logic out of tristates is not recommended, however.

HDL Example 4.15 Structural Model of 2:1 Multiplexer

SystemVerilog

module mux2(input  logic [3:0] d0, d1,

       input  logic     s,

        output tri   [3:0] y);

 tristate t0(d0, ~s, y);

 tristate t1(d1, s, y);

endmodule

In SystemVerilog, expressions such as ~s are permitted in the port list for an instance. Arbitrarily complicated expressions are legal but discouraged because they make the code difficult to read.

VHDL

library IEEE; use IEEE.STD_LOGIC_1164.all;

entity mux2 is

  port(d0, d1: in  STD_LOGIC_VECTOR(3 downto 0);

     s:    in  STD_LOGIC;

     y:    out STD_LOGIC_VECTOR(3 downto 0));

end;

architecture struct of mux2 is

  component tristate

   port(a:  in  STD_LOGIC_VECTOR(3 downto 0);

      en: in  STD_LOGIC;

      y:  out STD_LOGIC_VECTOR(3 downto 0));

  end component;

  signal sbar: STD_LOGIC;

begin

  sbar <= not s;

  t0:  tristate  port  map(d0, sbar, y);

  t1:  tristate  port  map(d1, s, y);

end;

In VHDL, expressions such as not s are not permitted in the port map for an instance. Thus, sbar must be defined as a separate signal.

image

Figure 4.12 mux2 synthesized circuit

HDL Example 4.16 shows how modules can access part of a bus. An 8-bit wide 2:1 multiplexer is built using two of the 4-bit 2:1 multiplexers already defined, operating on the low and high nibbles of the byte.

In general, complex systems are designed hierarchically. The overall system is described structurally by instantiating its major components. Each of these components is described structurally from its building blocks, and so forth recursively until the pieces are simple enough to describe behaviorally. It is good style to avoid (or at least to minimize) mixing structural and behavioral descriptions within a single module.

HDL Example 4.16 Accessing Parts of Busses

SystemVerilog

module mux2_8(input  logic [7:0] d0, d1,

         input  logic     s,

         output logic [7:0] y);

 mux2 lsbmux(d0[3:0], d1[3:0], s, y[3:0]);

 mux2 msbmux(d0[7:4], d1[7:4], s, y[7:4]);

endmodule

VHDL

library IEEE; use IEEE.STD_LOGIC_1164.all;

entity mux2_8 is

  port(d0, d1: in  STD_LOGIC_VECTOR(7 downto 0);

     s:    in  STD_LOGIC;

     y:    out STD_LOGIC_VECTOR(7 downto 0));

end;

architecture struct of mux2_8 is

  component mux2

   port(d0, d1: in  STD_LOGIC_VECTOR(3 downto 0);

      s:    in  STD_LOGIC;

      y:    out STD_LOGIC_VECTOR(3 downto 0));

  end component;

begin

  lsbmux: mux2

   port map(d0(3 downto 0), d1(3 downto 0),

        s, y(3 downto 0));

  msbmux: mux2

   port map(d0(7 downto 4), d1(7 downto 4),

        s, y(7 downto 4));

end;

image

Figure 4.13 mux2_8 synthesized circuit

4.4 Sequential Logic

HDL synthesizers recognize certain idioms and turn them into specific sequential circuits. Other coding styles may simulate correctly but synthesize into circuits with blatant or subtle errors. This section presents the proper idioms to describe registers and latches.

4.4.1 Registers

The vast majority of modern commercial systems are built with registers using positive edge-triggered D flip-flops. HDL Example 4.17 shows the idiom for such flip-flops.

In SystemVerilog always statements and VHDL process statements, signals keep their old value until an event in the sensitivity list takes place that explicitly causes them to change. Hence, such code, with appropriate sensitivity lists, can be used to describe sequential circuits with memory. For example, the flip-flop includes only clk in the sensitive list. It remembers its old value of q until the next rising edge of the clk, even if d changes in the interim.

In contrast, SystemVerilog continuous assignment statements (assign) and VHDL concurrent assignment statements (<=) are reevaluated anytime any of the inputs on the right hand side changes. Therefore, such code necessarily describes combinational logic.

HDL Example 4.17 Register

SystemVerilog

module flop(input  logic    clk,

       input  logic [3:0] d,

        output logic [3:0] q);

 always_ff @(posedge clk)

  q <= d;

endmodule

In general, a SystemVerilog always statement is written in the form

always @(sensitivity list)

 statement;

The statement is executed only when the event specified in the sensitivity list occurs. In this example, the statement is q <= d (pronounced “q gets d”). Hence, the flip-flop copies d to q on the positive edge of the clock and otherwise remembers the old state of q. Note that sensitivity lists are also referred to as stimulus lists.

<= is called a nonblocking assignment. Think of it as a regular = sign for now; we’ll return to the more subtle points in Section 4.5.4. Note that <= is used instead of assign inside an always statement.

As will be seen in subsequent sections, always statements can be used to imply flip-flops, latches, or combinational logic, depending on the sensitivity list and statement. Because of this flexibility, it is easy to produce the wrong hardware inadvertently. SystemVerilog introduces always_ff, always_latch, and always_comb to reduce the risk of common errors. always_ff behaves like always but is used exclusively to imply flip-flops and allows tools to produce a warning if anything else is implied.

VHDL

library IEEE; use IEEE.STD_LOGIC_1164.all;

entity flop is

  port(clk: in  STD_LOGIC;

     d:  in  STD_LOGIC_VECTOR(3 downto 0);

     q:  out STD_LOGIC_VECTOR(3 downto 0));

end;

architecture synth of flop is

begin

  process(clk) begin

   if rising_edge(clk) then

    q <= d;

   end if;

  end process;

end;

A VHDL process is written in the form

process(sensitivity list) begin

  statement;

end process;

The statement is executed when any of the variables in the sensitivity list change. In this example, the if statement checks if the change was a rising edge on clk. If so, then q <= d (pronounced “q gets d”). Hence, the flip-flop copies d to q on the positive edge of the clock and otherwise remembers the old state of q.

An alternative VHDL idiom for a flip-flop is

process(clk) begin

  if clk’event and clk = ‘1’ then

   q <= d;

  end if;

end process;

rising_edge(clk) is synonymous with clk’event and clk = ‘1’.

image

Figure 4.14 flop synthesized circuit

4.4.2 Resettable Registers

When simulation begins or power is first applied to a circuit, the output of a flop or register is unknown. This is indicated with x in SystemVerilog and u in VHDL. Generally, it is good practice to use resettable registers so that on powerup you can put your system in a known state. The reset may be either asynchronous or synchronous. Recall that asynchronous reset occurs immediately, whereas synchronous reset clears the output only on the next rising edge of the clock. HDL Example 4.18 demonstrates the idioms for flip-flops with asynchronous and synchronous resets. Note that distinguishing synchronous and asynchronous reset in a schematic can be difficult. The schematic produced by Synplify Premier places asynchronous reset at the bottom of a flip-flop and synchronous reset on the left side.

HDL Example 4.18 Resettable Register

SystemVerilog

module flopr(input  logic     clk,

        input  logic     reset,

        input  logic [3:0] d,

        output logic [3:0] q);

 // asynchronous reset

 always_ff @(posedge clk, posedge reset)

  if (reset) q <= 4’b0;

  else    q <= d;

endmodule

module flopr(input  logic     clk,

        input  logic     reset,

        input  logic [3:0] d,

        output logic [3:0] q);

 // synchronous reset

 always_ff @(posedge clk)

  if (reset) q <= 4’b0;

  else     q <= d;

endmodule

Multiple signals in an always statement sensitivity list are separated with a comma or the word or. Notice that posedge reset is in the sensitivity list on the asynchronously resettable flop, but not on the synchronously resettable flop. Thus, the asynchronously resettable flop immediately responds to a rising edge on reset, but the synchronously resettable flop responds to reset only on the rising edge of the clock.

Because the modules have the same name, flopr, you may include only one or the other in your design.

VHDL

library IEEE; use IEEE.STD_LOGIC_1164.all;

entity flopr is

  port(clk, reset: in  STD_LOGIC;

     d:      in  STD_LOGIC_VECTOR(3 downto 0);

     q:      out STD_LOGIC_VECTOR(3 downto 0));

end;

architecture asynchronous of flopr is

begin

  process(clk, reset) begin

   if reset then

    q <= "0000";

   elsif rising_edge(clk) then

    q <= d;

   end if;

  end process;

end;

library IEEE; use IEEE.STD_LOGIC_1164.all;

entity flopr is

  port(clk, reset: in  STD_LOGIC;

     d:       in  STD_LOGIC_VECTOR(3 downto 0);

     q:       out STD_LOGIC_VECTOR(3 downto 0));

end;

architecture synchronous of flopr is

begin

  process(clk) begin

   if rising_edge(clk) then

    if reset then q <= “0000”;

    else q <= d;

    end if;

   end if;

  end process;

end;

Multiple signals in a process sensitivity list are separated with a comma. Notice that reset is in the sensitivity list on the asynchronously resettable flop, but not on the synchronously resettable flop. Thus, the asynchronously resettable flop immediately responds to a rising edge on reset, but the synchronously resettable flop responds to reset only on the rising edge of the clock.

Recall that the state of a flop is initialized to ‘u’ at startup during VHDL simulation.

As mentioned earlier, the name of the architecture (asynchronous or synchronous, in this example) is ignored by the VHDL tools but may be helpful to the human reading the code. Because both architectures describe the entity flopr, you may include only one or the other in your design.

image

Figure 4.15 flopr synthesized circuit (a) asynchronous reset, (b) synchronous reset

4.4.3 Enabled Registers

Enabled registers respond to the clock only when the enable is asserted. HDL Example 4.19 shows an asynchronously resettable enabled register that retains its old value if both reset and en are FALSE.

HDL Example 4.19 Resettable Enabled Register

SystemVerilog

module flopenr(input  logic     clk,

         input  logic     reset,

         input  logic     en,

         input  logic [3:0] d,

         output logic [3:0] q);

 // asynchronous reset

  always_ff @(posedge clk, posedge reset)

   if    (reset) q <= 4’b0;

   else if (en)   q <= d;

endmodule

VHDL

library IEEE; use IEEE.STD_LOGIC_1164.all;

entity flopenr is

 port(clk,

    reset,

    en: in  STD_LOGIC;

    d:  in  STD_LOGIC_VECTOR(3 downto 0);

    q:  out STD_LOGIC_VECTOR(3 downto 0));

end;

architecture asynchronous of flopenr is

–– asynchronous reset

begin

 process(clk, reset) begin

  if reset then

   q <= "0000";

  elsif rising_edge(clk) then

   if en then

    q <= d;

   end if;

  end if;

  end process;

end;

image

Figure 4.16 flopenr synthesized circuit

4.4.4 Multiple Registers

A single always/process statement can be used to describe multiple pieces of hardware. For example, consider the synchronizer from Section 3.5.5 made of two back-to-back flip-flops, as shown in Figure 4.17. HDL Example 4.20 describes the synchronizer. On the rising edge of clk, d is copied to n1. At the same time, n1 is copied to q.

image

Figure 4.17 Synchronizer circuit

HDL Example 4.20 Synchronizer

SystemVerilog

module sync(input  logic  clk,

       input  logic  d,

       output logic q);

 logic n1;

 always_ff @(posedge clk)

   begin

   n1 <= d; // nonblocking

   q <= n1; // nonblocking

   end

endmodule

Notice that the begin/end construct is necessary because multiple statements appear in the always statement. This is analogous to {} in C or Java. The begin/end was not needed in the flopr example because if/else counts as a single statement.

VHDL

library IEEE; use IEEE.STD_LOGIC_1164.all;

entity sync is

 port(clk: in STD_LOGIC;

    d:  in STD_LOGIC;

    q:  out STD_LOGIC);

end;

architecture good of sync is

 signal n1: STD_LOGIC;

begin

 process(clk) begin

  if rising_edge(clk) then

    n1 <= d;

    q <= n1;

  end if;

 end process;

end;

n1 must be declared as a signal because it is an internal signal used in the module.

image

Figure 4.18 sync synthesized circuit

4.4.5 Latches

Recall from Section 3.2.2 that a D latch is transparent when the clock is HIGH, allowing data to flow from input to output. The latch becomes opaque when the clock is LOW, retaining its old state. HDL Example 4.21 shows the idiom for a D latch.

Not all synthesis tools support latches well. Unless you know that your tool does support latches and you have a good reason to use them, avoid them and use edge-triggered flip-flops instead. Furthermore, take care that your HDL does not imply any unintended latches, something that is easy to do if you aren’t attentive. Many synthesis tools warn you when a latch is created; if you didn’t expect one, track down the bug in your HDL. And if you don’t know whether you intended to have a latch or not, you are probably approaching HDLs like a programming language and have bigger problems lurking.

4.5 More Combinational Logic

In Section 4.2, we used assignment statements to describe combinational logic behaviorally. SystemVerilog always statements and VHDL process statements are used to describe sequential circuits, because they remember the old state when no new state is prescribed. However, always/process statements can also be used to describe combinational logic behaviorally if the sensitivity list is written to respond to changes in all of the inputs and the body prescribes the output value for every possible input combination. HDL Example 4.22 uses always/process statements to describe a bank of four inverters (see Figure 4.3 for the synthesized circuit).

HDL Example 4.21 D Latch

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
107.23.157.16