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:
This style guide defines style for SystemVerilog compliant code. Additionally, this style guide defines style for both synthesizable and testbench code.
Unless otherwise noted, the following terminology conventions apply to this style guide:
The following Style Linter tools can be used to automatically check the code for violations to the mandatory requirements of this Style Guide:
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.
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.
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.
Prefer SystemVerilog-2017.
All RTL and tests must be developed in SystemVerilog, following the SystemVerilog-2017 standard.
Google uses Verilog for logic design.
Precedent has already been established that Verilog is the choice for logic design at Google.
Common RTL that is intended to be used across multiple projects is referred to as reusable IP.
This section covers file conventions, mostly how files are to be named.
Use .sv and .svh file extensions for SystemVerilog
files.
File extensions have the following meanings:
.sv indicates a SystemVerilog file defining a module or package..svh indicates a SystemVerilog global macro file or
include file..v indicates a Verilog-2001 file defining a module or package..vh indicates a Verilog-2001 global macro file or
include file.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.
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.sv is the top-level module of the design.[design]_pkg.sv defines a package named [design]_pkg containing
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).
The overall structure of a SystemVerilog file must start with the header, followed by the declaration and then the implementation.
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.
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 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.
The Aesthetics and Formatting section addresses purely aesthetic aspects of style, primarily:
// Comment style comments.Use only ASCII characters with UNIX-style line endings(“\n”).
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.
Wrap the code at 100 characters per line.
The maximum line length for style-compliant Verilog code is 100 characters per line.
Exceptions:
Line-wrapping contains additional guidelines on how to wrap long lines.
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.
Delete trailing whitespace at the end of lines.
Delete redundant semicolons.
Semicolons may represent null statements or items in SystemVerilog. One is
enough; consecutive semicolons (;;) are likely typos.
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 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.)
Always add an additional level of indentation to the enclosed sections of all paired keywords. Examples of SystemVerilog keyword pairs:
begin and endmodule and endmodulepackage and endpackageclass and endclassfunction and endfunctionIndent all members of struct, union, and enum declarations by two spaces.
typedef struct packed {
logic bar;
} foo_t;
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.
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.
Include whitespace before and after SystemVerilog keywords.
Keywords must be delimited by whitespace, even if not syntactically required.
Do not include a whitespace:
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
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;
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));
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;
Add a space around packed dimensions.
Do not add a space:
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 [$];
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
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.
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 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 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 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 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)
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);
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.
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
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;
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.
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.
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.
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.
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
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;
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_bus port and one
with a completer_bus port might be connected by a foo_bar_bus signal.
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).
These signals must be named clk, rst, and arst.
Clocks and resets must follow the following naming rules:
clk.rst and is active high.arst and 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
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.
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.
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:
bram_ prefix.dram_clk rather than clk must share a
dram_ 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.
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;
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
);
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.
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:
_n suffix indicates an active-low signal._n and _p suffixes indicate a
differential pair of signals._ready, _valid, and _valid_next suffixes are expected to follow
standardized Ready/Valid semantics._req and _ack suffixes are expected to follow standardized
Request/Acknowledge semantics._next and _le suffixes indicate
the data and load-enable inputs for a clocked register._d suffix indicates a
clock-cycle delayed copy of a signal._sMpN and _uMpN suffixes (where M and N are integers) indicate a
fixed-point signal.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_address is clearer than write_address.
max_mibytes_per_sec is clearer than max_rate.
packet_size_beats or packet_size_kibytes is clearer than packet_size.
cycle_counter or transfer_counter is clearer than counter.
For a 64-bit signal foo_byte_address, it is assumed to be defined as
logic [63:0] foo_byte_address. A corresponding signal slice in units of 4B
address could then be defined as either logic [61:0] foo_4byte_address or
logic [63:2] foo_63_2_byte_address.
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;
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.
Use the _p and _n suffixes to indicate a differential
pair.
For example, in_p and in_n comprise a differential pair set.
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,
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.
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.
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});
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:
_bytes, _valid, _ready, _req, _ack)._sMpN, _uMpN)._n)._p, _n)._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,
This section provides guidance on the use of verilog preprocessor directives and pragmas.
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 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
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.
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))
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:
`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]])
The X_OF_TYPE macro presents a standard way to typecast 'X.
`define X_OF_TYPE(__type) \
(__type'({$bits(__type){1'bx}}))
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
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 DirectivesDo 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 SIMULATIONMay 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_ONMay 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_ONMay 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-patternsDo 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 ExceptionsThere 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” `ifdef macros).
Prefix your macro names with a unique project namespace identifier. For
example, FOOBAR__DEBUG is preferable to DEBUG to avoid namespace
collisions between projects.
Choose macro names that connote positive enablement. For example, prefer
FOOBAR__PERF_COUNTERS to FOOBAR__DISABLE_PERF_COUNTERS.
Files intended for `include
are referred to as “include files”.
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
);
endmodule
module foo ();
`include "common_assertions.svh" // Bad: wrap into a module instead
endmodule
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_
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
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:
Each import statement must appear on its own dedicated line of code.
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;
...
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.
Use module parameters to create customizable modules.
Examples of when to use parameterized modules:
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.
UpperCamelCase is the recommended style for module parameter names, except:
ALL_CAPS may be permitted for historic reasons, andlower_snake_case_t is required for parameters that are types, for
consistency with naming of other types.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...
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.
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)
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 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.
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)
);
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.
Do not instantiate recursively.
Modules may not instantiate themselves recursively.
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}.
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
There is no special guidance for declaring packages.
Package names must use lower_snake_case and contain only lowercase
alphanumeric characters and underscores. A package name must end with the suffix
_pkg.
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 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,
...
);
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:
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.
logic: logic foo;logic [7:0] byte;byte_t arr[0:N-1];Additionally, the semantics of specific naming conventions are elaborated on in the separate Naming Conventions section.
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.
logic for synthesisUse 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.
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.
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,
...
);
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.
When declaring a constant:
parameter.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.
UpperCamelCase is the recommended style for immutable constants. The
exceptions are:
kCamelCase to
declare enumeration constants.Explicitly declare the type for constants. Use type “int” (signed integer) for all simple numeric parameters (for example, quantities).
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 listsGenerally, 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
Use packed arrays and structs for synthesizable signals. Unpacked arrays are appropriate for behavioral models of SRAMs and DV code.
When declaring vectors and arrays, the msb and lsb must be non-negative.
logic [14:0] foo;
logic [13:-1] foo;
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 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:
[size] declaration is preferred for unpacked arrays and is
equivalent to [0:size-1].foreach construct, 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.
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
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;
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
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;
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.
assign foo = check_foo(bar);).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:
Do not infer a latch inside a function, as this may cause a simulation-synthesis mismatch.
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.
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);
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 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);
always_ff vs alwaysThere is no preference between always_ff and always for clocked processes,
other than consistency within a project.
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.
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 (=).
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:
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.
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.
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.
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).
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:
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:
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
always_comb or assignAvoid sensitivity lists.
Use either always_comb blocks or assign statements for combinational logic.
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.
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.
if statementsNote 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
See Case Statements for guidance and examples.
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;
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.
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.
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;
See Case Statements for guidance on how to avoid introducing don’t-care logic in case statements.
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)
Do not use three-state logic (Z state) to accomplish on-chip logic such as
muxing.
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.
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 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 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
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:
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.
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
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
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
Be careful about signal and expression widths.
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:
foo =
bar + 1) to avoid contrivances such as ,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.'0 or '1 literals to automatically create
correctly sized (context-determined) zero or “all ones”.bit, byte,
shortint, int, integer, and longint) 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
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
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
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;
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.
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.
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:
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};
The key ideas in this section include:
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 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.
requester/completer it is ok to use more accurate names (for example,
leader and follower). This guidance is intended to standardize the common
case.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.
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 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 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 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:
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
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
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:
access_requestN port might be connected to an access_request[5]
wire.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:
This section documents guidelines for writing Design Verification (DV) code in Verilog.
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:
Use standardized assertion macros whenever possible.
Thorough unit tests are required for shared libraries and reusable IP design blocks. All other design blocks require designer tests.
Unit test modules must end with the string “_test”. For example,
dma_controller_test.
If the RTL is a reusable IP, every feature must be exhaustively tested.
Otherwise:
The second category is also referred to as “designer tests”.
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 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
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"
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);
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!");
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
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
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());
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
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
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 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.
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.
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
Do not use the following functions.
$psprintf: $psprintf is 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.
$random and $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: $srandom must not be used as it is not part of the
SystemVerilog standard. Use process::self().srandom() instead.
$display, $displayb/o/h, $write, $writeb/o/h: Do not use $display
or 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.
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
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")
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);
Call the randomize() or randomize() with {} function of the sequence before
executing the sequence.
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");
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);
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:
uvm_object must define the constructor parameter as
string name = "" (empty string) and pass it to super.new(name). Do not
provide non-empty default object names.uvm_component must 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);
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