#!/bin/bash

# Script which runs LOON at opt level 0 on the supplied netlist
# and finds the delay from each input to the critical output.
# It uses this delay to calculate the input resistance needed on
# that input to make its delay equal to that of the critical path.
# The max resistance found is written to standard output.

MBK_IN_LO=vst
MBK_OUT_LO=vst
hi_res=50000
time_limit=30
if test -f .buf_loon
then
  for field in 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: find_rin [-l lax_file] input_file" 1>&2
  echo "#" 1>&2
  echo "# finds each input's critical path and resistance to match the longest critical path" 1>&2
  echo "# prop and path delays written to error output; max input resistance to standard output" 1>&2
  exit 1
fi

if [ "$1" = "-l" ]
then
  lax_exists=1
  shift  
  if test -f $1.lax
  then
    lax_file=$1
    shift
  else
    echo "# Usage: find_rin -l lax_file input_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
else
  lax_exists=0
fi

if test -f $1.vst
then
  in_file=$1
else
  echo "# Usage: find_rin -l lax_file input_file" 1>&2
  echo "#" 1>&2
  if [ "$1" = "" ]
  then
    echo "# no input_file supplied. Please check." 1>&2
  else
    echo "# the input_file supplied "$1".vst does not exist. Please check." 1>&2
  fi
  exit 1
fi

