Interface

In Verilog, the communication between blocks is specified using module ports.

Disadvantage of verilog module connections

  • Declaration must be duplicated in multiple modules.
  • Risk of mismatched declaration.
  • A change in design specifications can require modifications in multiple modules.

Interface#

SystemVerilog adds the interface construct which encapsulates the communication between blocks. An interface is a bundle of signals or nets through which a testbench communicates with a design.

sl. no.data type
1.Interface
2.Parameterized interface
3.Modports
4.Clocking blocks
5.Virtual interface
    Tabular column.1. Interface  

interface1

                                        Figure.1.Interface types

The interface construct is used to connect the design and testbench.

Systemverilog without Interface

Below diagram shows connecting design and testbench without interface.

int Diagram

                    Figure.2.top module without interface

SystemVerilog Interface

Below diagram shows connecting design and testbench with the interface.

interface Diagram

                    Figure.3.top module with interface

Syntax:

interface (interface_name) ([port_list]);  
 [list_of_signals]    
endinterface  

Example: Interface declaration

interface and_if; 
 logic input_a,input_b,output_y;      
endinterface    

int

                    Figure.4.and gate interface

Here the interface consist of group of signals, In test module we call the interface handle but we not declare the direction of signals. In test module we pass the value of a_input and b_input to the interface. In top module we instantiate the DUT signals with interface. Output of DUT ‘y’ is sent to the test module through interface. The modport is not used in the interface.we can declare the size of the each signal by decalaring ‘/’ on the signals. It is used to now the vector size of the signal.

TOP module for AND gate

    //Here the interface,testbench,design module are called.
    module top();
    
    //interface module
    and_if inf();
    
    //design module instantiate
    andg a1(.input_a(inf.input_a), .input_b(inf.input_b), .output_y(inf.output_y));
    
    //testbench
    tb a2(inf);
    
    endmodule:top

Design code for AND gate

     //module declaration  
     module andg(input_a,input_b,output_y);  
     input input_a,input_b;  
     output output_y;  
     //assign output  
     assign output_y=input_a&input_b;  
     endmodule:andg  

Testbench for AND gate

     //testbench for and gate  
     module tb(and_if inf);  
     initial begin  
     $display("\n// and gate output");  
     $monitor("\ninput_a=%b\t input_b=%b\t output_y=%b",inf.input_a,inf.input_b,inf.output_y);  
     inf.input_a = 0; inf.input_b = 0;  
     #1;  
     inf.input_a = 1; inf.input_b = 0;  
     #1;  
     inf.input_a = 0; inf.input_b = 1;  
     #1;  
     inf.input_a = 1; inf.input_b = 1;  
     end  
     endmodule:tb  

Below figure shows the output of and gate using interface.

interface 1png

                                 Figure.5.output for AND gate with interface

Github lab code link: https://github.com/muneeb-mbytes/SystemVerilog_Course/tree/production/interface/interface_error/interface_example

Github lab output link: https://github.com/muneeb-mbytes/SystemVerilog_Course/blob/production/interface/interface_example/interface.log

Advantages of SystemVerilog interfaces

  • In Verilog for the addition of new signals, it has to be manually changed everywhere that module has been instantiated. System Verilog made it easier to add new signals in the interface block for existing connections.
  • It has increased re-usability across the projects.
  • A set of signals can be easily shared across the components bypassing its handle.
  • It provides directional information (modports) and timing information (clocking blocks).

Parameterized interface#

Parameters can be used in interfaces to make vector sizes and other declarations within the interface reconfigurable using Verilog’s parameter redefinition constructs.

Syntax

interface (interface_name) #(parameter parameter_name = initialize);
 [list_of_signals]    
endinterface

Example

interface count_if #(parameter N=2) ;  
logic reset,clk;  
logic [N:0] counter;
endinterface:count_if

TOP module for COUNTER

    //Here the interface,testbench,design module are called.
     module top();
   
    //parameterised interface
     count_if inf();
  
    //design code of up_counter
    up_counter u1(.clk(inf.clk), .reset(inf.reset), .counter(inf.counter));
 
    //testbench for up_counter
    upcounter_testbench u2(inf);
 
    endmodule:top

