#!/bin/bash

# Debug mode: set EDA_DEBUG=1 to see verbose output
if [[ "${EDA_DEBUG:-0}" == "1" ]]; then
  set -x  # Print each command before executing
fi

# Display help/usage information
show_help() {
  cat << EOF
Usage: $(basename "$0") <top_module> <verilog_file> [verilog_file2 ...]

Run Yosys synthesis with parameterized optimization, OpenROAD placement,
OpenSTA timing analysis, and generate timing metrics for a Verilog design.

This script supports RL/Optuna parameter exploration via environment variables.

Arguments:
  top_module         Name of the top-level module
  verilog_file       Path to the Verilog RTL file(s)
                     Multiple files can be specified

Environment Variables (for parameter exploration):
  OPT_LEVEL          Optimization aggressiveness [0-4] (default: 2)
  SHARE_LEVEL        Resource sharing level [0-3] (default: 2)
  ABC_SCRIPT         ABC optimization strategy [0-5] (default: 2)
  RETIME_MODE        Retiming mode [0-2] (default: 0)
  FLATTEN_MODE       Flattening strategy [0-2] (default: 0)
  PIPE_STAGES        Pipeline stages (informational, set in RTL)

Environment Variables (for Docker and flow control):
  DOCKER_IMAGE       Docker image name (default: openroad-flow)
  DOCKER_SUDO        Set to "sudo" if Docker requires sudo
  RUN_OPENROAD       Set to 0 to skip OpenROAD and run STA on Yosys output (default: 1)

Options:
  -h, --help         Display this help message and exit

Examples:
  $(basename "$0") my_top_module design.v
  OPT_LEVEL=3 ABC_SCRIPT=4 $(basename "$0") my_top_module design.v

Description:
  This script runs the complete EDA flow with parameterized synthesis:
  1. Yosys synthesis (using syn_retime_rl.tcl with parameters)
  2. OpenROAD placement
  3. OpenSTA timing analysis
  4. Python timing metrics analysis with ASCII histogram

  Results are stored in the tmp_eda directory.

EOF
  exit 0
}

# Check for help flag
if [[ "$1" == "-h" || "$1" == "--help" ]]; then
  show_help
fi

