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 NextStateNextstate 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, bc, 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.

Subscribe to fpgadesign.io

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe