13.Interface - vineethkumarv/SystemVerilog_Course GitHub Wiki
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.
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
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.
Figure.2.top module without interface
SystemVerilog Interface
Below diagram shows connecting design and testbench with the interface.
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
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.
Figure.5.output for AND gate with interface
Github lab code link: https://github.com/muneeb-mbytes/SystemVerilog_Course/tree/b7_team_kachori/interface/interface_example
Github lab output link: https://github.com/muneeb-mbytes/SystemVerilog_Course/blob/b7_team_kachori/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).
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.
Figure.6.Output for counter with parameterized interface
Github lab code link: https://github.com/muneeb-mbytes/SystemVerilog_Course/tree/b7_team_kachori/interface/parameter_interface
Github lab output link: https://github.com/muneeb-mbytes/SystemVerilog_Course/blob/b7_team_kachori/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.
Fig.7: output of counter with pass by constant parameterized interface
Github lab code link: https://github.com/muneeb-mbytes/SystemVerilog_Course/tree/b7_team_kachori/interface/Types_of_parameter_passing/pass_constant_parameter
Github lab output link: https://github.com/muneeb-mbytes/SystemVerilog_Course/blob/b7_team_kachori/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.
Fig.8: output of counter with defparam parameterized interface
Github lab code link: https://github.com/muneeb-mbytes/SystemVerilog_Course/tree/b7_team_kachori/interface/Types_of_parameter_passing/Defpram_parameter
Github lab output link: https://github.com/muneeb-mbytes/SystemVerilog_Course/blob/b7_team_kachori/interface/Types_of_parameter_passing/Defpram_parameter/code.log
- 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 input_p,input input_q,output output_r);
modport TB_MP(output input_p,output input_q,input output_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.DUT_MP);
// testbench module called
tb a2(inf.TB_MP);
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.
Figure.9 AND gate Output
Github lab code link: https://github.com/muneeb-mbytes/SystemVerilog_Course/tree/b7_team_kachori/interface/modports
Github lab output link: https://github.com/muneeb-mbytes/SystemVerilog_Course/blob/b7_team_kachori/interface/modports/modport.log
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.
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
Fig.10: default input #1step output #0
The below figure shows the Input skew and output skew
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
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.
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.
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.
Fig.15: output waveform of case1.
Github lab code link: https://github.com/muneeb-mbytes/SystemVerilog_Course/tree/b7_team_kachori/interface/Clocking_Block/clocking_block_case1
Github lab output link: https://github.com/muneeb-mbytes/SystemVerilog_Course/blob/b7_team_kachori/interface/Clocking_Block/clocking_block_case1/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.
-
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:
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
Fig.17: Output of Full adder
Github lab code link: https://github.com/muneeb-mbytes/SystemVerilog_Course/tree/b7_team_kachori/interface/Virtual_interface
Github output code link: https://github.com/muneeb-mbytes/SystemVerilog_Course/blob/b7_team_kachori/interface/Virtual_interface/top.log