Section 19.2

LRM-107

Changes to Syntax 19-1 (change in red):

modport_ports_declaration ::=

 { attribute instance } modport_simple_ports_declaration

| { attribute instance } modport_hierarchical_ports_declaration

| { attribute instance } modport_tf_ports_declaration

| { attribute instance } modport_clocking_declaration

 

modport_clocking_declaration ::= clocking clocking_identifier

Section 19.4.5

LRM-107

Changes (change in red):

19.4.5 Clocking and modports

 

The modport construct can also be used to specify the direction of clocking blocks declared within an interface. As with other modport declarations, the directions of the clocking block are those seen from the module in which the interface becomes a port. The syntax for this is shown below.

 

modport_declaration ::= modport modport_item { , modport_item } ;                                           // from Annex A.2.9

 

modport_item ::= modport_identifier ( modport_ports_declaration { , modport_ports_declaration } )

 

modport_ports_declaration ::=

  { attribute instance } modport_simple_ports_declaration

| { attribute instance } modport_hierarchical_ports_declaration

| { attribute instance } modport_tf_ports_declaration

| { attribute instance } modport_clocking_declaration

 

modport_clocking_declaration ::= clocking clocking_identifier

 

Syntax 19-3—Interface modport declaration.

 

All of the clocking constructs used in a modport declaration shall be declared by the same interface as is the modport itself. Like all modport declarations, the direction of the clocking signals are those seen from the module in which the interface becomes a port. The example below shows how modports can be used to create both synchronous as well as asynchronous ports.  When used in conjunction with virtual interfaces (see Section 19.8.2), these constructs facilitate the creation of abstract synchronous models.

 

interface A_Bus( input bit clk );

wire req, gnt;

wire [7:0] addr, data;


clocking sb @(posedge clk);

input gnt;

output req, addr;

inout data;

 

property p1; req ##[1:3] gnt; endproperty

endclocking


modport DUT ( input clk, req, addr,            // Device under test modport

  output gnt,
      inout data );

 

modport STB ( clocking sb );                   // synchronous testbench modport

modport TB ( input gnt,                        // asynchronous testbench modport
                 output req, addr,

             inout data );

endinterface

 

The above interface A_Bus can then be instantiated as shown below:

 

module dev1(A_Bus.DUT b);                          // Some device: Part of the design

   ...

endmodule

 

module dev2(A_Bus.DUT b);                          // Some device: Part of the design

   ...

endmodule

 

module top;

bit clk;

 

A_Bus b1( clk );

A_Bus b2( clk );

 

dev1 d1( b1 );

dev2 d2( b2 );

 

T tb( b1, b2 );

endmodule

 

program T (A_Bus.STB b1, A_Bus.STB b2 );           // Testbench: 2 synchronous ports

 

assert property (b1.p1);                      // assert property from within program

 

initial begin

b1.sb.req <= 1;

wait( b1.sb.gnt == 1 );

...

b1.sb.req <= 0;

b2.sb.req <= 1;

wait( b2.sb.gnt == 1 );

...

b2.sb.req <= 0;

end

endprogram

The example above shows the program block using the synchronous interface designated by the clocking-modport of interface ports b1 and b2. In addition to the procedural drives and samples of the clocking block signals, the program asserts the property p1 of one of its interfaces b1.

Section 19.6.1

LRM-111

Changes (change in red):

The argument types in a prototype must match the argument types in the function or task declaration. The rules for matching are like those in C. The types must be exactly the same, or defined as being the same by a typedef declaration, or a series of typedef declarations. Two structure declarations containing the same members are not considered to be the same type.

The number and types of arguments in a prototype must match the argument types in the function or task declaration. The rules for type equivalency are described in section 5.8.1 Equivalent Types.

Section 19.7

LRM-86

Changes (change in red):

 

interface simple_bus #(parameter AWIDTH = 8, DWIDTH = 8)

 (input bit clk); // Define the interface

LRM-84

Changes (change in red):

module memMod(interface a);              // Uses just the interface keyword

logic avail;

 

always @(posedge b.clk a.clk)            // the clk signal from the interface

a.gnt <= a.req & avail;    //the gnt and req signals in the interface

 

always @(b.start a.start)

if (a.mode[0] == 1’b0)

a.slaveRead;

else

a.slaveWrite;

endmodule

Section 19.8

LRM-85

Changes (change in red):

 

19.8 Access without ports to Interface objects

In addition to interfaces being used to connect two or more modules, the interface object/method paradigm allows for interfaces to be instantiated directly as static data objects within a module. If the methods are used to access internal state information about the interface, then these methods can be called from different points in the design to share information.

 