Design code for counter

     //Design code for up counter
     module up_counter(clk,reset,counter);
     input clk, reset;
     output [2:0] counter;
     reg [2:0] counter_up;
     //up counter
     always @(posedge clk or posedge reset)
     begin
     //if reset=0 count will be incremented
     if(reset)
       counter_up <= 3'd0;
       else
       counter_up <= counter_up + 3'd1;
     end 
     assign counter = counter_up;
     endmodule:up_counter

Test bench for counter

     //testbench for up counter
     module upcounter_testbench(count_if inf);
     initial begin
     $display("\n // Parameterised interface example");
     //used to monitor the count values
     $monitor("\ncount=%0d",inf.counter);
     inf.clk=0;
     forever #5 inf.clk=~inf.clk;
     end
     initial begin
     inf.reset=1;
     #20;
     inf.reset=0;
     #70 $finish;
     end
     endmodule:upcounter_testbench

Here we’re considering 3 bit output, where the counter counts from 0 to 7.

Below figure shows the output of counter with parameterized interface.

para_interf1

                              Figure.6.Output for counter with parameterized interface

Github lab code link: https://github.com/muneeb-mbytes/SystemVerilog_Course/tree/production/interface/parameter_interface

Github lab output link: https://github.com/muneeb-mbytes/SystemVerilog_Course/blob/production/interface/parameter_interface/code.log


The parameter value can be updated in two ways

  • Pass constant value
  • Use the ‘defparam’ keyword

Pass constant value

Here the value of the parameter is passed to the interface by the top module instantiation of interface.
Example: count_if#(2) intf();
count_if is interface_name.
#(2)- is the parameter value passing to interface module.

Example:

interface module

     interface count_if #(parameter N);
     logic rst,clk;  
     logic [N:0] counter;  
     logic [N:0] counter_up;   
     endinterface:count_if  

top module

     module top();  

    //parameterised interface  
    //pass by constant value  
    count_if#(2) intf();  

    //design code of up_counter  
    up_counter u1(intf);  

    //testbench for up_counter  
    upcounter_testbench u2(intf);  

    endmodule:top  

OUTPUT:

The below Figure.7,The output of up_counter it count from 0 to 7. here the value of the parameter is passed to the interface in the top module instantiation.

pass_value

                               Fig.7: output of counter with pass by constant parameterized interface

Github lab code link: https://github.com/muneeb-mbytes/SystemVerilog_Course/tree/production/interface/Types_of_parameter_passing/pass_constant_parameter

Github lab output link: https://github.com/muneeb-mbytes/SystemVerilog_Course/blob/production/interface/Types_of_parameter_passing/pass_constant_parameter/code.log


Use the ‘defparam’ keyword

defparam is used for overridding the parameter value by using the hierarchical name instance.defparam allow the changing of parameter value during compilation time

Example:defparam intf.N=1;
Here intf is the handle of interface.
N is the parameter.

interface module

     interface count_if #(parameter N=4);
     // declaration of design signals
     logic rst,clk;
     logic [N:0] counter;
     logic [N:0] counter_up;
     endinterface:count_if

top module

     module top();

     //parameterised interface
     count_if intf();

     //Declaration of defparam 
     defparam intf.N=1;

     //instantiation of design module
     up_counter u1(intf);

     //testbench for up_counter
     upcounter_testbench u2(intf);

     endmodule:top

OUTPUT:

The Figure.8 below shows,The interface parameter value N=4. but at the top module instantiation by using the keyword defparam we can override the value of parameter.

defparam_output

                                Fig.8: output of counter with defparam parameterized interface

Github lab code link: https://github.com/muneeb-mbytes/SystemVerilog_Course/tree/production/interface/Types_of_parameter_passing/Defpram_parameter

Github lab output link: https://github.com/muneeb-mbytes/SystemVerilog_Course/blob/production/interface/Types_of_parameter_passing/Defpram_parameter/code.log


Modport:#

  • Modport is used to specifies the port directions to the signals declared within the interface. modports are declared inside the interface with the keyword modport.
  • modport is abbreviated as module port.

Characteristics of modports:

  • It can have, input, inout and output.
  • By specifying the port directions, modport provides access restrictions for signals.
  • The Interface can have any number of modports, the signal declared in the interface can be grouped in many modports.
  • modports are sythesizable.

Syntax:

