Testbench::Run

来源:互联网 发布:unity3d性能分析器 编辑:程序博客网 时间:2024/05/21 09:59
 ### 为迅速建立soc 验证环境写的perl package ###

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