Support class extern.

This commit is contained in:
Wilson Snyder 2020-08-22 19:46:21 -04:00
parent decbf79f39
commit 9702d11657
13 changed files with 121 additions and 30 deletions

View File

@ -9,6 +9,8 @@ The contributors that suggested a given feature are shown in []. Thanks!
** Support hierarchical Verilation (#2206). [Yutetsu TAKATSUKASA]
**** Support class extern.
**** Fix false DECLFILENAME on black-boxed modules (#2430). [Philipp Wagner]

View File

@ -2627,7 +2627,8 @@ private:
bool m_taskPublic : 1; // Public task
bool m_attrIsolateAssign : 1; // User isolate_assignments attribute
bool m_classMethod : 1; // Class method
bool m_extern : 1; // Extern prototype
bool m_externProto : 1; // Extern prototype
bool m_externDef : 1; // Extern definition
bool m_prototype : 1; // Just a prototype
bool m_dpiExport : 1; // DPI exported
bool m_dpiImport : 1; // DPI imported
@ -2646,7 +2647,8 @@ public:
, m_taskPublic{false}
, m_attrIsolateAssign{false}
, m_classMethod{false}
, m_extern{false}
, m_externProto{false}
, m_externDef{false}
, m_prototype{false}
, m_dpiExport{false}
, m_dpiImport{false}
@ -2694,8 +2696,10 @@ public:
bool attrIsolateAssign() const { return m_attrIsolateAssign; }
void classMethod(bool flag) { m_classMethod = flag; }
bool classMethod() const { return m_classMethod; }
void isExtern(bool flag) { m_extern = flag; }
bool isExtern() const { return m_extern; }
void isExternProto(bool flag) { m_externProto = flag; }
bool isExternProto() const { return m_externProto; }
void isExternDef(bool flag) { m_externDef = flag; }
bool isExternDef() const { return m_externDef; }
void prototype(bool flag) { m_prototype = flag; }
bool prototype() const { return m_prototype; }
void dpiExport(bool flag) { m_dpiExport = flag; }

View File

@ -1081,7 +1081,9 @@ const char* AstClassPackage::broken() const {
return nullptr;
}
void AstClass::insertCache(AstNode* nodep) {
if (VN_IS(nodep, Var) || VN_IS(nodep, NodeFTask) || VN_IS(nodep, EnumItemRef)) {
bool doit = (VN_IS(nodep, Var) || VN_IS(nodep, EnumItemRef)
|| (VN_IS(nodep, NodeFTask) && !VN_CAST(nodep, NodeFTask)->isExternProto()));
if (doit) {
if (m_members.find(nodep->name()) != m_members.end()) {
nodep->v3error("Duplicate declaration of member name: " << nodep->prettyNameQ());
} else {

View File

@ -992,13 +992,36 @@ class LinkDotFindVisitor : public AstNVisitor {
// Remember the existing symbol table scope
VSymEnt* oldCurSymp = m_curSymp;
{
// Change to appropriate package if extern declaration (vs definition)
if (nodep->packagep()) {
AstClassOrPackageRef* cpackagerefp = VN_CAST(nodep->packagep(), ClassOrPackageRef);
if (!cpackagerefp) {
nodep->v3warn(E_UNSUPPORTED,
"Unsupported: extern function definition with class-in-class");
} else {
AstClass* classp = VN_CAST(cpackagerefp->classOrPackagep(), Class);
if (!classp) {
nodep->v3error("Extern declaration's scope is not a defined class");
} else {
m_curSymp = m_statep->getNodeSym(classp);
if (!nodep->isExternDef()) {
// Move it to proper spot under the target class
nodep->unlinkFrBack();
classp->addStmtp(nodep);
nodep->isExternDef(true); // So we check there's a matching extern
nodep->packagep()->unlinkFrBack()->deleteTree();
}
}
}
}
// Create symbol table for the task's vars
m_curSymp = m_statep->insertBlock(m_curSymp, nodep->name(), nodep, m_packagep);
string name = string{nodep->isExternProto() ? "extern " : ""} + nodep->name();
m_curSymp = m_statep->insertBlock(m_curSymp, name, nodep, m_packagep);
m_curSymp->fallbackp(oldCurSymp);
// Convert the func's range to the output variable
// This should probably be done in the Parser instead, as then we could
// just attach normal signal attributes to it.
if (nodep->fvarp() && !VN_IS(nodep->fvarp(), Var)) {
if (!nodep->isExternProto() && nodep->fvarp() && !VN_IS(nodep->fvarp(), Var)) {
AstNodeDType* dtypep = VN_CAST(nodep->fvarp(), NodeDType);
// If unspecified, function returns one bit; however when we
// support NEW() it could also return the class reference.
@ -2634,11 +2657,15 @@ private:
if (nodep->classMethod() && nodep->lifetime().isStatic()) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: 'static' class method");
}
if (nodep->isExtern()) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: extern class methods");
if (nodep->isExternDef()) {
if (!m_curSymp->findIdFallback("extern " + nodep->name())) {
nodep->v3error("extern not found that declares " + nodep->prettyNameQ());
}
}
if (nodep->packagep()) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: extern function definition");
if (nodep->isExternProto()) {
if (!m_curSymp->findIdFallback(nodep->name())) {
nodep->v3error("definition not found for extern " + nodep->prettyNameQ());
}
}
VSymEnt* oldCurSymp = m_curSymp;
{

View File

@ -134,6 +134,13 @@ private:
// NodeTask: Remember its name for later resolution
// Remember the existing symbol table scope
if (m_classp) nodep->classMethod(true);
// V3LinkDot moved the isExternDef into the class, the extern proto was
// checked to exist, and now isn't needed
nodep->isExternDef(false);
if (nodep->isExternProto()) {
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
return;
}
VLifetime origLifetime = m_lifetime;
{
m_lifetime = nodep->lifetime();

View File

@ -6028,11 +6028,11 @@ class_method<nodep>: // ==IEEE: class_method
| yPURE yVIRTUAL__ETC memberQualListE method_prototype ';'
{ $$ = $4; $3.applyToNodes($4); $4->pureVirtual(true); $4->isVirtual(true); }
| yEXTERN memberQualListE method_prototype ';'
{ $$ = $3; $2.applyToNodes($3); $3->isExtern(true); }
{ $$ = $3; $2.applyToNodes($3); $3->isExternProto(true); }
// // IEEE: "method_qualifierE class_constructor_declaration"
// // part of function_declaration
| yEXTERN memberQualListE class_constructor_prototype
{ $$ = $3; $2.applyToNodes($3); $3->isExtern(true); }
{ $$ = $3; $2.applyToNodes($3); $3->isExternProto(true); }
;
// IEEE: class_constructor_prototype

View File

@ -12,6 +12,7 @@ class Cls;
class SubCls;
int smembera;
int smemberb;
// TODO put extern function here or in t_class_extern.v to check link
endclass : SubCls
SubCls sc;
endclass : Cls

View File

@ -1,7 +0,0 @@
%Error-UNSUPPORTED: t/t_class_extern.v:8:24: Unsupported: extern class methods
8 | extern function void extfunc();
| ^~~~~~~
%Error-UNSUPPORTED: t/t_class_extern.v:11:15: Unsupported: extern function definition
11 | function void Cls::extfunc();
| ^~~
%Error: Exiting due to

View File

@ -11,13 +11,11 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
scenarios(simulator => 1);
compile(
fails => $Self->{vlt_all},
expect_filename => $Self->{golden_filename},
);
#execute(
# check_finished => 1,
# );
execute(
check_finished => 1,
);
ok(1);
1;

View File

@ -5,17 +5,23 @@
// SPDX-License-Identifier: CC0-1.0
class Cls;
extern function void extfunc();
extern task extcls();
extern function int extone();
endclass
function void Cls::extfunc();
function int Cls::extone();
return 1;
endfunction
task Cls::extcls();
$write("*-* All Finished *-*\n");
$finish;
endfunction
endtask
module t (/*AUTOARG*/);
initial begin
Cls c;
c.extfunc();
Cls c = new;
if (c.extone() != 1) $stop;
c.extcls();
end
endmodule

View File

@ -0,0 +1,16 @@
%Error: t/t_class_extern_bad.v:9:16: Duplicate declaration of task: 'nodef'
9 | extern task nodef();
| ^~~~~
t/t_class_extern_bad.v:8:16: ... Location of original declaration
8 | extern task nodef();
| ^~~~~
%Error: t/t_class_extern_bad.v:8:16: definition not found for extern 'nodef'
8 | extern task nodef();
| ^~~~~
%Error: t/t_class_extern_bad.v:9:16: definition not found for extern 'nodef'
9 | extern task nodef();
| ^~~~~
%Error: t/t_class_extern_bad.v:12:6: extern not found that declares 'noproto'
12 | task Base1::noproto();
| ^~~~~
%Error: Exiting due to

View File

@ -0,0 +1,19 @@
#!/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(linter => 1);
lint(
fails => 1,
expect_filename => $Self->{golden_filename},
);
ok(1);
1;

View File

@ -0,0 +1,16 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2020 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
class Base1;
extern task nodef();
extern task nodef(); // duplicate
endclass
task Base1::noproto(); // no such prototype
endtask
module t (/*AUTOARG*/);
endmodule