You are on page 1of 45

Just When You Thought It Was Safe to Start Coding Again

... Return of the SystemVerilog Gotchas

Shalom Bresticker

Intel Corporation

Shalom.Bresticker@intel.com

ABSTRACT

All programming languages have gotchas: easy-to-make errors or misunderstandings that look
OK, but don't work, don't behave as expected, or differ between tools. The canonical example
from C is writing "if (a=b)" instead of "if (a==b)". Knowing about SystemVerilog's gotchas helps
prevent making these mistakes, and eases detecting and debugging them when they do occur.
Sutherland and Mills published a book of gotchas in 2007, but there are many more. This paper
presents a selection of additional gotchas that have bitten the author and his colleagues over the
years, some of them very nasty. The gotchas are divided into (1) gotchas that exist in Verilog (2)
gotchas that are new in SystemVerilog (3) gotchas connected to how the RTL code is
synthesized.

This is a revision of a paper of the same name that was presented at Boston SNUG 2008.

This work does not in any way constitute an Intel endorsement of a product or supplier.
Table of Contents

1.0 Introduction ......................................................................................................................... 4


1.1 Abbreviations and terminology........................................................................................... 4
1.2 Why are there gotchas? ....................................................................................................... 5
1.3 What types of gotchas are there? ........................................................................................ 6
1.4 Where do gotchas come from? ........................................................................................... 7
1.5 How to deal with gotchas .................................................................................................... 8
1.6 Previous work ..................................................................................................................... 9
1.7 What follows in the rest of the paper .................................................................................. 9
2.0 Verilog Gotchas ................................................................................................................ 10
2.1 always @* oscillations (V2K1) .................................................................................... 10
2.2 always procedures that are never executed .................................................................... 12
2.3 Misuse of logical AND and OR ........................................................................................ 13
2.4 Zero replication constants ................................................................................................. 14
2.5 Negative replication constants .......................................................................................... 16
2.6 Elaboration of unused illegal parameterized code ............................................................ 16
2.7 Incorrectly written indexed part-select (V2K1) ................................................................ 17
2.8 String size-extension ......................................................................................................... 18
2.9 Macro expressions need to be in parentheses ................................................................... 19
2.10 Macro call within string literal .......................................................................................... 20
2.11 Null ports .......................................................................................................................... 21
3.0 SystemVerilog Gotchas..................................................................................................... 22
3.1 Types defined in different scopes ..................................................................................... 22
3.2 Arrays vs. queues .............................................................................................................. 23
3.3 Using `` for concatenation within an escaped identifier ................................................... 24
3.4 Use of special macro character sequences outside of macros ........................................... 25
3.5 Are spaces around macro arguments dropped? ................................................................. 26
3.6 String equality and compare functions have opposite return values ................................. 27
3.7 Glitches in unique/priority ..................................................................................... 27
3.8 Glitches in immediate assertions ...................................................................................... 28
3.9 Assignments in expressions .............................................................................................. 29
3.10 Uneven coverage bin distribution ..................................................................................... 30
3.11 const variables are not parameters ................................................................................. 31
3.12 The static initialization order fiasco .................................................................................. 32
4.0 Synthesis Gotchas ............................................................................................................. 33
4.1 always_comb does not guarantee combinational logic ................................................ 33
4.2 My flip-flop is a latch! ...................................................................................................... 34
4.3 Myth: default/full_case/unique/priority prevent latches........................ 36
4.4 Why is my combinational logic considered sequential? ................................................... 39
4.5 A synthesis engine is not as smart as a formal engine ...................................................... 39
4.6 Asynchronous reset loops in flip-flops ............................................................................. 41
5.0 Conclusions and Recommendations ................................................................................. 42
6.0 Acknowledgements ........................................................................................................... 43
7.0 References ......................................................................................................................... 43

SNUG Boston 2008 2 Return of the SystemVerilog Gotchas


8.0 About the Author .............................................................................................................. 44

SNUG Boston 2008 3 Return of the SystemVerilog Gotchas


1.0 Introduction
What can be said about Verilog and SystemVerilog gotchas that has not yet been said?

Plenty, it turns out. That‘s bad for users and application engineers, but it‘s good for authors of
papers.

What is a gotcha? A gotcha is code that looks right, feels right, and smells right ... but isn‘t. (See
[21] for a fuller definition.) If you‘re lucky, it won‘t pass compilation, so that you‘ll know
immediately that something is wrong. If you‘re less lucky, it will do something obviously wrong
in simulation, like getting stuck in reset. If you‘re really unlucky, it will pass compilation and
simulate, but with a subtle error that you‘ll be hard pressed to detect. And in the worst case, it
will cause a bug in silicon.

Two papers in recent SNUGs have described Verilog and SystemVerilog gotchas, by Stuart
Sutherland and Don Mills (and Chris Spear on the second one). Stuart and Don followed this up
with a book that took the two papers and revised them, as well as adding additional gotchas.

This paper follows in their footsteps. The list of gotchas is not over yet, and even after this paper,
we still have more. Gotchas come in various degrees of severity, but we will try to describe some
of the nastier ones, which we or our colleagues have actually experienced. However, we will also
show a few mild ones as well.

The gotchas come from all versions of Verilog and SystemVerilog, so that both a person who is
still using only Verilog-1995 and one who is writing in SystemVerilog can learn from this paper.
In addition to gotchas in the language, we will also mention a few gotchas which derive purely
from how various constructs are synthesized.

This is a revised version of a paper of the same name that was presented at Boston SNUG 2008.
Minor changes were made in various places, sections 2.11, 3.9-3.12, and 4.6 are new, and
reference [22] was added.

1.1 Abbreviations and terminology


We will be using the following abbreviations:

 LRM - Language Reference Manual. Refers to the language standard documents. See the
References section at the end of the paper for the exact references.
 V95 – Verilog-1995, IEEE Std 1364-1995, Reference [4].
 V2K1 – Verilog-2001, IEEE Std 1364-2001, Reference [5].
 V2K5 – Verilog-2005, IEEE Std 1364-2005, Reference [6].
 SV – the SystemVerilog language, in a generic sense.
 SV-2005 - SystemVerilog-2005, IEEE Std 1800-2005, Reference [7].
 SV-2009 - SystemVerilog-2009. The next revision of IEEE Std 1800, in progress,
planned to be approved in 2009.
 WG – Working Group. Refers to the IEEE P1800 SystemVerilog standards committee.

SNUG Boston 2008 4 Return of the SystemVerilog Gotchas


Many users to refer to code segments that begin with "always" as "blocks", i.e., "always
blocks". The IEEE SV WG has decided to call them consistently "procedures", and so that is the
term we use in this paper.

Mantis[35] is the issues database of the IEEE 1800 SV standards committee. Mantis is the name
of an insect, i.e., a "bug", but the database contains clarification and enhancement requests as
well as reports of errata in the standard. This paper references some Mantis items. Read access to
the database is public. To get to an issue, you go to http://www.eda.org/svdb, and log in with
Username and Password both ―guest‖. Then in the upper right hand corner, you enter the Issue #
in the box that says ―Issue #‖ and press on ―Jump‖. That‘s it. (As shorthand, we often say simply
"Mantis N" instead of "Mantis issue number N". I use "Manti" as a shorthand plural form
instead of "Mantis issues".)

1.2 Why are there gotchas?


Why do gotchas exist? I could give a few humorous answers, such as:

 They give us something to write papers and books about.


 They give us an excuse why our design does not work and why we are late.
 They give job security for verification engineers.

But seriously, this is actually an important question, because sometimes people really ask why
there have to be gotchas. So here are a few serious answers:

ALL programming languages have gotchas. It is inherent in their nature. It does NOT mean that
SV is a bad language, but it does mean that you need to be careful. There are some languages that
have fewer gotchas. Generally, they are more verbose and less flexible and users don‘t like them
(e.g., ADA). Such languages can be good for writing code for life-critical applications, like
spacecraft.

Also, SV is not just a programming language. It is a Hardware Description and Verification


Language, making it much more than a general purpose programming language. SV is big (the
LRM is over 1200 pages long) and complex. It is also still relatively new and still evolving. Like
any other human endeavor, it is not perfect, just as the only programs without bugs are trivial
ones. Such a document is difficult to verify completely and cannot be done automatically.

It is also important to understand that most of the members of the standards committee work
either as volunteers on their own time, or at best their employer allows them a limited amount of
time to work on it. There are very few people, if any, whose main assignment from their
employer is to work on the SV standard.

Sometimes you hear comments that basically accuse the standards committee of making silly
decisions. Sorry, I disagree. They sometimes make errors or bad decisions, like all humans, but
the committee contains some of the top industry experts. Usually there was a good reason why
the standard was defined in a certain way. In retrospect, it can sometimes turn out that it was not

SNUG Boston 2008 5 Return of the SystemVerilog Gotchas


always the best decision. There is also often a need to make compromise between multiple
considerations. In my experience, inter-company politics is rarely a factor.

Ambiguities can arise because the contributors to the standards committee are engineers, not
technical writers. Sometimes, they write not in the best way. They may forget that readers of the
LRM are not as expert in the language as they are. Sometimes they thought that what they wrote
was clear or that a particular point was obvious, and maybe it is to most readers, and yet there
may still be a significant minority of readers for whom it is not clear or obvious. Another
problem is that the full description of all the information related to a particular construct is
sometimes distributed over several places, or simply difficult to find.

Additionally, the textual descriptions in the LRM are often informal. This is actually good, as it
makes the LRM much more understandable than many others I have seen, but has the price
sometimes of less preciseness. There is sometimes a tradeoff between clarity and preciseness.

Finally, sometimes the committee knew there was an issue, but did not have time to handle it.
With limited resources (manpower, time), priorities are often determined by importance and
complexity.

1.3 What types of gotchas are there?


There are different ways to classify gotchas. One way is by the symptoms it causes. For example:

 You think it should compile/elaborate cleanly, but it doesn‘t. Either it fails completely or it
gives an unexpected warning.
 It may compile, but behave differently than you expected. It may not work at all, such as
producing X outputs, getting stuck in a loop, or not finishing reset.
 One of the most dangerous types of gotchas is where it behaves differently, but not blatantly.
Then it is hard to spot that there is even a problem.
 Another difficult type of gotcha is where it fails intermittently. Then you also have to try to
understand under what conditions it fails.
 A gotcha may express itself by working on some tools or tool versions and failing on others.

Another way to classify gotchas is whether they are written "accidentally" or "deliberately". By
this, I mean the following:

 An "accidentally" written gotcha is one where you write something different than what you
