#!/bin/bash
#buf_loon
# Copyright (c) 2008 Graham Petley
# Released under the terms of the GNU Lesser General Public Licence

# Script which runs LOON, checks the fanin and adjusts the LAX file
# with higher input resistances in an attempt to lower the fanin.

max_fanin=4
rin=10000
time_limit=30
if test -f .buf_loon
then
  for field in rin max_fanin time_limit
  do
    values=$(grep "^ *$field " .buf_loon | sed 's/^ *//' | tr -s ' ' | cut -f 2 -d' ')
    for value in $values
    do
      let "${field}=value"
    done
  done
fi

if [ "$#" -eq 0 ]
then
  echo "# Usage: buf_loon -l lax_file input_file output_file [-t time_limit]" 1>&2
  echo "#" 1>&2
  echo "# runs loon with successive LAX files until the fanin is lower than 4" 1>&2
  exit 1
fi

read_in_file=0
while [ "$#" -gt 0 ]
do
  case $1 in
  -l)
    shift
    if test -f $1.lax
    then
      lax_file=$1
      sed 's/^#M{\(.\)} *$/#M{0}/' ${lax_file}.lax > $$_lax0.lax
      shift
    else
      echo "# Usage: buf_loon -l lax_file input_file output_file" 1>&2
      echo "#" 1>&2
      if [ "$1" = "" ]
      then
        echo "# no lax_file supplied. Please check." 1>&2
      else
        echo "# the lax_file supplied "$1".lax does not exist. Please check." 1>&2
      fi
      exit 1
    fi ;;
  -f)
    shift  
    if [ "$1" = "" ]
    then
      echo "# Usage: buf_loon -l lax_file input_file output_file [-f fanin]" 1>&2
      echo "#" 1>&2
      echo "# there is no maximum fanin parameter. Please check." 1>&2
      exit 1
    else
      max_fanin=$1
      shift
    fi ;;
  -r)
    shift  
    if [ "$1" = "" ]
    then
      echo "# Usage: buf_loon -l lax_file input_file output_file [-r rin]" 1>&2
      echo "#" 1>&2
      echo "# there is no input resistance parameter. Please check." 1>&2
      exit 1
    else
      rin=$1
      shift
    fi ;;
  -t)
    shift  
    if [ "$1" = "" ]
    then
      echo "# Usage: buf_loon -l lax_file input_file output_file [-t time_limit]" 1>&2
      echo "#" 1>&2
      echo "# there is no time limit parameter. Please check." 1>&2
      exit 1
    else
      time_limit=$1
      shift
    fi ;;
  *)
    if [ "$read_in_file" -eq 0 ]
    then
      if test -f $1.vst
      then
        cp -p $1.vst c_$$_vst.vst
        x2y vst vst $1 c_$$_boog >/dev/null
        flatlo -r c_$$_boog $1 >/dev/null
        rm c_$$_boog.vst
        in_file=$1
        read_in_file=1
        shift
      else
        echo "# Usage: buf_loon -l lax_file input_file output_file" 1>&2
        echo "#" 1>&2
        echo "# the input_file supplied "$1".vst does not exist. Please check." 1>&2
        exit 1
      fi
    else
      out_file=$1
      shift
    fi ;;
  esac
done
if [ "$lax_file" = "" ]
then
  echo "# Usage: buf_loon -l lax_file input_file output_file" 1>&2
  echo "#" 1>&2
  echo "# no lax_file supplied. Please check." 1>&2
  exit 1
fi
if [ "$in_file" = "" ]
then
  echo "# Usage: buf_loon -l lax_file input_file output_file" 1>&2
  echo "#" 1>&2
  echo "# no input_file supplied. Please check." 1>&2
  exit 1
fi
if [ "$out_file" = "" ]
then
  echo "# Usage: buf_loon -l lax_file input_file output_file" 1>&2
  echo "#" 1>&2
  echo "# no output_file supplied. Please check." 1>&2
  exit 1
fi

if test ! -d $MBK_TARGET_LIB
then
  echo "# Environment variable \$MBK_TARGET_LIB must point to the library." 1>&2
  echo "# \$MBK_TARGET_LIB is "$MBK_TARGET_LIB" and doesn't exist." 1>&2
  echo "# Please check." 1>&2
  exit 1
fi

