# ========================================================================= # 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 import hdlparse.verilog_parser as vlog from PyQt5 import QtCore, QtWidgets from PyQt5.QtCore import QThread from configuration.Appconfig import Appconfig import os 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 = [] # This function is called to accept TOS of makerchip def makerchipTOSAccepted(display=True): if not os.path.isfile(home + "/.makerchip_accepted"): if display: reply = QtWidgets.QMessageBox.warning( None, "Terms of Service", "Please review the Makerchip \ Terms of Service \ (\ https://www.makerchip.com/terms/). \ Have you read and do you \ accept these Terms of Service?", QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No ) if reply == QtWidgets.QMessageBox.Yes: f = open(home + "/.makerchip_accepted", "w") f.close() return True return False return True # 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", "No Verilog File Chosen. \ Please choose a verilog file.", QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel) if reply == QtWidgets.QMessageBox.Ok: self.addverilog() if self.verilogfile == "": return 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): try: wr = self.entry_var[1].toPlainText() open(self.verilogfile, "w+").write(wr) except BaseException as err: self.msg = QtWidgets.QErrorMessage(self) self.msg.setModal(True) self.msg.setWindowTitle("Error Message") self.msg.showMessage( "Error in saving verilog file. Please check if it is chosen." ) self.msg.exec_() print("Error in saving verilog file: " + str(err)) # This is used to run the makerchip-app def runmakerchip(self): init_path = '../../' if os.name == 'nt': init_path = '' try: if not makerchipTOSAccepted(True): return print("Running Makerchip IDE...........................") # 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 the top module? ", "Click on YES button if you want the top module \ to be added automatically. A .tlv file will be created \ in the directory of current verilog file \ and the Makerchip IDE will be running on \ this file. Otherwise click on NO button. \ To not open Makerchip IDE, click on CANCEL button. \

NOTE: Makerchip IDE requires an active \ internet connection and a browser.", 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') code = code.replace(" wire ", " ") code = code.replace(" reg ", " ") vlog_ex = vlog.VerilogExtractor() vlog_mods = vlog_ex.extract_objects_from_source(code) lint_off = open( init_path + "library/tlv/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", "Error: File name and module \ name are not same. Please \ ensure that they are same.", 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 IDE 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 IDE. \ Please check if verilog file is chosen.") self.msg.exec_() print("Error in running Makerchip IDE. \ Please check if verilog file is 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.runoptions.setToolTip( "Requires internet connection and a browser" ) self.runoptions.setToolTipDuration(5000) 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 makerchipTOSAccepted(False): self.acceptTOS = QtWidgets.QPushButton("Accept Makerchip TOS") self.optionsgroupbtn.addButton(self.acceptTOS) self.acceptTOS.clicked.connect(lambda: makerchipTOSAccepted(True)) 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 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