Access to all objects declared in an interface is always available by hierarchical reference, regardless of whether or not the interface is connected through a port. When an interface is connected with a modport in either the module header or port connection, access  by port reference is limited to only those objects listed in the modport, for only those types of objects legal to be listed in modports (wires, variables, tasks, and functions) All objects are still visible by hierarchical reference. For example:

 

interface intf_mutex;

 

task lock ();

...

endtask

 

function unlock();

...

endfunction

endinterface

 

function int f(input int i);

return(i); // just returns arg

endfunction

 

function int g(input int i);

return(i); // just returns arg

endfunction

 

module mod1(input int in, output int out);

 

intf_mutex mutex();

 

always begin

#10 mutex.lock();

@(in) out = f(in);

mutex.unlock;

end

 

always begin

#10 mutex.lock();

@(in) out = g(in);

mutex.unlock;

end

endmodule

 

interface ebus_i;

integer I;           // reference to I not allowed through modport mp

typedef enum {Y,N} choice;

choice Q;

parameter True = 1;

modport mp(input Q);

endinterface

 

module Top;

ebus_i ebus;

sub s1(ebus.mod);

endmodule

 

module sub(interface.mp i);

typedef i.choice yes_no;   // import type from interface

yes_no P;

assign P = i.Q;            // refer to Q with a port reference

initial

Top.s1.Q = i.True;         // refer to Q with a hierarchical reference

initial

Top.s1.I = 0;              // refering to i.I would not be legal because

// is not in modport mp

endmodule

Section 19.8 (New)

LRM-72 LRM-74

Add the following new section and renumber current 19.8 to 19.9:

19.8 Virtual interfaces

 

Virtual interfaces provide a mechanism for separating abstract models and test programs from the actual signals that make up the design. A virtual interface allows the same subprogram to operate on different portions of a design, and to dynamically control the set of signals associated with the subprogram. Instead of referring to the actual set of signals directly, users are able to manipulate a set of virtual signals. Changes to the underlying design do not require the code using virtual interfaces to be re-written. By abstracting the connectivity and functionality of a set of blocks, virtual interfaces promote code-reuse.

 

A virtual interface is a variable that represents an interface instance. The syntax to declare a virtual interface variable is given below.

 

virtual_interface_declaration ::=                                                                                                     // from Annex A.2.9

                             virtual  [interface] interface_identifier  list_of_virtual_interface_decl ;

 

list_of_virtual_interface_decl ::=                                                                                                    // from Annex A.2.3

  variable_identifier [ = interface_instance_identifier ]

{ , variable_identifier [ = interface_instance_identifier ] }

 

data_declaration :=                                                                                                                                           // from Annex A.2.1.3

                            

                             virtual_interface_declaration

 

data_type_common_item ::=                                                                                                          // from Annex A.2.2.1

 

| virtual [interface] interface_identifier

 

Syntax 19-2—Virtual interface declaration.

 

Virtual interface variables can be passed as arguments to tasks, functions, or methods. A single virtual interface variable can thus represent different interface instances at different times throughout the simulation. A virtual interface must be initialized before it can be used; it has the value null before it is initialized. Attempting to use an uninitialized virtual interface shall result in a fatal run-time error.

 

Only the following operations are directly allowed on virtual interface variables:

¾      Assignment (=) to:

¾      another virtual interface of the same type

¾      an interface instance of the same type

¾      the special constant null

¾      Equality (==) and inequality (!=) with:

¾      another virtual interface of the same type

¾      an interface instance of the same type

¾      the special constant null

 

Virtual interfaces shall not be used as ports, interface items, or as members of unions.

 

Once a virtual interface has been initialized, all the components of the underlying interface instance are directly available to the virtual interface via the dot notation. These components can only be used in procedural statements; they cannot be used in continuous assignments or sensitivity lists. In order for a net to be driven via a virtual interface, the interface itself must provide a procedural means to do so. This can be accomplished either via a clocking domain or by including a driver that is updated by a continuous assignment from a variable within the interface.

 

Virtual interfaces can be declared as class properties, which can be initialized procedurally or by an argument to new(). This allows the same virtual interface to be used in different classes. The following example shows how the same transactor class can be used to interact with various different devices.

 

interface SBus;                             // A Simple bus interface

logic req, grant;

logic [7:0] addr, data;

endinterface

 

class SBusTransctor;                        // SBus transactor class

   virtual SBus bus;                        // virtual interface of type Sbus

 

   function new( virtual SBus s );

          bus = s;                          // initialize the virtual interface

   endfunction

 

   task request();                          // request the bus

          bus.req <= 1’b1;

   endtask

 

   task wait_for_bus();                     // wait for the bus to be granted

          @(posedge bus.grant);

   endtask

endclass

 

module devA( Sbus s ) ...  endmodule       // devices that use SBus

module devB( Sbus s ) ...  endmodule

 

module top;

 

