Add LATCH and NOLATCH warnings (#1609) (#2740).

This commit is contained in:
Julien Margetts 2021-01-05 19:26:01 +00:00 committed by GitHub
parent 6d80e8f856
commit a11700271f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 605 additions and 24 deletions

View File

@ -4591,6 +4591,16 @@ exposed is acceptable.
Ignoring this warning will only suppress the lint check, it will simulate Ignoring this warning will only suppress the lint check, it will simulate
correctly. correctly.
=item LATCH
Warns that a signal is not assigned in all control paths of a combinational
always block, resulting in the inference of a latch. For intentional
latches, consider using the always_latch (SystemVerilog) keyword instead.
The warning may be disabled with a lint_off pragma around the always block.
Ignoring this warning will only suppress the lint check, it will simulate
correctly.
=item LITENDIAN =item LITENDIAN
Warns that a packed vector is declared with little endian bit numbering Warns that a packed vector is declared with little endian bit numbering
@ -4654,6 +4664,15 @@ based on the top module name followed by __02E (a Verilator-encoded ASCII
".'). This renaming is done even if the two modules' signals seem ".'). This renaming is done even if the two modules' signals seem
identical, e.g. multiple modules with a "clk" input. identical, e.g. multiple modules with a "clk" input.
=item NOLATCH
Warns that no latch was detected in an always_latch block. The warning may
be disabled with a lint_off pragma around the always block, but recoding
using a regular always may be more appropriate.
Ignoring this warning will only suppress the lint check, it will simulate
correctly.
=item PINCONNECTEMPTY =item PINCONNECTEMPTY
Warns that an instance has a pin which is connected to .pin_name(), Warns that an instance has a pin which is connected to .pin_name(),

View File

@ -35,11 +35,168 @@
#include "V3EmitCBase.h" #include "V3EmitCBase.h"
#include "V3Const.h" #include "V3Const.h"
#include "V3SenTree.h" // for SenTreeSet #include "V3SenTree.h" // for SenTreeSet
#include "V3Graph.h"
#include <unordered_map> #include <unordered_map>
//***** See below for main transformation engine //***** See below for main transformation engine
//######################################################################
// Extend V3GraphVertex class for use in latch detection graph
class LatchDetectGraphVertex final : public V3GraphVertex {
public:
enum VertexType : uint8_t { VT_BLOCK, VT_BRANCH, VT_OUTPUT };
private:
string m_name; // Only used for .dot file generation
VertexType m_type; // Vertex type (BLOCK/BRANCH/OUTPUT)
string typestr() const { // "
switch (m_type) {
case VT_BLOCK: return "(||)"; // basic block node
case VT_BRANCH: return "(&&)"; // if/else branch mode
case VT_OUTPUT: return "(out)"; // var assignment
default: return "??"; // unknown
}
}
public:
LatchDetectGraphVertex(V3Graph* graphp, const string& name, VertexType type = VT_BLOCK)
: V3GraphVertex(graphp)
, m_name(name)
, m_type(type) {}
virtual string name() const { return m_name + " " + typestr(); }
virtual string dotColor() const { return user() ? "green" : "black"; }
virtual int type() const { return m_type; }
};
//######################################################################
// Extend V3Graph class for use as a latch detection graph
class LatchDetectGraph final : public V3Graph {
protected:
typedef std::vector<AstVarRef*> VarRefVec;
LatchDetectGraphVertex* m_curVertexp; // Current latch detection graph vertex
VarRefVec m_outputs; // Vector of lvalues encountered on this pass
VL_DEBUG_FUNC; // Declare debug()
static LatchDetectGraphVertex* castVertexp(void* vertexp) {
return reinterpret_cast<LatchDetectGraphVertex*>(vertexp);
}
// Recursively traverse the graph to determine whether every control 'BLOCK' has an assignment
// to the output we are currently analysing (the output whose 'user() is set), if so return
// true. Where a BLOCK contains a BRANCH, both the if and else sides of the branch must return
// true for the BRANCH to evalute to true. A BLOCK however needs only a single one of its
// siblings to evaluate true in order to evaluate true itself. On output vertex only evaluates
// true if it is the vertex we are analyzing on this check
bool latchCheckInternal(LatchDetectGraphVertex* vertexp) {
bool result = false;
switch (vertexp->type()) {
case LatchDetectGraphVertex::VT_OUTPUT: // Base case
result = vertexp->user();
break;
case LatchDetectGraphVertex::VT_BLOCK: // (OR of potentially many siblings)
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
if (latchCheckInternal(castVertexp(edgep->top()))) {
result = true;
break;
}
}
break;
case LatchDetectGraphVertex::VT_BRANCH: // (AND of both sibling)
// A BRANCH vertex always has exactly 2 siblings
LatchDetectGraphVertex* ifp = castVertexp(vertexp->outBeginp()->top());
LatchDetectGraphVertex* elsp = castVertexp(vertexp->outBeginp()->outNextp()->top());
result = latchCheckInternal(ifp) && latchCheckInternal(elsp);
break;
}
vertexp->user(result);
return result;
}
public:
LatchDetectGraph() { clear(); }
~LatchDetectGraph() { clear(); }
// ACCESSORS
LatchDetectGraphVertex* currentp() { return m_curVertexp; }
void currentp(LatchDetectGraphVertex* vertex) { m_curVertexp = vertex; }
// METHODS
void begin() {
// Start a new if/else tracking graph
// See NODE STATE comment in ActiveLatchCheckVisitor
AstNode::user1ClearTree();
m_curVertexp = new LatchDetectGraphVertex(this, "ROOT");
}
// Clear out userp field of referenced outputs on destruction
// (occurs at the end of each combinational always block)
void clear() {
m_outputs.clear();
// Calling base class clear will unlink & delete all edges & vertices
V3Graph::clear();
m_curVertexp = nullptr;
}
// Add a new control path and connect it to its parent
LatchDetectGraphVertex* addPathVertex(LatchDetectGraphVertex* parent, const string& name,
bool branch = false) {
m_curVertexp = new LatchDetectGraphVertex(this, name,
branch ? LatchDetectGraphVertex::VT_BRANCH
: LatchDetectGraphVertex::VT_BLOCK);
new V3GraphEdge(this, parent, m_curVertexp, 1);
return m_curVertexp;
}
// Add a new output variable vertex and store a pointer to it in the user1 field of the
// variables AstNode
LatchDetectGraphVertex* addOutputVertex(AstVarRef* nodep) {
LatchDetectGraphVertex* outVertexp
= new LatchDetectGraphVertex(this, nodep->name(), LatchDetectGraphVertex::VT_OUTPUT);
nodep->varp()->user1p(outVertexp);
m_outputs.push_back(nodep);
return outVertexp;
}
// Connect an output assignment to its parent control block
void addAssignment(AstVarRef* nodep) {
LatchDetectGraphVertex* outVertexp;
if (!nodep->varp()->user1p()) { // Not seen this output before
outVertexp = addOutputVertex(nodep);
} else
outVertexp = castVertexp(nodep->varp()->user1p());
new V3GraphEdge(this, m_curVertexp, outVertexp, 1);
}
// Run latchCheckInternal on each variable assigned by the always block to see if all control
// paths make an assignment. Detected latches are flagged in the variables AstVar
void latchCheck(AstNode* nodep, bool latch_expected) {
bool latch_detected = false;
for (const auto& vrp : m_outputs) {
LatchDetectGraphVertex* vertp = castVertexp(vrp->varp()->user1p());
vertp->user(true); // Identify the output vertex we are checking paths _to_
if (!latchCheckInternal(castVertexp(verticesBeginp()))) { latch_detected = true; }
if (latch_detected && !latch_expected) {
nodep->v3warn(
LATCH,
"Latch inferred for signal "
<< vrp->prettyNameQ()
<< " (not all control paths of combinational always assign a value)\n"
<< nodep->warnMore()
<< "... Suggest use of always_latch for intentional latches");
if (debug() >= 9) { dumpDotFilePrefixed("latch_" + vrp->name()); }
}
vertp->user(false); // Clear again (see above)
vrp->varp()->isLatched(latch_detected);
}
// Should _all_ variables assigned in always_latch be latches? Probably, but this only
// warns if none of them are
if (latch_expected && !latch_detected)
nodep->v3warn(NOLATCH, "No latches detected in always_latch block");
}
};
//###################################################################### //######################################################################
// Collect existing active names // Collect existing active names
@ -135,6 +292,48 @@ public:
void main(AstScope* nodep) { iterate(nodep); } void main(AstScope* nodep) { iterate(nodep); }
}; };
//######################################################################
// Latch checking visitor
class ActiveLatchCheckVisitor final : public ActiveBaseVisitor {
private:
// NODE STATE
// Input:
// AstVar::user1p // V2LatchGraphVertex* The vertex handling this node
AstUser1InUse m_inuser1;
// STATE
LatchDetectGraph m_graph; // Graph used to detect latches in combo always
// VISITORS
virtual void visit(AstVarRef* nodep) {
AstVar* varp = nodep->varp();
if (nodep->access().isWriteOrRW() && varp->isSignal() && !varp->isUsedLoopIdx()) {
m_graph.addAssignment(nodep);
}
}
virtual void visit(AstNodeIf* nodep) {
LatchDetectGraphVertex* parentp = m_graph.currentp();
LatchDetectGraphVertex* branchp = m_graph.addPathVertex(parentp, "BRANCH", true);
m_graph.addPathVertex(branchp, "IF");
iterateAndNextNull(nodep->ifsp());
m_graph.addPathVertex(branchp, "ELSE");
iterateAndNextNull(nodep->elsesp());
m_graph.currentp(parentp);
}
// Empty visitors, speed things up
virtual void visit(AstNodeMath* nodep) {}
//--------------------
virtual void visit(AstNode* nodep) { iterateChildren(nodep); }
public:
// CONSTRUCTORS
ActiveLatchCheckVisitor(AstNode* nodep, VAlwaysKwd kwd) {
m_graph.begin();
iterate(nodep);
m_graph.latchCheck(nodep, kwd == VAlwaysKwd::ALWAYS_LATCH);
}
virtual ~ActiveLatchCheckVisitor() {}
};
//###################################################################### //######################################################################
// Active AssignDly replacement functions // Active AssignDly replacement functions
@ -157,11 +356,14 @@ private:
<< "... Suggest blocking assignments (=)"); << "... Suggest blocking assignments (=)");
} else if (m_check == CT_LATCH) { } else if (m_check == CT_LATCH) {
// Suppress. Shouldn't matter that the interior of the latch races // Suppress. Shouldn't matter that the interior of the latch races
} else { } else if (!(VN_IS(nodep->lhsp(), VarRef)
&& VN_CAST(nodep->lhsp(), VarRef)->varp()->isLatched())) {
nodep->v3warn(COMBDLY, "Delayed assignments (<=) in non-clocked" nodep->v3warn(COMBDLY, "Delayed assignments (<=) in non-clocked"
" (non flop or latch) block\n" " (non flop or latch) block\n"
<< nodep->warnMore() << nodep->warnMore()
<< "... Suggest blocking assignments (=)"); << "... Suggest blocking assignments (=)");
// Conversely, we could also suggest latches use delayed assignments, as
// recommended by Cliff Cummings?
} }
AstNode* newp = new AstAssign(nodep->fileline(), nodep->lhsp()->unlinkFrBack(), AstNode* newp = new AstAssign(nodep->fileline(), nodep->lhsp()->unlinkFrBack(),
nodep->rhsp()->unlinkFrBack()); nodep->rhsp()->unlinkFrBack());
@ -341,6 +543,7 @@ private:
// Warn and/or convert any delayed assignments // Warn and/or convert any delayed assignments
if (combo && !sequent) { if (combo && !sequent) {
ActiveLatchCheckVisitor latchvisitor(nodep, kwd);
if (kwd == VAlwaysKwd::ALWAYS_LATCH) { if (kwd == VAlwaysKwd::ALWAYS_LATCH) {
ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_LATCH); ActiveDlyVisitor dlyvisitor(nodep, ActiveDlyVisitor::CT_LATCH);
} else { } else {

View File

@ -157,7 +157,7 @@ public:
bool isReadOnly() const { return m_e == READ; } // False with READWRITE bool isReadOnly() const { return m_e == READ; } // False with READWRITE
bool isReadOrRW() const { return m_e == READ || m_e == READWRITE; } bool isReadOrRW() const { return m_e == READ || m_e == READWRITE; }
bool isWriteOrRW() const { return m_e == WRITE || m_e == READWRITE; } bool isWriteOrRW() const { return m_e == WRITE || m_e == READWRITE; }
bool isRW() const { return m_e == READWRITE; } // False with READWRITE bool isRW() const { return m_e == READWRITE; }
}; };
inline bool operator==(const VAccess& lhs, const VAccess& rhs) { return lhs.m_e == rhs.m_e; } inline bool operator==(const VAccess& lhs, const VAccess& rhs) { return lhs.m_e == rhs.m_e; }
inline bool operator==(const VAccess& lhs, VAccess::en rhs) { return lhs.m_e == rhs; } inline bool operator==(const VAccess& lhs, VAccess::en rhs) { return lhs.m_e == rhs; }

View File

@ -1580,6 +1580,7 @@ void AstVar::dump(std::ostream& str) const {
if (isPulldown()) str << " [PULLDOWN]"; if (isPulldown()) str << " [PULLDOWN]";
if (isUsedClock()) str << " [CLK]"; if (isUsedClock()) str << " [CLK]";
if (isSigPublic()) str << " [P]"; if (isSigPublic()) str << " [P]";
if (isLatched()) str << " [LATCHED]";
if (isUsedLoopIdx()) str << " [LOOP]"; if (isUsedLoopIdx()) str << " [LOOP]";
if (attrClockEn()) str << " [aCLKEN]"; if (attrClockEn()) str << " [aCLKEN]";
if (attrIsolateAssign()) str << " [aISO]"; if (attrIsolateAssign()) str << " [aISO]";

View File

@ -1949,6 +1949,7 @@ private:
bool m_noSubst : 1; // Do not substitute out references bool m_noSubst : 1; // Do not substitute out references
bool m_overridenParam : 1; // Overridden parameter by #(...) or defparam bool m_overridenParam : 1; // Overridden parameter by #(...) or defparam
bool m_trace : 1; // Trace this variable bool m_trace : 1; // Trace this variable
bool m_isLatched : 1; // Not assigned in all control paths of combo always
VLifetime m_lifetime; // Lifetime VLifetime m_lifetime; // Lifetime
VVarAttrClocker m_attrClocker; VVarAttrClocker m_attrClocker;
MTaskIdSet m_mtaskIds; // MTaskID's that read or write this var MTaskIdSet m_mtaskIds; // MTaskID's that read or write this var
@ -1989,6 +1990,7 @@ private:
m_noSubst = false; m_noSubst = false;
m_overridenParam = false; m_overridenParam = false;
m_trace = false; m_trace = false;
m_isLatched = false;
m_attrClocker = VVarAttrClocker::CLOCKER_UNKNOWN; m_attrClocker = VVarAttrClocker::CLOCKER_UNKNOWN;
} }
@ -2146,6 +2148,7 @@ public:
void overriddenParam(bool flag) { m_overridenParam = flag; } void overriddenParam(bool flag) { m_overridenParam = flag; }
bool overriddenParam() const { return m_overridenParam; } bool overriddenParam() const { return m_overridenParam; }
void trace(bool flag) { m_trace = flag; } void trace(bool flag) { m_trace = flag; }
void isLatched(bool flag) { m_isLatched = flag; }
// METHODS // METHODS
virtual void name(const string& name) override { m_name = name; } virtual void name(const string& name) override { m_name = name; }
virtual void tag(const string& text) override { m_tag = text; } virtual void tag(const string& text) override { m_tag = text; }
@ -2199,6 +2202,7 @@ public:
bool isRand() const { return m_isRand; } bool isRand() const { return m_isRand; }
bool isConst() const { return m_isConst; } bool isConst() const { return m_isConst; }
bool isStatic() const { return m_isStatic; } bool isStatic() const { return m_isStatic; }
bool isLatched() const { return m_isLatched; }
bool isFuncLocal() const { return m_funcLocal; } bool isFuncLocal() const { return m_funcLocal; }
bool isFuncReturn() const { return m_funcReturn; } bool isFuncReturn() const { return m_funcReturn; }
bool isPullup() const { return m_isPullup; } bool isPullup() const { return m_isPullup; }