modport identifier (input <port_list>, output<port_list>);

Example:

interface and_intr;    
  logic p,q;  
  logic r;  
  modport DUT_MP(input p,input q,output r);   
  modport TB_MP(output p,output q,input r);  
endinterface : and_intr  

Top module for AND gate while calling modport name in testbench and design file:

     // creating top module 
     // in this file design,testbench,interface modules are called
     module top();
     // interfce module called
     and_intr inf();
     // design module called
     and_gate a1(inf);
     // testbench module called   
     tb a2(inf);
     endmodule : top

design file for AND gate:

     // and gate design file  
     // module defination for and gate with interface instanciation  
     module and_gate(and_intr inf);
     // assign the output using continuous assignment
       assign inf.DUT_MP.r = (inf.DUT_MP.p) & (inf.DUT_MP.q); 
     endmodule : and_gate   

testbench file for AND gate:

    // testbench file for and gate design
    // module defination for testbench with interface instanciation
    module tb(and_intr inf);

     initial
        begin
          $display("// and gate output using modports\n");
          repeat(5)
            begin
              inf.TB_MP.p = $random;
              #1;
              inf.TB_MP.q = $random;
              #1;
              $display("input_p=%b\t input_q=%b\t output_r=%b",inf.TB_MP.p,inf.TB_MP.q,inf.TB_MP.r);
            end
        end
    endmodule : tb

Output of AND gate using modports in interface remains same for both of the above mentioned ways. it showing in below figure.

modport_andgate

                            Figure.9 AND gate Output

Github lab code link: https://github.com/muneeb-mbytes/SystemVerilog_Course/tree/production/interface/modports

Github lab output link: https://github.com/muneeb-mbytes/SystemVerilog_Course/blob/production/interface/modports/modport.log


Clocking Block#

The clocking block is defined that the mechanism to synchronize sampling and driving of input and output signal with respect to clock event. It is quite useful to use clocking blocks inside a testbench to avoid race condition in simulation. We can make timing explicitly when signals are synchronous to a particular clock. Clocking block can only declared inside a module ,interface. Clocking block only deals with how the inputs and outputs are sampled and synchronized. Assigning a value to a variable is done by module, interface not the clocking block.

Clocking block terminologies#

1. Clocking event

clocking  clockingblock_name  @(posedge clk);

The event specification used to synchronize the clocking block, @(posedge clk) is the clocking event.

2. Clocking signal

input  from_Dut;  
output to_Dut; 

Signals sampled and driven by the clocking block, from_DUT and to_DUT are the clocking signals,

3. Clocking skew

Clocking skew specifies with respect to at which input and output clocking signals are to be sampled or driven respectively. A skew must be a constant expression and can be specified as a parameter.

Input and Output skews

default input #1step output #0;

The default input skew and output skew is declared like this, default input #1step output #0; .Here default input skew takes #1step delay for sampling process to get the stable input. The output skew takes only #0 delay means that we get the stable output at the current time slot itself.

The below figure shows that default input and output skew

image

                                    Fig.10: default input #1step output #0  

The below figure shows the Input skew and output skew

image

                                     Fig.11: Input skew and output skew    

Input signals are sampling with respect to the clock event. If an input skew is specified then the signal is sampled at skew time units before the clock event. Then the output signals are driving skew simulation time units after the corresponding clock event. Input skew is implicitly negative because it happens before the clock.

Eg. default input #3ps output #2

Syntax:

clocking cb @(posedge clk); 
default input #1step output #0;
input  from_Dut; 
output to_Dut; 
endclocking     

Example:D_flipflop

clocking block (1)

                       Fig.12: Block diagram of d_ff design

The above figure.13 shows the design block diagram for d_ff. The Interface connect the DUT and test. The test provide the randomized value d. it is driven to DUT through interface. The DUT gives the sampled value q. The sampled value q is given as input to the test. Here the top module consist of all blocks such as test, interface and DUT. The instance of each block is created in top module.


posedge_df_cb

           Fig.13: Block diagram for triggering the DUT and Test at posedge clock 

In this example both DUT (clocking block clock) and Test is triggered at positive edge (interface clock).
Here in this scenario, the wave form output and display statement output is mismatching.
the output is shown in the below Figure.12

Example code:

