Support FSM detection in primitive wrappers (#7607)
This commit is contained in:
parent
6a74112f0b
commit
f282335600
|
|
@ -100,6 +100,27 @@ The grammar of control commands is as follows:
|
|||
|
||||
Same as :option:`/*verilator&32;forceable*/` metacomment.
|
||||
|
||||
.. option:: fsm_register_wrapper -module "<modulename>" -d "<port>" -q "<port>" -clock "<port>" [-reset "<port>"] [-reset_value "<param>"]
|
||||
|
||||
Declares that the specified module is a transparent FSM state-register
|
||||
wrapper for FSM coverage extraction. One declaration applies to all
|
||||
instances of that module.
|
||||
|
||||
The ``-d`` option names the wrapper input port carrying next-state data.
|
||||
The ``-q`` option names the wrapper output port carrying registered state.
|
||||
The ``-clock`` option names the wrapper clock port.
|
||||
|
||||
Verilator does not infer wrapper semantics from module or port naming
|
||||
conventions. A wrapper module must be declared with this option before
|
||||
Verilator will use instances of it to extract FSM state and transition
|
||||
coverage.
|
||||
|
||||
The ``-reset`` and ``-reset_value`` options are optional. Reset arcs are
|
||||
emitted only when both the reset polarity can be inferred from the
|
||||
wrapper's event control and ``-reset_value`` names a static parameter value.
|
||||
If reset metadata is incomplete, Verilator may still emit FSM state and
|
||||
transition coverage without reset arcs.
|
||||
|
||||
.. option:: full_case -file "<filename>" -lines <lineno>
|
||||
|
||||
Same as ``//synthesis full_case``. When these synthesis directives
|
||||
|
|
|
|||
|
|
@ -262,6 +262,35 @@ the extracted coverage model:
|
|||
- ``/*verilator fsm_arc_include_cond*/`` keeps conditional branch
|
||||
arcs that would otherwise be skipped by the conservative extractor.
|
||||
|
||||
State registers may also be wrapped by a transparent instance, for
|
||||
example a project flop wrapper or primitive. Such wrappers must be
|
||||
described explicitly with a VLT command file action before Verilator will
|
||||
use their data, state, clock, or reset connections for FSM extraction:
|
||||
|
||||
.. code-block:: sv
|
||||
|
||||
`verilator_config
|
||||
fsm_register_wrapper -module "my_fsm_flop" -d "state_i" -q "state_o" -clock "clk_i"
|
||||
|
||||
The same command may be placed in a separate ``.vlt`` file:
|
||||
|
||||
.. code-block:: sv
|
||||
|
||||
fsm_register_wrapper -module "my_fsm_flop" -d "state_i" -q "state_o" -clock "clk_i"
|
||||
|
||||
Optional reset metadata may also be supplied:
|
||||
|
||||
.. code-block:: sv
|
||||
|
||||
fsm_register_wrapper -module "my_fsm_flop" -d "state_i" -q "state_o" -clock "clk_i" \
|
||||
-reset "rst_ni" -reset_value "ResetValue"
|
||||
|
||||
Reset arcs are emitted only when the configured reset port has an
|
||||
inferable edge in the wrapper and the configured reset value parameter is
|
||||
statically resolvable. If reset metadata is incomplete, Verilator warns
|
||||
and may still emit FSM state and transition coverage, but reset arcs are
|
||||
omitted.
|
||||
|
||||
Reset transitions are included in the collected data either way. By
|
||||
default, :command:`verilator_coverage` summarizes reset-only arcs rather
|
||||
than printing them alongside non-reset arcs. Use
|
||||
|
|
|
|||
|
|
@ -1964,6 +1964,7 @@ class AstVar final : public AstNode {
|
|||
bool m_attrSFormat : 1; // User sformat attribute
|
||||
bool m_attrSplitVar : 1; // declared with split_var metacomment
|
||||
bool m_attrFsmState : 1; // declared with fsm_state metacomment
|
||||
bool m_attrFsmRegisterWrapper : 1; // connected to an fsm_register_wrapper instance
|
||||
bool m_attrFsmResetArc : 1; // declared with fsm_reset_arc metacomment
|
||||
bool m_attrFsmArcInclCond : 1; // declared with fsm_arc_include_cond metacomment
|
||||
bool m_fileDescr : 1; // File descriptor
|
||||
|
|
@ -2025,6 +2026,7 @@ class AstVar final : public AstNode {
|
|||
m_attrSFormat = false;
|
||||
m_attrSplitVar = false;
|
||||
m_attrFsmState = false;
|
||||
m_attrFsmRegisterWrapper = false;
|
||||
m_attrFsmResetArc = false;
|
||||
m_attrFsmArcInclCond = false;
|
||||
m_fileDescr = false;
|
||||
|
|
@ -2172,6 +2174,7 @@ public:
|
|||
void attrSFormat(bool flag) { m_attrSFormat = flag; }
|
||||
void attrSplitVar(bool flag) { m_attrSplitVar = flag; }
|
||||
void attrFsmState(bool flag) { m_attrFsmState = flag; }
|
||||
void attrFsmRegisterWrapper(bool flag) { m_attrFsmRegisterWrapper = flag; }
|
||||
void attrFsmResetArc(bool flag) { m_attrFsmResetArc = flag; }
|
||||
void attrFsmArcInclCond(bool flag) { m_attrFsmArcInclCond = flag; }
|
||||
void rand(const VRandAttr flag) { m_rand = flag; }
|
||||
|
|
@ -2338,6 +2341,7 @@ public:
|
|||
bool attrSFormat() const { return m_attrSFormat; }
|
||||
bool attrSplitVar() const { return m_attrSplitVar; }
|
||||
bool attrFsmState() const { return m_attrFsmState; }
|
||||
bool attrFsmRegisterWrapper() const { return m_attrFsmRegisterWrapper; }
|
||||
bool attrFsmResetArc() const { return m_attrFsmResetArc; }
|
||||
bool attrFsmArcInclCond() const { return m_attrFsmArcInclCond; }
|
||||
bool attrIsolateAssign() const { return m_attrIsolateAssign; }
|
||||
|
|
|
|||
|
|
@ -227,8 +227,10 @@ class V3ControlModule final {
|
|||
V3ControlVarResolver m_params; // Parameters in module
|
||||
V3ControlVarResolver m_ports; // Ports in module
|
||||
V3ControlVarResolver m_vars; // Variables in module
|
||||
V3Control::FsmRegisterWrapper m_fsmRegisterWrapper; // FSM wrapper descriptor
|
||||
std::unordered_set<std::string> m_coverageOffBlocks; // List of block names for coverage_off
|
||||
std::set<VPragmaType> m_modPragmas; // List of Pragmas for modules
|
||||
bool m_hasFsmRegisterWrapper = false; // Whether descriptor has been set
|
||||
bool m_inline = false; // Whether to force the inline
|
||||
bool m_inlineValue = false; // The inline value (on/off)
|
||||
|
||||
|
|
@ -240,6 +242,10 @@ public:
|
|||
m_params.update(m.m_params);
|
||||
m_ports.update(m.m_ports);
|
||||
m_vars.update(m.m_vars);
|
||||
if (m.m_hasFsmRegisterWrapper) {
|
||||
m_fsmRegisterWrapper = m.m_fsmRegisterWrapper;
|
||||
m_hasFsmRegisterWrapper = true;
|
||||
}
|
||||
for (const string& i : m.m_coverageOffBlocks) m_coverageOffBlocks.insert(i);
|
||||
if (!m_inline) {
|
||||
m_inline = m.m_inline;
|
||||
|
|
@ -261,6 +267,18 @@ public:
|
|||
m_inline = true;
|
||||
m_inlineValue = set;
|
||||
}
|
||||
void setFsmRegisterWrapper(FileLine* fl, const V3Control::FsmRegisterWrapper& desc) {
|
||||
if (m_hasFsmRegisterWrapper) {
|
||||
fl->v3warn(BADVLTPRAGMA, "Duplicate fsm_register_wrapper descriptor for module "
|
||||
<< AstNode::prettyNameQ(desc.moduleName)
|
||||
<< "; replacing previous descriptor");
|
||||
}
|
||||
m_fsmRegisterWrapper = desc;
|
||||
m_hasFsmRegisterWrapper = true;
|
||||
}
|
||||
const V3Control::FsmRegisterWrapper* fsmRegisterWrapperp() const {
|
||||
return m_hasFsmRegisterWrapper ? &m_fsmRegisterWrapper : nullptr;
|
||||
}
|
||||
void addModulePragma(VPragmaType pragma) { m_modPragmas.insert(pragma); }
|
||||
|
||||
void apply(AstNodeModule* modp) {
|
||||
|
|
@ -877,6 +895,23 @@ void V3Control::addHierWorkers(FileLine* fl, const string& model, int workers) {
|
|||
V3ControlResolver::s().addHierWorkers(fl, model, workers);
|
||||
}
|
||||
|
||||
void V3Control::addFsmRegisterWrapper(FileLine* fl, const string& module, const string& d,
|
||||
const string& q, const string& clock,
|
||||
const string& reset, const string& resetValue) {
|
||||
string missing;
|
||||
if (module.empty()) missing += "-module";
|
||||
if (d.empty()) missing = VString::dot(missing, ", ", "-d");
|
||||
if (q.empty()) missing = VString::dot(missing, ", ", "-q");
|
||||
if (clock.empty()) missing = VString::dot(missing, ", ", "-clock");
|
||||
if (!missing.empty()) {
|
||||
fl->v3error("fsm_register_wrapper missing " << missing);
|
||||
return;
|
||||
}
|
||||
|
||||
const FsmRegisterWrapper desc{module, d, q, clock, reset, resetValue};
|
||||
V3ControlResolver::s().modules().at(module).setFsmRegisterWrapper(fl, desc);
|
||||
}
|
||||
|
||||
void V3Control::addIgnore(V3ErrorCode code, bool on, const string& filename, int min, int max) {
|
||||
UINFO(9, "addIgnore " << code << " " << min << "-" << max << " fn=" << filename);
|
||||
if (filename == "*") { // For "lint_off/lint_on [--rule x]"
|
||||
|
|
@ -1066,6 +1101,10 @@ int V3Control::getHierWorkers(const string& model) {
|
|||
FileLine* V3Control::getHierWorkersFileLine(const string& model) {
|
||||
return V3ControlResolver::s().getHierWorkersFileLine(model);
|
||||
}
|
||||
const V3Control::FsmRegisterWrapper* V3Control::getFsmRegisterWrapper(const string& module) {
|
||||
V3ControlModule* const modp = V3ControlResolver::s().modules().resolve(module);
|
||||
return modp ? modp->fsmRegisterWrapperp() : nullptr;
|
||||
}
|
||||
uint64_t V3Control::getProfileData(const string& hierDpi) {
|
||||
return V3ControlResolver::s().getProfileData(hierDpi);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,15 @@
|
|||
|
||||
class V3Control final {
|
||||
public:
|
||||
struct FsmRegisterWrapper final {
|
||||
string moduleName;
|
||||
string d;
|
||||
string q;
|
||||
string clock;
|
||||
string reset;
|
||||
string resetValue;
|
||||
};
|
||||
|
||||
enum class VarSpecKind {
|
||||
PARAM, // Select only matching parameters
|
||||
PORT, // Select only matching ports
|
||||
|
|
@ -40,6 +49,9 @@ public:
|
|||
static void addCoverageBlockOff(const string& file, int lineno);
|
||||
static void addCoverageBlockOff(const string& module, const string& blockname);
|
||||
static void addHierWorkers(FileLine* fl, const string& model, int workers);
|
||||
static void addFsmRegisterWrapper(FileLine* fl, const string& module, const string& d,
|
||||
const string& q, const string& clock,
|
||||
const string& reset, const string& resetValue);
|
||||
static void addIgnore(V3ErrorCode code, bool on, const string& filename, int min, int max);
|
||||
static void addIgnoreMatch(V3ErrorCode code, const string& filename, const string& contents,
|
||||
const string& match);
|
||||
|
|
@ -64,6 +76,7 @@ public:
|
|||
|
||||
static int getHierWorkers(const string& model);
|
||||
static FileLine* getHierWorkersFileLine(const string& model);
|
||||
static const FsmRegisterWrapper* getFsmRegisterWrapper(const string& module);
|
||||
static uint64_t getProfileData(const string& hierDpi);
|
||||
static uint64_t getProfileData(const string& model, const string& key);
|
||||
static FileLine* getProfileDataFileLine();
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
#include "V3FsmDetect.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Control.h"
|
||||
#include "V3Graph.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
|
@ -108,22 +109,73 @@ struct FsmSenDesc final {
|
|||
struct FsmResetCondDesc final {
|
||||
// Reset signal used by the FSM in the saved scoped AST.
|
||||
AstVarScope* varScopep = nullptr;
|
||||
bool activeLow = false;
|
||||
};
|
||||
|
||||
class FsmResetArcDesc final {
|
||||
FsmStateValue m_toValue; // Encoded reset target state.
|
||||
AstNodeAssign* m_nodep = nullptr; // Source assignment for warnings and emitted metadata
|
||||
AstNode* m_nodep = nullptr; // Source node for warnings and emitted metadata.
|
||||
AstNodeExpr* m_valuep = nullptr; // Expression that provided the reset value.
|
||||
|
||||
public:
|
||||
FsmResetArcDesc() = default;
|
||||
FsmResetArcDesc(FsmStateValue toValue, AstNodeAssign* nodep)
|
||||
: m_toValue{toValue}
|
||||
, m_nodep{nodep} {}
|
||||
, m_nodep{nodep}
|
||||
, m_valuep{nodep->rhsp()} {}
|
||||
FsmResetArcDesc(FsmStateValue toValue, AstNode* nodep, AstNodeExpr* valuep)
|
||||
: m_toValue{toValue}
|
||||
, m_nodep{nodep}
|
||||
, m_valuep{valuep} {}
|
||||
|
||||
FsmStateValue toValue() const { return m_toValue; }
|
||||
AstNodeAssign* nodep() const { return m_nodep; }
|
||||
AstNode* nodep() const { return m_nodep; }
|
||||
AstNodeExpr* valuep() const { return m_valuep; }
|
||||
};
|
||||
|
||||
struct FsmWrapperRoles final {
|
||||
string dPort;
|
||||
string qPort;
|
||||
string clkPort;
|
||||
string rstPort;
|
||||
string rstValParam;
|
||||
bool hasRstActiveLow = false;
|
||||
bool rstActiveLow = false;
|
||||
};
|
||||
|
||||
static bool fsmWrapperResetPolarityFromWrapperAst(AstCell* cellp, const string& portName,
|
||||
bool& activeLow) {
|
||||
bool matched = false;
|
||||
cellp->modp()->foreach([&](AstSenItem* itemp) {
|
||||
AstNodeVarRef* const vrefp = itemp->varrefp();
|
||||
if (!vrefp) return;
|
||||
if (vrefp->varp()->name() != portName) return;
|
||||
activeLow = itemp->edgeType() == VEdgeType::ET_NEGEDGE;
|
||||
matched = true;
|
||||
});
|
||||
return matched;
|
||||
}
|
||||
|
||||
static const V3Control::FsmRegisterWrapper* fsmRegisterWrapperDesc(AstCell* cellp) {
|
||||
AstNodeModule* const modp = cellp->modp();
|
||||
const string origName = modp->origName();
|
||||
if (const V3Control::FsmRegisterWrapper* const descp
|
||||
= V3Control::getFsmRegisterWrapper(origName)) {
|
||||
return descp;
|
||||
}
|
||||
return V3Control::getFsmRegisterWrapper(modp->prettyDehashOrigOrName());
|
||||
}
|
||||
|
||||
static FsmWrapperRoles rolesFromDesc(const V3Control::FsmRegisterWrapper& desc) {
|
||||
FsmWrapperRoles roles;
|
||||
roles.dPort = desc.d;
|
||||
roles.qPort = desc.q;
|
||||
roles.clkPort = desc.clock;
|
||||
roles.rstPort = desc.reset;
|
||||
roles.rstValParam = desc.resetValue;
|
||||
return roles;
|
||||
}
|
||||
|
||||
class FsmRegisterCandidate final {
|
||||
AstScope* m_scopep = nullptr; // Owning scope for the paired FSM.
|
||||
AstAlways* m_alwaysp = nullptr; // Register process that commits the state.
|
||||
|
|
@ -135,6 +187,7 @@ class FsmRegisterCandidate final {
|
|||
bool m_hasResetCond = false; // Whether the FSM had a modeled reset predicate.
|
||||
bool m_resetInclude = false; // Whether reset arcs count toward summary totals.
|
||||
bool m_inclCond = false; // Whether conditional/default arcs are kept explicitly.
|
||||
FileLine* m_flp = nullptr; // Representative source location.
|
||||
|
||||
public:
|
||||
AstScope* scopep() const { return m_scopep; }
|
||||
|
|
@ -157,6 +210,8 @@ public:
|
|||
void resetInclude(bool flag) { m_resetInclude = flag; }
|
||||
bool inclCond() const { return m_inclCond; }
|
||||
void inclCond(bool flag) { m_inclCond = flag; }
|
||||
FileLine* fileline() const { return m_flp; }
|
||||
void fileline(FileLine* flp) { m_flp = flp; }
|
||||
};
|
||||
|
||||
class FsmComboAlways final {
|
||||
|
|
@ -386,6 +441,8 @@ struct FsmIfChainCandidate final {
|
|||
// Aliases are accepted only when they are equivalent to spelling the state
|
||||
// comparison inline; this avoids inferring FSM semantics from arbitrary logic.
|
||||
using FsmAliasMap = std::unordered_map<const AstVarScope*, FsmStateComparison>;
|
||||
using FsmCellPortMap = std::unordered_map<string, AstVarScope*>;
|
||||
using FsmCellPortAliasMap = std::unordered_map<const AstCell*, FsmCellPortMap>;
|
||||
|
||||
struct StateConstLabel final {
|
||||
string text;
|
||||
|
|
@ -439,11 +496,21 @@ class FsmDetectVisitor final : public VNVisitor {
|
|||
std::vector<FsmComboAlways> m_oneBlockAlwayss;
|
||||
std::vector<FsmComboAlways> m_comboAlwayss;
|
||||
std::vector<FsmComboAlways> m_nonComboAlwayss;
|
||||
// Wrapper FSM detection has a second path for designs compiled without
|
||||
// inlining. In that shape the state register stays behind an AstCell, so we
|
||||
// remember candidate cells and resolve them only after the surrounding
|
||||
// transition logic and post-link port wiring have both been seen.
|
||||
std::vector<std::pair<AstScope*, AstCell*>> m_wrapperCells;
|
||||
std::unordered_map<const AstVarScope*, FsmCaseCandidate> m_comboPaired;
|
||||
// Continuous aliases are order-independent, while procedural aliases must
|
||||
// remain source-order scoped to avoid using assignments not yet executed.
|
||||
FsmAliasMap m_stateAliases;
|
||||
std::unordered_set<const AstVarScope*> m_ambiguousStateAliases;
|
||||
// A surviving wrapper's semantic d/q relationship is split across the
|
||||
// parent scope and the child module scope. This table is the narrow bridge
|
||||
// between those scopes: only transparent port aliases are recorded, so the
|
||||
// detector does not become a general cross-module dataflow engine.
|
||||
FsmCellPortAliasMap m_cellPortAliases;
|
||||
|
||||
// METHODS
|
||||
// Enum-backed FSMs may be wrapped in refs/typedefs; normalize to the
|
||||
|
|
@ -460,6 +527,93 @@ class FsmDetectVisitor final : public VNVisitor {
|
|||
+ firstCand.warnNodep->warnContextSecondary();
|
||||
}
|
||||
|
||||
static bool rejectFsmWrapperCell(AstCell* cellp, const string& reason) {
|
||||
cellp->v3warn(COVERIGN, "Ignoring unsupported: " + reason);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool simpleParamStateValue(AstCell* cellp, const string& name, FsmStateValue& value,
|
||||
AstNodeExpr*& valuepr) {
|
||||
// Cell-path reset recovery must behave like the inlined path when the
|
||||
// instance relies on a parameter default. Looking into the linked module
|
||||
// default preserves that equivalence while keeping the cell detector's
|
||||
// contract narrow: only static, known reset encodings become reset arcs.
|
||||
valuepr = nullptr;
|
||||
for (AstNode* stmtp = cellp->modp()->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
|
||||
AstVar* const varp = VN_CAST(stmtp, Var);
|
||||
if (!varp || !varp->isParam() || varp->name() != name) continue;
|
||||
valuepr = VN_AS(varp->valuep(), NodeExpr);
|
||||
return constValueStatus(valuepr, value) == ConstValueStatus::OK;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool childPortInScope(AstVarScope* vscp, AstScope* parentScopep, AstCell*& cellpr) {
|
||||
if (!vscp->varp()->isIO()) return false;
|
||||
AstScope* const scopep = vscp->scopep();
|
||||
UASSERT_OBJ(scopep, vscp, "VarScope without scope");
|
||||
if (scopep->aboveScopep() != parentScopep) return false;
|
||||
UASSERT_OBJ(scopep->aboveCellp(), vscp,
|
||||
"Child port scope should retain the instance that created it");
|
||||
cellpr = scopep->aboveCellp();
|
||||
return true;
|
||||
}
|
||||
|
||||
static AstVarScope* simpleAssignVarScope(AstNodeExpr* exprp) {
|
||||
AstVarRef* const vrefp = VN_CAST(exprp, VarRef);
|
||||
return vrefp ? vrefp->varScopep() : nullptr;
|
||||
}
|
||||
|
||||
void addWrapperCell(AstScope* scopep, AstCell* cellp) {
|
||||
m_cellPortAliases.emplace(cellp, FsmCellPortMap{});
|
||||
const std::pair<AstScope*, AstCell*> item{scopep, cellp};
|
||||
if (std::find(m_wrapperCells.cbegin(), m_wrapperCells.cend(), item)
|
||||
!= m_wrapperCells.cend()) {
|
||||
return;
|
||||
}
|
||||
m_wrapperCells.emplace_back(item);
|
||||
}
|
||||
|
||||
void collectCellPortAlias(AstAssignW* nodep) {
|
||||
UASSERT_OBJ(m_scopep, nodep, "Cell port alias collection requires a scoped assignment");
|
||||
AstVarScope* const lhsVscp = simpleAssignVarScope(nodep->lhsp());
|
||||
AstVarScope* const rhsVscp = simpleAssignVarScope(nodep->rhsp());
|
||||
if (!lhsVscp || !rhsVscp) return;
|
||||
AstCell* cellp = nullptr;
|
||||
// The cell path is intentionally a transparent-wrapper recognizer. A
|
||||
// direct parent<->child variable assignment preserves the register's
|
||||
// identity across the hierarchy boundary; any expression, slice, or
|
||||
// transform is outside this phase's contract and therefore not recorded.
|
||||
if (childPortInScope(lhsVscp, m_scopep, cellp)) {
|
||||
if (!fsmRegisterWrapperDesc(cellp)) return;
|
||||
UASSERT_OBJ(lhsVscp->varp()->isInput(), nodep,
|
||||
"Child-side port alias lhs should be an input");
|
||||
UASSERT_OBJ(rhsVscp->scopep() == m_scopep, nodep,
|
||||
"Child input port alias should connect from the parent scope");
|
||||
m_cellPortAliases[cellp][lhsVscp->varp()->name()] = rhsVscp;
|
||||
addWrapperCell(m_scopep, cellp);
|
||||
} else if (childPortInScope(rhsVscp, m_scopep, cellp)) {
|
||||
if (!fsmRegisterWrapperDesc(cellp)) return;
|
||||
UASSERT_OBJ(rhsVscp->varp()->isWritable(), nodep,
|
||||
"Child-side port alias rhs should be writable");
|
||||
UASSERT_OBJ(lhsVscp->scopep() == m_scopep, nodep,
|
||||
"Child output port alias should connect into the parent scope");
|
||||
m_cellPortAliases[cellp][rhsVscp->varp()->name()] = lhsVscp;
|
||||
addWrapperCell(m_scopep, cellp);
|
||||
}
|
||||
}
|
||||
|
||||
AstVarScope* roleVarScope(AstCell* cellp, const string& portName) const {
|
||||
// At this point explicit AstPin expressions have been lowered away, so
|
||||
// role resolution crosses the wrapper boundary only through the
|
||||
// transparent alias table above. This keeps wrapper support aligned with
|
||||
// direct-register detection instead of growing into interprocedural FSM
|
||||
// inference.
|
||||
const FsmCellPortMap& ports = m_cellPortAliases.at(cellp);
|
||||
const FsmCellPortMap::const_iterator portIt = ports.find(portName);
|
||||
return portIt == ports.end() ? nullptr : portIt->second;
|
||||
}
|
||||
|
||||
class RegisterAlwaysAnalyzer final {
|
||||
AstScope* const m_scopep;
|
||||
|
||||
|
|
@ -546,6 +700,85 @@ class FsmDetectVisitor final : public VNVisitor {
|
|||
}
|
||||
};
|
||||
|
||||
bool matchFsmWrapperCell(AstScope* scopep, AstCell* cellp, FsmRegisterCandidate& cand) const {
|
||||
FsmWrapperRoles roles = rolesFromDesc(*fsmRegisterWrapperDesc(cellp));
|
||||
|
||||
AstVarScope* const nextVscp = roleVarScope(cellp, roles.dPort);
|
||||
AstVarScope* const stateVscp = roleVarScope(cellp, roles.qPort);
|
||||
if (!nextVscp || !stateVscp) {
|
||||
return rejectFsmWrapperCell(
|
||||
cellp, "fsm_register_wrapper d and q connections must be simple variables");
|
||||
}
|
||||
AstVarScope* const clkVscp = roleVarScope(cellp, roles.clkPort);
|
||||
if (!clkVscp) {
|
||||
return rejectFsmWrapperCell(
|
||||
cellp, "fsm_register_wrapper instance requires a simple clock connection");
|
||||
}
|
||||
|
||||
FsmSenDesc clkSense;
|
||||
clkSense.edgeType = VEdgeType::ET_POSEDGE;
|
||||
clkSense.varScopep = clkVscp;
|
||||
cand.senses().push_back(clkSense);
|
||||
|
||||
AstVarScope* resetVscp = nullptr;
|
||||
if (!roles.rstPort.empty()) resetVscp = roleVarScope(cellp, roles.rstPort);
|
||||
if (resetVscp) {
|
||||
// The descriptor identifies the reset port but not its polarity. Use
|
||||
// the wrapper's own event control AST as the contract for sampling
|
||||
// the connected parent signal.
|
||||
bool inferredActiveLow = false;
|
||||
if (fsmWrapperResetPolarityFromWrapperAst(cellp, roles.rstPort, inferredActiveLow)) {
|
||||
roles.hasRstActiveLow = true;
|
||||
roles.rstActiveLow = inferredActiveLow;
|
||||
}
|
||||
}
|
||||
|
||||
AstNodeExpr* resetValuep = nullptr;
|
||||
FsmStateValue resetValue;
|
||||
const bool hasResetValue
|
||||
= !roles.rstValParam.empty()
|
||||
&& simpleParamStateValue(cellp, roles.rstValParam, resetValue, resetValuep);
|
||||
if (resetVscp && roles.hasRstActiveLow && hasResetValue) {
|
||||
FsmSenDesc rstSense;
|
||||
rstSense.edgeType = roles.rstActiveLow ? VEdgeType::ET_NEGEDGE : VEdgeType::ET_POSEDGE;
|
||||
rstSense.varScopep = resetVscp;
|
||||
cand.senses().push_back(rstSense);
|
||||
cand.resetCond().varScopep = resetVscp;
|
||||
cand.resetCond().activeLow = roles.rstActiveLow;
|
||||
cand.hasResetCond(true);
|
||||
cand.resetArcs().emplace_back(resetValue, cellp, resetValuep);
|
||||
} else if (!roles.rstPort.empty() || !roles.rstValParam.empty()) {
|
||||
string reason;
|
||||
if (roles.rstPort.empty()) {
|
||||
reason = "reset port is not configured";
|
||||
} else if (!resetVscp) {
|
||||
reason = "reset connection is missing or not a simple variable";
|
||||
} else if (!roles.hasRstActiveLow) {
|
||||
reason = "reset polarity could not be inferred from the wrapper";
|
||||
} else if (roles.rstValParam.empty()) {
|
||||
reason = "reset_value parameter is not configured";
|
||||
} else {
|
||||
reason = "reset_value parameter is missing or not static";
|
||||
}
|
||||
cellp->v3warn(COVERIGN,
|
||||
"Ignoring unsupported: fsm_register_wrapper reset arcs require both "
|
||||
"reset polarity and static reset value; " + reason);
|
||||
}
|
||||
|
||||
// This candidate represents a register proven through an instance
|
||||
// boundary, so there is no parent always_ff body to annotate. Lowering
|
||||
// treats null alwaysp as the explicit cell-path contract and builds its
|
||||
// sampling block from the recovered clock/reset interface instead.
|
||||
cand.scopep(scopep);
|
||||
cand.alwaysp(nullptr);
|
||||
cand.stateVscp(stateVscp);
|
||||
cand.nextVscp(nextVscp);
|
||||
cand.resetInclude(stateVscp->varp()->attrFsmResetArc());
|
||||
cand.inclCond(stateVscp->varp()->attrFsmArcInclCond());
|
||||
cand.fileline(cellp->fileline());
|
||||
return true;
|
||||
}
|
||||
|
||||
class ComboAlwaysAnalyzer final {
|
||||
public:
|
||||
struct ComboMatch final {
|
||||
|
|
@ -748,6 +981,7 @@ class FsmDetectVisitor final : public VNVisitor {
|
|||
|
||||
static AstNodeAssign* directCondStateVarAssign(AstNode* nodep, AstVarScope*& stateVscp,
|
||||
AstVarScope*& fromVscp, AstNodeExpr*& condp,
|
||||
bool& resetActiveLow,
|
||||
FsmStateValue& resetValue) {
|
||||
AstNodeAssign* const assp = VN_CAST(nodep, NodeAssign);
|
||||
if (!assp) return nullptr;
|
||||
|
|
@ -756,12 +990,18 @@ class FsmDetectVisitor final : public VNVisitor {
|
|||
"conditional register commit lhs should be normalized to a VarRef");
|
||||
AstCond* const rhsp = VN_CAST(assp->rhsp(), Cond);
|
||||
if (!rhsp) return nullptr;
|
||||
AstVarRef* const elsep = VN_CAST(rhsp->elsep(), VarRef);
|
||||
if (!elsep || constValueStatus(rhsp->thenp(), resetValue) != ConstValueStatus::OK) {
|
||||
if (AstVarRef* const elsep = VN_CAST(rhsp->elsep(), VarRef)) {
|
||||
if (constValueStatus(rhsp->thenp(), resetValue) != ConstValueStatus::OK) return nullptr;
|
||||
fromVscp = elsep->varScopep();
|
||||
resetActiveLow = false;
|
||||
} else if (AstVarRef* const thenp = VN_CAST(rhsp->thenp(), VarRef)) {
|
||||
if (constValueStatus(rhsp->elsep(), resetValue) != ConstValueStatus::OK) return nullptr;
|
||||
fromVscp = thenp->varScopep();
|
||||
resetActiveLow = true;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
stateVscp = lhsp->varScopep();
|
||||
fromVscp = elsep->varScopep();
|
||||
condp = rhsp->condp();
|
||||
return assp;
|
||||
}
|
||||
|
|
@ -1169,11 +1409,20 @@ class FsmDetectVisitor final : public VNVisitor {
|
|||
cand.hasResetCond(cand.resetCond().varScopep != nullptr);
|
||||
} else {
|
||||
AstNodeExpr* resetCondp = nullptr;
|
||||
bool resetActiveLow = false;
|
||||
FsmStateValue resetValue;
|
||||
if (AstNodeAssign* const assp
|
||||
= directCondStateVarAssign(nodep, stateVscp, nextVscp, resetCondp, resetValue)) {
|
||||
= directCondStateVarAssign(nodep, stateVscp, nextVscp, resetCondp, resetActiveLow,
|
||||
resetValue)) {
|
||||
// Inlined wrappers can normalize into a compact active-low
|
||||
// assignment form that earlier direct-register FSM support did
|
||||
// not accept. The pre-inline marker is the architectural fence:
|
||||
// it lets wrapper-derived registers use that shape without
|
||||
// changing the meaning of unrelated legacy RTL.
|
||||
if (resetActiveLow && !stateVscp->varp()->attrFsmRegisterWrapper()) return false;
|
||||
cand.resetArcs().emplace_back(resetValue, assp);
|
||||
cand.resetCond() = describeResetCond(resetCondp);
|
||||
cand.resetCond().activeLow = resetActiveLow;
|
||||
cand.hasResetCond(cand.resetCond().varScopep != nullptr);
|
||||
} else if (!nodeStateVarAssign(nodep, stateVscp, nextVscp)) {
|
||||
return false;
|
||||
|
|
@ -1186,6 +1435,7 @@ class FsmDetectVisitor final : public VNVisitor {
|
|||
cand.senses() = describeSenTree(alwaysp->sentreep());
|
||||
cand.resetInclude(stateVscp->varp()->attrFsmResetArc());
|
||||
cand.inclCond(stateVscp->varp()->attrFsmArcInclCond());
|
||||
cand.fileline(alwaysp->fileline());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -1253,7 +1503,7 @@ class FsmDetectVisitor final : public VNVisitor {
|
|||
FsmStateSpace& stateSpace) {
|
||||
for (const FsmResetArcDesc& resetArc : resetArcs) {
|
||||
StateConstLabel label{resetArc.toValue().ascii(), false, 0};
|
||||
if (AstConst* const constp = VN_CAST(resetArc.nodep()->rhsp(), Const)) {
|
||||
if (AstConst* const constp = VN_CAST(resetArc.valuep(), Const)) {
|
||||
label = stateLabelForConst(constp);
|
||||
}
|
||||
UASSERT_OBJ(
|
||||
|
|
@ -1768,6 +2018,14 @@ class FsmDetectVisitor final : public VNVisitor {
|
|||
// Continuous aliases are unordered hardware connections, so source
|
||||
// order should not affect whether an if-chain FSM is recognized.
|
||||
collectAliasFromAssign(nodep, m_stateAliases, m_ambiguousStateAliases);
|
||||
collectCellPortAlias(nodep);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
void visit(AstCell* nodep) override {
|
||||
// Cells are matched after the full traversal because linkdot lowers
|
||||
// uninlined port connections into sibling continuous assignments.
|
||||
if (m_scopep && fsmRegisterWrapperDesc(nodep)) addWrapperCell(m_scopep, nodep);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
|
|
@ -1781,6 +2039,12 @@ public:
|
|||
FsmDetectVisitor(FsmState& state, AstNetlist* rootp)
|
||||
: m_state{state} {
|
||||
iterate(rootp);
|
||||
for (const std::pair<AstScope*, AstCell*>& wrapperCell : m_wrapperCells) {
|
||||
FsmRegisterCandidate reg;
|
||||
if (matchFsmWrapperCell(wrapperCell.first, wrapperCell.second, reg)) {
|
||||
m_registerCandidates.emplace_back(reg);
|
||||
}
|
||||
}
|
||||
for (const FsmComboAlways& oneBlock : m_oneBlockAlwayss) processOneBlockAlways(oneBlock);
|
||||
for (const FsmComboAlways& combo : m_comboAlwayss) processComboAlways(combo);
|
||||
for (const FsmComboAlways& combo : m_nonComboAlwayss) warnUnsupportedComboAlways(combo);
|
||||
|
|
@ -1812,8 +2076,9 @@ class FsmLowerVisitor final {
|
|||
}
|
||||
|
||||
static AstNodeExpr* buildResetCond(FileLine* flp, AstVarScope* resetVscp,
|
||||
const FsmResetCondDesc&) {
|
||||
return new AstVarRef{flp, resetVscp, VAccess::READ};
|
||||
const FsmResetCondDesc& desc) {
|
||||
AstNodeExpr* const refp = new AstVarRef{flp, resetVscp, VAccess::READ};
|
||||
return desc.activeLow ? static_cast<AstNodeExpr*>(new AstLogNot{flp, refp}) : refp;
|
||||
}
|
||||
|
||||
// Rebuild the original event control from the saved sense description so
|
||||
|
|
@ -1862,14 +2127,25 @@ class FsmLowerVisitor final {
|
|||
scopep->addBlocksp(initActivep);
|
||||
|
||||
AstAlwaysPost* const covPostp = new AstAlwaysPost{flp};
|
||||
// Save the previous state as plain sequential logic at the front of
|
||||
// the original always_ff body, then evaluate coverage in post logic
|
||||
// after the delayed state update commits. This avoids a scheduler race
|
||||
// between a separate AstAlwaysPre task and the real state commit.
|
||||
AstNode* const bodysp = alwaysp->stmtsp()->unlinkFrBackWithNext();
|
||||
alwaysp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, prevVscp, VAccess::WRITE},
|
||||
new AstVarRef{flp, stateVscp, VAccess::READ}});
|
||||
alwaysp->addStmtsp(bodysp);
|
||||
bool updatePrevAfterPost = false;
|
||||
if (alwaysp) {
|
||||
// Save the previous state as plain sequential logic at the front of
|
||||
// the original always_ff body, then evaluate coverage in post logic
|
||||
// after the delayed state update commits. This avoids a scheduler
|
||||
// race between a separate AstAlwaysPre task and the real state
|
||||
// commit for direct parent-level registers.
|
||||
AstNode* const bodysp = alwaysp->stmtsp()->unlinkFrBackWithNext();
|
||||
alwaysp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, prevVscp, VAccess::WRITE},
|
||||
new AstVarRef{flp, stateVscp, VAccess::READ}});
|
||||
alwaysp->addStmtsp(bodysp);
|
||||
} else {
|
||||
// Wrapper-derived register candidates do not have a parent
|
||||
// always_ff body to splice into. Sample coverage first, then save
|
||||
// the current state for the next clock tick; this survives cell
|
||||
// boundary scheduling where the real flop update lives elsewhere.
|
||||
updatePrevAfterPost = true;
|
||||
prevVscp->varp()->setIgnorePostRead();
|
||||
}
|
||||
|
||||
for (const V3GraphVertex& vtx : graph.vertices()) {
|
||||
const FsmVertex* const vertexp = vtx.as<FsmVertex>();
|
||||
|
|
@ -1960,6 +2236,10 @@ class FsmLowerVisitor final {
|
|||
covPostp->addStmtsp(new AstIf{flp, guardp, new AstCoverInc{flp, declp}});
|
||||
}
|
||||
}
|
||||
if (updatePrevAfterPost) {
|
||||
covPostp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, prevVscp, VAccess::WRITE},
|
||||
new AstVarRef{flp, stateVscp, VAccess::READ}});
|
||||
}
|
||||
|
||||
AstSenTree* const sentreep = buildSenTree(flp, graph.senses());
|
||||
AstActive* const activep = new AstActive{flp, "fsm-coverage", sentreep};
|
||||
|
|
@ -1979,8 +2259,48 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
// Wrapper FSM support has two architectural paths. If V3Inline removes the
|
||||
// wrapper, the main detector will later see an ordinary parent-scope always_ff;
|
||||
// this pre-inline visitor leaves just enough provenance on the q-side state
|
||||
// variable for that direct path to accept wrapper-specific normalized shapes.
|
||||
// If the wrapper survives, this marker is harmless and the cell-path detector
|
||||
// builds a register candidate from the instance itself.
|
||||
class FsmWrapperMarkerVisitor final : public VNVisitor {
|
||||
static AstPin* findPin(AstCell* cellp, const string& name) {
|
||||
for (AstPin* pinp = cellp->pinsp(); pinp; pinp = VN_AS(pinp->nextp(), Pin)) {
|
||||
if (pinp->name() == name) return pinp;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void visit(AstCell* cellp) override {
|
||||
if (const V3Control::FsmRegisterWrapper* const descp = fsmRegisterWrapperDesc(cellp)) {
|
||||
AstPin* const qp = findPin(cellp, descp->q);
|
||||
if (qp && VN_IS(qp->exprp(), VarRef)) {
|
||||
AstVarRef* const qrefp = VN_AS(qp->exprp(), VarRef);
|
||||
// The q-side parent variable is the point where the wrapper
|
||||
// abstraction collapses into direct RTL after inlining.
|
||||
// Marking only that variable keeps the provenance narrow:
|
||||
// transition detection still has to prove the d/q FSM pair.
|
||||
qrefp->varp()->attrFsmRegisterWrapper(true);
|
||||
}
|
||||
}
|
||||
iterateChildren(cellp);
|
||||
}
|
||||
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
explicit FsmWrapperMarkerVisitor(AstNetlist* rootp) { iterate(rootp); }
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void V3FsmDetect::markWrapperStateVars(AstNetlist* rootp) {
|
||||
UINFO(2, __FUNCTION__ << ":");
|
||||
FsmWrapperMarkerVisitor marker{rootp};
|
||||
}
|
||||
|
||||
void V3FsmDetect::detect(AstNetlist* rootp) {
|
||||
UINFO(2, __FUNCTION__ << ":");
|
||||
FsmState state;
|
||||
|
|
|
|||
|
|
@ -24,6 +24,10 @@ class AstNetlist;
|
|||
|
||||
class V3FsmDetect final {
|
||||
public:
|
||||
// Mark parent-scope state variables connected through configured wrapper
|
||||
// instances before inlining lowers those instances away.
|
||||
static void markWrapperStateVars(AstNetlist* rootp) VL_MT_DISABLED;
|
||||
|
||||
// Detect FSMs while the original clocked/case structure is still visible,
|
||||
// then immediately lower the recovered graphs into concrete coverage
|
||||
// instrumentation as a second local phase in the same pass.
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@
|
|||
#include "V3PreShell.h"
|
||||
#include "V3Stats.h"
|
||||
|
||||
#include <cctype>
|
||||
#include <sstream>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
|
|
|||
|
|
@ -316,6 +316,7 @@ static void process() {
|
|||
// Module inlining
|
||||
// Cannot remove dead variables after this, as alias information for final
|
||||
// V3Scope's V3LinkDot is in the AstVar.
|
||||
if (v3Global.opt.coverageFsm()) V3FsmDetect::markWrapperStateVars(v3Global.rootp());
|
||||
if (v3Global.opt.fInline()) {
|
||||
V3Inline::inlineAll(v3Global.rootp());
|
||||
V3LinkDot::linkDotArrayed(v3Global.rootp()); // Cleanup as made new modules
|
||||
|
|
|
|||
|
|
@ -136,6 +136,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5}
|
|||
"coverage_off" { FL; return yVLT_COVERAGE_OFF; }
|
||||
"coverage_on" { FL; return yVLT_COVERAGE_ON; }
|
||||
"forceable" { FL; return yVLT_FORCEABLE; }
|
||||
"fsm_register_wrapper" { FL; return yVLT_FSM_REGISTER_WRAPPER; }
|
||||
"full_case" { FL; return yVLT_FULL_CASE; }
|
||||
"hier_block" { FL; return yVLT_HIER_BLOCK; }
|
||||
"hier_params" { FL; return yVLT_HIER_PARAMS; }
|
||||
|
|
@ -166,6 +167,8 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5}
|
|||
-?"-block" { FL; return yVLT_D_BLOCK; }
|
||||
-?"-contents" { FL; return yVLT_D_CONTENTS; }
|
||||
-?"-cost" { FL; return yVLT_D_COST; }
|
||||
-?"-clock" { FL; return yVLT_D_CLOCK; }
|
||||
-?"-d" { FL; return yVLT_D_D; }
|
||||
-?"-file" { FL; return yVLT_D_FILE; }
|
||||
-?"-function" { FL; return yVLT_D_FUNCTION; }
|
||||
-?"-hier-dpi" { FL; return yVLT_D_HIER_DPI; }
|
||||
|
|
@ -178,6 +181,9 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5}
|
|||
-?"-param" { FL; return yVLT_D_PARAM; }
|
||||
-?"-port" { FL; return yVLT_D_PORT; }
|
||||
-?"-rule" { FL; return yVLT_D_RULE; }
|
||||
-?"-q" { FL; return yVLT_D_Q; }
|
||||
-?"-reset" { FL; return yVLT_D_RESET; }
|
||||
-?"-reset_value" { FL; return yVLT_D_RESET_VALUE; }
|
||||
-?"-scope" { FL; return yVLT_D_SCOPE; }
|
||||
-?"-task" { FL; return yVLT_D_TASK; }
|
||||
-?"-var" { FL; return yVLT_D_VAR; }
|
||||
|
|
|
|||
|
|
@ -245,6 +245,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"})
|
|||
%token<fl> yVLT_COVERAGE_OFF "coverage_off"
|
||||
%token<fl> yVLT_COVERAGE_ON "coverage_on"
|
||||
%token<fl> yVLT_FORCEABLE "forceable"
|
||||
%token<fl> yVLT_FSM_REGISTER_WRAPPER "fsm_register_wrapper"
|
||||
%token<fl> yVLT_FULL_CASE "full_case"
|
||||
%token<fl> yVLT_HIER_BLOCK "hier_block"
|
||||
%token<fl> yVLT_HIER_PARAMS "hier_params"
|
||||
|
|
@ -275,6 +276,8 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"})
|
|||
%token<fl> yVLT_D_BLOCK "--block"
|
||||
%token<fl> yVLT_D_CONTENTS "--contents"
|
||||
%token<fl> yVLT_D_COST "--cost"
|
||||
%token<fl> yVLT_D_CLOCK "--clock"
|
||||
%token<fl> yVLT_D_D "--d"
|
||||
%token<fl> yVLT_D_FILE "--file"
|
||||
%token<fl> yVLT_D_FUNCTION "--function"
|
||||
%token<fl> yVLT_D_HIER_DPI "--hier-dpi"
|
||||
|
|
@ -287,6 +290,9 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"})
|
|||
%token<fl> yVLT_D_PARAM "--param"
|
||||
%token<fl> yVLT_D_PORT "--port"
|
||||
%token<fl> yVLT_D_RULE "--rule"
|
||||
%token<fl> yVLT_D_Q "--q"
|
||||
%token<fl> yVLT_D_RESET "--reset"
|
||||
%token<fl> yVLT_D_RESET_VALUE "--reset_value"
|
||||
%token<fl> yVLT_D_SCOPE "--scope"
|
||||
%token<fl> yVLT_D_TASK "--task"
|
||||
%token<fl> yVLT_D_VAR "--var"
|
||||
|
|
@ -8291,6 +8297,8 @@ vltItem:
|
|||
{ V3Control::addProfileData($<fl>1, *$2, $3->toUQuad()); }
|
||||
| yVLT_PROFILE_DATA vltDModel vltDMtask vltDCost
|
||||
{ V3Control::addProfileData($<fl>1, *$2, *$3, $4->toUQuad()); }
|
||||
| yVLT_FSM_REGISTER_WRAPPER vltDModule vltDFsmD vltDFsmQ vltDFsmClock vltDFsmResetE vltDFsmResetValueE
|
||||
{ V3Control::addFsmRegisterWrapper($<fl>1, *$2, *$3, *$4, *$5, *$6, *$7); }
|
||||
| yVLT_VERILATOR_LIB vltDModule
|
||||
{ V3Control::addModulePragma(*$2, VPragmaType::VERILATOR_LIB); }
|
||||
;
|
||||
|
|
@ -8331,6 +8339,28 @@ vltDFile<strp>: // --file <arg>
|
|||
yVLT_D_FILE str { $$ = $2; }
|
||||
;
|
||||
|
||||
vltDFsmClock<strp>: // --clock <arg>
|
||||
yVLT_D_CLOCK str { $$ = $2; }
|
||||
;
|
||||
|
||||
vltDFsmD<strp>: // --d <arg>
|
||||
yVLT_D_D str { $$ = $2; }
|
||||
;
|
||||
|
||||
vltDFsmQ<strp>: // --q <arg>
|
||||
yVLT_D_Q str { $$ = $2; }
|
||||
;
|
||||
|
||||
vltDFsmResetE<strp>: // [--reset <arg>]
|
||||
/* empty */ { static string empty; $$ = ∅ }
|
||||
| yVLT_D_RESET str { $$ = $2; }
|
||||
;
|
||||
|
||||
vltDFsmResetValueE<strp>: // [--reset_value <arg>]
|
||||
/* empty */ { static string empty; $$ = ∅ }
|
||||
| yVLT_D_RESET_VALUE str { $$ = $2; }
|
||||
;
|
||||
|
||||
vltDHierDpi<strp>: // --hier-dpi <arg>
|
||||
yVLT_D_HIER_DPI str { $$ = $2; }
|
||||
;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
|
||||
module fsm_if_mixed_vars_bad (
|
||||
input logic clk
|
||||
);
|
||||
|
|
@ -13,20 +13,20 @@
|
|||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
|
||||
state_t state_q /*verilator fsm_state*/;
|
||||
state_t other_q;
|
||||
state_t state_d;
|
||||
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
if (state_q == S0) state_d = S1;
|
||||
else if (other_q == S1) state_d = S2;
|
||||
end
|
||||
|
||||
|
||||
always_ff @(posedge clk) state_q <= state_d;
|
||||
endmodule
|
||||
|
||||
|
||||
module fsm_if_one_branch_bad (
|
||||
input logic clk
|
||||
);
|
||||
|
|
@ -34,18 +34,18 @@
|
|||
S0 = 2'd0,
|
||||
S1 = 2'd1
|
||||
} state_t;
|
||||
|
||||
|
||||
state_t state_q /*verilator fsm_state*/;
|
||||
state_t state_d;
|
||||
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
if (state_q == S0) state_d = S1;
|
||||
end
|
||||
|
||||
|
||||
always_ff @(posedge clk) state_q <= state_d;
|
||||
endmodule
|
||||
|
||||
|
||||
module fsm_if_duplicate_bad (
|
||||
input logic clk
|
||||
);
|
||||
|
|
@ -53,19 +53,19 @@
|
|||
S0 = 2'd0,
|
||||
S1 = 2'd1
|
||||
} state_t;
|
||||
|
||||
|
||||
state_t state_q /*verilator fsm_state*/;
|
||||
state_t state_d;
|
||||
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
if (state_q == S0) state_d = S1;
|
||||
else if (state_q == S0) state_d = S0;
|
||||
end
|
||||
|
||||
|
||||
always_ff @(posedge clk) state_q <= state_d;
|
||||
endmodule
|
||||
|
||||
|
||||
module fsm_if_two_comparisons_bad (
|
||||
input logic clk,
|
||||
input logic start
|
||||
|
|
@ -75,19 +75,19 @@
|
|||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
|
||||
state_t state_q /*verilator fsm_state*/;
|
||||
state_t state_d;
|
||||
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
if ((state_q == S0) && (state_q == S1)) state_d = S2;
|
||||
else if (state_q == S1) state_d = S0;
|
||||
end
|
||||
|
||||
|
||||
always_ff @(posedge clk) state_q <= start ? state_d : state_q;
|
||||
endmodule
|
||||
|
||||
|
||||
module fsm_if_or_bad (
|
||||
input logic clk
|
||||
);
|
||||
|
|
@ -96,19 +96,19 @@
|
|||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
|
||||
state_t state_q /*verilator fsm_state*/;
|
||||
state_t state_d;
|
||||
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
if ((state_q == S0) || (state_q == S1)) state_d = S2;
|
||||
else if (state_q == S2) state_d = S0;
|
||||
end
|
||||
|
||||
|
||||
always_ff @(posedge clk) state_q <= state_d;
|
||||
endmodule
|
||||
|
||||
|
||||
module fsm_if_alias_guard_bad (
|
||||
input logic clk,
|
||||
input logic start
|
||||
|
|
@ -118,21 +118,21 @@
|
|||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
|
||||
logic idle_state;
|
||||
state_t state_q /*verilator fsm_state*/;
|
||||
state_t state_d;
|
||||
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
idle_state = (state_q == S0) && start;
|
||||
if (idle_state) state_d = S1;
|
||||
else if (state_q == S1) state_d = S2;
|
||||
end
|
||||
|
||||
|
||||
always_ff @(posedge clk) state_q <= state_d;
|
||||
endmodule
|
||||
|
||||
|
||||
module fsm_if_ambiguous_alias_bad (
|
||||
input logic clk
|
||||
);
|
||||
|
|
@ -141,11 +141,11 @@
|
|||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
|
||||
logic idle_state;
|
||||
state_t state_q /*verilator fsm_state*/;
|
||||
state_t state_d;
|
||||
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
idle_state = (state_q == S0);
|
||||
|
|
@ -154,10 +154,10 @@
|
|||
if (idle_state) state_d = S2;
|
||||
else if (state_q == S2) state_d = S0;
|
||||
end
|
||||
|
||||
|
||||
always_ff @(posedge clk) state_q <= state_d;
|
||||
endmodule
|
||||
|
||||
|
||||
module fsm_if_missing_default_bad (
|
||||
input logic clk
|
||||
);
|
||||
|
|
@ -166,19 +166,19 @@
|
|||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
|
||||
state_t state_q /*verilator fsm_state*/;
|
||||
state_t state_d;
|
||||
|
||||
|
||||
always_comb begin
|
||||
state_d = S0;
|
||||
if (state_d == S0) state_d = S1;
|
||||
else if (state_d == S1) state_d = S2;
|
||||
end
|
||||
|
||||
|
||||
always_ff @(posedge clk) state_q <= state_d;
|
||||
endmodule
|
||||
|
||||
|
||||
module fsm_if_no_assign_bad (
|
||||
input logic clk
|
||||
);
|
||||
|
|
@ -186,21 +186,21 @@
|
|||
S0 = 2'd0,
|
||||
S1 = 2'd1
|
||||
} state_t;
|
||||
|
||||
|
||||
logic flag;
|
||||
state_t state_q /*verilator fsm_state*/;
|
||||
state_t state_d;
|
||||
|
||||
|
||||
always_comb begin
|
||||
flag = 1'b0;
|
||||
state_d = state_q;
|
||||
if (state_q == S0) flag = 1'b1;
|
||||
else if (state_q == S1) state_d = S0;
|
||||
end
|
||||
|
||||
|
||||
always_ff @(posedge clk) state_q <= state_d;
|
||||
endmodule
|
||||
|
||||
|
||||
module fsm_if_nonvar_compare_bad (
|
||||
input logic clk
|
||||
);
|
||||
|
|
@ -208,10 +208,10 @@
|
|||
S0 = 2'd0,
|
||||
S1 = 2'd1
|
||||
} state_t;
|
||||
|
||||
|
||||
state_t state_q /*verilator fsm_state*/;
|
||||
state_t state_d;
|
||||
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
%000003 if ((state_q + 2'd0) == S0) state_d = S1;
|
||||
|
|
@ -222,10 +222,10 @@
|
|||
%000003 // [fsm_state t.nonvar_compare_u.state_q::S1]
|
||||
else if (state_q == S1) state_d = S0;
|
||||
end
|
||||
|
||||
|
||||
always_ff @(posedge clk) state_q <= state_d;
|
||||
endmodule
|
||||
|
||||
|
||||
module fsm_if_var_rhs_compare_bad (
|
||||
input logic clk
|
||||
);
|
||||
|
|
@ -233,19 +233,19 @@
|
|||
S0 = 2'd0,
|
||||
S1 = 2'd1
|
||||
} state_t;
|
||||
|
||||
|
||||
state_t state_q /*verilator fsm_state*/;
|
||||
state_t state_d;
|
||||
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
if (state_q == state_d) state_d = S1;
|
||||
else if (state_q == S1) state_d = S0;
|
||||
end
|
||||
|
||||
|
||||
always_ff @(posedge clk) state_q <= state_d;
|
||||
endmodule
|
||||
|
||||
|
||||
module fsm_if_var_target_bad (
|
||||
input logic clk,
|
||||
input logic [1:0] dyn
|
||||
|
|
@ -255,19 +255,19 @@
|
|||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
|
||||
state_t state_q /*verilator fsm_state*/;
|
||||
state_t state_d;
|
||||
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
if (state_q == S0) state_d = state_t'(dyn);
|
||||
else if (state_q == S1) state_d = S2;
|
||||
end
|
||||
|
||||
|
||||
always_ff @(posedge clk) state_q <= state_d;
|
||||
endmodule
|
||||
|
||||
|
||||
module fsm_if_alias_other_state_bad (
|
||||
input logic clk
|
||||
);
|
||||
|
|
@ -275,12 +275,12 @@
|
|||
S0 = 2'd0,
|
||||
S1 = 2'd1
|
||||
} state_t;
|
||||
|
||||
|
||||
logic idle_state;
|
||||
state_t state_q /*verilator fsm_state*/;
|
||||
state_t other_q;
|
||||
state_t state_d;
|
||||
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
idle_state = (state_q == S0);
|
||||
|
|
@ -288,10 +288,10 @@
|
|||
if (idle_state) state_d = S1;
|
||||
else if (state_q == S1) state_d = S0;
|
||||
end
|
||||
|
||||
|
||||
always_ff @(posedge clk) state_q <= state_d;
|
||||
endmodule
|
||||
|
||||
|
||||
module fsm_if_bit_or_bad (
|
||||
input logic clk,
|
||||
input logic start
|
||||
|
|
@ -301,19 +301,19 @@
|
|||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
|
||||
state_t state_q /*verilator fsm_state*/;
|
||||
state_t state_d;
|
||||
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
if ((state_q == S0) | start) state_d = S1;
|
||||
else if (state_q == S1) state_d = S2;
|
||||
end
|
||||
|
||||
|
||||
always_ff @(posedge clk) state_q <= state_d;
|
||||
endmodule
|
||||
|
||||
|
||||
module fsm_if_reduction_bad (
|
||||
input logic clk
|
||||
);
|
||||
|
|
@ -322,10 +322,10 @@
|
|||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
|
||||
state_t state_q /*verilator fsm_state*/;
|
||||
state_t state_d;
|
||||
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
if (&state_q) state_d = S1;
|
||||
|
|
@ -335,26 +335,45 @@
|
|||
if (^state_q) state_d = S0;
|
||||
else if (state_q == S0) state_d = S1;
|
||||
end
|
||||
|
||||
|
||||
always_ff @(posedge clk) state_q <= state_d;
|
||||
endmodule
|
||||
|
||||
module fsm_direct_active_low_dynamic_reset_bad (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic [1:0] dyn_reset
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1
|
||||
} state_t;
|
||||
state_t state_q /*verilator fsm_state*/;
|
||||
state_t state_d;
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
if (state_q == S0) state_d = S1;
|
||||
else state_d = S0;
|
||||
end
|
||||
always_ff @(posedge clk or negedge rst_n) begin
|
||||
state_q <= rst_n ? state_d : state_t'(dyn_reset);
|
||||
end
|
||||
endmodule
|
||||
module t (
|
||||
input logic clk
|
||||
);
|
||||
|
||||
|
||||
typedef enum logic [2:0] {
|
||||
S0 = 3'd0,
|
||||
S1 = 3'd1,
|
||||
S2 = 3'd2
|
||||
} state_t;
|
||||
|
||||
|
||||
int cyc;
|
||||
logic start;
|
||||
logic side;
|
||||
logic [2:0] dyn_case;
|
||||
state_t state /*verilator fsm_reset_arc*/;
|
||||
|
||||
|
||||
fsm_if_mixed_vars_bad mixed_vars_u (.clk(clk));
|
||||
fsm_if_one_branch_bad one_branch_u (.clk(clk));
|
||||
fsm_if_duplicate_bad duplicate_u (.clk(clk));
|
||||
|
|
@ -370,14 +389,16 @@
|
|||
fsm_if_alias_other_state_bad alias_other_state_u (.clk(clk));
|
||||
fsm_if_bit_or_bad bit_or_u (.clk(clk), .start(start));
|
||||
fsm_if_reduction_bad reduction_u (.clk(clk));
|
||||
|
||||
fsm_direct_active_low_dynamic_reset_bad active_low_dynamic_reset_u (
|
||||
.clk(clk), .rst_n(cyc != 0), .dyn_reset(dyn_case[1:0]));
|
||||
|
||||
initial begin
|
||||
cyc = 0;
|
||||
start = 1'b0;
|
||||
side = 1'b0;
|
||||
dyn_case = 3'd7;
|
||||
end
|
||||
|
||||
|
||||
always @(posedge clk) begin
|
||||
cyc <= cyc + 1;
|
||||
if (cyc == 1) side <= 1'b1;
|
||||
|
|
@ -387,7 +408,7 @@
|
|||
$finish;
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
// The grouped S0/dyn_case arm keeps the supported S0 baseline while the
|
||||
// non-constant case item is skipped. The S1 and default arms are
|
||||
// deliberately unsupported extractor shapes: one has two meaningful
|
||||
|
|
@ -416,6 +437,6 @@
|
|||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
endmodule
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,13 @@ test.run(cmd=[
|
|||
],
|
||||
verilator_run=True)
|
||||
|
||||
test.files_identical(test.obj_dir + "/annotated/" + test.name + ".v", test.golden_filename)
|
||||
annotated_filename = test.obj_dir + "/annotated/" + test.name + ".v"
|
||||
normalized_filename = test.obj_dir + "/annotated/" + test.name + ".normalized.v"
|
||||
with open(annotated_filename, encoding="utf-8") as in_file:
|
||||
with open(normalized_filename, "w", encoding="utf-8") as out_file:
|
||||
for line in in_file:
|
||||
out_file.write(line.rstrip() + "\n")
|
||||
|
||||
test.files_identical(normalized_filename, test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
|
|
|
|||
|
|
@ -332,7 +332,26 @@ module fsm_if_reduction_bad (
|
|||
|
||||
always_ff @(posedge clk) state_q <= state_d;
|
||||
endmodule
|
||||
|
||||
module fsm_direct_active_low_dynamic_reset_bad (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic [1:0] dyn_reset
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1
|
||||
} state_t;
|
||||
state_t state_q /*verilator fsm_state*/;
|
||||
state_t state_d;
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
if (state_q == S0) state_d = S1;
|
||||
else state_d = S0;
|
||||
end
|
||||
always_ff @(posedge clk or negedge rst_n) begin
|
||||
state_q <= rst_n ? state_d : state_t'(dyn_reset);
|
||||
end
|
||||
endmodule
|
||||
module t (
|
||||
input logic clk
|
||||
);
|
||||
|
|
@ -364,6 +383,8 @@ module t (
|
|||
fsm_if_alias_other_state_bad alias_other_state_u (.clk(clk));
|
||||
fsm_if_bit_or_bad bit_or_u (.clk(clk), .start(start));
|
||||
fsm_if_reduction_bad reduction_u (.clk(clk));
|
||||
fsm_direct_active_low_dynamic_reset_bad active_low_dynamic_reset_u (
|
||||
.clk(clk), .rst_n(cyc != 0), .dyn_reset(dyn_case[1:0]));
|
||||
|
||||
initial begin
|
||||
cyc = 0;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,522 @@
|
|||
// // verilator_coverage annotation
|
||||
// DESCRIPTION: Verilator: FSM coverage for fsm_register_wrapper state register wrappers
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module my_fsm_flop #(
|
||||
parameter int Width = 1,
|
||||
parameter logic [Width-1:0] ResetValue = '0
|
||||
) (
|
||||
input logic clk_i,
|
||||
input logic rst_ni,
|
||||
input logic [Width-1:0] state_i,
|
||||
output logic [Width-1:0] state_o
|
||||
);
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (!rst_ni) state_o <= ResetValue;
|
||||
else state_o <= state_i;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module prim_flop #(
|
||||
parameter int Width = 1,
|
||||
parameter logic [Width-1:0] ResetValue = '0
|
||||
) (
|
||||
input logic clk_i,
|
||||
input logic rst_ni,
|
||||
input logic [Width-1:0] d_i,
|
||||
output logic [Width-1:0] q_o
|
||||
);
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (!rst_ni) q_o <= ResetValue;
|
||||
else q_o <= d_i;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module prim_sparse_fsm_flop #(
|
||||
parameter int Width = 1,
|
||||
parameter logic [Width-1:0] ResetValue = '0
|
||||
) (
|
||||
input logic clk_i,
|
||||
input logic rst_ni,
|
||||
input logic [Width-1:0] state_i,
|
||||
output logic [Width-1:0] state_o
|
||||
);
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (!rst_ni) state_o <= ResetValue;
|
||||
else state_o <= state_i;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module odd_fsm_flop #(
|
||||
parameter int Width = 1,
|
||||
parameter logic [Width-1:0] ResetValue = '0
|
||||
) (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic [Width-1:0] din,
|
||||
output logic [Width-1:0] dout
|
||||
);
|
||||
always_ff @(posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) dout <= ResetValue;
|
||||
else dout <= din;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module ambiguous_fsm_flop (
|
||||
input logic clk_i,
|
||||
input logic rst_ni,
|
||||
input logic [1:0] state_i,
|
||||
input logic [1:0] d_i,
|
||||
output logic [1:0] state_o
|
||||
);
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (!rst_ni) state_o <= 2'd0;
|
||||
else state_o <= state_i ^ d_i;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module fsm_auto (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
%000006 unique case (state_q)
|
||||
// [FSM coverage]
|
||||
%000001 // [fsm_arc t.auto_u.state_q::ANY->S0[reset]] [reset arc, excluded from %]
|
||||
%000006 // [fsm_arc t.auto_u.state_q::S0->S0]
|
||||
%000001 // [fsm_arc t.auto_u.state_q::S0->S1]
|
||||
%000001 // [fsm_arc t.auto_u.state_q::S1->S2]
|
||||
%000001 // [fsm_state t.auto_u.state_q::S0]
|
||||
%000001 // [fsm_state t.auto_u.state_q::S1]
|
||||
%000001 // [fsm_state t.auto_u.state_q::S2]
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
my_fsm_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk_i(clk),
|
||||
.rst_ni(rst_n),
|
||||
.state_i(state_d),
|
||||
.state_o(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_noargs_hint (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
%000006 unique case (state_q)
|
||||
// [FSM coverage]
|
||||
%000001 // [fsm_arc t.noargs_u.state_q::ANY->S0[reset]] [reset arc, excluded from %]
|
||||
%000006 // [fsm_arc t.noargs_u.state_q::S0->S0]
|
||||
%000001 // [fsm_arc t.noargs_u.state_q::S0->S1]
|
||||
%000001 // [fsm_arc t.noargs_u.state_q::S1->S2]
|
||||
%000001 // [fsm_state t.noargs_u.state_q::S0]
|
||||
%000001 // [fsm_state t.noargs_u.state_q::S1]
|
||||
%000001 // [fsm_state t.noargs_u.state_q::S2]
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
my_fsm_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk_i(clk),
|
||||
.rst_ni(rst_n),
|
||||
.state_i(state_d),
|
||||
.state_o(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_prim (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
%000006 unique case (state_q)
|
||||
// [FSM coverage]
|
||||
%000001 // [fsm_arc t.prim_u.state_q::ANY->S0[reset]] [reset arc, excluded from %]
|
||||
%000006 // [fsm_arc t.prim_u.state_q::S0->S0]
|
||||
%000001 // [fsm_arc t.prim_u.state_q::S0->S1]
|
||||
%000001 // [fsm_arc t.prim_u.state_q::S1->S2]
|
||||
%000001 // [fsm_state t.prim_u.state_q::S0]
|
||||
%000001 // [fsm_state t.prim_u.state_q::S1]
|
||||
%000001 // [fsm_state t.prim_u.state_q::S2]
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
prim_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk_i(clk),
|
||||
.rst_ni(rst_n),
|
||||
.d_i(state_d),
|
||||
.q_o(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_sparse_prim (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [5:0] {
|
||||
S0 = 6'b00_0001,
|
||||
S1 = 6'b10_0100,
|
||||
S2 = 6'b11_0010
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
%000005 unique case (state_q)
|
||||
// [FSM coverage]
|
||||
%000001 // [fsm_arc t.sparse_prim_u.state_q::ANY->S0[reset]] [reset arc, excluded from %]
|
||||
%000005 // [fsm_arc t.sparse_prim_u.state_q::S0->S0]
|
||||
%000001 // [fsm_arc t.sparse_prim_u.state_q::S0->S1]
|
||||
%000001 // [fsm_arc t.sparse_prim_u.state_q::S1->S2]
|
||||
%000002 // [fsm_state t.sparse_prim_u.state_q::S0]
|
||||
%000001 // [fsm_state t.sparse_prim_u.state_q::S1]
|
||||
%000001 // [fsm_state t.sparse_prim_u.state_q::S2]
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
prim_sparse_fsm_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk_i(clk),
|
||||
.rst_ni(rst_n),
|
||||
.state_i(state_d),
|
||||
.state_o(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_ifchain (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
if (state_q == S0) begin
|
||||
state_d = start ? S1 : S0;
|
||||
end
|
||||
else if (state_q == S1) begin
|
||||
state_d = S2;
|
||||
end
|
||||
else begin
|
||||
state_d = S0;
|
||||
end
|
||||
end
|
||||
|
||||
my_fsm_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk_i(clk),
|
||||
.rst_ni(rst_n),
|
||||
.state_i(state_d),
|
||||
.state_o(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_wide_sparse (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [39:0] {
|
||||
S0 = 40'h0000_0000_01,
|
||||
S1 = 40'h8000_0000_02,
|
||||
S2 = 40'hffff_0000_03
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
%000005 unique case (state_q)
|
||||
// [FSM coverage]
|
||||
%000001 // [fsm_arc t.wide_sparse_u.state_q::ANY->S0[reset]] [reset arc, excluded from %]
|
||||
%000005 // [fsm_arc t.wide_sparse_u.state_q::S0->S0]
|
||||
%000001 // [fsm_arc t.wide_sparse_u.state_q::S0->S1]
|
||||
%000001 // [fsm_arc t.wide_sparse_u.state_q::S1->S2]
|
||||
%000002 // [fsm_state t.wide_sparse_u.state_q::S0]
|
||||
%000001 // [fsm_state t.wide_sparse_u.state_q::S1]
|
||||
%000001 // [fsm_state t.wide_sparse_u.state_q::S2]
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
my_fsm_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk_i(clk),
|
||||
.rst_ni(rst_n),
|
||||
.state_i(state_d),
|
||||
.state_o(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_annotated (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
%000006 unique case (state_q)
|
||||
// [FSM coverage]
|
||||
%000001 // [fsm_arc t.annotated_u.state_q::ANY->S0[reset]] [reset arc, excluded from %]
|
||||
%000006 // [fsm_arc t.annotated_u.state_q::S0->S0]
|
||||
%000001 // [fsm_arc t.annotated_u.state_q::S0->S1]
|
||||
%000001 // [fsm_arc t.annotated_u.state_q::S1->S2]
|
||||
%000001 // [fsm_state t.annotated_u.state_q::S0]
|
||||
%000001 // [fsm_state t.annotated_u.state_q::S1]
|
||||
%000001 // [fsm_state t.annotated_u.state_q::S2]
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
odd_fsm_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk(clk),
|
||||
.rst_n(rst_n),
|
||||
.din(state_d),
|
||||
.dout(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_non_simple (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
%000006 unique case (state_q)
|
||||
// [FSM coverage]
|
||||
%000001 // [fsm_arc t.non_simple_u.state_q::ANY->S0[reset]] [reset arc, excluded from %]
|
||||
%000006 // [fsm_arc t.non_simple_u.state_q::S0->S0]
|
||||
%000001 // [fsm_arc t.non_simple_u.state_q::S0->S1]
|
||||
%000001 // [fsm_arc t.non_simple_u.state_q::S1->S2]
|
||||
%000001 // [fsm_state t.non_simple_u.state_q::S0]
|
||||
%000001 // [fsm_state t.non_simple_u.state_q::S1]
|
||||
%000001 // [fsm_state t.non_simple_u.state_q::S2]
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
my_fsm_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk_i(clk),
|
||||
.rst_ni(rst_n),
|
||||
.state_i(state_d[1:0]),
|
||||
.state_o(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_ambiguous (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
unique case (state_q)
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
ambiguous_fsm_flop u_state_regs (
|
||||
.clk_i(clk),
|
||||
.rst_ni(rst_n),
|
||||
.state_i(state_d),
|
||||
.d_i(state_d),
|
||||
.state_o(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_ignored (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
%000006 unique case (state_q)
|
||||
// [FSM coverage]
|
||||
%000001 // [fsm_arc t.ignored_u.state_q::ANY->S0[reset]] [reset arc, excluded from %]
|
||||
%000006 // [fsm_arc t.ignored_u.state_q::S0->S0]
|
||||
%000001 // [fsm_arc t.ignored_u.state_q::S0->S1]
|
||||
%000001 // [fsm_arc t.ignored_u.state_q::S1->S2]
|
||||
%000001 // [fsm_state t.ignored_u.state_q::S0]
|
||||
%000001 // [fsm_state t.ignored_u.state_q::S1]
|
||||
%000001 // [fsm_state t.ignored_u.state_q::S2]
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
odd_fsm_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk(clk),
|
||||
.rst_n(rst_n),
|
||||
.din(state_d),
|
||||
.dout(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module t (
|
||||
input logic clk
|
||||
);
|
||||
logic rst_n;
|
||||
logic start;
|
||||
integer cyc;
|
||||
|
||||
initial begin
|
||||
rst_n = 1'b0;
|
||||
start = 1'b0;
|
||||
cyc = 0;
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
cyc <= cyc + 1;
|
||||
if (cyc == 1) rst_n <= 1'b1;
|
||||
if (cyc == 2) start <= 1'b1;
|
||||
if (cyc == 3) start <= 1'b0;
|
||||
if (cyc == 8) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
|
||||
fsm_auto auto_u (.clk(clk), .rst_n(rst_n), .start(start));
|
||||
fsm_noargs_hint noargs_u (.clk(clk), .rst_n(rst_n), .start(start));
|
||||
fsm_prim prim_u (.clk(clk), .rst_n(rst_n), .start(start));
|
||||
fsm_sparse_prim sparse_prim_u (.clk(clk), .rst_n(rst_n), .start(start));
|
||||
fsm_ifchain ifchain_u (.clk(clk), .rst_n(rst_n), .start(start));
|
||||
fsm_wide_sparse wide_sparse_u (.clk(clk), .rst_n(rst_n), .start(start));
|
||||
fsm_annotated annotated_u (.clk(clk), .rst_n(rst_n), .start(start));
|
||||
fsm_non_simple non_simple_u (.clk(clk), .rst_n(rst_n), .start(start));
|
||||
fsm_ambiguous ambiguous_u (.clk(clk), .rst_n(rst_n), .start(start));
|
||||
fsm_ignored ignored_u (.clk(clk), .rst_n(rst_n), .start(start));
|
||||
endmodule
|
||||
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: FSM coverage for fsm_register_wrapper state register wrappers
|
||||
#
|
||||
# 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-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import os
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=['--cc --coverage-fsm t/t_fsm_register_wrapper.vlt'])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.run(
|
||||
cmd=[
|
||||
os.environ["VERILATOR_ROOT"] + "/bin/verilator_coverage",
|
||||
"--annotate",
|
||||
test.obj_dir + "/annotated",
|
||||
test.obj_dir + "/coverage.dat",
|
||||
],
|
||||
verilator_run=True,
|
||||
)
|
||||
|
||||
test.files_identical(test.obj_dir + "/annotated/" + test.name + ".v", test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,456 @@
|
|||
// DESCRIPTION: Verilator: FSM coverage for fsm_register_wrapper state register wrappers
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module my_fsm_flop #(
|
||||
parameter int Width = 1,
|
||||
parameter logic [Width-1:0] ResetValue = '0
|
||||
) (
|
||||
input logic clk_i,
|
||||
input logic rst_ni,
|
||||
input logic [Width-1:0] state_i,
|
||||
output logic [Width-1:0] state_o
|
||||
);
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (!rst_ni) state_o <= ResetValue;
|
||||
else state_o <= state_i;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module prim_flop #(
|
||||
parameter int Width = 1,
|
||||
parameter logic [Width-1:0] ResetValue = '0
|
||||
) (
|
||||
input logic clk_i,
|
||||
input logic rst_ni,
|
||||
input logic [Width-1:0] d_i,
|
||||
output logic [Width-1:0] q_o
|
||||
);
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (!rst_ni) q_o <= ResetValue;
|
||||
else q_o <= d_i;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module prim_sparse_fsm_flop #(
|
||||
parameter int Width = 1,
|
||||
parameter logic [Width-1:0] ResetValue = '0
|
||||
) (
|
||||
input logic clk_i,
|
||||
input logic rst_ni,
|
||||
input logic [Width-1:0] state_i,
|
||||
output logic [Width-1:0] state_o
|
||||
);
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (!rst_ni) state_o <= ResetValue;
|
||||
else state_o <= state_i;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module odd_fsm_flop #(
|
||||
parameter int Width = 1,
|
||||
parameter logic [Width-1:0] ResetValue = '0
|
||||
) (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic [Width-1:0] din,
|
||||
output logic [Width-1:0] dout
|
||||
);
|
||||
always_ff @(posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) dout <= ResetValue;
|
||||
else dout <= din;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module ambiguous_fsm_flop (
|
||||
input logic clk_i,
|
||||
input logic rst_ni,
|
||||
input logic [1:0] state_i,
|
||||
input logic [1:0] d_i,
|
||||
output logic [1:0] state_o
|
||||
);
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (!rst_ni) state_o <= 2'd0;
|
||||
else state_o <= state_i ^ d_i;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module fsm_auto (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
unique case (state_q)
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
my_fsm_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk_i(clk),
|
||||
.rst_ni(rst_n),
|
||||
.state_i(state_d),
|
||||
.state_o(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_noargs_hint (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
unique case (state_q)
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
my_fsm_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk_i(clk),
|
||||
.rst_ni(rst_n),
|
||||
.state_i(state_d),
|
||||
.state_o(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_prim (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
unique case (state_q)
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
prim_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk_i(clk),
|
||||
.rst_ni(rst_n),
|
||||
.d_i(state_d),
|
||||
.q_o(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_sparse_prim (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [5:0] {
|
||||
S0 = 6'b00_0001,
|
||||
S1 = 6'b10_0100,
|
||||
S2 = 6'b11_0010
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
unique case (state_q)
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
prim_sparse_fsm_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk_i(clk),
|
||||
.rst_ni(rst_n),
|
||||
.state_i(state_d),
|
||||
.state_o(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_ifchain (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
if (state_q == S0) begin
|
||||
state_d = start ? S1 : S0;
|
||||
end
|
||||
else if (state_q == S1) begin
|
||||
state_d = S2;
|
||||
end
|
||||
else begin
|
||||
state_d = S0;
|
||||
end
|
||||
end
|
||||
|
||||
my_fsm_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk_i(clk),
|
||||
.rst_ni(rst_n),
|
||||
.state_i(state_d),
|
||||
.state_o(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_wide_sparse (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [39:0] {
|
||||
S0 = 40'h0000_0000_01,
|
||||
S1 = 40'h8000_0000_02,
|
||||
S2 = 40'hffff_0000_03
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
unique case (state_q)
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
my_fsm_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk_i(clk),
|
||||
.rst_ni(rst_n),
|
||||
.state_i(state_d),
|
||||
.state_o(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_annotated (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
unique case (state_q)
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
odd_fsm_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk(clk),
|
||||
.rst_n(rst_n),
|
||||
.din(state_d),
|
||||
.dout(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_non_simple (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
unique case (state_q)
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
my_fsm_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk_i(clk),
|
||||
.rst_ni(rst_n),
|
||||
.state_i(state_d[1:0]),
|
||||
.state_o(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_ambiguous (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
unique case (state_q)
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
ambiguous_fsm_flop u_state_regs (
|
||||
.clk_i(clk),
|
||||
.rst_ni(rst_n),
|
||||
.state_i(state_d),
|
||||
.d_i(state_d),
|
||||
.state_o(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_ignored (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
unique case (state_q)
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
odd_fsm_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk(clk),
|
||||
.rst_n(rst_n),
|
||||
.din(state_d),
|
||||
.dout(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module t (
|
||||
input logic clk
|
||||
);
|
||||
logic rst_n;
|
||||
logic start;
|
||||
integer cyc;
|
||||
|
||||
initial begin
|
||||
rst_n = 1'b0;
|
||||
start = 1'b0;
|
||||
cyc = 0;
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
cyc <= cyc + 1;
|
||||
if (cyc == 1) rst_n <= 1'b1;
|
||||
if (cyc == 2) start <= 1'b1;
|
||||
if (cyc == 3) start <= 1'b0;
|
||||
if (cyc == 8) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
|
||||
fsm_auto auto_u (.clk(clk), .rst_n(rst_n), .start(start));
|
||||
fsm_noargs_hint noargs_u (.clk(clk), .rst_n(rst_n), .start(start));
|
||||
fsm_prim prim_u (.clk(clk), .rst_n(rst_n), .start(start));
|
||||
fsm_sparse_prim sparse_prim_u (.clk(clk), .rst_n(rst_n), .start(start));
|
||||
fsm_ifchain ifchain_u (.clk(clk), .rst_n(rst_n), .start(start));
|
||||
fsm_wide_sparse wide_sparse_u (.clk(clk), .rst_n(rst_n), .start(start));
|
||||
fsm_annotated annotated_u (.clk(clk), .rst_n(rst_n), .start(start));
|
||||
fsm_non_simple non_simple_u (.clk(clk), .rst_n(rst_n), .start(start));
|
||||
fsm_ambiguous ambiguous_u (.clk(clk), .rst_n(rst_n), .start(start));
|
||||
fsm_ignored ignored_u (.clk(clk), .rst_n(rst_n), .start(start));
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
// DESCRIPTION: Verilator: FSM register wrapper VLT descriptors
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
`verilator_config
|
||||
|
||||
fsm_register_wrapper -module "my_fsm_flop" -d "state_i" -q "state_o" -clock "clk_i" -reset "rst_ni" -reset_value "ResetValue"
|
||||
fsm_register_wrapper -module "prim_flop" -d "d_i" -q "q_o" -clock "clk_i" -reset "rst_ni" -reset_value "ResetValue"
|
||||
fsm_register_wrapper -module "prim_sparse_fsm_flop" -d "state_i" -q "state_o" -clock "clk_i" -reset "rst_ni" -reset_value "ResetValue"
|
||||
fsm_register_wrapper -module "odd_fsm_flop" -d "din" -q "dout" -clock "clk" -reset "rst_n" -reset_value "ResetValue"
|
||||
|
|
@ -0,0 +1,850 @@
|
|||
// // verilator_coverage annotation
|
||||
// DESCRIPTION: Verilator: FSM coverage for non-inlined fsm_register_wrapper state register wrappers
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module my_fsm_flop #(
|
||||
parameter int Width = 1,
|
||||
parameter logic [Width-1:0] ResetValue = '0
|
||||
) (
|
||||
input logic clk_i,
|
||||
input logic rst_ni,
|
||||
input logic [Width-1:0] state_i,
|
||||
output logic [Width-1:0] state_o
|
||||
);
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (!rst_ni) state_o <= ResetValue;
|
||||
else state_o <= state_i;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module prim_flop #(
|
||||
parameter int Width = 1,
|
||||
parameter logic [Width-1:0] ResetValue = '0
|
||||
) (
|
||||
input logic clk_i,
|
||||
input logic rst_ni,
|
||||
input logic [Width-1:0] d_i,
|
||||
output logic [Width-1:0] q_o
|
||||
);
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (!rst_ni) q_o <= ResetValue;
|
||||
else q_o <= d_i;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module prim_sparse_fsm_flop #(
|
||||
parameter int Width = 1,
|
||||
parameter type StateEnumT = logic [Width-1:0],
|
||||
parameter logic [Width-1:0] ResetValue = '0,
|
||||
parameter bit EnableAlertTriggerSVA = 1
|
||||
) (
|
||||
input logic clk_i,
|
||||
input logic rst_ni,
|
||||
input StateEnumT state_i,
|
||||
output StateEnumT state_o
|
||||
);
|
||||
logic unused_alert_sva;
|
||||
logic [Width-1:0] state_raw;
|
||||
|
||||
prim_flop #(
|
||||
.Width(Width),
|
||||
.ResetValue(ResetValue)
|
||||
) u_state_flop (
|
||||
.clk_i(clk_i),
|
||||
.rst_ni(rst_ni),
|
||||
.d_i(state_i),
|
||||
.q_o(state_raw)
|
||||
);
|
||||
|
||||
assign state_o = StateEnumT'(state_raw);
|
||||
assign unused_alert_sva = EnableAlertTriggerSVA;
|
||||
endmodule
|
||||
|
||||
module active_high_fsm_flop #(
|
||||
parameter int Width = 1,
|
||||
parameter logic [Width-1:0] ResetValue = '0
|
||||
) (
|
||||
input logic clk,
|
||||
input logic rst,
|
||||
input logic [Width-1:0] d,
|
||||
output logic [Width-1:0] q
|
||||
);
|
||||
always_ff @(posedge clk or posedge rst) begin
|
||||
if (rst) q <= ResetValue;
|
||||
else q <= d;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module active_high_rst_i_fsm_flop #(
|
||||
parameter int Width = 1,
|
||||
parameter logic [Width-1:0] ResetValue = '0
|
||||
) (
|
||||
input logic clk,
|
||||
input logic rst_i,
|
||||
input logic [Width-1:0] d,
|
||||
output logic [Width-1:0] q
|
||||
);
|
||||
always_ff @(posedge clk or posedge rst_i) begin
|
||||
if (rst_i) q <= ResetValue;
|
||||
else q <= d;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module custom_reset_fsm_flop #(
|
||||
parameter int Width = 1,
|
||||
parameter logic [Width-1:0] ResetValue = '0
|
||||
) (
|
||||
input logic clk,
|
||||
input logic my_reset,
|
||||
input logic [Width-1:0] d,
|
||||
output logic [Width-1:0] q
|
||||
);
|
||||
logic [Width-1:0] d_shadow;
|
||||
|
||||
always_comb begin
|
||||
d_shadow = d;
|
||||
end
|
||||
|
||||
always_ff @(posedge clk or negedge my_reset) begin
|
||||
if (!my_reset) q <= ResetValue;
|
||||
else q <= d_shadow;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module custom_reset_active_high_fsm_flop #(
|
||||
parameter int Width = 1,
|
||||
parameter logic [Width-1:0] ResetValue = '0
|
||||
) (
|
||||
input logic clk,
|
||||
input logic my_reset,
|
||||
input logic [Width-1:0] d,
|
||||
output logic [Width-1:0] q
|
||||
);
|
||||
always_ff @(posedge clk or posedge my_reset) begin
|
||||
if (my_reset) q <= ResetValue;
|
||||
else q <= d;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module no_reset_fsm_flop #(
|
||||
parameter int Width = 1
|
||||
) (
|
||||
input logic clk,
|
||||
input logic [Width-1:0] d,
|
||||
output logic [Width-1:0] q
|
||||
);
|
||||
always_ff @(posedge clk) begin
|
||||
q <= d;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module reset_no_param_fsm_flop #(
|
||||
parameter int Width = 1
|
||||
) (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic [Width-1:0] d,
|
||||
output logic [Width-1:0] q
|
||||
);
|
||||
always_ff @(posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) q <= '0;
|
||||
else q <= d;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module sync_reset_fsm_flop #(
|
||||
parameter int Width = 1,
|
||||
parameter logic [Width-1:0] ResetValue = '0
|
||||
) (
|
||||
input logic clk,
|
||||
input logic my_reset,
|
||||
input logic [Width-1:0] d,
|
||||
output logic [Width-1:0] q
|
||||
);
|
||||
always_ff @(posedge clk) begin
|
||||
if (my_reset) q <= ResetValue;
|
||||
else q <= d;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module ambiguous_fsm_flop (
|
||||
input logic clk_i,
|
||||
input logic rst_ni,
|
||||
input logic [1:0] state_i,
|
||||
input logic [1:0] d_i,
|
||||
output logic [1:0] state_o
|
||||
);
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (!rst_ni) state_o <= 2'd0;
|
||||
else state_o <= state_i ^ d_i;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module fsm_auto (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
%000006 unique case (state_q)
|
||||
// [FSM coverage]
|
||||
%000001 // [fsm_arc t.auto_u.state_q::ANY->S0[reset]] [reset arc, excluded from %]
|
||||
%000006 // [fsm_arc t.auto_u.state_q::S0->S0]
|
||||
%000001 // [fsm_arc t.auto_u.state_q::S0->S1]
|
||||
%000001 // [fsm_arc t.auto_u.state_q::S1->S2]
|
||||
%000001 // [fsm_state t.auto_u.state_q::S0]
|
||||
%000001 // [fsm_state t.auto_u.state_q::S1]
|
||||
%000001 // [fsm_state t.auto_u.state_q::S2]
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
my_fsm_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk_i(clk),
|
||||
.rst_ni(rst_n),
|
||||
.state_i(state_d),
|
||||
.state_o(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_prim (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
%000006 unique case (state_q)
|
||||
// [FSM coverage]
|
||||
%000001 // [fsm_arc t.prim_u.state_q::ANY->S0[reset]] [reset arc, excluded from %]
|
||||
%000006 // [fsm_arc t.prim_u.state_q::S0->S0]
|
||||
%000001 // [fsm_arc t.prim_u.state_q::S0->S1]
|
||||
%000001 // [fsm_arc t.prim_u.state_q::S1->S2]
|
||||
%000001 // [fsm_state t.prim_u.state_q::S0]
|
||||
%000001 // [fsm_state t.prim_u.state_q::S1]
|
||||
%000001 // [fsm_state t.prim_u.state_q::S2]
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
prim_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk_i(clk),
|
||||
.rst_ni(rst_n),
|
||||
.d_i(state_d),
|
||||
.q_o(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_prim_override (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
%000006 unique case (state_q)
|
||||
// [FSM coverage]
|
||||
%000001 // [fsm_arc t.prim_override_u.state_q::ANY->S0[reset]] [reset arc, excluded from %]
|
||||
%000006 // [fsm_arc t.prim_override_u.state_q::S0->S0]
|
||||
%000001 // [fsm_arc t.prim_override_u.state_q::S0->S1]
|
||||
%000001 // [fsm_arc t.prim_override_u.state_q::S1->S2]
|
||||
%000001 // [fsm_state t.prim_override_u.state_q::S0]
|
||||
%000001 // [fsm_state t.prim_override_u.state_q::S1]
|
||||
%000001 // [fsm_state t.prim_override_u.state_q::S2]
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
prim_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk_i(clk),
|
||||
.rst_ni(rst_n),
|
||||
.d_i(state_d),
|
||||
.q_o(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_sparse_prim (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [5:0] {
|
||||
S0 = 6'b00_0001,
|
||||
S1 = 6'b10_0100,
|
||||
S2 = 6'b11_0010
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
%000005 unique case (state_q)
|
||||
// [FSM coverage]
|
||||
%000005 // [fsm_arc t.sparse_prim_u.state_q::S0->S0]
|
||||
%000001 // [fsm_arc t.sparse_prim_u.state_q::S0->S1]
|
||||
%000001 // [fsm_arc t.sparse_prim_u.state_q::S1->S2]
|
||||
%000002 // [fsm_state t.sparse_prim_u.state_q::S0]
|
||||
%000001 // [fsm_state t.sparse_prim_u.state_q::S1]
|
||||
%000001 // [fsm_state t.sparse_prim_u.state_q::S2]
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
prim_sparse_fsm_flop #(
|
||||
.StateEnumT(state_t),
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk_i(clk),
|
||||
.rst_ni(rst_n),
|
||||
.state_i(state_d),
|
||||
.state_o(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_sparse_prim_override (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [5:0] {
|
||||
S0 = 6'b00_0001,
|
||||
S1 = 6'b10_0100,
|
||||
S2 = 6'b11_0010
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
%000005 unique case (state_q)
|
||||
// [FSM coverage]
|
||||
%000005 // [fsm_arc t.sparse_prim_override_u.state_q::S0->S0]
|
||||
%000001 // [fsm_arc t.sparse_prim_override_u.state_q::S0->S1]
|
||||
%000001 // [fsm_arc t.sparse_prim_override_u.state_q::S1->S2]
|
||||
%000002 // [fsm_state t.sparse_prim_override_u.state_q::S0]
|
||||
%000001 // [fsm_state t.sparse_prim_override_u.state_q::S1]
|
||||
%000001 // [fsm_state t.sparse_prim_override_u.state_q::S2]
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
prim_sparse_fsm_flop #(
|
||||
.StateEnumT(state_t),
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk_i(clk),
|
||||
.rst_ni(rst_n),
|
||||
.state_i(state_d),
|
||||
.state_o(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_active_high (
|
||||
input logic clk,
|
||||
input logic rst,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
%000007 unique case (state_q)
|
||||
// [FSM coverage]
|
||||
%000002 // [fsm_arc t.active_high_u.state_q::ANY->S0[reset]] [reset arc, excluded from %]
|
||||
%000007 // [fsm_arc t.active_high_u.state_q::S0->S0]
|
||||
%000001 // [fsm_arc t.active_high_u.state_q::S0->S1]
|
||||
%000001 // [fsm_arc t.active_high_u.state_q::S1->S2]
|
||||
%000001 // [fsm_state t.active_high_u.state_q::S0]
|
||||
%000001 // [fsm_state t.active_high_u.state_q::S1]
|
||||
%000001 // [fsm_state t.active_high_u.state_q::S2]
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
active_high_fsm_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
.d(state_d),
|
||||
.q(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_active_high_rst_i (
|
||||
input logic clk,
|
||||
input logic rst,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
%000007 unique case (state_q)
|
||||
// [FSM coverage]
|
||||
%000002 // [fsm_arc t.active_high_rst_i_u.state_q::ANY->S0[reset]] [reset arc, excluded from %]
|
||||
%000007 // [fsm_arc t.active_high_rst_i_u.state_q::S0->S0]
|
||||
%000001 // [fsm_arc t.active_high_rst_i_u.state_q::S0->S1]
|
||||
%000001 // [fsm_arc t.active_high_rst_i_u.state_q::S1->S2]
|
||||
%000001 // [fsm_state t.active_high_rst_i_u.state_q::S0]
|
||||
%000001 // [fsm_state t.active_high_rst_i_u.state_q::S1]
|
||||
%000001 // [fsm_state t.active_high_rst_i_u.state_q::S2]
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
active_high_rst_i_fsm_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk(clk),
|
||||
.rst_i(rst),
|
||||
.d(state_d),
|
||||
.q(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_no_reset (
|
||||
input logic clk,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
%000006 unique case (state_q)
|
||||
// [FSM coverage]
|
||||
%000006 // [fsm_arc t.no_reset_u.state_q::S0->S0]
|
||||
%000001 // [fsm_arc t.no_reset_u.state_q::S0->S1]
|
||||
%000001 // [fsm_arc t.no_reset_u.state_q::S1->S2]
|
||||
%000001 // [fsm_state t.no_reset_u.state_q::S0]
|
||||
%000001 // [fsm_state t.no_reset_u.state_q::S1]
|
||||
%000001 // [fsm_state t.no_reset_u.state_q::S2]
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
no_reset_fsm_flop #(
|
||||
.Width($bits(state_t))
|
||||
) u_state_regs (
|
||||
.clk(clk),
|
||||
.d(state_d),
|
||||
.q(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_reset_no_param (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
%000006 unique case (state_q)
|
||||
// [FSM coverage]
|
||||
%000006 // [fsm_arc t.reset_no_param_u.state_q::S0->S0]
|
||||
%000001 // [fsm_arc t.reset_no_param_u.state_q::S0->S1]
|
||||
%000001 // [fsm_arc t.reset_no_param_u.state_q::S1->S2]
|
||||
%000001 // [fsm_state t.reset_no_param_u.state_q::S0]
|
||||
%000001 // [fsm_state t.reset_no_param_u.state_q::S1]
|
||||
%000001 // [fsm_state t.reset_no_param_u.state_q::S2]
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
reset_no_param_fsm_flop #(
|
||||
.Width($bits(state_t))
|
||||
) u_state_regs (
|
||||
.clk(clk),
|
||||
.rst_n(rst_n),
|
||||
.d(state_d),
|
||||
.q(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_sync_reset_unknown_polarity (
|
||||
input logic clk,
|
||||
input logic parent_reset,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
%000006 unique case (state_q)
|
||||
// [FSM coverage]
|
||||
%000006 // [fsm_arc t.sync_reset_u.state_q::S0->S0]
|
||||
%000001 // [fsm_arc t.sync_reset_u.state_q::S0->S1]
|
||||
%000001 // [fsm_arc t.sync_reset_u.state_q::S1->S2]
|
||||
%000001 // [fsm_state t.sync_reset_u.state_q::S0]
|
||||
%000001 // [fsm_state t.sync_reset_u.state_q::S1]
|
||||
%000001 // [fsm_state t.sync_reset_u.state_q::S2]
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
sync_reset_fsm_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk(clk),
|
||||
.my_reset(parent_reset),
|
||||
.d(state_d),
|
||||
.q(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_custom_reset_parent_polarity (
|
||||
input logic clk,
|
||||
input logic parent_reset,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
%000006 unique case (state_q)
|
||||
// [FSM coverage]
|
||||
%000001 // [fsm_arc t.custom_reset_u.state_q::ANY->S0[reset]] [reset arc, excluded from %]
|
||||
%000006 // [fsm_arc t.custom_reset_u.state_q::S0->S0]
|
||||
%000001 // [fsm_arc t.custom_reset_u.state_q::S0->S1]
|
||||
%000001 // [fsm_arc t.custom_reset_u.state_q::S1->S2]
|
||||
%000001 // [fsm_state t.custom_reset_u.state_q::S0]
|
||||
%000001 // [fsm_state t.custom_reset_u.state_q::S1]
|
||||
%000001 // [fsm_state t.custom_reset_u.state_q::S2]
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
custom_reset_fsm_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk(clk),
|
||||
.my_reset(parent_reset),
|
||||
.d(state_d),
|
||||
.q(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_custom_reset_active_high (
|
||||
input logic clk,
|
||||
input logic parent_reset,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
%000007 unique case (state_q)
|
||||
// [FSM coverage]
|
||||
%000002 // [fsm_arc t.custom_reset_active_high_u.state_q::ANY->S0[reset]] [reset arc, excluded from %]
|
||||
%000007 // [fsm_arc t.custom_reset_active_high_u.state_q::S0->S0]
|
||||
%000001 // [fsm_arc t.custom_reset_active_high_u.state_q::S0->S1]
|
||||
%000001 // [fsm_arc t.custom_reset_active_high_u.state_q::S1->S2]
|
||||
%000001 // [fsm_state t.custom_reset_active_high_u.state_q::S0]
|
||||
%000001 // [fsm_state t.custom_reset_active_high_u.state_q::S1]
|
||||
%000001 // [fsm_state t.custom_reset_active_high_u.state_q::S2]
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
custom_reset_active_high_fsm_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk(clk),
|
||||
.my_reset(parent_reset),
|
||||
.d(state_d),
|
||||
.q(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_ambiguous_ignored (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
unique case (state_q)
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
ambiguous_fsm_flop u_state_regs (
|
||||
.clk_i(clk),
|
||||
.rst_ni(rst_n),
|
||||
.state_i(state_d),
|
||||
.d_i(state_d),
|
||||
.state_o(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_missing_reset_param (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
%000006 unique case (state_q)
|
||||
// [FSM coverage]
|
||||
%000001 // [fsm_arc t.missing_reset_u.state_q::ANY->S0[reset]] [reset arc, excluded from %]
|
||||
%000006 // [fsm_arc t.missing_reset_u.state_q::S0->S0]
|
||||
%000001 // [fsm_arc t.missing_reset_u.state_q::S0->S1]
|
||||
%000001 // [fsm_arc t.missing_reset_u.state_q::S1->S2]
|
||||
%000001 // [fsm_state t.missing_reset_u.state_q::S0]
|
||||
%000001 // [fsm_state t.missing_reset_u.state_q::S1]
|
||||
%000001 // [fsm_state t.missing_reset_u.state_q::S2]
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
my_fsm_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk_i(clk),
|
||||
.rst_ni(rst_n),
|
||||
.state_i(state_d),
|
||||
.state_o(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_competing_direct (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic start
|
||||
);
|
||||
/* verilator lint_off BLKANDNBLK */
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
%000006 unique case (state_q)
|
||||
// [FSM coverage]
|
||||
%000001 // [fsm_arc t.competing_u.state_q::ANY->S0[reset]] [reset arc, excluded from %]
|
||||
%000006 // [fsm_arc t.competing_u.state_q::S0->S0]
|
||||
%000001 // [fsm_arc t.competing_u.state_q::S0->S1]
|
||||
%000001 // [fsm_arc t.competing_u.state_q::S1->S2]
|
||||
%000001 // [fsm_state t.competing_u.state_q::S0]
|
||||
%000001 // [fsm_state t.competing_u.state_q::S1]
|
||||
%000001 // [fsm_state t.competing_u.state_q::S2]
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
always_ff @(posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) state_q <= S0;
|
||||
else state_q <= state_d;
|
||||
end
|
||||
|
||||
my_fsm_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk_i(clk),
|
||||
.rst_ni(rst_n),
|
||||
.state_i(state_d),
|
||||
.state_o(state_q)
|
||||
);
|
||||
/* verilator lint_on BLKANDNBLK */
|
||||
endmodule
|
||||
|
||||
module t (
|
||||
input logic clk
|
||||
);
|
||||
logic rst_n;
|
||||
logic rst;
|
||||
logic start;
|
||||
integer cyc;
|
||||
|
||||
initial begin
|
||||
rst_n = 1'b0;
|
||||
rst = 1'b1;
|
||||
start = 1'b0;
|
||||
cyc = 0;
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
cyc <= cyc + 1;
|
||||
if (cyc == 1) rst_n <= 1'b1;
|
||||
if (cyc == 1) rst <= 1'b0;
|
||||
if (cyc == 2) start <= 1'b1;
|
||||
if (cyc == 3) start <= 1'b0;
|
||||
if (cyc == 8) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
|
||||
fsm_auto auto_u (.clk(clk), .rst_n(rst_n), .start(start));
|
||||
fsm_prim prim_u (.clk(clk), .rst_n(rst_n), .start(start));
|
||||
fsm_prim_override prim_override_u (.clk(clk), .rst_n(rst_n), .start(start));
|
||||
fsm_sparse_prim sparse_prim_u (.clk(clk), .rst_n(rst_n), .start(start));
|
||||
fsm_sparse_prim_override sparse_prim_override_u (.clk(clk), .rst_n(rst_n), .start(start));
|
||||
fsm_active_high active_high_u (.clk(clk), .rst(rst), .start(start));
|
||||
fsm_active_high_rst_i active_high_rst_i_u (.clk(clk), .rst(rst), .start(start));
|
||||
fsm_no_reset no_reset_u (.clk(clk), .start(start));
|
||||
fsm_reset_no_param reset_no_param_u (.clk(clk), .rst_n(rst_n), .start(start));
|
||||
fsm_sync_reset_unknown_polarity sync_reset_u (.clk(clk), .parent_reset(rst), .start(start));
|
||||
fsm_custom_reset_parent_polarity custom_reset_u (.clk(clk), .parent_reset(rst_n), .start(start));
|
||||
fsm_custom_reset_active_high custom_reset_active_high_u (
|
||||
.clk(clk), .parent_reset(rst), .start(start));
|
||||
fsm_ambiguous_ignored ambiguous_u (.clk(clk), .rst_n(rst_n), .start(start));
|
||||
fsm_missing_reset_param missing_reset_u (.clk(clk), .rst_n(rst_n), .start(start));
|
||||
fsm_competing_direct competing_u (.clk(clk), .rst_n(rst_n), .start(start));
|
||||
endmodule
|
||||
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: FSM coverage for non-inlined fsm_register_wrapper state register wrappers
|
||||
#
|
||||
# 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-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import os
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=['--cc --coverage-fsm -fno-inline t/t_fsm_register_wrapper_noinline.vlt'])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.run(
|
||||
cmd=[
|
||||
os.environ["VERILATOR_ROOT"] + "/bin/verilator_coverage",
|
||||
"--annotate",
|
||||
test.obj_dir + "/annotated",
|
||||
test.obj_dir + "/coverage.dat",
|
||||
],
|
||||
verilator_run=True,
|
||||
)
|
||||
|
||||
test.files_identical(test.obj_dir + "/annotated/" + test.name + ".v", test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,741 @@
|
|||
// DESCRIPTION: Verilator: FSM coverage for non-inlined fsm_register_wrapper state register wrappers
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module my_fsm_flop #(
|
||||
parameter int Width = 1,
|
||||
parameter logic [Width-1:0] ResetValue = '0
|
||||
) (
|
||||
input logic clk_i,
|
||||
input logic rst_ni,
|
||||
input logic [Width-1:0] state_i,
|
||||
output logic [Width-1:0] state_o
|
||||
);
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (!rst_ni) state_o <= ResetValue;
|
||||
else state_o <= state_i;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module prim_flop #(
|
||||
parameter int Width = 1,
|
||||
parameter logic [Width-1:0] ResetValue = '0
|
||||
) (
|
||||
input logic clk_i,
|
||||
input logic rst_ni,
|
||||
input logic [Width-1:0] d_i,
|
||||
output logic [Width-1:0] q_o
|
||||
);
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (!rst_ni) q_o <= ResetValue;
|
||||
else q_o <= d_i;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module prim_sparse_fsm_flop #(
|
||||
parameter int Width = 1,
|
||||
parameter type StateEnumT = logic [Width-1:0],
|
||||
parameter logic [Width-1:0] ResetValue = '0,
|
||||
parameter bit EnableAlertTriggerSVA = 1
|
||||
) (
|
||||
input logic clk_i,
|
||||
input logic rst_ni,
|
||||
input StateEnumT state_i,
|
||||
output StateEnumT state_o
|
||||
);
|
||||
logic unused_alert_sva;
|
||||
logic [Width-1:0] state_raw;
|
||||
|
||||
prim_flop #(
|
||||
.Width(Width),
|
||||
.ResetValue(ResetValue)
|
||||
) u_state_flop (
|
||||
.clk_i(clk_i),
|
||||
.rst_ni(rst_ni),
|
||||
.d_i(state_i),
|
||||
.q_o(state_raw)
|
||||
);
|
||||
|
||||
assign state_o = StateEnumT'(state_raw);
|
||||
assign unused_alert_sva = EnableAlertTriggerSVA;
|
||||
endmodule
|
||||
|
||||
module active_high_fsm_flop #(
|
||||
parameter int Width = 1,
|
||||
parameter logic [Width-1:0] ResetValue = '0
|
||||
) (
|
||||
input logic clk,
|
||||
input logic rst,
|
||||
input logic [Width-1:0] d,
|
||||
output logic [Width-1:0] q
|
||||
);
|
||||
always_ff @(posedge clk or posedge rst) begin
|
||||
if (rst) q <= ResetValue;
|
||||
else q <= d;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module active_high_rst_i_fsm_flop #(
|
||||
parameter int Width = 1,
|
||||
parameter logic [Width-1:0] ResetValue = '0
|
||||
) (
|
||||
input logic clk,
|
||||
input logic rst_i,
|
||||
input logic [Width-1:0] d,
|
||||
output logic [Width-1:0] q
|
||||
);
|
||||
always_ff @(posedge clk or posedge rst_i) begin
|
||||
if (rst_i) q <= ResetValue;
|
||||
else q <= d;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module custom_reset_fsm_flop #(
|
||||
parameter int Width = 1,
|
||||
parameter logic [Width-1:0] ResetValue = '0
|
||||
) (
|
||||
input logic clk,
|
||||
input logic my_reset,
|
||||
input logic [Width-1:0] d,
|
||||
output logic [Width-1:0] q
|
||||
);
|
||||
logic [Width-1:0] d_shadow;
|
||||
|
||||
always_comb begin
|
||||
d_shadow = d;
|
||||
end
|
||||
|
||||
always_ff @(posedge clk or negedge my_reset) begin
|
||||
if (!my_reset) q <= ResetValue;
|
||||
else q <= d_shadow;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module custom_reset_active_high_fsm_flop #(
|
||||
parameter int Width = 1,
|
||||
parameter logic [Width-1:0] ResetValue = '0
|
||||
) (
|
||||
input logic clk,
|
||||
input logic my_reset,
|
||||
input logic [Width-1:0] d,
|
||||
output logic [Width-1:0] q
|
||||
);
|
||||
always_ff @(posedge clk or posedge my_reset) begin
|
||||
if (my_reset) q <= ResetValue;
|
||||
else q <= d;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module no_reset_fsm_flop #(
|
||||
parameter int Width = 1
|
||||
) (
|
||||
input logic clk,
|
||||
input logic [Width-1:0] d,
|
||||
output logic [Width-1:0] q
|
||||
);
|
||||
always_ff @(posedge clk) begin
|
||||
q <= d;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module reset_no_param_fsm_flop #(
|
||||
parameter int Width = 1
|
||||
) (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic [Width-1:0] d,
|
||||
output logic [Width-1:0] q
|
||||
);
|
||||
always_ff @(posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) q <= '0;
|
||||
else q <= d;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module sync_reset_fsm_flop #(
|
||||
parameter int Width = 1,
|
||||
parameter logic [Width-1:0] ResetValue = '0
|
||||
) (
|
||||
input logic clk,
|
||||
input logic my_reset,
|
||||
input logic [Width-1:0] d,
|
||||
output logic [Width-1:0] q
|
||||
);
|
||||
always_ff @(posedge clk) begin
|
||||
if (my_reset) q <= ResetValue;
|
||||
else q <= d;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module ambiguous_fsm_flop (
|
||||
input logic clk_i,
|
||||
input logic rst_ni,
|
||||
input logic [1:0] state_i,
|
||||
input logic [1:0] d_i,
|
||||
output logic [1:0] state_o
|
||||
);
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (!rst_ni) state_o <= 2'd0;
|
||||
else state_o <= state_i ^ d_i;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module fsm_auto (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
unique case (state_q)
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
my_fsm_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk_i(clk),
|
||||
.rst_ni(rst_n),
|
||||
.state_i(state_d),
|
||||
.state_o(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_prim (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
unique case (state_q)
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
prim_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk_i(clk),
|
||||
.rst_ni(rst_n),
|
||||
.d_i(state_d),
|
||||
.q_o(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_prim_override (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
unique case (state_q)
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
prim_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk_i(clk),
|
||||
.rst_ni(rst_n),
|
||||
.d_i(state_d),
|
||||
.q_o(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_sparse_prim (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [5:0] {
|
||||
S0 = 6'b00_0001,
|
||||
S1 = 6'b10_0100,
|
||||
S2 = 6'b11_0010
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
unique case (state_q)
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
prim_sparse_fsm_flop #(
|
||||
.StateEnumT(state_t),
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk_i(clk),
|
||||
.rst_ni(rst_n),
|
||||
.state_i(state_d),
|
||||
.state_o(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_sparse_prim_override (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [5:0] {
|
||||
S0 = 6'b00_0001,
|
||||
S1 = 6'b10_0100,
|
||||
S2 = 6'b11_0010
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
unique case (state_q)
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
prim_sparse_fsm_flop #(
|
||||
.StateEnumT(state_t),
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk_i(clk),
|
||||
.rst_ni(rst_n),
|
||||
.state_i(state_d),
|
||||
.state_o(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_active_high (
|
||||
input logic clk,
|
||||
input logic rst,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
unique case (state_q)
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
active_high_fsm_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
.d(state_d),
|
||||
.q(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_active_high_rst_i (
|
||||
input logic clk,
|
||||
input logic rst,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
unique case (state_q)
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
active_high_rst_i_fsm_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk(clk),
|
||||
.rst_i(rst),
|
||||
.d(state_d),
|
||||
.q(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_no_reset (
|
||||
input logic clk,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
unique case (state_q)
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
no_reset_fsm_flop #(
|
||||
.Width($bits(state_t))
|
||||
) u_state_regs (
|
||||
.clk(clk),
|
||||
.d(state_d),
|
||||
.q(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_reset_no_param (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
unique case (state_q)
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
reset_no_param_fsm_flop #(
|
||||
.Width($bits(state_t))
|
||||
) u_state_regs (
|
||||
.clk(clk),
|
||||
.rst_n(rst_n),
|
||||
.d(state_d),
|
||||
.q(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_sync_reset_unknown_polarity (
|
||||
input logic clk,
|
||||
input logic parent_reset,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
unique case (state_q)
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
sync_reset_fsm_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk(clk),
|
||||
.my_reset(parent_reset),
|
||||
.d(state_d),
|
||||
.q(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_custom_reset_parent_polarity (
|
||||
input logic clk,
|
||||
input logic parent_reset,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
unique case (state_q)
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
custom_reset_fsm_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk(clk),
|
||||
.my_reset(parent_reset),
|
||||
.d(state_d),
|
||||
.q(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_custom_reset_active_high (
|
||||
input logic clk,
|
||||
input logic parent_reset,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
unique case (state_q)
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
custom_reset_active_high_fsm_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk(clk),
|
||||
.my_reset(parent_reset),
|
||||
.d(state_d),
|
||||
.q(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_ambiguous_ignored (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
unique case (state_q)
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
ambiguous_fsm_flop u_state_regs (
|
||||
.clk_i(clk),
|
||||
.rst_ni(rst_n),
|
||||
.state_i(state_d),
|
||||
.d_i(state_d),
|
||||
.state_o(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_missing_reset_param (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic start
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
unique case (state_q)
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
my_fsm_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk_i(clk),
|
||||
.rst_ni(rst_n),
|
||||
.state_i(state_d),
|
||||
.state_o(state_q)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module fsm_competing_direct (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic start
|
||||
);
|
||||
/* verilator lint_off BLKANDNBLK */
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1,
|
||||
S2 = 2'd2
|
||||
} state_t;
|
||||
|
||||
state_t state_q;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
unique case (state_q)
|
||||
S0: state_d = start ? S1 : S0;
|
||||
S1: state_d = S2;
|
||||
default: state_d = S0;
|
||||
endcase
|
||||
end
|
||||
|
||||
always_ff @(posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) state_q <= S0;
|
||||
else state_q <= state_d;
|
||||
end
|
||||
|
||||
my_fsm_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_state_regs (
|
||||
.clk_i(clk),
|
||||
.rst_ni(rst_n),
|
||||
.state_i(state_d),
|
||||
.state_o(state_q)
|
||||
);
|
||||
/* verilator lint_on BLKANDNBLK */
|
||||
endmodule
|
||||
|
||||
module t (
|
||||
input logic clk
|
||||
);
|
||||
logic rst_n;
|
||||
logic rst;
|
||||
logic start;
|
||||
integer cyc;
|
||||
|
||||
initial begin
|
||||
rst_n = 1'b0;
|
||||
rst = 1'b1;
|
||||
start = 1'b0;
|
||||
cyc = 0;
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
cyc <= cyc + 1;
|
||||
if (cyc == 1) rst_n <= 1'b1;
|
||||
if (cyc == 1) rst <= 1'b0;
|
||||
if (cyc == 2) start <= 1'b1;
|
||||
if (cyc == 3) start <= 1'b0;
|
||||
if (cyc == 8) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
|
||||
fsm_auto auto_u (.clk(clk), .rst_n(rst_n), .start(start));
|
||||
fsm_prim prim_u (.clk(clk), .rst_n(rst_n), .start(start));
|
||||
fsm_prim_override prim_override_u (.clk(clk), .rst_n(rst_n), .start(start));
|
||||
fsm_sparse_prim sparse_prim_u (.clk(clk), .rst_n(rst_n), .start(start));
|
||||
fsm_sparse_prim_override sparse_prim_override_u (.clk(clk), .rst_n(rst_n), .start(start));
|
||||
fsm_active_high active_high_u (.clk(clk), .rst(rst), .start(start));
|
||||
fsm_active_high_rst_i active_high_rst_i_u (.clk(clk), .rst(rst), .start(start));
|
||||
fsm_no_reset no_reset_u (.clk(clk), .start(start));
|
||||
fsm_reset_no_param reset_no_param_u (.clk(clk), .rst_n(rst_n), .start(start));
|
||||
fsm_sync_reset_unknown_polarity sync_reset_u (.clk(clk), .parent_reset(rst), .start(start));
|
||||
fsm_custom_reset_parent_polarity custom_reset_u (.clk(clk), .parent_reset(rst_n), .start(start));
|
||||
fsm_custom_reset_active_high custom_reset_active_high_u (
|
||||
.clk(clk), .parent_reset(rst), .start(start));
|
||||
fsm_ambiguous_ignored ambiguous_u (.clk(clk), .rst_n(rst_n), .start(start));
|
||||
fsm_missing_reset_param missing_reset_u (.clk(clk), .rst_n(rst_n), .start(start));
|
||||
fsm_competing_direct competing_u (.clk(clk), .rst_n(rst_n), .start(start));
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
// DESCRIPTION: Verilator: FSM register wrapper VLT descriptors
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
`verilator_config
|
||||
|
||||
fsm_register_wrapper -module "my_fsm_flop" -d "state_i" -q "state_o" -clock "clk_i" -reset "rst_ni" -reset_value "ResetValue"
|
||||
fsm_register_wrapper -module "prim_flop" -d "d_i" -q "q_o" -clock "clk_i" -reset "rst_ni" -reset_value "ResetValue"
|
||||
fsm_register_wrapper -module "prim_sparse_fsm_flop" -d "state_i" -q "state_o" -clock "clk_i"
|
||||
fsm_register_wrapper -module "active_high_fsm_flop" -d "d" -q "q" -clock "clk" -reset "rst" -reset_value "ResetValue"
|
||||
fsm_register_wrapper -module "active_high_rst_i_fsm_flop" -d "d" -q "q" -clock "clk" -reset "rst_i" -reset_value "ResetValue"
|
||||
fsm_register_wrapper -module "custom_reset_fsm_flop" -d "d" -q "q" -clock "clk" -reset "my_reset" -reset_value "ResetValue"
|
||||
fsm_register_wrapper -module "custom_reset_active_high_fsm_flop" -d "d" -q "q" -clock "clk" -reset "my_reset" -reset_value "ResetValue"
|
||||
fsm_register_wrapper -module "no_reset_fsm_flop" -d "d" -q "q" -clock "clk"
|
||||
fsm_register_wrapper -module "reset_no_param_fsm_flop" -d "d" -q "q" -clock "clk"
|
||||
fsm_register_wrapper -module "sync_reset_fsm_flop" -d "d" -q "q" -clock "clk"
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
%Error: t/t_fsm_register_wrapper_vlt_bad.vlt:9:1: fsm_register_wrapper missing -module
|
||||
9 | fsm_register_wrapper -module "" -d "din" -q "dout" -clock "clk"
|
||||
| ^~~~~~~~~~~~~~~~~~~~
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
%Error: t/t_fsm_register_wrapper_vlt_bad.vlt:10:1: fsm_register_wrapper missing -module, -d, -q, -clock
|
||||
10 | fsm_register_wrapper -module "" -d "" -q "" -clock ""
|
||||
| ^~~~~~~~~~~~~~~~~~~~
|
||||
%Error: t/t_fsm_register_wrapper_vlt_bad.vlt:11:1: fsm_register_wrapper missing -d
|
||||
11 | fsm_register_wrapper -module "missing_d_fsm_flop" -d "" -q "dout" -clock "clk"
|
||||
| ^~~~~~~~~~~~~~~~~~~~
|
||||
%Error: t/t_fsm_register_wrapper_vlt_bad.vlt:12:1: fsm_register_wrapper missing -q
|
||||
12 | fsm_register_wrapper -module "missing_q_fsm_flop" -d "din" -q "" -clock "clk"
|
||||
| ^~~~~~~~~~~~~~~~~~~~
|
||||
%Error: t/t_fsm_register_wrapper_vlt_bad.vlt:13:1: fsm_register_wrapper missing -clock
|
||||
13 | fsm_register_wrapper -module "missing_clock_fsm_flop" -d "din" -q "dout" -clock ""
|
||||
| ^~~~~~~~~~~~~~~~~~~~
|
||||
%Error-BADVLTPRAGMA: t/t_fsm_register_wrapper_vlt_bad.vlt:15:1: Duplicate fsm_register_wrapper descriptor for module 'duplicate_fsm_flop'; replacing previous descriptor
|
||||
15 | fsm_register_wrapper -module "duplicate_fsm_flop" -d "din" -q "dout" -clock "clk"
|
||||
| ^~~~~~~~~~~~~~~~~~~~
|
||||
... For error description see https://verilator.org/warn/BADVLTPRAGMA?v=latest
|
||||
%Error: Exiting due to
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: FSM register wrapper VLT bad descriptor coverage
|
||||
#
|
||||
# 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-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
test.lint(
|
||||
verilator_flags2=["t/t_fsm_register_wrapper_vlt_bad.vlt"],
|
||||
fails=True,
|
||||
expect_filename=test.golden_filename,
|
||||
)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
// DESCRIPTION: Verilator: FSM register wrapper VLT bad descriptor coverage
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module t;
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
// DESCRIPTION: Verilator: FSM register wrapper VLT bad descriptor coverage
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
`verilator_config
|
||||
|
||||
fsm_register_wrapper -module "" -d "din" -q "dout" -clock "clk"
|
||||
fsm_register_wrapper -module "" -d "" -q "" -clock ""
|
||||
fsm_register_wrapper -module "missing_d_fsm_flop" -d "" -q "dout" -clock "clk"
|
||||
fsm_register_wrapper -module "missing_q_fsm_flop" -d "din" -q "" -clock "clk"
|
||||
fsm_register_wrapper -module "missing_clock_fsm_flop" -d "din" -q "dout" -clock ""
|
||||
fsm_register_wrapper -module "duplicate_fsm_flop" -d "din" -q "dout" -clock "clk"
|
||||
fsm_register_wrapper -module "duplicate_fsm_flop" -d "din" -q "dout" -clock "clk"
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
%Warning-COVERIGN: t/t_fsm_register_wrapper_warn_bad.v:156:5: Ignoring unsupported: fsm_register_wrapper d and q connections must be simple variables
|
||||
: ... note: In instance 't'
|
||||
156 | ) u_bad_simple (
|
||||
| ^~~~~~~~~~~~
|
||||
... For warning description see https://verilator.org/warn/COVERIGN?v=latest
|
||||
... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message.
|
||||
%Warning-COVERIGN: t/t_fsm_register_wrapper_warn_bad.v:166:5: Ignoring unsupported: fsm_register_wrapper d and q connections must be simple variables
|
||||
: ... note: In instance 't'
|
||||
166 | ) u_missing_q_port (
|
||||
| ^~~~~~~~~~~~~~~~
|
||||
%Warning-COVERIGN: t/t_fsm_register_wrapper_warn_bad.v:176:5: Ignoring unsupported: fsm_register_wrapper instance requires a simple clock connection
|
||||
: ... note: In instance 't'
|
||||
176 | ) u_missing_clock_port (
|
||||
| ^~~~~~~~~~~~~~~~~~~~
|
||||
%Warning-COVERIGN: t/t_fsm_register_wrapper_warn_bad.v:186:5: Ignoring unsupported: fsm_register_wrapper reset arcs require both reset polarity and static reset value; reset_value parameter is not configured
|
||||
: ... note: In instance 't'
|
||||
186 | ) u_partial_reset (
|
||||
| ^~~~~~~~~~~~~~~
|
||||
%Warning-COVERIGN: t/t_fsm_register_wrapper_warn_bad.v:196:5: Ignoring unsupported: fsm_register_wrapper reset arcs require both reset polarity and static reset value; reset_value parameter is missing or not static
|
||||
: ... note: In instance 't'
|
||||
196 | ) u_missing_reset_value (
|
||||
| ^~~~~~~~~~~~~~~~~~~~~
|
||||
%Warning-COVERIGN: t/t_fsm_register_wrapper_warn_bad.v:206:5: Ignoring unsupported: fsm_register_wrapper reset arcs require both reset polarity and static reset value; reset port is not configured
|
||||
: ... note: In instance 't'
|
||||
206 | ) u_value_no_reset (
|
||||
| ^~~~~~~~~~~~~~~~
|
||||
%Warning-COVERIGN: t/t_fsm_register_wrapper_warn_bad.v:216:5: Ignoring unsupported: fsm_register_wrapper reset arcs require both reset polarity and static reset value; reset polarity could not be inferred from the wrapper
|
||||
: ... note: In instance 't'
|
||||
216 | ) u_sync_reset (
|
||||
| ^~~~~~~~~~~~
|
||||
%Warning-COVERIGN: t/t_fsm_register_wrapper_warn_bad.v:226:5: Ignoring unsupported: fsm_register_wrapper reset arcs require both reset polarity and static reset value; reset connection is missing or not a simple variable
|
||||
: ... note: In instance 't'
|
||||
226 | ) u_missing_reset_connection (
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
%Error: Exiting due to
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: FSM register wrapper VLT warning coverage
|
||||
#
|
||||
# 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-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
test.lint(
|
||||
verilator_flags2=["--coverage-fsm", "-fno-inline", "t/t_fsm_register_wrapper_warn_bad.vlt"],
|
||||
fails=True,
|
||||
expect_filename=test.golden_filename,
|
||||
)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,232 @@
|
|||
// DESCRIPTION: Verilator: FSM register wrapper VLT warning coverage
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module odd_fsm_flop #(
|
||||
parameter int Width = 1,
|
||||
parameter logic [Width-1:0] ResetValue = '0
|
||||
) (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic [Width-1:0] din,
|
||||
output logic [Width-1:0] dout
|
||||
);
|
||||
always_ff @(posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) dout <= ResetValue;
|
||||
else dout <= din;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module missing_q_fsm_flop #(
|
||||
parameter int Width = 1,
|
||||
parameter logic [Width-1:0] ResetValue = '0
|
||||
) (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic [Width-1:0] din,
|
||||
output logic [Width-1:0] dout
|
||||
);
|
||||
always_ff @(posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) dout <= ResetValue;
|
||||
else dout <= din;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module missing_clock_fsm_flop #(
|
||||
parameter int Width = 1,
|
||||
parameter logic [Width-1:0] ResetValue = '0
|
||||
) (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic [Width-1:0] din,
|
||||
output logic [Width-1:0] dout
|
||||
);
|
||||
always_ff @(posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) dout <= ResetValue;
|
||||
else dout <= din;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module partial_reset_fsm_flop #(
|
||||
parameter int Width = 1,
|
||||
parameter logic [Width-1:0] ResetValue = '0
|
||||
) (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic [Width-1:0] din,
|
||||
output logic [Width-1:0] dout
|
||||
);
|
||||
always_ff @(posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) dout <= ResetValue;
|
||||
else dout <= din;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module missing_reset_value_fsm_flop #(
|
||||
parameter int Width = 1,
|
||||
parameter logic [Width-1:0] ResetValue = '0
|
||||
) (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic [Width-1:0] din,
|
||||
output logic [Width-1:0] dout
|
||||
);
|
||||
always_ff @(posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) dout <= ResetValue;
|
||||
else dout <= din;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module value_no_reset_fsm_flop #(
|
||||
parameter int Width = 1,
|
||||
parameter logic [Width-1:0] ResetValue = '0
|
||||
) (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic [Width-1:0] din,
|
||||
output logic [Width-1:0] dout
|
||||
);
|
||||
always_ff @(posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) dout <= ResetValue;
|
||||
else dout <= din;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module sync_reset_fsm_flop #(
|
||||
parameter int Width = 1,
|
||||
parameter logic [Width-1:0] ResetValue = '0
|
||||
) (
|
||||
input logic clk,
|
||||
input logic rst,
|
||||
input logic [Width-1:0] din,
|
||||
output logic [Width-1:0] dout
|
||||
);
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst) dout <= ResetValue;
|
||||
else dout <= din;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module missing_reset_connection_fsm_flop #(
|
||||
parameter int Width = 1,
|
||||
parameter logic [Width-1:0] ResetValue = '0
|
||||
) (
|
||||
input logic clk,
|
||||
input logic rst_n,
|
||||
input logic [Width-1:0] din,
|
||||
output logic [Width-1:0] dout
|
||||
);
|
||||
always_ff @(posedge clk or negedge rst_n) begin
|
||||
if (!rst_n) dout <= ResetValue;
|
||||
else dout <= din;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module t (
|
||||
input logic clk
|
||||
);
|
||||
typedef enum logic [1:0] {
|
||||
S0 = 2'd0,
|
||||
S1 = 2'd1
|
||||
} state_t;
|
||||
|
||||
logic rst_n;
|
||||
logic rst;
|
||||
state_t state_q;
|
||||
state_t missing_d;
|
||||
state_t missing_q;
|
||||
state_t missing_clock_d;
|
||||
state_t missing_clock_q;
|
||||
state_t partial_reset_d;
|
||||
state_t partial_reset_q;
|
||||
state_t missing_reset_value_d;
|
||||
state_t missing_reset_value_q;
|
||||
state_t value_no_reset_d;
|
||||
state_t value_no_reset_q;
|
||||
state_t sync_reset_d;
|
||||
state_t sync_reset_q;
|
||||
state_t missing_reset_connection_d;
|
||||
state_t missing_reset_connection_q;
|
||||
|
||||
odd_fsm_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_bad_simple (
|
||||
.clk(clk),
|
||||
.rst_n(rst_n),
|
||||
.din(S0),
|
||||
.dout(state_q)
|
||||
);
|
||||
|
||||
missing_q_fsm_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_missing_q_port (
|
||||
.clk(clk),
|
||||
.rst_n(rst_n),
|
||||
.din(missing_d),
|
||||
.dout(missing_q)
|
||||
);
|
||||
|
||||
missing_clock_fsm_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_missing_clock_port (
|
||||
.clk(clk),
|
||||
.rst_n(rst_n),
|
||||
.din(missing_clock_d),
|
||||
.dout(missing_clock_q)
|
||||
);
|
||||
|
||||
partial_reset_fsm_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_partial_reset (
|
||||
.clk(clk),
|
||||
.rst_n(rst_n),
|
||||
.din(partial_reset_d),
|
||||
.dout(partial_reset_q)
|
||||
);
|
||||
|
||||
missing_reset_value_fsm_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_missing_reset_value (
|
||||
.clk(clk),
|
||||
.rst_n(rst_n),
|
||||
.din(missing_reset_value_d),
|
||||
.dout(missing_reset_value_q)
|
||||
);
|
||||
|
||||
value_no_reset_fsm_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_value_no_reset (
|
||||
.clk(clk),
|
||||
.rst_n(rst_n),
|
||||
.din(value_no_reset_d),
|
||||
.dout(value_no_reset_q)
|
||||
);
|
||||
|
||||
sync_reset_fsm_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_sync_reset (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
.din(sync_reset_d),
|
||||
.dout(sync_reset_q)
|
||||
);
|
||||
|
||||
missing_reset_connection_fsm_flop #(
|
||||
.Width($bits(state_t)),
|
||||
.ResetValue(S0)
|
||||
) u_missing_reset_connection (
|
||||
.clk(clk),
|
||||
.rst_n(rst_n),
|
||||
.din(missing_reset_connection_d),
|
||||
.dout(missing_reset_connection_q)
|
||||
);
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// DESCRIPTION: Verilator: FSM register wrapper VLT warning coverage
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
`verilator_config
|
||||
|
||||
fsm_register_wrapper -module "odd_fsm_flop" -d "din" -q "dout" -clock "clk" -reset "rst_n" -reset_value "ResetValue"
|
||||
fsm_register_wrapper -module "missing_q_fsm_flop" -d "din" -q "missing" -clock "clk" -reset "rst_n" -reset_value "ResetValue"
|
||||
fsm_register_wrapper -module "missing_clock_fsm_flop" -d "din" -q "dout" -clock "missing" -reset "rst_n" -reset_value "ResetValue"
|
||||
fsm_register_wrapper -module "partial_reset_fsm_flop" -d "din" -q "dout" -clock "clk" -reset "rst_n"
|
||||
fsm_register_wrapper -module "missing_reset_value_fsm_flop" -d "din" -q "dout" -clock "clk" -reset "rst_n" -reset_value "MissingParam"
|
||||
fsm_register_wrapper -module "value_no_reset_fsm_flop" -d "din" -q "dout" -clock "clk" -reset_value "ResetValue"
|
||||
fsm_register_wrapper -module "sync_reset_fsm_flop" -d "din" -q "dout" -clock "clk" -reset "rst" -reset_value "ResetValue"
|
||||
fsm_register_wrapper -module "missing_reset_connection_fsm_flop" -d "din" -q "dout" -clock "clk" -reset "missing_rst" -reset_value "ResetValue"
|
||||
Loading…
Reference in New Issue