Skip to content

SystemVerilog Cheatsheet

zwabbit edited this page Mar 17, 2023 · 5 revisions

The below are a reference to a variety of useful SystemVerilog features and the expected behavior that results.

Interfaces

Interfaces, while extremely handy for simplifying module connections, have a couple of quirks that are non-intuitive.

Interface Array Declaration

Parenthesizes come after the array delimiters.

axi4_intf m_axi[INTF_CNT]();

Interface Array Iteration

While it is possible to define an array of interfaces in most current SV compilers, the Xilinx Vivado compiler at least does NOT support iterating over such an array in a combinatorial for loop. This means that some code like this is illegal.

for(int i = 0; i < INTF_CNT; i++) begin
  data = axi4_s[i].w_data;
end

At the same time, you can iterate over an array of interfaces with a generate for loop, so this code is legal.

for(genvar i = 0; i < INTF_CNT; i++) begin
  assign data = axi4_s[i].w_data;
end

Parameterized Interfaces

There is a weird semantic gap when it comes to instantiating a parameterized interface that can cause some confusion. With a parameterized interface, the modification to the parameter is done with the interface that is used to connect to the module that uses the interface. The parameter value is not set in the module's port list.

This means that the following is incorrect.

module axi_endpoint #(int parameter DATA_WIDTH = 32)(axi4_intf.slave #(.DATA_WIDTH(DATA_WIDTH)) axi4_s);

The interfaces in a module's port list is not where the parameterization happens. It instead happens in whatever is the top level declaration of the interface instance.

axi4_intf #(.DATA_WIDTH(32)) axi4_s();

axi_endpoint axi_endpoint_inst(axi4_s);

The module that the parameterized interface instance is connected to will inherit the specified width.

Inheriting Interface Parameters

It is possible to get the parameter value of an interface by accessing it as if it were an interface member like so:

localparam int DATA_WIDTH = s_axis.DATA_WIDTH;

The issue is that not all synthesis tools support this. Internally the first version of Vivado that supported this was 2022.1.

An alternative is to make use of the $bits function, like so.

localparam int DATA_WIDTH = $bits(s_axis.tdata);

This way one does not need to pass down duplicate parameter values that may not match that of the interfaces used.

Packed Ordering

Datatypes of the packed type can be flattened out into an array of bits and concatenated in various fashions. The following demonstrate the various ways in which the ordering works.

Packed Structs

typedef struct packed {
  logic [7:0] upper_byte;  // [23:16]
  logic [7:0] middle_byte; // [15:8]
  logic [7:0] lower_byte;  // [7:0]
} packed_t;

Packed Left-Handed Concatenation

logic [7:0] upper_byte;
logic [7:0] lower_byte;
logic [15:0] packed_vector;
assign {upper_byte, lower_byte} = packed_vector;

Bindfiles

Bindfiles are an extremely useful construct for simulation purposes, but they do have a couple of cases where a syntax template is useful.

Parameterized Bindfiles

Bindfiles can also have parameters like regular modules, but they require manual connection in the bind command like so.

bind rtl_module bind_module #(.DATA_WIDTH(PARENT_DATA_WIDTH)) bind_module_inst(.*);

In the above, the PARENT_DATA_WIDTH parameter passed in is the name of a parameter that exists in the rtl_module that bind_module is being bound to. This needs to be done for all parameters one wants to have overridden in the bind module.

Clone this wiki locally