# =============================================================================
# pdk_config.tcl — PDK Parameter Resolution for OpenROAD Flow
# =============================================================================
# Maps PLATFORM env var to PDK-specific parameters and provides procs to
# source platform scripts from the ORFS installation.
#
# Supported platforms: sky130hs, sky130hd, nangate45, asap7, gf180, ihp-sg13g2
#
# Environment Variables:
#   Required:
#     PLATFORM          — PDK platform identifier (default: sky130hs)
#
#   Optional (override PDK defaults):
#     PLACE_DENSITY     — Placement density (0.0-1.0)
#     CORE_UTILIZATION  — Floorplan utilization %
#     CORE_ASPECT_RATIO — Floorplan aspect ratio
#     CORE_SPACE        — Core boundary spacing
#     CTS_BUF_LIST      — CTS buffer cells (space-separated)
#     SETUP_SLACK_MARGIN — Setup timing repair margin (ps)
#     HOLD_SLACK_MARGIN  — Hold timing repair margin (ps)
#
# Usage:
#   source pdk_config.tcl
#   pdk_source_setrc
#   pdk_source_tracks
#   pdk_source_pdn
#   pdk_source_tapcell
#   pdk_source_fastroute
# =============================================================================

# --- Resolve platform ---
set _pdk_platform [expr {[info exists ::env(PLATFORM)] ? $::env(PLATFORM) : "sky130hs"}]
set _pdk_orfs_base "/tools/OpenROAD-flow-scripts/flow/platforms"
set _pdk_platform_dir "$_pdk_orfs_base/$_pdk_platform"

puts "========================================================"
puts "PDK CONFIGURATION"
puts "========================================================"
puts "Platform:       $_pdk_platform"
puts "Platform Dir:   $_pdk_platform_dir"

# --- Per-PDK parameter tables ---
# Each dict maps: parameter -> value
# These come from analyzing each platform's config.mk

dict set _pdk_params sky130hs {
    PLACE_SITE          unit
    IO_PLACER_H         met3
    IO_PLACER_V         met2
    MIN_ROUTING_LAYER   met1
    MIN_CLK_ROUTING_LAYER met3
    MAX_ROUTING_LAYER   met5
    TAP_CELL_NAME       sky130_fd_sc_hs__tapvpwrvgnd_1
    PLACE_DENSITY       0.50
    CORE_SPACE          10.0
    DONT_USE_CELLS      {}
    FILL_CELLS          {sky130_fd_sc_hs__fill_1 sky130_fd_sc_hs__fill_2 sky130_fd_sc_hs__fill_4 sky130_fd_sc_hs__fill_8}
    PDN_SCRIPT          pdn.tcl
    TAPCELL_SCRIPT      tapcell.tcl
    TRACKS_SCRIPT       make_tracks.tcl
    FASTROUTE_SCRIPT    fastroute.tcl
    SETRC_SCRIPT        setRC.tcl
    MACRO_PLACE_HALO    {40 40}
}

dict set _pdk_params sky130hd {
    PLACE_SITE          unithd
    IO_PLACER_H         met3
    IO_PLACER_V         met2
    MIN_ROUTING_LAYER   met1
    MIN_CLK_ROUTING_LAYER met3
    MAX_ROUTING_LAYER   met5
    TAP_CELL_NAME       sky130_fd_sc_hd__tapvpwrvgnd_1
    PLACE_DENSITY       0.60
    CORE_SPACE          10.0
    DONT_USE_CELLS      {sky130_fd_sc_hd__probe_p_8 sky130_fd_sc_hd__probec_p_8 sky130_fd_sc_hd__lpflow_bleeder_1 sky130_fd_sc_hd__lpflow_clkbufkapwr_1 sky130_fd_sc_hd__lpflow_clkbufkapwr_16 sky130_fd_sc_hd__lpflow_clkbufkapwr_2 sky130_fd_sc_hd__lpflow_clkbufkapwr_4 sky130_fd_sc_hd__lpflow_clkbufkapwr_8 sky130_fd_sc_hd__lpflow_clkinvkapwr_1 sky130_fd_sc_hd__lpflow_clkinvkapwr_16 sky130_fd_sc_hd__lpflow_clkinvkapwr_2 sky130_fd_sc_hd__lpflow_clkinvkapwr_4 sky130_fd_sc_hd__lpflow_clkinvkapwr_8 sky130_fd_sc_hd__lpflow_decapkapwr_12 sky130_fd_sc_hd__lpflow_decapkapwr_3 sky130_fd_sc_hd__lpflow_decapkapwr_4 sky130_fd_sc_hd__lpflow_decapkapwr_6 sky130_fd_sc_hd__lpflow_decapkapwr_8 sky130_fd_sc_hd__lpflow_inputiso0n_1 sky130_fd_sc_hd__lpflow_inputiso0p_1 sky130_fd_sc_hd__lpflow_inputiso1n_1 sky130_fd_sc_hd__lpflow_inputiso1p_1 sky130_fd_sc_hd__lpflow_inputisolatch_1 sky130_fd_sc_hd__lpflow_isobufsrc_1 sky130_fd_sc_hd__lpflow_isobufsrc_16 sky130_fd_sc_hd__lpflow_isobufsrc_2 sky130_fd_sc_hd__lpflow_isobufsrc_4 sky130_fd_sc_hd__lpflow_isobufsrc_8 sky130_fd_sc_hd__lpflow_isobufsrckapwr_16 sky130_fd_sc_hd__lpflow_lsbuf_lh_hl_isowell_tap_1 sky130_fd_sc_hd__lpflow_lsbuf_lh_hl_isowell_tap_2 sky130_fd_sc_hd__lpflow_lsbuf_lh_hl_isowell_tap_4 sky130_fd_sc_hd__lpflow_lsbuf_lh_isowell_4 sky130_fd_sc_hd__lpflow_lsbuf_lh_isowell_tap_1 sky130_fd_sc_hd__lpflow_lsbuf_lh_isowell_tap_2 sky130_fd_sc_hd__lpflow_lsbuf_lh_isowell_tap_4}
    FILL_CELLS          {sky130_fd_sc_hd__fill_1 sky130_fd_sc_hd__fill_2 sky130_fd_sc_hd__fill_4 sky130_fd_sc_hd__fill_8}
    PDN_SCRIPT          pdn.tcl
    TAPCELL_SCRIPT      tapcell.tcl
    TRACKS_SCRIPT       make_tracks.tcl
    FASTROUTE_SCRIPT    fastroute.tcl
    SETRC_SCRIPT        setRC.tcl
    MACRO_PLACE_HALO    {40 40}
}

dict set _pdk_params nangate45 {
    PLACE_SITE          FreePDK45_38x28_10R_NP_162NW_34O
    IO_PLACER_H         metal5
    IO_PLACER_V         metal6
    MIN_ROUTING_LAYER   metal2
    MIN_CLK_ROUTING_LAYER metal4
    MAX_ROUTING_LAYER   metal10
    TAP_CELL_NAME       TAPCELL_X1
    PLACE_DENSITY       0.30
    CORE_SPACE          10.0
    DONT_USE_CELLS      {TAPCELL_X1 FILLCELL_X1 AOI211_X1 OAI211_X1}
    FILL_CELLS          {FILLCELL_X1 FILLCELL_X2 FILLCELL_X4 FILLCELL_X8 FILLCELL_X16 FILLCELL_X32}
    PDN_SCRIPT          grid_strategy-M1-M4-M7.tcl
    TAPCELL_SCRIPT      tapcell.tcl
    TRACKS_SCRIPT       make_tracks.tcl
    FASTROUTE_SCRIPT    fastroute.tcl
    SETRC_SCRIPT        setRC.tcl
    MACRO_PLACE_HALO    {22.4 15.12}
}

dict set _pdk_params asap7 {
    PLACE_SITE          asap7sc7p5t
    IO_PLACER_H         M4
    IO_PLACER_V         M5
    MIN_ROUTING_LAYER   M2
    MIN_CLK_ROUTING_LAYER M4
    MAX_ROUTING_LAYER   M7
    TAP_CELL_NAME       TAPCELL_ASAP7_75t_R
    PLACE_DENSITY       0.60
    CORE_SPACE          2.0
    DONT_USE_CELLS      {*x1p*_ASAP7* *xp*_ASAP7* SDF* ICG*}
    FILL_CELLS          {FILLERxp5_ASAP7_75t_R FILLER_ASAP7_75t_R DECAPx1_ASAP7_75t_R DECAPx2_ASAP7_75t_R DECAPx4_ASAP7_75t_R DECAPx6_ASAP7_75t_R DECAPx10_ASAP7_75t_R}
    PDN_SCRIPT          openRoad/pdn/grid_strategy-M1-M2-M5-M6.tcl
    TAPCELL_SCRIPT      openRoad/tapcell.tcl
    TRACKS_SCRIPT       openRoad/make_tracks.tcl
    FASTROUTE_SCRIPT    fastroute.tcl
    SETRC_SCRIPT        setRC.tcl
    MACRO_PLACE_HALO    {10 10}
    MACRO_ROWS_HALO_X   2
    MACRO_ROWS_HALO_Y   2
}

