SystemVerilog .name and .* Notations
Do you use the .name and .* constructs in SystemVerilog? The advantages of using them and some more thoughts about streamlining your code.
SystemVerilog is Case Sensitive
Verilog and SystemVerilog are both case sensitive languages. Uppercase and lowercase letters are perceived differently in relation to both identifiers as well as keywords.
VHDL is a case insensitive Hardware Description Language. Keeping case sensitivity in mind while coding is especially important for those transitioning from VHDL to SystemVerilog.
// !!DO NOT DO THIS!!
module CaseExample;
reg myVar; // Declare a register variable named "myVar"
initial begin
myvar = 1'b1; // Error: SystemVerilog does not recognize "myvar"
MyVar = 1'b0; // Error: SystemVerilog does not recognize "MyVar"
myVar = 1'b0; // Correct: This matches the declared variable
end
endmodule
Case Sensitivity in SystemVerilog
In this code snippet, the variable myVar
is declared. However, attempts to
assign values using myvar
and MyVar
result in errors because they are not
recognized as the same variable.
// !!DO NOT DO THIS!!
// Enumerated Data Type for a State Machine
enum logic [1:0] {WAIT, IDLE, READY, SEND} State, NextState;
Copy
Enumerated Data Type in SystemVerilog
In this example of an enum declaration for an FSM, the signals State
and NextState
are declared with a mix of uppercase and lower case characters. It is very easy to miss one of the uppercase characters when these identifiers are used in the code at a different place. The compiler will consider NextState
, Nextstate
and nextstate
as different signals and you can imagine the carnage to follow.
It is generally not a good idea to use a mix of lowercase and uppercase characters for SystemVerilog variables and other identifiers.
Can you spot one more slight issue with the enum declaration above?
The statement is syntactically correct, the way it is written. SystemVerilog has an inbuilt keyword, wait
. The uppercase, enumerated label, WAIT
is, in theory, not to be confused with the lowercase keyword (and the compiler won't either), but you can see how issues could pop-up if all lowercase characters were used elsewhere to refer to the enumerated label.
The best way to avoid falling into this case sensitivity trap is to adopt good coding guidelines and follow proper naming conventions across teams and the organization as a whole.
💡Spend some time while coming up with identifier names. Keep the names concise, but unambiguous. Easier said than done, but in all the rush to get to the more complex part of coding, we tend to sometimes trivialize the process of choosing appropriate names for signals and variables.
Implicit Net Declaration in SystemVerilog
In SystemVerilog, implicit declarations occur when you use a signal name without explicitly declaring it. Take a look at the snippet of code below.
module implicit_example;
logic a, d; // Explicitly declared signals
assign a = b & c; // 'b' and 'c' are implicitly declared as logic
assign d = a | e; // 'e' is implicitly declared as logic
endmodule
Copy
Implicit Declaration in SystemVerilog
Here, b
, c
, and e
are not explicitly declared, but SystemVerilog treats them as logic
by default. This can lead to unintended errors if a name is used
incorrectly. It’s often recommended to declare all signals explicitly to avoid
confusion and maintain code clarity.
Countless hours have been spent debugging issues caused by implicit declarations. The compiler is happy with the code, but the synthesis tool typically prunes out a big chunk of logic because of improper connections.
There is a method to implicitly enable checking for undeclared instances, by using the .name
and .*
notations.
Using .name
and .*
notations for instance mapping
The .name
Notation
The .name
notation in SystemVerilog is a shorthand way to connect ports in module instances. When you declare ports with the same name in both the parent module and
the instantiated module, .name
automatically matches them.
This can significantly reduce the amount of repetitive code and potential
errors.
Let's take the case of an aximaster
module.
module aximaster (
input logic aclk,
input logic aresetn,
output logic [31:0] awaddr,
output logic awvalid,
input logic awready,
output logic [31:0] wdata,
output logic wvalid,
input logic wready,
input logic [31:0] rdata,
output logic rready,
input logic rvalid
);
// Module architecture goes here
endmodule
Example Code for AXI Master Module
Let's see how this module can be instantiated in the top-level using the .name convention.
module top;
logic aclk;
logic aresetn;
logic [31:0] awaddr;
logic awvalid;
logic awready;
logic [31:0] wdata;
logic wvalid;
logic wready;
logic [31:0] rdata;
logic rready;
logic rvalid;
aximaster u_aximaster (
.aclk,
.aresetn,
.awaddr,
.awvalid,
.awready,
.wdata,
.wvalid,
.wready,
.rdata,
.rready,
.rvalid
);
endmodule
Example Code for Instantiation of the AXI Master Module
The compiler checks the names and the size of the signals in the top
module and connects the corresponding signals to the ones on the aximaster
interface.
The .name
instantiation takes care of errors introduced due to typos leading to implicit net declarations.
The .*
notation
With the .*
notation, SystemVerilog automatically connects all the instance ports of aximaster
to the signals with matching names in the top
module.
This notation reduces boilerplate code and the potential for errors by
ensuring that signal names are matched and connected correctly, as long as
the names are consistent.
module top;
logic aclk;
logic aresetn;
logic [31:0] awaddr;
logic awvalid;
logic awready;
logic [31:0] wdata;
logic wvalid;
logic wready;
logic [31:0] rdata;
logic rready;
logic rvalid;
aximaster u_aximaster (
.*
);
endmodule
SystemVerilog .* Notation
More Rules to be Aware of
- .name and . implicit ports are not allowed to be mixed in the same instantiation. Instantiating one module with .name implicit ports and another module with . implicit ports is permitted.
- .name or .* implicit ports are not allowed to be mixed in the same instantiation with positional port connections. (A caveat here - Positional Port Connections should never ever be used, unless there is a good enough reason to do so.)
- A named port connection is required if the port size does not match the size of the connecting net or bus. For example: a 16-bit data bus connected to an 8-bit data port requires a named port connection to show which of the 16 bits are connected to the 8-bit data port.
- A named port connection is required if the port is unconnected.
Takeaways
Understanding the nuances of SystemVerilog's case sensitivity and port connection notations can significantly improve your coding efficiency and reduce errors. Develop a set of guidelines for naming signals and modules and stick to those guidelines. You might not be able to get everyone on board to follow the guidelines, but at least there should be consistency across multiple design modules written by the same designer.