# Check if minimum number of arguments provided
if [ $# -lt 2 ]; then
  echo "Error: Incorrect number of arguments." >&2
  echo "Expected at least 2 arguments (top_module and at least one verilog_file), got $#" >&2
  echo "" >&2
  show_help
fi

# Capture arguments
export TOP_MODULE="${1}"
shift  # Remove first argument, leaving only the file list

# Build the list of Verilog files and check they exist
VERILOG_FILES=""
MISSING_FILES=0

for file in "$@"; do
  if [ ! -f "$file" ]; then
    echo "Error: Verilog file '$file' not found." >&2
    MISSING_FILES=$((MISSING_FILES + 1))
  else
    if [ -z "$VERILOG_FILES" ]; then
      VERILOG_FILES="$file"
    else
      VERILOG_FILES="$VERILOG_FILES $file"
    fi
  fi
done

# Exit if any files are missing
if [ $MISSING_FILES -gt 0 ]; then
  echo "Error: $MISSING_FILES file(s) not found. Exiting." >&2
  exit 1
fi

# Set default values for synthesis parameters if not provided
export OPT_LEVEL="${OPT_LEVEL:-2}"
export SHARE_LEVEL="${SHARE_LEVEL:-2}"
export ABC_SCRIPT="${ABC_SCRIPT:-2}"
export RETIME_MODE="${RETIME_MODE:-0}"
export FLATTEN_MODE="${FLATTEN_MODE:-0}"
export ALUMINUM_REDUCE="${ALUMINUM_REDUCE:-1}"
export EXTRACT_MUX="${EXTRACT_MUX:-1}"
export BOOTH_MULT="${BOOTH_MULT:-0}"

export LIBERTY_FILE=/tools/OpenROAD-flow-scripts/flow/platforms/sky130hs/lib/sky130_fd_sc_hs__tt_025C_1v80.lib
export LEF_TECH_FILE=/tools/OpenROAD-flow-scripts/flow/platforms/sky130hs/lef/sky130_fd_sc_hs.tlef
export LEF_FILE=/tools/OpenROAD-flow-scripts/flow/platforms/sky130hs/lef/sky130_fd_sc_hs_merged.lef
export TOP_DIR=${PWD}
export OUT_DIR=${TOP_DIR}/tmp_eda

# Docker and flow control settings
export DOCKER_IMAGE="${DOCKER_IMAGE:-openroad-flow}"
export RUN_OPENROAD="${RUN_OPENROAD:-1}"

# Use TRIAL_ID if provided (from Optuna), otherwise generate random suffix for standalone runs
if [ -n "${TRIAL_ID}" ]; then
  export FILE_PREFIX="${TOP_MODULE}_trial${TRIAL_ID}"
else
  # Generate random suffix - compatible with both macOS and Linux
  # macOS mktemp supports --dry-run, Linux mktemp does not
  if mktemp --dry-run XXXXX >/dev/null 2>&1; then
    RAND_POSTFIX=$(mktemp --dry-run XXXXX)
  else
    # Linux fallback: use $RANDOM or /dev/urandom
    RAND_POSTFIX=$(head /dev/urandom | tr -dc 'A-Za-z0-9' | head -c 5)
  fi
  export FILE_PREFIX="${TOP_MODULE}_${RAND_POSTFIX}"
fi

# Suppress verbose output for cleaner parallel execution
# echo "Running RL-parameterized EDA flow for design: ${TOP_MODULE}"
# echo "Using RTL file(s): ${VERILOG_FILES}"
# echo "Synthesis parameters:"
# echo "  OPT_LEVEL=${OPT_LEVEL}, SHARE_LEVEL=${SHARE_LEVEL}, ABC_SCRIPT=${ABC_SCRIPT}"
# echo "  RETIME_MODE=${RETIME_MODE}, FLATTEN_MODE=${FLATTEN_MODE}"
# echo "Flow: Yosys (parameterized) → OpenROAD → OpenSTA → Timing Analysis"
# echo ""

# Build Docker command with optional sudo
DOCKER_CMD="${DOCKER_SUDO:+${DOCKER_SUDO} }docker"

# Run the EDA flow inside Docker and capture individual tool exit codes
# Note: Using -i without -t to avoid TTY corruption in parallel runs
# Use DOCKER_SUDO if set (for non-rootless Docker systems like AWS)
# Assumes the project is mounted at the same path inside the container
${DOCKER_CMD} run --rm --user "$(id -u)":"$(id -g)" \
  --cpus 1 --memory "10000m" \
  -v "${TOP_DIR}":"${TOP_DIR}":ro -v "${OUT_DIR}":"${OUT_DIR}" -w "${TOP_DIR}" \
  -e LIBERTY_FILE=${LIBERTY_FILE} \
  -e LEF_TECH_FILE=${LEF_TECH_FILE} \
  -e LEF_FILE=${LEF_FILE} \
  -e FILE_PREFIX="${FILE_PREFIX}" \
  -e OUT_DIR=${OUT_DIR} \
  -e VERILOG_FILES="${VERILOG_FILES}" \
  -e TOP_MODULE=${TOP_MODULE} \
  -e PIPE_STAGES=${PIPE_STAGES:-1} \
  -e OPT_LEVEL=${OPT_LEVEL} \
  -e SHARE_LEVEL=${SHARE_LEVEL} \
  -e ABC_SCRIPT=${ABC_SCRIPT} \
  -e RETIME_MODE=${RETIME_MODE} \
  -e FLATTEN_MODE=${FLATTEN_MODE} \
  -e ALUMINUM_REDUCE=${ALUMINUM_REDUCE} \
  -e EXTRACT_MUX=${EXTRACT_MUX} \
  -e BOOTH_MULT=${BOOTH_MULT} \
  -e RUN_OPENROAD=${RUN_OPENROAD} \
  ${DOCKER_IMAGE} /bin/bash -c "
    mkdir -p ${OUT_DIR}

    # Function to convert seconds to HH:MM:SS format
    format_time() {
      local seconds=\$1
      printf '%02d:%02d:%02d' \$((seconds/3600)) \$((seconds%3600/60)) \$((seconds%60))
    }

    # Initialize status tracking
    YOSYS_STATUS=0
    OPENROAD_STATUS=0
    OPENSTA_STATUS=0
    PYTHON_STATUS=0
    YOSYS_TIME=0
    OPENROAD_TIME=0
    OPENSTA_TIME=0
    PYTHON_TIME=0

    echo \"========================================\" > ${OUT_DIR}/pnr_${FILE_PREFIX}.log
    echo \"RL Synthesis Parameters:\" >> ${OUT_DIR}/pnr_${FILE_PREFIX}.log
    echo \"  OPT_LEVEL=${OPT_LEVEL}\" >> ${OUT_DIR}/pnr_${FILE_PREFIX}.log
    echo \"  SHARE_LEVEL=${SHARE_LEVEL}\" >> ${OUT_DIR}/pnr_${FILE_PREFIX}.log
    echo \"  ABC_SCRIPT=${ABC_SCRIPT}\" >> ${OUT_DIR}/pnr_${FILE_PREFIX}.log
    echo \"  RETIME_MODE=${RETIME_MODE}\" >> ${OUT_DIR}/pnr_${FILE_PREFIX}.log
    echo \"  FLATTEN_MODE=${FLATTEN_MODE}\" >> ${OUT_DIR}/pnr_${FILE_PREFIX}.log
    echo \"  PIPE_STAGES=${PIPE_STAGES:-1}\" >> ${OUT_DIR}/pnr_${FILE_PREFIX}.log
    echo \"========================================\" >> ${OUT_DIR}/pnr_${FILE_PREFIX}.log
    echo \"Starting yosys with verilog file: $VERILOG_FILES\" >> ${OUT_DIR}/pnr_${FILE_PREFIX}.log

    # Run Yosys with parameterized synthesis script (silent for parallel runs)
    YOSYS_START=\$(date +%s)
    yosys -m slang -c ${TOP_DIR}/eda_flow/syn_retime_rl.tcl >> ${OUT_DIR}/pnr_${FILE_PREFIX}.log 2>&1
    YOSYS_STATUS=\$?
    YOSYS_END=\$(date +%s)
    YOSYS_TIME=\$((YOSYS_END - YOSYS_START))

    # Check if Yosys succeeded
    if [ \$YOSYS_STATUS -eq 0 ]; then

      # Check if synthesis output exists
      if [ -f ${OUT_DIR}/${TOP_MODULE}_${FILE_PREFIX}.syn.v ]; then

        # Conditionally run OpenROAD based on RUN_OPENROAD env var
        if [ \"\${RUN_OPENROAD}\" = \"1\" ]; then
          OPENROAD_START=\$(date +%s)
          openroad -exit ${TOP_DIR}/eda_flow/pnr_basic.tcl >> ${OUT_DIR}/pnr_${FILE_PREFIX}.log 2>&1
          OPENROAD_STATUS=\$?
          OPENROAD_END=\$(date +%s)
          OPENROAD_TIME=\$((OPENROAD_END - OPENROAD_START))
        else
          # Skip OpenROAD - mark as success
          echo \"Skipping OpenROAD (RUN_OPENROAD=0)\" >> ${OUT_DIR}/pnr_${FILE_PREFIX}.log
          OPENROAD_STATUS=0
          OPENROAD_TIME=0
        fi

        if [ \$OPENROAD_STATUS -eq 0 ]; then
          # Run OpenSTA timing analysis
          OPENSTA_START=\$(date +%s)

          # Set environment variables needed by sta.tcl
          # Use Yosys output netlist directly (works for both RUN_OPENROAD=0 and RUN_OPENROAD=1)
          export NETLIST_FILE=${OUT_DIR}/${TOP_MODULE}_${FILE_PREFIX}.syn.v
          export CLOCK_PERIOD=1.0
          export CLOCK_PORT=clk
          export INPUT_DELAY=0.4
          export OUTPUT_DELAY=0.4

          sta -exit ${TOP_DIR}/eda_flow/sta.tcl >> ${OUT_DIR}/pnr_${FILE_PREFIX}.log 2>&1
          OPENSTA_STATUS=\$?
          OPENSTA_END=\$(date +%s)
          OPENSTA_TIME=\$((OPENSTA_END - OPENSTA_START))

          if [ \$OPENSTA_STATUS -eq 0 ]; then

            # Check if timing report was generated (unique filename to prevent parallel clobbering)
            export TIMING_REPORT_FILE=${OUT_DIR}/timing_report_${FILE_PREFIX}.txt
            if [ -f \${TIMING_REPORT_FILE} ]; then
              PYTHON_START=\$(date +%s)

              # Run Python analysis script
              export OUTPUT_CSV=${OUT_DIR}/timing_metrics_${FILE_PREFIX}.csv

              python3 ${TOP_DIR}/eda_flow/process_timing_metrics.py >> ${OUT_DIR}/pnr_${FILE_PREFIX}.log 2>&1
              PYTHON_STATUS=\$?
              PYTHON_END=\$(date +%s)
              PYTHON_TIME=\$((PYTHON_END - PYTHON_START))

              if [ \$PYTHON_STATUS -eq 0 ]; then
                # Clean up temporary timing reports
                rm -f timing_report_${FILE_PREFIX}.txt
                rm -f timing_report_pipe_${FILE_PREFIX}.txt
              fi
            else
              OPENSTA_STATUS=1
            fi
          fi
        fi
      else
        YOSYS_STATUS=1
      fi
    fi

    # Exit with failure if any tool failed (silent for parallel runs)
    if [ \$YOSYS_STATUS -ne 0 ] || [ \$OPENROAD_STATUS -ne 0 ] || [ \$OPENSTA_STATUS -ne 0 ] || [ \$PYTHON_STATUS -ne 0 ]; then
      exit 1
    else
      exit 0
    fi
  "

# Capture docker exec exit code
DOCKER_EXIT_CODE=$?

# Silent exit for parallel runs - status shown by optimizer
exit $DOCKER_EXIT_CODE
