Add --main support for dumping coverage, and +verilator+coverage+file runtime option.

This commit is contained in:
Wilson Snyder 2024-01-20 12:28:49 -05:00
parent a38fea1f9e
commit 1a92502788
12 changed files with 110 additions and 23 deletions

View File

@ -14,6 +14,8 @@ Verilator 5.021 devel
**Minor:**
* Add predicted stack overflow warning (#4799).
* Add +verilator+coverage+file runtime option.
* Add --main support for dumping coverage.
* Remove deprecated 32-bit pointer mode (`gcc -m32`).
* Change zero replication width error to ZEROREPL warning (#4753) (#4762). [Pengcheng Xu]
* Support `vpiConstType` in `vpi_get_str()` (#4797). [Marlon James]

View File

@ -19,6 +19,11 @@ Summary:
.. include:: ../_build/gen/args_verilated.rst
.. option:: +verilator+coverage+file+<filename>
When a model was Verilated using :vlopt:`--coverage`, sets the filename
to write coverage data into. Defaults to :file:`coverage.dat`.
.. option:: +verilator+debug
Enable simulation runtime debugging. Equivalent to

View File

@ -230,14 +230,20 @@ Coverage Collection
When any coverage flag is used to Verilate, Verilator will add appropriate
coverage point insertions into the model and collect the coverage data.
To get the coverage data from the model, in the user wrapper code,
typically at the end once a test passes, call
:code:`Verilated::threadContextp()->coveragep()->write` with an argument of the filename for
the coverage data file to write coverage data to (typically
"logs/coverage.dat").
To get the coverage data from the model, write the coverage with either:
1. Using :vlopt:`--binary` or :vlopt:`--main`, and Verilator will dump
coverage when the test completes to the filename specified with
:vlopt:`+verilator+coverage+file+\<filename\>`.
2. In the user wrapper code, typically at the end once a test passes, call
:code:`Verilated::threadContextp()->coveragep()->write` with an argument
of the filename for the coverage data file to write coverage data to
(typically "logs/coverage.dat").
Run each of your tests in different directories, potentially in parallel.
Each test will create a :file:`logs/coverage.dat` file.
Each test will create the file specified above,
e.g. :file:`logs/coverage.dat`.
After running all of the tests, execute the :command:`verilator_coverage`
command, passing arguments pointing to the filenames of all the

View File

@ -2430,6 +2430,7 @@ VerilatedContext::VerilatedContext()
: m_impdatap{new VerilatedContextImpData} {
Verilated::lastContextp(this);
Verilated::threadContextp(this);
m_ns.m_coverageFilename = "coverage.dat";
m_ns.m_profExecFilename = "profile_exec.dat";
m_ns.m_profVltFilename = "profile.vlt";
m_fdps.resize(31);
@ -2465,6 +2466,14 @@ void VerilatedContext::calcUnusedSigs(bool flag) VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
m_s.m_calcUnusedSigs = flag;
}
void VerilatedContext::coverageFilename(const std::string& flag) VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
m_ns.m_coverageFilename = flag;
}
std::string VerilatedContext::coverageFilename() const VL_MT_SAFE {
const VerilatedLockGuard lock{m_mutex};
return m_ns.m_coverageFilename;
}
void VerilatedContext::dumpfile(const std::string& flag) VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
const VerilatedLockGuard lock{m_timeDumpMutex};
m_dumpfile = flag;
@ -2705,7 +2714,9 @@ void VerilatedContextImp::commandArgVl(const std::string& arg) {
if (0 == std::strncmp(arg.c_str(), "+verilator+", std::strlen("+verilator+"))) {
std::string str;
uint64_t u64;
if (arg == "+verilator+debug") {
if (commandArgVlString(arg, "+verilator+coverage+file+", str)) {
coverageFilename(str);
} else if (arg == "+verilator+debug") {
Verilated::debug(4);
} else if (commandArgVlUint64(arg, "+verilator+debugi+", u64, 0,
std::numeric_limits<int>::max())) {

View File

@ -385,6 +385,7 @@ protected:
uint64_t m_profExecStart = 1; // +prof+exec+start time
uint32_t m_profExecWindow = 2; // +prof+exec+window size
// Slow path
std::string m_coverageFilename; // +coverage+file filename
std::string m_profExecFilename; // +prof+exec+file filename
std::string m_profVltFilename; // +prof+vlt filename
} m_ns;
@ -576,6 +577,10 @@ public:
VerilatedVirtualBase*
enableExecutionProfiler(VerilatedVirtualBase* (*construct)(VerilatedContext&));
// Internal: coverage
void coverageFilename(const std::string& flag) VL_MT_SAFE;
std::string coverageFilename() const VL_MT_SAFE;
// Internal: $dumpfile
void dumpfile(const std::string& flag) VL_MT_SAFE_EXCLUDES(m_timeDumpMutex);
std::string dumpfile() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex);

View File

@ -106,6 +106,7 @@ private:
using ItemList = std::deque<VerilatedCovImpItem*>;
// MEMBERS
VerilatedContext* const m_contextp; // Context VerilatedCovImp is pointed-to by
mutable VerilatedMutex m_mutex; // Protects all members
ValueIndexMap m_valueIndexes VL_GUARDED_BY(m_mutex); // Unique arbitrary value for values
IndexValueMap m_indexValues VL_GUARDED_BY(m_mutex); // Unique arbitrary value for keys
@ -120,7 +121,8 @@ private:
public:
// CONSTRUCTORS
VerilatedCovImp() = default;
VerilatedCovImp(VerilatedContext* contextp)
: m_contextp{contextp} {}
VL_UNCOPYABLE(VerilatedCovImp);
protected:
@ -253,6 +255,7 @@ private:
public:
// PUBLIC METHODS
std::string defaultFilename() VL_MT_SAFE { return m_contextp->coverageFilename(); }
void forcePerInstance(const bool flag) VL_MT_SAFE_EXCLUDES(m_mutex) {
Verilated::quiesce();
const VerilatedLockGuard lock{m_mutex};
@ -354,7 +357,7 @@ public:
m_insertp = nullptr;
}
void write(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
void write(const std::string& filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
Verilated::quiesce();
const VerilatedLockGuard lock{m_mutex};
selftest();
@ -426,6 +429,7 @@ public:
//=============================================================================
// VerilatedCovContext
std::string VerilatedCovContext::defaultFilename() VL_MT_SAFE { return impp()->defaultFilename(); }
void VerilatedCovContext::forcePerInstance(bool flag) VL_MT_SAFE {
impp()->forcePerInstance(flag);
}
@ -434,7 +438,9 @@ void VerilatedCovContext::clearNonMatch(const char* matchp) VL_MT_SAFE {
impp()->clearNonMatch(matchp);
}
void VerilatedCovContext::zero() VL_MT_SAFE { impp()->zero(); }
void VerilatedCovContext::write(const char* filenamep) VL_MT_SAFE { impp()->write(filenamep); }
void VerilatedCovContext::write(const std::string& filename) VL_MT_SAFE {
impp()->write(filename);
}
void VerilatedCovContext::_inserti(uint32_t* itemp) VL_MT_SAFE {
impp()->inserti(new VerilatedCoverItemSpec<uint32_t>{itemp});
}
@ -516,7 +522,7 @@ VerilatedCovContext* VerilatedContext::coveragep() VL_MT_SAFE {
const VerilatedLockGuard lock{s_mutex};
// cppcheck-suppress identicalInnerCondition
if (VL_LIKELY(!m_coveragep)) { // LCOV_EXCL_LINE // Not redundant, prevents race
m_coveragep.reset(new VerilatedCovImp);
m_coveragep.reset(new VerilatedCovImp{this});
}
}
return reinterpret_cast<VerilatedCovContext*>(m_coveragep.get());

View File

@ -96,12 +96,13 @@ class VerilatedCovContext VL_NOT_FINAL : public VerilatedVirtualBase {
public:
// METHODS
/// Return default filename
static const char* defaultFilename() VL_PURE { return "coverage.dat"; }
/// Return default filename, may override with +verilator+coverage+file
std::string defaultFilename() VL_MT_SAFE;
/// Make all data per_instance, overriding point's per_instance
void forcePerInstance(bool flag) VL_MT_SAFE;
/// Write all coverage data to a file
void write(const char* filenamep = defaultFilename()) VL_MT_SAFE;
void write(const std::string& filename) VL_MT_SAFE;
void write() VL_MT_SAFE { write(defaultFilename()); }
/// Clear coverage points (and call delete on all items)
void clear() VL_MT_SAFE;
/// Clear items not matching the provided string
@ -170,11 +171,10 @@ class VerilatedCov final {
public:
// METHODS
/// Return default filename for the current thread
static const char* defaultFilename() VL_PURE { return VerilatedCovContext::defaultFilename(); }
static std::string defaultFilename() VL_MT_SAFE { return threadCovp()->defaultFilename(); }
/// Write all coverage data to a file for the current thread
static void write(const char* filenamep = defaultFilename()) VL_MT_SAFE {
threadCovp()->write(filenamep);
}
static void write(const std::string filename) VL_MT_SAFE { threadCovp()->write(filename); }
static void write() VL_MT_SAFE { write(defaultFilename()); }
/// Clear coverage points (and call delete on all items) for the current thread
static void clear() VL_MT_SAFE { threadCovp()->clear(); }
/// Clear items not matching the provided string for the current thread

View File

@ -100,8 +100,16 @@ private:
puts("}\n");
puts("\n");
puts("// Final model cleanup\n");
puts("// Execute 'final' processes\n");
puts("topp->final();\n");
puts("\n");
if (v3Global.opt.coverage()) {
puts("// Write coverage data (since Verilated with --coverage)\n");
puts("contextp->coveragep()->write();\n");
puts("\n");
}
puts("return 0;\n");
puts("}\n");

View File

@ -64,7 +64,7 @@ int main() {
coverw[5] = 300;
#ifdef T_COVER_LIB
TEST_CHECK_CSTR(covContextp->defaultFilename(), "coverage.dat");
TEST_CHECK_EQ(covContextp->defaultFilename(), "coverage.dat");
covContextp->write(VL_STRINGIFY(TEST_OBJ_DIR) "/coverage1.dat");
covContextp->forcePerInstance(true);
covContextp->write(VL_STRINGIFY(TEST_OBJ_DIR) "/coverage1_per_instance.dat");
@ -74,16 +74,20 @@ int main() {
covContextp->zero();
covContextp->write(VL_STRINGIFY(TEST_OBJ_DIR) "/coverage3.dat");
covContextp->clear();
covContextp->write(VL_STRINGIFY(TEST_OBJ_DIR) "/coverage4.dat");
Verilated::defaultContextp()->coverageFilename(VL_STRINGIFY(TEST_OBJ_DIR) "/coverage4.dat");
TEST_CHECK_EQ(covContextp->defaultFilename(), VL_STRINGIFY(TEST_OBJ_DIR) "/coverage4.dat");
covContextp->write(); // Uses defaultFilename()
#elif defined(T_COVER_LIB_LEGACY)
TEST_CHECK_CSTR(VerilatedCov::defaultFilename(), "coverage.dat");
TEST_CHECK_EQ(VerilatedCov::defaultFilename(), "coverage.dat");
VerilatedCov::write(VL_STRINGIFY(TEST_OBJ_DIR) "/coverage1.dat");
VerilatedCov::clearNonMatch("kept_");
VerilatedCov::write(VL_STRINGIFY(TEST_OBJ_DIR) "/coverage2.dat");
VerilatedCov::zero();
VerilatedCov::write(VL_STRINGIFY(TEST_OBJ_DIR) "/coverage3.dat");
VerilatedCov::clear();
VerilatedCov::write(VL_STRINGIFY(TEST_OBJ_DIR) "/coverage4.dat");
Verilated::defaultContextp()->coverageFilename(VL_STRINGIFY(TEST_OBJ_DIR) "/coverage4.dat");
TEST_CHECK_EQ(VerilatedCov::defaultFilename(), VL_STRINGIFY(TEST_OBJ_DIR) "/coverage4.dat");
VerilatedCov::write(); // Uses defaultFilename()
#else
#error
#endif

View File

@ -0,0 +1,2 @@
# SystemC::Coverage-3
C 'ft/t_cover_main.vl9n4pagev_line/toblockS9-11hTOP.t' 1

25
test_regress/t/t_cover_main.pl Executable file
View File

@ -0,0 +1,25 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003-2009 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
scenarios(simulator => 1);
compile(
verilator_flags2 => ['--binary --coverage-line'],
);
execute(
all_run_flags => [" +verilator+coverage+file+$Self->{obj_dir}/coverage_renamed.dat"],
check_finished => 1,
);
files_identical_sorted("$Self->{obj_dir}/coverage_renamed.dat", "t/t_cover_main.out");
ok(1);
1;

View File

@ -0,0 +1,13 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2024 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
module t (/*AUTOARG*/);
initial begin
$write("*-* All Finished *-*\n");
$finish;
end
endmodule