DUT code:

     // module:d_flipflop   
     module d_flipflop(dff.dut intf);  
 
       //clocking block cd  
       always @(intf.cd)  

       //Non-Blocking assignment   
       intf.cd.q <= intf.cd.d;  
 
     endmodule : d_flipflop    

Interface code:

     //module: Interface  
     interface dff(input clk);    

       //declare the signals    
       logic d;   
       logic q;   

       //Clocking block for dut    
       clocking cd @(posedge clk);   
         default input #1step output #0;   
         output q;   
         input d;    
       endclocking     
  
       //modport for dut    
       modport dut(clocking cd);    
       //modport for tb     
       modport tb(input q, output d, input clk);`   
   
     endinterface: dff    

Test code:

     //module: test
     module test(dff.tb intf);
  
       //task:drv
       task drv;
         //loop
         repeat(10)
           begin
           //test triggering at posedge
           @(posedge intf.clk )

           //randomzing the d
           intf.d <= $random;
           $display("test side[%0t]=d_tb_drive:%d q_dut_sample:%d",$time,intf.d, intf.q);
         end
         $finish;
       endtask 
  
       //calling the task drv
       initial begin
         drv();
       end 
     endmodule :test

Top module

     //including the file test.sv and interface.sv  
     `include "test.sv"      
     `include "interface.sv"    
 
     module top;
       bit clk=1;
       initial
         forever #5 clk = ~clk;

       //creating interface instance
       dff intf(clk);

       //d_flipflop instance
       d_flipflop t1(intf);  

       //test Instance
       test t2(intf);

       initial
         $monitor("DUT side [%0t]=d_tb_drive:%d q_dut_sample:%d",$time,intf.cd.d, intf.cd.q);
     endmodule : top

Figure:14,In below output, first give posedge for both DUT and test. In this example at 0 time both DUT and test output is x, after at 10ns test randomize d value x to 0, that time Dut get 0 and give sample q output to 0, this clock cycle test(tb) only randomize value d = 0 but test(tb) take sampled previous value q = x.
At 10ns my DUT give output d = 0 and q = 0, and at that time my test(tb) give output d = 0 and q = x. Now, at 20ns test randomize d value 0 to 1, At that time DUT get 1 and give sampled q output to 1, this clock cycle test(tb) only randomize value d = 1 but test(tb) takes sampled previous value q = 0. at 20ns my DUT give output d = 1 and q = 1, and at that time my test give output d = 1 and q = 0.

Transcript output

The below Figure.14 shows the output of dflipflop.

case_t1

                  Fig.14: Transcript output of above example.

output of d_ff for all clock cycles

The below figure.15 shows the output waveform of d_flipflop.

case1

                     Fig.15: output waveform of case1.

Github lab code link: https://github.com/muneeb-mbytes/SystemVerilog_Course/tree/production/interface/Clocking_Block/clocking_block_example

Github lab output link: https://github.com/muneeb-mbytes/SystemVerilog_Course/blob/production/interface/Clocking_Block/clocking_block_example/top.log


Advantages of Clocking Block

  • Clocking block provides race free condition between testbench and DUT.
  • Clocking block can be declared inside interface, module.
  • Clocking block helps the user to write testbenches with higher level of abstraction.
  • Simulation is more faster.
  • Separating clocking activities of design from its data assignments activities.
  • Save amount of code and time in design execution.

Virtual Interface#

  • The virtual interface is a variable that represent the interface instance.

  • The virtual interface is used to create a interface instance in the class because the interface is a static component and the system verilog test bench is a dynamic component. we cannot directly declare the interface in the class by using the variable virtual we can declare the interface instance in the class

    synatax: virtual interface_name instance_name;

interface_name: name of the interface
instance_name: name of the virtual interface instance can be used in the class with variables Ex: vif.variable;

  • The virtual interface must be initialized in the class pointing to actual interface. Declaration of virtual interface in the class Example: Virtual intf vif;

  • Accessing of uninitialized virtual interface result in fatal error.

  • The virtual interface can be passed as argument to the task and function methods.

  • The virtual interface can be a property of class and which is initialized by using the function argument i.e it can call the actual interface in the particular class and create the instance of interface in that class

    Example: function new(virtual intf vif);

  • The virtual interface can be passed as argument to the function method Calling the actual interface ‘intf’ to declare the virtual interface in the class using the procedure or in function argument by using new() construct.

  • The interface variables can be accessed by virtual interface handles inside the class function and task methods as virtual_instance_name.variable;

Example : vif.a

vif is a virtual_instance_name;
a is the variable/property of class

  • The keyword/signal virtual interface variable represent the different interface instance in different time through out the simulation time

    syntax:

interface <interface_name>(); 
               <port_list>; 
                .......... 
                endmodule 
           To connect static(interface module) to
           to dynamic(class) we use virtual interface  
            class clase_name;  
             virtual <interface_name> <interface_instance>;  
             ....... 
           properties; 
             ..... 
           function() 
            .....
          endfunction 
            task();  
             ...... 
             endtask 
             endclass   

Example1: Fulladder

Design code of Full adder

    //Module:fullinput_adder  
    module fulladder(in_a,in_b,in_c,out_sum,out_carry) ;    
    //Declaration of input variables
     input in_a,in_b,in_c;
     //Declaration of output variables
      output out_sum;
      output out_carry;
    //continuous input_assignment statement
     assign out_sum = in_a^in_b^in_c;
     assign out_carry = (in_a&in_b)|(in_b&in_c)| (in_c&in_a);  
      endmodule:fulladder  

Interface module of full adder

     interface adder();
     //declaring the signals
      logic in_a,in_b,in_c;
      logic out_sum,out_carry;
     endinterface

Virtual Interface declaration inside the class

     //class:driver  
     class driver;  
      //Declaration of virtual interface  
      //syntax: virtual interface_name interface_instance;
      virtual adder vif;
      //constructor  
      function new(virtual adder vif);  
      //this.vif refer to class driver  
      //vif refer to the function argument  
      this.vif = vif;  
      endfunction  
      //task  
      task run();  
      repeat(10) begin  
      //interface_instance.variable  
      vif.in_a = $random;  
      vif.in_b = $random;  
      vif.in_c = $random;  
      $display("");  
      $display("//INPUT:Inputs of full adder  \n a=%0b, b=%0b, cin =%0b", vif.in_a,vif.in_b, vif.in_c);  
      #5;  
      $display("");  
      $display("//OUTPUT:Outputs of full adder \n sum=%0b, carry = %0b\n", vif.out_sum, vif.out_carry);  
      end  
      endtask  
     endclass  

Test module of full adder

    `//include the driver file  
      include "driver.sv"  
      //module:test  
     module test(adder intf);  
    //declaring the driver instance  
     driver drv;  
     initial  
     begin  
     //creating the driver instance  
     drv = new(intf);  
    //calling the task run  
     drv.run();  
     end  
    endmodule:test  