dict set _pdk_params gf180 {
    PLACE_SITE          GF018hv5v_green_sc9
    IO_PLACER_H         Metal3
    IO_PLACER_V         Metal4
    MIN_ROUTING_LAYER   Metal2
    MIN_CLK_ROUTING_LAYER Metal2
    MAX_ROUTING_LAYER   Metal5
    TAP_CELL_NAME       {}
    PLACE_DENSITY       0.40
    CORE_SPACE          10.0
    DONT_USE_CELLS      {*_1}
    FILL_CELLS          {gf180mcu_fd_sc_mcu9t5v0__fill_64 gf180mcu_fd_sc_mcu9t5v0__fill_32 gf180mcu_fd_sc_mcu9t5v0__fill_16 gf180mcu_fd_sc_mcu9t5v0__fill_8 gf180mcu_fd_sc_mcu9t5v0__fill_4 gf180mcu_fd_sc_mcu9t5v0__fill_2 gf180mcu_fd_sc_mcu9t5v0__fill_1}
    PDN_SCRIPT          openROAD/pdn/pdn_grid_strategy_9t_6M.cfg
    TAPCELL_SCRIPT      openROAD/tapcell.tcl
    TRACKS_SCRIPT       {}
    FASTROUTE_SCRIPT    fastroute.tcl
    SETRC_SCRIPT        setRC.tcl
    MACRO_PLACE_HALO    {10 10}
    METAL_OPTION        5LM_1TM
    CORNER              BC
}

dict set _pdk_params ihp-sg13g2 {
    PLACE_SITE          CoreSite
    IO_PLACER_H         Metal3
    IO_PLACER_V         Metal2
    MIN_ROUTING_LAYER   Metal2
    MIN_CLK_ROUTING_LAYER Metal2
    MAX_ROUTING_LAYER   Metal5
    TAP_CELL_NAME       {}
    PLACE_DENSITY       0.65
    CORE_SPACE          17.5
    DONT_USE_CELLS      {sg13g2_lgcp_1 sg13g2_sighold sg13g2_slgcp_1 sg13g2_dfrbp_2}
    FILL_CELLS          {sg13g2_fill_1 sg13g2_fill_2 sg13g2_decap_4 sg13g2_decap_8}
    PDN_SCRIPT          pdn.tcl
    TAPCELL_SCRIPT      tapcell.tcl
    TRACKS_SCRIPT       make_tracks.tcl
    FASTROUTE_SCRIPT    fastroute.tcl
    SETRC_SCRIPT        setRC.tcl
    MACRO_PLACE_HALO    {40 40}
}

# --- Validate platform ---
if {![dict exists $_pdk_params $_pdk_platform]} {
    set _supported [dict keys $_pdk_params]
    error "Unsupported PLATFORM '$_pdk_platform'. Supported: $_supported"
}

set _pdk_cfg [dict get $_pdk_params $_pdk_platform]

# --- Helper: get PDK param with optional env override ---
proc _pdk_get {key {env_name ""}} {
    global _pdk_cfg
    if {$env_name ne "" && [info exists ::env($env_name)] && $::env($env_name) ne ""} {
        return $::env($env_name)
    }
    return [dict get $_pdk_cfg $key]
}

# --- Set environment variables that platform scripts expect ---
# These must be set BEFORE sourcing platform scripts, because those scripts
# read them via $::env(...)

set ::env(MIN_ROUTING_LAYER)     [_pdk_get MIN_ROUTING_LAYER]
set ::env(MAX_ROUTING_LAYER)     [_pdk_get MAX_ROUTING_LAYER]
set ::env(MIN_CLK_ROUTING_LAYER) [_pdk_get MIN_CLK_ROUTING_LAYER]