View File

@ -127,6 +127,7 @@ private:
// STATE // STATE
VDouble0 m_statCaseFast; // Statistic tracking VDouble0 m_statCaseFast; // Statistic tracking
VDouble0 m_statCaseSlow; // Statistic tracking VDouble0 m_statCaseSlow; // Statistic tracking
AstNode* m_alwaysp = nullptr; // Always in which case is located
// Per-CASE // Per-CASE
int m_caseWidth = 0; // Width of valueItems int m_caseWidth = 0; // Width of valueItems
@ -475,12 +476,17 @@ private:
++m_statCaseFast; ++m_statCaseFast;
VL_DO_DANGLING(replaceCaseFast(nodep), nodep); VL_DO_DANGLING(replaceCaseFast(nodep), nodep);
} else { } else {
// If a case statement is whole, presume signals involved aren't forming a latch
if (m_alwaysp) m_alwaysp->fileline()->warnOff(V3ErrorCode::LATCH, true);
++m_statCaseSlow; ++m_statCaseSlow;
VL_DO_DANGLING(replaceCaseComplicated(nodep), nodep); VL_DO_DANGLING(replaceCaseComplicated(nodep), nodep);
} }
} }
//-------------------- //--------------------
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); } virtual void visit(AstNode* nodep) override {
if (VN_IS(nodep, Always)) { m_alwaysp = nodep; }
iterateChildren(nodep);
}
public: public:
// CONSTRUCTORS // CONSTRUCTORS

