mirror of
https://github.com/verilator/verilator.git
synced 2025-01-01 04:07:34 +00:00
Removed deprecated --cdc option.
This commit is contained in:
parent
f8b0e359b9
commit
026bbc306b
1
Changes
1
Changes
@ -27,6 +27,7 @@ Verilator 5.007 devel
|
||||
* Support class extending its parameter (#3904). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Support static function variables (#3830). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Support vpiDefName (#3906) (#3931). [Andrew Nolte]
|
||||
* Removed deprecated --cdc option.
|
||||
* Fix real parameters of infinity and NaN.
|
||||
* Fix pattern assignment to unpacked structs (#3510). [Mostafa Garnal]
|
||||
* Fix single-element replication to dynarray/unpacked/queue (#3548). [Gustav Svensk]
|
||||
|
@ -22,10 +22,6 @@ Verilated_heavy.h
|
||||
"verilated.h". Verilated_heavy.h is planned for removal no sooner than
|
||||
July 2022.
|
||||
|
||||
Option `--cdc`
|
||||
The experimental `--cdc` option is believed to be generally unused and is
|
||||
planned for removal no sooner than January 2023.
|
||||
|
||||
Option `-O<letter>`
|
||||
The debug `-O<letter>` options have been replaced with
|
||||
`-fno-<optimization>` debug options to match GCC. The old options are
|
||||
|
@ -155,18 +155,6 @@ Summary:
|
||||
Specify C++ without SystemC output mode; see also the :vlopt:`--sc`
|
||||
option.
|
||||
|
||||
.. option:: --cdc
|
||||
|
||||
Permanently experimental. Perform some clock domain crossing checks and
|
||||
issue related warnings (CDCRSTLOGIC) and then exit; if warnings other
|
||||
than CDC warnings are needed, make a second run with
|
||||
:vlopt:`--lint-only`. Additional warning information is also written to
|
||||
the file :file:`<prefix>__cdc.txt`.
|
||||
|
||||
Currently only checks some items that other CDC tools missed; if you are
|
||||
interested in adding more traditional CDC checks, please contact the
|
||||
authors.
|
||||
|
||||
.. option:: -CFLAGS <flags>
|
||||
|
||||
Add specified C compiler argument to the generated makefiles. For
|
||||
|
@ -318,9 +318,11 @@ List Of Warnings
|
||||
|
||||
.. option:: CDCRSTLOGIC
|
||||
|
||||
With :vlopt:`--cdc` only, it warns that asynchronous flop reset terms come
|
||||
from other than primary inputs or flopped outputs, creating the
|
||||
potential for reset glitches.
|
||||
Historical, never issued since version 5.008.
|
||||
|
||||
Warned with a no longer supported clock domain crossing option that
|
||||
asynchronous flop reset terms came from other than primary inputs or
|
||||
flopped outputs, creating the potential for reset glitches.
|
||||
|
||||
|
||||
.. option:: CLKDATA
|
||||
|
@ -47,7 +47,6 @@ set(HEADERS
|
||||
V3Case.h
|
||||
V3Cast.h
|
||||
V3CCtors.h
|
||||
V3Cdc.h
|
||||
V3Class.h
|
||||
V3Clean.h
|
||||
V3Clock.h
|
||||
@ -187,7 +186,6 @@ set(COMMON_SOURCES
|
||||
V3CUse.cpp
|
||||
V3Case.cpp
|
||||
V3Cast.cpp
|
||||
V3Cdc.cpp
|
||||
V3Class.cpp
|
||||
V3Clean.cpp
|
||||
V3Clock.cpp
|
||||
|
@ -164,7 +164,6 @@ RAW_OBJS = \
|
||||
V3CUse.o \
|
||||
V3Case.o \
|
||||
V3Cast.o \
|
||||
V3Cdc.o \
|
||||
V3Class.o \
|
||||
V3Clean.o \
|
||||
V3Clock.o \
|
||||
|
765
src/V3Cdc.cpp
765
src/V3Cdc.cpp
@ -1,765 +0,0 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Clock Domain Crossing Lint
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-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 OR Artistic-2.0
|
||||
//
|
||||
//*************************************************************************
|
||||
// V3Cdc's Transformations:
|
||||
//
|
||||
// Create V3Graph-ish graph
|
||||
// Find all negedge reset flops
|
||||
// Trace back to previous flop
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Cdc.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Const.h"
|
||||
#include "V3EmitV.h"
|
||||
#include "V3File.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3Graph.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <deque>
|
||||
#include <iomanip>
|
||||
#include <memory>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
constexpr int CDC_WEIGHT_ASYNC = 0x1000; // Weight for edges that feed async logic
|
||||
|
||||
//######################################################################
|
||||
|
||||
class CdcBaseVisitor VL_NOT_FINAL : public VNVisitor {
|
||||
public:
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Graph support classes
|
||||
|
||||
class CdcEitherVertex VL_NOT_FINAL : public V3GraphVertex {
|
||||
AstScope* const m_scopep;
|
||||
AstNode* const m_nodep;
|
||||
AstSenTree* m_srcDomainp = nullptr;
|
||||
AstSenTree* m_dstDomainp = nullptr;
|
||||
bool m_srcDomainSet : 1;
|
||||
bool m_dstDomainSet : 1;
|
||||
bool m_asyncPath : 1;
|
||||
|
||||
public:
|
||||
CdcEitherVertex(V3Graph* graphp, AstScope* scopep, AstNode* nodep)
|
||||
: V3GraphVertex{graphp}
|
||||
, m_scopep{scopep}
|
||||
, m_nodep{nodep}
|
||||
, m_srcDomainSet{false}
|
||||
, m_dstDomainSet{false}
|
||||
, m_asyncPath{false} {}
|
||||
~CdcEitherVertex() override = default;
|
||||
// ACCESSORS
|
||||
FileLine* fileline() const override { return nodep()->fileline(); }
|
||||
AstScope* scopep() const { return m_scopep; }
|
||||
AstNode* nodep() const { return m_nodep; }
|
||||
AstSenTree* srcDomainp() const { return m_srcDomainp; }
|
||||
void srcDomainp(AstSenTree* nodep) { m_srcDomainp = nodep; }
|
||||
bool srcDomainSet() const { return m_srcDomainSet; }
|
||||
void srcDomainSet(bool flag) { m_srcDomainSet = flag; }
|
||||
AstSenTree* dstDomainp() const { return m_dstDomainp; }
|
||||
void dstDomainp(AstSenTree* nodep) { m_dstDomainp = nodep; }
|
||||
bool dstDomainSet() const { return m_dstDomainSet; }
|
||||
void dstDomainSet(bool flag) { m_dstDomainSet = flag; }
|
||||
bool asyncPath() const { return m_asyncPath; }
|
||||
void asyncPath(bool flag) { m_asyncPath = flag; }
|
||||
};
|
||||
|
||||
class CdcVarVertex final : public CdcEitherVertex {
|
||||
AstVarScope* const m_varScp;
|
||||
int m_cntAsyncRst = 0;
|
||||
bool m_fromFlop = false;
|
||||
|
||||
public:
|
||||
CdcVarVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp)
|
||||
: CdcEitherVertex{graphp, scopep, varScp}
|
||||
, m_varScp{varScp} {}
|
||||
~CdcVarVertex() override = default;
|
||||
// ACCESSORS
|
||||
AstVarScope* varScp() const { return m_varScp; }
|
||||
string name() const override { return (cvtToHex(m_varScp) + " " + varScp()->name()); }
|
||||
string dotColor() const override {
|
||||
return fromFlop() ? "green" : cntAsyncRst() ? "red" : "blue";
|
||||
}
|
||||
int cntAsyncRst() const { return m_cntAsyncRst; }
|
||||
void cntAsyncRst(int flag) { m_cntAsyncRst = flag; }
|
||||
bool fromFlop() const { return m_fromFlop; }
|
||||
void fromFlop(bool flag) { m_fromFlop = flag; }
|
||||
};
|
||||
|
||||
class CdcLogicVertex final : public CdcEitherVertex {
|
||||
bool m_hazard : 1;
|
||||
bool m_isFlop : 1;
|
||||
|
||||
public:
|
||||
CdcLogicVertex(V3Graph* graphp, AstScope* scopep, AstNode* nodep, AstSenTree* sensenodep)
|
||||
: CdcEitherVertex{graphp, scopep, nodep}
|
||||
, m_hazard{false}
|
||||
, m_isFlop{false} {
|
||||
srcDomainp(sensenodep);
|
||||
dstDomainp(sensenodep);
|
||||
}
|
||||
~CdcLogicVertex() override = default;
|
||||
// ACCESSORS
|
||||
string name() const override { return (cvtToHex(nodep()) + "@" + scopep()->prettyName()); }
|
||||
string dotColor() const override { return hazard() ? "black" : "yellow"; }
|
||||
bool hazard() const { return m_hazard; }
|
||||
void setHazard(AstNode* nodep) {
|
||||
m_hazard = true;
|
||||
nodep->user3(true);
|
||||
}
|
||||
void clearHazard() { m_hazard = false; }
|
||||
bool isFlop() const { return m_isFlop; }
|
||||
void isFlop(bool flag) { m_isFlop = flag; }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
||||
class CdcDumpVisitor final : public CdcBaseVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// Entire netlist:
|
||||
// {statement}Node::user3 -> bool, indicating not hazard
|
||||
std::ofstream* const m_ofp = nullptr; // Output file
|
||||
string m_prefix;
|
||||
|
||||
void visit(AstNode* nodep) override {
|
||||
*m_ofp << m_prefix;
|
||||
if (nodep->user3()) {
|
||||
*m_ofp << " %%";
|
||||
} else {
|
||||
*m_ofp << " ";
|
||||
}
|
||||
*m_ofp << nodep->prettyTypeName() << "\n";
|
||||
const string lastPrefix = m_prefix;
|
||||
m_prefix = lastPrefix + "1:";
|
||||
iterateAndNextNull(nodep->op1p());
|
||||
m_prefix = lastPrefix + "2:";
|
||||
iterateAndNextNull(nodep->op2p());
|
||||
m_prefix = lastPrefix + "3:";
|
||||
iterateAndNextNull(nodep->op3p());
|
||||
m_prefix = lastPrefix + "4:";
|
||||
iterateAndNextNull(nodep->op4p());
|
||||
m_prefix = lastPrefix;
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
CdcDumpVisitor(AstNode* nodep, std::ofstream* ofp, const string& prefix)
|
||||
: m_ofp{ofp}
|
||||
, m_prefix{prefix} {
|
||||
iterate(nodep);
|
||||
}
|
||||
~CdcDumpVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
||||
class CdcWidthVisitor final : public CdcBaseVisitor {
|
||||
private:
|
||||
size_t m_maxFilenameLen = 0;
|
||||
int m_maxLineno = 0;
|
||||
|
||||
void visit(AstNode* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
// Keeping line+filename lengths separate is much faster than calling ascii().length()
|
||||
if (nodep->fileline()->lineno() >= m_maxLineno) {
|
||||
m_maxLineno = nodep->fileline()->lineno() + 1;
|
||||
}
|
||||
if (nodep->fileline()->filename().length() >= m_maxFilenameLen) {
|
||||
m_maxFilenameLen = nodep->fileline()->filename().length() + 1;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit CdcWidthVisitor(AstNode* nodep) { iterate(nodep); }
|
||||
~CdcWidthVisitor() override = default;
|
||||
// ACCESSORS
|
||||
int maxWidth() const {
|
||||
size_t width = 1;
|
||||
width += m_maxFilenameLen;
|
||||
width += 1; // The :
|
||||
width += cvtToStr(m_maxLineno).length();
|
||||
width += 1; // Final :
|
||||
return static_cast<int>(width);
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Cdc class functions
|
||||
|
||||
class CdcVisitor final : public CdcBaseVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// Entire netlist:
|
||||
// AstVarScope::user1p -> CdcVarVertex* for usage var, 0=not set yet
|
||||
// AstVarScope::user2 -> bool Used in sensitivity list
|
||||
// {statement}Node::user1p -> CdcLogicVertex* for this statement
|
||||
// AstNode::user3 -> bool True indicates to print %% (via V3EmitV)
|
||||
const VNUser1InUse m_inuser1;
|
||||
const VNUser2InUse m_inuser2;
|
||||
const VNUser3InUse m_inuser3;
|
||||
|
||||
// STATE
|
||||
V3Graph m_graph; // Scoreboard of var usages/dependencies
|
||||
CdcLogicVertex* m_logicVertexp = nullptr; // Current statement being tracked, nullptr=ignored
|
||||
AstScope* m_scopep = nullptr; // Current scope being processed
|
||||
const AstNodeModule* m_modp = nullptr; // Current module
|
||||
AstSenTree* m_domainp = nullptr; // Current sentree
|
||||
bool m_inDly = false; // In delayed assign
|
||||
int m_inSenItem = 0; // Number of senitems
|
||||
string m_ofFilename; // Output filename
|
||||
std::ofstream* m_ofp; // Output file
|
||||
uint32_t m_userGeneration = 0; // Generation count to avoid slow userClearVertices
|
||||
int m_filelineWidth = 0; // Characters in longest fileline
|
||||
|
||||
// METHODS
|
||||
void iterateNewStmt(AstNode* nodep) {
|
||||
if (m_scopep) {
|
||||
VL_RESTORER(m_logicVertexp);
|
||||
UINFO(4, " STMT " << nodep << endl);
|
||||
m_logicVertexp = new CdcLogicVertex{&m_graph, m_scopep, nodep, m_domainp};
|
||||
if (m_domainp && m_domainp->hasClocked()) { // To/from a flop
|
||||
m_logicVertexp->isFlop(true);
|
||||
m_logicVertexp->srcDomainp(m_domainp);
|
||||
m_logicVertexp->srcDomainSet(true);
|
||||
m_logicVertexp->dstDomainp(m_domainp);
|
||||
m_logicVertexp->dstDomainSet(true);
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
|
||||
if (false && debug() >= 9) {
|
||||
UINFO(9, "Trace Logic:\n");
|
||||
nodep->dumpTree("- log1: ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CdcVarVertex* makeVarVertex(AstVarScope* varscp) {
|
||||
CdcVarVertex* vertexp = reinterpret_cast<CdcVarVertex*>(varscp->user1p());
|
||||
if (!vertexp) {
|
||||
UINFO(6, "New vertex " << varscp << endl);
|
||||
vertexp = new CdcVarVertex{&m_graph, m_scopep, varscp};
|
||||
varscp->user1p(vertexp);
|
||||
if (varscp->varp()->isUsedClock()) {}
|
||||
if (varscp->varp()->isPrimaryIO()) {
|
||||
// Create IO vertex - note it's relative to the pointed to var, not where we are
|
||||
// now This allows reporting to easily print the input statement
|
||||
CdcLogicVertex* const ioVertexp
|
||||
= new CdcLogicVertex{&m_graph, varscp->scopep(), varscp->varp(), nullptr};
|
||||
if (varscp->varp()->isWritable()) {
|
||||
new V3GraphEdge{&m_graph, vertexp, ioVertexp, 1};
|
||||
} else {
|
||||
new V3GraphEdge{&m_graph, ioVertexp, vertexp, 1};
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m_inSenItem) {
|
||||
varscp->user2(true); // It's like a clock...
|
||||
// TODO: In the future mark it here and do normal clock tree glitch checks also
|
||||
} else if (varscp->user2()) { // It was detected in a sensitivity list earlier
|
||||
// And now it's used as logic. So must be a reset.
|
||||
vertexp->cntAsyncRst(vertexp->cntAsyncRst() + 1);
|
||||
}
|
||||
return vertexp;
|
||||
}
|
||||
|
||||
void warnAndFile(AstNode* nodep, V3ErrorCode code, const string& msg) {
|
||||
static bool told_file = false;
|
||||
nodep->v3warnCode(code, msg);
|
||||
if (!told_file) {
|
||||
told_file = true;
|
||||
std::cerr << V3Error::msgPrefix() << " See details in " << m_ofFilename << endl;
|
||||
}
|
||||
*m_ofp << "%Warning-" << code.ascii() << ": " << nodep->fileline() << " " << msg << '\n';
|
||||
}
|
||||
|
||||
void setNodeHazard(AstNode* nodep) {
|
||||
// Need to not clear if warnings are off (rather than when report it)
|
||||
// as bypassing this warning may turn up another path that isn't warning off'ed.
|
||||
// We can't modifyWarnOff here, as one instantiation might not be
|
||||
// an issue until we find a hitting flop.
|
||||
// Furthermore, a module like a "Or" module would only get flagged
|
||||
// once, even though the signals feeding it are radically different.
|
||||
if (!m_domainp || m_domainp->hasCombo()) {
|
||||
// Source flop logic in a posedge block is OK for reset (not async though)
|
||||
if (m_logicVertexp && !nodep->fileline()->warnIsOff(V3ErrorCode::CDCRSTLOGIC)) {
|
||||
UINFO(8, "Set hazard " << nodep << endl);
|
||||
m_logicVertexp->setHazard(nodep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static string spaces(int level) {
|
||||
string out;
|
||||
while (level--) out += " ";
|
||||
return out;
|
||||
} // LCOV_EXCL_LINE
|
||||
static string pad(unsigned column, const string& in) {
|
||||
string out = in;
|
||||
while (out.length() < column) out += ' ';
|
||||
return out;
|
||||
}
|
||||
|
||||
void analyze() {
|
||||
UINFO(3, __FUNCTION__ << ": " << endl);
|
||||
if (dumpGraph() > 6) m_graph.dumpDotFilePrefixed("cdc_pre");
|
||||
// This will MAX across edge weights
|
||||
m_graph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue);
|
||||
//
|
||||
if (dumpGraph() >= 3) m_graph.dumpDotFilePrefixed("cdc_simp");
|
||||
//
|
||||
analyzeReset();
|
||||
}
|
||||
|
||||
int filelineWidth() {
|
||||
if (!m_filelineWidth) {
|
||||
const CdcWidthVisitor visitor{v3Global.rootp()};
|
||||
m_filelineWidth = visitor.maxWidth();
|
||||
}
|
||||
return m_filelineWidth;
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
// RESET REPORT
|
||||
|
||||
void analyzeReset() {
|
||||
// Find all async reset wires, and trace backwards
|
||||
// userClearVertices is very slow, so we use a generation count instead
|
||||
m_graph.userClearVertices(); // user1: uint32_t - was analyzed generation
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
if (CdcVarVertex* const vvertexp = dynamic_cast<CdcVarVertex*>(itp)) {
|
||||
if (vvertexp->cntAsyncRst()) {
|
||||
m_userGeneration++; // Effectively a userClearVertices()
|
||||
UINFO(8, " Trace One async: " << vvertexp << endl);
|
||||
// Twice, as we need to detect, then propagate
|
||||
CdcEitherVertex* const markp = traceAsyncRecurse(vvertexp, false);
|
||||
if (markp) { // Mark is non-nullptr if something bad on this path
|
||||
UINFO(9, " Trace One bad! " << vvertexp << endl);
|
||||
m_userGeneration++; // Effectively a userClearVertices()
|
||||
traceAsyncRecurse(vvertexp, true);
|
||||
m_userGeneration++; // Effectively a userClearVertices()
|
||||
dumpAsync(vvertexp, markp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CdcEitherVertex* traceAsyncRecurse(CdcEitherVertex* vertexp, bool mark) {
|
||||
// First pass: Return vertex of any hazardous stuff attached, or nullptr if OK
|
||||
// If first pass returns true, second pass calls asyncPath() on appropriate nodes
|
||||
if (vertexp->user() >= m_userGeneration) return nullptr; // Processed - prevent loop
|
||||
vertexp->user(m_userGeneration);
|
||||
|
||||
CdcEitherVertex* mark_outp = nullptr;
|
||||
UINFO(9, " Trace: " << vertexp << endl);
|
||||
|
||||
// Clear out in prep for marking next path
|
||||
if (!mark) vertexp->asyncPath(false);
|
||||
|
||||
if (CdcLogicVertex* const vvertexp = dynamic_cast<CdcLogicVertex*>(vertexp)) {
|
||||
// Any logic considered bad, at the moment, anyhow
|
||||
if (vvertexp->hazard() && !mark_outp) mark_outp = vvertexp;
|
||||
// And keep tracing back so the user can understand what's up
|
||||
} else if (CdcVarVertex* const vvertexp = dynamic_cast<CdcVarVertex*>(vertexp)) {
|
||||
if (mark) vvertexp->asyncPath(true);
|
||||
// If primary I/O, it's ok here back
|
||||
if (vvertexp->varScp()->varp()->isPrimaryInish()) {
|
||||
// Show the source "input" statement if it exists
|
||||
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
CdcEitherVertex* const eFromVertexp
|
||||
= static_cast<CdcEitherVertex*>(edgep->fromp());
|
||||
eFromVertexp->asyncPath(true);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
// Also ok if from flop, but partially trace the flop so more obvious to users
|
||||
if (vvertexp->fromFlop()) {
|
||||
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
CdcEitherVertex* const eFromVertexp
|
||||
= static_cast<CdcEitherVertex*>(edgep->fromp());
|
||||
eFromVertexp->asyncPath(true);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
CdcEitherVertex* const eFromVertexp = static_cast<CdcEitherVertex*>(edgep->fromp());
|
||||
CdcEitherVertex* const submarkp = traceAsyncRecurse(eFromVertexp, mark);
|
||||
if (submarkp && !mark_outp) mark_outp = submarkp;
|
||||
}
|
||||
|
||||
if (mark) vertexp->asyncPath(true);
|
||||
return mark_outp;
|
||||
}
|
||||
|
||||
void dumpAsync(CdcVarVertex* vertexp, CdcEitherVertex* markp) {
|
||||
const AstNode* const nodep = vertexp->varScp();
|
||||
*m_ofp << "\n";
|
||||
*m_ofp << "\n";
|
||||
CdcEitherVertex* targetp = vertexp; // One example destination flop (of possibly many)
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
CdcEitherVertex* const eToVertexp = static_cast<CdcEitherVertex*>(edgep->top());
|
||||
if (!eToVertexp) targetp = eToVertexp;
|
||||
if (const CdcLogicVertex* const vvertexp = dynamic_cast<CdcLogicVertex*>(eToVertexp)) {
|
||||
if (vvertexp->isFlop() // IE the target flop that is upsetting us
|
||||
&& edgep->weight() >= CDC_WEIGHT_ASYNC) { // And var feeds an async reset line
|
||||
targetp = eToVertexp;
|
||||
// UINFO(9," targetasync "<<targetp->name()<<" "<<" from
|
||||
// "<<vertexp->name()<<endl);
|
||||
break;
|
||||
}
|
||||
} // else it might be random logic that's not relevant
|
||||
}
|
||||
// UINFO(9," finalflop "<<targetp->name()<<" "<<targetp->nodep()->fileline()<<endl);
|
||||
warnAndFile(markp->nodep(), V3ErrorCode::CDCRSTLOGIC,
|
||||
"Logic in path that feeds async reset, via signal: " + nodep->prettyNameQ());
|
||||
dumpAsyncRecurse(targetp, "", " ", 0);
|
||||
}
|
||||
bool dumpAsyncRecurse(CdcEitherVertex* vertexp, const string& prefix, const string& sep,
|
||||
int level) {
|
||||
// level=0 is special, indicates to dump destination flop
|
||||
// Return true if printed anything
|
||||
// If mark, also mark the output even if nothing hazardous below
|
||||
if (vertexp->user() >= m_userGeneration) return false; // Processed - prevent loop
|
||||
vertexp->user(m_userGeneration);
|
||||
if (!vertexp->asyncPath() && level != 0) return false; // Not part of path
|
||||
|
||||
// Other logic in the path
|
||||
const string cont = prefix + sep;
|
||||
string nextsep = " ";
|
||||
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
CdcEitherVertex* const eFromVertexp = static_cast<CdcEitherVertex*>(edgep->fromp());
|
||||
if (dumpAsyncRecurse(eFromVertexp, cont, nextsep, level + 1)) nextsep = " | ";
|
||||
}
|
||||
|
||||
// Dump single variable/logic block
|
||||
// See also OrderGraph::loopsVertexCb(V3GraphVertex* vertexp)
|
||||
AstNode* const nodep = vertexp->nodep();
|
||||
const string front
|
||||
= pad(filelineWidth(), nodep->fileline()->ascii() + ":") + " " + prefix + " +- ";
|
||||
if (VN_IS(nodep, VarScope)) {
|
||||
*m_ofp << front << "Variable: " << nodep->prettyName() << '\n';
|
||||
} else {
|
||||
V3EmitV::verilogPrefixedTree(nodep, *m_ofp, prefix + " +- ", filelineWidth(),
|
||||
vertexp->srcDomainp(), true);
|
||||
if (debug()) { CdcDumpVisitor{nodep, m_ofp, front + "DBG: "}; }
|
||||
}
|
||||
|
||||
nextsep = " | ";
|
||||
if (level)
|
||||
*m_ofp << V3OutFile::indentSpaces(filelineWidth()) << " " << prefix << nextsep << "\n";
|
||||
|
||||
if (CdcLogicVertex* const vvertexp = dynamic_cast<CdcLogicVertex*>(vertexp)) {
|
||||
// Now that we've printed a path with this hazard, don't bother to print any more
|
||||
// Otherwise, we'd get a path for almost every destination flop
|
||||
vvertexp->clearHazard();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
// EDGE REPORTS
|
||||
|
||||
void edgeReport() {
|
||||
// Make report of all signal names and what clock edges they have
|
||||
//
|
||||
// Due to flattening, many interesting direct-connect signals are
|
||||
// lost, so we can't make a report showing I/Os for a low level
|
||||
// module. Disabling flattening though makes us consider each
|
||||
// signal in it's own unique clock domain.
|
||||
|
||||
UINFO(3, __FUNCTION__ << ": " << endl);
|
||||
|
||||
// Trace all sources and sinks
|
||||
for (const bool& traceDests : {false, true}) {
|
||||
UINFO(9, " Trace Direction " << (traceDests ? "dst" : "src") << endl);
|
||||
m_graph.userClearVertices(); // user1: bool - was analyzed
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
if (CdcVarVertex* const vvertexp = dynamic_cast<CdcVarVertex*>(itp)) {
|
||||
UINFO(9, " Trace One edge: " << vvertexp << endl);
|
||||
edgeDomainRecurse(vvertexp, traceDests, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const string filename
|
||||
= v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "__cdc_edges.txt";
|
||||
const std::unique_ptr<std::ofstream> ofp{V3File::new_ofstream(filename)};
|
||||
if (ofp->fail()) v3fatal("Can't write " << filename);
|
||||
*ofp << "Edge Report for " << v3Global.opt.prefix() << '\n';
|
||||
|
||||
std::deque<string> report; // Sort output by name
|
||||
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
if (const CdcVarVertex* const vvertexp = dynamic_cast<CdcVarVertex*>(itp)) {
|
||||
const AstVar* const varp = vvertexp->varScp()->varp();
|
||||
{
|
||||
string what = "wire";
|
||||
if (varp->isPrimaryIO()) what = varp->direction().prettyName();
|
||||
|
||||
std::ostringstream os;
|
||||
os.setf(std::ios::left);
|
||||
// Module name - doesn't work due to flattening having lost the original
|
||||
// so we assume the modulename matches the filebasename
|
||||
const string fname = vvertexp->varScp()->fileline()->filebasename() + ":";
|
||||
os << " " << std::setw(20) << fname;
|
||||
os << " " << std::setw(8) << what;
|
||||
os << " " << std::setw(40) << vvertexp->varScp()->prettyName();
|
||||
os << " SRC=";
|
||||
if (vvertexp->srcDomainp()) {
|
||||
V3EmitV::verilogForTree(vvertexp->srcDomainp(), os);
|
||||
}
|
||||
os << " DST=";
|
||||
if (vvertexp->dstDomainp()) {
|
||||
V3EmitV::verilogForTree(vvertexp->dstDomainp(), os);
|
||||
}
|
||||
os << std::setw(0);
|
||||
os << '\n';
|
||||
report.push_back(os.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
stable_sort(report.begin(), report.end());
|
||||
for (const auto& line : report) *ofp << line;
|
||||
}
|
||||
|
||||
void edgeDomainRecurse(CdcEitherVertex* vertexp, bool traceDests, int level) {
|
||||
// Scan back to inputs/outputs, flops, and compute clock domain information
|
||||
UINFO(8, spaces(level) << " Tracein " << vertexp << endl);
|
||||
if (vertexp->user() >= m_userGeneration) return; // Mid-Processed - prevent loop
|
||||
vertexp->user(m_userGeneration);
|
||||
|
||||
// Variables from flops already are domained
|
||||
if (traceDests ? vertexp->dstDomainSet() : vertexp->srcDomainSet()) {
|
||||
return;
|
||||
} // Fully computed
|
||||
|
||||
std::set<AstSenTree*> senouts; // List of all sensitivities for new signal
|
||||
if (dynamic_cast<CdcLogicVertex*>(vertexp)) {
|
||||
} else if (const CdcVarVertex* const vvertexp = dynamic_cast<CdcVarVertex*>(vertexp)) {
|
||||
// If primary I/O, give it domain of the input
|
||||
const AstVar* const varp = vvertexp->varScp()->varp();
|
||||
if (varp->isPrimaryIO() && varp->isNonOutput() && !traceDests) {
|
||||
senouts.insert(new AstSenTree{
|
||||
varp->fileline(), new AstSenItem{varp->fileline(), AstSenItem::Combo{}}});
|
||||
}
|
||||
}
|
||||
|
||||
// Now combine domains of sources/dests
|
||||
if (traceDests) {
|
||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
CdcEitherVertex* const eToVertexp = static_cast<CdcEitherVertex*>(edgep->top());
|
||||
edgeDomainRecurse(eToVertexp, traceDests, level + 1);
|
||||
if (eToVertexp->dstDomainp()) senouts.insert(eToVertexp->dstDomainp());
|
||||
}
|
||||
} else {
|
||||
for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
CdcEitherVertex* const eFromVertexp
|
||||
= static_cast<CdcEitherVertex*>(edgep->fromp());
|
||||
edgeDomainRecurse(eFromVertexp, traceDests, level + 1);
|
||||
if (eFromVertexp->srcDomainp()) senouts.insert(eFromVertexp->srcDomainp());
|
||||
}
|
||||
}
|
||||
|
||||
// Convert list of senses into one sense node
|
||||
AstSenTree* senoutp = nullptr;
|
||||
bool senedited = false;
|
||||
for (const auto& itr : senouts) {
|
||||
if (!senoutp) {
|
||||
senoutp = itr;
|
||||
} else {
|
||||
if (!senedited) {
|
||||
senedited = true;
|
||||
senoutp = senoutp->cloneTree(true);
|
||||
}
|
||||
senoutp->addSensesp(itr->sensesp()->cloneTree(true));
|
||||
}
|
||||
}
|
||||
// If multiple domains need to do complicated optimizations
|
||||
if (senedited) senoutp = VN_AS(V3Const::constifyExpensiveEdit(senoutp), SenTree);
|
||||
if (traceDests) {
|
||||
vertexp->dstDomainSet(true); // Note it's set - domainp may be null, so can't use that
|
||||
vertexp->dstDomainp(senoutp);
|
||||
if (debug() >= 9) {
|
||||
UINFO(9, spaces(level) + " Tracedst " << vertexp);
|
||||
if (senoutp) {
|
||||
V3EmitV::verilogForTree(senoutp, cout);
|
||||
cout << endl;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
vertexp->srcDomainSet(true); // Note it's set - domainp may be null, so can't use that
|
||||
vertexp->srcDomainp(senoutp);
|
||||
if (debug() >= 9) {
|
||||
UINFO(9, spaces(level) + " Tracesrc " << vertexp);
|
||||
if (senoutp) {
|
||||
V3EmitV::verilogForTree(senoutp, cout);
|
||||
cout << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
VL_RESTORER(m_modp);
|
||||
{
|
||||
m_modp = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
}
|
||||
void visit(AstScope* nodep) override {
|
||||
UINFO(4, " SCOPE " << nodep << endl);
|
||||
m_scopep = nodep;
|
||||
m_logicVertexp = nullptr;
|
||||
iterateChildren(nodep);
|
||||
m_scopep = nullptr;
|
||||
}
|
||||
void visit(AstActive* nodep) override {
|
||||
// Create required blocks and add to module
|
||||
UINFO(4, " BLOCK " << nodep << endl);
|
||||
AstNode::user2ClearTree();
|
||||
m_domainp = nodep->sensesp();
|
||||
// Ignore static initializers, initial and final blocks
|
||||
if (!m_domainp || m_domainp->hasCombo() || m_domainp->hasClocked()) {
|
||||
iterateNewStmt(nodep);
|
||||
}
|
||||
m_domainp = nullptr;
|
||||
AstNode::user2ClearTree();
|
||||
}
|
||||
void visit(AstNodeVarRef* nodep) override {
|
||||
if (m_scopep) {
|
||||
UASSERT_OBJ(m_logicVertexp, nodep, "Var ref not under a logic block");
|
||||
AstVarScope* const varscp = nodep->varScopep();
|
||||
UASSERT_OBJ(varscp, nodep, "Var didn't get varscoped in V3Scope.cpp");
|
||||
CdcVarVertex* const varvertexp = makeVarVertex(varscp);
|
||||
UINFO(5, " VARREF to " << varscp << endl);
|
||||
// We use weight of one for normal edges,
|
||||
// Weight of CDC_WEIGHT_ASYNC to indicate feeds async (for reporting)
|
||||
// When simplify we'll take the MAX weight
|
||||
if (nodep->access().isWriteOrRW()) {
|
||||
new V3GraphEdge{&m_graph, m_logicVertexp, varvertexp, 1};
|
||||
if (m_inDly) {
|
||||
varvertexp->fromFlop(true);
|
||||
varvertexp->srcDomainp(m_domainp);
|
||||
varvertexp->srcDomainSet(true);
|
||||
}
|
||||
} else {
|
||||
if (varvertexp->cntAsyncRst()) {
|
||||
// UINFO(9," edgeasync "<<varvertexp->name()<<" to "<<m_logicVertexp<<endl);
|
||||
new V3GraphEdge{&m_graph, varvertexp, m_logicVertexp, CDC_WEIGHT_ASYNC};
|
||||
} else {
|
||||
// UINFO(9," edgena "<<varvertexp->name()<<" to "<<m_logicVertexp<<endl);
|
||||
new V3GraphEdge{&m_graph, varvertexp, m_logicVertexp, 1};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void visit(AstAssignDly* nodep) override {
|
||||
VL_RESTORER(m_inDly);
|
||||
m_inDly = true;
|
||||
iterateChildren(nodep);
|
||||
m_inDly = false;
|
||||
}
|
||||
void visit(AstSenItem* nodep) override {
|
||||
m_inSenItem = true;
|
||||
iterateChildren(nodep);
|
||||
m_inSenItem = false;
|
||||
}
|
||||
void visit(AstAlways* nodep) override { iterateNewStmt(nodep); }
|
||||
void visit(AstAlwaysPublic* nodep) override {
|
||||
// CDC doesn't care about public variables
|
||||
}
|
||||
void visit(AstCFunc* nodep) override { iterateNewStmt(nodep); }
|
||||
void visit(AstAssignAlias* nodep) override { iterateNewStmt(nodep); }
|
||||
void visit(AstAssignW* nodep) override { iterateNewStmt(nodep); }
|
||||
|
||||
// Expressions that shouldn't cause us to clear hazard
|
||||
void visit(AstConst*) override {}
|
||||
void visit(AstReplicate* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstConcat* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstNot* nodep) override { iterateChildren(nodep); }
|
||||
void visit(AstSel* nodep) override {
|
||||
if (!VN_IS(nodep->lsbp(), Const)) setNodeHazard(nodep);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstNodeSel* nodep) override {
|
||||
if (!VN_IS(nodep->bitp(), Const)) setNodeHazard(nodep);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
// Ignores
|
||||
void visit(AstInitial*) override {}
|
||||
void visit(AstInitialAutomatic*) override {}
|
||||
void visit(AstInitialStatic*) override {}
|
||||
void visit(AstTraceDecl*) override {}
|
||||
void visit(AstCoverToggle*) override {}
|
||||
void visit(AstNodeDType*) override {}
|
||||
|
||||
//--------------------
|
||||
// Default
|
||||
void visit(AstNodeExpr* nodep) override {
|
||||
setNodeHazard(nodep);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit CdcVisitor(AstNode* nodep) {
|
||||
// Make report of all signal names and what clock edges they have
|
||||
const string filename = v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "__cdc.txt";
|
||||
m_ofp = V3File::new_ofstream(filename);
|
||||
if (m_ofp->fail()) v3fatal("Can't write " << filename);
|
||||
m_ofFilename = filename;
|
||||
*m_ofp << "CDC Report for " << v3Global.opt.prefix() << '\n';
|
||||
*m_ofp
|
||||
<< "Each dump below traces logic from inputs/source flops to destination flop(s).\n";
|
||||
*m_ofp << "First source logic is listed, then a variable that logic generates,\n";
|
||||
*m_ofp << "repeating recursively forwards to the destination flop(s).\n";
|
||||
*m_ofp << "%% Indicates the operator considered potentially hazardous.\n";
|
||||
|
||||
iterate(nodep);
|
||||
analyze();
|
||||
if (debug() >= 1) edgeReport(); // Not useful to users at the moment
|
||||
if (false) {
|
||||
*m_ofp << "\nDBG-test-dumper\n";
|
||||
V3EmitV::verilogPrefixedTree(nodep, *m_ofp, "DBG ", 40, nullptr, true);
|
||||
*m_ofp << '\n';
|
||||
}
|
||||
}
|
||||
~CdcVisitor() override {
|
||||
if (m_ofp) VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Cdc class functions
|
||||
|
||||
void V3Cdc::cdcAll(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
{ CdcVisitor{nodep}; }
|
||||
}
|
32
src/V3Cdc.h
32
src/V3Cdc.h
@ -1,32 +0,0 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Break always into sensitivity block domains
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-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 OR Artistic-2.0
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef VERILATOR_V3CDC_H_
|
||||
#define VERILATOR_V3CDC_H_
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
class AstNetlist;
|
||||
|
||||
//============================================================================
|
||||
|
||||
class V3Cdc final {
|
||||
public:
|
||||
static void cdcAll(AstNetlist* nodep);
|
||||
};
|
||||
|
||||
#endif // Guard
|
@ -82,7 +82,7 @@ public:
|
||||
CASEWITHX, // Case with X values
|
||||
CASEX, // Casex
|
||||
CASTCONST, // Cast is constant
|
||||
CDCRSTLOGIC, // Logic in async reset path
|
||||
CDCRSTLOGIC, // Logic in async reset path. Historical, never issued.
|
||||
CLKDATA, // Clock used as data. Historical, never issued.
|
||||
CMPCONST, // Comparison is constant due to limited range
|
||||
COLONPLUS, // :+ instead of +:
|
||||
|
@ -776,15 +776,11 @@ void V3Options::notify() {
|
||||
FileLine* const cmdfl = new FileLine{FileLine::commandLineFilename()};
|
||||
|
||||
if (!outFormatOk() && v3Global.opt.main()) ccSet(); // --main implies --cc if not provided
|
||||
if (!outFormatOk() && !cdc() && !dpiHdrOnly() && !lintOnly() && !preprocOnly() && !xmlOnly()) {
|
||||
v3fatal("verilator: Need --binary, --cc, --sc, --cdc, --dpi-hdr-only, --lint-only, "
|
||||
if (!outFormatOk() && !dpiHdrOnly() && !lintOnly() && !preprocOnly() && !xmlOnly()) {
|
||||
v3fatal("verilator: Need --binary, --cc, --sc, --dpi-hdr-only, --lint-only, "
|
||||
"--xml-only or --E option");
|
||||
}
|
||||
|
||||
if (cdc()) {
|
||||
cmdfl->v3warn(DEPRECATED, "Option --cdc is deprecated and is planned for removal");
|
||||
}
|
||||
|
||||
if (m_build && (m_gmake || m_cmake)) {
|
||||
cmdfl->v3error("--make cannot be used together with --build. Suggest see manual");
|
||||
}
|
||||
@ -826,16 +822,14 @@ void V3Options::notify() {
|
||||
// Default some options if not turned on or off
|
||||
if (v3Global.opt.skipIdentical().isDefault()) {
|
||||
v3Global.opt.m_skipIdentical.setTrueOrFalse( //
|
||||
!v3Global.opt.cdc() //
|
||||
&& !v3Global.opt.dpiHdrOnly() //
|
||||
!v3Global.opt.dpiHdrOnly() //
|
||||
&& !v3Global.opt.lintOnly() //
|
||||
&& !v3Global.opt.preprocOnly() //
|
||||
&& !v3Global.opt.xmlOnly());
|
||||
}
|
||||
if (v3Global.opt.makeDepend().isDefault()) {
|
||||
v3Global.opt.m_makeDepend.setTrueOrFalse( //
|
||||
!v3Global.opt.cdc() //
|
||||
&& !v3Global.opt.dpiHdrOnly() //
|
||||
!v3Global.opt.dpiHdrOnly() //
|
||||
&& !v3Global.opt.lintOnly() //
|
||||
&& !v3Global.opt.preprocOnly() //
|
||||
&& !v3Global.opt.xmlOnly());
|
||||
@ -1085,7 +1079,6 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
|
||||
|
||||
DECL_OPTION("-CFLAGS", CbVal, callStrSetter(&V3Options::addCFlags));
|
||||
DECL_OPTION("-cc", CbCall, [this]() { ccSet(); });
|
||||
DECL_OPTION("-cdc", OnOff, &m_cdc);
|
||||
DECL_OPTION("-clk", CbVal, callStrSetter(&V3Options::addClocker));
|
||||
DECL_OPTION("-no-clk", CbVal, callStrSetter(&V3Options::addNoClocker));
|
||||
DECL_OPTION("-comp-limit-blocks", Set, &m_compLimitBlocks).undocumented();
|
||||
|
@ -224,7 +224,6 @@ private:
|
||||
bool m_bboxSys = false; // main switch: --bbox-sys
|
||||
bool m_bboxUnsup = false; // main switch: --bbox-unsup
|
||||
bool m_build = false; // main switch: --build
|
||||
bool m_cdc = false; // main switch: --cdc
|
||||
bool m_cmake = false; // main switch: --make cmake
|
||||
bool m_context = true; // main switch: --Wcontext
|
||||
bool m_coverageLine = false; // main switch: --coverage-block
|
||||
@ -441,7 +440,6 @@ public:
|
||||
bool build() const { return m_build; }
|
||||
string buildDepBin() const { return m_buildDepBin; }
|
||||
void buildDepBin(const string& flag) { m_buildDepBin = flag; }
|
||||
bool cdc() const { return m_cdc; }
|
||||
bool cmake() const { return m_cmake; }
|
||||
bool context() const VL_MT_SAFE { return m_context; }
|
||||
bool coverage() const VL_MT_SAFE {
|
||||
|
@ -26,7 +26,6 @@
|
||||
#include "V3CUse.h"
|
||||
#include "V3Case.h"
|
||||
#include "V3Cast.h"
|
||||
#include "V3Cdc.h"
|
||||
#include "V3Class.h"
|
||||
#include "V3Clean.h"
|
||||
#include "V3Clock.h"
|
||||
@ -372,13 +371,6 @@ static void process() {
|
||||
V3Const::constifyAll(v3Global.rootp());
|
||||
V3Dead::deadifyAllScoped(v3Global.rootp());
|
||||
|
||||
// Clock domain crossing analysis
|
||||
if (v3Global.opt.cdc()) {
|
||||
V3Cdc::cdcAll(v3Global.rootp());
|
||||
V3Error::abortIfErrors();
|
||||
return;
|
||||
}
|
||||
|
||||
// Reorder assignments in pipelined blocks
|
||||
if (v3Global.opt.fReorder()) V3Split::splitReorderAll(v3Global.rootp());
|
||||
|
||||
@ -579,8 +571,6 @@ static void process() {
|
||||
if (v3Global.opt.cmake()) V3EmitCMake::emit();
|
||||
if (v3Global.opt.gmake()) V3EmitMk::emitmk();
|
||||
}
|
||||
|
||||
// Note early return above when opt.cdc()
|
||||
}
|
||||
|
||||
static void verilate(const string& argString) {
|
||||
|
@ -1,14 +0,0 @@
|
||||
%Warning-DEPRECATED: Option --cdc is deprecated and is planned for removal
|
||||
... For warning description see https://verilator.org/warn/DEPRECATED?v=latest
|
||||
... Use "/* verilator lint_off DEPRECATED */" and lint_on around source to disable this message.
|
||||
%Warning-CDCRSTLOGIC: t/t_cdc_async_bad.v:53:21: Logic in path that feeds async reset, via signal: 't.rst6a_bad_n'
|
||||
53 | wire rst6a_bad_n = rst6_bad_n ^ $c1("0");
|
||||
| ^
|
||||
%Warning-CDCRSTLOGIC: See details in obj_vlt/t_cdc_async_bad/Vt_cdc_async_bad__cdc.txt
|
||||
%Warning-CDCRSTLOGIC: t/t_cdc_async_bad.v:54:21: Logic in path that feeds async reset, via signal: 't.rst6b_bad_n'
|
||||
54 | wire rst6b_bad_n = rst6_bad_n ^ $c1("1");
|
||||
| ^
|
||||
%Warning-CDCRSTLOGIC: t/t_cdc_async_bad.v:28:21: Logic in path that feeds async reset, via signal: 't.rst2_bad_n'
|
||||
28 | wire rst2_bad_n = rst0_n | rst1_n;
|
||||
| ^
|
||||
%Error: Exiting due to
|
@ -1,25 +0,0 @@
|
||||
#!/usr/bin/env perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 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);
|
||||
|
||||
compile(
|
||||
v_flags => ['--cdc'],
|
||||
verilator_make_gmake => 0,
|
||||
make_top_shell => 0,
|
||||
make_main => 0,
|
||||
fails => 1,
|
||||
expect_filename => $Self->{golden_filename},
|
||||
);
|
||||
|
||||
file_grep("$Self->{obj_dir}/V$Self->{name}__cdc.txt", qr/CDC Report/);
|
||||
|
||||
ok(1);
|
||||
1;
|
@ -1,82 +0,0 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2009 by Wilson Snyder.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module t (/*AUTOARG*/
|
||||
// Outputs
|
||||
q0, q1, q2, q3, q4, q5, q6a, q6b,
|
||||
// Inputs
|
||||
clk, d, rst0_n
|
||||
);
|
||||
input clk;
|
||||
input d;
|
||||
|
||||
// OK -- from primary
|
||||
input rst0_n;
|
||||
output wire q0;
|
||||
Flop flop0 (.q(q0), .rst_n(rst0_n), .clk(clk), .d(d));
|
||||
|
||||
// OK -- from flop
|
||||
reg rst1_n;
|
||||
always @ (posedge clk) rst1_n <= rst0_n;
|
||||
output wire q1;
|
||||
Flop flop1 (.q(q1), .rst_n(rst1_n), .clk(clk), .d(d));
|
||||
|
||||
// Bad - logic
|
||||
wire rst2_bad_n = rst0_n | rst1_n;
|
||||
output wire q2;
|
||||
Flop flop2 (.q(q2), .rst_n(rst2_bad_n), .clk(clk), .d(d));
|
||||
|
||||
// Bad - logic in submodule
|
||||
wire rst3_bad_n;
|
||||
Sub sub (.z(rst3_bad_n), .a(rst0_n), .b(rst1_n));
|
||||
output wire q3;
|
||||
Flop flop3 (.q(q3), .rst_n(rst3_bad_n), .clk(clk), .d(d));
|
||||
|
||||
// OK - bit selection
|
||||
reg [3:0] rst4_n;
|
||||
always @ (posedge clk) rst4_n <= {4{rst0_n}};
|
||||
output wire q4;
|
||||
Flop flop4 (.q(q4), .rst_n(rst4_n[1]), .clk(clk), .d(d));
|
||||
|
||||
// Bad - logic, but waived
|
||||
// verilator lint_off CDCRSTLOGIC
|
||||
wire rst5_waive_n = rst0_n & rst1_n;
|
||||
// verilator lint_on CDCRSTLOGIC
|
||||
output wire q5;
|
||||
Flop flop5 (.q(q5), .rst_n(rst5_waive_n), .clk(clk), .d(d));
|
||||
|
||||
// Bad - for graph test - logic feeds two signals, three destinations
|
||||
wire rst6_bad_n = rst0_n ^ rst1_n;
|
||||
wire rst6a_bad_n = rst6_bad_n ^ $c1("0"); // $c prevents optimization
|
||||
wire rst6b_bad_n = rst6_bad_n ^ $c1("1");
|
||||
output wire q6a;
|
||||
output wire q6b;
|
||||
Flop flop6a (.q(q6a), .rst_n(rst6a_bad_n), .clk(clk), .d(d));
|
||||
Flop flop6v (.q(q6b), .rst_n(rst6b_bad_n), .clk(clk), .d(d));
|
||||
|
||||
initial begin
|
||||
$display("%%Error: Not a runnable test");
|
||||
$stop;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
module Flop (
|
||||
input clk,
|
||||
input d,
|
||||
input rst_n,
|
||||
output logic q);
|
||||
|
||||
always @ (posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) q <= 1'b0;
|
||||
else q <= d;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module Sub (input a, b,
|
||||
output z);
|
||||
assign z = a|b;
|
||||
endmodule
|
@ -1,20 +0,0 @@
|
||||
Edge Report for Vt_cdc_async_debug_bad
|
||||
t_cdc_async_bad.v: input clk SRC=@(*) DST=@(posedge clk or negedge rst0_n or negedge t.__Vcellinp__flop4__rst_n or negedge t.rst1_n or negedge t.rst2_bad_n or negedge t.rst5_waive_n or negedge t.rst6a_bad_n or negedge t.rst6b_bad_n)
|
||||
t_cdc_async_bad.v: input d SRC=@(*) DST=@(posedge clk or negedge rst0_n or negedge t.__Vcellinp__flop4__rst_n or negedge t.rst1_n or negedge t.rst2_bad_n or negedge t.rst5_waive_n or negedge t.rst6a_bad_n or negedge t.rst6b_bad_n)
|
||||
t_cdc_async_bad.v: input rst0_n SRC=@(*) DST=@(posedge clk or negedge rst0_n or negedge t.rst2_bad_n or negedge t.rst5_waive_n or negedge t.rst6a_bad_n or negedge t.rst6b_bad_n)
|
||||
t_cdc_async_bad.v: output q0 SRC=@(posedge clk or negedge rst0_n) DST=
|
||||
t_cdc_async_bad.v: output q1 SRC=@(posedge clk or negedge t.rst1_n) DST=
|
||||
t_cdc_async_bad.v: output q2 SRC=@(posedge clk or negedge t.rst2_bad_n) DST=
|
||||
t_cdc_async_bad.v: output q3 SRC=@(posedge clk or negedge t.rst2_bad_n) DST=
|
||||
t_cdc_async_bad.v: output q4 SRC=@(posedge clk or negedge t.__Vcellinp__flop4__rst_n) DST=
|
||||
t_cdc_async_bad.v: output q5 SRC=@(posedge clk or negedge t.rst5_waive_n) DST=
|
||||
t_cdc_async_bad.v: output q6a SRC=@(posedge clk or negedge t.rst6a_bad_n) DST=
|
||||
t_cdc_async_bad.v: output q6b SRC=@(posedge clk or negedge t.rst6b_bad_n) DST=
|
||||
t_cdc_async_bad.v: wire t.__Vcellinp__flop4__rst_n SRC=@(posedge clk) DST=@(posedge clk or negedge t.__Vcellinp__flop4__rst_n)
|
||||
t_cdc_async_bad.v: wire t.rst1_n SRC=@(posedge clk) DST=@(posedge clk or negedge t.rst1_n or negedge t.rst2_bad_n or negedge t.rst5_waive_n or negedge t.rst6a_bad_n or negedge t.rst6b_bad_n)
|
||||
t_cdc_async_bad.v: wire t.rst2_bad_n SRC=@(* or posedge clk) DST=@(posedge clk or negedge t.rst2_bad_n)
|
||||
t_cdc_async_bad.v: wire t.rst4_n SRC=@(posedge clk) DST=@(posedge clk or negedge t.__Vcellinp__flop4__rst_n)
|
||||
t_cdc_async_bad.v: wire t.rst5_waive_n SRC=@(* or posedge clk) DST=@(posedge clk or negedge t.rst5_waive_n)
|
||||
t_cdc_async_bad.v: wire t.rst6_bad_n SRC=@(* or posedge clk) DST=@(posedge clk or negedge t.rst6a_bad_n or negedge t.rst6b_bad_n)
|
||||
t_cdc_async_bad.v: wire t.rst6a_bad_n SRC=@(* or posedge clk) DST=@(posedge clk or negedge t.rst6a_bad_n)
|
||||
t_cdc_async_bad.v: wire t.rst6b_bad_n SRC=@(* or posedge clk) DST=@(posedge clk or negedge t.rst6b_bad_n)
|
@ -1,27 +0,0 @@
|
||||
#!/usr/bin/env perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 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);
|
||||
|
||||
top_filename("t/t_cdc_async_bad.v");
|
||||
|
||||
compile(
|
||||
# --debug so we get code coverage of Cdc
|
||||
v_flags => ['--cdc --debug'],
|
||||
verilator_make_gmake => 0,
|
||||
make_top_shell => 0,
|
||||
make_main => 0,
|
||||
fails => 1,
|
||||
);
|
||||
|
||||
files_identical("$Self->{obj_dir}/V$Self->{name}__cdc_edges.txt", $Self->{golden_filename});
|
||||
|
||||
ok(1);
|
||||
1;
|
@ -1 +1 @@
|
||||
%Error: verilator: Need --binary, --cc, --sc, --cdc, --dpi-hdr-only, --lint-only, --xml-only or --E option
|
||||
%Error: verilator: Need --binary, --cc, --sc, --dpi-hdr-only, --lint-only, --xml-only or --E option
|
||||
|
Loading…
Reference in New Issue
Block a user