Skip to content

Commit

Permalink
Add an extra trigger for virtual interfaces
Browse files Browse the repository at this point in the history
Signed-off-by: Krzysztof Bieganski <[email protected]>
  • Loading branch information
kbieganski committed Nov 7, 2023
1 parent dc10118 commit d54ef81
Show file tree
Hide file tree
Showing 12 changed files with 141 additions and 14 deletions.
22 changes: 21 additions & 1 deletion src/V3AstNodeDType.h
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,7 @@ class AstIfaceRefDType final : public AstNodeDType {
// Reference to an interface, either for a port, or inside parent cell
// @astgen op1 := paramsp : List[AstPin]
private:
bool m_virtual = false; // True if virtual interface
FileLine* m_modportFileline; // Where modport token was
string m_cellName; // "" = no cell, such as when connects to 'input' iface
string m_ifaceName; // Interface name
Expand All @@ -840,9 +841,26 @@ class AstIfaceRefDType final : public AstNodeDType {
, m_cellName{cellName}
, m_ifaceName{ifaceName}
, m_modportName{modport} {}
AstIfaceRefDType(FileLine* fl, FileLine* modportFl, const string& cellName,
struct Virtual {};
AstIfaceRefDType(FileLine* fl, Virtual, const string& cellName, const string& ifaceName)
: ASTGEN_SUPER_IfaceRefDType(fl)
, m_virtual{true}
, m_modportFileline{nullptr}
, m_cellName{cellName}
, m_ifaceName{ifaceName}
, m_modportName{""} {}
AstIfaceRefDType(FileLine* fl, Virtual, FileLine* modportFl, const string& cellName,
const string& ifaceName, const string& modport)
: ASTGEN_SUPER_IfaceRefDType(fl)
, m_virtual{true}
, m_modportFileline{modportFl}
, m_cellName{cellName}
, m_ifaceName{ifaceName}
, m_modportName{modport} {}
AstIfaceRefDType(FileLine* fl, Virtual, FileLine* modportFl, const string& cellName,
const string& ifaceName, const string& modport, AstPin* paramsp)
: ASTGEN_SUPER_IfaceRefDType(fl)
, m_virtual{true}
, m_modportFileline{modportFl}
, m_cellName{cellName}
, m_ifaceName{ifaceName} {
Expand All @@ -861,6 +879,8 @@ class AstIfaceRefDType final : public AstNodeDType {
bool similarDType(const AstNodeDType* samep) const override { return this == samep; }
int widthAlignBytes() const override { return 1; }
int widthTotalBytes() const override { return 1; }
void isVirtual(bool flag) { m_virtual = flag; }
bool isVirtual() const { return m_virtual; }
FileLine* modportFileline() const { return m_modportFileline; }
string cellName() const { return m_cellName; }
void cellName(const string& name) { m_cellName = name; }
Expand Down
9 changes: 9 additions & 0 deletions src/V3AstNodeOther.h
Original file line number Diff line number Diff line change
Expand Up @@ -1198,6 +1198,7 @@ class AstNetlist final : public AstNode {
AstCFunc* m_evalp = nullptr; // The '_eval' function
AstCFunc* m_evalNbap = nullptr; // The '_eval__nba' function
AstVarScope* m_dpiExportTriggerp = nullptr; // The DPI export trigger variable
AstVarScope* m_virtIfaceTriggerp = nullptr; // The virtual interface trigger variable
AstVar* m_delaySchedulerp = nullptr; // The delay scheduler variable
AstVarScope* m_nbaEventp = nullptr; // The NBA event variable
AstVarScope* m_nbaEventTriggerp = nullptr; // If set to 1, the NBA event should get triggered
Expand Down Expand Up @@ -1228,6 +1229,8 @@ class AstNetlist final : public AstNode {
void evalNbap(AstCFunc* funcp) { m_evalNbap = funcp; }
AstVarScope* dpiExportTriggerp() const { return m_dpiExportTriggerp; }
void dpiExportTriggerp(AstVarScope* varScopep) { m_dpiExportTriggerp = varScopep; }
AstVarScope* virtIfaceTriggerp() const { return m_virtIfaceTriggerp; }
void virtIfaceTriggerp(AstVarScope* varScopep) { m_virtIfaceTriggerp = varScopep; }
AstVar* delaySchedulerp() const { return m_delaySchedulerp; }
void delaySchedulerp(AstVar* const varScopep) { m_delaySchedulerp = varScopep; }
AstVarScope* nbaEventp() const { return m_nbaEventp; }
Expand Down Expand Up @@ -2001,6 +2004,12 @@ class AstVar final : public AstNode {
string verilogKwd() const override;
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
VLifetime lifetime() const { return m_lifetime; }
bool isVirtIface() const {
if (AstIfaceRefDType* ifaceRefDtp = VN_CAST(dtypep(), IfaceRefDType)) {
return ifaceRefDtp->isVirtual();
}
return false;
}
void propagateAttrFrom(const AstVar* fromp) {
// This is getting connected to fromp; keep attributes
// Note the method below too
Expand Down
2 changes: 1 addition & 1 deletion src/V3Gate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ class GateVisitor final : public VNVisitor {
UINFO(6, "New vertex " << varscp << endl);
vertexp = new GateVarVertex{&m_graph, m_scopep, varscp};
varscp->user1p(vertexp);
if (varscp->varp()->isUsedVirtIface()) {
if (varscp->varp()->isUsedVirtIface() || varscp->varp()->isVirtIface()) {
// Can be used in a class method, which cannot be tracked statically
vertexp->clearReducibleAndDedupable("VirtIface");
vertexp->setConsumed("VirtIface");
Expand Down
1 change: 1 addition & 0 deletions src/V3Localize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ class LocalizeVisitor final : public VNVisitor {
&& !nodep->varp()->isFuncLocal() // Not already a function local (e.g.: argument)
&& !nodep->varp()->isStatic() // Not a static variable
&& !nodep->varp()->isClassMember() // Statically exists in design hierarchy
&& !nodep->varp()->isVirtIface() // Is not a virtual interface
&& !nodep->varp()->isUsedVirtIface() // Not used through a virtual interface
&& !nodep->varp()->valuep() // Does not have an initializer
) {
Expand Down
29 changes: 22 additions & 7 deletions src/V3Sched.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -507,16 +507,16 @@ struct TriggerKit {
m_funcp->stmtsp()->addHereThisAsNext(callp->makeStmt());
}

// Utility to set then clear the dpiExportTrigger trigger
void addDpiExportTriggerAssignment(AstVarScope* dpiExportTriggerVscp, uint32_t index) const {
FileLine* const flp = dpiExportTriggerVscp->fileline();
// Utility to set then clear an extra trigger
void addExtraTriggerAssignment(AstVarScope* extraTriggerVscp, uint32_t index) const {
FileLine* const flp = extraTriggerVscp->fileline();
AstVarRef* const vrefp = new AstVarRef{flp, m_vscp, VAccess::WRITE};
AstCMethodHard* const callp = new AstCMethodHard{flp, vrefp, "set"};
callp->addPinsp(new AstConst{flp, index});
callp->addPinsp(new AstVarRef{flp, dpiExportTriggerVscp, VAccess::READ});
callp->addPinsp(new AstVarRef{flp, extraTriggerVscp, VAccess::READ});
callp->dtypeSetVoid();
AstNode* const stmtp = callp->makeStmt();
stmtp->addNext(new AstAssign{flp, new AstVarRef{flp, dpiExportTriggerVscp, VAccess::WRITE},
stmtp->addNext(new AstAssign{flp, new AstVarRef{flp, extraTriggerVscp, VAccess::WRITE},
new AstConst{flp, AstConst::BitFalse{}}});
m_funcp->stmtsp()->addHereThisAsNext(stmtp);
}
Expand Down Expand Up @@ -840,7 +840,7 @@ AstNode* createInputCombLoop(AstNetlist* netlistp, AstCFunc* const initFuncp,
= createTriggers(netlistp, initFuncp, senExprBuilder, senTreeps, "ico", extraTriggers);

if (dpiExportTriggerVscp) {
trig.addDpiExportTriggerAssignment(dpiExportTriggerVscp, dpiExportTriggerIndex);
trig.addExtraTriggerAssignment(dpiExportTriggerVscp, dpiExportTriggerIndex);
}

// Remap sensitivities
Expand Down Expand Up @@ -1150,13 +1150,18 @@ void schedule(AstNetlist* netlistp) {

// Step 8: Create the pre/act/nba triggers
AstVarScope* const dpiExportTriggerVscp = netlistp->dpiExportTriggerp();
AstVarScope* const virtIfaceTriggerVscp = netlistp->virtIfaceTriggerp();

// We may have an extra trigger for variable updated in DPI exports
ExtraTriggers extraTriggers;
const size_t dpiExportTriggerIndex = dpiExportTriggerVscp
? extraTriggers.allocate("DPI export trigger")
: std::numeric_limits<unsigned>::max();

const size_t virtIfaceTriggerIndex = virtIfaceTriggerVscp
? extraTriggers.allocate("Virtual interface trigger")
: std::numeric_limits<unsigned>::max();

const auto& senTreeps = getSenTreesUsedBy({&logicRegions.m_pre, //
&logicRegions.m_act, //
&logicRegions.m_nba, //
Expand All @@ -1170,7 +1175,10 @@ void schedule(AstNetlist* netlistp) {
if (timingKit.m_postUpdates) actTrig.m_funcp->addStmtsp(timingKit.m_postUpdates);

if (dpiExportTriggerVscp) {
actTrig.addDpiExportTriggerAssignment(dpiExportTriggerVscp, dpiExportTriggerIndex);
actTrig.addExtraTriggerAssignment(dpiExportTriggerVscp, dpiExportTriggerIndex);
}
if (virtIfaceTriggerVscp) {
actTrig.addExtraTriggerAssignment(virtIfaceTriggerVscp, virtIfaceTriggerIndex);
}

AstVarScope* const actTrigVscp = actTrig.m_vscp;
Expand Down Expand Up @@ -1223,12 +1231,18 @@ void schedule(AstNetlist* netlistp) {
? createTriggerSenTree(netlistp, actTrig.m_vscp, dpiExportTriggerIndex)
: nullptr;

AstSenTree* const virtIfaceTriggeredAct
= virtIfaceTriggerVscp
? createTriggerSenTree(netlistp, actTrig.m_vscp, virtIfaceTriggerIndex)
: nullptr;

AstCFunc* const actFuncp = V3Order::order(
netlistp, {&logicRegions.m_pre, &logicRegions.m_act, &logicReplicas.m_act}, trigToSenAct,
"act", false, false, [&](const AstVarScope* vscp, std::vector<AstSenTree*>& out) {
auto it = actTimingDomains.find(vscp);
if (it != actTimingDomains.end()) out = it->second;
if (vscp->varp()->isWrittenByDpi()) out.push_back(dpiExportTriggeredAct);
if (vscp->varp()->isUsedVirtIface()) out.push_back(virtIfaceTriggeredAct);
});
splitCheck(actFuncp);
if (v3Global.opt.stats()) V3Stats::statsStage("sched-create-act");
Expand Down Expand Up @@ -1314,6 +1328,7 @@ void schedule(AstNetlist* netlistp) {
splitCheck(initp);

netlistp->dpiExportTriggerp(nullptr);
netlistp->virtIfaceTriggerp(nullptr);

V3Global::dumpCheckGlobalTree("sched", 0, dumpTreeLevel() >= 3);
}
Expand Down
1 change: 1 addition & 0 deletions src/V3SchedReplicate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ class SchedReplicateVarVertex final : public SchedReplicateVertex {
// the act region, which means combinational logic driven from a suspendable
// processes must be present in the 'act' region
if (varp()->isWrittenBySuspendable()) addDrivingRegions(ACTIVE);
if (varp()->isUsedVirtIface()) addDrivingRegions(ACTIVE);
}
AstVarScope* vscp() const { return m_vscp; }
AstVar* varp() const { return m_vscp->varp(); }
Expand Down
2 changes: 1 addition & 1 deletion src/V3Width.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2723,7 +2723,7 @@ class WidthVisitor final : public VNVisitor {
if (AstVar* const varp = VN_CAST(foundp, Var)) {
nodep->dtypep(foundp->dtypep());
nodep->varp(varp);
varp->usedVirtIface(true);
varp->usedVirtIface(adtypep->isVirtual());
return;
}
UINFO(1, "found object " << foundp << endl);
Expand Down
21 changes: 21 additions & 0 deletions src/Verilator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,27 @@ static void process() {
V3Timing::timingAll(v3Global.rootp());
}

const auto getVirtualInterfaceTriggerp = []() {
if (!v3Global.rootp()->virtIfaceTriggerp()) {
v3Global.rootp()->virtIfaceTriggerp(
v3Global.rootp()->topScopep()->scopep()->createTemp("__VvirtIfaceTrigger", 1));
}
return v3Global.rootp()->virtIfaceTriggerp();
};
v3Global.rootp()->foreach([&](AstNodeStmt* stmtp) {
// If this statement writes to a virtual interface
bool triggerVirtIface = stmtp->exists([](AstVarRef* varrefp) {
return varrefp->access().isWriteOrRW() && varrefp->varp()->isVirtIface();
});
if (!triggerVirtIface) return;
FileLine* flp = stmtp->fileline();
// Then set the trigger
// This surely breaks for AstAssignW
stmtp->addNextHere(new AstAssign(
flp, new AstVarRef{flp, getVirtualInterfaceTriggerp(), VAccess::WRITE},
new AstConst{flp, AstConst::BitTrue{}}));
});

// Create delayed assignments
// This creates lots of duplicate ACTIVES so ActiveTop needs to be after this step
V3Delayed::delayedAll(v3Global.rootp());
Expand Down
8 changes: 4 additions & 4 deletions src/verilog.y
Original file line number Diff line number Diff line change
Expand Up @@ -2134,12 +2134,12 @@ data_typeNoRef<nodeDTypep>: // ==IEEE: data_type, excluding class_ty

data_typeVirtual<nodeDTypep>: // ==IEEE: data_type after yVIRTUAL [ yINTERFACE ]
// // Parameters here are SV2009
id/*interface*/ { $$ = new AstIfaceRefDType{$<fl>1, "", *$1}; }
| id/*interface*/ '.' id/*modport*/ { $$ = new AstIfaceRefDType{$<fl>1, $<fl>3, "", *$1, *$3}; }
id/*interface*/ { $$ = new AstIfaceRefDType{$<fl>1, AstIfaceRefDType::Virtual{}, "", *$1}; }
| id/*interface*/ '.' id/*modport*/ { $$ = new AstIfaceRefDType{$<fl>1, AstIfaceRefDType::Virtual{}, $<fl>3, "", *$1, *$3}; }
| id/*interface*/ parameter_value_assignmentClass
{ $$ = new AstIfaceRefDType{$<fl>1, nullptr, "", *$1, "", $2}; }
{ $$ = new AstIfaceRefDType{$<fl>1, AstIfaceRefDType::Virtual{}, nullptr, "", *$1, "", $2}; }
| id/*interface*/ parameter_value_assignmentClass '.' id/*modport*/
{ $$ = new AstIfaceRefDType{$<fl>1, $<fl>4, "", *$1, *$4, $2}; }
{ $$ = new AstIfaceRefDType{$<fl>1, AstIfaceRefDType::Virtual{}, $<fl>4, "", *$1, *$4, $2}; }
;

data_type_or_void<nodeDTypep>: // ==IEEE: data_type_or_void
Expand Down
7 changes: 7 additions & 0 deletions test_regress/t/t_interface_virtual_trig.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[0] intf.data==0000
[0] intf.data==0000
[10] intf.data==0000
[20] intf.data==dead
[30] intf.data==beef
*-* All Finished *-*
[40] intf.data==beef
22 changes: 22 additions & 0 deletions test_regress/t/t_interface_virtual_trig.pl
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2020 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(
);

execute(
check_finished => 1,
expect_filename => $Self->{golden_filename},
);

ok(1);
1;
31 changes: 31 additions & 0 deletions test_regress/t/t_interface_virtual_trig.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2023 by Antmicro Ltd.
// SPDX-License-Identifier: CC0-1.0

interface Bus;
logic [15:0] data;
endinterface

module t(clk);
input clk;
integer cyc = 0;
Bus intf();
virtual Bus vif = intf;

initial vif = intf;
always @(posedge clk) begin
cyc <= cyc + 1;
if (cyc == 1) begin
vif.data = 'hdead;
end else if (cyc == 2) begin
vif.data = 'hbeef;
end else if (cyc >= 3) begin
$write("*-* All Finished *-*\n");
$finish;
end
end

always_comb $display("[%0t] intf.data==%h", $time, intf.data);
endmodule

0 comments on commit d54ef81

Please sign in to comment.