mirror of
https://github.com/verilator/verilator.git
synced 2025-01-01 04:07:34 +00:00
Add --main support for dumping coverage, and +verilator+coverage+file runtime option.
This commit is contained in:
parent
a38fea1f9e
commit
1a92502788
2
Changes
2
Changes
@ -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]
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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())) {
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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
|
||||
|
2
test_regress/t/t_cover_main.out
Normal file
2
test_regress/t/t_cover_main.out
Normal 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
25
test_regress/t/t_cover_main.pl
Executable 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;
|
13
test_regress/t/t_cover_main.v
Normal file
13
test_regress/t/t_cover_main.v
Normal 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
|
Loading…
Reference in New Issue
Block a user