find_fanin()
{
top_fanin=0
sed -nr '/^ *(E|e)(N|n)(T|t)(I|i)(T|t)(Y|y) /,/^ *(E|e)(N|n)(D|d)/ p' $1.vst |
  grep -i bit_vector | grep ': *in ' | sed 's/^  *//' | sed 's/;.*$//' | tr -s ' ' > $$_vectors

cat $$_vectors |
while read in_vector
do
  name=$(echo $in_vector | cut -f1 -d' ')
  dir_index=$(echo $in_vector | cut -f2 -d'(' | cut -f1 -d')' | cut -f2 -d' ' | tr 'A-Z' 'a-z')
  if [ "$dir_index" = downto ]
  then
    first_index=$(echo $in_vector | cut -f2 -d'(' | cut -f1 -d')' | cut -f3 -d' ')
    second_index=$(echo $in_vector | cut -f2 -d'(' | cut -f1 -d')' | cut -f1 -d' ')
  else
    first_index=$(echo $in_vector | cut -f2 -d'(' | cut -f1 -d')' | cut -f1 -d' ')
    second_index=$(echo $in_vector | cut -f2 -d'(' | cut -f1 -d')' | cut -f3 -d' ')
  fi

  index=$first_index
  while [ "$index" -le "$second_index" ]
  do
    echo -n ${name}"("${index}")=" >> $$_inputs
    fanin=$(sed -nr '/^ *(B|b)(E|e)(G|g)(I|i)(N|n) *$/,$ p' $1.vst | grep -c " ${name}(${index})")
    echo $fanin >> $$_inputs
    if [ "$fanin" -gt "$top_fanin" ]
    then
      top_fanin=$fanin
      worst_input=${name}"("${index}")"
      echo "Max fanin is "$worst_input"="$top_fanin > $$_fanin
    fi
    let index=$index+1
  done
done

sed -nr '/^ *(E|e)(N|n)(T|t)(I|i)(T|t)(Y|y) /,/^ *(E|e)(N|n)(D|d)/ p' $1.vst |
  egrep -i ': *in  *bit( |;|$)' | egrep -v 'vdd *:|vss *:' | sed 's/^  *//' |
  sed 's/;.*$//' | tr -s ' ' > $$_bits

cat $$_bits |
while read in_bit
do
  name=$(echo $in_bit | cut -f1 -d' ')
  echo -n ${name}"=" >> $$_inputs
  fanin=$(sed -nr '/^ *(B|b)(E|e)(G|g)(I|i)(N|n) *$/,$ p' $1.vst | grep -c " ${name}")
  echo $fanin >> $$_inputs
  if [ "$fanin" -gt "$top_fanin" ]
  then
    top_fanin=$fanin
    worst_input=$name
    echo "Max fanin is "$worst_input"="$top_fanin > $$_fanin
  fi
done
if [ "$2" != 2 ]
then
  sort $$_inputs
else
  cat $$_inputs |
  while read line
  do
    echo -n $line" "
  done
  cat $$_fanin
fi
rm $$_vectors $$_inputs $$_bits $$_fanin
}

local_sleep()
{
exit_status=2
time=$time_limit
while [ "$time" -gt 0 ]
do
  if [ -f "$1" ]
  then
    exit_status=0
    time=0
  else
    if ! ps h $! >/dev/null
    then
#     LOON has crashed and PID has disappeared
      exit_status=1
      time=0
    else
      sleep 1
      let time="$time"-1
    fi
  fi
done
}

#######################
# Execution begins here.

MBK_IN_LO=vst
cp ${lax_file}.lax lax.lax
cp lax.lax old_lax.lax
prev_fanin=999
opt_level=$(grep '^#M{' lax.lax | cut -f2 -d{ | cut -f1 -d})
if [ "$opt_level" = "" ]
then
  opt_level=2
fi
echo "buf_loon -l $lax_file $in_file $out_file -f $max_fanin -r $rin -t $time_limit"
index=0; max_index=10
while [ "$index" -lt "$max_index" ]
do
  if [ "$opt_level" -eq 0 ]
  then
#   Input buffering won't take place and no macros used
    index=$max_index
    rm -f ${out_file}.xsc ${out_file}.al ${out_file}.vst
    loon -l lax $in_file $out_file 2>/dev/null >$$_loonout &
    local_sleep ${out_file}.xsc
    MBK_OUT_LO=vst
    if [ "$exit_status" -eq 1 ]
    then
      echo "#? Error: LOON crashed" 1>&2
      mv c_$$_vst.vst ${in_file}.vst
      rm -f $$_lax0.lax $$_lax.lax c_$$_loon.vst $$_loonout
      exit 1
    elif [ "$exit_status" -eq 2 ]
    then
      echo "#? Error: LOON locked up and has been killed" 1>&2
      kill -9 $!
      mv c_$$_vst.vst ${in_file}.vst
      rm -f $$_lax0.lax $$_lax.lax c_$$_loon.vst $$_loonout
      exit 2
    fi
  elif [ "$opt_level" -eq 1 ]
  then
