EDN Access

 

November 6, 1997


To create successful designs, know your
HDL simulation and synthesis issues

Douglas J Smith, VeriBest

When creating HDL-based chip designs, you need to know some techniques for developing well-structured and efficient simulation and synthesis models. Coupling these techniques with an understanding of simulation and synthesis runtime issues provides the basis for good Verilog and VHDL designs.

Creating top-down chip designs is more than just learning Verilog and VHDL and describing your design in terms of one of them. Similar to what you do during other design phases, HDL design benefits from knowing the best way to write HDL code to represent your design. Because optimum ways exist to implement an electronics function in logic, you can optimally represent this logic in an HDL.

Synthesis and simulation tools benefit from optimized HDL-design representation, both in runtimes and, for synthesis, in the quality of gate-level code generated representing your design. In some cases, designing code with simulation- and synthesis-tool issues and limitations in mind can mean the difference between a successful design and one that simulates or synthesizes incorrectly.

Design and modeling

Adopt a hierarchical, top-down design and modeling methodology and bottom-up optimization strategy. You should define a design's requirement specification as tightly as practically possible in input, output, associated timing, and functionality before writing HDL models. Otherwise, you can easily design the "wrong thing right."

It is also good practice to use global clock and reset signals if possible. Consider testability issues early in the system design; otherwise, synthesis can be a fast and efficient way to produce lots of untestable logic. You should consider boundary scan, full or partial internal scan, and BIST. Full scan is often too expensive in silicon area and, possibly, timing degradation; a mixture of partial-scan and BIST techniques is often the most suitable compromise.

To reduce power, use dynamic power management to switch circuits to a low-frequency standby mode when applicable and wake them again with interrupts. Disable the clock to inactive parts of a circuit and activate them only when the design needs them to process data. To minimize output-current drain, use weak drivers on tristate buses.

To design for test (DFT) and for good testability, ensure that your design avoids asynchronous feedback, remove race conditions, split large counters into smaller ones, and use spare pins to aid controllability and observability of internal circuit nodes. In addition, ensure that the circuit is easy to initialize to a known state, and use scan testing where appropriate on register elements clocked by a common clock.

Also, run fault simulation on circuit areas not covered with scan logic, such as gated clocks and an asynchronous interface to a µP. Further, use test-vector comparison during simulation to ensure that test insertion does not alter design functionality.

Break a scan chain into several small chains of similar length. Use spare pins to increase the number of scan chains and to reduce scan-chain length. This technique reduces the number of both test vectors and chip-tester test cycles. Chip vendors normally base their test costs on the number of clock cycles. Minimizing the length of scan chains helps minimize this cost. If extra pins are unavailable, consider using a pin to put the chip in test mode and then multiplex functional input and output pins with test pins to include scan-in and -out test functions.

Use test harnesses only when necessary to verify functional behavior. With experience, you will find that test harnesses become unnecessary at lower hierarchical levels. Exploit the full richness of constructs in the HDL you are using.

For general HDL modeling, determine an appropriate architecture and partition the design accordingly before coding an RTL model. Write HDL code to reflect the architectural partitioning of a design. Partitioning should be sufficiently coarse-grained to allow the synthesis tool enough scope to efficiently optimize logic. A synthesis tool can typically perform efficient synthesis on circuits containing as many as 5000 equivalent gates. The synthesis tools use algorithms that may not yield optimal results and may be CPU-intensive for circuits containing more than 5000 equivalent gates. You can get more detailed structural partitioning with concurrent statements process (VHDL) and always (Verilog); however, this approach does not require you to describe the model down to the gate level. Include timing in a model only when critical at interface boundaries. Timing within a model should come from the technology cells that are mapped to by a synthesis tool.

VHDL is a strongly typed language; Verilog is not. This difference allows you the freedom of assigning different-width signals to each other in Verilog. For this reason, be more diligent when using Verilog because Verilog compilers cannot detect unintentional bit-width mismatches. If widths do not match in Verilog, a compiler either chops bits off or fills extra bits with logic 0s.

