Coverage for klayout_pex/magic/magic_runner.py: 29%
45 statements
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-17 17:24 +0000
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-17 17:24 +0000
1#
2# --------------------------------------------------------------------------------
3# SPDX-FileCopyrightText: 2024 Martin Jan Köhler and Harald Pretl
4# Johannes Kepler University, Institute for Integrated Circuits.
5#
6# This file is part of KPEX
7# (see https://github.com/martinjankoehler/klayout-pex).
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21# SPDX-License-Identifier: GPL-3.0-or-later
22# --------------------------------------------------------------------------------
23#
24from enum import StrEnum
25import time
26from typing import *
28import os
29import subprocess
31from ..log import (
32 info,
33 # warning,
34 rule,
35 subproc,
36)
37from ..version import __version__
40class MagicPEXMode(StrEnum):
41 CC = "CC"
42 RC = "RC"
43 DEFAULT = "CC"
46def prepare_magic_script(gds_path: str,
47 cell_name: str,
48 run_dir_path: str,
49 script_path: str,
50 output_netlist_path: str,
51 pex_mode: MagicPEXMode,
52 c_threshold: float,
53 r_threshold: float,
54 halo: Optional[float]):
55 gds_path = os.path.abspath(gds_path)
56 run_dir_path = os.path.abspath(run_dir_path)
57 output_netlist_path = os.path.abspath(output_netlist_path)
59 halo_scale = 200.0
60 halo_decl = '' if halo is None else f"\nextract halo {round(halo * halo_scale)}"
62 script: str = ""
63 match pex_mode:
64 case MagicPEXMode.CC:
65 script = f"""# Generated by kpex {__version__}
66crashbackups stop
67drc off
68gds read {gds_path}
69load {cell_name}
70select top cell
71flatten {cell_name}_flat
72load {cell_name}_flat
73cellname delete {cell_name} -noprompt
74cellname rename {cell_name}_flat {cell_name}
75select top cell
76extract path {run_dir_path}{halo_decl}
77extract all
78ext2spice cthresh {c_threshold}
79ext2spice format ngspice
80ext2spice -p {run_dir_path} -o {output_netlist_path}
81quit -noprompt"""
82 case MagicPEXMode.RC:
83 script = f"""# Generated by kpex {__version__}
84crashbackups stop
85drc off
86gds read {gds_path}
87load {cell_name}
88select top cell
89flatten {cell_name}_flat
90load {cell_name}_flat
91cellname delete {cell_name} -noprompt
92cellname rename {cell_name}_flat {cell_name}
93select top cell
94extract path {run_dir_path}{halo_decl}
95extract do resistance
96extract all
97ext2sim labels on
98ext2sim
99extresist tolerance {r_threshold}
100extresist all
101ext2spice cthresh {c_threshold}
102ext2spice rthresh {r_threshold}
103ext2spice extresist on
104ext2spice format ngspice
105ext2spice -p {run_dir_path} -o {output_netlist_path}
106quit -noprompt
107"""
108 with open(script_path, 'w') as f:
109 f.write(script)
111def run_magic(exe_path: str,
112 magicrc_path: str,
113 script_path: str,
114 log_path: str):
115 args = [
116 exe_path,
117 '-dnull', #
118 '-noconsole', #
119 '-rcfile', #
120 magicrc_path, #
121 script_path, # TCL script
122 ]
124 info('Calling MAGIC')
125 subproc(f"{' '.join(args)}, output file: {log_path}")
127 rule('MAGIC Output')
129 start = time.time()
131 proc = subprocess.Popen(args,
132 stdin=subprocess.DEVNULL,
133 stdout=subprocess.PIPE,
134 stderr=subprocess.STDOUT,
135 universal_newlines=True,
136 text=True)
137 with open(log_path, 'w') as f:
138 while True:
139 line = proc.stdout.readline()
140 if not line:
141 break
142 subproc(line[:-1]) # remove newline
143 f.writelines([line])
144 proc.wait()
146 duration = time.time() - start
148 rule()
150 if proc.returncode == 0:
151 info(f"MAGIC succeeded after {'%.4g' % duration}s")
152 else:
153 raise Exception(f"MAGIC failed with status code {proc.returncode} after {'%.4g' % duration}s, "
154 f"see log file: {log_path}")