# Copyright 2023 Efabless Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from decimal import Decimal
from typing import List, Optional, Dict, Union

from .variable import Variable, Macro
from ..common import Path


pdk_variables = [
    # Core/Common
    Variable(
        "STD_CELL_LIBRARY",
        str,
        "Specifies the default standard cell library to be used under the specified PDK.",
        pdk=True,
    ),
    Variable(
        "VDD_PIN",
        str,
        "The power pin for the cells.",
        pdk=True,
    ),
    Variable(
        "GND_PIN",
        str,
        "The ground pin for the cells.",
        pdk=True,
    ),
    Variable(
        "WIRE_LENGTH_THRESHOLD",
        Optional[Decimal],
        "A value above which wire lengths generate warnings.",
        units="µm",
        pdk=True,
    ),
    Variable(
        "TECH_LEFS",
        Dict[str, Path],
        "Map of corner patterns to to technology LEF files. A corner not matched here will not be supported by OpenRCX in the default flow.",
        pdk=True,
    ),
    Variable(
        "GPIO_PADS_LEF",
        Optional[List[Path]],
        "Path(s) to GPIO pad LEF file(s).",
        pdk=True,
    ),
    Variable(
        "GPIO_PADS_LEF_CORE_SIDE",
        Optional[List[Path]],
        "Path(s) to GPIO pad LEF file(s) as used for routing (?).",
        pdk=True,
    ),
    Variable(
        "GPIO_PADS_VERILOG",
        Optional[List[Path]],
        "Path(s) to GPIO pad Verilog models.",
        pdk=True,
    ),
    Variable(
        "GPIO_PADS_PREFIX",
        Optional[List[str]],
        "A list of pad cell name prefixes.",
        pdk=True,
    ),
    Variable(
        "PRIMARY_SIGNOFF_TOOL",
        str,
        "Specify the primary signoff tool for taping out with this PDK. For most open-source PDKs, that would be 'magic'.",
        pdk=True,
    ),
    # Timing and Power
    Variable(
        "DEFAULT_MAX_TRAN",
        Optional[Decimal],
        "Defines the default maximum transition value used in Synthesis and CTS.\nA minimum of 0.1 * CLOCK_PERIOD and this variable, if defined, is used.",
        units="ns",
        pdk=True,
    ),
    Variable(
        "DATA_WIRE_RC_LAYER",
        Optional[str],
        "A metal layer with which to estimate parasitics for data nets in earlier stages of the flow.",
        pdk=True,
        deprecated_names=["WIRE_RC_LAYER"],
    ),
    Variable(
        "CLOCK_WIRE_RC_LAYER",
        Optional[str],
        "A metal layer with which to estimate parasitics for clock nets in earlier stages of the flow.",
        pdk=True,
    ),
    Variable(
        "DEFAULT_CORNER",
        str,
        "The interconnect/process/voltage/temperature corner (IPVT) to use the characterized lib files compatible with by default.",
        pdk=True,
    ),
    Variable(
        "STA_CORNERS",
        List[str],
        "A list of fully qualified IPVT (Interconnect, transistor Process, Voltage, and Temperature) timing corners on which to conduct multi-corner static timing analysis.",
        pdk=True,
    ),
    # Floorplanning
    Variable(
        "FP_TRACKS_INFO",
        Path,
        "A path to the a classic OpenROAD `.tracks` file. Used by the floorplanner to generate tracks.",
        deprecated_names=["TRACKS_INFO_FILE"],
        pdk=True,
    ),
    Variable(
        "FP_TAPCELL_DIST",
        Decimal,
        "The distance between tap cell columns.",
        units="µm",
        pdk=True,
    ),
    Variable(
        "FP_PDN_RAIL_OFFSET",
        Decimal,
        "The offset for the power distribution network rails for first metal layer.",
        units="µm",
        pdk=True,
    ),
    Variable(
        "FP_PDN_VWIDTH",
        Decimal,
        "The strap width for the vertical layer in generated power distribution networks.",
        units="µm",
        pdk=True,
    ),
    Variable(
        "FP_PDN_VSPACING",
        Decimal,
        "The spacing between vertical straps in generated power distribution networks.",
        units="µm",
        pdk=True,
    ),
    Variable(
        "FP_PDN_HSPACING",
        Decimal,
        "The spacing between horizontal straps in generated power distribution networks.",
        units="µm",
        pdk=True,
    ),
    Variable(
        "FP_PDN_HWIDTH",
        Decimal,
        "The strap width for the horizontal layer in generated power distribution networks.",
        units="µm",
        pdk=True,
    ),
    Variable(
        "FP_PDN_CORE_RING_VWIDTH",
        Decimal,
        "The width for the vertical layer in the core ring of generated power distribution networks.",
        units="µm",
        pdk=True,
    ),
    Variable(
        "FP_PDN_CORE_RING_HWIDTH",
        Decimal,
        "The width for the horizontal layer in the core ring of generated power distribution networks.",
        units="µm",
        pdk=True,
    ),
    Variable(
        "FP_PDN_CORE_RING_VSPACING",
        Decimal,
        "The spacing for the vertical layer in the core ring of generated power distribution networks.",
        units="µm",
        pdk=True,
    ),
    Variable(
        "FP_PDN_CORE_RING_HSPACING",
        Decimal,
        "The spacing for the horizontal layer in the core ring of generated power distribution networks.",
        units="µm",
        pdk=True,
    ),
    Variable(
        "FP_PDN_CORE_RING_VOFFSET",
        Decimal,
        "The offset for the vertical layer in the core ring of generated power distribution networks.",
        units="µm",
        pdk=True,
    ),
    Variable(
        "FP_PDN_CORE_RING_HOFFSET",
        Decimal,
        "The offset for the horizontal layer in the core ring of generated power distribution networks.",
        units="µm",
        pdk=True,
    ),
    Variable(
        "FP_IO_HLAYER",
        str,
        "The metal layer on which to place horizontal IO pins, i.e., the top and bottom of the die.",
        pdk=True,
    ),
    Variable(
        "FP_IO_VLAYER",
        str,
        "The metal layer on which to place vertial IO pins, i.e., the top and bottom of the die.",
        pdk=True,
    ),
    Variable("RT_MIN_LAYER", str, "The lowest metal layer to route on.", pdk=True),
    Variable("RT_MAX_LAYER", str, "The highest metal layer to route on.", pdk=True),
]

