diff --git a/Changes b/Changes index 54ff45b55..d791fd4b7 100644 --- a/Changes +++ b/Changes @@ -5,6 +5,8 @@ The contributors that suggested a given feature are shown in []. Thanks! * Verilator 4.035 devel +*** Support verilator_coverage --write-info for lcov HTML reports. + **** Support multi channel descriptor I/O (#2190) [Stephen Henry] **** Support $countbits. (#2287) [Yossi Nivin] diff --git a/bin/verilator b/bin/verilator index f01769c2b..f2bf9c41a 100755 --- a/bin/verilator +++ b/bin/verilator @@ -5171,6 +5171,10 @@ For an example, after running 'make test' in the Verilator distribution, see the examples/make_tracing_c/logs directory. Grep for lines starting with '%' to see what lines Verilator believes need more coverage. +Info files can be written by verilator_coverage for import to C. +This enables use of C for HTML reports and importing reports to +sites such as L. + =item Where is the translate_off command? (How do I ignore a construct?) Translate on/off pragmas are generally a bad idea, as it's easy to have diff --git a/bin/verilator_coverage b/bin/verilator_coverage index 37b931c29..e3d0bf45e 100755 --- a/bin/verilator_coverage +++ b/bin/verilator_coverage @@ -156,6 +156,8 @@ verilator_coverage - Verilator coverage analyzer verilator_coverage -write merged.dat -read ... + verilator_coverage -write-info merged.info -read ... + Verilator_coverage processes Verilator coverage reports. With --anotate, it reads the specified data file and generates annotated @@ -224,8 +226,20 @@ Displays program version and exits. =item --write I Specifies the aggregate coverage results, summed across all the files, -should be written to the given filename. This is useful in scripts to -combine many sequential runs into one master coverage file. +should be written to the given filename in verilator_coverage data format. +This is useful in scripts to combine many sequential runs into one master +coverage file. + +=item --write-info I + +Specifies the aggregate coverage results, summed across all the files, +should be written to the given filename in C .info format. +This may be used to use C to aggregate or generate reports. + +The info format loses data compared to the Verilator coverage data format; +the info will all forms of coverage converted to line style coverage, and +if there are multiple coverage points on a single line, the minimum +coverage across those points will be used to report coverage of the line. =back @@ -277,7 +291,7 @@ Wilson Snyder =head1 SEE ALSO -C +C, C L which is the source for this document. diff --git a/src/VlcMain.cpp b/src/VlcMain.cpp index 5cdbdb36d..c712811c9 100644 --- a/src/VlcMain.cpp +++ b/src/VlcMain.cpp @@ -108,6 +108,9 @@ void VlcOptions::parseOptsList(int argc, char** argv) { } else if (!strcmp(sw, "-write") && (i + 1) < argc) { shift; m_writeFile = argv[i]; + } else if (!strcmp(sw, "-write-info") && (i + 1) < argc) { + shift; + m_writeInfoFile = argv[i]; } else { v3fatal("Invalid option: " << argv[i]); } @@ -170,8 +173,9 @@ int main(int argc, char** argv, char** /*env*/) { top.tests().dump(false); } - if (!top.opt.writeFile().empty()) { - top.writeCoverage(top.opt.writeFile()); + if (!top.opt.writeFile().empty() || !top.opt.writeInfoFile().empty()) { + if (!top.opt.writeFile().empty()) top.writeCoverage(top.opt.writeFile()); + if (!top.opt.writeInfoFile().empty()) top.writeInfo(top.opt.writeInfoFile()); V3Error::abortIfWarnings(); if (top.opt.unlink()) { const VlStringSet& readFiles = top.opt.readFiles(); diff --git a/src/VlcOptions.h b/src/VlcOptions.h index 6d76dd99d..e0382766a 100644 --- a/src/VlcOptions.h +++ b/src/VlcOptions.h @@ -42,6 +42,7 @@ class VlcOptions { bool m_rank; // main switch: --rank bool m_unlink; // main switch: --unlink string m_writeFile; // main switch: --write + string m_writeInfoFile; // main switch: --write-info // clang-format on private: @@ -72,6 +73,7 @@ public: bool rank() const { return m_rank; } bool unlink() const { return m_unlink; } string writeFile() const { return m_writeFile; } + string writeInfoFile() const { return m_writeInfoFile; } // METHODS (from main) static string version(); diff --git a/src/VlcTop.cpp b/src/VlcTop.cpp index 864479146..2cb311ae2 100644 --- a/src/VlcTop.cpp +++ b/src/VlcTop.cpp @@ -77,6 +77,61 @@ void VlcTop::writeCoverage(const string& filename) { } } +void VlcTop::writeInfo(const string& filename) { + UINFO(2, "writeInfo " << filename << endl); + + std::ofstream os(filename.c_str()); + if (!os) { + v3fatal("Can't write " << filename); + return; + } + + annotateCalc(); + + // See 'man lcov' for format details + // TN: + // Source file: + // SF: + // FN:, + // FNDA:, + // FNF: + // FNH: + // Branches: + // BRDA:,,, + // BRF: + // BRH: + // Line counts: + // DA:, + // LF: + // LH: + // Section ending: + // end_of_record + + os << "TN:verilator_coverage\n"; + for (VlcSources::NameMap::iterator sit = m_sources.begin(); sit != m_sources.end(); ++sit) { + VlcSource& source = sit->second; + os << "SF:" << source.name() << endl; + VlcSource::LinenoMap& lines = source.lines(); + for (VlcSource::LinenoMap::iterator lit = lines.begin(); lit != lines.end(); ++lit) { + int lineno = lit->first; + VlcSource::ColumnMap& cmap = lit->second; + bool first = true; + vluint64_t min_count = 0; // Minimum across all columns on line + for (VlcSource::ColumnMap::iterator cit = cmap.begin(); cit != cmap.end(); ++cit) { + VlcSourceCount& col = cit->second; + if (first) { + min_count = col.count(); + first = false; + } else { + min_count = std::min(min_count, col.count()); + } + } + os << "DA:" << lineno << "," << min_count << "\n"; + } + os << "end_of_record\n"; + } +} + //******************************************************************** struct CmpComputrons { diff --git a/src/VlcTop.h b/src/VlcTop.h index c314b81b2..d0583ca54 100644 --- a/src/VlcTop.h +++ b/src/VlcTop.h @@ -58,6 +58,7 @@ public: void annotate(const string& dirname); void readCoverage(const string& filename, bool nonfatal = false); void writeCoverage(const string& filename); + void writeInfo(const string& filename); void rank(); }; diff --git a/test_regress/t/t_cover_line_cc.pl b/test_regress/t/t_cover_line_cc.pl index 563ce9ffd..287e35d93 100755 --- a/test_regress/t/t_cover_line_cc.pl +++ b/test_regress/t/t_cover_line_cc.pl @@ -31,5 +31,24 @@ run(cmd => ["../bin/verilator_coverage", files_identical("$Self->{obj_dir}/annotated/t_cover_line.v", "t/t_cover_line.out"); +# Also try lcov +run(cmd => ["../bin/verilator_coverage", + "--write-info", "$Self->{obj_dir}/coverage.info", + "$Self->{obj_dir}/coverage.dat"], + verilator_run => 1, + ); + +# If installed +if (`lcov --help` !~ /Usage:/ + || `genhtml --help` !~ /Usage:/) { + skip("lcov or genhtml not installed"); +} else { + run(cmd => ["genhtml", + "$Self->{obj_dir}/coverage.info", + "--output-directory $Self->{obj_dir}/html", + ]); +} + + ok(1); 1; diff --git a/test_regress/t/t_vlcov_info.out b/test_regress/t/t_vlcov_info.out new file mode 100644 index 000000000..40b3c320b --- /dev/null +++ b/test_regress/t/t_vlcov_info.out @@ -0,0 +1,4 @@ +TN:verilator_coverage +SF:file1.sp +DA:159,53 +end_of_record diff --git a/test_regress/t/t_vlcov_info.pl b/test_regress/t/t_vlcov_info.pl new file mode 100755 index 000000000..ee4467fc2 --- /dev/null +++ b/test_regress/t/t_vlcov_info.pl @@ -0,0 +1,26 @@ +#!/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(dist => 1); + +run(cmd => ["../bin/verilator_coverage", + "--write-info", "$Self->{obj_dir}/coverage.info", + "t/t_vlcov_data_a.dat", + "t/t_vlcov_data_b.dat", + "t/t_vlcov_data_c.dat", + "t/t_vlcov_data_d.dat", + ], + verilator_run => 1, + ); + +files_identical("$Self->{obj_dir}/coverage.info", $Self->{golden_filename}); + +ok(1); +1;