diff options
Diffstat (limited to 'src')
25 files changed, 3074 insertions, 11 deletions
diff --git a/src/frontEnd/Application.py b/src/frontEnd/Application.py index 94bce4ae..d22f7ccc 100644..100755 --- a/src/frontEnd/Application.py +++ b/src/frontEnd/Application.py @@ -11,13 +11,16 @@ # NOTES: --- # AUTHOR: Fahim Khan, fahim.elex@gmail.com # MODIFIED: Rahul Paknikar, rahulp@iitb.ac.in +# Sumanto Kar, sumantokar@iitb.ac.in, FOSSEE, IIT Bombay # ORGANIZATION: eSim Team at FOSSEE, IIT Bombay # CREATED: Tuesday 24 February 2015 -# REVISION: Sunday 13 December 2020 +# REVISION: Wednesday 25 August 2021 # ========================================================================= import os +import traceback # noqa:F401 +import traceback if os.name == 'nt': # noqa from frontEnd import pathmagic # noqa:F401 init_path = '' @@ -39,9 +42,11 @@ from PyQt5.Qt import QSize import shutil import time import sys - +import psutil # Its our main window of application. + + class Application(QtWidgets.QMainWindow): """This class initializes all objects used in this file.""" global project_name @@ -210,6 +215,12 @@ class Application(QtWidgets.QMainWindow): ) self.nghdl.triggered.connect(self.open_nghdl) + self.makerchip = QtWidgets.QAction( + QtGui.QIcon(init_path + 'images/makerchip.png'), + '<b>Makerchip-NgVeri</b>', self + ) + self.makerchip.triggered.connect(self.open_makerchip) + self.omedit = QtWidgets.QAction( QtGui.QIcon(init_path + 'images/omedit.png'), '<b>Modelica Converter</b>', self @@ -230,6 +241,7 @@ class Application(QtWidgets.QMainWindow): self.lefttoolbar.addAction(self.ngspice) self.lefttoolbar.addAction(self.model) self.lefttoolbar.addAction(self.subcircuit) + self.lefttoolbar.addAction(self.makerchip) self.lefttoolbar.addAction(self.nghdl) self.lefttoolbar.addAction(self.omedit) self.lefttoolbar.addAction(self.omoptim) @@ -523,18 +535,79 @@ class Application(QtWidgets.QMainWindow): print("Current Project is : ", self.obj_appconfig.current_project) self.obj_Mainview.obj_dockarea.usermanual() + def checkIfProcessRunning(self, processName): + ''' + Check if there is any running process + that contains the given name processName. + ''' + # Iterate over the all the running process + for proc in psutil.process_iter(): + try: + # Check if process name contains the given name string. + if processName.lower() in proc.name().lower(): + return True + except (psutil.NoSuchProcess, + psutil.AccessDenied, psutil.ZombieProcess): + pass + return False + def open_ngspice(self): """This Function execute ngspice on current project.""" self.projDir = self.obj_appconfig.current_project["ProjectName"] if self.projDir is not None: + self.obj_Mainview.obj_dockarea.ngspiceEditor(self.projDir) + if self.obj_Mainview.obj_dockarea.ngspiceEditor( + self.projDir) is False: + print( + "No netlist (*.cir.out) file" + "Check netlist file to change simulation parameters." + ) + self.msg = QtWidgets.QErrorMessage() + self.msg.setModal(True) + self.msg.setWindowTitle("Warning Message") + self.msg.showMessage( + 'No netlist (*.cir.out) file' + ) + self.msg.exec_() + return +======= + # Edited by Sumanto Kar 25/08/2021 + if self.obj_Mainview.obj_dockarea.ngspiceEditor( + self.projDir) is False: + print( + "No netlist file (*.cir.out)" + "Check netlist file to change simulation parameters." + ) +>>>>>>> master + + self.msg = QtWidgets.QErrorMessage() + self.msg.setModal(True) + self.msg.setWindowTitle("Warning Message") + self.msg.showMessage( + 'No netlist file (*.cir.out)' + ) + self.msg.exec_() + return currTime = time.time() count = 0 while True: try: + # Edited by Sumanto Kar 25/08/2021 st = os.stat(os.path.join(self.projDir, "plot_data_i.txt")) + if self.checkIfProcessRunning('xterm') is False: + self.msg = QtWidgets.QErrorMessage() + self.msg.setModal(True) + self.msg.setWindowTitle("Warning Message") + self.msg.showMessage( + 'Simulation was interuppted. ' + 'Please close all the Xterm windows.' + 'And then rerun the simulation' + ) + self.msg.exec_() + return if st.st_mtime >= currTime: break except Exception: @@ -546,7 +619,8 @@ class Application(QtWidgets.QMainWindow): if count >= 10: print( "Ngspice taking too long for simulation. " - "Check netlist file to change simulation parameters." + "Check netlist file (*.cir.out) " + "to change simulation parameters." ) self.msg = QtWidgets.QErrorMessage() @@ -554,7 +628,8 @@ class Application(QtWidgets.QMainWindow): self.msg.setWindowTitle("Warning Message") self.msg.showMessage( 'Ngspice taking too long for simulation. ' - 'Check netlist file to change simulation parameters.' + 'Check netlist file (*.cir.out) ' + 'to change simulation parameters.' ) self.msg.exec_() @@ -572,7 +647,7 @@ class Application(QtWidgets.QMainWindow): ' Please look at console for more details.' ) self.msg.exec_() - print("Exception Message:", str(e)) + print("Exception Message:", str(e), traceback.format_exc()) self.obj_appconfig.print_error('Exception Message : ' + str(e)) else: @@ -626,6 +701,20 @@ class Application(QtWidgets.QMainWindow): 'Please make sure it is installed') 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'. + """ + 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): """ This function opens model editor option in left-tool-bar. @@ -707,7 +796,7 @@ class Application(QtWidgets.QMainWindow): else: self.msg = QtWidgets.QErrorMessage() self.msg.setModal(True) - self.msg.setWindowTitle("Missing Ngspice netlist") + 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' diff --git a/src/frontEnd/DockArea.py b/src/frontEnd/DockArea.py index b96c468a..461240b9 100644..100755 --- a/src/frontEnd/DockArea.py +++ b/src/frontEnd/DockArea.py @@ -4,6 +4,7 @@ from ngspiceSimulation.NgspiceWidget import NgspiceWidget from configuration.Appconfig import Appconfig from modelEditor.ModelEditor import ModelEditorclass from subcircuit.Subcircuit import Subcircuit +from maker.makerchip import makerchip from kicadtoNgspice.KicadtoNgspice import MainWindow from browser.Welcome import Welcome from browser.UserManual import UserManual @@ -123,6 +124,10 @@ class DockArea(QtWidgets.QMainWindow): self.ngspiceNetlist = os.path.join( self.projDir, self.projName + ".cir.out") + # Edited by Sumanto Kar 25/08/2021 + if os.path.isfile(self.ngspiceNetlist) is False: + return False + global count self.ngspiceWidget = QtWidgets.QWidget() @@ -254,6 +259,34 @@ class DockArea(QtWidgets.QMainWindow): count = count + 1 + def makerchip(self): + """This function creates a widget for different subcircuit options.""" + global count + self.makerWidget = QtWidgets.QWidget() + self.makerLayout = QtWidgets.QVBoxLayout() + self.makerLayout.addWidget(makerchip(self)) + + self.makerWidget.setLayout(self.makerLayout) + dock['Makerchip-' + + str(count)] = QtWidgets.QDockWidget('Makerchip-' + str(count)) + dock['Makerchip-' + str(count)].setWidget(self.makerWidget) + self.addDockWidget(QtCore.Qt.TopDockWidgetArea, + dock['Makerchip-' + str(count)]) + self.tabifyDockWidget(dock['Welcome'], + dock['Makerchip-' + str(count)]) + + # CSS + dock['Makerchip-' + str(count)].setStyleSheet(" \ + .QWidget { border-radius: 15px; border: 1px solid gray;\ + padding: 5px; width: 200px; height: 150px; } \ + ") + + dock['Makerchip-' + str(count)].setVisible(True) + dock['Makerchip-' + str(count)].setFocus() + dock['Makerchip-' + str(count)].raise_() + + count = count + 1 + def usermanual(self): """This function creates a widget for user manual.""" global count diff --git a/src/frontEnd/ProjectExplorer.py b/src/frontEnd/ProjectExplorer.py index 456276c8..456276c8 100644..100755 --- a/src/frontEnd/ProjectExplorer.py +++ b/src/frontEnd/ProjectExplorer.py diff --git a/src/frontEnd/Workspace.py b/src/frontEnd/Workspace.py index 4d033539..4d033539 100644..100755 --- a/src/frontEnd/Workspace.py +++ b/src/frontEnd/Workspace.py diff --git a/src/frontEnd/__init__.py b/src/frontEnd/__init__.py index e69de29b..e69de29b 100644..100755 --- a/src/frontEnd/__init__.py +++ b/src/frontEnd/__init__.py diff --git a/src/frontEnd/pathmagic.py b/src/frontEnd/pathmagic.py index 5f0d712c..5f0d712c 100644..100755 --- a/src/frontEnd/pathmagic.py +++ b/src/frontEnd/pathmagic.py diff --git a/src/maker/Appconfig.py b/src/maker/Appconfig.py new file mode 100755 index 00000000..efeac75a --- /dev/null +++ b/src/maker/Appconfig.py @@ -0,0 +1,35 @@ +import os.path +from configparser import SafeConfigParser + + +class Appconfig: + home = os.path.expanduser("~") + # Reading all variables from eSim config.ini + parser_esim = SafeConfigParser() + parser_esim.read(os.path.join(home, os.path.join('.esim', 'config.ini'))) + try: + src_home = parser_esim.get('eSim', 'eSim_HOME') + xml_loc = os.path.join(src_home, 'library/modelParamXML') + lib_loc = os.path.expanduser('~') + except BaseException: + pass + esimFlag = 0 + + # Reading all variables from ngveri config.ini + # parser_ngveri = SafeConfigParser() + # parser_ngveri.read(os.path.join(home, + # os.path.join('.ngveri', 'config.ini'))) + + kicad_lib_template = { + "start_def": "DEF comp_name U 0 40 Y Y 1 F N", + "U_field": "F0 \"U\" 2850 1800 60 H V C CNN", + "comp_name_field": "F1 \"comp_name\" 2850 2000 60 H V C CNN", + "blank_field": ["F2 blank_quotes 2850 1950 60 H V C CNN", + "F3 blank_quotes 2850 1950 60 H V C CNN"], + "start_draw": "DRAW", + "draw_pos": "S 2350 2100 3350 1800 0 1 0 N", + "input_port": "X in 1 2150 2000 200 R 50 50 1 1 I", + "output_port": "X out 2 3550 2000 200 L 50 50 1 1 O", + "end_draw": "ENDDRAW", + "end_def": "ENDDEF" + } diff --git a/src/maker/Maker.py b/src/maker/Maker.py new file mode 100755 index 00000000..f4c696f6 --- /dev/null +++ b/src/maker/Maker.py @@ -0,0 +1,543 @@ +# ========================================================================= +# FILE: Maker.py +# +# USAGE: --- +# +# DESCRIPTION: This define all components of the Makerchip 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 xml.etree import ElementTree as ET # noqa:F401 +import hdlparse.verilog_parser as vlog +import time # noqa:F401 +from PyQt5 import QtCore, QtWidgets +from PyQt5.QtCore import QThread, Qt # noqa:F401 +from PyQt5.QtWidgets \ + import QApplication, \ + QWidget, QLabel, QVBoxLayout # noqa:F401 +from configuration.Appconfig import Appconfig +import os +import subprocess # noqa:F401 +import watchdog.events +import watchdog.observers +from os.path import expanduser +home = expanduser("~") +# import inotify.adapters + +# declaring the global variables +# verilogfile stores the name of the file +# toggle flag stores the object of the toggling button +verilogFile = [] +toggle_flag = [] + +# beginning class Maker. This class create the Maker Tab + + +class Maker(QtWidgets.QWidget): + + # initailising the varaibles + def __init__(self, filecount): + print(self) + + QtWidgets.QWidget.__init__(self) + self.count = 0 + self.text = "" + self.filecount = filecount + self.entry_var = {} + self.createMakerWidget() + self.obj_Appconfig = Appconfig() + verilogFile.append("") + + # Creating the various components of the Widget(Maker Tab) + def createMakerWidget(self): + + 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.grid.addWidget(self.creategroup(), 1, 0, 5, 0) + self.show() + + # This function is to Add new verilog file + def addverilog(self): + + init_path = '../../../' + if os.name == 'nt': + init_path = '' + self.verilogfile = QtCore.QDir.toNativeSeparators( + QtWidgets.QFileDialog.getOpenFileName( + self, "Open verilog Directory", + init_path + "home", "*v" + )[0] + ) + if self.verilogfile == "": + self.verilogfile = self.entry_var[0].text() + + if self.verilogfile == "": + reply = QtWidgets.QMessageBox.critical( + None, + "Error Message", + "<b>Error: No Verilog File Chosen.\ + Please chose a Verilog file</b>", + QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel) + if reply == QtWidgets.QMessageBox.Ok: + self.addverilog() + self.obj_Appconfig.print_info('Add Verilog File Called') + + elif reply == QtWidgets.QMessageBox.Cancel: + self.obj_Appconfig.print_info('No Verilog File Chosen') + return + + self.text = open(self.verilogfile).read() + self.entry_var[0].setText(self.verilogfile) + self.entry_var[1].setText(self.text) + global verilogFile + + verilogFile[self.filecount] = self.verilogfile + if self.refreshoption in toggle_flag: + toggle_flag.remove(self.refreshoption) + + self.observer = watchdog.observers.Observer() + self.event_handler = Handler( + self.verilogfile, + self.refreshoption, + self.observer) + + self.observer.schedule( + self.event_handler, + path=self.verilogfile, + recursive=True) + self.observer.start() + # self.notify=notify(self.verilogfile,self.refreshoption) + # self.notify.start() + # open("filepath.txt","w").write(self.verilogfile) + + # This function is used to call refresh while + # running Ngspice to Verilog Converter + # (as the original one gets destroyed) + def refresh_change(self): + if self.refreshoption in toggle_flag: + self.toggle = toggle(self.refreshoption) + self.toggle.start() + + # It is used to refresh the file in eSim if its edited anywhere else + def refresh(self): + if not hasattr(self, 'verilogfile'): + return + self.text = open(self.verilogfile).read() + self.entry_var[1].setText(self.text) + print("NgVeri File: " + self.verilogfile + " Refreshed") + self.obj_Appconfig.print_info( + "NgVeri File: " + self.verilogfile + " Refreshed") + self.observer = watchdog.observers.Observer() + self.event_handler = Handler( + self.verilogfile, + self.refreshoption, + self.observer) + + self.observer.schedule( + self.event_handler, + path=self.verilogfile, + recursive=True) + self.observer.start() + # self.notify.start() + global toggle_flag + if self.refreshoption in toggle_flag: + toggle_flag.remove(self.refreshoption) + + # This function is used to save the edited file in eSim + def save(self): + wr = self.entry_var[1].toPlainText() + open(self.verilogfile, "w+").write(wr) + + # This is used to run the makerchip-app + def runmakerchip(self): + init_path = '../../' + if os.name == 'nt': + init_path = '' # noqa:F841 + try: + if not os.path.isfile(home + "/.makerchip_accepted"): + reply = QtWidgets.QMessageBox.warning( + None, "Terms of Services", "Please review the makerchip\ + Terms of Service \ + (<a href='https://www.makerchip.com/terms/'>\ + https://www.makerchip.com/terms/</a> ).\ + Have you read and do you accept \ + these Terms of Service? [y/N]:", + QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No + ) + + if reply == QtWidgets.QMessageBox.Yes: + f = open(home + "/.makerchip_accepted", "w") + f.close() + else: + return + print("Running Makerchip..............................") + # self.file = open(self.verilogfile,"w") + # self.file.write(self.entry_var[1].toPlainText()) + # self.file.close() + filename = self.verilogfile + if self.verilogfile.split('.')[-1] != "tlv": + reply = QtWidgets.QMessageBox.warning( + None, + "Do you want to automate top module?", + "<b>Click on YES if you want top module \ + to be automatically added. \ + NOTE: a .tlv file will be created \ + in the directory of current verilog file\ + and the makerchip will be running on \ + this file. Otherwise click on NO.</b><br/> \ + <b> To not open Makerchip, click CANCEL</b>", + QtWidgets.QMessageBox.Yes + | QtWidgets.QMessageBox.No + | QtWidgets.QMessageBox.Cancel) + if reply == QtWidgets.QMessageBox.Cancel: + return + if reply == QtWidgets.QMessageBox.Yes: + code = open(self.verilogfile).read() + text = code + filename = '.'.join( + self.verilogfile.split('.')[:-1]) + ".tlv" + file = os.path.basename('.'.join( + self.verilogfile.split('.')[:-1])) + f = open(filename, 'w') + flag = 1 # noqa F841 + ports = "" # noqa F841 + code = code.replace(" wire ", " ") + code = code.replace(" reg ", " ") + vlog_ex = vlog.VerilogExtractor() + vlog_mods = vlog_ex.extract_objects_from_source(code) + lint_off = open("../maker/lint_off.txt").readlines() + string = '''\\TLV_version 1d: tl-x.org\n\\SV\n''' + for item in lint_off: + string += "/* verilator lint_off " + \ + item.strip("\n") + "*/ " + string += '''\n\n//Your Verilog/System \ +Verilog Code Starts Here:\n''' + \ + text + '''\n\n//Top Module Code \ +Starts here:\n\tmodule top(input \ +logic clk, input logic reset, input logic [31:0] cyc_cnt, \ +output logic passed, output logic failed);\n''' + print(file) + for m in vlog_mods: + if m.name.lower() == file.lower(): + for p in m.ports: + if str( + p.name) != "clk" and str( + p.name) != "reset" and str( + p.name) != "cyc_cnt" and str( + p.name) != "passed" and str( + p.name) != "failed": + string += '\t\tlogic ' + p.data_type\ + + " " + p.name + ";//" + p.mode + "\n" + if m.name.lower() != file.lower(): + QtWidgets.QMessageBox.critical( + None, + "Error Message", + "<b>Error: File name and module \ + name are not same. Please \ + ensure that they are same</b>", + QtWidgets.QMessageBox.Ok) + + self.obj_Appconfig.print_info( + 'NgVeri Stopped due to File \ +name and module name not matching error') + return + string += "//The $random() can be replaced \ +if user wants to assign values\n" + for m in vlog_mods: + if m.name.lower() == file.lower(): + for p in m.ports: + if str( + p.mode) == "input" or str( + p.mode) == "inout": + if str( + p.name) != "clk" and str( + p.name) != "reset" and str( + p.name) != "cyc_cnt" and str( + p.name) != "passed" and str( + p.name) != "failed": + string += '\t\tassign ' + p.name\ + + " = " + "$random();\n" + + for m in vlog_mods: + if m.name.lower() == file.lower(): + string += '\t\t' + m.name + " " + m.name + '(' + i = 0 + for p in m.ports: + i = i + 1 + string += "."+p.name+"("+p.name+")" + if i == len(m.ports): + string += ");\n\t\n\\TLV\n//\ +Add \\TLV here if desired\ + \n\\SV\nendmodule\n\n" + else: + string += ", " + f.write(string) + + self.process = QtCore.QProcess(self) + cmd = 'makerchip ' + filename + print("File: " + filename) + self.process.start(cmd) + print( + "Makerchip command process pid ---------- >", + self.process.pid()) + except BaseException as e: + print(e) + self.msg = QtWidgets.QErrorMessage(self) + self.msg.setModal(True) + self.msg.setWindowTitle("Error Message") + self.msg.showMessage( + "Error in running Makerchip. \ +Please check if Verilog File Chosen.") + self.msg.exec_() + print("Error in running Makerchip. \ +Please check if Verilog File Chosen.") + # initial = self.read_file() + + # while True: + # current = self.read_file() + # if initial != current: + # for line in current: + # if line not in initial: + # print(line) + # initial = current + # self.processfile = QtCore.QProcess(self) + # self.processfile.start("python3 notify.py") + # print(self.processfile.readChannel()) + + # This creates the buttons/options + + def createoptionsBox(self): + + self.optionsbox = QtWidgets.QGroupBox() + self.optionsbox.setTitle("Select Options") + self.optionsgrid = QtWidgets.QGridLayout() + # self.optionsbox2 = QtWidgets.QGroupBox() + # self.optionsbox2.setTitle("Note: Please save the file once edited") + # self.optionsgrid2 = QtWidgets.QGridLayout() + self.optionsgroupbtn = QtWidgets.QButtonGroup() + self.addoptions = QtWidgets.QPushButton("Add Top Level Verilog file") + self.optionsgroupbtn.addButton(self.addoptions) + self.addoptions.clicked.connect(self.addverilog) + self.optionsgrid.addWidget(self.addoptions, 0, 1) + # self.optionsbox.setLayout(self.optionsgrid) + # self.grid.addWidget(self.creategroup(), 1, 0, 5, 0 + self.refreshoption = QtWidgets.QPushButton("Refresh") + self.optionsgroupbtn.addButton(self.refreshoption) + self.refreshoption.clicked.connect(self.refresh) + self.optionsgrid.addWidget(self.refreshoption, 0, 2) + # self.optionsbox.setLayout(self.optionsgrid) + # self.grid.addWidget(self.creategroup(), 1, 0, 5, 0) + self.saveoption = QtWidgets.QPushButton("Save") + self.optionsgroupbtn.addButton(self.saveoption) + self.saveoption.clicked.connect(self.save) + self.optionsgrid.addWidget(self.saveoption, 0, 3) + # self.optionsbox.setLayout(self.optionsgrid) + # self.grid.addWidget(self.creategroup(), 1, 0, 5, 0) + self.runoptions = QtWidgets.QPushButton("Edit in Makerchip") + self.optionsgroupbtn.addButton(self.runoptions) + self.runoptions.clicked.connect(self.runmakerchip) + self.optionsgrid.addWidget(self.runoptions, 0, 4) + # self.optionsbox.setLayout(self.optionsgrid) + # self.grid.addWidget(self.creategroup(), 1, 0, 5, 0) + if not os.path.isfile(home + "/.makerchip_accepted"): + self.acceptTOS = QtWidgets.QPushButton("Accept Makerchip TOS") + self.optionsgroupbtn.addButton(self.acceptTOS) + self.acceptTOS.clicked.connect(self.makerchipaccepted) + self.optionsgrid.addWidget(self.acceptTOS, 0, 5) + # self.optionsbox.setLayout(self.optionsgrid) + # self.grid.addWidget(self.creategroup(), 1, 0, 5, 0) + self.optionsbox.setLayout(self.optionsgrid) + return self.optionsbox + + # This function is called to accept TOS of makerchip + + def makerchipaccepted(self): + reply = QtWidgets.QMessageBox.warning( + None, "Terms of Services", "Please review the makerchip\ + Terms of Service \ + (<a href='https://www.makerchip.com/terms/'>\ + https://www.makerchip.com/terms/</a> ).\ + Have you read and do you \ + accept these Terms of Service? [y/N]:", + QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No + ) + + if reply == QtWidgets.QMessageBox.Yes: + f = open(home + "/.makerchip_accepted", "w") + f.close() + # else: + # return + + # This function adds the other parts of widget like text box + + def creategroup(self): + + self.trbox = QtWidgets.QGroupBox() + self.trbox.setTitle(".tlv file") + # self.trbox.setDisabled(True) + # self.trbox.setVisible(False) + self.trgrid = QtWidgets.QGridLayout() + self.trbox.setLayout(self.trgrid) + + self.start = QtWidgets.QLabel("Path to .tlv file") + self.trgrid.addWidget(self.start, 1, 0) + self.count = 0 + self.entry_var[self.count] = QtWidgets.QLabel(" - ") + self.trgrid.addWidget(self.entry_var[self.count], 1, 1) + self.entry_var[self.count].setMaximumWidth(1000) + 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; } \ + ") + + self.start = QtWidgets.QLabel(".tlv code") + # self.start2 = QtWidgets.QLabel("Note: \ + # Please save the file once edited") + # self.start2.setStyleSheet("background-color: red") + self.trgrid.addWidget(self.start, 2, 0) + # self.trgrid.addWidget(self.start2, 3,0) + self.entry_var[self.count] = QtWidgets.QTextEdit() + self.trgrid.addWidget(self.entry_var[self.count], 2, 1) + self.entry_var[self.count].setMaximumWidth(1000) + self.entry_var[self.count].setMaximumHeight(1000) + # self.entry_var[self.count].textChanged.connect(self.save) + 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 + + +# The Handler class is used to create a watch on the files using WatchDog +class Handler(watchdog.events.PatternMatchingEventHandler): + # this function initialisses the variable and the objects of watchdog + def __init__(self, verilogfile, refreshoption, observer): + # Set the patterns for PatternMatchingEventHandler + watchdog.events.PatternMatchingEventHandler.__init__( + self, ignore_directories=True, case_sensitive=False) + self.verilogfile = verilogfile + self.refreshoption = refreshoption + self.obj_Appconfig = Appconfig() + self.observer = observer + self.toggle = toggle(self.refreshoption) + + # if a file is modified, toggle starts to toggle the refresh button + def on_modified(self, event): + print("Watchdog received modified event - % s." % event.src_path) + msg = QtWidgets.QErrorMessage() + msg.setWindowTitle("eSim Message") + msg.showMessage( + "NgVeri File: " + + self.verilogfile + + " modified. Please click on Refresh") + msg.exec_() + print("NgVeri File: " + self.verilogfile + + " modified. Please click on Refresh") + # self.obj_Appconfig.print_info("NgVeri File:\ + # "+self.verilogfile+" modified. Please click on Refresh") + global toggle_flag + if not(self.refreshoption in toggle_flag): + toggle_flag.append(self.refreshoption) + # i.rm_watch() + self.observer.stop() + self.toggle.start() + + +# class notify(QThread): +# def __init__(self,verilogfile,refreshoption):#,obj_Appconfig): +# QThread.__init__(self) +# self.verilogfile=verilogfile +# self.refreshoption=refreshoption +# self.obj_Appconfig = Appconfig() +# self.toggle=toggle(self.refreshoption) + + +# def __del__(self): +# self.wait() + +# def run(self): +# i = inotify.adapters.Inotify() + +# i.add_watch(self.verilogfile) + +# for event in i.event_gen(): +# if not self.refreshoption.isVisible(): +# break +# if event!=None: +# print(event) +# if "IN_CLOSE_WRITE" in event[1] : +# msg = QtWidgets.QErrorMessage() +# msg.setModal(True) +# msg.setWindowTitle("eSim Message") +# msg.showMessage( +# "NgVeri File: "+self.verilogfile+"\ +# modified. Please click on Refresh") +# msg.exec_() +# print("NgVeri File: "+self.verilogfile+"\ +# modified. Please click on Refresh") +# # self.obj_Appconfig.print_info("NgVeri File: \ +# "+self.verilogfile+" modified. Please click on Refresh") +# global toggle_flag +# toggle_flag.append(self.refreshoption) +# #i.rm_watch() +# self.toggle.start() +# break + + +# This class is used to toggle a button(change colour by toggling) +class toggle(QThread): + # initialising the threads + def __init__(self, option): + QThread.__init__(self) + self.option = option + + def __del__(self): + self.wait() + + # running the thread to toggle + def run(self): + + while True: + self.option.setStyleSheet("background-color: red") + self.sleep(1) + self.option.setStyleSheet("background-color: none") + self.sleep(1) + print(toggle_flag) + if not self.option.isVisible(): + break + if self.option not in toggle_flag: + break diff --git a/src/maker/ModelGeneration.py b/src/maker/ModelGeneration.py new file mode 100755 index 00000000..71edad85 --- /dev/null +++ b/src/maker/ModelGeneration.py @@ -0,0 +1,1157 @@ +# ========================================================================= +# FILE: ModelGeneration.py +# +# USAGE: --- +# +# DESCRIPTION: This define all model generation processes of NgVeri. +# +# 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 +import re +import os +import sys # noqa:F401 +import shutil # noqa:F401 +import subprocess # noqa:F401 +from PyQt5 import QtGui, QtCore, QtWidgets # noqa:F401 +from PyQt5.QtGui import * # noqa:F401 F403 +from configparser import ConfigParser # noqa:F401 +from configuration import Appconfig +from . import createkicad +import hdlparse.verilog_parser as vlog +from configparser import SafeConfigParser # noqa:F401 + +# Class is used to generate the Ngspice Model + + +class ModelGeneration(QtWidgets.QWidget): + + # initialising the variables + def __init__(self, file, termedit): + QtWidgets.QWidget.__init__(self) + super().__init__() + self.obj_Appconfig = Appconfig.Appconfig() + print("Argument is : ", file) + self.file = file + self.termedit = termedit + self.cur_dir = os.getcwd() + self.fname = os.path.basename(file) + self.fname = self.fname.lower() + print("Verilog/SystemVerilog/TL Verilog filename is : ", self.fname) + self.home = os.path.expanduser("~") + self.parser = SafeConfigParser() + self.parser.read(os.path.join( + self.home, os.path.join('.nghdl', 'config.ini'))) + self.ngspice_home = self.parser.get('NGSPICE', 'NGSPICE_HOME') + self.release_dir = self.parser.get('NGSPICE', 'RELEASE') + self.src_home = self.parser.get('SRC', 'SRC_HOME') + self.licensefile = self.parser.get('SRC', 'LICENSE') + self.digital_home = self.parser.get('NGSPICE', 'DIGITAL_MODEL') + + self.digital_home = self.digital_home.split("/ghdl")[0] + "/Ngveri" + # # #### Creating connection_info.txt file from verilog file #### # + + # Readinf the file and performing operations and copying it in the Ngspice + # folder + def verilogfile(self): + Text = "<span style=\" font-size:25pt;\ + font-weight:1000; color:#008000;\" >" + Text += ".................Running NgVeri..................." + Text += "</span>" + self.termedit.append(Text) + + read_verilog = open(self.file, 'r') + verilog_data = read_verilog.readlines() + read_verilog.close() + self.modelpath = self.digital_home + \ + "/" + self.fname.split('.')[0] + "/" + if not os.path.isdir(self.modelpath): + os.mkdir(self.modelpath) + + if self.fname.split('.')[1] == "tlv": + self.sandpiper() + read_verilog = open(self.modelpath + self.fname, 'r') + verilog_data = read_verilog.readlines() + read_verilog.close() + f = open(self.modelpath + self.fname, 'w') + + for item in verilog_data: + if self.fname.split('.')[1] == "sv": + string = item.replace("top", self.fname.split('.')[0]) + else: + string = item + f.write(string) + f.write("\n") + f.close() + + # This function is call the sandpiper to convert .tlv file to .sv file + def sandpiper(self): + # Text="Running Sandpiper............" + print("Running Sandpiper-Saas for TLV to SV Conversion") + self.cmd = "cp ../maker/tlv/clk_gate.v ../maker/tlv/pseudo_rand.sv \ +../maker/tlv/sandpiper.vh ../maker/tlv/sandpiper_gen.vh \ +../maker/tlv/sp_default.vh ../maker/tlv/pseudo_rand_gen.sv \ +../maker/tlv/pseudo_rand.m4out.tlv " + self.file + " " + self.modelpath + + self.process = QtCore.QProcess(self) + self.args = ['-c', self.cmd] + self.process.start('sh', self.args) + self.termedit.append("Command: " + self.cmd) + self.process \ + .readyReadStandardOutput.connect(self.readAllStandard) + self.process.waitForFinished(50000) + print("Copied the files required for TLV successfully") + self.cur_dir = os.getcwd() + print("Running Sandpiper............") + os.chdir(self.modelpath) + self.cmd = "sandpiper-saas -i " + \ + self.fname.split('.')[0] + ".tlv -o "\ + + self.fname.split('.')[0] + ".sv" + self.args = ['-c', self.cmd] + self.process.start('sh', self.args) + self.termtitle("RUN SANDPIPER-SAAS") + self.termtext("Current Directory: " + self.modelpath) + self.termtext("Command: " + self.cmd) + # self.process.setProcessChannelMode(QtCore.QProcess.MergedChannels) + self.process \ + .readyReadStandardOutput.connect(self.readAllStandard) + self.process \ + .readyReadStandardError.connect(self.readAllStandard) + self.process.waitForFinished(50000) + print("Ran Sandpiper successfully") + os.chdir(self.cur_dir) + self.fname = self.fname.split('.')[0] + ".sv" + + # This function parses the module name and \ + # input/output ports of verilog code using HDL parse + # and writes to the connection_info.txt + + def verilogParse(self): + + with open(self.modelpath + self.fname, 'rt') as fh: + code = fh.read() + + code = code.replace("wire", " ") + code = code.replace("reg", " ") + vlog_ex = vlog.VerilogExtractor() + vlog_mods = vlog_ex.extract_objects_from_source(code) + f = open(self.modelpath + "connection_info.txt", 'w') + for m in vlog_mods: + if m.name.lower() == self.fname.split('.')[0]: + print(str(m.name) + " " + self.fname.split('.')[0]) + for p in m.ports: + print(p.data_type) + if str(p.data_type).find(':') == -1: + p.port_number = "1" + else: + x = p.data_type.split(":") + print(x) + y = x[0].split("[") + z = x[1].split("]") + z = int(y[1]) - int(z[0]) + p.port_number = z + 1 + + for m in vlog_mods: + if m.name.lower() == self.fname.split('.')[0]: + m.name = m.name.lower() + print('Module "{}":'.format(m.name)) + for p in m.generics: + print('\t{:20}{:8}{}'.format(p.name, p.mode, p.data_type)) + print(' Ports:') + for p in m.ports: + print( + '\t{:20}{:8}{}'.format( + p.name, p.mode, p.port_number)) + f.write( + '\t{:20}{:8}{}\n'.format( + p.name, p.mode, p.port_number)) + break + f.close() + if m.name.lower() != self.fname.split(".")[0]: + QtWidgets.QMessageBox.critical( + None, + "Error Message", + "<b>Error: File name and module \ + name are not same. Please ensure that they are same</b>", + QtWidgets.QMessageBox.Ok) + + self.obj_Appconfig.print_info( + 'NgVeri Stopped due to File \ + name and module name not matching error') + return "Error" + modelname = str(m.name) + schematicLib = createkicad.AutoSchematic() + schematicLib.init(modelname, self.modelpath) + error = schematicLib.createkicad() + if error == "Error": + return "Error" + return "No Error" + + # This function is used to get the Port Information from + # connection_info.txt + def getPortInfo(self): + readfile = open(self.modelpath + 'connection_info.txt', 'r') + data = readfile.readlines() + self.input_list = [] + self.output_list = [] + 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: + self.input_list.append(line.split()) + if inout_items: + self.input_list.append(line.split()) + if out_items: + self.output_list.append(line.split()) + + self.input_port = [] + self.output_port = [] + + # creating list of input and output port with its weight + for input in self.input_list: + self.input_port.append(input[0] + ":" + input[2]) + for output in self.output_list: + self.output_port.append(output[0] + ":" + output[2]) + + # This function is used to create the cfunc.mod file in Ngspice folder + # automatically + + def cfuncmod(self): + + # ############# Creating content for cfunc.mod file ############## # + + print("Starting With cfunc.mod file") + cfunc = open(self.modelpath + 'cfunc.mod', 'w') + print("Building content for cfunc.mod file") + + comment = '''/* This is cfunc.mod file auto generated by gen_con_info.py + Developed by Sumanto Kar at IIT Bombay */\n + ''' + + header = ''' + #include <stdio.h> + #include <math.h> + #include <string.h> + #include "sim_main_''' + self.fname.split('.')[0] + '''.h" + + ''' + + function_open = ( + '''void cm_''' + self.fname.split('.')[0] + '''(ARGS) \n{''') + + digital_state_output = [] + for item in self.output_port: + digital_state_output.append( + "Digital_State_t *_op_" + item.split(':')[0] + + ", *_op_" + item.split(':')[0] + "_old;" + ) + + var_section = ''' + static int inst_count=0; + int count=0; + ''' + + # Start of INIT function + init_start_function = ''' + if(INIT) + { + inst_count++; + PARAM(instance_id)=inst_count; + foo''' + self.fname.split('.')[0] + '''(0,inst_count); + /* Allocate storage for output ports \ +and set the load for input ports */ + + ''' + port_init = [] + for i, item in enumerate(self.input_port + self.output_port): + port_init.append(''' + port_''' + item.split(':')[0] + '''=PORT_SIZE(''' + item.split(':')[0] + '''); +''') + + cm_event_alloc = [] + cm_count_output = 0 + for item in self.output_port: + cm_event_alloc.append( + "cm_event_alloc(" + + str(cm_count_output) + "," + item.split(':')[1] + + "*sizeof(Digital_State_t));" + ) + cm_count_output = cm_count_output + 1 + + load_in_port = [] + for item in self.input_port: + load_in_port.append( + "for(Ii=0;Ii<PORT_SIZE(" + item.split(':')[0] + + ");Ii++)\n\t\t{\n\t\t\tLOAD(" + item.split(':')[0] + + "[Ii])=PARAM(input_load); \n\t\t}" + ) + + cm_count_ptr = 0 + cm_event_get_ptr = [] + for item in self.output_port: + cm_event_get_ptr.append( + "_op_" + item.split(':')[0] + " = _op_" + + item.split(':')[0] + + "_old = (Digital_State_t *) cm_event_get_ptr(" + + str(cm_count_ptr) + ",0);" + ) + + cm_count_ptr = cm_count_ptr + 1 + + els_evt_ptr = [] + els_evt_count1 = 0 + els_evt_count2 = 0 + for item in self.output_port: + els_evt_ptr.append("_op_" + item.split(":")[0] + + " = (Digital_State_t *) cm_event_get_ptr(" + + str(els_evt_count1) + "," + + str(els_evt_count2) + ");") + els_evt_count2 = els_evt_count2 + 1 + els_evt_ptr.append("_op_" + item.split(":")[0] + "_old" + + " = (Digital_State_t *) cm_event_get_ptr(" + + str(els_evt_count1) + "," + + str(els_evt_count2) + ");") + els_evt_count1 = els_evt_count1 + 1 + + # Assign bit value to every input + assign_data_to_input = [] + for item in self.input_port: + assign_data_to_input.append("\ + for(Ii=0;Ii<PORT_SIZE(" + item.split(':')[0] + ");Ii++)\n\ + {\n\ + if( INPUT_STATE(" + item.split(':')[0] + "[Ii])==ZERO )\n\ + {\n\ + temp_" + item.split(':')[0] + "[Ii]=0;\ + }\n\ + else\n\ + {\n\ + temp_" + item.split(':')[0] + "[Ii]=1;\n\ + }\n\ + }\n") + + # Scheduling output event + sch_output_event = [] + + for item in self.output_port: + sch_output_event.append( + "\t/* Scheduling event and processing them */\n\ + for(Ii=0;Ii<PORT_SIZE(" + item.split(':')[0] + ");Ii++)\n\ + {\n\ + if(temp_" + item.split(':')[0] + "[Ii]==0)\n\ + {\n\ + _op_" + item.split(':')[0] + "[Ii]=ZERO;\n\ + }\n\ + else if(temp_" + item.split(':')[0] + "[Ii]==1)\n\ + {\n\ + _op_" + item.split(':')[0] + "[Ii]=ONE;\n\ + }\n\ + else\n\ + {\n\ + printf(\"Unknown value\\n\");\n\ + }\n\n\ + if(ANALYSIS == DC)\n\ + {\n\ + OUTPUT_STATE(" + item.split(':')[0] + "[Ii]) = _op_" + item.split(':')[0] + "[Ii];\n\ + }\n\ + else if(_op_" + item.split(':')[0] + "[Ii] != _op_" + item.split(':')[0] + "_old[Ii])\n\ + {\n\ + OUTPUT_STATE(" + item.split(':')[0] + "[Ii]) = _op_" + item.split(':')[0] + "[Ii];\n\ + OUTPUT_DELAY(" + item.split(':')[0] + "[Ii]) = ((_op_" + item.split(':')[0] + "[Ii] == ZERO) ? PARAM(fall_delay) : PARAM(rise_delay));\n\ + }\n\ + else\n\ + {\n\ + OUTPUT_CHANGED(" + item.split(':')[0] + "[Ii]) = FALSE;\n\ + }\n\ + OUTPUT_STRENGTH(" + item.split(':')[0] + "[Ii]) = STRONG;\n\ + }\n") + + # Writing content in cfunc.mod file + cfunc.write(comment) + cfunc.write(header) + cfunc.write("\n") + cfunc.write(function_open) + cfunc.write("\n") + + # Adding digital state Variable + for item in digital_state_output: + cfunc.write("\t" + item + "\n") + + # Adding variable declaration section + cfunc.write(var_section) + + # Adding INIT portion + cfunc.write(init_start_function) + for item in port_init: + cfunc.write(item) + for item in cm_event_alloc: + cfunc.write(2 * "\t" + item) + cfunc.write("\n") + + cfunc.write(2 * "\t" + "/* set the load for input ports. */") + cfunc.write("\n") + cfunc.write(2 * "\t" + "int Ii;") + cfunc.write("\n") + + for item in load_in_port: + cfunc.write(2 * "\t" + item) + cfunc.write("\n") + cfunc.write("\n") + cfunc.write(2 * "\t" + "/*Retrieve Storage for output*/") + cfunc.write("\n") + for item in cm_event_get_ptr: + cfunc.write(2 * "\t" + item) + cfunc.write("\n") + cfunc.write("\n") + + # if os.name == 'nt': + # digital_home = parser.get('NGSPICE', 'DIGITAL_MODEL') + # msys_home = parser.get('COMPILER', 'MSYS_HOME') + # cmd_str2 = "/start_server.sh %d %s & read" + "\\" + "\"" + "\"" + # cmd_str1 = os.path.normpath( + # "\"" + digital_home + "/" + + # fname.split('.')[0] + "/DUTghdl/" + # ) + # cmd_str1 = cmd_str1.replace("\\", "/") + + # cfunc.write( + # '\t\tsnprintf(command,1024, "start mintty.exe -t ' + + # '\\"VHDL-Testbench Logs\\" -h always bash.exe -c ' + + # '\\' + cmd_str1 + cmd_str2 + ', sock_port, my_ip);' + # ) + # else: + # cfunc.write( + # '\t\tsnprintf(command,1024,"' + home + + # '/ngspice-nghdl/src/xspice/icm/ghdl/' + + # fname.split('.')[0] + + # '/DUTghdl/start_server.sh %d %s &", sock_port, my_ip);' + # ) + + cfunc.write("\n\t}") + cfunc.write("\n") + cfunc.write("\telse\n\t{\n") + + for item in els_evt_ptr: + cfunc.write(2 * "\t" + item) + cfunc.write("\n") + cfunc.write("\t}") + cfunc.write("\n\n") + + cfunc.write("\t//Formating data for sending it to client\n") + cfunc.write("\tint Ii;\n") + cfunc.write("\tcount=(int)PARAM(instance_id);\n\n") + for item in assign_data_to_input: + cfunc.write(item) + + cfunc.write("\tfoo" + self.fname.split('.')[0] + "(1,count);\n\n") + + for item in sch_output_event: + cfunc.write(item) + + # Close cm_ function + cfunc.write("\n}") + cfunc.close() + + # This function creates the ifspec file automatically in Ngspice folder + + def ifspecwrite(self): + print("Starting with ifspec.ifs file") + ifspec = open(self.modelpath + 'ifspec.ifs', 'w') + + print("Gathering Al the content for ifspec file") + + ifspec_comment = ''' + /* + SUMMARY: This file is auto generated and it contains the interface + specification for the code model. */\n + ''' + + name_table = 'NAME_TABLE:\n\ + C_Function_Name: cm_' + self.fname.split('.')[0] + '\n\ + Spice_Model_Name: ' + self.fname.split('.')[0] + '\n\ + Description: "Model generated from ghdl code ' + self.fname + '" \n' + + # Input and Output Port Table + in_port_table = [] + out_port_table = [] + + for item in self.input_port: + port_table = 'PORT_TABLE:\n' + port_name = 'Port_Name:\t' + item.split(':')[0] + '\n' + description = ( + 'Description:\t"input port ' + item.split(':')[0] + '"\n' + ) + direction = 'Direction:\tin\n' + default_type = 'Default_Type:\td\n' + allowed_type = 'Allowed_Types:\t[d]\n' + vector = 'Vector:\tyes\n' + vector_bounds = ( + 'Vector_Bounds:\t[' + item.split(':')[1] + + ' ' + item.split(":")[1] + ']\n' + ) + null_allowed = 'Null_Allowed:\tno\n' + + # Insert detail in the list + in_port_table.append( + port_table + port_name + description + + direction + default_type + allowed_type + + vector + vector_bounds + null_allowed + ) + + for item in self.output_port: + port_table = 'PORT_TABLE:\n' + port_name = 'Port_Name:\t' + item.split(':')[0] + '\n' + description = ( + 'Description:\t"output port ' + item.split(':')[0] + '"\n' + ) + direction = 'Direction:\tout\n' + default_type = 'Default_Type:\td\n' + allowed_type = 'Allowed_Types:\t[d]\n' + vector = 'Vector:\tyes\n' + vector_bounds = ( + 'Vector_Bounds:\t[' + item.split(':')[1] + + ' ' + item.split(":")[1] + ']\n' + ) + null_allowed = 'Null_Allowed:\tno\n' + + # Insert detail in the list + in_port_table.append( + port_table + port_name + description + + direction + default_type + allowed_type + + vector + vector_bounds + null_allowed + ) + + parameter_table = ''' + + PARAMETER_TABLE: + Parameter_Name: instance_id input_load + Description: "instance_id" "input load value (F)" + Data_Type: real real + Default_Value: 0 1.0e-12 + Limits: - - + Vector: no no + Vector_Bounds: - - + Null_Allowed: yes yes + + PARAMETER_TABLE: + Parameter_Name: rise_delay fall_delay + Description: "rise delay" "fall delay" + Data_Type: real real + Default_Value: 1.0e-9 1.0e-9 + Limits: [1e-12 -] [1e-12 -] + Vector: no no + Vector_Bounds: - - + Null_Allowed: yes yes + + ''' + + # Writing all the content in ifspec file + ifspec.write(ifspec_comment) + ifspec.write(name_table + "\n\n") + + for item in in_port_table: + ifspec.write(item + "\n") + + ifspec.write("\n") + + for item in out_port_table: + ifspec.write(item + "\n") + + ifspec.write("\n") + ifspec.write(parameter_table) + ifspec.write("\n") + ifspec.close() + + # This function creates the header file of sim_main file automatically in + # Ngspice folder + + def sim_main_header(self): + print("Starting With sim_main_" + self.fname.split('.')[0] + ".h file") + simh = open( + self.modelpath + + 'sim_main_' + + self.fname.split('.')[0] + + '.h', + 'w') + print("Building content for sim_main_" + + self.fname.split('.')[0] + ".h file") + simh.write("int foo" + self.fname.split('.')[0] + "(int,int);") + extern_var = [] + for i, item in enumerate(self.input_port + self.output_port): + extern_var.append(''' + int temp_''' + item.split(':')[0] + '''[1024]; + int port_''' + item.split(':')[0] + ''';''') + for item in extern_var: + simh.write(item) + simh.close() + + # This function creates the sim_main file needed by verilator + # automatically in Ngspice folder + def sim_main(self): + print( + "Starting With sim_main_" + + self.fname.split('.')[0] + + ".cpp file") + csim = open( + self.modelpath + + 'sim_main_' + + self.fname.split('.')[0] + + '.cpp', + 'w') + print( + "Building content for sim_main_" + + self.fname.split('.')[0] + + ".cpp file") + + comment = '''/* This is cfunc.mod file auto generated by gen_con_info.py + Developed by Sumanto Kar at IIT Bombay */\n + ''' + + header = ''' + #include <memory> + #include <verilated.h> + #include "V''' + self.fname.split('.')[0] + '''.h" + #include <stdio.h> + #include <stdio.h> + #include <fstream> + #include <stdlib.h> + #include <string> + #include <iostream> + #include <cstring> + using namespace std; + ''' + + extern_var = [] + for i, item in enumerate(self.input_port + self.output_port): + extern_var.append(''' + extern "C" int temp_''' + item.split(':')[0] + '''[1024]; + extern "C" int port_''' + item.split(':')[0] + ''';''') + + extern_var.append(''' + extern "C" int foo''' + self.fname.split('.')[0] + '''(int,int); + ''') + convert_func = ''' + void int2arr''' + self.fname.split('.')[0] + '''(int num, int array[], int n) + { + for (int i = 0; i < n && num>=0; i++) + { + array[n-i-1] = num % 2; + num /= 2; + } + } + int arr2int''' + self.fname.split('.')[0] + '''(int array[],int n) + { + int i,k=0; + for (i = 0; i < n; i++) + k = 2 * k + array[i]; + return k; + } + ''' + foo_func = ''' + int foo''' + self.fname.split('.')[0] + '''(int init,int count) + { + static VerilatedContext* contextp = new VerilatedContext; + static V''' + self.fname.split('.')[0] + "* " + self.fname.split('.')[0] + '''[1024]; + count--; + if (init==0) + { + ''' + self.fname.split('.')[0] + '''[count]=new V''' + self.fname.split('.')[0] + '''{contextp}; + contextp->traceEverOn(true); + } + else + { + contextp->timeInc(1); + printf("=============''' + self.fname.split('.')[0] + ''' : New Iteration==========="); + printf("\\nInstance : %d\\n",count); + printf("\\nInside foo before eval.....\\n"); +''' + + before_eval = [] + after_eval = [] + for i, item in enumerate(self.input_port + self.output_port): + before_eval.append( + '''\t\t\t\tprintf("''' + + item.split(':')[0] + + '''=%d\\n", ''' + + self.fname.split('.')[0] + + '''[count] ->''' + + item.split(':')[0] + + ''');\n''') + for i, item in enumerate(self.input_port): + + before_eval.append( + '''\t\t\t\t''' + + self.fname.split('.')[0] + + '''[count]->''' + + item.split(':')[0] + + ''' = arr2int''' + + self.fname.split('.')[0] + + '''(temp_''' + + item.split(':')[0] + + ", port_" + + item.split(':')[0] + + ''');\n''') + before_eval.append( + "\t\t\t\t" + + self.fname.split('.')[0] + + "[count]->eval();\n") + + after_eval.append(''' + printf("\\nInside foo after eval.....\\n");\n''') + for i, item in enumerate(self.input_port + self.output_port): + after_eval.append( + '''\t\t\t\tprintf("''' + + item.split(':')[0] + + '''=%d\\n", ''' + + self.fname.split('.')[0] + + '''[count] ->''' + + item.split(':')[0] + + ''');\n''') + + for i, item in enumerate(self.output_port): + after_eval.append( + "\t\t\t\tint2arr" + + self.fname.split('.')[0] + + "(" + + self.fname.split('.')[0] + + '''[count] -> ''' + + item.split(':')[0] + + ''', temp_''' + + item.split(':')[0] + + ''', port_''' + + item.split(':')[0] + + ''');\n''') + after_eval.append(''' + } + return 0; + }''') + + csim.write(comment) + csim.write(header) + for item in extern_var: + csim.write(item) + csim.write(convert_func) + csim.write(foo_func) + + for item in before_eval: + csim.write(item) + for item in after_eval: + csim.write(item) + csim.close() + + # This function creates modpathlst in Ngspice folder + def modpathlst(self): + print("Editing modpath.lst file") + mod = open(self.digital_home + '/modpath.lst', 'r') + text = mod.read() + mod.close() + mod = open(self.digital_home + '/modpath.lst', 'a+') + if not self.fname.split('.')[0] in text: + mod.write(self.fname.split('.')[0] + "\n") + mod.close() + + # This function is used to run the Verilator using the verilator commands + def run_verilator(self): + self.cur_dir = os.getcwd() + file = open("../maker/lint_off.txt").readlines() + wno = " " + for item in file: + wno += " -Wno-" + item.strip("\n") + print("Running Verilator.............") + os.chdir(self.modelpath) + self.release_home = self.parser.get('NGSPICE', 'RELEASE') + # print(self.modelpath) + + self.cmd = "verilator -Wall " + wno + "\ + --cc --exe --Mdir . -CFLAGS -fPIC sim_main_" + \ + self.fname.split('.')[0] + ".cpp " + self.fname + self.process = QtCore.QProcess(self) + self.process.readyReadStandardOutput.connect(self.readAllStandard) + self.process.start('sh', ['-c', self.cmd]) + self.termtitle("RUN VERILATOR") + self.termtext("Current Directory: " + self.modelpath) + self.termtext("Command: " + self.cmd) + # self.process.setProcessChannelMode(QtCore.QProcess.MergedChannels) + self.process \ + .readyReadStandardOutput.connect(self.readAllStandard) + self.process \ + .readyReadStandardError.connect(self.readAllStandard) + self.process.waitForFinished(50000) + print("Verilator Executed") + os.chdir(self.cur_dir) + + # Running make verilator using this function + def make_verilator(self): + self.cur_dir = os.getcwd() + print("Make Verilator.............") + os.chdir(self.modelpath) + self.cmd = "make -f V" + self.fname.split('.')[0]\ + + ".mk V" + self.fname.split( + '.')[0] + "__ALL.a sim_main_" \ + + self.fname.split('.')[0] + ".o verilated.o" + self.process = QtCore.QProcess(self) + self.process.readyReadStandardOutput.connect(self.readAllStandard) + self.process.start('sh', ['-c', self.cmd]) + self.termtitle("MAKE VERILATOR") + self.termtext("Current Directory: " + self.modelpath) + self.termtext("Command: " + self.cmd) + self.process \ + .readyReadStandardOutput.connect(self.readAllStandard) + self.process \ + .readyReadStandardError.connect(self.readAllStandard) + self.process.waitForFinished(50000) + + print("Make Verilator Executed") + os.chdir(self.cur_dir) + + # This function copies the verilator files/object files from + # src/xspice/icm/Ngveri/ to release/src/xspice/icm/Ngveri/ + def copy_verilator(self): + self.cur_dir = os.getcwd() + print("Copying the required files to Release Folder.............") + os.chdir(self.modelpath) + self.release_home = self.parser.get('NGSPICE', 'RELEASE') + path_icm = os.path.join(self.release_home, "src/xspice/icm/Ngveri/") + if not os.path.isdir(path_icm + self.fname.split('.')[0]): + os.mkdir(path_icm + self.fname.split('.')[0]) + path_icm = path_icm + self.fname.split('.')[0] + if os.path.exists( + path_icm + + "sim_main_" + + self.fname.split('.')[0] + + ".o"): + os.remove(path_icm + "sim_main_" + self.fname.split('.')[0] + ".o") + if os.path.exists( + self.release_home + + "src/xspice/icm/" + + "verilated.o"): + os.remove(self.release_home + "src/xspice/icm/" + "verilated.o") + if os.path.exists( + path_icm + + "V" + + self.fname.split('.')[0] + + "__ALL.o"): + os.remove(path_icm + "V" + self.fname.split('.')[0] + "__ALL.o") + # print(self.modelpath) + try: + self.cmd = "cp sim_main_" + \ + self.fname.split('.')[0] + ".o V" + \ + self.fname.split('.')[0] + "__ALL.o " + path_icm + self.process = QtCore.QProcess(self) + self.args = ['-c', self.cmd] + self.process \ + .readyReadStandardOutput.connect(self.readAllStandard) + self.process \ + .readyReadStandardError.connect(self.readAllStandard) + self.process.start('sh', self.args) + self.termtitle("COPYING FILES") + self.termtext("Current Directory: " + self.modelpath) + self.termtext("Command: " + self.cmd) + self.process.waitForFinished(50000) + self.cmd = "cp verilated.o " + self.release_home \ + + "/src/xspice/icm/" + self.process.start('sh', ['-c', self.cmd]) + self.termtext("Command: " + self.cmd) + self.process \ + .readyReadStandardOutput.connect(self.readAllStandard) + self.process.waitForFinished(50000) + print("Copied the files") + os.chdir(self.cur_dir) + except BaseException: + print("There is error in Copying Files ") + + # Running the make command for Ngspice + def runMake(self): + print("run Make Called") + self.release_home = self.parser.get('NGSPICE', 'RELEASE') + path_icm = os.path.join(self.release_home, "src/xspice/icm") + os.chdir(path_icm) + + try: + if os.name == 'nt': + # path to msys bin directory where make is located + self.msys_bin = self.parser.get('COMPILER', 'MSYS_HOME') + self.cmd = self.msys_bin + "\\make.exe" + else: + self.cmd = "make" + + print("Running Make command in " + path_icm) + path = os.getcwd() # noqa + self.process = QtCore.QProcess(self) + self.process.start('sh', ['-c', self.cmd]) + print("make command process pid ---------- >", self.process.pid()) + + self.termtitle("MAKE 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: + print("There is error in 'make' ") + # sys.exit() + + # Running the make install command for Ngspice + def runMakeInstall(self): + self.cur_dir = os.getcwd() + print("run Make Install Called") + self.release_home = self.parser.get('NGSPICE', 'RELEASE') + path_icm = os.path.join(self.release_home, "src/xspice/icm") + os.chdir(path_icm) + + try: + if os.name == 'nt': + self.msys_bin = self.parser.get('COMPILER', 'MSYS_HOME') + self.cmd = self.msys_bin + "\\make.exe install" + else: + self.cmd = "make install" + print("Running Make Install") + path = os.getcwd() # noqa + try: + self.process.close() + except BaseException: + pass + + self.process = QtCore.QProcess(self) + self.process.start('sh', ['-c', self.cmd]) + # text="<span style=\" font-size:8pt; font-weight:600; + # color:#000000;\" >" + 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' ") + # sys.exit() + + # This function is used to add additional files required by the verilog + # top module + def addfile(self): + print("Adding the files required by the top level module file") + + init_path = '../../../' + 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", + "<b>Error: No File Chosen. Please chose a file</b>", + 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() + 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) + + # This function is used to add additional folder required by the verilog + # top module + + def addfolder(self): + # self.cur_dir = os.getcwd() + print("Adding the folder required by the top level module file") + + init_path = '../../../' + if os.name == 'nt': + init_path = '' # noqa:F841 + includefolder = QtCore.QDir.toNativeSeparators( + QtWidgets.QFileDialog.getExistingDirectory( + self, "open", "home" + ) + ) + if includefolder == "": + reply = QtWidgets.QMessageBox.critical( + None, "Error Message", + "<b>Error: No Folder Chosen. Please chose a folder</b>", + QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel + ) + if reply == QtWidgets.QMessageBox.Ok: + self.addfolder() + self.obj_Appconfig.print_info('Add Folder Called') + + elif reply == QtWidgets.QMessageBox.Cancel: + self.obj_Appconfig.print_info('No File Chosen') + + self.modelpath = self.digital_home + \ + "/" + self.fname.split('.')[0] + "/" + + reply = QtWidgets.QMessageBox.question( + None, "Message", + '''<b>If you want only the contents\ + of the folder to be added press "Yes".\ + If you want complete folder \ + to be added, press "No". </b>''', + 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) + + # This function is used to print the titles in the terminal of Ngveri tab + + def termtitle(self, textin): + + Text = "<span style=\" font-size:20pt; \ + font-weight:1000; color:#0000FF;\" >" + Text += "<br>================================<br>" + Text += textin + Text += "<br>================================<br>" + Text += "</span>" + self.termedit.append(Text) + + # This function is used to print the text/commands in the terminal of + # Ngveri tab + def termtext(self, textin): + + Text = "<span style=\" font-size:12pt;\ + font-weight:500; color:#000000;\" >" + Text += textin + Text += "</span>" + self.termedit.append(Text) + + # This function reads all the Standard output data and the errors from the + # process that aree being run + @QtCore.pyqtSlot() + def readAllStandard(self): + # self.termedit = termedit + # self.termedit.append(str(self.process.readAll().data(),\ + # encoding='utf-8')) + stdoutput = self.process.readAll() + TextStdOut = "<span style=\" font-size:12pt;\ + font-weight:300; color:#000000;\" >" + for line in str(stdoutput.data(), encoding='utf-8').split("\n"): + TextStdOut += "<br>" + line + TextStdOut += "</span>" + 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 = "<span style=\" font-size:12pt; \ + font-weight:1000; color:#ff0000;\" >" + for line in str(stderror.data(), encoding='utf-8').split("\n"): + TextErr += "<br>" + line + TextErr += "</span>" + 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 = "<span style=\" font-size:12pt;\ + # font-weight:1000; color:#ff0000;\" >" + # for line in str(stderror.data(), encoding='utf-8').split("\n"): + # Text += "<br>"+line+"<br>" + # Text += "</span>" + # self.termedit.append(Text+"\n") + + # init_path = '../../../' + # 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", + # "<b>Error: No File Chosen. Please chose a file</b>", + # 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/src/maker/NgVeri.py b/src/maker/NgVeri.py new file mode 100755 index 00000000..c67ab45c --- /dev/null +++ b/src/maker/NgVeri.py @@ -0,0 +1,341 @@ +# ========================================================================= +# 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, QtGui +from . import Maker +from . import ModelGeneration +import os +import subprocess +from configuration.Appconfig import Appconfig +from configparser import SafeConfigParser +from configparser import ConfigParser + + +# beginning class NgVeri. This class create the NgVeri Tab +class NgVeri(QtWidgets.QWidget): + + # initialising the variables + def __init__(self, filecount): + print(self) + QtWidgets.QWidget.__init__(self) + # Maker.addverilog(self) + self.obj_Appconfig = Appconfig() + self.home = os.path.expanduser("~") + self.parser = SafeConfigParser() + self.parser.read(os.path.join( + self.home, os.path.join('.nghdl', 'config.ini'))) + self.ngspice_home = self.parser.get('NGSPICE', 'NGSPICE_HOME') + self.release_dir = self.parser.get('NGSPICE', 'RELEASE') + self.src_home = self.parser.get('SRC', 'SRC_HOME') + self.licensefile = self.parser.get('SRC', 'LICENSE') + self.digital_home = self.parser.get('NGSPICE', 'DIGITAL_MODEL') + self.digital_home = self.digital_home.split("/ghdl")[0] + "/Ngveri" + self.count = 0 + self.text = "" + self.entry_var = {} + self.createNgveriWidget() + self.fname = "" + self.filecount = filecount + + # Creating the various components of the Widget(Ngveri Tab) + + def createNgveriWidget(self): + + 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() + + # Adding the verilog file in Maker tab to Ngveri Tab automatically + def addverilog(self): + + init_path = '../../../' + if os.name == 'nt': + init_path = '' + # b=Maker.Maker(self) + print(Maker.verilogFile) + if Maker.verilogFile[self.filecount] == "": + reply = QtWidgets.QMessageBox.critical( + None, + "Error Message", + "<b>Error: No Verilog File Chosen. \ + Please chose a Verilog file in Makerchip Tab</b>", + QtWidgets.QMessageBox.Ok) + if reply == QtWidgets.QMessageBox.Ok: + self.obj_Appconfig.print_error( + 'No VerilogFile. Please add a File in Makerchip Tab') + return + + self.fname = Maker.verilogFile[self.filecount] + model = ModelGeneration.ModelGeneration(self.fname, self.entry_var[0]) + file = (os.path.basename(self.fname)).split('.')[0] + if self.entry_var[1].findText(file) == -1: + self.entry_var[1].addItem(file) + model.verilogfile() + error = model.verilogParse() + if error is not "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() + model.runMakeInstall() + + # This function is used to add additional files required by the verilog + # top module + + def addfile(self): + if len(Maker.verilogFile) < (self.filecount + 1): + reply = QtWidgets.QMessageBox.critical( + None, + "Error Message", + "<b>Error: No Verilog File Chosen. \ + Please chose a Verilog file in Makerchip Tab</b>", + QtWidgets.QMessageBox.Ok) + if reply == QtWidgets.QMessageBox.Ok: + self.obj_Appconfig.print_error( + 'No VerilogFile. Please chose\ + 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() + + # This function is used to add additional folder required by the verilog + # top module + def addfolder(self): + if len(Maker.verilogFile) < (self.filecount + 1): + reply = QtWidgets.QMessageBox.critical( + None, + "Error Message", + "<b>Error: No Verilog File Chosen. \ + Please chose a Verilog file in Makerchip Tab</b>", + QtWidgets.QMessageBox.Ok) + if reply == QtWidgets.QMessageBox.Ok: + self.obj_Appconfig.print_error( + 'No VerilogFile. Please chose \ + 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() + + # This function is used to clear the terminal + + def clearTerminal(self): + self.entry_var[0].setText("") + + # This function is used to create buttons/options + def createoptionsBox(self): + + self.optionsbox = QtWidgets.QGroupBox() + self.optionsbox.setTitle("Select Options") + self.optionsgrid = QtWidgets.QGridLayout() + + self.optionsgroupbtn = QtWidgets.QButtonGroup() + + self.addverilogbutton = QtWidgets.QPushButton( + "Run Verilog to NgSpice Converter") + 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 Other file") + 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 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 + + # This function is used to remove models in modlst of Ngspice folder if + # the user wants to remove a model.Note: files do not get removed + def edit_modlst(self, text): + if text == "Edit modlst": + 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", '''<b>Do you want to remove 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]) + model.runMake() + model.runMakeInstall() + return + + else: + return + + # This is to remove lint_off comments needed by the verilator warnings + # This function writes to the lint_off.txt here in the same folder + def lint_off_edit(self, text): + if text == "Edit 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", + '''<b>Do you want to remove the lint off error:''' + + text, + QtWidgets.QMessageBox.Ok, + QtWidgets.QMessageBox.Cancel) + if ret == QtWidgets.QMessageBox.Ok: + file = open("../maker/lint_off.txt", 'r') + data = file.readlines() + file.close() + + data.remove(text + "\n") + file = open("../maker/lint_off.txt", 'w') + for item in data: + file.write(item) + return + + else: + return + + # This is to add lint_off comments needed by the verilator warnings + # This function writes to the lint_off.txt here in the same folder + def add_lint_off(self): + text = self.entry_var[3].text() + + if self.entry_var[2].findText(text) == -1: + self.entry_var[2].addItem(text) + file = open("../maker/lint_off.txt", 'a+') + file.write(text + "\n") + file.close() + self.entry_var[3].setText("") + + # creating various other groups like terminal, edit modlst, edit lint_off + # and add lint_off + + def creategroup(self): + + 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("Edit modlst") + 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("Edit lint_off") + self.lint_off = open("../maker/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/src/maker/__init__.py b/src/maker/__init__.py new file mode 100755 index 00000000..e69de29b --- /dev/null +++ b/src/maker/__init__.py diff --git a/src/maker/createkicad.py b/src/maker/createkicad.py new file mode 100755 index 00000000..1a00bde9 --- /dev/null +++ b/src/maker/createkicad.py @@ -0,0 +1,346 @@ +# ========================================================================= +# 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 +# ORGANIZATION: eSim Team at FOSSEE, IIT Bombay +# CREATED: Monday 29, November 2021 +# REVISION: Tuesday 25, January 2022 +# ========================================================================= + +# importing the files and libraries +from . import Appconfig +import re +import os +import sys # noqa F401 +import xml.etree.cElementTree as ET +from PyQt5 import QtWidgets + +# beginning the AutoSchematic Class + + +class AutoSchematic: + + # initialising the variables here + def init(self, modelname, modelpath): + self.App_obj = Appconfig.Appconfig() + self.modelname = modelname.split('.')[0] + self.template = self.App_obj.kicad_lib_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 = Appconfig.src_home + inst_dir = eSim_src.replace('\\eSim', '') + self.kicad_ngveri_lib = \ + inst_dir + '/KiCad/share/kicad/library/eSim_Ngveri.lib' + else: + self.kicad_ngveri_lib = '/usr/share/kicad/library/eSim_Ngveri.lib' + # self.parser = self.App_obj.parser_ngveri + + # creating KiCAD library using this function + def createkicad(self): + + xmlFound = None + # read_file = open(self.modelpath+'connection_info.txt', 'r') + # data = read_file.readlines() + # print(data) + xmlFound = None + for root, dirs, files in os.walk(self.xml_loc): + if (str(self.modelname) + '.xml') in files: + xmlFound = root + print(xmlFound) + if xmlFound is None: + self.getPortInformation() + self.createXML() + self.createLib() + elif (xmlFound == os.path.join(self.xml_loc, 'Ngveri')): + print('Library already exists...') + ret = QtWidgets.QMessageBox.warning( + None, "Warning", '''<b>Library files for this model''' + + ''' already exist. Do you want to overwrite it?</b><br/> + If yes press ok, else cancel it and ''' + + '''change the name of your vhdl file.''', + QtWidgets.QMessageBox.Ok, QtWidgets.QMessageBox.Cancel + ) + if ret == QtWidgets.QMessageBox.Ok: + print("Overwriting existing libraries") + self.getPortInformation() + self.createXML() + self.removeOldLibrary() # Removes the exisitng library + self.createLib() + else: + print("Library Creation Cancelled") + return "Error" + + else: + print('Pre existing library...') + ret = QtWidgets.QMessageBox.critical( + self.parent, "Error", '''<b>A standard library already ''' + + '''exists with this name.</b><br/><b>Please change the ''' + + '''name of your vhdl file and upload it again</b>''', + QtWidgets.QMessageBox.Ok + ) + + # getting the port information here + + def getPortInformation(self): + portInformation = PortInfo(self, self.modelpath) + portInformation.getPortInfo() + self.portInfo = portInformation.bit_list + self.input_length = portInformation.input_len + self.portName = portInformation.port_name + + # creating the XML files in eSim-2.1/library/modelParamXML/Ngveri + def createXML(self): + 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) + + # Calculates the maximum between input and output ports + def findBlockSize(self): + 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]) + + # removing the old library + def removeOldLibrary(self): + cwd = os.getcwd() + os.chdir(self.lib_loc) + print("Changing directory to ", self.lib_loc) + f = open(self.kicad_ngveri_lib) + lines = f.readlines() + f.close() + + output = [] + line_reading_flag = False + + for line in lines: + if line.startswith("DEF"): + if line.split()[1] == self.modelname: + line_reading_flag = True + if not line_reading_flag: + output.append(line) + if line.startswith("ENDDEF"): + line_reading_flag = False + + f = open(self.kicad_ngveri_lib, 'w') + for line in output: + f.write(line) + + os.chdir(cwd) + print("Leaving directory, ", self.lib_loc) + + # creating the library + def createLib(self): + self.dist_port = 100 # Distance between two ports + self.inc_size = 100 # Increment size of a block + cwd = os.getcwd() + os.chdir(self.lib_loc) + print("Changing directory to ", self.lib_loc) + + lib_file = open(self.kicad_ngveri_lib, "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_lib).st_size == 0: + lib_file.write("EESchema-LIBRARY Version 2.3" + "\n\n") + # lib_file.write("#encoding utf-8"+ "\n"+ "#"+ "\n" + + # "#test_compo" + "\n"+ "#"+ "\n") + lib_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) + + lib_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 + + lib_file.write( + line4[0] + "\n" + line4[1] + "\n" + + self.template["start_draw"] + "\n" + ) + + draw_pos = self.template["draw_pos"] + draw_pos = draw_pos.split() + draw_pos[4] = str( + int(draw_pos[4]) - self.findBlockSize() * self.inc_size) + self.template["draw_pos"] = ' '.join(draw_pos) + + lib_file.write(self.template["draw_pos"] + "\n") + + input_port = self.template["input_port"] + input_port = input_port.split() + output_port = self.template["output_port"] + output_port = output_port.split() + 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)) + + # print("INPUTS AND OUTPUTS ") + # print("INPUTS:"+inputName) + # print("OUTPUTS:"outputName) + # print(inputs) + # print(outputs) + + inputs = self.char_sum(inputs) + outputs = self.char_sum(outputs) + + total = inputs + outputs + + port_list = [] + j = 0 + k = 0 # noqa F841 + for i in range(total): + if (i < inputs): + input_port[1] = inputName[i] + input_port[2] = str(i + 1) + input_port[4] = str(int(input_port[4]) - self.dist_port) + input_list = ' '.join(input_port) + port_list.append(input_list) + j = j + 1 + + else: + output_port[1] = outputName[i - inputs] + output_port[2] = str(i + 1) + output_port[4] = str(int(output_port[4]) - self.dist_port) + output_list = ' '.join(output_port) + port_list.append(output_list) + + for ports in port_list: + lib_file.write(ports + "\n") + lib_file.write( + self.template["end_draw"] + "\n" + + self.template["end_def"] + "\n\n\n" + ) + + os.chdir(cwd) + + +# beginning the PortInfo Class containing Port Information +class PortInfo: + + # initialising the variables + def __init__(self, model, modelpath): + self.modelname = model.modelname + # self.model_loc = model.parser.get('NGVERI', 'DIGITAL_MODEL') + self.bit_list = [] + self.port_name = [] + self.input_len = 0 + self.modelpath = modelpath + + # getting the port information from connection_info.txt + def getPortInfo(self): + 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()) + # print(input_list) + # print(output_list) + 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/src/maker/lint_off.txt b/src/maker/lint_off.txt new file mode 100755 index 00000000..5d4b7f0a --- /dev/null +++ b/src/maker/lint_off.txt @@ -0,0 +1,29 @@ +UNUSED +DECLFILENAME +BLKSEQ +WIDTH +SELRANGE +PINCONNECTEMPTY +DEFPARAM +IMPLICIT +COMBDLY +SYNCASYNCNET +UNOPTFLAT +UNSIGNED +CASEINCOMPLETE +UNDRIVEN +VARHIDDEN +CASEX +CASEOVERLAP +PINMISSING +LATCH +BLKANDNBLK +MULTIDRIVEN +NULLPORT +EOFNEWLINE +WIDTHCONCAT +ASSIGNDLY +MODDUP +STMTDLY +LITENDIAN +INITIALDLY diff --git a/src/maker/makerchip.py b/src/maker/makerchip.py new file mode 100755 index 00000000..29e1421d --- /dev/null +++ b/src/maker/makerchip.py @@ -0,0 +1,103 @@ +# ========================================================================= +# FILE: makerchip.py +# +# USAGE: --- +# +# DESCRIPTION: This defines all components of the Makerchip. +# +# 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 +import sys +import os +from PyQt5 import QtWidgets +from configuration.Appconfig import Appconfig +from projManagement.Validation import Validation +# from .Processing import PrcocessNetlist +from . import Maker +from . import NgVeri + +from xml.etree import ElementTree as ET + +# filecount is used to count thenumber of objects created +filecount = 0 + +# this class creates objects for creating the Maker and the Ngveri tabs + + +class makerchip(QtWidgets.QWidget): + + # initialising the variables + def __init__(self, parent=None): + QtWidgets.QWidget.__init__(self) + + # filecount=int(open("a.txt",'r').read()) + print(filecount) + # self.splitter.setOrientation(QtCore.Qt.Vertical) + print("==================================") + print("Makerchip and Verilog to Ngspice Converter") + print("==================================") + self.createMainWindow() + + # Creating the main Window(Main tab) + + def createMainWindow(self): + self.vbox = QtWidgets.QVBoxLayout() + self.hbox = QtWidgets.QHBoxLayout() + self.hbox.addStretch(1) + self.vbox.addWidget(self.createWidget()) + self.vbox.addLayout(self.hbox) + + self.setLayout(self.vbox) + self.setWindowTitle("Makerchip and Verilog to Ngspice Converter") + self.show() + + # Creating the maker and ngveri widgets + def createWidget(self): + global obj_Maker + global filecount + self.convertWindow = QtWidgets.QWidget() + + self.MakerTab = QtWidgets.QScrollArea() + obj_Maker = Maker.Maker(filecount) + self.MakerTab.setWidget(obj_Maker) + self.MakerTab.setWidgetResizable(True) + + global obj_NgVeri + self.NgVeriTab = QtWidgets.QScrollArea() + obj_NgVeri = NgVeri.NgVeri(filecount) + self.NgVeriTab.setWidget(obj_NgVeri) + self.NgVeriTab.setWidgetResizable(True) + self.tabWidget = QtWidgets.QTabWidget() + self.tabWidget.addTab(self.MakerTab, "Makerchip") + self.tabWidget.addTab(self.NgVeriTab, "NgVeri") + # The object refresh gets destroyed when Ngspice\ + # to verilog converter is called + # so calling refresh_change to start toggling of refresh again + self.tabWidget.currentChanged.connect(obj_Maker.refresh_change) + self.mainLayout = QtWidgets.QVBoxLayout() + self.mainLayout.addWidget(self.tabWidget) + self.convertWindow.setLayout(self.mainLayout) + self.convertWindow.show() + # incrementing filecount for every new window + filecount = filecount + 1 + return self.convertWindow diff --git a/src/maker/tlv/clk_gate.v b/src/maker/tlv/clk_gate.v new file mode 100755 index 00000000..77e9186d --- /dev/null +++ b/src/maker/tlv/clk_gate.v @@ -0,0 +1,40 @@ +/* +Copyright (c) 2015, Steven F. Hoover + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of Steven F. Hoover + may not be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +`include "sp_default.vh" +/* verilator lint_off LATCH */ + +// Clock gate module used by SandPiper default project. + +module clk_gate (output gated_clk, input free_clk, func_en, pwr_en, gating_override); + wire clk_en; + reg latched_clk_en /*verilator clock_enable*/; + assign clk_en = func_en & (pwr_en | gating_override); + `TLV_BLATCH(latched_clk_en, clk_en, free_clk) + assign gated_clk = latched_clk_en & free_clk; +endmodule + diff --git a/src/maker/tlv/pseudo_rand.m4out.tlv b/src/maker/tlv/pseudo_rand.m4out.tlv new file mode 100755 index 00000000..cb0d6149 --- /dev/null +++ b/src/maker/tlv/pseudo_rand.m4out.tlv @@ -0,0 +1,69 @@ +\m4_TLV_version 1b: tl-x.org +\SV +/* +Copyright (c) 2014, Steven F. Hoover + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of Steven F. Hoover + may not be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +module pseudo_rand + #(parameter WIDTH=257) // Random vector width, to a max of 257. + (input logic clk, + input logic reset, + output logic [WIDTH-1:0] rand_vect + ); + +// Currently, this implements a Galois LFSR. +// TODO: It should be XORed with something else so it doesn't just shift. +// Using polynomials with maximal number of taps would have less regular shifting behavior. + +// Bits are numbered in the reverse of the traditional order. This puts the taps in the lower bit positions. + +// Choose optimal parameters for given WIDTH. +localparam LFSR_WIDTH = + (WIDTH <= 64) ? 64 : + (WIDTH <= 128) ? 128 : + (WIDTH <= 257) ? 257 : 0; // 257 enables a large non-power of two for replication on an irregular boundary. +// Polynomial source: http://www.eej.ulst.ac.uk/~ian/modules/EEE515/files/old_files/lfsr/lfsr_table.pdf +localparam [LFSR_WIDTH-1:0] LFSR_POLY = {{(LFSR_WIDTH-8){1'b0}}, + (LFSR_WIDTH == 64) ? 8'b00011011 : + (LFSR_WIDTH == 128) ? 8'b10000111 : + (LFSR_WIDTH == 257) ? 8'b11000101 : 8'b0}; + +bit [256:0] SEED = 257'h0_7163e168_713d5431_6684e132_5cd84848_f3048b46_76874654_0c45f864_04e4684a; + + + +\TLV + |default + @0 + $reset = reset; + @1 + $lfsr[LFSR_WIDTH-1:0] = $reset ? *SEED : {$lfsr#+1[LFSR_WIDTH-2:0], 1'b0} ^ ({LFSR_WIDTH{$lfsr#+1[LFSR_WIDTH-1]}} & *LFSR_POLY); + @2 + *rand_vect = $lfsr[WIDTH-1:0]; + +\SV + +endmodule diff --git a/src/maker/tlv/pseudo_rand.sv b/src/maker/tlv/pseudo_rand.sv new file mode 100755 index 00000000..a9988b58 --- /dev/null +++ b/src/maker/tlv/pseudo_rand.sv @@ -0,0 +1,70 @@ +`line 2 "pseudo_rand.m4out.tlv" 0 //_\TLV_version 1b: tl-x.org, generated by SandPiper(TM) 1.11-2021/01/28-beta +`include "sp_default.vh" //_\SV +/* +Copyright (c) 2014, Steven F. Hoover + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of Steven F. Hoover + may not be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +module pseudo_rand + #(parameter WIDTH=257) // Random vector width, to a max of 257. + (input logic clk, + input logic reset, + output logic [WIDTH-1:0] rand_vect + ); + +// Currently, this implements a Galois LFSR. +// TODO: It should be XORed with something else so it doesn't just shift. +// Using polynomials with maximal number of taps would have less regular shifting behavior. + +// Bits are numbered in the reverse of the traditional order. This puts the taps in the lower bit positions. + +// Choose optimal parameters for given WIDTH. +localparam LFSR_WIDTH = + (WIDTH <= 64) ? 64 : + (WIDTH <= 128) ? 128 : + (WIDTH <= 257) ? 257 : 0; // 257 enables a large non-power of two for replication on an irregular boundary. +// Polynomial source: http://www.eej.ulst.ac.uk/~ian/modules/EEE515/files/old_files/lfsr/lfsr_table.pdf +localparam [LFSR_WIDTH-1:0] LFSR_POLY = {{(LFSR_WIDTH-8){1'b0}}, + (LFSR_WIDTH == 64) ? 8'b00011011 : + (LFSR_WIDTH == 128) ? 8'b10000111 : + (LFSR_WIDTH == 257) ? 8'b11000101 : 8'b0}; + +bit [256:0] SEED = 257'h0_7163e168_713d5431_6684e132_5cd84848_f3048b46_76874654_0c45f864_04e4684a; + + + +`include "pseudo_rand_gen.sv" //_\TLV + //_|default + //_@0 + assign DEFAULT_reset_a0 = reset; + //_@1 + assign DEFAULT_lfsr_a1[LFSR_WIDTH-1:0] = DEFAULT_reset_a1 ? SEED : {DEFAULT_lfsr_a2[LFSR_WIDTH-2:0], 1'b0} ^ ({LFSR_WIDTH{DEFAULT_lfsr_a2[LFSR_WIDTH-1]}} & LFSR_POLY); + //_@2 + assign rand_vect = DEFAULT_lfsr_a2[WIDTH-1:0]; endgenerate + +//_\SV + +endmodule + diff --git a/src/maker/tlv/pseudo_rand_gen.sv b/src/maker/tlv/pseudo_rand_gen.sv new file mode 100755 index 00000000..ec008179 --- /dev/null +++ b/src/maker/tlv/pseudo_rand_gen.sv @@ -0,0 +1,46 @@ +// Generated by SandPiper(TM) 1.11-2021/01/28-beta from Redwood EDA. +// Redwood EDA does not claim intellectual property rights to this file and provides no warranty regarding its correctness or quality. + + +`include "sandpiper_gen.vh" + + + + + +// +// Signals declared top-level. +// + +// For |default$lfsr. +logic [LFSR_WIDTH-1:0] DEFAULT_lfsr_a1, + DEFAULT_lfsr_a2; + +// For |default$reset. +logic DEFAULT_reset_a0, + DEFAULT_reset_a1; + + + +generate + + + // + // Scope: |default + // + + // For $lfsr. + always_ff @(posedge clk) DEFAULT_lfsr_a2[LFSR_WIDTH-1:0] <= DEFAULT_lfsr_a1[LFSR_WIDTH-1:0]; + + // For $reset. + always_ff @(posedge clk) DEFAULT_reset_a1 <= DEFAULT_reset_a0; + + + + +endgenerate + + + + +generate // This is awkward, but we need to go into 'generate' context in the line that `includes the declarations file. diff --git a/src/maker/tlv/sandpiper.vh b/src/maker/tlv/sandpiper.vh new file mode 100755 index 00000000..ccba8b0e --- /dev/null +++ b/src/maker/tlv/sandpiper.vh @@ -0,0 +1,72 @@ +/* +Copyright (c) 2015, Steven F. Hoover + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of Steven F. Hoover + may not be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Project-independent SandPiper header file. + +`ifndef SANDPIPER_VH +`define SANDPIPER_VH + + +// Note, these have no SP prefix, so collisions are possible. + + +`ifdef WHEN + // Make sure user definition does not collide. + !!!ERROR: WHEN macro already defined +`else + `ifdef SP_PHYS + // Phys compilation disabled X-injection. + `define WHEN(valid_sig) + `else + // Inject X. + `define WHEN(valid_sig) !valid_sig ? 'x : + `endif +`endif + + +// SandPiper does not generate set/reset flops. Reset is implemented as combinational +// logic, and it is up to synthesis to infer set/reset flops when possible. +//`ifdef RESET +// // Make sure user definition does not collide. +// !!!ERROR: RESET macro already defined +//`else +// `define RESET(i, reset) ((reset) ? '0 : i) +//`endif +// +//`ifdef SET +// // Make sure user definition does not collide. +// !!!ERROR: SET macro already defined +//`else +// `define SET(i, set) ((set) ? '1 : i) +//`endif + +// Since SandPiper required use of all signals, this is useful to create a +// bogus use and keep SandPiper happy when a signal, by intent, has no uses. +`define BOGUS_USE(ignore) + +`endif // SANDPIPER_VH + diff --git a/src/maker/tlv/sandpiper_gen.vh b/src/maker/tlv/sandpiper_gen.vh new file mode 100755 index 00000000..d063661a --- /dev/null +++ b/src/maker/tlv/sandpiper_gen.vh @@ -0,0 +1,4 @@ +// This just verifies that sandpiper.vh has been included. +`ifndef SANDPIPER_VH + !!!ERROR: SandPiper project's sp_<proj>.vh file must include sandpiper.vh. +`endif diff --git a/src/maker/tlv/sp_default.vh b/src/maker/tlv/sp_default.vh new file mode 100755 index 00000000..5e74259a --- /dev/null +++ b/src/maker/tlv/sp_default.vh @@ -0,0 +1,66 @@ +`ifndef SP_DEFAULT +`define SP_DEFAULT +/* +Copyright (c) 2015, Steven F. Hoover + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The name of Steven F. Hoover + may not be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +// File included by SandPiper-generated code for the default project configuration. +`include "sandpiper.vh" + + +// Latch macros. Inject 'x in simulation for clk === 'x. + +// A-phase latch. +`ifdef SP_PHYS +`define TLV_LATCH(in, out, clk) \ +always @ (in, clk) begin \ + if (clk === 1'b1) \ + out <= in; \ + else if (clk === 1'bx) \ + out <= 'x; \ +end +`else +`define TLV_LATCH(in, out, clk) always @ (in, clk) if (clk == 1'b1) out <= in; +`endif // SP_PHYS + +// B-phase latch. +`ifdef SP_PHYS +`define TLV_BLATCH(out, in, clk) \ +always @ (in, clk) begin \ + if (!clk === 1'b1) \ + out <= in; \ + else if (!clk === 1'bx) \ + out <= 'x; \ +end +`else +`define TLV_BLATCH(out, in, clk) always @ (in, clk) if (!clk == 1'b1) out <= in; +`endif // SP_PHYS + + + +`endif // SP_DEFAULT + diff --git a/src/maker/verilated.o b/src/maker/verilated.o Binary files differnew file mode 100755 index 00000000..db5f1163 --- /dev/null +++ b/src/maker/verilated.o diff --git a/src/ngspicetoModelica/ModelicaUI.py b/src/ngspicetoModelica/ModelicaUI.py index 6cd736b7..a687bb93 100644..100755 --- a/src/ngspicetoModelica/ModelicaUI.py +++ b/src/ngspicetoModelica/ModelicaUI.py @@ -28,7 +28,7 @@ class OpenModelicaEditor(QtWidgets.QWidget): self.FileEdit.setText(self.ngspiceNetlist) self.grid.addWidget(self.FileEdit, 0, 0) - self.browsebtn = QtWidgets.QPushButton("Browse") + self.browsebtn = QtWidgets.QPushButton("Browse Netlist (*.cir.out)") self.browsebtn.clicked.connect(self.browseFile) self.grid.addWidget(self.browsebtn, 0, 1) @@ -40,10 +40,29 @@ class OpenModelicaEditor(QtWidgets.QWidget): self.loadOMbtn.clicked.connect(self.callOMEdit) self.grid.addWidget(self.loadOMbtn, 3, 1) + self.OMPathtext = QtWidgets.QLineEdit() + self.OMPathtext.setText("") + self.grid.addWidget(self.OMPathtext, 4, 0) + + self.OMPathbrowsebtn = QtWidgets.QPushButton("Browse OM") + self.OMPathbrowsebtn.clicked.connect(self.OMPathbrowseFile) + self.grid.addWidget(self.OMPathbrowsebtn, 4, 1) + # self.setGeometry(300, 300, 350, 300) self.setLayout(self.grid) self.show() + def OMPathbrowseFile(self): + temp = QtCore.QDir.toNativeSeparators( + QtWidgets.QFileDialog.getExistingDirectory( + self, "Open OpenModelica Directory", "home" + ) + ) + + if temp: + self.OMPath = temp + self.OMPathtext.setText(self.OMPath) + def browseFile(self): temp = QtCore.QDir.toNativeSeparators( QtWidgets.QFileDialog.getOpenFileName( @@ -216,16 +235,17 @@ class OpenModelicaEditor(QtWidgets.QWidget): def callOMEdit(self): - if self.obj_validation.validateTool("OMEdit"): + try: modelFiles = glob.glob(self.modelicaNetlist) modelFiles = ' '.join(file for file in modelFiles) - self.cmd2 = "OMEdit " + modelFiles + self.cmd2 = self.OMPath+"/OMEdit " + modelFiles + print(self.cmd2) self.obj_workThread2 = Worker.WorkerThread(self.cmd2) self.obj_workThread2.start() print("OMEdit called") self.obj_appconfig.print_info("OMEdit called") - else: + except BaseException: self.msg = QtWidgets.QMessageBox() self.msgContent = ( "There was an error while opening OMEdit.<br/>" diff --git a/src/ngspicetoModelica/NgspicetoModelica.py b/src/ngspicetoModelica/NgspicetoModelica.py index 4df65c8d..6951e3c8 100644..100755 --- a/src/ngspicetoModelica/NgspicetoModelica.py +++ b/src/ngspicetoModelica/NgspicetoModelica.py @@ -344,7 +344,7 @@ class NgMoConverter: stat = self.mappingData["Sources"][sourceType][typ[0]] +\ ' ' + compName + '(offset = ' +\ self.getUnitVal(typ[1]) +\ - ', V = ' + self.getUnitVal(words[4]) + ', freqHz = ' +\ + ', V = ' + self.getUnitVal(words[4]) + ', f = ' +\ self.getUnitVal(words[5]) + ', startTime = ' +\ self.getUnitVal(words[6]) + ', phase = ' +\ self.getUnitVal(theta[0]) + ');' diff --git a/src/ngspicetoModelica/__init__.py b/src/ngspicetoModelica/__init__.py index e69de29b..e69de29b 100644..100755 --- a/src/ngspicetoModelica/__init__.py +++ b/src/ngspicetoModelica/__init__.py |