From 25ac57d103ec9652b85eabb75ac705d6a390bead Mon Sep 17 00:00:00 2001 From: Tim Edwards Date: Tue, 1 May 2018 14:07:16 -0400 Subject: [PATCH] First pass at incorporating the efabless LVS manager GUI into the netgen distribution. It sort of works, but not quite there yet. --- Makefile | 4 +- README | 2 +- TO_DO | 25 +- defs.mak | 5 +- python/Makefile | 54 +++ python/consoletext.py | 54 +++ python/helpwindow.py | 229 +++++++++++ python/lvs_help.txt | 5 + python/lvs_manager.py | 819 +++++++++++++++++++++++++++++++++++++++ python/lvs_manager.py.in | 819 +++++++++++++++++++++++++++++++++++++++ python/tksimpledialog.py | 86 ++++ python/tooltip.py | 158 ++++++++ python/treeviewsplit.py | 653 +++++++++++++++++++++++++++++++ scripts/config.log | 519 +++++++++++++------------ scripts/config.status | 3 +- scripts/configure | 55 +++ scripts/configure.in | 18 + scripts/defs.mak | 5 +- scripts/defs.mak.in | 1 + tcltk/Makefile | 1 + tcltk/netgen.sh.in | 8 +- 21 files changed, 3240 insertions(+), 283 deletions(-) create mode 100644 python/Makefile create mode 100755 python/consoletext.py create mode 100755 python/helpwindow.py create mode 100644 python/lvs_help.txt create mode 100755 python/lvs_manager.py create mode 100755 python/lvs_manager.py.in create mode 100755 python/tksimpledialog.py create mode 100755 python/tooltip.py create mode 100755 python/treeviewsplit.py diff --git a/Makefile b/Makefile index 402b26d..5aaafcd 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ # NETGENDIR = . -PROGRAMS = netgen +PROGRAMS = netgen python MODULES = base MAKEFLAGS = @@ -61,7 +61,7 @@ install-real: install-dirs install-tcl-dirs: ${NETGENDIR}/scripts/mkdirs $(DESTDIR)${BINDIR} $(DESTDIR)${MANDIR} \ - $(DESTDIR)${TCLDIR} + $(DESTDIR)${TCLDIR} $(DESTDIR)${PYDIR} install-dirs: ${NETGENDIR}/scripts/mkdirs $(DESTDIR)${BINDIR} $(DESTDIR)${MANDIR} diff --git a/README b/README index b2d35b4..0a0a899 100644 --- a/README +++ b/README @@ -78,7 +78,7 @@ of CVS check-in notes on http://opencircuitdesign.com/netgen/. BUILDING NETGEN: ---------------- -NETGEN version 1.4 uses the same "make" procedure as Magic version 7.5 +NETGEN version 1.5 uses the same "make" procedure as Magic version 8.1 and IRSIM version 9.7: ./configure diff --git a/TO_DO b/TO_DO index d1277c5..8da7042 100644 --- a/TO_DO +++ b/TO_DO @@ -6,33 +6,24 @@ TO_DO list for version 1.5 Possible useful additions (not critical) ---------------------- - 1) Add new output style allowing netlists to be described by Tcl scripts - (similar to the C-Code format, but does not require compiling) (this - was done in 1.5.72, but now needs some scripts to make use of it.) - 2) Add automatic file format guessing from file extension for "write" command - 3) Add Tk GUI to match (and improve upon!) the original X11 GUI. - 4) Incorporate into Tcl-based xcircuit - 5) Extend 2-terminal device handling (namely R and C) to more formats + 1) Add automatic file format guessing from file extension for "write" command + 2) Incorporate into Tcl-based xcircuit + 3) Extend 2-terminal device handling (namely R and C) to more formats (where appropriate). - 6) Generate feedback in the form of a .mag file to facilitate interactive + 4) Generate feedback in the form of a .mag file to facilitate interactive netlist comparison in conjunction with magic. This may be an error file, but it may also make use of magic 7.2's "element" command to build rat's nests or outline areas of interest or anything else that might be helpful to interactive comparison. - 7) Improve upon the readability of the netlist comparator output. - In particular, pinpoint situations in which an error does not show - up in an obvious way in either the element or node lists. - 8) Handle the hierarchy of ".ext" files automatically. - 9) Retain position information from ".ext" files for the purpose of writing + 5) Handle the hierarchy of ".ext" files automatically. + 6) Retain position information from ".ext" files for the purpose of writing feedback information into magic and for matching devices in the element list to their counterparts in the original layout [note---done for .sim format, which contains position information. Usefulness of improving the .ext parsing is questionable. Ditto for item 8]. -10) Expand upon the details of format-to-format translation, (especially + 7) Expand upon the details of format-to-format translation, (especially ntk and ext formats, SPICE format writing, and sim format reading). -11) Add a "property" command to add/remove properties to check, and change - the slop values (done). -12) Add a "subcircuit" command to mimic a SPICE ".SUBCKT ... .ENDS" card + 8) Add a "subcircuit" command to mimic a SPICE ".SUBCKT ... .ENDS" card (i.e., the ability to add subcircuit "stub" definitions from the command line or from a setup file). diff --git a/defs.mak b/defs.mak index dc4bf68..5e7a5c1 100644 --- a/defs.mak +++ b/defs.mak @@ -33,6 +33,7 @@ MANDIR = ${mandir} LIBDIR = ${libdir} DOCDIR = ${libdir}/netgen/doc TCLDIR = ${libdir}/netgen/tcl +PYDIR = ${libdir}/netgen/python MAIN_EXTRA_LIBS = ${NETGENDIR}/tcltk/libtcltk.o LD_EXTRA_LIBS = @@ -66,8 +67,8 @@ CPP = gcc -E -x c CXX = @CXX@ CPPFLAGS = -I. -I${NETGENDIR} -DFLAGS = -DCAD_DIR=\"${LIBDIR}\" -DTCL_DIR=\"${TCLDIR}\" -DUSE_TCL_STUBS -DUSE_TK_STUBS -DPACKAGE_NAME=\"netgen\" -DPACKAGE_TARNAME=\"netgen\" -DPACKAGE_VERSION=\"1.3\" -DPACKAGE_STRING=\"netgen\ 1.3\" -DPACKAGE_BUGREPORT=\"eda-dev@opencircuitdesign.com\" -DPACKAGE_URL=\"\" -DNETGEN_VERSION=\"1.5\" -DNETGEN_REVISION=\"90\" -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DSIZEOF_VOID_P=8 -DSIZEOF_UNSIGNED_INT=4 -DSIZEOF_UNSIGNED_LONG=8 -DSIZEOF_UNSIGNED_LONG_LONG=8 -DSTDC_HEADERS=1 -DHAVE_SETENV=1 -DHAVE_PUTENV=1 -DHAVE_DIRENT_H=1 -DHAVE_LIMITS_H=1 -DHAVE_VA_COPY=1 -DHAVE___VA_COPY=1 -DDBUG_OFF=1 -DTCL_NETGEN=1 -Dlinux=1 -DSYSV=1 -DISC=1 -DSHDLIB_EXT=\".so\" -DNDEBUG -DFLAGS_NOSTUB = -DCAD_DIR=\"${LIBDIR}\" -DTCL_DIR=\"${TCLDIR}\" -DPACKAGE_NAME=\"netgen\" -DPACKAGE_TARNAME=\"netgen\" -DPACKAGE_VERSION=\"1.3\" -DPACKAGE_STRING=\"netgen\ 1.3\" -DPACKAGE_BUGREPORT=\"eda-dev@opencircuitdesign.com\" -DPACKAGE_URL=\"\" -DNETGEN_VERSION=\"1.5\" -DNETGEN_REVISION=\"90\" -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DSIZEOF_VOID_P=8 -DSIZEOF_UNSIGNED_INT=4 -DSIZEOF_UNSIGNED_LONG=8 -DSIZEOF_UNSIGNED_LONG_LONG=8 -DSTDC_HEADERS=1 -DHAVE_SETENV=1 -DHAVE_PUTENV=1 -DHAVE_DIRENT_H=1 -DHAVE_LIMITS_H=1 -DHAVE_VA_COPY=1 -DHAVE___VA_COPY=1 -DDBUG_OFF=1 -DTCL_NETGEN=1 -Dlinux=1 -DSYSV=1 -DISC=1 -DSHDLIB_EXT=\".so\" -DNDEBUG +DFLAGS = -DCAD_DIR=\"${LIBDIR}\" -DTCL_DIR=\"${TCLDIR}\" -DUSE_TCL_STUBS -DUSE_TK_STUBS -DPACKAGE_NAME=\"netgen\" -DPACKAGE_TARNAME=\"netgen\" -DPACKAGE_VERSION=\"1.3\" -DPACKAGE_STRING=\"netgen\ 1.3\" -DPACKAGE_BUGREPORT=\"eda-dev@opencircuitdesign.com\" -DPACKAGE_URL=\"\" -DNETGEN_VERSION=\"1.5\" -DNETGEN_REVISION=\"98\" -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DSIZEOF_VOID_P=8 -DSIZEOF_UNSIGNED_INT=4 -DSIZEOF_UNSIGNED_LONG=8 -DSIZEOF_UNSIGNED_LONG_LONG=8 -DSTDC_HEADERS=1 -DHAVE_SETENV=1 -DHAVE_PUTENV=1 -DHAVE_DIRENT_H=1 -DHAVE_LIMITS_H=1 -DHAVE_VA_COPY=1 -DHAVE___VA_COPY=1 -DDBUG_OFF=1 -DTCL_NETGEN=1 -Dlinux=1 -DSYSV=1 -DISC=1 -DSHDLIB_EXT=\".so\" -DNDEBUG +DFLAGS_NOSTUB = -DCAD_DIR=\"${LIBDIR}\" -DTCL_DIR=\"${TCLDIR}\" -DPACKAGE_NAME=\"netgen\" -DPACKAGE_TARNAME=\"netgen\" -DPACKAGE_VERSION=\"1.3\" -DPACKAGE_STRING=\"netgen\ 1.3\" -DPACKAGE_BUGREPORT=\"eda-dev@opencircuitdesign.com\" -DPACKAGE_URL=\"\" -DNETGEN_VERSION=\"1.5\" -DNETGEN_REVISION=\"98\" -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DSIZEOF_VOID_P=8 -DSIZEOF_UNSIGNED_INT=4 -DSIZEOF_UNSIGNED_LONG=8 -DSIZEOF_UNSIGNED_LONG_LONG=8 -DSTDC_HEADERS=1 -DHAVE_SETENV=1 -DHAVE_PUTENV=1 -DHAVE_DIRENT_H=1 -DHAVE_LIMITS_H=1 -DHAVE_VA_COPY=1 -DHAVE___VA_COPY=1 -DDBUG_OFF=1 -DTCL_NETGEN=1 -Dlinux=1 -DSYSV=1 -DISC=1 -DSHDLIB_EXT=\".so\" -DNDEBUG CFLAGS = -g -m64 -fPIC -fPIC DEPEND_FILE = Depend diff --git a/python/Makefile b/python/Makefile new file mode 100644 index 0000000..9aca2f6 --- /dev/null +++ b/python/Makefile @@ -0,0 +1,54 @@ +MODULE = python +NETGENDIR = .. +SRCS = +SCRIPTS = consoletext.py helpwindow.py lvs_manager.py treeviewsplit.py +SCRIPTS += tksimpledialog.py tooltip.py lvs_help.txt + +SCRIPTINSTALL = $(DESTDIR)${PYDIR} + +include ${NETGENDIR}/defs.mak + +main: lvs_manager.py + +tcl-main: lvs_manager.py + +$(DESTDIR)${PYDIR}/consoletext.py: + ${RM} $@ + ${CP} consoletext.py $@ + +$(DESTDIR)${PYDIR}/helpwindow.py: + ${RM} $@ + ${CP} helpwindow.py $@ + +lvs_manager.py: lvs_manager.py.in + sed -e '/SUBST_SCRIPT_DIR/s#SUBST_SCRIPT_DIR#$(SCRIPTINSTALL)#' \ + lvs_manager.py.in > lvs_manager.py + +$(DESTDIR)${PYDIR}/lvs_manager.py: lvs_manager.py + ${RM} $@ + ${CP} lvs_manager.py $@ + +$(DESTDIR)${PYDIR}/treeviewsplit.py: treeviewsplit.py + ${RM} $@ + ${CP} treeviewsplit.py $@ + +$(DESTDIR)${PYDIR}/tksimpledialog.py: tksimpledialog.py + ${RM} $@ + ${CP} tksimpledialog.py $@ + +$(DESTDIR)${PYDIR}/tooltip.py: tooltip.py + ${RM} $@ + ${CP} tooltip.py $@ + +$(DESTDIR)${PYDIR}/lvs_help.txt: lvs_help.txt + ${RM} $@ + ${CP} lvs_help.txt $@ + +install: install-tcl + +install-tcl: $(DESTDIR)${PYDIR} $(DESTDIR)${PYDIR}/consoletext.py \ + $(DESTDIR)${PYDIR}/helpwindow.py $(DESTDIR)${PYDIR}/lvs_manager.py \ + $(DESTDIR)${PYDIR}/treeviewsplit.py $(DESTDIR)${PYDIR}/tksimpledialog.py \ + $(DESTDIR)${PYDIR}/tooltip.py $(DESTDIR)${PYDIR}/lvs_help.txt + +include ${NETGENDIR}/rules.mak diff --git a/python/consoletext.py b/python/consoletext.py new file mode 100755 index 0000000..f2da471 --- /dev/null +++ b/python/consoletext.py @@ -0,0 +1,54 @@ +#!/bin/env python3 +# +#-------------------------------------------------------- +""" + consoletext --- extends tkinter class Text + with stdout and stderr redirection. +""" +#-------------------------------------------------------- +# Written by Tim Edwards +# efabless, inc. +# September 11, 2016 +# Version 0.1 +#-------------------------------------------------------- + +import sys +import tkinter + +class ConsoleText(tkinter.Text): + linelimit = 500 + class IORedirector(object): + '''A general class for redirecting I/O to this Text widget.''' + def __init__(self,text_area): + self.text_area = text_area + + class StdoutRedirector(IORedirector): + '''A class for redirecting stdout to this Text widget.''' + def write(self,str): + self.text_area.write(str,False) + + class StderrRedirector(IORedirector): + '''A class for redirecting stderr to this Text widget.''' + def write(self,str): + self.text_area.write(str,True) + + def __init__(self, master=None, cnf={}, **kw): + '''See the __init__ for Tkinter.Text.''' + + tkinter.Text.__init__(self, master, cnf, **kw) + + self.tag_configure('stdout',background='white',foreground='black') + self.tag_configure('stderr',background='white',foreground='red') + # None of these works! Cannot change selected text background! + self.config(selectbackground='blue', selectforeground='white') + self.tag_configure('sel',background='blue',foreground='white') + + def write(self, val, is_stderr=False): + lines = int(self.index('end-1c').split('.')[0]) + if lines > self.linelimit: + self.delete('1.0', str(lines - self.linelimit) + '.0') + self.insert('end',val,'stderr' if is_stderr else 'stdout') + self.see('end') + + def limit(self, val): + self.linelimit = val diff --git a/python/helpwindow.py b/python/helpwindow.py new file mode 100755 index 0000000..cd2f01f --- /dev/null +++ b/python/helpwindow.py @@ -0,0 +1,229 @@ +#!/bin/env python3 +# +#-------------------------------------------------------- +# Help Window for the Open Galaxy project manager +# +#-------------------------------------------------------- +# Written by Tim Edwards +# efabless, inc. +# September 12, 2016 +# Version 0.1 +#-------------------------------------------------------- + +import re +import tkinter +from tkinter import ttk + +class HelpWindow(tkinter.Toplevel): + """Open Galaxy help window.""" + + def __init__(self, parent=None, fontsize = 11, *args, **kwargs): + '''See the __init__ for Tkinter.Toplevel.''' + tkinter.Toplevel.__init__(self, parent, *args, **kwargs) + + s = ttk.Style() + s.configure('normal.TButton', font=('Helvetica', fontsize), border = 3, relief = 'raised') + + self.withdraw() + self.title('Open Galaxy Help') + + self.helptitle = ttk.Label(self, style='title.TLabel', text = '(no text)') + self.helptitle.grid(column = 0, row = 0, sticky = "news") + self.helpbar = ttk.Separator(self, orient='horizontal') + self.helpbar.grid(column = 0, row = 1, sticky = "news") + + self.hframe = tkinter.Frame(self) + self.hframe.grid(column = 0, row = 2, sticky = "news") + self.hframe.helpdisplay = ttk.Frame(self.hframe) + self.hframe.helpdisplay.pack(side = 'left', fill = 'both', expand = 'true') + + self.hframe.helpdisplay.helptext = tkinter.Text(self.hframe.helpdisplay, wrap='word') + self.hframe.helpdisplay.helptext.pack(side = 'top', fill = 'both', expand = 'true') + # Add scrollbar to help window + self.hframe.scrollbar = ttk.Scrollbar(self.hframe) + self.hframe.scrollbar.pack(side='right', fill='y') + # attach help window to scrollbar + self.hframe.helpdisplay.helptext.config(yscrollcommand = self.hframe.scrollbar.set) + self.hframe.scrollbar.config(command = self.hframe.helpdisplay.helptext.yview) + + self.hframe.toc = ttk.Treeview(self.hframe, selectmode='browse') + self.hframe.toc.bind('<>', self.toc_to_page) + self.hframe.toc.bind('<>', self.toc_toggle) + self.hframe.toc.bind('<>', self.toc_toggle) + self.hframe.toc.tag_configure('title', font=('Helvetica', fontsize, 'bold italic'), + foreground = 'brown', anchor = 'center') + self.hframe.toc.heading('#0', text = "Table of Contents") + + self.bbar = ttk.Frame(self) + self.bbar.grid(column = 0, row = 3, sticky = "news") + self.bbar.close_button = ttk.Button(self.bbar, text='Close', + command=self.close, style = 'normal.TButton') + self.bbar.close_button.grid(column=0, row=0, padx = 5) + + self.bbar.prev_button = ttk.Button(self.bbar, text='Prev', + command=self.prevpage, style = 'normal.TButton') + self.bbar.prev_button.grid(column=1, row=0, padx = 5) + + self.bbar.next_button = ttk.Button(self.bbar, text='Next', + command=self.nextpage, style = 'normal.TButton') + self.bbar.next_button.grid(column=2, row=0, padx = 5) + + self.bbar.contents_button = ttk.Button(self.bbar, text='Table of Contents', + command=self.page_to_toc, style = 'normal.TButton') + self.bbar.contents_button.grid(column=3, row=0, padx = 5) + + self.rowconfigure(0, weight=0) + self.rowconfigure(1, weight=0) + self.rowconfigure(2, weight=1) + self.rowconfigure(3, weight=0) + self.columnconfigure(0, weight=1) + + # Help pages + self.pages = [] + self.pageno = -1 # No page + self.toggle = False + + def grid_configure(self, padx, pady): + pass + + def redisplay(self): + # remove contents + if self.pageno >= 0 and self.pageno < len(self.pages): + self.hframe.helpdisplay.helptext.delete('1.0', 'end') + self.hframe.helpdisplay.helptext.insert('end', self.pages[self.pageno]['text']) + self.helptitle.configure(text = self.pages[self.pageno]['title']) + + def toc_toggle(self, event): + self.toggle = True + + def toc_to_page(self, event): + treeview = event.widget + selection = treeview.item(treeview.selection()) + + # Make sure any open/close callback is handled first! + self.update_idletasks() + if self.toggle: + # Item was opened or closed, so consider this a 'false select' and + # do not go to the page. + self.toggle = False + return + + if 'values' in selection: + pagenum = selection['values'][0] + else: + print('Unknown page selected.') + pagenum = 0 + + # Display a page after displaying the table of contents + self.hframe.toc.pack_forget() + self.hframe.scrollbar.pack_forget() + self.hframe.helpdisplay.pack(side='left', fill='both', expand = 'true') + self.hframe.scrollbar.pack(side='right', fill='y') + self.hframe.scrollbar.config(command = self.hframe.helpdisplay.helptext.yview) + # Enable Prev and Next buttons + self.bbar.prev_button.configure(state='enabled') + self.bbar.next_button.configure(state='enabled') + # Redisplay + self.page(pagenum) + + def page_to_toc(self): + # Display the table of contents after displaying a page + self.hframe.scrollbar.pack_forget() + self.hframe.helpdisplay.pack_forget() + self.hframe.toc.pack(side='left', fill='both', expand = 'true') + self.hframe.scrollbar.pack(side='right', fill='y') + self.hframe.scrollbar.config(command = self.hframe.toc.yview) + # Disable Prev and Next buttons + self.bbar.prev_button.configure(state='disabled') + self.bbar.next_button.configure(state='disabled') + + # Simple add page with a single block of plain text + def add_page(self, toc_text, text_block): + newdict = {} + newdict['text'] = text_block + newdict['title'] = toc_text + self.pages.append(newdict) + newpageno = len(self.pages) + self.hframe.toc.insert('', 'end', text=str(newpageno) + '. ' + toc_text, + tag='title', value = newpageno - 1) + if self.pageno < 0: + self.pageno = 0 # First page + + # Fill the help text from a file. The format of the file is: + # + # + # <text> + # '.' + # Text is multi-line and ends when '.' is encountered by itself + + def add_pages_from_file(self, filename): + endpagerex = re.compile('^\.$') + newpagerex = re.compile('^[0-9\.]+$') + commentrex = re.compile('^[\-]+$') + hierarchy = '' + print('Loading help text from file ' + filename) + with open(filename, 'r') as f: + toc_text = [] + page_text = [] + for line in f: + if newpagerex.match(line) or endpagerex.match(line): + if toc_text and page_text: + newdict = {} + self.pages.append(newdict) + newpageno = len(self.pages) + if '.' in hierarchy: + pageinfo = hierarchy.rsplit('.', 1) + if pageinfo[1] == '': + parentid = '' + pageid = pageinfo[0] + else: + parentid = pageinfo[0] + pageid = pageinfo[1] + else: + parentid = '' + pageid = hierarchy + if parentid: + pageid = parentid + '.' + pageid + newdict['text'] = page_text + newdict['title'] = pageid + '. ' + toc_text + self.hframe.toc.insert(parentid, 'end', + text=newdict['title'], tag='title', + value = newpageno - 1, iid = pageid) + if newpagerex.match(line): + hierarchy = line.rstrip() + toc_text = [] + elif not toc_text: + toc_text = line.rstrip() + page_text = [] + elif not commentrex.match(line): + if not page_text: + page_text = line + else: + page_text += line + + def nextpage(self): + # Go to next page + if self.pageno < len(self.pages) - 1: + self.pageno += 1 + self.redisplay() + + def prevpage(self): + # Go to previous page + if self.pageno > 0: + self.pageno -= 1 + self.redisplay() + + def page(self, pagenum): + # Go to indicated page + if pagenum >= 0 and pagenum < len(self.pages): + self.pageno = pagenum + self.redisplay() + + def close(self): + # pop down help window + self.withdraw() + + def open(self): + # pop up help window + self.deiconify() + self.lift() diff --git a/python/lvs_help.txt b/python/lvs_help.txt new file mode 100644 index 0000000..cb7b9ec --- /dev/null +++ b/python/lvs_help.txt @@ -0,0 +1,5 @@ +1. +Selecting a project +------- +Start by selecting a project. This step is only necessary if the application is started from the command line. If started from the project manager, then it can only be run if a project was selected. +. diff --git a/python/lvs_manager.py b/python/lvs_manager.py new file mode 100755 index 0000000..ae902db --- /dev/null +++ b/python/lvs_manager.py @@ -0,0 +1,819 @@ +#!/bin/env python3 +# +#-------------------------------------------------------- +# LVS Manager GUI. +# +# This is a Python tkinter script that handles the +# process of running LVS and interpreting results. +# +#-------------------------------------------------------- +# Written by Tim Edwards +# efabless, inc. +# Version 1. November 30, 2016 +# Version 2. March 6, 2017. Reads JSON format output +# Version 3. April 25, 2018. Handles layout vs. verilog +#-------------------------------------------------------- + +import io +import os +import re +import sys +import json +import shutil +import signal +import socket +import select +import datetime +import contextlib +import subprocess + +import tkinter +from tkinter import ttk +from tkinter import filedialog + +import tksimpledialog +import tooltip +from consoletext import ConsoleText +from helpwindow import HelpWindow +from treeviewsplit import TreeViewSplit + +# User preferences file (if it exists) +prefsfile = '~/.profile/prefs.json' + +netgen_script_dir = '/usr/local/lib/netgen/python' + +#------------------------------------------------------ +# Simple dialog for confirming quit +#------------------------------------------------------ + +class ConfirmDialog(tksimpledialog.Dialog): + def body(self, master, warning, seed): + if warning: + ttk.Label(master, text=warning, wraplength=500).grid(row = 0, columnspan = 2, sticky = 'wns') + return self + + def apply(self): + return 'okay' + +#------------------------------------------------------ +# Main class for this application +#------------------------------------------------------ + +class LVSManager(ttk.Frame): + """LVS Manager GUI.""" + + def __init__(self, parent, *args, **kwargs): + ttk.Frame.__init__(self, parent, *args, **kwargs) + self.root = parent + self.init_gui() + parent.protocol("WM_DELETE_WINDOW", self.on_quit) + + def on_quit(self): + """Exits program.""" + if self.msock: + self.msock.close() + quit() + + def init_gui(self): + """Builds GUI.""" + global prefsfile + + message = [] + fontsize = 11 + + # Read user preferences file, get default font size from it. + prefspath = os.path.expanduser(prefsfile) + if os.path.exists(prefspath): + with open(prefspath, 'r') as f: + self.prefs = json.load(f) + if 'fontsize' in self.prefs: + fontsize = self.prefs['fontsize'] + else: + self.prefs = {} + + s = ttk.Style() + + available_themes = s.theme_names() + s.theme_use(available_themes[0]) + + s.configure('bg.TFrame', background='gray40') + s.configure('italic.TLabel', font=('Helvetica', fontsize, 'italic')) + s.configure('title.TLabel', font=('Helvetica', fontsize, 'bold italic'), + foreground = 'brown', anchor = 'center') + s.configure('normal.TLabel', font=('Helvetica', fontsize)) + s.configure('red.TLabel', font=('Helvetica', fontsize), foreground = 'red') + s.configure('green.TLabel', font=('Helvetica', fontsize), foreground = 'green3') + s.configure('blue.TLabel', font=('Helvetica', fontsize), foreground = 'blue') + s.configure('normal.TButton', font=('Helvetica', fontsize), + border = 3, relief = 'raised') + s.configure('red.TButton', font=('Helvetica', fontsize), foreground = 'red', + border = 3, relief = 'raised') + s.configure('green.TButton', font=('Helvetica', fontsize), foreground = 'green3', + border = 3, relief = 'raised') + s.configure('blue.TButton', font=('Helvetica', fontsize), foreground = 'blue', + border = 3, relief = 'raised') + s.configure('redtitle.TButton', font=('Helvetica', fontsize, 'bold italic'), + foreground = 'red', border = 3, relief = 'raised') + s.configure('bluetitle.TButton', font=('Helvetica', fontsize, 'bold italic'), + foreground = 'blue', border = 3, relief = 'raised') + + # These values to be overridden from arguments + self.rootpath = None + self.project = None + self.logfile = None + self.msock = None + self.help = None + + # Create the help window + if os.path.exists(netgen_script_dir + '/netgen_help.txt'): + self.help = HelpWindow(self, fontsize = fontsize) + with io.StringIO() as buf, contextlib.redirect_stdout(buf): + self.help.add_pages_from_file('lvs_help.txt') + message = buf.getvalue() + + # Set the help display to the first page + self.help.page(0) + + # Variables used by option menus and other stuff + self.project = "(no selection)" + self.layout = "(default)" + self.schematic = "(default)" + self.tech = "(none)" + self.lvs_setup = '' + self.lvsdata = {} + + # Root window title + self.root.title('LVS Manager') + self.root.option_add('*tearOff', 'FALSE') + self.pack(side = 'top', fill = 'both', expand = 'true') + + pane = tkinter.PanedWindow(self, orient = 'vertical', sashrelief='groove', sashwidth=6) + pane.pack(side = 'top', fill = 'both', expand = 'true') + self.toppane = ttk.Frame(pane) + self.botpane = ttk.Frame(pane) + + # Get username + if 'username' in self.prefs: + username = self.prefs['username'] + else: + username = os.environ['USER'] + + # Label with the user + self.toppane.title_frame = ttk.Frame(self.toppane) + self.toppane.title_frame.pack(side = 'top', fill = 'x') + + self.toppane.title_frame.title = ttk.Label(self.toppane.title_frame, text='User:', style = 'red.TLabel') + self.toppane.title_frame.user = ttk.Label(self.toppane.title_frame, text=username, style = 'blue.TLabel') + + self.toppane.title_frame.title.grid(column=0, row=0, ipadx = 5) + self.toppane.title_frame.user.grid(column=1, row=0, ipadx = 5) + + self.toppane.title2_frame = ttk.Frame(self.toppane) + self.toppane.title2_frame.pack(side = 'top', fill = 'x') + self.toppane.title2_frame.project_label = ttk.Label(self.toppane.title2_frame, text="Project:", + style = 'title.TLabel') + self.toppane.title2_frame.project_label.grid(column=0, row=0, ipadx = 5) + + # New project select button + self.toppane.title2_frame.project_select = ttk.Button(self.toppane.title2_frame, + text=self.project, style='normal.TButton', command=self.choose_project) + self.toppane.title2_frame.project_select.grid(column=1, row=0, ipadx = 5) + + tooltip.ToolTip(self.toppane.title2_frame.project_select, + text = "Select new project") + + # Show path to project + self.toppane.title2_frame.path_label = ttk.Label(self.toppane.title2_frame, text=self.project, + style = 'normal.TLabel') + self.toppane.title2_frame.path_label.grid(column=2, row=0, ipadx = 5, padx = 10) + + # Show top-level layout cellname with select button. Initial cell name is the top-level cell. + self.toppane.title2_frame.tech_label = ttk.Label(self.toppane.title2_frame, text="Technology setup:", + style = 'title.TLabel') + + self.toppane.title2_frame.tech_label.grid(column=3, row=0, ipadx = 5) + self.toppane.title2_frame.tech_select = ttk.Button(self.toppane.title2_frame, + text=self.tech, style='normal.TButton', command=self.choose_tech) + self.toppane.title2_frame.tech_select.grid(column=4, row=0, ipadx = 3, padx = 3) + + self.toppane.title2_frame.layout_label = ttk.Label(self.toppane.title2_frame, text="Layout:", + style = 'title.TLabel') + self.toppane.title2_frame.layout_label.grid(column=0, row=1, ipadx = 5) + self.toppane.title2_frame.layout_select = ttk.Button(self.toppane.title2_frame, + text=self.layout, style='normal.TButton', command=self.choose_layout) + self.toppane.title2_frame.layout_select.grid(column=1, row=1, ipadx = 3, padx = 3) + + # Show top-level schematic cellname with select button. Initial cell name is the top-level cell. + self.toppane.title2_frame.schem_label = ttk.Label(self.toppane.title2_frame, text="Schematic:", + style = 'title.TLabel') + self.toppane.title2_frame.schem_label.grid(column=3, row=1, ipadx = 5) + self.toppane.title2_frame.schem_select = ttk.Button(self.toppane.title2_frame, + text=self.schematic, style='normal.TButton', command=self.choose_netlist) + self.toppane.title2_frame.schem_select.grid(column=4, row=1, ipadx = 3, padx = 3) + + tooltip.ToolTip(self.toppane.title2_frame.project_select, + text = "Select new project") + tooltip.ToolTip(self.toppane.title2_frame.layout_select, + text = "Select a layout subcirucit to compare") + tooltip.ToolTip(self.toppane.title2_frame.schem_select, + text = "Select a schematic subcirucit to compare") + + #--------------------------------------------- + ttk.Separator(self.toppane, orient='horizontal').pack(side = 'top', fill = 'x') + #--------------------------------------------- + + # Create listbox of Circuit1 vs. Circuit2 results + height = 10 + self.toppane.lvsreport = TreeViewSplit(self.toppane, fontsize = fontsize) + self.toppane.lvsreport.populate("Layout:", [], "Schematic:", [], + [["Run", True, self.run_lvs], + # ["Find", True, self.findrecord] + ], height = height) + self.toppane.lvsreport.set_title("Line") + self.toppane.lvsreport.pack(side = 'top', fill = 'both', expand = 'true') + + tooltip.ToolTip(self.toppane.lvsreport.get_button(0), text="Run LVS") + + #--------------------------------------------- + # ttk.Separator(self, orient='horizontal').grid(column=0, row=3, sticky='ew') + #--------------------------------------------- + + # Add a text window below the project name to capture output. Redirect + # print statements to it. + + self.botpane.console = ttk.Frame(self.botpane) + self.botpane.console.pack(side = 'top', fill = 'both', expand = 'true') + + self.text_box = ConsoleText(self.botpane.console, wrap='word', height = 4) + self.text_box.pack(side='left', fill='both', expand='true') + console_scrollbar = ttk.Scrollbar(self.botpane.console) + console_scrollbar.pack(side='right', fill='y') + # attach console to scrollbar + self.text_box.config(yscrollcommand = console_scrollbar.set) + console_scrollbar.config(command = self.text_box.yview) + + # Add button bar at the bottom of the window + self.botpane.bbar = ttk.Frame(self.botpane) + self.botpane.bbar.pack(side = 'top', fill = 'x') + + # Define the "quit" button and action + self.botpane.bbar.quit_button = ttk.Button(self.botpane.bbar, text='Quit', command=self.on_quit, + style = 'normal.TButton') + self.botpane.bbar.quit_button.grid(column=0, row=0, padx = 5) + + # Define help button + if self.help: + self.botpane.bbar.help_button = ttk.Button(self.botpane.bbar, text='Help', + command=self.help.open, style = 'normal.TButton') + self.botpane.bbar.help_button.grid(column = 2, row = 0, padx = 5) + tooltip.ToolTip(self.botpane.bbar.help_button, text = "Show help window") + + # Add the panes once the internal geometry is known. + pane.add(self.toppane) + pane.add(self.botpane) + pane.paneconfig(self.toppane, stretch='first') + + # Redirect stdout and stderr to the console as the last thing to do. . . + # Otherwise errors in the GUI get sucked into the void. + + self.stdout = sys.stdout + self.stderr = sys.stderr + sys.stdout = ConsoleText.StdoutRedirector(self.text_box) + sys.stderr = ConsoleText.StderrRedirector(self.text_box) + + if message: + print(message) + + def logprint(self, message, doflush=False): + if self.logfile: + self.logfile.buffer.write(message.encode('utf-8')) + self.logfile.buffer.write('\n'.encode('utf-8')) + if doflush: + self.logfile.flush() + + def printout(self, output): + # Generate output + if not output: + return + + outlines = output.splitlines() + for line in outlines: + try: + print(line) + except TypeError: + line = line.decode('utf-8') + pritn(line) + + def printwarn(self, output): + # Check output for warning or error + if not output: + return 0 + + warnrex = re.compile('.*warning', re.IGNORECASE) + errrex = re.compile('.*error', re.IGNORECASE) + + errors = 0 + outlines = output.splitlines() + for line in outlines: + try: + wmatch = warnrex.match(line) + except TypeError: + line = line.decode('utf-8') + wmatch = warnrex.match(line) + ematch = errrex.match(line) + if ematch: + errors += 1 + if ematch or wmatch: + print(line) + return errors + + def choose_tech(self): + try: + project_path = self.rootpath + initdirname = self.rootpath + '/tech', + except: + print('Must choose a project first.') + return + techname = filedialog.askopenfilename(multiple=False, + initialdir = initdirname, + filetypes = (("Tcl script", "*.tcl"),("All Files","*.*")), + title = "Choose a netgen technology setup script.") + if techname != '': + print("Selected technology setup script " + techname) + techbase = os.path.split(techname)[1] + self.tech = os.path.splitext(techbase)[0] + self.lvs_setup = techname + self.toppane.title2_frame.layout_select.config(text = self.tech) + + def choose_layout(self): + try: + project_path = self.rootpath + initdirname = self.rootpath + '/layout', + except: + print('Must choose a project first.') + return + cellname = filedialog.askopenfilename(multiple=False, + initialdir = initdirname, + filetypes = (("Magic layout", "*.mag"),("All Files","*.*")), + title = "Choose a layout cell to compare.") + if cellname != '': + print("Selected compare cell " + cellname) + cellbase = os.path.split(cellname)[1] + self.layout = os.path.splitext(cellbase)[0] + self.toppane.title2_frame.layout_select.config(text = self.layout) + + def choose_netlist(self): + try: + project_path = self.rootpath + initdirname = self.rootpath + '/netlist/' + self.project + '.spi' + except: + print('Must choose a project first.') + return + cellname = filedialog.askopenfilename(multiple=False, + initialdir = initdirname, + filetypes = (("Spice netlist", "*.spi"),("Verilog netlist", "*.v")), + title = "Choose a netlist to compare.") + if cellname != '': + print("Selected compare cell " + cellname) + cellbase = os.path.split(cellname)[1] + self.schematic = os.path.splitext(cellbase)[0] + self.toppane.title2_frame.schem_select.config(text = self.schematic) + fileext = os.path.splitext(cellbase)[1] + if fileext == '.v': + self.toppane.title2_frame.schem_label.config(text = 'Verilog netlist:') + elif fileext == '.sp' or fileext == '.spi' or fileext == '.spice' or fileext == '.spc' or fileext == '.ckt': + self.toppane.title2_frame.schem_label.config(text = 'SPICE netlist:') + elif fileext == '.cdl': + self.toppane.title2_frame.schem_label.config(text = 'CDL netlist:') + else: + self.toppane.title2_frame.schem_label.config(text = 'Unknown netlist:') + + def choose_project(self): + project = filedialog.askdirectory(initialdir = os.getcwd(), + title = "Find a project.") + if project != '': + print("Selected project " + str(project)) + result = self.set_project(project) + + def set_project(self, rootpath, project_name=None): + + # Check if rootpath is valid. For LVS, there should be subdirectories + # "layout/" and "netlist/" or "verilog/". + + haslay = os.path.isdir(rootpath + '/layout') + hasvlog = os.path.isdir(rootpath + '/verilog') + hasnet = os.path.isdir(rootpath + '/netlist') + if not haslay or not (hasvlog or hasnet): + if not haslay: + print("Project path has no layout (/layout) subdirectory.") + if not (hasvlog or hasnet): + print("Project path has no verilog (/verilog), or netlist (/netlist) subdirectory.") + # Continue anyway; assume that netlists will be selected manually + + if self.logfile: + self.logfile.close() + self.logfile = None + + if not project_name: + project = os.path.split(rootpath)[1] + else: + project = project_name + + if self.project != project: + + self.rootpath = rootpath + self.project = project + + # Clear out old project data + self.toppane.lvsreport.repopulate([], []) + + # Close any open logfile. + if self.logfile: + self.logfile.close() + self.logfile = None + + # Put new log file called 'lvs.log' in the mag/ subdirectory + self.logfile = open(rootpath + '/layout/lvs.log', 'w') + # Print some initial information to the logfile. + self.logprint('Starting new log file ' + datetime.datetime.now().strftime('%c'), + doflush=True) + + # Update project button + self.toppane.title2_frame.project_select.config(text = self.project) + self.toppane.title2_frame.path_label.config(text = self.rootpath) + # Cell name is the same as project name initially + self.layout = self.project + self.schematic = self.project + self.toppane.title2_frame.layout_select.config(text = self.layout) + self.toppane.title2_frame.schem_select.config(text = self.schematic) + + # Update schematic button + if os.path.isfile(rootpath + '/verilog/' + self.schematic + '.v'): + self.toppane.title2_frame.schem_label.config(text = 'Verilog netlist:') + else: + self.toppane.title2_frame.schem_label.config(text = 'Schematic netlist:') + + # If there is a comparison file that post-dates both netlists, load it. + self.check_lvs() + return True + + def check_layout_out_of_date(self, spipath, layoutpath): + # Check if a netlist (spipath) is out-of-date relative to the layouts + # (layoutpath). Need to read the netlist and check all of the subcells. + need_capture = False + if not os.path.isfile(spipath): + return True + if os.path.isfile(layoutpath): + spi_statbuf = os.stat(spipath) + lay_statbuf = os.stat(layoutpath) + if spi_statbuf.st_mtime < lay_statbuf.st_mtime: + # netlist exists but is out-of-date + need_capture = True + else: + # only found that the top-level-layout is older than the + # netlist. Now need to read the netlist, find all subcircuits, + # and check those dates, too. + layoutdir = os.path.split(layoutpath)[0] + subrex = re.compile('^[^\*]*[ \t]*.subckt[ \t]+([^ \t]+).*$', re.IGNORECASE) + with open(spipath, 'r') as ifile: + duttext = ifile.read() + + dutlines = duttext.replace('\n+', ' ').splitlines() + for line in dutlines: + lmatch = subrex.match(line) + if lmatch: + subname = lmatch.group(1) + sublayout = layoutdir + '/' + subname + '.mag' + # subcircuits that cannot be found in the current directory are + # assumed to be library components and therefore never out-of-date. + if os.path.exists(sublayout): + sub_statbuf = os.stat(sublayout) + if spi_statbuf.st_mtime < lay_statbuf.st_mtime: + # netlist exists but is out-of-date + need_capture = True + break + return need_capture + + def check_schematic_out_of_date(self, spipath, schempath): + # Check if a netlist (spipath) is out-of-date relative to the schematics + # (schempath). Need to read the netlist and check all of the subcells. + need_capture = False + if not os.path.isfile(spipath): + return True + if os.path.isfile(schempath): + spi_statbuf = os.stat(spipath) + sch_statbuf = os.stat(schempath) + if spi_statbuf.st_mtime < sch_statbuf.st_mtime: + # netlist exists but is out-of-date + need_capture = True + else: + # only found that the top-level-schematic is older than the + # netlist. Now need to read the netlist, find all subcircuits, + # and check those dates, too. + schemdir = os.path.split(schempath)[0] + subrex = re.compile('^[^\*]*[ \t]*.subckt[ \t]+([^ \t]+).*$', re.IGNORECASE) + with open(spipath, 'r') as ifile: + duttext = ifile.read() + + dutlines = duttext.replace('\n+', ' ').splitlines() + for line in dutlines: + lmatch = subrex.match(line) + if lmatch: + subname = lmatch.group(1) + # NOTE: Electric uses library:cell internally to track libraries, + # and maps the ":" to "__" in the netlist. Not entirely certain that + # the double-underscore uniquely identifies the library:cell. . . + librex = re.compile('(.*)__(.*)', re.IGNORECASE) + lmatch = librex.match(subname) + if lmatch: + elecpath = os.path.split(os.path.split(schempath)[0])[0] + libname = lmatch.group(1) + subschem = elecpath + '/' + libname + '.delib/' + lmatch.group(2) + '.sch' + else: + libname = {} + subschem = schemdir + '/' + subname + '.sch' + # subcircuits that cannot be found in the current directory are + # assumed to be library components and therefore never out-of-date. + if os.path.exists(subschem): + sub_statbuf = os.stat(subschem) + if spi_statbuf.st_mtime < sub_statbuf.st_mtime: + # netlist exists but is out-of-date + need_capture = True + break + # mapping of characters to what's allowed in SPICE makes finding + # the associated schematic file a bit difficult. Requires wild-card + # searching. + elif libname: + restr = lmatch.group(2) + '.sch' + restr = restr.replace('.', '\.') + restr = restr.replace('_', '.') + schrex = re.compile(restr, re.IGNORECASE) + libpath = elecpath + '/' + libname + '.delib' + if os.path.exists(libpath): + liblist = os.listdir(libpath) + for file in liblist: + lmatch = schrex.match(file) + if lmatch: + subschem = libpath + '/' + file + sub_statbuf = os.stat(subschem) + if spi_statbuf.st_mtime < sch_statbuf.st_mtime: + # netlist exists but is out-of-date + need_capture = True + break + return need_capture + + def check_lvs(self): + # If both netlists exist, and comp.json is more recent than both, then + # load LVS results from comp.json + project_path = self.rootpath + project_name = self.project + layout_path = project_path + '/layout/' + project_name + '.spc' + net_path = project_path + '/netlist/' + project_name + '.spi' + comp_path = project_path + '/layout/comp.json' + + if os.path.exists(layout_path) and os.path.exists(net_path) and os.path.exists(comp_path): + magtime = os.stat(layout_path).st_mtime + schemtime = os.stat(net_path).st_mtime + comptime = os.stat(comp_path).st_mtime + if comptime > magtime and comptime > schemtime: + print("Loading LVS results from file.") + self.generate(comp_path) + + def generate_layout_netlist(self, layout_path, layout_src, project_path): + # Does layout netlist exist and is it current? + if self.check_layout_out_of_date(layout_path, layout_src): + print('Generating layout netlist.') + self.update_idletasks() + mproc = subprocess.Popen(['magic', '-dnull', '-noconsole', + self.layout], stdin = subprocess.PIPE, stdout = subprocess.PIPE, + stderr = subprocess.PIPE, cwd = project_path + '/layout', + universal_newlines = True) + mproc.stdin.write("select top cell\n") + mproc.stdin.write("expand\n") + mproc.stdin.write("extract all\n") + mproc.stdin.write("ext2spice hierarchy on\n") + mproc.stdin.write("ext2spice format ngspice\n") + mproc.stdin.write("ext2spice scale off\n") + mproc.stdin.write("ext2spice renumber off\n") + mproc.stdin.write("ext2spice subcircuit top auto\n") + mproc.stdin.write("ext2spice cthresh infinite\n") + mproc.stdin.write("ext2spice rthresh infinite\n") + mproc.stdin.write("ext2spice blackbox on\n") + mproc.stdin.write("ext2spice -o " + self.layout + ".spi\n") + mproc.stdin.write("quit -noprompt\n") + magicout = mproc.communicate()[0] + self.printwarn(magicout) + if mproc.returncode != 0: + print('Failure to generate new layout netlist.') + return False + + # Move .spi netlist to project_dir/netlist/lvs/ + shutil.move(project_path + '/layout/' + self.layout + '.spi', layout_path) + # Remove extraction files + for file in os.listdir(project_path + '/layout'): + if os.path.splitext(file)[1] == '.ext': + os.remove(project_path + '/layout/' + file) + else: + print('Layout netlist is up-to-date, not regenerating.') + return True + + def run_lvs(self, value): + # "value" is ignored (?) + + # Check if netlists exist and are current; otherwise create them. + # Then run LVS. + + project_path = self.rootpath + project_name = self.project + comp_path = project_path + '/layout/comp.json' + has_vlog = False + vlog_path = project_path + '/verilog/' + project_name + '.v' + + layout_path = project_path + '/netlist/lvs/' + self.layout + '.spi' + net_path = project_path + '/netlist/schem/' + self.schematic + '.spi' + layout_src = project_path + '/layout/' + self.layout + '.mag' + + # Does the setup file exist (this is optional)? + if self.lvs_setup != '' and not os.path.isfile('setup.tcl'): + print('No technology setup file selected.') + + # Does schematic netlist exist? + if not os.path.isfile(vlog_path) and not os.path.isfile(net_path): + print('Error: No schematic netlist or verilog netlist.') + return + + # Does LVS netlist subdirectory exist? + if not os.path.exists(project_path + '/netlist/lvs'): + os.makedirs(project_path + '/netlist/lvs') + + # Does layout netlist exist and is it current? + if not self.generate_layout_netlist(layout_path, layout_src, project_path): + return False + + # Final check + if not os.path.isfile(layout_path): + print('Error: No netlist generated from magic.') + return + + else: + # Read in netlist and convert commas from [X,Y] arrays to vertical bars + # as something that can be converted back as necessary. ngspice treats + # commas as special characters for some reason. + with open(layout_path) as ifile: + spitext = ifile.read() + + # spilines = spitext.replace(',', '|') + # with open(layout_path, 'w') as ofile: + # ofile.write(spilines) + + # Check the netlist to see if the cell to match is a subcircuit. If + # not, then assume it is the top level. + + is_subckt = False + subrex = re.compile('^[^\*]*[ \t]*.subckt[ \t]+([^ \t]+).*$', re.IGNORECASE) + dutlines = spitext.replace('\n+', ' ').splitlines() + for line in dutlines: + lmatch = subrex.match(line) + if lmatch: + subname = lmatch.group(1) + if subname == self.layout: + is_subckt = True + break + + if is_subckt: + layout_arg = layout_path + ' ' + self.layout + layout_text = '"' + layout_arg + '"' + else: + layout_arg = layout_path + layout_text = layout_arg + + if has_vlog: + schem_arg = vlog_path + ' ' + self.schematic + else: + schem_arg = net_path + ' ' + self.schematic + schem_text = '"' + schem_arg + '"' + + # Remove any previous comparison output file + comp_out_path = os.path.splitext(comp_path)[0] + '.out' + if os.path.exists(comp_out_path): + os.remove(comp_out_path) + + # Run netgen as subprocess + print('Running: netgen -batch lvs ' + layout_text + + ' ' + schem_text + ' ' + self.lvs_setup + ' ' + comp_out_path + + ' -json -blackbox') + # Note: Because arguments to subprocess are list items, the {filename cell} + # pair does *not* have to be quoted or braced. Doing so causes a parse + # error. + self.lvsproc = subprocess.Popen(['netgen', '-batch', 'lvs', + layout_arg, schem_arg, + self.lvs_setup, comp_out_path, '-json', '-blackbox'], + cwd=project_path + '/layout', + stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=0) + # This is largely unnecessary as netgen usually runs to completion very quickly. + self.watchclock(comp_path) + + def watchclock(self, filename): + if self.lvsproc == None: + return + + lvs_status = self.lvsproc.poll() + sresult = select.select([self.lvsproc.stdout, self.lvsproc.stderr], [], [], 0)[0] + if self.lvsproc.stdout in sresult: + outstring = self.lvsproc.stdout.readline().decode().strip() + self.logprint(outstring, doflush=True) + print(outstring) + elif self.lvsproc.stderr in sresult: + errstring = self.lvsproc.stderr.readline().decode().strip() + self.logprint(errstring, doflush = True) + print(errstring, file=sys.stderr) + + if lvs_status != None: + print("netgen LVS exited with status " + str(lvs_status)) + self.lvsproc = None + if lvs_status != 0: + print('Errors encountered in LVS.') + self.logprint('Errors in LVS, lvs status = ' + str(lvs_status), doflush=True) + # Done; now read comp.json and fill the treeview listbox. + self.generate(filename) + else: + self.after(500, lambda: self.watchclock(filename)) + + # Generate display from "comp.out" file (json file now preferred) + + def generate_orig(self, lvspath): + lefttext = [] + righttext = [] + print("Reading LVS output file " + lvspath) + if os.path.exists(lvspath): + with open(lvspath, 'r') as ifile: + lvslines = ifile.read().splitlines() + for line in lvslines: + if '|' in line: + # parts = line.split('|') + # lefttext.append(parts[0]) + # righttext.append(parts[1]) + lefttext.append(line[0:42].strip()) + righttext.append(line[44:].strip()) + else: + lefttext.append(line) + righttext.append('') + # Populate treeview with text + self.toppane.lvsreport.repopulate(lefttext, righttext) + + else: + print("Error: No output file generated from LVS.") + + # Generate output from LVS report JSON file comp.json + + def generate(self, lvspath): + lefttext = [] + righttext = [] + print("Reading LVS output file " + lvspath) + if os.path.exists(lvspath): + with open(lvspath, 'r') as ifile: + self.lvsdata = json.load(ifile) + + # Populate treeview with text + self.toppane.lvsreport.json_repopulate(self.lvsdata) + + else: + print("Error: No output file generated from LVS.") + + def findrecord(self, value): + print("Unimplemented function") + + def findrecord_test(self, value): + # Check if socket is defined; if not, attempt to open one + if not self.msock: + try: + self.msock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + except: + print("No response from layout tool.") + + if self.msock: + self.msock.connect(("0.0.0.0", 12946)) + self.msock.setblocking(False) + if self.msock: + # Pull name of net or device from 'value' + # This is a test: + self.msock.sendall(b'box 0 0 100 100\r\n') + +if __name__ == '__main__': + options = [] + arguments = [] + for item in sys.argv[1:]: + if item.find('-', 0) == 0: + options.append(item) + else: + arguments.append(item) + + root = tkinter.Tk() + app = LVSManager(root) + if arguments: + if len(arguments) >= 2: + app.set_project(arguments[0], project_name=arguments[1]) + else: + app.set_project(arguments[0]) + + root.mainloop() diff --git a/python/lvs_manager.py.in b/python/lvs_manager.py.in new file mode 100755 index 0000000..416f12f --- /dev/null +++ b/python/lvs_manager.py.in @@ -0,0 +1,819 @@ +#!/bin/env python3 +# +#-------------------------------------------------------- +# LVS Manager GUI. +# +# This is a Python tkinter script that handles the +# process of running LVS and interpreting results. +# +#-------------------------------------------------------- +# Written by Tim Edwards +# efabless, inc. +# Version 1. November 30, 2016 +# Version 2. March 6, 2017. Reads JSON format output +# Version 3. April 25, 2018. Handles layout vs. verilog +#-------------------------------------------------------- + +import io +import os +import re +import sys +import json +import shutil +import signal +import socket +import select +import datetime +import contextlib +import subprocess + +import tkinter +from tkinter import ttk +from tkinter import filedialog + +import tksimpledialog +import tooltip +from consoletext import ConsoleText +from helpwindow import HelpWindow +from treeviewsplit import TreeViewSplit + +# User preferences file (if it exists) +prefsfile = '~/.profile/prefs.json' + +netgen_script_dir = 'SUBST_SCRIPT_DIR' + +#------------------------------------------------------ +# Simple dialog for confirming quit +#------------------------------------------------------ + +class ConfirmDialog(tksimpledialog.Dialog): + def body(self, master, warning, seed): + if warning: + ttk.Label(master, text=warning, wraplength=500).grid(row = 0, columnspan = 2, sticky = 'wns') + return self + + def apply(self): + return 'okay' + +#------------------------------------------------------ +# Main class for this application +#------------------------------------------------------ + +class LVSManager(ttk.Frame): + """LVS Manager GUI.""" + + def __init__(self, parent, *args, **kwargs): + ttk.Frame.__init__(self, parent, *args, **kwargs) + self.root = parent + self.init_gui() + parent.protocol("WM_DELETE_WINDOW", self.on_quit) + + def on_quit(self): + """Exits program.""" + if self.msock: + self.msock.close() + quit() + + def init_gui(self): + """Builds GUI.""" + global prefsfile + + message = [] + fontsize = 11 + + # Read user preferences file, get default font size from it. + prefspath = os.path.expanduser(prefsfile) + if os.path.exists(prefspath): + with open(prefspath, 'r') as f: + self.prefs = json.load(f) + if 'fontsize' in self.prefs: + fontsize = self.prefs['fontsize'] + else: + self.prefs = {} + + s = ttk.Style() + + available_themes = s.theme_names() + s.theme_use(available_themes[0]) + + s.configure('bg.TFrame', background='gray40') + s.configure('italic.TLabel', font=('Helvetica', fontsize, 'italic')) + s.configure('title.TLabel', font=('Helvetica', fontsize, 'bold italic'), + foreground = 'brown', anchor = 'center') + s.configure('normal.TLabel', font=('Helvetica', fontsize)) + s.configure('red.TLabel', font=('Helvetica', fontsize), foreground = 'red') + s.configure('green.TLabel', font=('Helvetica', fontsize), foreground = 'green3') + s.configure('blue.TLabel', font=('Helvetica', fontsize), foreground = 'blue') + s.configure('normal.TButton', font=('Helvetica', fontsize), + border = 3, relief = 'raised') + s.configure('red.TButton', font=('Helvetica', fontsize), foreground = 'red', + border = 3, relief = 'raised') + s.configure('green.TButton', font=('Helvetica', fontsize), foreground = 'green3', + border = 3, relief = 'raised') + s.configure('blue.TButton', font=('Helvetica', fontsize), foreground = 'blue', + border = 3, relief = 'raised') + s.configure('redtitle.TButton', font=('Helvetica', fontsize, 'bold italic'), + foreground = 'red', border = 3, relief = 'raised') + s.configure('bluetitle.TButton', font=('Helvetica', fontsize, 'bold italic'), + foreground = 'blue', border = 3, relief = 'raised') + + # These values to be overridden from arguments + self.rootpath = None + self.project = None + self.logfile = None + self.msock = None + self.help = None + + # Create the help window + if os.path.exists(netgen_script_dir + '/netgen_help.txt'): + self.help = HelpWindow(self, fontsize = fontsize) + with io.StringIO() as buf, contextlib.redirect_stdout(buf): + self.help.add_pages_from_file('lvs_help.txt') + message = buf.getvalue() + + # Set the help display to the first page + self.help.page(0) + + # Variables used by option menus and other stuff + self.project = "(no selection)" + self.layout = "(default)" + self.schematic = "(default)" + self.tech = "(none)" + self.lvs_setup = '' + self.lvsdata = {} + + # Root window title + self.root.title('LVS Manager') + self.root.option_add('*tearOff', 'FALSE') + self.pack(side = 'top', fill = 'both', expand = 'true') + + pane = tkinter.PanedWindow(self, orient = 'vertical', sashrelief='groove', sashwidth=6) + pane.pack(side = 'top', fill = 'both', expand = 'true') + self.toppane = ttk.Frame(pane) + self.botpane = ttk.Frame(pane) + + # Get username + if 'username' in self.prefs: + username = self.prefs['username'] + else: + username = os.environ['USER'] + + # Label with the user + self.toppane.title_frame = ttk.Frame(self.toppane) + self.toppane.title_frame.pack(side = 'top', fill = 'x') + + self.toppane.title_frame.title = ttk.Label(self.toppane.title_frame, text='User:', style = 'red.TLabel') + self.toppane.title_frame.user = ttk.Label(self.toppane.title_frame, text=username, style = 'blue.TLabel') + + self.toppane.title_frame.title.grid(column=0, row=0, ipadx = 5) + self.toppane.title_frame.user.grid(column=1, row=0, ipadx = 5) + + self.toppane.title2_frame = ttk.Frame(self.toppane) + self.toppane.title2_frame.pack(side = 'top', fill = 'x') + self.toppane.title2_frame.project_label = ttk.Label(self.toppane.title2_frame, text="Project:", + style = 'title.TLabel') + self.toppane.title2_frame.project_label.grid(column=0, row=0, ipadx = 5) + + # New project select button + self.toppane.title2_frame.project_select = ttk.Button(self.toppane.title2_frame, + text=self.project, style='normal.TButton', command=self.choose_project) + self.toppane.title2_frame.project_select.grid(column=1, row=0, ipadx = 5) + + tooltip.ToolTip(self.toppane.title2_frame.project_select, + text = "Select new project") + + # Show path to project + self.toppane.title2_frame.path_label = ttk.Label(self.toppane.title2_frame, text=self.project, + style = 'normal.TLabel') + self.toppane.title2_frame.path_label.grid(column=2, row=0, ipadx = 5, padx = 10) + + # Show top-level layout cellname with select button. Initial cell name is the top-level cell. + self.toppane.title2_frame.tech_label = ttk.Label(self.toppane.title2_frame, text="Technology setup:", + style = 'title.TLabel') + + self.toppane.title2_frame.tech_label.grid(column=3, row=0, ipadx = 5) + self.toppane.title2_frame.tech_select = ttk.Button(self.toppane.title2_frame, + text=self.tech, style='normal.TButton', command=self.choose_tech) + self.toppane.title2_frame.tech_select.grid(column=4, row=0, ipadx = 3, padx = 3) + + self.toppane.title2_frame.layout_label = ttk.Label(self.toppane.title2_frame, text="Layout:", + style = 'title.TLabel') + self.toppane.title2_frame.layout_label.grid(column=0, row=1, ipadx = 5) + self.toppane.title2_frame.layout_select = ttk.Button(self.toppane.title2_frame, + text=self.layout, style='normal.TButton', command=self.choose_layout) + self.toppane.title2_frame.layout_select.grid(column=1, row=1, ipadx = 3, padx = 3) + + # Show top-level schematic cellname with select button. Initial cell name is the top-level cell. + self.toppane.title2_frame.schem_label = ttk.Label(self.toppane.title2_frame, text="Schematic:", + style = 'title.TLabel') + self.toppane.title2_frame.schem_label.grid(column=3, row=1, ipadx = 5) + self.toppane.title2_frame.schem_select = ttk.Button(self.toppane.title2_frame, + text=self.schematic, style='normal.TButton', command=self.choose_netlist) + self.toppane.title2_frame.schem_select.grid(column=4, row=1, ipadx = 3, padx = 3) + + tooltip.ToolTip(self.toppane.title2_frame.project_select, + text = "Select new project") + tooltip.ToolTip(self.toppane.title2_frame.layout_select, + text = "Select a layout subcirucit to compare") + tooltip.ToolTip(self.toppane.title2_frame.schem_select, + text = "Select a schematic subcirucit to compare") + + #--------------------------------------------- + ttk.Separator(self.toppane, orient='horizontal').pack(side = 'top', fill = 'x') + #--------------------------------------------- + + # Create listbox of Circuit1 vs. Circuit2 results + height = 10 + self.toppane.lvsreport = TreeViewSplit(self.toppane, fontsize = fontsize) + self.toppane.lvsreport.populate("Layout:", [], "Schematic:", [], + [["Run", True, self.run_lvs], + # ["Find", True, self.findrecord] + ], height = height) + self.toppane.lvsreport.set_title("Line") + self.toppane.lvsreport.pack(side = 'top', fill = 'both', expand = 'true') + + tooltip.ToolTip(self.toppane.lvsreport.get_button(0), text="Run LVS") + + #--------------------------------------------- + # ttk.Separator(self, orient='horizontal').grid(column=0, row=3, sticky='ew') + #--------------------------------------------- + + # Add a text window below the project name to capture output. Redirect + # print statements to it. + + self.botpane.console = ttk.Frame(self.botpane) + self.botpane.console.pack(side = 'top', fill = 'both', expand = 'true') + + self.text_box = ConsoleText(self.botpane.console, wrap='word', height = 4) + self.text_box.pack(side='left', fill='both', expand='true') + console_scrollbar = ttk.Scrollbar(self.botpane.console) + console_scrollbar.pack(side='right', fill='y') + # attach console to scrollbar + self.text_box.config(yscrollcommand = console_scrollbar.set) + console_scrollbar.config(command = self.text_box.yview) + + # Add button bar at the bottom of the window + self.botpane.bbar = ttk.Frame(self.botpane) + self.botpane.bbar.pack(side = 'top', fill = 'x') + + # Define the "quit" button and action + self.botpane.bbar.quit_button = ttk.Button(self.botpane.bbar, text='Quit', command=self.on_quit, + style = 'normal.TButton') + self.botpane.bbar.quit_button.grid(column=0, row=0, padx = 5) + + # Define help button + if self.help: + self.botpane.bbar.help_button = ttk.Button(self.botpane.bbar, text='Help', + command=self.help.open, style = 'normal.TButton') + self.botpane.bbar.help_button.grid(column = 2, row = 0, padx = 5) + tooltip.ToolTip(self.botpane.bbar.help_button, text = "Show help window") + + # Add the panes once the internal geometry is known. + pane.add(self.toppane) + pane.add(self.botpane) + pane.paneconfig(self.toppane, stretch='first') + + # Redirect stdout and stderr to the console as the last thing to do. . . + # Otherwise errors in the GUI get sucked into the void. + + self.stdout = sys.stdout + self.stderr = sys.stderr + sys.stdout = ConsoleText.StdoutRedirector(self.text_box) + sys.stderr = ConsoleText.StderrRedirector(self.text_box) + + if message: + print(message) + + def logprint(self, message, doflush=False): + if self.logfile: + self.logfile.buffer.write(message.encode('utf-8')) + self.logfile.buffer.write('\n'.encode('utf-8')) + if doflush: + self.logfile.flush() + + def printout(self, output): + # Generate output + if not output: + return + + outlines = output.splitlines() + for line in outlines: + try: + print(line) + except TypeError: + line = line.decode('utf-8') + pritn(line) + + def printwarn(self, output): + # Check output for warning or error + if not output: + return 0 + + warnrex = re.compile('.*warning', re.IGNORECASE) + errrex = re.compile('.*error', re.IGNORECASE) + + errors = 0 + outlines = output.splitlines() + for line in outlines: + try: + wmatch = warnrex.match(line) + except TypeError: + line = line.decode('utf-8') + wmatch = warnrex.match(line) + ematch = errrex.match(line) + if ematch: + errors += 1 + if ematch or wmatch: + print(line) + return errors + + def choose_tech(self): + try: + project_path = self.rootpath + initdirname = self.rootpath + '/tech', + except: + print('Must choose a project first.') + return + techname = filedialog.askopenfilename(multiple=False, + initialdir = initdirname, + filetypes = (("Tcl script", "*.tcl"),("All Files","*.*")), + title = "Choose a netgen technology setup script.") + if techname != '': + print("Selected technology setup script " + techname) + techbase = os.path.split(techname)[1] + self.tech = os.path.splitext(techbase)[0] + self.lvs_setup = techname + self.toppane.title2_frame.layout_select.config(text = self.tech) + + def choose_layout(self): + try: + project_path = self.rootpath + initdirname = self.rootpath + '/layout', + except: + print('Must choose a project first.') + return + cellname = filedialog.askopenfilename(multiple=False, + initialdir = initdirname, + filetypes = (("Magic layout", "*.mag"),("All Files","*.*")), + title = "Choose a layout cell to compare.") + if cellname != '': + print("Selected compare cell " + cellname) + cellbase = os.path.split(cellname)[1] + self.layout = os.path.splitext(cellbase)[0] + self.toppane.title2_frame.layout_select.config(text = self.layout) + + def choose_netlist(self): + try: + project_path = self.rootpath + initdirname = self.rootpath + '/netlist/' + self.project + '.spi' + except: + print('Must choose a project first.') + return + cellname = filedialog.askopenfilename(multiple=False, + initialdir = initdirname, + filetypes = (("Spice netlist", "*.spi"),("Verilog netlist", "*.v")), + title = "Choose a netlist to compare.") + if cellname != '': + print("Selected compare cell " + cellname) + cellbase = os.path.split(cellname)[1] + self.schematic = os.path.splitext(cellbase)[0] + self.toppane.title2_frame.schem_select.config(text = self.schematic) + fileext = os.path.splitext(cellbase)[1] + if fileext == '.v': + self.toppane.title2_frame.schem_label.config(text = 'Verilog netlist:') + elif fileext == '.sp' or fileext == '.spi' or fileext == '.spice' or fileext == '.spc' or fileext == '.ckt': + self.toppane.title2_frame.schem_label.config(text = 'SPICE netlist:') + elif fileext == '.cdl': + self.toppane.title2_frame.schem_label.config(text = 'CDL netlist:') + else: + self.toppane.title2_frame.schem_label.config(text = 'Unknown netlist:') + + def choose_project(self): + project = filedialog.askdirectory(initialdir = os.getcwd(), + title = "Find a project.") + if project != '': + print("Selected project " + str(project)) + result = self.set_project(project) + + def set_project(self, rootpath, project_name=None): + + # Check if rootpath is valid. For LVS, there should be subdirectories + # "layout/" and "netlist/" or "verilog/". + + haslay = os.path.isdir(rootpath + '/layout') + hasvlog = os.path.isdir(rootpath + '/verilog') + hasnet = os.path.isdir(rootpath + '/netlist') + if not haslay or not (hasvlog or hasnet): + if not haslay: + print("Project path has no layout (/layout) subdirectory.") + if not (hasvlog or hasnet): + print("Project path has no verilog (/verilog), or netlist (/netlist) subdirectory.") + # Continue anyway; assume that netlists will be selected manually + + if self.logfile: + self.logfile.close() + self.logfile = None + + if not project_name: + project = os.path.split(rootpath)[1] + else: + project = project_name + + if self.project != project: + + self.rootpath = rootpath + self.project = project + + # Clear out old project data + self.toppane.lvsreport.repopulate([], []) + + # Close any open logfile. + if self.logfile: + self.logfile.close() + self.logfile = None + + # Put new log file called 'lvs.log' in the mag/ subdirectory + self.logfile = open(rootpath + '/layout/lvs.log', 'w') + # Print some initial information to the logfile. + self.logprint('Starting new log file ' + datetime.datetime.now().strftime('%c'), + doflush=True) + + # Update project button + self.toppane.title2_frame.project_select.config(text = self.project) + self.toppane.title2_frame.path_label.config(text = self.rootpath) + # Cell name is the same as project name initially + self.layout = self.project + self.schematic = self.project + self.toppane.title2_frame.layout_select.config(text = self.layout) + self.toppane.title2_frame.schem_select.config(text = self.schematic) + + # Update schematic button + if os.path.isfile(rootpath + '/verilog/' + self.schematic + '.v'): + self.toppane.title2_frame.schem_label.config(text = 'Verilog netlist:') + else: + self.toppane.title2_frame.schem_label.config(text = 'Schematic netlist:') + + # If there is a comparison file that post-dates both netlists, load it. + self.check_lvs() + return True + + def check_layout_out_of_date(self, spipath, layoutpath): + # Check if a netlist (spipath) is out-of-date relative to the layouts + # (layoutpath). Need to read the netlist and check all of the subcells. + need_capture = False + if not os.path.isfile(spipath): + return True + if os.path.isfile(layoutpath): + spi_statbuf = os.stat(spipath) + lay_statbuf = os.stat(layoutpath) + if spi_statbuf.st_mtime < lay_statbuf.st_mtime: + # netlist exists but is out-of-date + need_capture = True + else: + # only found that the top-level-layout is older than the + # netlist. Now need to read the netlist, find all subcircuits, + # and check those dates, too. + layoutdir = os.path.split(layoutpath)[0] + subrex = re.compile('^[^\*]*[ \t]*.subckt[ \t]+([^ \t]+).*$', re.IGNORECASE) + with open(spipath, 'r') as ifile: + duttext = ifile.read() + + dutlines = duttext.replace('\n+', ' ').splitlines() + for line in dutlines: + lmatch = subrex.match(line) + if lmatch: + subname = lmatch.group(1) + sublayout = layoutdir + '/' + subname + '.mag' + # subcircuits that cannot be found in the current directory are + # assumed to be library components and therefore never out-of-date. + if os.path.exists(sublayout): + sub_statbuf = os.stat(sublayout) + if spi_statbuf.st_mtime < lay_statbuf.st_mtime: + # netlist exists but is out-of-date + need_capture = True + break + return need_capture + + def check_schematic_out_of_date(self, spipath, schempath): + # Check if a netlist (spipath) is out-of-date relative to the schematics + # (schempath). Need to read the netlist and check all of the subcells. + need_capture = False + if not os.path.isfile(spipath): + return True + if os.path.isfile(schempath): + spi_statbuf = os.stat(spipath) + sch_statbuf = os.stat(schempath) + if spi_statbuf.st_mtime < sch_statbuf.st_mtime: + # netlist exists but is out-of-date + need_capture = True + else: + # only found that the top-level-schematic is older than the + # netlist. Now need to read the netlist, find all subcircuits, + # and check those dates, too. + schemdir = os.path.split(schempath)[0] + subrex = re.compile('^[^\*]*[ \t]*.subckt[ \t]+([^ \t]+).*$', re.IGNORECASE) + with open(spipath, 'r') as ifile: + duttext = ifile.read() + + dutlines = duttext.replace('\n+', ' ').splitlines() + for line in dutlines: + lmatch = subrex.match(line) + if lmatch: + subname = lmatch.group(1) + # NOTE: Electric uses library:cell internally to track libraries, + # and maps the ":" to "__" in the netlist. Not entirely certain that + # the double-underscore uniquely identifies the library:cell. . . + librex = re.compile('(.*)__(.*)', re.IGNORECASE) + lmatch = librex.match(subname) + if lmatch: + elecpath = os.path.split(os.path.split(schempath)[0])[0] + libname = lmatch.group(1) + subschem = elecpath + '/' + libname + '.delib/' + lmatch.group(2) + '.sch' + else: + libname = {} + subschem = schemdir + '/' + subname + '.sch' + # subcircuits that cannot be found in the current directory are + # assumed to be library components and therefore never out-of-date. + if os.path.exists(subschem): + sub_statbuf = os.stat(subschem) + if spi_statbuf.st_mtime < sub_statbuf.st_mtime: + # netlist exists but is out-of-date + need_capture = True + break + # mapping of characters to what's allowed in SPICE makes finding + # the associated schematic file a bit difficult. Requires wild-card + # searching. + elif libname: + restr = lmatch.group(2) + '.sch' + restr = restr.replace('.', '\.') + restr = restr.replace('_', '.') + schrex = re.compile(restr, re.IGNORECASE) + libpath = elecpath + '/' + libname + '.delib' + if os.path.exists(libpath): + liblist = os.listdir(libpath) + for file in liblist: + lmatch = schrex.match(file) + if lmatch: + subschem = libpath + '/' + file + sub_statbuf = os.stat(subschem) + if spi_statbuf.st_mtime < sch_statbuf.st_mtime: + # netlist exists but is out-of-date + need_capture = True + break + return need_capture + + def check_lvs(self): + # If both netlists exist, and comp.json is more recent than both, then + # load LVS results from comp.json + project_path = self.rootpath + project_name = self.project + layout_path = project_path + '/layout/' + project_name + '.spc' + net_path = project_path + '/netlist/' + project_name + '.spi' + comp_path = project_path + '/layout/comp.json' + + if os.path.exists(layout_path) and os.path.exists(net_path) and os.path.exists(comp_path): + magtime = os.stat(layout_path).st_mtime + schemtime = os.stat(net_path).st_mtime + comptime = os.stat(comp_path).st_mtime + if comptime > magtime and comptime > schemtime: + print("Loading LVS results from file.") + self.generate(comp_path) + + def generate_layout_netlist(self, layout_path, layout_src, project_path): + # Does layout netlist exist and is it current? + if self.check_layout_out_of_date(layout_path, layout_src): + print('Generating layout netlist.') + self.update_idletasks() + mproc = subprocess.Popen(['magic', '-dnull', '-noconsole', + self.layout], stdin = subprocess.PIPE, stdout = subprocess.PIPE, + stderr = subprocess.PIPE, cwd = project_path + '/layout', + universal_newlines = True) + mproc.stdin.write("select top cell\n") + mproc.stdin.write("expand\n") + mproc.stdin.write("extract all\n") + mproc.stdin.write("ext2spice hierarchy on\n") + mproc.stdin.write("ext2spice format ngspice\n") + mproc.stdin.write("ext2spice scale off\n") + mproc.stdin.write("ext2spice renumber off\n") + mproc.stdin.write("ext2spice subcircuit top auto\n") + mproc.stdin.write("ext2spice cthresh infinite\n") + mproc.stdin.write("ext2spice rthresh infinite\n") + mproc.stdin.write("ext2spice blackbox on\n") + mproc.stdin.write("ext2spice -o " + self.layout + ".spi\n") + mproc.stdin.write("quit -noprompt\n") + magicout = mproc.communicate()[0] + self.printwarn(magicout) + if mproc.returncode != 0: + print('Failure to generate new layout netlist.') + return False + + # Move .spi netlist to project_dir/netlist/lvs/ + shutil.move(project_path + '/layout/' + self.layout + '.spi', layout_path) + # Remove extraction files + for file in os.listdir(project_path + '/layout'): + if os.path.splitext(file)[1] == '.ext': + os.remove(project_path + '/layout/' + file) + else: + print('Layout netlist is up-to-date, not regenerating.') + return True + + def run_lvs(self, value): + # "value" is ignored (?) + + # Check if netlists exist and are current; otherwise create them. + # Then run LVS. + + project_path = self.rootpath + project_name = self.project + comp_path = project_path + '/layout/comp.json' + has_vlog = False + vlog_path = project_path + '/verilog/' + project_name + '.v' + + layout_path = project_path + '/netlist/lvs/' + self.layout + '.spi' + net_path = project_path + '/netlist/schem/' + self.schematic + '.spi' + layout_src = project_path + '/layout/' + self.layout + '.mag' + + # Does the setup file exist (this is optional)? + if self.lvs_setup != '' and not os.path.isfile('setup.tcl'): + print('No technology setup file selected.') + + # Does schematic netlist exist? + if not os.path.isfile(vlog_path) and not os.path.isfile(net_path): + print('Error: No schematic netlist or verilog netlist.') + return + + # Does LVS netlist subdirectory exist? + if not os.path.exists(project_path + '/netlist/lvs'): + os.makedirs(project_path + '/netlist/lvs') + + # Does layout netlist exist and is it current? + if not self.generate_layout_netlist(layout_path, layout_src, project_path): + return False + + # Final check + if not os.path.isfile(layout_path): + print('Error: No netlist generated from magic.') + return + + else: + # Read in netlist and convert commas from [X,Y] arrays to vertical bars + # as something that can be converted back as necessary. ngspice treats + # commas as special characters for some reason. + with open(layout_path) as ifile: + spitext = ifile.read() + + # spilines = spitext.replace(',', '|') + # with open(layout_path, 'w') as ofile: + # ofile.write(spilines) + + # Check the netlist to see if the cell to match is a subcircuit. If + # not, then assume it is the top level. + + is_subckt = False + subrex = re.compile('^[^\*]*[ \t]*.subckt[ \t]+([^ \t]+).*$', re.IGNORECASE) + dutlines = spitext.replace('\n+', ' ').splitlines() + for line in dutlines: + lmatch = subrex.match(line) + if lmatch: + subname = lmatch.group(1) + if subname == self.layout: + is_subckt = True + break + + if is_subckt: + layout_arg = layout_path + ' ' + self.layout + layout_text = '"' + layout_arg + '"' + else: + layout_arg = layout_path + layout_text = layout_arg + + if has_vlog: + schem_arg = vlog_path + ' ' + self.schematic + else: + schem_arg = net_path + ' ' + self.schematic + schem_text = '"' + schem_arg + '"' + + # Remove any previous comparison output file + comp_out_path = os.path.splitext(comp_path)[0] + '.out' + if os.path.exists(comp_out_path): + os.remove(comp_out_path) + + # Run netgen as subprocess + print('Running: netgen -batch lvs ' + layout_text + + ' ' + schem_text + ' ' + self.lvs_setup + ' ' + comp_out_path + + ' -json -blackbox') + # Note: Because arguments to subprocess are list items, the {filename cell} + # pair does *not* have to be quoted or braced. Doing so causes a parse + # error. + self.lvsproc = subprocess.Popen(['netgen', '-batch', 'lvs', + layout_arg, schem_arg, + self.lvs_setup, comp_out_path, '-json', '-blackbox'], + cwd=project_path + '/layout', + stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=0) + # This is largely unnecessary as netgen usually runs to completion very quickly. + self.watchclock(comp_path) + + def watchclock(self, filename): + if self.lvsproc == None: + return + + lvs_status = self.lvsproc.poll() + sresult = select.select([self.lvsproc.stdout, self.lvsproc.stderr], [], [], 0)[0] + if self.lvsproc.stdout in sresult: + outstring = self.lvsproc.stdout.readline().decode().strip() + self.logprint(outstring, doflush=True) + print(outstring) + elif self.lvsproc.stderr in sresult: + errstring = self.lvsproc.stderr.readline().decode().strip() + self.logprint(errstring, doflush = True) + print(errstring, file=sys.stderr) + + if lvs_status != None: + print("netgen LVS exited with status " + str(lvs_status)) + self.lvsproc = None + if lvs_status != 0: + print('Errors encountered in LVS.') + self.logprint('Errors in LVS, lvs status = ' + str(lvs_status), doflush=True) + # Done; now read comp.json and fill the treeview listbox. + self.generate(filename) + else: + self.after(500, lambda: self.watchclock(filename)) + + # Generate display from "comp.out" file (json file now preferred) + + def generate_orig(self, lvspath): + lefttext = [] + righttext = [] + print("Reading LVS output file " + lvspath) + if os.path.exists(lvspath): + with open(lvspath, 'r') as ifile: + lvslines = ifile.read().splitlines() + for line in lvslines: + if '|' in line: + # parts = line.split('|') + # lefttext.append(parts[0]) + # righttext.append(parts[1]) + lefttext.append(line[0:42].strip()) + righttext.append(line[44:].strip()) + else: + lefttext.append(line) + righttext.append('') + # Populate treeview with text + self.toppane.lvsreport.repopulate(lefttext, righttext) + + else: + print("Error: No output file generated from LVS.") + + # Generate output from LVS report JSON file comp.json + + def generate(self, lvspath): + lefttext = [] + righttext = [] + print("Reading LVS output file " + lvspath) + if os.path.exists(lvspath): + with open(lvspath, 'r') as ifile: + self.lvsdata = json.load(ifile) + + # Populate treeview with text + self.toppane.lvsreport.json_repopulate(self.lvsdata) + + else: + print("Error: No output file generated from LVS.") + + def findrecord(self, value): + print("Unimplemented function") + + def findrecord_test(self, value): + # Check if socket is defined; if not, attempt to open one + if not self.msock: + try: + self.msock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + except: + print("No response from layout tool.") + + if self.msock: + self.msock.connect(("0.0.0.0", 12946)) + self.msock.setblocking(False) + if self.msock: + # Pull name of net or device from 'value' + # This is a test: + self.msock.sendall(b'box 0 0 100 100\r\n') + +if __name__ == '__main__': + options = [] + arguments = [] + for item in sys.argv[1:]: + if item.find('-', 0) == 0: + options.append(item) + else: + arguments.append(item) + + root = tkinter.Tk() + app = LVSManager(root) + if arguments: + if len(arguments) >= 2: + app.set_project(arguments[0], project_name=arguments[1]) + else: + app.set_project(arguments[0]) + + root.mainloop() diff --git a/python/tksimpledialog.py b/python/tksimpledialog.py new file mode 100755 index 0000000..24caf38 --- /dev/null +++ b/python/tksimpledialog.py @@ -0,0 +1,86 @@ +#!/bin/env python3 +# +# Dialog class for tkinter + +import os +import tkinter +from tkinter import ttk + +class Dialog(tkinter.Toplevel): + + def __init__(self, parent, message = None, title = None, seed = None, border = 'blue', **kwargs): + + tkinter.Toplevel.__init__(self, parent) + self.transient(parent) + + if title: + self.title(title) + + self.configure(background=border, padx=2, pady=2) + self.obox = ttk.Frame(self) + self.obox.pack(side = 'left', fill = 'both', expand = 'true') + + self.parent = parent + self.result = None + body = ttk.Frame(self.obox) + self.initial_focus = self.body(body, message, seed, **kwargs) + body.pack(padx = 5, pady = 5) + self.buttonbox() + self.grab_set() + + if not self.initial_focus: + self.initial_focus = self + + self.protocol("WM_DELETE_WINDOW", self.cancel) + self.geometry("+%d+%d" % (parent.winfo_rootx() + 50, + parent.winfo_rooty() + 50)) + + self.initial_focus.focus_set() + self.wait_window(self) + + # Construction hooks + + def body(self, master, **kwargs): + # Create dialog body. Return widget that should have + # initial focus. This method should be overridden + pass + + def buttonbox(self): + # Add standard button box. Override if you don't want the + # standard buttons + + box = ttk.Frame(self.obox) + + self.okb = ttk.Button(box, text="OK", width=10, command=self.ok, default='active') + self.okb.pack(side='left', padx=5, pady=5) + w = ttk.Button(box, text="Cancel", width=10, command=self.cancel) + w.pack(side='left', padx=5, pady=5) + + self.bind("<Return>", self.ok) + self.bind("<Escape>", self.cancel) + box.pack(fill='x', expand='true') + + # Standard button semantics + + def ok(self, event=None): + + if not self.validate(): + self.initial_focus.focus_set() # put focus back + return + + self.withdraw() + self.update_idletasks() + self.result = self.apply() + self.cancel() + + def cancel(self, event=None): + + # Put focus back to the parent window + self.parent.focus_set() + self.destroy() + + def validate(self): + return 1 # Override this + + def apply(self): + return None # Override this diff --git a/python/tooltip.py b/python/tooltip.py new file mode 100755 index 0000000..ac3444b --- /dev/null +++ b/python/tooltip.py @@ -0,0 +1,158 @@ +#!/bin/env python3 +'''Michael Lange <klappnase (at) freakmail (dot) de> +The ToolTip class provides a flexible tooltip widget for tkinter; it is based on IDLE's ToolTip +module which unfortunately seems to be broken (at least the version I saw). +INITIALIZATION OPTIONS: +anchor : where the text should be positioned inside the widget, must be on of "n", "s", "e", "w", "nw" and so on; + default is "center" +bd : borderwidth of the widget; default is 1 (NOTE: don't use "borderwidth" here) +bg : background color to use for the widget; default is "lightyellow" (NOTE: don't use "background") +delay : time in ms that it takes for the widget to appear on the screen when the mouse pointer has + entered the parent widget; default is 1500 +fg : foreground (i.e. text) color to use; default is "black" (NOTE: don't use "foreground") +follow_mouse : if set to 1 the tooltip will follow the mouse pointer instead of being displayed + outside of the parent widget; this may be useful if you want to use tooltips for + large widgets like listboxes or canvases; default is 0 +font : font to use for the widget; default is system specific +justify : how multiple lines of text will be aligned, must be "left", "right" or "center"; default is "left" +padx : extra space added to the left and right within the widget; default is 4 +pady : extra space above and below the text; default is 2 +relief : one of "flat", "ridge", "groove", "raised", "sunken" or "solid"; default is "solid" +state : must be "normal" or "disabled"; if set to "disabled" the tooltip will not appear; default is "normal" +text : the text that is displayed inside the widget +textvariable : if set to an instance of tkinter.StringVar() the variable's value will be used as text for the widget +width : width of the widget; the default is 0, which means that "wraplength" will be used to limit the widgets width +wraplength : limits the number of characters in each line; default is 150 + +WIDGET METHODS: +configure(**opts) : change one or more of the widget's options as described above; the changes will take effect the + next time the tooltip shows up; NOTE: follow_mouse cannot be changed after widget initialization + +Other widget methods that might be useful if you want to subclass ToolTip: +enter() : callback when the mouse pointer enters the parent widget +leave() : called when the mouse pointer leaves the parent widget +motion() : is called when the mouse pointer moves inside the parent widget if follow_mouse is set to 1 and the + tooltip has shown up to continually update the coordinates of the tooltip window +coords() : calculates the screen coordinates of the tooltip window +create_contents() : creates the contents of the tooltip window (by default a tkinter.Label) +''' +# Ideas gleaned from PySol + +import tkinter + +class ToolTip: + def __init__(self, master, text='Your text here', delay=1500, **opts): + self.master = master + self._opts = {'anchor':'center', 'bd':1, 'bg':'lightyellow', 'delay':delay, 'fg':'black',\ + 'follow_mouse':0, 'font':None, 'justify':'left', 'padx':4, 'pady':2,\ + 'relief':'solid', 'state':'normal', 'text':text, 'textvariable':None,\ + 'width':0, 'wraplength':150} + self.configure(**opts) + self._tipwindow = None + self._id = None + self._id1 = self.master.bind("<Enter>", self.enter, '+') + self._id2 = self.master.bind("<Leave>", self.leave, '+') + self._id3 = self.master.bind("<ButtonPress>", self.leave, '+') + self._follow_mouse = 0 + if self._opts['follow_mouse']: + self._id4 = self.master.bind("<Motion>", self.motion, '+') + self._follow_mouse = 1 + + def configure(self, **opts): + for key in opts: + if key in self._opts: + self._opts[key] = opts[key] + else: + KeyError = 'KeyError: Unknown option: "%s"' %key + raise KeyError + + ##----these methods handle the callbacks on "<Enter>", "<Leave>" and "<Motion>"---------------## + ##----events on the parent widget; override them if you want to change the widget's behavior--## + + def enter(self, event=None): + self._schedule() + + def leave(self, event=None): + self._unschedule() + self._hide() + + def motion(self, event=None): + if self._tipwindow and self._follow_mouse: + x, y = self.coords() + self._tipwindow.wm_geometry("+%d+%d" % (x, y)) + + ##------the methods that do the work:---------------------------------------------------------## + + def _schedule(self): + self._unschedule() + if self._opts['state'] == 'disabled': + return + self._id = self.master.after(self._opts['delay'], self._show) + + def _unschedule(self): + id = self._id + self._id = None + if id: + self.master.after_cancel(id) + + def _show(self): + if self._opts['state'] == 'disabled': + self._unschedule() + return + if not self._tipwindow: + self._tipwindow = tw = tkinter.Toplevel(self.master) + # hide the window until we know the geometry + tw.withdraw() + tw.wm_overrideredirect(1) + + if tw.tk.call("tk", "windowingsystem") == 'aqua': + tw.tk.call("::tk::unsupported::MacWindowStyle", "style", tw._w, "help", "none") + + self.create_contents() + tw.update_idletasks() + x, y = self.coords() + tw.wm_geometry("+%d+%d" % (x, y)) + tw.deiconify() + tw.lift() + + def _hide(self): + tw = self._tipwindow + self._tipwindow = None + if tw: + tw.destroy() + + ##----these methods might be overridden in derived classes:----------------------------------## + + def coords(self): + # The tip window must be completely outside the master widget; + # otherwise when the mouse enters the tip window we get + # a leave event and it disappears, and then we get an enter + # event and it reappears, and so on forever :-( + # or we take care that the mouse pointer is always outside the tipwindow :-) + tw = self._tipwindow + twx, twy = tw.winfo_reqwidth(), tw.winfo_reqheight() + w, h = tw.winfo_screenwidth(), tw.winfo_screenheight() + # calculate the y coordinate: + if self._follow_mouse: + y = tw.winfo_pointery() + 20 + # make sure the tipwindow is never outside the screen: + if y + twy > h: + y = y - twy - 30 + else: + y = self.master.winfo_rooty() + self.master.winfo_height() + 3 + if y + twy > h: + y = self.master.winfo_rooty() - twy - 3 + # we can use the same x coord in both cases: + x = tw.winfo_pointerx() - twx / 2 + if x < 0: + x = 0 + elif x + twx > w: + x = w - twx + return x, y + + def create_contents(self): + opts = self._opts.copy() + for opt in ('delay', 'follow_mouse', 'state'): + del opts[opt] + label = tkinter.Label(self._tipwindow, **opts) + label.pack() diff --git a/python/treeviewsplit.py b/python/treeviewsplit.py new file mode 100755 index 0000000..b7a318a --- /dev/null +++ b/python/treeviewsplit.py @@ -0,0 +1,653 @@ +#!/bin/env python3 +# +# Simple ttk treeview with split view, scrollbar, and +# row of callback buttons + +import os +import re +import itertools + +import tkinter +from tkinter import ttk + +#------------------------------------------------------ +# Tree view used as a multi-column list box +#------------------------------------------------------ + +class TreeViewSplit(ttk.Frame): + def __init__(self, parent, fontsize=11, *args, **kwargs): + ttk.Frame.__init__(self, parent, *args, **kwargs) + s = ttk.Style() + s.configure('normal.TLabel', font=('Helvetica', fontsize)) + s.configure('title.TLabel', font=('Helvetica', fontsize, 'bold')) + s.configure('normal.TButton', font=('Helvetica', fontsize), + border = 3, relief = 'raised') + s.configure('Treeview.Heading', font=('Helvetica', fontsize, 'bold')) + s.configure('Treeview.Column', font=('Helvetica', fontsize)) + self.fontsize = fontsize + + # Last item is a list of 2-item lists, each containing the name of a button + # to place along the button bar at the bottom, and a callback function to + # run when the button is pressed. + + def populate(self, title1="", item1list=[], title2="", item2list=[], buttons=[], height=10): + self.item1list = item1list[:] + self.item2list = item2list[:] + columns = [0, 1] + + treeFrame = ttk.Frame(self) + treeFrame.pack(side='top', padx=5, pady=5, fill='both', expand='true') + + scrollBar = ttk.Scrollbar(treeFrame) + scrollBar.pack(side='right', fill='y') + self.treeView = ttk.Treeview(treeFrame, selectmode='browse', columns=columns, height=height) + self.treeView.pack(side='left', fill='both', expand='true') + scrollBar.config(command=self.treeView.yview) + self.treeView.config(yscrollcommand=scrollBar.set) + self.treeView.column('#0', width=120, stretch='false') + self.treeView.heading(0, text=title1, anchor='w') + self.treeView.heading(1, text=title2, anchor='w') + buttonFrame = ttk.Frame(self) + buttonFrame.pack(side='bottom', fill='x') + + self.treeView.tag_configure('select',background='darkslategray',foreground='white') + + # Test type tags + self.treeView.tag_configure('error', font=('Helvetica', self.fontsize - 1), foreground = 'red') + self.treeView.tag_configure('clean', font=('Helvetica', self.fontsize - 1), foreground = 'green3') + self.treeView.tag_configure('normal', font=('Helvetica', self.fontsize - 1), foreground = 'black') + self.treeView.tag_configure('prep', font=('Helvetica', self.fontsize, 'bold italic'), + foreground = 'black', anchor = 'center') + self.treeView.tag_configure('header1', font=('Helvetica', self.fontsize, 'bold italic'), + foreground = 'brown', anchor = 'center') + self.treeView.tag_configure('header2', font=('Helvetica', self.fontsize - 1, 'bold'), + foreground = 'blue', anchor = 'center') + self.treeView.tag_configure('header3', font=('Helvetica', self.fontsize - 1, 'italic'), + foreground = 'green4', anchor = 'center') + self.treeView.tag_configure('header4', font=('Helvetica', self.fontsize - 1), + foreground = 'purple', anchor = 'center') + + + self.func_buttons = [] + for button in buttons: + func = button[2] + # Each func_buttons entry is a list of two items; first is the + # button widget, and the second is a boolean that is True if the + # button is to be present always, False if the button is only + # present when there are entries in the itemlists. + self.func_buttons.append([ttk.Button(buttonFrame, text=button[0], + style = 'normal.TButton', + command = lambda func=func: self.func_callback(func)), + button[1]]) + + self.selectcallback = None + self.lastselected = None + self.lasttag = None + self.repopulate(item1list, item2list) + + def get_button(self, index): + if index >= 0 and index < len(self.func_buttons): + return self.func_buttons[index][0] + else: + return None + + def set_title(self, title): + self.treeView.heading('#0', text=title, anchor='w') + + def repopulate(self, item1list=[], item2list=[]): + + # Remove all children of treeview + self.treeView.delete(*self.treeView.get_children()) + + self.item1list = item1list[:] + self.item2list = item2list[:] + lines = max(len(self.item1list), len(self.item2list)) + + # Parse the information coming from comp.out. This is preferably + # handled from inside netgen, but that requires development in netgen. + # Note: A top-level group is denoted by an empty string. + + nested = [''] + if lines > 0: + # print("Create item ID 0 parent = ''") + self.treeView.insert(nested[-1], 'end', text='-', iid='0', + value=['Initialize', 'Initialize'], tags=['prep']) + nested.append('0') + tagstyle = 'header1' + + emptyrec = re.compile('^[\t ]*$') + subrex = re.compile('Subcircuit summary') + cktrex = re.compile('Circuit[\t ]+[12]:[\t ]+([^ \t]+)') + netrex = re.compile('NET mismatches') + devrex = re.compile('DEVICE mismatches') + seprex = re.compile('-----') + sumrex = re.compile('Netlists ') + matchrex = re.compile('.*\*\*Mismatch\*\*') + incircuit = False + watchgroup = False + groupnum = 0 + + for item1, item2, index in zip(self.item1list, self.item2list, range(lines)): + cname1 = 'Layout' # Placeholder + cname2 = 'Schematic' # Placeholder + + # Remove blank lines from the display + lmatch = emptyrec.match(item1) + if lmatch: + lmatch = emptyrec.match(item2) + if lmatch: + continue + index = str(index + 1) + # Parse text to determine how to structure and display it. + tagstyle = 'normal' + nextnest = None + lmatch = subrex.match(item1) + if lmatch: + nested = [''] # pop back to topmost level + nextnest = index + tagstyle = 'header1' + incircuit = False + watchgroup = False + groupnum = 0 + item1 = 'Layout compare' + item2 = 'Schematic compare' + else: + lmatch = cktrex.match(item1) + if lmatch and not incircuit: + # Pick up circuit names and replace them in the title, then use them + # for all following titles. + cname1 = lmatch.group(1) + lmatch = cktrex.match(item2) + cname2 = lmatch.group(1) + print("Circuit names " + cname1 + " " + cname2) + # Rewrite title + cktitem = self.treeView.item(nested[-1], values=[cname1 + ' compare', + cname2 + ' compare']) + nextnest = index + tagstyle = 'header2' + incircuit = True + item1 = cname1 + ' Summary' + item2 = cname2 + ' Summary' + elif lmatch: + continue + else: + lmatch = netrex.match(item1) + if lmatch: + if watchgroup: + nested = nested[0:-1] + nested = nested[0:-1] + nextnest = index + tagstyle = 'header2' + groupnum = 1 + watchgroup = True + item1 = cname1 + ' Net mismatches' + item2 = cname2 + ' Net mismatches' + else: + lmatch = devrex.match(item1) + if lmatch: + if watchgroup: + nested = nested[0:-1] + nested = nested[0:-1] + nextnest = index + tagstyle = 'header2' + groupnum = 1 + watchgroup = True + item1 = cname1 + ' Device mismatches' + item2 = cname2 + ' Device mismatches' + else: + lmatch = seprex.match(item1) + if lmatch: + if watchgroup: + tagstyle = 'header3' + item1 = 'Group ' + str(groupnum) + item2 = 'Group ' + str(groupnum) + if groupnum > 1: + nested = nested[0:-1] + groupnum += 1 + nextnest = index + watchgroup = False + else: + if groupnum > 0: + watchgroup = True + continue + else: + lmatch = sumrex.match(item1) + if lmatch: + if watchgroup: + nested = nested[0:-1] + nested = nested[0:-1] + watchgroup = False + tagstyle = 'header2' + groupnum = 0 + + lmatch1 = matchrex.match(item1) + lmatch2 = matchrex.match(item2) + if lmatch1 or lmatch2: + tagstyle='error' + + # print("Create item ID " + str(index) + " parent = " + str(nested[-1])) + self.treeView.insert(nested[-1], 'end', text=index, iid=index, value=[item1, item2], + tags=[tagstyle]) + + if nextnest: + nested.append(nextnest) + + for button in self.func_buttons: + button[0].pack_forget() + + if lines == 0: + self.treeView.insert('', 'end', text='-', value=['(no items)', '(no items)']) + for button in self.func_buttons: + if button[1]: + button[0].pack(side='left', padx = 5) + else: + for button in self.func_buttons: + button[0].pack(side='left', padx = 5) + + # Special routine to pull in the JSON file data produced by netgen-1.5.72 + def json_repopulate(self, lvsdata): + + # Remove all children of treeview + self.treeView.delete(*self.treeView.get_children()) + + # Parse the information coming from comp.out. This is preferably + # handled from inside netgen, but that requires development in netgen. + # Note: A top-level group is denoted by an empty string. + + index = 0 + errtotal = {} + errtotal['net'] = 0 + errtotal['netmatch'] = 0 + errtotal['device'] = 0 + errtotal['devmatch'] = 0 + errtotal['property'] = 0 + errtotal['pin'] = 0 + ncells = len(lvsdata) + for c in range(0, ncells): + cellrec = lvsdata[c] + if c == ncells - 1: + topcell = True + else: + topcell = False + + errcell = {} + errcell['net'] = 0 + errcell['netmatch'] = 0 + errcell['device'] = 0 + errcell['devmatch'] = 0 + errcell['property'] = 0 + errcell['pin'] = 0; + + cname1 = 'Layout' + cname2 = 'Schematic' + + # cellrec is a dictionary. Parse the cell summary, then failing nets, + # devices, and properties, and finally pins. + + if 'name' in cellrec: + names = cellrec['name'] + cname1 = names[0] + cname2 = names[1] + else: + # This is an error but don't halt the program because of it. + cname1 = '(unknown layout)' + cname2 = '(unknown schematic)' + + item1 = cname1 + item2 = cname2 + tagstyle = 'header1' + index += 1 + nest0 = index + self.treeView.insert('', 'end', text=index, iid=index, value=[item1, item2], + tags=[tagstyle]) + + if 'devices' in cellrec or 'nets' in cellrec: + item1 = cname1 + " Summary" + item2 = cname2 + " Summary" + tagstyle = 'header2' + index += 1 + nest1 = index + self.treeView.insert(nest0, 'end', text=index, iid=index, value=[item1, item2], + tags=[tagstyle]) + + if 'devices' in cellrec: + item1 = cname1 + " Devices" + item2 = cname2 + " Devices" + tagstyle = 'header3' + index += 1 + nest2 = index + self.treeView.insert(nest1, 'end', text=index, iid=index, value=[item1, item2], + tags=[tagstyle]) + + devices = cellrec['devices'] + devlist = [val for pair in zip(devices[0], devices[1]) for val in pair] + devpair = list(devlist[p:p + 2] for p in range(0, len(devlist), 2)) + for dev in devpair: + c1dev = dev[0] + c2dev = dev[1] + + item1 = c1dev[0] + "(" + str(c1dev[1]) + ")" + item2 = c2dev[0] + "(" + str(c2dev[1]) + ")" + + diffdevs = abs(c1dev[1] - c2dev[1]) + if diffdevs == 0: + tagstyle = 'normal' + else: + tagstyle = 'error' + errcell['device'] += diffdevs + if topcell: + errtotal['device'] += diffdevs + index += 1 + nest2 = index + self.treeView.insert(nest1, 'end', text=index, iid=index, + value=[item1, item2], tags=[tagstyle]) + + if 'nets' in cellrec: + item1 = cname1 + " Nets" + item2 = cname2 + " Nets" + tagstyle = 'header3' + index += 1 + nest2 = index + self.treeView.insert(nest1, 'end', text=index, iid=index, value=[item1, item2], + tags=[tagstyle]) + + nets = cellrec['nets'] + + item1 = nets[0] + item2 = nets[1] + diffnets = abs(nets[0] - nets[1]) + if diffnets == 0: + tagstyle = 'normal' + else: + tagstyle = 'error' + errcell['net'] = diffnets + if topcell: + errtotal['net'] += diffnets + index += 1 + nest2 = index + self.treeView.insert(nest1, 'end', text=index, iid=index, + value=[item1, item2], tags=[tagstyle]) + + if 'badnets' in cellrec: + badnets = cellrec['badnets'] + + if len(badnets) > 0: + item1 = cname1 + " Net Mismatches" + item2 = cname2 + " Net Mismatches" + tagstyle = 'header2' + index += 1 + nest1 = index + self.treeView.insert(nest0, 'end', text=index, iid=index, + value=[item1, item2], tags=[tagstyle]) + + groupnum = 0 + for group in badnets: + groupc1 = group[0] + groupc2 = group[1] + nnets = len(groupc1) + + groupnum += 1 + tagstyle = 'header3' + index += 1 + nest2 = index + item1 = "Group " + str(groupnum) + ' (' + str(nnets) + ' nets)' + self.treeView.insert(nest1, 'end', text=index, iid=index, + value=[item1, item1], tags=[tagstyle]) + + tagstyle = 'error' + errcell['netmatch'] += nnets + if topcell: + errtotal['netmatch'] += nnets + + for netnum in range(0, nnets): + if netnum > 0: + item1 = "" + index += 1 + nest3 = index + self.treeView.insert(nest2, 'end', text=index, iid=index, + value=[item1, item1], tags=[tagstyle]) + + net1 = groupc1[netnum] + net2 = groupc2[netnum] + tagstyle = 'header4' + item1 = net1[0] + item2 = net2[0] + index += 1 + nest3 = index + self.treeView.insert(nest2, 'end', text=index, iid=index, + value=[item1, item2], tags=[tagstyle]) + + # Pad shorter device list to the length of the longer one + netdevs = list(itertools.zip_longest(net1[1], net2[1])) + for devpair in netdevs: + devc1 = devpair[0] + devc2 = devpair[1] + tagstyle = 'normal' + if devc1 and devc1[0] != "": + item1 = devc1[0] + '/' + devc1[1] + ' = ' + str(devc1[2]) + else: + item1 = "" + if devc2 and devc2[0] != "": + item2 = devc2[0] + '/' + devc2[1] + ' = ' + str(devc2[2]) + else: + item2 = "" + index += 1 + nest3 = index + self.treeView.insert(nest2, 'end', text=index, iid=index, + value=[item1, item2], tags=[tagstyle]) + + if 'badelements' in cellrec: + badelements = cellrec['badelements'] + + if len(badelements) > 0: + item1 = cname1 + " Device Mismatches" + item2 = cname2 + " Device Mismatches" + tagstyle = 'header2' + index += 1 + nest1 = index + self.treeView.insert(nest0, 'end', text=index, iid=index, + value=[item1, item2], tags=[tagstyle]) + + groupnum = 0 + for group in badelements: + groupc1 = group[0] + groupc2 = group[1] + ndevs = len(groupc1) + + groupnum += 1 + tagstyle = 'header3' + index += 1 + nest2 = index + item1 = "Group " + str(groupnum) + ' (' + str(ndevs) + ' devices)' + self.treeView.insert(nest1, 'end', text=index, iid=index, + value=[item1, item1], tags=[tagstyle]) + + tagstyle = 'error' + errcell['devmatch'] += ndevs + if topcell: + errtotal['devmatch'] += ndevs + + for elemnum in range(0, ndevs): + if elemnum > 0: + item1 = "" + index += 1 + nest3 = index + self.treeView.insert(nest2, 'end', text=index, iid=index, + value=[item1, item1], tags=[tagstyle]) + + elem1 = groupc1[elemnum] + elem2 = groupc2[elemnum] + tagstyle = 'header4' + item1 = elem1[0] + item2 = elem2[0] + index += 1 + nest3 = index + self.treeView.insert(nest2, 'end', text=index, iid=index, + value=[item1, item2], tags=[tagstyle]) + + # Pad shorter pin list to the length of the longer one + elempins = list(itertools.zip_longest(elem1[1], elem2[1])) + for pinpair in elempins: + pinc1 = pinpair[0] + pinc2 = pinpair[1] + tagstyle = 'normal' + if pinc1 and pinc1[0] != "": + item1 = pinc1[0] + ' = ' + str(pinc1[1]) + else: + item1 = "" + if pinc2 and pinc2[0] != "": + item2 = pinc2[0] + ' = ' + str(pinc2[1]) + else: + item2 = "" + index += 1 + nest3 = index + self.treeView.insert(nest2, 'end', text=index, iid=index, + value=[item1, item2], tags=[tagstyle]) + + if 'properties' in cellrec: + properties = cellrec['properties'] + numproperr = len(properties) + if numproperr > 0: + item1 = cname1 + " Properties" + item2 = cname2 + " Properties" + tagstyle = 'header2' + index += 1 + nest1 = index + self.treeView.insert(nest0, 'end', text=index, iid=index, value=[item1, item2], + tags=[tagstyle]) + errcell['property'] = numproperr + errtotal['property'] += numproperr + + for prop in properties: + + if prop != properties[0]: + item1 = "" + index += 1 + nest2 = index + self.treeView.insert(nest1, 'end', text=index, iid=index, + value=[item1, item1], tags=[tagstyle]) + + propc1 = prop[0] + propc2 = prop[1] + + tagstyle = 'header3' + item1 = propc1[0] + item2 = propc2[0] + index += 1 + nest2 = index + self.treeView.insert(nest1, 'end', text=index, iid=index, + value=[item1, item2], tags=[tagstyle]) + + # Pad shorter property list to the length of the longer one + elemprops = list(itertools.zip_longest(propc1[1], propc2[1])) + for proppair in elemprops: + perrc1 = proppair[0] + perrc2 = proppair[1] + tagstyle = 'normal' + if perrc1 and perrc1[0] != "": + item1 = perrc1[0] + ' = ' + str(perrc1[1]) + else: + item1 = "" + if perrc2 and perrc2[0] != "": + item2 = perrc2[0] + ' = ' + str(perrc2[1]) + else: + item2 = "" + index += 1 + nest2 = index + self.treeView.insert(nest1, 'end', text=index, iid=index, + value=[item1, item2], tags=[tagstyle]) + + if 'pins' in cellrec: + item1 = cname1 + " Pins" + item2 = cname2 + " Pins" + tagstyle = 'header2' + index += 1 + nest1 = index + self.treeView.insert(nest0, 'end', text=index, iid=index, value=[item1, item2], + tags=[tagstyle]) + + pins = cellrec['pins'] + pinlist = [val for pair in zip(pins[0], pins[1]) for val in pair] + pinpair = list(pinlist[p:p + 2] for p in range(0, len(pinlist), 2)) + for pin in pinpair: + item1 = pin[0] + item2 = pin[1] + if item1.lower() == item2.lower(): + tagstyle = 'header4' + else: + tagstyle = 'error' + errcell['pin'] += 1 + if topcell: + errtotal['pin'] += 1 + index += 1 + nest2 = index + self.treeView.insert(nest1, 'end', text=index, iid=index, + value=[item1, item2], tags=[tagstyle]) + + allcellerror = errcell['net'] + errcell['device'] + errcell['property'] + errcell['pin'] + errcell['netmatch'] + errcell['devmatch'] + if allcellerror > 0: + item1 = 'Errors: Net = ' + str(errcell['net']) + ', Device = ' + str(errcell['device']) + ', Property = ' + str(errcell['property']) + ', Pin = ' + str(errcell['pin']) + ', Net match = ' + str(errcell['netmatch']) + ', Device match = ' + str(errcell['devmatch']) + tagstyle = 'error' + else: + item1 = 'LVS Clean' + tagstyle = 'clean' + + item2 = "" + index += 1 + nest0 = index + self.treeView.insert('', 'end', text=index, iid=index, value=[item1, item2], + tags=[tagstyle]) + + item1 = "Final LVS result:" + item2 = "" + tagstyle = 'header1' + index += 1 + nest0 = index + self.treeView.insert('', 'end', text=index, iid=index, value=[item1, item2], + tags=[tagstyle]) + + allerror = errtotal['net'] + errtotal['device'] + errtotal['property'] + errtotal['pin'] + errtotal['netmatch'] + errtotal['devmatch'] + if allerror > 0: + item1 = 'Errors: Net = ' + str(errtotal['net']) + ', Device = ' + str(errtotal['device']) + ', Property = ' + str(errtotal['property']) + ', Pin = ' + str(errtotal['pin']) + ', Net match = ' + str(errtotal['netmatch']) + ', Device match = ' + str(errtotal['devmatch']) + tagstyle = 'error' + else: + item1 = 'LVS Clean' + tagstyle = 'clean' + + item2 = "" + index += 1 + nest0 = index + self.treeView.insert('', 'end', text=index, iid=index, value=[item1, item2], + tags=[tagstyle]) + + for button in self.func_buttons: + button[0].pack_forget() + + if index == 0: + self.treeView.insert('', 'end', text='-', value=['(no items)', '(no items)']) + for button in self.func_buttons: + if button[1]: + button[0].pack(side='left', padx = 5) + else: + for button in self.func_buttons: + button[0].pack(side='left', padx = 5) + + # Return values from the treeview + def getlist(self): + return self.treeView.get_children() + + def func_callback(self, callback, event=None): + callback(self.treeView.item(self.treeView.selection())) + + def bindselect(self, callback): + self.selectcallback = callback + + def setselect(self, value): + self.treeView.selection_set(value) + + def selected(self): + value = self.treeView.item(self.treeView.selection()) + if value['values']: + return value + else: + return None diff --git a/scripts/config.log b/scripts/config.log index 3915813..7c1e4c2 100644 --- a/scripts/config.log +++ b/scripts/config.log @@ -39,24 +39,24 @@ PATH: /usr/local/bin ## Core tests. ## ## ----------- ## -configure:2397: checking build system type -configure:2411: result: x86_64-unknown-linux-gnu -configure:2431: checking host system type -configure:2444: result: x86_64-unknown-linux-gnu -configure:2464: checking target system type -configure:2477: result: x86_64-unknown-linux-gnu -configure:2568: checking for gcc -configure:2584: found /bin/gcc -configure:2595: result: gcc -configure:2824: checking for C compiler version -configure:2833: gcc --version >&5 +configure:2398: checking build system type +configure:2412: result: x86_64-unknown-linux-gnu +configure:2432: checking host system type +configure:2445: result: x86_64-unknown-linux-gnu +configure:2465: checking target system type +configure:2478: result: x86_64-unknown-linux-gnu +configure:2569: checking for gcc +configure:2585: found /bin/gcc +configure:2596: result: gcc +configure:2825: checking for C compiler version +configure:2834: gcc --version >&5 gcc (GCC) 4.9.2 20150212 (Red Hat 4.9.2-6) Copyright (C) 2014 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -configure:2844: $? = 0 -configure:2833: gcc -v >&5 +configure:2845: $? = 0 +configure:2834: gcc -v >&5 Using built-in specs. COLLECT_GCC=gcc COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/4.9.2/lto-wrapper @@ -64,58 +64,58 @@ Target: x86_64-redhat-linux Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --enable-multilib --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-languages=c,c++,objc,obj-c++,fortran,ada,go,lto --enable-plugin --enable-initfini-array --disable-libgcj --with-isl=/builddir/build/BUILD/gcc-4.9.2-20150212/obj-x86_64-redhat-linux/isl-install --with-cloog=/builddir/build/BUILD/gcc-4.9.2-20150212/obj-x86_64-redhat-linux/cloog-install --enable-gnu-indirect-function --with-tune=generic --with-arch_32=i686 --build=x86_64-redhat-linux Thread model: posix gcc version 4.9.2 20150212 (Red Hat 4.9.2-6) (GCC) -configure:2844: $? = 0 -configure:2833: gcc -V >&5 +configure:2845: $? = 0 +configure:2834: gcc -V >&5 gcc: error: unrecognized command line option '-V' gcc: fatal error: no input files compilation terminated. -configure:2844: $? = 4 -configure:2833: gcc -qversion >&5 +configure:2845: $? = 4 +configure:2834: gcc -qversion >&5 gcc: error: unrecognized command line option '-qversion' gcc: fatal error: no input files compilation terminated. -configure:2844: $? = 4 -configure:2864: checking whether the C compiler works -configure:2886: gcc -g conftest.c >&5 -configure:2890: $? = 0 -configure:2938: result: yes -configure:2941: checking for C compiler default output file name -configure:2943: result: a.out -configure:2949: checking for suffix of executables -configure:2956: gcc -o conftest -g conftest.c >&5 -configure:2960: $? = 0 -configure:2982: result: -configure:3004: checking whether we are cross compiling -configure:3012: gcc -o conftest -g conftest.c >&5 -configure:3016: $? = 0 -configure:3023: ./conftest -configure:3027: $? = 0 -configure:3042: result: no -configure:3047: checking for suffix of object files -configure:3069: gcc -c -g conftest.c >&5 -configure:3073: $? = 0 -configure:3094: result: o -configure:3098: checking whether we are using the GNU C compiler -configure:3117: gcc -c -g conftest.c >&5 -configure:3117: $? = 0 -configure:3126: result: yes -configure:3135: checking whether gcc accepts -g -configure:3155: gcc -c -g conftest.c >&5 -configure:3155: $? = 0 -configure:3196: result: yes -configure:3213: checking for gcc option to accept ISO C89 -configure:3276: gcc -c -g conftest.c >&5 -configure:3276: $? = 0 -configure:3289: result: none needed -configure:3314: checking how to run the C preprocessor -configure:3345: gcc -E conftest.c -configure:3345: $? = 0 -configure:3359: gcc -E conftest.c +configure:2845: $? = 4 +configure:2865: checking whether the C compiler works +configure:2887: gcc -g conftest.c >&5 +configure:2891: $? = 0 +configure:2939: result: yes +configure:2942: checking for C compiler default output file name +configure:2944: result: a.out +configure:2950: checking for suffix of executables +configure:2957: gcc -o conftest -g conftest.c >&5 +configure:2961: $? = 0 +configure:2983: result: +configure:3005: checking whether we are cross compiling +configure:3013: gcc -o conftest -g conftest.c >&5 +configure:3017: $? = 0 +configure:3024: ./conftest +configure:3028: $? = 0 +configure:3043: result: no +configure:3048: checking for suffix of object files +configure:3070: gcc -c -g conftest.c >&5 +configure:3074: $? = 0 +configure:3095: result: o +configure:3099: checking whether we are using the GNU C compiler +configure:3118: gcc -c -g conftest.c >&5 +configure:3118: $? = 0 +configure:3127: result: yes +configure:3136: checking whether gcc accepts -g +configure:3156: gcc -c -g conftest.c >&5 +configure:3156: $? = 0 +configure:3197: result: yes +configure:3214: checking for gcc option to accept ISO C89 +configure:3277: gcc -c -g conftest.c >&5 +configure:3277: $? = 0 +configure:3290: result: none needed +configure:3315: checking how to run the C preprocessor +configure:3346: gcc -E conftest.c +configure:3346: $? = 0 +configure:3360: gcc -E conftest.c conftest.c:11:28: fatal error: ac_nonexistent.h: No such file or directory #include <ac_nonexistent.h> ^ compilation terminated. -configure:3359: $? = 1 +configure:3360: $? = 1 configure: failed program was: | /* confdefs.h */ | #define PACKAGE_NAME "netgen" @@ -125,18 +125,18 @@ configure: failed program was: | #define PACKAGE_BUGREPORT "eda-dev@opencircuitdesign.com" | #define PACKAGE_URL "" | #define NETGEN_VERSION "1.5" -| #define NETGEN_REVISION "90" +| #define NETGEN_REVISION "98" | /* end confdefs.h. */ | #include <ac_nonexistent.h> -configure:3384: result: gcc -E -configure:3404: gcc -E conftest.c -configure:3404: $? = 0 -configure:3418: gcc -E conftest.c +configure:3385: result: gcc -E +configure:3405: gcc -E conftest.c +configure:3405: $? = 0 +configure:3419: gcc -E conftest.c conftest.c:11:28: fatal error: ac_nonexistent.h: No such file or directory #include <ac_nonexistent.h> ^ compilation terminated. -configure:3418: $? = 1 +configure:3419: $? = 1 configure: failed program was: | /* confdefs.h */ | #define PACKAGE_NAME "netgen" @@ -146,104 +146,104 @@ configure: failed program was: | #define PACKAGE_BUGREPORT "eda-dev@opencircuitdesign.com" | #define PACKAGE_URL "" | #define NETGEN_VERSION "1.5" -| #define NETGEN_REVISION "90" +| #define NETGEN_REVISION "98" | /* end confdefs.h. */ | #include <ac_nonexistent.h> -configure:3447: checking for library containing strerror -configure:3478: gcc -o conftest -g conftest.c >&5 -configure:3478: $? = 0 -configure:3495: result: none required -configure:3517: checking for a BSD-compatible install -configure:3585: result: /bin/install -c -configure:3639: checking for ranlib -configure:3655: found /bin/ranlib -configure:3666: result: ranlib -configure:3693: checking for gm4 -configure:3726: result: no -configure:3693: checking for gnum4 -configure:3726: result: no -configure:3693: checking for m4 -configure:3711: found /bin/m4 -configure:3723: result: /bin/m4 -configure:3764: checking for ld used by GCC -configure:3827: result: /bin/ld -configure:3834: checking if the linker (/bin/ld) is GNU ld +configure:3448: checking for library containing strerror +configure:3479: gcc -o conftest -g conftest.c >&5 +configure:3479: $? = 0 +configure:3496: result: none required +configure:3518: checking for a BSD-compatible install +configure:3586: result: /bin/install -c +configure:3640: checking for ranlib +configure:3656: found /bin/ranlib +configure:3667: result: ranlib +configure:3694: checking for gm4 +configure:3727: result: no +configure:3694: checking for gnum4 +configure:3727: result: no +configure:3694: checking for m4 +configure:3712: found /bin/m4 +configure:3724: result: /bin/m4 +configure:3765: checking for ld used by GCC +configure:3828: result: /bin/ld +configure:3835: checking if the linker (/bin/ld) is GNU ld GNU ld version 2.24 -configure:3846: result: yes -configure:3853: checking for grep that handles long lines and -e -configure:3911: result: /bin/grep -configure:3916: checking for egrep -configure:3978: result: /bin/grep -E -configure:3983: checking for ANSI C header files -configure:4003: gcc -c -g conftest.c >&5 -configure:4003: $? = 0 -configure:4076: gcc -o conftest -g conftest.c >&5 -configure:4076: $? = 0 -configure:4076: ./conftest -configure:4076: $? = 0 -configure:4087: result: yes -configure:4100: checking for sys/types.h -configure:4100: gcc -c -g conftest.c >&5 -configure:4100: $? = 0 -configure:4100: result: yes -configure:4100: checking for sys/stat.h -configure:4100: gcc -c -g conftest.c >&5 -configure:4100: $? = 0 -configure:4100: result: yes -configure:4100: checking for stdlib.h -configure:4100: gcc -c -g conftest.c >&5 -configure:4100: $? = 0 -configure:4100: result: yes -configure:4100: checking for string.h -configure:4100: gcc -c -g conftest.c >&5 -configure:4100: $? = 0 -configure:4100: result: yes -configure:4100: checking for memory.h -configure:4100: gcc -c -g conftest.c >&5 -configure:4100: $? = 0 -configure:4100: result: yes -configure:4100: checking for strings.h -configure:4100: gcc -c -g conftest.c >&5 -configure:4100: $? = 0 -configure:4100: result: yes -configure:4100: checking for inttypes.h -configure:4100: gcc -c -g conftest.c >&5 -configure:4100: $? = 0 -configure:4100: result: yes -configure:4100: checking for stdint.h -configure:4100: gcc -c -g conftest.c >&5 -configure:4100: $? = 0 -configure:4100: result: yes -configure:4100: checking for unistd.h -configure:4100: gcc -c -g conftest.c >&5 -configure:4100: $? = 0 -configure:4100: result: yes -configure:4116: checking size of void * -configure:4121: gcc -o conftest -g conftest.c >&5 -configure:4121: $? = 0 -configure:4121: ./conftest -configure:4121: $? = 0 -configure:4135: result: 8 -configure:4149: checking size of unsigned int -configure:4154: gcc -o conftest -g conftest.c >&5 -configure:4154: $? = 0 -configure:4154: ./conftest -configure:4154: $? = 0 -configure:4168: result: 4 -configure:4182: checking size of unsigned long -configure:4187: gcc -o conftest -g conftest.c >&5 -configure:4187: $? = 0 -configure:4187: ./conftest -configure:4187: $? = 0 -configure:4201: result: 8 -configure:4215: checking size of unsigned long long -configure:4220: gcc -o conftest -g conftest.c >&5 -configure:4220: $? = 0 -configure:4220: ./conftest -configure:4220: $? = 0 -configure:4234: result: 8 -configure:4245: checking whether byte ordering is bigendian -configure:4260: gcc -c -g conftest.c >&5 +configure:3847: result: yes +configure:3854: checking for grep that handles long lines and -e +configure:3912: result: /bin/grep +configure:3917: checking for egrep +configure:3979: result: /bin/grep -E +configure:3984: checking for ANSI C header files +configure:4004: gcc -c -g conftest.c >&5 +configure:4004: $? = 0 +configure:4077: gcc -o conftest -g conftest.c >&5 +configure:4077: $? = 0 +configure:4077: ./conftest +configure:4077: $? = 0 +configure:4088: result: yes +configure:4101: checking for sys/types.h +configure:4101: gcc -c -g conftest.c >&5 +configure:4101: $? = 0 +configure:4101: result: yes +configure:4101: checking for sys/stat.h +configure:4101: gcc -c -g conftest.c >&5 +configure:4101: $? = 0 +configure:4101: result: yes +configure:4101: checking for stdlib.h +configure:4101: gcc -c -g conftest.c >&5 +configure:4101: $? = 0 +configure:4101: result: yes +configure:4101: checking for string.h +configure:4101: gcc -c -g conftest.c >&5 +configure:4101: $? = 0 +configure:4101: result: yes +configure:4101: checking for memory.h +configure:4101: gcc -c -g conftest.c >&5 +configure:4101: $? = 0 +configure:4101: result: yes +configure:4101: checking for strings.h +configure:4101: gcc -c -g conftest.c >&5 +configure:4101: $? = 0 +configure:4101: result: yes +configure:4101: checking for inttypes.h +configure:4101: gcc -c -g conftest.c >&5 +configure:4101: $? = 0 +configure:4101: result: yes +configure:4101: checking for stdint.h +configure:4101: gcc -c -g conftest.c >&5 +configure:4101: $? = 0 +configure:4101: result: yes +configure:4101: checking for unistd.h +configure:4101: gcc -c -g conftest.c >&5 +configure:4101: $? = 0 +configure:4101: result: yes +configure:4117: checking size of void * +configure:4122: gcc -o conftest -g conftest.c >&5 +configure:4122: $? = 0 +configure:4122: ./conftest +configure:4122: $? = 0 +configure:4136: result: 8 +configure:4150: checking size of unsigned int +configure:4155: gcc -o conftest -g conftest.c >&5 +configure:4155: $? = 0 +configure:4155: ./conftest +configure:4155: $? = 0 +configure:4169: result: 4 +configure:4183: checking size of unsigned long +configure:4188: gcc -o conftest -g conftest.c >&5 +configure:4188: $? = 0 +configure:4188: ./conftest +configure:4188: $? = 0 +configure:4202: result: 8 +configure:4216: checking size of unsigned long long +configure:4221: gcc -o conftest -g conftest.c >&5 +configure:4221: $? = 0 +configure:4221: ./conftest +configure:4221: $? = 0 +configure:4235: result: 8 +configure:4246: checking whether byte ordering is bigendian +configure:4261: gcc -c -g conftest.c >&5 conftest.c:26:9: error: unknown type name 'not' not a universal capable compiler ^ @@ -251,7 +251,7 @@ conftest.c:26:15: error: expected '=', ',', ';', 'asm' or '__attribute__' before not a universal capable compiler ^ conftest.c:26:15: error: unknown type name 'universal' -configure:4260: $? = 1 +configure:4261: $? = 1 configure: failed program was: | /* confdefs.h */ | #define PACKAGE_NAME "netgen" @@ -261,7 +261,7 @@ configure: failed program was: | #define PACKAGE_BUGREPORT "eda-dev@opencircuitdesign.com" | #define PACKAGE_URL "" | #define NETGEN_VERSION "1.5" -| #define NETGEN_REVISION "90" +| #define NETGEN_REVISION "98" | #define STDC_HEADERS 1 | #define HAVE_SYS_TYPES_H 1 | #define HAVE_SYS_STAT_H 1 @@ -282,9 +282,9 @@ configure: failed program was: | #endif | typedef int dummy; | -configure:4305: gcc -c -g conftest.c >&5 -configure:4305: $? = 0 -configure:4323: gcc -c -g conftest.c >&5 +configure:4306: gcc -c -g conftest.c >&5 +configure:4306: $? = 0 +configure:4324: gcc -c -g conftest.c >&5 conftest.c: In function 'main': conftest.c:32:4: error: unknown type name 'not' not big endian @@ -292,7 +292,7 @@ conftest.c:32:4: error: unknown type name 'not' conftest.c:32:12: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'endian' not big endian ^ -configure:4323: $? = 1 +configure:4324: $? = 1 configure: failed program was: | /* confdefs.h */ | #define PACKAGE_NAME "netgen" @@ -302,7 +302,7 @@ configure: failed program was: | #define PACKAGE_BUGREPORT "eda-dev@opencircuitdesign.com" | #define PACKAGE_URL "" | #define NETGEN_VERSION "1.5" -| #define NETGEN_REVISION "90" +| #define NETGEN_REVISION "98" | #define STDC_HEADERS 1 | #define HAVE_SYS_TYPES_H 1 | #define HAVE_SYS_STAT_H 1 @@ -331,48 +331,48 @@ configure: failed program was: | ; | return 0; | } -configure:4451: result: no -configure:4470: checking for ANSI C header files -configure:4574: result: yes -configure:4586: checking for setenv -configure:4586: gcc -o conftest -g conftest.c >&5 -configure:4586: $? = 0 -configure:4586: result: yes -configure:4586: checking for putenv -configure:4586: gcc -o conftest -g conftest.c >&5 -configure:4586: $? = 0 -configure:4586: result: yes -configure:4596: checking for vfork -configure:4596: gcc -o conftest -g conftest.c >&5 -configure:4596: $? = 0 -configure:4596: result: yes -configure:4604: checking dirent.h usability -configure:4604: gcc -c -g conftest.c >&5 -configure:4604: $? = 0 -configure:4604: result: yes -configure:4604: checking dirent.h presence -configure:4604: gcc -E -x c conftest.c -configure:4604: $? = 0 -configure:4604: result: yes -configure:4604: checking for dirent.h -configure:4604: result: yes -configure:4617: checking limits.h usability -configure:4617: gcc -c -g conftest.c >&5 -configure:4617: $? = 0 -configure:4617: result: yes -configure:4617: checking limits.h presence -configure:4617: gcc -E -x c conftest.c -configure:4617: $? = 0 -configure:4617: result: yes -configure:4617: checking for limits.h -configure:4617: result: yes -configure:4630: checking param.h usability -configure:4630: gcc -c -g conftest.c >&5 +configure:4452: result: no +configure:4471: checking for ANSI C header files +configure:4575: result: yes +configure:4587: checking for setenv +configure:4587: gcc -o conftest -g conftest.c >&5 +configure:4587: $? = 0 +configure:4587: result: yes +configure:4587: checking for putenv +configure:4587: gcc -o conftest -g conftest.c >&5 +configure:4587: $? = 0 +configure:4587: result: yes +configure:4597: checking for vfork +configure:4597: gcc -o conftest -g conftest.c >&5 +configure:4597: $? = 0 +configure:4597: result: yes +configure:4605: checking dirent.h usability +configure:4605: gcc -c -g conftest.c >&5 +configure:4605: $? = 0 +configure:4605: result: yes +configure:4605: checking dirent.h presence +configure:4605: gcc -E -x c conftest.c +configure:4605: $? = 0 +configure:4605: result: yes +configure:4605: checking for dirent.h +configure:4605: result: yes +configure:4618: checking limits.h usability +configure:4618: gcc -c -g conftest.c >&5 +configure:4618: $? = 0 +configure:4618: result: yes +configure:4618: checking limits.h presence +configure:4618: gcc -E -x c conftest.c +configure:4618: $? = 0 +configure:4618: result: yes +configure:4618: checking for limits.h +configure:4618: result: yes +configure:4631: checking param.h usability +configure:4631: gcc -c -g conftest.c >&5 conftest.c:63:19: fatal error: param.h: No such file or directory #include <param.h> ^ compilation terminated. -configure:4630: $? = 1 +configure:4631: $? = 1 configure: failed program was: | /* confdefs.h */ | #define PACKAGE_NAME "netgen" @@ -382,7 +382,7 @@ configure: failed program was: | #define PACKAGE_BUGREPORT "eda-dev@opencircuitdesign.com" | #define PACKAGE_URL "" | #define NETGEN_VERSION "1.5" -| #define NETGEN_REVISION "90" +| #define NETGEN_REVISION "98" | #define STDC_HEADERS 1 | #define HAVE_SYS_TYPES_H 1 | #define HAVE_SYS_STAT_H 1 @@ -437,14 +437,14 @@ configure: failed program was: | # include <unistd.h> | #endif | #include <param.h> -configure:4630: result: no -configure:4630: checking param.h presence -configure:4630: gcc -E -x c conftest.c +configure:4631: result: no +configure:4631: checking param.h presence +configure:4631: gcc -E -x c conftest.c conftest.c:30:19: fatal error: param.h: No such file or directory #include <param.h> ^ compilation terminated. -configure:4630: $? = 1 +configure:4631: $? = 1 configure: failed program was: | /* confdefs.h */ | #define PACKAGE_NAME "netgen" @@ -454,7 +454,7 @@ configure: failed program was: | #define PACKAGE_BUGREPORT "eda-dev@opencircuitdesign.com" | #define PACKAGE_URL "" | #define NETGEN_VERSION "1.5" -| #define NETGEN_REVISION "90" +| #define NETGEN_REVISION "98" | #define STDC_HEADERS 1 | #define HAVE_SYS_TYPES_H 1 | #define HAVE_SYS_STAT_H 1 @@ -476,52 +476,55 @@ configure: failed program was: | #define HAVE_LIMITS_H 1 | /* end confdefs.h. */ | #include <param.h> -configure:4630: result: no -configure:4630: checking for param.h -configure:4630: result: no -configure:4641: checking for va_copy -configure:4659: gcc -o conftest -g conftest.c >&5 -configure:4659: $? = 0 -configure:4668: result: yes -configure:4676: checking for __va_copy -configure:4694: gcc -o conftest -g conftest.c >&5 -configure:4694: $? = 0 -configure:4703: result: yes -configure:4834: checking for tclConfig.sh -configure:4906: result: /usr/lib64/tclConfig.sh -configure:4922: checking for tkConfig.sh -configure:4995: result: /usr/lib64/tkConfig.sh -configure:5128: checking for wish executable -configure:5155: result: /usr/bin/wish -configure:5161: checking for tclsh executable -configure:5187: result: /usr/bin/tclsh -configure:5310: checking for X -configure:5449: gcc -o conftest -g conftest.c -lX11 >&5 -configure:5449: $? = 0 -configure:5499: result: libraries , headers -configure:5598: gcc -o conftest -g conftest.c -lX11 >&5 -configure:5598: $? = 0 -configure:5696: checking for gethostbyname -configure:5696: gcc -o conftest -g conftest.c >&5 -configure:5696: $? = 0 -configure:5696: result: yes -configure:5793: checking for connect -configure:5793: gcc -o conftest -g conftest.c >&5 -configure:5793: $? = 0 -configure:5793: result: yes -configure:5842: checking for remove -configure:5842: gcc -o conftest -g conftest.c >&5 -configure:5842: $? = 0 -configure:5842: result: yes -configure:5891: checking for shmat -configure:5891: gcc -o conftest -g conftest.c >&5 -configure:5891: $? = 0 -configure:5891: result: yes -configure:5949: checking for IceConnectionNumber in -lICE -configure:5974: gcc -o conftest -g conftest.c -lICE >&5 -configure:5974: $? = 0 -configure:5983: result: yes -configure:6704: creating ./config.status +configure:4631: result: no +configure:4631: checking for param.h +configure:4631: result: no +configure:4644: checking for python3 +configure:4660: found /bin/python3 +configure:4672: result: yes +configure:4681: checking for va_copy +configure:4699: gcc -o conftest -g conftest.c >&5 +configure:4699: $? = 0 +configure:4708: result: yes +configure:4716: checking for __va_copy +configure:4734: gcc -o conftest -g conftest.c >&5 +configure:4734: $? = 0 +configure:4743: result: yes +configure:4874: checking for tclConfig.sh +configure:4946: result: /usr/lib64/tclConfig.sh +configure:4962: checking for tkConfig.sh +configure:5035: result: /usr/lib64/tkConfig.sh +configure:5168: checking for wish executable +configure:5195: result: /usr/bin/wish +configure:5201: checking for tclsh executable +configure:5227: result: /usr/bin/tclsh +configure:5350: checking for X +configure:5489: gcc -o conftest -g conftest.c -lX11 >&5 +configure:5489: $? = 0 +configure:5539: result: libraries , headers +configure:5638: gcc -o conftest -g conftest.c -lX11 >&5 +configure:5638: $? = 0 +configure:5736: checking for gethostbyname +configure:5736: gcc -o conftest -g conftest.c >&5 +configure:5736: $? = 0 +configure:5736: result: yes +configure:5833: checking for connect +configure:5833: gcc -o conftest -g conftest.c >&5 +configure:5833: $? = 0 +configure:5833: result: yes +configure:5882: checking for remove +configure:5882: gcc -o conftest -g conftest.c >&5 +configure:5882: $? = 0 +configure:5882: result: yes +configure:5931: checking for shmat +configure:5931: gcc -o conftest -g conftest.c >&5 +configure:5931: $? = 0 +configure:5931: result: yes +configure:5989: checking for IceConnectionNumber in -lICE +configure:6014: gcc -o conftest -g conftest.c -lICE >&5 +configure:6014: $? = 0 +configure:6023: result: yes +configure:6745: creating ./config.status ## ---------------------- ## ## Running config.status. ## @@ -538,8 +541,8 @@ generated by GNU Autoconf 2.69. Invocation command line was on stravinsky -config.status:783: creating defs.mak -config.status:886: WARNING: 'defs.mak.in' seems to ignore the --datarootdir setting +config.status:784: creating defs.mak +config.status:887: WARNING: 'defs.mak.in' seems to ignore the --datarootdir setting ## ---------------- ## ## Cache variables. ## @@ -600,6 +603,7 @@ ac_cv_path_LD=/bin/ld ac_cv_path_M4=/bin/m4 ac_cv_path_install='/bin/install -c' ac_cv_prog_CPP='gcc -E' +ac_cv_prog_HAVE_PYTHON3=yes ac_cv_prog_ac_ct_CC=gcc ac_cv_prog_ac_ct_RANLIB=ranlib ac_cv_prog_cc_c89= @@ -621,7 +625,7 @@ CC='gcc' CFLAGS='-g -m64 -fPIC' CPP='gcc -E -x c' CPPFLAGS='' -DEFS='-DPACKAGE_NAME=\"netgen\" -DPACKAGE_TARNAME=\"netgen\" -DPACKAGE_VERSION=\"1.3\" -DPACKAGE_STRING=\"netgen\ 1.3\" -DPACKAGE_BUGREPORT=\"eda-dev@opencircuitdesign.com\" -DPACKAGE_URL=\"\" -DNETGEN_VERSION=\"1.5\" -DNETGEN_REVISION=\"90\" -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DSIZEOF_VOID_P=8 -DSIZEOF_UNSIGNED_INT=4 -DSIZEOF_UNSIGNED_LONG=8 -DSIZEOF_UNSIGNED_LONG_LONG=8 -DSTDC_HEADERS=1 -DHAVE_SETENV=1 -DHAVE_PUTENV=1 -DHAVE_DIRENT_H=1 -DHAVE_LIMITS_H=1 -DHAVE_VA_COPY=1 -DHAVE___VA_COPY=1 -DDBUG_OFF=1 -DTCL_NETGEN=1 -Dlinux=1 -DSYSV=1 -DISC=1' +DEFS='-DPACKAGE_NAME=\"netgen\" -DPACKAGE_TARNAME=\"netgen\" -DPACKAGE_VERSION=\"1.3\" -DPACKAGE_STRING=\"netgen\ 1.3\" -DPACKAGE_BUGREPORT=\"eda-dev@opencircuitdesign.com\" -DPACKAGE_URL=\"\" -DNETGEN_VERSION=\"1.5\" -DNETGEN_REVISION=\"98\" -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DSIZEOF_VOID_P=8 -DSIZEOF_UNSIGNED_INT=4 -DSIZEOF_UNSIGNED_LONG=8 -DSIZEOF_UNSIGNED_LONG_LONG=8 -DSTDC_HEADERS=1 -DHAVE_SETENV=1 -DHAVE_PUTENV=1 -DHAVE_DIRENT_H=1 -DHAVE_LIMITS_H=1 -DHAVE_VA_COPY=1 -DHAVE___VA_COPY=1 -DDBUG_OFF=1 -DTCL_NETGEN=1 -Dlinux=1 -DSYSV=1 -DISC=1' DEPEND_FLAG='-MM' ECHO_C='' ECHO_N='printf' @@ -630,6 +634,7 @@ EGREP='/bin/grep -E' EXEEXT='' EXTRA_LIB_SPECS='-ldl' GREP='/bin/grep' +HAVE_PYTHON3='yes' INC_SPECS='' INSTALL_DATA='${INSTALL} -m 644' INSTALL_PROGRAM='${INSTALL}' @@ -736,7 +741,7 @@ unused='' #define PACKAGE_BUGREPORT "eda-dev@opencircuitdesign.com" #define PACKAGE_URL "" #define NETGEN_VERSION "1.5" -#define NETGEN_REVISION "90" +#define NETGEN_REVISION "98" #define STDC_HEADERS 1 #define HAVE_SYS_TYPES_H 1 #define HAVE_SYS_STAT_H 1 diff --git a/scripts/config.status b/scripts/config.status index 61bdda3..bee0993 100755 --- a/scripts/config.status +++ b/scripts/config.status @@ -624,6 +624,7 @@ S["X_LIBS"]="" S["X_PRE_LIBS"]=" -lSM -lICE" S["X_CFLAGS"]="" S["XMKMF"]="" +S["HAVE_PYTHON3"]="yes" S["EGREP"]="/bin/grep -E" S["GREP"]="/bin/grep" S["M4"]="/bin/m4" @@ -659,7 +660,7 @@ S["ECHO_T"]="" S["ECHO_N"]="-n" S["ECHO_C"]="" S["DEFS"]="-DPACKAGE_NAME=\\\"netgen\\\" -DPACKAGE_TARNAME=\\\"netgen\\\" -DPACKAGE_VERSION=\\\"1.3\\\" -DPACKAGE_STRING=\\\"netgen\\ 1.3\\\" -DPACKAGE_BUGREPORT=\\\"eda-dev@open"\ -"circuitdesign.com\\\" -DPACKAGE_URL=\\\"\\\" -DNETGEN_VERSION=\\\"1.5\\\" -DNETGEN_REVISION=\\\"90\\\" -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -"\ +"circuitdesign.com\\\" -DPACKAGE_URL=\\\"\\\" -DNETGEN_VERSION=\\\"1.5\\\" -DNETGEN_REVISION=\\\"98\\\" -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -"\ "DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DSIZEOF_VOID_P=8 -D"\ "SIZEOF_UNSIGNED_INT=4 -DSIZEOF_UNSIGNED_LONG=8 -DSIZEOF_UNSIGNED_LONG_LONG=8 -DSTDC_HEADERS=1 -DHAVE_SETENV=1 -DHAVE_PUTENV=1 -DHAVE_DIRENT_H=1 -DHA"\ "VE_LIMITS_H=1 -DHAVE_VA_COPY=1 -DHAVE___VA_COPY=1 -DDBUG_OFF=1 -DTCL_NETGEN=1 -Dlinux=1 -DSYSV=1 -DISC=1" diff --git a/scripts/configure b/scripts/configure index 715ebe7..e4e1a0d 100755 --- a/scripts/configure +++ b/scripts/configure @@ -664,6 +664,7 @@ X_LIBS X_PRE_LIBS X_CFLAGS XMKMF +HAVE_PYTHON3 EGREP GREP M4 @@ -4638,6 +4639,45 @@ fi done +# Extract the first word of "python3", so it can be a program name with args. +set dummy python3; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_HAVE_PYTHON3+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$HAVE_PYTHON3"; then + ac_cv_prog_HAVE_PYTHON3="$HAVE_PYTHON3" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_HAVE_PYTHON3="yes" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_prog_HAVE_PYTHON3" && ac_cv_prog_HAVE_PYTHON3="no" +fi +fi +HAVE_PYTHON3=$ac_cv_prog_HAVE_PYTHON3 +if test -n "$HAVE_PYTHON3"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $HAVE_PYTHON3" >&5 +$as_echo "$HAVE_PYTHON3" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for va_copy" >&5 $as_echo_n "checking for va_copy... " >&6; } if ${ac_cv_c_va_copy+:} false; then : @@ -6546,6 +6586,7 @@ fi + ac_config_files="$ac_config_files defs.mak" @@ -7737,6 +7778,20 @@ else echo fi +${ECHO_N} "Python3: " +if test "x${HAVE_PYTHON3}" == "x"; then + echo "no" + echo + echo " Without Python3, you cannot run the GUI interface to netgen." + echo " This is purely optional, as it contains no features not" + echo " available in the command-line (Tcl/Tk) version. If you want" + echo " python3 support, make sure that you have installed python3" + echo " on your system." + echo +else + echo "yes" +fi + echo "-----------------------------------------------------------" echo diff --git a/scripts/configure.in b/scripts/configure.in index 7f1ab08..24080f2 100644 --- a/scripts/configure.in +++ b/scripts/configure.in @@ -163,6 +163,9 @@ AC_CHECK_HEADERS(limits.h) dnl Check for <param.h> AC_CHECK_HEADERS(param.h) +dnl Check for Python3 +AC_CHECK_PROG(HAVE_PYTHON3, python3, yes, no) + dnl Check for va_copy AC_CACHE_CHECK([for va_copy], ac_cv_c_va_copy, AC_TRY_LINK( @@ -1274,6 +1277,7 @@ AC_SUBST(gr_cflags) AC_SUBST(gr_dflags) AC_SUBST(gr_libs) AC_SUBST(gr_srcs) +AC_SUBST(HAVE_PYTHON3) AC_SUBST(X_LIBS) AC_SUBST(X_CFLAGS) @@ -1314,6 +1318,20 @@ else echo fi +${ECHO_N} "Python3: " +if test "x${HAVE_PYTHON3}" == "x"; then + echo "no" + echo + echo " Without Python3, you cannot run the GUI interface to netgen." + echo " This is purely optional, as it contains no features not" + echo " available in the command-line (Tcl/Tk) version. If you want" + echo " python3 support, make sure that you have installed python3" + echo " on your system." + echo +else + echo "yes" +fi + echo "-----------------------------------------------------------" echo diff --git a/scripts/defs.mak b/scripts/defs.mak index dc4bf68..5e7a5c1 100644 --- a/scripts/defs.mak +++ b/scripts/defs.mak @@ -33,6 +33,7 @@ MANDIR = ${mandir} LIBDIR = ${libdir} DOCDIR = ${libdir}/netgen/doc TCLDIR = ${libdir}/netgen/tcl +PYDIR = ${libdir}/netgen/python MAIN_EXTRA_LIBS = ${NETGENDIR}/tcltk/libtcltk.o LD_EXTRA_LIBS = @@ -66,8 +67,8 @@ CPP = gcc -E -x c CXX = @CXX@ CPPFLAGS = -I. -I${NETGENDIR} -DFLAGS = -DCAD_DIR=\"${LIBDIR}\" -DTCL_DIR=\"${TCLDIR}\" -DUSE_TCL_STUBS -DUSE_TK_STUBS -DPACKAGE_NAME=\"netgen\" -DPACKAGE_TARNAME=\"netgen\" -DPACKAGE_VERSION=\"1.3\" -DPACKAGE_STRING=\"netgen\ 1.3\" -DPACKAGE_BUGREPORT=\"eda-dev@opencircuitdesign.com\" -DPACKAGE_URL=\"\" -DNETGEN_VERSION=\"1.5\" -DNETGEN_REVISION=\"90\" -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DSIZEOF_VOID_P=8 -DSIZEOF_UNSIGNED_INT=4 -DSIZEOF_UNSIGNED_LONG=8 -DSIZEOF_UNSIGNED_LONG_LONG=8 -DSTDC_HEADERS=1 -DHAVE_SETENV=1 -DHAVE_PUTENV=1 -DHAVE_DIRENT_H=1 -DHAVE_LIMITS_H=1 -DHAVE_VA_COPY=1 -DHAVE___VA_COPY=1 -DDBUG_OFF=1 -DTCL_NETGEN=1 -Dlinux=1 -DSYSV=1 -DISC=1 -DSHDLIB_EXT=\".so\" -DNDEBUG -DFLAGS_NOSTUB = -DCAD_DIR=\"${LIBDIR}\" -DTCL_DIR=\"${TCLDIR}\" -DPACKAGE_NAME=\"netgen\" -DPACKAGE_TARNAME=\"netgen\" -DPACKAGE_VERSION=\"1.3\" -DPACKAGE_STRING=\"netgen\ 1.3\" -DPACKAGE_BUGREPORT=\"eda-dev@opencircuitdesign.com\" -DPACKAGE_URL=\"\" -DNETGEN_VERSION=\"1.5\" -DNETGEN_REVISION=\"90\" -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DSIZEOF_VOID_P=8 -DSIZEOF_UNSIGNED_INT=4 -DSIZEOF_UNSIGNED_LONG=8 -DSIZEOF_UNSIGNED_LONG_LONG=8 -DSTDC_HEADERS=1 -DHAVE_SETENV=1 -DHAVE_PUTENV=1 -DHAVE_DIRENT_H=1 -DHAVE_LIMITS_H=1 -DHAVE_VA_COPY=1 -DHAVE___VA_COPY=1 -DDBUG_OFF=1 -DTCL_NETGEN=1 -Dlinux=1 -DSYSV=1 -DISC=1 -DSHDLIB_EXT=\".so\" -DNDEBUG +DFLAGS = -DCAD_DIR=\"${LIBDIR}\" -DTCL_DIR=\"${TCLDIR}\" -DUSE_TCL_STUBS -DUSE_TK_STUBS -DPACKAGE_NAME=\"netgen\" -DPACKAGE_TARNAME=\"netgen\" -DPACKAGE_VERSION=\"1.3\" -DPACKAGE_STRING=\"netgen\ 1.3\" -DPACKAGE_BUGREPORT=\"eda-dev@opencircuitdesign.com\" -DPACKAGE_URL=\"\" -DNETGEN_VERSION=\"1.5\" -DNETGEN_REVISION=\"98\" -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DSIZEOF_VOID_P=8 -DSIZEOF_UNSIGNED_INT=4 -DSIZEOF_UNSIGNED_LONG=8 -DSIZEOF_UNSIGNED_LONG_LONG=8 -DSTDC_HEADERS=1 -DHAVE_SETENV=1 -DHAVE_PUTENV=1 -DHAVE_DIRENT_H=1 -DHAVE_LIMITS_H=1 -DHAVE_VA_COPY=1 -DHAVE___VA_COPY=1 -DDBUG_OFF=1 -DTCL_NETGEN=1 -Dlinux=1 -DSYSV=1 -DISC=1 -DSHDLIB_EXT=\".so\" -DNDEBUG +DFLAGS_NOSTUB = -DCAD_DIR=\"${LIBDIR}\" -DTCL_DIR=\"${TCLDIR}\" -DPACKAGE_NAME=\"netgen\" -DPACKAGE_TARNAME=\"netgen\" -DPACKAGE_VERSION=\"1.3\" -DPACKAGE_STRING=\"netgen\ 1.3\" -DPACKAGE_BUGREPORT=\"eda-dev@opencircuitdesign.com\" -DPACKAGE_URL=\"\" -DNETGEN_VERSION=\"1.5\" -DNETGEN_REVISION=\"98\" -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DSIZEOF_VOID_P=8 -DSIZEOF_UNSIGNED_INT=4 -DSIZEOF_UNSIGNED_LONG=8 -DSIZEOF_UNSIGNED_LONG_LONG=8 -DSTDC_HEADERS=1 -DHAVE_SETENV=1 -DHAVE_PUTENV=1 -DHAVE_DIRENT_H=1 -DHAVE_LIMITS_H=1 -DHAVE_VA_COPY=1 -DHAVE___VA_COPY=1 -DDBUG_OFF=1 -DTCL_NETGEN=1 -Dlinux=1 -DSYSV=1 -DISC=1 -DSHDLIB_EXT=\".so\" -DNDEBUG CFLAGS = -g -m64 -fPIC -fPIC DEPEND_FILE = Depend diff --git a/scripts/defs.mak.in b/scripts/defs.mak.in index eb5acb9..1c2e9ae 100644 --- a/scripts/defs.mak.in +++ b/scripts/defs.mak.in @@ -33,6 +33,7 @@ MANDIR = ${mandir} LIBDIR = ${libdir} DOCDIR = ${libdir}/netgen/doc TCLDIR = ${libdir}/netgen/tcl +PYDIR = ${libdir}/netgen/python MAIN_EXTRA_LIBS = @extra_libs@ LD_EXTRA_LIBS = @ld_extra_libs@ diff --git a/tcltk/Makefile b/tcltk/Makefile index afe9e61..861e185 100644 --- a/tcltk/Makefile +++ b/tcltk/Makefile @@ -34,6 +34,7 @@ netgen.tcl: netgen.tcl.in netgen.sh: netgen.sh.in sed -e 's%TCL_DIR%${TCLDIR}%g' \ + -e 's%PY_DIR%${PYDIR}%g' \ -e 's%TCLLIB_DIR%${TCL_LIB_DIR}%g' \ -e 's%WISH_EXE%${WISH_EXE}%g' \ netgen.sh.in > netgen.sh diff --git a/tcltk/netgen.sh.in b/tcltk/netgen.sh.in index 27bdc8b..59aa4ae 100755 --- a/tcltk/netgen.sh.in +++ b/tcltk/netgen.sh.in @@ -14,6 +14,7 @@ TKCON=true BATCH= +GUI= NETGEN_WISH=WISH_EXE export NETGEN_WISH @@ -29,6 +30,7 @@ for i in "$@" ; do case $i in -noc*) TKCON=;; -bat*) BATCH=true; TKCON=;; + -gui) GUI=true; TKCON=;; *) arglist="$arglist${arglist:+ }\"${i//\"/\\\"}\"";; esac done @@ -40,7 +42,10 @@ if [ $TKCON ]; then -slave "package require Tk; set argc $#; set argv [list $arglist]; \ source TCL_DIR/netgen.tcl" -else +# Run the Python LVS manager GUI + +elif [ $GUI ]; then + exec PY_DIR/lvs_manager.py $@ # # Run the stand-in for wish (netgenexec), which acts exactly like "wish" @@ -49,6 +54,7 @@ else # capable of sourcing the startup script. # +else exec TCL_DIR/netgenexec -- "$@" fi