#!/bin/bash
# run_synthesis_sweep.sh
# Sweep through Yosys synthesis parameter space and collect timing metrics

# ============================================================================
# CONFIGURATION
# ============================================================================
# Set your design parameters here
export TOP_MODULE="${TOP_MODULE:-counter}"
VERILOG_FILE="${VERILOG_FILE:-../rtl/counter.v}"
LIBERTY_FILE="${LIBERTY_FILE:-../lib/sky130_fd_sc_hd__tt_025C_1v80.lib}"
OUT_DIR="${OUT_DIR:-../build}"
export CLOCK_PORT="${CLOCK_PORT:-clk}"
export CLOCK_PERIOD="${CLOCK_PERIOD:-10.0}"
export INPUT_DELAY="${INPUT_DELAY:-2.0}"
export OUTPUT_DELAY="${OUTPUT_DELAY:-2.0}"

# Note: VERILOG_FILE, LIBERTY_FILE, and OUT_DIR will be converted to absolute paths below

# CSV output file (will be converted to absolute path below)
CSV_OUTPUT="${CSV_OUTPUT:-timing_comparison.csv}"

# Synthesis script
SYN_SCRIPT="syn_advanced.tcl"
STA_SCRIPT="sta.tcl"
TIMING_PARSER="process_timing_metrics.py"

# Parallel execution settings
MAX_PARALLEL_JOBS="${MAX_PARALLEL_JOBS:-10}"

# ============================================================================
# PARAMETER RANGES
# ============================================================================
# Define which parameters to sweep and their ranges
# Format: "param_name:start:end:step" or "param_name:value1,value2,value3"

# OPT_LEVEL: [0-4]
OPT_LEVELS=(0 2 4)

# FLATTEN_MODE: [0-2]
FLATTEN_MODES=(0 2)

# SHARE_LEVEL: [0-3]
SHARE_LEVELS=(0 3)

# ABC_SCRIPT: [0-5]
ABC_SCRIPTS=(0 1 5)

# RETIME_MODE: [0-2]
RETIME_MODES=(0 2)

# ALUMINUM_REDUCE: [0-2]
ALUMINUM_REDUCES=(0 2)

# EXTRACT_MUX: [0-1]
EXTRACT_MUXES=(0 1)

# BOOTH_MULT: [0-1]
BOOTH_MULTS=(0 1)

# ============================================================================
# SWEEP CONTROL
# ============================================================================
# Set to "full" for complete sweep, or "quick" for reduced sweep
SWEEP_MODE="${SWEEP_MODE:-quick}"

if [ "$SWEEP_MODE" = "quick" ]; then
    echo "Running QUICK sweep (reduced parameter space)"
    OPT_LEVELS=(1 2 3)
    FLATTEN_MODES=(0 2)
    SHARE_LEVELS=(1 2)
    ABC_SCRIPTS=(1 2 3 4)
    RETIME_MODES=(0 1)
    ALUMINUM_REDUCES=(1 2)
    EXTRACT_MUXES=(1)
    BOOTH_MULTS=(0)
elif [ "$SWEEP_MODE" = "debug" ]; then
  echo "Running DEBUG sweep (one permutation only.)"
  OPT_LEVELS=(1)
  FLATTEN_MODES=(0)
  SHARE_LEVELS=(1)
  ABC_SCRIPTS=(0 1 2 3 4 5)
  RETIME_MODES=(1)
  ALUMINUM_REDUCES=(1)
  EXTRACT_MUXES=(1)
  BOOTH_MULTS=(0)
else
    echo "Running FULL sweep (complete parameter space)"
fi

# ============================================================================
# SETUP
# ============================================================================
# Get absolute path to script directory
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

