#!/usr/local/bin/perl -w
#====================================================================================
# Design unit  : SEUs simulation Tool (SST)
#
# File name    : SPT_check_and_upset.pl
#
# Purpose      : - This script sets up the evaluation and upset of several signals of 
#                  a loaded design (in modelsim) at random time values.
#                - The selection of instances, signals and time values it is done via 
#                  some command line interface options. Type SST_upset_generator.pl 
#                  -help for more information about this topic.
#
# Authors      : Daniel González Gutiérrez TEC-EDM
#
# Contact      : mailto:microelectronics@estec.esa.int
#                http://www.estec.esa.int/microelectronics
#
# Copyright (C): European Space Agency (ESA) 2004. No part may be reproduced
#                in any form without the prior written permission of ESA.
#
# Disclaimer   : All information is provided "as is", there is no warranty that
#                the information is correct or suitable for any purpose,
#                neither implicit nor explicit. This information does not
#                necessarily reflect the policy of the European Space Agency.
#====================================================================================
#====================================================================================
# Revision control
# 
###Version 1.0	31/10/03 --DGG
#
###Revision 1.1	22/03/04 --DGG
#
###Revision 1.2	27/07/04 --DGG 
#  - Added the hash %instance_path_names to keep the full names of every instance and
#    use them when writing the forces commands to SST.do 
#  - Hash %total_i_and_s renamed to %instance_valid_lines
#  - Subroutine td_hiearchy_enable renamed to update_instance_hashes, the old feature
#    of selecting instances hierarchycally if td_hierarchy_on = 1, is now done
#    in the filtering instances option.
#  - Some other minor changes to make it compliant with the above changes and the 
#    ones done in SST_list_instances.pl
###Revision 1.3 25/11/04 --DGG
#  - Intermediate variable $wire_to_file added to avoid bug present when upsetting the  
#    same wire at differente times (only if the wire name contains at least one '\')
#  - Added the number of wires that have been upset, in the sst.do header
#====================================================================================

#----------------------------------------------------------------------------
# Imported modules
#---------------------------------------------------------------------------- 
use File::Spec;        # This module allows us to use some methods to avoid  
                       # different directory naming convention in Windows 
                       # and Unix.                 
use File::Basename;
use lib "./SST/control_files"; 
use SST_perl_package;  # Several refining parameters and subroutines are declared 
                       # inside this package.
use POSIX;             # Import ceil and floor functions                       

#----------------------------------------------------------------------------
# My variables
#----------------------------------------------------------------------------  

my ($ai_line);
my ($default_instances_switch) = 'read';
my ($default_number_switch) = 1;
my ($force_type);
my ($in_line);
my ($input_line);
my ($instance_file_name);
my ($instance_full_file_name);
my ($instances_option);
my ($instances_switch);
my ($manual_option);
my ($moment);
my ($new_time);
my ($number_of_instances);
my ($number_of_forces) = 0;
my ($number_option);
my ($number_switch);
my ($option);
my ($print_instance) = 0;
my ($running_time) = 0;
my ($run_step) = 0;
my ($start_unit);
my ($start_value);
my ($time_1);
my ($time_2);
my ($time_option);
my ($time_unit);
my ($time_value);
my ($unit_1);
my ($unit_2);
my ($window_length);
my ($window_unit);
my ($wire);
my ($wire_name);
my ($wire_to_file);
my (@all_time_values);
my (@fields);
my (@force_fields);
my (@selected_instances);
my (@wires);
my (%all_time_values_hash);
my (%check_times);
my (%unit2number) = (
    "fs" => -15,
    "ps" => -12,
    "ns" => -9,
    "us" => -6,
    "ms" => -3,
    "s"  =>  1,
  );
my(%number2unit) = reverse %unit2number;

my(%instance_valid_lines); # This hash is filled in the subroutine td_hierarchy_crawler and is used 
                           # not only in the main program but in the subroutine select_inputs.
                           # Its keys are the file names of the selected instances and its values are
                           # arrays that contain the valid line numbers of each $wire_files_ext file.
                           # Valid lines are those that hold inputs/signals -all the lines but the 
                           # header 
                    
my(%instance_path_names); # This hash is filled in the subroutine td_hierarchy_crawler and is used 
                          # not only in the main program but in the subroutine select_inputs.
                          # Its keys are the file names of the selected instances and its values are
                          # the instances full names (path names)                    
                     
# The Wire files are stored in  SST/wire_files
my($wire_f_dir) = File::Spec->catdir('./SST', 'wire_files');

