Support invoking interface methods on virtual interface variables (#4774) (#4775)

This commit is contained in:
Jordan McConnon 2023-12-21 12:49:07 +00:00 committed by GitHub
parent 016e630ecf
commit 56d6791205
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 268 additions and 0 deletions

View File

@ -86,6 +86,7 @@ John Coiner
John Demme
John Wehle
Jonathan Drolet
Jordan McConnon
Jose Loyola
Josep Sans
Joseph Nwabueze

View File

@ -811,6 +811,7 @@ public:
TableMap& tableMap() { return m_tableMap; }
const TableMap& tableMap() const { return m_tableMap; }
};
class AstIfaceRefDType final : public AstNodeDType {
// Reference to an interface, either for a port, or inside parent cell
// @astgen op1 := paramsp : List[AstPin]
@ -846,6 +847,7 @@ public:
addParamsp(paramsp);
}
ASTGEN_MEMBERS_AstIfaceRefDType;
// METHODS
void dump(std::ostream& str = std::cout) const override;
void dumpSmall(std::ostream& str) const override;

View File

@ -919,6 +919,7 @@ class LinkDotFindVisitor final : public VNVisitor {
UINFO(5, "Module not under any CELL or top - dead module: " << nodep << endl);
}
}
void visit(AstClass* nodep) override {
UASSERT_OBJ(m_curSymp, nodep, "Class not under module/package/$unit");
UINFO(8, " " << nodep << endl);

View File

@ -74,6 +74,16 @@ private:
if (AstClass::isCacheableChild(itemp)) memberInsert(mmapr, itemp);
}
}
} else if (const AstIface* const anodep = VN_CAST(nodep, Iface)) {
for (AstNode* itemp = anodep->stmtsp(); itemp; itemp = itemp->nextp()) {
if (const AstScope* const scopep = VN_CAST(itemp, Scope)) {
for (AstNode* blockp = scopep->blocksp(); blockp; blockp = blockp->nextp()) {
memberInsert(mmapr, blockp);
}
} else {
memberInsert(mmapr, itemp);
}
}
} else if (const AstNodeUOrStructDType* const anodep
= VN_CAST(nodep, NodeUOrStructDType)) {
for (AstNode* itemp = anodep->membersp(); itemp; itemp = itemp->nextp()) {

View File

@ -2941,6 +2941,8 @@ class WidthVisitor final : public VNVisitor {
methodCallQueue(nodep, adtypep);
} else if (AstClassRefDType* const adtypep = VN_CAST(fromDtp, ClassRefDType)) {
methodCallClass(nodep, adtypep);
} else if(AstIfaceRefDType* const adtypep = VN_CAST(fromDtp, IfaceRefDType)) {
methodCallIfaceRef(nodep, adtypep);
} else if (AstUnpackArrayDType* const adtypep = VN_CAST(fromDtp, UnpackArrayDType)) {
methodCallUnpack(nodep, adtypep);
} else if (AstConstraintRefDType* const adtypep = VN_CAST(fromDtp, ConstraintRefDType)) {
@ -3524,6 +3526,38 @@ class WidthVisitor final : public VNVisitor {
<< "() should be handled");
}
}
void methodCallIfaceRef(AstMethodCall* nodep, AstIfaceRefDType* adtypep) {
AstIface* const ifacep = adtypep->ifacep();
UINFO(1, __FUNCTION__ << ":" << nodep << endl);
if (AstNodeFTask* const ftaskp
= VN_CAST(m_memberMap.findMember(ifacep, nodep->name()), NodeFTask)) {
UINFO(1, __FUNCTION__ << "AstNodeFTask" << nodep << endl);
userIterate(ftaskp, nullptr);
if (ftaskp->isStatic()) {
AstNodeExpr* argsp = nullptr;
if (nodep->pinsp()) argsp = nodep->pinsp()->unlinkFrBackWithNext();
AstNodeFTaskRef* newp = nullptr;
if (VN_IS(ftaskp, Task)) {
newp = new AstTaskRef{nodep->fileline(), ftaskp->name(), argsp};
} else {
newp = new AstFuncRef{nodep->fileline(), ftaskp->name(), argsp};
}
newp->taskp(ftaskp);
newp->classOrPackagep(ifacep);
nodep->replaceWith(newp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
} else {
nodep->taskp(ftaskp);
nodep->dtypeFrom(ftaskp);
nodep->classOrPackagep(ifacep);
if (VN_IS(ftaskp, Task)) nodep->dtypeSetVoid();
processFTaskRefArgs(nodep);
}
return;
}
nodep->v3error( "Member reference from interface to " << nodep->prettyNameQ()
<< " is not referencing a valid task or function ");
}
void methodCallClass(AstMethodCall* nodep, AstClassRefDType* adtypep) {
// No need to width-resolve the class, as it was done when we did the child
AstClass* const first_classp = adtypep->classp();

View File

@ -0,0 +1,24 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
scenarios(simulator => 1);
compile(
v_flags2 => ["--binary"],
verilator_make_gmake => 0,
make_main => 0,
);
execute(
check_finished => 1,
);
ok(1);
1;

View File

@ -0,0 +1,136 @@
// Copyright 2003 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
// Create stimulus and Drive the interface
class DriverStim;
protected virtual example_if v_if;
task run();
bit[7:0] x;
bit[7:0] y;
v_if.reset();
forever begin
x++;
y++;
$display("[DriverStim] initiating calculation, x: %8b y: %8b", x, y);
v_if.initiate_calculation(x, y);
end
endtask: run
function void bind_if(virtual example_if v_if);
this.v_if = v_if;
endfunction: bind_if
endclass: DriverStim
// Monitor returns from interface and check them
class MonitorCheck;
localparam NUM_TXNS = 10;
protected virtual example_if v_if;
task run();
logic[8:0] result;
int txns_received = 0;
forever begin
v_if.wait_for_result(result);
$display(
"[MonitorCheck] (%d) result %7b carry_out %1b",
txns_received, result[7:0], result[8]
);
if(++txns_received == NUM_TXNS) begin
$write("*-* All Finished *-*\n");
$finish();
end
end
endtask: run
function void bind_if(virtual example_if v_if);
this.v_if = v_if;
endfunction: bind_if
endclass: MonitorCheck
module example(
input logic clk,
input logic rstn,
input logic[7:0] x,
input logic[7:0] y,
output logic[8:0] z
);
// 8 bit full adder
always_ff @(posedge clk)
if(!rstn) z <= '0;
else z <= x + y;
endmodule: example
// interfaces with the DUT
interface example_if();
localparam CLK_FREQ_MHz = 400;
localparam CLK_PERIOD = 1/((CLK_FREQ_MHz * 1e6) * (1e-12));
logic clk;
logic rstn;
logic[7:0] x;
logic[7:0] y;
logic[8:0] z;
initial begin: clk_gen
forever #(CLK_PERIOD/2) clk = !clk;
end: clk_gen
task reset();
$display("reset called");
rstn = 0;
@(posedge clk);
$display("clock tick");
rstn = 1;
@(posedge clk);
endtask: reset
event calc_clkd;
task initiate_calculation(
input logic[7:0] x_in,
input logic[7:0] y_in
);
x = x_in;
y = y_in;
@(posedge clk);
->calc_clkd;
endtask: initiate_calculation
task wait_for_result(output logic[8:0] result);
@(calc_clkd);
result = z;
endtask: wait_for_result
endinterface: example_if
module t(/*AUTOARG*/);
example_if example_if_inst();
example DUT(
.clk (example_if_inst.clk),
.rstn(example_if_inst.rstn),
.x (example_if_inst.x),
.y (example_if_inst.y),
.z (example_if_inst.z)
);
initial begin: main
DriverStim driverStim = new();
MonitorCheck monitorCheck = new();
driverStim.bind_if(example_if_inst);
monitorCheck.bind_if(example_if_inst);
fork
driverStim.run();
monitorCheck.run();
join_none
end: main
endmodule: t

View File

@ -0,0 +1,5 @@
%Error: t/t_virtual_interface_method_bad.v:11:10: Member reference from interface to 'x' is not referencing a valid task or function
: ... note: In instance 't'
11 | v_if.x();
| ^
%Error: Exiting due to

View File

@ -0,0 +1,20 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
scenarios(vlt => 1);
lint(
verilator_flags2 => ["--lint-only --language 1800-2017"],
fails => 1,
expect_filename => $Self->{golden_filename},
);
ok(1);
1;

View File

@ -0,0 +1,35 @@
// Copyright 2003 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
class ExampleClass;
localparam NUM_TXNS = 10;
protected virtual example_if v_if;
task run();
v_if.x();
endtask: run
function void bind_if(virtual example_if v_if);
this.v_if = v_if;
endfunction: bind_if
endclass: ExampleClass
interface example_if();
logic clk;
logic rstn;
logic[7:0] x;
endinterface: example_if
module t(/*AUTOARG*/);
example_if example_if_inst();
initial begin: main
ExampleClass exampleClass = new();
exampleClass.bind_if(example_if_inst);
exampleClass.run();
end: main
endmodule: t