diff --git a/configuration/checkers.tcl b/configuration/checkers.tcl
index 2ae70df73..5c2bee429 100755
--- a/configuration/checkers.tcl
+++ b/configuration/checkers.tcl
@@ -16,27 +16,22 @@
set ::env(CHECK_ASSIGN_STATEMENTS) 0
set ::env(CHECK_UNMAPPED_CELLS) 1
-# Static timing analysis
+# STA
set ::env(QUIT_ON_TIMING_VIOLATIONS) 1
set ::env(QUIT_ON_HOLD_VIOLATIONS) 1
set ::env(QUIT_ON_SETUP_VIOLATIONS) 1
-# Floor Planning
-
-
-# Placement
-
# Routing
set ::env(QUIT_ON_TR_DRC) 1
set ::env(QUIT_ON_LONG_WIRE) 0
# Magic
-# This is disabled by default for now until we are 100% sure we want to make this
-# shift in flow dynamics, as it will affect the current benchmarks.
set ::env(QUIT_ON_MAGIC_DRC) 1
set ::env(QUIT_ON_ILLEGAL_OVERLAPS) 1
-# NetGen
-# This is disabled by default as it's the stage before the last, so why not do the last stage anyways.
+# Netgen
set ::env(QUIT_ON_LVS_ERROR) 1
+
+# Klayout
+set ::env(QUIT_ON_XOR_ERROR) 0
diff --git a/configuration/general.tcl b/configuration/general.tcl
index bf4357503..d1b5ac495 100755
--- a/configuration/general.tcl
+++ b/configuration/general.tcl
@@ -78,6 +78,7 @@ set ::env(RUN_KLAYOUT) 1
set ::env(RUN_KLAYOUT_DRC) 0
set ::env(KLAYOUT_XOR_GDS) 1
set ::env(KLAYOUT_XOR_XML) 1
+set ::env(KLAYOUT_XOR_THREADS) 1
set ::env(TAKE_LAYOUT_SCROT) 0
set ::env(KLAYOUT_DRC_KLAYOUT_GDS) 0
set ::env(RUN_KLAYOUT_XOR) 1
diff --git a/dependencies/tool_metadata.yml b/dependencies/tool_metadata.yml
index 1baabcbb1..6c82755ae 100644
--- a/dependencies/tool_metadata.yml
+++ b/dependencies/tool_metadata.yml
@@ -53,7 +53,7 @@
make PREFIX=$PREFIX install
- name: klayout
repo: https://github.com/KLayout/klayout
- commit: 428d0fe8c941faece4eceebc54170cc04d916c03
+ commit: 8bed8bcc3ca19f7e1a810815541977fd16bc1db5
build: ''
in_install: false
- name: openroad_app
diff --git a/docs/source/reference/configuration.md b/docs/source/reference/configuration.md
index e18fb1dfe..74c51475d 100644
--- a/docs/source/reference/configuration.md
+++ b/docs/source/reference/configuration.md
@@ -297,6 +297,7 @@ These variables worked initially, but they were too sky130 specific and will be
| `KLAYOUT_XOR_GDS` | If `RUN_KLAYOUT_XOR` is enabled, this will enable producing a GDS output from the XOR along with it's PNG export. 1 = Enabled, 0 = Disabled
(Default: `1`)|
| `KLAYOUT_XOR_XML` | If `RUN_KLAYOUT_XOR` is enabled, this will enable producing an XML output from the XOR. 1 = Enabled, 0 = Disabled
(Default: `1`)|
| `TAKE_LAYOUT_SCROT` | Enables running KLayout to take a PNG screenshot of the produced layout (currently configured to run on the results of each stage).1 = Enabled, 0 = Disabled
(Default: `0`)|
+| `KLAYOUT_XOR_THREADS` | Specifies number of threads used in klayout xor check
(Default: `1`)|
| `DIODE_INSERTION_STRATEGY` | Specifies the insertion strategy of diodes to be used in the flow. |
| | 0: No diode insertion. |
| | 1: Spray diodes. |
@@ -310,6 +311,8 @@ These variables worked initially, but they were too sky130 specific and will be
| `MAGIC_CONVERT_DRC_TO_RDB` | **Removed: Will always run** Specifies whether or not generate a Calibre RDB out of the magic.drc report. Result is saved in `/results/magic/`. 1=enabled 0=disabled
Default: `1`|
| `TEST_MISMATCHES` | **Removed: See `./flow.tcl -test_mismatches`** Test for mismatches between the OpenLane tool versions and the current environment. `all` tests all mismatches. `tools` tests all except the PDK. `pdk` only tests the PDK. `none` disables the check.
(Default: `all`) |
| `QUIT_ON_MISMATCHES` | **Removed: See `./flow.tcl -ignore_mismatches`** Whether to halt the flow execution or not if mismatches are found. (Default: `1`) |
+| `KLAYOUT_XOR_GDS` | **Removed: XML always generated** If `RUN_KLAYOUT_XOR` is enabled, this will enable producing a GDS output from the XOR along with it's PNG export. 1 = Enabled, 0 = Disabled
(Default: `1`)|
+| `KLAYOUT_XOR_XML` | **Removed: XML always generated** If `RUN_KLAYOUT_XOR` is enabled, this will enable producing an XML output from the XOR. 1 = Enabled, 0 = Disabled
(Default: `1`)|
### Checkers
diff --git a/klayoutrc b/klayoutrc
new file mode 100644
index 000000000..7d2c67099
--- /dev/null
+++ b/klayoutrc
@@ -0,0 +1,5 @@
+
+
+ true
+ only-top-level-shown-by-default=3
+
\ No newline at end of file
diff --git a/scripts/klayout/Readme.md b/scripts/klayout/Readme.md
new file mode 100644
index 000000000..597b68692
--- /dev/null
+++ b/scripts/klayout/Readme.md
@@ -0,0 +1,2 @@
+## Reference
+https://www.klayout.org/downloads/pymod/doc-qt5/code/index.html
\ No newline at end of file
diff --git a/scripts/klayout/def2gds.py b/scripts/klayout/def2gds.py
deleted file mode 100644
index 0f64fccc9..000000000
--- a/scripts/klayout/def2gds.py
+++ /dev/null
@@ -1,239 +0,0 @@
-# Copyright 2021 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.
-
-# Original Copyright Follows
-#
-# BSD 3-Clause License
-#
-# Copyright (c) 2018, The Regents of the University of California
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# * Redistributions of source code must retain the above copyright notice, this
-# list of conditions and the following disclaimer.
-#
-# * Redistributions in binary form must reproduce the above copyright notice,
-# this list of conditions and the following disclaimer in the documentation
-# and/or other materials provided with the distribution.
-#
-# * Neither the name of the copyright holder nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-import pya
-
-import re
-import copy
-import json
-
-print(
- """
-Input: {in_def}
-Output: {out_gds}
-Design: {design_name}
-Technology File: {tech_file}
-GDS File List: {in_gds}
-LEF File: {lef_file}
-""".format(
- in_def=in_def,
- design_name=design_name,
- tech_file=tech_file,
- in_gds=in_gds.split(),
- lef_file=lef_file,
- out_gds=out_gds,
- )
-)
-
-try:
- # Expand layers in json
- def expand_cfg_layers(cfg):
- layers = cfg["layers"]
- expand = [layer for layer in layers if "layers" in layers[layer]]
- for layer in expand:
- for i, (name, num) in enumerate(
- zip(layers[layer]["names"], layers[layer]["layers"])
- ):
- new_layer = copy.deepcopy(layers[layer])
- del new_layer["names"]
- new_layer["name"] = name
- del new_layer["layers"]
- new_layer["layer"] = num
- layers[name] = new_layer
- del layers[layer]
-
- def read_cfg():
- print("INFO: Reading config file: " + config_file)
- with open(config_file, "r") as f:
- cfg = json.load(f)
-
- expand_cfg_layers(cfg)
- cfg = cfg["layers"] # ignore the rest
-
- # Map gds layers & datatype to KLayout indices
- # These are arrays for the different mask numbers
- for layer, vals in cfg.items():
- layer = vals["layer"]
- for key in ("opc", "non-opc"):
- if key not in vals:
- continue
- data = vals[key]
- if isinstance(data["datatype"], int):
- data["datatype"] = [data["datatype"]] # convert to array
- data["klayout"] = [
- main_layout.find_layer(layer, datatype)
- for datatype in data["datatype"]
- ]
-
- return cfg
-
- # match a line like:
- # - LAYER M2 + MASK 2 + OPC RECT ( 3000 3000 ) ( 5000 5000 ) ;
- rect_pat = re.compile(
- r"""
- \s*\-\ LAYER\ (?P\S+) # The layer name
- (?: # Non-capturing group
- \s+\+\ MASK\ (?P\d+) # Mask, None if absent
- )?
- (?P # OPC, None if absent
- \s+\+\ OPC
- )?
- \s+RECT\
- \(\ (?P\d+)\ (?P\d+)\ \)\ # rect lower-left pt
- \(\ (?P\d+)\ (?P\d+)\ \)\ ; # rect upper-right pt
- """,
- re.VERBOSE,
- )
-
- def read_fills(top):
- if config_file == "":
- print("WARNING: no fill config file specified")
- return
- # KLayout doesn't support FILL in DEF so we have to side load them :(
- cfg = read_cfg()
- in_fills = False
- units = None
- with open(in_def) as fp:
- for line in fp:
- if in_fills:
- if re.match("END FILLS", line):
- break # done with fills; don't care what follows
- m = re.match(rect_pat, line)
- if not m:
- raise Exception("Unrecognized fill: " + line)
- opc_type = "opc" if m.group("opc") else "non-opc"
- mask = m.group("mask")
- if not mask: # uncolored just uses first entry
- mask = 0
- else:
- mask = int(mask) - 1 # DEF is 1-based indexing
- layer = cfg[m.group("layer")][opc_type]["klayout"][mask]
- xlo = int(m.group("xlo")) / units
- ylo = int(m.group("ylo")) / units
- xhi = int(m.group("xhi")) / units
- yhi = int(m.group("yhi")) / units
- top.shapes(layer).insert(pya.DBox(xlo, ylo, xhi, yhi))
- elif re.match("FILLS \d+ ;", line):
- in_fills = True
- elif not units:
- m = re.match("UNITS DISTANCE MICRONS (\d+)", line)
- if m:
- units = float(m.group(1))
-
- # Load technology file
- tech = pya.Technology()
- tech.load(tech_file)
- layoutOptions = tech.load_layout_options
- layoutOptions.lefdef_config.macro_resolution_mode = 1
- layoutOptions.lefdef_config.lef_files = [lef_file]
-
- # Load def file
- main_layout = pya.Layout()
- main_layout.read(in_def, layoutOptions)
-
- # Clear cells
- top_cell_index = main_layout.cell(design_name).cell_index()
-
- print("[INFO] Clearing cells...")
- for i in main_layout.each_cell():
- if i.cell_index() != top_cell_index:
- if not i.name.startswith("VIA"):
- # print("\t" + i.name)
- i.clear()
-
- # Load in the gds to merge
- print("[INFO] Merging GDS files...")
- for gds in in_gds.split():
- print("\t{0}".format(gds))
- main_layout.read(gds)
-
- # Copy the top level only to a new layout
- print("[INFO] Copying toplevel cell '{0}'".format(design_name))
- top_only_layout = pya.Layout()
- top_only_layout.dbu = main_layout.dbu
- top = top_only_layout.create_cell(design_name)
- top.copy_tree(main_layout.cell(design_name))
-
- read_fills(top)
-
- print("[INFO] Checking for missing GDS...")
- missing_gds = False
- for i in top_only_layout.each_cell():
- if i.is_empty():
- missing_gds = True
- print(
- "[ERROR] LEF Cell '{0}' has no matching GDS cell. Cell will be empty".format(
- i.name
- )
- )
-
- if not missing_gds:
- print("[INFO] All LEF cells have matching GDS cells")
-
- if seal_gds:
- top_cell = top_only_layout.top_cell()
-
- print("[INFO] Reading seal GDS file...")
- print("\t{0}".format(seal_gds))
- top_only_layout.read(seal_gds)
-
- for cell in top_only_layout.top_cells():
- if cell != top_cell:
- print(
- "[INFO] Merging '{0}' as child of '{1}'".format(
- cell.name, top_cell.name
- )
- )
- top.insert(pya.CellInstArray(cell.cell_index(), pya.Trans()))
-
- # Write out the GDS
- print("[INFO] Writing out GDS '{0}'".format(out_gds))
- top_only_layout.write(out_gds)
- print("[INFO] Done.")
- pya.Application.instance().exit(0)
-except Exception as e:
- print(e)
- pya.Application.instance().exit(1)
diff --git a/scripts/klayout/mv_shapes.py b/scripts/klayout/mv_shapes.py
deleted file mode 100644
index 15eda01d5..000000000
--- a/scripts/klayout/mv_shapes.py
+++ /dev/null
@@ -1,69 +0,0 @@
-# Copyright 2020 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.
-
-import pya
-from time import sleep
-import os
-
-try:
- if output_layout == "":
- raise NameError
- _output_layout = output_layout
-except NameError:
- _output_layout = input_layout
- print(
- "Warning: output_layout was not provided; will do the modifications in place!"
- )
- print("Hit CTRL-C to cancel...")
- sleep(3)
-
-print("Starting...")
-app = pya.Application.instance()
-win = app.main_window()
-
-# Load technology file
-tech = pya.Technology()
-tech.load(tech_file)
-layoutOptions = tech.load_layout_options
-
-# Load def/gds file in the main window
-cell_view = win.load_layout(input_layout, layoutOptions, 0)
-layout_view = cell_view.view()
-layout_view.load_layer_props(os.path.splitext(tech_file)[0] + ".lyp")
-layout_view.max_hier()
-
-# gets the corresponding layout object
-layout = cell_view.layout()
-
-# gets the cell to change is "INV2X"
-# cell = layout.cell("Active_area")
-cell = cell_view.cell
-
-# finds source layer
-layer, purpose = source_layer.split("/")
-assert layer and purpose
-_source_layer = layout.layer(int(layer), int(purpose))
-
-# finds (or creates) target layer
-layer, purpose = target_layer.split("/")
-assert layer and purpose
-_target_layer = layout.layer(int(layer), int(purpose))
-
-layout.move_layer(_source_layer, _target_layer)
-
-layout.write(_output_layout)
-
-print("Successfully wrote", _output_layout)
-
-app.exit(0)
diff --git a/scripts/klayout/mv_shapes.sh b/scripts/klayout/mv_shapes.sh
deleted file mode 100644
index 6e58e444e..000000000
--- a/scripts/klayout/mv_shapes.sh
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/bin/sh
-# Copyright 2020 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.
-
-: ${1?"Usage: $0 file.gds src_layer/src_purpose targ_layer/targ_purpose [output.gds]"}
-: ${2?"Usage: $0 file.gds src_layer/src_purpose targ_layer/targ_purpose [output.gds]"}
-: ${3?"Usage: $0 file.gds src_layer/src_purpose targ_layer/targ_purpose [output.gds]"}
-: ${PDK_ROOT?"You need to export PDK_ROOT"}
-TECH=${TECH:-sky130A}
-
-klayout -b\
- -rm $(dirname $0)/mv_shapes.py\
- -rd input_layout=$1\
- -rd tech_file=$PDK_ROOT/$TECH/libs.tech/klayout/$TECH.lyt\
- -rd source_layer=$2\
- -rd target_layer=$3\
- -rd output_layout=$4
diff --git a/scripts/klayout/open_design.py b/scripts/klayout/open_design.py
old mode 100644
new mode 100755
index 88039735f..45133b658
--- a/scripts/klayout/open_design.py
+++ b/scripts/klayout/open_design.py
@@ -1,3 +1,4 @@
+#!/usr/bin/env python3
# Copyright 2020-2022 Efabless Corporation
# Copyright 2021 The American University in Cairo and the Cloud V Project
#
@@ -15,24 +16,70 @@
import os
import sys
-import pya
-
-app = pya.Application.instance()
+from typing import TYPE_CHECKING
try:
- win = app.main_window()
-
- layout = os.getenv("LAYOUT")
- if layout is None:
- raise Exception("LAYOUT environment variable is not set.")
-
- pdk_root = os.getenv("PDK_ROOT")
- if pdk_root is None:
- raise Exception("PDK_ROOT environment variable is not set.")
+ import pya
+except ImportError:
+ import click
+
+ @click.command()
+ @click.option(
+ "-l",
+ "--input-lef",
+ required=os.getenv("MERGED_LEF") is None,
+ default=os.getenv("MERGED_LEF"),
+ )
+ @click.option(
+ "-T",
+ "--tech-file",
+ "lyt",
+ required=os.getenv("KLAYOUT_TECH") is None,
+ default=os.getenv("KLAYOUT_TECH"),
+ help="KLayout .lyt file",
+ )
+ @click.option(
+ "-P",
+ "--props-file",
+ "lyp",
+ required=os.getenv("KLAYOUT_PROPERTIES") is None,
+ default=os.getenv("KLAYOUT_PROPERTIES"),
+ help="KLayout .lyp file",
+ )
+ @click.argument("input_def")
+ def open_design(
+ input_lef,
+ lyt,
+ lyp,
+ input_def,
+ ):
+ args = [
+ "klayout",
+ "-rm",
+ __file__,
+ "-rd",
+ f"input_lef={os.path.abspath(input_lef)}",
+ "-rd",
+ f"tech_file={os.path.abspath(lyt)}",
+ "-rd",
+ f"props_file={os.path.abspath(lyp)}",
+ "-rd",
+ f"input_def={os.path.abspath(input_def)}",
+ ]
+ os.execlp("klayout", *args)
+
+ if __name__ == "__main__":
+ open_design()
+
+if TYPE_CHECKING:
+ # Dummy data for type-checking
+ input_def: str = ""
+ tech_file: str = ""
+ props_file: str = ""
+ input_lef: str = ""
- pdk_name = os.getenv("PDK")
- if pdk_name is None:
- raise Exception("PDK environment variable is not set.")
+try:
+ main_window = pya.Application.instance().main_window()
# Relative to the layout path, ':' delimited. If not provided, all LEFs
# in the same folder as the layout will be loaded.
@@ -40,27 +87,17 @@
use_explicitly_listed_lefs = explicitly_listed_lefs_raw is not None
- tech_file_path = os.path.join(
- pdk_root, pdk_name, "libs.tech", "klayout", f"{pdk_name}.lyt"
- )
-
tech = pya.Technology()
- tech.load(tech_file_path)
+ tech.load(tech_file)
layout_options = tech.load_layout_options
-
layout_options.keep_other_cells = True
-
- layout_options = tech.load_layout_options
layout_options.lefdef_config.macro_resolution_mode = 1
+ layout_options.lefdef_config.read_lef_with_def = False
+ layout_options.lefdef_config.lef_files = [input_lef]
- if use_explicitly_listed_lefs:
- explicitly_listed_lefs = explicitly_listed_lefs_raw.split(":")
- layout_options.lefdef_config.read_lef_with_def = False
- layout_options.lefdef_config.lef_files = explicitly_listed_lefs
-
- cell_view = win.load_layout(layout, layout_options, 0)
-
+ cell_view = main_window.load_layout(input_def, layout_options, 0)
+ exit(0)
except Exception as e:
print(e, file=sys.stderr)
- app.exit(-1)
+ exit(-1)
diff --git a/scripts/klayout/open_design_cmd.py b/scripts/klayout/open_design_cmd.py
deleted file mode 100644
index 69ccf3dce..000000000
--- a/scripts/klayout/open_design_cmd.py
+++ /dev/null
@@ -1,61 +0,0 @@
-#!/usr/bin/env python3
-# Copyright 2020-2022 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.
-
-import os
-import click
-import subprocess
-
-
-@click.command("open_design")
-@click.option(
- "-l",
- "--input-lef",
- default=os.getenv("MERGED_LEF"),
- help="Input merged technology/cells LEF file",
-)
-@click.option(
- "-P",
- "--pdk-root",
- default=os.getenv("PDK_ROOT"),
- required=not os.getenv("PDK_ROOT"),
- help="PDK Root",
-)
-@click.option("-p", "--pdk", default="sky130A", help="Name of the PDK")
-@click.argument("input_def")
-def open_design(input_lef, pdk_root, pdk, input_def):
- """
- Opens a design in KLayout.
- """
- dir = os.path.dirname(__file__)
- klayout_script_path = os.path.join(dir, "open_design.py")
- env = os.environ.copy()
-
- env["EXPLICITLY_LISTED_LEFS"] = input_lef
- env["PDK_ROOT"] = pdk_root
- env["PDK"] = pdk
- env["LAYOUT"] = input_def
-
- subprocess.check_call(
- [
- "klayout",
- "-rm",
- klayout_script_path,
- ],
- env=env,
- )
-
-
-if __name__ == "__main__":
- open_design()
diff --git a/scripts/klayout/screenshot_layout.py b/scripts/klayout/screenshot_layout.py
new file mode 100755
index 000000000..ffa722a3c
--- /dev/null
+++ b/scripts/klayout/screenshot_layout.py
@@ -0,0 +1,129 @@
+#!/usr/bin/env python3
+# Copyright 2020-2022 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.
+
+import os
+from typing import TYPE_CHECKING
+
+try:
+ import pya
+except ImportError:
+ import click
+
+ @click.command()
+ @click.option("-o", "--output", required=True)
+ @click.option(
+ "-l",
+ "--input-lef",
+ required=os.getenv("MERGED_LEF") is None,
+ default=os.getenv("MERGED_LEF"),
+ )
+ @click.option(
+ "-T",
+ "--tech-file",
+ "lyt",
+ required=os.getenv("KLAYOUT_TECH") is None,
+ default=os.getenv("KLAYOUT_TECH"),
+ help="KLayout .lyt file",
+ )
+ @click.option(
+ "-P",
+ "--props-file",
+ "lyp",
+ required=os.getenv("KLAYOUT_PROPERTIES") is None,
+ default=os.getenv("KLAYOUT_PROPERTIES"),
+ help="KLayout .lyp file",
+ )
+ @click.argument("input_def")
+ def screenshot_layout(
+ output,
+ input_lef,
+ lyt,
+ lyp,
+ input_def,
+ ):
+ args = [
+ # "xvfb-run",
+ # "-a",
+ "klayout",
+ "-rm",
+ __file__,
+ "-rd",
+ f"out_png={os.path.abspath(output)}",
+ "-rd",
+ f"lef_file={os.path.abspath(input_lef)}",
+ "-rd",
+ f"tech_file={os.path.abspath(lyt)}",
+ "-rd",
+ f"props_file={os.path.abspath(lyp)}",
+ "-rd",
+ f"in_def={os.path.abspath(input_def)}",
+ ]
+ os.execlp("klayout", *args)
+
+ if __name__ == "__main__":
+ screenshot_layout()
+
+if TYPE_CHECKING:
+ # Dummy data for type-checking
+ in_def: str = ""
+ out_png: str = ""
+ tech_file: str = ""
+ props_file: str = ""
+ lef_file: str = ""
+
+try:
+ WIDTH = 2048
+ HEIGHT = 2048
+
+ main_window = pya.Application.instance().main_window()
+
+ # Load technology file
+ print(f"[INFO] Reading tech file: '{str(tech_file)}'...")
+ tech = pya.Technology()
+ tech.load(tech_file)
+
+ layout_options = tech.load_layout_options
+ layout_options.keep_other_cells = True
+ layout_options.lefdef_config.macro_resolution_mode = 1
+ layout_options.lefdef_config.read_lef_with_def = False
+ layout_options.lefdef_config.lef_files = [lef_file]
+
+ # Load def file in the main window
+ print(f"[INFO] Reading layout file: '{str(in_def)}'...")
+ cell_view = main_window.load_layout(in_def, layout_options, 0)
+ layout_view = cell_view.view()
+ layout_view.load_layer_props(props_file)
+ layout_view.max_hier()
+
+ # Hide layers with these purposes
+ hidden_purposes = [0, 4, 5]
+
+ li = layout_view.begin_layers()
+ while not li.at_end():
+ lp = li.current()
+ if lp.source_datatype in hidden_purposes:
+ new_lp = lp.dup()
+ new_lp.visible = False
+ layout_view.set_layer_properties(li, new_lp)
+
+ li.next()
+
+ print(f"[INFO] Writing out screenshot to '{out_png}'...")
+ layout_view.save_image(out_png, WIDTH, HEIGHT)
+ print("Done.")
+ exit(0)
+except Exception as e:
+ print(e)
+ exit(-1)
diff --git a/scripts/klayout/scrotLayout.py b/scripts/klayout/scrotLayout.py
deleted file mode 100644
index fe5f1dc7e..000000000
--- a/scripts/klayout/scrotLayout.py
+++ /dev/null
@@ -1,58 +0,0 @@
-# Copyright 2020 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.
-
-import pya
-import re
-import os
-
-WIDTH = 2048
-HEIGHT = 2048
-
-app = pya.Application.instance()
-win = app.main_window()
-
-# Load technology file
-print("[INFO] Reading tech file: " + str(tech_file))
-tech = pya.Technology()
-tech.load(tech_file)
-
-layoutOptions = tech.load_layout_options
-
-# Load def file in the main window
-print("[INFO] Reading Layout file: " + str(input_layout))
-cell_view = win.load_layout(input_layout, layoutOptions, 0)
-layout_view = cell_view.view()
-
-layout_view.load_layer_props(os.path.splitext(tech_file)[0] + ".lyp")
-
-layout_view.max_hier()
-# layout_view.clear_layers()
-
-# Hide layers with these purposes
-hidden_purposes = [0, 4, 5]
-
-li = layout_view.begin_layers()
-while not li.at_end():
- lp = li.current()
- if lp.source_datatype in hidden_purposes:
- new_lp = lp.dup()
- new_lp.visible = False
- layout_view.set_layer_properties(li, new_lp)
-
- li.next()
-
-print("[INFO] Writing out PNG screenshot '{0}'".format(input_layout + ".png"))
-layout_view.save_image(input_layout + ".png", WIDTH, HEIGHT)
-print("Done")
-app.exit(0)
diff --git a/scripts/klayout/scrotLayout.sh b/scripts/klayout/scrotLayout.sh
deleted file mode 100644
index d83378397..000000000
--- a/scripts/klayout/scrotLayout.sh
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/bin/sh
-# Copyright 2020 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.
-
-set -e
-
-: ${1?"Usage: $0 tech_file input"}
-: ${2?"Usage: $0 tech_file input"}
-
-echo "Using Techfile: $1"
-echo "Using layout file: $2"
-
-# The -a here is necessary to handle race conditions.
-# This limits the max number of possible jobs to 100.
-xvfb-run -a klayout -z \
- -rd input_layout=$2 \
- -rd tech_file=$1 \
- -rm $(dirname $0)/scrotLayout.py
-
-exit 0
diff --git a/scripts/klayout/stream_out.py b/scripts/klayout/stream_out.py
new file mode 100755
index 000000000..65c1e18f1
--- /dev/null
+++ b/scripts/klayout/stream_out.py
@@ -0,0 +1,203 @@
+#!/usr/bin/env python3
+# Copyright (c) 2021-2022 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.
+
+# Original Copyright Follows
+#
+# BSD 3-Clause License
+#
+# Copyright (c) 2018, The Regents of the University of California
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice, this
+# list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# * Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+import os
+from typing import TYPE_CHECKING, Optional
+
+try:
+ import pya
+except ImportError:
+ import click
+
+ @click.command()
+ @click.option("-o", "--output", required=True)
+ @click.option(
+ "-l",
+ "--input-lef",
+ required=os.getenv("MERGED_LEF") is None,
+ default=os.getenv("MERGED_LEF"),
+ )
+ @click.option(
+ "-T",
+ "--tech-file",
+ "lyt",
+ required=os.getenv("KLAYOUT_TECH") is None,
+ default=os.getenv("KLAYOUT_TECH"),
+ help="KLayout .lyt file",
+ )
+ @click.option(
+ "-P",
+ "--props-file",
+ "lyp",
+ required=os.getenv("KLAYOUT_PROPERTIES") is None,
+ default=os.getenv("KLAYOUT_PROPERTIES"),
+ help="KLayout .lyp file",
+ )
+ @click.option("-w", "--with-gds-file", "input_gds_files", multiple=True, default=[])
+ @click.option("-s", "--seal-gds-file", default=None)
+ @click.option("-t", "--top", required=True, help="Name of the design/top module")
+ @click.argument("input_def")
+ def stream_out(
+ output,
+ input_lef,
+ lyt,
+ lyp,
+ input_gds_files,
+ seal_gds_file,
+ top,
+ input_def,
+ ):
+ args = [
+ "klayout",
+ "-b",
+ "-rm",
+ __file__,
+ "-rd",
+ f"out_gds={output}",
+ "-rd",
+ f"lef_file={input_lef}",
+ "-rd",
+ f"tech_file={lyt}",
+ "-rd",
+ f"props_file={lyp}",
+ "-rd",
+ f"design_name={top}",
+ "-rd",
+ f"in_def={input_def}",
+ "-rd",
+ f"in_gds={';'.join(list(input_gds_files))}",
+ "-rd",
+ f"seal_gds={seal_gds_file}",
+ ]
+ os.execlp("klayout", *args)
+
+ if __name__ == "__main__":
+ stream_out()
+
+
+if TYPE_CHECKING:
+ # Dummy data for type-checking
+ in_def: str = ""
+ out_gds: str = ""
+ design_name: str = ""
+ tech_file: str = ""
+ props_file: str = ""
+ in_gds: str = ""
+ lef_file: str = ""
+ seal_gds: Optional[str] = ""
+
+if seal_gds == "None":
+ seal_gds = None
+
+
+try:
+ # Load technology file
+ tech = pya.Technology()
+ tech.load(tech_file)
+ layout_options = tech.load_layout_options
+ layout_options.lefdef_config.macro_resolution_mode = 1
+ layout_options.lefdef_config.lef_files = [lef_file]
+
+ # Load def file
+ main_layout = pya.Layout()
+ # main_layout.load_layer_props(props_file)
+ main_layout.read(in_def, layout_options)
+
+ # Clear cells
+ top_cell_index = main_layout.cell(design_name).cell_index()
+
+ print("[INFO] Clearing cells...")
+ for i in main_layout.each_cell():
+ if i.cell_index() != top_cell_index:
+ if not i.name.startswith("VIA"):
+ i.clear()
+
+ # Load in the gds to merge
+ print("[INFO] Merging GDS files...")
+ for gds in in_gds.split(";"):
+ print(f"\t{gds}")
+ main_layout.read(gds)
+
+ # Copy the top level only to a new layout
+ print(f"[INFO] Copying top level cell '{design_name}'...")
+ top_only_layout = pya.Layout()
+ top_only_layout.dbu = main_layout.dbu
+ top = top_only_layout.create_cell(design_name)
+ top.copy_tree(main_layout.cell(design_name))
+
+ print("[INFO] Checking for missing GDS...")
+ missing_gds = False
+ for i in top_only_layout.each_cell():
+ if i.is_empty():
+ missing_gds = True
+ print(f"[ERROR] LEF Cell '{i.name}' has no matching GDS cell.")
+
+ if missing_gds:
+ raise Exception("One or more cell GDS files are missing.")
+ else:
+ print("[INFO] All LEF cells have matching GDS cells.")
+
+ if seal_gds is not None:
+ top_cell = top_only_layout.top_cell()
+
+ print(f"[INFO] Reading seal GDS file '{seal_gds}'...")
+ top_only_layout.read(seal_gds)
+
+ for cell in top_only_layout.top_cells():
+ if cell != top_cell:
+ print(f"[INFO] Merging '{cell.name}' as child of '{top_cell.name}'...")
+ top.insert(pya.CellInstArray(cell.cell_index(), pya.Trans()))
+
+ # Write out the GDS
+ print(f"[INFO] Writing out GDS '{out_gds}'...")
+ top_only_layout.write(out_gds)
+ print("[INFO] Done.")
+ pya.Application.instance().exit(0)
+except Exception as e:
+ print(e)
+ pya.Application.instance().exit(1)
diff --git a/scripts/klayout/xor.drc b/scripts/klayout/xor.drc
old mode 100644
new mode 100755
index 6caee9162..68f7a1f2a
--- a/scripts/klayout/xor.drc
+++ b/scripts/klayout/xor.drc
@@ -1,28 +1,75 @@
-# A general XOR script
-# (https://www.klayout.de/forum/discussion/100/xor-vs-diff-tool)
-# This script uses KLayout's DRC language to implement a generic
-# XOR between two layouts. The name of the layouts is given
-# in $a and $b.
+#!/usr/bin/env ruby
+# A general XOR script from: https://www.klayout.de/forum/discussion/100/xor-vs-diff-tool
-# For layout-to-layout XOR with multiple cores, run this script with
-# ./klayout -r xor.drc -rd thr=NUM_CORES -rd top_cell=TOP_CELL_NAME -rd a=a.gds -rd b=b.gds -rd ol=xor.gds -zz
-# (replace NUM_CORES by the desired number of cores to utilize
+# This script uses KLayout's Ruby-based DRC language to implement a generic
+# XOR between two layouts. The names of the layouts are given in global variables
+# $a and $b.
+
+if !defined?(RBA)
+ require 'optparse'
+
+ options = {
+ :rpt_out => "/dev/null"
+ }
+ optparse = OptionParser.new do |opts|
+ opts.banner = "Usage: #{File.basename($0)} [options] "
+
+ opts.on("-o", "--output OUTPUT", "Klayout RDB (.xml) output file (required)") do |rdb_out|
+ options[:rdb_out] = rdb_out
+ end
+ opts.on("-R", "--report REPORT_FILE", "Text file (default: #{options[:rpt_out]})") do |rpt_out|
+ options[:rpt_out] = rpt_out
+ end
+ # opts.on("-O", "--gds-out OUTPUT", "GDS output file (required)") do |gds_out|
+ # options[:gds_out] = gds_out
+ # end
+ opts.on("-t", "--top TOP_CELL", "Top cell name (required)") do |top_cell|
+ options[:top_cell] = top_cell
+ end
+ end
+ optparse.parse!
+
+ if [options[:rdb_out], options[:top_cell]].include?(nil)
+ puts optparse.help
+ exit 64
+ end
+
+ args = [
+ "klayout", "-b",
+ "-r", $0,
+ "-rd", "top_cell=#{options[:top_cell]}",
+ "-rd", "a=#{ARGV[0]}",
+ "-rd", "b=#{ARGV[1]}",
+ "-rd", "jobs=1",
+ "-rd", "rdb_out=#{File.absolute_path(options[:rdb_out])}",
+ "-rd", "rpt_out=#{File.absolute_path(options[:rpt_out])}",
+ # "-rd", "gds_out=#{options[:gds_out]}",
+ ]
+ puts "Running: '#{args.join(" ")}'..."
+ exec *args
+
+end
-# enable timing output
verbose
-# set up input a
+# Set up inputs
a = source($a, $top_cell)
-
-# set up input b
b = source($b, $top_cell)
-$o && $ext != "gds" && report("XOR #{$a} vs. #{$b}", $o)
-$ol && $ext == "gds" && target($ol, $co || "XOR")
+# Set up output
+# target($gds_out, "XOR")
+report("XOR #{$a} vs. #{$b}", $rdb_out)
+
+def write_data(xor_data, layer_info)
+ # xor_data.output(layer_info.layer, layer_info.datatype, layer_info.name)
+ xor_data.output(layer_info.to_s, "XOR data for layer #{layer_info.to_s}")
+end
-$thr && threads($thr) || threads(2)
+# Run XOR
+$jobs = $jobs.to_i
+threads($jobs) unless $jobs <= 1
-# collect all common layers
+## Collect all common layers
layers = {}
[ a.layout, b.layout ].each do |ly|
ly.layer_indices.each do |li|
@@ -31,12 +78,21 @@ layers = {}
end
end
-# perform the XOR's
-layers.keys.sort.each do |l|
- i = layers[l]
- info("--- Running XOR for #{l} ---")
- x = a.input(l) ^ b.input(l)
- info("XOR differences: #{x.data.size}")
- $o && $ext != "gds" && x.output(l, "XOR results for layer #{l} #{i.name}")
- $ol && $ext == "gds" && x.output(i.layer, i.datatype, i.name)
+## Perform per-layer XOR
+total_xor_differences = 0
+layers.keys.sort.each do |layer_name|
+ info "--- Running XOR for layer #{layer_name} ---"
+
+ layer_info = layers[layer_name]
+ xor_data = a.input(layer_name) ^ b.input(layer_name)
+ total_xor_differences += xor_data.data.size
+ info "XOR differences: #{xor_data.data.size}"
+
+ write_data xor_data, layer_info
end
+
+info "---"
+info "Total XOR differences: #{total_xor_differences}"
+File.open($rpt_out, "w") do |f|
+ f.puts "Total XOR differences = #{total_xor_differences}"
+end
\ No newline at end of file
diff --git a/scripts/klayout/xor.sh b/scripts/klayout/xor.sh
deleted file mode 100755
index b71ef7987..000000000
--- a/scripts/klayout/xor.sh
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/bin/sh
-# Copyright 2020 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.
-
-: ${1?"Usage: $0 file1.gds file2.gds output.gds|markers.xml"}
-: ${2?"Usage: $0 file1.gds file2.gds output.gds|markers.xml"}
-: ${3?"Usage: $0 file1.gds file2.gds output.gds|markers.xml"}
-: ${4?"Usage: $0 file1.gds file2.gds output.gds|markers.xml"}
-
-
-echo "First Layout: $1"
-echo "Second Layout: $2"
-echo "Design Name: $3"
-echo "Output GDS will be: $4"
-
-klayout -b\
- -r $(dirname $0)/xor.drc \
- -rd top_cell=$3 \
- -rd a=$1 \
- -rd b=$2 \
- -rd thr=$(nproc) \
- -rd ol=$4 \
- -rd o=$4 \
- -rd ext=${4##*.}
diff --git a/scripts/parse_klayout_xor_log.py b/scripts/parse_klayout_xor_log.py
deleted file mode 100644
index db3fcb0a4..000000000
--- a/scripts/parse_klayout_xor_log.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# Copyright 2020 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.
-
-import argparse
-import re
-
-parser = argparse.ArgumentParser(
- description="extracts the total xor differnces from an xor log"
-)
-
-parser.add_argument("--log_file", "-l", required=True, help="log file")
-
-parser.add_argument(
- "--output", "-o", required=True, help="output file to store results"
-)
-
-args = parser.parse_args()
-log_file_name = args.log_file
-out_file_name = args.output
-
-string = "XOR differences:"
-pattern = re.compile(r"\s*%s\s*([\d+]+)" % string)
-tot_cnt = 0
-with open(log_file_name, "r") as f:
- for line in f:
- m = pattern.match(line)
- if m:
- tot_cnt += int(m.group(1))
-
-outFileOpener = open(out_file_name, "w")
-outFileOpener.write("Total XOR differences = " + str(tot_cnt))
-outFileOpener.close()
diff --git a/scripts/tcl_commands/checkers.tcl b/scripts/tcl_commands/checkers.tcl
index c43155691..40f7d7e60 100755
--- a/scripts/tcl_commands/checkers.tcl
+++ b/scripts/tcl_commands/checkers.tcl
@@ -270,6 +270,24 @@ proc quit_on_lvs_error {args} {
}
}
+proc quit_on_xor_error {args} {
+ if { [info exists ::env(QUIT_ON_XOR_ERROR)] && $::env(QUIT_ON_XOR_ERROR) } {
+ set options {
+ {-log required}
+ }
+ parse_key_args "quit_on_xor_error" args arg_values $options
+ set checker [catch {exec grep -E -o "Total XOR differences = 0" $arg_values(-log)} error]
+
+ if { $checker != 0 } {
+ set log_relative [relpath . $arg_values(-log)]
+ puts_err "There are XOR differences in the design: See '$log_relative' for details."
+ flow_fail
+ } else {
+ puts_info "No XOR differences between KLayout and Magic gds."
+ }
+ }
+}
+
proc quit_on_illegal_overlaps {args} {
if { [info exists ::env(QUIT_ON_ILLEGAL_OVERLAPS)] && $::env(QUIT_ON_ILLEGAL_OVERLAPS) } {
set options {
diff --git a/scripts/tcl_commands/klayout.tcl b/scripts/tcl_commands/klayout.tcl
index 7dad9cc7f..04f89ce8c 100755
--- a/scripts/tcl_commands/klayout.tcl
+++ b/scripts/tcl_commands/klayout.tcl
@@ -28,17 +28,19 @@ proc run_klayout {args} {
set cells_gds $::env(GDS_FILES)
}
+ set gds_file_arg ""
+ foreach gds_file "$cells_gds $gds_files_in" {
+ set gds_file_arg "$gds_file_arg --with-gds-file $gds_file"
+ }
set klayout_out $::env(signoff_results)/$::env(DESIGN_NAME).klayout.gds
- try_catch klayout -b\
- -rm $::env(SCRIPTS_DIR)/klayout/def2gds.py\
- -rd out_gds=$klayout_out\
- -rd tech_file=$::env(KLAYOUT_TECH)\
- -rd design_name=$::env(DESIGN_NAME)\
- -rd in_def=$::env(CURRENT_DEF)\
- -rd "in_gds=$cells_gds $gds_files_in"\
- -rd "config_file="\
- -rd "seal_gds="\
- -rd lef_file=$::env(MERGED_LEF)\
+ try_catch python3 $::env(SCRIPTS_DIR)/klayout/stream_out.py\
+ --output $klayout_out\
+ --tech-file $::env(KLAYOUT_TECH)\
+ --props-file $::env(KLAYOUT_PROPERTIES)\
+ --top $::env(DESIGN_NAME)\
+ {*}$gds_file_arg \
+ --input-lef $::env(MERGED_LEF)\
+ $::env(CURRENT_DEF)\
|& tee $::env(TERMINAL_OUTPUT) $log
@@ -140,36 +142,28 @@ proc run_klayout_gds_xor {args} {
set_if_unset arg_values(-layout2) $::env(signoff_results)/$::env(DESIGN_NAME).klayout.gds
set_if_unset arg_values(-output_xml) $::env(signoff_reports)/$::env(DESIGN_NAME).xor.xml
set_if_unset arg_values(-output_gds) $::env(signoff_reports)/$::env(DESIGN_NAME).xor.gds
+
if { [file exists $arg_values(-layout1)]} {
if { [file exists $arg_values(-layout2)] } {
increment_index
TIMER::timer_start
set log [index_file $::env(signoff_logs)/xor.log]
+ set report [index_file $::env(signoff_reports)/xor.rpt]
+ set db [index_file $::env(signoff_reports)/xor.xml]
puts_info "Running XOR on the layouts using KLayout (log: [relpath . $log])..."
-
- if { $::env(KLAYOUT_XOR_GDS) } {
- try_catch bash $::env(SCRIPTS_DIR)/klayout/xor.sh \
- $arg_values(-layout1) $arg_values(-layout2) $::env(DESIGN_NAME) \
- $arg_values(-output_gds) \
- |& tee $::env(TERMINAL_OUTPUT) $log
- try_catch python3 $::env(SCRIPTS_DIR)/parse_klayout_xor_log.py \
- -l [index_file $::env(signoff_logs)/xor.log] \
- -o [index_file $::env(signoff_reports)/xor.rpt]
- scrot_klayout -layout $arg_values(-output_gds) -log $::env(signoff_logs)/screenshot.klayout.xor.log
- }
-
- if { $::env(KLAYOUT_XOR_XML) } {
- try_catch bash $::env(SCRIPTS_DIR)/klayout/xor.sh \
- $arg_values(-layout1) $arg_values(-layout2) $::env(DESIGN_NAME) \
- $arg_values(-output_xml) \
- |& tee $::env(TERMINAL_OUTPUT) [index_file $::env(signoff_logs)/xor.log]
- try_catch python3 $::env(SCRIPTS_DIR)/parse_klayout_xor_log.py \
- -l [index_file $::env(signoff_logs)/xor.log] \
- -o [index_file $::env(signoff_reports)/xor.rpt]
- }
+ try_catch klayout \
+ -b \
+ -r $::env(SCRIPTS_DIR)/klayout/xor.drc \
+ -rd a=$arg_values(-layout1) \
+ -rd b=$arg_values(-layout2) \
+ -rd jobs=$::env(KLAYOUT_XOR_THREADS) \
+ -rd rdb_out=$db \
+ -rd rpt_out=$report \
+ |& tee $::env(TERMINAL_OUTPUT) $log
TIMER::timer_stop
exec echo "[TIMER::get_runtime]" | python3 $::env(SCRIPTS_DIR)/write_runtime.py "xor - klayout"
+ quit_on_xor_error -log $report
} else {
set layout2_rel [relpath . $arg_values(-layout2)]
puts_warn "'$layout2_rel' wasn't found. Skipping GDS XOR."