- Interface
- MODPORT
- accessing interface in Class and Testbench top
- adding value to Interface
- Creating Generator and Driver Class
- Creating Monitor and Scoreboard Class
Interface
Adding Interface to Simple RTL
Design of an Adder
module add(
input [3:0] a,b,
output [4:0] sum
);
assign sum = a + b;
endmodule
interface add_if;
logic [3:0] a;
logic [3:0] b;
logic [4:0] sum;
endinterface
module tb;
and_if aif();
//add dut (aif.a, aif.b, aif.sum); ////Positional Map
add dut (.b(aif.b), .a(aif.a), .sum(aif.sum)); ///mapping by name
initial begin
aif.a = 4;
aif.b = 4;
#10;
aif.a = 3;
#10;
aif.b = 7;
end
initial begin
$dumpfile("dump.vcd");
$dumpvars;
endmodule
Using blocking operator for Interface Variables
module add(
input [3:0] a,b,
output reg [4:0] sum,
input clk
);
always @(posedge clk)
begin
sum <= a + b;
end
endmodule
interface add_if;
logic [3:0] a;
logic [3:0] b;
logic [4:0] sum;
logic clk;
endinterface
module tb;
add_if aif();
//add dut (aif.a, aif.b, aif.sum); //positional map
add dut(.b(aif.b), .a(aif.a), .sum(aif.sum), .clk(aif.clk)); //mapping by name
initial begin
aif.clk = 0;
end
always #10 aif.clk = ~aif.clk;
initial begin
aif.a = 1;
aif.b = 5;
#22;
aif.a = 3;
#20;
aif.a = 4;
#8
aif.a = 5;
#20;
aif.a = 6;
end
initial begin
$dumpfile("dump.vcd");
$dumpvars;
#100;
$finish();
end
endmodule
Using Non-Blocking Operator for Interface Variables
module add(
input [3:0] a,b,
output reg [4:0] sum,
input clk
);
always @(posedge clk)
begin
sum <= a + b;
end
endmodule
interface add_if;
logic [3:0] a;
logic [3:0] b;
logic [4:0] sum;
logic clk;
endinterface
module tb;
add_if aif();
//add dut (aif.a, aif.b, aif.sum); //positional map
add dut(.b(aif.b), .a(aif.a), .sum(aif.sum), .clk(aif.clk)); //mapping by name
initial begin
aif.clk = 0;
end
always #10 aif.clk = ~aif.clk;
initial begin
aif.a = 1;
aif.b = 5;
repeat(3) @(posedge aif.clk);
#22;
aif.a = 3;
#20;
aif.a = 4;
#8
aif.a = 5;
#20;
aif.a = 6;
end
initial begin
$dumpfile("dump.vcd");
$dumpvars;
#100;
$finish();
end
endmodule
Why we prefer LOGIC and WIRE over REG in Interface
module add(
input [3:0] a,b,
output [4:0] sum
);
assign sum = a + b;
endmodule
interface add_if;
wire [3:0] a;
wire [3:0] b;
wire [4:0] sum;
endinterface
////// interface with all reg type, then we are not allowed to connect variable in interface to the output port of DUT
///// interface with all wire type, we are not allowed to apply stimulus using initial or always
module tb;
add_if aif();
add dut (aif.a, aif.b, aif.sum); ////Positional Map
initial begin
aif.a = 1;
aif.b = 3;
end
initial begin
$dumpfile("dump.vcd");
$dumpvars;
endmodule
Adding Driver Code to Interface
module add(
input [3:0] a,b,
output reg [4:0] sum,
input clk
);
always @(posedge clk)
begin
sum <= a + b;
end
endmodule
interface add_if;
logic [3:0] a,b;
logic [4:0] sum;
logic clk;
endinterface
class driver;
virtual add_if aif;
task run();
forever begin
@(posedge aif.clk);
aif.a <= 3;
aif,b <= 3;
end
endtask
endclass
module tb;
add_if aif();
add dut(.a(aif.a),.b(aif.b), .sum(aif.sum), .clk(aif.clk));
initial begin
aif.clk <= 0;
end
always #10 aif.clk <= ~aif.clk;
driver drv;
initial begin
drv = new();
drv.aif = aif;
drv.run();
end
initial begin
$dumpfile("dump.vcd");
$dumpvars;
#100;
$finish();
end
endmodule
Understanding MODPORT
module add(
input [3:0] a,b,
output reg [4:0] sum,
input clk
);
always @(posedge clk)
begin
sum <= a + b;
end
endmodule
interface add_if;
logic [3:0] a;
logic [3:0] b;
logic [4:0] sum;
logic clk;
modport DRV (output a,b, input sum,clk);
endinterface
class driver;
virtual add_if aif;
task run();
forever begin
@(posedge aif.clk);
aif.a <= 2;
aif.b <= 3;
$display("[DRV] : Interface Trigger");
end
endtask
endclasss
module tb;
add_if aif();
driver drv;
add dut (aif.a, aif.b, aif.sum, aif.clk );
initial begin
aif.clk <= 0;
end
always #10 aif.clk <= ~aif.clk;
initial begin
drv = new();
drv.aif = aif;
drv.run();
end
initial begin
$dumpfile("dump.vcd");
$dumpvars;
#100;
$finish
end
endmodule
Adding Generator
module add(
input [3:0] a,b,
output reg [4:0] sum,
input clk
);
always @(posedge clk)
begin
sum <= a + b;
end
endmodule
////1.add transaction constructor in generator custom constructor
////2.Send deep copy of transaction between generator and driver
class transaction;
randc bit [3:0] a;
randc bit [3:0] b;
bit [4:0] sum;
function void display();
$display("a : %0d \t b: %0d \t sum : %0d",a,b,sum)
endfunction
function transaction copy();
copy = new();
copy.a = this.a;
copy.b = this.b;
copy.sum = this.sum;
endfunction
endclass
class generator;
transaction trans;
mailbox #(transaction) mbx;
event done;
function new(mailbox #(transaction) mbx);
this.mbx = mbx;
trans = new();
endfunction
function transaction copy();
task run();
//trans = new();
for(int i = 0; i<10, i++) begin
trans.randomize();
mbx.put(trans.copy);
$display("[GEN] : DATA SENT TO DRIVER");
trans.display();
#20;
end
-> done;
endtask
endclass
interface add_if;
logic [3:0] a;
logic [3:0] b;
logic [4:0] sum;
logic clk;
endinterface
class driver;
virtual add_if aif;
mailbox #(transaction) mbx;
transaction data;
event next;
function new(mailbox #(transaction) mbx);
this.mbx = mbx;
endfunction
task run();
forever begin
mbx.get(data);
@(posedge aif.clk);
aif.a <= data.a;
aif.b <= data.b;
$display("[DRV] : Interface Trigger");
data.display();
end
endtask
endclasss
module tb;
add_if aif();
driver drv;
generator gen;
event done;
mailbox #(transaction) mbx;
add dut (aif.a, aif.b, aif.sum, aif.clk );
initial begin
aif.clk <= 0;
end
always #10 aif.clk <= ~aif.clk;
initial begin
mbx = new();
drv = new(mbx);
gen = new(mbx);
drv.aif = aif;
done = gen.done;
end
initial begin
fork
gen.run();
drv.run();
join_none
wait(done.triggered);
$finish();
end
initial begin
$dumpfile("dump.vcd");
$dumpvars;
end
endmodule
Injecting Error
module add(
input [3:0] a,b,
output reg [4:0] sum,
input clk
);
always @(posedge clk)
begin
sum <= a + b;
end
endmodule
////1.add transaction constructor in generator custom constructor
////2.Send deep copy of transaction between generator and driver
class transaction;
randc bit [3:0] a;
randc bit [3:0] b;
bit [4:0] sum;
function void display();
$display("a : %0d \t b: %0d \t sum : %0d",a,b,sum)
endfunction
endclass
virtual function transaction copy();
copy = new();
copy.a = this.a;
copy.b = this.b;
copy.sum = this.sum;
endfunction
endclass
class error extends transaction;
// constraint data_c (a == 0; b == 0;)
virtual function transaction copy();
copy = new();
copy.a = 0;
copy.b = 0;
copy.sum = this.sum;
endfunction
endclass
class generator;
transaction trans;
mailbox #(transaction) mbx;
event done;
function new(mailbox #(transaction) mbx);
this.mbx = mbx;
trans = new();
endfunction
function transaction copy();
task run();
//trans = new();
for(int i = 0; i<10, i++) begin
trans.randomize();
mbx.put(trans.copy);
$display("[GEN] : DATA SENT TO DRIVER");
trans.display();
#20;
end
-> done;
endtask
endclass
interface add_if;
logic [3:0] a;
logic [3:0] b;
logic [4:0] sum;
logic clk;
endinterface
class driver;
virtual add_if aif;
mailbox #(transaction) mbx;
transaction data;
event next;
function new(mailbox #(transaction) mbx);
this.mbx = mbx;
trans = new();
endfunction
task run();
forever begin
mbx.get(data);
@(posedge aif.clk);
aif.a <= data.a;
aif.b <= data.b;
$display("[DRV] : Interface Trigger");
data.display();
end
endtask
endclasss
module tb;
add_if aif();
driver drv;
generator gen;
event done;
error err;
mailbox #(transaction) mbx;
add dut (aif.a, aif.b, aif.sum, aif.clk );
initial begin
aif.clk <= 0;
end
always #10 aif.clk <= ~aif.clk;
initial begin
mbx = new();
err = new();
drv = new(mbx);
gen = new(mbx);
gen.trans = err;
drv.aif = aif;
done = gen.done;
end
initial begin
fork
gen.run();
drv.run();
join_none
wait(done.triggered);
$finish();
end
initial begin
$dumpfile("dump.vcd");
$dumpvars;
end
endmodule
Adding Monitor and Scoreboard
module add(
input [3:0] a,b,
output reg [4:0] sum,
input clk
);
always @(posedge clk)
begin
sum <= a + b;
end
endmodule
////1.add transaction constructor in generator custom constructor
////2.Send deep copy of transaction between generator and driver
class transaction;
randc bit [3:0] a;
randc bit [3:0] b;
bit [4:0] sum;
function void display();
$display("a : %0d \t b: %0d \t sum : %0d",a,b,sum)
endfunction
endclass
interface add_if;
logic [3:0] a;
logic [3:0] b;
logic [4:0] sum;
logic clk;
endinterface
class monitor;
mailbox #(transaction) mbx;
transaction trans;
virtual add_if aif;
function new(mailbox #(transaction) mbx);
this.mbx = mbx;
endfunction
task run();
trans = new();
forever begin
@(posedge aif.clk);
trans.a = aif.a;
// Operator used to add value to data member of transaction must be "="
trans.b = aif.b;
trans.sum = aif.sum;
$display("[MON]: DATA SENT TO SCOREBOARD");
trans.display();
mbx.put(trans)
end
endtask
endclass
class scoreboard;
mailbox #(transaction) mbx;
transaction trans;
function new(mailbox #(transaction) mbx);
this.mbx = mbx;
endfunction
task run();
forever begin
mbx.get(trans);
$display("[SCO] : DATA RCVD FROM MONITOR")
trans.display();
#20;
end
endtask
endclass
module tb;
add_if aif();
monitor mon;
scoreboard sco;
mailbox #(transaction) mbx;
add dut (aif.a, aif.b, aif.sum, aif.clk );
initial begin
aif.clk <= 0;
end
always #10 aif.clk <= ~aif.clk;
initial begin
for (int i = 0; i < 20; i++) begin
@(posedge aif.clk);
aif.a <= $urandom_range(0,15);
aif.b <= $urandom_range(0,15);
end
end
initial begin
mbx = new();
mon = new(mbx);
sco = new(mbx);
mon.aif = aif;
end
initial begin
fork
mon.run();
sco.run();
join
end
initial begin
$dumpfile("dump.vcd");
$dumpvars;
#450;
$finish();
end
endmodule
Tweaking Monitor and Scoreboard Code
module add(
input [3:0] a,b,
output reg [4:0] sum,
input clk
);
always @(posedge clk)
begin
sum <= a + b;
end
endmodule
////1.add transaction constructor in generator custom constructor
////2.Send deep copy of transaction between generator and driver
class transaction;
randc bit [3:0] a;
randc bit [3:0] b;
bit [4:0] sum;
function void display();
$display("a : %0d \t b: %0d \t sum : %0d",a,b,sum)
endfunction
endclass
interface add_if;
logic [3:0] a;
logic [3:0] b;
logic [4:0] sum;
logic clk;
endinterface
class monitor;
mailbox #(transaction) mbx;
transaction trans;
virtual add_if aif;
function new(mailbox #(transaction) mbx);
this.mbx = mbx;
endfunction
task run();
trans = new();
forever begin
repeat(2)@(posedge aif.clk);
trans.a = aif.a;
// Operator used to add value to data member of transaction must be "="
trans.b = aif.b;
trans.sum = aif.sum;
$display("[MON]: DATA SENT TO SCOREBOARD");
trans.display();
mbx.put(trans)
end
endtask
endclass
class scoreboard;
mailbox #(transaction) mbx;
transaction trans;
function new(mailbox #(transaction) mbx);
this.mbx = mbx;
endfunction
task run();
forever begin
mbx.get(trans);
$display("[SCO] : DATA RCVD FROM MONITOR")
trans.display();
#40;
end
endtask
endclass
module tb;
add_if aif();
monitor mon;
scoreboard sco;
mailbox #(transaction) mbx;
add dut (aif.a, aif.b, aif.sum, aif.clk );
initial begin
aif.clk <= 0;
end
always #10 aif.clk <= ~aif.clk;
initial begin
for (int i = 0; i < 20; i++) begin
repeat(2)@(posedge aif.clk);
aif.a <= $urandom_range(0,15);
aif.b <= $urandom_range(0,15);
end
end
initial begin
mbx = new();
mon = new(mbx);
sco = new(mbx);
mon.aif = aif;
end
initial begin
fork
mon.run();
sco.run();
join
end
initial begin
$dumpfile("dump.vcd");
$dumpvars;
#450;
$finish();
end
endmodule
Adding Simple Scoreboard Model
module add(
input [3:0] a,b,
output reg [4:0] sum,
input clk
);
always @(posedge clk)
begin
sum <= a + b;
end
endmodule
////1.add transaction constructor in generator custom constructor
////2.Send deep copy of transaction between generator and driver
class transaction;
randc bit [3:0] a;
randc bit [3:0] b;
bit [4:0] sum;
function void display();
$display("a : %0d \t b: %0d \t sum : %0d",a,b,sum)
endfunction
endclass
interface add_if;
logic [3:0] a;
logic [3:0] b;
logic [4:0] sum;
logic clk;
endinterface
class monitor;
mailbox #(transaction) mbx;
transaction trans;
virtual add_if aif;
function new(mailbox #(transaction) mbx);
this.mbx = mbx;
endfunction
task run();
trans = new();
forever begin
repeat(2)@(posedge aif.clk);
trans.a = aif.a;
// Operator used to add value to data member of transaction must be "="
trans.b = aif.b;
trans.sum = aif.sum;
$display("-----------------------------------------------");
$display("[MON]: DATA SENT TO SCOREBOARD");
trans.display();
mbx.put(trans)
end
endtask
endclass
class scoreboard;
mailbox #(transaction) mbx;
transaction trans;
function new(mailbox #(transaction) mbx);
this.mbx = mbx;
endfunction
task compare(input transaction trans);
if((trans.sum) == (trans.a + trans.b))
$display("[SCO]: SUM RESULT MATCHED");
else
$error ("[SCO]: SUM RESULT MISMATCHED"); //$warning, $fatal
endtask
task run();
forever begin
mbx.get(trans);
$display("[SCO] : DATA RCVD FROM MONITOR")
trans.display();
compare(trans);
$display("-----------------------------------------------");
#40;
end
endtask
endclass
module tb;
add_if aif();
monitor mon;
scoreboard sco;
mailbox #(transaction) mbx;
add dut (aif.a, aif.b, aif.sum, aif.clk );
initial begin
aif.clk <= 0;
end
always #10 aif.clk <= ~aif.clk;
initial begin
for (int i = 0; i < 20; i++) begin
repeat(2)@(posedge aif.clk);
aif.a <= $urandom_range(0,15);
aif.b <= $urandom_range(0,15);
end
end
initial begin
mbx = new();
mon = new(mbx);
sco = new(mbx);
mon.aif = aif;
end
initial begin
fork
mon.run();
sco.run();
join
end
initial begin
$dumpfile("dump.vcd");
$dumpvars;
#450;
$finish();
end
endmodule