172 KiB
Google Verilog Style Guide
- TOC {:toc}
Basics
Summary
Verilog is the main logic design language at Google.
Verilog can be written in vastly different styles, which can lead to code conflicts and code review latency. This style guide aims to promote Verilog readability across groups. To quote the C++ style guide: "Creating common, required idioms and patterns makes code much easier to understand."
This guide defines a Google style for Verilog. The goals are to:
- promote consistency across hardware development projects
- promote best practices
- increase code sharing and reuse
This style guide defines style for SystemVerilog compliant code. Additionally, this style guide defines style for both synthesizable and testbench code.
Terminology Conventions
Unless otherwise noted, the following terminology conventions apply to this style guide:
- The word must indicates a mandatory requirement. Similarly, do not indicates a prohibition. Imperative and declarative statements correspond to must.
- The word recommended indicates that a certain course of action is preferred or is most suitable. Similarly, not recommended indicates that a course of action is unsuitable, but not prohibited. There may be reasons to use other options, but the implications and reasons for doing so must be fully understood.
- The word may indicates a course of action is permitted and optional.
- The word can indicates a course of action is possible given material, physical, or causal constraints.
Style Linter
The following Style Linter tools can be used to automatically check the code for violations to the mandatory requirements of this Style Guide:
Style Guide Exceptions
Justify all exceptions with a comment.
No style guide is perfect. There are times when the best path to a working design, or for working around a tool issue, is to simply cut the Gordian Knot and create code that is at variance with this style guide. It is always okay to deviate from the style guide by necessity, as long as that necessity is clearly justified by a brief comment, as well as a Style Linter waiver pragma where appropriate.
Inclusive Language
In all code, including naming and comments, use inclusive language and avoid terms that others might find disrespectful or offensive (such as "master" and "slave", "blacklist" and "whitelist", or "redline"), even if the terms also have an ostensibly neutral meaning. Similarly, use gender-neutral language unless you're referring to a specific person (and using their pronouns). For example, use "they", "them", or "their" for people of unspecified gender (even when singular), and "it" or "its" for software, computers, and other things that aren't people.
The bus protocols section has additional conventions for replacing non-inclusive terms in that common case.
Non-Inclusive Language in External IP
In some cases, non-inclusive terms are embedded in industry standards or third party IP (or similar) as names or keywords. Whenever possible, minimize your use of the term (hence avoid propagating it as a term of art), while still providing clear documentation to your readers.
Common Scenarios and Preferred Approaches:
-
A third party IP's top level module or port names include non-inclusive terms. Create a wrapper for that top level module that replaces the non-inclusive terms with preferred terminology. In the wrapper, clearly document the terminology mapping. All uses of that module instantiate the wrapper rather than directly instantiating the problematic module. Do not modify the third party code directly.
-
An industry standard interface names its signals with non-inclusive terms. When using that standard, use preferred terminology in the code. In comments adjacent to the signal declarations, document the terminology mapping.
Which Verilog to Use
Prefer SystemVerilog-2017.
All RTL and tests must be developed in SystemVerilog, following the SystemVerilog-2017 standard.
What about VHDL?
Google uses Verilog for logic design.
Precedent has already been established that Verilog is the choice for logic design at Google.
Reusable IP
Common RTL that is intended to be used across multiple projects is referred to as reusable IP.
File Conventions
Summary
This section covers file conventions, mostly how files are to be named.
File Extensions
Use .sv and .svh file extensions for SystemVerilog
files.
File extensions have the following meanings:
.svindicates a SystemVerilog file defining a module or package..svhindicates a SystemVerilog global macro file or include file..vindicates a Verilog-2001 file defining a module or package..vhindicates a Verilog-2001 global macro file or include file.
File Names
File names must match the design element name and indicate the Verilog dialect.
If a source file declares a design element, the file name must begin with the
name of that design element. For example, a file declaring a module named
interconnect must be named interconnect.sv.
Declare only one design element per file. As per IEEE 1800-2017 section 3.2, a design element may be either a module, package, program, interface, checker, primitive, or configuration.
For variants of a design element, it is permissible to insert dot-delimited
strings between the design element name and the file extension to indicate the
particular variant contained in that file. For example, a file containing a
gate-level model of an interconnect module might be named
interconnect.gate.sv.
Standard Design Files
The following filenames are special: [design]_top.sv,
[design]_pkg.sv.
Each design must have two standard files, [design]_top.sv and
[design]_pkg.sv, where [design] is the unique name of the design.
[design]_top.svis the top-level module of the design.[design]_pkg.svdefines a package named[design]_pkgcontaining declarations related to the design.
A "design" here is one of the following hierarchical elements: project top, cluster (a collection of design blocks) or a design block.
A "design block" is the fundamental architecture or microarchitecture building block of the design. Every design block has its black box behavior fully specified. In particular, a design block’s interfaces are fully specified in the documentation (both the port list and the behavior of every port).
File Structure
The overall structure of a SystemVerilog file must start with the header, followed by the declaration and then the implementation.
Header Comment
Every file must start with a header comment.
A header comment contains a short description of the design element, explaining what problem it solves. Additional text must give an overview of any non-obvious parts of the theory of operation of this particular implementation.
Basic Template
A template that demonstrates many of the items detailed throughout the style guide.
Template:
// A quick description of the design element goes here. What its intent is,
// and a general theory of operation.
module my_module #(
parameter int Width = 80,
parameter int Height = 24
) (
input logic clk,
input logic rst,
...
);
...
endmodule
Generated Code
Generated code must be accompanied by all necessary input files and scripts necessary to automatically reproduce the generated output.
Generators include Verilog preprocessors and programs that create Verilog from other sources like Haskell, Chisel, C or C++.
Code generation tools that operate in-place on Verilog files must not strip out the generating macros, and must preserve those recipes within the file (in other words, as comments). Code generators that operate in this fashion must be idempotent and repeatable.
Comments wrapped in /** and **/ comment lines are reserved for use by
generators.
Generated code must meet the style guidelines.
Aesthetics and Formatting
Summary
The Aesthetics and Formatting section addresses purely aesthetic aspects of style, primarily:
- Wrap lines at 100 characters.
- Indentation is two spaces per level, no tabs.
- Space-delimit all keywords and binary operators.
- Prefer
// Commentstyle comments.
General File Appearance
Characters
Use only ASCII characters with UNIX-style line endings("\n").
POSIX File Endings
All lines on non-empty files must end with a newline ("\n").
The POSIX standard mandates that lines end with a newline character. This includes the final line in every file.
Line Length
Wrap the code at 100 characters per line.
The maximum line length for style-compliant Verilog code is 100 characters per line.
Exceptions:
- Any place where line wraps are impossible (for example, an include path might extend past 100 characters).
Line-wrapping contains additional guidelines on how to wrap long lines.
No Tabs
Do not use tabs anywhere.
Use spaces to indent or align text. See Indentation for rules about indentation and wrapping.
To convert tabs to spaces on any file, you can use the
UNIX expand utility.
No Trailing Spaces
Delete trailing whitespace at the end of lines.
No Redundant Semicolons
Delete redundant semicolons.
Semicolons may represent null statements or items in SystemVerilog. One is
enough; consecutive semicolons (;;) are likely typos.
Eschatology
Use begin and end unless the whole statement fits on a
single line.
If a whole semicolon-terminated statement fits on a single line, begin and
end may be omitted, otherwise enclose it in begin and end.
// Wrapped procedural block requires begin and end.
always @(posedge clk) begin
q <= d;
end
// The exception case, where begin and end may be omitted as the entire
// structure fits on a single line.
always @(posedge clk) q <= d;
// Incorrect because a wrapped statement must have begin and end.
always @(posedge clk)
q <= d;
Assume that any macro call could expand to multiple statements, and enclose them
in begin and end where necessary (for example, inside conditional and loop
blocks).
// The contents of the called macro are safely enclosed and controlled.
if (expr) begin
`MACRO(...);
end
if (expr) ...;
else begin
`MACRO(...);
end
// A multi-statement macro would have only its first statement controlled:
if (expr) `MACRO(...);
if (expr) ...;
else `MACRO(...);
begin must be on the same line as the preceding keyword, and ends the line.
end must start a new line. end else begin must be together on one line. The
only exception is if end has a label, a following else must be on a new
line.
// "end else begin" are on the same line.
if (condition) begin
foo = bar;
end else begin
foo = bum;
end
For single-statement blocks, begin and end may be omitted if the statement
fits on a single line. Both styles (including or omitting begin and end) are
acceptable.
// begin/end may be omitted because each semicolon-terminated statement fits on
// a single line (both styles are acceptable).
if (condition) foo = bar;
else foo = bum;
// Incorrect because "else" must be on the same line as "end".
if (condition) begin
foo = bar;
end
else begin
foo = bum;
end
// An exception is made for labeled blocks.
if (condition) begin : a
foo = bar;
end : a
else begin : b
foo = bum;
end : b
The preceding style also applies to individual case items within a case
statement. begin and end may be omitted if the entire case item (the case
expression and the associated statement) fits on a single line. Otherwise, use
the begin keyword on the same line as the case expression.
// Consistent use of begin and end for each case item is good.
unique case (state)
kIdle: begin
state_next = kA;
end
kA: begin
state_next = kB;
end
kB: begin
state_next = kIdle;
foo = bar;
end
default: begin
state_next = kIdle;
end
endcase
// Case items that fit on a single line may omit begin and end.
unique case (state)
kIdle: state_next = kA;
kA: state_next = kB;
kB: begin
state_next = kIdle;
foo = bar;
end
default: state_next = kIdle;
endcase
unique case (state)
kIdle: // These lines are incorrect because we must not wrap
state_next = kA; // case items at a block boundary without using begin
kA: // and end. Case items must fit on a single line, or
state_next = kB; // else the procedural block must have begin and end.
kB: begin
foo = bar;
state_next = kIdle;
end
default: begin
state_next = kIdle;
end
endcase
Indentation
Indentation is two spaces per level.
Indentation is two spaces per level, no tabs. (Configure your editor to emit spaces when you hit the tab key.)
Indented Sections
Always add an additional level of indentation to the enclosed sections of all paired keywords. Examples of SystemVerilog keyword pairs:
beginandendmoduleandendmodulepackageandendpackageclassandendclassfunctionandendfunction- and similar.
Indent all members of struct, union, and enum declarations by two spaces.
typedef struct packed {
logic bar;
} foo_t;
Line Wrapping
When wrapping a long expression, indent the continued part of the expression by four spaces, like this:
assign zulu = enabled && (
alpha < bravo &&
charlie < delta);
assign addr = addr_gen_function_with_many_params(
thing, other_thing, long_parameter_name, x, y,
extra_param1, extra_param2);
assign structure = '{
src: src,
dest: dest,
default: '0};
assign foo = function_with_structure(model[offset].very.long.
structure.name);
module my_module (
input clk, // this is a line wrap of the module header definition
output bar
);
my_module my_module (
.clk(clk), // this is a line wrap of the module instantiation
.bar(bar)
);
Or, if it improves readability, align the continued part of the expression with a grouping open parenthesis or brace. Similarly, an expression containing a nested struct may be aligned with the first structure member name. In the absence of other considerations, use the minimum number of lines.
assign zulu = enabled && (alpha < bravo &&
charlie < delta);
assign addr = addr_gen_function(thing, other_thing,
long_parameter_name,
x, y);
assign structure = '{src: src,
dest: dest,
default: '0};
assign foo = function_with_structure(model[offset].very.long.
structure.name);
Operators in a wrapped expression can be placed at either the end or the beginning of each line, but this must be done consistently within a file.
Preprocessor Directives
Keep branching preprocessor directives left-aligned and un-indented.
Keep branching preprocessor directives (`ifdef, `ifndef, `else,
`elsif, `endif) aligned to the left, even if they are nested. Indent
the conditional branches of text as if the preprocessor directives were absent.
Non-branching preprocessor directives must follow the same indentation rules as
the regular code.
package foo_pkg;
`ifdef FOO // Good: branching directive left-aligned
`include "foo.svh" // normal indentation for non-branching directives
parameter int A = 1; // normal indentation for the regular code
`ifdef BAR // Good: branching directive left-aligned
parameter int A = 2;
`else
parameter int A = 3;
`endif // BAR
`endif // FOO
endpackage : foo_pkg
package foo_pkg;
`ifdef FOO // Bad: needs to be un-indented
`include "foo.svh"
parameter int A = 1;
`ifdef BAR // Bad: needs to be un-indented
parameter int A = 2;
`else // Bad: needs to be un-indented
parameter int A = 3;
`endif // BAR
`endif // FOO
endpackage : foo_pkg
package foo_pkg;
`ifdef FOO // Bad: needs to be un-indented
`include "foo.svh" // Bad: extra indentation level
parameter int A = 1; // Bad: extra indentation level
`ifdef BAR
parameter int A = 2;
`else
parameter int A = 3;
`endif // BAR
`endif // FOO
endpackage : foo_pkg
Un-indented branching preprocessor directives disrupt the flow of reading to emphasize that there is conditional text. Leaving conditional branch text un-indented will result in post-preprocessed text looking properly indented.
Spacing
Space Around Keywords
Include whitespace before and after SystemVerilog keywords.
Keywords must be delimited by whitespace, even if not syntactically required.
Do not include a whitespace:
- before keywords that immediately follow a group opening, such as an open parenthesis.
- before a keyword at the beginning of a line.
- after a keyword at the end of a line.
For example:
// Normal indentation before if. Include a space after if.
if (foo) begin
end
// Include a space after always, but not before posedge.
always @(posedge clk) begin
end
Space Around Binary Operators
Include whitespace on both sides of all binary operators.
Use spaces around all binary operators. Add sufficient whitespace to aid readability.
For example:
assign a = ((addr & mask) == MyAddress) ? b[1] : ~b[0];
parameter int Width = Msb - Lsb;
function logic crc(input logic [7:0] data, mask = 8'hff);
assign a=((addr&mask)==MyAddress)?b[1]:~b[0]; // Bad: no space around operators
parameter int Width=Msb - Lsb; // Bad: no space around '='
function automatic logic crc(input logic [7:0] data, mask=8'hff); // Bad: no space around '='
Exception: When declaring a bit vector or indexing or slicing an array, it is acceptable to use the compact notation for simple expressions. For example:
logic [Width-1:0] foo; // Declaration: compact is acceptable
logic [Width - 1 : 0] foo; // Declaration: spaced is fine also
assign foo = bar[Width-1:0]; // Slice: compact is acceptable
assign foo = bar[Width - 1:0]; // Slice: spaced is fine also
For more complex expressions in array indices, space-delimit all binary operators.
assign foo = bar[Factor1 - Factor0 - 1:0]; // Good: operators are space-delimited
assign foo = bar[Factor1-Factor0-1:0]; // Bad: complex expression without spaces
When splitting alternation expressions into multiple lines, use a format that is similar to an equivalent if-then-else line. For example:
assign a = ((addr & mask) == MyAddress)
? matches_value
: doesnt_match_value;
Comma-delimited Lists
For multiple items on a line, one space must separate the comma and the next character.
Additional whitespace is allowed for readability.
bus = {addr, parity, data};
a = myfunc(lorem, ipsum, dolor, sit, amet, consectetur, adipiscing, elit,
rhoncus);
mymodule mymodule (.a(a), .b(b));
{parity,data} = bus;
a = myfunc(a,b,c);
mymodule mymodule (.a(a),.b(b));
Tabular Alignment
Adding whitespace to align related items is recommended.
Where it is reasonable to do so, align a group of two or more similar lines so that the identical parts are directly above one another. This alignment makes it easy to see which characters are the same and which characters are different between lines.
For example:
logic [7:0] my_interface_data;
logic [15:0] my_interface_address;
logic my_interface_enable;
Array Dimensions in Declarations
Add a space around packed dimensions.
Do not add a space:
- between identifier and unpacked dimensions.
- between multiple dimensions.
Applies to packed and unpacked arrays as well as dynamic arrays, associative arrays, and queues.
logic [7:0][3:0] data[128][2];
typedef logic [31:0] word_t;
bit bit_array[512];
data_t some_array[];
data_t some_map[addr_t];
data_t some_q[$];
// There must not be a space between dimensions.
logic [7:0] [3:0] data[128] [2];
// There must be a space around packed dimensions.
typedef logic[31:0]word_t;
// There must not be a space between identifier and unpacked dimension.
bit bit_array [512];
// Dynamic, associative, and queue "dimensions" are treated the same as unpacked
// dimensions. There must not be a space.
data_t some_array [];
data_t some_map [addr_t];
data_t some_q [$];
Parameterized Types
Add one space before type parameters, except when the type is part of a qualified name.
A qualified name contains at least one scope :: operator connecting its
segments. A space in a qualified name would break the continuity of a reference
to one symbol, so it must not be added. Parameter lists must follow the
space-after-comma rule.
my_fifo #(.Width($bits(nibble_t)), .Depth(2)) my_fifo_nibble(.in, .out);
class foo extends bar #(32, 8); // unqualified base class
...
endclass
foo_h = my_class#(.X(1), .Y(0))::type_id::create("foo_h"); // static method call
my_pkg::x_class#(8, 1) bar; // package-qualified name
my_fifo#(.Width(4), .Depth(2)) my_fifo_2by4(.in, .out); // instantiation
class foo extends bar#(32, 8); // unqualified base class
...
endclass
foo_h = my_class #(.X(1), .Y(0))::type_id::create("foo_h"); // static method call
my_pkg::x_class #(8, 1) bar; // package-qualified name
Labels
Code block labels have one space before and after the colon. Statement labels have no space before the colon and one space after the colon.
For example:
begin : foo
my_statement_label: assert(ptr > 0);
end : foo
end:bar // Block label must have a space before and after the colon.
endmodule: foobar // Block label must have a space before the colon.
my_assert : assert(ptr > 0); // Statement label must have no space before the colon.
Case items
There must be no whitespace before a case item's colon; there must be at least one space after the case item's colon.
The default case item must include a colon.
For example:
unique case (my_state)
kInit: $display("Shall we begin");
kError: $display("/this is fine meme");
default: begin
my_state = kInit;
interrupt = 1;
end
endcase
unique case (1'b1)
(my_state == kError) : interrupt = 1; // Excess whitespace before colon
default:begin end // Missing space after colon
endcase
Module Header Spacing
Module header definitions have spaces after the module name and between any parameter lists and port lists.
For example:
module my_module (
...
);
module my_module #(
...
) (
...
);
module my_module( // There must be a space after the module name.
...
);
module my_module #(
...
)( // There must be a space between the parameter list and the port list.
...
);
Module Instantiation Spacing
Module instances have spaces after the instance name and between any parameter lists and port lists.
For example:
my_module #(...) my_module (...);
my_module my_module (...);
// The space after the instance name is missing.
my_module #(...) my_module(...);
my_module my_module(...);
Function And Task Calls
Function and task calls must not have any spaces between the function name or task name and the open parenthesis.
For example:
process_packet(pkt);
process_packet (pkt); // There must not be a space before "("
Macro Calls
Macro calls must not have any spaces between the macro name and the open parenthesis.
For example:
`uvm_error(ID, "you fail")
`ASSERT(name_A, a & b, clk, rst)
`uvm_error (ID, "you fail") // There must not be a space before "("
`ASSERT (name_A, a & b, clk, rst)
Line Continuation
It is recommended to right-align line continuations.
Aligning line continuations ('\ ' character) helps visually mark the end of a
multi-line macro. The position of alignment only needs to be beyond the
rightmost extent of a multi-line macro by at least one space, when a space does
not split a token, but must not exceed the maximum line length.
`define REALLY_LONG_MACRO(arg1, arg2, arg3) \
do_something(arg1); \
do_something_else(arg2); \
final_action(arg3);
Parentheses
Use parentheses to make operations unambiguous.
In any instance where a reasonable human would need to expend thought or refer to an operator precedence chart, use parentheses instead to make the order of operations unambiguous.
Reduction Operator
Parentheses are required to isolate any reduction operation when immediately preceded by any other operator.
logic [7:0] a;
logic b;
logic c;
assign c = b & (&a); // Parentheses make the intent clear
logic [7:0] a;
logic b;
logic c;
assign c = b & &a; // Bad: This can easily be a typo, still valid Systemverilog
Ternary Expressions
Nested ternary expressions must be enclosed in parentheses.
For example:
assign state_next = condition_b
? (condition_a
? a_and_b
: b_and_not_a)
: state;
While the following nested ternary has only one meaning to the compiler, the meaning can be unclear and error-prone to humans:
assign state_next = condition_b
? condition_a
? a_and_b
: b_and_not_a
: state;
Comments
C++ style comments (// foo) are strongly recommended for
single and multi-line comments. The /** token is reserved for use with
generators.
A standalone comment above a block of code explains that code. An inline comment explains the code that is on the same line as the comment.
For example:
// This is a multi-line comment that describes the basic functionality and key
// features of the following module.
module foo;
...
endmodule : foo
localparam bit Baz = 1; // This comment describes the item to the left. If
// using multiple lines, it must remain clear which
// line of code the comment is referring to.
/* Grepping this line looks commented.
Grepping this line looks uncommented.
That is confusing. */
Comments that start with /** are reserved for use by generator directives.
Punctuation, Spelling, and Grammar
Maintain consistent and correct punctuation, spelling, and grammar within comments.
Pay attention to punctuation, spelling, and grammar; it is easier to read well-written comments than badly written ones.
Comments must be as readable as narrative text, with proper capitalization and punctuation. In many cases, complete sentences are more readable than sentence fragments. Shorter comments, such as comments at the end of a line of code, can sometimes be less formal as long as the style remains consistent.
Although it can be frustrating to have a code reviewer point out that you are using a comma instead of a semicolon, it is very important that source code maintain a high level of clarity and readability. Proper punctuation, spelling, and grammar help with that goal.
Dead code
Do not keep commented out or unreachable code. Instead, rely on the version control system to preserve older versions of the code. In cases where the "dead code" might be there for special purposes, clearly document the intent.
Naming Conventions
Naming Overview
All identifiers are styled lower_snake_case unless
otherwise specified in the following table.
"There are only two hard things in Computer Science: cache invalidation and naming things." -- Phil Karlton
All identifiers are styled lower_snake_case, except for the constructs
summarized in the following table:
| Construct | Style |
|---|---|
`define macros |
ALL_CAPS |
| Tunable parameters for parameterized modules, classes, and interfaces | UpperCamelCase |
| Constants | UpperCamelCase |
Type parameter |
lower_snake_case_t |
| Struct and union types | lower_snake_case_t |
| Constraints | lower_snake_case_c |
| Interface declarations | lower_snake_case_if |
| Enumeration types | lower_snake_case_t or lower_snake_case_e |
| Enumerated value names | kUpperCamelCase |
| Chip primary IOs | UPPER_SNAKE_CASE or lower_snake_case |
Everything else, including
declarations (module, class, package),
instance names,
signals (nets and ports), genvars,
labels, functions, tasks, and other variables, must be named
consistent with the lower_snake_case convention.
External library identifiers and their overrides in the user code that violate
the preceding naming conventions are permitted. Example: UVM's
convert2string() can be used without renaming.
When an identifier is styled as UpperCamelCase, it is permissible to separate
numeric values with an underscore. For example, IndexedThing_5_12.
Further, there are a number of naming conventions for signals, constants, and ports that are documented in more detail in the following sections.
Use Descriptive Names
Names must describe what an element's purpose is. Optimize for readability using names that would be clear even to people on a different team.
Use names that describe the purpose or intent of the object. Do not worry about saving horizontal space as it is far more important to make your code immediately understandable by a new reader. Minimize the use of abbreviations that would likely be unknown to someone outside your project (especially acronyms and initialisms). Do not abbreviate by deleting letters within a word. As a rule of thumb, an abbreviation is probably OK if it's listed in Wikipedia. If additional abbreviations must be used, make sure your project has a glossary of acceptable abbreviations.
Descriptiveness is recommended to be proportional to the name's scope of
visibility. For example, n may be a fine name within a 5-line function, but
within the scope of a 1000-line module, it's likely too vague.
When adding a comment to a signal declaration, consider whether an improved signal name would be more appropriate.
Don't reuse variables for convenience. Instead create a new variable with a unique, descriptive name.
logic temp; // Bad: temp is not a descriptive name
always_comb begin
temp = a ^ b;
c = ~temp + 1'b1;
temp = d | e; // Bad: temp was reused. Instead declare a new signal
f = temp + 1'b1;
end
function automatic logic foo(logic bar);
bar = !bar; // Bad: reusing the input variable of a function
return bar;
endfunction
Prefer Positive Enablement
Signals, ports, and parameters must be named positively.
All boolean signals are assumed to be active-high, unless explicitly named with
a _n suffix to indicate that the signal is active-low.
Active-low signals are not recommended.
When naming boolean parameters, prefer positive enablement names ("Foo" or "EnableFoo") over disablement ("DisableFoo").
// Good: binary parameters/localparams are named positively ("Enable"),
// regardless of their default values.
parameter bit EnableFoo = 0;
parameter bit EnableBar = 1;
// Bad: Parameter names are named negatively. Both the names and the values
// must instead be inverted.
parameter bit DisableFoo = 1;
parameter bit DisableBar = 0;
Hierarchical consistency
The same signal must have the same name at any level of the hierarchy.
A signal that connects to a port of an instance must have the same name as that port. By proceeding in this manner, signals that are directly connected must maintain the same name at any level of hierarchy.
Exceptions to this convention are expected, such as:
-
When connecting a port to an element of an array of signals.
-
When mapping a generic port name to something more specific to the design. For example, two generic blocks, one with a
requester_busport and one with acompleter_busport might be connected by afoo_bar_bussignal.
In each exceptional case, care must be taken to make the mapping of port names to signal names as unambiguous and consistent as possible.
A corollary of this principle is that signal names must change when that
signal's meaning changes. For example, if a write_address signal means "byte
index" at one level of hierarchy but "word index" within a sub-module, those
signals must have different names to indicate their differences (for example,
write_byte_address and write_word_address).
Clock and Reset
These signals must be named clk, rst, and arst.
Clocks and resets must follow the following naming rules:
- Clock is named
clk. - Synchronous reset is named
rstand is active high. - Asynchronous reset is named
arstand is active high.
These names may optionally be prefixed to specify more specific use. Most commonly, these signals will be prefixed with a mnemonic indicating a clock domain, as required by the Clock Domain Prefixes section.
For example:
input logic smbus_clk, // Clocks an "smbus" clock domain.
input logic smbus_rst, // Synchronous reset, clocked by smbus_clk
input logic smbus_arst, // Asynchronous reset, de-assert clocked by smbus_clk
input logic [7:0] smbus_byte, // Synchronous with smbus_clk
input logic smbus_valid, // Synchronous with smbus_clk
Avoid signal names that end with underscore-number
Signal names must never end with an underscore followed by a number (for
example, foo_1, foo_2, etc.). Many synthesis tools map buses into nets using
that naming convention, so similarly named nets can lead to confusion when
examining a synthesized netlist.
Avoid reserved keywords as signal names
Reserved Verilog or SystemVerilog keywords may never be used as names (see Tables 22.1-22.6 of SystemVerilog-2017 standard for the full list of reserved keywords).
When interoperating with different languages, be mindful not to use keywords from other languages.
Prefixes
Use common prefixes to identify groups of signals that operate together. For
example, all elements of an AXI-S interface would share a prefix: foo_valid,
foo_ready, and foo_data.
Additionally, prefixes must be used to clearly label which signal is in which clock group for any module with multiple clocks. See the section on clock domains for more details.
Examples:
- Signals associated with controlling a blockram might share a
bram_prefix. - Signals that are synchronous with
dram_clkrather thanclkmust share adram_prefix.
Code example:
module fifo_controller (
input logic clk,
input logic rst,
// writer interface
input logic [15:0] in_data,
input logic in_valid,
output logic in_ready,
// reader interface
output logic [15:0] out_data,
output logic out_valid,
output logic [7:0] out_fullness,
input logic out_ack,
// memory interface:
output logic [7:0] mem_addr,
output logic [15:0] mem_wdata,
output logic mem_we,
input logic [15:0] mem_rdata
);
This naming convention makes it easier to map port names onto similar signal names using simple and consistent rules. See the section on Hierarchical Consistency for more information.
Clock Domain Prefixes
If a module contains multiple clocks, all signals must be prefixed by a clock domain mnemonic.
Generally, it is recommended to structure modules so that most modules only have a single clock domain (fully synchronous). For modules that must span multiple clock domains, it is required that every port and signal within the module be labelled with a prefix that uniquely associates each signal with a clock domain. This prefix must be the same prefix used in the naming of the associated clock.
Even asynchronous resets must be de-asserted synchronously with a specific clock domain, and so must be named accordingly.
For example:
module foo_to_bar_cdc_fifo (
// This module has two clocks, foo and bar:
input logic foo_clk,
input logic foo_arst, // reset for flops in the foo_clk domain
input logic bar_clk,
input logic bar_arst, // reset for flops in the bar_clk domain
// These signals are all in the foo_clk domain:
input logic [7:0] foo_payload_data,
input logic foo_payload_valid,
output logic foo_payload_ready,
// These signals are all in the bar_clk domain:
output logic [7:0] bar_payload_data,
output logic bar_payload_valid,
input logic bar_payload_ready
);
// Internal nets must all be labelled as well:
wire foo_transfer = foo_payload_valid && foo_payload_ready;
Source-Destination Prefixes
It is sometimes useful to document the expected connectivity between modules in
the signal name. The convention for indicating this connectivity is
<src>_<dest>__, where <src> and <dest> are convenient abbreviations for
the endpoints of the communication (at least, from a top-level perspective). The
endpoint abbreviation must specify a specific instance, or a reasonable generic
name if the specific instance is not known.
The <src> endpoint must always be the producer or requester of a given
communication (which can either be an individual flow-controlled channel or a
protocol-level group of individual flow-controlled channels). <dest> must
always be the consumer or completer of the communication. A communication
direction is not necessarily the same as each signal's direction. In this
manner, a set of related signals that work together to form a distinct
communication between modules always have the same naming prefix. The scope of
the communication (in other words, channel or group of channels) must be
explicitly documented using comments so that there is no naming ambiguity.
Source-destination prefixes are never mandatory, and must only be used at levels
of hierarchy where they clarify the intent of the design. When stripping this
prefix from a signal name at a hierarchical boundary, the right-hand side of the
__ separator must remain unchanged.
An example:
module foo (
input logic clk,
input logic rst,
// This is a simple standalone control signal from endpoint bar.
input logic bar_foo__enable,
// This is a protocol-level group of Ready/Valid flow-controlled channels
// where foo is the requester endpoint and bar is the completer endpoint.
// Good: All signals in this request channel have the same <src>_<dest>
// prefix regardless of signal direction.
input logic foo_bar__req_ready,
output logic foo_bar__req_valid,
output req_t foo_bar__req,
// Good: Although this response channel flows in the opposite direction, it
// is part of the same protocol interface as the request channel above, so
// the <src>_<dest> prefix is still "foo_bar".
output logic foo_bar__resp_ready,
input logic foo_bar__resp_valid,
input resp_t foo_bar__resp
);
module foo (
input logic clk,
input logic rst,
// Bad: The ready signal prefix must be "foo_bar". The <src>_<dest> prefix
// depends on the endpoint roles and not the signal direction.
input logic bar_foo__req_ready,
output logic foo_bar__req_valid,
output req_t foo_bar__req,
// Bad: Although this response channel flows in the opposite direction, it
// is part of the same protocol interface as the request channel, where foo
// is the requester endpoint and bar is the completer endpoint. Thus, the
// <src>_<dest> prefix must still be "foo_bar".
output logic bar_foo__resp_ready,
input logic bar_foo__resp_valid,
input resp_t bar_foo__resp
);
Pipeline Stage Prefixes
Clearly label your pipeline stage variables.
When implementing a multi-stage pipeline, label your variables so that it is
clear which pipeline stage each variable is associated with. For example, a
three stage pipeline might use the prefixes p1_, p2_, and p3_ to indicate
which signal is associated with which stage.
Clearly labelling which pipeline stage a signal is associated with helps to detect and avoid bugs where signals that are valid in one pipeline stage are accidentally used in a different pipeline stage.
Example:
// Pipe stage 1:
always @(posedge clk) begin
p1_x <= in_x;
p1_y <= in_y;
p1_valid <= in_valid;
end
// Pipe stage 2:
always @(posedge clk) begin
p2_product <= p1_x * p1_y;
p2_valid <= p1_valid;
end
// Pipe stage 3:
always @(posedge clk) begin
p3_accumulator <= p3_accumulator + p2_product;
p3_valid <= p2_valid;
end
NOTE: This guide makes a distinction between delayed signals and pipelines. A delayed signal is a stand-alone signal that is delayed by a number of cycles. A pipeline is a collection of signals across a number of cycles, as in the preceding example. In this example, the valid signal is a delayed version of the original in_valid signal, but the other signals are not delayed signals. The pipelining naming convention emphasizes the grouping of signals in the same stage, not their relative delays from their origin. The use of prefixes for pipeline stages and suffixes for delayed signals makes the distinction clear. See Delayed Signal Suffix for details.
Suffixes
There are some standard suffixes for signals that have special meanings. Signals must only end with these suffixes when the standard meanings are intended.
Some common conventions for suffixes are summarized here:
- Specify units as a suffix where possible.
- The
_nsuffix indicates an active-low signal. - When used as a pair, the
_nand_psuffixes indicate a differential pair of signals. - The
_ready,_valid, and_valid_nextsuffixes are expected to follow standardized Ready/Valid semantics. - The
_reqand_acksuffixes are expected to follow standardized Request/Acknowledge semantics. - The
_nextand_lesuffixes indicate the data and load-enable inputs for a clocked register. - The
_dsuffix indicates a clock-cycle delayed copy of a signal. - The
_sMpNand_uMpNsuffixes (where M and N are integers) indicate a fixed-point signal. - When combining multiple suffixes on a single signal, follow the standardized order of suffixes.
Specify units
Just as with constants, include the units for a signal in the signal's symbolic name. Counters must indicate what is being counted. Address signals must be named to indicate the word size of the thing being indexed.
Unitless signals (for example, foo_is_enabled) are excepted from this rule.
Additionally, some standard interfaces (for example, AXI-M) have standardized
naming of signals. In that context, the standardized signal names (waddr) are
preferable to more precise names (write_byte_address).
Prefer specifying units for logic-vector type signals assuming a 0-based least-significant bit. If that is not practical and a bit slice is required, then the signal name must encode the bit-vector range.
Examples:
-
write_byte_addressis clearer thanwrite_address. -
max_mibytes_per_secis clearer thanmax_rate. -
packet_size_beatsorpacket_size_kibytesis clearer thanpacket_size. -
cycle_counterortransfer_counteris clearer thancounter. -
For a 64-bit signal
foo_byte_address, it is assumed to be defined aslogic [63:0] foo_byte_address. A corresponding signal slice in units of 4B address could then be defined as eitherlogic [61:0] foo_4byte_addressorlogic [63:2] foo_63_2_byte_address.
Units for Constants
Just as with signals, include the units for a constant in the constant's symbolic name (preferably, as a suffix). The exceptions to this rule are for constants that are inherently unitless, or if the constant is describing the default unit type, "bits."
Example:
localparam int InterfaceWidth = 64; // Bits
localparam int InterfaceWidthBytes = (InterfaceWidth + 7) / 8;
localparam int InterfaceWidth64bWords = (InterfaceWidth + 63) / 64;
localparam int ImageWidthPixels = 640;
localparam int Mega = 1000 * 1000; // Unitless
localparam int Mebi = 1024 * 1024; // Unitless
localparam int SystemClockHz = 200 * Mega;
Active-Low Signals
The _n suffix indicates an active-low signal.
It's recommended to avoid active-low signals. If active-low signals are used,
they must have the _n suffix in their name. Otherwise, all signals are assumed
to be active high.
Differential Pairs
Use the _p and _n suffixes to indicate a differential
pair.
For example, in_p and in_n comprise a differential pair set.
Valid Qualifier Suffix
foo_valid indicates when foo_* signals are valid.
Signals named with the "_valid" suffix indicate when a set of related signals are valid. The signals being labelled by the "foo_valid" signal must all have the same "foo_" prefix.
Signals named with the "_valid_next" suffix indicate when a set of related
signals will be valid in the next clock cycle (distinct from the
_next register suffix). An interface may use _valid_next
instead of _valid to provide early valid indication; all of the following
rules applied to _valid similarly apply to _valid_next.
Signals in the "foo_" group must be ignored when "foo_valid" is not asserted.
Note that the additional presence of a signal ending with _ready indicates the
use of ready/valid flow control semantics.
For example:
output logic [`DATA_WORD] data,
output logic data_valid,
output logic [`COMMAND_OP_WORD] command_operation,
output logic [`OPERAND_WORD] command_operand_a,
output logic [`OPERAND_WORD] command_operand_b,
output logic command_valid,
Register Signal Suffixes
The _next and _le suffixes have standard meanings
related to registers.
The _next suffix indicates a signal that is a D input into a register.
Therefore, the _next suffix is permitted on interface signals and module ports
to indicate next-state signals passing across hierarchical boundaries.
The _le suffix indicates an optional load-enable signal to a register.
Example:
logic [15:0] foo; // the Q output of a register
logic [15:0] foo_next; // the D input of that same register
logic foo_le; // the load-enable for the foo register
always @(posedge clk) begin
if (foo_le) begin
foo <= foo_next;
end
end
Additionally:
The _inc suffix indicates an increment-enable signal when implementing a
counter.
The _clear suffix indicates a register-specific synchronous clear operation.
Delayed Signal Suffix
Signals delayed by a single clock cycle must end in a _d
suffix.
If one signal is only a delayed version of another signal, the _d suffix must
be used to indicate this relationship.
If another signal is then delayed by another clock cycle, the next signal must
be identified with the _d2 suffix, and then _d3 and so on.
Example:
always @(posedge clk) begin
data_valid_d <= data_valid;
data_valid_d2 <= data_valid_d;
data_valid_d3 <= data_valid_d2;
end
NOTE: This guide makes a distinction between delayed signals and pipelines. A delayed signal is a stand-alone signal that is delayed by a number of cycles. A pipeline is a collection of signals across a number of cycles. Pipelines have a different style.
Fixed-Point Signals
Signals used for fixed-point arithmetic may be suffixed to indicate their sign, number of integer bits (including sign bit for signed signals), and number of fractional bits.
In modules with multiple fixed-point data types, adding a suffix to signal names
may be useful to clearly capture intended design behavior. If a suffix is used,
signal names must be in the form _sMpN for signed signals and _uMpN for
unsigned signals. "N" indicates the number of fractional bits. "M" indicates the
number of integer bits, including the sign bit for signed signals. This format
is based on Q Notation.
Example:
// signed, 3 bit integer, 12 bit fractional
logic signed [14:0] foo_s3p12;
// unsigned, 4 bit integer, 9 bit fractional
logic [12:0] bar_u4p9;
// Addition result of foo and bar signals
// signed, 6 bit integer, 12 bit fractional
logic signed [17:0] foo_bar_s6p12;
assign foo_bar_s6p12 = foo_s3p12 + signed'({1'b0, bar_u4p9, 3'd0});
Order of Suffixes
When combining multiple suffixes on a single signal, order them from inner semantic qualifiers to outer temporal qualifiers.
When multiple standard suffixes apply to a single signal, they must be appended in the following order:
- Units / Semantic Qualifiers: Functional qualifiers and units (for
example,
_bytes,_valid,_ready,_req,_ack). - Fixed-Point Format: Fixed-point format specifiers (
_sMpN,_uMpN). - Active-Low Polarity: Active-low indicator (
_n). - Differential Polarity: Differential pair polarity (
_p,_n). - Stage / Delay / Register Qualifiers: Temporal qualifiers (
_next,_le,_inc,_clear,_d,_d<N>).
Examples:
// Semantic/unit qualifier before register/delay suffix
logic foo_valid_d;
logic [15:0] packet_len_bytes_next;
// Fixed-point format before register/delay suffix
logic signed [14:0] coeff_s3p12_next;
// Active-low before register/delay suffix
logic rst_n_d;
// Active-low before differential pair
logic sync_n_p, // Positive pin of active-low sync pair
logic sync_n_n, // Negative pin of active-low sync pair
// Full multi-tier chain: semantic qualifier -> active-low -> delay
logic dma_req_n_d;
// Bad: Register/delay suffix placed before semantic or fixed-point suffix
logic in_d_valid;
logic [15:0] packet_len_next_bytes;
logic signed [14:0] coeff_next_s3p12;
// Bad: Differential suffix placed before active-low suffix
logic sync_p_n,
// Ambiguous whether second _n is differential or active-low
logic sync_n_n,
Preprocessor Directives and Pragmas
This section provides guidance on the use of verilog preprocessor directives and pragmas.
Global Macro Files
A global macro file is a file that contains only global defines. Global defines are only permitted within global macro files.
Global macro files are distinct from include files. Global
macro files may not be included using `include. They are intended to be
visible by all source files in a project.
Macro Definitions
Macro names must be ALL_CAPS (in other words, consist of
only capital letters, digits, or underscores).
A global define is a tick-defined macro in a global macro file that is shared by all source files in a project. To reduce namespace collisions, global defines must be prefixed by the name of a group of related macros, followed by a pair of underscores:
// The following is the BAR macro in the FOO namespace (optional)
// of the DMA block.
`define DMA_FOO__BAR(params) \
...
A global define that is a natural extension or abbreviation of a construct in
the UVM library is exempt from these rules - it is acceptable to define such
macros with the same convention as the UVM library (in other words,
lower_snake_case).
Local macros must be prefixed with a single underscore.
A local define is a tick-defined macro that must only be used within the scope of a single local file. It must be explicitly undefined after use, to avoid polluting the global macro namespace. To indicate that a macro is only meant to be used in the local scope, the macro name must be prefixed with a single underscore.
To ensure that local defines stay local, be careful not to `include other
files between the macro definition and `undef.
Example:
`define _MAKE_THING(_x) \
thing i_thing_##_x (.clk(clk), .i(i##_x) .o(o##_x));
`_MAKE_THING(a)
`_MAKE_THING(b)
`_MAKE_THING(c)
`undef _MAKE_THING
Standard Macros
Use standard macros when available.
Whenever possible, reuse standard macro libraries rather than writing your own. This style guide provides a reference implementation for the following standard macros.
STRINGIFY Macro
The STRINGIFY macro presents a standard way to convert a macro argument into a
string.
`define STRINGIFY(_x) `"_x`"
For example:
`define DISPLAY_VALUE(_x) $display("%s: %p", `STRINGIFY(_x), (_x))
Don't invent alternative implementations:
// This doesn't work with all tools, use STRINGIFY instead:
`define BAD_DISPLAY_VALUE(_x) $display("%s: %p", "_``_x``_", (_x))
BOUNDED_LOOKUP Macro
The BOUNDED_LOOKUP macro presents a standard way to build an rvalue that
indexes into an array, when the array size is smaller than maximum possible
value of the index.
The verilog expression array[index] would return 'X for out-of-bound
indices. This alternative has the following properties:
- Efficient: unnecessary high bits of index are discarded (aliasing the array).
- Safe: returns '0 for all other out-of-bound indices.
`define BOUNDED_LOOKUP(__arr, __idx) \
(($size(__arr) == 1'b1) ? __arr[$high(__arr)] \
: (__idx[(($high(__arr) == 0) ? 1 : $clog2($high(__arr) + 1)) - 1 : 0] < $low(__arr) || \
__idx[(($high(__arr) == 0) ? 1 : $clog2($high(__arr) + 1)) - 1 : 0] > $high(__arr)) ? '0 \
: __arr[__idx[(($high(__arr) == 0) ? 1 : $clog2($high(__arr) + 1)) - 1 : 0]])
X_OF_TYPE Macro
The X_OF_TYPE macro presents a standard way to typecast 'X.
`define X_OF_TYPE(__type) \
(__type'({$bits(__type){1'bx}}))
Assertion Macros
Use assertion libraries to enforce unified implementation throughout the design.
Following is a reference implementation of standard assertion macros used in this document.
// Concurrent assertion to check that a property is not violated. The assertion
// is disabled if reset is active. The sampling events are positive edges of the
// clock.
`define ASSERT(__name, __prop, __clk, __rst) \
`ifdef ASSERT_ON \
__name: assert property ( \
@(posedge __clk) disable iff (__rst !== 0) (__prop)); \
`endif
// Concurrent assertion to check that an expression evaluates to a known value
// (all bits are either '0' or '1') when not in reset.
`define ASSERT_KNOWN(__name, __expr, __clk, __rst) \
`ASSERT(__name, ~$isunknown(__expr), __clk, __rst)
// A static assertion that evaluates during elaboration, to check that an
// expression is true. Primarily used to validate module parameters.
`define ASSERT_STATIC(__name, __expr) \
`ifdef ASSERT_ON \
if (!(__expr)) \
$fatal(2, "Failed static assertion: %s: (%s) is not true.", \
`"__name`", `"__expr`"); \
`endif // ASSERT_ON
Synthesis Pragmas
Do not use synthesis pragmas that can lead to simulation-synthesis differences.
There are several pragmas that impact the creation of the design database
leading to different tools inferring different functionality. These can lead to
hard-to-debug simulation-synthesis mismatches that (depending on methodology)
might not be caught by logical equivalence checks. These include translate_off
and translate_on, synthesis_off and synthesis_on, read_comments_as_hdl on and read_comments_as_hdl off, parallel case, and full case. Do not use
these pragmas.
`ifdef Directives
Do not use `ifdef, `else, `elsif, or `ifndef compiler directives except as prescribed.
Avoid `ifdef whenever possible. Misuse of `ifdef-style compiler
directives can negatively impact code coverage, and can lead to a mismatch
between simulation and synthesis. For most use cases, separate source units or
generate-if statements provide a better solution.
The following exceptions are permitted.
`ifdef SIMULATION
May only be used additively, to add debug logic or logging statements. SIMULATION will typically be defined in simulation environments, and will never be defined in any other environment.
Do not use the SIMULATION macro to select between RTL and behavioral models (see anti-patterns). Do not use the SIMULATION macro to wrap simple assertions (see ASSERT_ON).
For example:
`ifdef SIMULATION
state_t prev_state = IDLE;
always @(state) begin
$display("%t: %m: state transition: %s -> %s",
$time, prev_state.name(), state.name());
prev_state = state;
end
`endif // SIMULATION
`ifdef ASSERT_ON
May only be used additively, to add assertions or assertion-specific modeling code. This enables an easy path to disable assertions in cases where faster simulation is needed. See the Assertion macros section for macros that implement this switch.
For example:
`ifdef ASSERT_ON
int outstanding_credits = 0;
always @(posedge clk) begin
if (credit) outstanding_credits += 1;
if (valid) outstanding_credits -= 1;
if (rst) outstanding_credits = 0;
end
credits_never_underflow: assert property (
@(posedge clk) disable iff (rst)
outstanding_credits >= 0);
`endif // ASSERT_ON
`ifdef FPV_ON
May only be used additively, to add monitors and logic used exclusively by formal property verification tools.
`ifdef <simulator>
Some simulators have simulator-specific debug features that need preprocessor
guards. No functional RTL must ever be enclosed by an `ifdef <simulator>
block.
The legal names to identify supported simulators are:
VCS: for Synopsys VCS simulators.INCA: for Cadence Incisive simulators.XCELIUM: for Cadence Xcelium simulators.DSIM: for Metrics DSim simulators.
`ifdef VCS
$stack();
`endif // VCS
`ifdef <technology>
It is permitted to use `ifdef directives to select between implementations
enabled by specific technologies (standard cell libraries, emulation, processes,
FPGA architectures).
If used, there must always be a valid `else clause that describes a default
implementation. There must also be unit tests that exercise all technology
cases.
Some of the standard names are:
EMULATION: for emulation-specific code.FPGA: for FPGA-specific code.
`ifdef Anti-patterns
Do not enable features with `ifdef. Instead, use parameters and generate-if
statements.
Do not use directives similar to `ifdef DISABLED to disable blocks of code.
Delete dead code instead.
Do not use include guards. Use a properly ordered compilation file list (flist) so that include guards are unnecessary. See the Include Guards section for more details.
`ifdef Exceptions
There are always unanticipated exceptional scenarios. If your project must use
non-standard `ifdef directives, follow the following guidelines:
-
The canonical design that gets deployed is what is represented with no defines set.
-
Clearly document and justify your macro in the project documentation (no "ad hoc"
`ifdefmacros). -
Prefix your macro names with a unique project namespace identifier. For example,
FOOBAR__DEBUGis preferable toDEBUGto avoid namespace collisions between projects. -
Choose macro names that connote positive enablement. For example, prefer
FOOBAR__PERF_COUNTERStoFOOBAR__DISABLE_PERF_COUNTERS.
Includes
Include Files
Files intended for `include
are referred to as "include files".
Include Directives
Use `include directives only to include files into a
verilog package file or to include files with code snippets.
Include directives used for the purpose of compiling each file standalone are forbidden. Instead, use a properly ordered compilation file list and Multi-File Compilation Unit (MFCU) mode.
There are only a few cases where include directives are allowed:
-
Including files into a verilog package file is allowed. Each file must be included into only one package.
package foo_pkg; // Good: breaking large package into smaller files is okay. `include "foo_functions.svh" endpackage// Include 'foo_pkg' package because module 'foo_top' depends on it. // Bad: can lead to a repeated declaration of a package. `include "foo_pkg.sv" module foo_top; localparam foo_pkg::data_t data = 0; endmodule -
Including files with code snippets is allowed, only when the code snippet cannot be wrapped into its own module or package.
module foo ( `include "generated_signals.svh" // Good: snippet, which cannot be wrapped ); endmodulemodule foo (); `include "common_assertions.svh" // Bad: wrap into a module instead endmodule
Include Guards
Don't use include guards.
Include guards protect against repeated expansion within the same translation unit. They must not be used since the current style guide rule on the use of includes will prevent this from happening.
// Bad: include guards are not needed.
`ifndef PATH_TO_MY_CODE_VH_
`define PATH_TO_MY_CODE_VH_
...
`endif // PATH_TO_MY_CODE_VH_
Stopping Compilation
Use the `DIE() macro to fail compilation.
Using this (undefined) macro allows the source file to be parsed by tools that
lack preprocessing, such as code search and IDE outline generators. Pass an
explanation as a string to `DIE. This convention does not depend on any
other macro files.
`ifndef USING_SIMULATOR_A
`ifndef USING_SIMULATOR_B
`DIE("Unknown or unsupported simulator.")
`endif // USING_SIMULATOR_B
`endif // USING_SIMULATOR_A
`ifndef USING_SIMULATOR_A
`ifndef USING_SIMULATOR_B
Error: intentional syntax error makes this unparseable without preprocessing
`endif // USING_SIMULATOR_B
`endif // USING_SIMULATOR_A
Modules
Module Header Definition
Use the Verilog-2001 full port declaration style, and use the following format.
Use the Verilog-2001 combined port and I/O declaration style. Do not use the Verilog-95 list style. Port declarations in the module header must fully declare the port name, type, and direction.
The clock port(s) must be declared first in the port list, followed by any and all reset inputs.
For port or parameter lists with declarations, the closing parenthesis of each list must be on its own line, in column zero. The opening parenthesis must occur on the same line as the module name or the previous list's closing parenthesis. The following are exceptions to these rules:
- If a list has no declarations, then the parentheses are optional. If used, the opening and closing parenthesis must be on the same line.
- If import statements are used, then the opening parenthesis of the following list must be on its own line.
Each import statement must appear on its own dedicated line of code.
Module Header Definition Examples
Example of a module header without parameters:
module foo (
input logic clk,
input logic rst,
input logic [7:0] d,
output logic [7:0] q
);
Example of a module header with a parameter declaration:
module foo #(
parameter int Width = 8
) (
input logic clk,
input logic rst,
input logic [Width-1:0] d,
output logic [Width-1:0] q
);
Example of a module header with an import statement:
module foo
// Imports the package for use in the header definition.
// This is a complete statement within the module, so we indent one level.
import foo_pkg::AddrWidth;
#(
parameter int DataWidth = 8
) (
input logic clk,
input logic rst,
input logic [AddrWidth-1:0] addr,
input logic [DataWidth-1:0] d,
output logic [DataWidth-1:0] q
);
Do not use Verilog-95 style:
module foo (a, b, c, d);
input logic [2:0] a;
output logic b;
...
Module Names
The top level module for a design [design] must be named
[design]_top. Unit test module names end with _test.
Module names must use lower_snake_case and contain only lowercase alphanumeric
characters and underscores. Use lowercase even when module-names contain proper
nouns, such as the project's name.
Top-level modules must be named [design]_top. For example, the top-level
module for Snout must be named snout_top. In this context, the "top level
module" is the highest possible level of a logic design's hierarchy, the
pin-level interface.
Unit test modules must end with the string "_test". For example,
dma_controller_test.
Parameterized Modules
When to parameterize
Use module parameters to create customizable modules.
Examples of when to use parameterized modules:
- When multiple instances of a module will be instantiated, and need to be differentiated by a parameter.
- As a means of specializing a module for a specific bus width.
- As a means of documenting which design-wide parameters are permitted to change within the module.
Do not use module parameters to transmit constants through a design hierarchy
(use package constants) or to declare a constant whose scope is
internal to the module (use localparam).
For additional reading, see New Verilog-2001 Techniques for Creating Parameterized Models.
Naming module parameters
UpperCamelCase is the recommended style for module parameter names, except:
ALL_CAPSmay be permitted for historic reasons, andlower_snake_case_tis required for parameters that are types, for consistency with naming of other types.
Use ANSI-style parameter port lists
Declare parameterized modules using a parameter port list, not with defparam.
module foo #(
parameter int ParameterName = 1,
parameter type type_parameter_name_t = logic
) (
input type_parameter_name_t ib_headward,
...etc...
Parameter defaults
Default values assigned to tunable parameters must be reasonable (in other words, default values must represent a valid design).
Omitting a default value assignment to a parameter is permitted if it helps mitigate potential integration bugs.
For example, a reusable IP's clock frequency parameter may be reassigned in the majority of instantiations. In this case, the designer may choose to omit the default value to eliminate the possibility of unintentionally leaving a parameter unassigned.
Omitting default values for composite type parameters is not permitted due to inconsistent tool support.
Specify parameter data types
Explicitly declare the type for parameters. Use type "int" (signed integer) for all simple numeric parameters (for example, quantities). Always write assertions to validate parameters (bounds checks, etc.). See the Assertion macros section for a reference implementation.
For example:
module cdc_fifo #(
parameter int Width = 16,
parameter int Depth = 4
) (
...
);
`ASSERT_STATIC(width_must_be_positive_A, Width > 0)
`ASSERT_STATIC(depth_greater_than_1_A, Depth > 1)
Module Instances
Module Instance Names
All module instance names must start with the name of the module being instantiated.
All module instance names must be equal to the module name, plus an optional unique suffix. This unique suffix may be a string or a number, as appropriate. The intent of this naming convention is to make it easier to map from instance names to module names, especially in contexts where the module name is not readily available (for example, synthesis reports, assertions, etc.).
Exceptions can be made in instances where this style decreases the overall legibility of the code (for example, when working with long, hard to read library module names). In such cases, the module name must follow a project specific pattern that readily identifies the original module.
Some examples of legal instance names:
// A single instance of my_decoder within a module:
my_decoder my_decoder ( ... );
// A set of enumerated instances:
my_counter my_counter0 ( ... );
my_counter my_counter1 ( ... );
// A set of symbolically named instances:
my_fifo my_fifo_foo ( ... );
my_fifo my_fifo_bar ( ... );
// Descriptive specifiers:
nand nand_this_that (.a(this), .b(that), .y(not_this_and_that));
register #(10) register_foo (.d(foo_next), .q(foo), .clk, .rst);
sram sram_bar (.address(bar_address), .data(bar_data), .clk, .rst);
Use named ports
Use named ports to fully specify all instantiations.
When connecting signals to ports for an instantiation, use the named port style, like this:
my_module my_module(
.clk(clk),
.rst(rst),
.in(from_here),
.out(to_there)
);
If the port and the connecting signal have the same name, you can use the
.port syntax (without brackets) to indicate connectivity. For example:
my_module my_module(
.clk,
.rst,
.in(from_here),
.out(to_there)
);
All declared ports must be present in the instantiation blocks. Unused outputs
must be explicitly written as no-connects (for example: .output_port()), and
unused inputs must be explicitly tied to ground (for example:
.unused_input_port(8'd0)).
.* is not permitted.
Do not use positional arguments to connect signals to ports.
Instantiate ports in the same order as they are defined in the module.
Instance connection widths
Port connections on module instances must always match widths correctly.
It is recommended to use explicit widths, rather than relying on Verilog's implicit zero-extension and truncation operations, whenever practical.
Examples:
my_module my_module (
.thirty_two_bit_input({16'd0, sixteen_bit_word})
);
my_module my_module (
// Incorrectly implicitly extends from 16 bit to 32 bit
.thirty_two_bit_input(sixteen_bit_word)
);
Parameterized Module Instantiation
Use named parameters for all instantiations.
Instances must be named as specified in Module Instance Names.
When parameterizing an instance, do not specify parameters positionally. Specify parameters using the named parameter style, as follows:
my_module #(
.Height(5),
.Width(10)
) my_module (...);
my_module #(5, 10) my_module (...);
Do not use defparam.
Recursive Instantiation
Do not instantiate recursively.
Modules may not instantiate themselves recursively.
Generate Constructs
Always name generate construct blocks. The block name must start with the "gen_" prefix.
When using a generate construct, always explicitly name each block of generated code. Name each possible outcome of the generating if statement, and name the iterated block of a generating for statement.
All generate construct block names must start with the "gen_" prefix.
This ensures that generated hierarchical signal names are consistent across different tools.
Example of a conditional generate construct:
if (TypeIsPosedge) begin : gen_posedge_type
always @(posedge clk) foo <= bar;
end else begin : gen_negedge_type
always @(negedge clk) foo <= bar;
end
Example of a loop generate construct:
for (genvar bus = 0; bus < NumberOfBuses; bus++) begin : gen_my_buses
my_bus #(.index(bus)) my_bus (.foo(foo), .bar(bar[bus]));
end
Do not wrap a generate construct with an additional begin block.
Do not use generate regions {generate, endgenerate}.
Floating begin-end blocks
The use of generate blocks other than for loop, if, or case generate
constructs is not LRM compliant. While such usage might be accepted by some
tools, this guide prohibits such "bare" generate blocks. Note that the similar
"sequential block" construct is LRM compliant and allowed.
module foo (
input logic bar,
output logic foo
);
begin // illegal generate block
assign foo = bar;
end
endmodule
Packages
Package Declaration
There is no special guidance for declaring packages.
Package Names
Package names must use lower_snake_case and contain only lowercase
alphanumeric characters and underscores. A package name must end with the suffix
_pkg.
Package Dependencies
Packages must not have cyclic dependencies.
Package files may depend on constants and types in other package files, but there must not be any cyclic dependencies. That is: if package A depends on a constant from package B, package B must not depend on anything from package A. While cyclic dependencies are permitted by the SystemVerilog language specification, their use can break some tools.
For example:
package foo_pkg;
parameter int PageSizeBytes = 16 * bar_pkg::Kibi;
...
endpackage
package bar_pkg;
// Bad: cyclic dependency, package "bar_pkg" must
// not depend on anything in "foo_pkg"
import foo_pkg::PageSizeBytes;
...
endpackage
Package imports
Package imports are permitted within a local scope, like a module or package. Explicit imports are recommended because it is clear where everything comes from. Wildcard imports are permitted if there is a reasonable argument that the reduced verbosity enhances readability.
Packages must not be imported into the global scope, as this increases the risk of conflicts.
module foo
import bar_pkg::AddressWidth; // Good: allows usage of package object in ports list
(
input bar_pkg::data_t data, // Use fully-qualified name in portlist
input logic [AddressWidth-1:0] addr, // Use imported parameter in ports list
...
);
import foo_pkg::*; // Good: but be mindful of potential conflicts
import bar_pkg::PageSizeBytes; // Good: preferred explicit import
import foo_pkg::*; // Bad: pollutes global namespace
module foo
(
input data_t data,
...
);
Interfaces
Overview
There is a general desire to adopt interfaces in synthesizable code (DV code can and does use interfaces). However, in practice, projects have found that there are several quirks and challenges when working with interfaces. These include:
- Tool support (although this continues to get better).
- Global scope (how does one fix just one block with an interface ECO).
- Challenging interactions with top level Physical Design flows (for example, forced bit-blasting).
- Large amount of "extra typing" to declare and instantiate.
Since many of the benefits of interfaces can be realized with structs, the cost of allowing interfaces outweighs their benefits. There is likely a set of best practices that can be defined that would realize the potential of interfaces. Adoption of interfaces would require including such best practices in the style guide.
Ports, Signals, and Constants
Overview
- Declare all signals and
use
logic:logic foo; - Signals are named using lower_snake_case.
- Avoid default port values.
- How to declare constants.
- How to declare structs and unions.
- How to declare enumerations.
- Use packed arrays and structs.
- Packed arrays are little-endian:
logic [7:0] byte; - Unpacked arrays are big-endian:
byte_t arr[0:N-1];
Additionally, the semantics of specific naming conventions are elaborated on in the separate Naming Conventions section.
Declare all signals
Signals must be declared before they are used. This means that implicit net declarations must not be used.
All signals must be explicitly declared before use. All declared signals must specify a data type. A correct design contains no inferred nets.
Within modules, it is recommended that signals, types, enums, and localparams be declared close to their first use. This makes it easier for the reader to find the declaration and see the signal type.
Use logic for synthesis
Use logic for synthesis. wire is allowed when
necessary.
All signals in synthesizable RTL must be implemented in terms of 4-state data
types. This means that all signals must ultimately be constructed of nets with
the storage type of logic. While SystemVerilog does provide other data
primitives with 4-state storage (for example, integer), those primitives are
prone to misunderstandings and misuse.
For example:
logic signed [31:0] x_velocity; // say what you mean: a signed 32-bit integer.
typedef logic [7:0] byte_t;
bit signed [63:0] stars_in_the_sky; // 2-state logic doesn't belong in RTL
int grains_of_sand; // Or wait, did I mean integer? Easy to confuse!
It is permissible to use wire as a short-hand to both declare a net and perform continuous assignment. Take care not to confuse continuous assignment with initialization. For example:
wire [7:0] sum = a + b; // Continuous assignment
logic [7:0] acc = '0; // Initialization
There are exceptions for places where logic is inappropriate. For example,
nets that connect to bidirectional (inout) ports must be declared with wire.
These exceptions must be justified with a short comment.
It is permissible for DV to make use of 2-state logic, but all interfaces
between 4-state and 2-state signals must assert a check for X on the 4-state
net before resolving to a 2-state variable.
Signal Naming
Use lower_snake_case when naming signals.
In this context, a signal is meant to mean a net, variable, or port within a SystemVerilog design.
Signal names may contain lowercase alphanumeric characters and underscores.
Ports which connect directly to chip primary IO pads (for example, die IOs,
ASIC package IOs, or FPGA IO buffers) may use UPPER_SNAKE_CASE.
module chip_top (
input logic RESET_N,
output logic LINK_LED,
inout wire SPI_DATA
);
There are a number of naming conventions associated with naming signals, which are complicated enough to warrant their own section of the style guide.
Default Port Values
Do not use default port values.
A default port value is a constant expression that is assigned to an input port of a module. Output ports that are assigned constant expressions are not considered default values and are permitted.
module foo (
// Input d must be assigned when this module is instantiated
input logic d,
// This is an initialization of output q and is okay
output logic q = '0
);
module foo (
// Bad: Do not assign a default value to an input port
input logic d = '0,
...
);
Constants
Declare design-wide constants using parameters in the
design package file. Declare local constants using localparam.
In this context, constants are distinct from tunable parameters for objects such as parameterized modules, classes, etc.
Declaring Constants
When declaring a constant:
- Within a package, use
parameter. - Within a module or class, use
localparam.
The recommended method of defining constants is to declare a package and
declare all constants as a parameter within that package.
Define design-wide constants in the design's package file.
Other packages may also be declared with their own parameter constants to
facilitate the creation of reusable IP.
Naming Constants
UpperCamelCase is the recommended style for immutable constants. The
exceptions are:
- When declaring an enumerated type, use
kCamelCaseto declare enumeration constants.
Specify constant data types
Explicitly declare the type for constants. Use type "int" (signed integer) for all simple numeric parameters (for example, quantities).
Avoid Magic Numbers
It is recommended to use symbolically named constants instead of unnamed numerical constants (aka magic numbers).
Try to give commonly used constants symbolic names rather than repeatedly typing magic numbers.
// Declares a symbolically named constant for the value 0xD5
localparam logic [DataWidthBits-1:0] StartFrameDelimiter = 8'hD5;
...
// The named constant helps clarify the code
if (data == StartFrameDelimiter) begin
next_state = kRxData;
end
// Bad: What's the significance of this number?
if (data == 8'hD5) begin
next_state = kRxData;
end
Unnamed numerical constants may be acceptable in cases where adding a symbolic name adds no value. These type of constants are not considered magic numbers.
// Counter uses unnamed constant 1, but the meaning is obvious
cycle_cnt <= cycle_cnt + 1;
localparam in parameter port lists
Generally, module scoped constants, including those derived from
module parameters are declared as
localparam within the module body close to first use. It
can be useful to have module scoped constants (potentially derived from module
parameters) that control module port dimensions. Declare and define these
constants as localparam in the module port list to prevent them from being
overridden when the module is instantiated.
module foo #(
parameter int NumEntries = 1, // Tunable parameter
localparam int IndexWidth = $clog2(NumEntries) // Must not be overridden
) (
input [IndexWidth-1:0] foo
);
localparam int Bar = 7;
...
endmodule
module foo #(
localparam int Bar = 7, // Bad: Bar is a constant so it goes close to usage
parameter int NumEntries = 1,
parameter int IndexWidth = $clog2(NumEntries) // Bad: IndexWidth is linked to NumEntries
) (
input [IndexWidth-1:0] foo
);
...
endmodule
Packed vs. Unpacked
Use packed arrays and structs for synthesizable signals. Unpacked arrays are appropriate for behavioral models of SRAMs and DV code.
Positive Array Bounds
When declaring vectors and arrays, the msb and lsb must be non-negative.
logic [14:0] foo;
logic [13:-1] foo;
Packed Ordering
Bit vectors and packed arrays must be little-endian.
When declaring bit vectors and packed arrays, the index of the most-significant bound (left of the colon) must be greater than or equal to the least-significant bound (right of the colon). This style of bit vector declaration keeps packed variables little-endian.
For example:
typedef logic [7:0] u8_t;
logic [31:0] u32_word;
u8_t [1:0] u16_word;
u8_t byte3, byte2, byte1, byte0;
assign u16_word = {byte1, byte0};
assign u32_word = {byte3, byte2, u16_word};
Unpacked Ordering
Unpacked arrays must be big-endian.
When declaring unpacked arrays, the index to the left of the colon must be less than or equal to the index to the right of the colon.
Declare zero-based unpacked arrays using the shorter notation [size]. It is
understood that [size] is equivalent to the big-endian declaration
[0:size-1].
Assigning directly to an unpacked array as a whole is acceptable, but not recommended, because mixing big-endian unpacked arrays with little-endian packed arrays is prone to reversed-ordering mistakes. Prefer assigning elements individually by index when possible (for parameter arrays, where element-by-element assignment is not possible without constant functions, initializing the unpacked array directly is acceptable with caution).
// Good: Assign unpacked array elements individually by index to avoid
// reversed-ordering mistakes.
logic [15:0] word_array[3];
assign word_array[0] = word0;
assign word_array[1] = word1;
assign word_array[2] = word2;
// Acceptable, but not recommended for signals: Avoid assigning to an unpacked
// array as a whole. Result: word_array[0] == word0, word_array[1] == word1, and
// word_array[2] == word2.
assign word_array = '{word0, word1, word2};
// Acceptable for parameters, but be cautious with big-endian ([0:3]) ordering.
// Result: Vec[0] == 0, Vec[1] == 1, Vec[2] == 2, and Vec[3] == 3.
parameter int Vec[4] = '{0, 1, 2, 3};
Rationale: Big-endian ordering was selected for unpacked arrays over little-endian because:
- The C-like
[size]declaration is preferred for unpacked arrays and is equivalent to[0:size-1]. - Big-endian is required for typical use of the
foreachconstruct, which creates a loop variable with a positive increment.
This rule only applies to variables and parameters, not to other things such as arrays of instances. Instance arrays must be declared so that the order of instances is correct for the ordering of packed ports.
Structs and Unions
Declaring Structs and Unions
Name structs and unions with a typedef.
Always name struct and union types using typedef. Anonymous types are not
allowed because they are not reusable. Struct type names must contain only
lowercase alphanumeric characters and underscores and end with _t.
typedef struct packed {
logic dir;
int value;
} data_t; // new named type is created
data_t packet;
struct packed { // Typedef is missing
logic dir;
int value;
} packet; // anonymous struct type
Naming Union Members
Union member names must be prefixed with as_*.
Always prefix union member names with as_*. A consistent naming convention
reduces the effort required to understand RTL with deeply nested struct or union
type hierarchies by making the type-cast like behavior of unions explicit.
typedef union packed {
read_id_t as_read_id; // as_read_id prefix to refer to read_id_t type.
write_id_t as_write_id;
} id_t;
assign write_id = cmd.id.as_write_id;
typedef union packed {
read_id_t read_id; // as_* is missing
write_id_t write_id;
} id_t;
assign write_id = cmd.id.write_id;
Enumerations
Name enumeration types snake_case_t (or snake_case_e).
Name enumeration constants kCamelCase.
Always name enum types using typedef. The storage type of any enumerated
type must be specified. For synthesizable enums, the storage type must be a
4-state data type (logic rather than bit).
Anonymous enum types are not allowed as they make it harder to use the type in
other places throughout the project and across projects.
Enumeration type names must contain only lowercase alphanumeric characters and
underscores. You can suffix enumeration type names with _t or _e, as long as
the suffix is consistent within the project.
Enumeration value names (constants) must be kCamelCase, for example,
kReadyToSend. This indicates that the values are neither overrideable nor
assignable.
typedef enum logic { // A 4-state storage type
kWrite,
kRead
} access_t; // new named type is created
access_t req_access, resp_access;
enum { // Typedef is missing, storage type is missing.
Write,
Read
} req_access, resp_access; // anonymous enum type
Vector Typedefs
Prefer no single vector typedefs unless they are broadly used.
Do not use typedef to define local or single-use vector types. Defining a new
type for a vector that is only used in a limited scope adds unnecessary
redirection. Instead, use the raw vector type directly.
For vectors that are used broadly across multiple modules or packages (for
example, a global address type), defining a named type using typedef is
recommended to ensure consistency.
// Declare design-wide vector typedefs in a package.
package my_pkg;
typedef logic [AddrWidth-1:0] addr_t;
endpackage
module my_module (
...
);
// Good: Use design-wide typedef for broadly used vector.
my_pkg::addr_t addr;
...
endmodule
module my_module (
...
);
// Bad: Unnecessary local typedef for a single-use vector.
typedef logic [DataWidth-1:0] local_data_t;
local_data_t local_data;
...
endmodule
An exception is made when defining signed packed arrays, where a typedef is
necessary to apply signedness to the elements of the array rather than the array
as a whole (see
mixed-sign arithmetic operations).
// Good: Use local typedef to apply signedness to elements of a packed array.
typedef logic signed [DataWidth - 1:0] data_t;
data_t [NumChannels - 1:0] my_channel_data;
// Bad: Signedness applies to the entire packed array, not the elements.
logic signed [NumChannels - 1:0][DataWidth - 1:0] my_channel_data;
Functions and Tasks
Choosing Between Functions, Tasks, and Modules
In synthesizable RTL, use modules as the default construct to encapsulate behavior. Tasks must not be used in synthesizable RTL. Functions may be used for short, simple combinational logic with a single output.
- Modules: Use modules as the default mechanism to encapsulate design behavior.
- Tasks: Do not use tasks in synthesizable RTL; use modules instead. Tasks are permitted in testbenches.
- Functions: Functions are useful as a lightweight alternative to modules
for simple combinational logic.
- Prefer functions over modules when there is a single output and the
relationship between inputs and output can be expressed with a direct
assignment (for example,
assign foo = check_foo(bar);). - Prefer modules over functions when there are multiple outputs.
- Functions can be defined in a package for wider reuse.
- Prefer functions over modules when there is a single output and the
relationship between inputs and output can be expressed with a direct
assignment (for example,
Templates
Function and task definitions must follow the template in the following example.
// Description of the function's behavior, inputs, and outputs
function automatic logic foo(logic x, logic y);
// Local variables
logic z;
// Statements
z = x ^ y;
// Return
return z;
endfunction : foo
// Description of the task's behavior, inputs, and outputs
task automatic bar(input logic x, output logic y);
// Variable declarations
// Statements
y = x;
endtask : bar
Some points about the function template to note in addition to the following sections:
- The function was declared with the formal arguments in parentheses (matching module style).
- No assignment in the function body to the input variables .
Latches inside functions
Do not infer a latch inside a function, as this may cause a simulation-synthesis mismatch.
Lifetime
Explicitly declare functions and tasks defined outside of a class as static or automatic, preferring automatic. Functions must be automatic in synthesizable RTL.
Depending on their declaration context, functions and tasks have different default lifetimes. Within a class, they are always automatic, whereas the default is static in modules, interfaces, programs, and packages. To prevent potential confusion, functions and tasks declared outside of a class must always explicitly specify their lifetime. Automatic is generally the proper lifetime because it allows a function or task to be independently called from different contexts.
In synthesizable RTL, functions must always be declared as automatic.
Function Data Types
Explicitly declare the data type for a function, or
declare the function as void.
// The return type of the function is explicitly defined as 'bit'
function automatic bit foo(int a);
// The function has no return value.
function automatic void foo(int a);
// The function's data type is implicitly declared
function automatic foo(int a);
Argument Types
Explicitly declare storage types for all arguments in function and task arguments lists.
For example:
// Both arguments have explicitly declared 'int' as their storage type.
task automatic foo(int a, int b);
// Bad: No storage type explicitly declared for 'b'.
task automatic foo(int a, b);
Argument Directions
Argument directions for functions and tasks are optional and considered inputs by default. If a direction is specified for any argument, then it must be specified for all arguments.
For example:
task automatic foo(int a, int b); // Both arguments treated as inputs.
function automatic bit foo(input int a, output int b, inout int c, ref int d);
// Potential bug: Although not labeled, "b" will be treated as "ref".
task automatic foo(ref int a, int b);
Sequential Logic
always_ff vs always
There is no preference between always_ff and always for clocked processes,
other than consistency within a project.
Use Non-Blocking Assignments
Sequential logic must use non-blocking assignments.
Never mix assignment types within a block declaration.
A sequential block (a block that latches state on a clock edge) must exclusively use non-blocking assignments, as shown in the section on declaring Registers.
This stands in contrast to combinational logic, which must use blocking assignments in process blocks.
This is one of Clifford Cummings's Golden Rules of Verilog.
Sequential Logic (Latches)
The use of latches is not recommended - use flip-flops when possible.
Unless absolutely necessary, use flip-flops or registers instead of latches.
If you must use a latch, use always_latch over always, and use non-blocking
assignments (<=). Never use blocking assignments (=).
Sequential Logic (Registers)
Use the standard format for declaring sequential blocks.
In a sequential always block, only use non-blocking assignments (<=). Never
use blocking assignments (=).
Designs that mix blocking and non-blocking assignments for registers simulate incorrectly because simulators evaluate blocking assignments in a different simulation event region than non-blocking assignments. This causes race conditions where signals bypass pipeline stages, leading to simulation-synthesis mismatches.
If your next-state generation logic requires blocking assignments to state clearly, use a separate combinational-only block to generate that next-state value.
For reusable IP, it is recommended that registers support all three forms of reset so integrators can tie off unused resets:
- Synchronous reset
- Asynchronous reset
- Initialization (configuration-time initial values for FPGA designs)
Initialization is commonly recommended for SRAM-based FPGAs, whereas synchronous reset is recommended for ASICs.
A correctly implemented 8-bit register with an initial value of "0xAB" would be implemented as follows:
logic foo_le;
logic [7:0] foo, foo_next;
initial foo = 8'hAB; // initialization
always @(posedge clk or posedge arst) begin
if (arst) begin
foo <= 8'hAB;
end else if (rst) begin
foo <= 8'hAB;
end else if (foo_le) begin
foo <= foo_next;
end
end
Do not allow multiple non-blocking assignments to the same bit.
Example:
if (cond1) begin
abc <= 4'h1;
end
if (cond2) begin
abc <= 4'h2;
end
If both cond1 and cond2 are true, the Verilog standard says that the second assignment will take effect, but this is a style violation.
Even if cond1 and cond2 are mutually exclusive, make the second "if" into an "else if".
Exception: It is fine to set default values first, then specific values. However, it is recommended to do this work in a separate combinational block with explicit blocking assignments.
Example:
`ASSERT_KNOWN(state_is_known_A, state, clk, rst)
always @(posedge clk) begin
s1 <= s1_default;
s2 <= s2_default;
s3 <= s3_default;
unique case (state)
kState1: begin
s1 <= real_s1_value;
end
kState2: begin
s1 <= real_s1_value;
s2 <= real_s2_value;
end
kState3: begin
s3 <= real_s3_value;
end
default: ; // empty default, ensures case statement is fully specified
endcase
end
Keep work in sequential blocks simple. If a sequential block becomes
sufficiently complicated, consider splitting the combinational logic into a
separate combinational (always_comb) block. It's recommended for a sequential
block to contain only a register instantiation, with perhaps a load enable or an
increment.
Resets
Reusable IP must consistently implement all three varieties of reset (synchronous reset, asynchronous reset, and configuration-time initialization) so that the non-preferred reset schemes can be disabled at implementation time. See Sequential Logic for more details.
Delay Modeling
Do not use #delay in synthesizable design modules.
Synthesizable design modules must be designed around a zero-delay simulation
methodology. All forms of #delay, including #0, are not permitted.
Delays and Time Units
In non-synthesizable code always specify a time unit if
#delay is used, except for #0.
Whenever using non-zero pound # delays, always specify a time unit in addition
to the raw number, to avoid timescale issues.
q <= #10ns d;
#(wait_in_ns * 1ns);
#0; // Special case. No time units required
// Missing time units.
q <= #10 d;
#wait_in_ns;
Note that specifying time units alone does not guarantee proper behavior. Use
project-wide timeunit and timeprecision settings in your module, package, or
interface to ensure the proper behavior (see section 3.14.2.2 of the 1800-2017
LRM for more details).
Finite State Machines
State machines use an enum to define states, and are implemented with two process blocks: a combinational block and a clocked block.
Every state machine description has three parts:
- An enum that declares and describes the states.
- A combinational process block that decodes state to produce next state and other combinational outputs.
- A clocked process block that updates state from next state.
Enumerating States
The enum statement for the state machine must list each state in the state machine. Comments describing the states must be deferred to the case statement in the combinational process block, as shown in the following example.
States must be named in kCamelCase, like other
enumeration constants.
Barring special circumstances, the initial idle state of the state machines will
be named kIdle. (Alternate names are acceptable if they improve clarity.)
It's recommended for each module to only contain one state machine. If your
module needs more than one state machine, you will need to add a unique prefix
(or suffix) to the states of each state machine, to distinguish which state is
associated with which state machine. For example, a module with a "reader"
machine and a "writer" machine might have a kRdIdle state and a kWrIdle
state.
Combinational Decode of State
The combinational process block must contain:
- A case statement that decodes state to produce next state and combinational outputs. For clarity, only cases where the output value deviates from the default must be coded.
- Before the case statement must be a block of code that defines default values for every combinational output, including "next state."
- The default value for the "next state" variable must be the current state. The case statement that decodes state will then only assign to "next state" when transitioning between states.
- Within the case statement, each state alternative must be preceded with a comment that describes the function of that state within the state machine.
The State Register
No logic except for reset must be performed in this process. The state variable must latch the value of the "next state" variable.
Other Guidelines
When possible, try to choose state names that differ near the beginning of their name, to make them more readable when viewing waveform traces.
Example
// Define the states
typedef enum logic [1:0] {
kIdle, kFrameStart, kAccStoreWrite, kBandEnd
} fsm_state_t;
fsm_state_t state_next, state;
// Combinational decode of the state
// Check that frame_start is not X since it is used in an if statement
`ASSERT_KNOWN(frame_start_is_known_A, frame_start, clk, rst)
always_comb begin
state_next = state;
foo = 1'b0;
bar = 1'b0;
bum = 1'b0;
unique case (state)
// kIdle: waiting for frame_start
kIdle:
if (frame_start) begin
foo = 1'b1;
state_next = kFrameStart;
end
// kFrameStart: Reset accumulators
kFrameStart: begin
// ...
end
kAccStoreWrite: begin
// ...
end
kBandEnd: begin
// ...
end
default: begin
// X's in the inputs propagate to X's in the outputs (Number of states is
// a power of two, therefore case is fully specified and no don't-care
// behavior will be introduced).
// See "Don't-care in case statements" section for information on
// case statements with non-power-of-two items or the X_OF_TYPE macro.
state_next = `X_OF_TYPE(fsm_state_t);
foo = 'X;
bar = 'X;
bum = 'X;
end
endcase
end
// Latch state
always_ff @(posedge clk) begin
if (rst) begin
state <= kIdle;
end else begin
state <= state_next;
end
end
Combinational Logic
Use always_comb or assign
Avoid sensitivity lists.
Use either always_comb blocks or assign statements for combinational logic.
Use blocking assignments
Synthesizable combinational process blocks must only use blocking assignments.
All assignments within a synthesizable
always_comb block must be blocking assignments.
Non-blocking assignments are used only in sequential logic blocks.
X Propagation
Either propagate X's, or catch them via assertions. Do
not silently squash X in logic.
Write logic that will either propagate X or assert when your inputs are X.
Avoid silently squashing X by writing logic that resolves an X input to a
non-X output. Instead, write logic that either explicitly propagates the X
or uses an assertion to raise an exception when an input is X.
Logical operations and if statements
Note that logical operations involving X will propagate X in a way that
accurately represents silicon behavior.
Example:
$display("%b", 1'bx == 1'b1); // produces 1'bx
$display("%b", 1'bx != 1'b1); // produces 1'bx
$display("%b", 1'bx & 1'b1); // produces 1'bx
$display("%b", 1'bx & 1'b0); // produces 1'b0
$display("%b", 1'bx | 1'b0); // produces 1'bx
$display("%b", 1'bx | 1'b1); // produces 1'b1
$display("%b", !(1'bx)); // produces 1'bx.
However, when evaluated in a boolean context, X always evaluates to false. As
a result, typical if/else statements do not propagate X in a way that
accurately represents silicon behavior.
Example:
always_comb begin
if (value) result = 1'b0; // value is 1'b1
else result = 1'b1; // value is 1'b0 or 1'bx
end
This can mask subtle problems in code and produce a mismatch between simulation and synthesis.
Instead, use conditional operators, which will propagate an X. If that hurts
readability, an alternative is to use assertions to catch X.
always_comb begin
result = value ? 1'b1 : 1'b0; // 1'bx produces 1'bx
end
`ASSERT_KNOWN(value_is_known_A, value, clk, rst)
always_comb begin
if (value) result = 1'b0; // value is 1'b1
else result = 1'b1; // value is 1'b0 or 1'bx
end
Another alternative is to have explicit if and else if clauses for 0 and
1. However, this is verbose and more difficult to maintain.
always_comb begin
if (value) result = 1'b0; // value is 1'b1
else if (!value) result = 1'b1; // value is 1'b0
else result = 1'bx; // value is 1'bx
end
X Propagation in Case Statements
See Case Statements for guidance and examples.
Array indexing
Writing into an array when the index is X does not propagate the X to the
array contents.
// An X in index gets silently squashed in simulation.
assign buf[index] = foo;
Instead, assertions must be used to catch X in the array index.
`ASSERT_KNOWN(index_is_known_A, index, clk, rst)
assign buf[index] = foo;
Clocks and Resets
The preceding X propagation guidelines do not apply to clocks and resets.
Instead, extra care must be taken at each clock-domain level in the hierarchy to
ensure clocks and resets are never driven to X along with !$isunknown()
assertions.
The reason for this is that the recommended coding styles for sequential logic
do not propagate X on clocks and resets. For example, posedge clk still
triggers on a 0 to X transition. Also, synthesis tools recommend using if
statements for asynchronous resets instead of conditional operators.
Further discussion
- "I'm Still In Love With My X!" by Stuart Sutherland
- "Being Assertive With Your X" by Don Mills
Don't-Care Assignments
Do not specify don't-care logic in synthesizable code.
For the purposes of this document, don't-care logic is defined as the explicit
or implicit assignment to X based on known (non-X) inputs. Don't-care logic
is a subset of unknown logic values, and not all assignments to X are
considered don't-care logic. For examples of allowed assignments to X, see
X Propagation.
Don't-care assignments are problematic for automated engineering change orders (ECOs) and complicate Logic Equivalence Checking (LEC) between netlists because synthesis can produce netlists that have chosen conflicting interpretations of the don't-care states. If designers need to take advantage of don't-care space for optimizations, there are external tools like Espresso that can be used instead.
Don't-Care in Explicit Assignments
Do not assign don't-care logic. A signal must be known if the signals driving it are known.
// Bad: When valid is '0', wdata is considered don't-care
assign wdata = valid ? data : 'x;
assign we = valid;
assign wdata = data;
assign we = valid;
Don't-Care in Case Statements
See Case Statements for guidance on how to avoid introducing don't-care logic in case statements.
Don't-Care in Array Indexing
Do not access an index that is outside the bounds of an array. This can happen if the array size is not a power-of-two, or if the index contains superfluous MSBs.
// For values of index > 4, the output ‘foo’ is treated as don’t-care
logic foo;
logic [2:0] index;
logic [4:0] buffer;
...
assign foo = buffer[index];
Instead, specify behavior for out-of-range indices or explicitly pad arrays up to power-of-two sizes. Also, add an assertion to catch out-of-range indices.
localparam BufferSize = 5;
logic foo;
logic [2:0] index;
logic [BufferSize-1:0] buffer;
...
`ASSERT(index_within_array_bounds_A, (index < BufferSize), clk, rst)
// This approach specifies the behavior for out-of-range indices.
// It is concise but might require a lint waiver
assign foo = (index < BufferSize) ? buffer[index] : '0;
logic bar;
logic [7:0] buffer_padded;
// This approach explicitly pads out the array. It eliminates the need for lint
// waivers, but it can be less concise.
assign buffer_padded = {3'b0, buffer}; // zero-pad the array
assign bar = buffer_padded[index];
Properly handling arbitrary array sizes is complex. It is recommended that
projects use the standard BOUNDED_LOOKUP macro for
the general case.
assign foo = `BOUNDED_LOOKUP(buffer, index)
Tri-State Logic
Do not use three-state logic (Z state) to accomplish on-chip logic such as
muxing.
Case Statements
Use unique case in synthesizable RTL. Always define a
default case-item. Do not specify don't-care behavior.
Do not use case-modifying pragmas.
Here is an example of a style-compliant case statement:
always_comb begin
unique case (select)
2'b00: foo = bar0;
2'b01: foo = bar1;
2'b10: foo = bar2;
2'b11: foo = bar3;
default: foo = 'X;
endcase
end
The unique prefix is recommended before all case statements, as it creates
simulation assertions that can catch certain mistakes. In some cases, priority
may be used instead of unique, though if-else-if structures are a more
readable representation for priority encoders. Code other than synthesizable RTL
can use case without unique or priority if necessary.
Be sure to use unique case correctly. In particular, outputs for any
unspecified case-items are treated as don't-care by the synthesis tools. This
can lead to a simulation-synthesis mismatch as described in
Don Mills' paper. Including a default case-item ensures the case
statement is always fully defined, which helps avoid unintuitive don't-care
behavior.
The default case-item is also required to avoid accidental inference of
latches, even if all cases are covered. In simulation, a case expression that
evaluates to X will not match any case and will behave as a latch, leading to
different behavior than synthesis. Instead, in most scenarios, the best choice
is to propagate the X by assigning all output variables to X by default.
Note that the SystemVerilog-2009 unique0 keyword is not currently supported by
all tools.
Wildcards in case items
Use case instead of casez whenever wildcard operator behavior is not
required. When wildcard behavior is needed, use casez.
When expressing a wildcard in a case item, use the '?' character since it more clearly expresses the intent.
Here is an example of a style-compliant casez statement:
always_comb begin
unique casez (select)
3'b000: operand = accum0 >> 0;
3'b001: operand = accum0 >> 1;
3'b010: operand = accum1 >> 0;
3'b011: operand = accum1 >> 1;
3'b1??: operand = regfile[select[1:0]];
default: operand = 'X; // propagate X
endcase
end
casex must not be used. casex implements a symmetric wildcard operator such
that an X in the case expression may match one or more case items. casez
only treats high-impedance states (Z or ?) as a wildcard, and performs exact
matches for undriven X inputs. While this does not completely fix the problems
with symmetric wildcard matching, it is harder to accidentally produce a Z
input than an X input, so this form is preferred.
Don't-care in case statements
Don’t-care behavior is introduced anytime X
assignments are made from a don’t-care branch of a case statement.
To avoid don't-care behavior, specify non-X behavior for all outputs, across
the full range of values in the case-expression. If this prevents x-propagation,
assertions must be used to ensure X's in the case-expression are not silently
squashed.
typedef enum logic [1:0] {kMode0, kMode1, kMode2} mode_t;
mode_t mode;
always_comb begin
unique case (mode)
kMode0: foo = bar0;
kMode1: foo = bar1;
kMode2: foo = bar2;
default: foo = 'x; // foo is don't-care when mode = 2'b11;
endcase
end
typedef enum logic [1:0] {kMode0, kMode1, kMode2} mode_t;
mode_t mode;
`ASSERT_KNOWN(mode_is_known_A, mode, clk, rst)
always_comb begin
unique case (mode)
kMode0: foo = bar0;
kMode1: foo = bar1;
kMode2: foo = bar2;
default: foo = '0; // explicit default value to avoid don't-care behavior
endcase
end
Note that assigning to X from the default case-item of an otherwise
fully-specified case statement does not introduce don’t-care logic. In this
case, using the default for x-propagation is the recommended style.
typedef enum logic [1:0] {kMode0, kMode1, kMode2, kMode3} mode_t;
mode_t mode;
always_comb begin
unique case (mode)
kMode0: foo = bar0;
kMode1: foo = bar1;
kMode2: foo = bar2;
kMode3: foo = bar3;
default: foo = 'X; // default can be used for x-propagation here,
// since the case statement is otherwise complete
endcase
end
If assigning X to an enumerated type in the default case-item, then the assignment must be typecast. It is recommended that projects use a macro to safely contain this cast.
typedef enum logic [1:0] {kFoo0, kFoo1, kFoo2, kFoo3} foo_t;
foo_t foo1, foo2;
always_comb begin
unique case (foo1)
kFoo0: foo2 = kFoo0;
kFoo1: foo2 = kFoo1;
kFoo2: foo2 = kFoo2;
kFoo3: foo2 = kFoo3;
default: foo2 = `X_OF_TYPE(foo_t); // typecast for X in default
endcase
end
With multiple outputs, it can be convenient to specify default values outside of
the case statement. If this is done, the unique case statement must be fully
specified, otherwise these outputs are treated as don't-cares for any
unspecified case-items.
`ASSERT_KNOWN(mode_is_known_A, mode, clk, rst)
always_comb begin
// Default behavior
out0 = in0;
out1 = in1;
unique case (mode)
kForceLow: {out1, out0} = '0;
kForceHigh: {out1, out0} = '1;
default: ; // empty default, ensures case statement is fully specified
endcase
end
Upside-down unique case statements
Upside-down unique case statements are another way of introducing don't-care behavior. If multiple conditions of the upside-down case statement are true, the output assignment is not explicitly provided in the code, effectively leading to a don't-care assignment. This will cause the same LEC and ECO issues as if the condition was explicitly coded as an X output. See Don't-Care Assignments. It is recommended to rewrite the code as a non-upside-down case statement, or do manual logic reduction if timing is a concern.
// If c_grant[0] == 1 and c_grant[1] == 1, out is don't-care.
always_comb begin
unique case (1'b1)
c_grant[0]: out = 1'b0;
c_grant[1]: out = 1'b1;
default: out = 1'b0;
endcase
end
// If c_grant[0] == 1 and c_grant[1] == 1, out is explicitly 0.
always_comb begin
unique case (c_grant)
2'b01: out = 1'b0;
2'b10: out = 1'b1;
default: out = 1'b0;
endcase
end
Synthesis Pragmas
Never use either the full_case or parallel_case pragmas. These pragmas can
easily cause synthesis-simulation mismatches. This is a subset of the
general guidance to avoid synthesis pragmas.
References:
- Don Mills, Yet Another Latch and Gotchas Paper
- Clifford Cummings, SystemVerilog-2009 Enhancements: ‘priority’, ‘unique’, ‘unique0’
- Sutherland, Mills, and Spear, Gotcha Again: More Subtleties in the Verilog and SystemVerilog Standards...
Signed Operations
Use the available signed operator constructs wherever signed operations are used.
Mixing signed and unsigned signals in an operation can result in unexpected
behavior and, in some cases, simulation-synthesis mismatches. For all operations
where signed and unsigned signals need to be used together, the unsigned signals
must first be explicitly cast to signed, using the signed' cast operator.
If any operand in a calculation is unsigned, Verilog implicitly casts all operands to unsigned and generates a warning. There must not be any signed-to-unsigned warnings from either the simulation or synthesis tools if all unsigned variables are properly cast.
Mixed-Sign Arithmetic Operations
Explicitly cast unsigned values to signed before use in mixed-sign arithmetic operations.
logic signed [7:0] a = 8'sh80; // equals signed(-128)
logic incr = 1'b1; // equals unsigned(1)
logic signed [15:0] sum;
always_comb begin
sum = a + signed'({1'b0, incr}); // Good: explicitly casting incr to signed
// produces expected sum equals signed(-127)
end
logic signed [7:0] a = 8'sh80; // equals signed(-128)
logic incr = 1'b1; // equals unsigned(1)
logic signed [15:0] sum;
always_comb begin
sum = a + incr; // Bad: mixed-sign addition results in implicit cast of `a`
// giving sum equals signed(129) instead of signed(-127) as
// expected
end
Mixed-Sign Comparison Operations
Explicitly cast unsigned values to signed before use in mixed-sign comparison operations.
logic [7:0] unsigned_foo = 8'h00; // equals unsigned(0)
logic signed [7:0] signed_bar = 8'shFF; // equals signed(-1)
logic bar_less_than_foo;
logic bar_less_than_zero;
always_comb begin
// Good: Explicitly casting `foo` to signed so comparison returns `true` as
// expected
bar_less_than_foo = (signed_bar < signed'({1'b0, unsigned_foo}));
// Good: Explicitly declare constant as signed decimal ensures consistent
// compiler-to-compiler behavior.
bar_less_than_zero = (signed_bar < 'sd0);
end
logic [7:0] unsigned_foo = 8'h00; // equals unsigned(0)
logic signed [7:0] signed_bar = 8'shFF; // equals signed(-1)
logic bar_less_than_foo;
logic bar_less_than_zero;
always_comb begin
// Bad: Mixed-sign comparison returns unexpected `false`
bar_less_than_foo = (signed_bar < unsigned_foo);
// Bad: Non-explicitly signed constant results in inconsistent behavior
// with different tools
bar_less_than_zero = (signed_bar < 0);
end
Arithmetic Right Shift Operations
Always use the >>> operator instead of the >> operator when arithmetically
right shifting signed values.
It is common practice to use right arithmetic shift operators to divide by
powers of 2. With signed operands, care must be taken that the correct shift
operator is used to preserve sign. The >> operator inserts a 0 at the most
significant bit location. If the argument is a negative value, this will flip
the sign. By using the >>> operator, the bit inserted at the most significant
bit location is determined by the sign of the argument and will not flip.
logic signed [7:0] foo = 8'sh80; // equals signed(-128)
logic signed [7:0] bar;
always_comb begin
bar = foo >>> 1; // Good: returns signed(-64)
end
logic signed [7:0] foo = 8'sh80; // equals signed(-128)
logic signed [7:0] bar;
always_comb begin
bar = foo >> 1; // Bad: returns signed(64). The `>>` operator inserts a 0 at
// the most significant bit location, thereby flipping the
// sign of `bar`.
end
Bit Widths
Be careful about signal and expression widths.
Always be explicit about the widths of number literals.
Always explicitly size number literals to match their final width. Verilog's implicit zero-extension and truncation operations can lead to subtle bugs.
If the literal is specified in a binary format then all digits must be explicitly written out to avoid unintentional zero-extension in the most significant bits. This can catch subtle bugs if an intermediate bit is dropped. Literals written in decimal and hexadecimal formats do not need to explicitly include all significant digits because they tend not to be used for hard-to-read patterns.
Exceptions:
- When using literals in expressions with parameterized widths, it is
acceptable to use integers without an explicit width (for example,
foo = bar + 1) to avoid contrivances such as{{(Bus_width-1){1'b0}},1'b1}. The language will properly pad 0s if the literal is narrower than the expression width and lint tools typically raise an error if the literal is wider than the expression width. - It is acceptable to use the
'0or'1literals to automatically create correctly sized (context-determined) zero or "all ones". - For literals assigned to integer variants (for example,
bit,byte,shortint,int,integer, andlongint) the explicit width is optional.
Examples:
localparam int Width = 5; // int doesn't need explicit width
localparam bit FeatureFlag = 1; // bit type doesn't need explicit width
localparam logic [3:0] Bar = 4'd4;
logic [15:0] foo1, foo2, foo3, foo4;
logic [7:0] foo5;
logic [63:0] foo6;
logic [Width-1:0] foo7, foo8;
assign foo1 = 16'd2; // OK to not have the tens column
assign foo2 = '0; // automatically sized 0 is ok
assign foo3 = {16{1'b1}}; // explicit "all ones"
assign foo4 = '1; // automatically sized "all ones"
assign foo5 = 8'b0010_1111; // exact width literal, no implicit zero-extension
assign foo6 = 64'h2000; // OK not to write out the whole hex constant
// OK to not explicitly size the integer literal in a parameterized expression
assign foo7 = foo8 + 4;
localparam logic [3:0] Bar = 4;
logic [7:0] foo1, foo2, foo3, foo4;
assign foo1 = 2; // literal is not explicitly sized
assign foo2 = 2'd2; // 2-bit literal implicitly 0 extended to 8 bits
assign foo4 = 8'b001_1111; // MSb is missing and has implicit zero-extension
Do not use multi-bit signals in a boolean context
Rather than letting boolean operations and if expressions reduce a multi-bit signal to a single bit, explicitly compare the multi-bit signal to 0. The implicit conversion can hide subtle logic bugs.
Examples:
logic [3:0] a, b;
logic out;
assign out = (a != '0) && (b == '0); // Good: Explicit comparisons
always_comb begin
if (a != '0) begin // Good: Explicit comparison
// ...
end
end
logic any_a;
// Good: Unary reductions are okay in a boolean context.
assign any_a = |a;
always_comb begin
if (&a) begin
// ...
end
end
logic [3:0] a, b;
logic out;
// Bad: Incorrect because it implicitly converts 4-bit signals to 1-bit before
// AND. Also, !b is different from ~b and can be hard to catch.
assign out = a && !b;
// Bad: Incorrect use of a multi-bit signal in an if expression.
always_comb begin
if (a) begin
// ...
end
end
Prefer Logical Operators for Single-Bit Signals
When performing logical operations on single bit signals, it is recommended to use the logical operators (for example, &&, ||, !) instead of bitwise operators (for example, &, |, ~).
logic a, b, c;
...
// This aids readability as the logical operator makes clear `a` and `b`
// are single-bit signals.
assign c = a && b;
logic a, b, c;
...
// `a` and `b` could be multi-bit vectors, it is not clear from the assignment.
assign c = a & b; // Not recommended
Handling Width Overflow
Beware of shift operations, which can produce a result wider than the operand. Bit-selection and concatenation may be clearer than shifting by a constant amount.
Addition and negation operations produce a result one bit wider than the operands, due to carry. An allowable exception to the rule about matching widths is to silently drop the carry on assignment.
Example:
assign abc_next = abc + 4'h1;
Bit Slicing
Only use the bit slicing operator when the intent is to refer to a portion of a bit vector.
Examples:
logic [7:0] a, b;
logic [6:0] c;
assign a = 8'd7; // Good
assign a[7:1] = 7'd5; // Good: it's partial assignment.
assign a = b; // Good: the parser would warn on width mismatch.
logic [7:0] a, b;
assign a[7:0] = 8'd7; // Bad: redundant and can mask linter warnings.
assign a = b[7:0]; // Bad: redundant and masks linter warnings.
Alias statement
Do not use alias, use port connection if possible or
tran if you must.
Alias statement can be used to connect inout nets, but there is a history
for alias statement to not be supported by EDA tools (as of 2019). Hence do
not use alias. The recommended alternatives are using conventional port
connections, if possible, or tran (a bidirectional pass switch) to connect
ports.
Implicitly Sized Expressions
Do not rely on the width of implicitly sized sub-expressions.
Instead, when requiring that a sub-expression produce a result of a specific width, either:
- Cast the sub-expressions to specific widths (note that this may cause a truncation).
- Break compound expressions into separate partial result variables.
The bit lengths of self-determined expressions (summarized in Table 11-21 of the
SystemVerilog-2017 standard) are counter-intuitive (for example, a * b
produces a result of length max($bits(a), $bits(b))). To avoid these gotchas,
it is best to not rely on implicitly sized results at all.
For example, the following code is wrong:
module compose_bitmap (
input logic en_a,
input logic en_b,
output logic [7:0] en_bitmap);
assign en_bitmap = {(en_a << 4), (en_b << 4)};
endmodule
The expression in the preceding example synthesizes to zero, because
(surprisingly) the length of the implicitly sized expression en_a << 4 is 1
($bits(en_a)).
A correct implementation would explicitly size each expression, either by adding explicit sizes to the sub-expressions:
assign en_bitmap = {4'(en_a << 3), 4'(en_b << 3)};
Or by decomposing the expression into explicitly sized partial results:
logic [3:0] en_a_bitmap;
logic [3:0] en_b_bitmap;
assign en_a_bitmap = en_a << 3;
assign en_b_bitmap = en_b << 3;
assign en_bitmap = {en_a_bitmap, en_b_bitmap};
Design Conventions
Summary
The key ideas in this section include:
- Use a consistent timescale for all RTL.
- Use flow control consistently.
- Use clock domain crossing consistently.
- Rules for top-level implementation.
- Prefer to register module outputs.
Timescale
Every project must define a consistent way of specifying timescale.
There are multiple ways to specify the timescale, each with pros and cons. Each project must define a single way to specify the timescale for the entire project. Individuals must not implement one-off timescale solutions.
Bus Protocols
Bus protocols are commonly used to connect components of the chip. When using an
industry standard protocol, use the terminology established by that protocol.
When designing a new protocol, the agent that controls the bus or initiates
transactions is named the requester while the agent that services the
transaction is the completer.
- Be sure to follow the guidance on inclusive language in external IP. Change non-inclusive industry standard names as appropriate.
- If a communication protocol has a pattern that doesn't map to
requester/completerit is ok to use more accurate names (for example, leader and follower). This guidance is intended to standardize the common case.
Flow Control
Standardize flow control using Ready/Valid semantics wherever possible.
Flow control is often used to facilitate back pressure as data moves through a pipeline.
The recommended flow control semantic is the Ready/Valid semantic. Experience shows that connecting two modules with inconsistent flow control semantics is a common source of hard-to-catch errors.
If an interface is defined without flow control, a comment must explain why flow control and back pressure are unnecessary in that particular case.
Struct Usage
Do not group flow control and data signals in the same struct. All data signals in a struct must share the same flow control.
When grouping data signals, flow control signals must not be included in the same structure as the data they are qualifying. Additionally, all data signals in the structure must have the same flow control behavior and be qualified by the same external flow control signals. Otherwise, use of the structure could easily lead to data propagation bugs.
typedef struct packed {
logic rw;
foo_address_t addr;
foo_data_t data;
} foo_t;
foo_t foo;
logic foo_valid; // Good: this is the flow control signal for foo.
bar_interrupt_t bar_intr; // Good: this has different flow control than foo.
typedef struct packed {
logic rw;
foo_address_t addr;
foo_data_t data;
logic valid; // Bad: this is the flow control signal.
bar_interrupt_t intr; // Bad: this data signal is not qualified by valid.
} foo_t;
foo_t foo;
Ready/Valid
Ready/Valid semantics express the recommended method of flow control.
Ready/Valid semantics mimic the requirements of the AXI handshake process, as defined in the AMBA AXI Protocol Specification.
With Ready/Valid semantics, the sender uses the Valid signal to advertise that a data transfer is available. The receiver generates the Ready signal to indicate that it can accept a data transfer. The transfer only occurs on any cycle where Ready and Valid are both asserted.
The Ready signal must be identified with the "_ready" suffix. The Valid signal must be identified with the "_valid" suffix. The other signals in the data transfer must share a prefix with the Ready and Valid signals (for example: "foo_ready", "foo_valid", "foo_data1", "foo_data2", etc.).
Once Valid is asserted it must remain asserted until the data transfer occurs.
For interfaces using _valid_next, once _valid_next is asserted in a given
cycle, the data transfer is scheduled for the next clock cycle; if _ready is
not asserted on that next cycle, the data transfer remains pending and the data
must remain held constant until the transfer completes.
Once Valid is asserted, the data bus must be held constant until the data transfer occurs.
Once Ready is asserted, the receiver is permitted to deassert Ready at any time.
On the sender interface, there may be no combinational path between input and
output signals. The receiver, however, is permitted to produce ready
combinationally from inputs such as valid or data.
NOTE: This is at odds with the AXI4 specification, which forbids combinational paths between inputs and outputs.
Some modules might have important reasons to change this protocol. If the assumptions of this protocol are violated, use a different signal name convention to clearly indicate that the standard Ready/Valid semantics are not in force. For example, if your protocol permits a "skid" of an extra data transfer after Valid gets de-asserted, consider documenting this in the signal name (for example, "foo_valid_for_two").
wr_en or rd_en are acceptable proxies for Valid, when appropriate.
Credit/Valid
Credit/Valid flow control decouples data transmission from flow-control feedback using a credit counter, making it suitable for long-distance, wide, or high-frequency pipelines where single-cycle Ready/Valid backpressure is impractical.
Req/Ack
Req/Ack semantics express an alternate method of flow control.
Req/Ack semantics are functionally equivalent to Ready/valid semantics. Ready/Valid is the recommended style for most new code. Credit/Valid is recommended to be considered for very long, very wide, or very high-speed buses.
A request and acknowledge interface is always defined by two signals:
- foo_req: the request signal, which is asserted to request an event, and is held active until the posedge of a clock cycle in which foo_ack is asserted.
- foo_ack: the acknowledge signal, which is asserted on the cycle in which the request is acknowledged. It is possible (likely, even) for foo_ack to be asserted within the same cycle that foo_req is asserted.
Additional signals associated with this interface (in other words, information that accompanies the request) must share a signal name prefix with the request and acknowledge pair. These signals must be kept valid the entire time that request is asserted.
If foo_req is not asserted, no assumptions can be made about foo_ack (the receiver might choose to tie foo_ack to 1'b1 if it can never stall) -- the transmitter must only care about the state of foo_ack on cycles where foo_req is valid.
For example:
output logic [63:0] write_to_fifo_data,
output logic write_to_fifo_req,
input logic write_to_fifo_ack
Synchronization
Users must avoid implementing their own CDC synchronizers and instead rely upon a proven library implementation.
Any module that contains CDC synchronizers must separate and encapsulate logic for each clock domain in separate sub-modules.
It is not acceptable to have combinational or sequential code written in the same level of hierarchy that includes a CDC instantiation. If there are multiple clock domains in a module, then all logic that belongs to each domain must be separated from the others and encapsulated within one or more sub-modules.
Exception: CDC implementations (for example, from a design library) are not subject to this rule. By design, they are required to mix logic from multiple clock domains within the same module (at some level of hierarchy).
Rationale: Mixing functional logic from multiple clock domains in the same module results in poor readability of combinational and sequential logic (for example, extra clock name prefix on signals in logical expressions) and can increase the risk of accidental CDC bugs (for example, crossing clock domains without a synchronizer). This style rule enforces structural separation of logic that belongs to different clock domains.
module module_foo_bar (
...
);
// Good: Using CDC synchronizer lib module.
cdc cdc_foo_bar (.foo_clk, .bar_clk, ...);
// Good: "foo" clock domain submodules; no unwrapped logic in a module that
// instantiates a CDC.
submodule_0 submodule_0_foo (.clk(foo_clk), ...);
submodule_1 submodule_1_foo (.clk(foo_clk), ...);
// Good: "bar" clock domain submodules; no unwrapped logic.
submodule_2 submodule_2_bar (.clk(bar_clk), ...);
// Good: No inferred logic here, just a structural assignment, and both
// signals are clearly in the same clock domain.
assign bar_x = bar_y;
endmodule
module module_foo_bar (
...
);
cdc cdc_foo_bar (.foo_clk, .bar_clk, ...);
// Bad: unwrapped combinational logic in a module that instantiates a CDC.
always_comb begin
// ... some foo_* combinational logic here ...
end
// Bad: unwrapped sequential logic in a module that instantiates a CDC.
always @(posedge bar_clk) begin
// ... some bar_* sequential logic here ...
end
// Bad: assign statement infers combinational logic in a module that
// instantiates a CDC.
assign bar_x = ^bar_y;
endmodule
Top-level Implementation
A set of guidelines that emphasize maintainability of the top-level implementation.
The top-level implementation is a pain point for many designs. The following recommendations focus on keeping top-level implementations simple and maintainable.
The following practices are recommended:
- No logic or registers in the top-level instance. All logic is implemented within sub-modules. The top level instance contains nothing but instances of sub-modules.
- Simple mapping of ports to signals. The goal is to make the mapping of
ports onto signals as simple (and automatable) as possible. The best
practice is to have top-level signal names that are identical to port names.
When this is not practical, the following simple and consistent mapping
schemes represent the next best thing:
- a consistent renaming that maps all ports with one prefix to signals with another prefix. For example, a set of "m_axi_*" ports might be mapped consistently to a set of "s05_axi_*" wires.
- mapping a port to a slice of a larger bus. For example, an
access_requestNport might be connected to anaccess_request[5]wire. - if interfaces are being used, it may be necessary to create a consistent instance name that is distinct from the interface name or modport name.
- Consider automating the production of your top-level block, using automatic tools such as verilogpp .
Registered Inputs and Outputs
Strongly prefer to place module-boundary registers on outputs.
When placing module-boundary registers, it is strongly recommended that the registers be placed on the module outputs, rather than the module inputs.
If the outputs of a non-trivial module aren't registered, this must be clearly documented in the header comment of the module.
Top-level modules must have registered inputs and outputs for maximum top-level routing flexibility.
It is assumed that inputs to all modules come from the output flops of a previous module, so it is generally not required to register the inputs of a module, with the following two exceptions:
- Inputs from an external pin must be registered (retimed) before use.
- Top-level blocks may register both inputs and outputs, to maximize routing flexibility.
Verification
Summary
This section documents guidelines for writing Design Verification (DV) code in Verilog.
Assertions
Assumptions and invariants in logic must be formally expressed as runtime assertions.
Assertions must represent behaviors that ALWAYS happen (and not be used to flag error conditions that might be exercised in simulation).
Assertions must be placed within RTL files so that assertions and their related logic are close together.
Assertions must be placed in a design according to these guidelines:
- All input ports to a block (except clocks, resets, test, debug or unconstrained data ports) must be included in at least one assertion that describes the protocol.
- All output ports of a block (except test and debug) must be included in at least one assertion that describes the functionality of the block and the protocol of the signals.
- All synthesized functional registers in a block must be within the cone of influence of an assertion. Further, they must be within three static levels of logic from the assertion.
- Important end-to-end behaviors of a block must be described with an assertion.
- Any minimum levels of performance for a block must be described with an assertion. Some performance measuring debug logic may be needed to supplement the assertions.
Use standardized assertion macros whenever possible.
Unit Tests and Designer Tests
Thorough unit tests are required for shared libraries and reusable IP design blocks. All other design blocks require designer tests.
Naming Unit Tests
Unit test modules must end with the string "_test". For example,
dma_controller_test.
Minimum Testing Requirements
If the RTL is a reusable IP, every feature must be exhaustively tested.
Otherwise:
- The design block must elaborate and pass lint checks.
- The design block must come out of reset with no X's on the outputs.
- At least one transaction must be sent through the block and observed correctly.
The second category is also referred to as "designer tests".
Constraints
Use lower_snake_case when naming constraints with an
_c suffix.
constraint legal_pkt_size_in_bytes_c {
pkt_size_in_bytes inside {[64:1500]};
}
constraint c_My_Constraint { // c_ at the beginning. Not lower_snake_case.
pkt_size_in_bytes inside {[64:1500]};
}
Plusargs
Plusarg Value Assignment
Plusargs must specify a value to be assigned to a variable (for example, use +en_foo=1, not +en_foo). Do not use $test$plusargs.
For example:
bit en_ingress;
bit plusarg_found = $value$plusargs("en_ingress=%d", en_ingress);
if (en_ingress) begin
// Do stuff...
end
bit en_ingress;
if ($test$plusargs("en_ingress")) begin
// Do stuff...
end
Plusarg Naming
The plusarg name must match the variable that stores the passed plusarg, exclusive of any prefix or suffix of either the plusarg or the variable.
For example:
int foo;
int prefix_bar_suffix;
// Any of the following plusarg run-time calls would be acceptable:
// +foo=42
// +FOO=42
// +prefix_foo_suffix=42
// +bar=123
// +prefix_bar_suffix=123
// +PREFIX_BAR_SUFFIX=123
string file_name;
// Plusarg string name and variable names must match. These are not acceptable:
// +filename="foo.txt"
// +file="foo.txt"
Text Formatting
Displayed Number Format
Printed numbers must be prefixed with their radix as shown in the following examples.
When formatting text representations of numbers for log files, make it clear what data you are including.
Make the base of a printed number clear. Print decimal numbers without the radix.
Binary: 0b or 'b
Hexadecimal: 0x or 'h
Decode individual fields of large structures individually, instead of expecting the user to manually decode raw values.
$display("0x%0x", some_hex_value);
$display("0b%0b", some_binary_value);
$display("'h%0h", some_hex_value);
$display("'b%0b", some_binary_value);
$display("%0d", some_decimal_value);
$display("%0x", some_hex_value);
$display("%0b", some_binary_value);
$display("0d%0d", some_decimal_value);
$display("'d%0d", some_decimal_value);
String Formatting
Use $sformatf() for string type formatting. Avoid using
$display() to format string types.
The SystemVerilog-2017 standard is vague on how string type formatting must be
handled by $display(). Simulator implementation of this functionality varies.
$sformatf() has a more consistent interpretation and implementation.
string str_type = "Hello %s";
// Good: String type formatting using $sformatf()
string str_type_sformat = $sformatf(str_type, "world!");
$display(str_type_sformat);
// Good: Calling $sformatf() on the same line as $display()
$display($sformatf(str_type, "world!"));
// String literal formatting with $display() is allowed
$display("Hello %s", "world!");
string str_type = "Hello %s";
// Bad: String type formatting using $display()
$display(str_type, "world!");
Line Continuation of String Literals
Do not use a '' (backslash) character to continue a long string literal to the
next line, use {} string concatenation operator instead.
$display({"a really long ",
"long long quoted string"});
$display("a really long \
long long quoted string"); // Bad: continuation of a string literal
Loop operators
Prefer foreach loops.
Where possible, prefer foreach loops over equivalent loop operators. They are concise and less prone to subtle errors.
foreach (data[byte_i]) begin
data[byte_i] = 0;
end
// Compact form for multidimensional arrays.
foreach (data[burst_i, byte_i]) begin
data[burst_i][byte_i] = 0;
end
// Less readable and note the typo below, must be '<' not '<='.
for (int byte_i = 0; byte_i <= data.size(); byte_i++) begin
data[byte_i] = 0;
end
Code Within Asserts
Do not assert on calls to randomize. More broadly, do not place any expression
with side effects into an assert statement.
SystemVerilog has a couple ways to disable all assertions such as $assertoff
or $assertkill. When an assertion is disabled, future assertion attempts will
not evaluate the assertion expression, as per the LRM (not until the assertion
is re-enabled via $asserton or one of its alternatives). This is particularly
bad when randomize is used within the assertion expression, because disabled
assertions result in unrandomized objects.
ret = item.randomize() with {<constraints>};
assert(ret);
assert(randomize(<some variables>) with {<constraints>});
assert(item.randomize() with {<constraints>});
assert(function_with_side_effects());
Wait and Fork
Always put wait fork or disable fork constructs inside of an isolation fork ... join block, or the environment can end up waiting on other, earlier forks
it was not intending to wait for.
fork begin : isolation_fork
fork
begin
fork1();
end
begin
fork2();
end
join_any
disable fork;
end : isolation_fork
join
Void Casts
Do not void cast calls that have useful or important return values. This
includes calls to uvm_config_db::get, uvm_hdl_read, randomize, etc.
For example, do not do the following:
void'(randomize(<some variables>) with {<constraints>});
void'(item.randomize() with {<constraints>});
void'(uvm_config_db#(<object>)::get(<parameters>));
void'(uvm_hdl_read($sformatf("%sglink_gsw_link_down", glink_path), rd_lnk_dwn));
Instead, check the return value of calls to randomize and similar calls in
order to handle the potential error.
if (!item.randomize() with {<constraints>}) begin
<issue error>
end
if (!uvm_config_db#(<object>)::get(<parameters>)) begin
<issue error>
end
Associative arrays
Do not use wildcard indexed [*] associative arrays.
Associative arrays with wildcard index are hard to use because the item type could be anything. Always specify a particular index type for associative arrays:
bit channel_enable[int];
register_t registers[string];
int object_count[index_type];
int object_count[*]; // Bad: wildcard indexed array
Bind Statements
bind statements may be used in verification. This is the preferred approach
for using assertion based monitors.
".*" is allowed for making implicit port declarations when binding a module whose ports are all inputs.
Program block
Do not use program and endprogram blocks.
It provides the false sense of security. Use module and clocking blocks to
avoid DUT race conditions instead.
Simulator-specific code
All simulator-specific code must be wrapped inside preprocessor guards.
The following macro names are always defined by each simulator. These names must be used to wrap all simulator-specific code, so the code can be compiled for every simulator.
VCS- for VCSINCA- for Incisive/NCsim/IrunXCELIUM- for Xcelium/xrunDSIM- for DSim
`ifdef VCS
$stack();
`endif
Forbidden System Tasks and Functions
Do not use the following functions.
-
$psprintf:$psprintfis supported by a number of tools because it was legal in older versions of the language. However, it is not in the language reference manual. Replace with SystemVerilog's$sformatf. -
$randomand$dist_*: these functions must not be used as they are not part of the SystemVerilog random stability model and may break simulation reproducibility. Use $urandom or randomize() instead. -
$srandom:$srandommust not be used as it is not part of the SystemVerilog standard. Useprocess::self().srandom()instead. -
$display,$displayb/o/h,$write,$writeb/o/h: Do not use$displayor similar "display" system tasks to display messages in UVM environments, instead use standard UVM logging macros for a consistent and controllable way of message logging. -
$info,$fatal,$error,$warning: Do not use "severity" system tasks in UVM environments, instead use corresponding UVM logging macros.
UVM
Phase Methods
Always call super.<phase_name>(phase) when overriding
UVM phase methods in derived classes.
When overriding UVM phase methods (such as build_phase, connect_phase,
run_phase, etc.) in a class derived from a UVM component, you must call the
parent class's implementation using super.<phase_name>(phase). This ensures
that any setup or configuration performed by the parent class is executed.
Exceptions are allowed if the base class is a direct UVM library class (like
uvm_component, uvm_test, uvm_env, etc.) where the default implementation
is known to be empty, BUT it is still recommended to call it for consistency and
future-proofing. For user-defined base classes, it is mandatory.
class my_test extends base_test;
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
// ... custom build logic ...
endfunction
endclass
class my_test extends base_test;
virtual function void build_phase(uvm_phase phase);
// Missing super.build_phase(phase)
// ... custom build logic ...
endfunction
endclass
UVM Logging
Only use `uvm_info, `uvm_error, and `uvm_fatal UVM messaging macros for message logging in UVM
environments.
Only use the following UVM macros to display log messages in UVM environments:
`uvm_info(ID, message, verbosity): For informational messages.`uvm_error(ID, message): For messages indicating a problem that needs to be addressed.`uvm_fatal(ID, message): For messages indicating a critical problem that stops the simulation.
Do not call underlying UVM functions like uvm_report_*. Macros provide better
control over verbosity, timestamping, and component identification.
Do not use the `uvm_warning macro. Categorize messages as informational
(info) or as conveying an issue that needs to be addressed (error or fatal).
Although forbidden, it's recommended to setup your infrastructure to treat `uvm_warning as errors.
`uvm_error(get_full_name(), $sformatf("Unsupported burst type : %s", burst_type.name()))
$display("Debug: Byte enable=%d", byte_en);
uvm_report_info("ID", "Debug message", UVM_LOW)
`uvm_warning(get_full_name(), "Incorrect condition, maybe")
UVM Macro Semicolons
Do not append a semicolon to UVM macro calls.
UVM macros (including messaging macros like `uvm_info and sequence action
macros like `uvm_do) must not be followed by a semicolon. Appending a
semicolon can result in an empty statement, which can cause syntax errors in
contexts like if statements without begin / end blocks.
// Good: UVM macros must not be followed by a semicolon.
`uvm_info("ID", "Message", UVM_LOW)
`uvm_do(my_seq)
// Bad: Do not append a trailing semicolon to UVM macro calls.
`uvm_info("ID", "Message", UVM_LOW);
`uvm_do(my_seq);
Sequences and Sequence_items
Call the randomize() or randomize() with {} function of the sequence before
executing the sequence.
Creating Objects and Components
Use the create() API to create UVM objects and
components.
Use the type_id::create() API to instantiate all UVM objects and components.
This is standard UVM programming practice and is necessary to take advantage of
UVM factory features, such as type and instance overrides.
Exceptions are permitted for objects or components that are not intended for
reuse (such as TLM ports, exports, and analysis ports) or when significant
performance gains can be achieved. In such exceptional cases, new() may be
used directly.
When creating classes derived from uvm_component, pass this as the parent
argument so that the component hierarchy is built properly.
env_h = tb_block_env::type_id::create("env_h", this);
ip_pkt = tx_seq_item::type_id::create("ip_pkt");
// Direct calls to new() bypass the UVM factory and prevent overrides.
env_h = new("env_h", this);
ip_pkt = new("ip_pkt");
UVM Naming
When creating a UVM object or component using
type_id::create(), the string name argument must match the name of the
variable to which it is assigned.
For array assignments, the string name must include the index in square brackets
(for example, [<index>]).
This ensures consistency and helps with debugging, as the hierarchical path in UVM will match the variable names in the code.
my_agent = my_agent_type::type_id::create("my_agent", this);
// For arrays, include the index in the string name
my_agents[i] = my_agent_type::type_id::create($sformatf("my_agents[%0d]", i), this);
// String name does not match variable name
my_agent = my_agent_type::type_id::create("different_name", this);
// For arrays, string name is missing the index in brackets
my_agents[i] = my_agent_type::type_id::create($sformatf("my_agents_%0d", i), this);
Class Constructor Default Values
For classes derived from uvm_object, the constructor
parameter name must default to "". For classes derived from uvm_component,
the constructor must not specify default values.
When defining a constructor for a UVM class:
- Classes extending
uvm_objectmust define the constructor parameter asstring name = ""(empty string) and pass it tosuper.new(name). Do not provide non-empty default object names. - Classes extending
uvm_componentmust define the constructor parameters as(string name, uvm_component parent)without default argument values.
// uvm_object derived class constructor
function new (string name = "");
// uvm_component derived class constructor
function new (string name, uvm_component parent);
// Bad: name default parameter must be ""
function new (string name = "my_object");
// Bad: default parameter value provided for parent or name
function new (string name = "my_component", uvm_component parent = this);
Test Execution
Do not pass arguments to run_test().
Always call run_test() without arguments. Specify the test name on the command
line using +UVM_TESTNAME=test_name instead of hardcoding it in the code.
initial begin
run_test();
end
initial begin
run_test("my_test_name");
end