really intended to write, due to a typographical error or due to forgetfulness, for example. If
someone would point out to you what you wrote, you would say, "Oops!". This turns from a
simple error into a gotcha if it is difficult to spot the mistake or if instead of completely
failing, the code works but wrongly.
 A "deliberately" written gotcha is one where you wrote what you intended to write, because
that is how you believe and understand it should be written, and you are unaware that your
understanding is incorrect or that there is some problem with it. If someone would point out
to you what you wrote, you would say, "So what? What is the problem?".

SNUG Boston 2008 6 Return of the SystemVerilog Gotchas


1.4 Where do gotchas come from?
There are various sources of gotchas. We‘ll mention a few, but there are others.

One of the classic gotcha forms is where there are two similar syntax forms that are both legal,
but behave differently. You may get confused between them, or simply accidentally write the
wrong one. Gotchas 2.3 and 2.7 below are of this type.

A major source of gotchas is ambiguities in the LRM. There are cases that are unspecified. In
some places, there is unclear or misleading language. Unspecified cases can occur because no
one thought of them or because someone did think of them but thought that the LRM does
specify the behavior in that case, and only deeper thought shows that it is not specified well
enough. Gotcha 2.10 below is an example of this.

Sometimes an ambiguity is deliberate because the committee was unable to reach a consensus on
the desired behavior and just leaves the issue open. Gotcha 3.3 below is one that is still
unresolved for this reason.

Other times, though, users or developers misunderstand the LRM even though the LRM does
explain it clearly. Someone may have a blind spot in reading that section of the LRM.
Occasionally, people make an incorrect deduction because some statement that appears in one
place is not repeated somewhere else, and then they (incorrectly) conclude that the statement
does not apply in the second place.

Among the worst problems is where tools implement an ambiguity differently. Then your code
becomes non-portable. You may not even be aware of the problem and discover it at the worst
possible time. Gotcha 3.4 below is a good example of this.

Another source of gotchas is errors in the LRM. It is not so bad when the error is obvious. But
when it is not so obvious, one tool may implement the LRM as written while another implements
the ―correct‖ behavior. Similarly, one user may expect the tool to implement the LRM as written,
whereas another may expect the tool to implement the ―correct‖ behavior. A mismatch between
the user and the tool, moving the code from one tool to another, even one user reusing the code
of another, may all result in unexpected behavior.

Another problem is differences between versions of the language. You can get confused. The
differences are almost not documented in the LRM itself. So what worked till now might not
work or might not work the same way in the future, although the standards committee tries to
minimize that. The biggest problem is usually new keywords, but occasionally there are other
things, such as generate syntax and semantics that changed from V2K1 to V2K5.

You especially have to be careful when writing code according to a newer version of the LRM,
then moving to a tool that uses an older one. In fact, sometimes different tools from the same
vendor implement different versions of the standard. And two versions of a tool may implement
different versions of the LRM. Or a tool may implement some features according to one version

SNUG Boston 2008 7 Return of the SystemVerilog Gotchas


of the LRM and others according to another. In addition, of course, each tool implements a
different subset of features of the LRM. It is also very difficult to obtain full and accurate
documentation about what each tool implements and how it does so.

(To be fair, the tool vendors have a tough job. Each version of the standard adds a bunch of new
features and corrects errors in the previous versions. Vendors are always playing catch-up.)

There are recognized gotchas that the committee would love to remove but is unable to do so,
because that would break back-compatibility. Legacy code would stop working.

A lot of SystemVerilog is based on older languages such as C and Vera. In some cases, SV
implements features in the same way as the older language and sometimes differently. Where it is
the same as the older language, it inherits gotchas from that language. Where it is different, there
is a new gotcha: people expect it to be like the older language, but it is different. Some days, you
just can't win!

Built-in default behavior in the language can also be a gotcha source. What happens when you do
not specify something explicitly in the code? If you are unaware of or forget the default behavior,
you may get unexpected results. An example is how module port attributes, such as direction,
kind (net or variable), and packed and unpacked dimensions, are inherited from previous ports in
the port list. (This particular one is clarified in the next revision of the LRM, see Mantis 1465.)

There are other sources of gotchas, but this paper is supposed to be about the gotchas themselves,
not where they come from, so we'll stop here.

1.5 How to deal with gotchas


What benefits do we have from knowing about all these gotchas?

It can help us prevent them from occurring. Awareness causes more careful coding. You can
formulate coding guidelines that reduce the risk of making these mistakes. Context-sensitive
editors (e.g., with auto-indent) can aid us.

Knowing about gotchas helps us to detect them after they have already been written. We can look
for them in code reviews. We can have Lint tools check for them.

When trying to debug a problem, they give us ideas of possible causes to look for, at least sub-
consciously. We can try to match the symptoms with possible causes. Experience also gives you
an idea of the frequency with which gotchas occur. Just hearing the symptom often enables you
to make a good guess of the problem.

Since some gotchas come from ambiguities and errors in the LRMs and from the fact that the
2005 standard is comprised of a combination of the Verilog and SystemVerilog LRMs, it is
helpful to look at an up-to-date draft of the new merged SystemVerilog LRM (if your company
has access to it). Besides eliminating most of the confusion that resulted from having two

SNUG Boston 2008 8 Return of the SystemVerilog Gotchas


different LRMs, the new merged LRM also contains many corrections and clarifications to the
2005 standard. This makes it much clearer in many cases what the correct behavior is.

Finally, consider participating in the IEEE P1800 SV standards committee. This brings you
together with the top SV experts in the industry and brings to your attention issues that you
would never otherwise hear about, often before you encounter them yourself. It also gives you a
forum before which you can bring up issues for discussion. Of course, it is not for beginners, but
you won‘t be a beginner any more after reading this paper!

1.6 Previous work


Gotchas are as old as the Verilog language, of course (Older, actually, since some of them are
inherited from earlier languages.). It's surprising, then, that only a couple of years ago, Stuart
Sutherland and Don Mills had the brilliant idea of compiling Verilog and SystemVerilog gotchas
in an orderly way, first in two SNUG papers ([1] and [2]) and then in a book based on those
papers ([3]).

At SNUG Israel 2007, Qualcomm engineers presented gotchas related to the Vera constraint
solver [22]. Since that part of SystemVerilog was based on Vera, those gotchas are largely
relevant to SystemVerilog as well. The same engineers are presenting more Vera gotchas at
SNUG Israel 2009.

Earlier, Don Mills and Cliff Cummings compiled some gotchas related to synthesis-simulation
mismatches ([14]) and there were a number of papers that touched on various specific types of
gotchas, particularly related to X-propagation (See [13] and [15]-[19], for example).

Two excellent recent books devoted to teaching the design and verification parts of
SystemVerilog ([9] and [10]) made a point of noting gotchas related to the new SystemVerilog
features discussed.

[11] is a very nice book written in Question and Answer fashion that also discusses a number of
gotchas. Although the book title mentions only Verilog, it also discusses some SystemVerilog.

Finally, the Doulos SV Reference Guide ([12]) is organized according to alphabetical order of
language construct names. Many of the sections include, in addition to a description of the
construct, "Tips" and "Gotchas!" related to the construct. Very nice.

1.7 What follows in the rest of the paper


The rest of this paper describes a number of gotchas of various types. Section 2 describes gotchas
that exist in older versions of Verilog (1995, 2001, 2005) as well as in SystemVerilog. Section 3
describes gotchas that are new in SystemVerilog. Section 4 describes gotchas that derive from
how your RTL code is synthesized. Section 5 contains some closing words of encouragement and
advice so that you won't finish reading this paper feeling that there is no hope of writing code that
will actually work.

SNUG Boston 2008 9 Return of the SystemVerilog Gotchas


2.0 Verilog Gotchas
This section describes some gotchas that already appear in Verilog (1995 or 2001) and are still in
SystemVerilog.

2.1 always @* oscillations (V2K1)


For coding synthesizable RTL in Verilog-2001, we usually recommend to code combinational
and latch blocks with always @* and its automatic sensitivity list instead of manually
enumerating the sensitivity list in a regular always procedure.

However, there is a case where that can get you into trouble. Suppose you have two always @*
procedures containing for-loops and you use the same loop index variable for both, like this:

integer k;
reg [31:0] out1[0:7], out2[0:15], in1, in2;

always @*
for (k = 0; k < 8; k = k + 1)
out1[k] = in1 + k ;

always @*
for (k = 0; k < 16; k = k + 1)
out2[k] = in2 * k ;

Why not? In Verilog-1995, we used the same loop index with multiple loops all the time for RTL
without problems. (Gotcha 66 in [3] does show a potential problem, but that rarely occurred in
synthesizable RTL.)

Suddenly, in V2K1, we find that some simulators get stuck. All we did was change the sensitivity
list to @*. Darn those language developers! Can‘t they leave well enough alone??

What happens here is that according to the LRM of V2K1, the first always @* turns into
―always @(in1 or k)‖ and the second turns into ―always @(in2 or k)‖. Note that k
appears in both lists.

Suppose that in1 changes. This triggers the first always @* procedure. It executes, including
the loop, and k changes a number of times. k changes, you said? That triggers the second
procedure, even though in2 has not changed. The second procedure executes unnecessarily,
wasting simulator time, but that is not the worst of it. When the second procedure executes, it
also executes its for-loop, also changing k. This retriggers the first procedure. And so they go on,
back and forth, with the simulator stuck in an infinite loop. GOTCHA!

Why did this not happen in Verilog-1995? Because then we did not put the loop index into the
manual sensitivity list. But in always @*, it is put in automatically. (This problem does not
occur with all simulators. Apparently some of them don‘t put the loop index into the sensitivity

SNUG Boston 2008 10 Return of the SystemVerilog Gotchas


list even though strict compliance to the LRM would do so.) This gotcha occurred in the
ovl_arbiter logic of the OVL (Open Verification Library) as well.

This only occurs when both loops are in always @* procedures. If only one loop is, and the
other is in a sequential always @(posedge clk) procedure or a regular always procedure
with a manual sensitivity list and the loop index does not appear in the list, then this will not
happen.

Workarounds:

How can we fix this? One way is to avoid using always @* with loops, but we would like to
avoid writing the sensitivity list manually. Within Verilog-2001, we have two workarounds. We
have to use different variables for each of the loops. We can declare two different variables
globally, like this:

integer k1, k2;


reg [31:0] out1[0:7], out2[0:15], in1, in2;

