diff --git a/Changes b/Changes index 5258e8914..c846c7a87 100644 --- a/Changes +++ b/Changes @@ -6,7 +6,7 @@ The contributors that suggested a given feature are shown in []. Thanks! *** Fix arrayed interfaces, broke in 4.038 (#2468). [Josh Redford] -**** Support $stable. [Peter Monsson] +**** Support $stable, $rose and $fell. [Peter Monsson] **** Fix combining different-width parameters (#2484). [abirkmanis] diff --git a/src/V3AssertPre.cpp b/src/V3AssertPre.cpp index 0d5859704..ec1775503 100644 --- a/src/V3AssertPre.cpp +++ b/src/V3AssertPre.cpp @@ -89,11 +89,39 @@ private: if (!nodep->immediate()) nodep->sentreep(newSenTree(nodep)); clearAssertInfo(); } + virtual void visit(AstFell* nodep) VL_OVERRIDE { + if (nodep->sentreep()) return; // Already processed + iterateChildren(nodep); + FileLine* fl = nodep->fileline(); + AstNode* exprp = nodep->exprp()->unlinkFrBack(); + if (exprp->width() > 1) exprp = new AstSel(fl, exprp, 0, 1); + AstNode* past = new AstPast(fl, exprp, NULL); + past->dtypeFrom(exprp); + exprp = new AstAnd(fl, past, new AstNot(fl, exprp->cloneTree(false))); + exprp->dtypeSetLogicBool(); + nodep->replaceWith(exprp); + nodep->sentreep(newSenTree(nodep)); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + } virtual void visit(AstPast* nodep) VL_OVERRIDE { if (nodep->sentreep()) return; // Already processed iterateChildren(nodep); nodep->sentreep(newSenTree(nodep)); } + virtual void visit(AstRose* nodep) VL_OVERRIDE { + if (nodep->sentreep()) return; // Already processed + iterateChildren(nodep); + FileLine* fl = nodep->fileline(); + AstNode* exprp = nodep->exprp()->unlinkFrBack(); + if (exprp->width() > 1) exprp = new AstSel(fl, exprp, 0, 1); + AstNode* past = new AstPast(fl, exprp, NULL); + past->dtypeFrom(exprp); + exprp = new AstAnd(fl, new AstNot(fl, past), exprp->cloneTree(false)); + exprp->dtypeSetLogicBool(); + nodep->replaceWith(exprp); + nodep->sentreep(newSenTree(nodep)); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + } virtual void visit(AstStable* nodep) VL_OVERRIDE { if (nodep->sentreep()) return; // Already processed iterateChildren(nodep); diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index cc4087f93..f24e6b37c 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -7991,6 +7991,31 @@ public: virtual bool isHeavy() const { return true; } }; +class AstFell : public AstNodeMath { + // Verilog $fell + // Parents: math + // Children: expression +public: + AstFell(FileLine* fl, AstNode* exprp) + : ASTGEN_SUPER(fl) { + addOp1p(exprp); + } + ASTNODE_NODE_FUNCS(Fell) + virtual string emitVerilog() { return "$fell(%l)"; } + 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() const { V3ERROR_NA_RETURN(""); } + virtual int instrCount() const { return widthInstrs(); } + AstNode* exprp() const { return op1p(); } // op1 = expression + AstSenTree* sentreep() const { return VN_CAST(op2p(), SenTree); } // op2 = clock domain + void sentreep(AstSenTree* sentreep) { addOp2p(sentreep); } // op2 = clock domain + virtual V3Hash sameHash() const { return V3Hash(); } + virtual bool same(const AstNode* samep) const { return true; } +}; + class AstPast : public AstNodeMath { // Verilog $past // Parents: math @@ -8018,6 +8043,31 @@ public: virtual bool same(const AstNode* samep) const { return true; } }; +class AstRose : public AstNodeMath { + // Verilog $rose + // Parents: math + // Children: expression +public: + AstRose(FileLine* fl, AstNode* exprp) + : ASTGEN_SUPER(fl) { + addOp1p(exprp); + } + ASTNODE_NODE_FUNCS(Rose) + virtual string emitVerilog() { return "$rose(%l)"; } + 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() const { V3ERROR_NA_RETURN(""); } + virtual int instrCount() const { return widthInstrs(); } + AstNode* exprp() const { return op1p(); } // op1 = expression + AstSenTree* sentreep() const { return VN_CAST(op2p(), SenTree); } // op2 = clock domain + void sentreep(AstSenTree* sentreep) { addOp2p(sentreep); } // op2 = clock domain + virtual V3Hash sameHash() const { return V3Hash(); } + virtual bool same(const AstNode* samep) const { return true; } +}; + class AstSampled : public AstNodeMath { // Verilog $sampled // Parents: math diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 6d4de0cde..194e46c18 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -1031,6 +1031,12 @@ 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(AstFell* nodep) VL_OVERRIDE { + if (m_vup->prelim()) { + iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH); + nodep->dtypeSetLogicBool(); + } + } virtual void visit(AstPast* nodep) VL_OVERRIDE { if (m_vup->prelim()) { iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH); @@ -1055,6 +1061,13 @@ private: } } } + virtual void visit(AstRose* nodep) VL_OVERRIDE { + if (m_vup->prelim()) { + iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH); + nodep->dtypeSetLogicBool(); + } + } + virtual void visit(AstSampled* nodep) VL_OVERRIDE { if (m_vup->prelim()) { iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH); diff --git a/src/verilog.l b/src/verilog.l index 7d4d5c843..f21a64f1c 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -422,6 +422,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} "$dimensions" { FL; return yD_DIMENSIONS; } "$error" { FL; return yD_ERROR; } "$fatal" { FL; return yD_FATAL; } + "$fell" { FL; return yD_FELL; } "$high" { FL; return yD_HIGH; } "$increment" { FL; return yD_INCREMENT; } "$info" { FL; return yD_INFO; } @@ -434,6 +435,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} "$past" { FL; return yD_PAST; } "$right" { FL; return yD_RIGHT; } "$root" { FL; return yD_ROOT; } + "$rose" { FL; return yD_ROSE; } "$size" { FL; return yD_SIZE; } "$stable" { FL; return yD_STABLE; } "$unpacked_dimensions" { FL; return yD_UNPACKED_DIMENSIONS; } diff --git a/src/verilog.y b/src/verilog.y index 1c65e6496..9ae8db91a 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -719,6 +719,7 @@ BISONPRE_VERSION(3.0,%define parse.error verbose) %token yD_FDISPLAYB "$fdisplayb" %token yD_FDISPLAYH "$fdisplayh" %token yD_FDISPLAYO "$fdisplayo" +%token yD_FELL "$fell" %token yD_FEOF "$feof" %token yD_FERROR "$ferror" %token yD_FFLUSH "$fflush" @@ -760,6 +761,7 @@ BISONPRE_VERSION(3.0,%define parse.error verbose) %token yD_REWIND "$rewind" %token yD_RIGHT "$right" %token yD_ROOT "$root" +%token yD_ROSE "$rose" %token yD_RTOI "$rtoi" %token yD_SAMPLED "$sampled" %token yD_SFORMAT "$sformat" @@ -3596,6 +3598,8 @@ system_f_call_or_t: // IEEE: part of system_tf_call (can be task or func) | yD_COUNTONES '(' expr ')' { $$ = new AstCountOnes($1,$3); } | yD_DIMENSIONS '(' exprOrDataType ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_DIMENSIONS,$3); } | yD_EXP '(' expr ')' { $$ = new AstExpD($1,$3); } + | yD_FELL '(' expr ')' { $$ = new AstFell($1,$3); } + | yD_FELL '(' expr ',' expr ')' { $$ = $3; BBUNSUP($1, "Unsupported: $fell and clock arguments"); } | yD_FEOF '(' expr ')' { $$ = new AstFEof($1,$3); } | yD_FERROR '(' idClassSel ',' idClassSel ')' { $$ = new AstFError($1, $3, $5); } | yD_FGETC '(' expr ')' { $$ = new AstFGetC($1,$3); } @@ -3637,6 +3641,8 @@ system_f_call_or_t: // IEEE: part of system_tf_call (can be task or func) | yD_REWIND '(' idClassSel ')' { $$ = new AstFSeek($1, $3, new AstConst($1, 0), new AstConst($1, 0)); } | yD_RIGHT '(' exprOrDataType ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_RIGHT,$3,NULL); } | yD_RIGHT '(' exprOrDataType ',' expr ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_RIGHT,$3,$5); } + | yD_ROSE '(' expr ')' { $$ = new AstRose($1,$3); } + | yD_ROSE '(' expr ',' expr ')' { $$ = $3; BBUNSUP($1, "Unsupported: $rose and clock arguments"); } | yD_RTOI '(' expr ')' { $$ = new AstRToIS($1,$3); } | yD_SAMPLED '(' expr ')' { $$ = new AstSampled($1, $3); } | yD_SFORMATF '(' exprDispList ')' { $$ = new AstSFormatF($1, AstSFormatF::NoFormat(), $3, 'd', false); } diff --git a/test_regress/t/t_fell.pl b/test_regress/t/t_fell.pl new file mode 100644 index 000000000..c1a6773e9 --- /dev/null +++ b/test_regress/t/t_fell.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(simulator => 1); + +compile( + verilator_flags2 => ['--assert'], + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_fell.v b/test_regress/t/t_fell.v new file mode 100644 index 000000000..eff06f114 --- /dev/null +++ b/test_regress/t/t_fell.v @@ -0,0 +1,81 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2020 by Peter Monsson. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + integer cyc; initial cyc=2; + wire [31:0] in = cyc; + + Test test (/*AUTOINST*/ + // Inputs + .clk (clk), + .in (in[31:0])); + + Test2 test2 (/*AUTOINST*/ + // Inputs + .clk (clk), + .in (in[31:0])); + + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + if (cyc==10) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end + +endmodule + +module Test (/*AUTOARG*/ + // Inputs + clk, in + ); + + input clk; + input [31:0] in; + + reg [31:0] dly0 = 1; + + // 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; + // In clock expression + $write("in=%0d, dly0=%0d, fell=%0d, past=%0d\n", in, dly0, $fell(dly0), $past(dly0)); + if ($fell(dly0[4])) $stop; + end + + assert property (@(posedge clk) $fell(dly0) || dly0%2==1); +endmodule + + +module Test2 (/*AUTOARG*/ + // Inputs + clk, in + ); + + input clk; + input [31:0] in; + + reg [31:0] dly0 = 1; + + always @(posedge clk) begin + dly0 <= in; + if ($fell(dly0[31:4])) $stop; + end + + default clocking @(posedge clk); endclocking + assert property ($fell(dly0[0]) || dly0%2==1); +endmodule diff --git a/test_regress/t/t_rose.pl b/test_regress/t/t_rose.pl new file mode 100644 index 000000000..c1a6773e9 --- /dev/null +++ b/test_regress/t/t_rose.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(simulator => 1); + +compile( + verilator_flags2 => ['--assert'], + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_rose.v b/test_regress/t/t_rose.v new file mode 100644 index 000000000..1eadaa61d --- /dev/null +++ b/test_regress/t/t_rose.v @@ -0,0 +1,81 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2020 by Peter Monsson. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + integer cyc; initial cyc=1; + wire [31:0] in = cyc; + + Test test (/*AUTOINST*/ + // Inputs + .clk (clk), + .in (in[31:0])); + + Test2 test2 (/*AUTOINST*/ + // Inputs + .clk (clk), + .in (in[31:0])); + + always @ (posedge clk) begin + if (cyc!=0) begin + cyc <= cyc + 1; + if (cyc==10) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + end + +endmodule + +module Test (/*AUTOARG*/ + // Inputs + clk, in + ); + + input clk; + input [31:0] in; + + reg [31:0] dly0 = 0; + + // 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; + // In clock expression + $write("in=%0d, dly0=%0d, rose=%0d, past=%0d\n", in, dly0, $rose(dly0), $past(dly0)); + if ($rose(dly0[4])) $stop; + end + + assert property (@(posedge clk) $rose(dly0) || dly0%2==0); +endmodule + + +module Test2 (/*AUTOARG*/ + // Inputs + clk, in + ); + + input clk; + input [31:0] in; + + reg [31:0] dly0; + + always @(posedge clk) begin + dly0 <= in; + if ($rose(dly0[31:4])) $stop; + end + + default clocking @(posedge clk); endclocking + assert property ($rose(dly0[0]) || dly0%2==0); +endmodule