View File

@ -151,6 +151,7 @@ class EmitXmlFileVisitor final : public AstNVisitor {
} }
if (nodep->attrClockEn()) puts(" clock_enable=\"true\""); if (nodep->attrClockEn()) puts(" clock_enable=\"true\"");
if (nodep->attrIsolateAssign()) puts(" isolate_assignments=\"true\""); if (nodep->attrIsolateAssign()) puts(" isolate_assignments=\"true\"");
if (nodep->isLatched()) puts(" latched=\"true\"");
if (nodep->isSigPublic()) puts(" public=\"true\""); if (nodep->isSigPublic()) puts(" public=\"true\"");
if (nodep->isSigUserRdPublic()) puts(" public_flat_rd=\"true\""); if (nodep->isSigUserRdPublic()) puts(" public_flat_rd=\"true\"");
if (nodep->isSigUserRWPublic()) puts(" public_flat_rw=\"true\""); if (nodep->isSigUserRWPublic()) puts(" public_flat_rw=\"true\"");

View File

@ -94,10 +94,12 @@ public:
INFINITELOOP, // Infinite loop INFINITELOOP, // Infinite loop
INITIALDLY, // Initial delayed statement INITIALDLY, // Initial delayed statement
INSECURE, // Insecure options INSECURE, // Insecure options
LATCH, // Latch detected outside of always_latch block
LITENDIAN, // Little bit endian vector LITENDIAN, // Little bit endian vector
MODDUP, // Duplicate module MODDUP, // Duplicate module
MULTIDRIVEN, // Driven from multiple blocks MULTIDRIVEN, // Driven from multiple blocks
MULTITOP, // Multiple top level modules MULTITOP, // Multiple top level modules
NOLATCH, // No latch detected in always_latch block
PINMISSING, // Cell pin not specified PINMISSING, // Cell pin not specified
PINNOCONNECT, // Cell pin not connected PINNOCONNECT, // Cell pin not connected
PINCONNECTEMPTY,// Cell pin connected by name with empty reference PINCONNECTEMPTY,// Cell pin connected by name with empty reference
@ -162,8 +164,8 @@ public:
"IFDEPTH", "IGNOREDRETURN", "IFDEPTH", "IGNOREDRETURN",
"IMPERFECTSCH", "IMPLICIT", "IMPORTSTAR", "IMPURE", "IMPERFECTSCH", "IMPLICIT", "IMPORTSTAR", "IMPURE",
"INCABSPATH", "INFINITELOOP", "INITIALDLY", "INSECURE", "INCABSPATH", "INFINITELOOP", "INITIALDLY", "INSECURE",
"LITENDIAN", "MODDUP", "LATCH", "LITENDIAN", "MODDUP",
"MULTIDRIVEN", "MULTITOP", "MULTIDRIVEN", "MULTITOP","NOLATCH",
"PINMISSING", "PINNOCONNECT", "PINCONNECTEMPTY", "PKGNODECL", "PROCASSWIRE", "PINMISSING", "PINNOCONNECT", "PINCONNECTEMPTY", "PKGNODECL", "PROCASSWIRE",
"RANDC", "REALCVT", "REDEFMACRO", "RANDC", "REALCVT", "REDEFMACRO",
"SELRANGE", "SHORTREAL", "SPLITVAR", "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET", "SELRANGE", "SHORTREAL", "SPLITVAR", "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET",
@ -199,8 +201,8 @@ public:
return (m_e == ALWCOMBORDER || m_e == BSSPACE || m_e == CASEINCOMPLETE return (m_e == ALWCOMBORDER || 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 == ENDLABEL || m_e == IMPLICIT || m_e == CMPCONST || m_e == COLONPLUS || m_e == ENDLABEL || m_e == IMPLICIT
|| m_e == LITENDIAN || m_e == PINMISSING || m_e == REALCVT || m_e == UNSIGNED || m_e == LATCH || m_e == LITENDIAN || m_e == PINMISSING || m_e == REALCVT
|| m_e == WIDTH); || m_e == UNSIGNED || m_e == WIDTH);
} }
// Warnings that are style only // Warnings that are style only
bool styleError() const { bool styleError() const {

View File

@ -51,6 +51,7 @@
// verilator lint_off MULTIDRIVEN // verilator lint_off MULTIDRIVEN
// verilator lint_off UNSIGNED // verilator lint_off UNSIGNED
// verilator lint_off WIDTH // verilator lint_off WIDTH
// verilator lint_off LATCH
// BEGINNING OF MODULE // BEGINNING OF MODULE
`timescale 1 ps / 1 ps `timescale 1 ps / 1 ps

View File

@ -4,6 +4,8 @@
// any use, without warranty, 2005 by Wilson Snyder. // any use, without warranty, 2005 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0 // SPDX-License-Identifier: CC0-1.0
// verilator lint_off LATCH
module t_case_huge_sub4 (/*AUTOARG*/ module t_case_huge_sub4 (/*AUTOARG*/
// Outputs // Outputs
outq, outq,

View File

@ -112,6 +112,7 @@ module clockgate (clk, sen, ena, gatedclk);
wire gatedclk = clk & ena_b; wire gatedclk = clk & ena_b;
// verilator lint_off COMBDLY // verilator lint_off COMBDLY
// verilator lint_off LATCH
always @(clk or ena or sen) begin always @(clk or ena or sen) begin
if (~clk) begin if (~clk) begin
ena_b <= ena | sen; ena_b <= ena | sen;
@ -120,6 +121,7 @@ module clockgate (clk, sen, ena, gatedclk);
if ((clk^sen)===1'bX) ena_b <= 1'bX; if ((clk^sen)===1'bX) ena_b <= 1'bX;
end end
end end
// verilator lint_on LATCH
// verilator lint_on COMBDLY // verilator lint_on COMBDLY
endmodule endmodule

View File

@ -112,6 +112,7 @@ module clockgate (clk, sen, ena, gatedclk);
wire gatedclk = clk & ena_b; wire gatedclk = clk & ena_b;
// verilator lint_off COMBDLY // verilator lint_off COMBDLY
// verilator lint_off LATCH
always @(clk or ena or sen) begin always @(clk or ena or sen) begin
if (~clk) begin if (~clk) begin
ena_b <= ena | sen; ena_b <= ena | sen;
@ -120,6 +121,7 @@ module clockgate (clk, sen, ena, gatedclk);
if ((clk^sen)===1'bX) ena_b <= 1'bX; if ((clk^sen)===1'bX) ena_b <= 1'bX;
end end
end end
// verilator lint_on LATCH
// verilator lint_on COMBDLY // verilator lint_on COMBDLY
endmodule endmodule

View File

@ -53,6 +53,7 @@ module t (/*AUTOARG*/
end end
// verilator lint_off COMBDLY // verilator lint_off COMBDLY
// verilator lint_off LATCH
always @ (`posstyle clk /*AS*/ or data) begin always @ (`posstyle clk /*AS*/ or data) begin
if (clk) begin if (clk) begin
data_a <= data + 8'd1; data_a <= data + 8'd1;

View File

@ -111,11 +111,13 @@ module llq (clk, d, q);
reg [WIDTH-1:0] qr; reg [WIDTH-1:0] qr;
/* verilator lint_off COMBDLY */ /* verilator lint_off COMBDLY */
/* verilator lint_off LATCH */
always @(clk or d) always @(clk or d)
if (clk == 1'b0) if (clk == 1'b0)
qr <= d; qr <= d;
/* verilator lint_on LATCH */
/* verilator lint_on COMBDLY */ /* verilator lint_on COMBDLY */
assign q = qr; assign q = qr;

View File

@ -17,7 +17,7 @@ compile(
); );
if ($Self->{vlt_all}) { if ($Self->{vlt_all}) {
file_grep("$out_filename", qr/\<var fl="e44" loc=".*?" name="t.f0.clock_gate.clken_latched" dtype_id="1" vartype="logic" origName="clken_latched" clock_enable="true"\/\>/i); file_grep("$out_filename", qr/\<var fl="e44" loc=".*?" name="t.f0.clock_gate.clken_latched" dtype_id="1" vartype="logic" origName="clken_latched" clock_enable="true" latched="true"\/\>/i);
file_grep($Self->{stats}, qr/Optimizations, Gate sigs deduped\s+(\d+)/i, 4); file_grep($Self->{stats}, qr/Optimizations, Gate sigs deduped\s+(\d+)/i, 4);
} }

View File

@ -45,7 +45,7 @@ module clock_gate_latch (gated_clk, clk, clken);
assign gated_clk = clk & clken_latched ; assign gated_clk = clk & clken_latched ;
wire clkb = ~clk; wire clkb = ~clk;
always @(clkb or clken) always_latch @(clkb or clken)
if(clkb) clken_latched = clken; if(clkb) clken_latched = clken;
endmodule endmodule

View File

@ -0,0 +1,17 @@
#!/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);
lint(
);
ok(1);
1;

View File

@ -0,0 +1,16 @@
// DESCRIPTION: Verilator: Verilog Test module for Issue#1609
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2020 by Julien Margetts.
module t (/*AUTOARG*/ a, b, o);
input a;
input b;
output reg o;
// verilator lint_off LATCH
always @(a or b)
if (a)
o <= b;
endmodule

View File

@ -0,0 +1,17 @@
#!/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);
lint(
);
ok(1);
1;

View File

@ -0,0 +1,20 @@
// DESCRIPTION: Verilator: Verilog Test module for Issue#1609
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2020 by Julien Margetts.
module t (/*AUTOARG*/ i, o);
input [1:0] i;
output reg [1:0] o;
// This should not detect a latch as all options are covered
always @* begin
if (i==2'b00) o = 2'b11;
else if (i==2'b01) o = 2'b10;
else if (i==2'b10) o = 2'b01;
else if (i==2'b11) o = 2'b00;
else o = 2'b00; // Without this else a latch is (falsely) detected
end
endmodule

View File

@ -0,0 +1,17 @@
#!/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);
lint(
);
ok(1);
1;

View File

@ -0,0 +1,50 @@
// DESCRIPTION: Verilator: Verilog Test module for Issue#1609
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2020 by Julien Margetts.
module t (/*AUTOARG*/ out, out2, in );
input [9:0] in;
output reg [3:0] out;
output reg [3:0] out2;
// Should be no latch here since the input space is fully covered
always @* begin
casez (in)
10'b0000000000 : out = 4'h0;
10'b?????????1 : out = 4'h0;
10'b????????10 : out = 4'h1;
10'b???????100 : out = 4'h2;
10'b??????1000 : out = 4'h3;
10'b?????10000 : out = 4'h4;
10'b????100000 : out = 4'h5;
10'b???1000000 : out = 4'h6;
10'b??10000000 : out = 4'h7;
10'b?100000000 : out = 4'h8;
10'b1000000000 : out = 4'h9;
endcase
end
// Should detect a latch here since not all paths assign
// BUT we don't because warnOff(LATCH) is set for any always containing a
// complex case statement
always @* begin
casez (in)
10'b0000000000 : out2 = 4'h0;
10'b?????????1 : out2 = 4'h0;
10'b????????10 : out2 = 4'h1;
10'b???????100 : out2 = 4'h2;
10'b??????1000 : out2 = 4'h3;
10'b?????10000 : /* No assignement */ ;
10'b????100000 : out2 = 4'h5;
10'b???1000000 : out2 = 4'h6;
10'b??10000000 : out2 = 4'h7;
10'b?100000000 : out2 = 4'h8;
10'b1000000000 : out2 = 4'h9;
endcase
end
endmodule

View File

@ -1,8 +1,11 @@
%Warning-NOLATCH: t/t_lint_latch_bad.v:17:4: No latches detected in always_latch block
17 | always_latch begin
| ^~~~~~~~~~~~
... Use "/* verilator lint_off NOLATCH */" and lint_on around source to disable this message.
%Warning-COMBDLY: t/t_lint_latch_bad.v:25:10: Delayed assignments (<=) in non-clocked (non flop or latch) block %Warning-COMBDLY: t/t_lint_latch_bad.v:25:10: Delayed assignments (<=) in non-clocked (non flop or latch) block
: ... Suggest blocking assignments (=) : ... Suggest blocking assignments (=)
25 | bc <= a; 25 | bc <= a;
| ^~ | ^~
... Use "/* verilator lint_off COMBDLY */" and lint_on around source to disable this message.
*** See the manual before disabling this, *** See the manual before disabling this,
else you may end up with different sim results. else you may end up with different sim results.
%Error: Exiting due to %Error: Exiting due to

View File

@ -0,0 +1,6 @@
%Warning-LATCH: t/t_lint_latch_bad_2.v:11:4: Latch inferred for signal 'o' (not all control paths of combinational always assign a value)
: ... Suggest use of always_latch for intentional latches
11 | always @(a or b)
| ^~~~~~
... Use "/* verilator lint_off LATCH */" and lint_on around source to disable this message.
%Error: Exiting due to

View File

@ -0,0 +1,18 @@
#!/usr/bin/perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2020 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.
scenarios(linter => 1);
lint(
fails => 1,
expect_filename => $Self->{golden_filename},
);
ok(1);
1;

View File

@ -0,0 +1,15 @@
// DESCRIPTION: Verilator: Verilog Test module for Issue#1609
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2020 by Julien Margetts.
module t (/*AUTOARG*/ a, b, o);
input a;
input b;
output reg o;
always @(a or b)
if (a)
o <= b;
endmodule

View File

@ -0,0 +1,12 @@
%Warning-LATCH: t/t_lint_latch_bad_3.v:18:1: Latch inferred for signal 'o5' (not all control paths of combinational always assign a value)
: ... Suggest use of always_latch for intentional latches
18 | always @(reset or en or a or b)
| ^~~~~~
... Use "/* verilator lint_off LATCH */" and lint_on around source to disable this message.
%Warning-COMBDLY: t/t_lint_latch_bad_3.v:70:12: Delayed assignments (<=) in non-clocked (non flop or latch) block
: ... Suggest blocking assignments (=)
70 | o4 <= 1'b0;
| ^~
*** See the manual before disabling this,
else you may end up with different sim results.
%Error: Exiting due to

View File

@ -0,0 +1,18 @@
#!/usr/bin/perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2020 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.
scenarios(linter => 1);
lint(
fails => 1,
expect_filename => $Self->{golden_filename},
);
ok(1);
1;

View File

@ -0,0 +1,74 @@
// DESCRIPTION: Verilator: Verilog Test module for Issue#1609
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2020 by Julien Margetts.
module t (/*AUTOARG*/ reset, a, b, c, en, o1, o2, o3, o4, o5);
input reset;
input a;
input b;
input c;
input en;
output reg o1; // Always assigned
output reg o2; // "
output reg o3; // "
output reg o4; // "
output reg o5; // Latch
always @(reset or en or a or b)
if (reset)
begin
o1 = 1'b0;
o2 = 1'b0;
o3 = 1'b0;
o4 = 1'b0;
o5 <= 1'b0; // Do NOT expect Warning-COMBDLY
end
else
begin
o1 = 1'b1;
if (en)
begin
o2 = 1'b0;
if (a)
begin
o3 = a;
o5 <= 1'b1; // Do NOT expect Warning-COMBDLY
end
else
begin
o3 = ~a;
o5 <= a; // Do NOT expect Warning-COMBDLY
end
// o3 is not assigned in either path of this if/else
// but no latch because always assigned above
if (c)
begin
o2 = a ^ b;
o4 = 1'b1;
end
else
o4 = ~a ^ b;
o2 = 1'b1;
end
else
begin
o2 = 1'b1;
if (b)
begin
o3 = ~a | b;
o5 <= ~b; // Do NOT expect Warning-COMBDLY
end
else
begin
o3 = a & ~b;
// No assignment to o5, expect Warning-LATCH
end
o4 <= 1'b0; // expect Warning-COMBDLY
end
end
endmodule

View File

@ -0,0 +1,5 @@
%Warning-NOLATCH: t/t_lint_nolatch_bad.v:11:4: No latches detected in always_latch block
11 | always_latch @(a or b)
| ^~~~~~~~~~~~
... Use "/* verilator lint_off NOLATCH */" and lint_on around source to disable this message.
%Error: Exiting due to

View File

@ -0,0 +1,18 @@
#!/usr/bin/perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2020 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.
scenarios(linter => 1);
lint(
fails => 1,
expect_filename => $Self->{golden_filename}
);
ok(1);
1;

View File

@ -0,0 +1,17 @@
// DESCRIPTION: Verilator: Verilog Test module for Issue#1609
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2020 by Julien Margetts.
module t (/*AUTOARG*/ a, b, o);
input a;
input b;
output reg o;
always_latch @(a or b)
if (a)
o <= b;
else
o <= ~b;
endmodule

View File

@ -11,10 +11,12 @@ module t
integer q; integer q;
// verilator lint_off LATCH
always @(*) always @(*)
if (rst) if (rst)
assign q = 0; assign q = 0;
else else
deassign q; deassign q;
// verilator lint_on LATCH
endmodule endmodule

View File

@ -82,6 +82,7 @@ module prover (
reg signed [WIDTH-1:0] bs; reg signed [WIDTH-1:0] bs;
wire [WIDTH-1:0] b = bs; wire [WIDTH-1:0] b = bs;
// verilator lint_off LATCH
always @* begin always @* begin
casez (index_a) casez (index_a)
3'd0: as = {(WIDTH){1'd0}}; // 0 3'd0: as = {(WIDTH){1'd0}}; // 0
@ -100,6 +101,7 @@ module prover (
default: $stop; default: $stop;
endcase endcase
end end
// verilator lint_on LATCH
reg [7:0] results[4:0][4:0]; reg [7:0] results[4:0][4:0];

View File

@ -11,6 +11,7 @@ module t (/*AUTOARG*/
input clk; input clk;
// verilator lint_off COMBDLY // verilator lint_off COMBDLY
// verilator lint_off LATCH
// verilator lint_off UNOPT // verilator lint_off UNOPT
// verilator lint_off UNOPTFLAT // verilator lint_off UNOPTFLAT
// verilator lint_off BLKANDNBLK // verilator lint_off BLKANDNBLK

View File

@ -1,23 +1,23 @@
%Warning-IMPERFECTSCH: t/t_order_clkinst.v:18:16: Imperfect scheduling of variable: 't.c1_start' %Warning-IMPERFECTSCH: t/t_order_clkinst.v:19:16: Imperfect scheduling of variable: 't.c1_start'
18 | reg c1_start; initial c1_start = 0; 19 | reg c1_start; initial c1_start = 0;
| ^~~~~~~~ | ^~~~~~~~
... Use "/* verilator lint_off IMPERFECTSCH */" and lint_on around source to disable this message. ... Use "/* verilator lint_off IMPERFECTSCH */" and lint_on around source to disable this message.
%Warning-IMPERFECTSCH: t/t_order_clkinst.v:19:16: Imperfect scheduling of variable: 't.c1_count' %Warning-IMPERFECTSCH: t/t_order_clkinst.v:20:16: Imperfect scheduling of variable: 't.c1_count'
19 | wire [31:0] c1_count; 20 | wire [31:0] c1_count;
| ^~~~~~~~ | ^~~~~~~~
%Warning-IMPERFECTSCH: t/t_order_clkinst.v:23:16: Imperfect scheduling of variable: 't.s2_count' %Warning-IMPERFECTSCH: t/t_order_clkinst.v:24:16: Imperfect scheduling of variable: 't.s2_count'
23 | wire [31:0] s2_count; 24 | wire [31:0] s2_count;
| ^~~~~~~~ | ^~~~~~~~
%Warning-IMPERFECTSCH: t/t_order_clkinst.v:27:16: Imperfect scheduling of variable: 't.c3_count' %Warning-IMPERFECTSCH: t/t_order_clkinst.v:28:16: Imperfect scheduling of variable: 't.c3_count'
27 | wire [31:0] c3_count; 28 | wire [31:0] c3_count;
| ^~~~~~~~ | ^~~~~~~~
%Warning-IMPERFECTSCH: t/t_order_clkinst.v:71:28: Imperfect scheduling of variable: 't.c1.runner' %Warning-IMPERFECTSCH: t/t_order_clkinst.v:72:28: Imperfect scheduling of variable: 't.c1.runner'
71 | reg [31:0] runnerm1, runner; initial runner = 0; 72 | reg [31:0] runnerm1, runner; initial runner = 0;
| ^~~~~~ | ^~~~~~
%Warning-IMPERFECTSCH: t/t_order_clkinst.v:100:28: Imperfect scheduling of variable: 't.s2.runner' %Warning-IMPERFECTSCH: t/t_order_clkinst.v:101:28: Imperfect scheduling of variable: 't.s2.runner'
100 | reg [31:0] runnerm1, runner; initial runner = 0; 101 | reg [31:0] runnerm1, runner; initial runner = 0;
| ^~~~~~ | ^~~~~~
%Warning-IMPERFECTSCH: t/t_order_clkinst.v:71:28: Imperfect scheduling of variable: 't.c3.runner' %Warning-IMPERFECTSCH: t/t_order_clkinst.v:72:28: Imperfect scheduling of variable: 't.c3.runner'
71 | reg [31:0] runnerm1, runner; initial runner = 0; 72 | reg [31:0] runnerm1, runner; initial runner = 0;
| ^~~~~~ | ^~~~~~
%Error: Exiting due to %Error: Exiting due to

View File

@ -12,6 +12,7 @@ module t (/*AUTOARG*/
// verilator lint_off BLKANDNBLK // verilator lint_off BLKANDNBLK
// verilator lint_off COMBDLY // verilator lint_off COMBDLY
// verilator lint_off LATCH
// verilator lint_off UNOPT // verilator lint_off UNOPT
// verilator lint_off UNOPTFLAT // verilator lint_off UNOPTFLAT
// verilator lint_off MULTIDRIVEN // verilator lint_off MULTIDRIVEN

View File

@ -11,6 +11,7 @@ module t (/*AUTOARG*/
input clk; input clk;
integer cyc; initial cyc=1; integer cyc; initial cyc=1;
// verilator lint_off LATCH
// verilator lint_off UNOPT // verilator lint_off UNOPT
// verilator lint_off UNOPTFLAT // verilator lint_off UNOPTFLAT
reg [31:0] runner; initial runner = 5; reg [31:0] runner; initial runner = 5;

View File

@ -11,6 +11,7 @@ module t (/*AUTOARG*/
input clk; input clk;
integer cyc; initial cyc=1; integer cyc; initial cyc=1;
// verilator lint_off LATCH
// verilator lint_off UNOPT // verilator lint_off UNOPT
// verilator lint_off UNOPTFLAT // verilator lint_off UNOPTFLAT
// verilator lint_off MULTIDRIVEN // verilator lint_off MULTIDRIVEN

View File

@ -155,7 +155,9 @@ module t #(parameter GATED_CLK = 0) (/*AUTOARG*/
if (GATED_CLK != 0) begin: yes_gated_clock if (GATED_CLK != 0) begin: yes_gated_clock
logic clk_en_latch /*verilator clock_enable*/; logic clk_en_latch /*verilator clock_enable*/;
/* verilator lint_off COMBDLY */ /* verilator lint_off COMBDLY */
/* verilator lint_off LATCH */
always_comb if (clk == '0) clk_en_latch <= clk_en; always_comb if (clk == '0) clk_en_latch <= clk_en;
/* verilator lint_on LATCH */
/* verilator lint_on COMBDLY */ /* verilator lint_on COMBDLY */
assign possibly_gated_clk = clk & clk_en_latch; assign possibly_gated_clk = clk & clk_en_latch;
end else begin: no_gated_clock end else begin: no_gated_clock

View File

@ -48,7 +48,9 @@ module secret #(parameter GATED_CLK = 0)
if (GATED_CLK != 0) begin: yes_gated_clock if (GATED_CLK != 0) begin: yes_gated_clock
logic clk_en_latch /*verilator clock_enable*/; logic clk_en_latch /*verilator clock_enable*/;
/* verilator lint_off COMBDLY */ /* verilator lint_off COMBDLY */
/* verilator lint_off LATCH */
always_comb if (clk == '0) clk_en_latch <= clk_en; always_comb if (clk == '0) clk_en_latch <= clk_en;
/* verilator lint_on LATCH */
/* verilator lint_on COMBDLY */ /* verilator lint_on COMBDLY */
assign the_clk = clk & clk_en_latch; assign the_clk = clk & clk_en_latch;
end else begin: no_gated_clock end else begin: no_gated_clock