aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/tools/scripts/uhd_image_builder_gui.py
diff options
context:
space:
mode:
authorMartin Braun <martin.braun@ettus.com>2020-01-23 16:10:22 -0800
committerMartin Braun <martin.braun@ettus.com>2020-01-28 09:35:36 -0800
commitbafa9d95453387814ef25e6b6256ba8db2df612f (patch)
tree39ba24b5b67072d354775272e687796bb511848d /fpga/usrp3/tools/scripts/uhd_image_builder_gui.py
parent3075b981503002df3115d5f1d0b97d2619ba30f2 (diff)
downloaduhd-bafa9d95453387814ef25e6b6256ba8db2df612f.tar.gz
uhd-bafa9d95453387814ef25e6b6256ba8db2df612f.tar.bz2
uhd-bafa9d95453387814ef25e6b6256ba8db2df612f.zip
Merge FPGA repository back into UHD repository
The FPGA codebase was removed from the UHD repository in 2014 to reduce the size of the repository. However, over the last half-decade, the split between the repositories has proven more burdensome than it has been helpful. By merging the FPGA code back, it will be possible to create atomic commits that touch both FPGA and UHD codebases. Continuous integration testing is also simplified by merging the repositories, because it was previously difficult to automatically derive the correct UHD branch when testing a feature branch on the FPGA repository. This commit also updates the license files and paths therein. We are therefore merging the repositories again. Future development for FPGA code will happen in the same repository as the UHD host code and MPM code. == Original Codebase and Rebasing == The original FPGA repository will be hosted for the foreseeable future at its original local location: https://github.com/EttusResearch/fpga/ It can be used for bisecting, reference, and a more detailed history. The final commit from said repository to be merged here is 05003794e2da61cabf64dd278c45685a7abad7ec. This commit is tagged as v4.0.0.0-pre-uhd-merge. If you have changes in the FPGA repository that you want to rebase onto the UHD repository, simply run the following commands: - Create a directory to store patches (this should be an empty directory): mkdir ~/patches - Now make sure that your FPGA codebase is based on the same state as the code that was merged: cd src/fpga # Or wherever your FPGA code is stored git rebase v4.0.0.0-pre-uhd-merge Note: The rebase command may look slightly different depending on what exactly you're trying to rebase. - Create a patch set for your changes versus v4.0.0.0-pre-uhd-merge: git format-patch v4.0.0.0-pre-uhd-merge -o ~/patches Note: Make sure that only patches are stored in your output directory. It should otherwise be empty. Make sure that you picked the correct range of commits, and only commits you wanted to rebase were exported as patch files. - Go to the UHD repository and apply the patches: cd src/uhd # Or wherever your UHD repository is stored git am --directory fpga ~/patches/* rm -rf ~/patches # This is for cleanup == Contributors == The following people have contributed mainly to these files (this list is not complete): Co-authored-by: Alex Williams <alex.williams@ni.com> Co-authored-by: Andrej Rode <andrej.rode@ettus.com> Co-authored-by: Ashish Chaudhari <ashish@ettus.com> Co-authored-by: Ben Hilburn <ben.hilburn@ettus.com> Co-authored-by: Ciro Nishiguchi <ciro.nishiguchi@ni.com> Co-authored-by: Daniel Jepson <daniel.jepson@ni.com> Co-authored-by: Derek Kozel <derek.kozel@ettus.com> Co-authored-by: EJ Kreinar <ej@he360.com> Co-authored-by: Humberto Jimenez <humberto.jimenez@ni.com> Co-authored-by: Ian Buckley <ian.buckley@gmail.com> Co-authored-by: Jörg Hofrichter <joerg.hofrichter@ni.com> Co-authored-by: Jon Kiser <jon.kiser@ni.com> Co-authored-by: Josh Blum <josh@joshknows.com> Co-authored-by: Jonathon Pendlum <jonathan.pendlum@ettus.com> Co-authored-by: Martin Braun <martin.braun@ettus.com> Co-authored-by: Matt Ettus <matt@ettus.com> Co-authored-by: Michael West <michael.west@ettus.com> Co-authored-by: Moritz Fischer <moritz.fischer@ettus.com> Co-authored-by: Nick Foster <nick@ettus.com> Co-authored-by: Nicolas Cuervo <nicolas.cuervo@ettus.com> Co-authored-by: Paul Butler <paul.butler@ni.com> Co-authored-by: Paul David <paul.david@ettus.com> Co-authored-by: Ryan Marlow <ryan.marlow@ettus.com> Co-authored-by: Sugandha Gupta <sugandha.gupta@ettus.com> Co-authored-by: Sylvain Munaut <tnt@246tNt.com> Co-authored-by: Trung Tran <trung.tran@ettus.com> Co-authored-by: Vidush Vishwanath <vidush.vishwanath@ettus.com> Co-authored-by: Wade Fife <wade.fife@ettus.com>
Diffstat (limited to 'fpga/usrp3/tools/scripts/uhd_image_builder_gui.py')
-rwxr-xr-xfpga/usrp3/tools/scripts/uhd_image_builder_gui.py656
1 files changed, 656 insertions, 0 deletions
diff --git a/fpga/usrp3/tools/scripts/uhd_image_builder_gui.py b/fpga/usrp3/tools/scripts/uhd_image_builder_gui.py
new file mode 100755
index 000000000..4d14cd256
--- /dev/null
+++ b/fpga/usrp3/tools/scripts/uhd_image_builder_gui.py
@@ -0,0 +1,656 @@
+#!/usr/bin/env python
+"""
+Copyright 2016-2018 Ettus Research
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+"""
+
+from __future__ import print_function
+import os
+import re
+import sys
+import signal
+import threading
+import xml.etree.ElementTree as ET
+from PyQt5 import (QtGui,
+ QtCore,
+ QtWidgets)
+from PyQt5.QtWidgets import QGridLayout
+from PyQt5.QtCore import (pyqtSlot,
+ Qt,
+ QModelIndex)
+import uhd_image_builder
+
+signal.signal(signal.SIGINT, signal.SIG_DFL)
+
+class MainWindow(QtWidgets.QWidget):
+ """
+ UHD_IMAGE_BUILDER
+ """
+ # pylint: disable=too-many-instance-attributes
+
+ def __init__(self):
+ super(MainWindow, self).__init__()
+ ##################################################
+ # Initial Values
+ ##################################################
+ self.target = 'x300'
+ self.device = 'x310'
+ self.build_target = 'X310_RFNOC_HG'
+ self.oot_dirs = []
+ self.max_allowed_blocks = 10
+ self.cmd_dict = {"target": '-t {}'.format(self.build_target),
+ "device": '-d {}'.format(self.device),
+ "include": '',
+ "fill_fifos": '',
+ "viv_gui": '',
+ "cleanall": '',
+ "show_file": ''}
+ self.cmd_name = ['./uhd_image_builder.py', ]
+ self.cmd_prefix = list(self.cmd_name)
+ self.instantiation_file = os.path.join(uhd_image_builder.get_scriptpath(),
+ '..', '..', 'top', self.target,
+ 'rfnoc_ce_auto_inst_' + self.device.lower() +
+ '.v')
+
+ # List of blocks that are part of our library but that do not take place
+ # on the process this tool provides
+ self.blacklist = ['noc_block_radio_core', 'noc_block_axi_dma_fifo', 'noc_block_pfb']
+ self.lock = threading.Lock()
+ self.init_gui()
+
+ def init_gui(self):
+ """
+ Initializes GUI init values and constants
+ """
+ # pylint: disable=too-many-statements
+
+ ettus_sources = os.path.join(uhd_image_builder.get_scriptpath(), '..', '..', 'lib',\
+ 'rfnoc', 'Makefile.srcs')
+ ##################################################
+ # Grid Layout
+ ##################################################
+ grid = QGridLayout()
+ grid.setSpacing(15)
+ ##################################################
+ # Buttons
+ ##################################################
+ oot_btn = QtWidgets.QPushButton('Add OOT Blocks', self)
+ oot_btn.setToolTip('Add your custom Out-of-tree blocks')
+ grid.addWidget(oot_btn, 9, 0)
+ from_grc_btn = QtWidgets.QPushButton('Import from GRC', self)
+ grid.addWidget(from_grc_btn, 9, 2)
+ show_file_btn = QtWidgets.QPushButton('Show instantiation File', self)
+ grid.addWidget(show_file_btn, 9, 1)
+ add_btn = QtWidgets.QPushButton('>>', self)
+ grid.addWidget(add_btn, 2, 2)
+ rem_btn = QtWidgets.QPushButton('<<', self)
+ grid.addWidget(rem_btn, 3, 2)
+ self.gen_bit_btn = QtWidgets.QPushButton('Generate .bit file', self)
+ grid.addWidget(self.gen_bit_btn, 9, 3)
+
+ ##################################################
+ # Checkbox
+ ##################################################
+ self.fill_with_fifos = QtWidgets.QCheckBox('Fill with FIFOs', self)
+ self.viv_gui = QtWidgets.QCheckBox('Open Vivado GUI', self)
+ self.cleanall = QtWidgets.QCheckBox('Clean IP', self)
+ grid.addWidget(self.fill_with_fifos, 5, 2)
+ grid.addWidget(self.viv_gui, 6, 2)
+ grid.addWidget(self.cleanall, 7, 2)
+
+ ##################################################
+ # uhd_image_builder command display
+ ##################################################
+ label_cmd_display = QtWidgets.QLabel(self)
+ label_cmd_display.setText("uhd_image_builder command:")
+ label_cmd_display.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
+ label_cmd_display.setStyleSheet(" QLabel {font-weight: bold; color: black}")
+ grid.addWidget(label_cmd_display, 10, 0)
+ self.cmd_display = QtWidgets.QTextEdit(self)
+ self.cmd_display.setMaximumHeight(label_cmd_display.sizeHint().height() * 3)
+ self.cmd_display.setReadOnly(True)
+ self.cmd_display.setText("".join(self.cmd_name))
+ grid.addWidget(self.cmd_display, 10, 1, 1, 3)
+
+ ##################################################
+ # uhd_image_builder target help display
+ ##################################################
+ self.help_display = QtWidgets.QLabel(self)
+ grid.addWidget(self.help_display, 11, 1, 1, 3)
+ self.help_display.setWordWrap(True)
+ help_description = QtWidgets.QLabel(self)
+ grid.addWidget(help_description, 11, 0)
+ help_description.setText("Target description: ")
+ help_description.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
+ help_description.setStyleSheet(" QLabel {font-weight: bold; color: black}")
+
+ ##################################################
+ # Panels - QTreeModels
+ ##################################################
+ ### Far-left Panel: Build targets
+ self.targets = QtWidgets.QTreeView(self)
+ self.targets.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
+ self.model_targets = QtGui.QStandardItemModel(self)
+ self.model_targets.setHorizontalHeaderItem(0, QtGui.QStandardItem("Select build target"))
+ self.targets.setModel(self.model_targets)
+ self.populate_target('x300')
+ self.populate_target('e300')
+ self.populate_target('e320')
+ self.populate_target('n3xx')
+ grid.addWidget(self.targets, 0, 0, 8, 1)
+
+ ### Central Panel: Available blocks
+ ### Create tree to categorize Ettus Block and OOT Blocks in different lists
+ self.blocks_available = QtWidgets.QTreeView(self)
+ self.blocks_available.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
+ self.blocks_available.setContextMenuPolicy(Qt.CustomContextMenu)
+ ettus_blocks = QtGui.QStandardItem("Ettus-provided Blocks")
+ ettus_blocks.setEnabled(False)
+ ettus_blocks.setForeground(Qt.black)
+ self.populate_list(ettus_blocks, ettus_sources)
+ self.oot = QtGui.QStandardItem("OOT Blocks for X300 devices")
+ self.oot.setEnabled(False)
+ self.oot.setForeground(Qt.black)
+ self.refresh_oot_dirs()
+ self.model_blocks_available = QtGui.QStandardItemModel(self)
+ self.model_blocks_available.appendRow(ettus_blocks)
+ self.model_blocks_available.appendRow(self.oot)
+ self.model_blocks_available.setHorizontalHeaderItem(
+ 0, QtGui.QStandardItem("List of blocks available")
+ )
+ self.blocks_available.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
+ self.blocks_available.setModel(self.model_blocks_available)
+ grid.addWidget(self.blocks_available, 0, 1, 8, 1)
+
+ ### Far-right Panel: Blocks in current design
+ self.blocks_in_design = QtWidgets.QTreeView(self)
+ self.blocks_in_design.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
+ self.model_in_design = QtGui.QStandardItemModel(self)
+ self.model_in_design.setHorizontalHeaderItem(
+ 0, QtGui.QStandardItem("Blocks in current design"))
+ self.blocks_in_design.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
+ self.blocks_in_design.setModel(self.model_in_design)
+ grid.addWidget(self.blocks_in_design, 0, 3, 8, 1)
+
+ ##################################################
+ # Informative Labels
+ ##################################################
+ block_num_hdr = QtWidgets.QLabel(self)
+ block_num_hdr.setText("Blocks in current design")
+ block_num_hdr.setStyleSheet(" QLabel {font-weight: bold; color: black}")
+ block_num_hdr.setAlignment(QtCore.Qt.AlignHCenter)
+ grid.addWidget(block_num_hdr, 0, 2)
+ self.block_num = QtWidgets.QLabel(self)
+ self.block_num.setText("-")
+ self.block_num.setAlignment(QtCore.Qt.AlignHCenter)
+ grid.addWidget(self.block_num, 1, 2)
+ self.block_num.setStyleSheet(" QLabel {color: green}")
+ self.generating_bitstream = QtWidgets.QLabel(self)
+ self.generating_bitstream.setText("")
+ self.generating_bitstream.setAlignment(QtCore.Qt.AlignHCenter)
+ grid.addWidget(self.generating_bitstream, 11, 0, 1, 5)
+ self.generating_bitstream.setStyleSheet(" QLabel {font-weight: bold; color: black}")
+
+ ##################################################
+ # Connection of the buttons with their signals
+ ##################################################
+ self.fill_with_fifos.clicked.connect(self.fill_slot)
+ self.fill_with_fifos.clicked.connect(self.cmd_display_slot)
+ self.viv_gui.clicked.connect(self.viv_gui_slot)
+ self.viv_gui.clicked.connect(self.cmd_display_slot)
+ self.cleanall.clicked.connect(self.cleanall_slot)
+ self.cleanall.clicked.connect(self.cmd_display_slot)
+ oot_btn.clicked.connect(self.file_dialog)
+ from_grc_btn.clicked.connect(self.blocks_to_add_slot)
+ from_grc_btn.clicked.connect(self.cmd_display_slot)
+ from_grc_btn.clicked.connect(self.file_grc_dialog)
+ add_btn.clicked.connect(self.add_to_design)
+ add_btn.clicked.connect(self.blocks_to_add_slot)
+ add_btn.clicked.connect(self.check_blk_num)
+ add_btn.clicked.connect(self.cmd_display_slot)
+ rem_btn.clicked.connect(self.remove_from_design)
+ rem_btn.clicked.connect(self.blocks_to_add_slot)
+ rem_btn.clicked.connect(self.cmd_display_slot)
+ show_file_btn.clicked.connect(self.show_file)
+ show_file_btn.clicked.connect(self.cmd_display_slot)
+ show_file_btn.clicked.connect(self.run_command)
+ self.gen_bit_btn.clicked.connect(self.generate_bit)
+ self.gen_bit_btn.clicked.connect(self.cmd_display_slot)
+ self.gen_bit_btn.clicked.connect(self.run_command)
+ self.targets.clicked.connect(self.ootlist)
+ self.targets.clicked.connect(self.set_target_and_device)
+ self.targets.clicked.connect(self.cmd_display_slot)
+ self.targets.clicked.connect(self.check_blk_num)
+ self.blocks_available.doubleClicked.connect(self.add_to_design)
+ self.blocks_available.doubleClicked.connect(self.blocks_to_add_slot)
+ self.blocks_available.doubleClicked.connect(self.check_blk_num)
+ self.blocks_available.doubleClicked.connect(self.cmd_display_slot)
+ self.blocks_in_design.doubleClicked.connect(self.remove_from_design)
+ self.blocks_in_design.doubleClicked.connect(self.blocks_to_add_slot)
+ self.blocks_in_design.doubleClicked.connect(self.cmd_display_slot)
+
+ ##################################################
+ # Set a default size based on screen geometry
+ ##################################################
+ screen_size = QtWidgets.QDesktopWidget().screenGeometry(-1)
+ self.resize(screen_size.width()/1.4, screen_size.height()/1.7)
+ self.setWindowTitle("uhd_image_builder.py GUI")
+ self.setLayout(grid)
+ self.show()
+
+ ##################################################
+ # Slots and functions/actions
+ ##################################################
+ @pyqtSlot()
+ def blocks_to_add_slot(self):
+ """
+ Retrieves a list of the blocks in design to be displayed in TextEdit
+ """
+ availables = []
+ blocks = []
+ availables = self.iter_tree(self.model_blocks_available, availables)
+ blk_count = self.model_in_design.rowCount()
+ self.block_num.setText("{}/{}".format(blk_count,
+ self.max_allowed_blocks))
+ for i in range(blk_count):
+ blocks.append(self.blocks_in_design.model().data(
+ self.blocks_in_design.model().index(i, 0)))
+ self.cmd_prefix = self.cmd_name + blocks
+
+ @pyqtSlot()
+ def check_blk_num(self):
+ """
+ Checks the amount of blocks in the design pannel
+ """
+ blk_count = self.model_in_design.rowCount()
+ if blk_count > self.max_allowed_blocks:
+ self.block_num.setStyleSheet(" QLabel {font-weight:bold; color: red}")
+ self.show_too_many_blocks_warning(blk_count)
+
+ @pyqtSlot()
+ def fill_slot(self):
+ """
+ Populates 'fill_fifos' value into the command dictionary
+ """
+ if self.fill_with_fifos.isChecked():
+ self.cmd_dict["fill_fifos"] = '--fill-with-fifos'
+ else:
+ self.cmd_dict["fill_fifos"] = ''
+
+ @pyqtSlot()
+ def viv_gui_slot(self):
+ """
+ Populates 'viv_gui' value into the command dictionary
+ """
+ if self.viv_gui.isChecked():
+ self.cmd_dict["viv_gui"] = '-g'
+ else:
+ self.cmd_dict["viv_gui"] = ''
+
+ @pyqtSlot()
+ def cleanall_slot(self):
+ """
+ Populates 'cleanall' value into the command dictionary
+ """
+ if self.cleanall.isChecked():
+ self.cmd_dict["cleanall"] = '-c'
+ else:
+ self.cmd_dict["cleanall"] = ''
+
+ @pyqtSlot()
+ def cmd_display_slot(self):
+ """
+ Displays the command to be run in a QTextEdit in realtime
+ """
+ text = [" ".join(self.cmd_prefix),]
+ for value in self.cmd_dict.values():
+ if value is not '':
+ text.append(value)
+ self.cmd_display.setText(" ".join(text))
+
+ @pyqtSlot()
+ def add_to_design(self):
+ """
+ Adds blocks from the 'available' pannel to the list to be added
+ into the design
+ """
+ indexes = self.blocks_available.selectedIndexes()
+ for index in indexes:
+ word = self.blocks_available.model().data(index)
+ element = QtGui.QStandardItem(word)
+ if word is not None:
+ self.model_in_design.appendRow(element)
+
+ @pyqtSlot()
+ def remove_from_design(self):
+ """
+ Removes blocks from the list that is to be added into the design
+ """
+ indexes = self.blocks_in_design.selectedIndexes()
+ for index in indexes:
+ self.model_in_design.removeRow(index.row())
+ # Edit Informative Label formatting
+ blk_count = self.model_in_design.rowCount()
+ if blk_count <= self.max_allowed_blocks:
+ self.block_num.setStyleSheet(" QLabel {color: green}")
+
+ @pyqtSlot()
+ def show_file(self):
+ """
+ Show the rfnoc_ce_auto_inst file in the default text editor
+ """
+ self.cmd_dict['show_file'] = '-o {}'.format(self.instantiation_file)
+
+ @pyqtSlot()
+ def generate_bit(self):
+ """
+ Runs the FPGA .bit generation command
+ """
+ self.cmd_dict['show_file'] = ''
+
+ @pyqtSlot()
+ def run_command(self):
+ """
+ Executes the uhd_image_builder command based on user options
+ """
+ if self.check_no_blocks() and self.check_blk_not_in_sources():
+ process = threading.Thread(target=self.generate_bitstream)
+ process.start()
+ if self.cmd_dict['show_file'] is not '':
+ os.system("xdg-open " + self.instantiation_file)
+
+ @pyqtSlot()
+ def set_target_and_device(self):
+ """
+ Populates the 'target' and 'device' values of the command directory
+ and the device dependent max_allowed_blocks in display
+ """
+ self.cmd_dict['target'] = '-t {}'.format(self.build_target)
+ self.cmd_dict['device'] = '-d {}'.format(self.device)
+ blk_count = self.model_in_design.rowCount()
+ self.block_num.setText("{}/{}".format(blk_count,
+ self.max_allowed_blocks))
+ self.instantiation_file = os.path.join(uhd_image_builder.get_scriptpath(),
+ '..', '..', 'top', self.target,
+ 'rfnoc_ce_auto_inst_' + self.device.lower() +
+ '.v')
+
+ @pyqtSlot()
+ def ootlist(self):
+ """
+ Lists the Out-of-tree module blocks
+ """
+ index = self.targets.currentIndex()
+ self.build_target = str(self.targets.model().data(index))
+ self.device = self.build_target[:4]
+ if self.device == 'X310' or self.device == 'X300':
+ self.target = 'x300'
+ self.max_allowed_blocks = 10
+ elif self.device == 'E310':
+ self.target = 'e300'
+ self.max_allowed_blocks = 14
+ elif self.device == 'E320':
+ self.target = 'e320'
+ self.max_allowed_blocks = 12
+ elif self.device == 'N300':
+ self.target = 'n3xx'
+ self.max_allowed_blocks = 11
+ elif self.device == 'N310' or self.device == 'N320':
+ self.target = 'n3xx'
+ self.max_allowed_blocks = 10
+ oot_sources = os.path.join(uhd_image_builder.get_scriptpath(), '..', '..', 'top',\
+ self.target, 'Makefile.srcs')
+ self.show_list(self.oot, self.target, oot_sources)
+
+ # Show the help string for a selected target
+ selected_makefile = os.path.join(uhd_image_builder.get_scriptpath(),
+ '..', '..', 'top', self.target, 'Makefile')
+ pattern = "^\#\S*{}.*".format(self.build_target)
+ with open(selected_makefile) as fil:
+ help_string = re.findall(pattern, fil.read(), re.MULTILINE)[0].replace("##","")
+ self.help_display.setText(help_string)
+
+ @pyqtSlot()
+ def file_dialog(self):
+ """
+ Opens a dialog window to add manually the Out-of-tree module blocks
+ """
+ append_directory = []
+ startpath = os.path.join(uhd_image_builder.get_scriptpath(), '..', '..', '..', '..')
+ new_oot = str(QtWidgets.QFileDialog.getExistingDirectory(self, 'RFNoC Out of Tree Directory', startpath))
+ if len(new_oot) > 0:
+ self.oot_dirs.append(new_oot)
+ uhd_image_builder.create_oot_include(self.device, self.oot_dirs)
+ self.refresh_oot_dirs()
+
+ @pyqtSlot()
+ def file_grc_dialog(self):
+ """
+ Opens a dialog window to add manually the GRC description file, from where
+ the RFNoC blocks will be parsed and added directly into the "Design" pannel
+ """
+ filename = QtWidgets.QFileDialog.getOpenFileName(self, 'Open File', '/home/')[0]
+ if len(filename) > 0:
+ self.grc_populate_list(self.model_in_design, filename)
+ self.set_target_and_device()
+ self.blocks_to_add_slot()
+ self.cmd_display_slot()
+
+ def check_no_blocks(self):
+ """
+ Checks if there are no blocks in the design pannel. Needs to be a
+ different slot because triggers from clicking signals from pannels
+ would be superfluous
+ """
+ blk_count = self.model_in_design.rowCount()
+ if blk_count == 0:
+ self.show_no_blocks_warning()
+ return False
+ return True
+
+ def show_no_srcs_warning(self, block_to_add):
+ """
+ Shows a warning message window when no sources are found for the blocks that
+ are in the design pannel
+ """
+ # Create Warning message window
+ msg = QtWidgets.QMessageBox()
+ msg.setIcon(QtWidgets.QMessageBox.Warning)
+ msg.setText("The following blocks are in your design but their sources"\
+ " have not been added: \n\n {0}. \n\nPlease be sure of adding them"\
+ "before continuing. Would you like to add them now?"\
+ "".format(block_to_add))
+ msg.setWindowTitle("No sources for design")
+ yes_btn = msg.addButton("Yes", QtWidgets.QMessageBox.YesRole)
+ no_btn = msg.addButton("No", QtWidgets.QMessageBox.NoRole)
+ msg.exec_()
+ if msg.clickedButton() == yes_btn:
+ self.file_dialog()
+ return False
+ elif msg.clickedButton() == no_btn:
+ return True
+
+ @staticmethod
+ def show_no_blocks_warning():
+ """
+ Shows a warning message window when no blocks are found in the 'design' pannel
+ """
+ # Create Warning message window
+ msg = QtWidgets.QMessageBox()
+ msg.setIcon(QtWidgets.QMessageBox.Warning)
+ msg.setText("There are no Blocks in the current design")
+ msg.exec_()
+
+ def show_too_many_blocks_warning(self, number_of_blocks):
+ """
+ Shows a warning message window when too many blocks are found in the 'design' pannel
+ """
+ # Create Warning message window
+ msg = QtWidgets.QMessageBox()
+ msg.setIcon(QtWidgets.QMessageBox.Warning)
+ msg.setText("You added {} blocks while the maximum allowed blocks for"\
+ " a {} device is {}. Please remove some of the blocks to "\
+ "continue with the design".format(number_of_blocks,
+ self.device, self.max_allowed_blocks))
+ msg.exec_()
+
+ def iter_tree(self, model, output, parent=QModelIndex()):
+ """
+ Iterates over the Index tree
+ """
+ for i in range(model.rowCount(parent)):
+ index = model.index(i, 0, parent)
+ item = model.data(index)
+ output.append(str(item))
+ if model.hasChildren(index):
+ self.iter_tree(model, output, index)
+ return output
+
+ def show_list(self, parent, target, files):
+ """
+ Shows the Out-of-tree blocks that are available for a given device
+ """
+ parent.setText('OOT Blocks for {} devices'.format(target.upper()))
+ self.refresh_oot_dirs()
+
+ def populate_list(self, parent, files, clear=True):
+ """
+ Populates the pannels with the blocks that are listed in the Makefile.srcs
+ of our library
+ """
+ # Clean the list before populating it again
+ if (clear):
+ parent.removeRows(0, parent.rowCount())
+ suffix = '.v \\\n'
+ with open(files) as fil:
+ blocks = fil.readlines()
+ for element in blocks:
+ if element.endswith(suffix) and 'noc_block' in element:
+ element = element[:-len(suffix)]
+ if element not in self.blacklist:
+ block = QtGui.QStandardItem(element.partition('noc_block_')[2])
+ parent.appendRow(block)
+
+ @staticmethod
+ def show_not_xml_warning():
+ """
+ Shows a warning message window when no blocks are found in the 'design' pannel
+ """
+ # Create Warning message window
+ msg = QtWidgets.QMessageBox()
+ msg.setIcon(QtWidgets.QMessageBox.Warning)
+ msg.setText("[ParseError]: The chosen file is not XML formatted")
+ msg.exec_()
+
+ def grc_populate_list(self, parent, files):
+ """
+ Populates the 'Design' list with the RFNoC blocks found in a GRC file
+ """
+ try:
+ tree = ET.parse(files)
+ root = tree.getroot()
+ for blocks in root.iter('block'):
+ for param in blocks.iter('param'):
+ for key in param.iter('key'):
+ if 'fpga_module_name' in key.text:
+ if param.findtext('value') in self.blacklist:
+ continue
+ block = QtGui.QStandardItem(param.findtext('value').\
+ partition('noc_block_')[2])
+ parent.appendRow(block)
+ except ET.ParseError:
+ self.show_not_xml_warning()
+ return
+
+ def refresh_oot_dirs(self):
+ """
+ Populates the OOT directory list from the OOT include file
+ """
+ oot_include = os.path.join(uhd_image_builder.get_scriptpath(), '..', '..', 'top',\
+ self.target, 'Makefile.OOT.inc')
+ dir_list = []
+ with open(oot_include, 'r') as fil:
+ text = fil.readlines()
+ for lines in text:
+ lines = lines.partition('$(BASE_DIR)/')
+ if (lines[1] == '$(BASE_DIR)/'):
+ relpath = lines[2].replace('\n', '')
+ ootpath = os.path.abspath(os.path.join(uhd_image_builder.get_scriptpath(), '..', '..', 'top', relpath))
+ dir_list.append(ootpath)
+ if (len(dir_list) == 0):
+ self.oot.removeRows(0, self.oot.rowCount())
+ self.cmd_dict["include"] = ''
+ else:
+ self.oot_dirs = dir_list
+ self.cmd_dict["include"] = '-I {}'.format(' '.join(self.oot_dirs))
+ for (ii, oot) in enumerate(dir_list):
+ self.populate_list(self.oot, os.path.join(oot, 'fpga-src', 'Makefile.srcs'), clear=ii==0)
+
+ def populate_target(self, selected_target):
+ """
+ Parses the Makefile available and lists the build targets into the left pannel
+ """
+ pattern = "^(?!\#)^\S*_RFNOC[^:]*"
+ build_targets = os.path.join(uhd_image_builder.get_scriptpath(), '..', '..', 'top',
+ selected_target, 'Makefile')
+ with open(build_targets) as fil:
+ targets = re.findall(pattern, fil.read(), re.MULTILINE)
+ for target in targets:
+ self.model_targets.appendRow(QtGui.QStandardItem(target))
+
+ def check_blk_not_in_sources(self):
+ """
+ Checks if a block added from GRC flowgraph is not yet in the sources
+ list
+ """
+ availables = []
+ notin = []
+ availables = self.iter_tree(self.model_blocks_available, availables)
+ for i in range(self.model_in_design.rowCount()):
+ block_to_add = self.blocks_in_design.model().data(
+ self.blocks_in_design.model().index(i, 0))
+ if str(block_to_add) not in availables:
+ notin.append(str(block_to_add))
+ if len(notin) > 0:
+ self.show_no_srcs_warning(notin)
+ return False
+ return True
+
+ def generate_bitstream(self):
+ """
+ Runs the bitstream generation command in a separate thread
+ """
+ self.lock.acquire()
+ self.gen_bit_btn.setEnabled(False)
+ command = self.cmd_display.toPlainText()
+ self.generating_bitstream.setText(
+ "[Generating BitStream]: The FPGA is currently being generated" + \
+ " with the blocks of the current design. See the terminal window" + \
+ " for further compilation details")
+ os.system(command)
+ self.lock.release()
+ self.gen_bit_btn.setEnabled(True)
+ self.generating_bitstream.setText("")
+
+def main():
+ """
+ Main GUI method
+ """
+ app = QtWidgets.QApplication(sys.argv)
+ _window = MainWindow()
+ sys.exit(app.exec_())
+
+if __name__ == '__main__':
+ main()