#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # Copyright 2020 SkyWater PDK Authors # # 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 # # https://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. # # SPDX-License-Identifier: Apache-2.0 """ A module containing various functions related to `magic` tool """ import os import re import subprocess from typing import Tuple FATAL_ERROR = re.compile('((Error parsing)|(No such file or directory)|(couldn\'t be read))') # noqa: E501 class MagicError(Exception): """ Raised when there are errors in magic tool execution. """ def __init__(self, message, errorcode): self.errorcode = errorcode super().__init__(message) def add_magic_tcl_header(ofile, gdsfile): """ Adds a header to a TCL file. Parameters ---------- ofile : TextIOWrapper output file stream gdsfile : str path to GDS file """ ofile.write('#!/bin/env wish\n') ofile.write('drc off\n') ofile.write('scalegrid 1 2\n') ofile.write('cif istyle vendorimport\n') ofile.write('gds readonly true\n') ofile.write('gds rescale false\n') ofile.write('tech unlock *\n') ofile.write('cif warning default\n') ofile.write('set VDD VPWR\n') ofile.write('set GND VGND\n') ofile.write('set SUB SUBS\n') ofile.write(f'gds read {gdsfile}\n') def create_tcl_plot_script_for_gds( input_gds, output_tcl=None, output_svg=None) -> Tuple[str, str]: """ Creates TCL script for creating cell layout image from GDS file. Parameters ---------- input_gds : str Path to GDS file output_tcl : str Path to created TCL file output_svg : str Path where the SVG file created by output_tcl script should be located Returns ------- Tuple(str, str) : paths to TCL and SVG files (can be used if autogenerated) """ input_gds = os.path.abspath(input_gds) destdir, gdsfile = os.path.split(input_gds) basename, ext = os.path.splitext(gdsfile) if not output_tcl: output_tcl = os.path.join(destdir, f'{basename}.gds2svg.tcl') if not output_svg: output_svg = os.path.join(destdir, f'{basename}.tmp.svg') with open(output_tcl, 'w') as ofile: add_magic_tcl_header(ofile, input_gds) ofile.write(f"load {basename}\n") ofile.write("box 0 0 0 0\n") ofile.write("select top cell\n") ofile.write("expand\n") ofile.write("view\n") ofile.write("select clear\n") ofile.write("box position -1000 -1000\n") ofile.write(f"plot svg {output_svg}\n") ofile.write("quit -noprompt\n") return output_tcl, output_svg def run_magic( tcl_file, technology_file, workdir=None, display_workstation='NULL', debug=False, magic_executable='magic') -> int: """ Generates layout files for a given TCL file and technology file. Uses `magic` tool for generating the layout. Parameters ---------- tcl_file : str path to input TCL file technology_file : str path to the technology file workdir : str path to the working directory for `magic` display_workstation : str graphics interface, can be NULL, X11 or OpenGL debug : bool True if all output from `magic` tool should be displayed magic_executable : str Path to `magic` executable Returns ------- int: return code for `magic` tool Raises ------ AssertionError Raised when display_workstation is not NULL, X11 or OpenGL MagicError Raised when `magic` tool failed to run for given files """ assert display_workstation in ['NULL', 'X11', 'OpenGL', 'XR'] cmd = [ magic_executable, '-nowrapper', '-noconsole', f'-d{display_workstation}', f'-T{os.path.abspath(technology_file)}', '-D' if debug else '', os.path.abspath(tcl_file) ] result = subprocess.run( cmd, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=workdir, universal_newlines=True ) errors_present = False for line in result.stdout.splitlines(): m = FATAL_ERROR.match(line) if m: errors_present = True break if result.returncode != 0 or errors_present: msg = ['ERROR: There were fatal errors in magic.'] msg += result.stdout.splitlines() msg += [f'ERROR: Magic exited with status {result.returncode}'] msg.append("") msg.append(" ".join(cmd)) msg.append('='*75) msg.append(result.stdout) msg.append('='*75) msg.append(tcl_file) msg.append('-'*75) msg.append(msg[0]) raise MagicError('\n'.join(msg), result.returncode) if debug: print(result.stdout)