#!/bin/sh

# simulator builder script
#
# warning: this MUST NOT be called directly by the user
#
# bsc invokes
#   bsc_build_vsim_... detect
# to detect whether this simulator can be built (exit status 0 = yes)
#
# bsc invokes
#   bsc_build_vsim_... link outexe topmod clibpath clibs linkopts vdirs vdefs vopts vfiles ofiles
# where
#   outexe   - the name of the simulator executable to create
#   topmod   - the toplevel module to simulate
#   clibpath - where to find external C libraries
#   clibs    - which external C libraries to link in
#   linkopts - other options passed to C/C++ linker
#   vdirs    - -y directories to search for Verilog files
#   vdefs    - -D macro definitions
#   vopts    - other options passed to Verilog
#   vfiles   - the Verilog files to simulate
#   ofiles   - any VPI object files to link in
# exits with status 0 if it successfully built the simulator, 1 otherwise

# tested on ModelSim 5.8d, revision 2004.06

BSC_COMMAND=$1           # "detect" or "link"

if [ "$BSC_COMMAND" = "detect" ]; then
# detect whether the commands "vlib", "vcom", and "vsim" are in the path
  SIM_NAME=modelsim
  hash vlib 2> /dev/null
  if [ "$?" != "0" ]; then
      echo "$SIM_NAME was not found"
      exit 1
  fi
  hash vcom 2> /dev/null
  if [ "$?" != "0" ]; then
      echo "$SIM_NAME was not found"
      exit 1
  fi
  hash vsim 2> /dev/null
  if [ "$?" != "0" ]; then
      echo "$SIM_NAME was not found"
      exit 1
  fi
  FOUND_CMD=`which vsim`
  echo "$SIM_NAME was found at $FOUND_CMD"
  exit 0
fi

# the only remaining command is "link"
if [ "$BSC_COMMAND" != "link" ]; then
  echo "ERROR: unknown command: $BSC_COMMAND" >&2
  exit 1
fi

BSC_SIM_EXECUTABLE=$2    # output executable filename
BSC_TOPLEVEL_MODULE=$3   # toplevel module to simulate

shift 3
BSC_VERILOG_FILES=""     # Verilog files to link
BSC_VHDL_FILES=""        # VHDL files to link
BSC_VERILOG_DIRS=""      # Verilog directories to link
BSC_VPI_FILES=""         # VPI object files to link in
BSC_C_LIB_PATH=""        # where to find C libraries for linking
BSC_C_LIBS=""            # which C files to link in
BSC_CLINK_OPTS=""        # C/C++ link options specified with -Xl in bsc
BSC_VERILOG_DEFS=""      # -D macro and -D macro=val to be passed to Verilog
BSC_VERILOG_OPTS=""      # Verilog link options specified with -Xv in bsc
BSC_OTHER_ARGUMENTS=""

VERBOSE="no"

USEDPI="no"