SBus s[1:4] ();                         // instantiate 4 interfaces

 

devA a1( s[1] );                        // instantiate 4 devices

devB b1( s[2] );

devA a2( s[3] );

devB b2( s[4] );

 

initial begin

                  SbusTransactor t[1:4];            // create 4 bus-transactors and bind

 

                 t[1] = new( s[1] );

                 t[2] = new( s[2] );

                 t[3] = new( s[3] );

t[4] = new( s[4] );

// test t[1:4]

end

endmodule

 

In the preceding example, the transaction class SbusTransctor is a simple reusable component. It is written without any global or hierarchical references, and is unaware of the particular device with which it will interact. Nevertheless, the class can interact with any number of devices (4 in the example) that adhere to the interface’s protocol.

 

19.8.1 Virtual interfaces and clocking domains

 

Clocking domains and interfaces can be combined to represent the interconnect between synchronous blocks. Moreover, because clocking domains provide a procedural mechanism to assign values to both nets and variables, they are ideally suited to be used by virtual interfaces. For example:

 

interface SyncBus( input bit clk );

wire a, b, c;


clocking sb @(posedge clk);

input a;

output b;

inout c;

endclocking

 

endinterface

typedef virtual SyncBus VI;                 // A virtual interface type

 

task do_it( VI v );                         // handles any SyncBus via clocking sb

if( v.sb.a == 1 )

     v.sb.b <= 0;

else

v.sb.c <= ##1 1;

endtask

 

In the preceding example, interface SyncBus includes a clocking domain, which is used by task do_it to ensure synchronous access to the interface’s signals: a, b, and c. Note that changes to the storage type of the interface signals (from net to variable and vice-versa) requires no changes to the task. The interfaces can be instantiated as shown below.

 

module top;

bit clk;

 

       SyncBus b1( clk );

SyncBus b2( clk );

 

initial begin

VI v[2] = { b1, b2 };

 

repeat( 20 )

do_it( v[ $urandom_range( 0, 1 ) ] );

end

endmodule

 

The top module above shows how a virtual interface can be used to randomly select among a set of interfaces to be manipulated, in this case by the do_it task.

 

19.8.2 Virtual interfaces modports and clocking domains

 

As shown in the example above, once a virtual interface is declared, its clocking-domain can be referenced using dot-notation. However, this only works for interfaces with no modports. Typically, a device under test and its testbench exhibit modport direction. This common case can be handled by including the clocking in the corresponding modport as described in Section 19.4.5.

 

The example below shows how modports used in conjunction with virtual interfaces facilitate the creation of abstract synchronous models.

 

interface A_Bus( input bit clk );

wire req, gnt;

wire [7:0] addr, data;


clocking sb @(posedge clk);

input gnt;

output req, addr;

inout data;

 

property p1; req ##[1:3] gnt; endproperty

endclocking


modport DUT ( input clk, req, addr,            // Device under test modport

  output gnt,
      inout data );

 

modport STB ( clocking sb );                   // synchronous testbench modport

modport TB ( input gnt,                        // asynchronous testbench modport
                 output req, addr,

             inout data );

endinterface

The above interface A_Bus can then be instantiated as shown below:

 

module dev1(A_Bus.DUT b);                   // Some device: Part of the design

   ...

endmodule

 

module dev2(A_Bus.DUT b);                   // Some device: Part of the design

   ...

endmodule

 

program T (A_Bus.STB b1, A_Bus.STB b2 );    // Testbench: 2 synchronous ports

              ...

endprogram

 

module top;

bit clk;

 

       A_Bus b1( clk );

A_Bus b2( clk );

 

dev1 d1( b1 );

dev2 d2( b2 );

 

T tb( b1, b2 );

endmodule


And, within the testbench program, the virtual interface can refer directly to the clocking domain.

 

program T (A_Bus.STB b1, A_Bus.STB b2 );    // Testbench: 2 synchronous ports

 

typedef virtual A_Bus.STB SYNCTB;

 

       task request( SYNCTB s );

                     s.sb.req <= 1;

              endtask

 

              task wait_grant( SYNCTB s );

                     wait( s.sb.gnt == 1 );

              endtask

 

              task drive(SYNCTB s, logic [7:0] adr, data );

if( s.sb.gnt == 0 ) begin

                 request(s);                // acquire bus if needed

wait_grant(s);

                     end

                     s.sb.addr = adr;

                     s.sb.data = data;

                     repeat(2) @s.sb;

s.sb.req = 0;                     //release bus

              endtask

 

              assert property (b1.p1);                 // assert property from within program

 

              initial begin

                     drive( b1, $random, $random );

                     drive( b2, $random, $random );

              end

endprogram

The example above shows how the clocking-domain is referenced via the virtual interface by the tasks within the program block.