feature article
Subscribe Now

How To Implement SystemVerilog for FPGA Design


Since its ratification in 2005, the SystemVerilog IEEE-1800 standard has experienced broad adoption in the verification and assertion space but has lagged for design constructs. Engineers may be wary of revamping current design methodologies, or they assume that SystemVerilog for design is not relevant to their projects, or they fear that field-programmable gate array (FPGA) synthesis tools do not fully support the new standard. All three of these concerns are either exaggerated or based on misconceptions. SystemVerilog is fully supported by leading synthesis tools, and the new design constructs are in fact relevant to most register-transfer level (RTL) coding styles and easy to learn and integrate into current methodologies.

The Benefits of SystemVerilog

The touted benefits of the design constructs include coding at higher levels of abstraction, compactness of code, and unification of design and verification environments. While it sounds appealing, in an engineer’s mind, a new language translates into a new learning curve, which translates into time to study, evangelize, and adapt to company standards. Most FPGA designers don’t have time to make their code more efficient because of all the code they have to write and would be better at keeping up with the latest standards if there weren’t so many to keep up with. Overwhelmed by tape-out schedules, designers can’t spend their days studying new languages, attending seminars, and perusing technical articles about the latest and greatest design trends.

The truth is, however, that the more pressure there is on design closure, the more relevant SystemVerilog is for design. Thanks to chip size and complexity, RTL is becoming more and more difficult to manage, re-use, and debug. In writing the new specification, the Accellera committee (an industry standards body) set out to address these problems. Its charter for the new SystemVerilog standard was to “extend Verilog IEEE 2001 to higher abstraction levels for architectural and algorithmic design, and advanced verification.”

The good news is that learning SystemVerilog for design is not difficut nor is it a quantum leap in methodology. It can (and should) be thought of as an extension to the existing Verilog standard, as opposed to a different language altogether.  Leading FPGA synthesis tools provide support for its synthesizable constructs. Designers need only recognize that the new extensions are easy to adopt, integrate, and tape out into real hardware.

While there are a variety of extensions available, the sub-set flow examined here focuses on the higher level of abstractions offered to RTL design. Each construct builds on the other. The degree of abstraction and complexity is up to the designer.

User-defined types

As a starting point, SystemVerilog allows the definition of new data types based on existing types with the typedef keyword, similar to C, as shown below. This is a fundamental building block in ultimately modeling a complex design at abstract levels while still being accurate and synthesizable. Once it is defined, the user-defined type can be used as with any built-in data-type, making data-structures more readable:

typedef  logic [1:0] opcode_t;
opcode_t [1:0] op1;

Note that the above example uses the naming convention of ending a user-defined type with the characters “_t”. This is done as a matter of readability and maintainability, since the user-defined type may have been defined elsewhere in the code or in another file.

In the above example, and the ones to follow, the logic data-type is used frequently so it is worth explaining. This is a replacement to the reg data-type, which is a misleading term since it implies a register is to be inferred, when in fact reg is a variable and may be implemented as sequential or combinational logic, depending on how it is used. The use of logic is recommended so as to avoid confusion. There are other differences between the reg data-type from Verilog-2001 and the logic type, but they will not be examined here.

Enumerated Types

Similar to VHDL’s enumeration data type, enumerated types in SystemVerilog provide the ability to declare a variable with a specific list of valid values, as shown below:

enum {ADD, SUB, MULT} opcode

The variable opcode is given a set of potential user-define names “ADD”, “SUB”, and “MULT” (also known as labels). This can be used in the following case statement (assume a, b, and c have been defined as variables):

 case (opcode)
   ADD:  c = a + b;
   SUB:  c = a – b;
   MULT: c = a * b;

The code shown above is readable and intuitive.  One might argue that the same can be achieved in Verilog with use of the `define macroor parameter constants to define a set of names with specific values, such as shown below:

‘define ADD 2’b00
‘define SUB 2’b01
‘define MULT 2’b10

reg [1:0] opcode;

 case (opcode)
   ‘ADD:  c = a + b;
   ‘SUB:  c = a – b;
   ‘MULT: c = a * b;

In the Verilog example above, the variables are defined as 2-bit vectors, so they can legally be assigned the value 2’b11—a value not accounted for in the above code. Designers typically have to be aware of this potential error. In addition to being more concise, enumerated types not only define a list of user-defined labels for a variable but also limit the variable to the set of valid values specified. By offering stronger type-checking in the language compiler, the code accurately describes designer intent.

Enumerated types can be used with the typedef keyword to create user-defined types with a set of valid values. This is useful when needing to make declarations in many places:

typedef enum {ADD, SUB, MULT} opcode_t;
opcode_t op1, op2;


SystemVerilog offers structures as the next level of abstraction. Structures are a means of aggregating variables or data-fields under a common name, usually those conceptually related in some way. With the struct keyword, users can intuitively describe data-structures and mask lower-level complexity as needed. Common applications for this include instruction registers, network packets, and bus packets.

struct {
logic [1:0] opcode;
int a, b;
logic [23:0] addr;
} instruction;

This is similar to C and to VHDL’s record construct. In Verilog, however, the members of a structure have to be defined as separate variables and managed individually, forcing the engineer to track how all the pieces fit together. By aggregating elements, data-structures can be represented in the HDL as they are understood conceptually.

Data elements (or members) within a struct can be accessed and manipulated using the struct and member names.

instruction.opcode = 2’b00

An entire struct can be a user-defined type to be re-used throughout in the code—effectively creating higher-level building blocks for design implementation.

typedef struct {
opcode_t opcode;
int a, b ;
logic [23:0] addr;
} instruction_t;

instruction_t instr1, instr2;

In the above example, note that the user-defined type opcode_t is a member of the user defined type instruction_t. Similarly, structures can also be declared as members of other structures, allowing multiple layers of abstraction.

They can also be passed through tasks, functions, and module ports, allowing abstraction to be maintained through data flow.
Assign instr1 = instr2;


Unions enhance structs or any data model by allowing multiple definition for a single storage element. Each definition has to occupy the same storage space (e.g., vector length), different data-types can be used. In the following example6, the same storage space is being defined as the  struct data with three members (source_address, destination_address, and data) and in another definition as a two-dimensional array bytes. Both of these definitions represent a 64-bit storage space identified as the union data_reg. Elements of the union are assigned values depending on the definition used (data or bytes).

union packed{
     struct packed {
     bit [15:0] source_address;
     bit [15:0] destination_address;
     bit [31:0] data;
} data;

bit [7:0][7:0] bytes;

} data_reg;

data_reg.data = data_in; //assumes data_in is a 32-bit vector

dest_low_byte = data_reg.bytes[4];

The above example briefly introduces the concept of packed structures and unions. We will not go into detail on the concept of packed arrays, but know that it refers to all elements being represented as contiguous bits, i.e., as a single vector.


While user-defined types, structures, and unions allow abstract data-modeling, they do not provide a complete mechanism for abstraction of data flow. The interface construct solves this problem by encapsulating port connectivity and functionality related to module-to-module communication. More than just abstraction of data-structures, interfaces raise the abstraction of communication protocols within a design.  This can reduce the size of code and improve maintainability.

The figure below shows a system-on-chip (SoC),  maintained by the Open Cores Project, with processor (CPU), UART, and parallel input/output (PIO) interface, all communicating through a bus interface following the wishbone specification.


An interface is defined similarly to a module but with the interface keyword as shown below. The set of signals to be bundled in the interface are declared without input or output direction. Since each block in the SoC will see a port differently (input, output, or bi-directional), the subset of signals and port directions for each design block is defined in modports—each modport having a uniqe name.  Interfaces support parameters, constants, tasks, functions, procedural blocks, program blocks, assertions, as well as any user-defined type.

When declaring a module in the design, an instance of the interface is used as a single module port, using the name of the interface as the port type. Signal details are masked with this single port declaration, as is all functionality embedded in the interface. As shown in the uart module below, individual signals are referenced within the module as needed.


One benefit of interfaces is bundling wires in one location, thereby masking inter-connectivity, reducing lines of code, and improving readability.

The more significant benefit is being able to localize communication logic between design blocks. The protocol details do not need to be duplicated in multiple modules so when the inevitable change of specification is made, modification to multiple areas of the code is minimized. These changes may include the addition or omission of a signal, a correction to a protocol error, or an entire revamp of the bus specification itself.


Beyond the constructs covered here, the standard provides a wide range of other extensions including enhancements to tasks, functions,  loops, procedural blocks, and the addition of new operators, new jump statements, and packages.

SystemVerilog is more of an evolution than revolution in RTL design and does not require an abandonment of existing coding styles.  With minimal effort for adoption, designers can realize its true benefits: coding at higher levels of abstraction and improved productivity by unifying the design and verification environment.


[1] April 2007 DeepChip Verification Survey
[2] “SystemVerilog enhancements for all chip designers”, Stuart Sutherland, EE Times, 2/26/2004
[3] DAC 2003 Accellera SystemVerilog Workshop
[4] SystemVerilog for Design: A Guide to Using SystemVerilog for Hardware Design and Modeling, Stuart Sutherland, Kluwer, Academic Publishers, Boston, MA, 2004, 0-4020-7530-8.
[5] IEEE Std. 1800-2005 IEEE Standard for SystemVerilog—Unified Hardware Design, Specification, and Verification Language, IEEE, 3 Park Avenue, NY, 2005.
[6] “Getting Ready for SystemVerilog at DAC [2004]” Presentation, Stuart Sutherland, Sutherland HDL, Inc.
[7] OPENCORES Project, opencores.org

8 thoughts on “How To Implement SystemVerilog for FPGA Design”

  1. Pingback: soccerphysics.co
  2. Pingback: bandar taruhan

Leave a Reply

featured blogs
Jul 6, 2020
If you were in the possession of one of these bodacious beauties, what sorts of games and effects would you create using the little scamp?...
Jul 3, 2020
[From the last episode: We looked at CNNs for vision as well as other neural networks for other applications.] We'€™re going to take a quick detour into math today. For those of you that have done advanced math, this may be a review, or it might even seem to be talking down...
Jul 2, 2020
In June, we continued to upgrade several key pieces of content across the website, including more interactive product explorers on several pages and a homepage refresh. We also made a significant update to our product pages which allows logged-in users to see customer-specifi...

Featured Video

Product Update: Advances in DesignWare Die-to-Die PHY IP

Sponsored by Synopsys

Hear the latest about Synopsys' DesignWare Die-to-Die PHY IP for SerDes-based 112G USR/XSR and parallel-based HBI interfaces. The IP, available in advanced FinFET processes, addresses the power, bandwidth, and latency requirements of high-performance computing SoCs targeting hyperscale data center, AI, and networking applications.

Click here for more information about DesignWare Die-to-Die PHY IP Solutions

Featured Paper

Cryptography: A Closer Look at the Algorithms

Sponsored by Maxim Integrated

Get more details about how cryptographic algorithms are implemented and how an asymmetric key algorithm can be used to exchange a shared private key.

Click here to download the whitepaper

Featured Chalk Talk

Nano Pulse Control Clears Issues in the Automotive and Industrial Markets

Sponsored by Mouser Electronics and ROHM Semiconductor

In EV and industrial applications, converting from high voltages on the power side to low voltages on the electronics side poses a big challenge. In order to convert big voltage drops efficiently, you need very narrow pulse widths. In this episode of Chalk Talk, Amelia Dalton chats with Satya Dixit from ROHM about new Nano Pulse Control technology that changes the game in DC to DC conversion.

More information about ROHM Semiconductor BD9V10xMUF Buck Converters