"
+ self.termtitle("MAKE INSTALL COMMAND")
+ self.termtext("Current Directory: " + path_icm)
+ self.termtext("Command: " + self.cmd)
+ self.process \
+ .readyReadStandardOutput.connect(self.readAllStandard)
+ self.process \
+ .readyReadStandardError.connect(self.readAllStandard)
+ self.process.waitForFinished(50000)
+ os.chdir(self.cur_dir)
+
+ except BaseException as e:
+ print(e)
+ print("There is error in 'make install' ")
+
+ def addfile(self):
+ '''
+ This function is used to add additional files
+ required by the verilog top module.
+ '''
+ print("Adding the files required by the top level module file")
+
+ init_path = '/home/adarsh_10811/eSim/'
+ if os.name == 'nt':
+ init_path = ''
+
+ includefile = QtCore.QDir.toNativeSeparators(
+ QtWidgets.QFileDialog.getOpenFileName(
+ self,
+ "Open adding other necessary files to be included",
+ init_path + "home")[0])
+
+ if includefile == "":
+ reply = QtWidgets.QMessageBox.critical(
+ None, "Error Message",
+ "Error: No File Chosen. Please chose a file",
+ QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel
+ )
+
+ if reply == QtWidgets.QMessageBox.Ok:
+ self.addfile()
+
+ if includefile == "":
+ return
+
+ self.obj_Appconfig.print_info('Add Other Files Called')
+
+ elif reply == QtWidgets.QMessageBox.Cancel:
+ self.obj_Appconfig.print_info('No File Chosen')
+ return
+
+ filename = os.path.basename(includefile)
+ self.modelpath = self.digital_home + \
+ "/" + self.fname.split('.')[0] + "/"
+
+ if not os.path.isdir(self.modelpath):
+ os.mkdir(self.modelpath)
+ text = open(includefile).read()
+ text = text + '\n'
+ f = open(self.modelpath + filename, 'w')
+ for item in text:
+ f.write(item)
+ f.write("\n")
+ f.close()
+ print("Added the File:" + filename)
+ self.termtitle("Added the File:" + filename)
+
+ def addfolder(self):
+ '''
+ This function is used to add additional folder required
+ by the verilog top module
+ '''
+ # self.cur_dir = os.getcwd()
+ print("Adding the folder required by the top level module file")
+
+ includefolder = QtCore.QDir.toNativeSeparators(
+ QtWidgets.QFileDialog.getExistingDirectory(
+ self, "open", "home"
+ )
+ )
+
+ if includefolder == "":
+ reply = QtWidgets.QMessageBox.critical(
+ None, "Error Message",
+ "Error: No Folder Chosen. Please chose a folder",
+ QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel
+ )
+
+ if reply == QtWidgets.QMessageBox.Ok:
+ self.addfolder()
+
+ if includefolder == "":
+ return
+
+ self.obj_Appconfig.print_info('Add Folder Called')
+
+ elif reply == QtWidgets.QMessageBox.Cancel:
+ self.obj_Appconfig.print_info('No Folder Chosen')
+ return
+
+ self.modelpath = self.digital_home + \
+ "/" + self.fname.split('.')[0] + "/"
+
+ reply = QtWidgets.QMessageBox.question(
+ None, "Message",
+ '''If you want only the contents\
+ of the folder to be added press "Yes".\
+ If you want complete folder \
+ to be added, press "No". ''',
+ QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No
+ )
+ if reply == QtWidgets.QMessageBox.Yes:
+ self.cmd = "cp -a " + includefolder + "/. " + self.modelpath
+ self.obj_Appconfig.print_info('Adding Contents of the Folder')
+ elif reply == QtWidgets.QMessageBox.No:
+ self.cmd = "cp -R " + includefolder + " " + self.modelpath
+ self.obj_Appconfig.print_info('Adding the Folder')
+
+ print("Adding the Folder:" + includefolder.split('/')[-1])
+ self.termtitle("Adding the Folder:" + includefolder.split('/')[-1])
+
+ self.process = QtCore.QProcess(self)
+ self.process.start('sh', ['-c', self.cmd])
+ self.termtext("Command: " + self.cmd)
+ self.process \
+ .readyReadStandardOutput.connect(self.readAllStandard)
+ self.process.waitForFinished(50000)
+ print("Added the folder")
+ # os.chdir(self.cur_dir)
+
+ def termtitle(self, textin):
+ '''
+ This function is used to print the titles
+ in the terminal of Ngveri tab.
+ '''
+ Text = ""
+ Text += "
================================
"
+ Text += textin
+ Text += "
================================
"
+ Text += ""
+ self.termedit.append(Text)
+
+ def termtext(self, textin):
+ '''
+ This function is used to print the text/commands
+ in the terminal of Ngveri tab.
+ '''
+ Text = ""
+ Text += textin
+ Text += ""
+ self.termedit.append(Text)
+
+ @QtCore.pyqtSlot()
+ def readAllStandard(self):
+ '''
+ This function reads all the standard output data and
+ the errors from the process that are being run.
+ '''
+ # self.termedit = termedit
+ # self.termedit.append(str(self.process.readAll().data(),\
+ # encoding='utf-8'))
+ stdoutput = self.process.readAll()
+ TextStdOut = ""
+ for line in str(stdoutput.data(), encoding='utf-8').split("\n"):
+ TextStdOut += "
" + line
+ TextStdOut += ""
+ self.termedit.append(TextStdOut)
+ # print(str(self.process.readAll().data(), encoding='utf-8'))
+
+ stderror = self.process.readAllStandardError()
+ if stderror.toUpper().contains(b"ERROR"):
+ self.errorFlag = True
+ TextErr = ""
+ for line in str(stderror.data(), encoding='utf-8').split("\n"):
+ TextErr += "
" + line
+ TextErr += ""
+ self.termedit.append(TextErr)
+
+ # @QtCore.pyqtSlot()
+ # def readAllStandard(self):
+ # #self.termedit = termedit
+ # self.termedit.append(str(self.process.\
+ # readAll().data(), encoding='utf-8'))
+
+ # print(str(self.process.readAll().data(), encoding='utf-8'))
+ # stderror = self.process.readAllStandardError()
+ # if stderror.toUpper().contains(b"ERROR"):
+ # self.errorFlag = True
+ # Text = ""
+ # for line in str(stderror.data(), encoding='utf-8').split("\n"):
+ # Text += "
"+line+"
"
+ # Text += ""
+ # self.termedit.append(Text+"\n")
+
+ # init_path = '/home/adarsh_10811/eSim/'
+ # if os.name == 'nt':
+ # init_path = ''
+ # includefile = QtCore.QDir.toNativeSeparators(\
+ # QtWidgets.QFileDialog.getOpenFileName(
+ # self, "Open adding other necessary files to be included",
+ # init_path + "home"
+ # )[0]
+ # )
+ # if includefile=="":
+ # reply=QtWidgets.QMessageBox.critical(
+ # None, "Error Message",
+ # "Error: No File Chosen. Please chose a file",
+ # QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel
+ # )
+ # if reply == QtWidgets.QMessageBox.Ok:
+ # self.addfile()
+ # self.obj_Appconfig.print_info('Add Other Files Called')
+
+ # elif reply == QtWidgets.QMessageBox.Cancel:
+ # self.obj_Appconfig.print_info('No File Chosen')
+ # filename = os.path.basename(includefile)
+ # self.modelpath=self.digital_home+"/"+self.fname.split('.')[0]+"/"
+
+ # if not os.path.isdir(self.modelpath):
+ # os.mkdir(self.modelpath)
+ # text = open(includefile).read()
+ # open(self.modelpath+filename,'w').write(text)
+ # includefile.close()
diff --git a/docker-launcher/src/maker/NgVeri.py b/docker-launcher/src/maker/NgVeri.py
new file mode 100644
index 0000000..bc37781
--- /dev/null
+++ b/docker-launcher/src/maker/NgVeri.py
@@ -0,0 +1,443 @@
+# =========================================================================
+# FILE: NgVeri.py
+#
+# USAGE: ---
+#
+# DESCRIPTION: This define all components of the NgVeri Tab.
+#
+# OPTIONS: ---
+# REQUIREMENTS: ---
+# BUGS: ---
+# NOTES: ---
+# AUTHOR: Sumanto Kar, sumantokar@iitb.ac.in, FOSSEE, IIT Bombay
+# ACKNOWLEDGEMENTS: Rahul Paknikar, rahulp@iitb.ac.in, FOSSEE, IIT Bombay
+# Digvijay Singh, digvijay.singh@iitb.ac.in, FOSSEE, IIT Bombay
+# Prof. Maheswari R. and Team, VIT Chennai
+# GUIDED BY: Steve Hoover, Founder Redwood EDA
+# Kunal Ghosh, VLSI System Design Corp.Pvt.Ltd
+# Anagha Ghosh, VLSI System Design Corp.Pvt.Ltd
+# OTHER CONTRIBUTERS:
+# Prof. Madhuri Kadam, Shree L. R. Tiwari College of Engineering
+# Rohinth Ram, Madras Institue of Technology
+# Charaan S., Madras Institue of Technology
+# Nalinkumar S., Madras Institue of Technology
+# ORGANIZATION: eSim Team at FOSSEE, IIT Bombay
+# CREATED: Monday 29, November 2021
+# REVISION: Tuesday 25, January 2022
+# =========================================================================
+
+
+# importing the files and libraries
+from PyQt5 import QtCore, QtWidgets
+from . import Maker
+from . import ModelGeneration
+import os
+import shutil
+from configuration.Appconfig import Appconfig
+from configparser import ConfigParser
+
+
+class NgVeri(QtWidgets.QWidget):
+ '''
+ This class create the NgVeri Tab
+ '''
+ def __init__(self, filecount):
+ QtWidgets.QWidget.__init__(self)
+ # Maker.addverilog(self)
+ self.obj_Appconfig = Appconfig()
+
+ if os.name == 'nt':
+ self.home = os.path.join('library', 'config')
+ else:
+ self.home = os.path.expanduser('~')
+
+ self.parser = ConfigParser()
+ self.parser.read(os.path.join(
+ self.home, os.path.join('.nghdl', 'config.ini')))
+ self.nghdl_home = self.parser.get('NGHDL', 'NGHDL_HOME')
+ self.release_dir = self.parser.get('NGHDL', 'RELEASE')
+ self.src_home = self.parser.get('SRC', 'SRC_HOME')
+ self.licensefile = self.parser.get('SRC', 'LICENSE')
+ self.digital_home = self.parser.get('NGHDL', 'DIGITAL_MODEL')
+ self.digital_home = self.digital_home + "/Ngveri"
+ self.count = 0
+ self.text = ""
+ self.entry_var = {}
+ self.createNgveriWidget()
+ self.fname = ""
+ self.filecount = filecount
+
+ def createNgveriWidget(self):
+ '''
+ Creating the various components of the Widget(Ngveri Tab)
+ '''
+ self.grid = QtWidgets.QGridLayout()
+ self.setLayout(self.grid)
+
+ self.grid.addWidget(self.createoptionsBox(), 0, 0, QtCore.Qt.AlignTop)
+ self.grid.addWidget(self.creategroup(), 1, 0, 5, 0)
+
+ self.show()
+
+ def addverilog(self):
+ '''
+ Adding the verilog file in Maker tab to Ngveri Tab automatically
+ '''
+ # b=Maker.Maker(self)
+ print(Maker.verilogFile)
+ if Maker.verilogFile[self.filecount] == "":
+ reply = QtWidgets.QMessageBox.critical(
+ None,
+ "Error Message",
+ "Error: No Verilog File Chosen. \
+ Please choose a verilog file in Makerchip Tab",
+ QtWidgets.QMessageBox.Ok)
+ if reply == QtWidgets.QMessageBox.Ok:
+ self.obj_Appconfig.print_error(
+ 'No Verilog File Chosen. '
+ 'Please choose a verilog file in Makerchip Tab'
+ )
+ return
+
+ self.fname = Maker.verilogFile[self.filecount]
+ currentTermLogs = QtWidgets.QTextEdit()
+ model = ModelGeneration.ModelGeneration(self.fname, currentTermLogs)
+ file = (os.path.basename(self.fname)).split('.')[0]
+ if self.entry_var[1].findText(file) == -1:
+ self.entry_var[1].addItem(file)
+
+ if not Maker.makerchipTOSAccepted(True):
+ QtWidgets.QMessageBox.warning(
+ None, "Warning Message",
+ "Please accept the Makerchip Terms of Service "
+ "to proceed further.",
+ QtWidgets.QMessageBox.Ok
+ )
+
+ return
+
+ try:
+ model.verilogfile()
+ error = model.verilogParse()
+ if error != "Error":
+ model.getPortInfo()
+ model.cfuncmod()
+ model.ifspecwrite()
+ model.sim_main_header()
+ model.sim_main()
+ model.modpathlst()
+ model.run_verilator()
+ model.make_verilator()
+ model.copy_verilator()
+ model.runMake()
+
+ if os.name != 'nt':
+ model.runMakeInstall()
+ else:
+ try:
+ shutil.copy(
+ self.release_dir +
+ "/src/xspice/icm/Ngveri/Ngveri.cm",
+ self.nghdl_home + "/lib/ngspice/"
+ )
+ except FileNotFoundError as err:
+ currentTermLogs.append(
+ "Error in copying Ngveri code model: " + str(err)
+ )
+
+ if "error" not in currentTermLogs.toPlainText().lower():
+ currentTermLogs.append('''
+ Model Created Successfully!
+
+ ''')
+
+ except BaseException as err:
+ currentTermLogs.append(
+ "Error in Ngspice code model generation " +
+ "from Verilog: " + str(err)
+ )
+
+ if "error" in currentTermLogs.toPlainText().lower():
+ currentTermLogs.append('''
+ There was an error during model creation,
+
Please rectify the error and try again!
+
+ ''')
+
+ self.entry_var[0].append(currentTermLogs.toHtml())
+
+ # Force scroll the terminal widget at bottom
+ self.entry_var[0].verticalScrollBar().setValue(
+ self.entry_var[0].verticalScrollBar().maximum()
+ )
+
+ def addfile(self):
+ '''
+ This function is used to add additional files required
+ by the verilog top module
+ '''
+ if len(Maker.verilogFile) < (self.filecount + 1):
+ reply = QtWidgets.QMessageBox.critical(
+ None,
+ "Error Message",
+ "Error: No Verilog File Chosen. \
+ Please choose a verilog file in Makerchip Tab",
+ QtWidgets.QMessageBox.Ok)
+ if reply == QtWidgets.QMessageBox.Ok:
+ self.obj_Appconfig.print_error(
+ 'No Verilog File Chosen. Please choose \
+ a verilog file in Makerchip Tab')
+ return
+
+ self.fname = Maker.verilogFile[self.filecount]
+ model = ModelGeneration.ModelGeneration(self.fname, self.entry_var[0])
+ # model.verilogfile()
+ model.addfile()
+
+ def addfolder(self):
+ '''
+ This function is used to add additional folder required
+ by the verilog top module.
+ '''
+ if len(Maker.verilogFile) < (self.filecount + 1):
+ reply = QtWidgets.QMessageBox.critical(
+ None,
+ "Error Message",
+ "Error: No Verilog File Chosen. \
+ Please choose a verilog file in Makerchip Tab",
+ QtWidgets.QMessageBox.Ok)
+ if reply == QtWidgets.QMessageBox.Ok:
+ self.obj_Appconfig.print_error(
+ 'No Verilog File Chosen. Please choose \
+ a verilog file in Makerchip Tab')
+ return
+ self.fname = Maker.verilogFile[self.filecount]
+ model = ModelGeneration.ModelGeneration(self.fname, self.entry_var[0])
+ # model.verilogfile()
+ model.addfolder()
+
+ def clearTerminal(self):
+ '''
+ This function is used to clear the terminal
+ '''
+ self.entry_var[0].setText("")
+
+ def createoptionsBox(self):
+ '''
+ This function is used to create buttons/options
+ '''
+ self.optionsbox = QtWidgets.QGroupBox()
+ self.optionsbox.setTitle("Select Options")
+ self.optionsgrid = QtWidgets.QGridLayout()
+
+ self.optionsgroupbtn = QtWidgets.QButtonGroup()
+
+ self.addverilogbutton = QtWidgets.QPushButton(
+ "Convert Verilog to Ngspice")
+ self.addverilogbutton.setToolTip(
+ "Requires internet connection for converting TL-Verilog models"
+ )
+ self.addverilogbutton.setToolTipDuration(5000)
+ self.optionsgroupbtn.addButton(self.addverilogbutton)
+ self.addverilogbutton.clicked.connect(self.addverilog)
+ self.optionsgrid.addWidget(self.addverilogbutton, 0, 1)
+ # self.optionsbox.setLayout(self.optionsgrid)
+ # self.grid.addWidget(self.creategroup(), 1, 0, 5, 0)
+
+ self.addfilebutton = QtWidgets.QPushButton("Add dependency files")
+ self.optionsgroupbtn.addButton(self.addfilebutton)
+ self.addfilebutton.clicked.connect(self.addfile)
+ self.optionsgrid.addWidget(self.addfilebutton, 0, 2)
+ # self.optionsbox.setLayout(self.optionsgrid)
+ # self.grid.addWidget(self.creategroup(), 1, 0, 5, 0)
+
+ self.addfolderbutton = QtWidgets.QPushButton("Add dependency folder")
+ self.optionsgroupbtn.addButton(self.addfolderbutton)
+ self.addfolderbutton.clicked.connect(self.addfolder)
+ self.optionsgrid.addWidget(self.addfolderbutton, 0, 3)
+ # self.optionsbox.setLayout(self.optionsgrid)
+ # self.grid.addWidget(self.creategroup(), 1, 0, 5, 0)
+
+ self.clearTerminalBtn = QtWidgets.QPushButton("Clear Terminal")
+ self.optionsgroupbtn.addButton(self.clearTerminalBtn)
+ self.clearTerminalBtn.clicked.connect(self.clearTerminal)
+ self.optionsgrid.addWidget(self.clearTerminalBtn, 0, 4)
+ self.optionsbox.setLayout(self.optionsgrid)
+ # self.grid.addWidget(self.creategroup(), 1, 0, 5, 0)
+
+ return self.optionsbox
+
+ def edit_modlst(self, text):
+ '''
+ This is used to remove models in modlst of Ngspice folder if
+ the user wants to remove a model. Note: files do not get removed.
+ '''
+ if text == "Remove Verilog Models":
+ return
+ index = self.entry_var[1].findText(text)
+ self.entry_var[1].removeItem(index)
+ self.entry_var[1].setCurrentIndex(0)
+ ret = QtWidgets.QMessageBox.warning(
+ None, "Warning", '''Do you want to remove the model: ''' +
+ text,
+ QtWidgets.QMessageBox.Ok, QtWidgets.QMessageBox.Cancel
+ )
+ if ret == QtWidgets.QMessageBox.Ok:
+ mod = open(self.digital_home + '/modpath.lst', 'r')
+ data = mod.readlines()
+ mod.close()
+
+ data.remove(text + "\n")
+ mod = open(self.digital_home + '/modpath.lst', 'w')
+ for item in data:
+ mod.write(item)
+ self.fname = Maker.verilogFile[self.filecount]
+ model = ModelGeneration.ModelGeneration(
+ self.fname, self.entry_var[0])
+
+ try:
+ model.runMake()
+
+ if os.name != 'nt':
+ model.runMakeInstall()
+ else:
+ shutil.copy(
+ self.release_dir + "/src/xspice/icm/Ngveri/Ngveri.cm",
+ self.nghdl_home + "/lib/ngspice/"
+ )
+ except BaseException as err:
+ QtWidgets.QMessageBox.critical(
+ None, "Error Message",
+ "The verilog model '" + str(text) +
+ "' could not be removed: " + str(err),
+ QtWidgets.QMessageBox.Ok
+ )
+
+ def lint_off_edit(self, text):
+ '''
+ This is to remove lint_off comments needed by the verilator warnings.
+ This function writes to the lint_off.txt in the library/tlv folder.
+ '''
+ init_path = '/home/adarsh_10811/eSim/'
+ if os.name == 'nt':
+ init_path = ''
+
+ if text == "Remove lint_off":
+ return
+ index = self.entry_var[2].findText(text)
+ self.entry_var[2].removeItem(index)
+ self.entry_var[2].setCurrentIndex(0)
+ ret = QtWidgets.QMessageBox.warning(
+ None,
+ "Warning",
+ '''Do you want to remove the lint off error: ''' +
+ text,
+ QtWidgets.QMessageBox.Ok,
+ QtWidgets.QMessageBox.Cancel)
+
+ if ret == QtWidgets.QMessageBox.Ok:
+ try:
+ file_path = os.path.join(init_path, "library/tlv/lint_off.txt")
+ with open(file_path, 'r') as file:
+ data = file.readlines()
+ data = [line for line in data if line.strip() != text]
+ with open(file_path, 'w') as file:
+ file.writelines(data)
+
+ except Exception as e:
+ QtWidgets.QMessageBox.warning(
+ None,
+ "Warning",
+ f"Could not remove lint_off entry '{text}'",
+ QtWidgets.QMessageBox.Ok
+ )
+
+ def add_lint_off(self):
+ '''
+ This is to add lint_off comments needed by the verilator warnings.
+ This function writes to the lint_off.txt in the library/tlv folder.
+ '''
+ init_path = '/home/adarsh_10811/eSim/'
+ if os.name == 'nt':
+ init_path = ''
+
+ text = self.entry_var[3].text()
+
+ if self.entry_var[2].findText(text) == -1:
+ self.entry_var[2].addItem(text)
+ file = open(init_path + "library/tlv/lint_off.txt", 'a+')
+ file.write(text + "\n")
+ file.close()
+ self.entry_var[3].setText("")
+
+ def creategroup(self):
+ '''
+ Creates various other groups like terminal, remove modlst,
+ remove lint_off and add lint_off
+ '''
+ self.trbox = QtWidgets.QGroupBox()
+ self.trbox.setTitle("Terminal")
+ # self.trbox.setDisabled(True)
+ # self.trbox.setVisible(False)
+ self.trgrid = QtWidgets.QGridLayout()
+ self.trbox.setLayout(self.trgrid)
+ self.count = 0
+
+ self.start = QtWidgets.QLabel("Terminal")
+ # self.trgrid.addWidget(self.start, 2,0)
+ self.entry_var[self.count] = QtWidgets.QTextEdit()
+ self.entry_var[self.count].setReadOnly(1)
+ self.trgrid.addWidget(self.entry_var[self.count], 1, 1, 5, 3)
+ self.entry_var[self.count].setMaximumWidth(1000)
+ self.entry_var[self.count].setMaximumHeight(1000)
+ self.count += 1
+
+ self.entry_var[self.count] = QtWidgets.QComboBox()
+ self.entry_var[self.count].addItem("Remove Verilog Models")
+ self.modlst = open(self.digital_home + '/modpath.lst', 'r')
+ self.data = self.modlst.readlines()
+ self.modlst.close()
+ for item in self.data:
+ if item != "\n":
+ self.entry_var[self.count].addItem(item.strip())
+ self.entry_var[self.count].activated[str].connect(self.edit_modlst)
+ self.trgrid.addWidget(self.entry_var[self.count], 1, 4, 1, 2)
+ self.count += 1
+ self.entry_var[self.count] = QtWidgets.QComboBox()
+ self.entry_var[self.count].addItem("Remove lint_off")
+
+ init_path = '/home/adarsh_10811/eSim/'
+ if os.name == 'nt':
+ init_path = ''
+ self.lint_off = open(init_path + "library/tlv/lint_off.txt", 'r')
+
+ self.data = self.lint_off.readlines()
+ self.lint_off.close()
+ for item in self.data:
+ if item != "\n":
+ self.entry_var[self.count].addItem(item.strip())
+ self.entry_var[self.count].activated[str].connect(self.lint_off_edit)
+ self.trgrid.addWidget(self.entry_var[self.count], 2, 4, 1, 2)
+ self.count += 1
+ self.entry_var[self.count] = QtWidgets.QLineEdit(self)
+ self.trgrid.addWidget(self.entry_var[self.count], 3, 4)
+ self.entry_var[self.count].setMaximumWidth(100)
+ self.count += 1
+ self.entry_var[self.count] = QtWidgets.QPushButton("Add lint_off")
+ self.entry_var[self.count].setMaximumWidth(100)
+ self.trgrid.addWidget(self.entry_var[self.count], 3, 5)
+ self.entry_var[self.count].clicked.connect(self.add_lint_off)
+
+ self.count += 1
+
+ # CSS
+ self.trbox.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; } \
+ ")
+
+ return self.trbox
diff --git a/docker-launcher/src/maker/OpenROAD.py b/docker-launcher/src/maker/OpenROAD.py
new file mode 100644
index 0000000..fa00026
--- /dev/null
+++ b/docker-launcher/src/maker/OpenROAD.py
@@ -0,0 +1,63 @@
+import os
+import subprocess
+from PyQt5 import QtWidgets
+
+class OpenROADLogic:
+ def __init__(self, project_path):
+ """
+ Initialize with the project path from eSim.
+ """
+ self.project_path = project_path
+ self.project_name = os.path.basename(project_path)
+
+ def run(self):
+ """
+ Main execution flow: Netlist -> Verilog -> OpenROAD Synthesis
+ """
+ print(f"\n[OpenROAD Flow] Initiating for: {self.project_name}")
+
+ # 1. Define Absolute Paths
+ # Using expanduser("~") ensures it works for /home/soumy/ on any machine
+ home_dir = os.path.expanduser("~")
+
+ # Path to your Netlist-to-Verilog script from Task 1
+ script_path = os.path.join(home_dir, "eSim", "src", "maker", "netlist_to_verilog.py")
+
+ # Path to the circuit netlist inside the project folder
+ netlist_path = os.path.join(self.project_path, f"{self.project_name}.cir.out")
+
+ # 2. Validation Check
+ if not os.path.exists(netlist_path):
+ print(f"[Error] Netlist not found at: {netlist_path}")
+ QtWidgets.QMessageBox.critical(
+ None, "Error",
+ "Netlist (.cir.out) not found!\n\nPlease run 'Convert KiCad to Ngspice' first."
+ )
+ return
+
+ # 3. Trigger the actual conversion script using Subprocess
+ try:
+ print(f"[OpenROAD] Executing: python3 {script_path} {netlist_path}")
+
+ # This line officially bridges eSim to your Verilog converter
+ result = subprocess.run(['python3', script_path, netlist_path], capture_output=True, text=True)
+
+ if result.returncode == 0:
+ print(f"[Success] {result.stdout}")
+ QtWidgets.QMessageBox.information(
+ None, "Success",
+ f"Verilog conversion successful for '{self.project_name}'!\n\nReady for OpenROAD synthesis."
+ )
+ else:
+ print(f"[Script Error] {result.stderr}")
+ QtWidgets.QMessageBox.warning(
+ None, "Script Error",
+ f"The conversion script failed:\n\n{result.stderr}"
+ )
+
+ except Exception as e:
+ print(f"[System Error] {str(e)}")
+ QtWidgets.QMessageBox.critical(
+ None, "Execution Error",
+ f"Could not trigger the conversion script:\n{str(e)}"
+ )
\ No newline at end of file
diff --git a/docker-launcher/src/maker/createkicad.py b/docker-launcher/src/maker/createkicad.py
new file mode 100644
index 0000000..562fd5e
--- /dev/null
+++ b/docker-launcher/src/maker/createkicad.py
@@ -0,0 +1,379 @@
+# ==============================================================================
+# FILE: createkicad.py
+#
+# USAGE: ---
+#
+# DESCRIPTION: This define all components of to create the Kicad Library.
+#
+# OPTIONS: ---
+# REQUIREMENTS: ---
+# BUGS: ---
+# NOTES: ---
+# AUTHOR: Sumanto Kar, sumantokar@iitb.ac.in, FOSSEE, IIT Bombay
+# ACKNOWLEDGEMENTS: Rahul Paknikar, rahulp@iitb.ac.in, FOSSEE, IIT Bombay
+# Digvijay Singh, digvijay.singh@iitb.ac.in, FOSSEE, IIT Bombay
+# Prof. Maheswari R. and Team, VIT Chennai
+# GUIDED BY: Steve Hoover, Founder Redwood EDA
+# Kunal Ghosh, VLSI System Design Corp.Pvt.Ltd
+# Anagha Ghosh, VLSI System Design Corp.Pvt.Ltd
+# OTHER CONTRIBUTERS:
+# Prof. Madhuri Kadam, Shree L. R. Tiwari College of Engineering
+# Rohinth Ram, Madras Institue of Technology
+# Charaan S., Madras Institue of Technology
+# Nalinkumar S., Madras Institue of Technology
+# Partha Singha Roy, Kalyani Government Engineering College
+# ORGANIZATION: eSim Team at FOSSEE, IIT Bombay
+# CREATED: Monday 29, November 2021
+# REVISION: Friday 16, June 2023
+# ==============================================================================
+
+from . import Appconfig
+import re
+import os
+import xml.etree.cElementTree as ET
+from PyQt5 import QtWidgets
+
+
+class AutoSchematic:
+ def init(self, modelname, modelpath):
+ self.App_obj = Appconfig.Appconfig()
+ self.modelname = modelname.split('.')[0]
+ self.template = self.App_obj.kicad_sym_template.copy()
+ self.xml_loc = self.App_obj.xml_loc
+ self.lib_loc = self.App_obj.lib_loc
+ self.modelpath = modelpath
+ if os.name == 'nt':
+ eSim_src = self.App_obj.src_home
+ inst_dir = eSim_src.replace('\\eSim', '')
+ self.kicad_ngveri_sym = \
+ inst_dir + '/KiCad/share/kicad/symbols/eSim_Ngveri.kicad_sym'
+ else:
+ self.kicad_ngveri_sym = \
+ os.path.join(self.App_obj.src_home, 'library/kicadLibrary/eSim-symbols/eSim_Ngveri.kicad_sym')
+ # self.parser = self.App_obj.parser_ngveri
+
+ def createKicadSymbol(self):
+ '''
+ creating KiCad library using this function
+ '''
+ xmlFound = None
+ for root, dirs, files in os.walk(self.xml_loc):
+ if (str(self.modelname) + '.xml') in files:
+ xmlFound = root
+ print(xmlFound)
+ break
+
+ if xmlFound is None:
+ self.getPortInformation()
+ self.createXML()
+ self.createSym()
+
+ elif (xmlFound == os.path.join(self.xml_loc, 'Ngveri')):
+ print('Library already exists...')
+ ret = QtWidgets.QMessageBox.warning(
+ None, "Warning", '''Library files for this model''' +
+ ''' already exist. Do you want to overwrite it?
+ If yes press ok, else cancel it and ''' +
+ '''change the name of your verilog model.''',
+ QtWidgets.QMessageBox.Ok, QtWidgets.QMessageBox.Cancel
+ )
+
+ if ret == QtWidgets.QMessageBox.Ok:
+ print("Overwriting existing libraries")
+ self.getPortInformation()
+ self.createXML()
+ self.removeOldLibrary() # Removes the existing library
+ self.createSym()
+ else:
+ print("Library Creation Cancelled")
+ return "Error"
+
+ else:
+ print('Pre-existing library...')
+ ret = QtWidgets.QMessageBox.critical(
+ self.parent, "Error", '''A standard library already ''' +
+ '''exists with this name.
Please change the ''' +
+ '''name of your verilog model and add it again.''',
+ QtWidgets.QMessageBox.Ok
+ )
+
+ def getPortInformation(self):
+ '''
+ getting the port information here
+ '''
+ portInformation = PortInfo(self, self.modelpath)
+ portInformation.getPortInfo()
+ self.portInfo = portInformation.bit_list
+ self.input_length = portInformation.input_len
+ self.portName = portInformation.port_name
+
+ def createXML(self):
+ '''
+ creating the XML files at `library/modelParamXML/Ngveri`
+ '''
+ cwd = os.getcwd()
+ xmlDestination = os.path.join(self.xml_loc, 'Ngveri')
+ self.splitText = ""
+ for bit in self.portInfo[:-1]:
+ self.splitText += bit + "-V:"
+ self.splitText += self.portInfo[-1] + "-V"
+
+ print("changing directory to ", xmlDestination)
+ os.chdir(xmlDestination)
+
+ root = ET.Element("model")
+ ET.SubElement(root, "name").text = self.modelname
+ ET.SubElement(root, "type").text = "Ngveri"
+ ET.SubElement(root, "node_number").text = str(len(self.portInfo))
+ ET.SubElement(root, "title").text = (
+ "Add parameters for " + str(self.modelname))
+ ET.SubElement(root, "split").text = self.splitText
+ param = ET.SubElement(root, "param")
+ ET.SubElement(param, "rise_delay", default="1.0e-9").text = (
+ "Enter Rise Delay (default=1.0e-9)")
+ ET.SubElement(param, "fall_delay", default="1.0e-9").text = (
+ "Enter Fall Delay (default=1.0e-9)")
+ ET.SubElement(param, "input_load", default="1.0e-12").text = (
+ "Enter Input Load (default=1.0e-12)")
+ ET.SubElement(param, "instance_id", default="1").text = (
+ "Enter Instance ID (Between 0-99)")
+
+ tree = ET.ElementTree(root)
+ tree.write(str(self.modelname) + '.xml')
+ print("Leaving the directory ", xmlDestination)
+ os.chdir(cwd)
+
+ def findBlockSize(self):
+ '''
+ Calculates the maximum between input and output ports
+ '''
+ ind = self.input_length
+ return max(
+ self.char_sum(self.portInfo[:ind]),
+ self.char_sum(self.portInfo[ind:])
+ )
+
+ def char_sum(self, ls):
+ return sum([int(x) for x in ls])
+
+ def removeOldLibrary(self):
+ '''
+ removing the old library
+ '''
+ cwd = os.getcwd()
+ os.chdir(self.lib_loc)
+ print("Changing directory to ", self.lib_loc)
+ sym_file = open(self.kicad_ngveri_sym)
+ lines = sym_file.readlines()
+ sym_file.close()
+ lines = lines[0:-2]
+ output = []
+ line_reading_flag = False
+
+ for line in lines:
+ if line.startswith("(symbol"): # Eeschema Template start
+ if line.split()[1] == f"\"{self.modelname}\"":
+ line_reading_flag = True
+ if not line_reading_flag:
+ output.append(line)
+ if line.startswith("))"): # Eeschema Template end
+ line_reading_flag = False
+
+ sym_file = open(self.kicad_ngveri_sym, 'w')
+ for line in output:
+ sym_file.write(line)
+
+ os.chdir(cwd)
+ print("Leaving directory, ", self.lib_loc)
+
+ def createSym(self):
+ '''
+ creating the symbol
+ (pins snapped to KiCad-6 grid)
+ '''
+ self.grid = 0.635
+ self.dist_port = 4 * self.grid # Distance between two ports # 100 mil (= 2.54 mm)
+ self.inc_size = self.dist_port # Increment size of a block (mil)
+ def snap(val):
+ snapped = round(float(val) / self.grid) * self.grid
+ return f"{snapped:.3f}"
+ cwd = os.getcwd()
+ os.chdir(self.lib_loc)
+ print("Changing directory to ", self.lib_loc)
+
+ # Removing ")" from "eSim_Ngveri.kicad_sym"
+ file = open(self.kicad_ngveri_sym, "r")
+ content_file = file.read()
+ new_content_file = content_file[:-2]
+ file.close()
+ file = open(self.kicad_ngveri_sym, "w")
+ file.write(new_content_file)
+ file.close()
+
+ # Appending new schematic block
+ sym_file = open(self.kicad_ngveri_sym, "a")
+ line1 = self.template["start_def"]
+ line1 = line1.split()
+ line1 = [w.replace('comp_name', self.modelname) for w in line1]
+ self.template["start_def"] = ' '.join(line1)
+
+ if os.stat(self.kicad_ngveri_sym).st_size == 0:
+ sym_file.write(
+ "(kicad_symbol_lib (version 20211014) " +
+ "(generator kicad_symbol_editor)" +
+ "\n\n"
+ ) # Eeschema starter code
+
+ # sym_file.write("#encoding utf-8"+ "\n"+ "#"+ "\n" +
+ # "#test_compo" + "\n"+ "#"+ "\n")
+ sym_file.write(
+ self.template["start_def"] + "\n" + self.template["U_field"] + "\n"
+ )
+
+ line3 = self.template["comp_name_field"]
+ line3 = line3.split()
+ line3 = [w.replace('comp_name', self.modelname) for w in line3]
+ self.template["comp_name_field"] = ' '.join(line3)
+
+ sym_file.write(self.template["comp_name_field"] + "\n")
+
+ line4 = self.template["blank_field"]
+ line4_1 = line4[0]
+ line4_2 = line4[1]
+ line4_1 = line4_1.split()
+ line4_1 = [w.replace('blank_quotes', '""') for w in line4_1]
+ line4_2 = line4_2.split()
+ line4_2 = [w.replace('blank_quotes', '""') for w in line4_2]
+ line4[0] = ' '.join(line4_1)
+ line4[1] = ' '.join(line4_2)
+ self.template["blank_qoutes"] = line4
+
+ sym_file.write(line4[0] + "\n" + line4[1] + "\n")
+
+ draw_pos = self.template["draw_pos"]
+ draw_pos = draw_pos.split()
+
+ draw_pos = \
+ [w.replace('comp_name', f"{self.modelname}_0_1") for w in draw_pos]
+ draw_pos[8] = snap(float(draw_pos[8]) + # previously it is (-)
+ float(self.findBlockSize() * self.inc_size))
+ draw_pos_rec = draw_pos[8]
+
+ self.template["draw_pos"] = ' '.join(draw_pos)
+
+ sym_file.write(
+ self.template["draw_pos"] + "\n" + self.template["start_draw"] +
+ " \"" + f"{self.modelname}_1_1\"" + "\n"
+ )
+
+ input_port = self.template["input_port"]
+ input_port = input_port.split()
+ output_port = self.template["output_port"]
+ output_port = output_port.split()
+ input_port[3] = snap(float(input_port[3]))
+ output_port[3] = snap(float(output_port[3]))
+ inputs = self.portInfo[0: self.input_length]
+ outputs = self.portInfo[self.input_length:]
+ inputName = []
+ outputName = []
+
+ for i in range(self.input_length):
+ for j in range(int(inputs[i])):
+ inputName.append(
+ self.portName[i] + str(int(inputs[i]) - j - 1))
+
+ for i in range(self.input_length, len(self.portName)):
+ for j in range(int(outputs[i - self.input_length])):
+ outputName.append(
+ self.portName[i] +
+ str(int(outputs[i - self.input_length]) - j - 1))
+
+ inputs = self.char_sum(inputs)
+ outputs = self.char_sum(outputs)
+
+ total = inputs + outputs
+
+ port_list = []
+
+ # Set input & output port
+ input_port[4] = draw_pos_rec
+ output_port[4] = draw_pos_rec
+
+ j = 0
+ for i in range(total):
+ if (i < inputs):
+ input_port[9] = f"\"{inputName[i]}\""
+ input_port[13] = f"\"{str(i + 1)}\""
+ input_port[4] = \
+ snap(float(input_port[4]) - float(self.dist_port))
+ input_list = ' '.join(input_port)
+ port_list.append(input_list)
+ j = j + 1
+
+ else:
+ output_port[9] = f"\"{outputName[i - inputs]}\""
+ output_port[13] = f"\"{str(i + 1)}\""
+ output_port[4] = \
+ snap(float(output_port[4]) - float(self.dist_port))
+ output_list = ' '.join(output_port)
+ port_list.append(output_list)
+
+ for ports in port_list:
+ sym_file.write(ports + "\n")
+ sym_file.write(
+ self.template["end_draw"] + "\n\n\n"+")"
+ )
+ sym_file.close()
+ os.chdir(cwd)
+
+
+class PortInfo:
+ '''
+ The class contains port information
+ '''
+ def __init__(self, model, modelpath):
+ self.modelname = model.modelname
+ self.bit_list = []
+ self.port_name = []
+ self.input_len = 0
+ self.modelpath = modelpath
+
+ def getPortInfo(self):
+ '''
+ getting the port information from `connection_info.txt`
+ '''
+ input_list = []
+ output_list = []
+ read_file = open(self.modelpath + 'connection_info.txt', 'r')
+ data = read_file.readlines()
+ # print(data)
+ read_file.close()
+
+ for line in data:
+ if re.match(r'^\s*$', line):
+ pass
+ else:
+ in_items = re.findall(
+ "INPUT", line, re.MULTILINE | re.IGNORECASE
+ )
+ inout_items = re.findall(
+ "INOUT", line, re.MULTILINE | re.IGNORECASE
+ )
+
+ out_items = re.findall(
+ "OUTPUT", line, re.MULTILINE | re.IGNORECASE
+ )
+ if in_items:
+ input_list.append(line.split())
+ if inout_items:
+ input_list.append(line.split())
+ if out_items:
+ output_list.append(line.split())
+
+ for in_list in input_list:
+ self.bit_list.append(in_list[2])
+ self.port_name.append(in_list[0])
+ self.input_len = len(self.bit_list)
+ for out_list in output_list:
+ self.bit_list.append(out_list[2])
+ self.port_name.append(out_list[0])
diff --git a/docker-launcher/src/maker/netlist_to_verilog.py b/docker-launcher/src/maker/netlist_to_verilog.py
new file mode 100644
index 0000000..0bab50b
--- /dev/null
+++ b/docker-launcher/src/maker/netlist_to_verilog.py
@@ -0,0 +1,289 @@
+import os
+import re
+import sys
+import shutil
+import subprocess
+
+
+# ─────────────────────────────────────────────────────────────
+# UTILITY FUNCTIONS
+# ─────────────────────────────────────────────────────────────
+
+def fail(message, code=1):
+ print(f"[✗] {message}", file=sys.stderr)
+ sys.exit(code)
+
+
+def write_file(path, content):
+ os.makedirs(os.path.dirname(path), exist_ok=True)
+ with open(path, "w", encoding="utf-8") as f:
+ f.write(content)
+
+
+def normalize_module_name(name):
+ """
+ Preserves original case (Halfwave_Rectifier stays Halfwave_Rectifier).
+ Only fixes invalid characters and collapses double underscores.
+ """
+ name = re.sub(r'[^a-zA-Z0-9_]', '_', name)
+ name = re.sub(r'_+', '_', name)
+ return name.strip('_')
+
+
+# ─────────────────────────────────────────────────────────────
+# ALL ORFS STAGE ODB FILES IN ORDER
+# Script will check from last to first and open
+# the GUI at whichever stage completed successfully.
+# ─────────────────────────────────────────────────────────────
+
+STAGE_ODBS = [
+ ("6_final.odb", "Stage 6 - Final"),
+ ("5_route.odb", "Stage 5 - Routing"),
+ ("4_cts.odb", "Stage 4 - Clock Tree Synthesis"),
+ ("3_5_place_dp.odb", "Stage 3.5 - Detail Placement"),
+ ("3_4_place_resized.odb", "Stage 3.4 - Resizing"),
+ ("3_3_place_mpl.odb", "Stage 3.3 - Macro Placement"),
+ ("3_2_place_iop.odb", "Stage 3.2 - IO Placement"),
+ ("3_1_place_gp.odb", "Stage 3.1 - Global Placement"),
+ ("2_floorplan.odb", "Stage 2 - Floorplan"),
+ ("1_synth.odb", "Stage 1 - Synthesis"),
+]
+
+
+def find_latest_odb(orfs_flow, module_name):
+ """
+ Checks all stage ODB files from last to first.
+ Returns (odb_filename, stage_label) of the furthest completed stage.
+ """
+ base_dir = os.path.join(orfs_flow, "results", "sky130hd", module_name, "base")
+
+ print(f"\n[*] Checking completed stages in: {base_dir}")
+
+ for odb_file, stage_label in STAGE_ODBS:
+ full_path = os.path.join(base_dir, odb_file)
+ if os.path.isfile(full_path):
+ print(f"[✓] Found: {stage_label} → {odb_file}")
+ return odb_file, stage_label
+
+ return None, None
+
+
+# ─────────────────────────────────────────────────────────────
+# VERILOG SEARCH
+# ─────────────────────────────────────────────────────────────
+
+def find_generated_verilog(selected_path, project_name, orfs_design_dir):
+ project_dir = os.path.dirname(selected_path) or "."
+ home_dir = os.path.expanduser("~")
+
+ candidates = [
+ os.path.join(project_dir, f"{project_name}.v"),
+ os.path.join(home_dir, f"{project_name}.v"),
+ os.path.join(orfs_design_dir, f"{project_name}.v"),
+ selected_path.replace(".cir.out", ".v"),
+ ]
+
+ print("[*] Looking for Verilog file in:")
+ for path in candidates:
+ print(f" - {path}")
+ if os.path.isfile(path):
+ print(f"[✓] Found Verilog: {path}")
+ return path
+
+ return None
+
+
+# ─────────────────────────────────────────────────────────────
+# VERILOG INSPECTION
+# ─────────────────────────────────────────────────────────────
+
+def extract_top_module_name(verilog_path):
+ with open(verilog_path, "r", encoding="utf-8") as f:
+ content = f.read()
+ match = re.search(r"^\s*module\s+([A-Za-z_][A-Za-z0-9_]*)", content, re.MULTILINE)
+ if not match:
+ fail(f"Could not find module name in: {verilog_path}")
+ return match.group(1)
+
+
+def is_clocked_design(verilog_path):
+ with open(verilog_path, "r", encoding="utf-8") as f:
+ content = f.read()
+ return (
+ re.search(r"\bposedge\b", content) is not None or
+ re.search(r"\bnegedge\b", content) is not None or
+ re.search(r"\binput\s+.*\bclk\b", content, re.IGNORECASE) is not None or
+ re.search(r"\binput\b[^;\n]*\bclock\b", content, re.IGNORECASE) is not None
+ )
+
+
+# ─────────────────────────────────────────────────────────────
+# FILE BUILDERS
+# ─────────────────────────────────────────────────────────────
+
+def build_config(module_name, project_name):
+ return f"""export DESIGN_NAME = {module_name}
+export PLATFORM = sky130hd
+export VERILOG_FILES = ./designs/sky130hd/{project_name}/{project_name}.v
+export SDC_FILE = ./designs/sky130hd/{project_name}/constraint.sdc
+export DIE_AREA = 0 0 100 100
+export CORE_AREA = 10 10 90 90
+"""
+
+
+def build_sdc(is_clocked):
+ if is_clocked:
+ return "create_clock [get_ports clk] -period 10\n"
+ return "set_units -time ns\n"
+
+
+def build_gui_tcl(module_name, stage_odb):
+ return (
+ f"read_db ./results/sky130hd/{module_name}/base/{stage_odb}\n"
+ "gui::fit\n"
+ )
+
+
+# ─────────────────────────────────────────────────────────────
+# MAIN
+# ─────────────────────────────────────────────────────────────
+
+def main():
+ if len(sys.argv) < 2:
+ fail("Usage: python3 openroad_integration.py ")
+
+ selected_path = os.path.abspath(sys.argv[1])
+ project_name = os.path.basename(selected_path).replace(".cir.out", "")
+
+ print(f"\n{'='*60}")
+ print(f" OpenROAD Integration — Project: {project_name}")
+ print(f"{'='*60}\n")
+ print(f"[*] Selected file: {selected_path}")
+
+ # ── ORFS paths ───────────────────────────────────────────
+ orfs_root = os.path.expanduser("~/OpenROAD-flow-scripts")
+ orfs_flow = os.path.join(orfs_root, "flow")
+ docker_shell = os.path.join(orfs_flow, "util", "docker_shell")
+
+ if not os.path.isdir(orfs_flow):
+ fail(f"OpenROAD flow directory not found: {orfs_flow}")
+ if not os.path.isfile(docker_shell):
+ fail(f"docker_shell not found: {docker_shell}")
+ if shutil.which("docker") is None:
+ fail("docker is not installed or not in PATH")
+
+ # ── Design directory ─────────────────────────────────────
+ design_dir = os.path.join(orfs_flow, "designs", "sky130hd", project_name)
+ os.makedirs(design_dir, exist_ok=True)
+
+ # ── Find Verilog ─────────────────────────────────────────
+ verilog_path = find_generated_verilog(selected_path, project_name, design_dir)
+
+ if verilog_path is None:
+ fail(
+ f"Verilog file '{project_name}.v' not found.\n"
+ f"Please place your Verilog file in ONE of these locations:\n"
+ f" 1. {os.path.dirname(selected_path)}/{project_name}.v\n"
+ f" 2. ~/{project_name}.v\n"
+ f" 3. {design_dir}/{project_name}.v"
+ )
+
+ # ── Extract module info ──────────────────────────────────
+ module_name = extract_top_module_name(verilog_path)
+ module_name = normalize_module_name(module_name)
+ clocked = is_clocked_design(verilog_path)
+
+ print(f"[*] Module name : {module_name}")
+ print(f"[*] Design type : {'clocked' if clocked else 'combinational'}")
+ print(f"[*] Flow target : {'finish' if clocked else 'place'}")
+
+ # ── Copy Verilog + write config files ────────────────────
+ target_verilog = os.path.join(design_dir, f"{project_name}.v")
+ target_sdc = os.path.join(design_dir, "constraint.sdc")
+ target_config = os.path.join(design_dir, "config.mk")
+
+ shutil.copyfile(verilog_path, target_verilog)
+ print(f"[*] Copied Verilog : {target_verilog}")
+
+ write_file(target_sdc, build_sdc(clocked))
+ print(f"[*] Wrote SDC : {target_sdc}")
+
+ write_file(target_config, build_config(module_name, project_name))
+ print(f"[*] Wrote config.mk : {target_config}")
+
+ # ── Environment ──────────────────────────────────────────
+ env = os.environ.copy()
+ env["QT_X11_NO_MITSHM"] = "1"
+ env["LIBGL_ALWAYS_SOFTWARE"] = "1"
+ env["OPENROAD_EXE"] = "/OpenROAD-flow-scripts/tools/install/OpenROAD/bin/openroad"
+ env["YOSYS_EXE"] = "/usr/local/bin/yosys"
+
+ # ── Run ORFS flow ─────────────────────────────────────────
+ design_config = f"./designs/sky130hd/{project_name}/config.mk"
+ flow_target = "finish"
+
+ cmd = [docker_shell, "make", f"DESIGN_CONFIG={design_config}", flow_target]
+
+ print(f"\n[*] Running: make DESIGN_CONFIG={design_config} {flow_target}")
+ print("-" * 60)
+
+ result = subprocess.run(
+ cmd,
+ cwd=orfs_flow,
+ env=env,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ text=True,
+ )
+ print(result.stdout)
+
+ if result.returncode != 0:
+ print(f"[!] Flow exited with code {result.returncode} — checking how far it got...")
+ else:
+ print(f"[✓] Flow completed successfully!")
+
+ # ── Find latest completed ODB ─────────────────────────────
+ # Works whether flow succeeded OR failed halfway.
+ # Opens GUI at whatever stage was last completed.
+ stage_odb, stage_label = find_latest_odb(orfs_flow, module_name)
+
+ if stage_odb is None:
+ fail(
+ f"No ODB files found at any stage.\n"
+ f"Flow likely failed at synthesis. Check logs:\n"
+ f" ~/OpenROAD-flow-scripts/flow/logs/sky130hd/{module_name}/base/"
+ )
+
+ print(f"\n[*] Opening GUI at: {stage_label}")
+
+ # ── Launch GUI ────────────────────────────────────────────
+ gui_tcl_path = os.path.join(orfs_flow, f"open_gui_{module_name}.tcl")
+ write_file(gui_tcl_path, build_gui_tcl(module_name, stage_odb))
+ print(f"[*] Wrote GUI Tcl: {gui_tcl_path}")
+
+ gui_cmd = [
+ docker_shell,
+ "openroad", "-gui",
+ f"./{os.path.basename(gui_tcl_path)}",
+ ]
+
+ print(f"[*] Launching: openroad -gui ./{os.path.basename(gui_tcl_path)}")
+
+ gui_result = subprocess.run(
+ gui_cmd,
+ cwd=orfs_flow,
+ env=env,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ text=True,
+ )
+ print(gui_result.stdout)
+
+ if gui_result.returncode != 0:
+ fail(f"OpenROAD GUI failed with exit code {gui_result.returncode}")
+
+ print(f"\n[✓] OpenROAD GUI closed for '{project_name}'")
+
+
+if __name__ == "__main__":
+ main()
From 1063a93389d93dce262b999ed07e102e752ccc46 Mon Sep 17 00:00:00 2001
From: Rishabh Jain <2r10j5@gmail.com>
Date: Wed, 13 May 2026 11:42:45 +0530
Subject: [PATCH 2/8] Update README with Docker setup details
Added information about the Docker environment and setup workflow.
---
README.md | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/README.md b/README.md
index 4b7cc0d..17aaa63 100644
--- a/README.md
+++ b/README.md
@@ -24,11 +24,15 @@ This project is intended for the FOSSEE eSim ecosystem and Ubuntu-based setup wo
| `orfs-setup.py` | OpenROAD Flow Scripts setup helper. |
| `install.md` | Detailed installation and usage guide. |
| `contribution.md` | Contribution guidelines. |
+| `docker-launcher/` | Docker environment and launcher scripts for running the eSim OpenROAD integration workflow. |
+
## Documentation
For the full setup workflow, see [`install.md`](install.md).
+For the docker setup workflow, see [`docker.md`](docker-launcher/README.md)
+
For contribution guidelines, see [`contribution.md`](contribution.md).
## License
From 85343cb04ae5966bde8bb758f98537b1fcc539f8 Mon Sep 17 00:00:00 2001
From: Rishabh Jain <2r10j5@gmail.com>
Date: Wed, 13 May 2026 13:02:57 +0530
Subject: [PATCH 3/8] Update modification details in Application.py
Added modification details for Rishabh Jain.
---
src/frontEnd/Application.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/frontEnd/Application.py b/src/frontEnd/Application.py
index 2c5bdb2..65f1f7e 100644
--- a/src/frontEnd/Application.py
+++ b/src/frontEnd/Application.py
@@ -13,6 +13,7 @@
# MAINTAINED: Rahul Paknikar, rahulp@iitb.ac.in
# Sumanto Kar, sumantokar@iitb.ac.in
# Pranav P, pranavsdreams@gmail.com
+# MODIFIED: Rishabh Jain, 2r10j5@gmail.com
# ORGANIZATION: eSim Team at FOSSEE, IIT Bombay
# CREATED: Tuesday 24 February 2015
# REVISION: Wednesday 07 June 2023
From 43a76ef1b97ac18a1c39173b0313f2ff653261ae Mon Sep 17 00:00:00 2001
From: Rishabh Jain <2r10j5@gmail.com>
Date: Wed, 13 May 2026 13:08:19 +0530
Subject: [PATCH 4/8] Add documentation header to netlist2rtl.py
Added header comments to netlist2rtl.py for documentation.
---
src/maker/netlist2rtl.py | 19 ++++++++++++++++++-
1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/src/maker/netlist2rtl.py b/src/maker/netlist2rtl.py
index cbda2f6..9c2731c 100644
--- a/src/maker/netlist2rtl.py
+++ b/src/maker/netlist2rtl.py
@@ -1,3 +1,20 @@
+# =========================================================================
+# FILE: netlist2rtl.py
+#
+# USAGE: ---
+#
+# DESCRIPTION: This file is used to convert netlist to verilog
+#
+# OPTIONS: ---
+# REQUIREMENTS: ---
+# BUGS: ---
+# NOTES: ---
+# AUTHOR: Rishabh Jain, 2r10j5@gmail.com
+# MAINTAINED: Sumanto Kar, sumantokar@iitb.ac.in
+# ORGANIZATION: eSim Team at FOSSEE, IIT Bombay
+# CREATED: Monday 2 March 2026
+# =========================================================================
+
import os
import sys
@@ -203,4 +220,4 @@ def main():
if __name__ == "__main__":
- main()
\ No newline at end of file
+ main()
From 4711815dadf588919dc3f06a85b365e80a32636d Mon Sep 17 00:00:00 2001
From: Rishabh Jain <2r10j5@gmail.com>
Date: Wed, 13 May 2026 13:10:04 +0530
Subject: [PATCH 5/8] Add file header and metadata to OpenROAD.py
Added header comments with metadata for the OpenROAD.py file.
---
src/maker/OpenROAD.py | 20 ++++++++++++++++++--
1 file changed, 18 insertions(+), 2 deletions(-)
diff --git a/src/maker/OpenROAD.py b/src/maker/OpenROAD.py
index 6410b4e..b009c10 100644
--- a/src/maker/OpenROAD.py
+++ b/src/maker/OpenROAD.py
@@ -1,9 +1,25 @@
+# =========================================================================
+# FILE: OpenROAD.py
+#
+# USAGE: ---
+#
+# DESCRIPTION: This file is used to connect with orfs to esim
+#
+# OPTIONS: ---
+# REQUIREMENTS: ---
+# BUGS: ---
+# NOTES: ---
+# AUTHOR: Rishabh Jain, 2r10j5@gmail.com
+# MAINTAINED: Sumanto Kar, sumantokar@iitb.ac.in
+# ORGANIZATION: eSim Team at FOSSEE, IIT Bombay
+# CREATED: Monday 2 March 2026
+# =========================================================================
+
import os
import shutil
import subprocess
import sys
-
class OpenROADFlow:
def __init__(
@@ -320,4 +336,4 @@ def run(self):
verilog_file
)
- flow.run()
\ No newline at end of file
+ flow.run()
From c645baaf340370eff16907b68d54dd41278a12bb Mon Sep 17 00:00:00 2001
From: Rishabh Jain <2r10j5@gmail.com>
Date: Wed, 13 May 2026 13:11:26 +0530
Subject: [PATCH 6/8] Add metadata comments to orfs-setup.py
Added header comments to provide file metadata.
---
orfs-setup.py | 20 +++++++++++++++++++-
1 file changed, 19 insertions(+), 1 deletion(-)
diff --git a/orfs-setup.py b/orfs-setup.py
index e0eedcc..a96fe22 100644
--- a/orfs-setup.py
+++ b/orfs-setup.py
@@ -1,4 +1,22 @@
#!/usr/bin/env python3
+
+# =========================================================================
+# FILE: OpenROAD.py
+#
+# USAGE: ---
+#
+# DESCRIPTION: This file is used setup of orfs
+#
+# OPTIONS: ---
+# REQUIREMENTS: ---
+# BUGS: ---
+# NOTES: ---
+# AUTHOR: Rishabh Jain, 2r10j5@gmail.com
+# MAINTAINED: Sumanto Kar, sumantokar@iitb.ac.in
+# ORGANIZATION: eSim Team at FOSSEE, IIT Bombay
+# CREATED: Monday 2 March 2026
+# =========================================================================
+
import os,sys,shutil,subprocess
from pathlib import Path
@@ -243,4 +261,4 @@ def main():
print("================================================")
if __name__=="__main__":
- main()
\ No newline at end of file
+ main()
From 4e19d162dc7de28f9b762846f32f55fe65777d38 Mon Sep 17 00:00:00 2001
From: Rishabh Jain <2r10j5@gmail.com>
Date: Wed, 13 May 2026 13:38:11 +0530
Subject: [PATCH 7/8] Revise eSim-OpenROAD Installation Guide
Updated installation guide for eSim-OpenROAD plugin, including restructured sections and improved formatting.
---
install.md | 76 +++++++++++++++++++-----------------------------------
1 file changed, 27 insertions(+), 49 deletions(-)
diff --git a/install.md b/install.md
index be313e2..3405579 100644
--- a/install.md
+++ b/install.md
@@ -1,8 +1,6 @@
-# eSim–OpenROAD Design Flow Plugin Installation Guide
+# eSim-OpenROAD Design Flow Plugin Installation Guide
-**Platform:** Ubuntu 22.04
-
----
+## **Platform :** Ubuntu 22.04 LTS
# 1. Clone the Repository
@@ -11,7 +9,6 @@ Open a terminal and run:
```bash
git clone https://github.com/FOSSEE/eSim-to-OpenROAD_Design_Flow_Plugin.git
```
-
---
# 2. Move to the Project Directory
@@ -22,7 +19,9 @@ cd eSim-to-OpenROAD_Design_Flow_Plugin
---
-# 3. Install eSim and Required Dependencies
+# 3. eSim-2.5 Installation and Uninstallation
+
+## • Install eSim and Required Dependencies
Give execution permission to the installation script and run it:
@@ -37,9 +36,19 @@ This step installs:
- OpenROAD dependencies
- Required tools and libraries
+## • Uninstall eSim and All Components
+
+```bash
+./install-eSim.sh --uninstall
+```
+
+This removes eSim and all installed components from the system.
+
---
-# 4. Build OpenROAD Flow Scripts Locally
+# 4. Build OpenROAD Flow Scripts
+
+## • Build orfs Locally
Run the following command:
@@ -49,9 +58,7 @@ python3 orfs-setup.py
This command builds the OpenROAD Flow Scripts required for RTL-to-GDSII generation.
----
-
-# 5. Rebuild OpenROAD Flow Scripts (If Build Fails)
+## • Rebuild orfs (If Build Fails)
If the build process fails:
@@ -64,40 +71,34 @@ python3 orfs-setup.py
---
-# Running the Tools
+# 5. Running the Tools
-# 6. Run eSim
+## • Run eSim
-## Using Terminal
+### 1. Using Terminal
```bash
esim
```
-## Using Desktop Shortcut
+### 2. Using Desktop Shortcut
Double-click the **eSim** desktop icon.
----
-
-# 7. Run OpenROAD GUI
+## • Run OpenROAD GUI
```bash
cd ~/OpenROAD-flow-scripts/flow
openroad -gui
```
----
-
-# 8. Run Yosys
+## • Run Yosys
```bash
yosys
```
----
-
-# 9. Run KLayout
+## • Run KLayout
```bash
klayout
@@ -105,17 +106,7 @@ klayout
---
-# 10. Uninstall eSim and All Components
-
-```bash
-./install-eSim.sh --uninstall
-```
-
-This removes eSim and all installed components from the system.
-
----
-
-# Viewing Layouts in OpenROAD GUI
+# 6. Viewing Layouts in OpenROAD GUI
Start the OpenROAD GUI:
@@ -124,9 +115,7 @@ cd ~/OpenROAD-flow-scripts/flow
openroad -gui
```
----
-
-## View Half Adder Layout
+## • View Half Adder Layout TCL Commands
```tcl
read_lef platforms/sky130hd/lef/sky130_fd_sc_hd.tlef
@@ -138,9 +127,7 @@ read_def results/sky130hd/Half_Adder/base/6_final.def
gui::fit
```
----
-
-## View Full Adder Layout
+## • View Full Adder Layout TCL Commands
```tcl
read_lef platforms/sky130hd/lef/sky130_fd_sc_hd.tlef
@@ -151,12 +138,3 @@ read_def results/sky130hd/FullAdder/base/6_final.def
gui::fit
```
-
----
-
-# Notes
-
-- Recommended Operating System: **Ubuntu 22.04**
-- Ensure Python 3 is installed before running the setup script.
-- Internet connection is required during installation.
-- Use terminal commands carefully with proper permissions.
\ No newline at end of file
From 81c11f575ec4b06dabc3a1ce4858769b195fad8d Mon Sep 17 00:00:00 2001
From: Rishabh Jain <2r10j5@gmail.com>
Date: Wed, 13 May 2026 13:42:37 +0530
Subject: [PATCH 8/8] Enhance install-eSim.sh with documentation and metadata
Added header comments with usage instructions and author details.
---
install-eSim.sh | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/install-eSim.sh b/install-eSim.sh
index 998cbf4..ab8e900 100644
--- a/install-eSim.sh
+++ b/install-eSim.sh
@@ -1,4 +1,24 @@
#!/bin/bash
+#=============================================================================
+# FILE: install-eSim.sh
+#
+# USAGE: ./install-eSim.sh --install
+# OR
+# ./install-eSim.sh --uninstall
+#
+# DESCRIPTION: Installation script for eSim EDA Suite
+#
+# OPTIONS: ---
+# REQUIREMENTS: ---
+# BUGS: ---
+# NOTES: ---
+# AUTHORS: Fahim Khan, Rahul Paknikar, Saurabh Bansode,
+# Sumanto Kar, Partha Singha Roy, Jayanth Tatineni,
+# Anshul Verma, Shiva Krishna Sangati, Harsha Narayana P
+# ORGANIZATION: eSim Team, FOSSEE, IIT Bombay
+# CREATED: Sunday 25 May 2025 17:40
+# REVISION: ---
+#=============================================================================
# Function to detect Ubuntu version and full version string
get_ubuntu_version() {