if test -d $MBK_TARGET_LIB
then
  orig_target_lib=$(echo $MBK_TARGET_LIB)
  orig_cata_lib=$(echo $MBK_CATA_LIB)
  orig_catal_name=$(echo $MBK_CATAL_NAME)
  if test -d mbk_rin
  then
    rm -rf mbk_rin
  fi
  mkdir mbk_rin
  comps=$(grep -i '^ *Component ' ${in_file}.vst | tr -s ' ' | cut -f2 -d' ')
  for comp in $comps
  do
    cp -p ${orig_target_lib}/${comp}.vbe mbk_rin
  done
  ls -1 mbk_rin/*.vbe | sed 's|^mbk_rin/\(.*\)\.vbe$|\1 C|' > mbk_rin/CATAL
  MBK_TARGET_LIB=mbk_rin
  MBK_CATA_LIB=mbk_rin
  MBK_CATAL_NAME=CATAL
else
  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_inputs()
{
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 ${name}"("${index}")" >> $$_inputs
    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
  echo $in_bit | cut -f1 -d' ' >> $$_inputs
done

sort $$_inputs
rm $$_vectors $$_inputs $$_bits
}

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.

echo "#M{0}" > lax.lax
if [ "$lax_exists" -eq 1 ]
then
  sed -n '/^#I{/,/^}/ p' ${lax_file}.lax >> lax.lax
  sed -n '/^#C{/,/^}/ p' ${lax_file}.lax >> lax.lax
fi
rm -f $$_out_file.xsc $$_out_file.vst
loon -l lax $in_file $$_out_file 2>/dev/null >$$_loonout &
local_sleep $$_out_file.xsc
if [ "$exit_status" -eq 1 ]
then
  echo "#? Error: Initial LOON crashed. Nothing can be done with this netlist." 1>&2
  exit 1
elif [ "$exit_status" -eq 2 ]
then
  echo "#? Error: Initial LOON locked up and has been killed. Nothing can be done with this netlist." 1>&2
  exit 2
  kill -9 $!
fi
max_critical_delay=$(grep '^Critical' $$_loonout | sed 's/\.\.\./ /' | cut -f5 -d' ')
max_critical_res=0

echo "# Using library "$orig_target_lib" gives critical path delay of "$max_critical_delay 1>&2
inputs=$(find_inputs $in_file)
for input in $inputs
do
  fanin=$(sed -nr '/^ *(B|b)(E|e)(G|g)(I|i)(N|n) *$/,$ p' ${in_file}.vst | grep -c " ${input}")
  echo "#M{0}" > lax.lax
  if [ "$lax_exists" -eq 1 ]
  then
    sed -n '/^#I{/,/^}/ p' ${lax_file}.lax >> lax.lax
    sed -n '/^#C{/,/^}/ p' ${lax_file}.lax >> lax.lax
  fi

  res_section_exists=$(grep '^#I{' lax.lax)
  if [ "$res_section_exists" = "" ]
  then
    ip_res=0
    echo "#I{" >> lax.lax
    echo $input":"$hi_res";" >> lax.lax
    echo "}" >> lax.lax
  else
    ip_res_exists=$(sed -n '/^#I{/,/^}/ p' lax.lax | grep "^$input")
    if [ "$ip_res_exists" = "" ]
    then
      ip_res=0
      sed -i "/^#I{/ a ${input}:${hi_res};" lax.lax
    else
      ip_res=$(sed -n '/^#I{/,/}/ p' lax.lax | grep "$input" | cut -f2 -d: | cut -f1 -d';')
      sed -i "/^#I{/,/^}/ s/^\(${input}\):\(.*\)$/\1:${hi_res};/" lax.lax
    fi
  fi

  rm -f $$_out_file.xsc $$_out_file.vst
  loon -l lax $in_file $$_out_file 2>/dev/null >$$_loonout &
  local_sleep $$_out_file.xsc
  if [ "$exit_status" -eq 1 ]
  then
    echo "#? Error: LOON crashed" 1>&2
  elif [ "$exit_status" -eq 2 ]
  then
    echo "#? Error: LOON locked up and has been killed" 1>&2
    kill -9 $!
  else
    path_delay=$(grep '^Critical' $$_loonout | sed 's/\.\.\./ /' | cut -f5 -d' ')
#   Vectored names can appear in two forms:
#   loon_ip is input name like 'x 2'
#   critical_ip is input name like x(2)
    loon_ip=$(grep '^Critical' $$_loonout | sed 's/^\(.* from  *\)\(.*\)  *to  *\(.*\)$/\2/' | sed "s/'//g")
    critical_ip=$(grep '^Critical' $$_loonout | sed 's/^\(.* from  *\)\(.*\)  *to  *\(.*\)$/\2/' |
                  sed "s/'//g" | sed 's/^\(.*\) \(.*\)$/\1(\2)/')
    if [ "$critical_ip" != "$input" ]
    then
      echo "# Pin "$input" fanin = "$fanin" the critical path input is not the high resistance one" 1>&2
      continue
    fi
  
    ip_delay=$(grep "^N:${loon_ip}" $$_out_file.xsc | cut -f4 -d: | cut -f1 -d ' ')
    if [ "$ip_delay" = "" ]
    then
      echo "#? Error: Something is wrong. Input "$loon_ip" has no input delay." 1>&2
      cat $$_loonout 1>&2
      break
    fi

    let "prop_delay=path_delay-ip_delay"
    let "critical_delay=(2*hi_res*prop_delay+2*ip_res*ip_delay+hi_res)/(2*hi_res)"
    let "input_delay=(2*ip_delay*ip_res+hi_res)/(2*hi_res)"
    let "critical_res=(2*hi_res*(max_critical_delay-prop_delay)+ip_delay)/(2*ip_delay)"
    let "max_critical_res=critical_res>max_critical_res?critical_res:max_critical_res"
    if [ "$fanin" -lt 10 ]
    then
      echo "# Pin "$input" fanin =  "$fanin" path delay = prop delay + input delay = "$critical_delay" = "$prop_delay" + "$input_delay". Measured delay "$path_delay", rin delay "$ip_delay 1>&2
    else
      echo "# Pin "$input" fanin = "$fanin" path delay = prop delay + input delay = "$critical_delay" = "$prop_delay" + "$input_delay". Measured delay "$path_delay", rin delay "$ip_delay 1>&2
    fi
  fi
done

rm -f $$_loonout $$_out_file.vst $$_out_file.xsc
#rm -rf mbk_rin
MBK_TARGET_LIB=$orig_target_lib
MBK_CATA_LIB=$orig_cata_lib
MBK_CATAL_NAME=$orig_catal_name
echo $max_critical_res

