#!/usr/bin/perl -w
########################################################
#  This script was built to prepare the output of parseDbg.pl
# for the formatMessage or the merge.pl. This process is needed only
# for SEM special lines (more details can be found in the user manual).
#
# Above each fuction there is the input and output of the relevant mode.
# In each cycle the semi output 128 bit which is equals to 2 lines in the input file.
########################################################

use strict;
use English;
use warnings;
use File::Path;
use Cwd;
my $MEM_FILE;
my $LINE;
my $MODE=0;
my $LASTHANDLER="";
my $CHIP_NUM;

#################### Parse_handler_msg #####################################
# Used for mode 0 (Handler + thread + fic / passive buffer)
# ParseDbg.pl seperate the messages according to the frame.
#
# if the chip is E1:
# Due to hardware bug in this mode, the frame is turn on only one cycle (2 lines) after the begining of a message
# The function drop the first message (and save only the last two lines).
# Otherwise, the script expected the messages to arrive properly.
# The first line structure (from the beginning of the original message - without the bug):
# [0] - is pram_addr high/low; [14:1] - the pram address; [19:16] - thread number;
# in E1H, bit [32] - is message source (FIC | PB)
# all other parts are zeros, the second line is also all zeros
# There are two options for the rest of the lines:
# If the mode was part_msg then there will be 8 lines, the first six are the first three cycles
# and the last two are the last cycle.
# If the mode was full_msg then there will be as many lines as written in the header (first line after the title, third
# line from the frame) [15:8] - length (Sometime there will be more lines of zeros).
# The parsing is done in the same way whether it was with/without passive buffer.
sub parse_handler_msg {
    my ($type)=@_;
    my ($sem_name)=$LINE;
    my @lines;
    my $lastLines="";
    my $line=<SRC>;
    my $comment="";
    my $neededLines=0;
    # 0 is fic, 1 is pb.
    my $source=0;
    $sem_name=~/\s(.)SEM:/;
    $sem_name=$1;
    $LINE=~/(#.+)/;
    if (defined($1)) {
        $comment=$1;
    }
    while ($line ne "" && $line ne "\n" && ! eof(SRC)) {
        @lines=(@lines,$line);
        $line =<SRC>;
    }
    # TODO: fit the script for E1H
    
    $neededLines=unpack("n",pack("B16","0"x8 . substr($lines[0],48,8)));
    if ($type eq "PART") {
        $neededLines=8;
    } elsif ($CHIP_NUM ne 'E1') {
	# checking if the source is PB or FIC
	if ($source=substr($lines[0],31,1)) {
	    $neededLines=$#lines-1;
	    
	}
    }
    if ($CHIP_NUM eq 'E1') {
	    
	if ($#lines==1) {
	    $LASTHANDLER=$lines[0];
	    return;
	}
	if ($LASTHANDLER eq "") {
	    print STDERR "Unknown handler for first msg, dropping it...\n";
	    $LASTHANDLER=$lines[$#lines-1];
	    return;
	}
	$LASTHANDLER=$lines[$#lines-1];
	print "DATA ".$sem_name."SEMI_HANDLER_".$type."_MSG\t$comment\n";
	print substr($LASTHANDLER,49,14)."\t".substr($LASTHANDLER,44,4)."\n";
    } else {
	if ($source) {
	    print "DATA ".$sem_name."SEMI_HANDLER_".$type."_PB_MSG\t$comment\n";
	} else {
	    print "DATA ".$sem_name."SEMI_HANDLER_".$type."_MSG\t$comment\n";
	}
    }
    for (my $i=0; $i<$neededLines; $i++){
        print $lines[$i];
    }
    print "\n";
    
}


################### parse_fin_and_data ###################
# Used for mode 1 (Fin command + DRA_READ )
# ParseDbg.pl seperate the messages according to the frame
# The 2 first lines are the fin command[108:0] and the thread number [119:115].
# The rest of the lines are the DRA read (all the registers from lowest RegPair
# to highest RegPair). This function seperate it to the fin command(109) and the thread number(4).
# And write each FOC if exists seperatly so the formatMessage could parse them.

# Finstruct
# [5:0] start_rp_foc3; [11:6] start_rp_foc2;  [17:12] start_rp_foc1; [23:18] start_rp_foc0; [29:24] end_rp_foc3;
# [35:30] end_rp_foc2; [41:36] end_rp_foc1; [47:42] end_rp_foc0; [53:48] lowest rp; [59:54] highest rp; [65:60] store start rp;
# [71:66] store end rp; [77:72] load start rp; [83:78] load end rp; [85:84] priority; [101:86] pram address; [102] pas;
# [103] foc3; [104] foc2; [105] foc1; [106] foc0; [107] release; [108] fin;

sub parse_fin_and_data  {
    my ($sem_name)=$LINE;
    $sem_name=~/\s(.)SEM:/;
    $sem_name=$1;
    my ($lowRP,$highRP,$foc0sRP,$foc0eRP,$foc1sRP,$foc1eRP,$foc2sRP,$foc2eRP,
	$foc3sRP,$foc3eRP,$pbsRP,$pbeRP);
    my $line;
    my @lines;
    while (<SRC>) {
        $line=$_;
	chop($line);
        if ($line ne "") {
            @lines=(@lines,$line);
        } else { last; }
    }
    my $thread=substr($lines[1],12,4);
    my $fin=substr($lines[1],19).$lines[0];
    shift(@lines);
    shift(@lines);
    
    $highRP=ord(pack("B8","00".substr($fin,49,6)));
    $lowRP=ord(pack("B8","00".substr($fin,55,6)));
    
    $foc0sRP=ord(pack("B8","00".substr($fin,85,6)));
    $foc1sRP=ord(pack("B8","00".substr($fin,91,6)));
    $foc2sRP=ord(pack("B8","00".substr($fin,97,6)));
    $foc3sRP=ord(pack("B8","00".substr($fin,103,6)));
    $pbsRP=ord(pack("B8","00".substr($fin,43,6)));

    $foc0eRP=ord(pack("B8","00".substr($fin,61,6)));
    $foc1eRP=ord(pack("B8","00".substr($fin,67,6)));
    $foc2eRP=ord(pack("B8","00".substr($fin,73,6)));
    $foc3eRP=ord(pack("B8","00".substr($fin,79,6)));
    $pbeRP=ord(pack("B8","00".substr($fin,37,6)));
    
    print "DATA ".$sem_name ."SEMI_FIN_COMMAND\n";
    print $fin."\t".$thread."\n\n";
    
    # foc 0
    
    if (substr($fin,2,1)) {
	print "DATA ".$sem_name."SEMI_TCM_FOC\n";
	for (my $i=$foc0sRP-$lowRP; $i<=$foc0eRP-$lowRP; $i++) {
	    print $lines[$i]."\n";
	}
	print "\n";
    }
    # foc 1
    if (substr($fin,3,1)) {
	print "DATA ".$sem_name."SEMI_UCM_FOC\n";
	for (my $i=$foc1sRP-$lowRP; $i<=$foc1eRP-$lowRP; $i++) {
	    print $lines[$i]."\n";
	}
	print "\n";
    }
    # foc 2
    if (substr($fin,4,1)) {
	print "DATA ".$sem_name."SEMI_CCM_FOC\n";
	for (my $i=$foc2sRP-$lowRP; $i<=$foc2eRP-$lowRP; $i++) {
	    print $lines[$i]."\n";
	}
	print "\n";
    }
    # foc 3
    if (substr($fin,5,1)) {
	print "DATA ".$sem_name."SEMI_XCM_FOC\n";
	for (my $i=$foc3sRP-$lowRP; $i<=$foc3eRP-$lowRP; $i++) {
	    print $lines[$i]."\n";
	}
	print "\n";
    }
    # Passive Buffer
    if (substr($fin,6,1)) {
	print "DATA ".$sem_name."SEMI_PASSIVE_BUFFER\n";
	for (my $i=$pbsRP-$lowRP; $i<=$pbeRP-$lowRP&&$i<=$#lines; $i++) {
	    print $lines[$i]."\n";
	}
	print "\n";
    }
}

# Used for mode 2 (Handler + fin)
# And for mode 1 when used without dra_read (just fin).
# In mode 2 if bit [127] equals 1 its a fin, otherwise it's an handler.
# In mode 1 it's always a fin but bit 127 is always 0.
# all comes with thread number.
# The function recieve a 0/1 which deteremine what to compare to bit [127].
# The function seperate the messages into FIN and HANDLER titled apporiratly and with exact size
# (FIN - 113  HANDLER - 18.
sub parse_handler_and_fin { 
    my ($symBit)=@_;
    
    my $comment=" # New Handler\n";
    my ($sem_name)=$LINE;
    $sem_name=~/\s(.)SEM:/;
    $sem_name=$1;
    
    my $line;
    my @lines;
    while (<SRC>) {
        $line=$_;
	chop($line);
        if ($line ne "") {
            @lines=(@lines,$line);
        } else { last; }
    }
    for (my $i=0;$i<$#lines;$i++){
        if (substr($lines[$i+1],0,1)==$symBit) {
            my $thread=substr($lines[$i+1],12,4);
            my $fin=substr($lines[$i+1],19).$lines[$i];
            
            print "DATA ".$sem_name ."SEMI_FIN_COMMAND".$comment."\n";
            print $fin."\t".$thread."\n\n";
           
        } else {
            my $pram=substr($lines[$i],49,14);
            my $thread=substr($lines[$i],44,4);
            print "DATA ".$sem_name."SEMI_HANDLER".$comment."\n";
            print $pram."\t".$thread."\n\n";
        }
        $comment="";
    }
    
}
# Used for mode 3 (Store to sdm)
# The SEMI output in every cycle 64 bit of data and 64 bit of address (the addr is actually the 13 lsb bits [12:0]).
# The script seperate each 2 lines to one message for the merge script.
# the output is "DATA <storm_letter>STORM_WR_BUS" "<address>(16 bit - 3 zero +address)) <DATA> <byte_enable all ones>.
sub parse_store_to_sdm {
    my ($sem_name)=$LINE;
    $sem_name=~/\s(.)SEM:/;
    $sem_name=$1;
    my $line;
    my @lines;
    my $addr=-1;
    
    while (<SRC>) {
        $line=$_;
	chop($line);
	if ($line eq "") {
	    last;
	}
	if ($addr==-1) {
	    $addr=substr($line,51);
	} else {
	    print "DATA ".$sem_name."STORM_WR_BUS\n";
	    print "0"x3 .$addr."\t$line\t".("1"x8)."\n\n";
	    $addr=-1;
	}
	
    }
}


#################### MAIN ####################

#get command line parameters
# cmdline arg -c represnt check mode, in which the script treat the first argument as a line and
# checks whether or not the profile require the merge script after  the sem script.
# it return 3 if so.
# if the data has to be enter to this script as one (not splitted into files in some activations)
# it return 1.

die "Format: $PROGRAM_NAME -cn <chip_num> -f <data file (sem.txt)> or $PROGRAM_NAME -c <\"line\">\n"
    unless ($#ARGV == 3 || ($#ARGV==1 && $ARGV[0] eq "-c"));
while ($#ARGV>-1) {
    if ($ARGV[0]=~/^-/) { 
	if ($ARGV[0] eq "-f") {
	    # getting the data file
	    shift(@ARGV);
	    $MEM_FILE=$ARGV[0];
	    shift(@ARGV);
	} elsif ($ARGV[0] eq "-cn") {
	    # gets the chip_ver
	    shift(@ARGV);
	    $CHIP_NUM=$ARGV[0];
	    shift(@ARGV);
	} elsif ($ARGV[0] eq "-c") {
	    if ($ARGV[1]=~/Store_to_sdm/) {
		exit 3;
	    } elsif ($ARGV[1]=~/Handler_(full|part)_msg/) {
		exit 1;
	    } else {
		exit 0;
	    }
	} else {
	    die "Unknown option: $ARGV[0]\nSyntax: perl $PROGRAM_NAME (Mandatory) <-f debug data file (buffer.txt)> <-o output folder> <-p profile name>\n (Optional) <-pf profiles file> <-smc smc path without an ending \\> <-fmt memories file (fmt.gp)> <-dropfirst>  <-filter h<num>>  <limit <size in MB>>\n";
	}   
	
    } else {
        print $#ARGV;
	die "Format: $PROGRAM_NAME -cn <chip_num> -f <data file (sem.txt)> or $PROGRAM_NAME -c <\"line\">\n";
    }
}
die "You must give a chip version and an input file\n" unless (defined($MEM_FILE) && defined($CHIP_NUM));

#open source file
die "Cannot open memories file: $MEM_FILE"
	unless open(SRC, "$MEM_FILE");

# Identify the line and trigger the appropriate function
while (<SRC>)
{
	$LINE=$_;
	if ($LINE eq "\n")
	{
            next;
        } elsif ($LINE=~/SEM:Fin_and_data/) {
            $MODE=1;
	    parse_fin_and_data();
        } elsif ($LINE=~/SEM:Handler_full_msg/) {
            $MODE=0;
	    parse_handler_msg("FULL");
        } elsif ($LINE=~/SEM:Handler_part_msg/) {
            $MODE=0;
	    parse_handler_msg("PART");
        } elsif ($LINE=~/SEM:Store_to_sdm/) {
            $MODE=3;
	    parse_store_to_sdm();
        } elsif ($LINE=~/SEM:Handler_and_fin/) {
            $MODE=2;
            parse_handler_and_fin(1);
        } elsif ($LINE=~/SEM:Fin/) {
            $MODE=1;
            parse_handler_and_fin(0);
        }
        else {
            die "unsupported data.\nExiting..";
        }
        
}
#close input file
close(SRC);

# Mode 3 need the merge script to be activate after it.
# The error level is set to 3 to notify other scripts whether or not to activate the merge.
# useful when working without a pipe in the parsedbg scripts.
if ($MODE==3) {
    exit 3;
}




