diff --git a/Changes b/Changes index 46e14d873..c85a27952 100644 --- a/Changes +++ b/Changes @@ -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] diff --git a/bin/verilator b/bin/verilator index 8f42da228..cf9f2e67c 100755 --- a/bin/verilator +++ b/bin/verilator @@ -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 diff --git a/src/V3Assert.cpp b/src/V3Assert.cpp index 54824081a..d7b9b4ea3 100644 --- a/src/V3Assert.cpp +++ b/src/V3Assert.cpp @@ -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; ifileline(), 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); } diff --git a/src/V3AssertPre.cpp b/src/V3AssertPre.cpp index 5a1a09e8f..4f1964343 100644 --- a/src/V3AssertPre.cpp +++ b/src/V3AssertPre.cpp @@ -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"<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); diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index b276a854b..deb4f9953 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -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, ... diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index 42a879397..ffb7a422e 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -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()); diff --git a/src/V3Error.h b/src/V3Error.h index 6f21c5fdc..819363dd0 100644 --- a/src/V3Error.h +++ b/src/V3Error.h @@ -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", diff --git a/src/V3Width.cpp b/src/V3Width.cpp index addafab35..d69d7a874 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -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 "<toSInt() + <<" may have a large performance cost"); + } + } + } + } + } virtual void visit(AstRand* nodep) { if (m_vup->prelim()) { nodep->dtypeSetSigned32(); // Says the spec diff --git a/src/verilog.l b/src/verilog.l index ce8373fb8..c72bfc953 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -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; } diff --git a/src/verilog.y b/src/verilog.y index 590972f71..fff35b3db 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -485,6 +485,7 @@ class AstSenTree; %token yD_LOW "$low" %token yD_ONEHOT "$onehot" %token yD_ONEHOT0 "$onehot0" +%token yD_PAST "$past" %token yD_POW "$pow" %token yD_RANDOM "$random" %token yD_READMEMB "$readmemb" @@ -2776,8 +2777,12 @@ system_f_call_or_t: // 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); } diff --git a/test_regress/t/t_past.pl b/test_regress/t/t_past.pl new file mode 100755 index 000000000..89a4e77d9 --- /dev/null +++ b/test_regress/t/t_past.pl @@ -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; diff --git a/test_regress/t/t_past.v b/test_regress/t/t_past.v new file mode 100644 index 000000000..2388be27f --- /dev/null +++ b/test_regress/t/t_past.v @@ -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 diff --git a/test_regress/t/t_past_bad.pl b/test_regress/t/t_past_bad.pl new file mode 100755 index 000000000..26610cc88 --- /dev/null +++ b/test_regress/t/t_past_bad.pl @@ -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; + diff --git a/test_regress/t/t_past_bad.v b/test_regress/t/t_past_bad.v new file mode 100644 index 000000000..0bada018b --- /dev/null +++ b/test_regress/t/t_past_bad.v @@ -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 diff --git a/test_regress/t/t_past_unsup_bad.pl b/test_regress/t/t_past_unsup_bad.pl new file mode 100755 index 000000000..e6683df33 --- /dev/null +++ b/test_regress/t/t_past_unsup_bad.pl @@ -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; + diff --git a/test_regress/t/t_past_unsup_bad.v b/test_regress/t/t_past_unsup_bad.v new file mode 100644 index 000000000..2ee501754 --- /dev/null +++ b/test_regress/t/t_past_unsup_bad.v @@ -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