# The rest of the files are located at: SST/control_files
my($control_f_dir) = File::Spec->catdir('./SST', 'control_files');
my($all_instances_dat) = File::Spec->catfile($control_f_dir, "all_instances.dat");
my($all_instances_tmp) = File::Spec->catfile($control_f_dir, "all_instances.tmp");
my($all_instances_bak) = File::Spec->catfile($control_f_dir, "all_instances.bak");
my($sst_do) = File::Spec->catfile($control_f_dir, "sst.do");

#----------------------------------------------------------------------------
# Script main body
#---------------------------------------------------------------------------- 

#----------------------------------------------------------------------------
# Command line options parsing.
#----------------------------------------------------------------------------                  

$input_line = "@ARGV[0..$#ARGV]"; # keep here the input command line
@fields = split(/ *- */,$input_line);
print ("\nRunning SST_upset_generator.pl...\n"); 
if (@fields > 0) {
  foreach $option (@fields) {
    if ($option eq '') {    # skip null character generated before the first option
      next;                 # when splitting
    }  
    #----------------------
    # -m (manual) option 
    #----------------------
    elsif ($option =~ /^m/i) {
      $manual_option = 1;
      last;
    }
    #----------------------
    # -i (intances) option 
    #----------------------
    elsif ($option =~ /^i/i) {
      $instances_option = 1;
      ($instances_switch) = ($option =~ /^[iI] *(.*)/);
    }
    #----------------------
    # -n (number) option 
    #----------------------
    elsif ($option =~ /^n/i) {
      $number_option = 1;
      ($number_switch) = ($option =~ /^[nN] *(.*)/);
    }
    #----------------------
    # -t (time) option    
    #----------------------
    elsif ($option =~ /^t/i) {
      $time_option = 1;
      ($time_1,$unit_1,$time_2,$unit_2) = ($option =~ /^[tT] *[(]?(\d+(?:\.\d+)?) *([A-Za-z]+)?[- ]*(\d+(?:\.\d+)?)?[ )]*(\w+)?/);
                  
      # Time limits extraction 
      if ( (defined $time_1) && (defined $time_2) ) {     # Starting point and simulation
        $start_value   = $time_1;                         # window specified
        $window_length = $time_2;
      }
      elsif ( (defined $time_1) && (!defined $time_2) ) { # total simulation length 
        $start_value   = 0;
        $window_length = $time_1;
      }
      else {                                              # empty imput -> assumed time 0 
        $start_value   = 0;                               
        $window_length = 0;                               
      }  
            
      # Time unit extraction
      if ( (defined $unit_1) && (defined $unit_2)) {      # both units defined
        $window_unit = $unit_2;
        $start_unit  = $unit_1;  
      }
      elsif ( (!defined $unit_1) && (defined $unit_2)) {  # one unit at the end
        $window_unit = $unit_2;
        $start_unit  = $unit_2;
      }
      elsif ( (defined $unit_1) && (!defined $unit_2)) {  # one unit at the beginnig 
        if (!defined $time_2) {  # time interval
          $window_unit = $unit_1;
          $start_unit  = $reference_unit;
        }
        else {                   # unit specified for the start_value
          $window_unit = $reference_unit;
          $start_unit  = $unit_1;
        }
      }
      else {                                               # No units (use default value)
        $window_unit = $reference_unit;
        $start_unit  = $reference_unit;
      }
    }
    #----------------------
    # -h (help) option    
    #----------------------
    elsif ($option =~ /^h/i) {
      print ("\n ## SST_upset_generator options and switches:\n\n");                                             
      print (" -m    Manual option. The script just reads all_instances.dat and the $wire_files_ext files\n");
      print ("       (which have been edited by the user) and prepares the evaluation of the\n");
      print ("       selected signals at the specified time values.\n");
      print ("       This option excludes the rest.\n\n");
      print (" -i    Instances option. This option is used to set the amount of instances to\n");
      print ("       be selected in all_instances.dat. It has 3 switches:\n\n");
      print ("       r(ead)      -> The user has selected the instances by manually editing\n");
      print ("                      the file all_instances.dat. The script will read the file\n");
      print ("                      as an input\n"); 
      print ("       f(ilter)    -> The selection of instances will be done by filtering their\n");
      print ("                      names using patterns introduced via the command line\n");
      print ("                      interface. The patterns should be perl like regular.\n");
      print ("                      expressions. Valid input format:\n");
      print ("                          -i( )filter (#) pattern1 (pattern2) (pattern3)...\n");
      print ("                          Where # is the number of filtered instances to be\n");
      print ("                          selected -if this number is not specified all the \n");
      print ("                          filtered instances will be selected-.\n");
      print ("                          Please note that there must be at least one space\n");
      print ("                          character between each pair of terms of the above\n");
      print ("                          mentioned expresion (except between 'i' and 'filter')\n");
      print ("       #           -> where # is the number of instances to be selected.\n\n");
      print (" -n    Number option. This options is used to set the amount of signals that will\n");
      print ("       be evaluated. It has 3 switches:\n\n");
      print ("       n(ot_fixed) -> The number of inputs and signals is calculated using a\n");
      print ("                      parameter defined in SST_config.tcl\n");
      print ("       f(ilter)    -> The selection of signals will be done by filtering their\n");
      print ("                      names using patterns introduced via the command line interface\n");
      print ("                      The patterns should be perl like regular expressions.\n");
      print ("                      Valid input format:\n");
      print ("                          -n( )filter (#) pattern1 (pattern2) (pattern3)...\n");
      print ("                          Where # is the number of filtered signals to be\n");
      print ("                          selected -if this number is not specified all the\n");
      print ("                          filtered signals will be selected-.\n");
      print ("                          Please note that there must be at least one space\n");
      print ("                          character between each pair of terms of the above\n");
      print ("                          mentioned expresion (except between 'i' and 'filter')\n");
      print ("       #           -> where # is the number of signals to be evaluated.\n\n");
      print (" -t    Time option. This option is used to schedule the evaluation process. Given a\n");
      print ("       starting time value (zero if omitted) and a simulation window,the script\n");
      print ("       will randomly set a time value between the limits specified,for each\n");
      print ("       forced signal. Follow the starting time value and the simulation window\n");
      print ("       by a time unit (ps,ns,us,ms).Valid input formats:\n\n"); 
      print ("                   -> -t window_length time_unit\n");
      print ("                   -> -t (starting_value window_length) time unit\n");
      print ("                   -> -t (starting_value time_unit window_length time_unit)\n\n");
      print (" -h    Help option. Displays this help message\n\n");
      print (" ## Examples:\n\n");
      print ("      - Example 1:\n");
      print ("       SST_upset_generator  -ifilter _reg$ -nfilter 3 q0$ -t200ns\n\n");
      print ("       From all the instances ending in _reg we will randomly select 3 signals\n");
      print ("       that end in q0, at a time between 0 and 200ns\n\n");  
      print ("      - Example 2:\n");
      print ("       SST_upset_generator -ir -n2 -t 1ms 100ns\n\n");
      print ("       2 inputs/signals chosen from any instance randomly selected amongst those\n");
      print ("       set by the user in all_instances.dat at time values between 1ms and\n");
      print ("       1.0001 ms");
      exit
    }
    #----------------------
    # invalid option
    #----------------------
    else {
      die ("### Error: invalid input option: $option. Type SST_upset_generator -h for help\n");
    } 
  }#foreach option
}
else {                # If no options have been selected at the command line 
  $manual_option = 1; # interface default to 'manual'. 
}                     # Change this line if a different default is required.

            
#---------------------------------------------------------------------------
# Act on all_intances.dat and the $wire_files_ext files according to the 
# options selected via the command line interface
#---------------------------------------------------------------------------      

if (!$manual_option) {
  # Set default values for those ommitted options
  if (!$instances_option || !defined($instances_switch)) {
    $instances_switch = $default_instances_switch; #defined in SPT.pm 
  }   
  if (!$number_option || !defined($number_switch)) {
    $number_switch = $default_number_switch;       #defined in SPT.pm 
  }
  if (!$time_option) {
    $start_value         = 0; #default time 
    $window_length       = 0;
    $start_unit          = $reference_unit;
    $window_unit         = $reference_unit;  
  }
  # select instances and inputs/signals according to what was 
  # introduced in the command line 
  $number_of_instances = select_instances ($instances_switch);
  select_inputs ($number_switch,$start_value,$window_length,$start_unit,$window_unit);
}
else {
  print (" Manual forces option (-m):\n");
  print (" Reading all_instances.dat...");
  $number_of_instances = update_instance_hashes ();
  print ("done\n");
}  
    
#----------------------------------------------------------------------------
# Extract the information related to those wires we would like to force
#----------------------------------------------------------------------------                  

print (" Setting up forces in the following instances:\n"); 

@selected_instances = keys (%instance_valid_lines);

foreach $instance_file_name (@selected_instances) {
  $instance_full_file_name = File::Spec->catfile($wire_f_dir, "$instance_file_name\.$wire_files_ext"); 
  open (INSTANCE_FILE,"$instance_full_file_name");
  $print_instance = 1;         # We use this valiable to avoid printing the instance name to
                               # STDOT once per input/signal forced in it. 
    
  $in_line = <INSTANCE_FILE>;  # getting rid of the header
  $in_line = <INSTANCE_FILE>;  #
  while ($in_line = <INSTANCE_FILE>){
    @force_fields = split(' ',$in_line);
    #----------------------------------------------------------------------
    # $force_fields[0] = Force (Yes/No)
    # $force_fields[1] = Name of the input/signal
    # $force_fields[2..$#force_fields] = @times when we want to force the input/signal
    #----------------------------------------------------------------------
    if ($force_fields[0]=~ /^y/i) { #if we want to force this input/signal
      $wire_name = "$instance_path_names{$instance_file_name}/$force_fields[1]";
      for ($i = 2; $i <= $#force_fields; $i++) {
        ($time_value,$time_unit) = ($force_fields[$i] =~ /(\d+(?:\.\d+)?)(\w+)/);
        # set the time in reference units
        $new_time = $time_value * (10 ** ($unit2number{$time_unit} - $unit2number{$reference_unit}));
        
        # Create and array (%check_times) which keys are the wire names selected
        # to be upset, and which values are arrays containing the times when 
        # each wire is going to be forced.
        push (@{ $check_times{$wire_name} },$new_time);
        ++$number_of_forces;
      }
      if ($print_instance) {
        print ("     $instance_path_names{$instance_file_name}\n");
        $print_instance = 0;
      }  
    }     
  }
  close (INSTANCE_FILE);
} #foreach  

   
#----------------------------------------------------------------------------
# Now that we have chosen the time values when we want the different inputs/
# signals to be checked and upset, we have to sort them in time, to shedule 
# the process.
#----------------------------------------------------------------------------   

@wires = keys(%check_times);
foreach $wire (@wires) {
  foreach $time_value (@{$check_times{$wire}}) {
    $all_time_values_hash{$time_value} = "";
  }  
}

# We have used the hash to avoid replicated values in all_time_values (which would
# have been the case if we had used an array and a push operation). Now we get
# rid of the hash since we are only interested in its keys.
@all_time_values = keys (%all_time_values_hash);

# set force type 
if (rand(10) > $seu_prob) {
  $force_type = 'freeze';
}
else {
  $force_type = 'deposit';
} 

open (SST_DO, ">$sst_do") or die "can't open spt.do: $!";
print (SST_DO "#Macro generated by SST_upset_generator.pl\n");
print (SST_DO "#$number_of_forces wires forced\n\n");
print (SST_DO "onbreak {abort 1}\n"); 

# follow the schedule and 'upset' wires.
foreach $time_value (sort {$a <=> $b} @all_time_values) {
  $run_step = $time_value - $running_time ;
  print (SST_DO "\nrun $run_step $reference_unit\n");
  # In case that the same time value applies to several wires
  foreach $wire_name (@wires) {
    foreach $moment (@ { $check_times{$wire_name} }) {
      if ($moment =~ /$time_value/) {
        print (SST_DO "# define signal path\n");
        $wire_to_file = $wire_name; 
        $wire_to_file =~ s/\\/\\\\/g; #replace all the '\' with '\\'
        print (SST_DO "set wire $wire_to_file\n");
        print (SST_DO "# examine current value\n");
        print (SST_DO "set wire_checked_val [exa \$wire]\n");
        print (SST_DO "# flip one bit of the wire\n");
        print (SST_DO "set wire_upset_val [exec perl -S SST_bit_flip.pl \$wire_checked_val]\n");
        print (SST_DO "if {\$wire_upset_val != \"undefined\"} {\n");
        print (SST_DO "  echo Forcing \$wire to \$wire_upset_val \@ $moment$reference_unit\n");
        print (SST_DO "  force -$force_type \$wire \$wire_upset_val\n");
        print (SST_DO "} else { \n");
        print (SST_DO "  echo Unable to upset \$wire. Undef value.\n");
        print (SST_DO "}\n");
      }
    }
  }  
  $running_time += $run_step;
}

print (SST_DO "run -all\n");
print (SST_DO "status"); 
close (SST_DO);

print ("\n --Total number of instances subject to have their wires forced: $number_of_instances\n");
print (" --Total number of wires forced: $number_of_forces\n\n");

#----------------------------------------------------------------------------
# subroutines from here down
#---------------------------------------------------------------------------- 

#----------------------------------------------------------------------------
# update_instance_hashes
# - In this subroutine we fill the following hashes:
#   * %instance_valid_lines -> Its keys are the file names of the selected  
#     instances and its values are arrays that contain the valid line numbers 
#     of each $wire_files_ext file. Valid lines are those that hold 
#     inputs/signals -all the lines but the header- 
#
#   * %instance_path_names -> Its keys are the file names of the selected 
#     instances and its values are the instances full names (path names)  
# Note that these hashes are defined as global variables in main.
# - This subroutine also returns the total number of instances selected in 
# all_instances.dat
#----------------------------------------------------------------------------
sub update_instance_hashes {
  my($ai_line);
  my($number_of_instances) = 0;
  my(@ai_fields);
    
  open (AI, "< $all_instances_dat") or die "can't open 'all_instances.dat': $!";
  
  #remove header
  $ai_line = <AI>;          
  $ai_line = <AI>;          
     
  while ($ai_line = <AI> ) {
    @ai_fields = split (' ',$ai_line);
    if ($ai_fields[0] =~ /^ *Y(es)?/i) {
      ++$number_of_instances;
      # Fill %instance_valid_lines and %instance_path_names
      $instance_valid_lines{$ai_fields[1]} = [3..($ai_fields[2]+2)];
      $instance_path_names{$ai_fields[1]} = $ai_fields[3];
    }
  }  
  close(AI);
  return $number_of_instances;
}  

#----------------------------------------------------------------------------
# select_instances
# This subroutine selects which instances could have inputs or signals checked
# and upset. The selection of instances is determined by an imput parameter 
# introduced through the command line, as a switch for the -i option.
#----------------------------------------------------------------------------
sub select_instances {
  my($instances_switch) = @_;
  my($ai_line);
  my($buffer);
  my($filtered_instances) = 0;
  my($index) = 0;
  my($instance_name);
  my($instances_to_select) = 0;
  my($line_nr) = 0;
  my($line_counter) = 0;
  my($number_not_specified) = 0;
  my($number_of_instances) = 0;
  my($pattern);
  my(@ai_fields);
  my(@all_lines);
  my(@filter_patterns);
  my(@i_fields);
  my(%selected_lines);
  
  #----------------------------------------------------------------------------
  # Parse the switch
  #----------------------------------------------------------------------------
  #--------------------
  # 'read_file' switch
  #--------------------
  if ($instances_switch =~ /^r/i) {        # Do nothing to all_instances.dat. 
    $instances_to_select = 0;              # The user had edited manually this file.
  }
  # Other valid switches
  elsif ( ($instances_switch =~ /^\d+/) || 
          ($instances_switch =~ /^f/i) ) {
    
    # Count the number of lines of all_instances.dat. The number of lines of  
    # this file is equal to the number of instances our design has.
    open(AI,"< $all_instances_dat") or die "Can't open 'all_instances.dat': $!";
    while (sysread AI, $buffer, 4096) {
      $number_of_instances += ($buffer =~ tr/\n//);
    }
    close AI;
    
    # Get the number of instances to be selected
    #--------------------
    # Number switch
    #--------------------
    if ($instances_switch =~ /^(\d+)/) {  
      # The nuber of instances to be selected has been introduced via
      # the command line interface.
      $instances_to_select = $1;             
      # The instances will be selected from all the instaces available
      # (no filtering)
      @all_lines = (3..$number_of_instances);  
    }
    #--------------------
    # 'filter'switch
    #--------------------
    else {
      # get patterns
      @i_fields = split (' ',$instances_switch);
      # If the number has been specified
      if ($i_fields[1] =~ /^(\d+)/) {
        $instances_to_select = $1; 
        @filter_patterns = @i_fields[2..$#i_fields];
      }
      else {
        @filter_patterns = @i_fields[1..$#i_fields];
        $number_not_specified = 1;
      }  
      # Detect which lines of all_instances.dat contain the 
      # desired patterns. Add their line numbers to @all_lines
      open (AI, "< $all_instances_dat") or die "can't open 'all_instances.dat': $!";      
      
      #remove header
      $ai_line = <AI>;
      $ai_line = <AI>;
      $line_counter = 2;
      while ($ai_line = <AI>) {
        ++$line_counter;
        @ai_fields = split(' ',$ai_line);
        foreach $pattern (@filter_patterns) {
          if ($td_hierarchy_on) { #global variable set in seus.pm
            if ($ai_fields[3] =~/$pattern/) {
	      push (@all_lines, $line_counter);
	      ++$filtered_instances;
            }
          }  
          else {
            $ai_fields[3] =~ /(\/.+)?\/(.+)/;
            $instance_name = $2; 
            if ($instance_name =~/$pattern/) {
              push (@all_lines, $line_counter);
              ++$filtered_instances;
            }
          }  
        }       
      }  
      
      # If no signals matched the pattern -> Error
      if ($filtered_instances == 0) {
        die ("\n### Error: no instances matched the specified pattern/s\n");
      }  
      
      # If the number of instances to be selected has not been specified
      # via the command line, or despite the fact that is has been specified, 
      # the number of filtered instances is lower than it, assume that we want 
      # to select all the instances that match the patterns
         
      if ( ($number_not_specified) || 
           ($filtered_instances < $instances_to_select) ){
        $instances_to_select = $filtered_instances;    
      } 
    }
  }
  # Not valid switch
  else {
    print ("### Error: invalid switch for the -i option. The only valid\n");
    die   ("    switches are: 'read','filter' and a number.\n");               
  }
  
  #----------------------------------------------------------------------------
  # Process the switch
  #----------------------------------------------------------------------------
  
  if ($instances_to_select > 0) {
    if ($instances_to_select > ($number_of_instances-2)) {
        die ("\n### Error: The design has less instances than the number specified\n");
    }  
    print (" Selecting instances...");
    # Pick $instances_to_select lines from @all_lines in a random manner.
    # The instances found in those lines will be the ones selected.
    # All_lines will keep those line numbers that have not been selected yet.
    for ($i = 0; $i < $instances_to_select; $i++) {  
      $index = POSIX::floor(rand @all_lines);
      $selected_lines{splice (@all_lines,$index,1)} = "";
    }
        
    # Open the file all_instances.dat to write a 'Yes' in the first column of those 
    # lines that have been previously selected.  
    # Use a temporary file in the process of updating all_instances.dat 
  
    open (AI, "< $all_instances_dat") or die "can't open 'all_instances.dat': $!";
    open (AI_TMP, "> $all_instances_tmp") or die "can't open 'all_instances.tmp': $!";
  
    $line_counter = 0;
    while ($ai_line = <AI>) {
      ++$line_counter;
      if ($ai_line =~ /^ *Y(es)?/i) {  # reset in case that we are working with an    
        $ai_line =~ s/Yes/No /i;       # old all_instances.dat
      }
      foreach $line_nr (keys (%selected_lines)) {
        next if ($line_counter != $line_nr); 
        $ai_line =~ s/No /Yes/i;
        delete $selected_lines{$line_counter}; #remove the processed line from the list
      }
      print (AI_TMP $ai_line);
    }
    print ("done\n");
    close(AI);
    close(AI_TMP);
    if (!rename ($all_instances_dat, $all_instances_bak)) {
      print ("\n### Error: Unable to rename the file $all_instances_dat\n");
      die   ("      The file $all_instances_bak might be in use by another aplication");
    }
    if (!rename ($all_instances_tmp, $all_instances_dat)) {
          print ("\n### Error: Unable to rename the file $all_instances_tmp\n");
          die   ("      The file $all_instances_dat might be in use by another aplication");
    }
  }
  
  print (" Reading all_instances.dat...");
  # Now number_of_instances will hold the value of those instances that have been selected
  # (not all the instances)
  $number_of_instances = update_instance_hashes (); 
  print ("done\n");
  return ($number_of_instances);
}  

#----------------------------------------------------------------------------
# set_rand_time
# This subroutine returns a random time value (and its correspondent time 
# unit) between start_value and start_value + window_length. If no valid units 
# have been introduced via the commnad line interface it returns undef.
# If start_unit or window unit are smaller that the reference unit it returns 
# 'not_valid'. It returns -1 if the window length is not valid.
#----------------------------------------------------------------------------
sub set_rand_time {
  my($start_value,$window_length,$start_unit,$window_unit) = @_;
  my($index) = 0;
  my($time_value) = 0;
  my($time_unit) = $reference_unit;
    
  if ( (!defined $unit2number{$window_unit}) || (!defined $unit2number{$start_unit}) ){ # Unit not valid
    return;
  }  
    
  else {
    # Set start_value and window_length in reference units.
    $start_value   = $start_value * (10 ** ($unit2number{$start_unit} - $unit2number{$reference_unit}));
    $window_length = $window_length * (10 ** ($unit2number{$window_unit}-$unit2number{$reference_unit}));
  
    if ( ( ($start_value > 0) && ($start_value < 1) ) ||      # If start_unit or window_unit are  
         ( ($window_length > 0) && ($window_length < 1) ) ) { # smaller that reference_unit -> error
    $time_unit = 'not_valid';
    return ($time_value,$time_unit);
    }
    elsif ( ($window_length > 0) && ($window_length < $reference_step)) { # not valid window length
      $time_value = -1;
      return ($time_value,$time_unit);
    }
    else {
      $time_value = $start_value + $reference_step * (int(rand ($window_length/$reference_step)));
      $index = $unit2number{$reference_unit};
      # unit conversion
      while ($time_value > 999) {
        $time_value = $time_value/1000;
        $index = $index + 3;
      }
    
      $time_unit = $number2unit{$index};
      return ($time_value,$time_unit);
    }
  }
}

#----------------------------------------------------------------------------
# select_inputs
# This subroutine selects which inputs/signals will be checked and upset and 
# when. The selection of inputs/signals is determined by 3 parameters introduced
# via the command line interface, as switches for the -i, -n and -t options.
#----------------------------------------------------------------------------
sub select_inputs {
  my($number_switch,$start_value,$window_length,$start_unit,$window_unit) = @_;
  my($ai_line);
  my($fill_hash);
  my($filtered_signals)=0;
  my($in_file);
  my($index_i_and_s) = 0;
  my($index_instance) = 0;
  my($instance_file_name);
  my($line_counter) = 0;
  my($line_nr);
  my($number_of_lines) = 0;
  my($number_not_specified) = 0;
  my($pattern);
  my($signals_to_upset) = 0;
  my($time_unit);
  my($time_value);
    
  my(@ai_fields);
  my(@filtered_instances);
  my(@filter_patterns);
  my(@n_fields);
  my(@in_fields);
  my(@times);
  my(@selected_instances);
    
  my(%selected_i_and_s);
   
  print (" Selecting wires...");
   
  # Create a hash (%selected_i_and_s) which keys are the file names of the selected  
  # instances and which values are arrays that contain the selected line numbers of 
  # each $wire_files_ext file. Initialize each array to ()
  
  @selected_instances = keys (%instance_valid_lines);
  foreach $instance_file_name (@selected_instances) {
    $selected_i_and_s{$instance_file_name} = [()];
  }  
  
  #----------------------------------------------------------------------------
  # Parse and process the switch
  #----------------------------------------------------------------------------
  # The way to select a particular input/signal (with independence of the switch)
  # is by randomly picking one of the line numbers of the instance we are working with.
    
  #-------------------- 
  # 'not_fixed' switch
  #--------------------
  if ($number_switch =~ /^n/i) { 
    foreach $instance_file_name (@selected_instances) {
      # Select a percentage of the inputs/signals of this intance. The percentage is
      # specified using $weight_in (defined in SEUS.pm) 
      $number_of_lines = scalar (@{ $instance_valid_lines{$instance_file_name} });
      $signals_to_upset = POSIX::ceil( ($weight_in * ($number_of_lines)/100));
      for ($i = 0; $i < $signals_to_upset; $i++) {
        $index_i_and_s = POSIX::floor(rand @{ $instance_valid_lines{$instance_file_name} });
        push (@{ $selected_i_and_s{$instance_file_name} },${ $instance_valid_lines{$instance_file_name} }[$index_i_and_s]);
      }   
    }
  }
  #--------------------
  # Number switch
  #--------------------
  elsif ($number_switch =~ /^(\d+)/) {
    # The nuber of input/signals to select has been introduced
    # via the command line interface.
    $signals_to_upset = $1;
    for ($i = 0; $i < $signals_to_upset; $i++) {
      if (@selected_instances > 0) {
        $index_instance = POSIX::floor(rand @selected_instances);
        $index_i_and_s = POSIX::floor(rand @{$instance_valid_lines{$selected_instances[$index_instance]}});
        push (@{ $selected_i_and_s{$selected_instances[$index_instance]} }, ${ $instance_valid_lines{$selected_instances[$index_instance]} }[$index_i_and_s]);
      }
    }
  }  
  #--------------------
  # 'filter'switch
  #--------------------
  elsif ($number_switch =~ /^f/i) {
    # get patterns
    @n_fields = split (' ',$number_switch);
    # If the number has been specified
    if ($n_fields[1] =~ /^(\d+)/) {
      $signals_to_upset = $1; 
      @filter_patterns = @n_fields[2..$#n_fields];
    }
    else {
     $number_not_specified = 1;
     @filter_patterns = @n_fields[1..$#n_fields];
    }  
    
    # open each selected instance to apply the filtering 
    foreach $instance_file_name (@selected_instances) {
      # reset %instance_valid_lines
      $instance_valid_lines{$instance_file_name} = [()];
      $in_file = File::Spec->catfile($wire_f_dir, "$instance_file_name\.$wire_files_ext"); 
      open (IN_FILE,"< $in_file") or die "can't open file: $in_file $!";;
        
      #remove header
      $in_line = <IN_FILE>;
      $in_line = <IN_FILE>;
      $line_counter = 2;
      while ($in_line = <IN_FILE>) {
        ++$line_counter;
        @in_fields = split(' ',$in_line);
        foreach $pattern (@filter_patterns) {
          if ($in_fields[1] =~/$pattern/) {
            push (@{ $instance_valid_lines{$instance_file_name} }, $line_counter);
            push (@filtered_instances,$instance_file_name); 
            ++$filtered_signals;
          }  
        }       
      }         
    }  
    
    # If no signals matched the pattern -> Error
    if ($filtered_signals == 0) {
      die ("\n### Error: no signals/wires matched the specified pattern/s\n");
    }  
    
    # If the number of signals to be selected has not been specified
    # via the command line, assume that we want to select all the signals 
    # that match the patterns
    if ( $number_not_specified ){
      %selected_i_and_s = %instance_valid_lines;
    } 
    
    # If the number of signals to be selected has been specified, 
    # select that amount of signals.
    # Prior to the selection of a particular signal we randomly
    # select an instance (amongst the ones available).
    else {
      for ($i = 0; $i < $signals_to_upset; $i++) {
        $index_instance = POSIX::floor(rand @filtered_instances);
        $index_i_and_s = POSIX::floor(rand @{$instance_valid_lines{$filtered_instances[$index_instance]}});
        push (@{ $selected_i_and_s{$filtered_instances[$index_instance]} }, ${ $instance_valid_lines{$filtered_instances[$index_instance]} }[$index_i_and_s]);
      }
    }
  } 
    
  #--------------------
  # Not valid switch
  #--------------------
  else {
    print ("### Error: invalid switch for the -n option. The only valid\n");
    die   ("    switches are:'not_fixed', 'filter' and a number.\n");               
  }
  
  # Open the $wire_files_ext files corresponding to those instances that have been selected and 
  # update those lines which contain inputs/signals that will be 'upset'
  
  foreach $instance_file_name (@selected_instances) {
    $in_file = File::Spec->catfile($wire_f_dir, "$instance_file_name\.$wire_files_ext"); 
    
    # read the $wire_files_ext file line by line 
    open (IN_FILE,"< $in_file") or die "can't open file: $in_file $!";;
    open (IN_FILE_TMP, "> in_file_new.tmp") or die "can't open file in_file_new.tmp $!";;
        
    #remove header
    $in_line = <IN_FILE>; 
    print (IN_FILE_TMP $in_line);
    $in_line = <IN_FILE>; 
    print (IN_FILE_TMP $in_line);
    $line_counter  = 2;
      
    while ($in_line = <IN_FILE> ) {
      @times = ();
      @in_fields = split(' ',$in_line);
      #----------------------------------------------------------------------
      # $in_fields[0] = Force (Yes/No) 
      # $in_fields[1] = Name of the input/signal 
      # $in_fields[2] = Time when we want the signal to be forced
      #----------------------------------------------------------------------
      if ($in_fields[0] =~ /Y(es)?/i) { # reset line if is already selected
        $in_fields[0] = 'No';
        $in_fields[2] = '0ns';
      } 
      
      ++$line_counter;
      foreach $line_nr (@{ $selected_i_and_s{$instance_file_name} }) {
        next if ($line_counter != $line_nr);
        $in_fields[0] = 'Yes';
                   
        # set force time
        ($time_value,$time_unit) = set_rand_time ($start_value,$window_length,$start_unit,$window_unit);
        if ( !defined($time_unit) ){
          die ("\n### Error: The only supported time units are -> fs ps ns us ms s \n");
        } 
        elsif ($time_unit eq 'not_valid' ){
          die ("\n### Error: The time unit specified is smaller than \"$reference_unit\" (reference unit)\n");
        }
        elsif ($time_value < 0 ) {
          die ("\n### Error: The window length specified is smaller than \"$reference_step $reference_unit\" (reference step)\n");
        }
        else {
          push (@times, "$time_value$time_unit");
        } 
      } #foreach
      
      # print default value for unselected inputs/signals
      if (@times == 0 ) {
        printf (IN_FILE_TMP "%-10s %-30s %-8s\n","$in_fields[0]","$in_fields[1]","$in_fields[2]");
      }  
      # print new value for selected inputs/signals 
      else {      
        printf (IN_FILE_TMP "%-10s %-30s %-8s\n","$in_fields[0]","$in_fields[1]","@times");
      }
    } #while 
    close(IN_FILE);
    close(IN_FILE_TMP);
    if (!rename ('in_file_new.tmp', $in_file)) { # Note that we don't make a backup of all the $wire_files_ext files
        print ("\n### Error: Unable to rename the file in_file_new.tmp\n");
        die   ("      The file $in_file might be in use by another aplication");
    }
  }#foreach
print ("done\n");
}  