# syn_advanced.tcl - Parameterized Yosys synthesis script
# This script supports various optimization strategies for PPA exploration
yosys -import

# ============================================================================
# SYNTHESIS PARAMETERS - Set these via environment variables
# ============================================================================
# Parameter ranges and defaults:
#
# OPT_LEVEL: General optimization aggressiveness [0-4]
#   0 = minimal, 1 = light, 2 = moderate (default), 3 = aggressive, 4 = extreme
#
# FLATTEN_MODE: Logic flattening strategy [0-2]
#   0 = preserve hierarchy, 1 = selective flatten, 2 = full flatten
#
# SHARE_LEVEL: Resource sharing aggressiveness [0-3]
#   0 = none, 1 = basic, 2 = moderate (default), 3 = aggressive
#
# ABC_SCRIPT: ABC optimization strategy [0-5]
#   0 = area-focused, 1 = balanced-area, 2 = balanced (default),
#   3 = balanced-delay, 4 = delay-focused, 5 = aggressive-delay
#
# RETIME_MODE: Retiming for timing optimization [0-2]
#   0 = disabled, 1 = forward retime, 2 = forward+backward retime
#
# ALUMINUM_REDUCE: Arithmetic optimizer aggressiveness [0-2]
#   0 = disabled, 1 = basic (default), 2 = aggressive
#
# EXTRACT_MUX: Mux extraction for datapath [0-1]
#   0 = disabled, 1 = enabled (default)
#
# BOOTH_MULT: Use Booth encoding for multipliers [0-1]
#   0 = disabled (default), 1 = enabled
#
# ============================================================================

# Read parameters with defaults
if {[info exists ::env(OPT_LEVEL)]} {
    set opt_level $::env(OPT_LEVEL)
} else {
    set opt_level 2
}

if {[info exists ::env(FLATTEN_MODE)]} {
    set flatten_mode $::env(FLATTEN_MODE)
} else {
    set flatten_mode 0
}

if {[info exists ::env(SHARE_LEVEL)]} {
    set share_level $::env(SHARE_LEVEL)
} else {
    set share_level 2
}

if {[info exists ::env(ABC_SCRIPT)]} {
    set abc_script $::env(ABC_SCRIPT)
} else {
    set abc_script 2
}

if {[info exists ::env(RETIME_MODE)]} {
    set retime_mode $::env(RETIME_MODE)
} else {
    set retime_mode 0
}

if {[info exists ::env(ALUMINUM_REDUCE)]} {
    set aluminum_reduce $::env(ALUMINUM_REDUCE)
} else {
    set aluminum_reduce 1
}

if {[info exists ::env(EXTRACT_MUX)]} {
    set extract_mux $::env(EXTRACT_MUX)
} else {
    set extract_mux 1
}

if {[info exists ::env(BOOTH_MULT)]} {
    set booth_mult $::env(BOOTH_MULT)
} else {
    set booth_mult 0
}

# Print configuration
puts "========================================"
puts "Synthesis Configuration:"
puts "========================================"
puts "OPT_LEVEL:        $opt_level"
puts "FLATTEN_MODE:     $flatten_mode"
puts "SHARE_LEVEL:      $share_level"
puts "ABC_SCRIPT:       $abc_script"
puts "RETIME_MODE:      $retime_mode"
puts "ALUMINUM_REDUCE:  $aluminum_reduce"
puts "EXTRACT_MUX:      $extract_mux"
puts "BOOTH_MULT:       $booth_mult"
puts "========================================"

# Read liberty files
read_liberty -lib $::env(LIBERTY_FILE)

# Read Verilog
read_slang --ignore-initial --ignore-timing --top $::env(TOP_MODULE) $::env(VERILOG_FILES)

# Hierarchy and elaboration
hierarchy -check -top $::env(TOP_MODULE)

# ============================================================================
# FLATTENING STRATEGY
# ============================================================================
if {$flatten_mode == 2} {
    puts "INFO: Full flatten enabled"
    flatten
} elseif {$flatten_mode == 1} {
    puts "INFO: Selective flatten enabled"
    # Flatten only modules with arithmetic or large muxes
    flatten -wb
}

# ============================================================================
# INITIAL COARSE-GRAIN SYNTHESIS
# ============================================================================
# Use coarse for arithmetic recognition
#coarse

# ============================================================================
# ARITHMETIC OPTIMIZATION
# ============================================================================
if {$aluminum_reduce > 0} {
    if {$aluminum_reduce == 2} {
        puts "INFO: Aggressive arithmetic reduction"
        alumacc
        opt -fast
        alumacc
    } else {
        puts "INFO: Basic arithmetic reduction"
        alumacc
    }
}

# Booth encoding for multipliers (can improve area/timing for certain multipliers)
if {$booth_mult == 1} {
    puts "INFO: Booth multiplier encoding enabled"
    booth
}

# ============================================================================
# RESOURCE SHARING
# ============================================================================
if {$share_level > 0} {
    if {$share_level == 3} {
        puts "INFO: Aggressive resource sharing"
        share -aggressive
    } elseif {$share_level == 2} {
        puts "INFO: Moderate resource sharing"
        share
    } else {
        puts "INFO: Basic resource sharing"
        share -limit 50
    }
}

# ============================================================================
# MUX EXTRACTION (helps with datapath)
# ============================================================================
if {$extract_mux == 1} {
    puts "INFO: Mux extraction enabled"
    muxcover -mux4 -mux8 -mux16
}

# ============================================================================
# MAIN SYNTHESIS
# ============================================================================
# Fine-grain synthesis
synth -top $::env(TOP_MODULE) -run fine:

# ============================================================================
# OPTIMIZATION PASSES
# ============================================================================
if {$opt_level >= 1} {
    opt -full
}

if {$opt_level >= 2} {
    opt_clean
    opt_expr
    opt_merge
}

if {$opt_level >= 3} {
    opt -full
    opt_clean -purge
    opt_expr -mux_undef -mux_bool
    opt_merge -share_all
}

if {$opt_level >= 4} {
    puts "INFO: Extreme optimization - multiple passes"
    opt -full
    opt -full
    opt_clean -purge
}

# FSM optimization
fsm
fsm_opt

# Memory optimization
memory -nomap
opt -fast

# ============================================================================
# RETIMING (can significantly improve timing for pipelined designs)
# ============================================================================
if {$retime_mode == 1} {
    puts "INFO: Forward retiming enabled"
    # Map memories first
    memory_map
    # Perform retiming
    abc -dff -D 1
    opt
    # Map flip-flops
    dfflibmap -prepare -liberty $::env(LIBERTY_FILE)
    # Perform retiming
    abc -dff -D 1
    abc -liberty $::env(LIBERTY_FILE) -dff
    opt
    dfflibmap -liberty $::env(LIBERTY_FILE)
    # Perform retiming
    abc -liberty $::env(LIBERTY_FILE) -dff
    opt
} elseif {$retime_mode == 2} {
    puts "INFO: Forward and backward retiming enabled"
    memory_map
    abc -dff -D 1
    opt
    dfflibmap -prepare -liberty $::env(LIBERTY_FILE)
    abc -dff -D 1
    abc -liberty $::env(LIBERTY_FILE) -dff
    opt
    # Additional retime pass
    abc -dff -D 1
    abc -liberty $::env(LIBERTY_FILE) -dff
    opt
    dfflibmap -liberty $::env(LIBERTY_FILE)
    abc -liberty $::env(LIBERTY_FILE) -dff
    opt
    # Additional retime pass
    abc -liberty $::env(LIBERTY_FILE) -dff
    opt
}

# ============================================================================
# TECHNOLOGY MAPPING
# ============================================================================

# Map memories if not already mapped
memory_map

# Technology mapping for flip-flops (if not done in retiming)
if {$retime_mode == 0} {
    dfflibmap -liberty $::env(LIBERTY_FILE)
}

# ============================================================================
# ABC MAPPING with different scripts
# ============================================================================
# Build ABC script based on strategy
switch $abc_script {
    0 {
        # Area-focused: minimize area at expense of delay
        puts "INFO: ABC area-focused mapping"
        set abc_cmd "abc -liberty $::env(LIBERTY_FILE) -script \"+strash;fraig;scorr;dc2;dretime;strash;dch,-f;map,-M,1,{D}\""
    }
    1 {
        # Balanced-area: favor area with reasonable timing
        puts "INFO: ABC balanced-area mapping"
        set abc_cmd "abc -liberty $::env(LIBERTY_FILE) -script \"+strash;dc2;map\""
    }
    2 {
        # Balanced: default ABC mapping
        puts "INFO: ABC balanced mapping"
        set abc_cmd "abc -liberty $::env(LIBERTY_FILE)"
    }
    3 {
        # Balanced-delay: favor timing with reasonable area
        puts "INFO: ABC balanced-delay mapping"
        set abc_cmd "abc -liberty $::env(LIBERTY_FILE) -script \"+strash;ifraig;dc2;map,-M,1,{D}\""
    }
    4 {
        # Delay-focused: minimize delay at expense of area
        puts "INFO: ABC delay-focused mapping"
        set abc_cmd "abc -liberty $::env(LIBERTY_FILE) -script \"+strash;ifraig;scorr;dc2;dch,-f;if,-K,6;mfs2\""
    }
    5 {
        # Aggressive-delay: multiple passes for minimum delay
        puts "INFO: ABC aggressive-delay mapping"
        set abc_cmd "abc -liberty $::env(LIBERTY_FILE) -script +\"strash;dch;if,-K,6;mfs2;strash;dch;if,-K,6;mfs2\""
    }
    default {
        set abc_cmd "abc -liberty $::env(LIBERTY_FILE)"
    }
}

eval $abc_cmd

# Post-ABC optimization
opt

# Additional ABC pass for high optimization levels
if {$opt_level >= 3} {
    puts "INFO: Additional ABC refinement pass"
    eval $abc_cmd
    opt
}

# ============================================================================
# FINAL CLEANUP
# ============================================================================
opt_clean
clean

# Hierarchy check
hierarchy -check

# ============================================================================
# STATISTICS AND OUTPUT
# ============================================================================
# Report statistics
stat -liberty $::env(LIBERTY_FILE)

# Build output filename with parameter signature
set param_sig "opt${opt_level}_flat${flatten_mode}_share${share_level}_abc${abc_script}_retime${retime_mode}_alum${aluminum_reduce}_mux${extract_mux}_booth${booth_mult}"

# Write outputs
if {[info exists ::env(FILE_SUFFIX)] && $::env(FILE_SUFFIX) ne ""} {
    set outfile "$::env(OUT_DIR)/$::env(TOP_MODULE)_$::env(FILE_SUFFIX).syn.v"
} else {
    set outfile "$::env(OUT_DIR)/$::env(TOP_MODULE)_${param_sig}.syn.v"
}

write_verilog -noattr $outfile
puts "========================================"
puts "Netlist written to: $outfile"
puts "========================================"

exit
