#!/usr/bin/perl
use List::Util 'max';
use warnings;
use strict;

my $filename = shift || die("Please provide a input STL file");

my $numberOfRows    = 16384;
my $numberOfBanks   = 8;
my $numberOfColumns = 1024;
my $numberOfBytes   = 8; # Byte Offset from e.g. DIMM

my $numberOfBits    = 32;
my $burstLength     = 8;

my $addressCorrection = 1;

my $numberOfRowBits    = log($numberOfRows)/log(2);
my $numberOfBankBits   = log($numberOfBanks)/log(2);
my $numberOfColumnBits = log($numberOfColumns)/log(2);
my $numberOfByteBits   = log($numberOfBytes)/log(2);
my $numberOfBurstBits  = log($burstLength)/log(2);

print "Number of Address Bits:\t".$numberOfBits."\n\n";
print "Number of Row Bits:\t".$numberOfRowBits."\n";
print "Number of Bank Bits:\t".$numberOfBankBits."\n";
print "Number of Col Bits:\t".$numberOfColumnBits."\n";
print ">Burst Bits in Col:\t".$numberOfBurstBits."\n";
print "Number of Byte Bits:\t".$numberOfByteBits."\n";

my $numberOfXBits = $numberOfBits - $numberOfRowBits - $numberOfBankBits - $numberOfColumnBits - $numberOfByteBits;

print "Number of Unused Bits:\t".$numberOfXBits."\n";
print "\n";
print "\n";

open(FH, "$filename");

my @activityCounter;
my @mapping;

# Initialize:
for(my $i = 0; $i < $numberOfBits; $i++)
{
	$activityCounter[$i] = 0;
}

for(my $i = $numberOfXBits-1; $i >= 0; $i--)
{
        $mapping[$numberOfBits-($numberOfXBits-$i)] = "X".$i;
}

my $old_address = "00000000000000000000000000000000";

while(<FH>)
{
	# Get the adress:
	$_ =~ /\d+:\s+\w+\s+0x([\w\d]+)\s*[\d\w]*/;
	my $address = $1;
        $address = sprintf( "%0".$numberOfBits."b", hex( $address ) * $addressCorrection );

	# $i = 0 :: most significant bit
        for(my $i = 0; $i < $numberOfBits; $i++)
	{
		my $new = substr($address, $i, 1);
		my $old = substr($old_address, $i, 1);

		if($new ne $old)
		{
                        $activityCounter[$numberOfBits-1-$i]++;
		}
	}

	$old_address = $address;
}

close(FH);

# Make Consistency Check:
for(my $i = 0; $i < ($numberOfByteBits+$numberOfBankBits); $i++)
{
    if($activityCounter[$i] != 0)
    {
        print "Bits of lower C part or Y have toggled, this should not happen\n";
        exit -1;
    }
}

# Print bit numbers:
print "Bits\t\t";
for(my $i = $numberOfBits-1; $i >= 0; $i--)
{
	print $i."\t";
}

#Print Activity
print "\nActivity\t";
for(my $i = $numberOfBits-1; $i >= 0; $i--)
{
	print $activityCounter[$i]."\t";
}

#Print relative Activity
print "\nPercent\t\t";
my $sum = 0;
my @percent;
for(my $i = $numberOfBits-1; $i >= 0; $i--)
{
	$sum = $sum + $activityCounter[$i];
}

for(my $i = $numberOfBits-1; $i >= 0; $i--)
{
        my $string = $activityCounter[$i]/$sum*100;
        $string = sprintf("%.2f", $string);
	$string =~ s/\./,/g;
        $percent[$i] = $string."%";
        $string .= "%\t";
        print $string;
}

#Fix Byte Offset:
for(my $i = 0; $i < $numberOfByteBits; $i++)
{
    $activityCounter[$i] = -1;
    $mapping[$i] = "Y$i";
}

#Fix Constant 0 Bits in Column due to Burstlength:
for(my $i=0; $i < $numberOfBurstBits; $i++)
{
    $activityCounter[$numberOfByteBits + $i] = -1;
    $mapping[$numberOfByteBits + $i] = "C$i";
}

#Search Column Locations
for(my $i = $numberOfBurstBits; $i < $numberOfColumnBits; $i++)
{
        my $maximum = max(@activityCounter);
        my ($index) = grep $activityCounter[$_] == $maximum , 0.. $#activityCounter;
        $mapping[$index] = "C$i";
        $activityCounter[$index] = -1;
}