Top module of full adder

      //including the test.sv and interface.sv files
    `include "test.sv"
    `include "interface.sv"
 
     //module:top
     module top;
    //creating  an instance of interface
     adder intf();
 
    // the instance of test  t1.
     test t1(intf);
 
    //fulladder DUT instance , connecting the interface signal to instance DUT
    fulladder dut(.in_a(intf.in_a), .in_b(intf.in_b), .in_c(intf.in_c), .out_sum(intf.out_sum), .out_carry(intf.out_carry));
    endmodule

Below figure shows the design block of the code:

fulladder

                                 Fig.16: Design Block diagram

Here in the Figure:16, the driver is a class here we declare the virtual interface because inside the class we cannot call the interface directly because interface is static component and class is dynamic component. so this virtual keyword is used to create the instance in the class (it will create the virtual interface) inside the class. In driver we generates the random stimulus and send to the interface, the DUT is connected to interface. The DUT output is given to the interface. The test block consist of class component i.e (driver.sv) and the top module consist of all the component such as test, interface and DUT. The instance of all component is created in the Top module/block.

Below figure shows the output of full adder:
Here in the Figure.17 shows the output of full adder where a, b & cin are the input of the full adder, sum and carry are the output of the fulladder

fulladder2

                                Fig.17: Output of Full adder 

Github lab code link: https://github.com/muneeb-mbytes/SystemVerilog_Course/tree/production/interface/Virtual_interface

Github output code link: https://github.com/muneeb-mbytes/SystemVerilog_Course/blob/production/interface/Virtual_interface/top.log