#   Input buffering won't take place but macros could be added
    index=$max_index
    rm -f ${out_file}.xsc ${out_file}.al ${out_file}.vst
    loon -l lax $in_file $out_file 2>/dev/null >$$_loonout &
    local_sleep ${out_file}.xsc
    MBK_OUT_LO=vst
    if [ "$exit_status" -eq 1 ]
    then
      echo "#? Error: LOON crashed" 1>&2
      mv c_$$_vst.vst ${in_file}.vst
      rm -f $$_lax0.lax $$_lax.lax c_$$_loon.vst $$_loonout
      exit 1
    elif [ "$exit_status" -eq 2 ]
    then
      echo "#? Error: LOON locked up and has been killed" 1>&2
      kill -9 $!
      mv c_$$_vst.vst ${in_file}.vst
      rm -f $$_lax0.lax $$_lax.lax c_$$_loon.vst $$_loonout
      exit 2
    fi
    x2y vst vst $out_file c_$$_loon >/dev/null
    flatlo -r c_$$_loon $out_file >/dev/null
    rm c_$$_loon.vst
  else
    let index=$index+1
    rm -f ${out_file}.xsc ${out_file}.al ${out_file}.vst
    MBK_OUT_LO=al
    loon -l lax $in_file $out_file 2>/dev/null >$$_loonout &
    local_sleep ${out_file}.xsc
    MBK_OUT_LO=vst
    if [ "$exit_status" -eq 1 ]
    then
      echo "#? Error: LOON crashed" 1>&2
      mv c_$$_vst.vst ${in_file}.vst
      rm -f $$_lax0.lax $$_lax.lax c_$$_loon.vst $$_loonout
      exit 1
    elif [ "$exit_status" -eq 2 ]
    then
      echo "#? Error: LOON locked up and has been killed" 1>&2
      kill -9 $!
      mv c_$$_vst.vst ${in_file}.vst
      rm -f $$_lax0.lax $$_lax.lax c_$$_loon.vst $$_loonout
      exit 2
    fi
    x2y al vst $out_file c_$$_loon >/dev/null
    flatlo -r c_$$_loon $out_file >/dev/null
    cat $$_loonout | grep '^Critical' | sed 's/^/#/' 1>&2
    if [ "$max_fanin" -le 0 ]
    then
#     we don't try to do fanin reduction
      index=$max_index
    else
      find_fanin $out_file 2 | sed 's/^/#/' 1>&2
      cp lax.lax $$_lax.lax
      highest_fanin=0
      fields=$(find_fanin $out_file | sed 's/ //')
      for field in $fields
      do
        input=$(echo $field | cut -f1 -d=)
        fanin=$(echo $field | cut -f2 -d=)
        let "highest_fanin=fanin>highest_fanin?fanin:highest_fanin"
        if [ "$fanin" -gt "$max_fanin" ]
        then
#         $inputs is list of inputs with fanin too large
          inputs=$(echo $input" "$inputs)
          sed -i "s/^\(${input}\):[0-9][0-9]*; *$/\1:${rin};/" $$_lax.lax
        fi
      done
      if [ "$inputs" = "" ]
      then
#       all inputs have fanin no greater than $max_fanin
        index=$max_index
      else
        if [ "$highest_fanin" -ge "$prev_fanin" ]
        then
          stop=1
          for input in $inputs
          do
#           if any input still has original rin value then continue
            lax_rin=$(grep "^${input}:" old_lax.lax | cut -f2 -d: | sed 's/;//g')
            if [ "$lax_rin" -ne "$rin" ]
            then
              stop=0
              break
            fi
          done
        else
          stop=0
        fi
        if [ "$stop" -eq 1 ]
        then
#         fanin has not got better; return to previous lax file
          mv lax.lax $$_lax
          mv old_lax.lax lax.lax
          mv $$_lax.lax old_lax.lax
          let "index=index>max_index-1?index:max_index-1"
        else
          mv lax.lax old_lax.lax
          mv $$_lax.lax lax.lax
          if diff -q old_lax.lax lax.lax >/dev/null
          then
            break
          fi
        fi
        prev_fanin=$highest_fanin
      fi
    fi
  fi
done
if [ "$opt_level" -gt 1 ]
then
  flatlo -r c_$$_loon c_$$_flatlo >/dev/null
  rm -f ${out_file}.xsc ${out_file}.vst
  loon -l $$_lax0 c_$$_flatlo $out_file 2>/dev/null >$$_loonout &
  local_sleep ${out_file}.xsc
  if [ "$exit_status" -eq 1 ]
  then
    echo "#? Error: LOON crashed" 1>&2
    mv c_$$_vst.vst ${in_file}.vst
    rm -f $$_lax0.lax $$_lax.lax c_$$_loon.vst c_$$_flatlo.vst $$_loonout
    exit 1
  elif [ "$exit_status" -eq 2 ]
  then
    echo "#? Error: LOON locked up and has been killed" 1>&2
    kill -9 $!
    mv c_$$_vst.vst ${in_file}.vst
    rm -f $$_lax0.lax $$_lax.lax c_$$_loon.vst c_$$_flatlo.vst $$_loonout
    exit 2
  fi
fi
rm -f $$_lax0.lax $$_lax.lax c_$$_loon.vst c_$$_flatlo.vst
cat $$_loonout
mv c_$$_vst.vst ${in_file}.vst
rm $$_loonout
exit 0