When writing HDL code, keep in mind your hardware intent, synthesis modeling style, and associated restrictions. Use subprograms wherever possible to help structure a design. Subprograms make code shorter and easier to read. Another advantage of subprogram use is easier code reuse. Make models as generic as possible for model reuse, including using parameterizable bit widths.

Do not repeat identical sections of code in different branches of the same conditional statement; move these sections out of the conditional expression. Similarly, do not put loop-invariant signals in a loop. Although the technique may seem obvious, designers often make this mistake, which results in slower simulation.

Verilog is case-sensitive, so identifiers A and a differ. VHDL is case-insensitive, so the language treats identifiers A and a in the same way. However, character literals in VHDL, such as "A" and "a," differ.

Using abstract data types provides models that are easier to read and maintain. This technique means using the VHDL enumerated data types and the Verilog 'define compiler directives to represent data values. Although Verilog does not allow enumerated data types, use of the 'define compiler directive can be powerful in many ways--not just for abstract data-type values.

Use meaningful signal names. For example, for active-low control signals, use <signal_name>_n for a clearer understanding of signal functionality and easier HDL debugging. For example, Reset_n would be active when at logic 0. You should also use comments liberally. A header should describe the functionality of the module, and each signal declaration should have a comment describing what the declaration does.

Improving simulation accuracy and speed

For VHDL and Verilog, ensure the sensitivity lists of process (VHDL) and the event lists of always (Verilog) statements are complete. Use a process or always statement in place of concurrent-signal assignments. This usage reduces the number of signals a simulator must continually monitor for changes, cutting simulation time. Design models to minimize the number of signals in the sensitivity list of process and always statements. The fewer signals to monitor, the faster the simulation. Do not model many small process and always statements. It takes time to activate and deactivate these processes. If your design has many registers clocked from the same clock source, group the registers in one process rather than in separate ones. To ensure simulation accuracy, make sure the sensitivity lists of process and always statements are complete.

For VHDL, use a process statement rather than the block statement in RTL modeling, because the block statement is always active during simulation. When convenient, convert vectored data types, such as signed and unsigned, to integer data types. Use variables instead of signals in a process wherever possible. Use 'event in preference to 'stable when using objects of type bit; the 'stable attribute looks for a logic level, so is always active during simulation. However, it is better to use the functions rising_edge and falling_edge than to use 'event to detect edge transitions. The IEEE 1076.3 packages NUMERIC_BIT and NUMERIC_STD define the rising_edge and falling_edge functions for types bit and std_logic, respectively.

In synthesis modeling of purely combinatorial logic with VHDL and Verilog, assign signals in every branch of conditional-signal assignments. For combinatorial logic from a case statement, assign default outputs immediately before the case statement or assign outputs regardless of which branch is taken through the case statement. This technique avoids inferring unwanted latches. The others (VHDL) default case branch is optional to ensure coverage of all branch values. The default (Verilog) default case branch is essential to ensure coverage of all branch values and to avoid inferring latches. You should assign a default value immediately before the for statement for data objects assigned from within a for loop.

In VHDL, use case statements instead of if statements containing multiple else-if clauses where applicable for efficiently synthesized circuits. The if statement operates on a priority-encoded basis. Unlike the case for VHDL, synthesis tools often interpret the Verilog case statement as being priority encoded like the if statement. Do not use unbounded integer data types. These data types default to the maximum range the language defines, which is 32 bits for IEEE 1076 93. This range causes 32-bit-wide logic to be synthesized and gives the synthesizer more optimization work to do in eliminating extra and redundant logic. Use IEEE packages STD_Logic_1164 and Numeric_STD as the minimum packages in a model. Use types std_logic for single-bit values and either signed or unsigned for vector-array types.

Also for VHDL, use 'event only for edge detection of two-value object types, such as bit and boolean. To use 'event with multivalued data types, such as std_logic, you must use the attribute last_value to detect a true rising edge from logic 0 to 1 and not, for example, X (unknown) to 1. Synthesis tools do not support last_value. Use parentheses in expressions to provide finer grained structural control. Use only variable assignments within a for-loop statement when possible. You need not use the wait statement in VHDL to infer flip-flops. The if statement can do anything that the wait statement can do and lets you model purely combinatorial logic and separate sequential logic in one process.

