summaryrefslogtreecommitdiff
path: root/src/kicadtoNgspice/DeviceModel.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/kicadtoNgspice/DeviceModel.py')
-rwxr-xr-x[-rw-r--r--]src/kicadtoNgspice/DeviceModel.py491
1 files changed, 479 insertions, 12 deletions
diff --git a/src/kicadtoNgspice/DeviceModel.py b/src/kicadtoNgspice/DeviceModel.py
index 5fd4d829..dd3c27af 100644..100755
--- a/src/kicadtoNgspice/DeviceModel.py
+++ b/src/kicadtoNgspice/DeviceModel.py
@@ -1,8 +1,9 @@
-from PyQt5 import QtWidgets, QtCore
-import os
+from PyQt5 import QtWidgets, QtCore
+from PyQt5.QtCore import QThread, Qt
+import os, wget, zipfile
from xml.etree import ElementTree as ET
from . import TrackWidget
-
+flag=0
class DeviceModel(QtWidgets.QWidget):
"""
@@ -26,7 +27,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 +36,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 +52,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,7 +66,208 @@ 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)
@@ -85,7 +287,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 +350,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 +413,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 +526,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 +558,89 @@ 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,9 +682,20 @@ 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
+
+
def trackLibraryWithoutButton(self, iter_value, path_value):
"""
This function is use to keep track of all Device Model widget
@@ -427,5 +723,176 @@ 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:
+ l="["+str(net.count(secondpart)-1)+":0"+"] "+secondpart
+ else:
+ l=secondpart
+
+ net2.append(secondpart)
+ if '_I_' in str(j):
+ inputlist.append(l)
+ if '_IR_' in str(j):
+ inputlist.append(l)
+ if '_O_' in str(j):
+ outputlist.append(l)
+ if '_OR_' in str(j):
+ realoutputlist.append(l)
+ if '_W_' in str(j) and not(l in wirelist):
+ wirelist.append(l)
+ if '_WR_' in str(j) and not(l in realwirelist):
+ realwirelist.append(l)
+
+ 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+realinputlist+outputlist+realoutputlist)+");")
+ 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)
+ 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
+
+