# Convert paths to absolute (required for parallel jobs in subdirectories)
# This ensures paths work correctly when jobs change to work directories
if [[ ! "$VERILOG_FILE" = /* ]]; then
    VERILOG_FILE="$SCRIPT_DIR/$VERILOG_FILE"
fi
if [[ ! "$LIBERTY_FILE" = /* ]]; then
    LIBERTY_FILE="$SCRIPT_DIR/$LIBERTY_FILE"
fi
if [[ ! "$OUT_DIR" = /* ]]; then
    OUT_DIR="$SCRIPT_DIR/$OUT_DIR"
fi

# Create output directory and get canonical path
OUT_DIR="$(mkdir -p "$OUT_DIR" && cd "$OUT_DIR" && pwd)"

# Convert CSV_OUTPUT to absolute path if relative
if [[ ! "$CSV_OUTPUT" = /* ]]; then
    CSV_OUTPUT="$SCRIPT_DIR/$CSV_OUTPUT"
fi

# Export paths for use by synthesis and STA tools
export VERILOG_FILE
export LIBERTY_FILE
export OUT_DIR
export CSV_OUTPUT

# Initialize CSV with header
rm -f "$CSV_OUTPUT"
echo "Config,OPT_LEVEL,FLATTEN_MODE,SHARE_LEVEL,ABC_SCRIPT,RETIME_MODE,ALUMINUM_REDUCE,EXTRACT_MUX,BOOTH_MULT,Top_Module,Total_Paths,WNS,TNS,NFE,Sum_Arrival_Required_Ratio,Path_Slacks" > "$CSV_OUTPUT"

# Create a temporary CSV to collect individual run results
TMP_CSV_DIR="$OUT_DIR/tmp_csvs"
mkdir -p "$TMP_CSV_DIR"
rm -f "$TMP_CSV_DIR"/*.csv
export TMP_CSV_DIR

# Create lock file for CSV writing (use absolute path)
CSV_LOCK="$OUT_DIR/.csv_lock"
rm -f "$CSV_LOCK"
export CSV_LOCK

# Counter for progress
total_runs=0
current_run=0

# Calculate total number of runs
for opt in "${OPT_LEVELS[@]}"; do
    for flat in "${FLATTEN_MODES[@]}"; do
        for share in "${SHARE_LEVELS[@]}"; do
            for abc in "${ABC_SCRIPTS[@]}"; do
                for retime in "${RETIME_MODES[@]}"; do
                    for alum in "${ALUMINUM_REDUCES[@]}"; do
                        for mux in "${EXTRACT_MUXES[@]}"; do
                            for booth in "${BOOTH_MULTS[@]}"; do
                                ((total_runs++))
                            done
                        done
                    done
                done
            done
        done
    done
done

echo "=========================================="
echo "Synthesis Parameter Sweep"
echo "=========================================="
echo "Total configurations to test: $total_runs"
echo "Parallel jobs: $MAX_PARALLEL_JOBS"
echo "Top module: $TOP_MODULE"
echo "Output directory: $OUT_DIR"
echo "CSV output: $CSV_OUTPUT"
echo "=========================================="

# ============================================================================
# FUNCTION: Run single configuration
# ============================================================================
run_single_config() {
    local opt=$1
    local flat=$2
    local share=$3
    local abc=$4
    local retime=$5
    local alum=$6
    local mux=$7
    local booth=$8
    local run_number=$9
    local total=${10}

    # Set parameter signature
    PARAM_SIG="opt${opt}_flat${flat}_share${share}_abc${abc}_retime${retime}_alum${alum}_mux${mux}_booth${booth}"

    # Create work directory for this run to avoid file conflicts
    WORK_DIR="$OUT_DIR/work_${PARAM_SIG}"
    mkdir -p "$WORK_DIR"
    cd "$WORK_DIR" || return 1

    echo ""
    echo " ${total} "
    echo "=========================================="
    echo "Run $run_number / $total : $PARAM_SIG"
    echo "=========================================="

    # Export parameters
    export OPT_LEVEL=$opt
    export FLATTEN_MODE=$flat
    export SHARE_LEVEL=$share
    export ABC_SCRIPT=$abc
    export RETIME_MODE=$retime
    export ALUMINUM_REDUCE=$alum
    export EXTRACT_MUX=$mux
    export BOOTH_MULT=$booth
    export FILE_PREFIX="$PARAM_SIG"

    # Run synthesis
    echo "Running synthesis..."
    if ! yosys -v 10 -m slang -c "$SCRIPT_DIR/$SYN_SCRIPT" > "../${PARAM_SIG}_syn.log" 2>&1; then
        echo "ERROR: Synthesis failed for $PARAM_SIG"
        echo "Check log: $OUT_DIR/${PARAM_SIG}_syn.log"
        cd - > /dev/null
        return 1
    fi

    # Update netlist path for STA
    NETLIST_FILE="$OUT_DIR/${TOP_MODULE}_${PARAM_SIG}.syn.v"

    if [ ! -f "$NETLIST_FILE" ]; then
        echo "ERROR: Netlist not found: $NETLIST_FILE"
        cd - > /dev/null
        return 1
    fi

    export NETLIST_FILE

    # Run STA
    echo "Running timing analysis..."
    if ! sta -exit "$SCRIPT_DIR/$STA_SCRIPT" > "../${PARAM_SIG}_sta.log" 2>&1; then
        echo "ERROR: STA failed for $PARAM_SIG"
        echo "Check log: $OUT_DIR/${PARAM_SIG}_sta.log"
        cd - > /dev/null
        return 1
    fi

    # Process timing report to generate metrics CSV
    echo "Processing timing metrics..."
    if [ ! -f "timing_report.txt" ]; then
        echo "ERROR: timing_report.txt not found for $PARAM_SIG"
        cd - > /dev/null
        return 1
    fi

    # Run Python parser to generate timing_metrics.csv
    if ! python3 "$SCRIPT_DIR/$TIMING_PARSER" timing_report.txt "$TOP_MODULE" timing_metrics.csv > "../${PARAM_SIG}_parser.log" 2>&1; then
        echo "ERROR: Timing metrics parser failed for $PARAM_SIG"
        echo "Check log: $OUT_DIR/${PARAM_SIG}_parser.log"
        cd - > /dev/null
        return 1
    fi

    # Save the timing metrics CSV with parameter info to temp location
    if [ -f "timing_metrics.csv" ]; then
        # Create a temporary CSV for this run
        TMP_RUN_CSV="$TMP_CSV_DIR/${PARAM_SIG}.csv"
        echo -n "$PARAM_SIG,$opt,$flat,$share,$abc,$retime,$alum,$mux,$booth," > "$TMP_RUN_CSV"
        tail -1 "timing_metrics.csv" >> "$TMP_RUN_CSV"

        # Append to main CSV with file locking (for real-time monitoring)
        (
            # Acquire lock with timeout
            flock -w 30 200 || exit 1

            # Append the row to the main CSV
            cat "$TMP_RUN_CSV" >> "$CSV_OUTPUT"
        ) 200>"$CSV_LOCK"

        echo "Completed: $PARAM_SIG ($run_number/$total)"
    else
        echo "WARNING: timing_metrics.csv not found for $PARAM_SIG"
        cd - > /dev/null
        return 1
    fi

    cd - > /dev/null
    return 0
}

# Export function so it can be called in subshells
export -f run_single_config
# Note: CSV_OUTPUT and CSV_LOCK are already exported above

# ============================================================================
# MAIN SWEEP LOOP (PARALLELIZED)
# ============================================================================
# Launch jobs in parallel
for opt in "${OPT_LEVELS[@]}"; do
    for flat in "${FLATTEN_MODES[@]}"; do
        for share in "${SHARE_LEVELS[@]}"; do
            for abc in "${ABC_SCRIPTS[@]}"; do
                for retime in "${RETIME_MODES[@]}"; do
                    for alum in "${ALUMINUM_REDUCES[@]}"; do
                        for mux in "${EXTRACT_MUXES[@]}"; do
                            for booth in "${BOOTH_MULTS[@]}"; do
                                ((current_run++))

                                # Wait if we've reached max parallel jobs
                                while [ $(jobs -r | wc -l) -ge $MAX_PARALLEL_JOBS ]; do
                                    sleep 0.5
                                done

                                # Launch job in background
                                run_single_config $opt $flat $share $abc $retime $alum $mux $booth $current_run $total_runs &
                            done
                        done
                    done
                done
            done
        done
    done
done

# Wait for all remaining jobs to complete
echo ""
echo "Waiting for all jobs to complete..."
wait

# ============================================================================
# VERIFY RESULTS
# ============================================================================
echo ""
echo "Verifying results..."

# Count results
completed_runs=$(ls "$TMP_CSV_DIR"/*.csv 2>/dev/null | wc -l | tr -d ' ')
csv_data_rows=$(($(wc -l < "$CSV_OUTPUT") - 1))  # Subtract header

echo "Individual result files: $completed_runs"
echo "CSV data rows: $csv_data_rows"

if [ "$csv_data_rows" -eq "$completed_runs" ]; then
    echo "✓ All results successfully written to CSV"
else
    echo "⚠ Warning: Row count mismatch (expected $completed_runs, got $csv_data_rows)"
fi

# ============================================================================
# SUMMARY
# ============================================================================
echo ""
echo "=========================================="
echo "Sweep Complete!"
echo "=========================================="
echo "Total runs completed: $current_run"
echo "Results saved to: $CSV_OUTPUT"
echo ""
echo "To view best WNS:"
echo "  sort -t',' -k12 -rn $CSV_OUTPUT | head -10"
echo ""
echo "To view best TNS:"
echo "  awk -F',' '\$13 > -999 {print}' $CSV_OUTPUT | sort -t',' -k13 -rn | head -10"
echo ""
echo "=========================================="