#Search Bank Locations
for(my $i = 0; $i < $numberOfBankBits; $i++)
{
	my $maximum = max(@activityCounter);
	my ($index) = grep $activityCounter[$_] == $maximum , 0.. $#activityCounter;
	$mapping[$index] = "B$i";
	$activityCounter[$index] = -1;
}

#Search Row Locations
for(my $i = 0; $i < $numberOfRowBits; $i++)
{
	my $maximum = max(@activityCounter);
	my ($index) = grep $activityCounter[$_] == $maximum , 0.. $#activityCounter;
	$mapping[$index] = "R$i";
	$activityCounter[$index] = -1;
}

#Print final mapping
my $header = '
strict graph G
{
    forcelabels=true;
    rankdir=LR;
    {
        graph [fontname = "courier"];
        node [shape=square, fontname="courier", fixedsize=true, width=0.85, height=0.85]
        edge [fontname = "courier"];
';

for(my $i = $numberOfBits-1; $i >= 0; $i--)
{
        $header .= "        $i [pos=\"".($numberOfBits-1-$i).",0!\", xlp=\"-5,0\", label=\"$i\n".$percent[$i]."\" ]\n";
}

my $pos = 0;
for(my $i = $numberOfXBits-1; $i >= 0; $i--)
{
        my $pos2 = $numberOfBits-$pos-1;
        $header .= "        X$i [ label=\"$pos2\nX$i\" pos=\"$pos,-5!\"]\n";
        $pos++;
}
for(my $i = $numberOfBankBits-1; $i >= 0; $i--)
{
        my $pos2 = $numberOfBits-$pos-1;
        $header .= "        B$i [ label=\"$pos2\nB$i\" pos=\"$pos,-5!\"]\n";
        $pos++;
}
for(my $i = $numberOfRowBits-1; $i >= 0; $i--)
{
        my $pos2 = $numberOfBits-$pos-1;
        $header .= "        R$i [ label=\"$pos2\nR$i\" pos=\"$pos,-5!\"]\n";
        $pos++;
}
for(my $i = $numberOfColumnBits-1; $i >= 0; $i--)
{
        my $pos2 = $numberOfBits-$pos-1;
        $header .= "        C$i [ label=\"$pos2\nC$i\" pos=\"$pos,-5!\"]\n";
        $pos++;
}
for(my $i = $numberOfByteBits-1; $i >= 0; $i--)
{
        my $pos2 = $numberOfBits-$pos-1;
        $header .= "        Y$i [ label=\"$pos2\nY$i\" pos=\"$pos,-5!\"]\n";
        $pos++;
}
$header .= "    }\n";

print "\nMapping\t\t";
my $maximum = max(@activityCounter);
for(my $i = $numberOfBits-1; $i >= 0; $i--)
{
    if($mapping[$i] =~ /X\d/)
    {
        $header .= "$i -- ".$mapping[$i]." [ color=\"grey\"];\n";
    }
    elsif($mapping[$i] =~ /B\d/)
    {
        $header .= "$i -- ".$mapping[$i]." [ color=\"green\"];\n";
    }
    elsif($mapping[$i] =~ /R\d/)
    {
        $header .= "$i -- ".$mapping[$i]." [ color=\"blue\"];\n";
    }
    elsif($mapping[$i] =~ /C\d/)
    {
        $header .= "$i -- ".$mapping[$i]." [ color=\"red\"];\n";
    }
    elsif($mapping[$i] =~ /Y\d/)
    {
        $header .= "$i -- ".$mapping[$i]." [ color=\"grey\"];\n";
    }
        print $mapping[$i]."\t";
}
$header .= "}";
print "\n";

#Generate Scrambled Trace:

#Generate Configuration for 32x32 MUX:
# Assumption e.g. 32 bit:
#   B R C
#  X X   B B B  R R R R R R R R R R R R R R  C C C C C C C C C C Y Y Y
# 31 30  29 27  26                       13  12                3 2   0
# A      B      C                            D                   E   F
#
#print "A:".($numberOfBits-1)."\n";
#print "B:".($numberOfBits-$numberOfXBits-1)."\n";
#print "C:".($numberOfBits-$numberOfXBits-$numberOfBankBits-1)."\n";
#print "D:".($numberOfBits-$numberOfXBits-$numberOfBankBits-$numberOfRowBits-1)."\n";
#print "E:".($numberOfBits-$numberOfXBits-$numberOfBankBits-$numberOfRowBits-$numberOfColumnBits-1)."\n";
#print "F:".($numberOfBits-$numberOfXBits-$numberOfBankBits-$numberOfRowBits-$numberOfColumnBits-$numberOfByteBits)."\n";

my @confVectorDecimal;
my @checkVector;
my $XCounter = 0;
my $rowCounter = 0;
my $bankCounter = 0;
my $columnCounter = 0;
my $byteCounter = 0;

for(my $i = $numberOfBits-1; $i >= 0; $i--)
{
    if($mapping[$i] =~ /X(\d+)/)
    {
        my $idx = $numberOfBits-$numberOfXBits+$1;
        #print "\$1=".$1." i=".$i." idx=".$idx." mapping=".$mapping[$i]."\n";
        $confVectorDecimal[$idx] = $i;
        $checkVector[$idx] = "X";
    }
    elsif($mapping[$i] =~ /B(\d+)/)
    {
        my $idx = $numberOfBits-$numberOfXBits-$numberOfBankBits+$1;
        #print "\$1=".$1." i=".$i." idx=".$idx." mapping=".$mapping[$i]."\n";
        $confVectorDecimal[$idx] = $i;
        $checkVector[$idx] = "B";
    }
    elsif($mapping[$i] =~ /R(\d+)/)
    {
        my $idx = $numberOfBits-$numberOfXBits-$numberOfBankBits-$numberOfRowBits+$1;
        #print "\$1=".$1." i=".$i." idx=".$idx." mapping=".$mapping[$i]."\n";
        $confVectorDecimal[$idx] = $i;
        $checkVector[$idx] = "R";
    }
    elsif($mapping[$i] =~ /C(\d+)/)
    {
        my $idx = $numberOfBits-$numberOfXBits-$numberOfBankBits-$numberOfRowBits-$numberOfColumnBits+$1;
        #print "\$1=".$1." i=".$i." idx=".$idx." mapping=".$mapping[$i]."\n";
        $confVectorDecimal[$idx] = $i;
        $checkVector[$idx] = "C";
    }
    elsif($mapping[$i] =~ /Y(\d+)/)
    {
        my $idx = $numberOfBits-$numberOfXBits-$numberOfBankBits-$numberOfRowBits-$numberOfColumnBits-$numberOfByteBits+$1;
        #print "\$1=".$1." i=".$i." idx=".$idx." mapping=".$mapping[$i]."\n";
        $confVectorDecimal[$idx] = $i;
        $checkVector[$idx] = "Y";
    }
}

print "\n";
print "\n";
print "Configuration Vector for the mapping ";
for(my $m = $numberOfBits-1; $m >= 0; $m--)
{
    print $checkVector[$m];
}
print ":\n\n";

for(my $m = $numberOfBits-1; $m >= 0; $m--)
{
    #Debug:
    print "mux: $m\t-->\t".$confVectorDecimal[$m]."\n";
}

print "\n";
print "\n";

for(my $m = $numberOfBits-1; $m >= 0; $m--)
{
    print sprintf( "%05b", $confVectorDecimal[$m] );
}
print "\n";
print "\n";

# Generate Graph
my $dotname;
my $pdfname;
my $stlname;

if($filename =~ /(.+)\.stl/)
{
    $dotname = $1.".dot";
    $pdfname = $1.".pdf";
    $stlname = $1."_scram.stl";

    open(FH,">$dotname");
    print FH $header;
    close(FH);
    system("neato -o $pdfname -Tpdf $dotname");
}


open(FH, "$filename");
open(SH, ">$stlname");

while(<FH>)
{
        # Get all the data adress:
        $_ =~ /(\d+):\s+(\w+)\s+0x([\w\d]+)/; # XXX
        my $time = $1;
        my $command = $2;
        my $address = $3;
        my $new_address;

        # Convert to binary:
        $address = sprintf( "%032b", hex( $address ) );

        # Swap adresses:
        $new_address = "";
        for(my $m = $numberOfBits-1; $m >= 0; $m--)
        {
            #print $confVectorDecimal[$m]."\n";
            $new_address .= substr($address,$numberOfBits-1-$confVectorDecimal[$m],1);
        }

        # Convert to Hex:
        $new_address = sprintf("%X", oct( "0b$new_address" ) );

        print SH $time.":\t".$command."\t0x".$new_address."\n";
}

close(FH);
close(SH);