always @*
for (k1 = 0; k1 < 8; k1 = k1 + 1)
out1[k1] = in1 + k1 ;

always @*
for (k2 = 0; k2 < 16; k2 = k2 + 1)
out2[k2] = in2 * k2 ;

Another way is to declare the loop variables locally within the always @* procedures, like
this:

reg [31:0] out1[0:7], out2[0:15], in1, in2;

always @* begin:loop1
integer k;
for (k = 0; k < 8; k = k + 1)
out1[k] = in1 + k ;
end

always @* begin:loop2
integer k;
for (k = 0; k < 16; k = k + 1)
out2[k] = in2 * k ;
end

If we can use SystemVerilog, then we can change the always @* to always_comb, which
will not put in the sensitivity list any variable that is written to within the procedure (which has

SNUG Boston 2008 11 Return of the SystemVerilog Gotchas


its own gotcha, but that‘s another story...). Another solution in SystemVerilog is to declare the
loop variables within the for-statement itself, like this:

for (int k = 0; k < 16; k = k + 1)

This also makes each loop variable separate. This is the best solution, as using the same variable
for the different loops is what caused the problem in the first place.

2.2 always procedures that are never executed


The first gotchas paper ([1], in sections 3.2 and 6.4) has pointed out that while always_comb
procedures and continuous assignments are executed at time 0 in order to initialize them, this is
not true of regular always procedures. There are additional gotchas to this.

We once had a case that looked something like this:

always @*
`ifdef FULL
out = in ;
`else
out = 0;

This did not work on some tools when FULL was not defined. It reduces to an always
procedure with a null sensitivity list, and was never executed. There was nothing to trigger its
execution. GOTCHA!

Changing the always @* to always_comb would solve the problem, but we were not using
SV. In the real case, the code inside the `ifdef FULL was actually a case statement of
substantial size, so the code rewrite was more work. But the big problem was simply to
understand why it did not work.

A variation of this is where the right-hand side contains a variable, but the variable never
changes. This can occur, for example, if the variable is initialized in the variable declaration, like
this:

logic a = 0 ;

This is called a variable declaration assignment. Such initializations are executed before the
always procedure is activated. Thus, although the variable‘s value changes at the time of
initialization, no change in the value occurs after activation of the always procedure.

This can also occur if the variable is initialized in an initial procedure at time 0. Since no
order is defined between initial and always procedures, the initial procedure may be
executed first. Or it can occur if there is some force to the variable value before the always
procedure starts working.

SNUG Boston 2008 12 Return of the SystemVerilog Gotchas


Even if the right-hand side variable value later changes, the output of the always procedure will
still not be initialized from the beginning of the simulation until the first time the variable
changes later on.

2.3 Misuse of logical AND and OR


Gotcha 53 in [3] and 6.11 in [1] are about mixing up the unary logical NOT (!) and bit-wise
NOT (~) operators. This one is about the binary logical AND and OR (&&, ||) and bit-wise
AND and OR (&, |) operators.

The logical AND and OR operators treat their operands as either TRUE or FALSE, non-zero and
zero, respectively. They return a 1-bit result, either 1‘b1 or 1‘b0 (or 1‘bx if the result is
unknown). Thus, 2’b10 && 2’b01 is TRUE because both operands are non-zero and returns
1‗b1. This is like writing (2’b10 != 0) && (2’b01 != 0).

On the other hand, the bit-wise operators treat each bit of the operands individually, size-extend
the shorter operand to the length of the longer operand, and return an N-bit result, where N is the
size of the longer operand. Thus, 2’b10 & 2’b01 is 2‘b00.

For 1-bit operands, the logical and bit-wise operators behave the same. However, a myth has
developed that says that you need to use logical operators for control signals and bit-wise
operators for data-path signals. No! Verilog simulators have no understanding of ―control‖ and
―data-path‖. Both forms of operators simply work according to their definitions in the LRM,
which are identical for one-bit operands.

The gotcha which can occur is using logical operators instead of bit-wise operators where the
operands are vectors. This mistake is very easy to make. It is simply to type the & or | character
twice instead of once. And the code is perfectly legal. It's just wrong.

Fortunately, it is uncommon to write code with logical operations on vectors, like this:

a[31:0] && b[31:0]

It is much more common and also much more readable to write it with explicit comparisons to
zero, like this:

(a[31:0] != 0) && (b[31:0] != 0)

Thus, we can configure our Lint tool (and we should all be using a Lint tool) to flag logical
ANDs and ORs with vector operands.

As a coding guideline, I strongly recommend to use only bit-wise AND and ORs in synthesizable
RTL, and not use logical ANDs and ORs at all. This has several reasons. One is that it avoids this
gotcha. Another is that anything that can be written with the logical operators can be written with
the bit-wise operators as well (by comparison to zero), whereas the reverse is not true. Writing

SNUG Boston 2008 13 Return of the SystemVerilog Gotchas


the comparisons to zero explicitly also makes the code more readable. That is what you should
do if you really want to achieve the effect of logical operations on vectors.

There are nevertheless two other differences between logical and bit-wise operators that fairness
requires me to mention. One is that some code coverage tools check logical operators but not bit-
wise operators. But these can usually be configured to cover the bit-wise operators as well.

The other difference is in short-circuiting. Short-circuiting is described in Gotcha 52 in [1] and


5.8 in [3]. The Verilog LRM up to now allowed, but did not require, short-circuiting on any
operators. (See section 5.1.4 of 1364-2005.)

In practice, however, many tools implement short-circuiting as in C, on logical operators, but not
on bit-wise operators. In practice, short-circuiting did not make any difference in real
synthesizable RTL descriptions. However, in behavioral or test-bench code, you might want the
short-circuiting behavior. The upcoming SV-2009 LRM will codify this behavior (Mantis 997).

In certain SV code involving auto-increment or auto-decrement operators (++ or --), short-


circuiting might make a difference even in synthesizable RTL code. But in those cases, you
would probably want the operations to always be performed and not have short-circuiting occur,
so there also the bit-wise operators would probably be better.

2.4 Zero replication constants


Verilog has a special form of concatenation called replication or multiple concatenation. You
write {N {expr}}, which replicates the expression N times. N, the replication constant, must
be an expression with a constant value. For example, {b, {3{a, b}}} yields the same value
as {b, a, b, a, b, a, b}.

What happens if N is zero? The 1995 LRM does not say. Most tools replace the entire replication
by 1‘b0. So {b, {0{a, b}}} becomes {b, 1’b0}.

A true zero replication would actually be very useful. What do you want/expect to happen if the
replication constant is zero? You want the replication to disappear entirely! The most common
place where this occurs is where you use replication to add N leading zeros to an expression that
is P-N bits wide in order to get a total of P bits. For example, if N is a parameter in the range 0 to
31,

a[31:0] = {{N{1’b0}}, b[31-N:0]};

This works fine for N=1 up to 31. What happens if N is 0? Now you have

a[31:0] = {{0{1’b0}}, b[31:0]};

and this turns into

a[31:0] = {1’b0, b[31:0]};

SNUG Boston 2008 14 Return of the SystemVerilog Gotchas


Now your right-hand side is 33 bits instead of 32, and if you have a Lint tool, it may yell at you
because of a size mismatch. But the only thing that happens is that you have an extra leading
zero, which gets truncated. Not so bad. It does not affect the result.

But what happens if the replication is not of leading zeros, but in the middle or end of your
concatenation? Suppose it was like this:

a[31:0] = {b[31-N:0], {N{1’b1}}};

Now with N=0 you get this:

a[31:0] = {b[31:0], 1’b0};

That‘s not what you wanted. GOTCHA!

The Verilog-2001 LRM attempted to deal with this gotcha by declaring zero replications illegal,
but it did not help much. Now many tools will issue a warning on a zero replication, but
engineers are infamous for ignoring warnings. Furthermore, since the vast majority of cases are
the harmless leading zero cases, you can‘t find the one warning which points out a real problem.
GOTCHA again! And even if you do find the real problems, the warning does not help you to
actually solve them.

The Verilog-2005 LRM attempts to deal with this in a better way. As we said, when the
replication constant is zero, you want the replication to disappear entirely That is exactly what
the 2005 LRM says should happen. In order for this to not cause the problem of having an
expression of zero bits, the LRM also says that a zero replication can only appear as part of a
concatenation where at least one element of the concatenation has a positive length. So {b,
{0{a, b}}} is legal, but {{0{a, b}}} is not. Unfortunately, most tools are still not
implementing this part of the 2005 LRM and are still doing the same erroneous behavior of the
past. GOTCHA yet again! They probably do this for reasons of backwards compatibility.

What can you do today if the current zero replication behavior will cause an error? Typically,
intermediate variables, shift operations, and part-selects can be used to make sure that the code is
correct for all values of the replication constant. For example, in our last case,

a[31:0] = {b[31-N:0], {N{1’b1}}};

we can recode this as

temp[63:0] = {b[31:0], {32{1’b1}}};


a[31:0] = temp[(63-N)-:32];

As a guideline, where a replication is used with a parameterized replication constant, you should
verify that the code works correctly for the maximum and minimum values of the replication
constant.

SNUG Boston 2008 15 Return of the SystemVerilog Gotchas


2.5 Negative replication constants
What happens if the replication constant is negative instead of zero? In contrast to zero
replication constants, which are very common and very useful, negative replication constants are
not very useful and thus are also very rare in practice. If they do occur, it is usually a mistake.

Like zero replications, the Verilog-2001 LRM also makes these illegal. But unlike zero
replications, these remain illegal in the 2005 standard, and should cause compilation errors.

However, many tools treat these in the same way as zero replications, turn them into 1‘b0, and
just give a warning. So you both only get a warning, which many ignore, and it also ends up
doing something strange. GOTCHA!

Sometimes the warning just says that the replication constant is ―zero or negative‖, and then you
have to examine it to see whether it is zero or negative.

2.6 Elaboration of unused illegal parameterized code


Sometimes you write parameterized code which can be legal or illegal depending on the
parameter value. However, you know that the illegal version is not used or is deselected when the
parameter has such a value.

In the following example, the parameter DEV_NUM determines how many times to instantiate
module mm, each time with a different value of parameter S. The code allows as many as three
instantiations, but it has been determined that only 2 are to be used in this version of the design,
so only S0 and S1 are needed. S2 has been set to 0 since it will not be used. mm will not be
instantiated using S2. Anytime S2 is to be used, it will be given a positive value.

module mm #(parameter S=1)(input sig[S-1:0]);


...
endmodule

module top;
parameter DEV_NUM = 2;
parameter S0 = 2, S1 = 1, S2 = 0;
wire sig[31:0];
generate
for (genvar ii=0; ii <DEV_NUM; ii++) begin :loop
mm mm #(.S((ii == 0) ? S0 :
(ii == 1) ? S1 : S2 )
(.sig((ii == 0) ? sig[S0-1:0] :
(ii == 1) ? sig[S1-1:0] : sig[S2-1:0]);
end
endgenerate
endmodule

SNUG Boston 2008 16 Return of the SystemVerilog Gotchas


When we try to compile this, we get an error saying that we have written sig[-1:0], which
has both an out-of-range index and is also in the wrong order, from low to high, whereas sig is
declared as high to low, 31 to 0. This comes from the expression sig[S2-1:0].

But, you say, ii never gets to be 2! It does not matter. The code generated for ii = 0 will be

(.sig((0 == 0) ? sig[2-1:0] :
(0 == 1) ? sig[1-1:0] : sig[0-1:0]);

And for ii =1, it will be

(.sig((1 == 0) ? sig[2-1:0] :
(1 == 1) ? sig[1-1:0] : sig[0-1:0]);

It is true that in simulation, short-circuiting will occur, but in the elaboration stage of the
compilation it sees sig[-1:0].

What can we do?

In this simple example, of course, we can just set S2 to a positive number, but in the real case
this came from, S2 needed to be 0.

A solution in this case is to rewrite the code as follows:

(.sig(sig[((ii == 0) ? S0 :
(ii == 1) ? S1 : S2)-1:0]);

In this way, the index needed is computed as part of a constant expression that is computed at the
elaboration stage. It never tries to actually reference sig[-1:0].

There is an additional advantage to this recoding. In the original code, there is a conditional
selection between sig[S2-1:0], sig[S1-1:0], and sig[S0-1:0]. Each has a different
length. Even if S0 did have a legal value, the compiler would do size-extension of each to the
length of the longest of them. This might not cause a functional problem (though it might in
some cases), but at the very least, you get port size mismatch warnings from the tools.

Sometimes the recoding is considerably more difficult than in this simple example.

2.7 Incorrectly written indexed part-select (V2K1)


This one is nasty. I have seen it only once, but it was very difficult to find.

A regular part-select is of the form [c1:c2], where c1 and c2 are constant expressions. The
placement of such a part-select is therefore constant. V2K1 introduced indexed part-selects to
allowed part-selects of constant width, but with variable positions. An indexed part-select has the
form [n +: c] or [n -: c], where n can be a constant or a variable, and c is a constant

SNUG Boston 2008 17 Return of the SystemVerilog Gotchas


expression. Here we‘ll discuss the [n +: c] case, but the same gotcha can occur with [n -:
c].

[n +: c] covers the bit-range [n : n+c-1] or [n+c-1 : n], depending on whether the


variable is declared with a bit-range [high:low] or [low:high]. The compiler arranges the
bits from the part-select in the same direction as specified in the declaration.

Suppose you forget whether the indexed part-select is written +: or :+? It took me a few years
to get it straight in my mind. What happens if your fingers are dyslexic and type them in the
wrong order?

Now we have [n :+ c]. This is the same as [n : +c] or simply [n : c]. If n is a


variable, you get a compilation error, because variables are not allowed in regular part-selects.
But suppose n as well as c is also a constant expression. This can be a completely legal
expression.

Example:
logic [255:0] cucu;
...
if (cucu[95:+32] == 0) ... // instead of cucu[95+:32]

In this example, the user wants to check the value of the 32 bits 127 to 95 from the vector cucu,
but by reversing the symbols of the indexed part-select operator, he instead got the 64 bits from
95 to 32. GOTCHA!

If your coding guidelines are strict on bit-size matching and you use a Lint tool, then you have a
good chance of detecting this error. For example, if you wrote

if (cucu[95:+32] == 32’b0) ... // instead of cucu[95+:32]

then the Lint tool could flag that the left-hand side of the comparison is 64-bits wide, whereas the
right-hand side is only 32 bits wide, and would indicate that something is wrong. It could still
take you time to figure out the problem, because the code looks correct, but at least you have a
hint of the problem.

It is possible to have a situation where even the size would remain the same, such as [15+:8]
vs. [15:+8], both of which are 8-bits wide, but that is rarer.

2.8 String size-extension


Most of us have gotten used to the idea that numerical operands in an expression are size-
extended to the size of the widest operand. We are less used to it with respect to strings, and it
can hit us when we least expect it.

SNUG Boston 2008 18 Return of the SystemVerilog Gotchas


One particular case where it is easy to forget size-extension is in the conditional operator. If we
write

cond ? expr1 : expr2

then the shorter expression of expr1 and expr2 is extended to the size of the wider one. But
suppose we have something like this:

integer file;
file = $fopen({"filename", dat1 ? ".dat1" : ".dat"}) ;

In this contrived example, we concatenate a file extension .dat1 or .dat to the given
filename, where a variable called dat1 tells us the type of the file. If the variable dat1 is true,
there is no problem, we open a file named ―filename.dat1‖, but if dat1 is false, then we try
to open a file called ―filename .dat‖, with a space before ―.dat‖, which is extended to the
size of ―.dat1‖ before being concatenated to ―filename‖. GOTCHA!

Actually, the shorter string literal is not extended with a space character, which is x20 ASCII, but
rather with zeroes (zero-extension), which are null characters. However, when used as a string,
this often becomes a space.

Note that if we had assigned the concatenation to a variable of string type, this would not
occur.

string temp;
temp = {"filename", dat1 ? ".dat1" : ".dat"} ;
file = $fopen(temp) ;

The shorter string literal would still be zero-extended. However, upon assignment to string
variables, null-characters are ignored, so ―.dat‖ would still be appended directly to
―filename‖.

2.9 Macro expressions need to be in parentheses


This gotcha exists in C as well. Suppose you have

`define CUCU a+4

