forked from github/verilator
New verilator_coverage and infrastructure to replace SystemPerl's vcoverage.
This commit is contained in:
parent
6da13c6486
commit
9ec35a2348
1
.gitignore
vendored
1
.gitignore
vendored
@ -23,3 +23,4 @@ gdbrun*
|
||||
internals.txt
|
||||
verilator.txt
|
||||
verilator_bin*
|
||||
verilator_coverage_bin*
|
||||
|
2
Changes
2
Changes
@ -5,6 +5,8 @@ indicates the contributor was also the author of the fix; Thanks!
|
||||
|
||||
* Verilator 3.867 devel
|
||||
|
||||
** New verilator_coverage program added to replace SystemPerl's vcoverage.
|
||||
|
||||
** PSL support was removed, please use System Verilog assertions.
|
||||
|
||||
|
||||
|
@ -28,6 +28,7 @@ config.status$
|
||||
verilator.log
|
||||
verilator.tex
|
||||
verilator_bin.*
|
||||
verilator_coverage_bin.*
|
||||
.vcsmx_rebuild$
|
||||
autom4te\.cache/
|
||||
nodist/
|
||||
|
17
Makefile.in
17
Makefile.in
@ -122,6 +122,7 @@ DISTFILES_INC = $(INFOS) .gitignore Artistic COPYING COPYING.LESSER \
|
||||
.*attributes */.*attributes */*/.*attributes \
|
||||
src/.*ignore src/*.in src/*.cpp src/*.[chly] \
|
||||
src/astgen src/bisonpre src/*fix src/cppcheck_filtered \
|
||||
src/vlcovgen \
|
||||
src/.gdbinit \
|
||||
src/*.pl src/*.pod \
|
||||
test_*/.*ignore test_*/Makefile* test_*/*.cpp \
|
||||
@ -140,6 +141,7 @@ DISTFILES_INC = $(INFOS) .gitignore Artistic COPYING COPYING.LESSER \
|
||||
|
||||
INST_PROJ_FILES = \
|
||||
bin/verilator \
|
||||
bin/verilator_coverage \
|
||||
bin/verilator_includer \
|
||||
bin/verilator_profcfunc \
|
||||
include/verilated.mk \
|
||||
@ -149,6 +151,7 @@ INST_PROJ_FILES = \
|
||||
INST_PROJ_BIN_FILES = \
|
||||
verilator_bin \
|
||||
verilator_bin_dbg \
|
||||
verilator_coverage_bin_dbg \
|
||||
|
||||
DISTFILES := $(DISTFILES_INC)
|
||||
|
||||
@ -165,7 +168,8 @@ all_nomsg: verilator_exe $(VL_INST_MAN_FILES)
|
||||
.PHONY:verilator_exe
|
||||
.PHONY:verilator_bin
|
||||
.PHONY:verilator_bin_dbg
|
||||
verilator_exe verilator_bin verilator_bin_dbg:
|
||||
.PHONY:verilator_coverage_bin_dbg
|
||||
verilator_exe verilator_bin verilator_bin_dbg verilator_coverage_bin_dbg:
|
||||
@echo ------------------------------------------------------------
|
||||
@echo "making verilator in src" ; \
|
||||
(cd src && $(MAKE) $(OBJCACHE_JOBS) )
|
||||
@ -261,8 +265,8 @@ internals.pdf: internals.pod Makefile
|
||||
-rm -f internals.toc internals.aux internals.idx internals.out
|
||||
|
||||
# See uninstall also - don't put wildcards in this variable, it might uninstall other stuff
|
||||
VL_INST_BIN_FILES = verilator verilator_bin verilator_bin_dbg \
|
||||
verilator_includer verilator_profcfunc
|
||||
VL_INST_BIN_FILES = verilator verilator_bin verilator_bin_dbg verilator_coverage_bin_dbg \
|
||||
verilator_coverage verilator_includer verilator_profcfunc
|
||||
# Some scripts go into both the search path and pkgdatadir,
|
||||
# so they can be found by the user, and under $VERILATOR_ROOT.
|
||||
|
||||
@ -286,9 +290,11 @@ VL_INST_DATA_SRCDIR_FILES = \
|
||||
installbin:
|
||||
$(SHELL) ${srcdir}/mkinstalldirs $(DESTDIR)$(bindir)
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator $(DESTDIR)$(bindir)/verilator )
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_coverage $(DESTDIR)$(bindir)/verilator_coverage )
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_profcfunc $(DESTDIR)$(bindir)/verilator_profcfunc )
|
||||
( $(INSTALL_PROGRAM) verilator_bin $(DESTDIR)$(bindir)/verilator_bin )
|
||||
( $(INSTALL_PROGRAM) verilator_bin_dbg $(DESTDIR)$(bindir)/verilator_bin_dbg )
|
||||
( $(INSTALL_PROGRAM) verilator_coverage_bin_dbg $(DESTDIR)$(bindir)/verilator_coverage_bin_dbg )
|
||||
$(SHELL) ${srcdir}/mkinstalldirs $(DESTDIR)$(pkgdatadir)/bin
|
||||
( cd ${srcdir}/bin ; $(INSTALL_PROGRAM) verilator_includer $(DESTDIR)$(pkgdatadir)/bin/verilator_includer )
|
||||
|
||||
@ -351,6 +357,7 @@ INST_PROJ_CVS = cp_if_cvs_diff
|
||||
install-project: dist
|
||||
@echo "Install-project to $(DIRPROJECT)"
|
||||
strip verilator_bin*
|
||||
strip verilator_coverage_bin*
|
||||
$(MAKE) install-project-quick
|
||||
for p in $(VL_INST_MAN_FILES) ; do \
|
||||
$(INSTALL_DATA) $$p $(DIRPROJECT_PREFIX)/man/man1/$$p; \
|
||||
@ -374,6 +381,7 @@ endif
|
||||
install-cadtools: dist
|
||||
@echo "Install-project to $(CAD_DIR)"
|
||||
strip verilator_bin*
|
||||
strip verilator_coverage_bin*
|
||||
$(MAKE) install-cadtools-quick
|
||||
$(SHELL) ${srcdir}/mkinstalldirs $(VERILATOR_CAD_DIR)/man/man1
|
||||
for p in $(VL_INST_MAN_FILES) ; do \
|
||||
@ -459,7 +467,8 @@ clean mostlyclean distclean maintainer-clean::
|
||||
rm -f *.tex
|
||||
|
||||
distclean maintainer-clean::
|
||||
rm -f Makefile config.status config.cache config.log verilator_bin* TAGS
|
||||
rm -f Makefile config.status config.cache config.log TAGS
|
||||
rm -f verilator_bin* verilator_coverage_bin*
|
||||
rm -f include/verilated.mk include/verilated_config.h
|
||||
|
||||
TAGFILES=${srcdir}/*/*.cpp ${srcdir}/*/*.h ${srcdir}/*/*.in \
|
||||
|
17
TODO
17
TODO
@ -30,23 +30,6 @@ Configure/Make/Install
|
||||
* Full MSVC++ compilation (does scons support this?) (4.000?)
|
||||
* Distribute with flex/bison already expanded?
|
||||
Flex library not needed. Probably too difficult to be worth it.
|
||||
* Integrate SystemPerl coverage
|
||||
see the SystemPerl git branch coverage_only
|
||||
(Note in /usr/include there are no upper cased include files.)
|
||||
Coverage.pm -- Need all functionality, but in C?
|
||||
Coverage/Item.pm -- Need all functionality, but in C?
|
||||
Coverage/ItemKey.pm -- Need all functionality, but in C?
|
||||
sp_preproc -- Some steps in here need to be moved to generated C
|
||||
-- -- note uses Verilog::Getopt
|
||||
src/Sp.cpp -- n/a
|
||||
src/SpCommon.h -- mostly overlaps verilatedos.h
|
||||
src/SpCoverage.cpp/h -- All needed
|
||||
src/SpFunctor.cpp/h -- No longer used
|
||||
src/SpTraceVcd.cpp/h -- MOVED
|
||||
src/SpTraceVcdC.cpp/h -- MOVED
|
||||
src/sp_log.cpp/h -- Not needed
|
||||
src/systemperl.h -- some stuff may be cut
|
||||
vcoverage -- Need all functionality, but in C?
|
||||
|
||||
Testing:
|
||||
* Move test_c/sp/v/verilated into test_regress format (4.000?)
|
||||
|
@ -3528,9 +3528,9 @@ Verilator's, it will do this for you.)
|
||||
Run your tests in different directories. Each test will create a
|
||||
logs/coverage.pl file.
|
||||
|
||||
After running all of your tests, the vcoverage utility (from the SystemPerl
|
||||
package) is executed. Vcoverage reads the logs/coverage.pl file(s), and
|
||||
creates an annotated source code listing showing code coverage details.
|
||||
After running all of your tests, verilator_coverage is executed.
|
||||
Verilator_coverage reads the logs/coverage.pl file(s), and creates an
|
||||
annotated source code listing showing code coverage details.
|
||||
|
||||
For an example, after running 'make test' in the Verilator distribution,
|
||||
see the test_sp/logs/coverage_source directory. Grep for lines starting
|
||||
@ -3825,7 +3825,7 @@ Major concepts by Paul Wasson and Duane Galbi.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<verilator_profcfunc>, L<systemperl>, L<vcoverage>, L<make>,
|
||||
L<verilator_coverage>, L<verilator_profcfunc>, L<make>,
|
||||
|
||||
L<verilator --help> which is the source for this document,
|
||||
|
||||
|
290
bin/verilator_coverage
Executable file
290
bin/verilator_coverage
Executable file
@ -0,0 +1,290 @@
|
||||
: # -*-Mode: perl;-*- use perl, wherever it is
|
||||
eval 'exec perl -wS $0 ${1+"$@"}'
|
||||
if 0;
|
||||
######################################################################
|
||||
#
|
||||
# Copyright 2003-2014 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.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
######################################################################
|
||||
|
||||
require 5.006_001;
|
||||
use warnings;
|
||||
BEGIN {
|
||||
if ($ENV{DIRPROJECT} && $ENV{DIRPROJECT_PERL_BOOT}) {
|
||||
# Magic to allow author testing of perl packages in local directory
|
||||
require $ENV{DIRPROJECT}."/".$ENV{DIRPROJECT_PERL_BOOT};
|
||||
}
|
||||
}
|
||||
|
||||
use Getopt::Long;
|
||||
use FindBin qw($RealBin $RealScript);
|
||||
use Pod::Usage;
|
||||
use Cwd qw(abs_path getcwd);
|
||||
|
||||
use strict;
|
||||
use vars qw ($Debug @Opt_Verilator_Sw);
|
||||
|
||||
#######################################################################
|
||||
#######################################################################
|
||||
# main
|
||||
|
||||
autoflush STDOUT 1;
|
||||
autoflush STDERR 1;
|
||||
|
||||
$Debug = 0;
|
||||
|
||||
# No arguments can't do anything useful. Give help
|
||||
if ($#ARGV < 0) {
|
||||
pod2usage(-exitstatus=>2, -verbose=>0);
|
||||
}
|
||||
|
||||
# We sneak a look at the flags so we can do some pre-environment checks
|
||||
# All flags will hit verilator...
|
||||
foreach my $sw (@ARGV) {
|
||||
$sw = "'$sw'" if $sw =~ m![^---a-zA-Z0-9_/\\:.+]!;
|
||||
push @Opt_Verilator_Sw, $sw;
|
||||
}
|
||||
|
||||
Getopt::Long::config ("no_auto_abbrev","pass_through");
|
||||
if (! GetOptions (
|
||||
# Major operating modes
|
||||
"help" => \&usage,
|
||||
"debug:s" => \&debug,
|
||||
# "version!" => \&version, # Also passthru'ed
|
||||
# Additional parameters
|
||||
"<>" => sub {}, # Ignored
|
||||
)) {
|
||||
pod2usage(-exitstatus=>2, -verbose=>0);
|
||||
}
|
||||
|
||||
# Normal, non gdb
|
||||
run (verilator_coverage_bin()
|
||||
." ".join(' ',@Opt_Verilator_Sw));
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
sub usage {
|
||||
pod2usage(-verbose=>2, -exitval=>2, -output=>\*STDOUT);
|
||||
}
|
||||
|
||||
sub debug {
|
||||
shift;
|
||||
my $level = shift;
|
||||
$Debug = $level||3;
|
||||
}
|
||||
|
||||
#######################################################################
|
||||
#######################################################################
|
||||
# Builds
|
||||
|
||||
sub verilator_coverage_bin {
|
||||
my $bin = "";
|
||||
# Use VERILATOR_ROOT if defined, else assume verilator_bin is in the search path
|
||||
my $basename = ($ENV{VERILATOR_COVERAGE_BIN}
|
||||
|| "verilator_coverage_bin_dbg");
|
||||
if (defined($ENV{VERILATOR_ROOT})) {
|
||||
my $dir = $ENV{VERILATOR_ROOT};
|
||||
if (-x "$dir/bin/$basename") { # From a "make install" into VERILATOR_ROOT
|
||||
$bin = "$dir/bin/$basename";
|
||||
} else {
|
||||
$bin = "$dir/$basename"; # From pointing to kit directory
|
||||
}
|
||||
} else {
|
||||
if (-x "$RealBin/$basename") {
|
||||
$bin = "$RealBin/$basename"; # From path/to/verilator with verilator_bin installed
|
||||
} else {
|
||||
$bin = $basename; # Find in PATH
|
||||
}
|
||||
# Note we don't look under bin/$basename which would be right if running
|
||||
# in the kit dir. Running that would likely break, since
|
||||
# VERILATOR_ROOT wouldn't be set and Verilator won't find internal files.
|
||||
}
|
||||
return $bin;
|
||||
}
|
||||
|
||||
#######################################################################
|
||||
#######################################################################
|
||||
# Utilities
|
||||
|
||||
sub run {
|
||||
# Run command, check errors
|
||||
my $command = shift;
|
||||
$! = undef; # Cleanup -x
|
||||
print "\t$command\n" if $Debug>=3;
|
||||
system($command);
|
||||
my $status = $?;
|
||||
if ($status) {
|
||||
if ($! =~ /no such file or directory/i) {
|
||||
warn "%Error: verilator_coverage: Misinstalled, or VERILATOR_ROOT might need to be in environment\n";
|
||||
}
|
||||
if ($Debug) { # For easy rerunning
|
||||
warn "%Error: export VERILATOR_ROOT=".($ENV{VERILATOR_ROOT}||"")."\n";
|
||||
warn "%Error: $command\n";
|
||||
}
|
||||
if ($status & 127) {
|
||||
if (($status & 127) == 8 || ($status & 127) == 11) { # SIGFPA or SIGSEGV
|
||||
warn "%Error: Verilator_coverage internal fault, sorry.\n" if !$Debug;
|
||||
} elsif (($status & 127) == 6) { # SIGABRT
|
||||
warn "%Error: Verilator_coverage aborted.\n" if !$Debug;
|
||||
} else {
|
||||
warn "%Error: Verilator_coverage threw signal $status.\n" if !$Debug;
|
||||
}
|
||||
}
|
||||
die "%Error: Command Failed $command\n";
|
||||
}
|
||||
}
|
||||
|
||||
#######################################################################
|
||||
#######################################################################
|
||||
package main;
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=head1 NAME
|
||||
|
||||
verilator_coverage - Verilator coverage analyzer
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
verilator_coverage --help
|
||||
verilator_coverage --version
|
||||
|
||||
verilator_coverage --annotate <obj>
|
||||
|
||||
verilator_coverage -write merged.dat -read <datafiles>...
|
||||
|
||||
Verilator_coverage processes Verilator coverage reports.
|
||||
|
||||
With --anotate, it reads the specified data file and generates annotated
|
||||
source code with coverage metrics annotated. If multiple coverage points
|
||||
exist on the same line, additional lines will be inserted to report the
|
||||
additional points.
|
||||
|
||||
Additional Verilog-standard arguments specify the search paths necessary to
|
||||
find the source code that the coverage analysis was performed on.
|
||||
|
||||
To get correct coverage percentages, you may wish to read logs/coverage.pl
|
||||
into Emacs and do a M-x keep-lines to include only those statistics of
|
||||
interest.
|
||||
|
||||
For Verilog conditions that should never occur, you should add a $stop
|
||||
statement. This will remove the coverage during the next build.
|
||||
|
||||
=head1 ARGUMENTS
|
||||
|
||||
=over 4
|
||||
|
||||
=item I<filename>
|
||||
|
||||
Specify input data file, may be repeated to read multiple inputs. If no
|
||||
data file is specified, by default coverage.dat is read.
|
||||
|
||||
=item --annotate I<output_directory>
|
||||
|
||||
Sprcifies the directory name that source files with annotated coverage data
|
||||
should be written to.
|
||||
|
||||
=item --annotate-all
|
||||
|
||||
Specifies all files should be shown. By default, only those source files
|
||||
which have low coverage are written to the output directory.
|
||||
|
||||
=item --annotate-min I<count>
|
||||
|
||||
Specifies the minimum occurrence count that should be flagged if the
|
||||
coverage point does not include a specified threshold. Defaults to 10.
|
||||
|
||||
=item --help
|
||||
|
||||
Displays this message and program version and exits.
|
||||
|
||||
=item --rank
|
||||
|
||||
Print an experimental report listing the relative importance of each test
|
||||
in covering all of the coverage points. The report shows "Covered" which
|
||||
indicates the number of points that test covers; a test is considered to
|
||||
cover a point if it has a bucket count of at least 1. The "rank" column has
|
||||
a higher number t indicate the test is more important, and rank 0 means the
|
||||
test does not need to be run to cover the points. "RankPts" indicates the
|
||||
number of coverage points this test will contribute to overall coverage if
|
||||
all tests are run in the order of highest to lowest rank.
|
||||
|
||||
=item --unlink
|
||||
|
||||
When using --write to combine coverage data, unlink all input files after
|
||||
the output has been created.
|
||||
|
||||
=item --version
|
||||
|
||||
Displays program version and exits.
|
||||
|
||||
=item --write I<filename>
|
||||
|
||||
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.
|
||||
|
||||
=back
|
||||
|
||||
=head1 VERILOG ARGUMENTS
|
||||
|
||||
The following arguments are compatible with GCC, VCS and most Verilog
|
||||
programs.
|
||||
|
||||
=over 4
|
||||
|
||||
=item +libext+I<ext>+I<ext>...
|
||||
|
||||
Defines the extensions for Verilog files.
|
||||
|
||||
=item +define+I<var>+I<value>
|
||||
=item -DI<var>=I<value>
|
||||
|
||||
Defines the given variable.
|
||||
|
||||
=item +incdir+I<dir>
|
||||
=item -II<dir>
|
||||
|
||||
Specifies a directory for finding include files.
|
||||
|
||||
=item -f I<file>
|
||||
|
||||
Specifies a file containing additional command line arguments.
|
||||
|
||||
=item -y I<dir>
|
||||
|
||||
Specifies a module search directory.
|
||||
|
||||
=back
|
||||
|
||||
=head1 DISTRIBUTION
|
||||
|
||||
The latest version is available from L<http://www.veripool.org/>.
|
||||
|
||||
Copyright 2003-2014 by Wilson Snyder. Verilator is free software; you can
|
||||
redistribute it and/or modify the Verilator internals under the terms of
|
||||
either the GNU Lesser General Public License Version 3 or the Perl Artistic
|
||||
License Version 2.0.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
Wilson Snyder <wsnyder@wsnyder.org>
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
C<verilator>
|
||||
|
||||
L<verilator_coverage --help> which is the source for this document.
|
||||
|
||||
=cut
|
||||
|
||||
######################################################################
|
451
include/verilated_cov.cpp
Normal file
451
include/verilated_cov.cpp
Normal file
@ -0,0 +1,451 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//=============================================================================
|
||||
//
|
||||
// THIS MODULE IS PUBLICLY LICENSED
|
||||
//
|
||||
// Copyright 2001-2014 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.
|
||||
//
|
||||
// This is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
// for more details.
|
||||
//
|
||||
//=============================================================================
|
||||
///
|
||||
/// \file
|
||||
/// \brief Verilator coverage analysis
|
||||
///
|
||||
/// AUTHOR: Wilson Snyder
|
||||
///
|
||||
//=============================================================================
|
||||
|
||||
#include "verilatedos.h"
|
||||
#include "verilated.h"
|
||||
#include "verilated_cov.h"
|
||||
#include "verilated_cov_key.h"
|
||||
|
||||
#include <map>
|
||||
#include <deque>
|
||||
#include <fstream>
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedCovImpBase
|
||||
/// Implementation base class for constants
|
||||
|
||||
struct VerilatedCovImpBase {
|
||||
// TYPES
|
||||
enum { MAX_KEYS = 33 }; /// Maximum user arguments + filename+lineno
|
||||
enum { KEY_UNDEF = 0 }; /// Magic key # for unspecified values
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedCovImpItem
|
||||
/// Implementation class for a VerilatedCov item
|
||||
|
||||
class VerilatedCovImpItem : VerilatedCovImpBase {
|
||||
public: // But only local to this file
|
||||
// MEMBERS
|
||||
int m_keys[MAX_KEYS]; ///< Key
|
||||
int m_vals[MAX_KEYS]; ///< Value for specified key
|
||||
// CONSTRUCTORS
|
||||
// Derived classes should call zero() in their constructor
|
||||
VerilatedCovImpItem() {
|
||||
for (int i=0; i<MAX_KEYS; i++) m_keys[i]=KEY_UNDEF;
|
||||
}
|
||||
virtual ~VerilatedCovImpItem() {}
|
||||
virtual vluint64_t count() const = 0;
|
||||
virtual void zero() const = 0;
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
/// VerilatedCoverItem templated for a specific class
|
||||
/// Creates a new coverage item for the specified type.
|
||||
/// This isn't in the header file for auto-magic conversion because it
|
||||
/// inlines to too much code and makes compilation too slow.
|
||||
|
||||
template <class T> class VerilatedCoverItemSpec : public VerilatedCovImpItem {
|
||||
private:
|
||||
// MEMBERS
|
||||
T* m_countp; ///< Count value
|
||||
public:
|
||||
// METHODS
|
||||
virtual vluint64_t count() const { return *m_countp; }
|
||||
virtual void zero() const { *m_countp = 0; }
|
||||
// CONSTRUCTORS
|
||||
VerilatedCoverItemSpec(T* countp) : m_countp(countp) { zero(); }
|
||||
virtual ~VerilatedCoverItemSpec() {}
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedCovImp
|
||||
/// Implementation class for VerilatedCov. See that class for public method information.
|
||||
/// All value and keys are indexed into a unique number. Thus we can greatly reduce
|
||||
/// the storage requirements for otherwise identical keys.
|
||||
|
||||
class VerilatedCovImp : VerilatedCovImpBase {
|
||||
private:
|
||||
// TYPES
|
||||
typedef map<string,int> ValueIndexMap;
|
||||
typedef map<int,string> IndexValueMap;
|
||||
typedef deque<VerilatedCovImpItem*> ItemList;
|
||||
|
||||
private:
|
||||
// MEMBERS
|
||||
ValueIndexMap m_valueIndexes; ///< For each key/value a unique arbitrary index value
|
||||
IndexValueMap m_indexValues; ///< For each key/value a unique arbitrary index value
|
||||
ItemList m_items; ///< List of all items
|
||||
|
||||
VerilatedCovImpItem* m_insertp; ///< Item about to insert
|
||||
const char* m_insertFilenamep; ///< Filename about to insert
|
||||
int m_insertLineno; ///< Line number about to insert
|
||||
|
||||
// CONSTRUCTORS
|
||||
VerilatedCovImp() {
|
||||
m_insertp = NULL;
|
||||
m_insertFilenamep = NULL;
|
||||
m_insertLineno = 0;
|
||||
}
|
||||
public:
|
||||
~VerilatedCovImp() { clear(); }
|
||||
static VerilatedCovImp& imp() {
|
||||
static VerilatedCovImp s_singleton;
|
||||
return s_singleton;
|
||||
}
|
||||
|
||||
private:
|
||||
// PRIVATE METHODS
|
||||
int valueIndex(const string& value) {
|
||||
static int nextIndex = KEY_UNDEF+1;
|
||||
ValueIndexMap::iterator iter = m_valueIndexes.find(value);
|
||||
if (iter != m_valueIndexes.end()) return iter->second;
|
||||
nextIndex++; assert(nextIndex>0);
|
||||
m_valueIndexes.insert(make_pair(value, nextIndex));
|
||||
m_indexValues.insert(make_pair(nextIndex, value));
|
||||
return nextIndex;
|
||||
}
|
||||
string dequote(const string& text) {
|
||||
// Quote any special characters
|
||||
string rtn;
|
||||
for (const char* pos = text.c_str(); *pos; pos++) {
|
||||
if (!isprint(*pos) || *pos=='%' || *pos=='"') {
|
||||
char hex[10]; sprintf(hex,"%%%02X",pos[0]);
|
||||
rtn += hex;
|
||||
} else {
|
||||
rtn += *pos;
|
||||
}
|
||||
}
|
||||
return rtn;
|
||||
}
|
||||
bool legalKey(const string& key) {
|
||||
// Because we compress long keys to a single letter, and
|
||||
// don't want applications to either get confused if they use
|
||||
// a letter differently, nor want them to rely on our compression...
|
||||
// (Considered using numeric keys, but will remain back compatible.)
|
||||
if (key.length()<2) return false;
|
||||
if (key.length()==2 && isdigit(key[1])) return false;
|
||||
return true;
|
||||
}
|
||||
string keyValueFormatter (const string& key, const string& value) {
|
||||
string name;
|
||||
if (key.length()==1 && isalpha(key[0])) {
|
||||
name += string("\001")+key;
|
||||
} else {
|
||||
name += string("\001")+dequote(key);
|
||||
}
|
||||
name += string("\002")+dequote(value);
|
||||
return name;
|
||||
}
|
||||
string combineHier (const string& old, const string& add) {
|
||||
// (foo.a.x, foo.b.x) => foo.*.x
|
||||
// (foo.a.x, foo.b.y) => foo.*
|
||||
// (foo.a.x, foo.b) => foo.*
|
||||
if (old == add) return add;
|
||||
if (old == "") return add;
|
||||
if (add == "") return old;
|
||||
|
||||
const char* a = old.c_str();
|
||||
const char* b = add.c_str();
|
||||
|
||||
// Scan forward to first mismatch
|
||||
const char* apre = a;
|
||||
const char* bpre = b;
|
||||
while (*apre == *bpre) { apre++; bpre++; }
|
||||
|
||||
// We used to backup and split on only .'s but it seems better to be verbose
|
||||
// and not assume . is the separator
|
||||
string prefix = string(a,apre-a);
|
||||
|
||||
// Scan backward to last mismatch
|
||||
const char* apost = a+strlen(a)-1;
|
||||
const char* bpost = b+strlen(b)-1;
|
||||
while (*apost == *bpost
|
||||
&& apost>apre && bpost>bpre) { apost--; bpost--; }
|
||||
|
||||
// Forward to . so we have a whole word
|
||||
string suffix = *bpost ? string(bpost+1) : "";
|
||||
|
||||
string out = prefix+"*"+suffix;
|
||||
|
||||
//cout << "\nch pre="<<prefix<<" s="<<suffix<<"\nch a="<<old<<"\nch b="<<add<<"\nch o="<<out<<endl;
|
||||
return out;
|
||||
}
|
||||
bool itemMatchesString(VerilatedCovImpItem* itemp, const string& match) {
|
||||
for (int i=0; i<MAX_KEYS; i++) {
|
||||
if (itemp->m_keys[i] != KEY_UNDEF) {
|
||||
// We don't compare keys, only values
|
||||
string val = m_indexValues[itemp->m_vals[i]];
|
||||
if (string::npos != val.find(match)) { // Found
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void selftest() {
|
||||
// Little selftest
|
||||
if (combineHier ("a.b.c","a.b.c") !="a.b.c") vl_fatal(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier ("a.b.c","a.b") !="a.b*") vl_fatal(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier ("a.x.c","a.y.c") !="a.*.c") vl_fatal(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier ("a.z.z.z.c","a.b.c") !="a.*.c") vl_fatal(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier ("z","a") !="*") vl_fatal(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier ("q.a","q.b") !="q.*") vl_fatal(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier ("q.za","q.zb") !="q.z*") vl_fatal(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
if (combineHier ("1.2.3.a","9.8.7.a") !="*.a") vl_fatal(__FILE__,__LINE__,"","%Error: selftest\n");
|
||||
}
|
||||
|
||||
public:
|
||||
// PUBLIC METHODS
|
||||
void clear() {
|
||||
for (ItemList::iterator it=m_items.begin(); it!=m_items.end(); ++it) {
|
||||
VerilatedCovImpItem* itemp = *(it);
|
||||
delete itemp;
|
||||
}
|
||||
m_items.clear();
|
||||
m_indexValues.clear();
|
||||
m_valueIndexes.clear();
|
||||
}
|
||||
void clearNonMatch (const char* matchp) {
|
||||
if (matchp && matchp[0]) {
|
||||
ItemList newlist;
|
||||
for (ItemList::iterator it=m_items.begin(); it!=m_items.end(); ++it) {
|
||||
VerilatedCovImpItem* itemp = *(it);
|
||||
if (!itemMatchesString(itemp, matchp)) {
|
||||
delete itemp;
|
||||
} else {
|
||||
newlist.push_back(itemp);
|
||||
}
|
||||
}
|
||||
m_items = newlist;
|
||||
}
|
||||
}
|
||||
void zero() {
|
||||
for (ItemList::iterator it=m_items.begin(); it!=m_items.end(); ++it) {
|
||||
(*it)->zero();
|
||||
}
|
||||
}
|
||||
|
||||
// We assume there's always call to i/f/p in that order
|
||||
void inserti (VerilatedCovImpItem* itemp) {
|
||||
assert(!m_insertp);
|
||||
m_insertp = itemp;
|
||||
}
|
||||
void insertf (const char* filenamep, int lineno) {
|
||||
m_insertFilenamep = filenamep;
|
||||
m_insertLineno = lineno;
|
||||
}
|
||||
void insertp (const char* ckeyps[MAX_KEYS],
|
||||
const char* valps[MAX_KEYS]) {
|
||||
assert(m_insertp);
|
||||
// First two key/vals are filename
|
||||
ckeyps[0]="filename"; valps[0]=m_insertFilenamep;
|
||||
VlCovCvtToCStr linestrp (m_insertLineno);
|
||||
ckeyps[1]="lineno"; valps[1]=linestrp;
|
||||
// Default page if not specified
|
||||
const char* fnstartp = m_insertFilenamep;
|
||||
while (const char* foundp = strchr(fnstartp,'/')) fnstartp=foundp+1;
|
||||
const char* fnendp = fnstartp;
|
||||
while (*fnendp && *fnendp!='.') fnendp++;
|
||||
string page_default = "sp_user/"+string(fnstartp,fnendp-fnstartp);
|
||||
ckeyps[2]="page"; valps[2]=page_default.c_str();
|
||||
|
||||
// Keys -> strings
|
||||
string keys[MAX_KEYS];
|
||||
for (int i=0; i<MAX_KEYS; i++) {
|
||||
if (ckeyps[i] && ckeyps[i][0]) {
|
||||
keys[i] = ckeyps[i];
|
||||
}
|
||||
}
|
||||
// Ignore empty keys
|
||||
for (int i=0; i<MAX_KEYS; i++) {
|
||||
if (keys[i]!="") {
|
||||
for (int j=i+1; j<MAX_KEYS; j++) {
|
||||
if (keys[i] == keys[j]) { // Duplicate key. Keep the last one
|
||||
keys[i] = "";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Insert the values
|
||||
int addKeynum=0;
|
||||
for (int i=0; i<MAX_KEYS; i++) {
|
||||
const string key = keys[i];
|
||||
if (keys[i]!="") {
|
||||
const string val = valps[i];
|
||||
//cout<<" "<<__FUNCTION__<<" "<<key<<" = "<<val<<endl;
|
||||
m_insertp->m_keys[addKeynum] = valueIndex(key);
|
||||
m_insertp->m_vals[addKeynum] = valueIndex(val);
|
||||
addKeynum++;
|
||||
if (!legalKey(key)) {
|
||||
string msg = "%Error: Coverage keys of one character, or letter+digit are illegal: "+key;
|
||||
vl_fatal("",0,"",msg.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
m_items.push_back(m_insertp);
|
||||
// Prepare for next
|
||||
m_insertp = NULL;
|
||||
}
|
||||
|
||||
void write (const char* filename) {
|
||||
#ifndef VM_COVERAGE
|
||||
vl_fatal("",0,"","%Error: Called VerilatedCov::write when VM_COVERAGE disabled\n");
|
||||
#endif
|
||||
selftest();
|
||||
|
||||
ofstream os (filename);
|
||||
if (os.fail()) {
|
||||
string msg = (string)"%Error: Can't write '"+filename+"'";
|
||||
vl_fatal("",0,"",msg.c_str());
|
||||
return;
|
||||
}
|
||||
os << "# SystemC::Coverage-3\n";
|
||||
|
||||
// Build list of events; totalize if collapsing hierarchy
|
||||
typedef map<string,pair<string,vluint64_t> > EventMap;
|
||||
EventMap eventCounts;
|
||||
for (ItemList::iterator it=m_items.begin(); it!=m_items.end(); ++it) {
|
||||
VerilatedCovImpItem* itemp = *(it);
|
||||
string name;
|
||||
string hier;
|
||||
bool per_instance = false;
|
||||
|
||||
for (int i=0; i<MAX_KEYS; i++) {
|
||||
if (itemp->m_keys[i] != KEY_UNDEF) {
|
||||
string key = VerilatedCovKey::shortKey(m_indexValues[itemp->m_keys[i]]);
|
||||
string val = m_indexValues[itemp->m_vals[i]];
|
||||
if (key == VL_CIK_PER_INSTANCE) {
|
||||
if (val != "0") per_instance = true;
|
||||
}
|
||||
if (key == VL_CIK_HIER) {
|
||||
hier = val;
|
||||
} else {
|
||||
// Print it
|
||||
name += keyValueFormatter(key,val);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (per_instance) { // Not collapsing hierarchies
|
||||
name += keyValueFormatter(VL_CIK_HIER,hier);
|
||||
hier = "";
|
||||
}
|
||||
|
||||
// Group versus point labels don't matter here, downstream
|
||||
// deals with it. Seems bad for sizing though and doesn't
|
||||
// allow easy addition of new group codes (would be
|
||||
// inefficient)
|
||||
|
||||
// Find or insert the named event
|
||||
EventMap::iterator cit = eventCounts.find(name);
|
||||
if (cit != eventCounts.end()) {
|
||||
const string& oldhier = cit->second.first;
|
||||
cit->second.second += itemp->count();
|
||||
cit->second.first = combineHier(oldhier, hier);
|
||||
} else {
|
||||
eventCounts.insert(make_pair(name, make_pair(hier,itemp->count())));
|
||||
}
|
||||
}
|
||||
|
||||
// Output body
|
||||
for (EventMap::iterator it=eventCounts.begin(); it!=eventCounts.end(); ++it) {
|
||||
os<<"C '"<<dec;
|
||||
os<<it->first;
|
||||
if (it->second.first != "") os<<keyValueFormatter(VL_CIK_HIER,it->second.first);
|
||||
os<<"' "<<it->second.second;
|
||||
os<<endl;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedCov
|
||||
|
||||
void VerilatedCov::clear() {
|
||||
VerilatedCovImp::imp().clear();
|
||||
}
|
||||
void VerilatedCov::clearNonMatch (const char* matchp) {
|
||||
VerilatedCovImp::imp().clearNonMatch(matchp);
|
||||
}
|
||||
void VerilatedCov::zero() {
|
||||
VerilatedCovImp::imp().zero();
|
||||
}
|
||||
void VerilatedCov::write (const char* filenamep) {
|
||||
VerilatedCovImp::imp().write(filenamep);
|
||||
}
|
||||
void VerilatedCov::_inserti (vluint32_t* itemp) {
|
||||
VerilatedCovImp::imp().inserti(new VerilatedCoverItemSpec<vluint32_t>(itemp));
|
||||
}
|
||||
void VerilatedCov::_inserti (vluint64_t* itemp) {
|
||||
VerilatedCovImp::imp().inserti(new VerilatedCoverItemSpec<vluint64_t>(itemp));
|
||||
}
|
||||
void VerilatedCov::_insertf (const char* filename, int lineno) {
|
||||
VerilatedCovImp::imp().insertf(filename,lineno);
|
||||
}
|
||||
|
||||
#define K(n) const char* key ## n
|
||||
#define A(n) const char* key ## n, const char* val ## n // Argument list
|
||||
#define C(n) key ## n, val ## n // Calling argument list
|
||||
#define N(n) "","" // Null argument list
|
||||
void VerilatedCov::_insertp (A(0),A(1),A(2),A(3),A(4),A(5),A(6),A(7),A(8),A(9),
|
||||
A(10),A(11),A(12),A(13),A(14),A(15),A(16),A(17),A(18),A(19),
|
||||
A(20),A(21),A(22),A(23),A(24),A(25),A(26),A(27),A(28),A(29)) {
|
||||
const char* keyps[VerilatedCovImpBase::MAX_KEYS]
|
||||
= {NULL,NULL,NULL, // filename,lineno,page
|
||||
key0,key1,key2,key3,key4,key5,key6,key7,key8,key9,
|
||||
key10,key11,key12,key13,key14,key15,key16,key17,key18,key19,
|
||||
key20,key21,key22,key23,key24,key25,key26,key27,key28,key29};
|
||||
const char* valps[VerilatedCovImpBase::MAX_KEYS]
|
||||
= {NULL,NULL,NULL, // filename,lineno,page
|
||||
val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,
|
||||
val10,val11,val12,val13,val14,val15,val16,val17,val18,val19,
|
||||
val20,val21,val22,val23,val24,val25,val26,val27,val28,val29};
|
||||
VerilatedCovImp::imp().insertp(keyps, valps);
|
||||
}
|
||||
|
||||
// And versions with fewer arguments (oh for a language with named parameters!)
|
||||
void VerilatedCov::_insertp (A(0),A(1),A(2),A(3),A(4),A(5),A(6),A(7),A(8),A(9)) {
|
||||
_insertp(C(0),C(1),C(2),C(3),C(4),C(5),C(6),C(7),C(8),C(9),
|
||||
N(10),N(11),N(12),N(13),N(14),N(15),N(16),N(17),N(18),N(19),
|
||||
N(20),N(21),N(22),N(23),N(24),N(25),N(26),N(27),N(28),N(29));
|
||||
}
|
||||
void VerilatedCov::_insertp (A(0),A(1),A(2),A(3),A(4),A(5),A(6),A(7),A(8),A(9),
|
||||
A(10),A(11),A(12),A(13),A(14),A(15),A(16),A(17),A(18),A(19)) {
|
||||
_insertp(C(0),C(1),C(2),C(3),C(4),C(5),C(6),C(7),C(8),C(9),
|
||||
C(10),C(11),C(12),C(13),C(14),C(15),C(16),C(17),C(18),C(19),
|
||||
N(20),N(21),N(22),N(23),N(24),N(25),N(26),N(27),N(28),N(29));
|
||||
}
|
||||
// Backward compatibility for Verilator
|
||||
void VerilatedCov::_insertp (A(0), A(1), K(2),int val2, K(3),int val3,
|
||||
K(4),const string& val4, A(5),A(6)) {
|
||||
_insertp(C(0),C(1),
|
||||
key2,VlCovCvtToCStr(val2), key3,VlCovCvtToCStr(val3), key4, val4.c_str(),
|
||||
C(5),C(6),N(7),N(8),N(9),
|
||||
N(10),N(11),N(12),N(13),N(14),N(15),N(16),N(17),N(18),N(19),
|
||||
N(20),N(21),N(22),N(23),N(24),N(25),N(26),N(27),N(28),N(29));
|
||||
}
|
||||
#undef A
|
||||
#undef C
|
||||
#undef N
|
||||
#undef K
|
146
include/verilated_cov.h
Normal file
146
include/verilated_cov.h
Normal file
@ -0,0 +1,146 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//=============================================================================
|
||||
//
|
||||
// THIS MODULE IS PUBLICLY LICENSED
|
||||
//
|
||||
// Copyright 2001-2014 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.
|
||||
//
|
||||
// This is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
// for more details.
|
||||
//
|
||||
//=============================================================================
|
||||
///
|
||||
/// \file
|
||||
/// \brief Coverage analysis support
|
||||
///
|
||||
/// AUTHOR: Wilson Snyder
|
||||
///
|
||||
//=============================================================================
|
||||
|
||||
#ifndef _VERILATED_COV_H_
|
||||
#define _VERILATED_COV_H_ 1
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
using namespace std;
|
||||
|
||||
//=============================================================================
|
||||
/// Conditionally compile coverage code
|
||||
|
||||
#ifdef VM_COVERAGE
|
||||
# define VL_IF_COVER(stmts) do { stmts ; } while(0)
|
||||
#else
|
||||
# define VL_IF_COVER(stmts) do { if(0) { stmts ; } } while(0)
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
/// Insert a item for coverage analysis.
|
||||
/// The first argument is a pointer to the count to be dumped.
|
||||
/// The remaining arguments occur in pairs: A string key, and a value.
|
||||
/// The value may be a string, or another type which will be auto-converted to a string.
|
||||
///
|
||||
/// Some typical keys:
|
||||
/// filename File the recording occurs in. Defaults to __FILE__
|
||||
/// lineno Line number the recording occurs in. Defaults to __LINE__
|
||||
/// column Column number (or occurrence# for dup file/lines). Defaults to undef.
|
||||
/// hier Hierarchical name. Defaults to name()
|
||||
/// type Type of coverage. Defaults to "user"
|
||||
/// Other types are 'block', 'fsm', 'toggle'.
|
||||
/// comment Description of the coverage event. Should be set by the user.
|
||||
/// Comments for type==block: 'if', 'else', 'elsif', 'case'
|
||||
/// thresh Threshold to consider fully covered.
|
||||
/// If unspecified, downstream tools will determine it.
|
||||
///
|
||||
/// Examples:
|
||||
///
|
||||
/// vluint32_t m_cases[10];
|
||||
/// constructor {
|
||||
/// for (int i=0; i<10; i++) { m_cases[i]=0; }
|
||||
/// }
|
||||
/// for (int i=0; i<10; i++) {
|
||||
/// VL_COVER_INSERT(&m_cases[i], "comment", "Coverage Case", "i", cvtToNumStr(i));
|
||||
/// }
|
||||
|
||||
#define VL_COVER_INSERT(countp,args...) \
|
||||
VL_IF_COVER(VerilatedCov::_inserti(countp); \
|
||||
VerilatedCov::_insertf(__FILE__,__LINE__); \
|
||||
VerilatedCov::_insertp("hier", name(), args))
|
||||
|
||||
//=============================================================================
|
||||
/// Convert VL_COVER_INSERT value arguments to strings
|
||||
|
||||
template< class T> std::string vlCovCvtToStr (const T& t) {
|
||||
ostringstream os; os<<t; return os.str();
|
||||
}
|
||||
|
||||
/// Usage: something(VlCovCvtToCStr(i))
|
||||
/// Note the pointer will only be valid for as long as the object remains
|
||||
/// in scope!
|
||||
struct VlCovCvtToCStr {
|
||||
string m_str;
|
||||
// Casters
|
||||
template< class T> VlCovCvtToCStr (const T& t) {
|
||||
ostringstream os; os<<t; m_str=os.str();
|
||||
}
|
||||
~VlCovCvtToCStr() {}
|
||||
operator const char* () const { return m_str.c_str(); };
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedCov
|
||||
/// SystemPerl coverage global class
|
||||
////
|
||||
/// Global class with methods affecting all coverage data.
|
||||
|
||||
class VerilatedCov {
|
||||
public:
|
||||
// GLOBAL METHODS
|
||||
/// Return default filename
|
||||
static const char* defaultFilename() { return "coverage.dat"; }
|
||||
/// Write all coverage data to a file
|
||||
static void write (const char* filenamep = defaultFilename());
|
||||
/// Insert a coverage item
|
||||
/// We accept from 1-30 key/value pairs, all as strings.
|
||||
/// Call _insert1, followed by _insert2 and _insert3
|
||||
/// Do not call directly; use VL_COVER_INSERT or higher level macros instead
|
||||
// _insert1: Remember item pointer with count. (Not const, as may add zeroing function)
|
||||
static void _inserti (vluint32_t* itemp);
|
||||
static void _inserti (vluint64_t* itemp);
|
||||
// _insert2: Set default filename and line number
|
||||
static void _insertf (const char* filename, int lineno);
|
||||
// _insert3: Set parameters
|
||||
// We could have just the maximum argument version, but this compiles
|
||||
// much slower (nearly 2x) than having smaller versions also. However
|
||||
// there's not much more gain in having a version for each number of args.
|
||||
#define K(n) const char* key ## n
|
||||
#define A(n) const char* key ## n, const char* valp ## n // Argument list
|
||||
#define D(n) const char* key ## n = NULL, const char* valp ## n = NULL // Argument list
|
||||
static void _insertp (D(0),D(1),D(2),D(3),D(4),D(5),D(6),D(7),D(8),D(9));
|
||||
static void _insertp (A(0),A(1),A(2),A(3),A(4),A(5),A(6),A(7),A(8),A(9)
|
||||
,A(10),D(11),D(12),D(13),D(14),D(15),D(16),D(17),D(18),D(19));
|
||||
static void _insertp (A(0),A(1),A(2),A(3),A(4),A(5),A(6),A(7),A(8),A(9)
|
||||
,A(10),A(11),A(12),A(13),A(14),A(15),A(16),A(17),A(18),A(19)
|
||||
,A(20),D(21),D(22),D(23),D(24),D(25),D(26),D(27),D(28),D(29));
|
||||
// Backward compatibility for Verilator
|
||||
static void _insertp (A(0), A(1), K(2),int val2, K(3),int val3,
|
||||
K(4),const string& val4, A(5),A(6));
|
||||
|
||||
#undef K
|
||||
#undef A
|
||||
#undef D
|
||||
/// Clear coverage points (and call delete on all items)
|
||||
static void clear();
|
||||
/// Clear items not matching the provided string
|
||||
static void clearNonMatch (const char* matchp);
|
||||
/// Zero coverage points
|
||||
static void zero();
|
||||
};
|
||||
|
||||
#endif // guard
|
147
include/verilated_cov_key.h
Normal file
147
include/verilated_cov_key.h
Normal file
@ -0,0 +1,147 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//=============================================================================
|
||||
//
|
||||
// THIS MODULE IS PUBLICLY LICENSED
|
||||
//
|
||||
// Copyright 2001-2014 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.
|
||||
//
|
||||
// This is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
// for more details.
|
||||
//
|
||||
//=============================================================================
|
||||
///
|
||||
/// \file
|
||||
/// \brief Coverage item keys
|
||||
///
|
||||
/// AUTHOR: Wilson Snyder
|
||||
///
|
||||
//=============================================================================
|
||||
|
||||
#ifndef _VERILATED_COV_KEY_H_
|
||||
#define _VERILATED_COV_KEY_H_ 1
|
||||
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include <string>
|
||||
using namespace std;
|
||||
|
||||
//=============================================================================
|
||||
// Data used to edit below file, using vlcovgen
|
||||
|
||||
#define VLCOVGEN_ITEM(string_parsed_by_vlcovgen)
|
||||
|
||||
VLCOVGEN_ITEM("name=>'col0_name', short=>'C0', group=>1, default=>undef, descr=>'The column title for the header line of this column'")
|
||||
VLCOVGEN_ITEM("name=>'col1_name', short=>'C1', group=>1, default=>undef, ")
|
||||
VLCOVGEN_ITEM("name=>'col2_name', short=>'C2', group=>1, default=>undef, ")
|
||||
VLCOVGEN_ITEM("name=>'col3_name', short=>'C3', group=>1, default=>undef, ")
|
||||
VLCOVGEN_ITEM("name=>'column', short=>'n', group=>1, default=>0, descr=>'Column number for the item. Used to disambiguate multiple coverage points on the same line number'")
|
||||
VLCOVGEN_ITEM("name=>'filename', short=>'f', group=>1, default=>undef, descr=>'Filename of the item'")
|
||||
VLCOVGEN_ITEM("name=>'groupdesc', short=>'d', group=>1, default=>'', descr=>'Description of the covergroup this item belongs to'")
|
||||
VLCOVGEN_ITEM("name=>'groupname', short=>'g', group=>1, default=>'', descr=>'Group name of the covergroup this item belongs to'")
|
||||
VLCOVGEN_ITEM("name=>'groupcmt', short=>'O', group=>1, default=>'', ")
|
||||
VLCOVGEN_ITEM("name=>'per_instance',short=>'P', group=>1, default=>0, descr=>'True if every hierarchy is independently counted; otherwise all hierarchies will be combined into a single count'")
|
||||
VLCOVGEN_ITEM("name=>'row0_name', short=>'R0', group=>1, default=>undef, descr=>'The row title for the header line of this row'")
|
||||
VLCOVGEN_ITEM("name=>'row1_name', short=>'R1', group=>1, default=>undef, ")
|
||||
VLCOVGEN_ITEM("name=>'row2_name', short=>'R2', group=>1, default=>undef, ")
|
||||
VLCOVGEN_ITEM("name=>'row3_name', short=>'R3', group=>1, default=>undef, ")
|
||||
VLCOVGEN_ITEM("name=>'table', short=>'T', group=>1, default=>undef, descr=>'The name of the table for automatically generated tables'")
|
||||
VLCOVGEN_ITEM("name=>'thresh', short=>'s', group=>1, default=>undef, ")
|
||||
VLCOVGEN_ITEM("name=>'type', short=>'t', group=>1, default=>'', descr=>'Type of coverage (block, line, fsm, etc)'")
|
||||
// Bin attributes
|
||||
VLCOVGEN_ITEM("name=>'col0', short=>'c0', group=>0, default=>undef, descr=>'The (enumeration) value name for this column in a table cross' ")
|
||||
VLCOVGEN_ITEM("name=>'col1', short=>'c1', group=>0, default=>undef, ")
|
||||
VLCOVGEN_ITEM("name=>'col2', short=>'c2', group=>0, default=>undef, ")
|
||||
VLCOVGEN_ITEM("name=>'col3', short=>'c3', group=>0, default=>undef, ")
|
||||
VLCOVGEN_ITEM("name=>'comment', short=>'o', group=>0, default=>'', descr=>'Textual description for the item'")
|
||||
VLCOVGEN_ITEM("name=>'hier', short=>'h', group=>0, default=>'', descr=>'Hierarchy path name for the item'")
|
||||
VLCOVGEN_ITEM("name=>'limit', short=>'L', group=>0, default=>undef, ")
|
||||
VLCOVGEN_ITEM("name=>'lineno', short=>'l', group=>0, default=>0, descr=>'Line number for the item'")
|
||||
VLCOVGEN_ITEM("name=>'row0', short=>'r0', group=>0, default=>undef, descr=>'The (enumeration) value name for this row in a table cross'")
|
||||
VLCOVGEN_ITEM("name=>'row1', short=>'r1', group=>0, default=>undef, ")
|
||||
VLCOVGEN_ITEM("name=>'row2', short=>'r2', group=>0, default=>undef, ")
|
||||
VLCOVGEN_ITEM("name=>'row3', short=>'r3', group=>0, default=>undef, ")
|
||||
VLCOVGEN_ITEM("name=>'weight', short=>'w', group=>0, default=>undef, descr=>'For totaling items, weight of this item'")
|
||||
|
||||
//=============================================================================
|
||||
// VerilatedCovKey
|
||||
/// SystemPerl coverage global class
|
||||
////
|
||||
/// Global class with methods affecting all coverage data.
|
||||
|
||||
// VLCOVGEN_CIK_AUTO_EDIT_BEGIN
|
||||
#define VL_CIK_COL0 "c0"
|
||||
#define VL_CIK_COL0_NAME "C0"
|
||||
#define VL_CIK_COL1 "c1"
|
||||
#define VL_CIK_COL1_NAME "C1"
|
||||
#define VL_CIK_COL2 "c2"
|
||||
#define VL_CIK_COL2_NAME "C2"
|
||||
#define VL_CIK_COL3 "c3"
|
||||
#define VL_CIK_COL3_NAME "C3"
|
||||
#define VL_CIK_COLUMN "n"
|
||||
#define VL_CIK_COMMENT "o"
|
||||
#define VL_CIK_FILENAME "f"
|
||||
#define VL_CIK_GROUPCMT "O"
|
||||
#define VL_CIK_GROUPDESC "d"
|
||||
#define VL_CIK_GROUPNAME "g"
|
||||
#define VL_CIK_HIER "h"
|
||||
#define VL_CIK_LIMIT "L"
|
||||
#define VL_CIK_LINENO "l"
|
||||
#define VL_CIK_PER_INSTANCE "P"
|
||||
#define VL_CIK_ROW0 "r0"
|
||||
#define VL_CIK_ROW0_NAME "R0"
|
||||
#define VL_CIK_ROW1 "r1"
|
||||
#define VL_CIK_ROW1_NAME "R1"
|
||||
#define VL_CIK_ROW2 "r2"
|
||||
#define VL_CIK_ROW2_NAME "R2"
|
||||
#define VL_CIK_ROW3 "r3"
|
||||
#define VL_CIK_ROW3_NAME "R3"
|
||||
#define VL_CIK_TABLE "T"
|
||||
#define VL_CIK_THRESH "s"
|
||||
#define VL_CIK_TYPE "t"
|
||||
#define VL_CIK_WEIGHT "w"
|
||||
// VLCOVGEN_CIK_AUTO_EDIT_END
|
||||
|
||||
class VerilatedCovKey {
|
||||
public:
|
||||
static string shortKey(const string& key) {
|
||||
// VLCOVGEN_SHORT_AUTO_EDIT_BEGIN
|
||||
if (key == "col0") return VL_CIK_COL0;
|
||||
if (key == "col0_name") return VL_CIK_COL0_NAME;
|
||||
if (key == "col1") return VL_CIK_COL1;
|
||||
if (key == "col1_name") return VL_CIK_COL1_NAME;
|
||||
if (key == "col2") return VL_CIK_COL2;
|
||||
if (key == "col2_name") return VL_CIK_COL2_NAME;
|
||||
if (key == "col3") return VL_CIK_COL3;
|
||||
if (key == "col3_name") return VL_CIK_COL3_NAME;
|
||||
if (key == "column") return VL_CIK_COLUMN;
|
||||
if (key == "comment") return VL_CIK_COMMENT;
|
||||
if (key == "filename") return VL_CIK_FILENAME;
|
||||
if (key == "groupcmt") return VL_CIK_GROUPCMT;
|
||||
if (key == "groupdesc") return VL_CIK_GROUPDESC;
|
||||
if (key == "groupname") return VL_CIK_GROUPNAME;
|
||||
if (key == "hier") return VL_CIK_HIER;
|
||||
if (key == "limit") return VL_CIK_LIMIT;
|
||||
if (key == "lineno") return VL_CIK_LINENO;
|
||||
if (key == "per_instance") return VL_CIK_PER_INSTANCE;
|
||||
if (key == "row0") return VL_CIK_ROW0;
|
||||
if (key == "row0_name") return VL_CIK_ROW0_NAME;
|
||||
if (key == "row1") return VL_CIK_ROW1;
|
||||
if (key == "row1_name") return VL_CIK_ROW1_NAME;
|
||||
if (key == "row2") return VL_CIK_ROW2;
|
||||
if (key == "row2_name") return VL_CIK_ROW2_NAME;
|
||||
if (key == "row3") return VL_CIK_ROW3;
|
||||
if (key == "row3_name") return VL_CIK_ROW3_NAME;
|
||||
if (key == "table") return VL_CIK_TABLE;
|
||||
if (key == "thresh") return VL_CIK_THRESH;
|
||||
if (key == "type") return VL_CIK_TYPE;
|
||||
if (key == "weight") return VL_CIK_WEIGHT;
|
||||
// VLCOVGEN_SHORT_AUTO_EDIT_END
|
||||
return key;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // guard
|
@ -46,7 +46,7 @@ obj_opt:
|
||||
obj_dbg:
|
||||
mkdir $@
|
||||
|
||||
.PHONY: ../verilator_bin ../verilator_bin_dbg
|
||||
.PHONY: ../verilator_bin ../verilator_bin_dbg ../verilator_coverage_bin_dbg
|
||||
|
||||
opt: ../verilator_bin
|
||||
ifeq ($(VERILATOR_NO_OPT_BUILD),1) # Faster laptop development... One build
|
||||
@ -59,11 +59,15 @@ else
|
||||
cd obj_opt && $(MAKE) TGT=../$@ -f ../Makefile_obj
|
||||
endif
|
||||
|
||||
dbg: ../verilator_bin_dbg
|
||||
dbg: ../verilator_bin_dbg ../verilator_coverage_bin_dbg
|
||||
../verilator_bin_dbg: obj_dbg prefiles
|
||||
cd obj_dbg && $(MAKE) -j 1 TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj serial
|
||||
cd obj_dbg && $(MAKE) TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj
|
||||
|
||||
../verilator_coverage_bin_dbg: obj_dbg prefiles
|
||||
cd obj_dbg && $(MAKE) TGT=../$@ VL_DEBUG=1 VL_VLCOV=1 -f ../Makefile_obj serial_vlcov
|
||||
cd obj_dbg && $(MAKE) TGT=../$@ VL_DEBUG=1 VL_VLCOV=1 -f ../Makefile_obj
|
||||
|
||||
prefiles::
|
||||
prefiles:: config_rev.h
|
||||
ifneq ($(UNDER_GIT),) # If local git tree... Else don't burden users
|
||||
|
@ -127,6 +127,7 @@ HEADERS = $(wildcard V*.h v*.h)
|
||||
ASTGEN = $(srcdir)/astgen
|
||||
BISONPRE = $(srcdir)/bisonpre
|
||||
FLEXFIX = $(srcdir)/flexfix
|
||||
VLCOVGEN = $(srcdir)/vlcovgen
|
||||
|
||||
######################################################################
|
||||
#### Top level
|
||||
@ -234,26 +235,30 @@ RAW_OBJS = \
|
||||
V3WidthSel.o \
|
||||
|
||||
# Non-concatable
|
||||
OBJS += \
|
||||
NC_OBJS += \
|
||||
V3ParseImp.o \
|
||||
V3ParseGrammar.o \
|
||||
V3ParseLex.o \
|
||||
V3PreProc.o \
|
||||
|
||||
# verilator_coverage
|
||||
VLCOV_OBJS = \
|
||||
VlcMain.o \
|
||||
|
||||
#### Linking
|
||||
|
||||
ifeq ($(VL_DEBUG),)
|
||||
# Building with fewer objects to better optimize
|
||||
#OBJS += V3__CONCAT.o
|
||||
OBJS += $(RAW_OBJS)
|
||||
ifeq ($(VL_VLCOV),)
|
||||
PREDEP_H = V3Ast__gen_classes.h
|
||||
OBJS += $(RAW_OBJS) $(NC_OBJS)
|
||||
else
|
||||
OBJS += $(RAW_OBJS)
|
||||
PREDEP_H =
|
||||
OBJS += $(VLCOV_OBJS)
|
||||
endif
|
||||
|
||||
V3__CONCAT.cpp: $(addsuffix .cpp, $(basename $(RAW_OBJS)))
|
||||
$(PERL) $(srcdir)/../bin/verilator_includer $^ > $@
|
||||
|
||||
$(TGT): V3Ast__gen_classes.h $(OBJS)
|
||||
$(TGT): $(PREDEP_H) $(OBJS)
|
||||
@echo " Linking $@..."
|
||||
-rm -rf $@ $@.exe
|
||||
${LINK} ${LDFLAGS} -o $@ $(OBJS) $(CCMALLOC) ${LIBS}
|
||||
@ -288,6 +293,12 @@ V3PreProc.o: V3PreProc.cpp V3PreLex.yy.cpp
|
||||
# Target rule called before parallel build to make generated files
|
||||
serial:: V3Ast__gen_classes.h V3ParseBison.c
|
||||
|
||||
serial_vlcov:: vlcovgen.d
|
||||
|
||||
vlcovgen.d: $(VLCOVGEN) $(srcdir)/include/verilated_cov_key.h
|
||||
$(PERL) $(VLCOVGEN) --srcdir $(srcdir)
|
||||
touch $@
|
||||
|
||||
V3Ast__gen_classes.h : $(ASTGEN) V3Ast.h V3AstNodes.h
|
||||
$(PERL) $(ASTGEN) -I$(srcdir) --classes
|
||||
|
||||
|
@ -1508,7 +1508,7 @@ void EmitCImp::emitConfigureImp(AstNodeModule* modp) {
|
||||
void EmitCImp::emitCoverageImp(AstNodeModule* modp) {
|
||||
if (v3Global.opt.coverage() ) {
|
||||
puts("\n// Coverage\n");
|
||||
// Rather than putting out SP_COVER_INSERT calls directly, we do it via this function
|
||||
// Rather than putting out VL_COVER_INSERT calls directly, we do it via this function
|
||||
// This gets around gcc slowness constructing all of the template arguments
|
||||
// SystemPerl 1.301 is much faster, but it's nice to remain back
|
||||
// compatible, and have a common wrapper.
|
||||
@ -1517,7 +1517,7 @@ void EmitCImp::emitCoverageImp(AstNodeModule* modp) {
|
||||
puts( "static uint32_t fake_zero_count = 0;\n"); // static doesn't need save-restore as constant
|
||||
puts( "if (!enable) countp = &fake_zero_count;\n"); // Used for second++ instantiation of identical bin
|
||||
puts( "*countp = 0;\n");
|
||||
puts( "SP_COVER_INSERT(countp,");
|
||||
puts( "VL_COVER_INSERT(countp,");
|
||||
puts( " \"filename\",filenamep,");
|
||||
puts( " \"lineno\",lineno,");
|
||||
puts( " \"column\",column,\n");
|
||||
@ -1827,7 +1827,7 @@ void EmitCImp::emitInt(AstNodeModule* modp) {
|
||||
puts("#include \"verilated_save.h\"\n");
|
||||
}
|
||||
if (v3Global.opt.coverage()) {
|
||||
puts("#include \"SpCoverage.h\"\n");
|
||||
puts("#include \"verilated_cov.h\"\n");
|
||||
if (v3Global.opt.savable()) v3error("--coverage and --savable not supported together");
|
||||
}
|
||||
if (v3Global.needHInlines()) { // Set by V3EmitCInlines; should have been called before us
|
||||
|
@ -83,13 +83,13 @@ public:
|
||||
if (v3Global.opt.savable()) {
|
||||
putMakeClassEntry(of, "verilated_save.cpp");
|
||||
}
|
||||
if (v3Global.opt.coverage()) {
|
||||
putMakeClassEntry(of, "verilated_cov.cpp");
|
||||
}
|
||||
if (v3Global.opt.systemPerl()) {
|
||||
putMakeClassEntry(of, "Sp.cpp"); // Note Sp.cpp includes SpTraceVcdC
|
||||
}
|
||||
else {
|
||||
if (v3Global.opt.coverage()) {
|
||||
putMakeClassEntry(of, "SpCoverage.cpp");
|
||||
}
|
||||
if (v3Global.opt.trace()) {
|
||||
putMakeClassEntry(of, "verilated_vcd_c.cpp");
|
||||
if (v3Global.opt.systemC()) {
|
||||
|
@ -23,7 +23,6 @@
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include "V3Error.h"
|
||||
#include "V3FileLine.h"
|
||||
#include <cstdio>
|
||||
#include <stack>
|
||||
#include <set>
|
||||
|
@ -275,18 +275,18 @@ bool V3Options::filenameIsRel(const string& filename) {
|
||||
}
|
||||
|
||||
bool V3Options::fileStatDir(const string& filename) {
|
||||
struct stat m_stat; // Stat information
|
||||
int err = stat(filename.c_str(), &m_stat);
|
||||
struct stat sstat; // Stat information
|
||||
int err = stat(filename.c_str(), &sstat);
|
||||
if (err!=0) return false;
|
||||
if (!S_ISDIR(m_stat.st_mode)) return false;
|
||||
if (!S_ISDIR(sstat.st_mode)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool V3Options::fileStatNormal(const string& filename) {
|
||||
struct stat m_stat; // Stat information
|
||||
int err = stat(filename.c_str(), &m_stat);
|
||||
struct stat sstat; // Stat information
|
||||
int err = stat(filename.c_str(), &sstat);
|
||||
if (err!=0) return false;
|
||||
if (S_ISDIR(m_stat.st_mode)) return false;
|
||||
if (S_ISDIR(sstat.st_mode)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
133
src/VlcBucket.h
Normal file
133
src/VlcBucket.h
Normal file
@ -0,0 +1,133 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: verilator_coverage: Bucket container
|
||||
//
|
||||
// Code available from: http://www.veripool.org/verilator
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2014 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.
|
||||
//
|
||||
// Verilator is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef _VLCBUCKET_H_
|
||||
#define _VLCBUCKET_H_ 1
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
//********************************************************************
|
||||
// VlcBuckets - Container of all coverage point hits for a given test
|
||||
// This is a bitmap array - we store a single bit to indicate a test
|
||||
// has hit that point with sufficient coverage.
|
||||
|
||||
class VlcBuckets {
|
||||
private:
|
||||
// MEMBERS
|
||||
vluint64_t* m_datap; ///< Pointer to first bucket (dynamically allocated)
|
||||
vluint64_t m_dataSize; ///< Current entries in m_datap
|
||||
vluint64_t m_bucketsCovered; ///< Num buckets with sufficient coverage
|
||||
|
||||
private:
|
||||
static inline vluint64_t covBit(vluint64_t point) { return 1ULL<<(point & 63); }
|
||||
inline vluint64_t allocSize() const { return sizeof(vluint64_t) * m_dataSize / 64; }
|
||||
void allocate(vluint64_t point) {
|
||||
vluint64_t oldsize = m_dataSize;
|
||||
if (m_dataSize<point) m_dataSize=(point+64) & ~63ULL; // Keep power of two
|
||||
m_dataSize *= 2;
|
||||
//UINFO(9, "Realloc "<<allocSize()<<" for "<<point<<" "<<(void*)(m_datap)<<endl);
|
||||
m_datap = (vluint64_t*)realloc(m_datap, allocSize());
|
||||
if (!m_datap) {v3fatal("Out of memory increasing buckets"); }
|
||||
for (vluint64_t i=oldsize; i<m_dataSize; i+=64) m_datap[i/64] = 0;
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlcBuckets() {
|
||||
m_dataSize = 0;
|
||||
m_datap = NULL;
|
||||
m_bucketsCovered = 0;
|
||||
allocate(1024);
|
||||
}
|
||||
~VlcBuckets() {
|
||||
m_dataSize = 0;
|
||||
free(m_datap); m_datap=NULL;
|
||||
}
|
||||
|
||||
// ACCESSORS
|
||||
static vluint64_t sufficient() { return 1; }
|
||||
vluint64_t bucketsCovered() const { return m_bucketsCovered; }
|
||||
|
||||
// METHODS
|
||||
void addData(vluint64_t point, vluint64_t hits) {
|
||||
if (hits >= sufficient()) {
|
||||
//UINFO(9," addData "<<point<<" "<<hits<<" size="<<m_dataSize<<endl);
|
||||
if (point >= m_dataSize) allocate(point);
|
||||
m_datap[point/64] |= covBit(point);
|
||||
m_bucketsCovered++;
|
||||
}
|
||||
}
|
||||
void clearHits(vluint64_t point) const {
|
||||
if (point >= m_dataSize) {
|
||||
return;
|
||||
} else {
|
||||
m_datap[point/64] &= ~covBit(point);
|
||||
}
|
||||
}
|
||||
bool exists(vluint64_t point) const {
|
||||
if (point >= m_dataSize) {
|
||||
return false;
|
||||
} else {
|
||||
return (m_datap[point/64] & covBit(point)) ? 1:0;
|
||||
}
|
||||
}
|
||||
vluint64_t hits(vluint64_t point) const {
|
||||
if (point >= m_dataSize) {
|
||||
return 0;
|
||||
} else {
|
||||
return (m_datap[point/64] & covBit(point)) ? 1:0;
|
||||
}
|
||||
}
|
||||
vluint64_t popCount() const {
|
||||
vluint64_t pop = 0;
|
||||
for (vluint64_t i=0; i<m_dataSize; i++) {
|
||||
if (hits(i)) pop++;
|
||||
}
|
||||
return pop;
|
||||
}
|
||||
vluint64_t dataPopCount(const VlcBuckets& remaining) {
|
||||
vluint64_t pop = 0;
|
||||
for (vluint64_t i=0; i<m_dataSize; i++) {
|
||||
if (hits(i) && remaining.hits(i)) pop++;
|
||||
}
|
||||
return pop;
|
||||
}
|
||||
void orData(const VlcBuckets& ordata) {
|
||||
for (vluint64_t i=0; i<m_dataSize; i++) {
|
||||
if (hits(i) && ordata.hits(i)) {
|
||||
clearHits(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dump() const {
|
||||
cout<<"# ";
|
||||
for (vluint64_t i=0; i<m_dataSize; i++) {
|
||||
if (hits(i)) cout<<","<<i;
|
||||
}
|
||||
cout<<endl;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//######################################################################
|
||||
|
||||
#endif // guard
|
202
src/VlcMain.cpp
Normal file
202
src/VlcMain.cpp
Normal file
@ -0,0 +1,202 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: verilator_coverage: main()
|
||||
//
|
||||
// Code available from: http://www.veripool.org/verilator
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2014 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.
|
||||
//
|
||||
// Verilator is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
// Cheat for speed and compile .cpp files into one object
|
||||
#define _V3ERROR_NO_GLOBAL_ 1
|
||||
#include "V3Error.cpp"
|
||||
#include "VlcTop.cpp"
|
||||
|
||||
#include "VlcOptions.h"
|
||||
#include "VlcTop.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
|
||||
//######################################################################
|
||||
// VlcOptions
|
||||
|
||||
void VlcOptions::addReadFile(const string& filename) {
|
||||
if (m_readFiles.find(filename) == m_readFiles.end()) {
|
||||
m_readFiles.insert(filename);
|
||||
}
|
||||
}
|
||||
|
||||
string VlcOptions::version() {
|
||||
string ver = DTVERSION;
|
||||
ver += " rev "+cvtToStr(DTVERSION_rev);
|
||||
return ver;
|
||||
}
|
||||
|
||||
bool VlcOptions::onoff(const char* sw, const char* arg, bool& flag) {
|
||||
// if sw==arg, then return true (found it), and flag=true
|
||||
// if sw=="-no-arg", then return true (found it), and flag=false
|
||||
// if sw=="-noarg", then return true (found it), and flag=false
|
||||
// else return false
|
||||
if (arg[0]!='-') v3fatalSrc("OnOff switches must have leading dash.\n");
|
||||
if (0==strcmp(sw,arg)) { flag=true; return true; }
|
||||
else if (0==strncmp(sw,"-no",3) && (0==strcmp(sw+3,arg+1))) { flag=false; return true; }
|
||||
else if (0==strncmp(sw,"-no-",4) && (0==strcmp(sw+4,arg+1))) { flag=false; return true; }
|
||||
return false;
|
||||
}
|
||||
|
||||
void VlcOptions::parseOptsList(int argc, char** argv) {
|
||||
// Parse parameters
|
||||
// Note argc and argv DO NOT INCLUDE the filename in [0]!!!
|
||||
// May be called recursively when there are -f files.
|
||||
#define shift { ++i; }
|
||||
for (int i=0; i<argc; ) {
|
||||
UINFO(9, " Option: "<<argv[i]<<endl);
|
||||
if (argv[i][0]=='-') {
|
||||
const char *sw = argv[i];
|
||||
bool flag = true;
|
||||
// Allow gnu -- switches
|
||||
if (sw[0]=='-' && sw[1]=='-') ++sw;
|
||||
if (0) {}
|
||||
// Single switches
|
||||
else if ( onoff (sw, "-annotate-all", flag/*ref*/) ) { m_annotateAll = flag; }
|
||||
else if ( onoff (sw, "-rank", flag/*ref*/) ) { m_rank = flag; }
|
||||
else if ( onoff (sw, "-unlink", flag/*ref*/) ) { m_unlink = flag; }
|
||||
// Parameterized switches
|
||||
else if ( !strcmp (sw, "-annotate") && (i+1)<argc ) {
|
||||
shift;
|
||||
m_annotateOut = argv[i];
|
||||
}
|
||||
else if ( !strcmp (sw, "-debug") ) {
|
||||
V3Error::debugDefault(3);
|
||||
}
|
||||
else if ( !strcmp (sw, "-debugi") && (i+1)<argc ) {
|
||||
shift;
|
||||
V3Error::debugDefault(atoi(argv[i]));
|
||||
}
|
||||
else if ( !strcmp (sw, "-V") ) {
|
||||
showVersion(true);
|
||||
exit(0);
|
||||
}
|
||||
else if ( !strcmp (sw, "-version") ) {
|
||||
showVersion(false);
|
||||
exit(0);
|
||||
}
|
||||
else if ( !strcmp (sw, "-write") && (i+1)<argc ) {
|
||||
shift;
|
||||
m_writeFile = argv[i];
|
||||
}
|
||||
else {
|
||||
v3fatal ("Invalid option: "<<argv[i]);
|
||||
}
|
||||
shift;
|
||||
} // - options
|
||||
else if (1) {
|
||||
addReadFile(argv[i]);
|
||||
shift;
|
||||
}
|
||||
else {
|
||||
v3fatal ("Invalid argument: "<<argv[i]);
|
||||
shift;
|
||||
}
|
||||
}
|
||||
#undef shift
|
||||
}
|
||||
|
||||
void VlcOptions::showVersion(bool verbose) {
|
||||
cout <<version();
|
||||
cout <<endl;
|
||||
if (!verbose) return;
|
||||
|
||||
cout <<endl;
|
||||
cout << "Copyright 2003-2014 by Wilson Snyder. Verilator is free software; you can\n";
|
||||
cout << "redistribute it and/or modify the Verilator internals under the terms of\n";
|
||||
cout << "either the GNU Lesser General Public License Version 3 or the Perl Artistic\n";
|
||||
cout << "License Version 2.0.\n";
|
||||
|
||||
cout <<endl;
|
||||
cout << "See http://www.veripool.org/verilator for documentation\n";
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// File searching
|
||||
// (TODO: Make a V3Os with these functions and share with Verilator)
|
||||
|
||||
string VlcOptions::filenameNonDir (const string& filename) {
|
||||
string::size_type pos;
|
||||
if ((pos = filename.rfind("/")) != string::npos) {
|
||||
return filename.substr(pos+1);
|
||||
} else {
|
||||
return filename;
|
||||
}
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
|
||||
int main(int argc, char** argv, char** env) {
|
||||
// General initialization
|
||||
ios::sync_with_stdio();
|
||||
|
||||
VlcTop top;
|
||||
|
||||
// Command option parsing
|
||||
top.opt.parseOptsList(argc-1, argv+1);
|
||||
|
||||
if (top.opt.readFiles().empty()) {
|
||||
top.opt.addReadFile("vlt_coverage.pl");
|
||||
}
|
||||
|
||||
const VlStringSet& readFiles = top.opt.readFiles();
|
||||
for (VlStringSet::iterator it = readFiles.begin(); it != readFiles.end(); ++it) {
|
||||
string filename = *it;
|
||||
top.readCoverage(filename);
|
||||
}
|
||||
|
||||
if (debug() >= 9) {
|
||||
top.tests().dump(true);
|
||||
top.points().dump();
|
||||
}
|
||||
|
||||
V3Error::abortIfWarnings();
|
||||
if (top.opt.annotateOut() != "") {
|
||||
top.annotate(top.opt.annotateOut());
|
||||
}
|
||||
|
||||
if (top.opt.rank()) {
|
||||
top.rank();
|
||||
top.tests().dump(false);
|
||||
}
|
||||
|
||||
if (top.opt.writeFile() != "") {
|
||||
top.writeCoverage(top.opt.writeFile());
|
||||
V3Error::abortIfWarnings();
|
||||
if (top.opt.unlink()) {
|
||||
const VlStringSet& readFiles = top.opt.readFiles();
|
||||
for (VlStringSet::iterator it = readFiles.begin(); it != readFiles.end(); ++it) {
|
||||
string filename = *it;
|
||||
unlink(filename.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Final writing shouldn't throw warnings, but...
|
||||
V3Error::abortIfWarnings();
|
||||
|
||||
UINFO(1,"Done, Exiting...\n");
|
||||
}
|
||||
|
||||
// Local Variables:
|
||||
// compile-command: "v4make bin/verilator_coverage --debugi 9 test_regress/t/t_vlcov_data_*.dat"
|
||||
// End:
|
||||
|
87
src/VlcOptions.h
Normal file
87
src/VlcOptions.h
Normal file
@ -0,0 +1,87 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: verilator_coverage: Command line options
|
||||
//
|
||||
// Code available from: http://www.veripool.org/verilator
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2014 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.
|
||||
//
|
||||
// Verilator is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef _VLCOPTIONS_H_
|
||||
#define _VLCOPTIONS_H_ 1
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include "config_rev.h"
|
||||
|
||||
//######################################################################
|
||||
// V3Options - Command line options
|
||||
|
||||
typedef vector<string> VlStringList;
|
||||
typedef set<string> VlStringSet;
|
||||
|
||||
class VlcOptions {
|
||||
// MEMBERS (general options)
|
||||
string m_annotateOut; // main switch: --annotate I<output_directory>
|
||||
bool m_annotateAll; // main switch: --annotate-all
|
||||
int m_annotateMin; // main switch: --annotate-min I<count>
|
||||
VlStringSet m_readFiles; // main switch: --read
|
||||
bool m_rank; // main switch: --rank
|
||||
bool m_unlink; // main switch: --unlink
|
||||
string m_writeFile; // main switch: --write
|
||||
|
||||
private:
|
||||
// METHODS
|
||||
void showVersion(bool verbose);
|
||||
bool onoff(const char* sw, const char* arg, bool& flag);
|
||||
|
||||
public:
|
||||
// CREATORS
|
||||
VlcOptions() {
|
||||
m_annotateAll = false;
|
||||
m_annotateMin = 10;
|
||||
m_rank = false;
|
||||
m_unlink = false;
|
||||
}
|
||||
~VlcOptions() {}
|
||||
void setDebugMode(int level);
|
||||
|
||||
// METHODS
|
||||
void parseOptsList(int argc, char** argv);
|
||||
void addReadFile(const string& filename);
|
||||
|
||||
// ACCESSORS (options)
|
||||
const VlStringSet& readFiles() const { return m_readFiles; }
|
||||
string annotateOut() const { return m_annotateOut; }
|
||||
bool annotateAll() const { return m_annotateAll; }
|
||||
int annotateMin() const { return m_annotateMin; }
|
||||
bool rank() const { return m_rank; }
|
||||
bool unlink() const { return m_unlink; }
|
||||
string writeFile() const { return m_writeFile; }
|
||||
|
||||
// METHODS (from main)
|
||||
static string version();
|
||||
|
||||
// METHODS (file searching)
|
||||
static string filenameNonDir(const string& filename);
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
||||
#endif // guard
|
152
src/VlcPoint.h
Normal file
152
src/VlcPoint.h
Normal file
@ -0,0 +1,152 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: verilator_coverage: Coverage points
|
||||
//
|
||||
// Code available from: http://www.veripool.org/verilator
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2014 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.
|
||||
//
|
||||
// Verilator is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef _VLCPOINT_H_
|
||||
#define _VLCPOINT_H_ 1
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include "verilated_cov_key.h"
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <iomanip>
|
||||
|
||||
//********************************************************************
|
||||
// VlcPoint - A coverage point (across all tests)
|
||||
|
||||
class VlcPoint {
|
||||
private:
|
||||
// MEMBERS
|
||||
string m_name; //< Name of the point
|
||||
vluint64_t m_pointNum; //< Point number
|
||||
vluint64_t m_testsCovering;//< Number tests with non-zero coverage of this point
|
||||
vluint64_t m_count; //< Count of hits across all tests
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlcPoint(const string& name, int pointNum) {
|
||||
m_name = name;
|
||||
m_pointNum = pointNum;
|
||||
m_testsCovering = 0;
|
||||
m_count = 0;
|
||||
}
|
||||
~VlcPoint() {}
|
||||
// ACCESSORS
|
||||
const string& name() const { return m_name; }
|
||||
vluint64_t pointNum() const { return m_pointNum; }
|
||||
vluint64_t testsCovering() const { return m_testsCovering; }
|
||||
void countInc(vluint64_t inc) { m_count += inc; }
|
||||
vluint64_t count() const { return m_count; }
|
||||
void testsCoveringInc() { m_testsCovering++; }
|
||||
// KEY ACCESSORS
|
||||
string filename() const { return keyExtract(VL_CIK_FILENAME); }
|
||||
string comment() const { return keyExtract(VL_CIK_COMMENT); }
|
||||
string type() const { return keyExtract(VL_CIK_TYPE); }
|
||||
string thresh() const { return keyExtract(VL_CIK_THRESH); } // string as maybe ""
|
||||
int lineno() const { return atoi(keyExtract(VL_CIK_LINENO).c_str()); }
|
||||
int column() const { return atoi(keyExtract(VL_CIK_COLUMN).c_str()); }
|
||||
// METHODS
|
||||
string keyExtract(const char* shortKey) const {
|
||||
// Hot function
|
||||
size_t shortLen = strlen(shortKey);
|
||||
const string namestr = name();
|
||||
for (const char* cp = namestr.c_str(); *cp; ++cp) {
|
||||
if (*cp == '\001') {
|
||||
if (0==strncmp(cp+1, shortKey, shortLen)
|
||||
&& cp[shortLen+1] == '\002') {
|
||||
cp += shortLen+2; // Skip \001+short+\002
|
||||
const char* ep = cp;
|
||||
while (*ep && *ep != '\001') ++ep;
|
||||
return string(cp, ep-cp);
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
static void dumpHeader() {
|
||||
cout<<"Points:\n";
|
||||
cout<<" Num, TestsCover, Count, Name"<<endl;
|
||||
}
|
||||
void dump() const {
|
||||
cout<<" "<<setw(8)<<setfill('0')<<pointNum()
|
||||
<<", "<<setw(7)<<setfill(' ')<<testsCovering()
|
||||
<<", "<<setw(7)<<setfill(' ')<<count()
|
||||
<<", \""<<name()<<"\""<<endl;
|
||||
}
|
||||
};
|
||||
|
||||
//********************************************************************
|
||||
// VlcPoints - Container of all points
|
||||
|
||||
class VlcPoints {
|
||||
private:
|
||||
// MEMBERS
|
||||
typedef std::map<string,vluint64_t> NameMap;
|
||||
NameMap m_nameMap; //< Name to point-number
|
||||
vector<VlcPoint> m_points; //< List of all points
|
||||
vluint64_t m_numPoints; //< Total unique points
|
||||
|
||||
public:
|
||||
// ITERATORS
|
||||
typedef NameMap ByName;
|
||||
typedef ByName::iterator iterator;
|
||||
ByName::iterator begin() { return m_nameMap.begin(); }
|
||||
ByName::iterator end() { return m_nameMap.end(); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlcPoints() {
|
||||
m_numPoints = 0;
|
||||
}
|
||||
~VlcPoints() {}
|
||||
|
||||
// METHODS
|
||||
void dump() {
|
||||
UINFO(2,"dumpPoints...\n");
|
||||
VlcPoint::dumpHeader();
|
||||
for (VlcPoints::ByName::iterator it=begin(); it!=end(); ++it) {
|
||||
const VlcPoint& point = pointNumber(it->second);
|
||||
point.dump();
|
||||
}
|
||||
}
|
||||
VlcPoint& pointNumber(vluint64_t num) {
|
||||
return m_points[num];
|
||||
}
|
||||
vluint64_t findAddPoint(const string& name, vluint64_t count) {
|
||||
vluint64_t pointnum;
|
||||
NameMap::iterator iter = m_nameMap.find(name);
|
||||
if (iter != m_nameMap.end()) {
|
||||
pointnum = iter->second;
|
||||
m_points[pointnum].countInc(count);
|
||||
}
|
||||
else {
|
||||
pointnum = m_numPoints++;
|
||||
VlcPoint point (name, pointnum);
|
||||
point.countInc(count);
|
||||
m_points.push_back(point);
|
||||
m_nameMap.insert(make_pair(point.name(), point.pointNum()));
|
||||
}
|
||||
return pointnum;
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
||||
#endif // guard
|
145
src/VlcSource.h
Normal file
145
src/VlcSource.h
Normal file
@ -0,0 +1,145 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: verilator_coverage: Source file to annotate with line coverage
|
||||
//
|
||||
// Code available from: http://www.veripool.org/verilator
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2014 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.
|
||||
//
|
||||
// Verilator is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef _VLCSOURCE_H_
|
||||
#define _VLCSOURCE_H_ 1
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
//********************************************************************
|
||||
// VlcColumnCount - count at specific source file, line and column
|
||||
|
||||
class VlcSourceCount {
|
||||
private:
|
||||
// MEMBERS
|
||||
int m_lineno; ///< Line number
|
||||
int m_column; ///< Column number
|
||||
vluint64_t m_count; ///< Count
|
||||
bool m_ok; ///< Coverage is above threshold
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlcSourceCount(int lineno, int column) {
|
||||
m_lineno = lineno;
|
||||
m_column = column;
|
||||
m_count = 0;
|
||||
m_ok = false;
|
||||
}
|
||||
~VlcSourceCount() {}
|
||||
|
||||
// ACCESSORS
|
||||
int lineno() const { return m_lineno; }
|
||||
int column() const { return m_column; }
|
||||
vluint64_t count() const { return m_count; }
|
||||
bool ok() const { return m_ok; }
|
||||
|
||||
// METHODS
|
||||
void incCount(vluint64_t count, bool ok) {
|
||||
m_count += count;
|
||||
if (ok) m_ok = true;
|
||||
}
|
||||
};
|
||||
|
||||
//********************************************************************
|
||||
// VlcSource - source file to annotate
|
||||
|
||||
class VlcSource {
|
||||
public:
|
||||
// TYPES
|
||||
typedef map<int,VlcSourceCount> ColumnMap; // Map of {column}
|
||||
typedef map<int,ColumnMap> LinenoMap; // Map of {lineno}{column}
|
||||
|
||||
private:
|
||||
// MEMBERS
|
||||
string m_name; //< Name of the source file
|
||||
bool m_needed; //< Need to annotate; has low coverage
|
||||
LinenoMap m_lines; //< Map of each annotated line
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlcSource(const string& name) {
|
||||
m_name = name;
|
||||
m_needed = false;
|
||||
}
|
||||
~VlcSource() {}
|
||||
|
||||
// ACCESSORS
|
||||
const string& name() const { return m_name; }
|
||||
void needed(bool flag) { m_needed = flag; }
|
||||
bool needed() const { return m_needed; }
|
||||
LinenoMap& lines() { return m_lines; }
|
||||
|
||||
// METHODS
|
||||
void incCount(int lineno, int column, vluint64_t count, bool ok) {
|
||||
LinenoMap::iterator lit = m_lines.find(lineno);
|
||||
if (lit == m_lines.end()) {
|
||||
lit = m_lines.insert(make_pair(lineno,ColumnMap())).first;
|
||||
}
|
||||
ColumnMap& cmap = lit->second;
|
||||
ColumnMap::iterator cit = cmap.find(column);
|
||||
if (cit == cmap.end()) {
|
||||
cit = cmap.insert(make_pair(column,VlcSourceCount(lineno, column))).first;
|
||||
}
|
||||
VlcSourceCount& sc = cit->second;
|
||||
sc.incCount(count,ok);
|
||||
}
|
||||
};
|
||||
|
||||
//********************************************************************
|
||||
// VlcSources - Container of all source files
|
||||
|
||||
class VlcSources {
|
||||
public:
|
||||
// TYPES
|
||||
typedef map<string,VlcSource> NameMap;
|
||||
private:
|
||||
// MEMBERS
|
||||
NameMap m_sources; //< List of all sources
|
||||
|
||||
public:
|
||||
// ITERATORS
|
||||
typedef NameMap::iterator iterator;
|
||||
NameMap::iterator begin() { return m_sources.begin(); }
|
||||
NameMap::iterator end() { return m_sources.end(); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlcSources() {}
|
||||
~VlcSources() {}
|
||||
|
||||
// METHODS
|
||||
VlcSource& findNewSource(const string& name) {
|
||||
NameMap::iterator iter = m_sources.find(name);
|
||||
if (iter != m_sources.end()) {
|
||||
return iter->second;
|
||||
}
|
||||
else {
|
||||
iter = m_sources.insert(make_pair(name, VlcSource(name))).first;
|
||||
return iter->second;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
||||
#endif // Guard
|
137
src/VlcTest.h
Normal file
137
src/VlcTest.h
Normal file
@ -0,0 +1,137 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: verilator_coverage: Test/coverage file container
|
||||
//
|
||||
// Code available from: http://www.veripool.org/verilator
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2014 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.
|
||||
//
|
||||
// Verilator is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef _VLCTEST_H_
|
||||
#define _VLCTEST_H_ 1
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include "VlcPoint.h"
|
||||
#include "VlcBucket.h"
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
//********************************************************************
|
||||
// VlcTest - a single testrun i.e. a file containing coverage data
|
||||
|
||||
class VlcTest {
|
||||
private:
|
||||
// MEMBERS
|
||||
string m_name; //< Name of the test
|
||||
double m_computrons; //< Runtime for the test
|
||||
vluint64_t m_testrun; //< Test run number, for database use
|
||||
vluint64_t m_rank; //< Execution rank suggestion
|
||||
vluint64_t m_rankPoints; //< Ranked additional points
|
||||
vluint64_t m_user; //< User data for algorithms (not persisted in .dat file)
|
||||
VlcBuckets m_buckets; //< Coverage data for each coverage point
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlcTest(const string& name, vluint64_t testrun, double comp) {
|
||||
m_name = name;
|
||||
m_computrons = comp;
|
||||
m_testrun = testrun;
|
||||
m_rank = 0;
|
||||
m_rankPoints = 0;
|
||||
m_user = 0;
|
||||
}
|
||||
~VlcTest() {}
|
||||
|
||||
// ACCESSORS
|
||||
const string& name() const { return m_name; }
|
||||
double computrons() const { return m_computrons; }
|
||||
vluint64_t testrun() const { return m_testrun; }
|
||||
VlcBuckets& buckets() { return m_buckets; }
|
||||
vluint64_t bucketsCovered() const { return m_buckets.bucketsCovered(); }
|
||||
vluint64_t rank() const { return m_rank; }
|
||||
void rank(vluint64_t flag) { m_rank = flag; }
|
||||
vluint64_t rankPoints() const { return m_rankPoints; }
|
||||
void rankPoints(vluint64_t flag) { m_rankPoints = flag; }
|
||||
vluint64_t user() const { return m_user; }
|
||||
void user(vluint64_t flag) { m_user = flag; }
|
||||
|
||||
// METHODS
|
||||
static void dumpHeader() {
|
||||
cout<<"Tests:\n";
|
||||
//cout<<" Testrun, Computrons,"; // Currently not loaded
|
||||
cout<<" Covered, Rank, RankPts, Filename"<<endl;
|
||||
}
|
||||
void dump(bool bucketsToo) {
|
||||
if (testrun() || computrons()) {
|
||||
cout<<" "<<setw(8)<<setfill('0')<<testrun()
|
||||
<<", "<<setw(7)<<setfill(' ')<<computrons()<<",";
|
||||
}
|
||||
cout<<" "<<setw(7)<<setfill(' ')<<bucketsCovered()
|
||||
<<", "<<setw(7)<<setfill(' ')<<rank()
|
||||
<<", "<<setw(7)<<setfill(' ')<<rankPoints()
|
||||
<<", \""<<name()<<"\""<<endl;
|
||||
if (bucketsToo) m_buckets.dump();
|
||||
}
|
||||
};
|
||||
|
||||
//********************************************************************
|
||||
// VlcTests - Container of all tests
|
||||
|
||||
class VlcTests {
|
||||
public:
|
||||
// TYPES
|
||||
typedef vector<VlcTest*> ByName;
|
||||
private:
|
||||
// MEMBERS
|
||||
ByName m_tests; //< List of all tests
|
||||
|
||||
public:
|
||||
// ITERATORS
|
||||
typedef ByName::iterator iterator;
|
||||
ByName::iterator begin() { return m_tests.begin(); }
|
||||
ByName::iterator end() { return m_tests.end(); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlcTests() {}
|
||||
~VlcTests() {
|
||||
for (VlcTests::ByName::iterator it=begin(); it!=end(); ++it) {
|
||||
delete *it; *it=NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// METHODS
|
||||
void dump(bool bucketsToo) {
|
||||
UINFO(2,"dumpTests...\n");
|
||||
VlcTest::dumpHeader();
|
||||
for (VlcTests::ByName::iterator it=begin(); it!=end(); ++it) {
|
||||
(*it)->dump(bucketsToo);
|
||||
}
|
||||
}
|
||||
VlcTest* newTest(const string& name, vluint64_t testrun, double comp) {
|
||||
VlcTest* testp = new VlcTest(name, testrun, comp);
|
||||
m_tests.push_back(testp);
|
||||
return testp;
|
||||
}
|
||||
void clearUser() {
|
||||
for (ByName::iterator it = m_tests.begin(); it != m_tests.end(); ++it) {
|
||||
(*it)->user(0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
||||
#endif // Guard
|
263
src/VlcTop.cpp
Normal file
263
src/VlcTop.cpp
Normal file
@ -0,0 +1,263 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: verilator_coverage: top implementation
|
||||
//
|
||||
// Code available from: http://www.veripool.org/verilator
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2014 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.
|
||||
//
|
||||
// Verilator is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
// Cheat for speed and compile .cpp files into one object
|
||||
#include "V3Error.h"
|
||||
#include "VlcOptions.h"
|
||||
#include "VlcTop.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
|
||||
//######################################################################
|
||||
|
||||
void VlcTop::readCoverage(const string& filename, bool nonfatal) {
|
||||
UINFO(2,"readCoverage "<<filename<<endl);
|
||||
|
||||
ifstream is (filename.c_str());
|
||||
if (!is) {
|
||||
if (!nonfatal) v3fatal("Can't read "<<filename);
|
||||
return;
|
||||
}
|
||||
|
||||
// Testrun and computrons argument unsupported as yet
|
||||
VlcTest* testp = tests().newTest(filename, 0, 0);
|
||||
|
||||
while (!is.eof()) {
|
||||
string line;
|
||||
getline(is, line);
|
||||
//UINFO(9," got "<<line<<endl);
|
||||
if (line[0] == 'C') {
|
||||
string::size_type secspace=3;
|
||||
for (; secspace<line.length(); secspace++) {
|
||||
if (line[secspace]=='\'' && line[secspace+1]==' ') break;
|
||||
}
|
||||
string point = line.substr(3,secspace-3);
|
||||
vluint64_t hits = atoll(line.c_str()+secspace+1);
|
||||
//UINFO(9," point '"<<point<<"'"<<" "<<hits<<endl);
|
||||
|
||||
vluint64_t pointnum = points().findAddPoint(point, hits);
|
||||
if (pointnum) {} // Prevent unused
|
||||
if (opt.rank()) { // Only if ranking - uses a lot of memory
|
||||
if (hits >= VlcBuckets::sufficient()) {
|
||||
points().pointNumber(pointnum).testsCoveringInc();
|
||||
testp->buckets().addData(pointnum, hits);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VlcTop::writeCoverage(const string& filename) {
|
||||
UINFO(2,"writeCoverage "<<filename<<endl);
|
||||
|
||||
ofstream os (filename.c_str());
|
||||
if (!os) {
|
||||
v3fatal("Can't write "<<filename);
|
||||
return;
|
||||
}
|
||||
|
||||
os << "# SystemC::Coverage-3" << endl;
|
||||
for (VlcPoints::ByName::iterator it=m_points.begin(); it!=m_points.end(); ++it) {
|
||||
const VlcPoint& point = m_points.pointNumber(it->second);
|
||||
os <<"C '"<<point.name()<<"' " << point.count()<<endl;
|
||||
}
|
||||
}
|
||||
|
||||
//********************************************************************
|
||||
|
||||
struct CmpComputrons {
|
||||
inline bool operator () (const VlcTest* lhsp, const VlcTest* rhsp) const {
|
||||
if (lhsp->computrons() != rhsp->computrons()) {
|
||||
return lhsp->computrons() < rhsp->computrons();
|
||||
}
|
||||
return lhsp->bucketsCovered() > rhsp->bucketsCovered();
|
||||
}
|
||||
};
|
||||
|
||||
void VlcTop::rank() {
|
||||
UINFO(2,"rank...\n");
|
||||
vluint64_t nextrank=1;
|
||||
|
||||
// Sort by computrons, so fast tests get selected first
|
||||
vector<VlcTest*> bytime;
|
||||
for (VlcTests::ByName::iterator it=m_tests.begin(); it!=m_tests.end(); ++it) {
|
||||
VlcTest* testp = *it;
|
||||
if (testp->bucketsCovered()) { // else no points, so can't help us
|
||||
bytime.push_back(*it);
|
||||
}
|
||||
}
|
||||
sort(bytime.begin(), bytime.end(), CmpComputrons()); // Sort the vector
|
||||
|
||||
VlcBuckets remaining;
|
||||
for (VlcPoints::ByName::iterator it=m_points.begin(); it!=m_points.end(); ++it) {
|
||||
VlcPoint* pointp = &points().pointNumber(it->second);
|
||||
// If any tests hit this point, then we'll need to cover it.
|
||||
if (pointp->testsCovering()) { remaining.addData(pointp->pointNum(), 1); }
|
||||
}
|
||||
|
||||
// Additional Greedy algorithm
|
||||
// O(n^2) Ouch. Probably the thing to do is randomize the order of data
|
||||
// then hierarchically solve a small subset of tests, and take resulting
|
||||
// solution and move up to larger subset of tests. (Aka quick sort.)
|
||||
while (1) {
|
||||
if (debug()) { UINFO(9,"Left on iter"<<nextrank<<": "); remaining.dump(); }
|
||||
VlcTest* bestTestp = NULL;
|
||||
vluint64_t bestRemain = 0;
|
||||
for (vector<VlcTest*>::iterator it=bytime.begin(); it!=bytime.end(); ++it) {
|
||||
VlcTest* testp = *it;
|
||||
if (!testp->rank()) {
|
||||
vluint64_t remain = testp->buckets().dataPopCount(remaining);
|
||||
if (remain > bestRemain) {
|
||||
bestTestp = testp;
|
||||
bestRemain = remain;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (VlcTest* testp = bestTestp) {
|
||||
testp->rank(nextrank++);
|
||||
testp->rankPoints(bestRemain);
|
||||
remaining.orData(bestTestp->buckets());
|
||||
} else {
|
||||
break; // No test covering more stuff found
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
|
||||
void VlcTop::annotateCalc() {
|
||||
// Calculate per-line information into filedata structure
|
||||
for (VlcPoints::ByName::iterator it=m_points.begin(); it!=m_points.end(); ++it) {
|
||||
const VlcPoint& point = m_points.pointNumber(it->second);
|
||||
string filename = point.filename();
|
||||
int lineno = point.lineno();
|
||||
if (filename!="" && lineno!=0) {
|
||||
int column = point.column();
|
||||
VlcSource& source = sources().findNewSource(filename);
|
||||
string threshStr = point.thresh();
|
||||
unsigned thresh = (threshStr!="") ? atoi(threshStr.c_str()) : opt.annotateMin();
|
||||
bool ok = (point.count() >= thresh);
|
||||
UINFO(9, "AnnoCalc count "<<filename<<" "<<lineno<<" "<<point.count()<<endl);
|
||||
source.incCount(lineno, column, point.count(), ok);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VlcTop::annotateCalcNeeded() {
|
||||
// Compute which files are needed. A file isn't needed if it has appropriate
|
||||
// coverage in all categories
|
||||
int totCases = 0;
|
||||
int totOk = 0;
|
||||
for (VlcSources::NameMap::iterator sit=m_sources.begin(); sit!=m_sources.end(); ++sit) {
|
||||
VlcSource& source = sit->second;
|
||||
//UINFO(1,"Source "<<source.name()<<endl);
|
||||
if (opt.annotateAll()) source.needed(true);
|
||||
VlcSource::LinenoMap& lines = source.lines();
|
||||
for (VlcSource::LinenoMap::iterator lit=lines.begin(); lit!=lines.end(); ++lit) {
|
||||
VlcSource::ColumnMap& cmap = lit->second;
|
||||
for (VlcSource::ColumnMap::iterator cit=cmap.begin(); cit!=cmap.end(); ++cit) {
|
||||
VlcSourceCount& col = cit->second;
|
||||
//UINFO(0,"Source "<<source.name()<<" lineno="<<col.lineno()<<" col="<<col.column()<<endl);
|
||||
++totCases;
|
||||
if (col.ok()) {
|
||||
++totOk;
|
||||
} else {
|
||||
source.needed(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
float pct = totCases ? (100*totOk / totCases) : 0;
|
||||
cout<<"Total coverage ("<<totOk<<"/"<<totCases<<") "
|
||||
<<fixed<<setw(3)<<setprecision(2)<<pct<<"%"<<endl;
|
||||
if (totOk != totCases) cout<<"See lines with '%00' in "<<opt.annotateOut()<<endl;
|
||||
}
|
||||
|
||||
void VlcTop::annotateOutputFiles(const string& dirname) {
|
||||
// Create if uncreated, ignore errors
|
||||
mkdir(dirname.c_str(), 0777);
|
||||
for (VlcSources::NameMap::iterator sit=m_sources.begin(); sit!=m_sources.end(); ++sit) {
|
||||
VlcSource& source = sit->second;
|
||||
if (!source.needed()) continue;
|
||||
string filename = source.name();
|
||||
string outfilename = dirname+"/"+VlcOptions::filenameNonDir(filename);
|
||||
|
||||
UINFO(1,"annotateOutputFile "<<filename<<" -> "<<outfilename<<endl);
|
||||
|
||||
ifstream is (filename.c_str());
|
||||
if (!is) {
|
||||
v3error("Can't read "<<filename);
|
||||
return;
|
||||
}
|
||||
|
||||
ofstream os (outfilename.c_str());
|
||||
if (!os) {
|
||||
v3fatal("Can't write "<<outfilename);
|
||||
return;
|
||||
}
|
||||
|
||||
os << "\t// verilator_coverage annotation"<<endl;
|
||||
|
||||
int lineno = 0;
|
||||
while (!is.eof()) {
|
||||
lineno++;
|
||||
string line;
|
||||
getline(is, line);
|
||||
|
||||
bool first = true;
|
||||
|
||||
VlcSource::LinenoMap& lines = source.lines();
|
||||
VlcSource::LinenoMap::iterator lit=lines.find(lineno);
|
||||
if (lit != lines.end()) {
|
||||
VlcSource::ColumnMap& cmap = lit->second;
|
||||
for (VlcSource::ColumnMap::iterator cit=cmap.begin(); cit!=cmap.end(); ++cit) {
|
||||
VlcSourceCount& col = cit->second;
|
||||
//UINFO(0,"Source "<<source.name()<<" lineno="<<col.lineno()<<" col="<<col.column()<<endl);
|
||||
os<<(col.ok()?" ":"%")
|
||||
<<setfill('0')<<setw(6)<<col.count()
|
||||
<<"\t"<<line<<endl;
|
||||
if (first) {
|
||||
first = false;
|
||||
// Multiple columns on same line; print line just once
|
||||
string indent = "";
|
||||
for (const char* cp=line.c_str(); isspace(*cp); ++cp) {
|
||||
indent += *cp;
|
||||
}
|
||||
line = indent + "verilator_coverage: (next point on previous line)\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (first) {
|
||||
os<<"\t"<<line<<endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VlcTop::annotate(const string& dirname) {
|
||||
// Calculate per-line information into filedata structure
|
||||
annotateCalc();
|
||||
annotateCalcNeeded();
|
||||
annotateOutputFiles(dirname);
|
||||
}
|
||||
|
69
src/VlcTop.h
Normal file
69
src/VlcTop.h
Normal file
@ -0,0 +1,69 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: verilator_coverage: Top global container
|
||||
//
|
||||
// Code available from: http://www.veripool.org/verilator
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2014 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.
|
||||
//
|
||||
// Verilator is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef _VLCTOP_H_
|
||||
#define _VLCTOP_H_ 1
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include "VlcOptions.h"
|
||||
#include "VlcTest.h"
|
||||
#include "VlcPoint.h"
|
||||
#include "VlcSource.h"
|
||||
|
||||
//######################################################################
|
||||
// VlcTop - Top level options container
|
||||
|
||||
class VlcTop {
|
||||
public:
|
||||
// PUBLIC MEMBERS
|
||||
VlcOptions opt; //< Runtime options
|
||||
private:
|
||||
// MEMBERS
|
||||
VlcTests m_tests; //< List of all tests (all coverage files)
|
||||
VlcPoints m_points; //< List of all points
|
||||
VlcSources m_sources; //< List of all source files to annotate
|
||||
|
||||
// METHODS
|
||||
void annotateCalc();
|
||||
void annotateCalcNeeded();
|
||||
void annotateOutputFiles(const string& dirname);
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlcTop() {}
|
||||
~VlcTop() {}
|
||||
|
||||
// ACCESSORS
|
||||
VlcTests& tests() { return m_tests; }
|
||||
VlcPoints& points() { return m_points; }
|
||||
VlcSources& sources() { return m_sources; }
|
||||
|
||||
// METHODS
|
||||
void annotate(const string& dirname);
|
||||
void readCoverage(const string& filename, bool nonfatal=false);
|
||||
void writeCoverage(const string& filename);
|
||||
|
||||
void rank();
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
||||
#endif // guard
|
@ -19,8 +19,8 @@ my @Opt_Cpt;
|
||||
my @Opt_I;
|
||||
Getopt::Long::config ("pass_through", "no_auto_abbrev");
|
||||
if (! GetOptions (
|
||||
"help" => \&report,
|
||||
"debug" => \&debug,
|
||||
"help" => \&usage,
|
||||
"debug" => sub { $Debug = 1; },
|
||||
"classes!" => \$opt_classes,
|
||||
"report!" => \$opt_report,
|
||||
"<>" => \¶meter,
|
||||
@ -54,10 +54,6 @@ sub usage {
|
||||
exit (1);
|
||||
}
|
||||
|
||||
sub debug {
|
||||
$Debug = 1;
|
||||
}
|
||||
|
||||
sub parameter {
|
||||
my $param = shift;
|
||||
if ($param =~ /^-+I(\S+)/) {
|
||||
|
173
src/vlcovgen
Executable file
173
src/vlcovgen
Executable file
@ -0,0 +1,173 @@
|
||||
#!/usr/bin/perl -w
|
||||
# See copyright, etc in below POD section.
|
||||
######################################################################
|
||||
|
||||
#require 5.006_001;
|
||||
use Getopt::Long;
|
||||
use IO::File;
|
||||
use Pod::Usage;
|
||||
use strict;
|
||||
use vars qw ($Debug);
|
||||
|
||||
our @Items;
|
||||
|
||||
#======================================================================
|
||||
# main
|
||||
|
||||
$Debug = 0;
|
||||
my $Opt_Srcdir = ".";
|
||||
Getopt::Long::config ("pass_through", "no_auto_abbrev");
|
||||
if (! GetOptions (
|
||||
"help" => \&usage,
|
||||
"debug" => sub { $Debug = 1; },
|
||||
"srcdir=s" => \$Opt_Srcdir,
|
||||
"<>" => sub { die "%Error: Unknown parameter: $_[0],"; },
|
||||
)) {
|
||||
usage();
|
||||
}
|
||||
|
||||
read_keys("$Opt_Srcdir/../include/verilated_cov_key.h");
|
||||
lint();
|
||||
write_keys("$Opt_Srcdir/../include/verilated_cov_key.h");
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
sub usage {
|
||||
pod2usage(-verbose=>2, -exitval=>2, -output=>\*STDOUT);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
#######################################################################
|
||||
|
||||
sub read_keys {
|
||||
my $filename = shift;
|
||||
|
||||
my $fh = IO::File->new("<$filename") or die "%Error: $! $filename,";
|
||||
while (defined (my $line = $fh->getline())) {
|
||||
$line =~ s/\/\/.*$//;
|
||||
next if $line =~ /^\s*$/;
|
||||
if ($line =~ /^\s*VLCOVGEN_ITEM/) {
|
||||
$line =~ /^\s*VLCOVGEN_ITEM *\( *"([^"]+)" *\)/
|
||||
or die "%Error: $filename:$.: Misformed VLCOVGEN_ITEM line,";
|
||||
my @data;
|
||||
my $code = "\@data = ($1);";
|
||||
eval $code;
|
||||
die "%Error: $filename:$.: Parsing '$code': $@," if $@;
|
||||
push @Items, {@data};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#######################################################################
|
||||
|
||||
sub lint {
|
||||
my %shorts;
|
||||
my $ok = 1;
|
||||
foreach my $itemref (@Items) {
|
||||
if ($shorts{$itemref->{short}}) {
|
||||
warn "%Error: Duplicate short code: $itemref->{short},";
|
||||
$ok = 0;
|
||||
}
|
||||
$shorts{$itemref->{short}} = 1;
|
||||
}
|
||||
return $ok;
|
||||
}
|
||||
|
||||
sub write_keys {
|
||||
my $filename = shift;
|
||||
|
||||
my $fh = IO::File->new("<$filename") or die "%Error: $! $filename\n";
|
||||
|
||||
my @in;
|
||||
my @out;
|
||||
my $deleting;
|
||||
while (defined(my $line = $fh->getline)) {
|
||||
push @in, $line;
|
||||
if ($line =~ /VLCOVGEN_CIK_AUTO_EDIT_BEGIN/) {
|
||||
$deleting = 1;
|
||||
push @out, $line;
|
||||
foreach my $keyref (sort {$a->{name} cmp $b->{name}} @Items) {
|
||||
push @out, sprintf("#define VL_CIK_%s \"%s\"\n",
|
||||
uc $keyref->{name}, $keyref->{short});
|
||||
}
|
||||
}
|
||||
elsif ($line =~ /VLCOVGEN_SHORT_AUTO_EDIT_BEGIN/) {
|
||||
$deleting = 1;
|
||||
push @out, $line;
|
||||
foreach my $keyref (sort {$a->{name} cmp $b->{name}} @Items) {
|
||||
push @out, sprintf("\tif (key == \"%s\") return VL_CIK_%s;\n",
|
||||
$keyref->{name}, uc $keyref->{name});
|
||||
}
|
||||
}
|
||||
elsif ($line =~ /VLCOVGEN_.*AUTO_EDIT_END/) {
|
||||
$deleting = 0;
|
||||
push @out, $line;
|
||||
}
|
||||
elsif ($deleting) {
|
||||
}
|
||||
else {
|
||||
push @out, $line;
|
||||
}
|
||||
}
|
||||
$fh->close;
|
||||
|
||||
my $ok = join("", @out) eq join("", @in);
|
||||
if (!$ok) {
|
||||
my $fh = IO::File->new(">$filename") or die "%Error: $! writing $filename\n";
|
||||
$fh->print(join "", @out);
|
||||
$fh->close;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#######################################################################
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=head1 NAME
|
||||
|
||||
vlcovgen - Generate verilated_cov headers to reduce C++ code duplication
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
(called from make rules)
|
||||
vlcovgen
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Generates several files for Verilator compilations.
|
||||
|
||||
=head1 ARGUMENTS
|
||||
|
||||
=over 4
|
||||
|
||||
=item --help
|
||||
|
||||
Displays this message and program version and exits.
|
||||
|
||||
=back
|
||||
|
||||
=head1 DISTRIBUTION
|
||||
|
||||
Copyright 2002-2014 by Wilson Snyder. Verilator 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.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
more details.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
Wilson Snyder <wsnyder@wsnyder.org>
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
=cut
|
||||
|
||||
######################################################################
|
||||
### Local Variables:
|
||||
### compile-command: "./vlcovgen --srcdir ."
|
||||
### End:
|
@ -388,7 +388,7 @@ sub new {
|
||||
$self->{stats} ||= "$self->{obj_dir}/V".$self->{name}."__stats.txt";
|
||||
$self->{status_filename} ||= "$self->{obj_dir}/V".$self->{name}.".status";
|
||||
$self->{run_log_filename} ||= "$self->{obj_dir}/vlt_sim.log";
|
||||
$self->{coverage_filename} ||= "$self->{obj_dir}/vlt_coverage.pl";
|
||||
$self->{coverage_filename} ||= "$self->{obj_dir}/coverage.dat";
|
||||
$self->{vcd_filename} ||= "$self->{obj_dir}/sim.vcd";
|
||||
$self->{main_filename} ||= "$self->{obj_dir}/$self->{VM_PREFIX}__main.cpp";
|
||||
($self->{top_filename} = $self->{pl_filename}) =~ s/\.pl$//;
|
||||
@ -1143,7 +1143,7 @@ sub _make_main {
|
||||
|
||||
if ($self->{coverage}) {
|
||||
$fh->print("#if VM_COVERAGE\n");
|
||||
$fh->print(" SpCoverage::write(\"",$self->{coverage_filename},"\");\n");
|
||||
$fh->print(" VerilatedCov::write(\"",$self->{coverage_filename},"\");\n");
|
||||
$fh->print("#endif //VM_COVERAGE\n");
|
||||
}
|
||||
if ($self->{trace}) {
|
||||
|
170
test_regress/t/t_cover_line.out
Normal file
170
test_regress/t/t_cover_line.out
Normal file
@ -0,0 +1,170 @@
|
||||
// verilator_coverage annotation
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2008 by Wilson Snyder.
|
||||
|
||||
module t (/*AUTOARG*/
|
||||
// Inputs
|
||||
clk
|
||||
);
|
||||
|
||||
input clk;
|
||||
|
||||
reg toggle; initial toggle=0;
|
||||
|
||||
integer cyc; initial cyc=1;
|
||||
wire [7:0] cyc_copy = cyc[7:0];
|
||||
|
||||
alpha a1 (/*AUTOINST*/
|
||||
// Inputs
|
||||
.clk (clk),
|
||||
.toggle (toggle));
|
||||
alpha a2 (/*AUTOINST*/
|
||||
// Inputs
|
||||
.clk (clk),
|
||||
.toggle (toggle));
|
||||
beta b1 (/*AUTOINST*/
|
||||
// Inputs
|
||||
.clk (clk),
|
||||
.toggle (toggle));
|
||||
beta b2 (/*AUTOINST*/
|
||||
// Inputs
|
||||
.clk (clk),
|
||||
.toggle (toggle));
|
||||
tsk t1 (/*AUTOINST*/
|
||||
// Inputs
|
||||
.clk (clk),
|
||||
.toggle (toggle));
|
||||
off o1 (/*AUTOINST*/
|
||||
// Inputs
|
||||
.clk (clk),
|
||||
.toggle (toggle));
|
||||
|
||||
always @ (posedge clk) begin
|
||||
000010 if (cyc!=0) begin
|
||||
cyc <= cyc + 1;
|
||||
toggle <= '0;
|
||||
%000001 if (cyc==3) begin
|
||||
toggle <= '1;
|
||||
end
|
||||
%000001 else if (cyc==5) begin
|
||||
`ifdef VERILATOR
|
||||
$c("call_task();");
|
||||
`else
|
||||
call_task();
|
||||
`endif
|
||||
end
|
||||
%000001 else if (cyc==10) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
task call_task;
|
||||
/* verilator public */
|
||||
t1.center_task(1'b1);
|
||||
endtask
|
||||
|
||||
endmodule
|
||||
|
||||
module alpha (/*AUTOARG*/
|
||||
// Inputs
|
||||
clk, toggle
|
||||
);
|
||||
input clk;
|
||||
input toggle;
|
||||
always @ (posedge clk) begin
|
||||
%000002 if (toggle) begin
|
||||
// CHECK_COVER(-1,"top.v.a*",2)
|
||||
// t.a1 and t.a2 collapse to a count of 2
|
||||
end
|
||||
if (toggle) begin
|
||||
// CHECK_COVER_MISSING(-1)
|
||||
// This doesn't even get added
|
||||
// verilator coverage_block_off
|
||||
$write("");
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
module beta (/*AUTOARG*/
|
||||
// Inputs
|
||||
clk, toggle
|
||||
);
|
||||
input clk;
|
||||
input toggle;
|
||||
|
||||
/* verilator public_module */
|
||||
|
||||
always @ (posedge clk) begin
|
||||
%000000 if (0) begin
|
||||
// CHECK_COVER(-1,"top.v.b*",0)
|
||||
// Make sure that we don't optimize away zero buckets
|
||||
end
|
||||
%000002 if (toggle) begin
|
||||
// CHECK_COVER(-1,"top.v.b*",2)
|
||||
// t.b1 and t.b2 collapse to a count of 2
|
||||
end
|
||||
if (toggle) begin
|
||||
// CHECK_COVER_MISSING(-1)
|
||||
// This doesn't
|
||||
// verilator coverage_block_off
|
||||
$write("");
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
module tsk (/*AUTOARG*/
|
||||
// Inputs
|
||||
clk, toggle
|
||||
);
|
||||
input clk;
|
||||
input toggle;
|
||||
|
||||
/* verilator public_module */
|
||||
|
||||
always @ (posedge clk) begin
|
||||
center_task(1'b0);
|
||||
end
|
||||
|
||||
task center_task;
|
||||
input external;
|
||||
begin
|
||||
%000001 if (toggle) begin
|
||||
// CHECK_COVER(-1,"top.v.t1",1)
|
||||
end
|
||||
%000001 if (external) begin
|
||||
// CHECK_COVER(-1,"top.v.t1",1)
|
||||
$write("[%0t] Got external pulse\n", $time);
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
endmodule
|
||||
|
||||
module off (/*AUTOARG*/
|
||||
// Inputs
|
||||
clk, toggle
|
||||
);
|
||||
input clk;
|
||||
input toggle;
|
||||
|
||||
// verilator coverage_off
|
||||
always @ (posedge clk) begin
|
||||
if (toggle) begin
|
||||
// CHECK_COVER_MISSING(-1)
|
||||
// because under coverage_module_off
|
||||
end
|
||||
end
|
||||
// verilator coverage_on
|
||||
always @ (posedge clk) begin
|
||||
%000001 if (toggle) begin
|
||||
// CHECK_COVER(-1,"top.v.o1",1)
|
||||
// because under coverage_module_off
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
@ -20,5 +20,12 @@ execute (
|
||||
# Read the input .v file and do any CHECK_COVER requests
|
||||
inline_checks();
|
||||
|
||||
ok(1);
|
||||
$Self->_run(cmd=>["../bin/verilator_coverage",
|
||||
"--annotate", "$Self->{obj_dir}/annotated",
|
||||
"$Self->{obj_dir}/coverage.dat",
|
||||
],
|
||||
);
|
||||
|
||||
ok(files_identical("$Self->{obj_dir}/annotated/t_cover_line.v", "t/t_cover_line.out"));
|
||||
|
||||
1;
|
||||
|
@ -11,6 +11,7 @@ $Self->{vlt} or $Self->skip("Verilator only test");
|
||||
|
||||
foreach my $prog (
|
||||
"../bin/verilator",
|
||||
"../bin/verilator_coverage",
|
||||
"../bin/verilator_difftree",
|
||||
"../bin/verilator_profcfunc",
|
||||
) {
|
||||
|
5
test_regress/t/t_vlcov_data_a.dat
Normal file
5
test_regress/t/t_vlcov_data_a.dat
Normal file
@ -0,0 +1,5 @@
|
||||
# SystemC::Coverage-3
|
||||
C 'CoverPoint0ffile1.sphl159' 0
|
||||
C 'CoverPoint1ffile1.sphl159' 1
|
||||
C 'CoverPoint2ffile1.sphl159' 10
|
||||
C 'CoverPoint3ffile1.sphl159' 0
|
5
test_regress/t/t_vlcov_data_b.dat
Normal file
5
test_regress/t/t_vlcov_data_b.dat
Normal file
@ -0,0 +1,5 @@
|
||||
# SystemC::Coverage-3
|
||||
C 'CoverPoint2ffile1.sphl159' 10
|
||||
C 'CoverPoint3ffile1.sphl159' 0
|
||||
C 'CoverPoint4ffile1.sphl159' 1
|
||||
C 'CoverPoint5ffile1.sphl159' 9
|
2
test_regress/t/t_vlcov_data_c.dat
Normal file
2
test_regress/t/t_vlcov_data_c.dat
Normal file
@ -0,0 +1,2 @@
|
||||
# SystemC::Coverage-3
|
||||
C 'CoverPoint6ffile1.sphl159' 10
|
2
test_regress/t/t_vlcov_data_d.dat
Normal file
2
test_regress/t/t_vlcov_data_d.dat
Normal file
@ -0,0 +1,2 @@
|
||||
# SystemC::Coverage-3
|
||||
C 'CoverPoint6ffile1.sphl159' 12
|
8
test_regress/t/t_vlcov_merge.out
Normal file
8
test_regress/t/t_vlcov_merge.out
Normal file
@ -0,0 +1,8 @@
|
||||
# SystemC::Coverage-3
|
||||
C 'CoverPoint0ffile1.sphl159' 0
|
||||
C 'CoverPoint1ffile1.sphl159' 1
|
||||
C 'CoverPoint2ffile1.sphl159' 20
|
||||
C 'CoverPoint3ffile1.sphl159' 0
|
||||
C 'CoverPoint4ffile1.sphl159' 1
|
||||
C 'CoverPoint5ffile1.sphl159' 9
|
||||
C 'CoverPoint6ffile1.sphl159' 22
|
21
test_regress/t/t_vlcov_merge.pl
Executable file
21
test_regress/t/t_vlcov_merge.pl
Executable file
@ -0,0 +1,21 @@
|
||||
#!/usr/bin/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.
|
||||
|
||||
$Self->{vlt} or $Self->skip("Verilator only test");
|
||||
|
||||
$Self->_run(cmd=>["../bin/verilator_coverage",
|
||||
"--write", "$Self->{obj_dir}/coverage.dat",
|
||||
"t/t_vlcov_data_a.dat",
|
||||
"t/t_vlcov_data_b.dat",
|
||||
"t/t_vlcov_data_c.dat",
|
||||
"t/t_vlcov_data_d.dat",
|
||||
],
|
||||
);
|
||||
ok(files_identical("$Self->{obj_dir}/coverage.dat", "t/$Self->{name}.out"));
|
||||
1;
|
6
test_regress/t/t_vlcov_rank.out
Normal file
6
test_regress/t/t_vlcov_rank.out
Normal file
@ -0,0 +1,6 @@
|
||||
Tests:
|
||||
Covered, Rank, RankPts, Filename
|
||||
2, 2, 1, "t/t_vlcov_data_a.dat"
|
||||
3, 1, 3, "t/t_vlcov_data_b.dat"
|
||||
1, 3, 1, "t/t_vlcov_data_c.dat"
|
||||
1, 0, 0, "t/t_vlcov_data_d.dat"
|
23
test_regress/t/t_vlcov_rank.pl
Executable file
23
test_regress/t/t_vlcov_rank.pl
Executable file
@ -0,0 +1,23 @@
|
||||
#!/usr/bin/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.
|
||||
|
||||
$Self->{vlt} or $Self->skip("Verilator only test");
|
||||
|
||||
$Self->_run(cmd=>["../bin/verilator_coverage",
|
||||
"--rank",
|
||||
"t/t_vlcov_data_a.dat",
|
||||
"t/t_vlcov_data_b.dat",
|
||||
"t/t_vlcov_data_c.dat",
|
||||
"t/t_vlcov_data_d.dat",
|
||||
],
|
||||
logfile=>"$Self->{obj_dir}/vlcov.log",
|
||||
tee => 0,
|
||||
);
|
||||
ok(files_identical("$Self->{obj_dir}/vlcov.log", "t/$Self->{name}.out"));
|
||||
1;
|
25
test_regress/t/t_vlcov_rewrite.pl
Executable file
25
test_regress/t/t_vlcov_rewrite.pl
Executable file
@ -0,0 +1,25 @@
|
||||
#!/usr/bin/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.
|
||||
|
||||
$Self->{vlt} or $Self->skip("Verilator only test");
|
||||
|
||||
foreach my $basename ("t_vlcov_data_a.dat",
|
||||
"t_vlcov_data_b.dat",
|
||||
"t_vlcov_data_c.dat",
|
||||
"t_vlcov_data_d.dat",
|
||||
) {
|
||||
$Self->_run(cmd=>["../bin/verilator_coverage",
|
||||
"t/${basename}",
|
||||
"--write", "$Self->{obj_dir}/${basename}"
|
||||
],
|
||||
tee=>0,
|
||||
);
|
||||
ok(files_identical("$Self->{obj_dir}/${basename}", "t/${basename}"));
|
||||
}
|
||||
1;
|
@ -25,9 +25,9 @@ DEBUG_ON = --debug --trace-dups
|
||||
|
||||
######################################################################
|
||||
ifneq ($(SYSTEMPERL),)
|
||||
test_default: precopy prep preproc compile run coverage
|
||||
test_debug: precopy prep_dbg preproc compile_dbg run coverage
|
||||
test_nopublic: precopy prep_dbg_np preproc compile_dbg run coverage
|
||||
test_default: precopy prep preproc compile run
|
||||
test_debug: precopy prep_dbg preproc compile_dbg run
|
||||
test_nopublic: precopy prep_dbg_np preproc compile_dbg run
|
||||
else
|
||||
test_default: nosp
|
||||
test_debug: nosp
|
||||
@ -39,7 +39,7 @@ V_FLAGS = -f $(VERILATOR_ROOT)/test_v/input.vc
|
||||
# Note the --public --output-split-cfunc is here for testing only,
|
||||
# Avoid using these settings in real application Makefiles!
|
||||
VERILATOR_FLAGS = --public --output-split-cfuncs 1000 --output-split 1000 \
|
||||
--sp --coverage --stats --trace $(V_FLAGS) top.v
|
||||
--sp --stats --trace $(V_FLAGS) top.v
|
||||
|
||||
precopy: obj_dir obj_dir/sc_main.cpp
|
||||
obj_dir/sc_main.cpp: ../test_sc/sc_main.cpp
|
||||
@ -66,7 +66,7 @@ run:
|
||||
obj_dir/simx
|
||||
|
||||
coverage:
|
||||
vcoverage $(V_FLAGS)
|
||||
$(VERILATOR_ROOT)/bin/verilator_coverage $(V_FLAGS)
|
||||
|
||||
######################################################################
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user