In Verilog, do not attempt to model synchronous logic in a task. You can call a task only from within a procedural block, which for synthesis means a sequential begin-end block. A begin-end block can reside only inside an always statement, which must contain a posedge or negedge construct in the sensitivity list to model synchronous logic. Because synthesis tools support no nested edge-triggered constructs, you can't use a task to model synchronous logic.

For both simulation and synthesis modeling in VHDL or Verilog, keep loop-invariant assignments outside for loop statements. If these assignments occur in for loops, models take longer to simulate and synthesize unneeded repeated blocks of logic that the optimizer must then eliminate.

Simulation compilation

21MS3211The following simulation-compilation issues relate only to VHDL because it is a strongly typed language. Verilog types are straightforward and allow you to assign different bit-width objects to each other. Be careful though: A Verilog compiler cannot detect an object having a different bit width than you intend.

A model containing ports of mode (direction) out can have the ports only written to (assigned) within the model itself; sum cannot read them (Figure 1). Declare and use an intermediate variable because the HDL can read its value outside the model. You can then assign the variable directly to a port of mode out (Figure 2).

21MS3212Sometimes, HDL code cannot determine an expression's resulting type from the context in which you use it. You eliminate this problem by qualifying an expression with its desired type. Qualification is also useful for type checking and does not imply any type conversion.

subtype unsigned_3bit is unsigned(0 to 2);

--must use "to" and not "downto"
case unsigned_3bit'(A&B&C) is

--case choice value is qualified
Y<=FN1(unsigned'(1), N);

--bit literal is qualified for the function call.


You cannot use operators on the left side of an assignment:

ShiftRegA & ShiftRegB <=shift_left((ShiftRegA &
ShiftRegB), 1);


Instead, declare an extra variable that holds the desired expression from the left side of the assignment and assign it to this extra variable:

variable ShiftRegAB: unsigned(A'left+B'left­1 downto 0); ShiftRegA<=ShiftRegAB(A'left+B'left­1 downto B'left); ShiftRegB<=ShiftRegAB(B'left­1 downto 0); ShiftRegAB<=shift_left(ShiftRegAB, 1);

You should model subprograms for reuse using unconstrained parameters. However, if a subprogram uses the others clause as an aggregate assigned to an object, that is, of an unconstrained array type, compilation causes an analysis error. Declare a fixed-range subtype whenever you use (call) the subprogram. The subtype must always have the same name but a different range, depending on its use. Use this subtype to qualify the range of the unqualified aggregate in the body of the subprogram itself.

If you declare two or more subprograms with the same name and parameter-type profile in separate packages, you cannot give both the same scope. In such a case, a compiler would not know which subprogram to use from a subprogram call. The effect is that the subprogram name is not directly visible, making it appear nonexistent.

Multiple subprogram declarations that have parameter-type profiles that differ only by an integer and a natural data type do not analyze when you compile them because type natural is a subtype of type integer and means that subprograms are indistinguishable from each other. To distinguish them, use functions with different names.

You can use the concatenation operator (&) in the expression for the inputs of a subprogram-call's parameter list. However, you cannot use it for output and bidirectional parameters. Instead, perform the concatenation inside the procedure.

Simulation runtime issues

A sensitivity list is a list of signals in a VHDL process statement; a simulator monitors this list for changes. If a change occurs in one or more signals, then the simulator executes the process. Similarly, an event list is a list of signals in a Verilog always statement; a simulator monitors this list for changes. If the process or always statement infers only flip-flops with associated combinatorial logic on their inputs or outputs, you need include only the clock signal and any asynchronous resets in the sensitivity/event list. On the other hand, if you are modeling only combinatorial logic, then you must include all input signals to the process and always statements in the sensitivity or event list.

The omission of a signal from the sensitivity list does not affect the synthesized circuit but may give unexpected and misleading simulation results. This situation occurs because the simulator does not always trigger the process or always statement into execution and thus does not always update assignments within process or always statements. Make sure you include all signals in the sensitivity list when modeling combinatorial logic.

Reversing a vectored-array direction

Declaring an object in one direction and assigning it in the opposite direction causes the bits to reverse and connect accordingly. This situation does not cause simulation or synthesis compilation errors, but simulation results may differ from what you expect and lead to confusion. You should use vector arrays defined with a descending range and finishing at bit 0 when possible. This approach avoids the possibility of your design's trying to access bits of an array that do not exist. This technique also simplifies your assignment of objects and slices of objects.

The edge detection of a data object whose type has more than two values must detect the current and previous values in order to detect a true logic 0-to-1 transition and not, for example, an X-to-1 transition. If this situation does not occur, the model does not simulate correctly. Your model should contain an additional check to ensure that the clock signal transitions from 0 to 1 and not from some other value, such as X to 1.

The order of concurrent statements in VHDL or Verilog does not affect how a synthesizer synthesizes a circuit. However, it can affect simulation results, depending on the execution order of the statements. The solution in VHDL is to use no shared variables in models you are synthesizing. For Verilog models, combine always statements to control statement-execution order.

All multiple-bit data objects, which include VHDL signals or variables and Verilog variables, must have a statically determinable number of bits at synthesis compile-time. Also, all for loop statements must also have a statically determinable range at synthesis compile-time. If either of these conditions is not statically determinable, a synthesis tool does not know how much logic to synthesize and the synthesis tool will return an appropriate error message. This problem does not occur during simulation.

Joint simulation and synthesis

It is important to know when and how to use the others clause in VHDL and default clause in Verilog; their use can greatly affect simulation results and synthesized-circuit generation. These clauses define a default branch condition in multiway branch statements, which for VHDL means a case statement or selected signal assignment, and for Verilog means just a case statement. You should use these clauses in similar situations for both languages, but subtle differences exist.

The VHDL Language Reference Manual (LRM) states that a case statement must have each value of the expression's base type represented once and only once in the set of choices and that no other value is allowed. This restriction means that if you do not want to explicitly define every choice value, then you must always use a when others =>... type statement.

The VHDL selected signal assignment resembles the case statement. The LRM states that a selected signal assignment must have an exact equivalent case statement. This restriction means that all the conditions for using the others clause in a case statement apply equally to a selected-signal assignment. A selected-signal assignment is a concurrent statement residing outside a process, and the case statement is a sequential statement that must reside inside a process statement.

The Verilog case statement uses the default clause to define a default branch for a choice case expression, much as the others clause does in VHDL. The difference in Verilog is that all case choice values need not have a branch defined to be Verilog LRM-compliant. However, when modeling combinatorial logic, after you think you have explicitly defined all case expression choice values using logic 1s and 0s (because it is nearly impossible to define every combination of logic 0, 1, X, and Z in a sequence for all bits), you still must use a default clause to define a branch to cover X and Z values. The default clause assigns an output value to avoid inferring a latch. One exception to this rule is when you include an output-signal assignment immediately before the case statement.

For Verilog LRM compliance, a case statement need not have a branch for each choice value. However, you should use a coding standard similar to the standard you use in VHDL. Define a branch for each case expression value once and only once either explicitly or implicitly using the default clause.

VHDL includes signal, variable, constant, and file data objects. Only signals, variables, and constants are relevant for synthesis. Consider signals as synthesized directly in hardware. Signals have hardware intent and are always associated with one or more drivers; each driver holds the signal's projected waveform values. Variables and constants provide containers for values in computing signal values. Variables are updated immediately, that is, before any delta delay in which the assignment executes. VHDL has no concept of delta delays for variables; they hold a single-current value.

Also, signal assignments schedule an event in a future cycle. This cycle can be a delta delay in the same simulation time-unit or at some future scheduled simulation time.

During simulation, signals are scheduled and assigned at a "simulation delta," a delay period during a simulation cycle. When a signal's predicted value matures, the driver holding that value becomes active. This activity first causes all driver contributions to signals to resolve to a single value. This resolution identifies which value drives that signal if there is more than one driver. Signals and ports immediately update their values or retain their old values. Next, the effect of the changed-signal values propagates from the port signals down through the circuit network. Finally, process-sensitive signal events cause the process to trigger into execution. This action means that signals and variables within a process may update; this circumstance, in turn, depends on the path taken through the sequential statements within the process.

The number of deltas needed to compute the new signal's current value might be different in presynthesis and postsynthesis models, especially when sharing resources, such as adders and multiplexers. Sometimes, statements executed in one delta in the RTL model must be execute in two deltas when simulating the synthesized gate-level model.

Both signal and variable assignments are acceptable within a VHDL loop. However, it is better to use only variable assignments because simulation is faster, and you can more easily predict the resulting synthesized circuit.

Verilog includes blocking and nonblocking procedural assignments. Depending on which procedural assignments you use in a sequential procedural block, that is, between reserved words begin and end, simulation and synthesis results may differ.

A blocking procedural assignment must execute before the procedural flow can pass to the subsequent statement. This restriction means that any timing delay associated with such statements relates to the time at which the previous statements in the procedural block execute. Successive blocking procedural assignments in an edge-triggered always statement do not infer successive stages of synchronous logic (flip-flops); they act like VHDL variables.

A nonblocking procedural assignment occurs without blocking the procedural flow to subsequent statements. This situation means that the timing in an assignment is relative to the absolute time at which the procedural block triggers into execution. Because synthesis tools ignore all timing from the model, and nonblocking signal assignments occur at the same time, successive assignments in an edge-triggered always statement infer synchronous logic (flip-flops).

Both VHDL and Verilog support "don't-care" input values to a case statement and "don't-care" output values from a case statement. You should be aware that a VHDL "don't-care" value is a simulation "don't care," not a "don't care" in terms of possible logic reduction by a synthesis tool. If you make wise use of "don't-care" output values, your synthesis tools can typically decide whether the values should be a logic 0 or logic 1 to minimize logic.

The expression in an if statement compares the values of multiple pairs of data objects. Each comparison returns a Boolean true or false, depending upon whether the comparison is true or false. The types being compared need not be of the same type.

Table 1 shows attributes typically defined in VHDL for synthesis. Attributes that are not supported in VHDL either relate to timing or are not needed to model physical logic structure.



Table 1--VHDL attributes typically supported by synthesis tools
Attribute Kind Prefix Returned
result type
Returned result
Type related
T'base Type Any type or subtype Base type of T  
T'left Value Any scalar type or subtype T Same type as T Left bound of T
T'right Value Any scalar type or subtype T Same type as T Right bound of T
T'high Value Any scalar type or subtype T Same type as T Upper bound of T
T'low Value Any scalar type or subtype T Same type as T Lower bound of T
Array related
A'range[(N)] Range Any array object A Type of the
Nth index of A
Range A'left(N) to
A'right(N) if A is
ascending or A'left(N)
downto A'right(N) if A
is descending
A'reverse_range[(N)] Range Any array object A Type of the
Nth index of A
Range A'right(N) downto
A'left(N) if A is
ascending for the Nth
index. A'right(N) downto
A'left(N) if A is descending
A'length[(N)] Range Any array object A Universal integer Number of values in the
Nth index range of N
Signal related
S'stable Signal Any signal S Boolean True when an event has not occurred; otherwise, false
S'event Function Any signal S Boolean True when an event has occurred; otherwise, false

 

Author's biography

Douglas Smith received a BSEE from Bath University (Bath, UK), after which he worked as a digital designer at a number of companies developing µP-based circuit boards and ICs. He has experience designing PLDs, FPGAs, gate arrays, and standard cell-based chips, and his EDA background includes synthesis-product application engineering and product-marketing management at GenRad Ltd. Smith is a member of technical staff at VeriBest Inc (Huntsville, AL) and author of the award-winning book HDL Chip Design--A Practical Guide for Designing, Synthesizing, and Simulating ASICs and FPGAs Using VHDL or Verilog, the source of this article. To order, call 1-800-311-3753 or visit www.doone.com on the Web. You can reach Smith via e-mail at djsmith@veribest.com.


| EDN Access | Feedback | Table of Contents |


Copyright © 1997 EDN Magazine, EDN Access. EDN is a registered trademark of Reed Properties Inc, used under license. EDN is published by Cahners Publishing Company, a unit of Reed Elsevier Inc.