diff options
Diffstat (limited to 'src')
-rwxr-xr-x[-rw-r--r--] | src/kicadtoNgspice/Convert.py | 17 | ||||
-rwxr-xr-x[-rw-r--r--] | src/kicadtoNgspice/DeviceModel.py | 507 |
2 files changed, 515 insertions, 9 deletions
diff --git a/src/kicadtoNgspice/Convert.py b/src/kicadtoNgspice/Convert.py index 24449a3b..9aec6cce 100644..100755 --- a/src/kicadtoNgspice/Convert.py +++ b/src/kicadtoNgspice/Convert.py @@ -525,6 +525,7 @@ class Convert: # print("Library Path :", libpath) # Copying library from devicemodelLibrary to Project Path # Special case for MOSFET + print(eachline[0:5]) if eachline[0] == 'm': # For mosfet library name come along with MOSFET # dimension information @@ -547,6 +548,22 @@ class Convert: src = os.path.join(src_path, src_lib) dst = projpath shutil.copy2(src, dst) + + elif eachline[0:6] == 'scmode': + tempStr = completeLibPath.split(':') + print(tempStr) + includeLine.append( + ".lib \"" + tempStr[0] + "\" " + tempStr[1]) + deviceLine[index] = '' + # words.append(completeLibPath) + # deviceLine[index] = words + + elif eachline[0:2] == 'sc' and eachline[0:6] != 'scmode': + temp_str = words[0].replace('sc', 'xsc') + words[0] = temp_str + words.append(completeLibPath) + deviceLine[index] = words + else: # Replace last word with library name # words[-1] = libname.split('.')[0] diff --git a/src/kicadtoNgspice/DeviceModel.py b/src/kicadtoNgspice/DeviceModel.py index 5fd4d829..b120f2e7 100644..100755 --- a/src/kicadtoNgspice/DeviceModel.py +++ b/src/kicadtoNgspice/DeviceModel.py @@ -1,7 +1,11 @@ from PyQt5 import QtWidgets, QtCore +from PyQt5.QtCore import QThread, Qt import os +import wget +import zipfile from xml.etree import ElementTree as ET from . import TrackWidget +flag = 0 class DeviceModel(QtWidgets.QWidget): @@ -26,7 +30,7 @@ class DeviceModel(QtWidgets.QWidget): kicadFile = self.clarg1 (projpath, filename) = os.path.split(kicadFile) project_name = os.path.basename(projpath) - + self.root = [] try: f = open( os.path.join( @@ -35,10 +39,10 @@ class DeviceModel(QtWidgets.QWidget): "_Previous_Values.xml"), 'r') tree = ET.parse(f) - parent_root = tree.getroot() - for child in parent_root: + parent_self = tree.getroot() + for child in parent_self: if child.tag == "devicemodel": - root = child + self.root = child except BaseException: print("Device Model Previous XML is Empty") @@ -51,10 +55,10 @@ class DeviceModel(QtWidgets.QWidget): self.row = 0 self.count = 1 # Entry count self.entry_var = {} - # For MOSFET self.widthLabel = {} self.lengthLabel = {} + self.parameterLabel = {} self.multifactorLable = {} self.devicemodel_dict_beg = {} self.devicemodel_dict_end = {} @@ -65,11 +69,214 @@ class DeviceModel(QtWidgets.QWidget): self.grid = QtWidgets.QGridLayout() self.setLayout(self.grid) # print("Reading Device model details from Schematic") + if "sky130" in " ".join(schematicInfo): + self.eSim_sky130(schematicInfo) + else: + self.eSim_general_libs(schematicInfo) + + def eSim_sky130(self, schematicInfo): + sky130box = QtWidgets.QGroupBox() + sky130grid = QtWidgets.QGridLayout() + i = self.count + beg = self.count + sky130box.setTitle( + "Various Options for Sky130") + self.row = self.row + 1 + self.downloadbtn = QtWidgets.QPushButton("Download Sky130_fd_pr PDK") + self.downloadbtn.clicked.connect(self.downloadPDK) + sky130grid.addWidget(self.downloadbtn, self.row, 1) + + self.SOCbtn = QtWidgets.QPushButton("Generate SoC") + self.SOCbtn.clicked.connect(self.GenerateSOCbutton) + sky130grid.addWidget(self.SOCbtn, self.row, 2) + self.SOCbtn.setToolTip('''This is the Generate SoC \ +option to convert SPICE to verilog. +Naming convention should be strictly: +IP for Analog Design: IPAD +IP for Digital Design: IPDD +Net labels: pinname_pinno_mode_bridgetype +For more info please see the documentation''') + sky130box.setLayout(sky130grid) + sky130box.setStyleSheet(" \ + QGroupBox { border: 1px solid gray; border-radius:\ + 9px; margin-top: 0.5em; } \ + QGroupBox::title { subcontrol-origin: margin; left:\ + 10px; padding: 0 3px 0 3px; } \ + ") + self.grid.addWidget(sky130box) + sky130box = QtWidgets.QGroupBox() + sky130grid = QtWidgets.QGridLayout() + self.count = self.count+1 + self.row = self.row + 1 + self.devicemodel_dict_beg["scmode1"] = self.count + i = self.count + beg = self.count + self.deviceDetail[self.count] = "scmode1" + sky130box.setTitle( + "Add parameters of sky130 library ") + # + + # " : " + + # words[6]) + self.parameterLabel[self.count] = QtWidgets.QLabel( + "Enter the path ") + self.row = self.row + 1 + sky130grid.addWidget(self.parameterLabel[self.count], self.row, 0) + self.entry_var[self.count] = QtWidgets.QLineEdit() + init_path = '../../' + if os.name == 'nt': + init_path = '' + + for child in self.root: + if child.tag == "scmode1": + if child[0].text \ + and os.path.exists(child[0].text): + self.entry_var[self.count] \ + .setText(child[0].text) + path_name = child[0].text + else: + path_name = os.path.abspath( + init_path + "library/deviceModelLibrary/\ +sky130_fd_pr/models/sky130.lib.spice") + self.entry_var[self.count].setText(path_name) + # self.trackLibraryWithoutButton(self.count, path_name) + + sky130grid.addWidget(self.entry_var[self.count], self.row, 1) + self.addbtn = QtWidgets.QPushButton("Add") + self.addbtn.setObjectName("%d" % beg) + self.addbtn.clicked.connect(self.trackLibrary) + sky130grid.addWidget(self.addbtn, self.row, 2) + # self.count = self.count + 1 + self.adddefaultbtn = QtWidgets.QPushButton("Add Default") + self.adddefaultbtn.setObjectName("%d" % beg) + self.adddefaultbtn.clicked.connect(self.trackDefaultLib) + sky130grid.addWidget(self.adddefaultbtn, self.row, 3) + self.count = self.count + 1 + self.parameterLabel[self.count] = QtWidgets.QLabel( + "Enter the corner e.g. tt") + self.row = self.row + 1 + sky130grid.addWidget(self.parameterLabel[self.count], self.row, 0) + self.entry_var[self.count] = QtWidgets.QLineEdit() + self.entry_var[self.count].setText("") + self.entry_var[self.count].setMaximumWidth(150) + self.entry_var[self.count].setObjectName("%d" % beg) + for child in self.root: + if child.tag == "scmode1": + if child[1].text: + self.entry_var[self.count] \ + .setText(child[1].text) + else: + self.entry_var[self.count].setText("") + + sky130grid.addWidget(self.entry_var[self.count], self.row, 1) + self.entry_var[self.count].textChanged.connect(self.textChange) + self.trackLibraryWithoutButton(beg, path_name) + + sky130box.setLayout(sky130grid) + sky130box.setStyleSheet(" \ + QGroupBox { border: 1px solid gray; border-radius:\ + 9px; margin-top: 0.5em; } \ + QGroupBox::title { subcontrol-origin: margin; left:\ + 10px; padding: 0 3px 0 3px; } \ + ") + self.grid.addWidget(sky130box) + # if self.entry_var[self.count-3].text() == "": + # pass + # else: + # self.trackLibraryWithoutButton(self.count-3, path_name) + + self.row = self.row + 1 + self.devicemodel_dict_end["scmode1"] = self.count + self.count = self.count + 1 for eachline in schematicInfo: print("=========================================") print(eachline) words = eachline.split() + # supporteddesignator = ['sc', 'u', 'x', 'v', 'i', 'a'] + if eachline[0:2] != 'sc' and eachline[0] != 'u' \ + and eachline[0] != 'x' and eachline[0] != '*'\ + and eachline[0] != 'v' and eachline[0] != 'i'\ + and eachline[0] != 'a': + print("Only components with designators 'sc', 'u', \ +'x', 'v', 'i', 'a'\ + can be used with sky130 mode") + print("Please remove other components") + self.msg = QtWidgets.QErrorMessage() + self.msg.setModal(True) + self.msg.setWindowTitle("Invalid components") + self.content = "Only components with designators " + \ + "'sc', 'u' and 'x' can be used \ + with sky130 mode. " + \ + "Please edit the schematic and \ + generate netlist again" + self.msg.showMessage(self.content) + self.msg.exec_() + return + + elif eachline[0:2] == 'sc' and eachline[0:6] != 'scmode': + self.devicemodel_dict_beg[words[0]] = self.count + self.deviceDetail[self.count] = words[0] + sky130box = QtWidgets.QGroupBox() + sky130grid = QtWidgets.QGridLayout() + i = self.count + beg = self.count + sky130box.setTitle( + "Add parameters for " + + words[0] + " : " + words[-1]) + + # Adding to get sky130 dimension + self.parameterLabel[self.count] = QtWidgets.QLabel( + "Enter the parameters of sky130 component " + words[0]) + sky130grid.addWidget( + self.parameterLabel[self.count], self.row, 0) + self.entry_var[self.count] = QtWidgets.QLineEdit() + self.entry_var[self.count].setText("") + self.entry_var[self.count].setMaximumWidth(1000) + self.entry_var[self.count].setObjectName("%d" % beg) + sky130grid.addWidget(self.entry_var[self.count], self.row, 1) + self.entry_var[self.count].textChanged.connect(self.textChange) + sky130box.setLayout(sky130grid) + sky130box.setStyleSheet(" \ + QGroupBox { border: 1px solid gray; border-radius: \ + 9px; margin-top: 0.5em; } \ + QGroupBox::title { subcontrol-origin: margin; left:\ + 10px; padding: 0 3px 0 3px; } \ + ") + try: + for child in self.root: + if child.tag == words[0]: + # print("DEVICE MODEL MATCHING---", \ + # child.tag, words[0]) + try: + if child[0].text: + self.entry_var[self.count] \ + .setText(child[0].text) + path_name = "" + else: + self.entry_var[self.count].setText("") + path_name = "" + except BaseException as e: + print("Error when set text of Device " + + "Sky130 Component :", str(e)) + except BaseException: + pass + self.trackLibraryWithoutButton(self.count, path_name) + self.grid.addWidget(sky130box) + + # Adding Device Details # + + # Increment row and widget count + self.row = self.row + 1 + self.devicemodel_dict_end[words[0]] = self.count + self.count = self.count + 1 + + self.show() + + def eSim_general_libs(self, schematicInfo): + for eachline in schematicInfo: + print("=========================================") + print(eachline) + words = eachline.split() if eachline[0] == 'q': # print("Device Model Transistor: ", words[0]) self.devicemodel_dict_beg[words[0]] = self.count @@ -85,7 +292,7 @@ class DeviceModel(QtWidgets.QWidget): global path_name try: - for child in root: + for child in self.root: if child.tag == words[0]: # print("DEVICE MODEL MATCHING---", \ # child.tag, words[0]) @@ -148,7 +355,7 @@ class DeviceModel(QtWidgets.QWidget): self.entry_var[self.count].setText("") # global path_name try: - for child in root: + for child in self.root: if child.tag == words[0]: # print("DEVICE MODEL MATCHING---", \ # child.tag, words[0]) @@ -211,7 +418,7 @@ class DeviceModel(QtWidgets.QWidget): self.entry_var[self.count].setText("") # global path_name try: - for child in root: + for child in self.root: if child.tag == words[0]: # print("DEVICE MODEL MATCHING---", \ # child.tag, words[0]) @@ -324,7 +531,7 @@ class DeviceModel(QtWidgets.QWidget): # global path_name try: - for child in root: + for child in self.root: if child.tag == words[0]: # print("DEVICE MODEL MATCHING---", \ # child.tag, words[0]) @@ -356,6 +563,92 @@ class DeviceModel(QtWidgets.QWidget): self.show() + def downloadPDK(self): + init_path = '../../' + if os.name == 'nt': + init_path = '' + path_name = os.path.abspath(init_path + "library/deviceModelLibrary/") + global flag + print("flag="+str(flag)) + if os.path.exists(path_name+'/sky130_fd_pr'): + print("Sky130_fd_pr PDK already exists") + self.msg = QtWidgets.QErrorMessage() + self.msg.setModal(True) + self.msg.setWindowTitle("PDK already exists") + self.content = "Sky130_fd_pr PDK already exists.\n" + \ + "Hence no need to download." + self.msg.showMessage(self.content) + self.msg.exec_() + return + elif flag == 1: + # print("Sky130_fd_pr PDK download in progress") + self.msg = QtWidgets.QErrorMessage() + self.msg.setModal(True) + self.msg.setWindowTitle("Download in Progress") + self.content = "PDK download in progress.\n" + \ + "Please see the eSim terminal " +\ + "to track the progress.\nClick on OK." + self.msg.showMessage(self.content) + self.msg.exec_() + return + else: + self.msg = QtWidgets.QErrorMessage() + self.msg.setModal(True) + self.msg.setWindowTitle("PDK download started") + self.content = "PDK download started.\n" + \ + "Please see the eSim terminal " +\ + "to track the progress.\nClick on OK." + self.msg.showMessage(self.content) + self.msg.exec_() + flag = 1 + self.downloadThread = downloadThread(path_name) + self.downloadThread.start() + + def trackDefaultLib(self): + init_path = '../../' + if os.name == 'nt': + init_path = '' + sending_btn = self.sender() + self.widgetObjCount = int(sending_btn.objectName()) + path_name = os.path.abspath( + init_path + "library/deviceModelLibrary/sky130_fd_pr\ +/models/sky130.lib.spice") + self.entry_var[self.widgetObjCount].setText(path_name) + self.trackLibraryWithoutButton(self.widgetObjCount, path_name) + + def textChange(self): + sending_btn = self.sender() + self.widgetObjCount = int(sending_btn.objectName()) + self.deviceName = self.deviceDetail[self.widgetObjCount] + # self.widgetObjCount = self.count_beg + # if self.deviceName[0] == 'm': + # width = str(self.entry_var[self.widgetObjCount + 1].text()) + # length = str(self.entry_var[self.widgetObjCount + 2].text()) + # multifactor = str(self.entry_var[self.widgetObjCount + 3].text()) + # if width == "": + # width = "100u" + # if length == "": + # length = "100u" + # if multifactor == "": + # multifactor = "1" + + # self.obj_trac.deviceModelTrack[self.deviceName] =\ + # str(self.entry_var[self.widgetObjCount].text()) + \ + # ":" + "W=" + width + " L=" + length + " M=" + multifactor + + if self.deviceName[0:6] == 'scmode': + self.obj_trac.deviceModelTrack[self.deviceName] = \ + self.entry_var[self.widgetObjCount].text() + \ + ":" + str(self.entry_var[self.widgetObjCount + 1].text()) + print(self.obj_trac.deviceModelTrack[self.deviceName]) + elif self.deviceName[0:2] == 'sc': + self.obj_trac.deviceModelTrack[self.deviceName] = str( + self.entry_var[self.widgetObjCount].text()) + print(self.obj_trac.deviceModelTrack[self.deviceName]) + + else: + self.obj_trac.deviceModelTrack[self.deviceName] = self.libfile + def trackLibrary(self): """ This function is use to keep track of all Device Model widget @@ -397,6 +690,16 @@ class DeviceModel(QtWidgets.QWidget): self.obj_trac.deviceModelTrack[self.deviceName] = self.libfile + \ ":" + "W=" + width + " L=" + length + " M=" + multifactor + elif self.deviceName[0:6] == 'scmode': + self.obj_trac.deviceModelTrack[self.deviceName] = self.libfile + \ + ":" + str(self.entry_var[self.widgetObjCount + 1].text()) + print(self.obj_trac.deviceModelTrack[self.deviceName]) + + elif self.deviceName[0:2] == 'sc': + self.obj_trac.deviceModelTrack[self.deviceName] = str( + self.entry_var[self.widgetObjCount].text()) + print(self.obj_trac.deviceModelTrack[self.deviceName]) + else: self.obj_trac.deviceModelTrack[self.deviceName] = self.libfile @@ -427,5 +730,191 @@ class DeviceModel(QtWidgets.QWidget): multifactor = "1" self.obj_trac.deviceModelTrack[self.deviceName] = self.libfile + \ ":" + "W=" + width + " L=" + length + " M=" + multifactor + + elif self.deviceName[0:6] == 'scmode': + self.obj_trac.deviceModelTrack[self.deviceName] = self.libfile + \ + ":" + str(self.entry_var[self.widgetObjCount + 1].text()) + print(self.obj_trac.deviceModelTrack[self.deviceName]) + + elif self.deviceName[0:2] == 'sc': + self.obj_trac.deviceModelTrack[self.deviceName] = str( + self.entry_var[self.widgetObjCount].text()) + print(self.obj_trac.deviceModelTrack[self.deviceName]) + else: self.obj_trac.deviceModelTrack[self.deviceName] = self.libfile + + def GenerateSOCbutton(self): + + ############################################################# + # ***************** SPICE to Verilog Converter ************** + + # The development is under progress and may not be accurate + + # Developed by: + # Sumanto Kar, sumantokar@iitb.ac.in + # Nagesh Karmali, nags@cse.iitb.ac.in + # Firuza Karmali, firuza@cse.iitb.ac.in + # Rahul Paknikar, rahulp@iitb.ac.in + # GUIDED BY: + # Kunal Ghosh, VLSI System Design Corp.Pvt.Ltd + # Anagha Ghosh, VLSI System Design Corp.Pvt.Ltd + # Philipp Gühring + + # *********************************************************** + + kicadFile = self.clarg1 + (projpath, filename) = os.path.split(kicadFile) + analysisfile = open(os.path.join(projpath, filename)) + # analysisfile = open(os.path.join(projpath, 'analysis')) + content = analysisfile.read() + contentlines = content.split("\n") + parsedfile = open(os.path.join(projpath, filename+'.parsed.v'), 'w') + parsedfile.write("") + # print("module "+filename) + i = 1 + inputlist = [] + realinputlist = [] + outputlist = [] + realoutputlist = [] + wirelist = [] + realwirelist = [] + uutlist = [] + filelist = [] + parsedcontent = [] + for contentlist in contentlines: + if "IPDD" in contentlist or "IPAD" in contentlist: + # if len(contentlist)>1 and ( contentlist[0:1]=='U'\ + # or contentlist[0:1]=='X') and not 'plot_' in contentlist : + # print(contentlist) + netnames = contentlist.split() + net = ' '.join(map(str, netnames[1:-1])) + netnames[-1] = netnames[-1].replace("IPAD", '') + netnames[-1] = netnames[-1].replace("IPDD", '') + # net=net.replace(netnames[-1],'') + # net=net.replace('BI_','') + # net=net.replace('BO_','') + net2 = [] + + for j in net.split(): + # print(j) + secondpart = j + if '_' in j: + secondpart = j.split('_')[1] + if secondpart in net2: + continue + if net.count(secondpart)-1 > 0: + k = "["+str(net.count(secondpart)-1) + \ + ":0"+"] "+secondpart + else: + k = secondpart + + net2.append(secondpart) + if '_I_' in str(j): + inputlist.append(k) + if '_IR_' in str(j): + inputlist.append(k) + if '_O_' in str(j): + outputlist.append(k) + if '_OR_' in str(j): + realoutputlist.append(k) + if '_W_' in str(j) and not(k in wirelist): + wirelist.append(k) + if '_WR_' in str(j) and not(k in realwirelist): + realwirelist.append(k) + + netnames[-1] = netnames[-1].replace("IPAD", '') + netnames[-1] = netnames[-1].replace("IPDD", '') + uutlist.append(netnames[-1]+" uut" + + str(i)+" ("+', '.join(net2)+');') + filelist.append(netnames[-1]) + i = i+1 + # print(inputlist) + # print(outputlist) + # print(wirelist) + parsedcontent.append( + "\\\\Generated from SPICE to Verilog. \ +Converter developed at FOSSEE, IIT Bombay\n") + parsedcontent.append( + "\\\\The development is under progress and may not be accurate\n") + + for j in filelist: + parsedcontent.append('''`include "'''+j+'''.v"''') + parsedcontent.append( + "module "+filename+"("+', '.join(inputlist # noqa + + realinputlist + outputlist + realoutputlist)+");") # noqa + if inputlist: + parsedcontent.append("input "+', '.join(inputlist)+";") + if realinputlist: + parsedcontent.append("input real "+', '.join(inputlist)+";") + if outputlist: + parsedcontent.append("output "+', '.join(outputlist)+";") + if realoutputlist: + parsedcontent.append("output real "+', '.join(realoutputlist)+";") + if wirelist: + parsedcontent.append("wire "+', '.join(wirelist)+";") + if realwirelist: + parsedcontent.append("wire real"+', '.join(realwirelist)+";") + for j in uutlist: + parsedcontent.append(j) + parsedcontent.append("endmodule;") + + print('\n**************Generated Verilog File:' + + filename+'.parsed.v***************\n') + for j in parsedcontent: + print(j) + parsedfile.write(j+"\n") + print( + '\n*************************************\ +************************************\n') + self.msg = QtWidgets.QErrorMessage() + self.msg.setModal(True) + self.msg.setWindowTitle("Verilog File Generated") + self.content = "The Verilog File has been successfully\ + generated from the SPICE file" + self.msg.showMessage(self.content) + self.msg.exec_() + return + + +class downloadThread(QThread): + # initialising the threads + # initialising the threads + def __init__(self, path_name): + QThread.__init__(self) + self.path_name = path_name + + def __del__(self): + self.wait() + + # running the thread to toggle + def run(self): + global flag + try: + print("\nSky130_fd_pr Download started at Location: " + + self.path_name) # noqa + print("\nYou will be notified once downloaded\n") + wget.download("https://static.fossee.in/esim/installation\ + -files/sky130_fd_pr.zip", + self.path_name+'/sky130_fd_pr.zip') + print("\nSky130_fd_pr Downloaded Successfully \ + at Location: "+self.path_name) + + except Exception as e: + print(e) + print("Download process Failed") + + try: + print("\nStarted Extracting................................\n") + print("\nYou will be notified once extracted") + zp = zipfile.ZipFile(self.path_name+'/sky130_fd_pr.zip') + curr_dir = os.getcwd() + os.chdir(self.path_name) + zp.extractall() + os.remove('sky130_fd_pr.zip') + print("\nZip File Extracted Successfully\n") + os.chdir(curr_dir) + except Exception as e: + print(e) + print("Extraction process Failed") + flag = 0 |