{{% lowrisc-doc-hdr Verilog Coding Style Guide }}
Verilog is the main logic design language for lowRISC Comportable IP.
Verilog and SystemVerilog (often generically referred to as just βVerilogβ in this document) 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 the Comportable style for Verilog. The goals are to:
This style guide defines style for both Verilog-2001 and SystemVerilog compliant code. Additionally, this style guide defines style for both synthesizable and test bench code.
See the Appendix for a condensed tabular representation of this style guide.
{{% toc 3 }}
Unless otherwise noted, the following terminology conventions apply to this style guide:
Where appropriate, format code consistent with https://google.github.io/styleguide/cppguide.html
Verilog is a C-like language, and where appropriate, we default to being consistent with Google's C++ Style Guide.
In particular, we inherit these specific formatting guidelines:
if
and the parenthesis in conditional expressions.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 lint waiver pragma where appropriate.
Prefer SystemVerilog-2012.
All RTL and tests should be developed in SystemVerilog, following the SystemVerilog-2012 standard, except for prohibited features.
This section addresses primarily aesthetic aspects of style: line length, indentation, spacing, etc.
Use the .sv
extension for SystemVerilog files (or .svh
for files that are included via the preprocessor).
File extensions have the following meanings:
.sv
indicates a SystemVerilog file defining a module or package..svh
indicates a SystemVerilog header file intended to be included in another file using a preprocessor `include
directive..v
indicates a Verilog-2001 file defining a module or package..vh
indicates a Verilog-2001 header file.Only .sv
and .v
files are intended to be compilation units. .svh
and .vh
files may only be `include
-ed into other files.
With exceptions of netlist files, each .sv or .v file should contain only one module, and the name should be associated. For instance, file foo.sv
should contain only the module foo
.
Use only ASCII characters with UNIX-style line endings("\n"
).
All lines on non-empty files must end with a newline ("\n"
).
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.
Use begin
and end
unless the whole statement fits on a single line.
If a statement wraps at a block boundary, it must use begin
and end.
Only if a whole semicolon-terminated statement fits on a single line can begin
and end
be omitted.
π
// Wrapped procedural block requires begin and end. always_ff @(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_ff @(posedge clk) q <= d;
π
// Incorrect because a wrapped statement must have begin and end. always_ff @(posedge clk) q <= d;
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
should be on a new line.
π
// "end else begin" are on the same line. if (condition) begin foo = bar; end else begin foo = bum; end
π
// begin/end are omitted because each semicolon-terminated statement fits on // a single line. 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 above 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) StIdle: begin next_state = StA; end StA: begin next_state = StB; end StB: begin next_state = StIdle; foo = bar; end default: begin next_state = StIdle; end endcase
π
// Case items that fit on a single line may omit begin and end. unique case (state) StIdle: next_state = StA; StA: next_state = StB; StB: begin next_state = StIdle; foo = bar; end default: next_state = StIdle; endcase
π
unique case (state) StIdle: // These lines are incorrect because we should not wrap next_state = StA; // case items at a block boundary without using begin StA: // and end. Case items should fit on a single line, or next_state = StB; // else the procedural block must have begin and end. StB: begin foo = bar; next_state = StIdle; end default: begin next_state = StIdle; end endcase
Indentation is two spaces per level.
Use spaces for indentation. Do not use tabs. You should set 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 / end
, module / endmodule
, package / endpackage
, class / endclass
, function / endfunction
.
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};
Or, if it improves readability, align the continued part of the expression with a grouping open parenthesis or brace, like this:
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};
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; `ifdef FOO // good: branching directive left-aligned `include "foo.sv"; // normal indentation for non-branching directives parameter bit A = 1; // normal indentation for the regular code `ifdef BAR // good: branching directive left-aligned parameter bit A = 2; `else parameter bit A = 3; `endif `endif endpackage : foo
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.
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 cause related things to align is encouraged.
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.
Use spaces, not tabs.
For example:
logic [7:0] my_interface_data; logic [15:0] my_interface_address; logic my_interface_enable;
Include whitespace on both sides of all binary operators.
Use spaces around binary operators. Add sufficient whitespace to aid readability.
For example:
π
assign a = ((addr & mask) == My_addr) ? b[1] : ~b[0]; // good
is better than
π
assign a=((addr&mask)==My_addr)?b[1]:~b[0]; // bad
Exception: when declaring a bit vector, it is acceptable to use the compact notation. For example:
π
wire [WIDTH-1:0] foo; // this is acceptable wire [WIDTH - 1 : 0] foo; // fine also, but not necessary
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) == `MY_ADDRESS) ? matches_value : doesnt_match_value;
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(4), .DEPTH(2)) my_fifo_nibble ... 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 ... 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
When labeling code blocks, add one space before and after the colon.
For example:
π
begin : foo end : foo
π
end:bar // There must be a space before and after the colon. endmodule: foobar // There must be a 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) StInit: $display("Shall we begin"); StError: $display("Oh boy this is Bad"); default: begin my_state = StInit; interrupt = 1; end endcase
π
unique case (1'b1) (my_state == StError) : interrupt = 1; // Excess whitespace before colon default:begin end // Missing space after colon endcase
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 & b, clk, rst)
π
`uvm_error (ID, "you fail") // There must not be a space before "(" `ASSERT (name, a & b, clk, rst)
It is mandatory 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 should not exceed the maximum line length.
`define REALLY_LONG_MACRO(arg1, arg2, arg3) \
do_something(arg1); \
do_something_else(arg2); \
final_action(arg3);
Include whitespace before and after SystemVerilog keywords.
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_ff @(posedge clk) begin end
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.
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 preferred. C style comments (/* bar */
) can also be used.
A comment on its own line describes the code that follows. A comment on a line with code describes that line of code.
For example:
// This comment describes the following module. module foo; ... endmodule : foo localparam bit ValBaz = 1; // This comment describes the item to the left.
Signals must be declared before they are used. This means that implicit net declarations must not be used.
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.
A template that demonstrates many of the items is given below.
Template:
// Copyright lowRISC contributors. // Licensed under the Apache License, Version 2.0, see LICENSE for details. // SPDX-License-Identifier: Apache-2.0 // // One line description of the module module my_module #( parameter Width = 80, parameter Height = 24 ) ( input clk_i, input rst_ni, input req_valid_i, input [Width-1:0] req_data_i, output req_ready_o, ... ); logic [Width-1:0] req_data_masked; submodule u_submodule ( .clk_i, .rst_ni, .req_valid_i, .req_data_i (req_data_masked), .req_ready_o, ... ); always_comb begin req_data_masked = req_data_i; case (fsm_state_q) ST_IDLE: begin req_data_masked = req_data_i & MASK_IDLE; ... end ... endmodule
Construct | Style |
---|---|
Declarations (module, class, package, interface) | lower_snake_case |
Instance names | lower_snake_case |
Signals (nets and ports) | lower_snake_case |
Variables, functions, tasks | lower_snake_case |
Named code blocks | lower_snake_case |
`define macros | ALL_CAPS |
Tunable parameters for parameterized modules, classes, and interfaces | UpperCamelCase |
Constants | ALL_CAPS or UpperCamelCase |
Enumeration types | lower_snake_case_e |
Other typedef types | lower_snake_case_t |
Enumerated value names | UpperCamelCase |
Declare global constants using parameters in the project package file.
In this context, constants are distinct from tuneable parameters for objects such as parameterized modules, classes, etc.
Explicitly declare the type for constants.
When declaring a constant:
parameter
.localparam
.The preferred method of defining constants is to declare a package
and declare all constants as a parameter
within that package. If the constants are to be used in only one file, it is acceptable to keep them defined within that file rather than a separate package.
Define project-wide constants in the project's main package.
Other packages may also be declared with their own parameter
constants to facilitate the creation of IP that may be re-used across many projects.
The preferred naming convention for all immutable constants is to use ALL_CAPS
, but there are times when the use of UpperCamelCase
might be considered more natural.
Constant Type | Style Preference | Conversation |
---|---|---|
`define | ALL_CAPS | Truly constant |
module parameter | UpperCamelCase | truly modifiable by instantiation, not constant |
derived localparam | UpperCamelCase | while not modified directly, still tracks module parameter |
tuneable localparam | UpperCamelCase | while not expected to change upon final RTL version, is used by designer to explore the design space conveniently |
true localparam constant | ALL_CAPS | Example localparam OP_JALR = 8'hA0; |
enum member true constant | ALL_CAPS | Example typedef enum ... { OP_JALR = 8'hA0; |
enum set member | ALL_CAPS or UpperCamelCase | Example typedef enum ... { ST_IDLE, ST_FRAME_START, ST_DYN_INSTR_READ ... , typedef enum ... { StIdle, StFrameStart, StDynInstrRead... . A collection of arbitrary values, could be either convention. |
The units for a constant should be described in the symbol name, unless the constant is unitless or the units are βbits.β For example, FooLengthBytes
.
Example:
π
// package-scope package my_pkg; parameter int unsigned NUM_CPU_CORES = 64; // reference elsewhere as my_pkg::NUM_CPU_CORES endpackage
Use parameter
to parameterize, and localparam
to declare module-scoped constants.
You can create parameterized modules, classes, and interfaces to facilitate design re-use.
Use the keyword parameter
within the module
declaration of a parameterized module to indicate what parameters the user is expected to tune at instantiation. The preferred naming convention for all parameters is UpperCamelCase
. Some projects may choose to use ALL_CAPS
to differentiate tuneable parameters from constants.
The preference for derived parameters within the module
declaration is to use localparam
, however currently several tools do not accept this legal (since SystemVerilog 2009) construct. For now all derived parameters should use parameter
with a comment // derived parameter
and create a static assertion with the name prefix paramCheck
somewhere within the module. An example is shown below.
// ideal, but currently untenable declaration module modname #( parameter int Depth = 2048, // 8kB default localparam int Aw = $clog2(Depth) // derived parameter ) ( ... ); // current declaration method with assertion module modname #( parameter int Depth = 2048, // 8kB default parameter int Aw = $clog2(Depth) // derived parameter ) ( ... ); `ASSERT_INIT(paramCheckAw, Aw == $clog2(Depth)) // alternate, expanded assertion macro initial begin paramCheckAw: assert (Aw == $clog2(Depth)) \ else $error("Assert failed: [%m] paramCheckAw, (Aw == $clog2(Depth))") end ... endmodule
`define
and defparam
should never be used to parameterize a module.
Use package parameters to transmit global constants through a hierarchy instead of parameters. To declare a constant whose scope is internal to the module, use localparam
instead.
Examples of when to use parameterized modules:
Explicitly declare the type for parameters.
Use the type of the parameter to help constrain the legal range. E.g. int unsigned
for general non-negative integer valuess, bit
for boolean values. Any further restrictions on tuneable parameter values must be documented with assertions.
Tuneable parameter values should always have reasonable defaults.
For additional reading, see New Verilog-2001 Techniques for Creating Parameterized Models.
Macros should be ALL_CAPITALS with underscores.
Macros should be all capitals with underscores.
A global define is a tick-defined macro in a header file that is shared by all source files in a project. To reduce namespace collisions, global defines should be prefixed by the name of a group of related macros, followed by a pair of underscores:
// The following two constants are in the FOO namespace of the // SN chip. `define SN_FOO__ALPHA_BETA 5 `define SN_FOO__GAMMA_OMEGA 6
A local define is a tick-defined macro that should 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 should 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
Suffixes are used in several places to give guidance to intent. The following table lists the suffixes that have special meaning.
Suffix(es) | Arena | Intent |
---|---|---|
_e | typedef | Enumerated types |
_t | typedef | Other typedefs, including signal clusters |
_n | signal name | Active low signal |
_n , _p | signal name | Differential pair, active low and active high |
_d , _q | signal name | Input and output of register |
_q2 ,_q3 , etc | signal name | Pipelined versions of signals; _q is one cycle of latency, _q2 is two cycles, _q3 is three, etc |
_i , _o , _io | signal name | Module inputs, outputs, and bidirectionals |
When multiple suffixes are necessary use the following guidelines:
_
characters (_ni
not _n_i
)_n
will be the first suffix_d
and _q
to module boundaries.Example:
π
module simple ( input clk_i, input rst_ni, // Active low reset // writer interface input [15:0] data_i, input valid_i, output ready_o, // bi-directional bus inout [7:0] driver_io, // Bi directional signal // Differential pair output output lvds_po, // Positive part of the differential signal output lvds_no // Negative part of the differential signal ); logic valid_d, valid_q, valid_q2, valid_q3; assign valid_d = valid_i; // next state assignment always_ff @(posedge clk or negedge rst_ni) begin if (!rst_ni) begin valid_q <= '0; valid_q2 <= '0; valid_q3 <= '0; end else begin valid_q <= valid_d; valid_q2 <= valid_q; valid_q3 <= valid_q2; end end assign ready_o = valid_q3; // three clock cycles delay endmodule // simple
Name enumeration types snake_case_e
. Name enumeration values ALL_CAPS
or UpperCamelCase
.
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 should contain only lower-case alphanumeric characters and underscores. You must suffix enumeration type names with _e
.
Enumeration value names (constants) should typically be ALL_CAPS
, for example, READY_TO_SEND
, to reflect their constant nature, especially for truly unchangeable values like defined opcode assignments. There are times when UpperCamelCase
might be preferred, when the enumerated typeβs assigned value is effectively a donβt care to the designer, like state machine values. See the conversation on constants for a discussion on how to think of this recommendation.
π
typedef enum logic [7:0] { // 8-bit opcodes OP_JALR = 8'hA0, OP_ADDI = 8'h47, OP_LDW = 8'h0B } opcode_e; opcode_e op_val;
π
typedef enum logic [1:0] { // A 2-bit enumerated type ACC_WRITE, ACC_READ, ACC_PAUSE } access_e; // new named type is created access_e req_access, resp_access;
π
typedef enum logic [1:0] { // A 2-bit enumerated type AccWrite, AccRead, AccPause } access_e; // new named type is created access_e req_access, resp_access;
π
enum { // Typedef is missing, storage type is missing. Write, Read } req_access, resp_access; // anonymous enum type
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.
Signal names should 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-2012 standard keywords may never be used as names.
When interoperating with different languages, be mindful not to use keywords from other languages.
Names should describe what a signal's purpose is.
Use whole words. Avoid abbreviations and contractions except in the most common places. Favor descriptive signal names over brevity.
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 should 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.clk_dram
rather than clk
should share a dram_
prefix.Code example:
π
module fifo_controller ( input clk_i, input rst_ni, // writer interface input [15:0] wr_data_i, input wr_valid_i, output wr_ready_o, // reader interface output [15:0] rd_data_o, output rd_valid_o, output [7:0] rd_fullness_o, input rd_ack_i, // memory interface: output [7:0] mem_addr_o, output [15:0] mem_wdata_o, output mem_we_o, input [15:0] mem_rdata_i );
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.
The same signal should have the same name at any level of the hierarchy.
A signal that connects to a port of an instance should have the same name as that port. By proceeding in this manner, signals that are directly connected should 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 master_bus
port and one with a slave_bus
port might be connected by a foo_bar_bus
signal.
In each exceptional case, care should be taken to make the mapping of port names to signal names as unambiguous and consistent as possible.
All clock signals must begin with clk
.
The main system clock for a design must be named clk
. It is acceptable to use clk
to refer to the default clock that the majority of the logic in a module is synchronous with.
If a module contains multiple clocks, the clocks that are not the system clock should be named with a unique identifier, preceded by the clk_
prefix. For example: clk_dram
, clk_axi
, etc. Note that this prefix will be used to identify other signals in that clock domain.
Resets are active-low and asynchronous. The default name is rst_n
.
Chip wide all resets are defined as active low and asynchronous. Thus they are defined as tied to the asynchronous reset input of the associated standard cell registers.
The default name is rst_n
. If they must be distinguished by their clock, the clock name should be included in the reset name like rst_domain_n
.
SystemVerilog allows either of the following syntax styles, but the style guide prefers the former.
// preferred always_ff @(posedge clk or negedge rst_n) begin if (!rst_n) begin q <= 1'b0; end else begin q <= d; end end // legal but not preferred always_ff @(posedge clk, negedge rst_n) begin if (!rst_n) begin q <= 1'b0; end else begin q <= d; end end
Use these SystemVerilog constructs instead of their Verilog-2001 equivalents:
always_comb
is required over always @*
.logic
is preferred over reg
and wire
.parameter
declarations are preferred over `define
globals.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; // Package "bar" must not depend on anything in "foo": parameter int unsigned PageSizeBytes = 16 * bar::Kibi; endpackage
Use the Verilog-2001 full port declaration style, and use the format below.
Use the Verilog-2001 combined port and I/O declaration style. Do not use the Verilog-95 list style. The port declaration in the module statement should fully declare the port name, type, and direction.
The opening parenthesis should be on the same line as the module declaration, and the first port should be declared on the following line.
The closing parenthesis should be on its own line, in column zero.
Indentation for module declaration follows the standard indentation rule of two space indentation.
The clock port(s) must be declared first in the port list, followed by any and all reset inputs.
Example without parameters:
π
module foo ( input clk_i, input rst_ni, input [7:0] d_i, output logic [7:0] q_o );
Example with parameters:
π
module foo #( parameter int unsigned Width = 8, ) ( input clk_i, input rst_ni, input [Width-1:0] d_i, output logic [Width-1:0] q_o );
Do not use Verilog-95 style:
π
// WRONG: module foo(a, b, c d); input wire [2:0] a; output logic b; ...
Use named parameters for all instantiations.
When parameterizing an instance, specify the parameter using the named parameter style. An exception is if there is only one parameter that is obvious such as register width, then the instantiation can be implicit.
Indentation for module instantiation follows the standard indentation rule of two space indentation.
my_module #( .Height(5), .Width(10) ) my_module ( ...etc... my_reg #(16) my_reg0 (.clk_i, .rst_ni, .d_i(data_in), .q_o(data_out));
Do not specify parameters positionally, unless there is only one parameter and the intent of that parameter is obvious, such as the width for a register instance.
Do not use defparam
.
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 i_my_instance ( .clk_i (clk_i), .rst_ni(rst_ni), .d_i (from_here), .q_o (to_there) );
If the port and the connecting signal have the same name, you can use the .port
syntax (without parentheses) to indicate connectivity. For example:
my_module i_my_instance ( .clk_i, .rst_ni, .d_i (from_here), .q_o (to_there) );
All declared ports must be present in the instantiation blocks. Unconnected 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.
Do not instantiate recursively.
Modules may not instantiate themselves recursively.
It is recommended to use symbolicly named constants instead of raw numbers.
Try to give commonly used constants symbolic names rather than repeatedly typing raw numbers.
Local constants should always be declared using localparam
.
Global constants should always be declared in a separate .vh
or .svh
include file.
For SystemVerilog code, global constants should always be declared as package parameters. For Verilog-2001 compatible code, top-level parameters are not supported and `define
macros must be used instead.
Include the units for a constant as a suffix in the constant's symbolic name. 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 unsigned INTERFACE_WIDTH = 64; // Bits localparam int unsigned INTERFACE_WIDTH_BYTES = (INTERFACE_WIDTH + 7) / 8; localparam int unsigned INTERFACE_WIDTH_64B_WORDS = (INTERFACE_WIDTH + 63) / 64; localparam int unsigned IMAGE_WIDTH_PIXELS = 640; localparam int unsigned MEGA = 1000 * 1000; // Unitless localparam int unsigned MEBI = 1024 * 1024; // Unitless localparam int unsigned SYSTEM_CLOCK_HZ = 200 * MEGA;
Be careful about signal widths.
Examples:
π
localparam logic [3:0] bar = 4'd4; assign foo = 8'd2;
π
localparam logic [3:0] bar = 4; assign foo = 2;
Exceptions:
1'b1
(e.g. when incrementing) rather than contrivances such as {{(Bus_width-1){1'b0}},1'b1}
It is recommended to use explicit widths, rather than relying on Verilog's implicit zero-extension and truncation operations, whenever practical.
Examples:
π
my_module i_module ( .thirty_two_bit_input({16'd0, sixteen_bit_word}) );
π
my_module i_module ( // Incorrectly implicitly extends from 16 bit to 32 bit .thirty_two_bit_input(sixteen_bit_word) );
Rather than letting boolean operations and if expressions reduce a multi-bit signal to a single bit, explictly 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); always_comb begin if (a != '0) ... else ... end
π
logic [3:0] a, b; logic out; // 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; // Incorrect use of a multi-bit signal in an if expression always_comb begin if (a) ... else ... end
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.
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 = abc + 4'h1;
Sequential logic must use non-blocking assignments. Combinational blocks must use 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-block assignments, as defined in the Sequential Logic section below.
Purely combinational blocks must exclusively use blocking assigments.
This is one of Cliff Cumming's Golden Rules of Verilog.
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.
The use of latches is discouraged - use flip-flops when possible.
Unless absolutely necessary, use flops/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 some simulators process some of the blocking assignments in an always block as occurring in a separate simulation event as the non-blocking assignment. This process makes some signals jump registers, potentially leading to total protonic reversal. That's bad.
Sequential statements for state assignments should only contain reset values and a next-state to state assignment, use a separate combinational-only block to generate that next-state value.
A correctly implemented 8-bit register with an initial value of β0xABβ would be implemented:
π
logic foo_en; logic [7:0] foo_q, foo_d; always_ff @(posedge clk or negedge rst_ni) begin if (!rst_ni) begin foo_q <= 8'hab; end else if (foo_en) begin foo_q <= foo_d; 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 preferred to do this work in a separate combinational block with explicit blocking assignments.
Example:
always_ff @(posedge clk or negedge rst_ni) begin if (!rst_ni) begin state_q <= StIdle; end else begin state_q <= state_d; end end always_comb begin state_d = state_q; // default assignment next state is present state unique case (state_q) StIdle: state_d = StInit; // Idle State move to Init StInit: begin // Initialize calculation if (conditional) begin state_d = StIdle; end else begin state_d = StCalc; end end StCalc: begin // Perform calculation if (conditional) begin state_d = StResult; end end StResult: state_d = Idle; default: state_d = 'X; 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. Ideally, sequential blocks should contain only a register instantiation, with perhaps a load enable or an increment.
X
's)Explicitly specify donβt cares when safe to do so. Donβt silently squash X
in logic.
Don't Care values can significantly help the quality of logic optimization and should be used wherever it is safe to do so. Explicitly declaring unused decodes as X
has the secondary effect of propagating X
's through the design if the input unused design assumption is violated, making those bugs easier to diagnose and fix.
Example:
always_comb begin unique case (state) ALPHA: decode = 16'd1; BETA: decode = 16'd127; GAMMA: decode = 16'd43; // all other states are unused: default: decode = 'X; // or {16{1'bx}} in Verilog-2001 endcase end
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 assert to raise an exception when an input is X
.
Be aware: Any logical operation involving X
always propagates the X
.
Example:
$display("%b", 1'bx == 1'b1); // produces 1'bx $display("%b", 1'bx != 1'b1); // produces 1'bx $display("%b", !(1'bx)); // produces 1'bx. // etc.
However, when evaluated in a boolean context, X
always evaluates to false.
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, consider these options:
always_comb begin result = value ? 1'b1 : 1'b0; // 1'bx produces 1'bx end
or
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
or
always_comb begin assert (!$isunknown(value)); // throws exception if 1'bx if (value) result = 1'b0; // value is 1'b1 else result = 1'b1; // value is 1'b0 or 1'bx end
Further discussion:
Avoid sensitivity lists, and use a consistent assignment type.
Use always_comb
for SystemVerilog combinational blocks. Use always @*
if only Verilog-2001 is supported. Never explicitly declare sensitivity lists for combinational logic.
Prefer assign statements wherever practical.
Example:
assign final_value = xyz ? value_a : value_b;
Where a case statement is needed, enclose it in its own always_comb
block.
Synthesizable combinational logic blocks should only use blocking assignments.
Do not use three-state logic (Z
state) to accomplish on-chip logic such as muxing.
Do not infer a latch inside a function, as this may cause a simulation / synthesis mismatch.
Avoid case-modifying pragmas. unique case
is the best practice. Always define a default case.
Never use either the full_case
or parallel_case
pragmas. These pragmas can easily cause synthesis-simulation mismatches.
Here is an example of a style-compliant case 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
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.
Be sure to use unique case
correctly. In particular, any variables assigned in one case item must be assigned in all case items, including the default. Failing to do this can lead to a simulation-synthesis mismatch as described in Don Mills' paper.
The default
case is 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.
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.
casex
should 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.
The SystemVerilog-2012 case-inside
construct should not be use used yet. It implements asymmetric wildcard matching, so that only X
s in the case-items will behave as wildcards. Unfortunately, tool support for case-inside
is not universal yet.
References:
Always name your generated blocks.
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.
This ensures that generated hierarchical signal names are consistent across different tools.
Generate and all named code blocks should use lower_snake_case
. A space should be placed between begin
and the code block name.
Example of a conditional generate construct:
π
if (TypeIsPosedge) begin : posedge_type always_ff @(posedge clk) foo <= bar; end else begin : negedge_type always_ff @(negedge clk) foo <= bar; end
Example of a loop generate construct:
π
for (genvar ii = 0; ii < NumberOfBuses; ii++) begin : my_buses my_bus #(.index(ii)) i_my_bus (.foo(foo), .bar(bar[ii])); end
Do not wrap a generate construct with an additional begin
block.
Do not use generate regions {generate
, endgenerate
}.
Use the available signed arithmetic constructs wherever signed arithmetic is used.
When it's necessary to convert from unsigned to signed, use the signed'
cast operator ($signed
in Verilog-2001).
If any operand in a calculation is unsigned, Verilog implicitly casts all operands to unsigned and generates a warning. There should not be any signed-to-unsigned warnings from either the simulation or synthesis tools if all unsigned variables are properly casted.
Example of implicit signed-to-unsigned casting:
logic signed [7:0] a; logic incr; logic signed [15:0] sum1, sum2; initial begin a = 8'sh80; incr = 1'b1; sum1 = a + incr; // sum1 = 16'h0081 sum2 = a + signed'({1'b0, incr}); // sum2 = 16'hFF81 end
In the above example, the fact that incr
is unsigned causes a
to be evaluated as unsigned as well. The sum1
evaluation is surprising and is flagged by a warning that should not be ignored.
Prefix printed binary numbers with 0b
. Prefix printed hexadecimal numbers with 0x
. Do not use prefixes for decimal numbers.
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. Only print decimal numbers without modifiers. Use a 0x
prefix for hexadecimal and 0b
prefix for binary.
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("%0d", some_decimal_value);
π
$display("%0x", some_hex_value); $display("%0b", some_binary_value); $display("0d%0d", some_decimal_value);
When assigning constant values, it is preferred to use underscore notation for hex or binary bit strengths of length beyond 8 for better readability. Zero prepending is not required unless it improves readability. Declare constants in the format (binary, hex, decimal) they are typically displayed in.
π
logic [15:0] val0, val1, val2; logic [39:0] addr0, addr1; always_comb begin val0 = 16'h0; if (condition1) begin val1 = 16'b0010_0011_0000_1101; val2 = 16'b0010_1100_0000_0000; addr1 = 40'h00_1fc0_0000; addr2 = 40'h00_efc0_0000; end else begin val0 = 16'hffff; val1 = 16'b1010_0011_0110_1001; val2 = 16'b1110_1100_1111_0110; addr1 = 40'h40_8000_0000; addr2 = 40'h41_c000_0000; end end
These language features are considered problematic and their use is discouraged unless otherwise noted:
import my_pkg::*;
.alias
statement.case inside
is broken inside some FPGA compile tools.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 bar, output foo ); begin // illegal generate block assign foo = bar; end endmodule
The key ideas in this section include:
logic
: logic foo;
logic [7:0] byte;
byte_t arr[0:N-1];
Do not rely on inferred nets.
All signals must be explicitly declared before use. All declared signals must specify a data type. A correct design contains no inferred nets.
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 (ie. 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 should be justified with a short comment.
It is permissible for DV (Design Verification) 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 logical constructs for logical comparisons, bit-wise for data.
Logical constructs (!
, ||
, &&
, ==
, !=
) should be used for all constructs that are evaluating logic (true or false) values, such as if clauses and ternary assignments. Use bit-wise constructs (~
, |
, &
, ^
) for all data constructs, even if scalar.
π
always_comb begin if (bool_a || (bool_b && !bool_c) begin x = 1'b1; end else begin x = 1'b0; end assign z = ((bool_a != bool_b) || bool_c) ? a : b; assign y = (a & ~b) | c;
π
always_comb begin if (bool_a | (bool_b & ~bool_c) begin x = 1'b1; end else begin x = 1'b0; end assign z = ((bool_a ^ bool_b) | bool_c) ? a : b; assign y = (a && !b) || c;
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.
Declare unpacked arrays in big-endian fashion (for instance, [n:m]
where n <= m
). Never declare an unpacked array in little-endian order, such as [size-1:0]
.
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]
.
logic [15:0] word_array[3] = '{word0, word1, word2};
State machines use an enum to define states, and be 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 should list each state in the state machine. Comments describing the states should be deferred to case statement in the combinational process block, below.
States should be named in UpperCamelCase
, like other enumeration constants.
Barring special circumstances, the initial idle state of the state machines will be named Idle
or StIdle
. (Alternate names are acceptable if they improve clarity.)
Ideally, each module should 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 StRdIdle
state and a StWrIdle
state.
Combinational Decode of State
The combinational process block should contain:
The State Register
No logic except for reset should be performed in this process. The state variable should 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 { StIdle, StFrameStart, StDynInstrRead, StBandCorr, StAccStoreWrite, StBandEnd } alcor_state_e; alcor_state_e alcor_state_d, alcor_state_q; // Combinational decode of the state always_comb begin alcor_state_d = alcor_state_q; foo = 1'b0; bar = 1'b0; bum = 1'b0; unique case (alcor_state_q) // StIdle: waiting for frame_start StIdle: if (frame_start) begin foo = 1'b1; alcor_state_d = StFrameStart; end // StFrameStart: Reset accumulators StFrameStart: begin // ... etc ... end default: begin // X's in the inputs propagate to X's in the outputs alcor_state_d = 'X; foo = 'X; bar = 'X; bum = 'X; end endcase end // Register the state always_ff @(posedge clk or negedge rst_n) begin if (!rst_n) begin alcor_state_q <= StIdle; end else begin alcor_state_q <= alcor_state_d; end end
The _n
suffix indicates an active-low signal.
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.
Signals delayed by a single clock cycle should end in a _q
suffix.
If one signal is only a delayed version of another signal, the _q
suffix should be used to indicate this relationship.
If another signal is then delayed by another clock cycle, the next signal should be identifed with the _q2
suffix, and then _q3
and so on.
Example:
always_ff @(posedge clk) begin data_valid_q <= data_valid_d; data_valid_q2 <= data_valid_q; data_valid_q3 <= data_valid_q2; end
This is a short summary of the Comportable style guide. Refer to the main text body for explanations examples, and exceptions.
//
begin
must be on the same line as the preceding keyword and end the lineend
must start a new lineclk
. All clock signals must start with clk_
rst_n
_i
to module inputs, _o
to module outputs or _io
for bi-directional module signals_d
and the output _q
as suffix_q2
, _q3
, etc. to reflect their latency_n
. When using differential signals use _p
for active high_e
_
. n
should come first i
, o
, or io
last.*
`define
globalslocalparam
, globals in a separate .svh file.logic
is preferred over reg
and wire
, declare all signals explicitlyalways_comb
, always_ff
and always_latch
are preferred over always
X
) when safe to do so.assign
statements wherever practical.unique case
and always define a default
case0b
and 0x
as a prefix for binary and hex. Use _
for clarity||
) for logical comparison, bit-wise (i.e |
) for data comparison