... `CUCU * 12

You want to get (a+4) * 12. Since macros are pure text substitution, what you really get is a+4 *
12, which is a + (4*12), which is not what you wanted. This can happen when a macro defines an
arithmetic or boolean expression that is then substituted into code next to an operator of higher
precedence. In this example, the macro contains the addition operator, but is substituted into text
next to a multiplication operator, which is of higher precedence.

SNUG Boston 2008 19 Return of the SystemVerilog Gotchas


I have seen this bug several times. The solution to this is to write macro expression definitions
with enclosing parentheses, like this:

`define CUCU (a+4)

But this is not enough. The book Code Complete, section 5.9, shows that if you have a macro
with arguments, then each use of the arguments also has to be enclosed in parentheses. For
example:

`define PR(x,y) (x*y)

... `PR(a+3,2)

This will still give the wrong result, a+3*2 instead of (a+3)*2. Instead the macro has to be
written like this:

`define PR(x,y) ((x) * (y))

2.10 Macro call within string literal


If you have a string literal that contains a character sequence that looks like a valid text macro
usage, is it considered to be a call to the text macro and substitution performed? Or is it just
considered to be a random character sequence like any other within a string literal, with no
semantic meaning? For example, if we have

`define HI Hello
initial $display("`HI, world");

is the result "`HI, world" or "Hello, world" ?

The LRM has not been sufficiently clear. In practice, some tools have interpreted the LRM in one
way and some have interpreted it the other way. This causes the problem that people have written
code assuming that a text macro call inside a string literal is expanded, because that is what their
tool does, and then the code no longer works when moving to a different tool. GOTCHA!

One of the justifications given for expanding macros within a string literal has been that this is a
very useful feature. The opposing opinion has been that this is inconsistent with the Verilog
concept that a string literal is a single lexical token. Furthermore, SystemVerilog has added the
ability to construct string literals (within macro text) using the `" token.

The IEEE SV WG has resolved this issue in Mantis 1119 in accordance with the opinion that no
expansion of any sort occurs within string literals. This means in addition that if a macro text
definition contains a string literal, and that literal contains a character sequence that matches the
name of a formal argument to the macro, it also is not recognized there as a call to the argument
and not expanded. Thus, in this example,

SNUG Boston 2008 20 Return of the SystemVerilog Gotchas


module main;
`define HI Hello
`define LO "`HI, world"
`define H(x) "Hello, x"
initial begin
$display("`HI, world");
$display(`LO);
$display(`H(world));
end
endmodule

no expansion of macro calls or macro arguments occurs within the string literals and the result is:

`HI, world
`HI, world
Hello, x

2.11 Null ports


What is wrong with the following module header?

module top(
a, b,
/* c */, d,
e, f) ;

The answer is that between ports b and d, there are two consecutive commas:

module top (a, b,, d, e, f);

How many ports does module top have? Six!

The third port is called a "null port" or an "empty port". It has no name, size, or direction, but
exists all the same. And it is legal syntax. It may even compile without any warnings.

Until you try to instantiate and connect it. Then you start getting warnings about unconnected
ports or if you connect the ports by position, you may get a type match error between the port
type and the connection type. GOTCHA!

This mistake is especially likely to occur when you list the ports one per line with a comma at the
end and then delete or comment out the last one, like this:

module top(
a,
b,
// c

SNUG Boston 2008 21 Return of the SystemVerilog Gotchas


);

When you get "unconnected port" warnings, and you can see that the number of connections is
correct, it is a good bet that this is the problem.

This error is also less likely to pass undetected by the tools when declaring ports in "ANSI-style,"
where the port direction and size as well as the port name appears in the module header. The
following will probably not compile, for example:

module top(
input a,
output b,
// c
);

3.0 SystemVerilog Gotchas


These are gotchas that are new in SV.

3.1 Types defined in different scopes


This gotcha is stated explicitly in the 1800-2005 LRM in Section 6.9, but not emphasized. I have
not seen it described in many SV trainings, either. But to many people, it is not intuitive.

The LRM words this as follows:


―The scope of a data type identifier shall include the hierarchical instance scope. In other words,
each instance with a user-defined type declared inside the instance creates a unique type. To have
type matching or equivalence among multiple instances of the same module, interface, or
program, a class, enum, unpacked structure, or unpacked union type must be declared at a higher
level in the compilation-unit scope than the declaration of the module, interface, or program, or
imported from a package.‖

This has several implications. For example,

typedef struct {int A; int B;} AB_t;


typedef struct {int A; int B;} otherAB_t;

defines two different types and you cannot simply assign a variable of one type to a variable of
the other, even though the type contents are identical. You must use an explicit type cast.
GOTCHA!

