package Testbench::Run;
require 5.000;
require Exporter;
use strict;
use vars qw ($Debug $VERSION $info $error $debug $align %Testbench_Default_Option %Testbench_Valid_Option);
use Carp;
use Pod::Find qw(pod_where);
use Pod::Usage;
use Getopt::Long;
use IO::File;
###############################################
#### Configuration Section
$Debug = 0;
$VERSION = '1.0';
### message
$info = "###(info) $0";
$error = "###(error) $0";
$debug = "###(debug) $0";
$align = "\\\n ";
%Testbench_Default_Option = (
wave => 'fsdb',
timeout => 0,
cov => 0,
gui => 0,
dryrun => 0,
verbose => 0,
sdf => 'no',
seed => 0,
debug => undef,
define => undef,
plusarg => undef
);
%Testbench_Valid_Option = (
phase => [qw(rtl gate)],
bfm => [qw(scm cpm vmm uvm)],
simulator => [qw(vcs nc)],
wave => [qw(no fsdb vpd evcd)],
timeout => [qw(integer)],
cov => [qw(switch)],
gui => [qw(switch)],
dryrun => [qw(switch)],
verbose => [qw(integer)],
sdf => [qw(no min max typ)],
seed => [qw(integer random)],
debug => [qw(string)],
define => [qw(string)],
plusarg => [qw(string)]
);
###############################################
###############################################
###############################################
sub new {
@_>=1 or croak 'usage: Testbench::Run->new({options})';
my $class = shift;
$class ||= "Testbench::Run";
my %defaultopt = %Testbench_Default_Option;
my $self = {
phase => 'rtl',
bfm => 'cpm',
simulator => 'vcs',
opt => \%defaultopt,
defines => '',
plusargs => '',
uservs => '',
testid => '',
_unparsed => [],
_configed => [],
@_
};
bless $self, $class;
$self->_do_initialization();
return $self;
}
# just check the validation of initialization process
sub _do_initialization {
my $self=shift;
my $invalid = 0;
foreach (@{$Testbench_Valid_Option{phase}}) {
if ($self->{phase} eq $_) {
$invalid=0;
last;
}
else {
$invalid=1;
}
}
croak "$self->{phase} not supported!" if $invalid;
foreach (@{$Testbench_Valid_Option{bfm}}) {
if ($self->{bfm} eq $_) {
$invalid=0;
last;
}
else {
$invalid=1;
}
}
croak "$self->{bfm} not supported!" if $invalid;
foreach (@{$Testbench_Valid_Option{simulator}}) {
if ($self->{simulator} eq $_) {
$invalid=0;
last;
}
else {
$invalid=1;
}
}
croak "$self->{simulator} not supported!" if $invalid;
}
###############################################
#### Option parsing
## load config file then parse options
sub getopt {
my $self = shift;
print "$debug: display class member initialized\n" if $Debug;
$self->display() if $Debug;
my $cfgfile = "$self->{phase}_$self->{bfm}_$self->{simulator}.opt";
$self->_loadcfg($cfgfile);
print "$debug: display class member $cfgfile loaded\n" if $Debug;
$self->display() if $Debug;
$self->_parse();
print "$debug: display class member parsed\n" if $Debug;
$self->display() if $Debug;
$self->defines();
$self->plusargs();
$self->uservs();
print "$debug: display class member finally\n" if $Debug;
$self->display() if $Debug;
return @{$self->{_unparsed}};
}
# load configuration file added to $self->{_configed}, for later use
sub _loadcfg {
my $self = shift;
my $cfgfile = shift;
if ( -e $cfgfile ) {
my $fh = IO::File->new("<$cfgfile");
print "$debug: loading $cfgfile...\n" if $Debug;
while (my $line= $fh->getline()) {
chomp $line;
$line =~ s/\/\/.*$//;
next if $line =~ /^s\s*$/;
my @p = split /\s+/,$line;
push @{$self->{_configed}},@p;
}
$fh->close();
}
else {
$self->_gencfg($cfgfile);
exit 1;
}
}
## Generate config file
sub _gencfg {
my $self = shift;
my $cfgfile = shift;
my $cfgtxt = "//\n";
$cfgtxt .= "// Auto Generated Template -- SOCIII Verification //\n";
$cfgtxt .= "// Annotation line start with // //\n";
$cfgtxt .= "//\n";
print "$debug: Generate Configuration File -- $cfgfile\n" if $Debug;
my $fh = IO::File->new(">$cfgfile");
print $fh $cfgtxt;
my $key;
foreach $key (keys %Testbench_Valid_Option) {
next if ($key eq "phase" || $key eq "bfm" || $key eq "simulator");
print $fh "// valid $key: ",join(" , ",@{$Testbench_Valid_Option{$key}}),"\n";
if (defined $self->{opt}->{$key}) {
if ($Testbench_Valid_Option{$key}->[0] eq "switch") {
if ($self->{opt}->{$key}) {
print $fh "--$key \n";
}
else {
print $fh "//--$key \n";
}
}
else {
print $fh "--$key $self->{opt}->{$key}\n";
}
}
else {
print $fh "//--$key \n";
}
}
$fh->close();
}
# parse the command line and $self->{_configed}
sub _parse {
my $self = shift;
Getopt::Long::config ("no_auto_abbrev","pass_through","auto_version");
local @ARGV = @ARGV;
unshift @ARGV, @{$self->{_configed}};
print "$debug: Final ARGV=@ARGV\n" if $Debug;
GetOptions (
$self->{opt},
"wave=s",
"timeout=i",
"cov!",
"gui!",
"dryrun!",
"verbose=i",
"sdf=s",
"seed=s",
"debug=s@",
"define=s@",
"plusarg=s@",
"test=s",
# using package pod
"help|?" => sub {pod2usage( -input => pod_where({-inc=>1},__PACKAGE__), -verbose=>1, -exitval=>1)},
"man" => sub {pod2usage( -input => pod_where({-inc=>1},__PACKAGE__), -verbose=>2, -exitval=>1)},
# do not use variables in _parse, so can be defined externally
"clean" => \&_clean,
# only anonymous sub can see variables in _parse
"<>" => sub {push @{$self->{_unparsed}},shift},
) or croak "$error:Bad usage, Try $0 --help or $0 --man\n";
croak "$error: testcase not found!" unless $self->{opt}->{test};
$self->testid();
if ($self->{opt}->{seed} eq "random") {
$self->{opt}->{seed} = int(rand 1<<32);
}
}
sub _clean {
print "$info: Clean Redundant Files!\n";
croak "$error: auxclean.mk do not exist!" if (! -e "./auxclean.mk");
system "make -f auxclean.mk";
exit 1;
}
# display class member, for debugging
sub display {
my $self = shift;
print "phase => $self->{phase}\n";
print "bfm => $self->{bfm}\n";
print "simulator => $self->{simulator}\n";
print "opt => {\n";
while (my ($opts,$value) = each %{$self->{opt}}) {
if (defined $value ) {
if ( ref $value && ref($value) eq 'ARRAY' ) {
print " $opts => @$value \n";
}
else {
print " $opts => $value \n";
}
}
else {
print " $opts => undef \n";
}
}
print "}\n";
print "defines => $self->{defines}\n";
print "plusargs => $self->{plusargs}\n";
print "uservs => $self->{uservs}\n";
print "testid => $self->{testid}\n";
print "_unparsed => (\n";
print "@{$self->{_unparsed}}\n";
print ")\n";
print "_configed => (\n";
print "@{$self->{_configed}}\n";
print ")\n";
}
## Setting Defines according to supported options
sub defines {
my $self = shift;
my @defines;
push @defines, "+define+OPTDEF_PHASE_\U$self->{phase}\E";
push @defines, "+define+OPTDEF_BFM_\U$self->{bfm}\E";
push @defines, "+define+OPTDEF_SIMULATOR_\U$self->{simulator}\E";
push @defines, "+define+OPTDEF_COV" if $self->{opt}->{cov};
push @defines, "+define+OPTDEF_SDF_\U$self->{opt}->{sdf}\E";
## --debug a,b,c --debug d; split and assemble to (a b c d)
foreach my $dbg (@{$self->{opt}->{debug}}) {
my @dbgsplit = split(/,/,$dbg);
foreach (@dbgsplit) {
push @defines, "+define+OPTDEF_DEBUG_$_";
}
}
## --define a,b,c --define d; split and assemble to (a b c d)
foreach my $def (@{$self->{opt}->{define}}) {
my @defsplit = split(/,/,$def);
foreach (@defsplit) {
push @defines, "+define+$_";
}
}
# searching define files in testcase
my $tcdef = "../$self->{bfm}_tests/$self->{opt}->{test}/defines.v";
if ( -e $tcdef ) {
my $fh = IO::File->new("<$tcdef");
while (<$fh>) {
chomp;
push @defines,"$_" if (/\+define\+/);
}
$fh->close();
}
$self->{defines} = join(' ',@defines);
print "$debug: defines=$self->{defines}\n" if $Debug;
return $self->{defines};
}
## Setting Plusargs according to supported options
sub plusargs {
my $self = shift;
my @plusargs;
push @plusargs, "+OPTARG_WAVE=\U$self->{opt}->{wave}\E";
push @plusargs, "+OPTARG_TIMEOUT=\U$self->{opt}->{timeout}\E";
push @plusargs, "+OPTARG_VERBOSE=\U$self->{opt}->{verbose}\E";
## --plusarg a,b,c --plusarg d; split and assemble to (a b c d)
foreach my $arg (@{$self->{opt}->{plusarg}}) {
my @argsplit = split(/,/,$arg);
foreach (@argsplit) {
push @plusargs, "+$_";
}
}
# searching plusargs files in testcase
my $tcplusarg = "../$self->{bfm}_tests/$self->{opt}->{test}/plusargs.v";
if ( -e $tcplusarg ) {
my $fh = IO::File->new("<$tcplusarg");
while (<$fh>) {
chomp;
die "$error: define in $tcplusarg!" if (/\+define\+/);
push @plusargs,"$_" if (/\+\w+/);
}
$fh->close();
}
$self->{plusargs} = join(' ',@plusargs);
print "$debug: plusargs=$self->{plusargs}\n" if $Debug;
return $self->{plusargs};
}
## Searching user.v and related verilog files
sub uservs {
my $self = shift;
my $uservs = '';
my $tchdl = "../$self->{bfm}_tests/$self->{opt}->{test}/hdl";
if ( -d $tchdl ) {
if ( -e "$tchdl/user.v" ) {
$uservs = "$tchdl/user.v ";
}
else {
$uservs = "../tb/user.v ";
}
$uservs .= "+incdir+$tchdl -y $tchdl";
}
$self->{uservs} = $uservs;
print "$debug: uservs=$self->{uservs}\n" if $Debug;
return $self->{uservs};
}
sub testid {
my $self = shift;
my $testdir = "$self->{bfm}_tests";
my $seed = $self->{opt}->{seed};
my $cleancase = $self->{opt}->{test};
$cleancase =~ s!^.*/$testdir/!!;
$cleancase =~ s!/!.!;
$cleancase = "$testdir.$cleancase";
$cleancase = "$cleancase-$seed" if $seed;
$self->{testid}=$cleancase;
print "$debug: testid=$self->{testid}\n" if $Debug;
return $self->{testid};
}
sub start {
my $self=shift;
my $sth=shift;
my $fh = IO::File->new(">./run_test");
print $fh $sth;
chmod 0750, "./run_test";
exec "./run_test" unless $self->{opt}->{dryrun};
}
1;
__END__
#-----------------------------------------------------------
=pod
=head1 NAME
Testbench::Run -- Parse and Update the Run options from
1) default value
2) configuration file
3) command line
=head1 SYNOPSIS
The supported options as follows:
--version Displays program version and exit
--help/? Show brief help and exit
--man Show full documentation and exit
--clean Clean redundant files
--cov/nocover coverage collection enable or disable
--gui/nogui open gui and simulator debug mode
--dryrun do not really run the simulation
--timeout n timeout enable or disable, default disable
--wave [no,fsdb,vpd,evcd] wave form dump option
--timeout [n] timeout enable or disable (0)
--verbose [n] message verbosity
--debug [string] add +define+OPTIONS_DEBUG_STRING ,can be multiple value
--sdf [min,max,typ] SDF with delay models
--seed [n,random] assigned interger as seed or randomize
--define [String] add +define+String ,can be multiple value
--plusarg [String] add +String ,can be multiple value
--test assign testname
=head1 Usage
use Testbench::Run;
my $run=new Testbench::Run(bfm=>'vmm');
@ARGV=$run->getopt();
print "$run->{opt}->{test}";
print "$run->{opt}->{bfm}";
=head1 DESCRIPTION
Testbench::Run is used for dealing with normal run options for soc verification
=over 4
=item $run = new Testbench::Run(params)
Testbench::Run Constructor support following parameters
<> phase => rtl/gate rtl level simulation or gate level simulation
<> bfm => cpm/scm/vmm complete processor or systemc or vmm BFM model
<> simulator => vcs/nc synopsys VCS or cadence IUS
=item $run->display();
Display the class members;
=item $run->getopt();
Return array with unparsed options, parse any supported options from following source
1) default value
2) configuration file
3) command line
=item $run->start(params);
Generate run_test. Once not in dryrun mode, execute the run_test script right now.
params is the variable containing the text describing what to run.
=item $run->{opt}->{...};
Now get the option value, normally using it after $run->getopt();
=item $run->{defines};
Now get the defines value, normally using it after $run->getopt();
for instance: +define+OPTDEF_BFM_CPM +define+OPTDEF_DEBUG_UART;
=item $run->{plusargs};
Now get the plusargs value, normally using it after $run->getopt();
for instance: +OPTARG_TIMEOUT=1000 +OPTARG_WAVE=FSDB;
=item $run->{uservs};
Now get the user's verilog files, normally using it after $run->getopt();
for instance:
../tests/c_example/hdl/user.v
+incdir+../tests/c_example/hdl
-y ../tests/c_example/hdl
=back
=head1 AUTHOR
-I<Michael.Kang >
Copyright (c) 2011 Verisilicon Verification Group
=head1 SEE ALSO
-I<run, set_env, rtl_cpm_vcs.opt, auxclean.mk, xxxlib.mk, xxxtest.mk>
-I<cpm_bench.vc, scm_bench.vc, vmm_bench.vc, netlist.vc, asic_top.vc, asic_xmr.vc>
=cut