while [ $# -gt 0 ]; do
  if [ "${1%%.v}" != "$1" ]; then
    BSC_VERILOG_FILES="$BSC_VERILOG_FILES $1"
  elif [ "${1%%.V}" != "$1" ]; then
    BSC_VERILOG_FILES="$BSC_VERILOG_FILES $1"
  elif [ "${1%%.vo}" != "$1" ]; then
    BSC_VERILOG_FILES="$BSC_VERILOG_FILES $1"
  elif [ "${1%%.vqm}" != "$1" ]; then
    BSC_VERILOG_FILES="$BSC_VERILOG_FILES $1"
  elif [ "${1%%.sv}" != "$1" ]; then
    BSC_VERILOG_FILES="$BSC_VERILOG_FILES $1"
  elif [ "${1%%.vhd}" != "$1" ]; then
    BSC_VHDL_FILES="$BSC_VHDL_FILES $1"
  elif [ "${1%%.vhdl}" != "$1" ]; then
    BSC_VHDL_FILES="$BSC_VHDL_FILES $1"
  elif [ "${1%%.o}" != "$1" ]; then
    BSC_VPI_FILES="$BSC_VPI_FILES $1"
  elif [ "$1" = "-L" ]; then
    shift 1
    BSC_C_LIB_PATH="$BSC_C_LIB_PATH -L$1"
  elif [ "$1" = "-y" ]; then
    shift 1
    BSC_VERILOG_DIRS="$BSC_VERILOG_DIRS -y $1"
  elif [ "$1" = "-l" ]; then
    shift 1
    BSC_C_LIBS="$BSC_C_LIBS -l$1"
  elif [ "$1" = "-Xl" ]; then
    shift 1
    BSC_CLINK_OPTS="$BSC_CLINK_OPTS $1"
  elif [ "$1" = "-D" ]; then
    shift 1
    BSC_VERILOG_DEFS="$BSC_VERILOG_DEFS +define+$1"
  elif [ "$1" = "-Xv" ]; then
    shift 1
    BSC_VERILOG_OPTS="$BSC_VERILOG_OPTS $1"
  elif [ "$1" = "-verbose" ]; then
    VERBOSE="yes"
  elif [ "$1" = "-dpi" ]; then
    USEDPI="yes"
  else
    BSC_OTHER_ARGUMENTS="$BSC_OTHER_ARGUMENTS $1";
  fi
  shift 1
done

# name of modelsim work directory
work_dir="work_$BSC_TOPLEVEL_MODULE"

if [ -z "$BSC_SIM_EXECUTABLE" ]; then
  echo "ERROR: simulator executable filename not specified"
  exit 1
fi

if [ -z "$BSC_TOPLEVEL_MODULE" ]; then
  echo "ERROR: no top-level module specified" >&2
  exit 1
fi

if [ -z "$BSC_VERILOG_FILES" ]; then
  echo "ERROR: no Verilog files to link" >&2
  exit 1
fi

if [ -n "$BSC_OTHER_ARGUMENTS" ]; then
  echo "ERROR: unrecognized arguments '$BSC_OTHER_ARGUMENTS'" >&2
  exit 1
fi

# create Modelsim work directory
if [ ! -d $work_dir ]; then
   if [ "$VERBOSE" = "yes" ]; then
     echo "exec: vlib $work_dir"
   fi
   vlib "$work_dir"
   status=$?
   if [ "$status" != "0" ]; then
       echo "ERROR: cannot create Modelsim work directory \"$work_dir\"" >&2
       exit $status
   fi
fi

# path to Verilog files
VSIM_PATH_FLAGS=$BSC_VERILOG_DIRS

######################################################################
# compile Verilog files
if [ "$VERBOSE" = "yes" ]; then
  echo "exec: vlog -sv -work $work_dir +libext+.v+.vqm $VSIM_PATH_FLAGS $BSC_VSIM_FLAGS +define+TOP=$BSC_TOPLEVEL_MODULE $BSC_VERILOG_DEFS $BSC_VERILOG_OPTS $BSC_VERILOG_FILES"
  VLOG_VERBOSE=""
else
  VLOG_VERBOSE="-quiet"
fi
vlog $VLOG_VERBOSE -sv -work $work_dir +libext+.v $VSIM_PATH_FLAGS $BSC_VSIM_FLAGS +define+TOP=$BSC_TOPLEVEL_MODULE $BSC_VERILOG_DEFS $BSC_VERILOG_OPTS $BSC_VERILOG_FILES
status=$?
if [ "$status" != "0" ]; then
    echo "ERROR: cannot compile Verilog files" >&2
    exit $status
fi


######################################################################

if [ "$BSC_VHDL_FILES" != "" ]; then
    if [ "$VERBOSE" = "yes" ]; then
      VCOM_VERBOSE=""
      echo "exec: vcom -work $work_dir $BSC_VHDL_FILES"
    else
      VCOM_VERBOSE="-quiet"
    fi
    vcom $VCOM_VERBOSE -work $work_dir $BSC_VHDL_FILES
    status=$?
    if [ "$status" != "0" ]; then
        echo "ERROR: cannot compile VHDL files" >&2
        exit $status
    fi
fi

######################################################################
# Have the optimizer preserve access to all signals, for VCD dumping

if [ "$VERBOSE" = "yes" ]; then
    VOPT_VERBOSE=""
    echo "exec: vopt -work $work_dir main +acc -o main_opt"
else
    VOPT_VERBOSE="-quiet"
fi
vopt $VOPT_VERBOSE -work $work_dir main +acc -o main_opt
status=$?
if [ "$status" != "0" ]; then
    echo "ERROR: cannot optimize the design" >&2
    exit $status
fi

######################################################################
rm -f $BSC_SIM_EXECUTABLE
status=$?
if [ "$status" != "0" ]; then
    echo "ERROR: cannot create $BSC_SIM_EXECUTABLE" >&2
    exit $status
fi

# include VPI modules if supplied
if [ -n "$BSC_VPI_FILES" ]; then
  if [ "$USEDPI" = "yes" ] ; then
    VPI_LINK="-sv_lib directc_$BSC_TOPLEVEL_MODULE"
    LINK_FLAGS=$BSC_VPI_FILES
  else
    VPI_LINK="-pli ./directc_$BSC_TOPLEVEL_MODULE.so"
    LINK_FLAGS="-Wl,-rpath,$BLUESPECDIR/VPI -L$BLUESPECDIR/VPI $BSC_VPI_FILES -lbdpi"
  fi
  ## If CXX is set, use that.  Otherwise just use c++
  if [ "$CXX" = "" ]
  then
    CXX=c++
  fi
  LDFLAGS="$LDFLAGS $BSC_LDFLAGS"
  SHARED=`$BLUESPECDIR/exec/platform.sh c++_shared_flags`
  if [ "$VERBOSE" = "yes" ]; then
    echo "exec: $CXX -v $LDFLAGS $SHARED -o directc_$BSC_TOPLEVEL_MODULE.so $LINK_FLAGS $BSC_C_LIB_PATH $BSC_C_LIBS $BSC_CLINK_OPTS"
    CXX_VERBOSE="-v"
  else
    CXX_VERBOSE=""
  fi
  $CXX $CXX_VERBOSE $LDFLAGS $SHARED -o directc_$BSC_TOPLEVEL_MODULE.so $LINK_FLAGS $BSC_C_LIB_PATH $BSC_C_LIBS $BSC_CLINK_OPTS
else
  VPI_LINK=""
fi

# generate a "simulator executable" shell script
echo '#!/bin/sh' > $BSC_SIM_EXECUTABLE
echo "vsim -quiet -lib $work_dir $VPI_LINK -do \"run -all; quit\" -c main_opt \$*" >> $BSC_SIM_EXECUTABLE
echo 'status=$?' >> $BSC_SIM_EXECUTABLE
echo 'if [ "$status" != "0" ]; then' >> $BSC_SIM_EXECUTABLE
echo '    echo "ERROR: cannot simulate design" >&2' >> $BSC_SIM_EXECUTABLE
echo '    exit $status' >> $BSC_SIM_EXECUTABLE
echo 'fi' >> $BSC_SIM_EXECUTABLE
chmod +x $BSC_SIM_EXECUTABLE

# echo "Run"
# echo "  ./$BSC_SIM_EXECUTABLE"
# echo "to simulate the design in batch mode or"
# echo "  vsim -lib $work_lib $BSC_TOPLEVEL_MODULE"
# echo "to simulate with the Modelsim GUI"
