Support 1800-2023 preprocessor ifdef expressions; add PREPROC zero warning.
This commit is contained in:
parent
3786f59e03
commit
214173c6b8
1
Changes
1
Changes
|
|
@ -14,6 +14,7 @@ Verilator 5.023 devel
|
||||||
**Major:**
|
**Major:**
|
||||||
|
|
||||||
* Support 1800-2023 keywords.
|
* Support 1800-2023 keywords.
|
||||||
|
* Support 1800-2023 preprocessor ifdef expressions.
|
||||||
* Support 1800-2023 DPI headers, svGetTime/svgGetTimeUnit/svGetTimePrecision methods.
|
* Support 1800-2023 DPI headers, svGetTime/svgGetTimeUnit/svGetTimePrecision methods.
|
||||||
|
|
||||||
**Minor:**
|
**Minor:**
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,9 @@ SystemVerilog 2023 (IEEE 1800-2023) Support
|
||||||
Verilator supports some of the 2023 improvements, including triple-quoted
|
Verilator supports some of the 2023 improvements, including triple-quoted
|
||||||
string blocks that may include newlines and single quotes.
|
string blocks that may include newlines and single quotes.
|
||||||
|
|
||||||
|
Verilator implements a full IEEE 1800-2023 compliant preprocessor,
|
||||||
|
including triple-quoted strings, and \`ifdef expressions.
|
||||||
|
|
||||||
|
|
||||||
Verilog AMS Support
|
Verilog AMS Support
|
||||||
-------------------
|
-------------------
|
||||||
|
|
|
||||||
|
|
@ -1332,6 +1332,31 @@ List Of Warnings
|
||||||
be declared before being used.
|
be declared before being used.
|
||||||
|
|
||||||
|
|
||||||
|
.. option:: PREPROCZERO
|
||||||
|
|
||||||
|
Warns that a preprocessor \`ifdef/\`ifndef expression (added in IEEE
|
||||||
|
1800-2023) evaluates a define value which has a value of :code:`0`.
|
||||||
|
This will evaluate in the expression as :code:`1` because the define has
|
||||||
|
a definition, unlike in the C preprocessor, which evaluates using the
|
||||||
|
define's value (of :code:`1`).
|
||||||
|
|
||||||
|
Referring to a define with an empty value does not give this warning, as
|
||||||
|
in C, the preprocessor will give an error on a preprocessor expression
|
||||||
|
of a define that is empty.
|
||||||
|
|
||||||
|
.. code-block:: sv
|
||||||
|
:linenos:
|
||||||
|
:emphasize-lines: 5-6
|
||||||
|
|
||||||
|
`define ZERO 0
|
||||||
|
`ifdef (ZERO || ZERO) //<--- warning PREPROCZERO
|
||||||
|
`error This_will_error_which_might_be_not_the_intent
|
||||||
|
`endif
|
||||||
|
|
||||||
|
The portable way to suppress this warning is to use a define value other
|
||||||
|
than zero to when used in a preprocessor expression.
|
||||||
|
|
||||||
|
|
||||||
.. option:: PROCASSWIRE
|
.. option:: PROCASSWIRE
|
||||||
|
|
||||||
.. TODO better example
|
.. TODO better example
|
||||||
|
|
|
||||||
|
|
@ -132,6 +132,7 @@ set(HEADERS
|
||||||
V3PartitionGraph.h
|
V3PartitionGraph.h
|
||||||
V3PchAstMT.h
|
V3PchAstMT.h
|
||||||
V3PchAstNoMT.h
|
V3PchAstNoMT.h
|
||||||
|
V3PreExpr.h
|
||||||
V3PreLex.h
|
V3PreLex.h
|
||||||
V3PreProc.h
|
V3PreProc.h
|
||||||
V3PreShell.h
|
V3PreShell.h
|
||||||
|
|
|
||||||
|
|
@ -127,6 +127,7 @@ public:
|
||||||
PINNOCONNECT, // Cell pin not connected
|
PINNOCONNECT, // Cell pin not connected
|
||||||
PINNOTFOUND, // instance port name not found in it's module
|
PINNOTFOUND, // instance port name not found in it's module
|
||||||
PKGNODECL, // Error: Package/class needs to be predeclared
|
PKGNODECL, // Error: Package/class needs to be predeclared
|
||||||
|
PREPROCZERO, // Preprocessor expression with zero
|
||||||
PROCASSWIRE, // Procedural assignment on wire
|
PROCASSWIRE, // Procedural assignment on wire
|
||||||
PROFOUTOFDATE, // Profile data out of date
|
PROFOUTOFDATE, // Profile data out of date
|
||||||
PROTECTED, // detected `pragma protected
|
PROTECTED, // detected `pragma protected
|
||||||
|
|
@ -203,7 +204,7 @@ public:
|
||||||
"INCABSPATH", "INFINITELOOP", "INITIALDLY", "INSECURE",
|
"INCABSPATH", "INFINITELOOP", "INITIALDLY", "INSECURE",
|
||||||
"LATCH", "LITENDIAN", "MINTYPMAXDLY", "MISINDENT", "MODDUP",
|
"LATCH", "LITENDIAN", "MINTYPMAXDLY", "MISINDENT", "MODDUP",
|
||||||
"MULTIDRIVEN", "MULTITOP", "NEWERSTD", "NOLATCH", "NULLPORT", "PINCONNECTEMPTY",
|
"MULTIDRIVEN", "MULTITOP", "NEWERSTD", "NOLATCH", "NULLPORT", "PINCONNECTEMPTY",
|
||||||
"PINMISSING", "PINNOCONNECT", "PINNOTFOUND", "PKGNODECL", "PROCASSWIRE",
|
"PINMISSING", "PINNOCONNECT", "PINNOTFOUND", "PKGNODECL", "PREPROCZERO", "PROCASSWIRE",
|
||||||
"PROFOUTOFDATE", "PROTECTED", "RANDC", "REALCVT", "REDEFMACRO", "RISEFALLDLY",
|
"PROFOUTOFDATE", "PROTECTED", "RANDC", "REALCVT", "REDEFMACRO", "RISEFALLDLY",
|
||||||
"SELRANGE", "SHORTREAL", "SIDEEFFECT", "SPLITVAR",
|
"SELRANGE", "SHORTREAL", "SIDEEFFECT", "SPLITVAR",
|
||||||
"STATICVAR", "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET",
|
"STATICVAR", "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET",
|
||||||
|
|
@ -242,9 +243,10 @@ public:
|
||||||
return (m_e == ALWCOMBORDER || m_e == ASCRANGE || m_e == BSSPACE || m_e == CASEINCOMPLETE
|
return (m_e == ALWCOMBORDER || m_e == ASCRANGE || m_e == BSSPACE || m_e == CASEINCOMPLETE
|
||||||
|| m_e == CASEOVERLAP || m_e == CASEWITHX || m_e == CASEX || m_e == CASTCONST
|
|| m_e == CASEOVERLAP || m_e == CASEWITHX || m_e == CASEX || m_e == CASTCONST
|
||||||
|| m_e == CMPCONST || m_e == COLONPLUS || m_e == IMPLICIT || m_e == IMPLICITSTATIC
|
|| m_e == CMPCONST || m_e == COLONPLUS || m_e == IMPLICIT || m_e == IMPLICITSTATIC
|
||||||
|| m_e == LATCH || m_e == MISINDENT || m_e == NEWERSTD || m_e == PINMISSING
|
|| m_e == LATCH || m_e == MISINDENT || m_e == NEWERSTD || m_e == PREPROCZERO
|
||||||
|| m_e == REALCVT || m_e == STATICVAR || m_e == UNSIGNED || m_e == WIDTH
|
|| m_e == PINMISSING || m_e == REALCVT || m_e == STATICVAR || m_e == UNSIGNED
|
||||||
|| m_e == WIDTHTRUNC || m_e == WIDTHEXPAND || m_e == WIDTHXZEXPAND);
|
|| m_e == WIDTH || m_e == WIDTHTRUNC || m_e == WIDTHEXPAND
|
||||||
|
|| m_e == WIDTHXZEXPAND);
|
||||||
}
|
}
|
||||||
// Warnings that are style only
|
// Warnings that are style only
|
||||||
bool styleError() const VL_MT_SAFE {
|
bool styleError() const VL_MT_SAFE {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,293 @@
|
||||||
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||||
|
//*************************************************************************
|
||||||
|
// DESCRIPTION: Verilog::Preproc: Preprocess verilog code
|
||||||
|
//
|
||||||
|
// Code available from: https://verilator.org
|
||||||
|
//
|
||||||
|
//*************************************************************************
|
||||||
|
//
|
||||||
|
// Copyright 2000-2023 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 LOR Artistic-2.0
|
||||||
|
//
|
||||||
|
//*************************************************************************
|
||||||
|
|
||||||
|
#ifndef VERILATOR_V3PREEXPR_H_
|
||||||
|
#define VERILATOR_V3PREEXPR_H_
|
||||||
|
|
||||||
|
#include "config_build.h"
|
||||||
|
#include "verilatedos.h"
|
||||||
|
|
||||||
|
#include "V3FileLine.h"
|
||||||
|
#include "V3Global.h"
|
||||||
|
|
||||||
|
#include <deque>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
class V3PreExprToken final {
|
||||||
|
public:
|
||||||
|
// TYPES
|
||||||
|
// Order of enum must match token table
|
||||||
|
enum token_t : uint8_t { ZERO, ONE, END, BRA, KET, LNOT, LAND, LOR, IMP, EQV, MAX };
|
||||||
|
|
||||||
|
private:
|
||||||
|
// MEMBERS
|
||||||
|
FileLine* const m_fileline; // Token fileline
|
||||||
|
token_t const m_token; // Token value
|
||||||
|
public:
|
||||||
|
// METHODS
|
||||||
|
V3PreExprToken(FileLine* fileline, token_t token)
|
||||||
|
: m_fileline{fileline}
|
||||||
|
, m_token{token} {}
|
||||||
|
V3PreExprToken(FileLine* fileline, bool value)
|
||||||
|
: m_fileline{fileline}
|
||||||
|
, m_token{value ? ONE : ZERO} {}
|
||||||
|
~V3PreExprToken() = default;
|
||||||
|
const char* ascii() const {
|
||||||
|
static const char* names[] = {"0", "1", "$", "(", ")", "!", "&&", "||", "->", "<->"};
|
||||||
|
return names[m_token];
|
||||||
|
}
|
||||||
|
FileLine* fileline() const { return m_fileline; }
|
||||||
|
token_t token() const { return m_token; }
|
||||||
|
bool isValue() const { return m_token == ZERO || m_token == ONE; }
|
||||||
|
bool value() const {
|
||||||
|
UASSERT(isValue(), "preproc expr fetch of non-value");
|
||||||
|
return m_token == ONE;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class V3PreExpr final {
|
||||||
|
// MEMBERS
|
||||||
|
std::deque<V3PreExprToken> m_inputs; // Input stack
|
||||||
|
std::deque<V3PreExprToken> m_values; // Value stack
|
||||||
|
std::deque<V3PreExprToken> m_ops; // Operator stack
|
||||||
|
FileLine* m_firstFileline = nullptr;
|
||||||
|
|
||||||
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||||
|
|
||||||
|
// PARSER DEFINITION
|
||||||
|
enum action_t : uint8_t {
|
||||||
|
VV, // Value
|
||||||
|
AA, // Accept
|
||||||
|
RR, // Reduce
|
||||||
|
SS, // Shift
|
||||||
|
EE // Error
|
||||||
|
};
|
||||||
|
static const char* actionAscii(action_t en) {
|
||||||
|
static const char* names[] = {"VV", "AA", "RR", "SS", "EE"};
|
||||||
|
return names[en];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Operators Associativity Precedence
|
||||||
|
// --------- ------------- ----------
|
||||||
|
// () Left Highest
|
||||||
|
// ! (unary)
|
||||||
|
// && Left
|
||||||
|
// || Left
|
||||||
|
// -> <-> Right Lowest
|
||||||
|
//
|
||||||
|
// If different op precedence, shift for higher to input, else reduce for lower
|
||||||
|
// If same op precedence, shift for right assoc, reduce for left assoc
|
||||||
|
|
||||||
|
action_t parseTable[V3PreExprToken::MAX][V3PreExprToken::MAX] = {
|
||||||
|
// stack ------------- inputs ------------------
|
||||||
|
// 0 1 $ ( ) ! && || -> <->
|
||||||
|
/* 0 */ {EE, EE, EE, EE, EE, EE, EE, EE, EE, EE}, // 0 never on op stack
|
||||||
|
/* 1 */ {EE, EE, EE, EE, EE, EE, EE, EE, EE, EE}, // 1 never on op stack
|
||||||
|
/* $ */ {VV, VV, AA, SS, EE, SS, SS, SS, SS, SS},
|
||||||
|
/* ( */ {VV, VV, EE, SS, RR, SS, SS, SS, SS, SS},
|
||||||
|
/* ) */ {VV, VV, RR, EE, RR, RR, RR, RR, RR, RR},
|
||||||
|
/* ! */ {VV, VV, RR, SS, RR, SS, RR, RR, RR, RR},
|
||||||
|
/* && */ {VV, VV, RR, SS, RR, SS, SS, RR, RR, RR},
|
||||||
|
/* || */ {VV, VV, RR, SS, RR, SS, SS, SS, RR, RR},
|
||||||
|
/* -> */ {VV, VV, RR, SS, RR, SS, SS, SS, RR, RR},
|
||||||
|
/* <-> */ {VV, VV, RR, SS, RR, SS, SS, SS, RR, RR}};
|
||||||
|
|
||||||
|
static void selfTestImp() {
|
||||||
|
selfTestOne("0", false);
|
||||||
|
selfTestOne("1", true);
|
||||||
|
selfTestOne("! 0", true);
|
||||||
|
selfTestOne("! 1", false);
|
||||||
|
selfTestOne("0 || 0", false);
|
||||||
|
selfTestOne("0 || 1", true);
|
||||||
|
selfTestOne("1 || 0", true);
|
||||||
|
selfTestOne("1 || 1", true);
|
||||||
|
selfTestOne("0 && 0", false);
|
||||||
|
selfTestOne("0 && 1", false);
|
||||||
|
selfTestOne("1 && 0", false);
|
||||||
|
selfTestOne("1 && 1", true);
|
||||||
|
selfTestOne("0 -> 0", true);
|
||||||
|
selfTestOne("0 -> 1", true);
|
||||||
|
selfTestOne("1 -> 0", false);
|
||||||
|
selfTestOne("1 -> 1", true);
|
||||||
|
selfTestOne("0 <-> 0", true);
|
||||||
|
selfTestOne("0 <-> 1", false);
|
||||||
|
selfTestOne("1 <-> 0", false);
|
||||||
|
selfTestOne("1 <-> 1", true);
|
||||||
|
selfTestOne("1 || 0 && 1", false);
|
||||||
|
selfTestOne("( 1 || 0 ) && 1", true);
|
||||||
|
selfTestOne("! 1 || ! 1", false);
|
||||||
|
selfTestOne("! 0 && ! 0", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void selfTestOne(const string& expr, bool expect) {
|
||||||
|
// This hacky self-test parser just looks at first character of
|
||||||
|
// operator, and requires space separation of operators/values
|
||||||
|
UINFO(9, "V3PreExpr selfTestOne " << expr << endl);
|
||||||
|
FileLine* const flp = nullptr;
|
||||||
|
V3PreExpr parser;
|
||||||
|
parser.reset(flp);
|
||||||
|
bool tstart = true;
|
||||||
|
for (const char* cp = expr.c_str(); *cp; ++cp) {
|
||||||
|
if (tstart) {
|
||||||
|
tstart = false;
|
||||||
|
switch (*cp) {
|
||||||
|
case '0': parser.pushInput(V3PreExprToken{flp, V3PreExprToken::ZERO}); break;
|
||||||
|
case '1': parser.pushInput(V3PreExprToken{flp, V3PreExprToken::ONE}); break;
|
||||||
|
case '!': parser.pushInput(V3PreExprToken{flp, V3PreExprToken::LNOT}); break;
|
||||||
|
case '|': parser.pushInput(V3PreExprToken{flp, V3PreExprToken::LOR}); break;
|
||||||
|
case '&': parser.pushInput(V3PreExprToken{flp, V3PreExprToken::LAND}); break;
|
||||||
|
case '-': parser.pushInput(V3PreExprToken{flp, V3PreExprToken::IMP}); break;
|
||||||
|
case '<': parser.pushInput(V3PreExprToken{flp, V3PreExprToken::EQV}); break;
|
||||||
|
case '(': parser.pushInput(V3PreExprToken{flp, V3PreExprToken::BRA}); break;
|
||||||
|
case ')': parser.pushInput(V3PreExprToken{flp, V3PreExprToken::KET}); break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
} else if (*cp == ' ') {
|
||||||
|
tstart = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const bool got = parser.result();
|
||||||
|
UASSERT_SELFTEST(bool, got, expect);
|
||||||
|
}
|
||||||
|
|
||||||
|
// METHODS
|
||||||
|
void pushOp(const V3PreExprToken& token) {
|
||||||
|
// UINFO(9, " pushOp " << token.ascii() << endl);
|
||||||
|
m_ops.push_back(token);
|
||||||
|
}
|
||||||
|
void pushValue(const V3PreExprToken& token) {
|
||||||
|
// UINFO(9, " pushValue " << token.ascii() << endl);
|
||||||
|
m_values.push_back(token);
|
||||||
|
}
|
||||||
|
V3PreExprToken popValue() {
|
||||||
|
if (m_values.empty()) {
|
||||||
|
m_firstFileline->v3error("Syntax error in `ifdef () expression");
|
||||||
|
return V3PreExprToken{m_firstFileline, false};
|
||||||
|
}
|
||||||
|
const V3PreExprToken tok = m_values.back();
|
||||||
|
m_values.pop_back();
|
||||||
|
// UINFO(9, " popValue " << tok.ascii() << endl;
|
||||||
|
return tok;
|
||||||
|
}
|
||||||
|
void reduce() {
|
||||||
|
UASSERT(!m_ops.empty(), "lost op stack beginning END");
|
||||||
|
V3PreExprToken tok = m_ops.back();
|
||||||
|
// UINFO(9, "Reduce " << tok.ascii() << endl);
|
||||||
|
m_ops.pop_back();
|
||||||
|
switch (tok.token()) {
|
||||||
|
case V3PreExprToken::KET: {
|
||||||
|
while (m_ops.back().token() != V3PreExprToken::END
|
||||||
|
&& m_ops.back().token() != V3PreExprToken::BRA)
|
||||||
|
reduce();
|
||||||
|
if (m_ops.back().token() == V3PreExprToken::BRA) {
|
||||||
|
m_ops.pop_back();
|
||||||
|
} else {
|
||||||
|
tok.fileline()->v3error("Syntax error in `ifdef () expression:" // LCOV_EXCL_LINE
|
||||||
|
" ) without matching )");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case V3PreExprToken::LNOT: {
|
||||||
|
const V3PreExprToken lhs = popValue();
|
||||||
|
pushValue(V3PreExprToken{tok.fileline(), !lhs.value()});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case V3PreExprToken::LAND: {
|
||||||
|
const V3PreExprToken rhs = popValue();
|
||||||
|
const V3PreExprToken lhs = popValue();
|
||||||
|
pushValue(V3PreExprToken{tok.fileline(), lhs.value() && rhs.value()});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case V3PreExprToken::LOR: {
|
||||||
|
const V3PreExprToken rhs = popValue();
|
||||||
|
const V3PreExprToken lhs = popValue();
|
||||||
|
pushValue(V3PreExprToken{tok.fileline(), lhs.value() || rhs.value()});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case V3PreExprToken::IMP: {
|
||||||
|
const V3PreExprToken rhs = popValue();
|
||||||
|
const V3PreExprToken lhs = popValue();
|
||||||
|
pushValue(V3PreExprToken{tok.fileline(), !lhs.value() || rhs.value()});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case V3PreExprToken::EQV: {
|
||||||
|
const V3PreExprToken rhs = popValue();
|
||||||
|
const V3PreExprToken lhs = popValue();
|
||||||
|
pushValue(V3PreExprToken{tok.fileline(), lhs.value() == rhs.value()});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
v3fatalSrc("bad case on operand stack");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void parse() {
|
||||||
|
while (!m_inputs.empty()) {
|
||||||
|
V3PreExprToken tok = m_inputs.front();
|
||||||
|
m_inputs.pop_front();
|
||||||
|
UINFO(9, "input read " << tok.ascii() << endl);
|
||||||
|
if (tok.isValue()) {
|
||||||
|
pushValue(tok);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
UASSERT(!m_ops.empty(), "lost op stack beginning END");
|
||||||
|
V3PreExprToken topTok = m_ops.back();
|
||||||
|
auto action = parseTable[topTok.token()][tok.token()];
|
||||||
|
UINFO(9, "pop action " << actionAscii(action) << " from parseTable[" << topTok.ascii()
|
||||||
|
<< "][" << tok.ascii() << "]\n");
|
||||||
|
switch (action) {
|
||||||
|
case RR: // Reduce
|
||||||
|
reduce();
|
||||||
|
break;
|
||||||
|
case SS: // Shift
|
||||||
|
m_ops.push_back(tok);
|
||||||
|
break;
|
||||||
|
case AA: // Accept
|
||||||
|
break;
|
||||||
|
default: tok.fileline()->v3error("Syntax error in `ifdef () expression"); return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
// METHODS
|
||||||
|
V3PreExpr() {}
|
||||||
|
~V3PreExpr() = default;
|
||||||
|
void reset(FileLine* flp) {
|
||||||
|
m_inputs.clear();
|
||||||
|
m_values.clear();
|
||||||
|
m_ops.clear();
|
||||||
|
m_firstFileline = flp;
|
||||||
|
pushOp(V3PreExprToken{flp, V3PreExprToken::END});
|
||||||
|
}
|
||||||
|
void pushInput(const V3PreExprToken& token) {
|
||||||
|
if (!m_firstFileline) m_firstFileline = token.fileline();
|
||||||
|
UINFO(9, "pushInput " << token.ascii() << endl);
|
||||||
|
m_inputs.push_back(token);
|
||||||
|
}
|
||||||
|
bool result() {
|
||||||
|
pushInput(V3PreExprToken{m_firstFileline, V3PreExprToken::END});
|
||||||
|
parse();
|
||||||
|
return popValue().value();
|
||||||
|
}
|
||||||
|
static void selfTest() VL_MT_DISABLED { selfTestImp(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // Guard
|
||||||
|
|
@ -217,6 +217,7 @@ public: // Used only by V3PreLex.cpp and V3PreProc.cpp
|
||||||
void pushStateDefArg(int level);
|
void pushStateDefArg(int level);
|
||||||
void pushStateDefForm();
|
void pushStateDefForm();
|
||||||
void pushStateDefValue();
|
void pushStateDefValue();
|
||||||
|
void pushStateExpr();
|
||||||
void pushStateIncFilename();
|
void pushStateIncFilename();
|
||||||
void scanNewFile(FileLine* filelinep);
|
void scanNewFile(FileLine* filelinep);
|
||||||
void scanBytes(const string& str);
|
void scanBytes(const string& str);
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,7 @@ static void appendDefValue(const char* t, size_t l) { LEXP->appendDefValue(t, l)
|
||||||
%x DEFFPAR
|
%x DEFFPAR
|
||||||
%x DEFVAL
|
%x DEFVAL
|
||||||
%x ENCBASE64
|
%x ENCBASE64
|
||||||
|
%x EXPR
|
||||||
%x INCMODE
|
%x INCMODE
|
||||||
%x PRAGMA
|
%x PRAGMA
|
||||||
%x PRAGMAERR
|
%x PRAGMAERR
|
||||||
|
|
@ -375,6 +376,23 @@ bom [\357\273\277]
|
||||||
<DEFCMT><<EOF>> { FL_FWDC; yyerrorf("EOF in '/* ... */' block comment\n");
|
<DEFCMT><<EOF>> { FL_FWDC; yyerrorf("EOF in '/* ... */' block comment\n");
|
||||||
yyleng=0; return VP_EOF_ERROR; }
|
yyleng=0; return VP_EOF_ERROR; }
|
||||||
|
|
||||||
|
/* Preprocessor expression */
|
||||||
|
<EXPR><<EOF>> { FL_FWDC; linenoInc(); yyerrorf("EOF in unterminated preprocessor expression");
|
||||||
|
yyleng = 0; return VP_EOF_ERROR; }
|
||||||
|
<EXPR>"/*" { yy_push_state(CMTMODE); yymore(); }
|
||||||
|
<EXPR>"//"[^\n\r]* { FL_FWDC; return VP_COMMENT;}
|
||||||
|
<EXPR>"(" { FL_FWDC; return VP_TEXT; } /* V3PreProc will push another EXPR state to stack */
|
||||||
|
<EXPR>")" { FL_FWDC; yy_pop_state(); return VP_TEXT; }
|
||||||
|
<EXPR>"!" { FL_FWDC; return VP_TEXT; }
|
||||||
|
<EXPR>"&&" { FL_FWDC; return VP_TEXT; }
|
||||||
|
<EXPR>"||" { FL_FWDC; return VP_TEXT; }
|
||||||
|
<EXPR>"->" { FL_FWDC; return VP_TEXT; }
|
||||||
|
<EXPR>"<->" { FL_FWDC; return VP_TEXT; }
|
||||||
|
<EXPR>{symb} { FL_FWDC; return VP_SYMBOL; }
|
||||||
|
<EXPR>{crnl} { FL_FWDC; linenoInc(); yytext=(char*)"\n"; yyleng=1; return VP_WHITE; }
|
||||||
|
<EXPR>{wsn}+ { FL_FWDC; return VP_WHITE; }
|
||||||
|
<EXPR>. { FL_FWDC; return VP_TEXT; }
|
||||||
|
|
||||||
/* Define arguments (use of a define) */
|
/* Define arguments (use of a define) */
|
||||||
<ARGMODE>"/*" { yy_push_state(CMTMODE); yymore(); }
|
<ARGMODE>"/*" { yy_push_state(CMTMODE); yymore(); }
|
||||||
<ARGMODE>"//"[^\n\r]* { FL_FWDC; return VP_COMMENT; }
|
<ARGMODE>"//"[^\n\r]* { FL_FWDC; return VP_COMMENT; }
|
||||||
|
|
@ -483,6 +501,11 @@ void V3PreLex::pushStateDefValue() {
|
||||||
m_defValue = "";
|
m_defValue = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void V3PreLex::pushStateExpr() {
|
||||||
|
// Enter preprocessor expression state
|
||||||
|
yy_push_state(EXPR);
|
||||||
|
}
|
||||||
|
|
||||||
void V3PreLex::pushStateIncFilename() {
|
void V3PreLex::pushStateIncFilename() {
|
||||||
// Enter include <> filename state
|
// Enter include <> filename state
|
||||||
yy_push_state(INCMODE);
|
yy_push_state(INCMODE);
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@
|
||||||
#include "V3File.h"
|
#include "V3File.h"
|
||||||
#include "V3Global.h"
|
#include "V3Global.h"
|
||||||
#include "V3LanguageWords.h"
|
#include "V3LanguageWords.h"
|
||||||
|
#include "V3PreExpr.h"
|
||||||
#include "V3PreLex.h"
|
#include "V3PreLex.h"
|
||||||
#include "V3PreShell.h"
|
#include "V3PreShell.h"
|
||||||
#include "V3String.h"
|
#include "V3String.h"
|
||||||
|
|
@ -134,12 +135,15 @@ public:
|
||||||
ps_DEFNAME_IFDEF,
|
ps_DEFNAME_IFDEF,
|
||||||
ps_DEFNAME_IFNDEF,
|
ps_DEFNAME_IFNDEF,
|
||||||
ps_DEFNAME_ELSIF,
|
ps_DEFNAME_ELSIF,
|
||||||
ps_DEFFORM,
|
|
||||||
ps_DEFVALUE,
|
|
||||||
ps_DEFPAREN,
|
|
||||||
ps_DEFARG,
|
ps_DEFARG,
|
||||||
ps_INCNAME,
|
ps_DEFFORM,
|
||||||
|
ps_DEFPAREN,
|
||||||
|
ps_DEFVALUE,
|
||||||
ps_ERRORNAME,
|
ps_ERRORNAME,
|
||||||
|
ps_EXPR_IFDEF,
|
||||||
|
ps_EXPR_IFNDEF,
|
||||||
|
ps_EXPR_ELSIF,
|
||||||
|
ps_INCNAME,
|
||||||
ps_JOIN,
|
ps_JOIN,
|
||||||
ps_STRIFY
|
ps_STRIFY
|
||||||
};
|
};
|
||||||
|
|
@ -147,8 +151,9 @@ public:
|
||||||
static const char* const states[]
|
static const char* const states[]
|
||||||
= {"ps_TOP", "ps_DEFNAME_UNDEF", "ps_DEFNAME_DEFINE",
|
= {"ps_TOP", "ps_DEFNAME_UNDEF", "ps_DEFNAME_DEFINE",
|
||||||
"ps_DEFNAME_IFDEF", "ps_DEFNAME_IFNDEF", "ps_DEFNAME_ELSIF",
|
"ps_DEFNAME_IFDEF", "ps_DEFNAME_IFNDEF", "ps_DEFNAME_ELSIF",
|
||||||
"ps_DEFFORM", "ps_DEFVALUE", "ps_DEFPAREN",
|
"ps_DEFARG", "ps_DEFFORM", "ps_DEFPAREN",
|
||||||
"ps_DEFARG", "ps_INCNAME", "ps_ERRORNAME",
|
"ps_DEFVALUE", "ps_ERRORNAME", "ps_EXPR_IFDEF",
|
||||||
|
"ps_EXPR_IFNDEF", "ps_EXPR_ELSIF", "ps_INCNAME",
|
||||||
"ps_JOIN", "ps_STRIFY"};
|
"ps_JOIN", "ps_STRIFY"};
|
||||||
return states[s];
|
return states[s];
|
||||||
}
|
}
|
||||||
|
|
@ -184,6 +189,10 @@ public:
|
||||||
// For `` join
|
// For `` join
|
||||||
std::stack<string> m_joinStack; ///< Text on lhs of join
|
std::stack<string> m_joinStack; ///< Text on lhs of join
|
||||||
|
|
||||||
|
// for `ifdef () expressions
|
||||||
|
V3PreExpr m_exprParser; ///< Parser for () expression
|
||||||
|
int m_exprParenLevel = 0; ///< Number of ( deep in `ifdef () expression
|
||||||
|
|
||||||
// For getline()
|
// For getline()
|
||||||
string m_lineChars; ///< Characters left for next line
|
string m_lineChars; ///< Characters left for next line
|
||||||
|
|
||||||
|
|
@ -290,6 +299,8 @@ V3PreProc* V3PreProc::createPreProc(FileLine* fl) {
|
||||||
return preprocp;
|
return preprocp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void V3PreProc::selfTest() VL_MT_DISABLED { V3PreExpr::selfTest(); }
|
||||||
|
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
// Defines
|
// Defines
|
||||||
|
|
||||||
|
|
@ -1098,6 +1109,21 @@ int V3PreProcImp::getStateToken() {
|
||||||
goto next_tok;
|
goto next_tok;
|
||||||
} else if (tok == VP_TEXT) {
|
} else if (tok == VP_TEXT) {
|
||||||
// IE, something like comment between define and symbol
|
// IE, something like comment between define and symbol
|
||||||
|
if (yyourleng() == 1 && yyourtext()[0] == '('
|
||||||
|
&& (state() == ps_DEFNAME_IFDEF || state() == ps_DEFNAME_IFNDEF
|
||||||
|
|| state() == ps_DEFNAME_ELSIF)) {
|
||||||
|
UINFO(4, "ifdef() start (\n");
|
||||||
|
m_lexp->pushStateExpr();
|
||||||
|
m_exprParser.reset(fileline());
|
||||||
|
m_exprParenLevel = 1;
|
||||||
|
switch (state()) {
|
||||||
|
case ps_DEFNAME_IFDEF: stateChange(ps_EXPR_IFDEF); break;
|
||||||
|
case ps_DEFNAME_IFNDEF: stateChange(ps_EXPR_IFNDEF); break;
|
||||||
|
case ps_DEFNAME_ELSIF: stateChange(ps_EXPR_ELSIF); break;
|
||||||
|
default: v3fatalSrc("bad case");
|
||||||
|
}
|
||||||
|
goto next_tok;
|
||||||
|
}
|
||||||
if (!m_off) {
|
if (!m_off) {
|
||||||
return tok;
|
return tok;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1111,6 +1137,92 @@ int V3PreProcImp::getStateToken() {
|
||||||
goto next_tok;
|
goto next_tok;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case ps_EXPR_IFDEF: // FALLTHRU
|
||||||
|
case ps_EXPR_IFNDEF: // FALLTHRU
|
||||||
|
case ps_EXPR_ELSIF: {
|
||||||
|
// `ifdef ( *here*
|
||||||
|
FileLine* const flp = m_lexp->m_tokFilelinep;
|
||||||
|
if (tok == VP_SYMBOL) {
|
||||||
|
m_lastSym.assign(yyourtext(), yyourleng());
|
||||||
|
const bool exists = defExists(m_lastSym);
|
||||||
|
if (exists) {
|
||||||
|
string value = defValue(m_lastSym);
|
||||||
|
if (VString::removeWhitespace(value) == "0") {
|
||||||
|
flp->v3warn(
|
||||||
|
PREPROCZERO,
|
||||||
|
"Preprocessor expression evaluates define with 0: '"
|
||||||
|
<< m_lastSym << "' with value '" << value
|
||||||
|
<< "'\n"
|
||||||
|
"... Suggest change define '"
|
||||||
|
<< m_lastSym
|
||||||
|
<< "' to non-zero value if used in preprocessor expression");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_exprParser.pushInput(V3PreExprToken{flp, exists});
|
||||||
|
goto next_tok;
|
||||||
|
} else if (tok == VP_WHITE) {
|
||||||
|
goto next_tok;
|
||||||
|
} else if (tok == VP_TEXT && yyourleng() == 1 && yyourtext()[0] == '(') {
|
||||||
|
m_exprParser.pushInput(V3PreExprToken{flp, V3PreExprToken::BRA});
|
||||||
|
goto next_tok;
|
||||||
|
} else if (tok == VP_TEXT && yyourleng() == 1 && yyourtext()[0] == ')') {
|
||||||
|
UASSERT(m_exprParenLevel, "Underflow of ); should have exited ps_EXPR earlier?");
|
||||||
|
if (--m_exprParenLevel > 0) {
|
||||||
|
m_exprParser.pushInput(V3PreExprToken{flp, V3PreExprToken::KET});
|
||||||
|
goto next_tok;
|
||||||
|
} else {
|
||||||
|
// Done with parsing expression
|
||||||
|
bool enable = m_exprParser.result();
|
||||||
|
UINFO(4, "ifdef() result=" << enable << endl);
|
||||||
|
if (state() == ps_EXPR_IFDEF || state() == ps_EXPR_IFNDEF) {
|
||||||
|
if (state() == ps_EXPR_IFNDEF) enable = !enable;
|
||||||
|
m_ifdefStack.push(VPreIfEntry{enable, false});
|
||||||
|
if (!enable) parsingOff();
|
||||||
|
statePop();
|
||||||
|
goto next_tok;
|
||||||
|
} else if (state() == ps_EXPR_ELSIF) {
|
||||||
|
if (m_ifdefStack.empty()) {
|
||||||
|
error("`elsif with no matching `if\n");
|
||||||
|
} else {
|
||||||
|
// Handle `else portion
|
||||||
|
const VPreIfEntry lastIf = m_ifdefStack.top();
|
||||||
|
m_ifdefStack.pop();
|
||||||
|
if (!lastIf.on()) parsingOn();
|
||||||
|
// Handle `if portion
|
||||||
|
enable = !lastIf.everOn() && enable;
|
||||||
|
UINFO(4, "Elsif " << m_lastSym << (enable ? " ON" : " OFF") << endl);
|
||||||
|
m_ifdefStack.push(VPreIfEntry{enable, lastIf.everOn()});
|
||||||
|
if (!enable) parsingOff();
|
||||||
|
}
|
||||||
|
statePop();
|
||||||
|
}
|
||||||
|
goto next_tok;
|
||||||
|
}
|
||||||
|
} else if (tok == VP_TEXT && yyourleng() == 1 && yyourtext()[0] == '!') {
|
||||||
|
m_exprParser.pushInput(V3PreExprToken{flp, V3PreExprToken::LNOT});
|
||||||
|
goto next_tok;
|
||||||
|
} else if (tok == VP_TEXT && yyourleng() == 2 && 0 == strncmp(yyourtext(), "&&", 2)) {
|
||||||
|
m_exprParser.pushInput(V3PreExprToken{flp, V3PreExprToken::LAND});
|
||||||
|
goto next_tok;
|
||||||
|
} else if (tok == VP_TEXT && yyourleng() == 2 && 0 == strncmp(yyourtext(), "||", 2)) {
|
||||||
|
m_exprParser.pushInput(V3PreExprToken{flp, V3PreExprToken::LOR});
|
||||||
|
goto next_tok;
|
||||||
|
} else if (tok == VP_TEXT && yyourleng() == 2 && 0 == strncmp(yyourtext(), "->", 2)) {
|
||||||
|
m_exprParser.pushInput(V3PreExprToken{flp, V3PreExprToken::IMP});
|
||||||
|
goto next_tok;
|
||||||
|
} else if (tok == VP_TEXT && yyourleng() == 3 && 0 == strncmp(yyourtext(), "<->", 3)) {
|
||||||
|
m_exprParser.pushInput(V3PreExprToken{flp, V3PreExprToken::EQV});
|
||||||
|
goto next_tok;
|
||||||
|
} else {
|
||||||
|
if (VString::removeWhitespace(string{yyourtext(), yyourleng()}).empty()) {
|
||||||
|
return tok;
|
||||||
|
} else {
|
||||||
|
error(std::string{"Syntax error in `ifdef () expression; unexpected: '"}
|
||||||
|
+ tokenName(tok) + "'\n");
|
||||||
|
}
|
||||||
|
goto next_tok;
|
||||||
|
}
|
||||||
|
}
|
||||||
case ps_DEFFORM: {
|
case ps_DEFFORM: {
|
||||||
if (tok == VP_DEFFORM) {
|
if (tok == VP_DEFFORM) {
|
||||||
m_formals = m_lexp->m_defValue;
|
m_formals = m_lexp->m_defValue;
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,7 @@ protected:
|
||||||
public:
|
public:
|
||||||
static V3PreProc* createPreProc(FileLine* fl) VL_MT_DISABLED;
|
static V3PreProc* createPreProc(FileLine* fl) VL_MT_DISABLED;
|
||||||
virtual ~V3PreProc() = default; // LCOV_EXCL_LINE // Persistent
|
virtual ~V3PreProc() = default; // LCOV_EXCL_LINE // Persistent
|
||||||
|
static void selfTest() VL_MT_DISABLED;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // Guard
|
#endif // Guard
|
||||||
|
|
|
||||||
|
|
@ -176,3 +176,4 @@ void V3PreShell::dumpDefines(std::ostream& os) { V3PreShellImp::s_preprocp->dump
|
||||||
void V3PreShell::candidateDefines(VSpellCheck* spellerp) {
|
void V3PreShell::candidateDefines(VSpellCheck* spellerp) {
|
||||||
V3PreShellImp::s_preprocp->candidateDefines(spellerp);
|
V3PreShellImp::s_preprocp->candidateDefines(spellerp);
|
||||||
}
|
}
|
||||||
|
void V3PreShell::selfTest() { V3PreProc::selfTest(); }
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ public:
|
||||||
static void undef(const string& name) VL_MT_DISABLED;
|
static void undef(const string& name) VL_MT_DISABLED;
|
||||||
static void dumpDefines(std::ostream& os) VL_MT_DISABLED;
|
static void dumpDefines(std::ostream& os) VL_MT_DISABLED;
|
||||||
static void candidateDefines(VSpellCheck* spellerp) VL_MT_DISABLED;
|
static void candidateDefines(VSpellCheck* spellerp) VL_MT_DISABLED;
|
||||||
|
static void selfTest() VL_MT_DISABLED;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // Guard
|
#endif // Guard
|
||||||
|
|
|
||||||
|
|
@ -678,6 +678,7 @@ static void verilate(const string& argString) {
|
||||||
V3ScoreboardBase::selfTest();
|
V3ScoreboardBase::selfTest();
|
||||||
V3Partition::selfTest();
|
V3Partition::selfTest();
|
||||||
V3Partition::selfTestNormalizeCosts();
|
V3Partition::selfTestNormalizeCosts();
|
||||||
|
V3PreShell::selfTest();
|
||||||
V3Broken::selfTest();
|
V3Broken::selfTest();
|
||||||
}
|
}
|
||||||
V3ThreadPool::selfTest();
|
V3ThreadPool::selfTest();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
`begin_keywords "1800-2023"
|
||||||
|
"ok ( ONE )"
|
||||||
|
"ok ( ! ONE )"
|
||||||
|
"ok ( ! ZERO )"
|
||||||
|
"ok ( ZERO || ZERO || ONE )"
|
||||||
|
"ok ( ONE && ONE && ONE )"
|
||||||
|
"ok ( ZERO && ZERO || ONE )"
|
||||||
|
"ok ( ZERO -> ZERO)"
|
||||||
|
"ok ( ZERO -> ONE)"
|
||||||
|
"ok ( ZERO -> ONE)"
|
||||||
|
"ok ( ZERO -> ONE)"
|
||||||
|
"ok ( ONE -> ZERO)"
|
||||||
|
"ok ( ONE -> ONE)"
|
||||||
|
"ok ( ZERO <-> ZERO)"
|
||||||
|
"ok ( ZERO <-> ONE)"
|
||||||
|
"ok ( ONE <-> ZERO)"
|
||||||
|
"ok ( ONE <-> ONE)"
|
||||||
|
"ok "
|
||||||
|
Line: 117
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
#!/usr/bin/env perl
|
||||||
|
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2003-2009 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(vlt => 1);
|
||||||
|
|
||||||
|
my $stdout_filename = "$Self->{obj_dir}/$Self->{name}__test.vpp";
|
||||||
|
|
||||||
|
compile(
|
||||||
|
verilator_flags2 => ['-E -P'],
|
||||||
|
verilator_make_gmake => 0,
|
||||||
|
make_top_shell => 0,
|
||||||
|
make_main => 0,
|
||||||
|
stdout_filename => $stdout_filename,
|
||||||
|
);
|
||||||
|
|
||||||
|
files_identical($stdout_filename, $Self->{golden_filename});
|
||||||
|
|
||||||
|
ok(1);
|
||||||
|
1;
|
||||||
|
|
@ -0,0 +1,117 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||||
|
// any use, without warranty, 2024 by Wilson Snyder.
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
`begin_keywords "1800-2023"
|
||||||
|
|
||||||
|
`define ONE
|
||||||
|
`undef ZERO
|
||||||
|
|
||||||
|
`ifdef ( ONE )
|
||||||
|
"ok ( ONE )"
|
||||||
|
`endif
|
||||||
|
// Test no spaces around ()
|
||||||
|
`ifdef (ZERO)
|
||||||
|
`error "( ZERO )"
|
||||||
|
`endif
|
||||||
|
|
||||||
|
`ifndef ( ! ONE )
|
||||||
|
"ok ( ! ONE )"
|
||||||
|
`endif
|
||||||
|
// Test no spaces around ()
|
||||||
|
`ifndef (!ZERO)
|
||||||
|
`error "( ! ZERO )"
|
||||||
|
`endif
|
||||||
|
|
||||||
|
`ifdef ( ! ZERO )
|
||||||
|
"ok ( ! ZERO )"
|
||||||
|
`endif
|
||||||
|
`ifdef ( ! ONE )
|
||||||
|
`error "( ! ONE )"
|
||||||
|
`endif
|
||||||
|
|
||||||
|
`ifdef ( ZERO || ZERO || ONE )
|
||||||
|
"ok ( ZERO || ZERO || ONE )"
|
||||||
|
`endif
|
||||||
|
`ifdef ( ZERO || ZERO || ZERO )
|
||||||
|
`error "( ZERO || ZERO || ZERO )"
|
||||||
|
`endif
|
||||||
|
|
||||||
|
`ifdef ( ONE && ONE && ONE )
|
||||||
|
"ok ( ONE && ONE && ONE )"
|
||||||
|
`endif
|
||||||
|
`ifdef ( ONE && ONE && ZERO )
|
||||||
|
`error "( ONE && ONE && ZERO )"
|
||||||
|
`endif
|
||||||
|
|
||||||
|
// Precedence of && is under ||
|
||||||
|
|
||||||
|
`ifdef ( ZERO && ZERO || ONE )
|
||||||
|
"ok ( ZERO && ZERO || ONE )"
|
||||||
|
`endif
|
||||||
|
`ifdef ( ONE || ZERO && ZERO )
|
||||||
|
"ok ( ONE || ZERO && ZERO )"
|
||||||
|
`endif
|
||||||
|
|
||||||
|
`ifdef ZERO
|
||||||
|
`elsif ( ONE && !( ZERO && ONE ) )
|
||||||
|
"ok ( ONE && !( ZERO && ONE ) )"
|
||||||
|
`endif
|
||||||
|
|
||||||
|
`ifdef ( ZERO -> ZERO)
|
||||||
|
"ok ( ZERO -> ZERO)"
|
||||||
|
`endif
|
||||||
|
|
||||||
|
// Text extra newlines
|
||||||
|
`ifdef ( ZERO
|
||||||
|
->
|
||||||
|
ONE)
|
||||||
|
"ok ( ZERO -> ONE)"
|
||||||
|
`endif
|
||||||
|
|
||||||
|
// Text comments
|
||||||
|
`ifdef ( ZERO // Zero
|
||||||
|
-> // Operator
|
||||||
|
ONE) // One
|
||||||
|
"ok ( ZERO -> ONE)"
|
||||||
|
`endif
|
||||||
|
`ifdef ( /*val*/ ZERO
|
||||||
|
/*op*/ ->
|
||||||
|
/*val*/ ONE)
|
||||||
|
"ok ( ZERO -> ONE)"
|
||||||
|
`endif
|
||||||
|
|
||||||
|
`ifndef ( ONE -> ZERO)
|
||||||
|
"ok ( ONE -> ZERO)"
|
||||||
|
`endif
|
||||||
|
`ifdef ( ONE -> ONE)
|
||||||
|
"ok ( ONE -> ONE)"
|
||||||
|
`endif
|
||||||
|
|
||||||
|
`ifdef ( ZERO <-> ZERO)
|
||||||
|
"ok ( ZERO <-> ZERO)"
|
||||||
|
`endif
|
||||||
|
`ifndef ( ZERO <-> ONE)
|
||||||
|
"ok ( ZERO <-> ONE)"
|
||||||
|
`endif
|
||||||
|
`ifndef ( ONE <-> ZERO)
|
||||||
|
"ok ( ONE <-> ZERO)"
|
||||||
|
`endif
|
||||||
|
`ifdef ( ONE <-> ONE)
|
||||||
|
"ok ( ONE <-> ONE)"
|
||||||
|
`endif
|
||||||
|
|
||||||
|
`ifdef (ZERO)
|
||||||
|
"bad"
|
||||||
|
`elsif (ZERO)
|
||||||
|
"bad"
|
||||||
|
`elsif (ONE)
|
||||||
|
"ok "
|
||||||
|
`elsif (ONE)
|
||||||
|
"bad"
|
||||||
|
`endif
|
||||||
|
|
||||||
|
// Did we end up right?
|
||||||
|
Line: `__LINE__
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
%Error: t/t_preproc_ifexpr_bad.v:12:14: `elsif with no matching `if
|
||||||
|
12 | `elsif ( ONE ) // BAD: elsif without if
|
||||||
|
| ^
|
||||||
|
%Error: t/t_preproc_ifexpr_bad.v:13:1: `endif with no matching `if
|
||||||
|
13 | `endif
|
||||||
|
| ^~~~~~
|
||||||
|
%Error: t/t_preproc_ifexpr_bad.v:15:10: Syntax error in `ifdef () expression
|
||||||
|
15 | `ifdef ( ) // BAD: Missing value
|
||||||
|
| ^
|
||||||
|
%Error: t/t_preproc_ifexpr_bad.v:18:17: Syntax error in `ifdef () expression
|
||||||
|
18 | `ifdef ( && ZERO) // BAD: Expr
|
||||||
|
| ^
|
||||||
|
%Error: t/t_preproc_ifexpr_bad.v:21:18: Syntax error in `ifdef () expression
|
||||||
|
21 | `ifdef ( ZERO && ) // BAD: Expr
|
||||||
|
| ^
|
||||||
|
%Error: t/t_preproc_ifexpr_bad.v:24:10: Syntax error in `ifdef () expression; unexpected: 'TEXT'
|
||||||
|
24 | `ifdef ( 1 ) // BAD: Constant
|
||||||
|
| ^
|
||||||
|
%Error: t/t_preproc_ifexpr_bad.v:24:12: Syntax error in `ifdef () expression
|
||||||
|
24 | `ifdef ( 1 ) // BAD: Constant
|
||||||
|
| ^
|
||||||
|
%Error: t/t_preproc_ifexpr_bad.v:27:14: Syntax error in `ifdef () expression; unexpected: 'TEXT'
|
||||||
|
27 | `ifdef ( ONE & ZERO) // BAD: Operator
|
||||||
|
| ^
|
||||||
|
%Error: t/t_preproc_ifexpr_bad.v:30:10: Syntax error in `ifdef () expression; unexpected: 'TEXT'
|
||||||
|
30 | `ifdef ( % ) // BAD: % is syntax error
|
||||||
|
| ^
|
||||||
|
%Error: t/t_preproc_ifexpr_bad.v:30:12: Syntax error in `ifdef () expression
|
||||||
|
30 | `ifdef ( % ) // BAD: % is syntax error
|
||||||
|
| ^
|
||||||
|
%Error: t/t_preproc_ifexpr_bad.v:34:1: Expecting define name. Found: ENDIF
|
||||||
|
34 | `endif
|
||||||
|
| ^~~~~~
|
||||||
|
%Error: t/t_preproc_ifexpr_bad.v:36:1: Expecting define name. Found: IFDEF
|
||||||
|
36 | `ifdef ( ONE // BAD: Missing paren
|
||||||
|
| ^~~~~~
|
||||||
|
%Error: t/t_preproc_ifexpr_bad.v:37:1: Syntax error in `ifdef () expression; unexpected: 'TEXT'
|
||||||
|
37 | `endif
|
||||||
|
| ^
|
||||||
|
%Error: t/t_preproc_ifexpr_bad.v:40:1: EOF in unterminated preprocessor expression
|
||||||
|
%Error: t/t_preproc_ifexpr_bad.v:33:2: syntax error, unexpected ')'
|
||||||
|
33 | )
|
||||||
|
| ^
|
||||||
|
%Error: Exiting due to
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
#!/usr/bin/env 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;
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||||
|
// any use, without warranty, 2024 by Wilson Snyder.
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
`begin_keywords "1800-2023"
|
||||||
|
|
||||||
|
`define ONE
|
||||||
|
`undef ZERO
|
||||||
|
|
||||||
|
`elsif ( ONE ) // BAD: elsif without if
|
||||||
|
`endif
|
||||||
|
|
||||||
|
`ifdef ( ) // BAD: Missing value
|
||||||
|
`endif
|
||||||
|
|
||||||
|
`ifdef ( && ZERO) // BAD: Expr
|
||||||
|
`endif
|
||||||
|
|
||||||
|
`ifdef ( ZERO && ) // BAD: Expr
|
||||||
|
`endif
|
||||||
|
|
||||||
|
`ifdef ( 1 ) // BAD: Constant
|
||||||
|
`endif
|
||||||
|
|
||||||
|
`ifdef ( ONE & ZERO) // BAD: Operator
|
||||||
|
`endif
|
||||||
|
|
||||||
|
`ifdef ( % ) // BAD: % is syntax error
|
||||||
|
`endif
|
||||||
|
|
||||||
|
`ifdef ) // BAD: ) without (
|
||||||
|
`endif
|
||||||
|
|
||||||
|
`ifdef ( ONE // BAD: Missing paren
|
||||||
|
`endif
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
%Warning-PREPROCZERO: t/t_preproc_preproczero_bad.v:11:10: Preprocessor expression evaluates define with 0: 'ZERO' with value '0'
|
||||||
|
... Suggest change define 'ZERO' to non-zero value if used in preprocessor expression
|
||||||
|
11 | `ifdef ( ZERO )
|
||||||
|
| ^~~~
|
||||||
|
... For warning description see https://verilator.org/warn/PREPROCZERO?v=latest
|
||||||
|
... Use "/* verilator lint_off PREPROCZERO */" and lint_on around source to disable this message.
|
||||||
|
%Error: Exiting due to
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
#!/usr/bin/env perl
|
||||||
|
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2003-2009 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(vlt => 1);
|
||||||
|
|
||||||
|
my $stdout_filename = "$Self->{obj_dir}/$Self->{name}__test.vpp";
|
||||||
|
|
||||||
|
compile(
|
||||||
|
verilator_flags2 => ['-E -P'],
|
||||||
|
verilator_make_gmake => 0,
|
||||||
|
make_top_shell => 0,
|
||||||
|
make_main => 0,
|
||||||
|
stdout_filename => $stdout_filename,
|
||||||
|
fails => 1,
|
||||||
|
expect_filename => $Self->{golden_filename},
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(1);
|
||||||
|
1;
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||||
|
// any use, without warranty, 2024 by Wilson Snyder.
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
`begin_keywords "1800-2023"
|
||||||
|
|
||||||
|
`define ZERO 0
|
||||||
|
|
||||||
|
`ifdef ( ZERO )
|
||||||
|
// ...
|
||||||
|
`endif
|
||||||
Loading…
Reference in New Issue