Furthermore, if the type declaration of AB_t is found in module m, and m is instantiated twice, as
m1 and m2, then the two types m1.AB_t and m2.AB_t are considered different types and again
cannot be assigned from one to the other without an explicit cast.

SNUG Boston 2008 22 Return of the SystemVerilog Gotchas


However, if the typedef is found at a higher level, such as in the compilation-unit scope of the
module ($unit), or in a package that is imported into the module, then the two module
instances are considered to have the same type definition.

An anonymous type declaration also defines its own type. An anonymous type declaration is
where the type definition appears as part of the variable declaration, and not as a separate
typedef. For example:

struct {bit[15:0] value;} AB4, AB5;


struct {bit[15:0] value;} AB6;

AB4 and AB5 are defined with the same anonymous type declaration, and so they are
assignment-compatible, but AB6 has a separate anonymous type definition and thus is not
assignment-compatible with AB4 and AB5 without a cast, even though the type definitions are
identical.

As stated in the LRM, these restrictions apply to enums, unpacked structures and unions, and
classes. So they do not apply, for example, to packed structs or to arrays, packed or unpacked.

So a function can return an unpacked struct, for example, but you won‘t want to define the struct
as an anonymous type in the function header, like this:

function struct {bit[15:0] value;} f(args);

because then you will not be able to assign the function return value to another variable in the
calling scope, as they will be considered to have different types:

AB4 = f(args); // illegal, different types

3.2 Arrays vs. queues


In SystemVerilog 3.1a, pre-IEEE 1800, array and structure literals and expressions were written
with curly brackets, like concatenations. For example:

bit unpackedbits [1:0] = {1,1};

This made for confusion between these and concatenations, which had essentially identical
syntax. It made life more difficult for tools also.

In IEEE 1800-2005, the syntax of structure and array literals and expressions (now called
"assignment patterns") was changed to require an apostrophe before the curly brackets, like this:
bit unpackedbits [1:0] = '{1,1};

SNUG Boston 2008 23 Return of the SystemVerilog Gotchas


This is described in Gotchas 17 and 100 in [3]. However, there is an additional gotcha in this
area. Even in IEEE 1800-2005, we still see some examples without the apostrophes that behave
neither like regular concatenations nor like regular assignment patterns.

For example, here is an example of a queue declaration with an initializer:

integer q[$] = {3, 2, 7}; // An initialized queue of integers

This initializes the queue array q to three elements, where q[0]=3, q[1]=2, and q[2]=7.
We also have examples like the following, which describes insertion of a new element to a
queue:
q = {q[0:pos-1], e, q[pos,$]}; // insert 'e' at position pos

Here is an example with string variables:


string d[1:5] = '{"a", "b", "c", "d", "e"};
string p[];
p = {d[1:3], "hello", d[4:5]};

This creates the dynamic array p with contents "a", "b", "c", "hello", "d", and "e".
This is obviously not a regular concatenation, whose elements can only be sized scalars or
vectors, but not array slices (e.g., d[1:3]) or unsized integers (e.g., 3).

These examples show a sort of concatenation where an element that is an unpacked array, such as
d[1:3], is expanded into its individual members, as though you had written d[1], d[2],
d[3].

There was a long discussion in the IEEE SV committee as to whether this syntax and these
examples should be legal, or whether they should be considered errors in the LRM and queue
concatenations should use apostrophes, like other assignment patterns. Tool support is not
consistent. So do you use apostrophes or not? Only your compiler knows for sure. GOTCHA!

In the end, it has been decided to allow all of these examples and define an ―unpacked array
concatenation‖ that will have the shown behavior. The exact description is beyond the scope of
this paper, but can be found in Mantis 1702, and is part of the draft for the next version of IEEE
1800, in 2009.

3.3 Using `` for concatenation within an escaped identifier


Verilog users have always wanted the ability to construct an identifier name by gluing together
two strings of characters. SV added this capability within text macros, allowing you to glue
together two fixed character strings and/or macro arguments, using two consecutive ―back-tic‖
(accent grave) characters.

For example,

SNUG Boston 2008 24 Return of the SystemVerilog Gotchas


`define foo(f) f``_suffix
`foo(bar)

expands to

bar_suffix

What happens if the left-hand side of this ―glue operator‖ is a back-slashed character string (i.e.,
an escaped identifier)? For example, suppose you have the following:

`define RND_MACRO(in, out) mod \inst_``out (.in1(in),.out1(out))


`RND_MACRO(sig_in[1], sig_out[1]);

What you want is the following result:

mod \inst_sig_out[1] (.in1(sig_in[1]),.out1(sig_out[1]))

You want to instantiate the mod module with an instance name that includes the name of the
output signal. However, the output signal can be a bit-select, containing square bracket
characters. In order for the name to include such characters, the created identifier has to be an
escaped identifier. So you prefix the left-hand side of the glue operator with a backslash.

The question is whether a double back-tic is recognized as a glue operator in this case. Normally
in an escaped identifier, all the characters after the backslash, until the concluding white space,
are considered part of the identifier name and lose any other special meanings they might
normally have. So a plus character does not mean addition, a quotation mark does not start a
string literal, and a square bracket does not indicate a bit-select.

The LRM does not indicate what happens in this case. VCS does treat the double back-tic as a
glue operator in this case, which is fortunate, because that is what you want. However, many
other tools do not do so. GOTCHA!

This issue came up to discussion in the IEEE SV committee as Mantis 1537. No consensus has
been reached. However, there is a lot of sentiment that treating the double back-tic here as a glue
operator is inconsistent and not backwards compatible with Verilog, but nevertheless a desirable
capability. There were several proposals as to alternate ways to do this, but no proposal has been
approved, and thus this will not be clarified any more in SV-2009. This means that tool behavior
in this case will continue to be inconsistent.

3.4 Use of special macro character sequences outside of macros


SV-2005 (Section 23.2) added some new capabilities in text macros.

Normally, quotation marks (" ") begin and end strings literals. Everything between the
quotation marks is considered part of the literal. That means that you could not construct a string
literal containing a call to a macro (See Section 2.10, above). Nor could a string literal appearing

SNUG Boston 2008 25 Return of the SystemVerilog Gotchas


inside macro text contain use of a macro argument. These would just be considered part of the
character string comprising the literal. So " `A " is a string literal with four characters: space,
back-tic, ―A‖, and another space. The `A is not recognized as a call to the macro A.

SV added a `" character sequence for macros that is transformed into a quotation mark in the
macro expansion, but still allow macro arguments and macro calls to be recognized correctly.

SV also added a `\`" to generate a \" character sequence, and the double back-tic (``)
sequence to act as a ―glue operator‖, as described in the preceding section in this paper.

The LRM says this: ―In SystemVerilog, the macro text can also include `", `\`", and ``.‖

The question is, can these special character sequences be used outside macro text as well? The
LRM does not say explicitly whether they can or cannot. It only describes them in the context of
macro text, but some people apparently thought either that the LRM was only saying that they
can also be used in macros or decided to extend the standard to allow them elsewhere as well.

While many tools implement the more restrictive interpretation of the LRM, which does seem its
plain meaning, the VCS implementation allows them elsewhere as well. And where a tool has an
extra capability, by golly, some people are going to use it. Many of those people are unaware that
other tools do not implement this extension, and their code no longer compiles when they try it
on another tool. GOTCHA!

3.5 Are spaces around macro arguments dropped?


One of the ways in which text macros are different from most of the SV language is that white
space in macro text is not ignored. That is because text macros perform text substitution without
interpretation of their contents. The macro definition has no knowledge of how and where the
macro will be used. Maybe it will be used in a place where white space has significance.

One aspect of this is when an actual macro argument is substituted for the formal argument into
the macro text. Is the white space around the macro argument considered part of the argument or
not? For example, you can call a macro A with an argument, like this:

`A( arg )

Is the argument considered to be ―arg‖ and the spaces around it have no significance, or is the
argument considered to be ― arg ―?

In Verilog, it is very difficult, maybe impossible, to find a case in which this makes a difference.
That is why this issue was not important in the past. But in SystemVerilog, where you can
construct a string literal from macro arguments, it now becomes important.

For example,

`define A(arg) `"AAargBB`"

SNUG Boston 2008 26 Return of the SystemVerilog Gotchas