scl_variables = [
    # Common
    Variable(
        "SCL_GROUND_PINS",
        List[str],
        "SCL-specific ground pins",
        deprecated_names=["STD_CELL_GROUND_PINS"],
        pdk=True,
    ),
    Variable(
        "SCL_POWER_PINS",
        List[str],
        "SCL-specific power pins",
        deprecated_names=["STD_CELL_POWER_PINS"],
        pdk=True,
    ),
    Variable(
        "FILL_CELL",
        List[str],
        "A list of cell names or wildcards of fill cells to be used in fill insertion.",
        pdk=True,
    ),
    Variable(
        "DECAP_CELL",
        List[str],
        "A list of cell names or wildcards of decap cells to be used in fill insertion.",
        pdk=True,
    ),
    Variable(
        "LIB",
        Dict[str, List[Path]],
        "A map from corner patterns to a list of associated liberty files. Exactly one entry must match the `DEFAULT_CORNER`.",
        pdk=True,
    ),
    Variable(
        "CELL_LEFS",
        List[Path],
        "Path(s) to the cells' LEF file(s).",
        deprecated_names=["CELLS_LEF"],
        pdk=True,
    ),
    Variable(
        "CELL_GDS",
        List[Path],
        "Path(s) to the cells' GDSII file(s).",
        deprecated_names=["GDS_FILES", "CELLS_GDS"],
        pdk=True,
    ),
    Variable(
        "CELL_SPICE_MODELS",
        Optional[List[Path]],
        "Path(s) to cells' SPICE model(s)",
        pdk=True,
    ),
    Variable(
        "SYNTH_EXCLUSION_CELL_LIST",
        Path,
        "Path to a text file containing a list of cells to be excluded from the lib file in synthesis alone. If not defined, the original lib file will be used as-is.",
        deprecated_names=["NO_SYNTH_CELL_LIST"],
        pdk=True,
    ),
    Variable(
        "PNR_EXCLUSION_CELL_LIST",
        Path,
        "Path to a text file containing a list of undesirable or bad (DRC-failed or complex pinout) cells to be excluded from synthesis AND PnR. If not defined, all cells will be used.",
        deprecated_names=["DRC_EXCLUDE_CELL_LIST"],
        pdk=True,
    ),
    # Constraints
    Variable(
        "OUTPUT_CAP_LOAD",
        Decimal,
        "Defines the capacitive load on the output ports.",
        units="fF",
        deprecated_names=["SYNTH_CAP_LOAD"],
        pdk=True,
    ),
    Variable(
        "MAX_FANOUT_CONSTRAINT",
        int,
        "The max load that the output ports can drive to be used as a constraint on Synthesis and CTS.",
        units="cells",
        deprecated_names=["SYNTH_MAX_FANOUT"],
        pdk=True,
    ),
    Variable(
        "MAX_TRANSITION_CONSTRAINT",
        Optional[Decimal],
        "The max transition time (slew) from high to low or low to high on cell inputs in ns to be used as a constraint on Synthesis and CTS. If not provided, it is calculated at runtime as `10%` of the provided clock period, unless that exceeds the PDK's `DEFAULT_MAX_TRAN` value.",
        units="ns",
        deprecated_names=["SYNTH_MAX_TRAN"],
        pdk=True,
    ),
    Variable(
        "CLOCK_UNCERTAINTY_CONSTRAINT",
        Decimal,
        "Specifies a value for the clock uncertainty/jitter for timing analysis.",
        units="ns",
        deprecated_names=["SYNTH_CLOCK_UNCERTAINTY"],
        pdk=True,
    ),
    Variable(
        "CLOCK_TRANSITION_CONSTRAINT",
        Decimal,
        "Specifies a value for the clock transition/slew for timing analysis.",
        units="ns",
        deprecated_names=["SYNTH_CLOCK_TRANSITION"],
        pdk=True,
    ),
    Variable(
        "TIME_DERATING_CONSTRAINT",
        Decimal,
        "Specifies a derating factor to multiply the path delays with. It specifies the upper and lower ranges of timing.",
        units="%",
        deprecated_names=["SYNTH_TIMING_DERATE"],
        pdk=True,
    ),
    Variable(
        "IO_DELAY_CONSTRAINT",
        Decimal,
        "Specifies the percentage of the clock period used in the input/output delays.",
        units="%",
        deprecated_names=["IO_PCT"],
        pdk=True,
    ),
    # Synthesis
    Variable(
        "SYNTH_DRIVING_CELL",
        str,
        "The cell to drive the input ports, used in synthesis and static timing analysis, in the format `{cell}/{port}`.",
        pdk=True,
    ),
    Variable(
        "SYNTH_CLK_DRIVING_CELL",
        Optional[str],
        "The cell to drive the clock input ports, used in synthesis and static timing analysis, in the format `{cell}/{port}`. If not specified, `SYNTH_DRIVING_CELL` will be used.",
        pdk=True,
    ),
    Variable(
        "SYNTH_TIEHI_CELL",
        str,
        "Defines the tie high cell followed by the port that implements the tie high functionality, in the format `{cell}/{port}`.",
        pdk=True,
    ),
    Variable(
        "SYNTH_TIELO_CELL",
        str,
        "Defines the tie high cell followed by the port that implements the tie low functionality, in the format `{cell}/{port}`.",
        pdk=True,
    ),
    Variable(
        "SYNTH_BUFFER_CELL",
        str,
        "Defines a buffer port to be used by yosys during synthesis: in the format `{cell}/{input_port}/{output_port}`",
        pdk=True,
    ),
    # Clock Tree Synthesis
    Variable(
        "CTS_ROOT_BUFFER",
        str,
        "Defines the cell inserted at the root of the clock tree. Used in CTS.",
        pdk=True,
    ),
    Variable(
        "CTS_CLK_BUFFERS",
        List[str],
        "Defines the list of clock buffers to be used in CTS.",
        deprecated_names=["CTS_CLK_BUFFER_LIST"],
        pdk=True,
    ),
    Variable(
        "CTS_MAX_CAP",
        Decimal,
        "Defines the maximum capacitance, used in CTS.",
        units="pF",
        pdk=True,
    ),
    # Floorplanning
    Variable(
        "FP_WELLTAP_CELL",
        str,
        "Defines the cell used for tap insertion.",
        pdk=True,
    ),
    Variable(
        "FP_ENDCAP_CELL",
        str,
        "Defines so-called 'end-cap' cells- decap cells placed at either sides of a design.",
        pdk=True,
    ),
    Variable(
        "FP_PDN_RAIL_LAYER",
        str,
        "Defines the metal layer used for PDN rails.",
        deprecated_names=["FP_PDN_RAILS_LAYER"],
        pdk=True,
    ),
    Variable(
        "FP_PDN_RAIL_WIDTH",
        Decimal,
        "Defines the width of PDN rails on the `FP_PDN_RAILS_LAYER` layer.",
        units="µm",
        pdk=True,
    ),
    Variable(
        "FP_PDN_HORIZONTAL_LAYER",
        str,
        "Defines the horizontal PDN layer.",
        deprecated_names=["FP_PDN_UPPER_LAYER"],
        pdk=True,
    ),
    Variable(
        "FP_PDN_VERTICAL_LAYER",
        str,
        "Defines the vertical PDN layer.",
        deprecated_names=["FP_PDN_LOWER_LAYER"],
        pdk=True,
    ),
    Variable(
        "IGNORE_DISCONNECTED_MODULES",
        Optional[List[str]],
        "Modules (or cells) to ignore when checking for disconnected pins.",
        pdk=True,
    ),
    # Placement
    Variable(
        "PLACE_SITE",
        str,
        "Defines the main placement site in placement as specified in the technology LEF files, to generate the placement grid.",
        pdk=True,
    ),
    Variable(
        "PLACE_SITE_WIDTH",
        Decimal,
        "The site width for the previously designated place site.",
        units="µm",
        pdk=True,
    ),
    Variable(
        "PLACE_SITE_HEIGHT",
        Decimal,
        "The site height for the previously designated place site.",
        units="µm",
        pdk=True,
    ),
    Variable(
        "GPL_CELL_PADDING",
        Decimal,
        "Cell padding value (in sites) for global placement. The number will be integer divided by 2 and placed on both sides.",
        units="sites",
        pdk=True,
    ),
    Variable(
        "DPL_CELL_PADDING",
        Decimal,
        "Cell padding value (in sites) for detailed placement. The number will be integer divided by 2 and placed on both sides. Should be <= global placement.",
        units="sites",
        pdk=True,
    ),
    Variable(
        "CELL_PAD_EXCLUDE",
        List[str],
        "Defines a list of cells to be excluded from cell padding.",
        pdk=True,
    ),
    # Antenna
    Variable(
        "DIODE_CELL",
        Optional[str],
        "Defines a diode cell used to fix antenna violations, in the format {name}/{port}.",
        pdk=True,
    ),
    # Routing
    Variable(
        "GRT_LAYER_ADJUSTMENTS",
        List[Decimal],
        "Layer-specific reductions in the routing capacity of the edges between the cells in the global routing graph, delimited by commas. Values range from 0 through 1.",
        pdk=True,
    ),
    # CVC
    Variable(
        "CVC_SCRIPTS_DIR",
        Optional[Path],
        "Path to a directory of Circuit Validity Checker (CVC) scripts for the relevant PDK. Must contain the following set of files: `cvcrc`, an initialization file, `cdl.awk`, an awk script to remove black box definitions from SPICE files, `models`, cell models, and finally `power.awk`, an awk script that adds power information to the verilog netlists.\nIf this path is not defined, this PDK will be marked incompatible with CVC.",
        pdk=True,
    ),
]
option_variables = [
    # Common
    Variable(
        "DESIGN_DIR",
        Path,
        "The directory of the design. Does not need to be provided explicitly.",
    ),
    Variable(
        "DESIGN_NAME",
        str,
        "The name of the top level module of the design. This is the only required variable for all steps and all flows.",
    ),
    Variable(
        "PDK_ROOT",
        str,
        "The path to all PDKs. This variable is special and tools should typically handle them.",
    ),
    Variable(
        "PDK",
        str,
        "Specifies the process design kit (PDK).",
        default="sky130A",
    ),
    Variable(
        "CLOCK_PERIOD",
        Decimal,
        "The clock period for the design.",
        units="ns",
        default=10.0,
    ),
    Variable(
        "CLOCK_PORT",
        Union[None, str, List[str]],
        "The name(s) of the design's clock port(s).",
    ),
    Variable(
        "CLOCK_NET",
        Union[None, str, List[str]],
        "The name of the net input to root clock buffer. If unset, it is presumed to be equal to CLOCK_PORT.",
    ),
    Variable(
        "VDD_NETS",
        Optional[List[str]],
        "Specifies the power nets/pins to be used when creating the power grid for the design.",
    ),
    Variable(
        "GND_NETS",
        Optional[List[str]],
        "Specifies the ground nets/pins to be used when creating the power grid for the design.",
    ),
    Variable(
        "DIE_AREA",
        Optional[str],
        'Specific die area to be used in floorplanning when `FP_SIZING` is set to `absolute`. Specified as a 4-corner rectangle "x0 y0 x1 y1".',
        units="µm",
    ),
    # Macros
    Variable(
        "MACROS",
        Optional[Dict[str, Macro]],
        "A dictionary of Macro definition objects. See {py:class}`openlane.config.Macro` for more info.",
    ),
    Variable(
        "EXTRA_LEFS",
        Optional[List[Path]],
        "Specifies miscellaneous LEF files to be loaded indiscriminately whenever LEFs are loaded.",
    ),
    Variable(
        "EXTRA_VERILOG_MODELS",
        Optional[List[Path]],
        "Specifies miscellaneous Verilog models to be loaded indiscriminately during synthesis.",
        deprecated_names=["VERILOG_FILES_BLACKBOX"],
    ),
    Variable(
        "EXTRA_SPICE_MODELS",
        Optional[List[Path]],
        "Specifies miscellaneous SPICE models to be loaded indiscriminately whenever SPICE models are loaded.",
    ),
    Variable(
        "EXTRA_LIBS",
        Optional[List[Path]],
        "Specifies LIB files of pre-hardened macros used in the current design, used during timing analyses (and during parasitics-based STA as a fallback). These are loaded indiscriminately for all timing corners.",
    ),
    Variable(
        "EXTRA_GDS_FILES",
        Optional[List[Path]],
        "Specifies GDS files of pre-hardened macros used in the current design, used during tape-out.",
    ),
    # Unimplemented - To be moved to steps
    Variable(
        "FP_CONTEXT_DEF",
        Optional[Path],
        "Points to the parent DEF file that includes this macro/design and uses this DEF file to determine the best locations for the pins. It must be used with `FP_CONTEXT_LEF`, otherwise it's considered non-existing. If not set, then the IO pins will be placed based on one of the other methods depending on the rest of the configurations.",
    ),
    Variable(
        "FP_CONTEXT_LEF",
        Optional[Path],
        "Points to the parent LEF file that includes this macro/design and uses this LEF file to determine the best locations for the pins. It must be used with `FP_CONTEXT_DEF`, otherwise it's considered non-existing. If not set, then the IO pins will be placed based on one of the other methods depending on the rest of the configurations.",
    ),
    Variable(
        "FP_PADFRAME_CFG",
        Optional[str],
        "A configuration file passed to `padringer`, a padframe generator.",
    ),
    Variable(
        "GRT_OBS",
        Optional[List[str]],
        'Specifies custom obstruction to be added prior to global routing. List of layer and coordinates: `layer llx lly urx ury`, where `ll` and `ur` stand for "lower left" and "upper right" respectively. (Example: `li1 0 100 1000 300, met5 0 0 1000 500`).',
    ),
    Variable(
        "LVS_INSERT_POWER_PINS",
        bool,
        "Enables power pin insertion before running LVS.",
        default=True,
    ),
    Variable(
        "RUN_CVC",
        bool,
        "Runs the Circuit Validity Checker on the output spice, which is a voltage-aware ERC checker for CDL netlists. Will not run unless supported by the current PDK.",
        default=True,
    ),
    Variable(
        "LEC_ENABLE",
        bool,
        "Enables logic verification using yosys, for comparing each netlist at each stage of the flow with the previous netlist and verifying that they are logically equivalent. Warning: this will increase the runtime significantly.",
        default=False,
    ),
    Variable(
        "CHECK_ASSIGN_STATEMENTS",
        bool,
        "Checks for assign statement in the generated gate level netlist and aborts if any were found.",
        default=False,
    ),
]

flow_common_variables = pdk_variables + scl_variables + option_variables