set _tap_cell [_pdk_get TAP_CELL_NAME]
if {$_tap_cell ne ""} {
    set ::env(TAP_CELL_NAME) $_tap_cell
}

set _macro_halo [_pdk_get MACRO_PLACE_HALO]
set ::env(MACRO_PLACE_HALO) $_macro_halo

# ASAP7-specific env vars
if {[dict exists $_pdk_cfg MACRO_ROWS_HALO_X]} {
    set ::env(MACRO_ROWS_HALO_X) [dict get $_pdk_cfg MACRO_ROWS_HALO_X]
    set ::env(MACRO_ROWS_HALO_Y) [dict get $_pdk_cfg MACRO_ROWS_HALO_Y]
}

# GF180-specific env vars
if {[dict exists $_pdk_cfg METAL_OPTION]} {
    set ::env(METAL_OPTION) [dict get $_pdk_cfg METAL_OPTION]
}
if {[dict exists $_pdk_cfg CORNER]} {
    if {![info exists ::env(CORNER)]} {
        set ::env(CORNER) [dict get $_pdk_cfg CORNER]
    }
}

# --- Exported variables for use by the flow script ---
set pdk_platform       $_pdk_platform
set pdk_platform_dir   $_pdk_platform_dir
set pdk_place_site     [_pdk_get PLACE_SITE]
set pdk_io_placer_h    [_pdk_get IO_PLACER_H]
set pdk_io_placer_v    [_pdk_get IO_PLACER_V]
set pdk_place_density  [_pdk_get PLACE_DENSITY PLACE_DENSITY]
set pdk_core_space     [_pdk_get CORE_SPACE CORE_SPACE]
set pdk_dont_use       [_pdk_get DONT_USE_CELLS]
set pdk_fill_cells     [_pdk_get FILL_CELLS]

# --- Suppress known PDK-specific warnings ---
if {$_pdk_platform eq "asap7"} {
    # ASAP7 lib files have incomplete timing arcs that trigger STA-1212
    catch {suppress_message STA 1212}
}

puts "Place Site:     $pdk_place_site"
puts "IO Layers:      H=$pdk_io_placer_h V=$pdk_io_placer_v"
puts "Route Layers:   $::env(MIN_ROUTING_LAYER) - $::env(MAX_ROUTING_LAYER)"
puts "Clock Layers:   $::env(MIN_CLK_ROUTING_LAYER) - $::env(MAX_ROUTING_LAYER)"
puts "Place Density:  $pdk_place_density"
puts "========================================================"

# =============================================================================
# Platform script sourcing procs
# =============================================================================

proc pdk_source_setrc {} {
    global _pdk_cfg _pdk_platform_dir
    set script [dict get $_pdk_cfg SETRC_SCRIPT]
    set fpath "$_pdk_platform_dir/$script"
    puts "INFO: Sourcing wire RC from $fpath"
    source $fpath
}

proc pdk_source_pdn {} {
    global _pdk_cfg _pdk_platform_dir
    set script [dict get $_pdk_cfg PDN_SCRIPT]
    set fpath "$_pdk_platform_dir/$script"
    puts "INFO: Sourcing PDN config from $fpath"
    source $fpath
}

proc pdk_source_tapcell {} {
    global _pdk_cfg _pdk_platform_dir
    set script [dict get $_pdk_cfg TAPCELL_SCRIPT]
    set fpath "$_pdk_platform_dir/$script"
    puts "INFO: Sourcing tap cell config from $fpath"
    source $fpath
}

proc pdk_source_tracks {} {
    global _pdk_cfg _pdk_platform_dir
    set script [dict get $_pdk_cfg TRACKS_SCRIPT]
    if {$script eq ""} {
        puts "INFO: No make_tracks script for this PDK, using LEF defaults"
        make_tracks
        return
    }
    set fpath "$_pdk_platform_dir/$script"
    puts "INFO: Sourcing track definitions from $fpath"
    source $fpath
}

proc pdk_source_fastroute {} {
    global _pdk_cfg _pdk_platform_dir
    set script [dict get $_pdk_cfg FASTROUTE_SCRIPT]
    set fpath "$_pdk_platform_dir/$script"
    puts "INFO: Sourcing global routing config from $fpath"
    source $fpath
}
