Overview of SystemVerilog Interfaces


The concept of an interface has been part of SystemVerilog since the Accellera 3.0 version of the language. According to IEEE Std 1800-2012:

The interface construct [...] encapsulates the communication between design blocks, and between design and verification blocks [...].

At its lowest level, an interface is a named bundle of nets or variables. The interface is instantiated in a design and can be connected to interface ports of other instantiated modules, interfaces and programs. An interface can be accessed through a port as a single item, and the component nets or variables referenced where needed. [...]

Additional power of the interface comes from its ability to encapsulate functionality as well as connectivity, making an interface, at its highest level, more like a class template. An interface can have parameters, constants, variables, functions, and tasks. [...]

The following is an overview of this concept, with thoughts how the functionality relates to VHDL concepts.

Declaration and Use

An SV interface is a language construct whose declaration may appear a stand-alone declaration in the source text, in a module definition, or in an interface definition. An interface declaration may include parameters and ports, and in its body: declarations of local objects (variables and nets); declaration and instantiation of interfaces; declarations of tasks, initial blocks, and always blocks; assertions; generate regions; modport declarations; and more. For example:

interface ifc (input logic clk);
   logic req, gnt;
   logic [7:0] addr, data;

   modport master (input gnt, output req, addr, ref data);  // Declare the master view
   modport slave  (input req, addr, output gnt, ref data);  // Declare the slave view

An interface may be instantiated using a syntax corresponding to that of a module instantiation. The instantiation creates the data objects declared in the body of the interface, including the objects declared in nested interface declarations and instantiated in the interface.

module top;
   logic clk = 0;
   initial repeat(10) #10 clk++;
   ifc sb_intf(clk);           // Instantiate the interface
   memMod mem(.a(sb_intf));    // Connect the interface to the module instance
   cpuMod cpu(.b(sb_intf));

An interface may also be referenced in a port declaration, either directly or with a view defined by a modport declaration, which is a named collection of modes for the interface objects. If an interface is referenced directly, the mode is ref inout.

module memMod (ifc.slave a);   // This module uses the slave view

module cpuMod (ifc.master b);  // This module uses the master view

Finally, an interface may be parameterized, e.g. to specify a bus width.

interface ifc2 #(AWIDTH = 8, DWIDTH = 8) (input logic clk); // Define the interface
   logic req, gnt;
   logic [AWIDTH-1:0] addr;
   logic [DWIDTH-1:0] data;

The width of the real bus is then specified as part of the interface instantiation.

module top;
   logic clk = 0;
   ifc2 sb_intf(clk);                  // Instantiate default interface
   ifc2 #(.DWIDTH(16)) wide_intf(clk); // Interface with 16-bit data

Bundling Aspects

The variables and nets declared in an interface declaration are the objects bundled by the interface. They are private to the interface, i.e. if an object with the same name is declared in another interface declaration it denotes another object. In the above example, req, gnt, addr, and data are private to ifc. If it is necessary for an object to be a member of more than one interface, it has to be declared as a formal port of the interface and connected with the real object as part of the instantiation of the interface. In the above example, clk is declared as a port of ifc, and the real clock is connected to it when the interface is instantiated to create the interface object named sb_intf.

In the example the modport name is used to declare the formal ports of the modules, while the interface name is used to define the actual in the module instantiation statement. It is also possible to:

  • Use the interface name to declare the formal port and to define the actual in the module instantiation statement. In this case the mode of the port defaults to inout.
  • Use the interface name to declare the formal and the modport name to define the actual.
  • Use the modport name to declare the formal port and to define the actual in the module instantiation statement. In this case the same modport name must be used in both places.

A module port can be declared as a generic bundle by using the keyword interface instead of an interface name or modport name. In this case the details of the interface are specified as part of the module instantiation. The LRM refers to this case as a generic bundle, while the cases described earlier are called named bundles.

Finally, a modport declaration may specify only a portion of the objects that are part of the interface, i.e. certain objects of the interface may be left out of a modport, and for composite objects it is possible to specify a bit select or a part select (equivalent to indexed and slice names in VHDL), or selected objects may be combined to an aggregate. For such aggregates and also for bit select and part select the selection must be given a simple name in the modport declaration, for example:

interface ifc3;
   logic [7:0] r;
   const int x=1;
   bit R;
   modport A (output .P(r[3:0]), input .Q(x), R);  // Defines the name P to refer to a part select and Q as the external name for x

Behavioral Aspects

An interface declaration can include behavioral code in the form of initial, always, final, and clocking blocks, task declarations and function declarations, and programs. Tasks and functions can be called in a module that either instantiates the interface or has the interface as a formal port. A modport declaration can specify that only certain subprograms may be called in a module using that modport; this is done with an import clause as part of the modport declaration.

interface ifc4 (input logic clk); // Define the interface
   logic req, gnt;
   logic [7:0] addr, data;
   logic [1:0] mode;
   logic start, rdy;

   task slaveRead; // slaveRead method
      // ...
   endtask: slaveRead

   modport slave (input req, addr, mode, start, clk,
                  output gnt, rdy,
                  ref data,
                  import slaveRead); // import into module that uses the modport
endinterface: ifc4

module memMod(ifc4.slave a); // Uses any interface
   logic avail;
   always @(posedge a.clk)  // the clk signal from the interface
   a.gnt <= a.req & avail;  // the gnt and req signals in the interface
   always @(a.start)
      a.slaveRead;          // call the task

