siliconcompiler 0.26.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- siliconcompiler/__init__.py +24 -0
- siliconcompiler/__main__.py +12 -0
- siliconcompiler/_common.py +49 -0
- siliconcompiler/_metadata.py +36 -0
- siliconcompiler/apps/__init__.py +0 -0
- siliconcompiler/apps/_common.py +76 -0
- siliconcompiler/apps/sc.py +92 -0
- siliconcompiler/apps/sc_dashboard.py +94 -0
- siliconcompiler/apps/sc_issue.py +178 -0
- siliconcompiler/apps/sc_remote.py +199 -0
- siliconcompiler/apps/sc_server.py +39 -0
- siliconcompiler/apps/sc_show.py +142 -0
- siliconcompiler/apps/smake.py +232 -0
- siliconcompiler/checklists/__init__.py +0 -0
- siliconcompiler/checklists/oh_tapeout.py +41 -0
- siliconcompiler/core.py +3221 -0
- siliconcompiler/data/RobotoMono/LICENSE.txt +202 -0
- siliconcompiler/data/RobotoMono/RobotoMono-Regular.ttf +0 -0
- siliconcompiler/data/heartbeat.v +18 -0
- siliconcompiler/data/logo.png +0 -0
- siliconcompiler/flowgraph.py +570 -0
- siliconcompiler/flows/__init__.py +0 -0
- siliconcompiler/flows/_common.py +67 -0
- siliconcompiler/flows/asicflow.py +180 -0
- siliconcompiler/flows/asictopflow.py +38 -0
- siliconcompiler/flows/dvflow.py +86 -0
- siliconcompiler/flows/fpgaflow.py +202 -0
- siliconcompiler/flows/generate_openroad_rcx.py +66 -0
- siliconcompiler/flows/lintflow.py +35 -0
- siliconcompiler/flows/screenshotflow.py +51 -0
- siliconcompiler/flows/showflow.py +59 -0
- siliconcompiler/flows/signoffflow.py +53 -0
- siliconcompiler/flows/synflow.py +128 -0
- siliconcompiler/fpgas/__init__.py +0 -0
- siliconcompiler/fpgas/lattice_ice40.py +42 -0
- siliconcompiler/fpgas/vpr_example.py +109 -0
- siliconcompiler/issue.py +300 -0
- siliconcompiler/libs/__init__.py +0 -0
- siliconcompiler/libs/asap7sc7p5t.py +8 -0
- siliconcompiler/libs/gf180mcu.py +8 -0
- siliconcompiler/libs/nangate45.py +8 -0
- siliconcompiler/libs/sky130hd.py +8 -0
- siliconcompiler/libs/sky130io.py +8 -0
- siliconcompiler/package.py +412 -0
- siliconcompiler/pdks/__init__.py +0 -0
- siliconcompiler/pdks/asap7.py +8 -0
- siliconcompiler/pdks/freepdk45.py +8 -0
- siliconcompiler/pdks/gf180.py +8 -0
- siliconcompiler/pdks/skywater130.py +8 -0
- siliconcompiler/remote/__init__.py +36 -0
- siliconcompiler/remote/client.py +891 -0
- siliconcompiler/remote/schema.py +106 -0
- siliconcompiler/remote/server.py +507 -0
- siliconcompiler/remote/server_schema/requests/cancel_job.json +51 -0
- siliconcompiler/remote/server_schema/requests/check_progress.json +61 -0
- siliconcompiler/remote/server_schema/requests/check_server.json +38 -0
- siliconcompiler/remote/server_schema/requests/delete_job.json +51 -0
- siliconcompiler/remote/server_schema/requests/get_results.json +48 -0
- siliconcompiler/remote/server_schema/requests/remote_run.json +40 -0
- siliconcompiler/remote/server_schema/responses/cancel_job.json +18 -0
- siliconcompiler/remote/server_schema/responses/check_progress.json +30 -0
- siliconcompiler/remote/server_schema/responses/check_server.json +32 -0
- siliconcompiler/remote/server_schema/responses/delete_job.json +18 -0
- siliconcompiler/remote/server_schema/responses/get_results.json +21 -0
- siliconcompiler/remote/server_schema/responses/remote_run.json +25 -0
- siliconcompiler/report/__init__.py +13 -0
- siliconcompiler/report/html_report.py +74 -0
- siliconcompiler/report/report.py +355 -0
- siliconcompiler/report/streamlit_report.py +137 -0
- siliconcompiler/report/streamlit_viewer.py +944 -0
- siliconcompiler/report/summary_image.py +117 -0
- siliconcompiler/report/summary_table.py +105 -0
- siliconcompiler/report/utils.py +163 -0
- siliconcompiler/scheduler/__init__.py +2092 -0
- siliconcompiler/scheduler/docker_runner.py +253 -0
- siliconcompiler/scheduler/run_node.py +138 -0
- siliconcompiler/scheduler/send_messages.py +178 -0
- siliconcompiler/scheduler/slurm.py +208 -0
- siliconcompiler/scheduler/validation/email_credentials.json +54 -0
- siliconcompiler/schema/__init__.py +7 -0
- siliconcompiler/schema/schema_cfg.py +4014 -0
- siliconcompiler/schema/schema_obj.py +1841 -0
- siliconcompiler/schema/utils.py +93 -0
- siliconcompiler/sphinx_ext/__init__.py +0 -0
- siliconcompiler/sphinx_ext/dynamicgen.py +1006 -0
- siliconcompiler/sphinx_ext/schemagen.py +221 -0
- siliconcompiler/sphinx_ext/utils.py +166 -0
- siliconcompiler/targets/__init__.py +0 -0
- siliconcompiler/targets/asap7_demo.py +68 -0
- siliconcompiler/targets/asic_demo.py +38 -0
- siliconcompiler/targets/fpgaflow_demo.py +47 -0
- siliconcompiler/targets/freepdk45_demo.py +59 -0
- siliconcompiler/targets/gf180_demo.py +77 -0
- siliconcompiler/targets/skywater130_demo.py +70 -0
- siliconcompiler/templates/email/general.j2 +66 -0
- siliconcompiler/templates/email/summary.j2 +43 -0
- siliconcompiler/templates/issue/README.txt +26 -0
- siliconcompiler/templates/issue/run.sh +6 -0
- siliconcompiler/templates/report/bootstrap.min.css +7 -0
- siliconcompiler/templates/report/bootstrap.min.js +7 -0
- siliconcompiler/templates/report/bootstrap_LICENSE.md +24 -0
- siliconcompiler/templates/report/sc_report.j2 +427 -0
- siliconcompiler/templates/slurm/run.sh +9 -0
- siliconcompiler/templates/tcl/manifest.tcl.j2 +137 -0
- siliconcompiler/tools/__init__.py +0 -0
- siliconcompiler/tools/_common/__init__.py +432 -0
- siliconcompiler/tools/_common/asic.py +115 -0
- siliconcompiler/tools/_common/sdc/sc_constraints.sdc +76 -0
- siliconcompiler/tools/_common/tcl/sc_pin_constraints.tcl +63 -0
- siliconcompiler/tools/bambu/bambu.py +32 -0
- siliconcompiler/tools/bambu/convert.py +77 -0
- siliconcompiler/tools/bluespec/bluespec.py +40 -0
- siliconcompiler/tools/bluespec/convert.py +103 -0
- siliconcompiler/tools/builtin/_common.py +155 -0
- siliconcompiler/tools/builtin/builtin.py +26 -0
- siliconcompiler/tools/builtin/concatenate.py +85 -0
- siliconcompiler/tools/builtin/join.py +27 -0
- siliconcompiler/tools/builtin/maximum.py +46 -0
- siliconcompiler/tools/builtin/minimum.py +57 -0
- siliconcompiler/tools/builtin/mux.py +70 -0
- siliconcompiler/tools/builtin/nop.py +38 -0
- siliconcompiler/tools/builtin/verify.py +83 -0
- siliconcompiler/tools/chisel/SCDriver.scala +10 -0
- siliconcompiler/tools/chisel/build.sbt +27 -0
- siliconcompiler/tools/chisel/chisel.py +37 -0
- siliconcompiler/tools/chisel/convert.py +140 -0
- siliconcompiler/tools/execute/exec_input.py +41 -0
- siliconcompiler/tools/execute/execute.py +17 -0
- siliconcompiler/tools/genfasm/bitstream.py +61 -0
- siliconcompiler/tools/genfasm/genfasm.py +40 -0
- siliconcompiler/tools/ghdl/convert.py +87 -0
- siliconcompiler/tools/ghdl/ghdl.py +41 -0
- siliconcompiler/tools/icarus/compile.py +87 -0
- siliconcompiler/tools/icarus/icarus.py +36 -0
- siliconcompiler/tools/icepack/bitstream.py +20 -0
- siliconcompiler/tools/icepack/icepack.py +43 -0
- siliconcompiler/tools/klayout/export.py +117 -0
- siliconcompiler/tools/klayout/klayout.py +119 -0
- siliconcompiler/tools/klayout/klayout_export.py +205 -0
- siliconcompiler/tools/klayout/klayout_operations.py +363 -0
- siliconcompiler/tools/klayout/klayout_show.py +242 -0
- siliconcompiler/tools/klayout/klayout_utils.py +176 -0
- siliconcompiler/tools/klayout/operations.py +194 -0
- siliconcompiler/tools/klayout/screenshot.py +98 -0
- siliconcompiler/tools/klayout/show.py +101 -0
- siliconcompiler/tools/magic/drc.py +49 -0
- siliconcompiler/tools/magic/extspice.py +19 -0
- siliconcompiler/tools/magic/magic.py +85 -0
- siliconcompiler/tools/magic/sc_drc.tcl +96 -0
- siliconcompiler/tools/magic/sc_extspice.tcl +54 -0
- siliconcompiler/tools/magic/sc_magic.tcl +47 -0
- siliconcompiler/tools/montage/montage.py +30 -0
- siliconcompiler/tools/montage/tile.py +66 -0
- siliconcompiler/tools/netgen/count_lvs.py +132 -0
- siliconcompiler/tools/netgen/lvs.py +90 -0
- siliconcompiler/tools/netgen/netgen.py +36 -0
- siliconcompiler/tools/netgen/sc_lvs.tcl +46 -0
- siliconcompiler/tools/nextpnr/apr.py +24 -0
- siliconcompiler/tools/nextpnr/nextpnr.py +59 -0
- siliconcompiler/tools/openfpgaloader/openfpgaloader.py +39 -0
- siliconcompiler/tools/openroad/__init__.py +0 -0
- siliconcompiler/tools/openroad/cts.py +45 -0
- siliconcompiler/tools/openroad/dfm.py +66 -0
- siliconcompiler/tools/openroad/export.py +131 -0
- siliconcompiler/tools/openroad/floorplan.py +70 -0
- siliconcompiler/tools/openroad/openroad.py +977 -0
- siliconcompiler/tools/openroad/physyn.py +27 -0
- siliconcompiler/tools/openroad/place.py +41 -0
- siliconcompiler/tools/openroad/rcx_bench.py +95 -0
- siliconcompiler/tools/openroad/rcx_extract.py +34 -0
- siliconcompiler/tools/openroad/route.py +45 -0
- siliconcompiler/tools/openroad/screenshot.py +60 -0
- siliconcompiler/tools/openroad/scripts/sc_apr.tcl +499 -0
- siliconcompiler/tools/openroad/scripts/sc_cts.tcl +64 -0
- siliconcompiler/tools/openroad/scripts/sc_dfm.tcl +20 -0
- siliconcompiler/tools/openroad/scripts/sc_export.tcl +98 -0
- siliconcompiler/tools/openroad/scripts/sc_floorplan.tcl +413 -0
- siliconcompiler/tools/openroad/scripts/sc_metrics.tcl +158 -0
- siliconcompiler/tools/openroad/scripts/sc_physyn.tcl +7 -0
- siliconcompiler/tools/openroad/scripts/sc_place.tcl +84 -0
- siliconcompiler/tools/openroad/scripts/sc_procs.tcl +423 -0
- siliconcompiler/tools/openroad/scripts/sc_rcx.tcl +63 -0
- siliconcompiler/tools/openroad/scripts/sc_rcx_bench.tcl +20 -0
- siliconcompiler/tools/openroad/scripts/sc_rcx_extract.tcl +12 -0
- siliconcompiler/tools/openroad/scripts/sc_route.tcl +133 -0
- siliconcompiler/tools/openroad/scripts/sc_screenshot.tcl +21 -0
- siliconcompiler/tools/openroad/scripts/sc_write.tcl +5 -0
- siliconcompiler/tools/openroad/scripts/sc_write_images.tcl +361 -0
- siliconcompiler/tools/openroad/show.py +94 -0
- siliconcompiler/tools/openroad/templates/pex.tcl +8 -0
- siliconcompiler/tools/opensta/__init__.py +101 -0
- siliconcompiler/tools/opensta/report_libraries.py +28 -0
- siliconcompiler/tools/opensta/scripts/sc_procs.tcl +47 -0
- siliconcompiler/tools/opensta/scripts/sc_report_libraries.tcl +74 -0
- siliconcompiler/tools/opensta/scripts/sc_timing.tcl +268 -0
- siliconcompiler/tools/opensta/timing.py +214 -0
- siliconcompiler/tools/slang/__init__.py +49 -0
- siliconcompiler/tools/slang/lint.py +101 -0
- siliconcompiler/tools/surelog/__init__.py +123 -0
- siliconcompiler/tools/surelog/parse.py +183 -0
- siliconcompiler/tools/surelog/templates/output.v +7 -0
- siliconcompiler/tools/sv2v/convert.py +46 -0
- siliconcompiler/tools/sv2v/sv2v.py +37 -0
- siliconcompiler/tools/template/template.py +125 -0
- siliconcompiler/tools/verilator/compile.py +139 -0
- siliconcompiler/tools/verilator/lint.py +19 -0
- siliconcompiler/tools/verilator/parse.py +27 -0
- siliconcompiler/tools/verilator/verilator.py +172 -0
- siliconcompiler/tools/vivado/__init__.py +7 -0
- siliconcompiler/tools/vivado/bitstream.py +21 -0
- siliconcompiler/tools/vivado/place.py +21 -0
- siliconcompiler/tools/vivado/route.py +21 -0
- siliconcompiler/tools/vivado/scripts/sc_bitstream.tcl +6 -0
- siliconcompiler/tools/vivado/scripts/sc_place.tcl +2 -0
- siliconcompiler/tools/vivado/scripts/sc_route.tcl +4 -0
- siliconcompiler/tools/vivado/scripts/sc_run.tcl +45 -0
- siliconcompiler/tools/vivado/scripts/sc_syn_fpga.tcl +25 -0
- siliconcompiler/tools/vivado/syn_fpga.py +20 -0
- siliconcompiler/tools/vivado/vivado.py +147 -0
- siliconcompiler/tools/vpr/_json_constraint.py +63 -0
- siliconcompiler/tools/vpr/_xml_constraint.py +109 -0
- siliconcompiler/tools/vpr/place.py +137 -0
- siliconcompiler/tools/vpr/route.py +124 -0
- siliconcompiler/tools/vpr/screenshot.py +54 -0
- siliconcompiler/tools/vpr/show.py +88 -0
- siliconcompiler/tools/vpr/vpr.py +357 -0
- siliconcompiler/tools/xyce/xyce.py +36 -0
- siliconcompiler/tools/yosys/lec.py +56 -0
- siliconcompiler/tools/yosys/prepareLib.py +59 -0
- siliconcompiler/tools/yosys/sc_lec.tcl +84 -0
- siliconcompiler/tools/yosys/sc_syn.tcl +79 -0
- siliconcompiler/tools/yosys/syn_asic.py +565 -0
- siliconcompiler/tools/yosys/syn_asic.tcl +377 -0
- siliconcompiler/tools/yosys/syn_asic_fpga_shared.tcl +31 -0
- siliconcompiler/tools/yosys/syn_fpga.py +146 -0
- siliconcompiler/tools/yosys/syn_fpga.tcl +233 -0
- siliconcompiler/tools/yosys/syn_strategies.tcl +81 -0
- siliconcompiler/tools/yosys/techmaps/lcu_kogge_stone.v +39 -0
- siliconcompiler/tools/yosys/templates/abc.const +2 -0
- siliconcompiler/tools/yosys/yosys.py +147 -0
- siliconcompiler/units.py +259 -0
- siliconcompiler/use.py +177 -0
- siliconcompiler/utils/__init__.py +423 -0
- siliconcompiler/utils/asic.py +158 -0
- siliconcompiler/utils/showtools.py +25 -0
- siliconcompiler-0.26.5.dist-info/LICENSE +190 -0
- siliconcompiler-0.26.5.dist-info/METADATA +195 -0
- siliconcompiler-0.26.5.dist-info/RECORD +251 -0
- siliconcompiler-0.26.5.dist-info/WHEEL +5 -0
- siliconcompiler-0.26.5.dist-info/entry_points.txt +12 -0
- siliconcompiler-0.26.5.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# KLayout script to export a .GDS file from a .DEF-formatted design.
|
|
2
|
+
#
|
|
3
|
+
# Source: The OpenROAD Project.
|
|
4
|
+
# https://github.com/The-OpenROAD-Project/OpenROAD-flow-scripts/blob/master/flow/util/def2stream.py
|
|
5
|
+
#
|
|
6
|
+
# License: BSD 3-Clause.
|
|
7
|
+
#
|
|
8
|
+
# Copyright (c) 2018, The Regents of the University of California
|
|
9
|
+
# All rights reserved.
|
|
10
|
+
#
|
|
11
|
+
# Redistribution and use in source and binary forms, with or without
|
|
12
|
+
# modification, are permitted provided that the following conditions are met:
|
|
13
|
+
#
|
|
14
|
+
# * Redistributions of source code must retain the above copyright notice, this
|
|
15
|
+
# list of conditions and the following disclaimer.
|
|
16
|
+
#
|
|
17
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
|
18
|
+
# this list of conditions and the following disclaimer in the documentation
|
|
19
|
+
# and/or other materials provided with the distribution.
|
|
20
|
+
#
|
|
21
|
+
# * Neither the name of the copyright holder nor the names of its
|
|
22
|
+
# contributors may be used to endorse or promote products derived from
|
|
23
|
+
# this software without specific prior written permission.
|
|
24
|
+
#
|
|
25
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
26
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
27
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
28
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
29
|
+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
30
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
31
|
+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
32
|
+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
33
|
+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
34
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
35
|
+
|
|
36
|
+
import pya
|
|
37
|
+
import os
|
|
38
|
+
import sys
|
|
39
|
+
import fnmatch
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def gds_export(design_name, in_def, in_files, out_file, tech, allow_missing, config_file='',
|
|
43
|
+
seal_file='',
|
|
44
|
+
timestamps=True):
|
|
45
|
+
from tools.klayout.klayout_utils import get_write_options # noqa E402
|
|
46
|
+
|
|
47
|
+
# Load def file
|
|
48
|
+
main_layout = pya.Layout()
|
|
49
|
+
main_layout.technology_name = tech.name
|
|
50
|
+
main_layout.read(in_def, tech.load_layout_options)
|
|
51
|
+
|
|
52
|
+
# List cells
|
|
53
|
+
def_cells = []
|
|
54
|
+
for def_cell in main_layout.each_cell():
|
|
55
|
+
def_cells.append(def_cell.name)
|
|
56
|
+
|
|
57
|
+
def_cells.remove(design_name)
|
|
58
|
+
# Remove vias
|
|
59
|
+
def_cells = sorted([cell for cell in def_cells if not cell.startswith("VIA_")])
|
|
60
|
+
print(f"[INFO] Read in {len(def_cells)} cells from DEF file")
|
|
61
|
+
for cell in def_cells:
|
|
62
|
+
print(f" [INFO] DEF cell: {cell}")
|
|
63
|
+
|
|
64
|
+
# Load in the gds to merge
|
|
65
|
+
print("[INFO] Merging GDS/OAS files...")
|
|
66
|
+
for fil in in_files:
|
|
67
|
+
macro_layout = pya.Layout()
|
|
68
|
+
macro_layout.read(fil)
|
|
69
|
+
print(f"[INFO] Read in {fil}")
|
|
70
|
+
for cell in list(def_cells):
|
|
71
|
+
if macro_layout.has_cell(cell):
|
|
72
|
+
subcell = main_layout.cell(cell)
|
|
73
|
+
print(f" [INFO] Merging in {cell}")
|
|
74
|
+
subcell.copy_tree(macro_layout.cell(cell))
|
|
75
|
+
def_cells.remove(cell)
|
|
76
|
+
|
|
77
|
+
# Copy the top level only to a new layout
|
|
78
|
+
print("[INFO] Copying toplevel cell '{0}'".format(design_name))
|
|
79
|
+
top_only_layout = pya.Layout()
|
|
80
|
+
top_only_layout.dbu = main_layout.dbu
|
|
81
|
+
top = top_only_layout.create_cell(design_name)
|
|
82
|
+
top.copy_tree(main_layout.cell(design_name))
|
|
83
|
+
|
|
84
|
+
print("[INFO] Checking for missing GDS/OAS...")
|
|
85
|
+
missing_cell = False
|
|
86
|
+
for check_cell in def_cells:
|
|
87
|
+
missing_cell = True
|
|
88
|
+
allowed_missing = any([fnmatch.fnmatch(check_cell, pattern) for pattern in allow_missing])
|
|
89
|
+
print(f"[{'WARNING' if allowed_missing else 'ERROR'}] LEF Cell '{check_cell}' has no "
|
|
90
|
+
"matching GDS/OAS cell. Cell will be empty")
|
|
91
|
+
|
|
92
|
+
if not missing_cell:
|
|
93
|
+
print("[INFO] All LEF cells have matching GDS/OAS cells")
|
|
94
|
+
|
|
95
|
+
print("[INFO] Checking for orphan cell in the final layout...")
|
|
96
|
+
for i in top_only_layout.each_cell():
|
|
97
|
+
if i.name != design_name and i.parent_cells() == 0:
|
|
98
|
+
print("[ERROR] Found orphan cell '{0}'".format(i.name))
|
|
99
|
+
|
|
100
|
+
if seal_file:
|
|
101
|
+
top_cell = top_only_layout.top_cell()
|
|
102
|
+
|
|
103
|
+
print("[INFO] Reading seal GDS/OAS file...")
|
|
104
|
+
print("\t{0}".format(seal_file))
|
|
105
|
+
top_only_layout.read(seal_file)
|
|
106
|
+
|
|
107
|
+
for cell in top_only_layout.top_cells():
|
|
108
|
+
if cell != top_cell:
|
|
109
|
+
print("[INFO] Merging '{0}' as child of '{1}'".format(cell.name, top_cell.name))
|
|
110
|
+
top.insert(pya.CellInstArray(cell.cell_index(), pya.Trans()))
|
|
111
|
+
|
|
112
|
+
# Write out the GDS
|
|
113
|
+
print("[INFO] Writing out GDS/OAS '{0}'".format(out_file))
|
|
114
|
+
top_only_layout.write(out_file, get_write_options(out_file, timestamps))
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def main():
|
|
118
|
+
# SC_ROOT provided by CLI
|
|
119
|
+
sys.path.append(SC_ROOT) # noqa: F821
|
|
120
|
+
|
|
121
|
+
from tools.klayout.klayout_utils import (
|
|
122
|
+
technology,
|
|
123
|
+
get_streams,
|
|
124
|
+
save_technology,
|
|
125
|
+
get_schema
|
|
126
|
+
)
|
|
127
|
+
from tools.klayout.klayout_show import show
|
|
128
|
+
from tools._common.asic import get_libraries
|
|
129
|
+
|
|
130
|
+
schema = get_schema(manifest='sc_manifest.json')
|
|
131
|
+
|
|
132
|
+
# Extract info from manifest
|
|
133
|
+
sc_step = schema.get('arg', 'step')
|
|
134
|
+
sc_index = schema.get('arg', 'index')
|
|
135
|
+
sc_pdk = schema.get('option', 'pdk')
|
|
136
|
+
sc_flow = schema.get('option', 'flow')
|
|
137
|
+
sc_task = schema.get('flowgraph', sc_flow, sc_step, sc_index, 'task')
|
|
138
|
+
sc_klayout_vars = schema.getkeys('tool', 'klayout', 'task', sc_task, 'var')
|
|
139
|
+
sc_stream = schema.get('tool', 'klayout', 'task', sc_task, 'var', 'stream',
|
|
140
|
+
step=sc_step, index=sc_index)[0]
|
|
141
|
+
|
|
142
|
+
if schema.valid('option', 'stackup'):
|
|
143
|
+
sc_stackup = schema.get('option', 'stackup')
|
|
144
|
+
else:
|
|
145
|
+
sc_stackup = schema.get('pdk', sc_pdk, 'stackup')[0]
|
|
146
|
+
|
|
147
|
+
design = schema.get('option', 'entrypoint')
|
|
148
|
+
if not design:
|
|
149
|
+
design = schema.get('design')
|
|
150
|
+
|
|
151
|
+
in_def = None
|
|
152
|
+
for ext in ('def.gz', 'def'):
|
|
153
|
+
in_def = os.path.join('inputs', f'{design}.{ext}')
|
|
154
|
+
if os.path.exists(in_def):
|
|
155
|
+
break
|
|
156
|
+
in_def = None
|
|
157
|
+
if not in_def:
|
|
158
|
+
in_def = schema.get('input', 'layout', 'def', step=sc_step, index=sc_index)[0]
|
|
159
|
+
|
|
160
|
+
out_file = os.path.join('outputs', f'{design}.{sc_stream}')
|
|
161
|
+
|
|
162
|
+
libs = get_libraries(schema, 'logic')
|
|
163
|
+
libs += get_libraries(schema, 'macro')
|
|
164
|
+
|
|
165
|
+
in_files = []
|
|
166
|
+
for lib in libs:
|
|
167
|
+
for s in get_streams(schema):
|
|
168
|
+
if schema.valid('library', lib, 'output', sc_stackup, s):
|
|
169
|
+
in_files.extend(schema.get('library', lib, 'output', sc_stackup, s,
|
|
170
|
+
step=sc_step, index=sc_index))
|
|
171
|
+
break
|
|
172
|
+
|
|
173
|
+
allow_missing = []
|
|
174
|
+
for lib in libs:
|
|
175
|
+
if schema.valid('library', lib, 'option', 'var', 'klayout_allow_missing_cell'):
|
|
176
|
+
patterns = [pattern for pattern in schema.get('library', lib, 'option', 'var',
|
|
177
|
+
'klayout_allow_missing_cell') if pattern]
|
|
178
|
+
allow_missing.extend(patterns)
|
|
179
|
+
|
|
180
|
+
if 'timestamps' in sc_klayout_vars:
|
|
181
|
+
sc_timestamps = schema.get('tool', 'klayout', 'task', sc_task, 'var', 'timestamps',
|
|
182
|
+
step=sc_step, index=sc_index) == ['true']
|
|
183
|
+
else:
|
|
184
|
+
sc_timestamps = False
|
|
185
|
+
|
|
186
|
+
if 'screenshot' in sc_klayout_vars:
|
|
187
|
+
sc_screenshot = schema.get('tool', 'klayout', 'task', sc_task, 'var', 'screenshot',
|
|
188
|
+
step=sc_step, index=sc_index) == ['true']
|
|
189
|
+
else:
|
|
190
|
+
sc_screenshot = True
|
|
191
|
+
|
|
192
|
+
sc_tech = technology(design, schema)
|
|
193
|
+
|
|
194
|
+
gds_export(design, in_def, in_files, out_file, sc_tech, allow_missing,
|
|
195
|
+
config_file='', seal_file='', timestamps=sc_timestamps)
|
|
196
|
+
|
|
197
|
+
if sc_screenshot:
|
|
198
|
+
show(schema, sc_tech, out_file, f'outputs/{design}.png', screenshot=True)
|
|
199
|
+
|
|
200
|
+
# Save tech files
|
|
201
|
+
save_technology(design, sc_tech)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
if __name__ == '__main__':
|
|
205
|
+
main()
|
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
import pya
|
|
2
|
+
import sys
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def read_layout(stream_file):
|
|
7
|
+
print(f"[INFO] Reading '{stream_file}'")
|
|
8
|
+
layout = pya.Layout()
|
|
9
|
+
layout.read(stream_file)
|
|
10
|
+
|
|
11
|
+
return layout
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def __with_timestamps(schema):
|
|
15
|
+
sc_step = schema.get('arg', 'step')
|
|
16
|
+
sc_index = schema.get('arg', 'index')
|
|
17
|
+
|
|
18
|
+
return schema.get('tool', 'klayout', 'task', 'operations', 'var', 'timestamps',
|
|
19
|
+
step=sc_step, index=sc_index) == ['true']
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def __get_keypath_step_index(schema, *keypath):
|
|
23
|
+
ret = {
|
|
24
|
+
'step': schema.get('arg', 'step'),
|
|
25
|
+
'index': schema.get('arg', 'index')
|
|
26
|
+
}
|
|
27
|
+
pernode = schema.get(*keypath, field='pernode')
|
|
28
|
+
if pernode == 'never':
|
|
29
|
+
ret['step'] = None
|
|
30
|
+
ret['index'] = None
|
|
31
|
+
|
|
32
|
+
return ret
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def __do_cell_swap(parent, old_cell_idx, new_cell, checked):
|
|
36
|
+
if (parent.cell_index() in checked):
|
|
37
|
+
return 0
|
|
38
|
+
|
|
39
|
+
checked.append(parent.cell_index())
|
|
40
|
+
replacements = 0
|
|
41
|
+
for inst in parent.each_inst():
|
|
42
|
+
if (inst.cell_index == old_cell_idx):
|
|
43
|
+
inst.cell = new_cell
|
|
44
|
+
replacements += 1
|
|
45
|
+
else:
|
|
46
|
+
replacements += __do_cell_swap(inst.cell, old_cell_idx, new_cell, checked)
|
|
47
|
+
return replacements
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def swap_cells(base_layout, oldcell, newcell):
|
|
51
|
+
top_cell = base_layout.top_cell()
|
|
52
|
+
old_cell = base_layout.cell(oldcell)
|
|
53
|
+
new_cell = base_layout.cell(newcell)
|
|
54
|
+
|
|
55
|
+
if (old_cell is None):
|
|
56
|
+
return base_layout
|
|
57
|
+
if (new_cell is None):
|
|
58
|
+
return base_layout
|
|
59
|
+
|
|
60
|
+
checked = []
|
|
61
|
+
replacements = __do_cell_swap(top_cell, old_cell.cell_index(), new_cell, checked)
|
|
62
|
+
print(f"[INFO] Swapping '{old_cell.name}' to '{new_cell.name}' in "
|
|
63
|
+
f"'{top_cell.name}': {replacements} occurrences")
|
|
64
|
+
base_layout.delete_cell(old_cell.cell_index())
|
|
65
|
+
|
|
66
|
+
return base_layout
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def add_outline(base_layout, layer):
|
|
70
|
+
top_cell = base_layout.top_cell()
|
|
71
|
+
bbox = top_cell.bbox()
|
|
72
|
+
|
|
73
|
+
layer_info = base_layout.get_info(layer)
|
|
74
|
+
print(f"[INFO] Adding outline to '{top_cell.name}' on layer '{layer_info.to_s()}'")
|
|
75
|
+
|
|
76
|
+
shapes = top_cell.shapes(layer)
|
|
77
|
+
shapes.insert(pya.Box(bbox))
|
|
78
|
+
|
|
79
|
+
return base_layout
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def add_layout(base_layout, layout):
|
|
83
|
+
top_cell = base_layout.top_cell()
|
|
84
|
+
|
|
85
|
+
other_layout_top = layout.top_cell()
|
|
86
|
+
|
|
87
|
+
print(f"[INFO] Adding layout from '{other_layout_top.name}' to '{top_cell.name}'")
|
|
88
|
+
new_cell = base_layout.create_cell(other_layout_top.name)
|
|
89
|
+
new_cell.copy_tree(other_layout_top)
|
|
90
|
+
|
|
91
|
+
cell_inst = pya.CellInstArray(new_cell.cell_index(), pya.Trans())
|
|
92
|
+
top_cell.insert(cell_inst)
|
|
93
|
+
|
|
94
|
+
return base_layout
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def add_layout_to_top(base_layout, new_top_cell_name):
|
|
98
|
+
top_cell = base_layout.top_cell()
|
|
99
|
+
|
|
100
|
+
print(f"[INFO] Adding layout from '{top_cell.name}' to new top cell '{new_top_cell_name}'")
|
|
101
|
+
new_cell = base_layout.create_cell(new_top_cell_name)
|
|
102
|
+
|
|
103
|
+
cell_inst = pya.CellInstArray(top_cell.cell_index(), pya.Trans())
|
|
104
|
+
new_cell.insert(cell_inst)
|
|
105
|
+
|
|
106
|
+
return base_layout
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def merge_layouts(layout1, layout2):
|
|
110
|
+
cell1 = layout1.top_cell()
|
|
111
|
+
cell2 = layout2.top_cell()
|
|
112
|
+
|
|
113
|
+
print(f"[INFO] Merging cells '{cell1.name}' and '{cell2.name}' into '{cell1.name}'")
|
|
114
|
+
|
|
115
|
+
cell1.copy_tree(cell2)
|
|
116
|
+
|
|
117
|
+
return layout1
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def rotate_layout(base_layout):
|
|
121
|
+
top_cell = base_layout.top_cell()
|
|
122
|
+
bbox = top_cell.bbox()
|
|
123
|
+
|
|
124
|
+
print(f"[INFO] Rotating layout '{top_cell.name}' 90 degrees")
|
|
125
|
+
|
|
126
|
+
transform = pya.Trans.R270
|
|
127
|
+
transform = pya.Trans(transform, pya.Vector(0, bbox.p2.x))
|
|
128
|
+
|
|
129
|
+
top_cell.transform(transform)
|
|
130
|
+
|
|
131
|
+
return base_layout
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def rename_top(base_layout, new_name):
|
|
135
|
+
top_cell = base_layout.top_cell()
|
|
136
|
+
print(f"[INFO] Renaming '{top_cell.name}' to '{new_name}' layout: '{top_cell.name}'")
|
|
137
|
+
top_cell.name = new_name
|
|
138
|
+
return base_layout
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def rename_cell(base_layout, old_name, new_name):
|
|
142
|
+
cell = base_layout.cell(old_name)
|
|
143
|
+
if not cell:
|
|
144
|
+
print(f"[WARNING] Unable to find '{old_name}' to rename")
|
|
145
|
+
print(f"[INFO] Renaming '{cell.name}' to '{new_name}' layout: '{base_layout.top_cell().name}'")
|
|
146
|
+
cell.name = new_name
|
|
147
|
+
return base_layout
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def write_stream(layout, outfile, timestamps):
|
|
151
|
+
from tools.klayout.klayout_utils import get_write_options
|
|
152
|
+
|
|
153
|
+
print(f"[INFO] Writing layout: '{outfile}'")
|
|
154
|
+
|
|
155
|
+
layout.write(outfile, get_write_options(outfile, timestamps))
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def make_property_text(layout, property_layer, property_name, destination_layer):
|
|
159
|
+
property_layer_info = layout.get_info(property_layer)
|
|
160
|
+
destination_layer_info = layout.get_info(destination_layer)
|
|
161
|
+
print(f"[INFO] Generating properties from {property_layer_info.to_s()} "
|
|
162
|
+
f"/ {property_name} on {destination_layer_info.to_s()}")
|
|
163
|
+
|
|
164
|
+
top_cell = layout.top_cell()
|
|
165
|
+
# Generate list of text objects
|
|
166
|
+
source_shapes_itr = top_cell.begin_shapes_rec(property_layer)
|
|
167
|
+
dest_shapes = []
|
|
168
|
+
while (not source_shapes_itr.at_end()):
|
|
169
|
+
shape = source_shapes_itr.shape()
|
|
170
|
+
shape_prop = shape.property(property_name)
|
|
171
|
+
if (shape_prop is not None and (shape.is_box() or shape.is_polygon())):
|
|
172
|
+
shape_center = shape.bbox().center()
|
|
173
|
+
dest_shapes.append(pya.Text(shape_prop, shape_center.x, shape_center.y))
|
|
174
|
+
source_shapes_itr.next()
|
|
175
|
+
|
|
176
|
+
# Insert objects
|
|
177
|
+
dest_shapes_layer = top_cell.shapes(destination_layer)
|
|
178
|
+
for shape in dest_shapes:
|
|
179
|
+
dest_shapes_layer.insert(shape)
|
|
180
|
+
|
|
181
|
+
print(f"[INFO] Generated {len(dest_shapes)} text shapes.")
|
|
182
|
+
|
|
183
|
+
return layout
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def delete_layers(layout, layers):
|
|
187
|
+
for cell in layout.each_cell():
|
|
188
|
+
print(f'[INFO] Deleting layers from {cell.name}')
|
|
189
|
+
for layer in layers:
|
|
190
|
+
layer_info = layout.get_info(layer)
|
|
191
|
+
print(f"[INFO] Deleting layer {layer_info.to_s()}")
|
|
192
|
+
|
|
193
|
+
cell.shapes(layer).clear()
|
|
194
|
+
|
|
195
|
+
return layout
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def merge_shapes(layout, layers):
|
|
199
|
+
if layers == ['all']:
|
|
200
|
+
layers = layout.layer_indexes()
|
|
201
|
+
|
|
202
|
+
for cell in layout.each_cell():
|
|
203
|
+
print(f"[INFO] Merging shapes in {cell.name}")
|
|
204
|
+
for layer in layers:
|
|
205
|
+
layer_info = layout.get_info(layer)
|
|
206
|
+
print(f"[INFO] Merging shapes on layer {layer_info.to_s()}")
|
|
207
|
+
|
|
208
|
+
shape_proc = pya.ShapeProcessor()
|
|
209
|
+
output_shapes = pya.Shapes()
|
|
210
|
+
|
|
211
|
+
cell_layout = cell.layout()
|
|
212
|
+
print(" Shape count (old):", cell.shapes(layer).size())
|
|
213
|
+
shape_proc.boolean(cell_layout,
|
|
214
|
+
cell,
|
|
215
|
+
layer,
|
|
216
|
+
cell_layout,
|
|
217
|
+
cell,
|
|
218
|
+
layer,
|
|
219
|
+
output_shapes,
|
|
220
|
+
pya.EdgeProcessor.ModeOr,
|
|
221
|
+
True,
|
|
222
|
+
True,
|
|
223
|
+
True)
|
|
224
|
+
print(" Shape count (new):", output_shapes.size())
|
|
225
|
+
cell.shapes(layer).clear()
|
|
226
|
+
cell.shapes(layer).insert(output_shapes)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def flatten(layout):
|
|
230
|
+
top_cell = layout.top_cell()
|
|
231
|
+
|
|
232
|
+
print(f"[INFO] Flattening: {top_cell.name}")
|
|
233
|
+
top_cell.flatten(True)
|
|
234
|
+
|
|
235
|
+
return layout
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def parse_operations(schema, base_layout, steps):
|
|
239
|
+
for step in steps:
|
|
240
|
+
step = step.split(":")
|
|
241
|
+
step_name = step[0]
|
|
242
|
+
step_args = ":".join(step[1:])
|
|
243
|
+
args_key = step_args.split(',')
|
|
244
|
+
|
|
245
|
+
if (step_name == "merge" or step_name == "add"):
|
|
246
|
+
files = []
|
|
247
|
+
if len(args_key) > 1:
|
|
248
|
+
if 'file' not in schema.get(*args_key, field='type'):
|
|
249
|
+
raise ValueError(f'{step_name} requires {args_key} be a file type')
|
|
250
|
+
files = schema.get(*args_key, **__get_keypath_step_index(schema, *args_key))
|
|
251
|
+
else:
|
|
252
|
+
files = [f'inputs/{step_args}']
|
|
253
|
+
for op_file in files:
|
|
254
|
+
if step_name == "add":
|
|
255
|
+
base_layout = add_layout(base_layout, read_layout(op_file))
|
|
256
|
+
else:
|
|
257
|
+
base_layout = merge_layouts(base_layout, read_layout(op_file))
|
|
258
|
+
elif (step_name == "rotate"):
|
|
259
|
+
base_layout = rotate_layout(base_layout)
|
|
260
|
+
elif (step_name == "outline"):
|
|
261
|
+
outline_layer = [int(layer) for layer in schema.get(
|
|
262
|
+
*args_key, **__get_keypath_step_index(schema, *args_key))]
|
|
263
|
+
if len(outline_layer) != 2:
|
|
264
|
+
raise ValueError('outline layer requires two entries for layer and purpose, '
|
|
265
|
+
f'received: {len(outline_layer)}')
|
|
266
|
+
base_layout = add_outline(base_layout,
|
|
267
|
+
base_layout.layer(outline_layer[0], outline_layer[1]))
|
|
268
|
+
elif (step_name == "convert_property"):
|
|
269
|
+
options = schema.get(*args_key, **__get_keypath_step_index(schema, *args_key))
|
|
270
|
+
if len(options) != 3 and len(options) != 5:
|
|
271
|
+
raise ValueError(f'{step_name} requires 3 or 5 arguments in {args_key}')
|
|
272
|
+
prop_layer = [int(layer) for layer in options[0:2]]
|
|
273
|
+
prop_number = options[2]
|
|
274
|
+
if prop_number.isnumeric():
|
|
275
|
+
prop_number = int(prop_number)
|
|
276
|
+
if (len(options) == 5):
|
|
277
|
+
dest_layer = [int(layer) for layer in options[3:]]
|
|
278
|
+
else:
|
|
279
|
+
dest_layer = prop_layer
|
|
280
|
+
base_layout = make_property_text(base_layout,
|
|
281
|
+
base_layout.layer(prop_layer[0], prop_layer[1]),
|
|
282
|
+
prop_number,
|
|
283
|
+
base_layout.layer(dest_layer[0], dest_layer[1]))
|
|
284
|
+
elif (step_name == "rename"):
|
|
285
|
+
new_name = schema.get(*args_key, **__get_keypath_step_index(schema, *args_key))[0]
|
|
286
|
+
base_layout = rename_top(base_layout, new_name)
|
|
287
|
+
elif (step_name == "rename_cell"):
|
|
288
|
+
new_name = schema.get(*args_key, **__get_keypath_step_index(schema, *args_key))[0]
|
|
289
|
+
for renameset in schema.get(*args_key, **__get_keypath_step_index(schema, *args_key)):
|
|
290
|
+
oldcell, newcell = renameset.split("=")
|
|
291
|
+
base_layout = rename_cell(base_layout, oldcell, newcell)
|
|
292
|
+
elif (step_name == "swap"):
|
|
293
|
+
for swapset in schema.get(*args_key, **__get_keypath_step_index(schema, *args_key)):
|
|
294
|
+
oldcell, newcell = swapset.split("=")
|
|
295
|
+
base_layout = swap_cells(base_layout, oldcell, newcell)
|
|
296
|
+
elif (step_name == "add_top"):
|
|
297
|
+
new_name = schema.get(*args_key, **__get_keypath_step_index(schema, *args_key))[0]
|
|
298
|
+
base_layout = add_layout_to_top(base_layout, new_name)
|
|
299
|
+
elif (step_name == "write"):
|
|
300
|
+
write_stream(base_layout, f'outputs/{step_args}', __with_timestamps(schema))
|
|
301
|
+
elif (step_name == "flatten"):
|
|
302
|
+
base_layout = flatten(base_layout)
|
|
303
|
+
elif (step_name == "delete_layers"):
|
|
304
|
+
layers = []
|
|
305
|
+
for layer in schema.get(*args_key, **__get_keypath_step_index(schema, *args_key)):
|
|
306
|
+
layer_num = None
|
|
307
|
+
layer_purpose = None
|
|
308
|
+
if '/' in layer:
|
|
309
|
+
layer_num, layer_purpose = layer.split('/')
|
|
310
|
+
elif ' ' in layer:
|
|
311
|
+
layer_num, layer_purpose = layer.split(' ')
|
|
312
|
+
elif ':' in layer:
|
|
313
|
+
layer_num, layer_purpose = layer.split(':')
|
|
314
|
+
else:
|
|
315
|
+
raise ValueError(f'Unable to determine layer purpose pair for {layer}')
|
|
316
|
+
layers.append(base_layout.layer(int(layer_num), int(layer_purpose)))
|
|
317
|
+
base_layout = delete_layers(base_layout, layers)
|
|
318
|
+
elif (step_name == "merge_shapes"):
|
|
319
|
+
layers = schema.get(*args_key, **__get_keypath_step_index(schema, *args_key))
|
|
320
|
+
base_layout = merge_shapes(base_layout, layers)
|
|
321
|
+
else:
|
|
322
|
+
raise ValueError(f"Unknown step: {step_name}")
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
if __name__ == "__main__":
|
|
326
|
+
# SC_ROOT provided by CLI
|
|
327
|
+
sys.path.append(SC_ROOT) # noqa: F821
|
|
328
|
+
|
|
329
|
+
from tools.klayout.klayout_utils import (
|
|
330
|
+
technology,
|
|
331
|
+
get_streams,
|
|
332
|
+
get_schema
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
schema = get_schema(manifest='sc_manifest.json')
|
|
336
|
+
|
|
337
|
+
# Extract info from manifest
|
|
338
|
+
sc_step = schema.get('arg', 'step')
|
|
339
|
+
sc_index = schema.get('arg', 'index')
|
|
340
|
+
sc_tool = 'klayout'
|
|
341
|
+
sc_task = 'operations'
|
|
342
|
+
|
|
343
|
+
sc_ext = get_streams(schema)[0]
|
|
344
|
+
design = schema.get('option', 'entrypoint')
|
|
345
|
+
if not design:
|
|
346
|
+
design = schema.get('design')
|
|
347
|
+
|
|
348
|
+
in_gds = os.path.join('inputs', f'{design}.{sc_ext}.gz')
|
|
349
|
+
if not os.path.exists(in_gds):
|
|
350
|
+
in_gds = os.path.join('inputs', f'{design}.{sc_ext}')
|
|
351
|
+
if not os.path.exists(in_gds):
|
|
352
|
+
in_gds = schema.get('input', 'layout', sc_ext)[0]
|
|
353
|
+
out_gds = os.path.join('outputs', f'{design}.{sc_ext}')
|
|
354
|
+
|
|
355
|
+
tech = technology(design, schema)
|
|
356
|
+
base_layout = read_layout(in_gds)
|
|
357
|
+
base_layout.technology_name = tech.name
|
|
358
|
+
|
|
359
|
+
sc_klayout_ops = schema.get('tool', sc_tool, 'task', sc_task, 'var', 'operations',
|
|
360
|
+
step=sc_step, index=sc_index)
|
|
361
|
+
parse_operations(schema, base_layout, sc_klayout_ops)
|
|
362
|
+
|
|
363
|
+
write_stream(base_layout, out_gds, __with_timestamps(schema))
|