diff --git a/src/V3Ast.h b/src/V3Ast.h index f9797bc80..254935322 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -2420,6 +2420,7 @@ private: int m_level; // 1=top module, 2=cell off top module, ... int m_varNum; // Incrementing variable number int m_typeNum; // Incrementing implicit type number + VOptionBool m_unconnectedDrive; // State of `unconnected_drive public: AstNodeModule(AstType t, FileLine* fl, const string& name) : AstNode(t, fl) @@ -2461,6 +2462,8 @@ public: bool recursive() const { return m_recursive; } void recursiveClone(bool flag) { m_recursiveClone = flag; } bool recursiveClone() const { return m_recursiveClone; } + void unconnectedDrive(const VOptionBool flag) { m_unconnectedDrive = flag; } + VOptionBool unconnectedDrive() const { return m_unconnectedDrive; } }; class AstNodeRange : public AstNode { diff --git a/src/V3Inline.cpp b/src/V3Inline.cpp index 4525ee045..f1721588a 100644 --- a/src/V3Inline.cpp +++ b/src/V3Inline.cpp @@ -547,7 +547,6 @@ private: // we'll save work, and we can't call pinReconnectSimple in // this loop as it clone()s itself. for (AstPin* pinp = nodep->pinsp(); pinp; pinp = VN_CAST(pinp->nextp(), Pin)) { - if (!pinp->exprp()) continue; V3Inst::pinReconnectSimple(pinp, nodep, false); } diff --git a/src/V3Inst.cpp b/src/V3Inst.cpp index e3895e611..caa68d056 100644 --- a/src/V3Inst.cpp +++ b/src/V3Inst.cpp @@ -62,13 +62,15 @@ private: // PIN(p,expr) -> ASSIGNW(VARXREF(p),expr) (if sub's input) // or ASSIGNW(expr,VARXREF(p)) (if sub's output) UINFO(4," PIN "<user1()) { + // Simplify it + V3Inst::pinReconnectSimple(nodep, m_cellp, false); + } if (!nodep->exprp()) return; // No-connect if (debug()>=9) nodep->dumpTree(cout, " Pin_oldb: "); V3Inst::checkOutputShort(nodep); // Use user1p on the PIN to indicate we created an assign for this pin if (!nodep->user1SetOnce()) { - // Simplify it - V3Inst::pinReconnectSimple(nodep, m_cellp, false); // Make an ASSIGNW (expr, pin) AstNode* exprp = nodep->exprp()->cloneTree(false); UASSERT_OBJ(exprp->width() == nodep->modVarp()->width(), nodep, @@ -481,8 +483,20 @@ public: // Else create a intermediate wire to perform the interconnect // Return the new assignment, if one was made // Note this module calles cloneTree() via new AstVar - AstVar* pinVarp = pinp->modVarp(); + if (!pinp->exprp()) { + // No-connect, perhaps promote based on `unconnected_drive, + // otherwise done + if (pinVarp->direction() == VDirection::INPUT + && cellp->modp()->unconnectedDrive().isSetTrue()) { + pinp->exprp(new AstConst(pinp->fileline(), AstConst::StringToParse(), "'1")); + } else if (pinVarp->direction() == VDirection::INPUT + && cellp->modp()->unconnectedDrive().isSetFalse()) { + pinp->exprp(new AstConst(pinp->fileline(), AstConst::StringToParse(), "'0")); + } else { + return NULL; + } + } AstVarRef* connectRefp = VN_CAST(pinp->exprp(), VarRef); AstVarXRef* connectXRefp = VN_CAST(pinp->exprp(), VarXRef); AstBasicDType* pinBasicp = VN_CAST(pinVarp->dtypep(), BasicDType); // Maybe NULL diff --git a/src/V3Options.h b/src/V3Options.h index 890395aba..dff2275e2 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -53,6 +53,8 @@ public: bool isDefault() const { return m_e == OPT_DEFAULT_FALSE || m_e == OPT_DEFAULT_TRUE; } bool isTrue() const { return m_e == OPT_TRUE || m_e == OPT_DEFAULT_TRUE; } bool isFalse() const { return m_e == OPT_FALSE || m_e == OPT_DEFAULT_FALSE; } + bool isSetTrue() const { return m_e == OPT_TRUE; } + bool isSetFalse() const { return m_e == OPT_FALSE; } void setTrueOrFalse(bool flag) { m_e = flag ? OPT_TRUE : OPT_FALSE; } const char* ascii() const { static const char* const names[] = { diff --git a/src/V3ParseImp.h b/src/V3ParseImp.h index f6560326e..7c2076f4e 100644 --- a/src/V3ParseImp.h +++ b/src/V3ParseImp.h @@ -109,6 +109,7 @@ class V3ParseImp { bool m_inLibrary; // Currently reading a library vs. regular file int m_inBeginKwd; // Inside a `begin_keywords int m_lastVerilogState; // Last LEX state in `begin_keywords + VOptionBool m_unconnectedDrive; // Last unconnected drive int m_prevLexToken; // previous parsed token (for lexer) bool m_ahead; // aheadval is valid @@ -199,6 +200,8 @@ public: bool inCellDefine() const { return m_inCellDefine; } void inCellDefine(bool flag) { m_inCellDefine = flag; } bool inLibrary() const { return m_inLibrary; } + VOptionBool unconnectedDrive() const { return m_unconnectedDrive; } + void unconnectedDrive(const VOptionBool flag) { m_unconnectedDrive = flag; } // Interactions with parser int bisonParse(); diff --git a/src/V3Tristate.cpp b/src/V3Tristate.cpp index 362104e12..4886e99d6 100644 --- a/src/V3Tristate.cpp +++ b/src/V3Tristate.cpp @@ -1087,10 +1087,8 @@ class TristateVisitor : public TristateBaseVisitor { // Find child module's new variables. AstVar* enModVarp = static_cast(nodep->modVarp()->user1p()); if (!enModVarp) { - if (nodep->exprp()) { - // May have an output only that later connects to a tristate, so simplify now. - V3Inst::pinReconnectSimple(nodep, m_cellp, false); - } + // May have an output only that later connects to a tristate, so simplify now. + V3Inst::pinReconnectSimple(nodep, m_cellp, false); iteratePinGuts(nodep); return; // No __en signals on this pin } diff --git a/src/verilog.l b/src/verilog.l index 13a3229ef..9ab7bfdeb 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -966,7 +966,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} "`noremove_gatenames" { FL_FWD; FL_BRK; } // Verilog-XL compatibility "`noremove_netnames" { FL_FWD; FL_BRK; } // Verilog-XL compatibility "`nosuppress_faults" { FL_FWD; FL_BRK; } // Verilog-XL compatibility - "`nounconnected_drive" { FL_FWD; FL_BRK; } // Verilog-XL compatibility + "`nounconnected_drive" { FL_FWD; PARSEP->unconnectedDrive(VOptionBool::OPT_DEFAULT_FALSE); FL_BRK; } "`portcoerce" { FL_FWD; FL_BRK; } "`pragma"{ws}*[^\n\r]* { FL_FWD; FL_BRK; } // Verilog 2005 "`protect" { FL_FWD; FL_BRK; } @@ -976,6 +976,9 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} return yaT_RESETALL; } // Rest handled by preproc "`suppress_faults" { FL_FWD; FL_BRK; } // Verilog-XL compatibility "`timescale"{ws}+[^\n\r]* { FL_FWD; FL_BRK; } // Verilog spec - not supported + "`unconnected_drive"{ws}+"pull0" { FL_FWD; PARSEP->unconnectedDrive(VOptionBool::OPT_FALSE); FL_BRK; } + "`unconnected_drive"{ws}+"pull1" { FL_FWD; PARSEP->unconnectedDrive(VOptionBool::OPT_TRUE); FL_BRK; } + "`unconnected_drive" { FL_FWD; yyerrorf("Bad `unconnected_drive syntax"); FL_BRK; } "`uselib"{ws}+[^\n\r]* { FL_FWD; FL_BRK; } // Verilog-XL compatibility /* See also setLanguage below */ diff --git a/src/verilog.y b/src/verilog.y index a830caba0..2fee40057 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -953,6 +953,7 @@ modFront: { $$ = new AstModule($3,*$3); $$->inLibrary(PARSEP->inLibrary() || PARSEP->inCellDefine()); $$->modTrace(GRAMMARP->allTracingOn($$->fileline())); + $$->unconnectedDrive(PARSEP->unconnectedDrive()); PARSEP->rootp()->addModulep($$); SYMP->pushNew($$); } ; diff --git a/test_regress/t/t_unconnected.pl b/test_regress/t/t_unconnected.pl new file mode 100755 index 000000000..d89c3301f --- /dev/null +++ b/test_regress/t/t_unconnected.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 2004 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_unconnected.v b/test_regress/t/t_unconnected.v new file mode 100644 index 000000000..4cce7be42 --- /dev/null +++ b/test_regress/t/t_unconnected.v @@ -0,0 +1,46 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2020 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t(/*AUTOARG*/ + // Inputs + clk + ); + input clk; + + wire o_n; + wire o_0; + wire o_1; + + // verilator lint_off PINMISSING + sub_0 sub_0(.o_0); + sub_1 sub_1(.o_1); + sub_n sub_n(.o_n); + // verilator lint_on PINMISSING + + always @ (posedge clk) begin + if (o_0 !== 1'b0) $stop; + if (o_1 !== 1'b1) $stop; + //4-state if (o_n !== 1'bz) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule + +`unconnected_drive pull0 +module sub_0 (input i, output wire o_0); + assign o_0 = i; +endmodule + +`unconnected_drive pull1 +module sub_1 (input i, output wire o_1); + assign o_1 = i; +endmodule + +`nounconnected_drive +module sub_n (input i, output wire o_n); + assign o_n = i; +endmodule diff --git a/test_regress/t/t_unconnected_bad.out b/test_regress/t/t_unconnected_bad.out new file mode 100644 index 000000000..6ba09dcd1 --- /dev/null +++ b/test_regress/t/t_unconnected_bad.out @@ -0,0 +1,10 @@ +%Error: t/t_unconnected_bad.v:7:1: Bad `unconnected_drive syntax + 7 | `unconnected_drive + | ^~~~~~~~~~~~~~~~~~ +%Error: t/t_unconnected_bad.v:9:1: Bad `unconnected_drive syntax + 9 | `unconnected_drive pull2 + | ^~~~~~~~~~~~~~~~~~ +%Error: t/t_unconnected_bad.v:9:20: syntax error, unexpected IDENTIFIER + 9 | `unconnected_drive pull2 + | ^~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_unconnected_bad.pl b/test_regress/t/t_unconnected_bad.pl new file mode 100755 index 000000000..67a7abcb0 --- /dev/null +++ b/test_regress/t/t_unconnected_bad.pl @@ -0,0 +1,19 @@ +#!/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(linter => 1); + +lint( + fails => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_unconnected_bad.v b/test_regress/t/t_unconnected_bad.v new file mode 100644 index 000000000..46affd2f9 --- /dev/null +++ b/test_regress/t/t_unconnected_bad.v @@ -0,0 +1,12 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2018 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +`unconnected_drive + +`unconnected_drive pull2 + +module t; +endmodule