mirror of
https://github.com/verilator/verilator.git
synced 2025-01-01 04:07:34 +00:00
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:**
|
||||
|
||||
* Support 1800-2023 keywords.
|
||||
* Support 1800-2023 preprocessor ifdef expressions.
|
||||
* Support 1800-2023 DPI headers, svGetTime/svgGetTimeUnit/svGetTimePrecision methods.
|
||||
|
||||
**Minor:**
|
||||
|
@ -68,6 +68,9 @@ SystemVerilog 2023 (IEEE 1800-2023) Support
|
||||
Verilator supports some of the 2023 improvements, including triple-quoted
|
||||
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
|
||||
-------------------
|
||||
|
@ -1332,6 +1332,31 @@ List Of Warnings
|
||||
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
|
||||
|
||||
.. TODO better example
|
||||
|
@ -132,6 +132,7 @@ set(HEADERS
|
||||
V3PartitionGraph.h
|
||||
V3PchAstMT.h
|
||||
V3PchAstNoMT.h
|
||||
V3PreExpr.h
|
||||
V3PreLex.h
|
||||
V3PreProc.h
|
||||
V3PreShell.h
|
||||
|
@ -127,6 +127,7 @@ public:
|
||||
PINNOCONNECT, // Cell pin not connected
|
||||
PINNOTFOUND, // instance port name not found in it's module
|
||||
PKGNODECL, // Error: Package/class needs to be predeclared
|
||||
PREPROCZERO, // Preprocessor expression with zero
|
||||
PROCASSWIRE, // Procedural assignment on wire
|
||||
PROFOUTOFDATE, // Profile data out of date
|
||||
PROTECTED, // detected `pragma protected
|
||||
@ -203,7 +204,7 @@ public:
|
||||
"INCABSPATH", "INFINITELOOP", "INITIALDLY", "INSECURE",
|
||||
"LATCH", "LITENDIAN", "MINTYPMAXDLY", "MISINDENT", "MODDUP",
|
||||
"MULTIDRIVEN", "MULTITOP", "NEWERSTD", "NOLATCH", "NULLPORT", "PINCONNECTEMPTY",
|
||||
"PINMISSING", "PINNOCONNECT", "PINNOTFOUND", "PKGNODECL", "PROCASSWIRE",
|
||||
"PINMISSING", "PINNOCONNECT", "PINNOTFOUND", "PKGNODECL", "PREPROCZERO", "PROCASSWIRE",
|
||||
"PROFOUTOFDATE", "PROTECTED", "RANDC", "REALCVT", "REDEFMACRO", "RISEFALLDLY",
|
||||
"SELRANGE", "SHORTREAL", "SIDEEFFECT", "SPLITVAR",
|
||||
"STATICVAR", "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET",
|
||||
@ -242,9 +243,10 @@ public:
|
||||
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 == CMPCONST || m_e == COLONPLUS || m_e == IMPLICIT || m_e == IMPLICITSTATIC
|
||||
|| m_e == LATCH || m_e == MISINDENT || m_e == NEWERSTD || m_e == PINMISSING
|
||||
|| m_e == REALCVT || m_e == STATICVAR || m_e == UNSIGNED || m_e == WIDTH
|
||||
|| m_e == WIDTHTRUNC || m_e == WIDTHEXPAND || m_e == WIDTHXZEXPAND);
|
||||
|| m_e == LATCH || m_e == MISINDENT || m_e == NEWERSTD || m_e == PREPROCZERO
|
||||
|| m_e == PINMISSING || m_e == REALCVT || m_e == STATICVAR || m_e == UNSIGNED
|
||||
|| m_e == WIDTH || m_e == WIDTHTRUNC || m_e == WIDTHEXPAND
|
||||
|| m_e == WIDTHXZEXPAND);
|
||||
}
|
||||
// Warnings that are style only
|
||||
bool styleError() const VL_MT_SAFE {
|
||||
|
293
src/V3PreExpr.h
Normal file
293
src/V3PreExpr.h
Normal file
@ -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 pushStateDefForm();
|
||||
void pushStateDefValue();
|
||||
void pushStateExpr();
|
||||
void pushStateIncFilename();
|
||||
void scanNewFile(FileLine* filelinep);
|
||||
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 DEFVAL
|
||||
%x ENCBASE64
|
||||
%x EXPR
|
||||
%x INCMODE
|
||||
%x PRAGMA
|
||||
%x PRAGMAERR
|
||||
@ -375,6 +376,23 @@ bom [\357\273\277]
|
||||
<DEFCMT><<EOF>> { FL_FWDC; yyerrorf("EOF in '/* ... */' block comment\n");
|
||||
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) */
|
||||
<ARGMODE>"/*" { yy_push_state(CMTMODE); yymore(); }
|
||||
<ARGMODE>"//"[^\n\r]* { FL_FWDC; return VP_COMMENT; }
|
||||
@ -483,6 +501,11 @@ void V3PreLex::pushStateDefValue() {
|
||||
m_defValue = "";
|
||||
}
|
||||
|
||||
void V3PreLex::pushStateExpr() {
|
||||
// Enter preprocessor expression state
|
||||
yy_push_state(EXPR);
|
||||
}
|
||||
|
||||
void V3PreLex::pushStateIncFilename() {
|
||||
// Enter include <> filename state
|
||||
yy_push_state(INCMODE);
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "V3File.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3LanguageWords.h"
|
||||
#include "V3PreExpr.h"
|
||||
#include "V3PreLex.h"
|
||||
#include "V3PreShell.h"
|
||||
#include "V3String.h"
|
||||
@ -134,12 +135,15 @@ public:
|
||||
ps_DEFNAME_IFDEF,
|
||||
ps_DEFNAME_IFNDEF,
|
||||
ps_DEFNAME_ELSIF,
|
||||
ps_DEFFORM,
|
||||
ps_DEFVALUE,
|
||||
ps_DEFPAREN,
|
||||
ps_DEFARG,
|
||||
ps_INCNAME,
|
||||
ps_DEFFORM,
|
||||
ps_DEFPAREN,
|
||||
ps_DEFVALUE,
|
||||
ps_ERRORNAME,
|
||||
ps_EXPR_IFDEF,
|
||||
ps_EXPR_IFNDEF,
|
||||
ps_EXPR_ELSIF,
|
||||
ps_INCNAME,
|
||||
ps_JOIN,
|
||||
ps_STRIFY
|
||||
};
|
||||
@ -147,8 +151,9 @@ public:
|
||||
static const char* const states[]
|
||||
= {"ps_TOP", "ps_DEFNAME_UNDEF", "ps_DEFNAME_DEFINE",
|
||||
"ps_DEFNAME_IFDEF", "ps_DEFNAME_IFNDEF", "ps_DEFNAME_ELSIF",
|
||||
"ps_DEFFORM", "ps_DEFVALUE", "ps_DEFPAREN",
|
||||
"ps_DEFARG", "ps_INCNAME", "ps_ERRORNAME",
|
||||
"ps_DEFARG", "ps_DEFFORM", "ps_DEFPAREN",
|
||||
"ps_DEFVALUE", "ps_ERRORNAME", "ps_EXPR_IFDEF",
|
||||
"ps_EXPR_IFNDEF", "ps_EXPR_ELSIF", "ps_INCNAME",
|
||||
"ps_JOIN", "ps_STRIFY"};
|
||||
return states[s];
|
||||
}
|
||||
@ -184,6 +189,10 @@ public:
|
||||
// For `` 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()
|
||||
string m_lineChars; ///< Characters left for next line
|
||||
|
||||
@ -290,6 +299,8 @@ V3PreProc* V3PreProc::createPreProc(FileLine* fl) {
|
||||
return preprocp;
|
||||
}
|
||||
|
||||
void V3PreProc::selfTest() VL_MT_DISABLED { V3PreExpr::selfTest(); }
|
||||
|
||||
//*************************************************************************
|
||||
// Defines
|
||||
|
||||
@ -1098,6 +1109,21 @@ int V3PreProcImp::getStateToken() {
|
||||
goto next_tok;
|
||||
} else if (tok == VP_TEXT) {
|
||||
// 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) {
|
||||
return tok;
|
||||
} else {
|
||||
@ -1111,6 +1137,92 @@ int V3PreProcImp::getStateToken() {
|
||||
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: {
|
||||
if (tok == VP_DEFFORM) {
|
||||
m_formals = m_lexp->m_defValue;
|
||||
|
@ -99,6 +99,7 @@ protected:
|
||||
public:
|
||||
static V3PreProc* createPreProc(FileLine* fl) VL_MT_DISABLED;
|
||||
virtual ~V3PreProc() = default; // LCOV_EXCL_LINE // Persistent
|
||||
static void selfTest() VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
@ -176,3 +176,4 @@ void V3PreShell::dumpDefines(std::ostream& os) { V3PreShellImp::s_preprocp->dump
|
||||
void V3PreShell::candidateDefines(VSpellCheck* 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 dumpDefines(std::ostream& os) VL_MT_DISABLED;
|
||||
static void candidateDefines(VSpellCheck* spellerp) VL_MT_DISABLED;
|
||||
static void selfTest() VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
@ -678,6 +678,7 @@ static void verilate(const string& argString) {
|
||||
V3ScoreboardBase::selfTest();
|
||||
V3Partition::selfTest();
|
||||
V3Partition::selfTestNormalizeCosts();
|
||||
V3PreShell::selfTest();
|
||||
V3Broken::selfTest();
|
||||
}
|
||||
V3ThreadPool::selfTest();
|
||||
|
19
test_regress/t/t_preproc_ifexpr.out
Normal file
19
test_regress/t/t_preproc_ifexpr.out
Normal file
@ -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
|
26
test_regress/t/t_preproc_ifexpr.pl
Executable file
26
test_regress/t/t_preproc_ifexpr.pl
Executable file
@ -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;
|
117
test_regress/t/t_preproc_ifexpr.v
Normal file
117
test_regress/t/t_preproc_ifexpr.v
Normal file
@ -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__
|
44
test_regress/t/t_preproc_ifexpr_bad.out
Normal file
44
test_regress/t/t_preproc_ifexpr_bad.out
Normal file
@ -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
|
19
test_regress/t/t_preproc_ifexpr_bad.pl
Executable file
19
test_regress/t/t_preproc_ifexpr_bad.pl
Executable file
@ -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;
|
37
test_regress/t/t_preproc_ifexpr_bad.v
Normal file
37
test_regress/t/t_preproc_ifexpr_bad.v
Normal file
@ -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
|
7
test_regress/t/t_preproc_preproczero_bad.out
Normal file
7
test_regress/t/t_preproc_preproczero_bad.out
Normal file
@ -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
|
26
test_regress/t/t_preproc_preproczero_bad.pl
Executable file
26
test_regress/t/t_preproc_preproczero_bad.pl
Executable file
@ -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;
|
13
test_regress/t/t_preproc_preproczero_bad.v
Normal file
13
test_regress/t/t_preproc_preproczero_bad.v
Normal file
@ -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
Block a user