Finally, a module can define a subprogram and export it through a modport. In this case the modport has an export clause, which must include the signature of the subprogram. The exported subprogram can then be imported into another module with another modport. There are some special rules regarding multiple instances exporting the same subprogram: It's not allowed for functions, while for tasks they have to be declared to have forkjoin semantics.Here is an example of a task exported from one module and imported in another:

interface ifc5 (input logic clk); // Define the interface
   logic req, gnt;
   logic [7:0] addr, data;
   logic [1:0] mode;
   logic start, rdy;
   modport slave( input req, addr, mode, start, clk,
                  output gnt, rdy,
                  ref data,
                  export Read);   // export from module that uses the modport
   modport master(input gnt, rdy, clk,
                  output req, addr, mode, start,
                  ref data,
                  import task Read(input logic [7:0] raddr)); // import requires the full task prototype
endinterface: ifc5

module memMod(ifc5.slave a); // The slave view exports the Read task
   logic avail;
   task a.Read;              // Read method. Its definition indicates its association with the interface
      avail = 0;
      avail = 1;

module cpuMod(ifc5.master b);
   enum {read, write} instr;
   logic [7:0] raddr;
   always @(posedge b.clk)
      if (instr == read)
         b.Read(raddr);      // call the slave method via the interface

Virtual interfaces, classes

A virtual interface is a variable that represents an interface instance. It can be assigned a (physical) interface or another virtual interface, provided the types match. The LRM isn't clear whether the virtual interface is a copy of the values of the physical interface or a reference to it; the reference is more likely.

Note: It has been confirmed by several people that a virtual interface is in fact a pointer to an interface.

Interfaces also exist in the context of classes; I have not explored this aspect.

Access to elements of an interface

As already demonstrated by the examples, elements of the interface, such as objects declared in the interface, modport declarations, subprograms, etc., can be accessed using a hierarchical name reference, i.e. a name of the form object_name.element_name.

Observations and Considerations for VHDL

  • Should an interface be a design unit or a declarative item in a package, entity, architecture, etc.? I believe that a declaration as a separate design unit would prevent (or make harder) declarations of interfaces in an entity or architecture
  • There is a one-to-many relationship between interfaces and modports, similar to entities and architectures in VHDL. This would suggest that a view of an interface could be declared using a similar paradigm, e.g. modport master of ifc is ...
  • An interface is obviously its own declarative region. Should the creation of an interface object be done with a declaration similar to a signal declaration or an instantiation statement as it's done in SV? The former would make it more difficult syntactically to specify port associations. If instantiation is used, is the instantiated interface part of the design hierarchy? This affects how elements of the interface are referenced (selected/expanded name, or external name), and also 'PATH_NAME and 'INSTANCE_NAME.
  • What are the implications on visibility of the elements of an interface in either case?
  • Are the elements of an interface ordered? Probably not (where would the ports of the interface declaration be?) It is not relevant unless it is possible to associate the elements of an interface individually in a component instantiation statement, something that is of questionable value.
  • What impact do VHDL generic types have on interfaces? Is it necessary to instantiate the declaration first (similar to an instantiation of a package) before an interface object can be declared?
  • Configurations?
  • How do the SV ref semantics relate to VHDL?

Feedback from the WG meeting on June 11

  • We should try to define something that aligns with SV to make it easier for tools to support interfaces at the boundary between SV and VHDL.
  • It is imperative to have a robust extension mechanism. This is missing from protected types and restricts their usability.
  • Having a port mechanism in the same way as SV will be a requirement. As demonstrated in the examples, a clock (and possibly other signals) ought to be part of several interfaces, so they cannot be private to one. The port map that is required to associate the formal port of the interface with an actual will likely require an instantiation of an interface, as it's done in SV, not a declaration similar to a signal declaration.
  • Parameterization with generics is important. When supporting generic types, using a "single step" approach to instantiate an interface should be aimed at (similar to how entities are handled in VHDL). The "two-step approach" that is taken for packages and subprograms, where a generic package or a generic subprogram first has to be instantiated, should be avoided.
  • Modports provide a view of the interface, i.e. a modport declaration doesn't just define modes. In particular, it is possible to make a subelement or a slice or an object part of a view. There was a remark that this may be a better way to syntactically approach the issue described in New Interface Port/Parameter Mode Requirements that is solved there with creating a composite and a null mode. However, the issue in that example remains that if one element of a composite is driven, this means in VHDL that the whole composite is driven. This is different in Verilog, where the drivers for each element of a bus are separate.
  • It seems that an import capability in a modport is redundant at least functionally.
  • Many of the choices made by the SV interface definition are based on legacy restrictions in the language. At the time interfaces were defined there were no packages. We surmise that this may have been one of the motivations for the modport export capability (need an example of this). Today it would be more logical to define subroutines in packages rather than in modules if they have to be shared. For VHDL, packages it the better choice.
  • It may also not be necessary to allow definition of subprograms as part of the interface; this could be done in a package.
  • Something similar to virtual interfaces will be a requirement. Jim mentioned the example of a CPU being interrupted: virtual interfaces could provide an elegant means for describing the context switch. The virtual interface would have to be a reference of the real thing, not a copy. However, there should be a better name than "virtual interface".

-- ErnstChristen - 2015-06-10

Topic revision: r6 - 2015-07-08 - 21:07:08 - ErnstChristen
Copyright © 2008-2019 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback