Support $past.

This commit is contained in:
Wilson Snyder 2018-09-23 15:09:47 -04:00
parent a8519a7a53
commit 0e37747d2c
16 changed files with 328 additions and 9 deletions

View File

@ -4,6 +4,8 @@ The contributors that suggested a given feature are shown in []. Thanks!
* Verilator 4.003 devel
*** Support $past. [Dan Gisselquist]
*** Fix Mac OSX 10.13.6 / LLVM 9.1 compile issues, bug1348. [Kevin Kiningham]
*** Fix MinGW compile issues, msg2636. [HyungKi Jeong]

View File

@ -3731,6 +3731,17 @@ Change this to:
Verilator doesn't do this conversion for you, as some more complicated
cases would result in simulator mismatches.
=item TICKCOUNT
Warns that the number of ticks to delay a $past variable is greater than
10. At present Verilator effectively creates a flop for each delayed
signals, and as such any large counts may lead to large design size
increases.
Ignoring this warning will only slow simulations, it will simulate
correctly.
=item UNDRIVEN
Warns that the specified signal is never sourced. Verilator is fairly

View File

@ -45,6 +45,7 @@ private:
// STATE
AstNodeModule* m_modp; // Last module
AstBegin* m_beginp; // Last begin
unsigned m_modPastNum; // Module past numbering
V3Double0 m_statAsCover; // Statistic tracking
V3Double0 m_statAsPsl; // Statistic tracking
V3Double0 m_statAsFull; // Statistic tracking
@ -185,6 +186,7 @@ private:
pushDeletep(nodep); VL_DANGLING(nodep);
}
// VISITORS
virtual void visit(AstIf* nodep) {
if (nodep->user1SetOnce()) return;
if (nodep->uniquePragma() || nodep->unique0Pragma()) {
@ -244,7 +246,7 @@ private:
}
}
// VISITORS //========== Case assertions
//========== Case assertions
virtual void visit(AstCase* nodep) {
iterateChildren(nodep);
if (!nodep->user1SetOnce()) {
@ -302,7 +304,38 @@ private:
}
}
// VISITORS //========== Statements
//========== Past
virtual void visit(AstPast* nodep) {
iterateChildren(nodep);
uint32_t ticks = 1;
if (nodep->ticksp()) {
if (!VN_IS(nodep->ticksp(), Const)) nodep->v3fatalSrc("Expected constant ticks, checked in V3Width");
ticks = VN_CAST(nodep->ticksp(), Const)->toUInt();
}
if (ticks<1) nodep->v3fatalSrc("0 tick should have been checked in V3Width");
AstNode* inp = nodep->exprp()->unlinkFrBack();
AstVar* invarp = NULL;
AstSenTree* sentreep = nodep->sentreep(); sentreep->unlinkFrBack();
AstAlways* alwaysp = new AstAlways(nodep->fileline(), VAlwaysKwd::ALWAYS,
sentreep, NULL);
m_modp->addStmtp(alwaysp);
for (uint32_t i=0; i<ticks; ++i) {
AstVar* outvarp = new AstVar(nodep->fileline(), AstVarType::MODULETEMP,
"_Vpast_"+cvtToStr(m_modPastNum++)+"_"+cvtToStr(i),
inp->dtypep());
m_modp->addStmtp(outvarp);
AstNode* assp = new AstAssignDly(nodep->fileline(),
new AstVarRef(nodep->fileline(), outvarp, true),
inp);
alwaysp->addStmtp(assp);
//if (debug()>-9) assp->dumpTree(cout, "-ass: ");
invarp = outvarp;
inp = new AstVarRef(nodep->fileline(), invarp, false);
}
nodep->replaceWith(inp);
}
//========== Statements
virtual void visit(AstDisplay* nodep) {
iterateChildren(nodep);
// Replace the special types with standard text
@ -330,6 +363,7 @@ private:
virtual void visit(AstNodeModule* nodep) {
m_modp = nodep;
m_modPastNum = 0;
//
iterateChildren(nodep);
// Reset defaults
@ -354,6 +388,7 @@ public:
explicit AssertVisitor(AstNetlist* nodep) {
m_beginp = NULL;
m_modp = NULL;
m_modPastNum = 0;
// Process
iterate(nodep);
}

View File

@ -44,9 +44,11 @@ private:
// NODE STATE/TYPES
// STATE
// Reset each module:
AstNodeSenItem* m_seniDefaultp; // Default sensitivity (from AstDefClock)
AstNodeSenItem* m_seniDefaultp; // Default sensitivity (from AstDefClock)
// Reset each assertion:
AstNodeSenItem* m_senip; // Last sensitivity
AstNodeSenItem* m_senip; // Last sensitivity
// Reset each always:
AstNodeSenItem* m_seniAlwaysp; // Last sensitivity in always
// METHODS
VL_DEBUG_FUNC; // Declare debug()
@ -55,7 +57,9 @@ private:
// Create sentree based on clocked or default clock
// Return NULL for always
AstSenTree* newp = NULL;
AstNodeSenItem* senip = m_senip ? m_senip : m_seniDefaultp;
AstNodeSenItem* senip = m_senip;
if (!senip) senip = m_seniDefaultp;
if (!senip) senip = m_seniAlwaysp;
if (!senip) {
nodep->v3error("Unsupported: Unclocked assertion");
newp = new AstSenTree(nodep->fileline(), NULL);
@ -68,7 +72,8 @@ private:
m_senip = NULL;
}
// VISITORS //========== Statements
// VISITORS
//========== Statements
virtual void visit(AstClocking* nodep) {
UINFO(8," CLOCKING"<<nodep<<endl);
// Store the new default clock, reset on new module
@ -81,16 +86,31 @@ private:
}
pushDeletep(nodep); VL_DANGLING(nodep);
}
virtual void visit(AstAlways* nodep) {
iterateAndNextNull(nodep->sensesp());
if (nodep->sensesp()) {
m_seniAlwaysp = nodep->sensesp()->sensesp();
}
iterateAndNextNull(nodep->bodysp());
m_seniAlwaysp = NULL;
}
virtual void visit(AstNodePslCoverOrAssert* nodep) {
if (nodep->sentreep()) return; // Already processed
clearAssertInfo();
// Find PslClocking's burried under nodep->exprsp
iterateChildren(nodep);
nodep->sentreep(newSenTree(nodep));
clearAssertInfo();
}
virtual void visit(AstPslClocked* nodep) {
virtual void visit(AstPast* nodep) {
if (nodep->sentreep()) return; // Already processed
iterateChildren(nodep);
nodep->sentreep(newSenTree(nodep));
}
virtual void visit(AstPslClocked* nodep) {
// No need to iterate the body, once replace will get iterated
iterateAndNextNull(nodep->sensesp());
if (m_senip) {
nodep->v3error("Unsupported: Only one PSL clock allowed per assertion");
}
@ -120,6 +140,7 @@ public:
// CONSTRUCTORS
explicit AssertPreVisitor(AstNetlist* nodep) {
m_seniDefaultp = NULL;
m_seniAlwaysp = NULL;
clearAssertInfo();
// Process
iterate(nodep);

View File

@ -2155,7 +2155,7 @@ public:
//
virtual void dump(std::ostream& str);
AstSenTree* sensesp() const { return VN_CAST(op1p(), SenTree); } // op1 = Sensitivity list
AstNode* bodysp() const { return op2p(); } // op2 = Statements to evaluate
AstNode* bodysp() const { return op2p(); } // op2 = Statements to evaluate
void addStmtp(AstNode* nodep) { addOp2p(nodep); }
VAlwaysKwd keyword() const { return m_keyword; }
// Special accessors
@ -5193,6 +5193,30 @@ public:
virtual string emitC() { return "hypot(%li,%ri)"; }
};
class AstPast : public AstNodeMath {
// Verilog $past
// Parents: math
// Children: expression
public:
AstPast(FileLine* fl, AstNode* exprp, AstNode* ticksp) : AstNodeMath(fl) {
addOp1p(exprp);
addNOp2p(ticksp);
}
ASTNODE_NODE_FUNCS(Past)
virtual string emitVerilog() { V3ERROR_NA; return ""; }
virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { V3ERROR_NA; }
virtual string emitC() { V3ERROR_NA; return "";}
virtual string emitSimpleOperator() { V3ERROR_NA; return "";}
virtual bool cleanOut() { V3ERROR_NA; return "";}
virtual int instrCount() const { return widthInstrs(); }
AstNode* exprp() const { return op1p(); } // op1 = expression
AstNode* ticksp() const { return op2p(); } // op2 = ticks or NULL means 1
AstSenTree* sentreep() const { return VN_CAST(op4p(), SenTree); } // op4 = clock domain
void sentreep(AstSenTree* sentreep) { addOp4p(sentreep); } // op4 = clock domain
virtual V3Hash sameHash() const { return V3Hash(); }
virtual bool same(const AstNode* samep) const { return true; }
};
class AstPattern : public AstNodeMath {
// Verilog '{a,b,c,d...}
// Parents: AstNodeAssign, AstPattern, ...

View File

@ -332,6 +332,15 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
}
putqs(nodep,"end\n");
}
virtual void visit(AstPast* nodep) {
putfs(nodep, "$past(");
iterateAndNextNull(nodep->exprp());
if (nodep->ticksp()) {
puts(",");
iterateAndNextNull(nodep->ticksp());
}
puts(")");
}
virtual void visit(AstReturn* nodep) {
putfs(nodep,"return ");
iterateAndNextNull(nodep->lhsp());

View File

@ -94,6 +94,7 @@ public:
STMTDLY, // Delayed statement
SYMRSVDWORD, // Symbol is Reserved Word
SYNCASYNCNET, // Mixed sync + async reset
TICKCOUNT, // Too large tick count
UNDRIVEN, // No drivers
UNOPT, // Unoptimizable block
UNOPTFLAT, // Unoptimizable block after flattening
@ -140,6 +141,7 @@ public:
"PINMISSING", "PINNOCONNECT", "PINCONNECTEMPTY",
"REALCVT", "REDEFMACRO",
"SELRANGE", "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET",
"TICKCOUNT",
"UNDRIVEN", "UNOPT", "UNOPTFLAT", "UNOPTTHREADS",
"UNPACKED", "UNSIGNED", "UNUSED",
"USERERROR", "USERFATAL", "USERINFO", "USERWARN",

View File

@ -798,6 +798,26 @@ private:
// We don't size the constant until we commit the widths, as need parameters
// to remain unsized, and numbers to remain unsized to avoid backp() warnings
}
virtual void visit(AstPast* nodep) {
if (m_vup->prelim()) {
iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH);
nodep->dtypeFrom(nodep->exprp());
if (nodep->ticksp()) {
iterateCheckSizedSelf(nodep, "Ticks", nodep->ticksp(), SELF, BOTH);
V3Const::constifyParamsEdit(nodep->ticksp()); // ticksp may change
const AstConst* constp = VN_CAST(nodep->ticksp(), Const);
if (!constp || constp->toSInt() < 1) {
nodep->v3error("$past tick value must be constant and >= 1 (IEEE 2017 16.9.3)");
nodep->ticksp()->unlinkFrBack()->deleteTree();
} else {
if (constp->toSInt() > 10) {
nodep->v3warn(TICKCOUNT, "$past tick value of "<<constp->toSInt()
<<" may have a large performance cost");
}
}
}
}
}
virtual void visit(AstRand* nodep) {
if (m_vup->prelim()) {
nodep->dtypeSetSigned32(); // Says the spec

View File

@ -444,6 +444,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5}
"$low" { FL; return yD_LOW; }
"$onehot" { FL; return yD_ONEHOT; }
"$onehot0" { FL; return yD_ONEHOT0; }
"$past" { FL; return yD_PAST; }
"$right" { FL; return yD_RIGHT; }
"$size" { FL; return yD_SIZE; }
"$unpacked_dimensions" { FL; return yD_UNPACKED_DIMENSIONS; }

View File

@ -485,6 +485,7 @@ class AstSenTree;
%token<fl> yD_LOW "$low"
%token<fl> yD_ONEHOT "$onehot"
%token<fl> yD_ONEHOT0 "$onehot0"
%token<fl> yD_PAST "$past"
%token<fl> yD_POW "$pow"
%token<fl> yD_RANDOM "$random"
%token<fl> yD_READMEMB "$readmemb"
@ -2776,8 +2777,12 @@ system_f_call_or_t<nodep>: // IEEE: part of system_tf_call (can be task or func)
| yD_LOW '(' exprOrDataType ',' expr ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_LOW,$3,$5); }
| yD_ONEHOT '(' expr ')' { $$ = new AstOneHot($1,$3); }
| yD_ONEHOT0 '(' expr ')' { $$ = new AstOneHot0($1,$3); }
| yD_PAST '(' expr ')' { $$ = new AstPast($1,$3, NULL); }
| yD_PAST '(' expr ',' expr ')' { $$ = new AstPast($1,$3, $5); }
| yD_PAST '(' expr ',' expr ',' expr ')' { $1->v3error("Unsupported: $past expr2 and clock arguments"); $$ = $3; }
| yD_PAST '(' expr ',' expr ',' expr ',' expr')' { $1->v3error("Unsupported: $past expr2 and clock arguments"); $$ = $3; }
| yD_POW '(' expr ',' expr ')' { $$ = new AstPowD($1,$3,$5); }
| yD_RANDOM '(' expr ')' { $1->v3error("Unsupported: Seeding $random doesn't map to C++, use $c(\"srand\")"); }
| yD_RANDOM '(' expr ')' { $1->v3error("Unsupported: Seeding $random doesn't map to C++, use $c(\"srand\")"); $$ = NULL; }
| yD_RANDOM parenE { $$ = new AstRand($1); }
| yD_REALTIME parenE { $$ = new AstTimeD($1); }
| yD_REALTOBITS '(' expr ')' { $$ = new AstRealToBits($1,$3); }

20
test_regress/t/t_past.pl Executable file
View File

@ -0,0 +1,20 @@
#!/usr/bin/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.
scenarios(simulator => 1);
compile(
);
execute(
check_finished => 1,
);
ok(1);
1;

98
test_regress/t/t_past.v Normal file
View File

@ -0,0 +1,98 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2018 by Wilson Snyder.
module t (/*AUTOARG*/
// Inputs
clk
);
input clk;
integer cyc=0;
reg [63:0] crc;
reg [63:0] sum;
// Take CRC data and apply to testblock inputs
wire [31:0] in = crc[31:0];
Test test (/*AUTOINST*/
// Inputs
.clk (clk),
.in (in[31:0]));
Test2 test2 (/*AUTOINST*/
// Inputs
.clk (clk),
.in (in[31:0]));
// Test loop
always @ (posedge clk) begin
cyc <= cyc + 1;
crc <= {crc[62:0], crc[63]^crc[2]^crc[0]};
if (cyc==0) begin
// Setup
crc <= 64'h5aef0c8d_d70a4497;
end
else if (cyc<10) begin
end
else if (cyc<90) begin
end
else if (cyc==99) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule
module Test (/*AUTOARG*/
// Inputs
clk, in
);
input clk;
input [31:0] in;
reg [31:0] dly0;
reg [31:0] dly1;
reg [31:0] dly2;
reg [31:0] dly3;
// If called in an assertion, sequence, or property, the appropriate clocking event.
// Otherwise, if called in a disable condition or a clock expression in an assertion, sequence, or prop, explicit.
// Otherwise, if called in an action block of an assertion, the leading clock of the assertion is used.
// Otherwise, if called in a procedure, the inferred clock
// Otherwise, default clocking
always @(posedge clk) begin
dly0 <= in;
dly1 <= dly0;
dly2 <= dly1;
dly3 <= dly2;
// $past(expression, ticks, expression, clocking)
// In clock expression
if (dly0 != $past(in)) $stop;
if (dly0 != $past(in,1)) $stop;
if (dly1 != $past(in,2)) $stop;
end
assert property (@(posedge clk) dly0 == $past(in));
endmodule
module Test2 (/*AUTOARG*/
// Inputs
clk, in
);
input clk;
input [31:0] in;
reg [31:0] dly0;
reg [31:0] dly1;
default clocking @(posedge clk); endclocking
assert property (@(posedge clk) dly1 == $past(in, 2));
endmodule

