From 87e1c36e4a4c4a2bd44e8a886b8344aad380a9e6 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 25 Apr 2020 15:37:46 -0400 Subject: [PATCH] Support event data type (with some restrictions). --- Changes | 2 + src/V3Active.cpp | 10 +++ src/V3Ast.h | 18 ++-- src/V3AstNodes.h | 1 + src/V3Delayed.cpp | 17 +++- src/V3Width.cpp | 20 +++++ src/verilog.y | 11 ++- test_regress/t/t_event.pl | 21 +++++ test_regress/t/t_event.v | 81 ++++++++++++++++++ test_regress/t/t_event_copy.out | 9 ++ test_regress/t/t_event_copy.pl | 23 ++++++ test_regress/t/t_event_copy.v | 142 ++++++++++++++++++++++++++++++++ 12 files changed, 344 insertions(+), 11 deletions(-) create mode 100755 test_regress/t/t_event.pl create mode 100644 test_regress/t/t_event.v create mode 100644 test_regress/t/t_event_copy.out create mode 100755 test_regress/t/t_event_copy.pl create mode 100644 test_regress/t/t_event_copy.v diff --git a/Changes b/Changes index 483d49ecd..03d6ec43f 100644 --- a/Changes +++ b/Changes @@ -26,6 +26,8 @@ The contributors that suggested a given feature are shown in []. Thanks! **** Support $ferror, and $fflush without arguments, #1638. +**** Support event data type (with some restrictions). + **** Add error if use SystemC 2.2 and earlier (pre-2011) as is deprecated. **** Greatly improve FST/VCD dump performance, #2244, #2246, #2250, #2257. [Geza Lore] diff --git a/src/V3Active.cpp b/src/V3Active.cpp index 42c3ea5d1..13689cb9f 100644 --- a/src/V3Active.cpp +++ b/src/V3Active.cpp @@ -388,6 +388,16 @@ private: iterateChildren(nodep); } virtual void visit(AstSenItem* nodep) VL_OVERRIDE { + if (nodep->varrefp()) { + if (AstBasicDType* basicp = nodep->varrefp()->dtypep()->basicp()) { + if (basicp->isEventValue()) { + // Events need to be treated as active high so we only activate on event being + // 1 + UINFO(8, "Demote event to HIGHEDGE " << nodep << endl); + nodep->edgeType(VEdgeType::ET_HIGHEDGE); + } + } + } if (nodep->edgeType() == VEdgeType::ET_ANYEDGE) { m_itemCombo = true; // Delete the sensitivity diff --git a/src/V3Ast.h b/src/V3Ast.h index 2fd9c30ce..19a1fd6dc 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -372,6 +372,7 @@ public: BIT, BYTE, CHANDLE, + EVENTVALUE, // See comments in t_event_copy as to why this is EVENTVALUE INT, INTEGER, LOGIC, @@ -396,14 +397,16 @@ public: enum en m_e; const char* ascii() const { static const char* const names[] = { - "%E-unk", "bit", "byte", "chandle", "int", "integer", "logic", + "%E-unk", // + "bit", "byte", "chandle", "event", "int", "integer", "logic", "longint", "real", "shortint", "shortreal", "time", "string", "VerilatedScope*", "char*", "IData", "QData", "LOGIC_IMPLICIT", " MAX"}; return names[m_e]; } const char* dpiType() const { static const char* const names[] - = {"%E-unk", "svBit", "char", "void*", "int", "%E-integer", + = {"%E-unk", // + "svBit", "char", "void*", "char", "int", "%E-integer", "svLogic", "long long", "double", "short", "float", "%E-time", "const char*", "dpiScope", "const char*", "IData", "QData", "%E-logic-implicit", " MAX"}; @@ -428,6 +431,7 @@ public: case BIT: return 1; // scalar, can't bit extract unless ranged case BYTE: return 8; case CHANDLE: return 64; + case EVENTVALUE: return 1; case INT: return 32; case INTEGER: return 32; case LOGIC: return 1; // scalar, can't bit extract unless ranged @@ -449,15 +453,16 @@ public: || m_e == DOUBLE || m_e == FLOAT; } bool isUnsigned() const { - return m_e == CHANDLE || m_e == STRING || m_e == SCOPEPTR || m_e == CHARPTR - || m_e == UINT32 || m_e == UINT64; + return m_e == CHANDLE || m_e == EVENTVALUE || m_e == STRING || m_e == SCOPEPTR + || m_e == CHARPTR || m_e == UINT32 || m_e == UINT64; } bool isFourstate() const { return m_e == INTEGER || m_e == LOGIC || m_e == LOGIC_IMPLICIT || m_e == TIME; } bool isZeroInit() const { // Otherwise initializes to X - return (m_e == BIT || m_e == BYTE || m_e == CHANDLE || m_e == INT || m_e == LONGINT - || m_e == SHORTINT || m_e == STRING || m_e == DOUBLE || m_e == FLOAT); + return (m_e == BIT || m_e == BYTE || m_e == CHANDLE || m_e == EVENTVALUE || m_e == INT + || m_e == LONGINT || m_e == SHORTINT || m_e == STRING || m_e == DOUBLE + || m_e == FLOAT); } bool isIntNumeric() const { // Enum increment supported return (m_e == BIT || m_e == BYTE || m_e == INT || m_e == INTEGER || m_e == LOGIC @@ -481,6 +486,7 @@ public: || m_e == FLOAT); } bool isDouble() const { return m_e == DOUBLE; } + bool isEventValue() const { return m_e == EVENTVALUE; } bool isString() const { return m_e == STRING; } }; inline bool operator==(const AstBasicDTypeKwd& lhs, const AstBasicDTypeKwd& rhs) { diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 97014185a..602148564 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -840,6 +840,7 @@ public: } bool isBitLogic() const { return keyword().isBitLogic(); } bool isDouble() const { return keyword().isDouble(); } + bool isEventValue() const { return keyword().isEventValue(); } bool isOpaque() const { return keyword().isOpaque(); } bool isString() const { return keyword().isString(); } bool isSloppy() const { return keyword().isSloppy(); } diff --git a/src/V3Delayed.cpp b/src/V3Delayed.cpp index ecf80dccb..44b8bc858 100644 --- a/src/V3Delayed.cpp +++ b/src/V3Delayed.cpp @@ -385,6 +385,8 @@ private: nodep->v3warn(BLKLOOPINIT, "Unsupported: Delayed assignment to array inside for " "loops (non-delayed is ok - see docs)"); } + AstBasicDType* basicp = lhsp->dtypep()->basicp(); + if (basicp && basicp->isEventValue()) nodep->v3error("Unsupported: event arrays"); if (newlhsp) { nodep->lhsp(newlhsp); } else { @@ -418,9 +420,18 @@ private: if (!dlyvscp) { // First use of this delayed variable string newvarname = (string("__Vdly__") + nodep->varp()->shortName()); dlyvscp = createVarSc(oldvscp, newvarname, 0, NULL); - AstNodeAssign* prep = new AstAssignPre( - nodep->fileline(), new AstVarRef(nodep->fileline(), dlyvscp, true), - new AstVarRef(nodep->fileline(), oldvscp, false)); + AstNodeAssign* prep; + AstBasicDType* basicp = oldvscp->dtypep()->basicp(); + if (basicp && basicp->isEventValue()) { + // Events go to zero on next timestep unless reactivated + prep = new AstAssignPre( + nodep->fileline(), new AstVarRef(nodep->fileline(), dlyvscp, true), + new AstConst(nodep->fileline(), AstConst::LogicFalse())); + } else { + prep = new AstAssignPre(nodep->fileline(), + new AstVarRef(nodep->fileline(), dlyvscp, true), + new AstVarRef(nodep->fileline(), oldvscp, false)); + } AstNodeAssign* postp = new AstAssignPost( nodep->fileline(), new AstVarRef(nodep->fileline(), oldvscp, true), new AstVarRef(nodep->fileline(), dlyvscp, false)); diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 36ff94be0..940adb4fb 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -2025,6 +2025,8 @@ private: methodCallClass(nodep, adtypep); } else if (AstUnpackArrayDType* adtypep = VN_CAST(fromDtp, UnpackArrayDType)) { methodCallUnpack(nodep, adtypep); + } else if (basicp && basicp->isEventValue()) { + methodCallEvent(nodep, basicp); } else if (basicp && basicp->isString()) { methodCallString(nodep, basicp); } else { @@ -2431,6 +2433,18 @@ private: nodep->v3error("Unknown built-in array method " << nodep->prettyNameQ()); } } + void methodCallEvent(AstMethodCall* nodep, AstBasicDType* adtypep) { + // Method call on event + if (nodep->name() == "triggered") { + // We represent events as numbers, so can just return number + methodOkArguments(nodep, 0, 0); + AstNode* newp = nodep->fromp()->unlinkFrBack(); + nodep->replaceWith(newp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + } else { + nodep->v3error("Unknown built-in event method " << nodep->prettyNameQ()); + } + } void methodCallString(AstMethodCall* nodep, AstBasicDType* adtypep) { // Method call on string if (nodep->name() == "len") { @@ -3036,6 +3050,12 @@ private: iterateCheckAssign(nodep, "Assign RHS", nodep->rhsp(), FINAL, lhsDTypep); // if (debug()) nodep->dumpTree(cout, " AssignOut: "); } + if (AstBasicDType* basicp = nodep->rhsp()->dtypep()->basicp()) { + if (basicp->isEventValue()) { + // see t_event_copy.v for commentary on the mess involved + nodep->v3error("Unsupported: assignment of event data type"); + } + } if (AstNewDynamic* dynp = VN_CAST(nodep->rhsp(), NewDynamic)) { UINFO(9, "= new[] -> .resize(): " << nodep); AstCMethodHard* newp; diff --git a/src/verilog.y b/src/verilog.y index 4f6d2bd43..ea727f021 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -1623,7 +1623,7 @@ data_typeNoRef: // ==IEEE: data_type, excluding class_type etc referenc SYMP,VFlagChildDType(),$1); } | ySTRING { $$ = new AstBasicDType($1,AstBasicDTypeKwd::STRING); } | yCHANDLE { $$ = new AstBasicDType($1,AstBasicDTypeKwd::CHANDLE); } - | yEVENT { $$ = new AstBasicDType($1,AstBasicDTypeKwd::BIT); BBUNSUP($1, "Unsupported: event data types"); } + | yEVENT { $$ = new AstBasicDType($1,AstBasicDTypeKwd::EVENTVALUE); } // // Rules overlap virtual_interface_declaration // // Parameters here are SV2009 // // IEEE has ['.' modport] but that will conflict with port @@ -2813,8 +2813,15 @@ statement_item: // IEEE: statement_item | yDISABLE idAny/*hierarchical_identifier-task_or_block*/ ';' { $$ = new AstDisable($1,*$2); } | yDISABLE yFORK ';' { $$ = NULL; BBUNSUP($1, "Unsupported: disable fork statements"); } // // IEEE: event_trigger - //UNSUP yP_MINUSGT hierarchical_identifier/*event*/ ';' { UNSUP } + | yP_MINUSGT idDotted/*hierarchical_identifier-event*/ ';' + { // AssignDly because we don't have stratified queue, and need to + // read events, clear next event, THEN apply this set + $$ = new AstAssignDly($1, $2, new AstConst($1, AstConst::LogicTrue())); } //UNSUP yP_MINUSGTGT delay_or_event_controlE hierarchical_identifier/*event*/ ';' { UNSUP } + // // IEEE remove below + | yP_MINUSGTGT delayE idDotted/*hierarchical_identifier-event*/ ';' + { $$ = new AstAssignDly($1, $3, new AstConst($1, AstConst::LogicTrue())); } + // // // IEEE: loop_statement | yFOREVER stmtBlock { $$ = new AstWhile($1,new AstConst($1,AstConst::LogicTrue()),$2); } | yREPEAT '(' expr ')' stmtBlock { $$ = new AstRepeat($1,$3,$5);} diff --git a/test_regress/t/t_event.pl b/test_regress/t/t_event.pl new file mode 100755 index 000000000..1d046ed3f --- /dev/null +++ b/test_regress/t/t_event.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. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(simulator => 1); + +compile( + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_event.v b/test_regress/t/t_event.v new file mode 100644 index 000000000..0f5384c14 --- /dev/null +++ b/test_regress/t/t_event.v @@ -0,0 +1,81 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2003 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +`ifdef TEST_VERBOSE + `define WRITE_VERBOSE(args) $write args +`else + `define WRITE_VERBOSE(args) +`endif + +module t(/*AUTOARG*/ + // Inputs + clk + ); + input clk; + + event e1; + event e2; +`ifndef IVERILOG + event ev [3:0]; +`endif + + int cyc; + + int last_event; + always @(e1) begin + `WRITE_VERBOSE(("[%0t] e1\n", $time)); + if (!e1.triggered) $stop; + last_event[1] = 1; + end + + always @(e2) begin + `WRITE_VERBOSE(("[%0t] e2\n", $time)); + last_event[2] = 1; + end + + always @(posedge clk) begin + `WRITE_VERBOSE(("[%0t] cyc=%0d last_event=%5b\n", $time, cyc, last_event)); + cyc <= cyc + 1; + if (cyc == 1) begin + // Check no initial trigger + if (last_event != 0) $stop; + end + // + else if (cyc == 10) begin + last_event = 0; + -> e1; + end + else if (cyc == 12) begin + if (last_event != 32'b10) $stop; + last_event = 0; + end + else if (cyc == 13) begin + // Check not still triggering + if (last_event != 0) $stop; + last_event = 0; + end + // + else if (cyc == 10) begin + last_event = 0; + ->> e2; + end + else if (cyc == 12) begin + if (last_event != 32'b100) $stop; + last_event = 0; + end + else if (cyc == 13) begin + // Check not still triggering + if (last_event != 0) $stop; + last_event = 0; + end + // + else if (cyc == 99) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule diff --git a/test_regress/t/t_event_copy.out b/test_regress/t/t_event_copy.out new file mode 100644 index 000000000..3c2a9775c --- /dev/null +++ b/test_regress/t/t_event_copy.out @@ -0,0 +1,9 @@ +%Error: t/t_event_copy.v:100:13: Unsupported: assignment of event data type + : ... In instance t + 100 | e4 = e3; + | ^ +%Error: t/t_event_copy.v:101:13: Unsupported: assignment of event data type + : ... In instance t + 101 | e3 = e2; + | ^ +%Error: Exiting due to diff --git a/test_regress/t/t_event_copy.pl b/test_regress/t/t_event_copy.pl new file mode 100755 index 000000000..052947d96 --- /dev/null +++ b/test_regress/t/t_event_copy.pl @@ -0,0 +1,23 @@ +#!/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. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(simulator => 1); + +compile( + fails => $Self->{vlt_all}, + expect_filename => $Self->{golden_filename}, + ); + +#execute( +# check_finished => 1, +# ); + +ok(1); +1; diff --git a/test_regress/t/t_event_copy.v b/test_regress/t/t_event_copy.v new file mode 100644 index 000000000..7a26fb3d9 --- /dev/null +++ b/test_regress/t/t_event_copy.v @@ -0,0 +1,142 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2003 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +`ifdef TEST_VERBOSE + `define WRITE_VERBOSE(args) $write args +`else + `define WRITE_VERBOSE(args) +`endif + +module t(/*AUTOARG*/ + // Inputs + clk + ); + input clk; + + event e1; + event e2; + event e3; + event e4; +`ifndef IVERILOG + event ev [3:0]; +`endif + + int cyc; + + int last_event; + always @(e1) begin + `WRITE_VERBOSE(("[%0t] e1\n", $time)); +`ifndef IVERILOG + if (!e1.triggered) $stop; +`endif + last_event[1] = 1; + end + + always @(e2) begin + `WRITE_VERBOSE(("[%0t] e2\n", $time)); + last_event[2] = 1; + end + + always @(e3) begin + `WRITE_VERBOSE(("[%0t] e3\n", $time)); + last_event[3] = 1; + end + + always @(e4) begin + `WRITE_VERBOSE(("[%0t] e4\n", $time)); + last_event[4] = 1; + end + + always @(posedge clk) begin + `WRITE_VERBOSE(("[%0t] cyc=%0d last_event=%5b\n", $time, cyc, last_event)); + cyc <= cyc + 1; + if (cyc == 1) begin + // Check no initial trigger + if (last_event != 0) $stop; + end + // + else if (cyc == 10) begin + last_event = 0; + -> e1; + end + else if (cyc == 12) begin + if (last_event != 32'b10) $stop; + last_event = 0; + end + else if (cyc == 13) begin + // Check not still triggering + if (last_event != 0) $stop; + last_event = 0; + end + // + else if (cyc == 20) begin + last_event = 0; +`ifdef IVERILOG + -> e2; +`else + // Events are both references and events themselves. I.e. 'event e' + // declaration means 'event e = new'. Then e is a reference to that + // created event. + // + // Always having indirection is bad for performance, so Verilator + // should have 'event e' as an "EVENTVALUE" stored as a char, or + // ideally a one bit field reference (not vector as that can't be + // V3Ordered). + // + // Then events once copied become EVENTREFs, much like a ClassRef which + // points to an EVENTVALUE. Thus a Verilog "event" starts as an + // EVENTVALUE, and if an assignment is made it becomes an EVENTVALUE + // and an EVENTREF initing to that EVENTVALUE. + // + // All static scheduling for events would go out the window once an + // event can be pointed to by an EVENTREF, as basically any EVENTREF + // activation could be activating any event. A graph algorithm could + // determine what events/eventrefs are associated and only + // pessamistically schedule those events (users of EVENTVALUES) that + // are ever pointed to by an EVENTREF. + e4 = e3; // Old handle to e4 + e3 = e2; // Same event, also triggers e2 + // IEEE 2017 15.5.5.1 says that this causes a merge, and the below + // should also activate the "old e3". However we could not find any + // simulator that actually does this. Instead the "old e3" becomes + // unreachable (via old handle), but is reachable by "e4" as assigned + // earlier. + ->> e3; // Delayed +`endif + end + else if (cyc == 22) begin + if (last_event != 32'b100) $stop; + last_event = 0; + -> e2; // IEEE says triggers e3, but does not + end + else if (cyc == 24) begin + if (last_event != 32'b100) $stop; + last_event = 0; + -> e4; // Triggers old e3 + end + else if (cyc == 26) begin + if (last_event != 32'b1000) $stop; + last_event = 0; + end + // + else if (cyc == 30) begin + last_event = 0; +`ifndef IVERILOG + e3 = null; + -> e3; // Triggers nothing +`endif + end + else if (cyc == 32) begin + if (last_event != 0) $stop; + last_event = 0; + end + else if (cyc == 99) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule