Monte Carlo Simulation
Input Randomizer

This program takes in parameters: input netlist, number of repetitions and the parameters file.
In this case, the input netlist is input.scs, there are fiverepetitions and the deviations file is called parameters.txt.

Click here to download the MCInputRandomizer.pl file only.
Click here to go to the downloads page.


#!/usr/bin/perl -w
#Written by Hector Hung, Shouri Chatterjee and Professor Peter Kinget
# Copyright (C) 2004 by the authors and Columbia Integrated Systems
# Laboratory.

# There is no warranty or support and we cannot be held liable in any way.
# Everyone is permitted to copy verbatim copies of the code including
# this message.


# MCInputRandomizer - Monte Carlo Simulation Input Randomizer
#                     takes a Spectre netlist as an input, and
#                     generates variations of the netlist



# usage: MCInputRandomizer.pl <input_netlist.scs> <num_outputs> <deviation_file>
#constants:

my $EXECNAME = "MCInputRandomizer.pl";  #filename of the script
my $RESULTPATH = "results/"; 
<-- Modify here to change where results are placed

#####################
#   INITIALIZATION
#####################

#check and parse parameters
if (scalar(@ARGV) != 3) { 
<-- Makes sure there are 3 input parameters: input netlist, total outputs and deviations file
    print "$EXECNAME: Monte Carlo Simulation Input Randomizer takes a Spectre\n";
    print "                      netlist and generates variations of the netlist\n";
    print "usage: $EXECNAME <input_netlist.scs> <num_outputs> <deviation_file>\n";
    exit;
}
$InputNetlist   = $ARGV[0] || die "$EXECNAME: netlist input file not specified";
$TotalOutputs   = $ARGV[1] || die "$EXECNAME: number of runs not specified";
$DeviationsFile = $ARGV[2] || die "$EXECNAME: deviations file not specified";


#remove previous results, and recreate results directory,
#where the output netlists will be stored
`rm -rf $RESULTPATH`;
mkdir("$RESULTPATH", 0755)
    || die "$EXECNAME: cannot create results directory: $!";

#### Initalize Variables
#my @MOSFETsUsed;       #list of model names of the MOSFETs used (eg. "nch" )
@VoltageNodesUsed = (); #list of voltage nodes used (eg. "v(net1)" )
@CurrentNodesUsed = (); #list of current nodes used (eg. "i(v0)" )
$ResDev = 0;
$ResShift = 0;
$CapDev = 0  ;
<-- Add code here if more devices and parameters need to be saved
$CapShift = 0;
$IndDev = 0;
$IndShift = 0;
%MOSFETSpecs = ();
%MOSFETSUsedByInput = ();
&processDeviationsFile;
print "save @VoltageNodesUsed @CurrentNodesUsed\n";




############################
#   GENERATE OUTPUT FILES
############################

#generate 'TotalOutputs' number of randomized netlists
for ($OutputNumber=1; $OutputNumber<=$TotalOutputs; $OutputNumber++) {

    #pick appropriate filename format for output files
    @temp = split(/\//, $InputNetlist);  #split filename and path
    @temp = pop(@temp);
    @temp = split(/\./, $temp[0]); #separate filename from file extension
    $OutputFilename = "$RESULTPATH" . $temp[0] . $OutputNumber . "." . $temp[1];


    #open input netlist and new output file stream
    open (IN, $InputNetlist)
    || die "$EXECNAME: cannot open input file $InputNetlist";
    open (OUT, ">$OutputFilename")
    || die "$EXECNAME: cannot create output file  $OutputFilename";
   
   
   
    while (<IN>) {

    #combine multiple lines if necessary (handle lines at end with '\')
    while (s/(^.*)\\$/$1/) {
        chomp;
        $temp=$_;
        $lineb =<IN>;
        chomp $lineb;
        $_ = $temp . $lineb;
        s/\s+/ /g;
        $_.="\n";
    }


    #CAPACITORS
    #modify capacitor value by incorporating shift and deviation  
    if (m/(^[ ]*c.* c=[ ]*)([0123456789\.]*)(.*$)/i) { 
<-- copy this if/else loop if similar devices are to be added
        $base = $1;
        $value = $2;
        $expo = $3;
        if ($value =~ m/[0-9\.]/) {
        $value=(&RandomGaussian*$CapDev*$value)+$value*(1+$CapShift);
        print OUT "$base$value$expo\n";
        } else { print OUT; }
    }
    #RESISTORS
    #modify resistor value by incorporating shift and deviation
    elsif (m/(^[ ]*r.* r=[ ]*)([0123456789\.]*)(.*$)/i) {
        $base = $1;
        $value = $2;
        $expo = $3;
        if ($value =~ m/[0-9\.]/) {
        $value=(&RandomGaussian*$ResDev*$value)+$value*(1+$ResShift);
        print OUT "$base$value$expo\n";
        } else { print OUT; }
    }
    #INDUCTORS
    #modify inductor value by incorporating shift and deviation
    elsif (m/(^[ ]*l.* l=[ ]*)([0123456789\.]*)(.*$)/i) {
        $base = $1;
        $value = $2;
        $expo = $3;
        if ($value =~ m/[0-9\.]/) {
        $value=(&RandomGaussian*$IndDev*$value)+$value*(1+$IndShift);
        print OUT "$base$value$expo\n";
        } else { print OUT; }
    }
    #MOSFET

    elsif (m/^[ ]*(m\d*) (\(.*\)) (\w*) (.*$)/i) {   <--This line matches the <IN> line  for MOS model and other parameters
        $mos_number = $1; #spectre fet id
        $mos_nodes = $2; #fet's nodes
        $mos_model = $3; #fet's model
        $mos_stats = $4; #fet's parameters
        #mark mosfet used if not already used
        $MOSFETSUsedByInput{$mos_model} = "";
       
        $temp = $MOSFETSpecs{$mos_model};
        #params for this type of fet from the deviations file
        my %mos_params = %$temp;
         #params of this particular instance of the fet
        my %mos_instance_params;

        #fill mos_instance_params with values
        @_= split(/\s+/,$mos_stats);
        foreach my $params (@_) {
        my @temp = split(/\s*=\s*/,$params);
       
        for (my $i=0; $i < scalar(@temp); $i+=2) {
            $mos_instance_params{$temp[$i]} = $temp[$i+1];
        }
        }
        #UPDATE PARAMETERS OF THE MOSFET INSTANCE
        #first, update m value if needed
        if  ($mos_instance_params{"m"} &&  $mos_params{"m"} ) {
        $mos_instance_params{"m"} =
            (&RandomGaussian*$mos_params{"m"}+1)* $mos_instance_params{"m"};
        }

        $w = metricToDecimal($mos_instance_params{"w"});
        $l = metricToDecimal($mos_instance_params{"l"});
       
        $gain =$mos_params{"Ab"}/sqrt((2*$w*$l))*&RandomGaussian +$mos_params{"db_b"};
        $Vt=$mos_params{"avt"}/sqrt((2*$w*$l))*&RandomGaussian +$mos_params{"delvt"};


        $mos_instance_params{"gain"} = $gain;
        $mos_instance_params{"Vt"}   = $Vt;
               



        $mos_line = "I$mos_number $mos_nodes N$mos_model";
        foreach $param (keys %mos_instance_params) {
        $mos_line .= " $param=$mos_instance_params{$param}";
        }
        print OUT "$mos_line\n" ;
    }

    #comment out save line, and print our own save line (print desired nodes)
    elsif (m/(^[ ]*save.*$)/i) {
        $save = $1;
        print OUT "//original save line: $save\n"; #copies original save line
        print OUT "save @VoltageNodesUsed @CurrentNodesUsed\n";
    }


    #if nothing else matched, reproduce line w/o modificiation
    else {
        print OUT;
    }
    }#end of while (<IN>)
   
    #print subcircuits each mosfet model
    foreach $mod_mos (keys %MOSFETSUsedByInput) {
    &printMOSFETSubcircuit($mod_mos);
    }   


    #close file streams
    close(IN) || die "can't close $InputNetlist";
    close(OUT) || die "can't close $OutputFilename";
}



#convert eg -10m to -0.01
sub metricToDecimal {  
<--Changes Metric Suffix to Numeric Exponents of 10
    $number = shift;
    $number =~ m/(-?\d*\.?\d*)(\w?)/;
    $value = $1;
    $suffix = $2;
    return $value if ($suffix eq "");
   
    $value *= 10^-12 if ($suffix =~ s/^p$//i);
    $value *= 10^-9  if ($suffix =~ s/^n$//i);
    $value *= 10^-6  if ($suffix =~ s/^u$//i);
    $value *= 10^-3  if ($suffix =~ s/^m$//i);
    $value *= 10^3   if ($suffix =~ s/^k$//i);
    $value *= 10^6   if ($suffix =~ s/^M$//i);
    $value *= 10^9   if ($suffix =~ s/^g$//i);
    $value *= 10^12  if ($suffix =~ s/^t$//i);
    return $value;
}


#open and prase the user-specified devitations file
sub processDeviationsFile {      
<-- Opens the deviations file and parses information to be saved
    print "Starting parsing deviations file $DeviationsFile...\n";   
    $ResDev   = 0;
    $ResShift = 0;
    $CapDev   = 0;
    $CapShift = 0;
    $IndDev   = 0;
    $IndShift = 0;
    open (DEV, $DeviationsFile);
    while (defined ($line = <DEV>)) {
    #chomp and remove comments
    chomp($line);
    $line =~ s/\#.*//;
    trim($line);

    #ignore blank lines
    if ($line =~ m/^$/) {
    }
    #parse capacitor line
    elsif ($line =~ m/^capacitor/i) {
        @_= split(/\s+/,$line);
        $CapDev = $_[1];
        $CapShift = $_[2];
    }
    #parse resistor line
    elsif ($line =~ m/^resistor/i) {
        @_= split(/\s+/,$line);
        $ResDev = $_[1];
        $ResShift = $_[2];
    }
    #parse inductor line
    elsif ($line =~ m/^inductor/i) {
        @_= split(/\s+/,$line);
        $IndDev = $_[1];
        $IndShift = $_[2];
    }
    #parse save line
    elsif ($line =~ m/^save/i) {
        @_= split(/\s+/,$line);
        if ($_[1] =~ m/^v/) {
        push(@VoltageNodesUsed,$_[1]);
        }
        else {
        push(@CurrentNodesUsed,$_[1]);
        }
    }
    #parse mosfet lines
    else {
        #get model, then split each token by = to get param=value sets
        @_= split(/\s+/,$line);
        my $mos_model = shift(@_);
        my %mosparams;

        foreach my $param (@_) {
        my @temp = split(/\s*=\s*/,$param);
       
        for (my $i=0; $i < scalar(@temp); $i+=2) {
            $mosparams{$temp[$i]} = $temp[$i+1];
        }
        }
        $MOSFETSpecs{$mos_model}  = \%mosparams
    }
    }

    &printDeviationsInfo;
    close (DEV);
    print "Done parsing deviations file $DeviationsFile...\n\n";
}

#print all information parsed from the deviations file
sub printDeviationsInfo {
    print "Voltage Nodes:\t\t@VoltageNodesUsed\n";
    print "Voltage Nodes:\t\t@CurrentNodesUsed\n";
    print "Resistor Deviation:\t$ResDev\t\tShift:\t$ResShift\n";
    print "Capacitor Deviation:\t$CapDev\t\tShift:\t$CapShift\n";
    print "Inductor Deviation:\t$IndDev\t\tShift:\t$IndShift\n";
    foreach $key (sort keys %MOSFETSpecs) {
    $temp = $MOSFETSpecs{$key};
    %hash = %$temp;
    print "MOSFET $key\n";
    foreach $param (sort keys %hash) {
        print "\t$param:       \t$hash{$param}\n";
    }
   
    }   
}

#remove trailing and leading whitespace off of a string
sub trim {
    my $string = shift || return;
    $string =~ s/\s*$//;
    $string =~ s/^\s*//;
    return $string;
}#sub trim

#generates random numbers with gaussian distribution
sub RandomGaussian {     
<--Generates Random Number with a Standard Normal Gaussian Distribution
    my ($u1, $u2);  # uniformly distributed random numbers
    my $w;          # variance, then a weight
    my ($g1, $g2);  # gaussian-distributed numbers

    do {
        $u1 = 2 * rand() - 1;
        $u2 = 2 * rand() - 1;
        $w = $u1*$u1 + $u2*$u2;
    } while ( $w >= 1 );

    $w = sqrt( (-2 * log($w))  / $w );
    $g2 = $u1 * $w;
    $g1 = $u2 * $w;
    # return both if wanted, else just one
}#sub RandomGaussian




#function prints a subcircuit for the passed MOSFET model
sub printMOSFETSubcircuit {     
<--Prints the subcircuit.  modify here if you choose to use a different model
    my $mod_mos = shift || return; #grabs element from @model
    print OUT "subckt N$mod_mos d g s b\n";
    print OUT "    parameters w=1u l=1u ad=1p as=1p pd=1u ps=1u Vt=0 gain=0 // subcircuit parameters\n";
    print OUT "    vsub (g gp) vsource dc=Vt\n";
    print OUT "    vtest (d dp) vsource dc=0\n";
    print OUT "    N$mod_mos (dp gp s b) $mod_mos w=w l=l ad=ad as=as pd=pd ps=ps\n";
    print OUT "    F0 (d s) cccs gain=gain probe=vtest\n";
    print OUT "ends N$mod_mos\n";
}#sub printMOSFETSubcircuit


If you have any questions, please email me at hch2007@columbia.edu