22
test_regress/t/t_past_bad.pl Executable file
View File

@ -0,0 +1,22 @@
#!/usr/bin/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.
scenarios(vlt_all => 1);
compile(
fails => 1,
expect =>
'%Error: t/t_past_bad.v:\d+:.* \$past tick value must be constant and >= 1 \(IEEE 2017 16.9.3\)
%Warning-TICKCOUNT: t/t_past_bad.v:\d+: \$past tick value of 10000 may have a large performance cost
.*%Error: Exiting due to.*',
);
ok(1);
1;

View File

@ -0,0 +1,14 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2018 by Wilson Snyder.
module t (d, clk);
input d;
input clk;
always @ (posedge clk) begin
if ($past(d, 0)) $stop; // IEEE 16.9.3 must be >- 0
if ($past(d, 10000)) $stop; // TICKCOUNT
end
endmodule

View File

@ -0,0 +1,21 @@
#!/usr/bin/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.
scenarios(vlt_all => 1);
compile(
fails => 1,
expect =>
'%Error: t/t_past_unsup_bad.v:12: Unsupported: \$past expr2 and clock arguments
%Error: Exiting due to.*',
);
ok(1);
1;

View File

@ -0,0 +1,14 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2018 by Wilson Snyder.
module t (d, clk);
input d;
input clk;
always @ (posedge clk) begin
// Unsupported
if ($past(d, 0, 0, 0)) $stop;
end
endmodule