#!/usr/bin/python3
# This file create the gui to install code model in the ngspice.
#08.June.2020 - Bladen Martin - Added if-else constructs to make code OS independent#
import os
import shutil
import subprocess
import sys
from configparser import SafeConfigParser
from PyQt4 import QtCore
from PyQt4 import QtGui
from Appconfig import Appconfig
from createKicadLibrary import AutoSchematic
from model_generation import ModelGeneration
class Mainwindow(QtGui.QWidget):
def __init__(self):
# super(Mainwindow, self).__init__()
QtGui.QMainWindow.__init__(self)
print("Initializing..........")
self.home = os.path.expanduser("~")
# Reading all variables from config.ini
self.parser = SafeConfigParser()
self.parser.read(
os.path.join(self.home, os.path.join('.nghdl', 'config.ini'))
)
self.ngspice_home = self.parser.get('NGSPICE', 'NGSPICE_HOME')
self.release_dir = self.parser.get('NGSPICE', 'RELEASE')
self.src_home = self.parser.get('SRC', 'SRC_HOME')
self.licensefile = self.parser.get('SRC', 'LICENSE')
# Printing LICENCE file on terminal
fileopen = open(self.licensefile, 'r')
print(fileopen.read())
fileopen.close()
self.file_list = [] # to keep the supporting files
self.errorFlag = False # to keep the check of "make install" errors
self.initUI()
def initUI(self):
self.uploadbtn = QtGui.QPushButton('Upload')
self.uploadbtn.clicked.connect(self.uploadModel)
self.exitbtn = QtGui.QPushButton('Exit')
self.exitbtn.clicked.connect(self.closeWindow)
self.browsebtn = QtGui.QPushButton('Browse')
self.browsebtn.clicked.connect(self.browseFile)
self.addbtn = QtGui.QPushButton('Add Files')
self.addbtn.clicked.connect(self.addFiles)
self.removebtn = QtGui.QPushButton('Remove Files')
self.removebtn.clicked.connect(self.removeFiles)
self.ledit = QtGui.QLineEdit(self)
self.sedit = QtGui.QTextEdit(self)
self.process = QtCore.QProcess(self)
self.termedit = QtGui.QTextEdit(self)
self.termedit.setReadOnly(1)
pal = QtGui.QPalette()
bgc = QtGui.QColor(0, 0, 0)
pal.setColor(QtGui.QPalette.Base, bgc)
self.termedit.setPalette(pal)
self.termedit.setStyleSheet("QTextEdit {color:white}")
# Creating gridlayout
grid = QtGui.QGridLayout()
grid.setSpacing(5)
grid.addWidget(self.ledit, 1, 0)
grid.addWidget(self.browsebtn, 1, 1)
grid.addWidget(self.sedit, 2, 0, 4, 1)
grid.addWidget(self.addbtn, 2, 1)
grid.addWidget(self.removebtn, 3, 1)
grid.addWidget(self.termedit, 6, 0, 10, 1)
grid.addWidget(self.uploadbtn, 17, 0)
grid.addWidget(self.exitbtn, 17, 1)
self.setLayout(grid)
self.setGeometry(300, 300, 600, 600)
self.setWindowTitle("Ngspice Digital Model Creator")
# self.setWindowIcon(QtGui.QIcon('logo.png'))
self.show()
def closeWindow(self):
try:
self.process.close()
except BaseException:
pass
print("Close button clicked")
sys.exit()
def browseFile(self):
print("Browse button clicked")
self.filename = QtGui.QFileDialog.getOpenFileName(
self, 'Open File', '.')
self.ledit.setText(self.filename)
print("Vhdl file uploaded to process :", self.filename)
def addFiles(self):
print("Starts adding supporting files")
title = self.addbtn.text()
for file in QtGui.QFileDialog.getOpenFileNames(self, title):
self.sedit.append(str(file))
self.file_list.append(file)
print("Supporting Files are :", self.file_list)
def removeFiles(self):
self.fileRemover = FileRemover(self)
# Check extensions of all supporting files
def checkSupportFiles(self):
nonvhdl_count = 0
for file in self.file_list:
extension = os.path.splitext(str(file))[1]
if extension != ".vhdl":
nonvhdl_count += 1
self.file_list.remove(file)
if nonvhdl_count > 0:
QtGui.QMessageBox.critical(
self, 'Critical', '''Important Message.
Supporting files should be .vhdl file '''
)
def createModelDirectory(self):
print("Create Model Directory Called")
self.digital_home = self.parser.get('NGSPICE', 'DIGITAL_MODEL')
os.chdir(self.digital_home)
print("Current Working Directory Changed to", os.getcwd())
self.modelname = os.path.basename(str(self.filename)).split('.')[0]
print("Model to be created :", self.modelname)
# Looking if model directory is present or not
if os.path.isdir(self.modelname):
print("Model Already present")
ret = QtGui.QMessageBox.warning(
self, "Warning",
"This model already exist. Do you want to " +
"overwrite it?
If yes press ok, else cancel it and " +
"change the name of your vhdl file.",
QtGui.QMessageBox.Ok, QtGui.QMessageBox.Cancel
)
if ret == QtGui.QMessageBox.Ok:
print("Overwriting existing model " + self.modelname)
#08.June.2020 - BM - Delete existing model directory
if os.name == 'nt':
cmd = "rmdir " + self.modelname + "/s /q"
else:
cmd = "rm -rf " + self.modelname
# process = subprocess.Popen(
# cmd, stdout=subprocess.PIPE,
# stderr=subprocess.PIPE, shell=True
# )
subprocess.call(cmd, shell=True)
os.mkdir(self.modelname)
else:
print("Exiting application")
sys.exit()
else:
print("Creating model " + self.modelname + " directory")
os.mkdir(self.modelname)
def addingModelInModpath(self):
print("Adding Model " + self.modelname +
" in Modpath file " + self.digital_home)
# Adding name of model in the modpath file
# Check if the string is already in the file
with open(self.digital_home + "/modpath.lst", 'r+') as f:
flag = 0
for line in f:
if line.strip() == self.modelname:
print("Found model "+self.modelname+" in the modpath.lst")
flag = 1
break
if flag == 0:
print("Adding model name "+self.modelname+" into modpath.lst")
f.write(self.modelname + "\n")
else:
print("Model name is already into modpath.lst")
def createModelFiles(self):
print("Create Model Files Called")
os.chdir(self.cur_dir)
print("Current Working directory changed to " + self.cur_dir)
# Generate model corresponding to the uploaded VHDL file
model = ModelGeneration(str(self.ledit.text()))
model.readPortInfo()
model.createCfuncModFile()
model.createIfSpecFile()
model.createTestbench()
model.createServerScript()
model.createSockScript()
# Moving file to model directory
path = os.path.join(self.digital_home, self.modelname)
shutil.move("cfunc.mod", path)
shutil.move("ifspec.ifs", path)
# Creating directory inside model directoy
print("Creating DUT directory at " + os.path.join(path, "DUTghdl"))
os.mkdir(path + "/DUTghdl/")
print("Copying required file to DUTghdl directory")
shutil.move("connection_info.txt", path + "/DUTghdl/")
shutil.move("start_server.sh", path + "/DUTghdl/")
shutil.move("sock_pkg_create.sh", path + "/DUTghdl/")
shutil.move(self.modelname + "_tb.vhdl", path + "/DUTghdl/")
shutil.copy(str(self.filename), path + "/DUTghdl/")
shutil.copy(os.path.join(self.home, self.src_home) +
"/src/ghdlserver/compile.sh", path + "/DUTghdl/")
shutil.copy(os.path.join(self.home, self.src_home) +
"/src/ghdlserver/uthash.h", path + "/DUTghdl/")
shutil.copy(os.path.join(self.home, self.src_home) +
"/src/ghdlserver/ghdlserver.c", path + "/DUTghdl/")
shutil.copy(os.path.join(self.home, self.src_home) +
"/src/ghdlserver/ghdlserver.h", path + "/DUTghdl/")
shutil.copy(os.path.join(self.home, self.src_home) +
"/src/ghdlserver/Utility_Package.vhdl", path + "/DUTghdl/")
shutil.copy(os.path.join(self.home, self.src_home) +
"/src/ghdlserver/Vhpi_Package.vhdl", path + "/DUTghdl/")
#08.June.2020 - BM - If OS is Windows, copy C library libws2_32.a to DUTghl be linked with server by GHDL
if os.name == 'nt':
shutil.copy(os.path.join(self.home, self.src_home) +
"/src/ghdlserver/libws2_32.a", path + "/DUTghdl/")
for file in self.file_list:
shutil.copy(str(file), path + "/DUTghdl/")
os.chdir(path + "/DUTghdl")
#08.June.2020 - BM - Run following commands as per OS. Use bash.exe provided by MSYS for Windows
if os.name == 'nt':
self.msys_bin = self.parser.get('COMPILER', 'MSYS_HOME') #path to msys bin directory where bash is located
subprocess.call(self.msys_bin+"/bash.exe " +
path + "/DUTghdl/compile.sh", shell=True)
subprocess.call(self.msys_bin+"/bash.exe -c " +
"'chmod a+x start_server.sh'", shell=True)
subprocess.call(self.msys_bin+"/bash.exe -c " +
"'chmod a+x sock_pkg_create.sh'", shell=True)
else:
subprocess.call("bash " + path + "/DUTghdl/compile.sh", shell=True)
subprocess.call("chmod a+x start_server.sh", shell=True)
subprocess.call("chmod a+x sock_pkg_create.sh", shell=True)
os.remove("compile.sh")
os.remove("ghdlserver.c")
# os.remove("ghdlserver.h")
# os.remove("Utility_Package.vhdl")
# os.remove("Vhpi_Package.vhdl")
# Slot to redirect stdout and stderr to window console
@QtCore.pyqtSlot()
def readAllStandard(self):
self.termedit.append(
str(self.process.readAllStandardOutput().data(), encoding='utf-8')
)
stderror = self.process.readAllStandardError()
if stderror.toUpper().contains("ERROR"):
self.errorFlag = True
self.termedit.append(str(stderror.data(), encoding='utf-8'))
def runMake(self):
print("run Make Called")
self.release_home = self.parser.get('NGSPICE', 'RELEASE')
#08.June.2020 - BM - Changed make location to .../ngspice-nghdl/release/src/xspice/icm
path_icm = os.path.join(self.release_home, "src/xspice/icm")
print(path_icm)
os.chdir(path_icm)
try:
#08.June.2020 - BM - Use make.exe provided by MSYS for Windows
if os.name == 'nt':
self.msys_bin = self.parser.get('COMPILER', 'MSYS_HOME') #path to msys bin directory where make is located
cmd = self.msys_bin+"\make.exe"
else:
cmd = " make"
print("Running Make command in " + path_icm)
path = os.getcwd() # noqa
self.process = QtCore.QProcess(self)
self.process.start(cmd)
print("make command process pid ---------- >", self.process.pid())
except BaseException:
print("There is error in 'make' ")
sys.exit()
def runMakeInstall(self):
print("run Make Install Called")
try:
if os.name == 'nt':
self.msys_bin = self.parser.get('COMPILER', 'MSYS_HOME')
cmd = self.msys_bin+"\make.exe install"
else:
cmd = " make install"
print("Running Make Install")
path = os.getcwd() # noqa
try:
self.process.close()
except BaseException:
pass
self.process = QtCore.QProcess(self)
self.process.start(cmd)
self.process.finished.connect(self.createSchematicLib)
QtCore.QObject.connect(
self.process, QtCore.SIGNAL("readyReadStandardOutput()"),
self, QtCore.SLOT("readAllStandard()")
)
os.chdir(self.cur_dir)
except BaseException:
print("There is error in 'make install' ")
sys.exit()
def createSchematicLib(self):
if Appconfig.esimFlag == 1:
if not self.errorFlag:
print('Creating library files................................')
schematicLib = AutoSchematic(self.modelname)
schematicLib.createKicadLibrary()
else:
QtGui.QMessageBox.critical(
self, 'Error', '''Cannot create Schematic Library of ''' +
'''your model. Resolve the errors shown on ''' +
'''console of NGHDL window. '''
)
else:
QtGui.QMessageBox.information(
self, 'Message', '''Important Message
''' +
'''To create Schematic Library of your model, ''' +
'''use NGHDL through eSim '''
)
def uploadModel(self):
print("Upload button clicked")
try:
self.process.close()
except BaseException:
pass
try:
self.file_extension = os.path.splitext(str(self.filename))[1]
print("Uploaded File extension :" + self.file_extension)
self.cur_dir = os.getcwd()
print("Current Working Directory :" + self.cur_dir)
self.checkSupportFiles()
if self.file_extension == ".vhdl":
self.errorFlag = False
self.createModelDirectory()
self.addingModelInModpath()
self.createModelFiles()
self.runMake()
self.runMakeInstall()
else:
QtGui.QMessageBox.information(
self, 'Message', '''Important Message.
''' +
'''
This accepts only .vhdl file '''
)
except Exception as e:
QtGui.QMessageBox.critical(self, 'Error', str(e))
class FileRemover(QtGui.QWidget):
def __init__(self, main_obj):
super(FileRemover, self).__init__()
self.row = 0
self.col = 0
self.cb_dict = {}
self.marked_list = []
self.files = main_obj.file_list
self.sedit = main_obj.sedit
print(self.files)
self.grid = QtGui.QGridLayout()
removebtn = QtGui.QPushButton('Remove', self)
removebtn.clicked.connect(self.removeFiles)
self.grid.addWidget(self.createCheckBox(), 0, 0)
self.grid.addWidget(removebtn, 1, 1)
self.setLayout(self.grid)
self.show()
def createCheckBox(self):
self.checkbox = QtGui.QGroupBox()
self.checkbox.setTitle('Remove Files')
self.checkgrid = QtGui.QGridLayout()
self.checkgroupbtn = QtGui.QButtonGroup()
for path in self.files:
print(path)
self.cb_dict[path] = QtGui.QCheckBox(path)
self.checkgroupbtn.addButton(self.cb_dict[path])
self.checkgrid.addWidget(self.cb_dict[path], self.row, self.col)
self.row += 1
self.checkgroupbtn.setExclusive(False)
self.checkgroupbtn.buttonClicked.connect(self.mark_file)
self.checkbox.setLayout(self.checkgrid)
return self.checkbox
def mark_file(self):
for path in self.cb_dict:
if self.cb_dict[path].isChecked():
if path not in self.marked_list:
self.marked_list.append(path)
else:
if path in self.marked_list:
self.marked_list.remove(path)
def removeFiles(self):
for path in self.marked_list:
print(path + " is removed")
self.sedit.append(path + " removed")
self.files.remove(path)
self.sedit.clear()
for path in self.files:
self.sedit.append(path)
self.marked_list[:] = []
self.files[:] = []
self.close()
def main():
app = QtGui.QApplication(sys.argv)
if len(sys.argv) > 1:
if sys.argv[1] == '-e':
Appconfig.esimFlag = 1
# Mainwindow() object must be assigned to a variable.
# Otherwise, it is destroyed as soon as it gets created.
w = Mainwindow() # noqa
sys.exit(app.exec_())
if __name__ == '__main__':
main()