Support std::semaphore and typed std::mailbox (#3708)

Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
This commit is contained in:
Krzysztof Bieganski 2022-11-28 16:53:55 +01:00 committed by GitHub
parent 4452a9b10f
commit 68e1b473e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 329 additions and 82 deletions

106
include/std.sv Normal file
View File

@ -0,0 +1,106 @@
// DESCRIPTION: Verilator: built-in packages and classes
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2022 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
//
//*************************************************************************
// verilator lint_off DECLFILENAME
// verilator lint_off TIMESCALEMOD
// verilator lint_off UNUSEDSIGNAL
package std;
class mailbox #(type T);
protected int m_bound;
protected T m_queue[$];
function new(int bound = 0);
m_bound = bound;
endfunction
function int num();
return m_queue.size();
endfunction
task put(T message);
`ifdef VERILATOR_TIMING
if (m_bound != 0)
wait (m_queue.size() < m_bound);
m_queue.push_back(message);
`endif
endtask
function int try_put(T message);
if (num() < m_bound) begin
m_queue.push_back(message);
return 1;
end
return 0;
endfunction
task get(ref T message);
`ifdef VERILATOR_TIMING
wait (m_queue.size() > 0);
message = m_queue.pop_front();
`endif
endtask
function int try_get(ref T message);
if (num() > 0) begin
message = m_queue.pop_front();
return 1;
end
return 0;
endfunction
task peek(ref T message);
`ifdef VERILATOR_TIMING
wait (m_queue.size() > 0);
message = m_queue[0];
`endif
endtask
function int try_peek(ref T message);
if (num() > 0) begin
message = m_queue[0];
return 1;
end
return 0;
endfunction
endclass
class semaphore;
protected int m_keyCount;
function new(int keyCount = 0);
m_keyCount = keyCount;
endfunction
function void put(int keyCount = 1);
m_keyCount += keyCount;
endfunction
task get(int keyCount = 1);
`ifdef VERILATOR_TIMING
wait (m_keyCount >= keyCount);
m_keyCount -= keyCount;
`endif
endtask
function int try_get(int keyCount = 1);
if (m_keyCount >= keyCount) begin
m_keyCount -= keyCount;
return 1;
end
return 0;
endfunction
endclass
endpackage
// verilator lint_off IMPORTSTAR
import std::*;

View File

@ -1086,6 +1086,7 @@ class AstNetlist final : public AstNode {
AstTypeTable* const m_typeTablep; // Reference to top type table, for faster lookup
AstConstPool* const m_constPoolp; // Reference to constant pool, for faster lookup
AstPackage* m_dollarUnitPkgp = nullptr; // $unit
AstPackage* m_stdPackagep = nullptr; // SystemVerilog std package
AstCFunc* m_evalp = nullptr; // The '_eval' function
AstCFunc* m_evalNbap = nullptr; // The '_eval__nba' function
AstVarScope* m_dpiExportTriggerp = nullptr; // The DPI export trigger variable
@ -1119,6 +1120,8 @@ public:
void dpiExportTriggerp(AstVarScope* varScopep) { m_dpiExportTriggerp = varScopep; }
AstVar* delaySchedulerp() const { return m_delaySchedulerp; }
void delaySchedulerp(AstVar* const varScopep) { m_delaySchedulerp = varScopep; }
void stdPackagep(AstPackage* const packagep) { m_stdPackagep = packagep; }
AstPackage* stdPackagep() const { return m_stdPackagep; }
AstTopScope* topScopep() const { return m_topScopep; }
void createTopScope(AstScope* scopep);
VTimescale timeunit() const { return m_timeunit; }

View File

@ -63,6 +63,13 @@ void V3Global::readFiles() {
"Cannot find file containing module: ");
}
if (usesStdPackage()) {
// Parse the std package
parser.parseFile(new FileLine{FileLine::commandLineFilename()},
V3Options::getStdPackagePath(), false,
"Cannot find std.sv containing built-in std:: definitions: ");
}
// Read libraries
// To be compatible with other simulators,
// this needs to be done after the top file is read

View File

@ -107,6 +107,7 @@ class V3Global final {
bool m_dpi = false; // Need __Dpi include files
bool m_hasEvents = false; // Design uses SystemVerilog named events
bool m_hasClasses = false; // Design uses SystemVerilog classes
bool m_usesStdPackage = false; // Design uses the std package
bool m_usesTiming = false; // Design uses timing constructs
bool m_hasForceableSignals = false; // Need to apply V3Force pass
bool m_hasSCTextSections = false; // Has `systemc_* sections that need to be emitted
@ -152,6 +153,8 @@ public:
void setHasEvents() { m_hasEvents = true; }
bool hasClasses() const { return m_hasClasses; }
void setHasClasses() { m_hasClasses = true; }
bool usesStdPackage() const { return m_usesStdPackage; }
void setUsesStdPackage() { m_usesStdPackage = true; }
bool usesTiming() const { return m_usesTiming; }
void setUsesTiming() { m_usesTiming = true; }
bool hasForceableSignals() const { return m_hasForceableSignals; }

View File

@ -50,6 +50,7 @@ private:
using ImplTypedefMap = std::map<const std::pair<void*, std::string>, AstTypedef*>;
// STATE
AstPackage* const m_stdPackagep; // SystemVerilog std package
AstVar* m_varp = nullptr; // Variable we're under
ImplTypedefMap m_implTypedef; // Created typedefs for each <container,name>
std::unordered_set<FileLine*> m_filelines; // Filelines that have been seen
@ -600,6 +601,11 @@ private:
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
}
}
void visit(AstClassOrPackageRef* nodep) override {
if (nodep->name() == "std" && !nodep->classOrPackagep()) {
nodep->classOrPackagep(m_stdPackagep);
}
}
void visit(AstNode* nodep) override {
// Default: Just iterate
@ -609,7 +615,10 @@ private:
public:
// CONSTRUCTORS
explicit LinkParseVisitor(AstNetlist* rootp) { iterate(rootp); }
explicit LinkParseVisitor(AstNetlist* rootp)
: m_stdPackagep{rootp->stdPackagep()} {
iterate(rootp);
}
~LinkParseVisitor() override = default;
};

