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 ca0ff74
Show file tree
Hide file tree
Showing 15 changed files with 148 additions and 25 deletions.
14 changes: 11 additions & 3 deletions src/V3AstNodeDType.h
Original file line number Diff line number Diff line change
Expand Up @@ -818,7 +818,10 @@ class AstEnumDType final : public AstNodeDType {
class AstIfaceRefDType final : public AstNodeDType {
// Reference to an interface, either for a port, or inside parent cell
// @astgen op1 := paramsp : List[AstPin]
public:
enum Type { REF, VIRTUAL };
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 @@ -827,22 +830,25 @@ class AstIfaceRefDType final : public AstNodeDType {
AstCell* m_cellp = nullptr; // When exact parent cell known; not a guess
AstModport* m_modportp = nullptr; // nullptr = unlinked or no modport
public:
AstIfaceRefDType(FileLine* fl, const string& cellName, const string& ifaceName)
AstIfaceRefDType(FileLine* fl, Type type, const string& cellName, const string& ifaceName)
: ASTGEN_SUPER_IfaceRefDType(fl)
, m_virtual{type == VIRTUAL}
, m_modportFileline{nullptr}
, m_cellName{cellName}
, m_ifaceName{ifaceName}
, m_modportName{""} {}
AstIfaceRefDType(FileLine* fl, FileLine* modportFl, const string& cellName,
AstIfaceRefDType(FileLine* fl, Type type, FileLine* modportFl, const string& cellName,
const string& ifaceName, const string& modport)
: ASTGEN_SUPER_IfaceRefDType(fl)
, m_virtual{type == VIRTUAL}
, m_modportFileline{modportFl}
, m_cellName{cellName}
, m_ifaceName{ifaceName}
, m_modportName{modport} {}
AstIfaceRefDType(FileLine* fl, FileLine* modportFl, const string& cellName,
AstIfaceRefDType(FileLine* fl, Type type, FileLine* modportFl, const string& cellName,
const string& ifaceName, const string& modport, AstPin* paramsp)
: ASTGEN_SUPER_IfaceRefDType(fl)
, m_virtual{type == VIRTUAL}
, m_modportFileline{modportFl}
, m_cellName{cellName}
, m_ifaceName{ifaceName} {
Expand All @@ -861,6 +867,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
1 change: 1 addition & 0 deletions src/V3AstNodes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1941,6 +1941,7 @@ const char* AstNetlist::broken() const {
BROKEN_RTN(m_dollarUnitPkgp && !m_dollarUnitPkgp->brokeExists());
BROKEN_RTN(m_evalp && !m_evalp->brokeExists());
BROKEN_RTN(m_dpiExportTriggerp && !m_dpiExportTriggerp->brokeExists());
BROKEN_RTN(m_virtIfaceTriggerp && !m_virtIfaceTriggerp->brokeExists());
BROKEN_RTN(m_topScopep && !m_topScopep->brokeExists());
BROKEN_RTN(m_delaySchedulerp && !m_delaySchedulerp->brokeExists());
BROKEN_RTN(m_nbaEventp && !m_nbaEventp->brokeExists());
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
5 changes: 3 additions & 2 deletions src/V3LinkCells.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -427,8 +427,9 @@ class LinkCellsVisitor final : public VNVisitor {
if (!nodep->hasIfaceVar()) {
const string varName
= nodep->name() + "__Viftop"; // V3LinkDot looks for this naming
AstIfaceRefDType* const idtypep = new AstIfaceRefDType{
nodep->fileline(), nodep->name(), nodep->modp()->name()};
AstIfaceRefDType* const idtypep
= new AstIfaceRefDType{nodep->fileline(), AstIfaceRefDType::REF, nodep->name(),
nodep->modp()->name()};
idtypep->ifacep(nullptr); // cellp overrides
// In the case of arrayed interfaces, we replace cellp when de-arraying in V3Inst
idtypep->cellp(nodep); // Only set when real parent cell known.
Expand Down
8 changes: 5 additions & 3 deletions src/V3LinkLevel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -309,8 +309,9 @@ void V3LinkLevel::wrapTopCell(AstNetlist* rootp) {
ifacecellp->modp(ifacerefp->ifacep());
newmodp->addStmtsp(ifacecellp);

AstIfaceRefDType* const idtypep = new AstIfaceRefDType{
newmodp->fileline(), name, ifacerefp->ifaceName()};
AstIfaceRefDType* const idtypep
= new AstIfaceRefDType{newmodp->fileline(), AstIfaceRefDType::REF,
name, ifacerefp->ifaceName()};
idtypep->ifacep(nullptr);
idtypep->dtypep(idtypep);
idtypep->cellp(ifacecellp);
Expand Down Expand Up @@ -358,7 +359,8 @@ void V3LinkLevel::wrapTopCell(AstNetlist* rootp) {
newmodp->addStmtsp(ifacearraycellp);

AstIfaceRefDType* const idtypep = new AstIfaceRefDType{
newmodp->fileline(), name, ifacerefp->ifaceName()};
newmodp->fileline(), AstIfaceRefDType::REF, name,
ifacerefp->ifaceName()};
idtypep->ifacep(nullptr);
idtypep->dtypep(idtypep);
idtypep->cellp(ifacearraycellp);
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
22 changes: 22 additions & 0 deletions src/Verilator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,28 @@ 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
16 changes: 8 additions & 8 deletions src/verilog.y
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ public:
m_varDecl = VVarType::IFACEREF;
m_varIO = VDirection::NONE;
m_varLifetime = VLifetime::NONE;
setDType(new AstIfaceRefDType{fileline, "", GRAMMARP->m_instModule});
setDType(new AstIfaceRefDType{fileline, AstIfaceRefDType::REF, "", GRAMMARP->m_instModule});
m_varDeclTyped = true;
AstVar* const nodep = createVariable(fileline, name, rangelistp, nullptr);
return nodep;
Expand Down Expand Up @@ -1505,12 +1505,12 @@ port<nodep>: // ==IEEE: port
// // We use instantCb here because the non-port form looks just like a module instantiation
portDirNetE id/*interface*/ portSig variable_dimensionListE sigAttrListE
{ $$ = $3; VARDECL(IFACEREF); VARIO(NONE);
AstNodeDType* const dtp = new AstIfaceRefDType{$<fl>2, "", *$2};
AstNodeDType* const dtp = new AstIfaceRefDType{$<fl>2, AstIfaceRefDType::REF, "", *$2};
VARDTYPE(dtp);
addNextNull($$, VARDONEP($$, $4, $5)); }
| portDirNetE id/*interface*/ '.' idAny/*modport*/ portSig variable_dimensionListE sigAttrListE
{ $$ = $5; VARDECL(IFACEREF); VARIO(NONE);
AstNodeDType* const dtp = new AstIfaceRefDType{$<fl>2, $<fl>4, "", *$2, *$4};
AstNodeDType* const dtp = new AstIfaceRefDType{$<fl>2, AstIfaceRefDType::REF, $<fl>4, "", *$2, *$4};
VARDTYPE(dtp);
addNextNull($$, VARDONEP($$, $6, $7)); }
| portDirNetE yINTERFACE portSig rangeListE sigAttrListE
Expand Down 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 Expand Up @@ -3167,7 +3167,7 @@ instDecl<nodep>:
// // IEEE: interface_identifier' .' modport_identifier list_of_interface_identifiers
| id/*interface*/ '.' id/*modport*/
/*mid*/ { VARRESET_NONLIST(VVarType::IFACEREF);
AstNodeDType* const dtp = new AstIfaceRefDType{$<fl>1, $<fl>3, "", *$1, *$3};
AstNodeDType* const dtp = new AstIfaceRefDType{$<fl>1, AstIfaceRefDType::REF, $<fl>3, "", *$1, *$3};
VARDTYPE(dtp); }
/*cont*/ mpInstnameList ';'
{ $$ = VARDONEP($5, nullptr, nullptr); }
Expand Down
6 changes: 6 additions & 0 deletions test_regress/t/t_interface_virtual_trig.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[0] intf.data==0000
[0] intf.data==0000
[10] intf.data==0000
[20] intf.data==dead
[30] intf.data==beef
*-* All Finished *-*
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;
Loading

0 comments on commit ca0ff74

Please sign in to comment.