Add hierarchy-aware reporting to `verilator_coverage` (#7657)
This commit is contained in:
parent
14cf611c72
commit
947a08965e
|
|
@ -157,6 +157,10 @@ verilator_coverage - Verilator coverage analyzer
|
|||
|
||||
verilator_coverage --annotate <obj>
|
||||
|
||||
verilator_coverage --report summary,hier <datafiles>...
|
||||
|
||||
verilator_coverage --report hier <datafiles>...
|
||||
|
||||
verilator_coverage -write merged.dat <datafiles>...
|
||||
|
||||
verilator_coverage -write-info merged.info <datafiles>...
|
||||
|
|
@ -176,7 +180,9 @@ L<https://verilator.org/guide/latest/exe_verilator_coverage.html>.
|
|||
--filter-type <regex> Keep only records of given coverage type.
|
||||
--help Displays this message and version and exits.
|
||||
--include-reset-arcs Include reset arcs in FSM arc summaries.
|
||||
--levels <depth> Limit displayed hierarchy report depth.
|
||||
--rank Compute relative importance of tests.
|
||||
--report <kind>[,<kind>...] Generate reports: summary, hier, or hierarchy.
|
||||
--unlink With --write, unlink all inputs
|
||||
--version Displays program version and exits.
|
||||
--write <filename> Write aggregate coverage results.
|
||||
|
|
|
|||
|
|
@ -38,6 +38,10 @@ verilator_coverage Example Usage
|
|||
|
||||
verilator_coverage --annotate obj_dir coverage.dat
|
||||
|
||||
verilator_coverage --report summary coverage.dat
|
||||
|
||||
verilator_coverage --report hier --levels 3 coverage.dat
|
||||
|
||||
verilator_coverage --write merged.dat coverage.dat ...
|
||||
|
||||
verilator_coverage --write-info merged.info coverage.dat ...
|
||||
|
|
@ -143,6 +147,13 @@ verilator_coverage Arguments
|
|||
By default, reset arcs are tracked but summarized separately from the
|
||||
non-reset FSM arcs.
|
||||
|
||||
.. option:: --levels <depth>
|
||||
|
||||
With :option:`--report hierarchy`, limits displayed hierarchy depth.
|
||||
Deeper descendants still roll up into the visible parent totals. A depth
|
||||
of 0 shows the top-level hierarchy rollup. If negative or omitted, all
|
||||
depths are shown.
|
||||
|
||||
.. option:: --rank
|
||||
|
||||
Prints an experimental report listing the relative importance of each
|
||||
|
|
@ -155,6 +166,18 @@ verilator_coverage Arguments
|
|||
contribute to overall coverage if all tests are run in the order of
|
||||
highest to the lowest rank.
|
||||
|
||||
.. option:: --report <kind>[,<kind>...]
|
||||
|
||||
Generates a human-consumable report. Supported report kinds are
|
||||
``summary``, ``hier``, and ``hierarchy``. Multiple report kinds may be
|
||||
comma-separated, for example ``summary,hier``. With no explicit
|
||||
:option:`--report`, the legacy flat summary is printed. ``summary`` is
|
||||
equivalent to the flat type summary. ``hier`` and ``hierarchy`` build a
|
||||
deterministic hierarchy rollup from ``hier`` fields. Collapsed wildcard
|
||||
hierarchy can be reported, but it is not precise per-instance coverage. If
|
||||
no hierarchy fields are present, a warning is printed and the flat summary
|
||||
is shown instead.
|
||||
|
||||
.. option:: --unlink
|
||||
|
||||
With :option:`--write`, unlink all input files after the output has been
|
||||
|
|
|
|||
|
|
@ -57,6 +57,24 @@ string VlcOptions::version() {
|
|||
return ver;
|
||||
}
|
||||
|
||||
void VlcOptions::parseReportOption() {
|
||||
if (m_report.empty()) return;
|
||||
string::size_type start = 0;
|
||||
while (true) {
|
||||
const string::size_type comma = m_report.find(',', start);
|
||||
const string kind = m_report.substr(start, comma - start);
|
||||
if (kind == "summary") {
|
||||
m_reportSummary = true;
|
||||
} else if (kind == "hier" || kind == "hierarchy") {
|
||||
m_reportHierarchy = true;
|
||||
} else {
|
||||
v3fatal("Invalid --report option: " << m_report);
|
||||
}
|
||||
if (comma == string::npos) break;
|
||||
start = comma + 1;
|
||||
}
|
||||
}
|
||||
|
||||
void VlcOptions::parseOptsList(int argc, char** argv) {
|
||||
V3OptionParser parser;
|
||||
V3OptionParser::AppendHelper DECL_OPTION{parser};
|
||||
|
|
@ -70,7 +88,9 @@ void VlcOptions::parseOptsList(int argc, char** argv) {
|
|||
DECL_OPTION("-debugi", CbVal, [](int v) { V3Error::debugDefault(v); });
|
||||
DECL_OPTION("-filter-type", Set, &m_filterType);
|
||||
DECL_OPTION("-include-reset-arcs", OnOff, &m_includeResetArcs);
|
||||
DECL_OPTION("-levels", Set, &m_reportLevels);
|
||||
DECL_OPTION("-rank", OnOff, &m_rank);
|
||||
DECL_OPTION("-report", Set, &m_report);
|
||||
DECL_OPTION("-unlink", OnOff, &m_unlink);
|
||||
DECL_OPTION("-V", CbCall, []() {
|
||||
showVersion(true);
|
||||
|
|
@ -101,6 +121,7 @@ void VlcOptions::parseOptsList(int argc, char** argv) {
|
|||
++i;
|
||||
}
|
||||
}
|
||||
parseReportOption();
|
||||
}
|
||||
|
||||
void VlcOptions::showVersion(bool verbose) {
|
||||
|
|
@ -141,7 +162,12 @@ int main(int argc, char** argv) {
|
|||
top.points().dump();
|
||||
}
|
||||
|
||||
if (!top.opt.rank() && top.opt.writeFile().empty() && top.opt.writeInfoFile().empty()) {
|
||||
const bool defaultReport = !top.opt.reportSpecified() && !top.opt.rank()
|
||||
&& top.opt.writeFile().empty() && top.opt.writeInfoFile().empty();
|
||||
if (top.opt.reportSpecified()) {
|
||||
if (top.opt.reportSummary()) top.printTypeSummary();
|
||||
if (top.opt.reportHierarchy()) top.printHierarchyReport();
|
||||
} else if (defaultReport) {
|
||||
top.printTypeSummary();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,10 @@ class VlcOptions final {
|
|||
bool m_includeResetArcs = false; // main switch: --include-reset-arcs
|
||||
string m_filterType = "*"; // main switch: --filter-type
|
||||
VlStringSet m_readFiles; // main switch: --read
|
||||
int m_reportLevels = -1; // main switch: --levels, negative means all depths
|
||||
string m_report; // main switch: --report
|
||||
bool m_reportSummary = false; // main switch: --report summary
|
||||
bool m_reportHierarchy = false; // main switch: --report hier or hierarchy
|
||||
bool m_rank = false; // main switch: --rank
|
||||
bool m_unlink = false; // main switch: --unlink
|
||||
string m_writeFile; // main switch: --write
|
||||
|
|
@ -50,6 +54,7 @@ class VlcOptions final {
|
|||
|
||||
private:
|
||||
// METHODS
|
||||
void parseReportOption() VL_MT_DISABLED;
|
||||
static void showVersion(bool verbose) VL_MT_DISABLED;
|
||||
|
||||
public:
|
||||
|
|
@ -69,6 +74,10 @@ public:
|
|||
bool countOk(uint64_t count) const { return count >= static_cast<uint64_t>(m_annotateMin); }
|
||||
bool annotatePoints() const { return m_annotatePoints; }
|
||||
bool includeResetArcs() const { return m_includeResetArcs; }
|
||||
int reportLevels() const { return m_reportLevels; }
|
||||
bool reportSpecified() const { return !m_report.empty(); }
|
||||
bool reportSummary() const { return m_reportSummary; }
|
||||
bool reportHierarchy() const { return m_reportHierarchy; }
|
||||
bool rank() const { return m_rank; }
|
||||
bool unlink() const { return m_unlink; }
|
||||
string writeFile() const { return m_writeFile; }
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ public:
|
|||
string filename() const { return keyExtract(VL_CIK_FILENAME, m_name.c_str()); }
|
||||
string comment() const { return keyExtract(VL_CIK_COMMENT, m_name.c_str()); }
|
||||
string hier() const { return keyExtract(VL_CIK_HIER, m_name.c_str()); }
|
||||
string page() const { return keyExtract("page", m_name.c_str()); }
|
||||
string type() const { return typeExtract(m_name.c_str()); }
|
||||
string thresh() const {
|
||||
// string as maybe ""
|
||||
|
|
|
|||
229
src/VlcTop.cpp
229
src/VlcTop.cpp
|
|
@ -32,6 +32,139 @@
|
|||
|
||||
//######################################################################
|
||||
|
||||
namespace {
|
||||
|
||||
// Report helpers only. They keep the flat summary and hierarchy report using
|
||||
// the same hit/total accounting and output formatting. These helpers read the
|
||||
// fields already exposed by VlcPoint; they do not affect coverage point
|
||||
// identity, merging, or .dat writing.
|
||||
|
||||
// Map coverage type to (covered points, total points).
|
||||
using TypeTally = std::map<std::string, std::pair<uint64_t, uint64_t>>;
|
||||
|
||||
static const char* const s_orderedTypes[]
|
||||
= {"line", "toggle", "branch", "expr", "fsm_state", "fsm_arc"};
|
||||
static const size_t s_summaryIndent = 2;
|
||||
static const size_t s_reportRowIndent = 4;
|
||||
|
||||
string displayType(const VlcPoint& point) {
|
||||
const string type = point.type();
|
||||
return type.empty() ? "point" : type;
|
||||
}
|
||||
|
||||
bool isCollapsedHier(const string& hier) {
|
||||
return hier.find('*') != string::npos || hier.find('?') != string::npos;
|
||||
}
|
||||
|
||||
bool isOrderedType(const string& type) {
|
||||
for (const char* const typep : s_orderedTypes) {
|
||||
if (type == typep) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
string reportHier(const VlcPoint& point) {
|
||||
// FSM records currently have useful instance scope in Fv.
|
||||
if (point.isFsmState() || point.isFsmArc()) {
|
||||
const string fsmVar = point.fsmVarName();
|
||||
return fsmVar.substr(0, fsmVar.rfind('.'));
|
||||
}
|
||||
return point.hier();
|
||||
}
|
||||
|
||||
std::vector<string> splitHier(const string& hier) {
|
||||
// Verilator emits dot-separated non-empty hierarchy components.
|
||||
std::vector<string> parts;
|
||||
string::size_type start = 0;
|
||||
while (true) {
|
||||
const string::size_type dot = hier.find('.', start);
|
||||
if (dot == string::npos) break;
|
||||
parts.push_back(hier.substr(start, dot - start));
|
||||
start = dot + 1;
|
||||
}
|
||||
parts.push_back(hier.substr(start));
|
||||
return parts;
|
||||
}
|
||||
|
||||
string duName(const VlcPoint& point) {
|
||||
// Pages are emitted as v_<type>/<design-unit> for RTL coverage. Use the
|
||||
// suffix as the design-unit summary key.
|
||||
const string page = point.page();
|
||||
return page.substr(page.find('/') + 1);
|
||||
}
|
||||
|
||||
void tallyPoint(TypeTally& tally, const string& type, uint64_t count) {
|
||||
std::pair<uint64_t, uint64_t>& entry = tally[type];
|
||||
if (count > 0) ++entry.first;
|
||||
++entry.second;
|
||||
}
|
||||
|
||||
// Keep the percentage calculation in one place so flat summaries and hierarchy
|
||||
// reports cannot drift in formatting or zero-total handling.
|
||||
double pct(uint64_t hit, uint64_t total) {
|
||||
return total ? (100.0 * static_cast<double>(hit) / static_cast<double>(total)) : 0.0;
|
||||
}
|
||||
|
||||
// Shared row formatter. The callers choose which rows to print; this only keeps
|
||||
// the text layout identical between the flat and hierarchy reports.
|
||||
void printIndent(size_t indent) {
|
||||
for (size_t i = 0; i < indent; ++i) std::cout << ' ';
|
||||
}
|
||||
|
||||
void printTallyRow(const string& type, uint64_t hit, uint64_t total, size_t indent,
|
||||
size_t typeWidth, size_t countWidth) {
|
||||
printIndent(indent);
|
||||
std::cout << std::left << std::setw(typeWidth) << type << " : " << std::right << std::fixed
|
||||
<< std::setprecision(1) << pct(hit, total) << "% (" << std::setw(countWidth) << hit
|
||||
<< "/" << std::setw(countWidth) << total << ")\n";
|
||||
}
|
||||
|
||||
size_t countWidth(const TypeTally& tally) {
|
||||
size_t width = cvtToStr(0).size();
|
||||
for (TypeTally::const_iterator it = tally.begin(); it != tally.end(); ++it) {
|
||||
width = std::max(width, cvtToStr(it->second.first).size());
|
||||
width = std::max(width, cvtToStr(it->second.second).size());
|
||||
}
|
||||
return width;
|
||||
}
|
||||
|
||||
size_t typeWidth(const TypeTally& tally) {
|
||||
size_t typeWidth = 0;
|
||||
for (const char* const typep : s_orderedTypes) {
|
||||
const string type = typep;
|
||||
typeWidth = std::max(typeWidth, type.size());
|
||||
}
|
||||
for (TypeTally::const_iterator it = tally.begin(); it != tally.end(); ++it) {
|
||||
typeWidth = std::max(typeWidth, it->first.size());
|
||||
}
|
||||
return typeWidth;
|
||||
}
|
||||
|
||||
void printTypeTally(const TypeTally& tally, size_t indent, bool includeMissingOrdered) {
|
||||
// Print standard coverage types first for stable output. When requested,
|
||||
// missing standard rows are printed with zero counts for compatibility with
|
||||
// the historical flat summary output.
|
||||
const size_t typWidth = typeWidth(tally);
|
||||
const size_t cntWidth = countWidth(tally);
|
||||
for (const char* const typep : s_orderedTypes) {
|
||||
const string type = typep;
|
||||
const TypeTally::const_iterator it = tally.find(type);
|
||||
if (it != tally.end()) {
|
||||
printTallyRow(type, it->second.first, it->second.second, indent, typWidth, cntWidth);
|
||||
} else if (includeMissingOrdered) {
|
||||
printTallyRow(type, 0, 0, indent, typWidth, cntWidth);
|
||||
}
|
||||
}
|
||||
for (TypeTally::const_iterator it = tally.begin(); it != tally.end(); ++it) {
|
||||
if (!isOrderedType(it->first)) {
|
||||
printTallyRow(it->first, it->second.first, it->second.second, indent, typWidth,
|
||||
cntWidth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void VlcTop::readCoverage(const string& filename, bool nonfatal) {
|
||||
UINFO(2, "readCoverage " << filename);
|
||||
|
||||
|
|
@ -372,47 +505,69 @@ void VlcTop::annotate(const string& dirname) {
|
|||
}
|
||||
|
||||
void VlcTop::printTypeSummary() {
|
||||
static const std::vector<std::string> orderedTypes
|
||||
= {"line", "toggle", "branch", "expr", "fsm_state", "fsm_arc"};
|
||||
std::map<std::string, std::pair<uint64_t, uint64_t>> tally;
|
||||
for (const auto& i : m_points) {
|
||||
TypeTally tally;
|
||||
for (VlcPoints::ByName::value_type& i : m_points) {
|
||||
const VlcPoint& pt = m_points.pointNumber(i.second);
|
||||
const string type = pt.type().empty() ? "point" : pt.type();
|
||||
auto& entry = tally[type];
|
||||
if (pt.count() > 0) ++entry.first;
|
||||
++entry.second;
|
||||
tallyPoint(tally, displayType(pt), pt.count());
|
||||
}
|
||||
if (tally.empty()) return;
|
||||
std::set<std::string> printed;
|
||||
size_t typeWidth = 0;
|
||||
size_t countWidth = 0;
|
||||
for (const string& type : orderedTypes) typeWidth = std::max(typeWidth, type.size());
|
||||
countWidth = std::max(countWidth, cvtToStr(0).size());
|
||||
for (const auto& it : tally) {
|
||||
typeWidth = std::max(typeWidth, it.first.size());
|
||||
countWidth = std::max(countWidth, cvtToStr(it.second.first).size());
|
||||
countWidth = std::max(countWidth, cvtToStr(it.second.second).size());
|
||||
}
|
||||
std::cout << "Coverage Summary:\n";
|
||||
for (const string& type : orderedTypes) {
|
||||
const auto it = tally.find(type);
|
||||
printed.insert(type);
|
||||
const uint64_t hit = (it == tally.end()) ? 0 : it->second.first;
|
||||
const uint64_t total = (it == tally.end()) ? 0 : it->second.second;
|
||||
const double pct
|
||||
= total ? (100.0 * static_cast<double>(hit) / static_cast<double>(total)) : 0.0;
|
||||
std::cout << " " << std::left << std::setw(typeWidth) << type << " : " << std::right
|
||||
<< std::fixed << std::setprecision(1) << pct << "% (" << std::setw(countWidth)
|
||||
<< hit << "/" << std::setw(countWidth) << total << ")\n";
|
||||
// Keep the legacy summary behavior of showing standard coverage types even
|
||||
// when the input has no points of that type.
|
||||
printTypeTally(tally, s_summaryIndent, true);
|
||||
}
|
||||
|
||||
void VlcTop::printHierarchyReport() {
|
||||
std::map<string, TypeTally> hierTallies;
|
||||
std::map<string, TypeTally> duTallies;
|
||||
bool hasHier = false;
|
||||
bool hasCollapsedHier = false;
|
||||
for (VlcPoints::ByName::value_type& i : m_points) {
|
||||
const VlcPoint& pt = m_points.pointNumber(i.second);
|
||||
const string hier = reportHier(pt);
|
||||
if (hier.empty()) continue;
|
||||
hasHier = true;
|
||||
if (isCollapsedHier(hier)) hasCollapsedHier = true;
|
||||
const string type = displayType(pt);
|
||||
const std::vector<string> parts = splitHier(hier);
|
||||
string path;
|
||||
for (std::vector<string>::const_iterator it = parts.begin(); it != parts.end(); ++it) {
|
||||
path = path.empty() ? *it : path + "." + *it;
|
||||
tallyPoint(hierTallies[path], type, pt.count());
|
||||
}
|
||||
tallyPoint(duTallies[duName(pt)], type, pt.count());
|
||||
}
|
||||
for (const auto& it : tally) {
|
||||
if (printed.count(it.first)) continue;
|
||||
const uint64_t hit = it.second.first;
|
||||
const uint64_t total = it.second.second;
|
||||
const double pct
|
||||
= total ? (100.0 * static_cast<double>(hit) / static_cast<double>(total)) : 0.0;
|
||||
std::cout << " " << std::left << std::setw(typeWidth) << it.first << " : " << std::right
|
||||
<< std::fixed << std::setprecision(1) << pct << "% (" << std::setw(countWidth)
|
||||
<< hit << "/" << std::setw(countWidth) << total << ")\n";
|
||||
|
||||
if (!hasHier) {
|
||||
std::cout << "%Warning: --report hierarchy input has no hierarchy fields; "
|
||||
<< "printing flat summary instead.\n";
|
||||
printTypeSummary();
|
||||
return;
|
||||
}
|
||||
|
||||
const int levels = opt.reportLevels();
|
||||
if (hasCollapsedHier) {
|
||||
std::cout << "Note: hierarchy report contains collapsed hierarchy paths; "
|
||||
<< "it is not precise per-instance coverage.\n";
|
||||
}
|
||||
std::cout << "Hierarchy Coverage Summary:\n";
|
||||
for (std::map<string, TypeTally>::const_iterator it = hierTallies.begin();
|
||||
it != hierTallies.end(); ++it) {
|
||||
const std::vector<string> parts = splitHier(it->first);
|
||||
if (levels >= 0 && static_cast<int>(parts.size()) > levels + 1) continue;
|
||||
printIndent(s_summaryIndent);
|
||||
std::cout << it->first << "\n";
|
||||
// Hierarchy nodes can be numerous, so only print coverage types present
|
||||
// under this node instead of repeating absent zero-count rows.
|
||||
printTypeTally(it->second, s_reportRowIndent, false);
|
||||
}
|
||||
std::cout << "Design Unit Coverage Summary:\n";
|
||||
for (std::map<string, TypeTally>::const_iterator it = duTallies.begin(); it != duTallies.end();
|
||||
++it) {
|
||||
printIndent(s_summaryIndent);
|
||||
std::cout << it->first << "\n";
|
||||
// Design-unit summaries follow the hierarchy report style: present
|
||||
// types only, but in the same stable order as the flat summary.
|
||||
printTypeTally(it->second, s_reportRowIndent, false);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ public:
|
|||
|
||||
// METHODS
|
||||
void annotate(const string& dirname);
|
||||
void printHierarchyReport();
|
||||
void printTypeSummary();
|
||||
void readCoverage(const string& filename, bool nonfatal = false);
|
||||
void writeCoverage(const string& filename);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
# DESCRIPTION: Verilator: coverage test helpers
|
||||
#
|
||||
# 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 re
|
||||
|
||||
|
||||
def vlcov_bin():
|
||||
return os.environ["VERILATOR_ROOT"] + "/bin/verilator_coverage"
|
||||
|
||||
|
||||
def vlcov_run_context(test, log, tmp_log):
|
||||
return test, log, tmp_log
|
||||
|
||||
|
||||
def init_log(log):
|
||||
with open(log, "w", encoding="utf-8"):
|
||||
pass
|
||||
|
||||
|
||||
def run_vlcov(context, label, args, fails=False, normalize_errors=False):
|
||||
test, log, tmp_log = context
|
||||
with open(log, "a", encoding="utf-8") as log_fh:
|
||||
if log_fh.tell():
|
||||
log_fh.write("\n")
|
||||
log_fh.write("$ " + label + "\n")
|
||||
|
||||
test.run(cmd=[vlcov_bin(), *args], logfile=tmp_log, fails=fails, tee=False, verilator_run=True)
|
||||
|
||||
with open(tmp_log, encoding="utf-8") as in_fh:
|
||||
text = in_fh.read()
|
||||
if normalize_errors:
|
||||
text = re.sub(r"verilator_doc[.]html[?]v=[^ ]+", "verilator_doc.html?v=latest", text)
|
||||
with open(log, "a", encoding="utf-8") as log_fh:
|
||||
log_fh.write(text)
|
||||
|
|
@ -0,0 +1,251 @@
|
|||
$ verilator_coverage --report summary t/t_vlcov_data_e.dat
|
||||
Coverage Summary:
|
||||
line : 88.6% ( 39/ 44)
|
||||
toggle : 33.3% ( 35/105)
|
||||
branch : 78.1% ( 50/ 64)
|
||||
expr : 66.7% ( 8/ 12)
|
||||
fsm_state : 0.0% ( 0/ 0)
|
||||
fsm_arc : 0.0% ( 0/ 0)
|
||||
|
||||
$ verilator_coverage t/t_cover_hier.out
|
||||
Coverage Summary:
|
||||
line : 100.0% (4/4)
|
||||
toggle : 0.0% (0/0)
|
||||
branch : 100.0% (4/4)
|
||||
expr : 0.0% (0/0)
|
||||
fsm_state : 0.0% (0/0)
|
||||
fsm_arc : 0.0% (0/0)
|
||||
user : 100.0% (2/2)
|
||||
|
||||
$ verilator_coverage --report summary,hier t/t_cover_hier.out
|
||||
Coverage Summary:
|
||||
line : 100.0% (4/4)
|
||||
toggle : 0.0% (0/0)
|
||||
branch : 100.0% (4/4)
|
||||
expr : 0.0% (0/0)
|
||||
fsm_state : 0.0% (0/0)
|
||||
fsm_arc : 0.0% (0/0)
|
||||
user : 100.0% (2/2)
|
||||
Hierarchy Coverage Summary:
|
||||
top
|
||||
line : 100.0% (4/4)
|
||||
branch : 100.0% (4/4)
|
||||
user : 100.0% (2/2)
|
||||
top.t
|
||||
line : 100.0% (4/4)
|
||||
branch : 100.0% (4/4)
|
||||
user : 100.0% (2/2)
|
||||
top.t.u_a
|
||||
line : 100.0% (1/1)
|
||||
branch : 100.0% (2/2)
|
||||
user : 100.0% (1/1)
|
||||
top.t.u_a.same_stmt
|
||||
user : 100.0% (1/1)
|
||||
top.t.u_b
|
||||
line : 100.0% (1/1)
|
||||
branch : 100.0% (2/2)
|
||||
user : 100.0% (1/1)
|
||||
top.t.u_b.same_stmt
|
||||
user : 100.0% (1/1)
|
||||
Design Unit Coverage Summary:
|
||||
child
|
||||
line : 100.0% (2/2)
|
||||
branch : 100.0% (4/4)
|
||||
user : 100.0% (2/2)
|
||||
t
|
||||
line : 100.0% (2/2)
|
||||
|
||||
$ verilator_coverage --report hier,summary --levels 0 t/t_cover_hier.out
|
||||
Coverage Summary:
|
||||
line : 100.0% (4/4)
|
||||
toggle : 0.0% (0/0)
|
||||
branch : 100.0% (4/4)
|
||||
expr : 0.0% (0/0)
|
||||
fsm_state : 0.0% (0/0)
|
||||
fsm_arc : 0.0% (0/0)
|
||||
user : 100.0% (2/2)
|
||||
Hierarchy Coverage Summary:
|
||||
top
|
||||
line : 100.0% (4/4)
|
||||
branch : 100.0% (4/4)
|
||||
user : 100.0% (2/2)
|
||||
Design Unit Coverage Summary:
|
||||
child
|
||||
line : 100.0% (2/2)
|
||||
branch : 100.0% (4/4)
|
||||
user : 100.0% (2/2)
|
||||
t
|
||||
line : 100.0% (2/2)
|
||||
|
||||
$ verilator_coverage --report hier,summary --levels -4 t/t_cover_hier.out
|
||||
Coverage Summary:
|
||||
line : 100.0% (4/4)
|
||||
toggle : 0.0% (0/0)
|
||||
branch : 100.0% (4/4)
|
||||
expr : 0.0% (0/0)
|
||||
fsm_state : 0.0% (0/0)
|
||||
fsm_arc : 0.0% (0/0)
|
||||
user : 100.0% (2/2)
|
||||
Hierarchy Coverage Summary:
|
||||
top
|
||||
line : 100.0% (4/4)
|
||||
branch : 100.0% (4/4)
|
||||
user : 100.0% (2/2)
|
||||
top.t
|
||||
line : 100.0% (4/4)
|
||||
branch : 100.0% (4/4)
|
||||
user : 100.0% (2/2)
|
||||
top.t.u_a
|
||||
line : 100.0% (1/1)
|
||||
branch : 100.0% (2/2)
|
||||
user : 100.0% (1/1)
|
||||
top.t.u_a.same_stmt
|
||||
user : 100.0% (1/1)
|
||||
top.t.u_b
|
||||
line : 100.0% (1/1)
|
||||
branch : 100.0% (2/2)
|
||||
user : 100.0% (1/1)
|
||||
top.t.u_b.same_stmt
|
||||
user : 100.0% (1/1)
|
||||
Design Unit Coverage Summary:
|
||||
child
|
||||
line : 100.0% (2/2)
|
||||
branch : 100.0% (4/4)
|
||||
user : 100.0% (2/2)
|
||||
t
|
||||
line : 100.0% (2/2)
|
||||
|
||||
$ verilator_coverage --report summary empty.dat
|
||||
|
||||
$ verilator_coverage --report hierarchy t/t_cover_hier.out
|
||||
Hierarchy Coverage Summary:
|
||||
top
|
||||
line : 100.0% (4/4)
|
||||
branch : 100.0% (4/4)
|
||||
user : 100.0% (2/2)
|
||||
top.t
|
||||
line : 100.0% (4/4)
|
||||
branch : 100.0% (4/4)
|
||||
user : 100.0% (2/2)
|
||||
top.t.u_a
|
||||
line : 100.0% (1/1)
|
||||
branch : 100.0% (2/2)
|
||||
user : 100.0% (1/1)
|
||||
top.t.u_a.same_stmt
|
||||
user : 100.0% (1/1)
|
||||
top.t.u_b
|
||||
line : 100.0% (1/1)
|
||||
branch : 100.0% (2/2)
|
||||
user : 100.0% (1/1)
|
||||
top.t.u_b.same_stmt
|
||||
user : 100.0% (1/1)
|
||||
Design Unit Coverage Summary:
|
||||
child
|
||||
line : 100.0% (2/2)
|
||||
branch : 100.0% (4/4)
|
||||
user : 100.0% (2/2)
|
||||
t
|
||||
line : 100.0% (2/2)
|
||||
|
||||
$ verilator_coverage --report hierarchy --levels 2 t/t_cover_hier.out
|
||||
Hierarchy Coverage Summary:
|
||||
top
|
||||
line : 100.0% (4/4)
|
||||
branch : 100.0% (4/4)
|
||||
user : 100.0% (2/2)
|
||||
top.t
|
||||
line : 100.0% (4/4)
|
||||
branch : 100.0% (4/4)
|
||||
user : 100.0% (2/2)
|
||||
top.t.u_a
|
||||
line : 100.0% (1/1)
|
||||
branch : 100.0% (2/2)
|
||||
user : 100.0% (1/1)
|
||||
top.t.u_b
|
||||
line : 100.0% (1/1)
|
||||
branch : 100.0% (2/2)
|
||||
user : 100.0% (1/1)
|
||||
Design Unit Coverage Summary:
|
||||
child
|
||||
line : 100.0% (2/2)
|
||||
branch : 100.0% (4/4)
|
||||
user : 100.0% (2/2)
|
||||
t
|
||||
line : 100.0% (2/2)
|
||||
|
||||
$ verilator_coverage --report hierarchy --levels -1 t/t_cover_hier.out
|
||||
Hierarchy Coverage Summary:
|
||||
top
|
||||
line : 100.0% (4/4)
|
||||
branch : 100.0% (4/4)
|
||||
user : 100.0% (2/2)
|
||||
top.t
|
||||
line : 100.0% (4/4)
|
||||
branch : 100.0% (4/4)
|
||||
user : 100.0% (2/2)
|
||||
top.t.u_a
|
||||
line : 100.0% (1/1)
|
||||
branch : 100.0% (2/2)
|
||||
user : 100.0% (1/1)
|
||||
top.t.u_a.same_stmt
|
||||
user : 100.0% (1/1)
|
||||
top.t.u_b
|
||||
line : 100.0% (1/1)
|
||||
branch : 100.0% (2/2)
|
||||
user : 100.0% (1/1)
|
||||
top.t.u_b.same_stmt
|
||||
user : 100.0% (1/1)
|
||||
Design Unit Coverage Summary:
|
||||
child
|
||||
line : 100.0% (2/2)
|
||||
branch : 100.0% (4/4)
|
||||
user : 100.0% (2/2)
|
||||
t
|
||||
line : 100.0% (2/2)
|
||||
|
||||
$ verilator_coverage --report hier --levels 2 t/t_cover_hier.out
|
||||
Hierarchy Coverage Summary:
|
||||
top
|
||||
line : 100.0% (4/4)
|
||||
branch : 100.0% (4/4)
|
||||
user : 100.0% (2/2)
|
||||
top.t
|
||||
line : 100.0% (4/4)
|
||||
branch : 100.0% (4/4)
|
||||
user : 100.0% (2/2)
|
||||
top.t.u_a
|
||||
line : 100.0% (1/1)
|
||||
branch : 100.0% (2/2)
|
||||
user : 100.0% (1/1)
|
||||
top.t.u_b
|
||||
line : 100.0% (1/1)
|
||||
branch : 100.0% (2/2)
|
||||
user : 100.0% (1/1)
|
||||
Design Unit Coverage Summary:
|
||||
child
|
||||
line : 100.0% (2/2)
|
||||
branch : 100.0% (4/4)
|
||||
user : 100.0% (2/2)
|
||||
t
|
||||
line : 100.0% (2/2)
|
||||
|
||||
$ verilator_coverage --report hierarchy collapsed.dat
|
||||
Note: hierarchy report contains collapsed hierarchy paths; it is not precise per-instance coverage.
|
||||
Hierarchy Coverage Summary:
|
||||
tb
|
||||
line : 50.0% (1/2)
|
||||
branch : 50.0% (1/2)
|
||||
tb.cluster*
|
||||
line : 50.0% (1/2)
|
||||
branch : 100.0% (1/1)
|
||||
tb.cluster*.u_core
|
||||
line : 50.0% (1/2)
|
||||
branch : 100.0% (1/1)
|
||||
tb.cluster?
|
||||
branch : 0.0% (0/1)
|
||||
tb.cluster?.u_core
|
||||
branch : 0.0% (0/1)
|
||||
Design Unit Coverage Summary:
|
||||
core
|
||||
line : 50.0% (1/2)
|
||||
branch : 50.0% (1/2)
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# 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
|
||||
|
||||
from coverage_common import init_log, run_vlcov, vlcov_run_context
|
||||
|
||||
test.scenarios('dist')
|
||||
|
||||
log = test.obj_dir + "/vlcov.log"
|
||||
tmp_log = test.obj_dir + "/vlcov.tmp"
|
||||
|
||||
init_log(log)
|
||||
vlcov = vlcov_run_context(test, log, tmp_log)
|
||||
|
||||
collapsed_cov = test.obj_dir + "/collapsed.dat"
|
||||
with open(collapsed_cov, "w", encoding="utf-8") as fh:
|
||||
fh.write("# SystemC::Coverage-3\n")
|
||||
fh.write("C '\001f\002t/soc.v\001l\00210\001t\002line\001page\002v_line/core"
|
||||
"\001h\002tb.cluster*.u_core' 4\n")
|
||||
fh.write("C '\001f\002t/soc.v\001l\00211\001t\002line\001page\002v_line/core"
|
||||
"\001h\002tb.cluster*.u_core' 0\n")
|
||||
fh.write("C '\001f\002t/soc.v\001l\00212\001t\002branch\001page\002v_branch/core"
|
||||
"\001h\002tb.cluster*.u_core' 9\n")
|
||||
fh.write("C '\001f\002t/soc.v\001l\00213\001t\002branch\001page\002v_branch/core"
|
||||
"\001h\002tb.cluster?.u_core' 0\n")
|
||||
|
||||
empty_cov = test.obj_dir + "/empty.dat"
|
||||
with open(empty_cov, "w", encoding="utf-8") as fh:
|
||||
fh.write("# SystemC::Coverage-3\n")
|
||||
|
||||
run_vlcov(vlcov,
|
||||
"verilator_coverage --report summary t/t_vlcov_data_e.dat",
|
||||
args=["--report", "summary", "t/t_vlcov_data_e.dat"])
|
||||
run_vlcov(vlcov, "verilator_coverage t/t_cover_hier.out", args=["t/t_cover_hier.out"])
|
||||
run_vlcov(vlcov,
|
||||
"verilator_coverage --report summary,hier t/t_cover_hier.out",
|
||||
args=["--report", "summary,hier", "t/t_cover_hier.out"])
|
||||
run_vlcov(vlcov,
|
||||
"verilator_coverage --report hier,summary --levels 0 t/t_cover_hier.out",
|
||||
args=["--report", "hier,summary", "--levels", "0", "t/t_cover_hier.out"])
|
||||
run_vlcov(vlcov,
|
||||
"verilator_coverage --report hier,summary --levels -4 t/t_cover_hier.out",
|
||||
args=["--report", "hier,summary", "--levels", "-4", "t/t_cover_hier.out"])
|
||||
run_vlcov(vlcov,
|
||||
"verilator_coverage --report summary empty.dat",
|
||||
args=["--report", "summary", empty_cov])
|
||||
run_vlcov(vlcov,
|
||||
"verilator_coverage --report hierarchy t/t_cover_hier.out",
|
||||
args=["--report", "hierarchy", "t/t_cover_hier.out"])
|
||||
run_vlcov(vlcov,
|
||||
"verilator_coverage --report hierarchy --levels 2 t/t_cover_hier.out",
|
||||
args=["--report", "hierarchy", "--levels", "2", "t/t_cover_hier.out"])
|
||||
run_vlcov(vlcov,
|
||||
"verilator_coverage --report hierarchy --levels -1 t/t_cover_hier.out",
|
||||
args=["--report", "hierarchy", "--levels", "-1", "t/t_cover_hier.out"])
|
||||
run_vlcov(vlcov,
|
||||
"verilator_coverage --report hier --levels 2 t/t_cover_hier.out",
|
||||
args=["--report", "hier", "--levels", "2", "t/t_cover_hier.out"])
|
||||
run_vlcov(vlcov,
|
||||
"verilator_coverage --report hierarchy collapsed.dat",
|
||||
args=["--report", "hierarchy", collapsed_cov])
|
||||
|
||||
test.files_identical(log, test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
$ verilator_coverage --report unknown t/t_cover_hier.out
|
||||
%Error: Invalid --report option: unknown
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
|
||||
$ verilator_coverage --report summary,unknown t/t_cover_hier.out
|
||||
%Error: Invalid --report option: summary,unknown
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
|
||||
$ verilator_coverage --report hierarchy missing.dat
|
||||
%Warning: --report hierarchy input has no hierarchy fields; printing flat summary instead.
|
||||
Coverage Summary:
|
||||
line : 100.0% (1/1)
|
||||
toggle : 0.0% (0/0)
|
||||
branch : 0.0% (0/0)
|
||||
expr : 0.0% (0/0)
|
||||
fsm_state : 0.0% (0/0)
|
||||
fsm_arc : 0.0% (0/0)
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# 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
|
||||
|
||||
from coverage_common import init_log, run_vlcov, vlcov_run_context
|
||||
|
||||
test.scenarios('dist')
|
||||
|
||||
log = test.obj_dir + "/vlcov.log"
|
||||
tmp_log = test.obj_dir + "/vlcov.tmp"
|
||||
|
||||
init_log(log)
|
||||
vlcov = vlcov_run_context(test, log, tmp_log)
|
||||
|
||||
missing_cov = test.obj_dir + "/missing.dat"
|
||||
with open(missing_cov, "w", encoding="utf-8") as fh:
|
||||
fh.write("# SystemC::Coverage-3\n")
|
||||
fh.write("C '\001f\002t/missing.v\001l\0021\001t\002line\001page\002v_line/missing' 1\n")
|
||||
|
||||
run_vlcov(vlcov,
|
||||
"verilator_coverage --report unknown t/t_cover_hier.out",
|
||||
args=["--report", "unknown", "t/t_cover_hier.out"],
|
||||
fails=True,
|
||||
normalize_errors=True)
|
||||
run_vlcov(vlcov,
|
||||
"verilator_coverage --report summary,unknown t/t_cover_hier.out",
|
||||
args=["--report", "summary,unknown", "t/t_cover_hier.out"],
|
||||
fails=True,
|
||||
normalize_errors=True)
|
||||
run_vlcov(vlcov,
|
||||
"verilator_coverage --report hierarchy missing.dat",
|
||||
args=["--report", "hierarchy", missing_cov])
|
||||
|
||||
test.files_identical(log, test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
$ verilator_coverage --report summary coverage.dat
|
||||
Coverage Summary:
|
||||
line : 82.9% (29/35)
|
||||
toggle : 85.4% (82/96)
|
||||
branch : 78.6% (22/28)
|
||||
expr : 57.1% ( 8/14)
|
||||
fsm_state : 91.7% (11/12)
|
||||
fsm_arc : 71.4% (10/14)
|
||||
|
||||
$ verilator_coverage --report hierarchy --levels 3 coverage.dat
|
||||
Hierarchy Coverage Summary:
|
||||
tb
|
||||
line : 82.9% (29/35)
|
||||
toggle : 85.4% (82/96)
|
||||
branch : 78.6% (22/28)
|
||||
expr : 57.1% ( 8/14)
|
||||
fsm_state : 91.7% (11/12)
|
||||
fsm_arc : 71.4% (10/14)
|
||||
tb.cluster_a
|
||||
line : 73.3% (11/15)
|
||||
toggle : 76.1% (35/46)
|
||||
branch : 66.7% ( 8/12)
|
||||
expr : 100.0% ( 6/ 6)
|
||||
fsm_state : 83.3% ( 5/ 6)
|
||||
fsm_arc : 57.1% ( 4/ 7)
|
||||
tb.cluster_a.u_core
|
||||
line : 66.7% ( 8/12)
|
||||
toggle : 83.3% (20/24)
|
||||
branch : 62.5% ( 5/ 8)
|
||||
fsm_state : 83.3% ( 5/ 6)
|
||||
fsm_arc : 57.1% ( 4/ 7)
|
||||
tb.cluster_b
|
||||
line : 86.7% (13/15)
|
||||
toggle : 93.5% (43/46)
|
||||
branch : 83.3% (10/12)
|
||||
expr : 0.0% ( 0/ 6)
|
||||
fsm_state : 100.0% ( 6/ 6)
|
||||
fsm_arc : 85.7% ( 6/ 7)
|
||||
tb.cluster_b.u_core
|
||||
line : 83.3% (10/12)
|
||||
toggle : 100.0% (24/24)
|
||||
branch : 87.5% ( 7/ 8)
|
||||
fsm_state : 100.0% ( 6/ 6)
|
||||
fsm_arc : 85.7% ( 6/ 7)
|
||||
Design Unit Coverage Summary:
|
||||
$root
|
||||
fsm_state : 91.7% (11/12)
|
||||
fsm_arc : 71.4% (10/14)
|
||||
cluster
|
||||
line : 100.0% ( 3/ 3)
|
||||
toggle : 68.2% (15/22)
|
||||
branch : 75.0% ( 3/ 4)
|
||||
expr : 100.0% ( 6/ 6)
|
||||
cluster__M1
|
||||
line : 100.0% ( 3/ 3)
|
||||
toggle : 86.4% (19/22)
|
||||
branch : 75.0% ( 3/ 4)
|
||||
expr : 0.0% ( 0/ 6)
|
||||
fsm_core
|
||||
line : 75.0% (18/24)
|
||||
toggle : 91.7% (44/48)
|
||||
branch : 75.0% (12/16)
|
||||
tb
|
||||
line : 100.0% (5/5)
|
||||
toggle : 100.0% (4/4)
|
||||
branch : 100.0% (4/4)
|
||||
expr : 100.0% (2/2)
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Runtime hierarchy report with per-instance coverage data
|
||||
#
|
||||
# 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
|
||||
|
||||
from coverage_common import init_log, run_vlcov, vlcov_run_context
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
if not test.have_coroutines:
|
||||
test.skip("Test requires Coroutines; ignore error since not available")
|
||||
|
||||
test.compile(top_filename="t/t_vlcov_hier_report_runtime.v",
|
||||
verilator_flags2=[
|
||||
"--binary",
|
||||
"--coverage",
|
||||
"--coverage-fsm",
|
||||
"--coverage-per-instance",
|
||||
"--top-module",
|
||||
"tb",
|
||||
"--timing",
|
||||
])
|
||||
|
||||
test.execute(all_run_flags=[" +verilator+coverage+file+" + test.obj_dir + "/coverage.dat"])
|
||||
|
||||
summary_log = test.obj_dir + "/summary.log"
|
||||
hier_log = test.obj_dir + "/hierarchy.log"
|
||||
combined_log = test.obj_dir + "/vlcov.log"
|
||||
|
||||
init_log(combined_log)
|
||||
run_vlcov(vlcov_run_context(test, combined_log, summary_log),
|
||||
"verilator_coverage --report summary coverage.dat",
|
||||
args=["--report", "summary", test.obj_dir + "/coverage.dat"])
|
||||
run_vlcov(vlcov_run_context(test, combined_log, hier_log),
|
||||
"verilator_coverage --report hierarchy --levels 3 coverage.dat",
|
||||
args=["--report", "hierarchy", "--levels", "3", test.obj_dir + "/coverage.dat"])
|
||||
|
||||
test.files_identical(combined_log, test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
// DESCRIPTION: Verilator: Runtime hierarchy coverage data for report tests
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module fsm_core (
|
||||
input logic clk,
|
||||
input logic rst,
|
||||
input logic start,
|
||||
input logic finish,
|
||||
input logic fail,
|
||||
input logic retry
|
||||
);
|
||||
typedef enum logic [2:0] {
|
||||
S_IDLE = 3'd0,
|
||||
S_LOAD = 3'd1,
|
||||
S_RUN = 3'd2,
|
||||
S_WAIT = 3'd3,
|
||||
S_DONE = 3'd4,
|
||||
S_ERR = 3'd5
|
||||
} state_t;
|
||||
|
||||
state_t state_q /*verilator fsm_reset_arc*/;
|
||||
state_t state_d;
|
||||
|
||||
always_comb begin
|
||||
state_d = state_q;
|
||||
case (state_q)
|
||||
S_IDLE: if (start) state_d = S_LOAD;
|
||||
S_LOAD: state_d = S_RUN;
|
||||
S_RUN: begin
|
||||
if (fail) state_d = S_ERR;
|
||||
else if (finish) state_d = S_DONE;
|
||||
else state_d = S_WAIT;
|
||||
end
|
||||
S_WAIT: begin
|
||||
if (finish) state_d = S_DONE;
|
||||
else state_d = S_RUN;
|
||||
end
|
||||
S_DONE: state_d = S_IDLE;
|
||||
S_ERR: begin
|
||||
if (retry) state_d = S_LOAD;
|
||||
else state_d = S_ERR;
|
||||
end
|
||||
default: state_d = S_ERR;
|
||||
endcase
|
||||
end
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst) state_q <= S_IDLE;
|
||||
else state_q <= state_d;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module cluster #(
|
||||
parameter int MODE = 0
|
||||
) (
|
||||
input logic clk,
|
||||
input logic rst
|
||||
);
|
||||
logic [4:0] cyc = 0;
|
||||
logic start;
|
||||
logic finish;
|
||||
logic fail;
|
||||
logic retry;
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst) cyc <= 0;
|
||||
else cyc <= cyc + 1'b1;
|
||||
end
|
||||
|
||||
always_comb begin
|
||||
start = 1'b0;
|
||||
finish = 1'b0;
|
||||
fail = 1'b0;
|
||||
retry = 1'b0;
|
||||
if (MODE == 0) begin
|
||||
start = (cyc == 1) || (cyc == 7);
|
||||
finish = (cyc == 4) || (cyc == 10);
|
||||
end else begin
|
||||
start = (cyc == 1);
|
||||
fail = (cyc == 3);
|
||||
retry = (cyc == 6);
|
||||
finish = (cyc == 9);
|
||||
end
|
||||
end
|
||||
|
||||
fsm_core u_core (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
.start(start),
|
||||
.finish(finish),
|
||||
.fail(fail),
|
||||
.retry(retry)
|
||||
);
|
||||
endmodule
|
||||
|
||||
module tb;
|
||||
logic clk = 0;
|
||||
logic rst = 1;
|
||||
int cyc = 0;
|
||||
|
||||
always #1 clk = !clk;
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
cyc <= cyc + 1;
|
||||
if (cyc == 1) rst <= 0;
|
||||
if (cyc == 14) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
|
||||
cluster #(.MODE(0)) cluster_a (.clk(clk), .rst(rst));
|
||||
cluster #(.MODE(1)) cluster_b (.clk(clk), .rst(rst));
|
||||
endmodule
|
||||
Loading…
Reference in New Issue