View File

@ -706,6 +706,8 @@ string V3Options::getenvVERILATOR_ROOT() {
return var;
}
string V3Options::getStdPackagePath() { return getenvVERILATOR_ROOT() + "/include/std.sv"; }
string V3Options::getSupported(const string& var) {
// If update below, also update V3Options::showVersion()
if (var == "COROUTINES" && coroutineSupport()) {

View File

@ -666,6 +666,7 @@ public:
static string getenvSYSTEMC_INCLUDE();
static string getenvSYSTEMC_LIBDIR();
static string getenvVERILATOR_ROOT();
static string getStdPackagePath();
static string getSupported(const string& var);
static bool systemCSystemWide();
static bool systemCFound(); // SystemC installed, or environment points to it

View File

@ -532,14 +532,17 @@ void V3ParseImp::tokenPipelineSym() {
&& (*(yylval.strp) == "mailbox" // IEEE-standard class
|| *(yylval.strp) == "process" // IEEE-standard class
|| *(yylval.strp) == "semaphore")) { // IEEE-standard class
v3Global.setUsesStdPackage();
yylval.scp = nullptr;
if (token == yaID__LEX) token = yaID__aTYPE;
} else { // Not found
yylval.scp = nullptr;
if (token == yaID__CC) {
// IEEE does require this, but we may relax this as UVM breaks it, so allow bbox
// for today
if (!v3Global.opt.bboxUnsup()) {
if (!m_afterColonColon && *(yylval.strp) == "std") {
v3Global.setUsesStdPackage();
} else if (!v3Global.opt.bboxUnsup()) {
// IEEE does require this, but we may relax this as UVM breaks it, so allow
// bbox for today
// We'll get a parser error eventually but might not be obvious
// is missing package, and this confuses people
static int warned = false;
@ -554,6 +557,7 @@ void V3ParseImp::tokenPipelineSym() {
}
}
}
m_afterColonColon = token == yP_COLONCOLON;
yylval.token = token;
// effectively returns yylval
}

View File

@ -150,6 +150,7 @@ class V3ParseImp final {
VOptionBool m_unconnectedDrive; // Last unconnected drive
int m_lexPrevToken = 0; // previous parsed token (for lexer)
bool m_afterColonColon = false; // The previous token was '::'
std::deque<V3ParseBisonYYSType> m_tokensAhead; // Tokens we parsed ahead of parser
std::deque<string*> m_stringps; // Created strings for later cleanup

View File

@ -3291,6 +3291,16 @@ private:
VL_DANGLING(index_exprp); // May have been edited
return VN_AS(nodep->pinsp(), Arg)->exprp();
}
void methodCallWarnTiming(AstMethodCall* const nodep, const std::string& className) {
if (v3Global.opt.timing().isSetFalse()) {
nodep->v3warn(E_NOTIMING,
className << "::" << nodep->name() << "() requires --timing");
} else if (!v3Global.opt.timing().isSetTrue()) {
nodep->v3warn(E_NEEDTIMINGOPT, "Use --timing or --no-timing to specify how "
<< className << "::" << nodep->name()
<< "() should be handled");
}
}
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();
@ -3300,6 +3310,26 @@ private:
}
UASSERT_OBJ(first_classp, nodep, "Unlinked");
for (AstClass* classp = first_classp; classp;) {
if (nodep->fileline()->timingOn()) {
if (classp->name() == "semaphore"
|| VString::startsWith(classp->name(), "mailbox")) {
// Find the package the class is in
AstNode* pkgItemp = classp;
while (pkgItemp->backp() && pkgItemp->backp()->nextp() == pkgItemp) {
pkgItemp = pkgItemp->backp();
}
AstPackage* const packagep = VN_CAST(pkgItemp->backp(), Package);
// Check if it's std
if (packagep && packagep->name() == "std") {
if (classp->name() == "semaphore" && nodep->name() == "get") {
methodCallWarnTiming(nodep, "semaphore");
} else if (nodep->name() == "put" || nodep->name() == "get"
|| nodep->name() == "peek") {
methodCallWarnTiming(nodep, "mailbox");
}
}
}
}
if (AstNodeFTask* const ftaskp
= VN_CAST(classp->findMember(nodep->name()), NodeFTask)) {
userIterate(ftaskp, nullptr);

View File

@ -1162,6 +1162,13 @@ package_declaration: // ==IEEE: package_declaration
packageFront<nodeModulep>:
yPACKAGE lifetimeE idAny ';'
{ $$ = new AstPackage{$<fl>3, *$3};
if ($$->name() == "std") {
if ($$->fileline()->filename() != V3Options::getStdPackagePath()) {
$$->v3error("Redeclaring the 'std' package is not allowed");
} else {
PARSEP->rootp()->stdPackagep(VN_AS($$, Package));
}
}
$$->inLibrary(true); // packages are always libraries; don't want to make them a "top"
$$->lifetime($2);
$$->modTrace(GRAMMARP->allTracingOn($$->fileline()));

View File

@ -1,6 +0,0 @@
%Error: t/t_mailbox.v:24:4: Can't find typedef: 'mailbox'
24 | mailbox #(int) m;
| ^~~~~~~
%Error: Internal Error: t/t_mailbox.v:24:14: ../V3LinkDot.cpp:#: Pin not under instance?
24 | mailbox #(int) m;
| ^~~

View File

@ -10,14 +10,19 @@ 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},
);
if (!$Self->have_coroutines) {
skip("No coroutine support");
}
else {
compile(
verilator_flags2 => ["--exe --main --timing -Wall"],
make_main => 0,
);
execute(
check_finished => 1,
) if !$Self->{vlt_all};
execute(
check_finished => 1,
);
}
ok(1);
1;

View File

@ -20,6 +20,7 @@
`define MAILBOX_T mailbox
`endif
// verilator lint_off DECLFILENAME
module t(/*AUTOARG*/);
`MAILBOX_T #(int) m;
int msg;

View File

@ -1,6 +1,5 @@
%Error: t/t_mailbox_bad.v:8:4: Can't find typedef: 'mailbox'
8 | mailbox #(int) m;
| ^~~~~~~
%Error: Internal Error: t/t_mailbox_bad.v:8:14: ../V3LinkDot.cpp:#: Pin not under instance?
8 | mailbox #(int) m;
| ^~~
%Error: t/t_mailbox_bad.v:12:13: Class method 'bad_method' not found in class 'mailbox__Tz1'
: ... In instance t
12 | if (m.bad_method() != 0) $stop;
| ^~~~~~~~~~
%Error: Exiting due to

View File

@ -1,6 +0,0 @@
%Error: t/t_mailbox.v:24:4: Can't find typedef: 'mailbox'
24 | mailbox #(int) m;
| ^~~~~~~
%Error: Internal Error: t/t_mailbox.v:24:14: ../V3LinkDot.cpp:#: Pin not under instance?
24 | mailbox #(int) m;
| ^~~

View File

@ -10,17 +10,21 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
scenarios(simulator => 1);
top_filename("t/t_mailbox.v");
if (!$Self->have_coroutines) {
skip("No coroutine support");
}
else {
top_filename("t/t_mailbox.v");
compile(
v_flags2 => ["+define+T_MAILBOX+std::mailbox"],
fails => $Self->{vlt_all},
expect_filename => $Self->{golden_filename},
);
compile(
verilator_flags2 => ["--exe --main --timing -Wall -DMAILBOX_T=std::mailbox"],
make_main => 0,
);
execute(
check_finished => 1,
) if !$Self->{vlt_all};
execute(
check_finished => 1,
);
}
ok(1);
1;

View File

@ -30,4 +30,20 @@
: ... In instance t
27 | initial #2 $stop;
| ^
%Error-NOTIMING: t/t_notiming.v:33:10: mailbox::put() requires --timing
: ... In instance t
33 | m.put(i);
| ^~~
%Error-NOTIMING: t/t_notiming.v:34:10: mailbox::get() requires --timing
: ... In instance t
34 | m.get(i);
| ^~~
%Error-NOTIMING: t/t_notiming.v:35:10: mailbox::peek() requires --timing
: ... In instance t
35 | m.peek(i);
| ^~~~
%Error-NOTIMING: t/t_notiming.v:36:10: semaphore::get() requires --timing
: ... In instance t
36 | s.get();
| ^~~
%Error: Exiting due to

View File

@ -25,6 +25,16 @@ module t;
initial #1 ->e;
initial #2 $stop; // timeout
mailbox#(int) m = new;
semaphore s = new;
initial begin
int i;
m.put(i);
m.get(i);
m.peek(i);
s.get();
end
endmodule
`ifdef VERILATOR_TIMING

View File

@ -1,7 +0,0 @@
%Error: t/t_semaphore.v:21:4: Can't find typedef: 'semaphore'
21 | semaphore s;
| ^~~~~~~~~
%Error: t/t_semaphore.v:22:4: Can't find typedef: 'semaphore'
22 | semaphore s2;
| ^~~~~~~~~
%Error: Exiting due to

View File

@ -10,14 +10,19 @@ 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},
);
if (!$Self->have_coroutines) {
skip("No coroutine support");
}
else {
compile(
verilator_flags2 => ["--exe --main --timing -Wall"],
make_main => 0,
);
execute(
check_finished => 1,
) if !$Self->{vlt_all};
execute(
check_finished => 1,
);
}
ok(1);
1;

View File

@ -16,11 +16,11 @@
`define SEMAPHORE_T semaphore
`endif
// verilator lint_off DECLFILENAME
module t(/*AUTOARG*/);
// From UVM:
`SEMAPHORE_T s;
`SEMAPHORE_T s2;
int msg;
initial begin
s = new(1);

View File

@ -1,4 +1,5 @@
%Error: t/t_semaphore_bad.v:8:4: Can't find typedef: 'semaphore'
8 | semaphore s;
| ^~~~~~~~~
%Error: t/t_semaphore_bad.v:12:13: Class method 'bad_method' not found in class 'semaphore'
: ... In instance t
12 | if (s.bad_method() != 0) $stop;
| ^~~~~~~~~~
%Error: Exiting due to

View File

@ -1,7 +0,0 @@
%Error: t/t_semaphore.v:21:4: Can't find typedef: 'semaphore'
21 | semaphore s;
| ^~~~~~~~~
%Error: t/t_semaphore.v:22:4: Can't find typedef: 'semaphore'
22 | semaphore s2;
| ^~~~~~~~~
%Error: Exiting due to

View File

@ -10,17 +10,21 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
scenarios(simulator => 1);
top_filename("t/t_semaphore.v");
if (!$Self->have_coroutines) {
skip("No coroutine support");
}
else {
top_filename("t/t_semaphore.v");
compile(
v_flags2 => ["+define+T_SEMAPHORE+std::semaphore"],
fails => $Self->{vlt_all},
expect_filename => $Self->{golden_filename},
);
compile(
verilator_flags2 => ["--exe --main --timing -Wall -DSEMAPHORE_T=std::semaphore"],
make_main => 0,
);
execute(
check_finished => 1,
) if !$Self->{vlt_all};
execute(
check_finished => 1,
);
}
ok(1);
1;

View File

@ -0,0 +1,5 @@
%Error-PKGNODECL: t/t_std_identifier.v:16:20: Package/class 'std' not found, and needs to be predeclared (IEEE 1800-2017 26.3)
16 | int baz = foo::std::bar;
| ^~~
... For error description see https://verilator.org/warn/PKGNODECL?v=latest
%Error: Exiting due to

View File

@ -2,18 +2,21 @@
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
# Copyright 2022 by Antmicro Ltd. 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);
top_filename("t_semaphore.v");
scenarios(linter => 1);
lint(
verilator_flags2 => ["--debug-exit-uvm"],
fails => 1,
expect_filename => $Self->{golden_filename},
);
lint(
verilator_flags2 => ["-DTEST_DECLARE_STD"],
);
ok(1);

View File

@ -0,0 +1,17 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2022 by Antmicro Ltd.
// SPDX-License-Identifier: CC0-1.0
package foo;
`ifdef TEST_DECLARE_STD
class std;
static int bar;
endclass
`endif
endpackage
module t;
int baz = foo::std::bar;
endmodule

View File

@ -0,0 +1,4 @@
%Error: t/t_std_pkg_bad.v:7:9: Redeclaring the 'std' package is not allowed
7 | package std;
| ^~~
%Error: Exiting due to

View File

@ -2,18 +2,17 @@
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
# Copyright 2022 by Antmicro Ltd. 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);
top_filename("t_mailbox.v");
scenarios(linter => 1);
lint(
verilator_flags2 => ["--debug-exit-uvm"],
fails => 1,
expect_filename => $Self->{golden_filename},
);
ok(1);

View File

@ -0,0 +1,11 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2022 by Antmicro Ltd.
// SPDX-License-Identifier: CC0-1.0
package std;
endpackage
module t;
endmodule

View File

@ -31,4 +31,20 @@
: ... In instance t
27 | initial #2 $stop;
| ^
%Error-NEEDTIMINGOPT: t/t_notiming.v:33:10: Use --timing or --no-timing to specify how mailbox::put() should be handled
: ... In instance t
33 | m.put(i);
| ^~~
%Error-NEEDTIMINGOPT: t/t_notiming.v:34:10: Use --timing or --no-timing to specify how mailbox::get() should be handled
: ... In instance t
34 | m.get(i);
| ^~~
%Error-NEEDTIMINGOPT: t/t_notiming.v:35:10: Use --timing or --no-timing to specify how mailbox::peek() should be handled
: ... In instance t
35 | m.peek(i);
| ^~~~
%Error-NEEDTIMINGOPT: t/t_notiming.v:36:10: Use --timing or --no-timing to specify how semaphore::get() should be handled
: ... In instance t
36 | s.get();
| ^~~
%Error: Exiting due to