$display ( `A( 0 ) ) ;

Does this display "AA0BB" or "AA 0 BB" or "AA0 BB", etc. Again, it turns out that not all
tools do the same thing. GOTCHA!

Most tools do drop the white space around the arguments both before and after the argument, but
there are exceptions.

3.6 String equality and compare functions have opposite return values
SV introduced a string data type and a number of operations that can be performed on it. In
SV-2005, Table 4-2 describes the string equality operation: str1 == str2 works like the
regular == equality operator and returns 1 if the strings are equal and 0 if they are not.

The LRM also defines the compare() string comparison method. Intuitively, you would expect
this to work the same as the other comparison operator, ==. The LRM does not say otherwise. It
just says, ―str.compare(s) compares str and s, as in the ANSI C strcmp function,‖
without specifying the behavior of strcmp. You might not remember or be aware of the
definition of strcmp, or you might simply be unaware that compare() is defined in terms of
strcmp.

However, the return values of strcmp are the opposite of those of the equality operator. So
compare() returns 0, not 1, if the strings are equal and 1 if the strings are different.
GOTCHA!

Thus, where you would write ―if (str1 == str2)‖, you must write ―if
(!str1.compare(str2)). See also Solvnet article 022593
(https://solvnet.synopsys.com/retrieve/022593.html) on this subject.

A related Verilog gotcha is when comparing two vectors. Many people remember that the bit-
wise XOR operator ^ can be used to compare two vectors. People often gets its polarity mixed
up, and forget that vector1 ^ vector2 is 1 if they are different and 0 if they are the same.

3.7 Glitches in unique/priority


The unique and priority keywords that are new in SV are a great feature, but they have
pitfalls. The two gotchas papers have shown some (6.14 in [1], 2.1 and 2.2 in [2]), and we
present others in this paper as well.

One problem in the definition of how they work in SV-2005 is that glitches can occur. That is,
variables can go through intermediate state combinations when they are changing that cause
unique and priority violations to occur. These cause false warnings to be displayed, even
though no violation occurs after the signal values have stabilized.

The following is a toy example, but it illustrates the point:

SNUG Boston 2008 27 Return of the SystemVerilog Gotchas


always_comb
unique case({ff1_d,ff1})
2'b00: o = 1'b0;
2'b11: o = 1'b1;
endcase

always_comb
ff1_d = ff1 && 1 ;

always_ff @(posedge clk)


ff1 <= in;

In this example, the unique case asserts that ff1_d and ff1 are the same. When the
always_ff procedure executes, it assigns a value to ff1, which is then propagated to ff1_d.

All the simulators tested issued a violation message of the unique case between the
assignments to ff1 and ff1_d. Now you have to distinguish between the true violations and
the false violations. GOTCHA!

Intel‘s CPU projects suffered from this problem, and we filed this with the IEEE SV WG as
Mantis 2008. A solution was devised and it will be part of the SV-2009 revision of the LRM. A
high-level description of the solution is this: if an always_comb procedure containing a
unique or priority if-else or case statement is triggered more than once during the
same time step, then only violations detected during the last execution of the procedure during
the time step will be reported, and violations detected during previous executions of the
procedure during the same time step will be flushed.

This will work for zero-time glitches in the values of the variables. It will not help for glitches
whose length is more than a single time step, but it is assumed that at RTL level, most of the
glitches will be zero-time.

3.8 Glitches in immediate assertions


Just as glitches can occur with unique and priority, as described in the previous section,
they can also occur with immediate assertions. Here is an example:

assign not_a = !a;


always_comb begin
out = a && x || not_a && y;
assert (out == a ? x : y);
end

Here, the always_comb procedure implements a multiplexer. The always_comb may


execute twice when a changes. The assertion may incorrectly fire on the first execution, before
the change to not_a has been applied, reporting a failure that is really false from the point of
view of the user. GOTCHA!

SNUG Boston 2008 28 Return of the SystemVerilog Gotchas


It is not surprising that the same problem occurs in both constructs, as unique and priority
may indeed be thought of as special types of immediate assertions. The glitch problem in
immediate assertions can be even more serious, because these can contain "action blocks" to be
executed when the assertion fires.

This problem was filed with the IEEE SV WG as Mantis 2005. A solution was devised which
has the same basic idea as the solution for unique and priority, described in the previous
section, and this will also be part of the SV-2009 LRM. However, there is a significant difference
between the two solutions.

For unique and priority, the committee found that no one was interested in leaving the
glitchy behavior available, and so their behavior was redefined so as to eliminate the glitches. No
change in the code by the user is required in order to get the new behavior.

For immediate assertions, the committee decided not to change the existing behavior, in order to
avoid problems of backward compatibility. Remember that while unique and priority are
limited to very specific contexts, immediate assertions can be used much more generally, and it
was feared that changing the existing behavior might cause problems.

So a variation of immediate assertions was defined, called deferred assertions. Whereas a regular
immediate assertion is specified by writing assert, a deferred assertion will be specified by
writing assert #0. Verilog users are used to the meaning of #0 as a sort of "delta delay", so
this syntax can be easily understood.

3.9 Assignments in expressions


Our abstract mentions the infamous C gotcha of using an assignment instead of a comparison in
an expression by mistake, as in "if (a=b)" instead of "if (a==b)".

SystemVerilog does not let you do this. To use an assignment in an expression, you have to add
an extra pair of parentheses: "if ((a=b))".

The LRM relates to this as preventing the C gotcha. So does Gotcha 44 in [3]. In fact, the authors
in [3] note that this unavoidably creates a new gotcha, where someone will try to write as in C, a
language he is familiar with, without the extra parentheses, and then not understand why it fails
compilation.

The fact is, however, that SystemVerilog's requirement of extra parentheses only reduces the risk
of hitting this gotcha, but does not prevent it. If you write an assignment instead of a comparison
and enclose the assignment in parentheses, the gotcha will still occur. The code will be legal and
wrong, and the simulator compiler will be silent about it.

SNUG Boston 2008 29 Return of the SystemVerilog Gotchas


You might ask why you would write "if ((a=b))", with extra parentheses. You probably
would not write such a simple expression that way, but for a slightly more complex expression, it
is not at all unlikely.

Consider something like the following:

always_comb
b = (a=1) ? 1 : 0 ;

This line is quite legal, but likely unintentional.

We had such a case, in a more complex statement, and no tool even issued a warning.
GOTCHA!

3.10 Uneven coverage bin distribution


Suppose you want to do coverage of a variable or expression that can have a range of values. The
bins construct allows you to create a separate bin for each value in the given range list (an array
of bins) or a single bin for the entire range of values. You can also create a fixed number of bins
and have the values automatically distributed across the bins.

To create a separate bin for each value, square brackets, [], follow the bin name. To create a fixed
number of bins for a set of values, you specify the number of bins inside the square brackets. For
example:

bins fixed [4] = {1:10, 14, 15};

The 12 possible values are distributed as follows: <1,2,3>, <4,5,6>, <7,8,9>, <10,14,15>.

That works fine where the number of values can be distributed evenly among the bins. What
happens if the number of values is not divisible by the number of bins?

The LRM says,

"If a fixed number of bins is specified and that number is smaller than the specified
number of values, then the possible bin values are uniformly distributed among the
specified bins. The first ‗n‘ specified values are assigned to the first bin, the next ‗n‘
specified values are assigned to the next bin, etc. ... If the number of values is not
divisible by the number of bins, then the last bin will include the remaining items. For
example:

bins fixed [4] = {1:10, 1, 4, 7};

The 13 possible values are distributed as follows: <1,2,3>, <4,5,6>, <7,8,9>, <10,1,4,7>."

SNUG Boston 2008 30 Return of the SystemVerilog Gotchas


This example looks reasonable. But the key sentence here is "If the number of values is not
divisible by the number of bins, then the last bin will include the remaining items." This can
actually create some significant distortions. Suppose you have N bins and (P*N)-1 values, where
P is some integer. For example, 499 values in 100 bins. For a distribution as uniform as possible
among the bins, you would expect to see 4 values in one bin and 5 values in each of the other
bins. What will actually happen is that the first 99 bins will get 4 values each and 103 values in
the last bin! GOTCHA!

A proposal was submitted to the SV Working Group in Mantis 2055 to fix this and make the
distribution truly uniform, but it was rejected on the grounds that it is not backwards-compatible.
The thought behind the existing behavior is that it is easy and fast to implement. However, it was
shown that the new proposal would not be complex, either.

3.11 const variables are not parameters


SystemVerilog has added the ability to declare a variable as "constant" by adding the const
keyword to the declaration:

const logic a = 0;

const variables can be either static or automatic. Here we will just talk about static const
variables. Automatic const variables are a little different.

A const variable is assigned a value in its declaration and cannot be assigned a value in a
procedural assignment.

Many people see this and think that if it is a constant, then it can be used in place of a constant,
like a parameter, for example in a range specification, like this:

const integer a = 3;
logic [a:0] b;

And then they don't understand why they get a compiler error. GOTCHA!

The catch here is that a const variable is still a variable, and can only be used where a variable
can. Its value is assigned after compilation and elaboration, at the beginning of run-time.
Anywhere the language requires that an elaboration-time constant be used, such as in the
specification of the range of a data object's dimension, a const variable cannot be used. For
example, anywhere the language syntax specifies a "constant_expression", only elaboration-time
constants may be used.

The confusion arises because the standard uses the term "constant" with more than one meaning.
In fact, most uses of the term "constant" in the standard mean elaboration-time constants.

SNUG Boston 2008 31 Return of the SystemVerilog Gotchas


If so, what is the benefit of having static const variables? The truth is that defining a variable as
const does not convey upon it added abilities, with a few minor exceptions. For the most part,
its benefit is in preventing it from even accidentally and incorrectly being assigned a new value,
in documenting its intent to be initialized but not overwritten, and having the compiler enforce
that restriction.

3.12 The static initialization order fiasco


(See http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.12 for the name. Thanks to Dave
Rich from Mentor for pointing it out.)

The discussion in this section is limited to static variables. Automatic variables are a little
different.

In both Verilog and SystemVerilog, there is no guaranteed order of execution between initial
and always procedures. This is a frequent source of problems, as pointed out in Gotcha 56 in
[3].

However, you don't need always procedures to have execution order problems with initial
procedures and initializations.

Naturally, SV does not guarantee order of execution between initial procedures. So in the
following:

reg a, b;
initial a = 0;
initial b = a;

there is no guarantee that the first initial procedure will execute before the second and
therefore there is no guarantee as to the value that will be assigned to b.

Verilog-2001 introduced "variable declaration assignments" (VDAs), an initialization assignment


to a variable in its declaration. This was defined as being equivalent to doing the assignment in
an initial procedure. So the following:

reg b;
reg a = 0;
initial b = a;

was the same as the first code version above.

SystemVerilog has defined that VDAs are executed before initial and always procedures,
so now this code is guaranteed to initialize a before b.

However, no order is guaranteed between VDAs, so that the following:

SNUG Boston 2008 32 Return of the SystemVerilog Gotchas


reg a = 0;
reg b = a;

has the same problem as our original code.

Many people understand this. However, fewer people understand that the same is true for const
variable declarations. Many people think of const variables as being like parameters and expect
that the following will work:

const logic a = 0;
logic b = a;

However, const variables are still variables. The only difference is that a const variable must
be assigned a value in its declaration and may not be assigned a new value afterwards.

4.0 Synthesis Gotchas


4.1 always_comb does not guarantee combinational logic
One of the big traditional synthesis gotchas of Verilog has been that it is so easy to write an
always procedure that is intended to model combinational logic, but actually synthesizes as a
latch. Simulators do not give you any warning when this happens. Synthesis and Lint tools tell
you that you have a latch, but they do not distinguish between intentional and unintentional
latches. That is not so bad if your design does not contain many intentional latches, but if you do
use latches, then the latch report starts to be big and it is more difficult to find the unintentional
latches.

Enter SV‘s new always_comb and always_latch procedures. Now I can indicate explicitly
the intent of my always procedures as combinational or latches.

But always_comb does not guarantee that the code you write will actually model
combinational logic. If the code does not assign a value to the output under all input conditions,
it will still model latched logic, not combinational logic. The always_comb keyword indicates
your intent, but does not guarantee that your intent is actually implemented.

Well then, my simulator will at least tell me that I made a mistake, right? Don't count on it.

The 1800-2005 LRM says in Section 11.2, ―Software tools can perform additional checks to
warn if the behavior within an always_comb procedure does not represent combinational
logic, such as if latched behavior can be inferred.‖ The key word is ―can‖. That is, tools may
perform such additional checks, but they are not required to.

And in fact, most simulators do not do so (today). You can write an always_comb procedure
that will model and synthesize a latch, but it will pass simulator compilation silently. Simulators

SNUG Boston 2008 33 Return of the SystemVerilog Gotchas


do not like to know anything about synthesis. It makes extra work for them. GOTCHA! That is,
you cannot depend on simulators to detect non-combinational always_comb procedures.

Synthesis tools and Lint tools will, however, now be able to distinguish between intentional and
unintentional latches. However, you still cannot rely on simulators to give you any information
about this.

The SV-2009 LRM draft changes that ―can‖ to ―should‖, in order to encourage tool developers
to implement such checks. Some simulator developers opposed that change, saying that for non-
synthesis tools, it is not well-defined exactly what check they should perform. However, the
proponents of the change said that mostly what was wanted is a check that the outputs of the
always_comb procedure are assigned a value under all input conditions. The new wording still
leaves these checks non-mandatory, so no one will be able to claim that a tool is non-compliant
because it missed some corner case. This change was made in Mantis 1828.

4.2 My flip-flop is a latch!


We all know that if you write a supposedly combinational always procedure incorrectly, by not
assigning a value to an output of the procedure under all conditions, then you will get a latch
from synthesis.

This also makes some sense to us, because we know that we write latches in RTL in a way that is
very similar to combinational always procedures. In Verilog-1995, for both latches and
combinational logic, we write always with a sensitivity list that includes all the signals that are
read within the procedure. In Verilog-2001, we use always @* for both. In SystemVerilog,
there are separate keywords, always_comb and always_latch, but these only indicate
intent. They behave identically.

But how does it happen that we write code that looks like it should create a flip-flop and we still
get a latch out of it?

A normal flip-flop is written something like this:

always @(posedge clk or negedge rst_n)


if (!rst_n) out <= 1’b0 ;
else out <= in;

Suppose we write the following:

always @(posedge clk or negedge rst_n)


if (!rst_n) out <= 1’b0 ;

Here the signal out is assigned a value when the asynchronous reset is active and is never
overwritten by data loaded at the clock edge. This is equivalent to the following:

SNUG Boston 2008 34 Return of the SystemVerilog Gotchas


always @(rst_n)
if (!rst_n) out <= 1’b0 ;

This is clearly a latch style of code.

This example is not so bad. You can look at the original code and see that there is only a reset
condition and that no data is loaded otherwise. However, other cases are less obvious, such as
this:

always @(posedge clk or negedge rst_n)


if (!rst_n) out[31:0] <= 0 ;
else begin
out[31:18] <= in1;
out[16:11] <= in2;
out[10:00] <= in3;
end

Here out is a 32-bit register with three fields, bits 31-18, 16-11, and 11-0. Bit 17 is not used.
This leaves bit 17 with an asynchronous reset and no data load. Synthesis and Lint tools will tell
you that bit 17 is a latch, even though you think you wrote a register of 32 flip-flops. GOTCHA!

This is actually harmless, but can be quite surprising and confusing if you are unaware of this
behavior. The day before I wrote this, it actually pointed out an error in real code. A user reported
that he has this code (slightly edited):

always @(posedge clk or negedge reset_n)


if (!reset_n)
begin
a_en <= 1'b1;
a_err <= 1'b0;
b_en <= 1'b1;
b_err <= 1'b0;
end
else if (wr & (address[11:0] == DDPAR))//DDPAR is a parameter
begin
a_en <= data[0];
a_err <= data[1];
b_en <= data[2];
b_err <= data[3];
end
else if (cond)
begin
a_err <= a_done;
b_err <= b_done;
end

SNUG Boston 2008 35 Return of the SystemVerilog Gotchas


The user complained that his Lint tool indicates that a_en and b_en are latches. Why? This
does not look like the case described above. Here a_en and b_en are assigned data values in
non-reset conditions as well. Further study showed that the parameter DDPAR was defined as
16'h3508, a 16-bit value where bits 12 and 13 are non-zero, and it is being compared erroneously
to only a 12-bit address.

Thus, the comparison result is always false, and that part of the code is therefore ignored, as if it
did not exist. That leaves us with only a reset assignment and no data assignment to a_en and
b_en, and therefore they became latches. GOTCHA again!

Since this gotcha is really harmless, you just need to be aware of this behavior, and then you will
know why you get latch reports.

4.3 Myth: default/full_case/unique/priority prevent latches


It is well known that for an always procedure to synthesize to combinational logic, you must
assign a value to each of its outputs under all combinations of input conditions. Otherwise a latch
is formed. How do you deal with this in a case statement?

always_comb
case (sel[1:0])
2’b10: out = a;
2’b11: out = b;
endcase

This case statement assigns a value to out only for two of the four input combinations. In
order to prevent a latch from being formed, we need to assign a value to out for the other two
input combinations as well. We can add additional lines to the case statement for each of the
additional combinations, but for large case statements with a large number of possible
combinations, that can be very unwieldy.

Another method is to add a default clause to the case statement, like this:

always_comb
case (sel[1:0])
2’b10: out = a;
2’b11: out = b;
default: out = 1'b0 ;
endcase

Or to add a full_case synthesis pragma, like this:

always_comb
case (sel[1:0]) // synopsys full_case
2’b10: out = a ;
2’b11: out = b ;

SNUG Boston 2008 36 Return of the SystemVerilog Gotchas


endcase

The full_case pragma acts like adding a ―default: out = 'x;‖ clause to the case
statement, and specifies that the output value for all unspecified case expression values is
"don‘t care", and the synthesis tool is free to assign any value it wishes in order to optimize the
implementation of the logic.

In SV, the priority and unique keywords can be used instead of the full_case pragma.
(priority is like a "full_case" pragma whereas unique is like a "full_case
parallel_case" pragma combination.)

All these methods will work for this code and prevent latch logic from being synthesized instead
of combinational logic. As a result, many coding guideline documents specify one of these
methods as standard methodology to insure that latches will not be generated.

It Ain‘t Necessarily So. Take this case:

always_comb
unique case (sel[1:0]) // synopsys full_case
2’b00: out = 1’b0;
2’b01: out = 1’b0;
2’b10: if (cond) out = a;
2’b11: out = b;
default: out = 1’b0;
endcase

Here we have used all the methods together: the case statement is complete, with a default
clause, with a full_case pragma, and even unique pre-pended to it. None of this will help.
A latch will still be formed. Why? Because there is still an input condition for which out is not
assigned a value: if sel[1:0] is 2’b10 and cond is 0. The code will go to the 2’b10 branch
of the case, but the condition of the if statement is false, and the else branch is null. No
value is assigned to out in this case. GOTCHA!

All the methods mentioned have the effect of covering all values of the case expression and
thus that the list of case items is complete. However, they do not guarantee that within the
case items, all the outputs are assigned values for all possible input combinations.

Here is another variation of this situation:

always_comb
case (sel[1:0])
2’b10: begin out1 = a; out2 = 1’b1; end
2’b11: begin out2 = b; end
default: begin out1 = 1’b0; out2 = 1’b1; end
endcase

SNUG Boston 2008 37 Return of the SystemVerilog Gotchas


In this variation, the case item list, together with the default clause, covers all possible input
combinations, but one of them assigns a value only to one of the two output variables. In such a
case, neither the default clause, nor a full_case pragma, nor unique or priority will
prevent out1 from being synthesized as a latch.

However, there is one way to handle this that will guarantee that latches will not be formed. We
call this a ―default pre-assignment‖. That is to assign default values to all the outputs of the
combinational logic before the if-else and case statements, as in this example:

always_comb
begin
out1 = 1’b0;
out2 = 1’b1;
case (sel[1:0])
2’b10: begin out1 = a; out2 = 1’b1; end
2’b11: out2 = b;
endcase
end

This does guarantee that out1 and out2 will be assigned values within the always_comb
procedure for every input combination, if not by the case statement, then by the pre-assignment.

Until SV, this worked fine. But now, SV‘s unique keyword has introduced a new gotcha into
the use of default pre-assignments. One uses unique to assert that the case items are mutually
exclusive, i.e., that only one case item can match the case expression simultaneously. The
advantage of unique over the parallel_case pragma is that unique causes the simulator
to issue a violation message if the unique-ness of the case statement is violated.

But unique also includes the effects of a full_case pragma. In particular, if the case
statement does not include an explicit default clause, unique effectively adds an implicit
"default: out = 'x;" case item to the case statement, but only for synthesis tools, not
for simulators. See [18] for a fuller discussion of this.

The result is that if the case expression has a value which does not match any of the case
items, the simulator will use the default pre-assignment value, whereas the synthesis tool will use
the implicit default don't care assignment and choose whatever value it likes. Weirdly, the
unique construct, which had a goal of preventing synthesis-simulation mismatches, can itself
cause a mismatch.

For this and other reasons, SV-2009 will include a new unique0 construct, which will be like
parallel_case without full_case, checking unique-ness of the case items, but
allowing for none of them to match as well. Think of unique0 as meaning zero-one-hot
whereas unique means one-hot. The details can be found in Mantis 2131.

SNUG Boston 2008 38 Return of the SystemVerilog Gotchas


4.4 Why is my combinational logic considered sequential?
As discussed previously, you can write code that is intended to model combinational logic, but
actually synthesizes to a latch. This happens if the output variable is not assigned a value under
all possible input combinations.

Suppose a particular combinational always procedure has several output variables. As in usual
in combinational logic, you have written the procedure with blocking assignments, whereas you
use nonblocking assignments for latches and flip-flops.

All of the output variables but one are modeled correctly. An assignment to one of them was
omitted in some case, as in this example from earlier:

always_comb
case (sel[1:0])
2’b10: begin out1 = a; out2 = 1’b1; end
2’b11: begin out2 = b; end
default: begin out1 = 1’b0; out2 = 1’b1; end
endcase

out1 synthesizes to a latch instead of to combinational logic. You are as yet unaware of this.
You get messages from your synthesis or Lint tool that you have used blocking assignments
instead of nonblocking assignments for out2 in a sequential procedure, and you ask yourself,
―What sequential procedure??‖

It does not occur to you that one of the output variables has turned into a latch. And even if it did
occur to you, you would say to yourself, ―Maybe the procedure is sequential for some variable,
but I can see that this one is combinational, that it is assigned a value in all situations.‖ You
might even curse buggy tools.

What has happened is that once the tool has determined that one of the variables in the procedure
models a latch, the entire procedure is considered sequential, and therefore the tool expects
nonblocking assignments on all of the variables. GOTCHA!

Messages of this sort are a sign that one of the output variables in the procedure has turned into a
latch. The messages about blocking assignments used in a sequential procedure don‘t tell you
which one, though. For that, you have to look for the messages about inferred latches.

4.5 A synthesis engine is not as smart as a formal engine


We encountered this problem several times. It is not a language gotcha, but a true synthesis
gotcha. The following code is an example of the problem:

SNUG Boston 2008 39 Return of the SystemVerilog Gotchas


for (int i = 0; i < N; i = i + 1)
if (i == var)
array[var] = f(i);

That is, we have a loop of N iterations. On each iteration, we compare the value of the loop index
to some variable, and if their values are equal, then we do an assignment to a word whose index
is equal to the value of that variable. When the loop is unrolled, we get N conditional
assignments to a variable-indexed word of the array. Of course, all N assignments are to the same
word, the word whose index is var, and only one can be executed.

All the tools we saw turned this into very large logic with very high run-time and memory
consumption. But really we know that for each iteration, the assignment will be executed only if
the loop index value i is equal to the value of var. So instead of writing array[var], we can
write array[i].

Remember that when loops are synthesized, the loop variable turns into a constant value for each
iteration. Then we get N conditional assignments to the array, but each to a different, constant-
indexed word. This simple, almost trivial change makes a tremendous difference in the results.
The synthesis engine is not sophisticated enough to make this optimization.

For example, we had an always_ff procedure that contained the following code excerpt in the
middle of its body:

for (int i = 0 ; i < 64 ; i = i+1)


if (i == vf_g)
begin
vm_mb[vf_g][2] <= d_del[7] ;
vm_mb[vf_g][1:0] <= d_del[1:0];
end

Changing the subscript for vf_g to i reduced the cell count by over 60K !

You might ask why the loop is needed at all, why not just write
begin
vm_mb[vf_g][2] <= d_del[7] ;
vm_mb[vf_g][1:0] <= d_del[1:0];
end

The answer is that this was part of a much larger code segment. It made sense in context to use
the loop. This is often true.

You can test the effect yourself with this simple test case:

module test(output reg[255:0] tmp, input[255:0] a, input[7:0] j);


always @(a or j)
for (int i=0; i < 256; i = i +1) // 256 latches

SNUG Boston 2008 40 Return of the SystemVerilog Gotchas


if(i==j) tmp[j] <= a[j]; // BAD!
// if(i==j) tmp[i] <= a[i]; // GOOD!
endmodule

We also tried the intermediate cases in a synthesis tool, i.e., using the variable j just on the left-
hand side or just on the right-hand side. We found that using j on either side significantly
increases the area, but using it on the left-hand side explodes the run-time as well.

I recently found that Solvnet article 002218 (https://solvnet.synopsys.com/retrieve/002218.html)


talks about problems with loops in the old pre-Presto HDL Compiler, such as with conditional
assignments to arrays in loops. However, that article states that those limitations no longer exist
in Presto. Here we have seen that certain cases can still cause problems in Presto and in other
synthesis tools as well.

4.6 Asynchronous reset loops in flip-flops


The code for a simple flip-flop with asynchronous reset looks something like this:

always @(posedge clk or negedge rst_n)


if (!rst_n)
out <= 0 ;
else
out <= in;

Suppose that we want to describe in this code not a single register, but an entire array of registers.
We might write a loop that iterates over each of the words of the register, like this:

reg [15:0] out [0:N] ;


reg [15:0] in [0:N] ;
integer i;

always @(posedge clk or negedge rst_n)


for (i=0; i<N; i=i+1)
begin
if (!rst_n)
out[i] <= 0 ;
else
out[i] <= in[i];
end

If you are unlucky, you will only get a message from your synthesis tool telling you that this is
not synthesizable. If your tool is more friendly, then it might tell you that the asynchronous reset
needs to be at the top of the always procedure. GOTCHA!

SNUG Boston 2008 41 Return of the SystemVerilog Gotchas


This mistake is frequently made and not understood even after getting an error message. It is not
enough for the asynchronous reset condition to be at the top of the loop body. If it is inside the
loop body, it is no longer at the top of the always procedure, which is what synthesis tools
require. The code has to be written like this:

always @(posedge clk or negedge rst_n)


if (!rst_n)
for (i=0; i<N; i=i+1)
out[i] <= 0 ;
else
for (i=0; i<N; i=i+1)
out[i] <= in[i];

The code has to describe the reset of the entire array first, and only then the data load. This
requires using two loops instead of one.

In SystemVerilog, this gotcha is less likely to be encountered because one does not usually need
a loop to reset the array. One can usually reset the entire array in a single statement, like this:

Always_ff @(posedge clk or negedge rst_n)


if (!rst_n)
out <= '{default:0} ;
else
for (int i=0; i<N; i=i++)
out[i] <= in[i];

5.0 Conclusions and Recommendations


SystemVerilog is a good language, but it has gotchas.
SystemVerilog has gotchas, but it is a good language.

Don‘t give up, but be aware and be careful.

Where possible, prepare coding guidelines to prevent gotchas. Check for them using Lint tools.
In code reviews, look for those gotchas that your Lint tool does not check.

Remember gotchas when you come to debug problems. Consider whether the symptoms remind
you of any gotchas you know about.

When you meet a new gotcha, spread the word among your colleagues, and think about finding a
way to prevent it or to detect it earlier.

If you have an idea how to improve the language so as to remove or at least reduce a gotcha
(preferably in a backwards-compatible way), tell the IEEE SV WG.

SNUG Boston 2008 42 Return of the SystemVerilog Gotchas


6.0 Acknowledgements
A very big thanks to Chris Spear (Synopsys) for reviewing this paper thoroughly and making
great suggestions!

Thanks to Stuart Sutherland and Don Mills for encouragement to follow in their footsteps.

Thanks to Cliff Cummings for pushing me to submit a paper to SNUG and for a special
friendship.

7.0 References
[1] Stuart Sutherland and Don Mills, ―Standard Gotchas: Subtleties in the Verilog and
SystemVerilog Standards That Every Engineer Should Know,‖ SNUG Boston 2006

[2] Stuart Sutherland, Don Mills, and Chris Spear, ―Gotcha Again: More Subtleties in the
Verilog and SystemVerilog Standards That Every Engineer Should Know,‖ SNUG San Jose
2007

[3] Stuart Sutherland and Don Mills, Verilog and SystemVerilog Gotchas: 101 Common
Coding Errors and How to Avoid Them, Springer, 2008, ISBN 978-0-387-71714-2

[4] IEEE Std 1364-1995, IEEE Standard Hardware Description Language Based on the
Verilog® Hardware Description Language, IEEE, 1995, ISBN 1-55937-727-5

[5] IEEE Std 1364-2001, IEEE Standard Verilog® Hardware Description Language, IEEE,
2001, ISBN 0-7381-2827-9

[6] IEEE Std 1364-2005, IEEE Standard for Verilog® Hardware Description Language, IEEE,
2005, ISBN 0-7381-4851-2

[7] IEEE Std 1800-2005, IEEE Standard for SystemVerilog—Unified Hardware Design,
Specification, and Verification Language, IEEE, 2005, ISBN 0-7381-4811-3

[8] Mantis, http://www.eda.org/svdb

[9] Stuart Sutherland, Simon Davidmann, and Peter Flake, SystemVerilog for Design, 2ed,
Springer, 2006, ISBN 978-0-387-33399-1

[10] Chris Spear, SystemVerilog for Verification: A Guide to Learning the Testbench Language
Features, 2ed, Springer, 2008, ISBN 978-0-387-76529-7

[11] Shivakumar Chonnad and Needamangalam Balachander, Verilog: Frequently Asked


Questions, Springer, 2004, ISBN 0-387-22834-9

SNUG Boston 2008 43 Return of the SystemVerilog Gotchas


[12] Doulos Ltd, SystemVerilog Golden Reference Guide, Version 4.0, January 2006, ISBN 0-
9547345-3-X

[13] David Black and Lewis Sternberg, ―Avoiding Verilog Nightmares During Verification,‖
SNUG San Jose 2000

[14] Don Mills and Clifford E. Cummings, ―RTL Coding Styles That Yield Simulation and
Synthesis Mismatches,‖ SNUG Europe 2001

[15] Duane Galbi and Lok Kee Ting, ―RTL X's - A Treasure Trove of Trouble,‖ SNUG Boston
2002

[16] Mike Turpin, ―The Dangers of Living with an X (bugs hidden in your Verilog)‖, SNUG
Boston 2003

[17] Cliff Cummings, ―Verilog Nonblocking Assignments With Delays, Myths and Mysteries,‖
miniSUNG Ottawa 2003

[18] Cliff Cummings, ―SystemVerilog's priority & unique - A Solution to Verilog's "full_case" &
"parallel_case" Evil Twins!,‖ SNUG Israel 2005

[19] Moe Kinney, Denise Powell, and Ray Yock, ―The Living Hell of Identifiers and
define_name_rules,‖ SNUG Boston 2005

[20] Colin Paul Gloster, ―ACCU Book Review: The Design and Evolution of C++,‖
http://accu.org/index.php/book_reviews?url=view.xqy?review=14179489152797087495

[21] http://www.hyperdictionary.com/computing/gotcha

[22] Yossi Ginzburg, Eyal Skulsky, Ziv Baum, and Tzahi Sabo, "Are you satisfied with your
constraints? Eight ways to misuse Vera solver," SNUG Israel 2007

8.0 About the Author


Shalom Bresticker is a Senior CAD Engineer in the LAN Access Division in Intel's Jerusalem
design center. At Intel, Shalom supports front-end logic design and verification tools and
methodologies. He joined Intel in 2005 after more than 20 years at Motorola Semiconductor
Israel, later Freescale Semiconductor Israel, in VLSI design and verification. Shalom started
using Verilog in 1989, and started participating in the IEEE 1364 Verilog standards committee in
1995, later editing Version C of the 1364-2001 LRM and most of the 1364-2005 LRM. In recent
years, he has participated in the IEEE 1800 SystemVerilog standards committee and the
Accellera Verilog-AMS standards committee. He is a self-acknowledged Verilog and
SystemVerilog bigot.

SNUG Boston 2008 44 Return of the SystemVerilog Gotchas


Shalom holds a BSE from Princeton University and an MSEE from the Technion-Israel Institute
of Technology. He serves on the Technical Program Committees for SNUG Israel and Europe,
DVCon, and Intel's Design and Test Technology Conference.

SNUG Boston 2008 45 Return of the SystemVerilog Gotchas

You might also like