From 1a9250278825f383efe8d6ef15b55c477e6723e5 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 20 Jan 2024 12:28:49 -0500 Subject: [PATCH] Add --main support for dumping coverage, and +verilator+coverage+file runtime option. --- Changes | 2 ++ docs/guide/exe_sim.rst | 5 +++++ docs/guide/simulating.rst | 18 ++++++++++++------ include/verilated.cpp | 13 ++++++++++++- include/verilated.h | 5 +++++ include/verilated_cov.cpp | 14 ++++++++++---- include/verilated_cov.h | 14 +++++++------- src/V3EmitCMain.cpp | 10 +++++++++- test_regress/t/t_cover_lib_c.cpp | 12 ++++++++---- test_regress/t/t_cover_main.out | 2 ++ test_regress/t/t_cover_main.pl | 25 +++++++++++++++++++++++++ test_regress/t/t_cover_main.v | 13 +++++++++++++ 12 files changed, 110 insertions(+), 23 deletions(-) create mode 100644 test_regress/t/t_cover_main.out create mode 100755 test_regress/t/t_cover_main.pl create mode 100644 test_regress/t/t_cover_main.v diff --git a/Changes b/Changes index 293b244f2..98c816c51 100644 --- a/Changes +++ b/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] diff --git a/docs/guide/exe_sim.rst b/docs/guide/exe_sim.rst index 7fbeaff0c..0040e242f 100644 --- a/docs/guide/exe_sim.rst +++ b/docs/guide/exe_sim.rst @@ -19,6 +19,11 @@ Summary: .. include:: ../_build/gen/args_verilated.rst +.. option:: +verilator+coverage+file+ + + 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 diff --git a/docs/guide/simulating.rst b/docs/guide/simulating.rst index c4af6115a..e196444dd 100644 --- a/docs/guide/simulating.rst +++ b/docs/guide/simulating.rst @@ -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+\`. + +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 diff --git a/include/verilated.cpp b/include/verilated.cpp index 73d3cee4f..20a2d8e91 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -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::max())) { diff --git a/include/verilated.h b/include/verilated.h index 89181ace1..d12ac0148 100644 --- a/include/verilated.h +++ b/include/verilated.h @@ -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); diff --git a/include/verilated_cov.cpp b/include/verilated_cov.cpp index ea5d738fd..269597508 100644 --- a/include/verilated_cov.cpp +++ b/include/verilated_cov.cpp @@ -106,6 +106,7 @@ private: using ItemList = std::deque; // 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{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(m_coveragep.get()); diff --git a/include/verilated_cov.h b/include/verilated_cov.h index d759e1a9f..2be74336b 100644 --- a/include/verilated_cov.h +++ b/include/verilated_cov.h @@ -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 diff --git a/src/V3EmitCMain.cpp b/src/V3EmitCMain.cpp index d267cba86..4adbab1e6 100644 --- a/src/V3EmitCMain.cpp +++ b/src/V3EmitCMain.cpp @@ -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"); diff --git a/test_regress/t/t_cover_lib_c.cpp b/test_regress/t/t_cover_lib_c.cpp index 3ba805720..2e29e0214 100644 --- a/test_regress/t/t_cover_lib_c.cpp +++ b/test_regress/t/t_cover_lib_c.cpp @@ -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 diff --git a/test_regress/t/t_cover_main.out b/test_regress/t/t_cover_main.out new file mode 100644 index 000000000..d762fd325 --- /dev/null +++ b/test_regress/t/t_cover_main.out @@ -0,0 +1,2 @@ +# SystemC::Coverage-3 +C 'ft/t_cover_main.vl9n4pagev_line/toblockS9-11hTOP.t' 1 diff --git a/test_regress/t/t_cover_main.pl b/test_regress/t/t_cover_main.pl new file mode 100755 index 000000000..efd553940 --- /dev/null +++ b/test_regress/t/t_cover_main.pl @@ -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; diff --git a/test_regress/t/t_cover_main.v b/test_regress/t/t_cover_main.v new file mode 100644 index 000000000..4f5101d7d --- /dev/null +++ b/test_regress/t/t_cover_main.v @@ -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