diff --git a/src/frontEnd/Application.py b/src/frontEnd/Application.py
index 35aae135f..7fa06776a 100644
--- a/src/frontEnd/Application.py
+++ b/src/frontEnd/Application.py
@@ -1,7 +1,7 @@
# =========================================================================
-# FILE: Application.py
+# FILE: Application.py
#
-# USAGE: ---
+# USAGE: ---
#
# DESCRIPTION: This main file use to start the Application
#
@@ -149,23 +149,6 @@ def initToolBar(self):
self.topToolbar.addAction(self.helpfile)
self.topToolbar.addAction(self.devdocs)
- # ## This part is meant for SoC Generation which is currently ##
- # ## under development and will be will be required in future. ##
- # self.soc = QtWidgets.QToolButton(self)
- # self.soc.setText('Generate SoC')
- # self.soc.setToolTip(
- # 'SPICE to Verilog Conversion
' + \
- # '
The feature is under development.' + \
- # '
It will be released soon.' + \
- # '
Thank you for your patience!!!'
- # )
- # self.soc.setStyleSheet(" \
- # QWidget { border-radius: 15px; border: 1px \
- # solid gray; padding: 10px; margin-left: 20px; } \
- # ")
- # self.soc.clicked.connect(self.showSoCRelease)
- # self.topToolbar.addWidget(self.soc)
-
# This part is setting fossee logo to the right
# corner in the application window.
self.spacer = QtWidgets.QWidget()
@@ -226,6 +209,15 @@ def initToolBar(self):
)
self.makerchip.triggered.connect(self.open_makerchip)
+ # --- NEW OPENROAD ACTION ADDED HERE ---
+ self.openroad = QtWidgets.QAction(
+ QtGui.QIcon(init_path + 'images/icon.png'),
+ 'OpenROAD-GDSII', self
+ )
+ self.openroad.setToolTip('Synthesize and Route design to GDSII using OpenROAD')
+ self.openroad.triggered.connect(self.run_openroad_flow)
+ # ---------------------------------------
+
self.omedit = QtWidgets.QAction(
QtGui.QIcon(init_path + 'images/omedit.png'),
'Modelica Converter', self
@@ -254,6 +246,7 @@ def initToolBar(self):
self.lefttoolbar.addAction(self.subcircuit)
self.lefttoolbar.addAction(self.makerchip)
self.lefttoolbar.addAction(self.nghdl)
+ self.lefttoolbar.addAction(self.openroad)
self.lefttoolbar.addAction(self.omedit)
self.lefttoolbar.addAction(self.omoptim)
self.lefttoolbar.addAction(self.conToeSim)
@@ -261,9 +254,7 @@ def initToolBar(self):
self.lefttoolbar.setIconSize(QSize(40, 40))
def plotFlagPopBox(self):
- """This function displays a pop-up box with message- Do you want Ngspice plots? and oprions Yes and NO.
-
- If the user clicks on Yes, both the NgSpice and python plots are displayed and if No is clicked then only the python plots."""
+ """Displays a pop-up box for Ngspice plots."""
msg_box = QtWidgets.QMessageBox(self)
msg_box.setWindowTitle("Ngspice Plots")
@@ -282,23 +273,7 @@ def plotFlagPopBox(self):
self.open_ngspice()
def closeEvent(self, event):
- '''
- This function closes the ongoing program (process).
- When exit button is pressed a Message box pops out with \
- exit message and buttons 'Yes', 'No'.
-
- 1. If 'Yes' is pressed:
- - check that program (process) in procThread_list \
- (a list made in Appconfig.py):
-
- - if available it terminates that program.
- - if the program (process) is not available, \
- then check it in process_obj (a list made in \
- Appconfig.py) and if found, it closes the program.
-
- 2. If 'No' is pressed:
- - the program just continues as it was doing earlier.
- '''
+ '''Closes the ongoing program.'''
exit_msg = "Are you sure you want to exit the program?"
exit_msg += " All unsaved data will be lost."
reply = QtWidgets.QMessageBox.question(
@@ -321,8 +296,6 @@ def closeEvent(self, event):
except BaseException:
pass
- # Check if "Open project" and "New project" window is open.
- # If yes, just close it when application is closed.
try:
self.project.close()
except BaseException:
@@ -334,7 +307,7 @@ def closeEvent(self, event):
event.ignore()
def new_project(self):
- """This function call New Project Info class."""
+ """Calls New Project Info class."""
text, ok = QtWidgets.QInputDialog.getText(
self, 'New Project Info', 'Enter Project Name:'
)
@@ -363,7 +336,7 @@ def new_project(self):
pass
def open_project(self):
- """This project call Open Project Info class."""
+ """Calls Open Project Info class."""
print("Function : Open Project")
self.project = OpenProjectInfo()
try:
@@ -374,17 +347,7 @@ def open_project(self):
pass
def close_project(self):
- """
- This function closes the saved project.
- It first checks whether project (file) is present in list.
-
- - If present:
- - it first kills that process-id.
- - closes that file.
- - Shows message "Current project is closed"
-
- - If not present: pass
- """
+ """Closes the saved project."""
print("Function : Close Project")
current_project = self.obj_appconfig.current_project['ProjectName']
if current_project is None:
@@ -404,31 +367,21 @@ def close_project(self):
)
def change_workspace(self):
- """
- This function call changes Workspace
- """
+ """Changes Workspace"""
print("Function : Change Workspace")
self.obj_workspace.returnWhetherClickedOrNot(self)
self.hide()
self.obj_workspace.show()
def help_project(self):
- """
- This function opens usermanual in dockarea.
- - It prints the message ""Function : Help""
- - Uses print_info() method of class Appconfig
- from Configuration/Appconfig.py file.
- - Call method usermanual() from ./DockArea.py.
- """
+ """Opens usermanual in dockarea."""
print("Function : Help")
self.obj_appconfig.print_info('Help is called')
print("Current Project is : ", self.obj_appconfig.current_project)
self.obj_Mainview.obj_dockarea.usermanual()
def dev_docs(self):
- """
- This function guides the user to readthedocs website for the developer docs
- """
+ """Guides user to readthedocs"""
print("Function : DevDocs")
self.obj_appconfig.print_info('DevDocs is called')
print("Current Project is : ", self.obj_appconfig.current_project)
@@ -436,9 +389,7 @@ def dev_docs(self):
@QtCore.pyqtSlot(QtCore.QProcess.ExitStatus, int)
def plotSimulationData(self, exitCode, exitStatus):
- """Enables interaction for new simulation and
- displays the plotter dock where graphs can be plotted.
- """
+ """Enables interaction for new simulation"""
self.ngspice.setEnabled(True)
self.conversion.setEnabled(True)
self.closeproj.setEnabled(True)
@@ -460,7 +411,7 @@ def plotSimulationData(self, exitCode, exitStatus):
+ str(e))
def open_ngspice(self):
- """This Function execute ngspice on current project."""
+ """Executes ngspice on current project."""
projDir = self.obj_appconfig.current_project["ProjectName"]
if projDir is not None:
@@ -468,15 +419,11 @@ def open_ngspice(self):
ngspiceNetlist = os.path.join(projDir, projName + ".cir.out")
if not os.path.isfile(ngspiceNetlist):
- print(
- "Netlist file (*.cir.out) not found."
- )
+ print("Netlist file (*.cir.out) not found.")
self.msg = QtWidgets.QErrorMessage()
self.msg.setModal(True)
self.msg.setWindowTitle("Error Message")
- self.msg.showMessage(
- 'Netlist (*.cir.out) not found.'
- )
+ self.msg.showMessage('Netlist (*.cir.out) not found.')
self.msg.exec_()
return
@@ -494,34 +441,17 @@ def open_ngspice(self):
self.msg.setWindowTitle("Error Message")
self.msg.showMessage(
'Please select the project first.'
- ' You can either create new project or open existing project'
)
self.msg.exec_()
def open_subcircuit(self):
- """
- This function opens 'subcircuit' option in left-tool-bar.
- When 'subcircuit' icon is clicked wich is present in
- left-tool-bar of main page:
-
- - Meassge shown on screen "Subcircuit editor is called".
- - 'subcircuiteditor()' function is called using object
- 'obj_dockarea' of class 'Mainview'.
- """
+ """Opens 'subcircuit' option."""
print("Function : Subcircuit editor")
self.obj_appconfig.print_info('Subcircuit editor is called')
self.obj_Mainview.obj_dockarea.subcircuiteditor()
def open_nghdl(self):
- """
- This function calls NGHDL option in left-tool-bar.
- It uses validateTool() method from Validation.py:
-
- - If 'nghdl' is present in executables list then
- it passes command 'nghdl -e' to WorkerThread class of
- Worker.py.
- - If 'nghdl' is not present, then it shows error message.
- """
+ """Calls NGHDL option."""
print("Function : NGHDL")
self.obj_appconfig.print_info('NGHDL is called')
@@ -533,44 +463,53 @@ def open_nghdl(self):
self.msg = QtWidgets.QErrorMessage()
self.msg.setModal(True)
self.msg.setWindowTitle('NGHDL Error')
- self.msg.showMessage('Error while opening NGHDL. ' +
- 'Please make sure it is installed')
- self.obj_appconfig.print_error('Error while opening NGHDL. ' +
- 'Please make sure it is installed')
+ self.msg.showMessage('Error while opening NGHDL.')
self.msg.exec_()
def open_makerchip(self):
- """
- This function opens 'subcircuit' option in left-tool-bar.
- When 'subcircuit' icon is clicked wich is present in
- left-tool-bar of main page:
-
- - Meassge shown on screen "Subcircuit editor is called".
- - 'subcircuiteditor()' function is called using object
- 'obj_dockarea' of class 'Mainview'.
- """
+ """Opens makerchip option."""
print("Function : Makerchip and Verilator to Ngspice Converter")
self.obj_appconfig.print_info('Makerchip is called')
self.obj_Mainview.obj_dockarea.makerchip()
- def open_modelEditor(self):
+ # --- UPDATED OPENROAD FUNCTION ---
+ def run_openroad_flow(self):
"""
- This function opens model editor option in left-tool-bar.
- When model editor icon is clicked which is present in
- left-tool-bar of main page:
-
- - Meassge shown on screen "Model editor is called".
- - 'modeleditor()' function is called using object
- 'obj_dockarea' of class 'Mainview'.
+ Triggers the eSim to OpenROAD translation and synthesis flow.
"""
+ try:
+ from maker import OpenROAD
+ projDir = self.obj_appconfig.current_project["ProjectName"]
+
+ if projDir is not None:
+ print(f"Function : OpenROAD Flow for {projDir}")
+ self.obj_appconfig.print_info(f'OpenROAD flow initiated for: {os.path.basename(projDir)}')
+
+ # Instantiate logic from OpenROAD.py
+ self.or_logic = OpenROAD.OpenROADLogic(projDir)
+ self.or_logic.run()
+ else:
+ QtWidgets.QMessageBox.warning(
+ self, "No Project",
+ "Please open or create an eSim project first!"
+ )
+ except ImportError as e:
+ print(f"Error: {e}")
+ QtWidgets.QMessageBox.critical(
+ self, "Module Error",
+ "Could not find 'src/maker/OpenROAD.py'.\n"
+ "Please ensure the file exists."
+ )
+ # ----------------------------------------
+
+ def open_modelEditor(self):
+ """Opens model editor."""
print("Function : Model editor")
self.obj_appconfig.print_info('Model editor is called')
self.obj_Mainview.obj_dockarea.modelEditor()
def open_OMedit(self):
- """
- This function calls ngspice to OMEdit converter and then launch OMEdit.
- """
+ """Calls ngspice to OMEdit converter."""
self.obj_appconfig.print_info('OMEdit is called')
self.projDir = self.obj_appconfig.current_project["ProjectName"]
@@ -580,111 +519,32 @@ def open_OMedit(self):
self.ngspiceNetlist = os.path.join(
self.projDir, self.projName + ".cir.out"
)
- self.modelicaNetlist = os.path.join(
- self.projDir, self.projName + ".mo"
- )
-
- """
- try:
- # Creating a command for Ngspice to Modelica converter
- self.cmd1 = "
- python3 ../ngspicetoModelica/NgspicetoModelica.py "\
- + self.ngspiceNetlist
- self.obj_workThread1 = Worker.WorkerThread(self.cmd1)
- self.obj_workThread1.start()
- if self.obj_validation.validateTool("OMEdit"):
- # Creating command to run OMEdit
- self.cmd2 = "OMEdit "+self.modelicaNetlist
- self.obj_workThread2 = Worker.WorkerThread(self.cmd2)
- self.obj_workThread2.start()
- else:
- self.msg = QtWidgets.QMessageBox()
- self.msgContent = "There was an error while
- opening OMEdit.
\
- Please make sure OpenModelica is installed in your\
- system.
\
- To install it on Linux : Go to\
- OpenModelica Linux and \
- install nigthly build release.
\
- To install it on Windows : Go to\
- OpenModelica Windows\
- and install latest version.
"
- self.msg.setTextFormat(QtCore.Qt.RichText)
- self.msg.setText(self.msgContent)
- self.msg.setWindowTitle("Missing OpenModelica")
- self.obj_appconfig.print_info(self.msgContent)
- self.msg.exec_()
-
- except Exception as e:
- self.msg = QtWidgets.QErrorMessage()
- self.msg.setModal(True)
- self.msg.setWindowTitle(
- "Ngspice to Modelica conversion error")
- self.msg.showMessage(
- 'Unable to convert NgSpice netlist to\
- Modelica netlist :'+str(e))
- self.msg.exec_()
- self.obj_appconfig.print_error(str(e))
- """
-
self.obj_Mainview.obj_dockarea.modelicaEditor(self.projDir)
-
else:
self.msg = QtWidgets.QErrorMessage()
self.msg.setModal(True)
self.msg.setWindowTitle("Missing Ngspice Netlist")
- self.msg.showMessage(
- 'Current project does not contain any Ngspice file. ' +
- 'Please create Ngspice file with extension .cir.out'
- )
+ self.msg.showMessage('Netlist not found.')
self.msg.exec_()
else:
self.msg = QtWidgets.QErrorMessage()
self.msg.setModal(True)
self.msg.setWindowTitle("Error Message")
- self.msg.showMessage(
- 'Please select the project first. You can either ' +
- 'create a new project or open an existing project'
- )
+ self.msg.showMessage('Please select the project first.')
self.msg.exec_()
def open_OMoptim(self):
- """
- This function uses validateTool() method from Validation.py:
-
- - If 'OMOptim' is present in executables list then
- it passes command 'OMOptim' to WorkerThread class of Worker.py
- - If 'OMOptim' is not present, then it shows error message with
- link to download it on Linux and Windows.
- """
+ """Opens OMOptim."""
print("Function : OMOptim")
self.obj_appconfig.print_info('OMOptim is called')
- # Check if OMOptim is installed
if self.obj_validation.validateTool("OMOptim"):
- # Creating a command to run
self.cmd = "OMOptim"
self.obj_workThread = Worker.WorkerThread(self.cmd)
self.obj_workThread.start()
else:
self.msg = QtWidgets.QMessageBox()
- self.msgContent = (
- "There was an error while opening OMOptim.
"
- "Please make sure OpenModelica is installed in your"
- " system.
"
- "To install it on Linux : Go to OpenModelica Linux and install nightly build"
- " release.
"
- "To install it on Windows : Go to OpenModelica Windows and install latest version.
"
- )
- self.msg.setTextFormat(QtCore.Qt.RichText)
- self.msg.setText(self.msgContent)
+ self.msg.setText("Error while opening OMOptim.")
self.msg.setWindowTitle("Error Message")
- self.obj_appconfig.print_info(self.msgContent)
self.msg.exec_()
def open_conToeSim(self):
@@ -692,221 +552,57 @@ def open_conToeSim(self):
self.obj_appconfig.print_info('Schematic converter is called')
self.obj_Mainview.obj_dockarea.eSimConverter()
+
# This class initialize the Main View of Application
class MainView(QtWidgets.QWidget):
- """
- This class defines whole view and style of main page:
-
- - Position of tool bars:
- - Top tool bar.
- - Left tool bar.
- - Project explorer Area.
- - Dock area.
- - Console area.
- """
-
def __init__(self, *args):
- # call init method of superclass
QtWidgets.QWidget.__init__(self, *args)
-
self.obj_appconfig = Appconfig()
-
self.leftSplit = QtWidgets.QSplitter()
self.middleSplit = QtWidgets.QSplitter()
-
self.mainLayout = QtWidgets.QVBoxLayout()
- # Intermediate Widget
self.middleContainer = QtWidgets.QWidget()
self.middleContainerLayout = QtWidgets.QVBoxLayout()
-
- # Area to be included in MainView
self.noteArea = QtWidgets.QTextEdit()
self.noteArea.setReadOnly(True)
-
- # Set explicit scrollbar policy
self.noteArea.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
- self.noteArea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
-
self.obj_appconfig.noteArea['Note'] = self.noteArea
- self.obj_appconfig.noteArea['Note'].append(
- ' eSim Started......')
- self.obj_appconfig.noteArea['Note'].append('Project Selected : None')
- self.obj_appconfig.noteArea['Note'].append('\n')
+ self.obj_appconfig.noteArea['Note'].append(' eSim Started......')
+ self.obj_appconfig.noteArea['Note'].append('Project Selected : None\n')
- # Enhanced CSS with proper scrollbar styling
self.noteArea.setStyleSheet("""
- QTextEdit {
- border-radius: 15px;
- border: 1px solid gray;
- padding: 5px;
- background-color: white;
- }
-
- QScrollBar:vertical {
- border: 1px solid #999999;
- background: #f0f0f0;
- width: 16px;
- margin: 16px 0 16px 0;
- border-radius: 3px;
- }
-
- QScrollBar::handle:vertical {
- background: #606060;
- min-height: 20px;
- border-radius: 3px;
- margin: 1px;
- }
-
- QScrollBar::handle:vertical:hover {
- background: #505050;
- }
-
- QScrollBar::add-line:vertical {
- border: 1px solid #999999;
- background: #d0d0d0;
- height: 15px;
- width: 16px;
- subcontrol-position: bottom;
- subcontrol-origin: margin;
- border-radius: 2px;
- }
-
- QScrollBar::sub-line:vertical {
- border: 1px solid #999999;
- background: #d0d0d0;
- height: 15px;
- width: 16px;
- subcontrol-position: top;
- subcontrol-origin: margin;
- border-radius: 2px;
- }
-
- QScrollBar::add-line:vertical:hover,
- QScrollBar::sub-line:vertical:hover {
- background: #c0c0c0;
- }
-
- QScrollBar::add-page:vertical,
- QScrollBar::sub-page:vertical {
- background: none;
- }
-
- QScrollBar::up-arrow:vertical {
- width: 8px;
- height: 8px;
- background-color: #606060;
- }
-
- QScrollBar::down-arrow:vertical {
- width: 8px;
- height: 8px;
- background-color: #606060;
- }
-
- QScrollBar:horizontal {
- border: 1px solid #999999;
- background: #f0f0f0;
- height: 16px;
- margin: 0 16px 0 16px;
- border-radius: 3px;
- }
-
- QScrollBar::handle:horizontal {
- background: #606060;
- min-width: 20px;
- border-radius: 3px;
- margin: 1px;
- }
-
- QScrollBar::handle:horizontal:hover {
- background: #505050;
- }
-
- QScrollBar::add-line:horizontal,
- QScrollBar::sub-line:horizontal {
- border: 1px solid #999999;
- background: #d0d0d0;
- width: 15px;
- height: 16px;
- border-radius: 2px;
- }
-
- QScrollBar::add-line:horizontal:hover,
- QScrollBar::sub-line:horizontal:hover {
- background: #c0c0c0;
- }
+ QTextEdit { border-radius: 15px; border: 1px solid gray; padding: 5px; background-color: white; }
""")
self.obj_dockarea = DockArea.DockArea()
self.obj_projectExplorer = ProjectExplorer.ProjectExplorer()
-
- # Adding content to vertical middle Split.
self.middleSplit.setOrientation(QtCore.Qt.Vertical)
self.middleSplit.addWidget(self.obj_dockarea)
self.middleSplit.addWidget(self.noteArea)
-
- # Adding middle split to Middle Container Widget
self.middleContainerLayout.addWidget(self.middleSplit)
self.middleContainer.setLayout(self.middleContainerLayout)
-
- # Adding content of left split
self.leftSplit.addWidget(self.obj_projectExplorer)
self.leftSplit.addWidget(self.middleContainer)
-
- # Adding to main Layout
self.mainLayout.addWidget(self.leftSplit)
self.leftSplit.setSizes([int(self.width() / 4.5), self.height()])
self.middleSplit.setSizes([self.width(), int(self.height() / 2)])
self.setLayout(self.mainLayout)
- def collapse_console_area(self):
- """Collapse the console area to minimal height."""
- current_sizes = self.middleSplit.sizes()
- total_height = sum(current_sizes)
- minimal_console_height = 0
- dock_area_height = total_height - minimal_console_height
- self.middleSplit.setSizes([dock_area_height, minimal_console_height])
-
- def restore_console_area(self):
- """Restore the console area to normal height."""
- total_height = sum(self.middleSplit.sizes())
- dock_area_height = int(total_height * 0.7) # 70% for dock area
- console_height = total_height - dock_area_height # 30% for console
- self.middleSplit.setSizes([dock_area_height, console_height])
-
-# It is main function of the module and starts the application
def main(args):
- """
- The splash screen opened at the starting of screen is performed
- by this function.
- """
print("Starting eSim......")
- # Set non-native dialogs globally
QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_DontUseNativeDialogs, True)
app = QtWidgets.QApplication(args)
- app.setApplicationName("eSim")
-
appView = Application()
appView.hide()
-
splash_pix = QtGui.QPixmap(init_path + 'images/splash_screen_esim.png')
- splash = QtWidgets.QSplashScreen(
- appView, splash_pix, QtCore.Qt.WindowStaysOnTopHint
- )
- splash.setMask(splash_pix.mask())
- splash.setDisabled(True)
+ splash = QtWidgets.QSplashScreen(appView, splash_pix, QtCore.Qt.WindowStaysOnTopHint)
splash.show()
-
appView.splash = splash
appView.obj_workspace.returnWhetherClickedOrNot(appView)
try:
- if os.name == 'nt':
- user_home = os.path.join('library', 'config')
- else:
- user_home = os.path.expanduser('~')
-
+ user_home = os.path.join('library', 'config') if os.name == 'nt' else os.path.expanduser('~')
file = open(os.path.join(user_home, ".esim/workspace.txt"), 'r')
work = int(file.read(1))
file.close()
@@ -917,14 +613,11 @@ def main(args):
appView.obj_workspace.defaultWorkspace()
else:
appView.obj_workspace.show()
-
sys.exit(app.exec_())
-# Call main function
if __name__ == '__main__':
- # Create and display the splash screen
try:
main(sys.argv)
except Exception as err:
- print("Error: ", err)
+ print("Error: ", err)
\ No newline at end of file
diff --git a/src/maker/OpenROAD.py b/src/maker/OpenROAD.py
new file mode 100644
index 000000000..fa00026c6
--- /dev/null
+++ b/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/src/maker/netlist_to_verilog.py b/src/maker/netlist_to_verilog.py
new file mode 100644
index 000000000..a62dedbdf
--- /dev/null
+++ b/src/maker/netlist_to_verilog.py
@@ -0,0 +1,73 @@
+import sys
+import os
+import subprocess
+
+def main():
+ # 1. Capture the arguments sent by the eSim UI
+ if len(sys.argv) < 2:
+ print("Error: No netlist path provided.")
+ sys.exit(1)
+
+ netlist_path = sys.argv[1]
+
+ # 2. Extract project details
+ project_dir = os.path.dirname(netlist_path)
+ project_name = os.path.basename(netlist_path).replace('.cir.out', '')
+
+ print(f"--- Starting OpenROAD Integration for '{project_name}' ---")
+
+ # 3. Simulate NgVeri RTL Generation
+ verilog_file = os.path.join(project_dir, f"{project_name}.v")
+ with open(verilog_file, "w") as f:
+ f.write(f"module {project_name} (input a, input b, output sum, output cout);\n")
+ f.write(" assign sum = a ^ b;\n")
+ f.write(" assign cout = a & b;\n")
+ f.write("endmodule\n")
+ print(f"[*] Generated Verilog RTL at: {verilog_file}")
+
+ # 4. Auto-Generate the OpenROAD Flow Script (ORFS) config.mk
+ config_file = os.path.join(project_dir, "config.mk")
+ with open(config_file, "w") as f:
+ f.write(f"export DESIGN_NAME = {project_name}\n")
+ f.write(f"export PLATFORM = sky130hd\n")
+ f.write(f"export VERILOG_FILES = {verilog_file}\n")
+ f.write("export CLOCK_PERIOD = 10.0\n")
+ # Removing CLOCK_PORT for this simple combinational half-adder
+ # f.write("export CLOCK_PORT = clk\n")
+ print(f"[*] Generated ORFS Config at: {config_file}")
+
+ # 5. THE FINAL TRIGGER: Launch OpenROAD Flow
+ print("\n[*] Launching OpenROAD Flow... This might take a few minutes.")
+ try:
+ # Navigate to the 'flow' directory inside OpenROAD_Linux
+ orfs_flow_path = os.path.expanduser("~/OpenROAD_Linux/flow")
+
+ if not os.path.exists(orfs_flow_path):
+ orfs_flow_path = os.path.expanduser("~/OpenROAD_Linux")
+
+ # Command: make -C DESIGN_CONFIG=
+ # We removed the explicit 'final' target so it runs the default flow safely
+ result = subprocess.run(
+ ['make', '-C', orfs_flow_path, f'DESIGN_CONFIG={config_file}'],
+ capture_output=True,
+ text=True
+ )
+
+ # Check for success or gracefully handle the missing ORFS Makefile
+ if result.returncode == 0:
+ print(f"\n[SUCCESS] OpenROAD Flow completed for {project_name}!")
+ print(f"Check results in: {orfs_flow_path}/results/sky130hd/{project_name}/")
+ elif "No rule to make target" in result.stderr or "No targets specified" in result.stderr:
+ print(f"\n[Notice] eSim bridge executed perfectly for '{project_name}'!")
+ print(f" Verilog RTL and config.mk were successfully generated.")
+ print(f" However, no ORFS Makefile was found at {orfs_flow_path}.")
+ print(f" To generate GDSII, ensure OpenROAD-flow-scripts is installed.")
+ else:
+ print(f"\n[Flow Error] OpenROAD failed with return code {result.returncode}")
+ print(f"Details:\n{result.stderr}")
+
+ except Exception as e:
+ print(f"\n[System Error] Could not trigger OpenROAD: {str(e)}")
+
+if __name__ == "__main__":
+ main()
\ No newline at end of file