aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/tools/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'fpga/usrp3/tools/scripts')
-rw-r--r--fpga/usrp3/tools/scripts/auto_inst_e310.yml22
-rw-r--r--fpga/usrp3/tools/scripts/auto_inst_x310.yml29
-rw-r--r--fpga/usrp3/tools/scripts/check_config.json16
-rwxr-xr-xfpga/usrp3/tools/scripts/git-hash.sh75
-rwxr-xr-xfpga/usrp3/tools/scripts/ise_jtag_program.sh17
-rwxr-xr-xfpga/usrp3/tools/scripts/launch_vivado.py475
-rwxr-xr-xfpga/usrp3/tools/scripts/launch_vivado.sh94
-rw-r--r--fpga/usrp3/tools/scripts/setupenv_base.sh472
-rwxr-xr-xfpga/usrp3/tools/scripts/shared-ip-loc-manage.sh119
-rwxr-xr-xfpga/usrp3/tools/scripts/uhd_image_builder.py537
-rwxr-xr-xfpga/usrp3/tools/scripts/uhd_image_builder_gui.py656
-rw-r--r--fpga/usrp3/tools/scripts/viv_check_syntax.tcl14
-rw-r--r--fpga/usrp3/tools/scripts/viv_gen_ip_makefile.py54
-rw-r--r--fpga/usrp3/tools/scripts/viv_gen_part_id.py37
-rw-r--r--fpga/usrp3/tools/scripts/viv_generate_bd.tcl78
-rw-r--r--fpga/usrp3/tools/scripts/viv_generate_hls_ip.tcl36
-rw-r--r--fpga/usrp3/tools/scripts/viv_generate_ip.tcl43
-rw-r--r--fpga/usrp3/tools/scripts/viv_hardware_utils.tcl97
-rw-r--r--fpga/usrp3/tools/scripts/viv_ip_utils.tcl142
-rw-r--r--fpga/usrp3/tools/scripts/viv_ip_xci_editor.py95
-rw-r--r--fpga/usrp3/tools/scripts/viv_sim_project.tcl149
-rw-r--r--fpga/usrp3/tools/scripts/viv_strategies.tcl170
-rw-r--r--fpga/usrp3/tools/scripts/viv_synth.tcl16
-rw-r--r--fpga/usrp3/tools/scripts/viv_utils.tcl290
-rwxr-xr-xfpga/usrp3/tools/scripts/xil_bitfile_parser.py84
25 files changed, 3817 insertions, 0 deletions
diff --git a/fpga/usrp3/tools/scripts/auto_inst_e310.yml b/fpga/usrp3/tools/scripts/auto_inst_e310.yml
new file mode 100644
index 000000000..5842e17c2
--- /dev/null
+++ b/fpga/usrp3/tools/scripts/auto_inst_e310.yml
@@ -0,0 +1,22 @@
+# auto-inst file for E310
+# To regenerate rfnoc_ce_auto_inst_e310.v:
+# ./uhd_image_builder.py -d e310 -t E310_RFNOC_sg3 -y auto_inst_e310.yml -o rfnoc_ce_auto_inst_e310.v
+
+- block: axi_fifo_loopback
+ parameters:
+
+- block: window
+ parameters:
+
+- block: fft
+ parameters:
+
+- block: fosphor
+ parameters:
+ MTU: 12
+
+- block: axi_fifo_loopback
+ parameters:
+
+- block: fir_filter
+ parameters:
diff --git a/fpga/usrp3/tools/scripts/auto_inst_x310.yml b/fpga/usrp3/tools/scripts/auto_inst_x310.yml
new file mode 100644
index 000000000..26548031b
--- /dev/null
+++ b/fpga/usrp3/tools/scripts/auto_inst_x310.yml
@@ -0,0 +1,29 @@
+# auto-inst file for X310
+# To regenerate rfnoc_ce_auto_inst_x310.v:
+# ./uhd_image_builder.py -d x310 -t X310_RFNOC_HG -m 10 --fill-with-fifos -y auto_inst_x310.yml -o rfnoc_ce_auto_inst_x310.v
+
+- block: ddc
+ parameters:
+ NOC_ID: 64'hDDC0_0000_0000_0001
+ NUM_CHAINS: 1
+
+- block: duc
+ parameters:
+
+- block: fft
+ parameters:
+
+- block: window
+ parameters:
+
+- block: fir_filter
+ parameters:
+
+- block: siggen
+ parameters:
+
+- block: keep_one_in_n
+ parameters:
+
+- block: fosphor
+ parameters:
diff --git a/fpga/usrp3/tools/scripts/check_config.json b/fpga/usrp3/tools/scripts/check_config.json
new file mode 100644
index 000000000..f15904043
--- /dev/null
+++ b/fpga/usrp3/tools/scripts/check_config.json
@@ -0,0 +1,16 @@
+{
+ "warning": {
+ "ignore": [
+ "BD 41-1661",
+ "Synth 8-2306",
+ "build-ip",
+ "Synth 8-350",
+ "Synth 8-3331"
+ ]
+ },
+ "critical warning": {
+ "ignore": [
+ "Synth 8-2490"
+ ]
+ }
+}
diff --git a/fpga/usrp3/tools/scripts/git-hash.sh b/fpga/usrp3/tools/scripts/git-hash.sh
new file mode 100755
index 000000000..ed8d7963f
--- /dev/null
+++ b/fpga/usrp3/tools/scripts/git-hash.sh
@@ -0,0 +1,75 @@
+#!/bin/bash
+
+function help {
+ cat <<EOHELP
+Utilities to read/write the git hash for the project.
+
+The script will attempt to get info in the following order:
+- Using a git command
+- Using a hash file (if speficied by user)
+- Otherwise hash = 0xFFFFFFFF
+
+Usage: $0 [--help|-h] [--write] [--hashfile=HASH_FILE]
+
+--hashfile : Location of git hash file [project.githash]
+--write : Write the git hash to HASH_FILE in the Ettus 32-bit register format
+--help : Shows this message
+
+EOHELP
+}
+
+hashfile="project.githash"
+write=0
+for arg in "$@"; do
+ if [[ $arg == "--help" ]]; then
+ help
+ exit 0
+ elif [[ $arg =~ "--hashfile="(.*) ]]; then
+ hashfile=${BASH_REMATCH[1]}
+ elif [[ $arg =~ "--write" ]]; then
+ write=1
+ fi
+done
+
+# Default hash value (failsafe)
+ettus_githash32="ffffffff"
+
+if [[ $write -eq 0 ]]; then
+ git_success=0
+ # First attempt: Use git
+ if [[ $(command -v git) != "" ]]; then
+ # Attempt to get hash from git.
+ # This command will fail if we are not in a git tree
+ short_hash="$(git rev-parse --verify HEAD --short=7 2>/dev/null)" && git_success=1
+ if [[ $git_success -eq 1 ]]; then
+ # Check if tree is clean. If yes, the top 4 bits are 0
+ if (git diff --quiet 2>/dev/null); then
+ ettus_githash32="0$short_hash"
+ else
+ ettus_githash32="f$short_hash"
+ fi
+ fi
+ fi
+ # Second attempt: Read from file if it exists
+ if [[ $git_success -eq 0 ]]; then
+ if [[ -f $hashfile ]]; then
+ ettus_githash32=$(cat $hashfile)
+ fi
+ fi
+ echo ${ettus_githash32}
+ exit 0
+else
+ # Require git
+ command -v git >/dev/null || { echo "ERROR: git not found"; exit 1; }
+ # Get hash from git
+ short_hash="$(git rev-parse --verify HEAD --short=7 2>/dev/null)" || { echo "ERROR: Not a git tree"; exit 2; }
+ # Check if tree is clean. If yes, the top 4 bits are 0
+ if (git diff --quiet 2>/dev/null); then
+ ettus_githash32="0$short_hash"
+ else
+ ettus_githash32="f$short_hash"
+ fi
+ echo $ettus_githash32 > $hashfile
+ echo "INFO: Wrote $ettus_githash32 to $hashfile"
+ exit 0
+fi
diff --git a/fpga/usrp3/tools/scripts/ise_jtag_program.sh b/fpga/usrp3/tools/scripts/ise_jtag_program.sh
new file mode 100755
index 000000000..c6699424d
--- /dev/null
+++ b/fpga/usrp3/tools/scripts/ise_jtag_program.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+echo "loading $1 into FPGA..."
+
+CMD_PATH=/tmp/impact.cmd
+
+echo "generating ${CMD_PATH}..."
+
+echo "setmode -bscan" > ${CMD_PATH}
+echo "setcable -p auto" >> ${CMD_PATH}
+echo "addDevice -p 1 -file $1" >> ${CMD_PATH}
+echo "program -p 1" >> ${CMD_PATH}
+echo "quit" >> ${CMD_PATH}
+
+impact -batch ${CMD_PATH}
+
+echo "done!"
diff --git a/fpga/usrp3/tools/scripts/launch_vivado.py b/fpga/usrp3/tools/scripts/launch_vivado.py
new file mode 100755
index 000000000..01774bef3
--- /dev/null
+++ b/fpga/usrp3/tools/scripts/launch_vivado.py
@@ -0,0 +1,475 @@
+#!/usr/bin/env python
+#
+# Notice: Some parts of this file were copied from PyBOMBS, which has a
+# different copyright, and is highlighted appropriately. The following
+# copyright notice pertains to all the parts written specifically for this
+# script.
+#
+# Copyright 2016 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/>.
+#
+"""
+Run Vivado builds
+"""
+
+from __future__ import print_function
+import os
+import sys
+import re
+import json
+from datetime import datetime
+import time
+import argparse
+import subprocess
+import threading
+try:
+ from Queue import Queue, Empty
+except ImportError:
+ from queue import Queue, Empty # Py3k
+
+READ_TIMEOUT = 0.1 # s
+
+#############################################################################
+# The following functions were copied with minor modifications from PyBOMBS:
+def get_console_width():
+ '''
+ Returns width of console.
+
+ http://stackoverflow.com/questions/566746/how-to-get-console-window-width-in-python
+ '''
+ env = os.environ
+ def ioctl_GWINSZ(fd):
+ try:
+ import fcntl, termios, struct
+ cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234'))
+ except:
+ return
+ return cr
+ cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2)
+ if not cr:
+ try:
+ fd = os.open(os.ctermid(), os.O_RDONLY)
+ cr = ioctl_GWINSZ(fd)
+ os.close(fd)
+ except:
+ pass
+ if not cr:
+ cr = (env.get('LINES', 25), env.get('COLUMNS', 80))
+ return cr[1]
+
+def which(program):
+ """
+ Equivalent to Unix' `which` command.
+ Returns None if the executable `program` can't be found.
+
+ If a full path is given (e.g. /usr/bin/foo), it will return
+ the path if the executable can be found, or None otherwise.
+
+ If no path is given, it will search PATH.
+ """
+ def is_exe(fpath):
+ " Check fpath is an executable "
+ return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
+ if os.path.split(program)[0] and is_exe(program):
+ return program
+ else:
+ for path in os.environ.get("PATH", "").split(os.pathsep):
+ exe_file = os.path.join(path, program)
+ if is_exe(exe_file):
+ return exe_file
+ return None
+#
+# End of functions copied from PyBOMBS.
+#############################################################################
+
+def print_timer(time_delta):
+ """docstring for print_timer"""
+ hours, secs = divmod(time_delta.seconds, 3600)
+ mins, secs = divmod(secs, 60)
+ return "[{h:02}:{m:02}:{s:02}]".format(
+ h=hours, m=mins, s=secs,
+ )
+
+def list_search(patterns, string):
+ " Returns True if string matches any element of pattern "
+ for pattern in patterns:
+ if re.search(pattern, string) is not None:
+ return True
+ return False
+
+def parse_args():
+ " Parses args for this script, and for Vivado. "
+ parser = argparse.ArgumentParser(
+ description="Run Vivado and parse output.",
+ )
+ parser.add_argument(
+ '--no-color', action="store_true",
+ help="Don't colorize output.",
+ )
+ parser.add_argument(
+ '--vivado-command', default=None,
+ help="Vivado command.",
+ )
+ parser.add_argument(
+ '--parse-config', default=None,
+ help="Additional parser configurations",
+ )
+ parser.add_argument(
+ '-v', '--verbose', default=False,
+ action='store_true',
+ help="Print Vivado output")
+ parser.add_argument(
+ '--warnings', default=False,
+ action='store_true',
+ help="Print Vivado warnings")
+ our_args, viv_args = parser.parse_known_args()
+ return our_args, " ".join(viv_args)
+
+class VivadoRunner(object):
+ " Vivado Runner "
+ colors = {
+ 'warning': '\033[0;35m',
+ 'critical warning': '\033[33m',
+ 'error': '\033[1;31m',
+ 'fatal': '\033[1;31m',
+ 'task': '\033[32m',
+ 'cmd': '\033[1;34m',
+ 'normal': '\033[0m',
+ }
+ # Black 0;30 Dark Gray 1;30
+ # Blue 0;34 Light Blue 1;34
+ # Green 0;32 Light Green 1;32
+ # Cyan 0;36 Light Cyan 1;36
+ # Red 0;31 Light Red 1;31
+ # Purple 0;35 Light Purple 1;35
+ # Brown 0;33 Yellow 1;33
+ # Light Gray 0;37 White 1;37
+
+ viv_tcl_cmds = {
+ 'synth_design' : 'Synthesis',
+ 'opt_design': 'Logic Optimization',
+ 'place_design': 'Placer',
+ 'route_design': 'Routing',
+ 'phys_opt_design': 'Physical Synthesis',
+ 'report_timing' : 'Timing Reporting',
+ 'report_power': 'Power Reporting',
+ 'report_drc': 'DRC',
+ 'write_bitstream': 'Write Bitstream',
+ }
+
+ def __init__(self, args, viv_args):
+ self.status = ''
+ self.args = args
+ self.current_task = "Initialization"
+ self.current_phase = "Starting"
+ self.command = args.vivado_command + " " + viv_args
+ self.notif_queue = Queue()
+ self.msg_counters = {}
+ self.fatal_error_found = False
+ self.line_types = {
+ 'cmd': {
+ 'regexes': [
+ '^Command: .+',
+ ],
+ 'action': self.show_cmd,
+ 'id': "Command",
+ },
+ 'task': {
+ 'regexes': [
+ '^Starting .* Task',
+ '^.*Translating synthesized netlist.*',
+ '^\[TEST CASE .*',
+ ],
+ 'action': self.update_task,
+ 'id': "Task",
+ },
+ 'phase': {
+ 'regexes': [
+ '^Phase (?P<id>[a-zA-Z0-9/. ]*)$',
+ '^Start (?P<id>[a-zA-Z0-9/. ]*)$',
+ '^(?P<id>TESTBENCH STARTED: [\w_]*)$',
+ ],
+ 'action': self.update_phase,
+ 'id': "Phase",
+ },
+ 'warning': {
+ 'regexes': [
+ '^WARNING'
+ ],
+ 'action': lambda x: self.act_on_build_msg('warning', x),
+ 'id': "Warning",
+ 'fatal': [
+ ]
+ },
+ 'critical warning': {
+ 'regexes': [
+ '^CRITICAL WARNING'
+ ],
+ 'action': lambda x: self.act_on_build_msg('critical warning', x),
+ 'id': "Critical Warning",
+ 'fatal': [
+ ]
+ },
+ 'error': {
+ 'regexes': [
+ '^ERROR',
+ 'no such file or directory',
+ '^Result: FAILED'
+ ],
+ 'action': lambda x: self.act_on_build_msg('error', x),
+ 'id': "Error",
+ 'fatal': [
+ '.', # All errors are fatal by default
+ ]
+ },
+ 'test': {
+ 'regexes': [
+ '^ - T'
+ '^Result: '
+ ],
+ 'action': self.update_testbench,
+ 'id': "Test"
+ }
+ }
+ self.parse_config = None
+ if args.parse_config is not None:
+ try:
+ args.parse_config = os.path.normpath(args.parse_config)
+ parse_config = json.load(open(args.parse_config))
+ self.add_notification(
+ "Using parser configuration from: {pc}".format(pc=args.parse_config),
+ color=self.colors.get('normal')
+ )
+ loadables = ('regexes', 'ignore', 'fatal')
+ for line_type in self.line_types:
+ for loadable in loadables:
+ self.line_types[line_type][loadable] = \
+ self.line_types[line_type].get(loadable, []) + \
+ parse_config.get(line_type, {}).get(loadable, [])
+ except (IOError, ValueError):
+ self.add_notification(
+ "Could not read parser configuration from: {pc}".format(pc=args.parse_config),
+ color=self.colors.get('warning')
+ )
+ self.tty = sys.stdout.isatty()
+ self.timer = datetime.now() # Make sure this is the last line in ctor
+
+ def run(self):
+ """
+ Kick off Vivado build.
+
+ Returns True if it all passed.
+ """
+ def enqueue_output(stdout_data, stdout_queue):
+ " Puts the output from the process into the queue "
+ for line in iter(stdout_data.readline, b''):
+ stdout_queue.put(line)
+ stdout_data.close()
+ def poll_queue(q):
+ " Safe polling from queue "
+ try:
+ return q.get(timeout=READ_TIMEOUT).decode('utf-8')
+ except UnicodeDecodeError:
+ pass
+ except Empty:
+ pass
+ return ""
+ # Start process
+ self.add_notification(
+ "Executing command: {cmd}".format(cmd=self.command), add_time=True, color=self.colors.get('cmd')
+ )
+ proc = subprocess.Popen(
+ self.command,
+ shell=True, # Yes we run this in a shell. Unsafe but helps with Vivado.
+ stdout=subprocess.PIPE, stderr=subprocess.STDOUT # Pipe it all out via stdout
+ )
+ # Init thread and queue
+ q_stdout = Queue()
+ t = threading.Thread(target=enqueue_output, args=(proc.stdout, q_stdout))
+ # End the thread when the program terminates
+ t.daemon = True
+ t.start()
+ status_line_t = threading.Thread(target=VivadoRunner.run_loop, args=(self.print_status_line, 0.5 if self.tty else 60*10))
+ status_line_t.daemon = True
+ status_line_t.start()
+ # Run loop
+ while proc.poll() is None or not q_stdout.empty(): # Run while process is alive
+ line_stdout = poll_queue(q_stdout)
+ self.update_output(line_stdout)
+ success = (proc.returncode == 0) and not self.fatal_error_found
+ self.cleanup_output(success)
+ return success
+
+ def update_output(self, lines):
+ " Receives a line from Vivado output and acts upon it. "
+ self.process_line(lines)
+
+ @staticmethod
+ def run_loop(func, delay, *args, **kwargs):
+ while True:
+ func(*args, **kwargs)
+ time.sleep(delay)
+
+ def print_status_line(self):
+ " Prints status on stdout"
+ old_status_line_len = len(self.status)
+ self.update_status_line()
+ sys.stdout.write("\x1b[2K\r") # Scroll cursor back to beginning and clear last line
+ self.flush_notification_queue(old_status_line_len)
+ sys.stdout.write(self.status)
+ sys.stdout.flush()
+ # Make sure we print enough spaces to clear out all of the previous message
+ # if not msgs_printed:
+ # sys.stdout.write(" " * max(0, old_status_line_len - len(self.status)))
+
+ def cleanup_output(self, success):
+ " Run final printery after all is said and done. "
+ # Check message counts are within limits
+ self.update_phase("Finished")
+ self.add_notification(
+ "Process terminated. Status: {status}".format(status='Success' if success else 'Failure'),
+ add_time=True,
+ color=self.colors.get("task" if success else "error")
+ )
+ sys.stdout.write("\n")
+ self.flush_notification_queue(len(self.status))
+ print("")
+ print("========================================================")
+ print("Warnings: ", self.msg_counters.get('warning', 0))
+ print("Critical Warnings: ", self.msg_counters.get('critical warning', 0))
+ print("Errors: ", self.msg_counters.get('error', 0))
+ print("")
+ sys.stdout.flush()
+
+ def process_line(self, lines):
+ " process line "
+ for line in [l.rstrip() for l in lines.split("\n") if len(l.strip())]:
+ line_info, line_data = self.classify_line(line)
+ if line_info is not None:
+ self.line_types[line_info]['action'](line_data)
+ elif self.args.verbose:
+ print(line)
+
+ def classify_line(self, line):
+ """
+ Identify the current line. Return None if the line can't be identified.
+ """
+ for line_type in self.line_types:
+ for regex in self.line_types[line_type]['regexes']:
+ re_obj = re.search(regex, line)
+ if re_obj is not None:
+ return line_type, re_obj.groupdict().get('id', line)
+ return None, None
+
+ def update_status_line(self):
+ " Update self.status. Does not print anything! "
+ status_line = "{timer} Current task: {task} +++ Current Phase: {phase}"
+ self.status = status_line.format(
+ timer=print_timer(datetime.now() - self.timer),
+ task=self.current_task.strip(),
+ phase=self.current_phase.strip(),
+ )
+
+ def add_notification(self, msg, add_time=False, color=None):
+ """
+ Format msg and add it as a notification to the queue.
+ """
+ if add_time:
+ msg = print_timer(datetime.now() - self.timer) + " " + msg
+ if color is not None and not self.args.no_color:
+ msg = color + msg + self.colors.get('normal')
+ self.notif_queue.put(msg)
+
+ def flush_notification_queue(self, min_len):
+ " Print all strings in the notification queue. "
+ msg_printed = False
+ while not self.notif_queue.empty():
+ msg = self.notif_queue.get().strip()
+ print(msg)
+ msg_printed = True
+ return msg_printed
+
+ def act_on_build_msg(self, msg_type, msg):
+ """
+ Act on a warning, error, critical warning, etc.
+ """
+ if list_search(self.line_types[msg_type].get('fatal', []), msg):
+ self.add_notification(msg, color=self.colors.get('fatal'))
+ self.fatal_error_found = True
+ elif not list_search(self.line_types[msg_type].get('ignore', []), msg):
+ self.add_notification(msg, color=self.colors.get(msg_type))
+ self.msg_counters[msg_type] = self.msg_counters.get(msg_type, 0) + 1
+
+ def show_cmd(self, tcl_cmd):
+ " Show the current command "
+ self.update_phase("Finished")
+ tcl_cmd = tcl_cmd.replace("Command:", "").strip()
+ #sys.stdout.write("\n")
+ self.add_notification("Executing Tcl: " + tcl_cmd,
+ add_time=True, color=self.colors.get("cmd"))
+ cmd = tcl_cmd.strip().split()[0];
+ if cmd in self.viv_tcl_cmds:
+ cmd = self.viv_tcl_cmds[cmd]
+ self.update_task("Starting " + cmd + " Command", is_new=False)
+ #self.flush_notification_queue(len(self.status))
+
+ def update_task(self, task, is_new=True):
+ " Update current task "
+ # Special case: Treat "translation" as a phase as well
+ if "Translating synthesized netlist" in task:
+ task = "Translating Synthesized Netlist"
+ filtered_task = task.replace("Starting", "").replace("Task", "").replace("Command", "")
+ if is_new and (filtered_task != self.current_task):
+ self.update_phase("Finished")
+ self.current_task = filtered_task
+ self.current_phase = "Starting"
+ self.add_notification(task, add_time=True, color=self.colors.get("task"))
+ sys.stdout.write("\n")
+ self.print_status_line()
+
+ def update_phase(self, phase):
+ " Update current phase "
+ self.current_phase = phase.strip()
+ self.current_task = self.current_task.replace("Phase", "")
+ sys.stdout.write("\n")
+ self.print_status_line()
+
+ def update_testbench(self, testbench):
+ pass # Do nothing
+
+
+def main():
+ " Go, go, go! "
+ args, viv_args = parse_args()
+ if args.vivado_command is None:
+ if which("vivado"):
+ args.vivado_command = "vivado"
+ elif which("vivado_lab"):
+ args.vivado_command = "vivado_lab"
+ else:
+ print("Cannot find Vivado executable!")
+ return False
+ try:
+ return VivadoRunner(args, viv_args).run()
+ except KeyboardInterrupt:
+ print("")
+ print("")
+ print("Caught Ctrl-C. Exiting.")
+ print("")
+ return False
+
+if __name__ == "__main__":
+ exit(not main())
+
diff --git a/fpga/usrp3/tools/scripts/launch_vivado.sh b/fpga/usrp3/tools/scripts/launch_vivado.sh
new file mode 100755
index 000000000..a0cce6e99
--- /dev/null
+++ b/fpga/usrp3/tools/scripts/launch_vivado.sh
@@ -0,0 +1,94 @@
+#!/bin/bash
+
+#------------------------------------------
+# Parse command line args
+#------------------------------------------
+
+function help {
+ cat <<EOHELP
+
+Usage: $0 [--help|-h] [--no-color] [<vivado args>]
+
+--no-color : Don't colorize command output
+--help, -h : Shows this message.
+
+EOHELP
+}
+
+viv_args=""
+colorize=1
+for i in "$@"; do
+ case $i in
+ -h|--help)
+ help
+ exit 0
+ ;;
+ --no-color)
+ colorize=0
+ ;;
+ *)
+ viv_args="$viv_args $i"
+ ;;
+ esac
+done
+
+#------------------------------------------
+# Colorize
+#------------------------------------------
+
+# VIV_COLOR_SCHEME must be defined in the environment setup script
+if [ $colorize -eq 0 ]; then
+ VIV_COLOR_SCHEME=none
+fi
+
+case "$VIV_COLOR_SCHEME" in
+ default)
+ CLR_OFF='tput sgr0'
+ ERR_CLR='tput setaf 1'
+ CRIWARN_CLR='tput setaf 1'
+ WARN_CLR='tput setaf 3'
+ ;;
+ *)
+ CLR_OFF=''
+ ERR_CLR=$CLR_OFF
+ CRIWARN_CLR=$CLR_OFF
+ WARN_CLR=$CLR_OFF
+esac
+
+trim() {
+ local var="$*"
+ var="${var#"${var%%[![:space:]]*}"}" # remove leading whitespace characters
+ var="${var%"${var##*[![:space:]]}"}" # remove trailing whitespace characters
+ echo -n "$var"
+}
+
+VIVADO_COMMAND="vivado"
+if command -v vivado_lab >/dev/null 2>&1; then
+ VIVADO_COMMAND=vivado_lab
+fi
+
+$VIVADO_COMMAND $viv_args 2>&1 | while IFS= read -r line
+
+do
+ if [[ $line != \#* ]]; then # Ignore script output
+ case $(trim $line) in
+ *FATAL:*|*Fatal:*)
+ eval $ERR_CLR; echo "$line"; eval $CLR_OFF
+ ;;
+ *ERROR:*|*Error:*)
+ eval $ERR_CLR; echo "$line"; eval $CLR_OFF
+ ;;
+ *CRITICAL[[:space:]]WARNING:*|*Crtical[[:space:]]Warning:*)
+ eval $CRIWARN_CLR; echo "$line"; eval $CLR_OFF
+ ;;
+ *WARNING:*|*Warning:*)
+ eval $WARN_CLR; echo "$line"; eval $CLR_OFF
+ ;;
+ *)
+ echo "$line"
+ esac
+ else
+ echo "$line"
+ fi
+done
+exit ${PIPESTATUS[0]}
diff --git a/fpga/usrp3/tools/scripts/setupenv_base.sh b/fpga/usrp3/tools/scripts/setupenv_base.sh
new file mode 100644
index 000000000..0cdf71370
--- /dev/null
+++ b/fpga/usrp3/tools/scripts/setupenv_base.sh
@@ -0,0 +1,472 @@
+#!/bin/bash
+#
+# Copyright 2015 Ettus Research
+#
+
+#----------------------------------------------------------------------------
+# Global defaults
+#----------------------------------------------------------------------------
+export VIV_PLATFORM=$(uname -o)
+
+# Vivado specific
+if [[ $VIV_PLATFORM = "Cygwin" ]]; then
+ if [[ -d "/cygdrive/c/Xilinx/Vivado_Lab" ]]; then
+ VIVADO_BASE_PATH="/cygdrive/c/Xilinx/Vivado_Lab"
+ else
+ VIVADO_BASE_PATH="/cygdrive/c/Xilinx/Vivado"
+ fi
+ MODELSIM_BASE_PATH="/cygdrive/c/mentor/modelsim"
+else
+ if [[ -d "/opt/Xilinx/Vivado_Lab" ]]; then
+ VIVADO_BASE_PATH="/opt/Xilinx/Vivado_Lab"
+ else
+ VIVADO_BASE_PATH="/opt/Xilinx/Vivado"
+ fi
+ MODELSIM_BASE_PATH="/opt/mentor/modelsim"
+fi
+
+function resolve_viv_path {
+ if [[ $VIV_PLATFORM = "Cygwin" ]]; then
+ echo $(cygpath -aw $1)
+ else
+ echo $1
+ fi
+}
+
+#----------------------------------------------------------------------------
+# Validate prerequisites
+#----------------------------------------------------------------------------
+# Ensure required variables
+if [ -z "$REPO_BASE_PATH" ]; then
+ echo "ERROR: Please define the variable REPO_BASE_PATH before calling this script"
+ return
+fi
+if [ -z "$VIVADO_VER" ]; then
+ echo "ERROR: Please define the variable VIVADO_VER before calling this script"
+ return
+fi
+if [ -z "$DISPLAY_NAME" ]; then
+ echo "ERROR: Please define the variable DISPLAY_NAME before calling this script"
+ return
+fi
+if [ ${#PRODUCT_ID_MAP[@]} -eq 0 ]; then
+ echo "ERROR: Please define the variable PRODUCT_ID_MAP before calling this script"
+ return
+fi
+
+# Ensure that the script is sourced
+if [[ $BASH_SOURCE = $0 ]]; then
+ echo "ERROR: This script must be sourced."
+ help
+ exit 1
+fi
+
+#----------------------------------------------------------------------------
+# Help message display function
+#----------------------------------------------------------------------------
+function help {
+ cat <<EOHELP
+
+Usage: source setupenv.sh [--help|-h] [--vivado-path=<PATH>] [--modelsim-path=<PATH>]
+
+--vivado-path : Path to the base install directory for Xilinx Vivado
+ (Default: /opt/Xilinx/Vivado or /opt/Xilinx/Vivado_Lab)
+--modelsim-path : Path to the base install directory for Modelsim (optional simulation tool)
+ (Default: /opt/mentor/modelsim)
+--help -h : Shows this message.
+
+This script sets up the environment required to build FPGA images for the Ettus Research
+${DISPLAY_NAME}. It will also optionally set up the the environment to run the
+Modelsim simulator (although this tool is not required).
+
+Required tools: Xilinx Vivado $VIVADO_VER (Synthesis and Simulation)
+Optional tools: Mentor Graphics Modelsim (Simulation)
+
+EOHELP
+}
+
+#----------------------------------------------------------------------------
+# Setup and parse command line
+#----------------------------------------------------------------------------
+# Detect platform bitness
+if [ "$(uname -m)" = "x86_64" ]; then
+ BITNESS="64"
+else
+ BITNESS="32"
+fi
+
+# Go through cmd line options
+MODELSIM_REQUESTED=0
+MODELSIM_FOUND=0
+PARSE_STATE=""
+for i in "$@"; do
+ case $i in
+ -h|--help)
+ help
+ return 0
+ ;;
+ --vivado-path=*)
+ VIVADO_BASE_PATH="${i#*=}"
+ PARSE_STATE=""
+ ;;
+ --vivado-path)
+ PARSE_STATE="vivado-path"
+ ;;
+ --vivado-version=*)
+ VIVADO_USER_VER="${i#*=}"
+ PARSE_STATE=""
+ ;;
+ --vivado-version)
+ PARSE_STATE="vivado-version"
+ ;;
+ --modelsim-path=*)
+ MODELSIM_BASE_PATH="${i#*=}"
+ MODELSIM_REQUESTED=1
+ PARSE_STATE=""
+ ;;
+ --modelsim-path)
+ PARSE_STATE="modelsim-path"
+ ;;
+ *)
+ case $PARSE_STATE in
+ vivado-path)
+ VIVADO_BASE_PATH="$i"
+ PARSE_STATE=""
+ ;;
+ vivado-version)
+ VIVADO_USER_VER="$i"
+ PARSE_STATE=""
+ ;;
+ modelsim-path)
+ MODELSIM_BASE_PATH="$i"
+ MODELSIM_REQUESTED=1
+ PARSE_STATE=""
+ ;;
+ *)
+ echo "ERROR: Unrecognized option: $i"
+ help
+ return 1
+ ;;
+ esac
+ ;;
+ esac
+done
+
+# Vivado environment setup
+if [[ ${VIVADO_VER^^} = "CMDLINE_ARG" ]]; then
+ if [[ -z $VIVADO_USER_VER ]]; then
+ echo "ERROR: The --vivado-version argument must be specified when the env version is \"CMDLINE_ARG\""
+ return 1
+ else
+ VIVADO_VER=$VIVADO_USER_VER
+ fi
+fi
+export VIVADO_PATH=$VIVADO_BASE_PATH/$VIVADO_VER
+
+echo "Setting up a ${BITNESS}-bit FPGA build environment for the ${DISPLAY_NAME}..."
+#----------------------------------------------------------------------------
+# Prepare Vivado environment
+#----------------------------------------------------------------------------
+if [ -d "$VIVADO_PATH/bin" ]; then
+ echo "- Vivado: Found ($VIVADO_PATH/bin)"
+else
+ echo "- Vivado: Version $VIVADO_VER not found in $VIVADO_BASE_PATH (ERROR.. Builds and simulations will not work)"
+ if [[ -z $VIVADO_USER_VER ]]; then
+ echo " Use the --vivado-path option to override the search path"
+ else
+ echo " Use the --vivado-path option to override the search path or specify the correct --vivado-version"
+ fi
+ unset VIVADO_USER_VER
+ return 1
+fi
+
+$VIVADO_PATH/settings${BITNESS}.sh
+if [[ -e $VIVADO_PATH/.settings${BITNESS}-Vivado_Lab.sh ]]; then
+ $VIVADO_PATH/.settings${BITNESS}-Vivado_Lab.sh
+else
+ $VIVADO_PATH/.settings${BITNESS}-Vivado.sh
+fi
+if [[ -e $(readlink -f $VIVADO_BASE_PATH/..)/DocNav/.settings${BITNESS}-DocNav.sh ]]; then
+ $(readlink -f $VIVADO_BASE_PATH/..)/DocNav/.settings${BITNESS}-DocNav.sh
+fi
+
+if [[ -x `which tput 2>/dev/null` ]] ; then
+ export VIV_COLOR_SCHEME=default
+fi
+VIVADO_EXEC="$REPO_BASE_PATH/tools/scripts/launch_vivado.sh"
+
+#----------------------------------------------------------------------------
+# Prepare Modelsim environment
+#----------------------------------------------------------------------------
+if [[ -d $MODELSIM_BASE_PATH ]]; then
+ if [[ $VIV_PLATFORM = "Cygwin" ]]; then
+ VSIM_PATH=$(find -L $MODELSIM_BASE_PATH -maxdepth 3 -wholename '*win*/vsim.exe' | head -n 1)
+ else
+ VSIM_PATH=$(find -L $MODELSIM_BASE_PATH -maxdepth 3 -wholename '*linux*/vsim' | head -n 1)
+ fi
+fi
+if [[ $VSIM_PATH ]]; then
+ if [[ $($VSIM_PATH -version) =~ .*ModelSim[[:space:]](.+)[[:space:]]vsim.* ]]; then
+ MODELSIM_VER=${BASH_REMATCH[1]}
+ MODELSIM_PATH=$(dirname $VSIM_PATH)
+ fi
+ case $MODELSIM_VER in
+ DE-64|SE-64)
+ export MODELSIM_64BIT=1
+ export SIM_COMPLIBDIR=$VIVADO_PATH/modelsim64
+ ;;
+ DE|SE|PE)
+ export MODELSIM_64BIT=0
+ export SIM_COMPLIBDIR=$VIVADO_PATH/modelsim32
+ ;;
+ *)
+ ;;
+ esac
+fi
+
+function build_simlibs {
+ mkdir -p $SIM_COMPLIBDIR
+ pushd $SIM_COMPLIBDIR
+ CMD_PATH=`mktemp XXXXXXXX.vivado_simgen.tcl`
+ if [[ $MODELSIM_64BIT -eq 1 ]]; then
+ echo "compile_simlib -force -simulator modelsim -family all -language all -library all -directory $SIM_COMPLIBDIR" > $CMD_PATH
+ else
+ echo "compile_simlib -force -simulator modelsim -family all -language all -library all -32 -directory $SIM_COMPLIBDIR" > $CMD_PATH
+ fi
+ $VIVADO_EXEC -mode batch -source $(resolve_viv_path $CMD_PATH) -nolog -nojournal
+ rm -f $CMD_PATH
+ popd
+}
+
+if [[ $MODELSIM_VER ]]; then
+ echo "- Modelsim: Found ($MODELSIM_VER, $MODELSIM_PATH)"
+ if [[ -e "$SIM_COMPLIBDIR/modelsim.ini" ]]; then
+ echo "- Modelsim Compiled Libs: Found ($SIM_COMPLIBDIR)"
+ else
+ echo "- Modelsim Compiled Libs: Not found! (Run build_simlibs to generate them.)"
+ fi
+else
+ if [[ $MODELSIM_REQUESTED -eq 1 ]]; then
+ echo "- Modelsim: Not found in $MODELSIM_BASE_PATH (WARNING.. Simulations with vsim will not work)"
+ fi
+fi
+
+#----------------------------------------------------------------------------
+# Misc export variables
+#----------------------------------------------------------------------------
+export PATH=$(echo ${PATH} | tr ':' '\n' | awk '$0 !~ "/Vivado/"' | paste -sd:)
+export PATH=${PATH}:$VIVADO_PATH:$VIVADO_PATH/bin:$VIVADO_HLS_PATH:$VIVADO_HLS_PATH/bin:$MODELSIM_PATH
+
+for prod in "${!PRODUCT_ID_MAP[@]}"; do
+ IFS='/' read -r -a prod_tokens <<< "${PRODUCT_ID_MAP[$prod]}"
+ if [ ${#prod_tokens[@]} -eq 6 ]; then
+ export XIL_ARCH_${prod}=${prod_tokens[0]}
+ export XIL_PART_ID_${prod}=${prod_tokens[1]}/${prod_tokens[2]}/${prod_tokens[3]}/${prod_tokens[4]}/${prod_tokens[5]}
+ elif [ ${#prod_tokens[@]} -eq 5 ]; then
+ export XIL_ARCH_${prod}=${prod_tokens[0]}
+ export XIL_PART_ID_${prod}=${prod_tokens[1]}/${prod_tokens[2]}/${prod_tokens[3]}/${prod_tokens[4]}
+ elif [ ${#prod_tokens[@]} -eq 4 ]; then
+ export XIL_ARCH_${prod}=${prod_tokens[0]}
+ export XIL_PART_ID_${prod}=${prod_tokens[1]}/${prod_tokens[2]}/${prod_tokens[3]}
+ else
+ echo "ERROR: Invalid PRODUCT_ID_MAP entry: \"${PRODUCT_ID_MAP[$prod]}\". Must be <arch>/<part>/<pkg>/<sg>[/<tg>[/<rev>]]."
+ return 1
+ fi
+done
+
+#----------------------------------------------------------------------------
+# Define IP management aliases
+#----------------------------------------------------------------------------
+# Vivado specific
+VIV_IP_UTILS=$REPO_BASE_PATH/tools/scripts/viv_ip_utils.tcl
+
+function viv_create_ip {
+ if [[ -z $1 || -z $2 || -z $3 || -z $4 ]]; then
+ echo "Create a new Vivado IP instance and a Makefile for it"
+ echo ""
+ echo "Usage: viv_create_new_ip <IP Name> <IP Location> <IP VLNV> <Product>"
+ echo "- <IP Name>: Name of the IP instance"
+ echo "- <IP Location>: Base location for IP"
+ echo "- <IP VLNV>: The vendor, library, name, and version (VLNV) string for the IP as defined by Xilinx"
+ echo "- <Product>: Product to generate IP for. Choose from: ${!PRODUCT_ID_MAP[@]}"
+ return 1
+ fi
+
+ ip_name=$1
+ ip_dir=$(readlink -f $2)
+ ip_vlnv=$3
+ IFS='/' read -r -a prod_tokens <<< "${PRODUCT_ID_MAP[$4]}"
+ part_name=${prod_tokens[1]}${prod_tokens[2]}${prod_tokens[3]}
+ if [[ -z $part_name ]]; then
+ echo "ERROR: Invalid product name $4. Supported: ${!PRODUCT_ID_MAP[@]}"
+ return 1
+ fi
+ if [[ -d $ip_dir/$ip_name ]]; then
+ echo "ERROR: IP $ip_dir/$ip_name already exists. Please choose a different name."
+ return 1
+ fi
+
+ $VIVADO_EXEC -mode gui -source $(resolve_viv_path $VIV_IP_UTILS) -nolog -nojournal -tclargs create $part_name $ip_name $(resolve_viv_path $ip_dir) $ip_vlnv
+ echo "Generating Makefile..."
+ python $REPO_BASE_PATH/tools/scripts/viv_gen_ip_makefile.py --ip_name=$ip_name --dest=$ip_dir/$ip_name
+ echo "Done generating IP in $ip_dir/$ip_name"
+}
+
+function viv_modify_ip {
+ if [[ -z $1 ]]; then
+ echo "Modify an existing Vivado IP instance"
+ echo ""
+ echo "Usage: viv_modify_ip <IP XCI Path>"
+ echo "- <IP XCI Path>: Path to the IP XCI file."
+ return 1
+ fi
+
+ xci_path=$(readlink -f $1)
+ part_name=$(python $REPO_BASE_PATH/tools/scripts/viv_ip_xci_editor.py read_part $xci_path)
+ if [[ -z $part_name ]]; then
+ echo "ERROR: Invalid part name $part_name. XCI parse error."
+ return 1
+ fi
+ if [[ -f $xci_path ]]; then
+ $VIVADO_EXEC -mode gui -source $(resolve_viv_path $VIV_IP_UTILS) -nolog -nojournal -tclargs modify $part_name $(resolve_viv_path $xci_path)
+ else
+ echo "ERROR: IP $xci_path not found."
+ return 1
+ fi
+}
+
+function viv_modify_bd {
+ if [[ -z $1 || -z $2 ]]; then
+ echo "Modify an existing Vivado Block Design instance"
+ echo ""
+ echo "Usage: viv_modify_bd <BD Path> <Product>"
+ echo "- <BD Path>: Path to the BD file."
+ echo "- <Product>: Product to generate IP for. Choose from: ${!PRODUCT_ID_MAP[@]}"
+ return 1
+ fi
+
+ bd_path=$(readlink -f $1)
+ IFS='/' read -r -a prod_tokens <<< "${PRODUCT_ID_MAP[$2]}"
+ part_name=${prod_tokens[1]}${prod_tokens[2]}${prod_tokens[3]}
+ if [[ -f $bd_path ]]; then
+ $VIVADO_EXEC -mode gui -source $(resolve_viv_path $VIV_IP_UTILS) -nolog -nojournal -tclargs modify $part_name $(resolve_viv_path $bd_path)
+ else
+ echo "ERROR: IP $bd_path not found."
+ return 1
+ fi
+}
+
+function viv_modify_tcl_bd {
+ if [[ -z $1 || -z $2 ]]; then
+ echo "Modify an existing Vivado TCL-based Block Design instance."
+ echo ""
+ echo "Usage: viv_modify_bd_tcl <TCL Path> <Product>"
+ echo "- <TCL Path>: Path to the TCL source file."
+ echo "- <Product> : Product to generate IP for. Choose from: ${!PRODUCT_ID_MAP[@]}"
+ return 1
+ fi
+
+ src_path=$(readlink -f $1)
+ IFS='/' read -r -a prod_tokens <<< "${PRODUCT_ID_MAP[$2]}"
+ part_name=${prod_tokens[1]}${prod_tokens[2]}${prod_tokens[3]}
+ bd_ip_repo="${src_path%/top*}/lib/vivado_ipi"
+ if [[ -f $src_path ]]; then
+ $VIVADO_EXEC -mode gui -source $(resolve_viv_path $VIV_IP_UTILS) -nolog -nojournal -tclargs modify_bdtcl $part_name $(resolve_viv_path $src_path) $(resolve_viv_path $bd_ip_repo)
+ echo "INFO: Vivado BD was closed, writing source TCL..."
+ $VIVADO_EXEC -mode batch -source $(resolve_viv_path $VIV_IP_UTILS) -nolog -nojournal -tclargs write_bdtcl $part_name $(resolve_viv_path $src_path)
+ else
+ echo "ERROR: IP $src_path not found."
+ return 1
+ fi
+}
+
+function viv_ls_ip {
+ if [[ -z $1 ]]; then
+ echo "List the items in the Vivado IP catalog"
+ echo ""
+ echo "Usage: viv_ls_ip <Product>"
+ echo "- <Product>: Product to generate IP for. Choose from: ${!PRODUCT_ID_MAP[@]}"
+ return 1
+ fi
+
+ IFS='/' read -r -a prod_tokens <<< "${PRODUCT_ID_MAP[$1]}"
+ part_name=${prod_tokens[1]}${prod_tokens[2]}${prod_tokens[3]}
+ if [[ -z $part_name ]]; then
+ echo "ERROR: Invalid product name $1. Supported: ${!PRODUCT_ID_MAP[@]}"
+ return 1
+ fi
+ $VIVADO_EXEC -mode batch -source $(resolve_viv_path $VIV_IP_UTILS) -nolog -nojournal -tclargs list $part_name | grep -v -E '(^$|^#|\*\*)'
+ test ${PIPESTATUS[0]} -eq 0
+}
+
+function viv_upgrade_ip {
+ if [[ -z $1 ]]; then
+ echo "Upgrade one or more Xilinx IP targets"
+ echo ""
+ echo "Usage: viv_upgrade_ip <IP Directory> [--recursive]"
+ echo "- <IP Directory>: Path to the IP XCI file."
+ return 1
+ fi
+ max_depth="-maxdepth 1"
+ if [[ $2 == "--recursive" ]]; then
+ max_depth=""
+ fi
+ search_path=$(readlink -f $1)
+ IFS='' read -r -a xci_files <<< $(find $search_path $max_depth | grep .xci | xargs)
+ for xci_path in $xci_files; do
+ if [[ -f $xci_path ]]; then
+ echo "Upgrading $xci_path..."
+ part_name=$(python $REPO_BASE_PATH/tools/scripts/viv_ip_xci_editor.py read_part $xci_path)
+ $VIVADO_EXEC -mode batch -source $(resolve_viv_path $VIV_IP_UTILS) -nolog -nojournal -tclargs upgrade $part_name $(resolve_viv_path $xci_path) | grep -v -E '(^$|^#|\*\*)'
+ test ${PIPESTATUS[0]} -eq 0
+ else
+ echo "ERROR: IP $xci_path not found."
+ return 1
+ fi
+ done
+}
+
+#----------------------------------------------------------------------------
+# Define hardware programming aliases
+#----------------------------------------------------------------------------
+VIV_HW_UTILS=$REPO_BASE_PATH/tools/scripts/viv_hardware_utils.tcl
+
+function viv_hw_console {
+ vivado -mode tcl -source $(resolve_viv_path $VIV_HW_UTILS) -nolog -nojournal
+}
+
+function viv_jtag_list {
+ $VIVADO_EXEC -mode batch -source $(resolve_viv_path $VIV_HW_UTILS) -nolog -nojournal -tclargs list | grep -v -E '(^$|^#|\*\*)'
+ test ${PIPESTATUS[0]} -eq 0
+}
+
+function viv_jtag_program {
+ if [[ -z $1 ]]; then
+ echo "Downloads a bitfile to an FPGA device using Vivado"
+ echo ""
+ echo "Usage: viv_jtag_program <Bitfile Path> [<FTDI Serial> = .] [<Device Address> = 0:0]"
+ echo "- <Bitfile Path>: Path to a .bit FPGA configuration file"
+ echo "- <FTDI Serial>: Regular expression for filtering out devices by"
+ echo " their FTDI serial"
+ echo "- <Device Address>: Address to the device in the form <Target>:<Device>"
+ echo " Run viv_jtag_list to get a list of connected devices"
+ return 1
+ fi
+ $VIVADO_EXEC -mode batch -source $(resolve_viv_path $VIV_HW_UTILS) -nolog -nojournal -tclargs program $* | grep -v -E '(^$|^#|\*\*)'
+ test ${PIPESTATUS[0]} -eq 0
+}
+
+function probe_bitfile {
+ if [[ -z $1 ]]; then
+ echo "Probe a Xilinx bit file and report header information"
+ echo ""
+ echo "Usage: probe_bitfile <Bitfile Path>"
+ echo "- <Bitfile Path>: Path to a .bit FPGA configuration file"
+ return 1
+ fi
+ python $REPO_BASE_PATH/tools/scripts/xil_bitfile_parser.py --info $1
+}
+
+echo
+echo "Environment successfully initialized."
+return 0
diff --git a/fpga/usrp3/tools/scripts/shared-ip-loc-manage.sh b/fpga/usrp3/tools/scripts/shared-ip-loc-manage.sh
new file mode 100755
index 000000000..447f087e0
--- /dev/null
+++ b/fpga/usrp3/tools/scripts/shared-ip-loc-manage.sh
@@ -0,0 +1,119 @@
+#!/bin/bash
+
+function help {
+ cat <<EOHELP
+
+Usage: shared-ip-loc-manage.sh [--help|-h] [--timeout=<TIMEOUT>] --path=<PATH> <ACTION>
+
+--path : Path to IP location
+--timeout : Timeout in seconds for the operation to complete [Optional]
+ (Default: 1200)
+--force : Force operation
+--help -h : Shows this message.
+
+ACTION : Choose from reserve, release
+
+EOHELP
+}
+
+function wait_for_lock {
+ if [ -d "$ip_dir" ]; then
+ remaining=$(($timeout))
+ trap 'echo"";echo "BUILDER: Waiting for concurrent IP build to finish... (skipped)";break;' SIGINT; \
+ while [ -f "$ip_dir/.build_lock" ]; do
+ if [ $remaining -gt 0 ]; then
+ echo -ne "Waiting for concurrent IP build to finish... (${remaining}s [Ctrl-C to proceed])\033[0K\r"
+ sleep 1
+ : $((remaining--))
+ else
+ break
+ fi
+ done
+ trap - SIGINT; \
+ if [ $remaining -eq 0 ]; then
+ echo "BUILDER: Waiting for concurrent IP build to finish... (timeout)"
+ fi
+ fi
+}
+
+function lock {
+ if [ -d "$ip_dir" ]; then
+ touch $ip_dir/.build_lock
+ fi
+}
+
+function unlock {
+ rm -f $ip_dir/.build_lock
+}
+
+function reserve {
+ if [ -d "$ip_dir" ]; then
+ wait_for_lock
+ if [ $remaining -eq 0 ]; then
+ echo "Force creating new IP location: $ip_dir"
+ unlock
+ rm -rf $ip_dir
+ mkdir -p $ip_dir
+ fi
+ fi
+ if [ ! -d "$ip_dir" ]; then
+ mkdir -p $ip_dir
+ fi
+ echo "BUILDER: Reserving IP location: $ip_dir"
+ lock
+}
+
+function release {
+ echo "BUILDER: Releasing IP location: $ip_dir"
+ unlock
+}
+
+# Parse options
+ip_dir=""
+action=""
+timeout=1800
+remaining=0
+force=0
+
+for arg in "$@"; do
+ if [[ $arg == "--help" ]]; then
+ help
+ exit 0
+ elif [[ $arg == "--force" ]]; then
+ force=1
+ elif [[ $arg =~ "--path="(.+) ]]; then
+ ip_dir=`readlink -m ${BASH_REMATCH[1]}`
+ elif [[ $arg =~ "--timeout="(.+) ]]; then
+ timeout=${BASH_REMATCH[1]}
+ else
+ action=$arg
+ break
+ fi
+done
+
+# Validate inputs
+if [ -z $ip_dir ]; then
+ echo "ERROR: Please specify a valid path using the --path option."
+ exit 1
+fi
+
+case $action in
+ reserve)
+ if [ $force -eq 1 ]; then
+ echo "Force creating new IP location: $ip_dir"
+ rm -rf $ip_dir
+ mkdir -p $ip_dir
+ lock
+ else
+ reserve
+ fi
+ ;;
+ release)
+ release
+ ;;
+ *)
+ echo "ERROR: Please specify a valid action (reserve, release)"
+ exit 1
+ ;;
+esac
+
diff --git a/fpga/usrp3/tools/scripts/uhd_image_builder.py b/fpga/usrp3/tools/scripts/uhd_image_builder.py
new file mode 100755
index 000000000..7398b6e13
--- /dev/null
+++ b/fpga/usrp3/tools/scripts/uhd_image_builder.py
@@ -0,0 +1,537 @@
+#!/usr/bin/env python
+"""
+Copyright 2016-2017 Ettus Research
+Copyright 2019 Ettus Research, A National Instrument Brand
+
+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 argparse
+import os
+import re
+import glob
+
+HEADER_TMPL = """/////////////////////////////////////////////////////////
+// Auto-generated by uhd_image_builder.py! Any changes
+// in this file will be overwritten the next time
+// this script is run.
+/////////////////////////////////////////////////////////
+localparam NUM_CE = {num_ce};
+wire [NUM_CE*64-1:0] ce_flat_o_tdata, ce_flat_i_tdata;
+wire [63:0] ce_o_tdata[0:NUM_CE-1], ce_i_tdata[0:NUM_CE-1];
+wire [NUM_CE-1:0] ce_o_tlast, ce_o_tvalid, ce_o_tready, ce_i_tlast, ce_i_tvalid, ce_i_tready;
+wire [63:0] ce_debug[0:NUM_CE-1];
+// Flattern CE tdata arrays
+genvar k;
+generate
+ for (k = 0; k < NUM_CE; k = k + 1) begin
+ assign ce_o_tdata[k] = ce_flat_o_tdata[k*64+63:k*64];
+ assign ce_flat_i_tdata[k*64+63:k*64] = ce_i_tdata[k];
+ end
+endgenerate
+wire ce_clk = radio_clk;
+wire ce_rst = radio_rst;
+"""
+
+BLOCK_TMPL = """
+noc_block_{blockname} {blockparameters} {instname} (
+ .bus_clk(bus_clk), .bus_rst(bus_rst),
+ .ce_clk({clock}_clk), .ce_rst({clock}_rst),
+ .i_tdata(ce_o_tdata[{n}]), .i_tlast(ce_o_tlast[{n}]), .i_tvalid(ce_o_tvalid[{n}]), .i_tready(ce_o_tready[{n}]),
+ .o_tdata(ce_i_tdata[{n}]), .o_tlast(ce_i_tlast[{n}]), .o_tvalid(ce_i_tvalid[{n}]), .o_tready(ce_i_tready[{n}]),
+ .debug(ce_debug[{n}]){extraports}
+);
+"""
+
+FILL_FIFO_TMPL = """
+// Fill remaining crossbar ports with loopback FIFOs
+genvar n;
+generate
+ for (n = {fifo_start}; n < NUM_CE; n = n + 1) begin
+ noc_block_axi_fifo_loopback inst_noc_block_axi_fifo_loopback (
+ .bus_clk(bus_clk), .bus_rst(bus_rst),
+ .ce_clk(ce_clk), .ce_rst(ce_rst),
+ .i_tdata(ce_o_tdata[n]), .i_tlast(ce_o_tlast[n]), .i_tvalid(ce_o_tvalid[n]), .i_tready(ce_o_tready[n]),
+ .o_tdata(ce_i_tdata[n]), .o_tlast(ce_i_tlast[n]), .o_tvalid(ce_i_tvalid[n]), .o_tready(ce_i_tready[n]),
+ .debug(ce_debug[n])
+ );
+ end
+endgenerate
+"""
+
+# List of blocks that are part of our library but that do not take part
+# in the process this tool provides
+BLACKLIST = {'radio_core', 'axi_dma_fifo'}
+
+OOT_DIR_TMPL = """\nOOT_DIR = {oot_dir}\n"""
+OOT_INC_TMPL = """include $(OOT_DIR)/Makefile.inc\n"""
+OOT_SRCS_TMPL = """RFNOC_OOT_SRCS += {sources}\n"""
+OOT_SRCS_FILE_HDR = """##################################################
+# Include OOT makefiles
+##################################################\n"""
+
+
+def setup_parser():
+ """
+ Create argument parser
+ """
+ parser = argparse.ArgumentParser(
+ description="Generate the NoC block instantiation file",
+ )
+ parser.add_argument(
+ "-I", "--include-dir",
+ help="Path directory of the RFNoC Out-of-Tree module",
+ nargs='+',
+ default=None)
+ parser.add_argument(
+ "-y", "--yml",
+ help="YML file definition of onboard blocks\
+ (overrides the 'block' positional arguments)",
+ default=None)
+ parser.add_argument(
+ "-m", "--max-num-blocks", type=int,
+ help="Maximum number of blocks (Max. Allowed for x310|x300: 10,\
+ for e300: 14, for e320: 12, for n300: 11, \
+ for n310/n320: 10)",
+ default=10)
+ parser.add_argument(
+ "--fill-with-fifos",
+ help="If the number of blocks provided was smaller than the max\
+ number, fill the rest with FIFOs",
+ action="store_true")
+ parser.add_argument(
+ "-o", "--outfile",
+ help="Output /path/filename - By running this directive,\
+ you won't build your IP",
+ default=None)
+ parser.add_argument(
+ "--auto-inst-src",
+ help="Advanced Usage: The Verilog source for the auto_inst file that "
+ "will be used instead of generating one automatically",
+ default=None)
+ parser.add_argument(
+ "-d", "--device",
+ help="Device to be programmed [x300, x310, e310, e320, n300, n310, n320]",
+ default="x310")
+ parser.add_argument(
+ "-t", "--target",
+ help="Build target - image type [X3X0_RFNOC_HG, X3X0_RFNOC_XG,\
+ E310_RFNOC_sg3, E320_RFNOC_1G, N310_RFNOC_HG, ...]",
+ default=None)
+ parser.add_argument(
+ "-g", "--GUI",
+ help="Open Vivado GUI during the FPGA building process",
+ action="store_true")
+ parser.add_argument(
+ "-c", "--clean-all",
+ help="Cleans the IP before a new build",
+ action="store_true")
+ parser.add_argument(
+ "blocks",
+ help="List block names to instantiate.",
+ default="",
+ nargs='*',
+ )
+ return parser
+
+def get_default_parameters():
+ default = {"clock" : "ce",
+ "parameters" : None,
+ "extraports" : None}
+ return default
+
+
+def parse_yml(ymlfile):
+ """
+ Parse an input yaml file with a list of blocks and parameters!
+ """
+ try:
+ import yaml
+ except ImportError:
+ print('[ERROR] Could not import yaml module')
+ exit(1)
+
+ with open(ymlfile, 'r') as input_file:
+ data = yaml.load(input_file)
+ blocks = []
+ params = []
+ for val in data:
+ print(val['block'])
+ blocks.append(val['block'])
+ blockparams = get_default_parameters()
+ if "clock" in val:
+ blockparams["clock"] = val["clock"]
+ if "parameters" in val:
+ blockparams["parameters"] = val["parameters"]
+ if "extraports" in val:
+ blockparams["extraports"] = val["extraports"]
+ print(blockparams)
+ params.append(blockparams)
+ print(data)
+ return blocks, params
+
+def format_param_str(parameters):
+ """
+ Take a single block parameter dictionary and format as a verilog string
+ """
+ paramstr = ""
+ if parameters:
+ paramstrlist = []
+ for key in parameters.keys():
+ value = ""
+ if parameters[key] is not None:
+ value = parameters[key]
+ currstr = ".%s(%s)" % (str.upper(key), value)
+ paramstrlist.append(currstr)
+ paramstr = "#(%s)" % (", ".join(paramstrlist))
+ return paramstr
+
+def format_port_str(extraports):
+ """
+ Take a single dictionary and format as a verilog string representing extra block ports
+ """
+ portstr = ""
+ if extraports:
+ portstrlist = []
+ for key in extraports.keys():
+ value = ""
+ if extraports[key] is not None:
+ value = extraports[key]
+ currstr = ".%s(%s)" % (key, value)
+ portstrlist.append(currstr)
+ portstr = ",\n %s" % (",\n ".join(portstrlist))
+ return portstr
+
+def create_auto_inst(blocks, blockparams, max_num_blocks, fill_with_fifos=False):
+ """
+ Returns the Verilog source for the auto_inst file.
+ """
+ if len(blocks) == 0:
+ print("[GEN_RFNOC_INST ERROR] No blocks specified!")
+ exit(1)
+ if len(blocks) > max_num_blocks:
+ print("[GEN_RFNOC_INST ERROR] Trying to connect {} blocks, max is {}"
+ .format(len(blocks), max_num_blocks))
+ exit(1)
+ num_ce = max_num_blocks
+ if not fill_with_fifos:
+ num_ce = len(blocks)
+ vfile = HEADER_TMPL.format(num_ce=num_ce)
+ blocks_in_blacklist = [block for block in blocks if block in BLACKLIST]
+ if len(blocks_in_blacklist):
+ print("[RFNoC ERROR]: The following blocks require special treatment and"\
+ " can't be instantiated with this tool: ")
+ for element in blocks_in_blacklist:
+ print(" * ", element)
+ print("Remove them from the command and run the uhd_image_builder.py again.")
+ exit(0)
+ print("--Using the following blocks to generate image:")
+ block_count = {k: 0 for k in set(blocks)}
+ for i, (block, params) in enumerate(zip(blocks, blockparams)):
+ block_count[block] += 1
+ instname = "inst_{}{}".format(block, "" \
+ if block_count[block] == 1 else block_count[block])
+ print(" * {}".format(block))
+ vfile += BLOCK_TMPL.format(blockname=block,
+ blockparameters=format_param_str(params["parameters"]),
+ instname=instname,
+ n=i,
+ clock=params["clock"],
+ extraports=format_port_str(params["extraports"]))
+ if fill_with_fifos:
+ vfile += FILL_FIFO_TMPL.format(fifo_start=len(blocks))
+ return vfile
+
+def file_generator(args, vfile):
+ """
+ Takes the target device as an argument and, if no '-o' directive is given,
+ replaces the auto_ce file in the corresponding top folder. With the
+ presence of -o, it just generates a version of the verilog file which
+ is not intended to be build
+ """
+ fpga_utils_path = get_scriptpath()
+ print("Adding CE instantiation file for '%s'" % args.target)
+ path_to_file = fpga_utils_path +'/../../top/' + device_dict(args.device.lower()) +\
+ '/rfnoc_ce_auto_inst_' + args.device.lower() + '.v'
+ if args.outfile is None:
+ open(path_to_file, 'w').write(vfile)
+ else:
+ open(args.outfile, 'w').write(vfile)
+
+def append_re_line_sequence(filename, linepattern, newline):
+ """ Detects the re 'linepattern' in the file. After its last occurrence,
+ paste 'newline'. If the pattern does not exist, append the new line
+ to the file. Then, write. If the newline already exists, leaves the file
+ unchanged"""
+ oldfile = open(filename, 'r').read()
+ lines = re.findall(newline, oldfile, flags=re.MULTILINE)
+ if len(lines) != 0:
+ pass
+ else:
+ pattern_lines = re.findall(linepattern, oldfile, flags=re.MULTILINE)
+ if len(pattern_lines) == 0:
+ open(filename, 'a').write(newline)
+ return
+ last_line = pattern_lines[-1]
+ newfile = oldfile.replace(last_line, last_line + newline + '\n')
+ open(filename, 'w').write(newfile)
+
+def create_oot_include(device, include_dirs):
+ """
+ Create the include file for OOT RFNoC sources
+ """
+ oot_dir_list = []
+ target_dir = device_dict(device.lower())
+ dest_srcs_file = os.path.join(get_scriptpath(), '..', '..', 'top',\
+ target_dir, 'Makefile.OOT.inc')
+ incfile = open(dest_srcs_file, 'w')
+ incfile.write(OOT_SRCS_FILE_HDR)
+ if include_dirs is not None:
+ for dirs in include_dirs:
+ currpath = os.path.abspath(str(dirs))
+ if os.path.isdir(currpath) & (os.path.basename(currpath) == "rfnoc"):
+ # Case 1: Pointed directly to rfnoc directory
+ oot_path = currpath
+ elif os.path.isdir(os.path.join(currpath, 'rfnoc')):
+ # Case 2: Pointed to top level rfnoc module directory
+ oot_path = os.path.join(currpath, 'rfnoc')
+ elif os.path.isfile(os.path.join(currpath, 'Makefile.inc')):
+ # Case 3: Pointed to a random directory with a Makefile.inc
+ oot_path = currpath
+ else:
+ print('No RFNoC module found at ' + os.path.abspath(currpath))
+ continue
+ if oot_path not in oot_dir_list:
+ oot_dir_list.append(oot_path)
+ named_path = os.path.join('$(BASE_DIR)', get_relative_path(get_basedir(), oot_path))
+ incfile.write(OOT_DIR_TMPL.format(oot_dir=named_path))
+ if os.path.isfile(os.path.join(oot_path, 'Makefile.inc')):
+ # Check for Makefile.inc
+ incfile.write(OOT_INC_TMPL)
+ elif os.path.isfile(os.path.join(oot_path, 'rfnoc', 'Makefile.inc')):
+ # Check for Makefile.inc
+ incfile.write(OOT_INC_TMPL)
+ elif os.path.isfile(os.path.join(oot_path, 'rfnoc', 'fpga-src', 'Makefile.srcs')):
+ # Legacy: Check for fpga-src/Makefile.srcs
+ # Read, then append to file
+ curr_srcs = open(os.path.join(oot_path, 'rfnoc', 'fpga-src', 'Makefile.srcs'), 'r').read()
+ curr_srcs = curr_srcs.replace('SOURCES_PATH', os.path.join(oot_path, 'rfnoc', 'fpga-src', ''))
+ incfile.write(OOT_SRCS_TMPL.format(sources=curr_srcs))
+ else:
+ print('No valid makefile found at ' + os.path.abspath(currpath))
+ continue
+ incfile.close()
+
+def append_item_into_file(device, include_dir):
+ """
+ Basically the same as append_re_line_sequence function, but it does not
+ append anything when the input is not found
+ ---
+ Detects the re 'linepattern' in the file. After its last occurrence,
+ pastes the input string. If pattern doesn't exist
+ notifies and leaves the file unchanged
+ """
+ def get_oot_srcs_list(include_dir):
+ """
+ Pull the OOT sources out of the Makefile.srcs
+ """
+ oot_srcs_file = os.path.join(include_dir, 'Makefile.srcs')
+ oot_srcs_list = readfile(oot_srcs_file)
+ return [w.replace('SOURCES_PATH', include_dir) for w in oot_srcs_list]
+ # Here we go
+ target_dir = device_dict(device.lower())
+ if include_dir is not None:
+ for directory in include_dir:
+ dirs = os.path.join(directory, '')
+ checkdir_v(dirs)
+ dest_srcs_file = os.path.join(get_scriptpath(), '..', '..', 'top',\
+ target_dir, 'Makefile.srcs')
+ oot_srcs_list = get_oot_srcs_list(dirs)
+ dest_srcs_list = readfile(dest_srcs_file)
+ prefixpattern = re.escape('$(addprefix ' + dirs + ', \\\n')
+ linepattern = re.escape('RFNOC_OOT_SRCS = \\\n')
+ oldfile = open(dest_srcs_file, 'r').read()
+ prefixlines = re.findall(prefixpattern, oldfile, flags=re.MULTILINE)
+ if len(prefixlines) == 0:
+ lines = re.findall(linepattern, oldfile, flags=re.MULTILINE)
+ if len(lines) == 0:
+ print("Pattern {} not found. Could not write `{}'"
+ .format(linepattern, oldfile))
+ return
+ else:
+ last_line = lines[-1]
+ srcs = "".join(oot_srcs_list)
+ else:
+ last_line = prefixlines[-1]
+ srcs = "".join([
+ item
+ for item in oot_srcs_list
+ if item not in dest_srcs_list
+ ])
+ newfile = oldfile.replace(last_line, last_line + srcs)
+ open(dest_srcs_file, 'w').write(newfile)
+
+def compare(file1, file2):
+ """
+ compares two files line by line, and returns the lines of first file that
+ were not found on the second. The returned is a tuple item that can be
+ accessed in the form of a list as tuple[0], where each line takes a
+ position on the list or in a string as tuple [1].
+ """
+ notinside = []
+ with open(file1, 'r') as arg1:
+ with open(file2, 'r') as arg2:
+ text1 = arg1.readlines()
+ text2 = arg2.readlines()
+ for item in text1:
+ if item not in text2:
+ notinside.append(item)
+ return notinside
+
+def readfile(files):
+ """
+ compares two files line by line, and returns the lines of first file that
+ were not found on the second. The returned is a tuple item that can be
+ accessed in the form of a list as tuple[0], where each line takes a
+ position on the list or in a string as tuple [1].
+ """
+ contents = []
+ with open(files, 'r') as arg:
+ text = arg.readlines()
+ for item in text:
+ contents.append(item)
+ return contents
+
+def build(args):
+ " build "
+ cwd = get_scriptpath()
+ target_dir = device_dict(args.device.lower())
+ build_dir = os.path.join(cwd, '..', '..', 'top', target_dir)
+ if os.path.isdir(build_dir):
+ print("changing temporarily working directory to {0}".\
+ format(build_dir))
+ os.chdir(build_dir)
+ make_cmd = ". ./setupenv.sh "
+ if args.clean_all:
+ make_cmd = make_cmd + "&& make cleanall "
+ make_cmd = make_cmd + "&& make " + dtarget(args)
+ if args.GUI:
+ make_cmd = make_cmd + " GUI=1"
+ # Wrap it into a bash call:
+ make_cmd = '/bin/bash -c "{0}"'.format(make_cmd)
+ ret_val = os.system(make_cmd)
+ os.chdir(cwd)
+ return ret_val
+
+def device_dict(args):
+ """
+ helps selecting the device building folder based on the targeted device
+ """
+ build_dir = {
+ 'x300':'x300',
+ 'x310':'x300',
+ 'e300':'e31x',
+ 'e310':'e31x',
+ 'e320':'e320',
+ 'n300':'n3xx',
+ 'n310':'n3xx',
+ 'n320':'n3xx'
+ }
+ return build_dir[args]
+
+def dtarget(args):
+ """
+ If no target specified, selects the default building target based on the
+ targeted device
+ """
+ if args.target is None:
+ default_trgt = {
+ 'x300':'X300_RFNOC_HG',
+ 'x310':'X310_RFNOC_HG',
+ 'e310':'E310_SG3_RFNOC',
+ 'e320':'E320_RFNOC_1G',
+ 'n300':'N300_RFNOC_HG',
+ 'n310':'N310_RFNOC_HG',
+ 'n320':'N320_RFNOC_XG',
+ }
+ return default_trgt[args.device.lower()]
+ else:
+ return args.target
+
+def checkdir_v(include_dir):
+ """
+ Checks the existance of verilog files in the given include dir
+ """
+ nfiles = glob.glob(os.path.join(include_dir,'')+'*.v')
+ if len(nfiles) == 0:
+ print('[ERROR] No verilog files found in the given directory')
+ exit(0)
+ else:
+ print('Verilog sources found!')
+ return
+
+def get_scriptpath():
+ """
+ returns the absolute path where a script is located
+ """
+ return os.path.dirname(os.path.realpath(__file__))
+
+def get_basedir():
+ """
+ returns the base directory (BASE_DIR) used in rfnoc build process
+ """
+ return os.path.abspath(os.path.join(get_scriptpath(), '..', '..', 'top'))
+
+def get_relative_path(base, target):
+ """
+ Find the relative path (including going "up" directories) from base to target
+ """
+ basedir = os.path.abspath(base)
+ prefix = os.path.commonprefix([basedir, os.path.abspath(target)])
+ path_tail = os.path.relpath(os.path.abspath(target), prefix)
+ total_path = path_tail
+ if prefix != "":
+ while basedir != os.path.abspath(prefix):
+ basedir = os.path.dirname(basedir)
+ total_path = os.path.join('..', total_path)
+ return total_path
+ else:
+ print("Could not determine relative path")
+ return path_tail
+
+def main():
+ " Go, go, go! "
+ args = setup_parser().parse_args()
+ if args.yml:
+ print("Using yml file. Ignoring command line blocks arguments")
+ blocks, params = parse_yml(args.yml)
+ else:
+ blocks = args.blocks
+ params = [get_default_parameters()]*len(blocks)
+ if args.auto_inst_src is None:
+ vfile = create_auto_inst(blocks, params, args.max_num_blocks, args.fill_with_fifos)
+ else:
+ vfile = open(args.auto_inst_src, 'r').read()
+ file_generator(args, vfile)
+ create_oot_include(args.device, args.include_dir)
+ if args.outfile is None:
+ return build(args)
+ else:
+ print("Instantiation file generated at {}".\
+ format(args.outfile))
+ return 0
+
+if __name__ == "__main__":
+ exit(main())
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()
diff --git a/fpga/usrp3/tools/scripts/viv_check_syntax.tcl b/fpga/usrp3/tools/scripts/viv_check_syntax.tcl
new file mode 100644
index 000000000..304bd5405
--- /dev/null
+++ b/fpga/usrp3/tools/scripts/viv_check_syntax.tcl
@@ -0,0 +1,14 @@
+#
+# Copyright 2018 Ettus Research
+#
+
+source $::env(VIV_TOOLS_DIR)/scripts/viv_utils.tcl
+
+# STEP#1: Create project, add sources, refresh IP
+vivado_utils::initialize_project
+
+# STEP#2: Run elaboration
+vivado_utils::check_design
+
+# Cleanup
+vivado_utils::close_batch_project
diff --git a/fpga/usrp3/tools/scripts/viv_gen_ip_makefile.py b/fpga/usrp3/tools/scripts/viv_gen_ip_makefile.py
new file mode 100644
index 000000000..87572e86e
--- /dev/null
+++ b/fpga/usrp3/tools/scripts/viv_gen_ip_makefile.py
@@ -0,0 +1,54 @@
+#! /usr/bin/python
+
+import sys, os
+import collections
+import argparse
+import datetime
+
+# Parse command line options
+def get_options():
+ parser = argparse.ArgumentParser(description='Create a Makefile for Xilinx IP.')
+ parser.add_argument('--ip_name', type=str, default=None, help='Name for the IP core')
+ parser.add_argument('--dest', type=str, default=None, help='Destination directory')
+ parser.add_argument('--copright_auth', type=str, default='Ettus Research', help='Copyright author')
+ args = parser.parse_args()
+ if not args.ip_name:
+ print('ERROR: Please specify a name for the IP core\n')
+ parser.print_help()
+ sys.exit(1)
+ if not args.dest:
+ print('ERROR: Please specify the location for the IP core\n')
+ parser.print_help()
+ sys.exit(1)
+ return args
+
+g_makefile_template = """#
+# {copyright}
+#
+
+include $(TOOLS_DIR)/make/viv_ip_builder.mak
+
+{ip_srcs_var} = $(IP_BUILD_DIR)/{ip_name}/{ip_name}.xci
+
+{ip_outs_var} = $(addprefix $(IP_BUILD_DIR)/{ip_name}/, \\
+{ip_name}.xci.out \\
+)
+
+$({ip_srcs_var}) $({ip_outs_var}) : $(IP_DIR)/{ip_name}/{ip_name}.xci
+\t$(call BUILD_VIVADO_IP,{ip_name},$(ARCH),$(PART_ID),$(IP_DIR),$(IP_BUILD_DIR),0)
+"""
+
+def main():
+ args = get_options();
+
+ transform = {}
+ transform['ip_name'] = args.ip_name
+ transform['ip_srcs_var'] = 'IP_' + args.ip_name.upper() + '_SRCS'
+ transform['ip_outs_var'] = 'IP_' + args.ip_name.upper() + '_OUTS'
+ transform['copyright'] = 'Copyright ' + str(datetime.datetime.now().year) + ' ' + args.copright_auth
+
+ with open(os.path.join(args.dest, 'Makefile.inc'), 'w') as mak_file:
+ mak_file.write(g_makefile_template.format(**transform))
+
+if __name__ == '__main__':
+ main()
diff --git a/fpga/usrp3/tools/scripts/viv_gen_part_id.py b/fpga/usrp3/tools/scripts/viv_gen_part_id.py
new file mode 100644
index 000000000..b82c146aa
--- /dev/null
+++ b/fpga/usrp3/tools/scripts/viv_gen_part_id.py
@@ -0,0 +1,37 @@
+#!/usr/bin/python
+
+import argparse
+import os, sys
+import re
+
+# Parse command line options
+def get_options():
+ parser = argparse.ArgumentParser(description='Utility script to generate a properly formed partid for Xilinx projects')
+ parser.add_argument('target', type=str, default=None, help='Input value for target. Must be of the form <arch>/<device>/<package>/<speedgrade>[/<temperaturegrade>[/<silicon revision>]]')
+ args = parser.parse_args()
+ if not args.target:
+ print('ERROR: Please specify a target device tuple\n')
+ parser.print_help()
+ sys.exit(1)
+ return args
+
+def main():
+ args = get_options();
+
+ target_tok = args.target.split('/')
+ if len(target_tok) < 4:
+ print('ERROR: Invalid target format. Must be <arch>/<device>/<package>/<speedgrade>[/<temperaturegrade>[/<silicon_revision>]]')
+ print('ERROR: Parsed only ' + str(len(target_tok)) + ' tokens')
+ sys.exit(1)
+ if target_tok[0] in ['artix7', 'kintex7', 'zynq', 'spartan7', 'virtex7']:
+ print('' + target_tok[1] + target_tok[2] + target_tok[3])
+ elif target_tok[0] in ['zynquplus', 'zynquplusRFSOC']:
+ if len(target_tok) > 5:
+ print('' + target_tok[1] + '-' + target_tok[2] + target_tok[3] + '-' + target_tok[4] + '-' + target_tok[5])
+ else:
+ print('' + target_tok[1] + '-' + target_tok[2] + target_tok[3] + '-' + target_tok[4])
+ else:
+ print('unknown-part-error')
+
+if __name__ == '__main__':
+ main()
diff --git a/fpga/usrp3/tools/scripts/viv_generate_bd.tcl b/fpga/usrp3/tools/scripts/viv_generate_bd.tcl
new file mode 100644
index 000000000..546a190b1
--- /dev/null
+++ b/fpga/usrp3/tools/scripts/viv_generate_bd.tcl
@@ -0,0 +1,78 @@
+#
+# Copyright 2016 Ettus Research
+#
+
+# ---------------------------------------
+# Gather all external parameters
+# ---------------------------------------
+set bd_file $::env(BD_FILE) ;# Absolute path to BD/Tcl file from src dir
+set src_ext [file extension $bd_file] ;# BD or Tcl file?
+set part_name $::env(PART_NAME) ;# Full Xilinx part name
+set bd_name [file rootname [file tail $bd_file]] ;# Extract IP name
+if {[info exists env(BD_IP_REPOS)]} {
+ set ip_repos $::env(BD_IP_REPOS);# Any supporting IP repos
+} else {
+ set ip_repos {}
+}
+if {[info exists env(BD_HDL_SRCS)]} {
+ set hdl_sources $::env(BD_HDL_SRCS);# Any supporting HDL files
+} else {
+ set hdl_sources {}
+}
+
+# Delete any previous output cookie file
+file delete -force "$bd_file.out"
+# ---------------------------------------
+# Vivado Commands
+# ---------------------------------------
+create_project -part $part_name -in_memory
+# In non-project mode, the hierarchy must be updated for the HDL source files to be
+# correctly applied to the BD. See AR:
+# https://www.xilinx.com/support/answers/63488.html
+set_property source_mgmt_mode All [current_project]
+set_property ip_repo_paths "{$ip_repos}" [current_project]
+update_ip_catalog
+# Add supplementary HDL sources, if they exist.
+foreach src_file $hdl_sources {
+ set hdl_ext [file extension $src_file ]
+ if [expr [lsearch {.vhd .vhdl} $hdl_ext] >= 0] {
+ puts "BUILDER: Adding VHDL : $src_file"
+ read_vhdl -library work $src_file
+ } elseif [expr [lsearch {.v .sv .vh .svh} $hdl_ext] >= 0] {
+ puts "BUILDER: Adding Verilog : $src_file"
+ read_verilog $src_file
+ } else {
+ puts "BUILDER: \[WARNING\] File ignored!!!: $src_file"
+ }
+}
+# Open .tcl or .bd design directly.
+if [expr [lsearch {.tcl} $src_ext] >= 0] {
+ puts "BUILDER: Generating Block Diagram from script: $bd_file"
+ create_bd_design -dir . $bd_name
+ source $bd_file
+ report_ip_status
+ puts "BUILDER: Report_ip_status done"
+ set bd_file $bd_name.bd
+} else {
+ puts "BUILDER: Adding Block Diagram: $bd_file"
+ add_files -norecurse $bd_file
+ puts "BUILDER: Generating BD Target first pass..."
+ generate_target all [get_files $bd_file] -force
+ report_ip_status
+ puts "BUILDER: Report_ip_status done"
+ open_bd_design $bd_file
+}
+# Generate outputs.
+puts "BUILDER: Generating BD Target..."
+generate_target all [get_files $bd_file]
+puts "BUILDER: Generate all done"
+
+if { [get_msg_config -count -severity ERROR] == 0 } {
+ # Write output cookie file
+ set outfile [open "$bd_file.out" w]
+ puts $outfile "This file was auto-generated by viv_generate_bd.tcl and signifies that BD generation is done."
+ close $outfile
+} else {
+ exit 1
+}
+
diff --git a/fpga/usrp3/tools/scripts/viv_generate_hls_ip.tcl b/fpga/usrp3/tools/scripts/viv_generate_hls_ip.tcl
new file mode 100644
index 000000000..f32bfa876
--- /dev/null
+++ b/fpga/usrp3/tools/scripts/viv_generate_hls_ip.tcl
@@ -0,0 +1,36 @@
+#
+# Copyright 2015 Ettus Research
+#
+
+# ---------------------------------------
+# Gather all external parameters
+# ---------------------------------------
+set part_name $::env(PART_NAME) ;# Full Xilinx part name
+set hls_ip_name $::env(HLS_IP_NAME) ;# High level synthesis IP name
+set hls_ip_srcs $::env(HLS_IP_SRCS) ;# High level synthesis IP source files
+set hls_ip_inc $::env(HLS_IP_INCLUDES) ;# High level synthesis IP include directories
+
+# ---------------------------------------
+# Vivado Commands
+# ---------------------------------------
+open_project $hls_ip_name
+open_solution "solution"
+set_part $part_name
+set_top $hls_ip_name
+puts "BUILDER: Using include location : $hls_ip_inc"
+foreach src_file $hls_ip_srcs {
+ set src_ext [file extension $src_file ]
+ if [expr [lsearch {.c .cpp .cc .h .hpp} $src_ext] >= 0] {
+ puts "BUILDER: Adding C/C++ : $src_file"
+ add_files $src_file -cflags "-I $hls_ip_inc"
+ } elseif [expr [lsearch {.tcl} $src_ext] >= 0] {
+ puts "BUILDER: Executing tcl script : $src_file"
+ source $src_file
+ } else {
+ puts "BUILDER: \[WARNING\] File ignored!!!: $src_file"
+ }
+}
+csynth_design
+export_design -format ip_catalog
+
+exit
diff --git a/fpga/usrp3/tools/scripts/viv_generate_ip.tcl b/fpga/usrp3/tools/scripts/viv_generate_ip.tcl
new file mode 100644
index 000000000..8fe769336
--- /dev/null
+++ b/fpga/usrp3/tools/scripts/viv_generate_ip.tcl
@@ -0,0 +1,43 @@
+#
+# Copyright 2014 Ettus Research
+#
+
+# ---------------------------------------
+# Gather all external parameters
+# ---------------------------------------
+set xci_file $::env(XCI_FILE) ;# Absolute path to XCI file from src dir
+set part_name $::env(PART_NAME) ;# Full Xilinx part name
+set gen_example_proj $::env(GEN_EXAMPLE) ;# Generate an example project
+set synth_ip $::env(SYNTH_IP) ;# Synthesize generated IP
+set ip_name [file rootname [file tail $xci_file]] ;# Extract IP name
+
+# Delete any previous output cookie file
+file delete -force "$xci_file.out"
+
+# ---------------------------------------
+# Vivado Commands
+# ---------------------------------------
+create_project -part $part_name -in_memory -ip
+set_property target_simulator XSim [current_project]
+add_files -norecurse -force $xci_file
+reset_target all [get_files $xci_file]
+puts "BUILDER: Generating IP Target..."
+generate_target all [get_files $xci_file]
+if [string match $synth_ip "1"] {
+ puts "BUILDER: Synthesizing IP Target..."
+ synth_ip [get_ips $ip_name]
+}
+if [string match $gen_example_proj "1"] {
+ puts "BUILDER: Generating Example Design..."
+ open_example_project -force -dir . [get_ips $ip_name]
+}
+close_project
+
+if { [get_msg_config -count -severity ERROR] == 0 } {
+ # Write output cookie file
+ set outfile [open "$xci_file.out" w]
+ puts $outfile "This file was auto-generated by viv_generate_ip.tcl and signifies that IP generation is done."
+ close $outfile
+} else {
+ exit 1
+}
diff --git a/fpga/usrp3/tools/scripts/viv_hardware_utils.tcl b/fpga/usrp3/tools/scripts/viv_hardware_utils.tcl
new file mode 100644
index 000000000..2bdc02e18
--- /dev/null
+++ b/fpga/usrp3/tools/scripts/viv_hardware_utils.tcl
@@ -0,0 +1,97 @@
+# Function definitions
+proc ::connect_server { {hostname localhost} {port 3121} } {
+ if { [string compare [current_hw_server -quiet] ""] != 0 } {
+ disconnect_server
+ }
+ connect_hw_server -url $hostname:$port
+}
+
+proc ::disconnect_server { } {
+ disconnect_hw_server [current_hw_server]
+}
+
+proc ::jtag_list {} {
+ # Iterate through all hardware targets
+ set hw_targets [get_hw_targets -of_objects [current_hw_server -quiet] -quiet]
+ set idx_t 0
+ foreach hw_target $hw_targets {
+ puts "== Target${idx_t}: $hw_target =="
+ open_hw_target $hw_target -quiet
+ # Iterate through all hardware devices
+ set hw_devices [get_hw_devices]
+ set idx_d 0
+ foreach hw_device $hw_devices {
+ puts "--- Device${idx_d}: $hw_device (Address = ${idx_t}:${idx_d})"
+ set idx_d [expr $idx_d + 1]
+ }
+ close_hw_target -quiet
+ set idx_t [expr $idx_t + 1]
+ }
+}
+
+proc ::jtag_program { filepath {serial "."} {address "0:0"} } {
+ set idx_t [lindex [split $address :] 0]
+ set idx_d [lindex [split $address :] 1]
+
+ set hw_targets [get_hw_targets -of_objects [current_hw_server]]
+ set hw_targets_regexp {}
+
+ foreach target $hw_targets {
+ if { [regexp $serial $target] } {
+ set hw_targets_regexp [concat $hw_targets_regexp $target]
+ }
+ }
+
+ set hw_target [lindex $hw_targets_regexp $idx_t]
+
+ if { [string compare $hw_target ""] == 0 } {
+ error "ERROR: Could not open hw_target $idx_t. Either the address $address is incorrect or the device is not connected."
+ } else {
+ open_hw_target $hw_target -quiet
+ }
+
+ set hw_device [lindex [get_hw_devices] $idx_d]
+ if { [string compare $hw_device ""] == 0 } {
+ close_hw_target -quiet
+ error "ERROR: Could not open hw_device $idx_d. Either the address $address is incorrect or the device is not connected."
+ } else {
+ puts "- Target: $hw_target"
+ puts "- Device: $hw_device"
+ puts "- Filename: $filepath"
+ puts "Programming..."
+ current_hw_device $hw_device
+ set_property PROBES.FILE {} [current_hw_device]
+ set_property PROGRAM.FILE $filepath [current_hw_device]
+ program_hw_devices [current_hw_device]
+ close_hw_target -quiet
+ puts "Programming DONE"
+ }
+}
+
+# Initialization sequence
+open_hw
+connect_server
+
+if [expr $argc > 0] {
+ #Execute a command and exit
+ set cmd [lindex $argv 0]
+ if { [string compare $cmd "list"] == 0 } {
+ jtag_list
+ } elseif { [string compare $cmd "program"] == 0 } {
+ set filepath [lindex $argv 1]
+ if [expr $argc == 3] {
+ set serial [lindex $argv 2]
+ jtag_program $filepath $serial
+ } elseif [expr $argc > 3] {
+ set serial [lindex $argv 2]
+ set devaddr [lindex $argv 3]
+ jtag_program $filepath $serial $devaddr
+ } else {
+ jtag_program $filepath
+ }
+ } else {
+ error "Invalid command: $cmd"
+ }
+ disconnect_server
+ exit
+}
diff --git a/fpga/usrp3/tools/scripts/viv_ip_utils.tcl b/fpga/usrp3/tools/scripts/viv_ip_utils.tcl
new file mode 100644
index 000000000..ba0e87899
--- /dev/null
+++ b/fpga/usrp3/tools/scripts/viv_ip_utils.tcl
@@ -0,0 +1,142 @@
+#
+# Copyright 2015 Ettus Research
+#
+
+if [expr $argc < 2] {
+ error "ERROR: Invalid number of arguments"
+ exit
+}
+
+set cmd [lindex $argv 0]
+set part_name [lindex $argv 1]
+
+# Only create an in-memory roject when not using bdtcl commands.
+if [expr [string first "_bdtcl" $cmd] == -1] {
+ create_project -in_memory -ip -name inmem_ip_proj -part $part_name
+# Otherwise, set the system's TMP directory.
+} else {
+ set sys_tmpdir [pwd]
+ if {[file exists "/tmp"]} {set sys_tmpdir "/tmp"}
+ catch {set sys_tmpdir $::env(TRASH_FOLDER)} ;# very old Macintosh. Mac OS X doesn't have this.
+ catch {set sys_tmpdir $::env(TMP)}
+ catch {set sys_tmpdir $::env(TEMP)}
+}
+
+if { [string compare $cmd "create"] == 0 } {
+ if [expr $argc < 5] {
+ error "ERROR: Invalid number of arguments for the create operation"
+ exit
+ }
+ set ip_name [lindex $argv 2]
+ set ip_dir [lindex $argv 3]
+ set ip_vlnv [lindex $argv 4]
+ create_ip -vlnv $ip_vlnv -module_name $ip_name -dir $ip_dir
+
+} elseif { [string compare $cmd "modify"] == 0 } {
+ if [expr $argc < 3] {
+ error "ERROR: Invalid number of arguments for the modify operation"
+ exit
+ }
+
+ set src_file [lindex $argv 2]
+ set src_ext [file extension $src_file ]
+ if [expr [lsearch {.xci} $src_ext] >= 0] {
+ read_ip $src_file
+ } elseif [expr [lsearch {.bd} $src_ext] >= 0] {
+ add_files -norecurse $src_file
+ export_ip_user_files -of_objects [get_files $src_file] -force -quiet
+ open_bd_design $src_file
+ } else {
+ puts "ERROR: Invalid file extension: $src_ext"
+ }
+
+} elseif { [string compare $cmd "list"] == 0 } {
+ puts "Supported IP for device ${part_name}:"
+ foreach ip [lsort [get_ipdefs]] {
+ puts "- $ip"
+ }
+
+} elseif { [string compare $cmd "upgrade"] == 0 } {
+ if [expr $argc < 3] {
+ error "ERROR: Invalid number of arguments for the upgrade operation"
+ exit
+ }
+ set src_file [lindex $argv 2]
+ read_ip $src_file
+ upgrade_ip [get_ips *]
+
+} elseif { [string compare $cmd "modify_bdtcl"] == 0 } {
+ if [expr $argc < 4] {
+ error "ERROR: Invalid number of arguments for the modify operation"
+ exit
+ }
+
+ set src_file [lindex $argv 2]
+ set src_rootname [file rootname [file tail $src_file]]
+ set src_ext [file extension $src_file ]
+ set ip_repos [lindex $argv 3]
+ set hdl_sources "[file dirname $src_file]/hdl_sources.tcl"
+ if [expr [lsearch {.tcl} $src_ext] >= 0] {
+ # Create a temporary project to work on.
+ set tmp_bddir "${sys_tmpdir}/.viv_${src_rootname}"
+ file mkdir $tmp_bddir
+ cd $tmp_bddir
+ # Create temporary project to store user changes.
+ create_project tmp_bd $tmp_bddir -part $part_name -force
+ set_property ip_repo_paths "{$ip_repos}" [current_project]
+ update_ip_catalog
+ # Add any supporting HDL first
+ if {[file exists $hdl_sources] == 1} {
+ source $hdl_sources
+ } else {
+ puts "hdl_sources.tcl not found in IP directory. Skipping HDL import for BD design"
+ }
+ # Recreate BD design from source file (.tcl)
+ source $src_file
+ regenerate_bd_layout
+ validate_bd_design
+ save_bd_design
+ } else {
+ puts "ERROR: Invalid file extension: $src_ext"
+ }
+
+} elseif { [string compare $cmd "write_bdtcl"] == 0 } {
+ if [expr $argc < 3] {
+ error "ERROR: Invalid number of arguments for the create operation"
+ exit
+ }
+ # When regenerating a TCL file from a BD design, there should be a tmp project
+ # created by this tool ($cmd = modify_bdtcl).
+ set src_file [lindex $argv 2]
+ set src_rootname [file rootname [file tail $src_file]]
+ set src_ext [file extension $src_file ]
+ set src_dir [file dirname $src_file]
+ # Make sure a BD or TCL files is passed
+ if [expr [lsearch {.tcl} $src_ext] >= 0] {
+ # Validate that a previously created BD project exists.
+ set tmp_bddir "${sys_tmpdir}/.viv_${src_rootname}"
+ if {[file exists "$tmp_bddir/tmp_bd.xpr"] == 1} {
+ puts "INFO: Generating TCL file from BD design..."
+ # Open project and BD design
+ open_project "$tmp_bddir/tmp_bd.xpr"
+ open_bd_design [get_files "$src_rootname.bd"]
+ # Rewrite TCL BD file
+ write_bd_tcl -make_local -include_layout -force "$src_dir/$src_rootname.tcl"
+ puts "INFO: BD TCL source updated: $src_dir/$src_rootname.tcl"
+ # Close and delete tmp_bd project, not needed anymore.
+ close_project
+ puts "INFO: Deleting temp Vivado BD project..."
+ file delete -force -- $tmp_bddir
+ exit
+ } else {
+ puts "ERROR: No BD temp project found in: $tmp_bddir"
+ exit
+ }
+ } else {
+ puts "ERROR: Invalid file extension: $src_ext"
+ exit
+ }
+
+} else {
+ error "ERROR: Invalid command: $cmd"
+} \ No newline at end of file
diff --git a/fpga/usrp3/tools/scripts/viv_ip_xci_editor.py b/fpga/usrp3/tools/scripts/viv_ip_xci_editor.py
new file mode 100644
index 000000000..1f5ddf2c5
--- /dev/null
+++ b/fpga/usrp3/tools/scripts/viv_ip_xci_editor.py
@@ -0,0 +1,95 @@
+#!/usr/bin/python
+
+import argparse
+import os, sys
+import re
+
+# Parse command line options
+def get_options():
+ parser = argparse.ArgumentParser(description='Utility script to query and modify a Xilinx IP XCI file')
+ parser.add_argument('action', type=str, default=None, help='Action to perform')
+ parser.add_argument('xci_filepath', type=str, default=None, help='Name for the IP core')
+ parser.add_argument('--target', type=str, default=None, help='Input value for target. Must be of the form <arch>/<device>/<package>/<speedgrade>/<silicon revision>')
+ parser.add_argument("--output_dir", type=str, default='.', help="Build directory for IP")
+ args = parser.parse_args()
+ if not args.action:
+ print('ERROR: Please specify an action to perform\n')
+ parser.print_help()
+ sys.exit(1)
+ if not args.xci_filepath:
+ print('ERROR: Please specify the location for the XCI file to operate on\n')
+ parser.print_help()
+ sys.exit(1)
+ if (not os.path.isfile(args.xci_filepath)):
+ print('ERROR: XCI File ' + args.xci_filepath + ' could not be accessed or is not a file.\n')
+ parser.print_help()
+ sys.exit(1)
+ return args
+
+def get_match_str(item):
+ return '(.*\<spirit:configurableElementValue spirit:referenceId=\".*\.' + item + '\"\>)(.+)(\</spirit:configurableElementValue\>)'
+def get_empty_match_str(item):
+ return '(.*\<spirit:configurableElementValue spirit:referenceId=\".*\.' + item + '\")/\>'
+
+def main():
+ args = get_options();
+
+ # Read XCI File
+ with open(args.xci_filepath) as in_file:
+ xci_lines = in_file.readlines()
+
+ if args.action.startswith('read_'):
+ # Extract info from XCI File
+ xci_info = dict()
+ for line in xci_lines:
+ m = re.search(get_match_str('(ARCHITECTURE|DEVICE|PACKAGE|SPEEDGRADE|TEMPERATURE_GRADE|SILICON_REVISION)'), line)
+ if m is not None:
+ xci_info[m.group(2)] = m.group(3)
+ else:
+ m = re.search(get_empty_match_str('(ARCHITECTURE|DEVICE|PACKAGE|SPEEDGRADE|TEMPERATURE_GRADE|SILICON_REVISION)'),line)
+ if m is not None:
+ xci_info[m.group(2)] = ''
+ if args.action == 'read_target':
+ print(xci_info['ARCHITECTURE'] + '/' + xci_info['DEVICE'] + '/' + xci_info['PACKAGE'] + '/' + xci_info['SPEEDGRADE'])
+ if args.action == 'read_arch':
+ print(xci_info['ARCHITECTURE'])
+ if args.action == 'read_partid':
+ print(xci_info['DEVICE'] + '/' + xci_info['PACKAGE'] + '/' + xci_info['SPEEDGRADE'] + '/' + xci_info['TEMPERATURE_GRADE'] + '/' + xci_info['SILICON_REVISION'])
+ if args.action == 'read_part':
+ print(xci_info['DEVICE'] + xci_info['PACKAGE'] + xci_info['SPEEDGRADE'])
+ elif args.action == 'retarget':
+ # Write a new XCI file with modified target info
+ if (not os.path.isdir(args.output_dir)):
+ print('ERROR: IP Build directory ' + args.output_dir + ' could not be accessed or is not a directory.')
+ sys.exit(1)
+ if not args.target:
+ print('ERROR: No target specified.')
+ sys.exit(1)
+ target_tok = args.target.split('/')
+ if len(target_tok) < 4:
+ print('ERROR: Invalid target format. Must be <arch>/<device>/<package>/<speedgrade>/<tempgrade>/<silicon revision>')
+ sys.exit(1)
+
+ replace_dict = {'ARCHITECTURE': target_tok[0], 'DEVICE': target_tok[1], 'PACKAGE': target_tok[2], 'SPEEDGRADE': target_tok[3], \
+ 'C_XDEVICEFAMILY': target_tok[0], 'C_FAMILY': target_tok[0], 'C_XDEVICE': target_tok[1]}
+ if len(target_tok) > 4:
+ replace_dict['TEMPERATURE_GRADE'] = target_tok[4]
+ if len(target_tok) > 5:
+ replace_dict['SILICON_REVISION'] = target_tok[5]
+ out_xci_filename = os.path.join(os.path.abspath(args.output_dir), os.path.basename(args.xci_filepath))
+
+ with open(out_xci_filename, 'w') as out_file:
+ for r_line in xci_lines:
+ w_line = r_line
+ m = re.search(get_match_str('(' + '|'.join(replace_dict.keys()) + ')'), r_line)
+ if m is not None:
+ w_line = m.group(1) + replace_dict[m.group(2)] + m.group(4) +'\n'
+ else:
+ m = re.search(get_empty_match_str('(' + '|'.join(replace_dict.keys()) + ')'), r_line)
+ if m is not None:
+ w_line = m.group(1) + '>' + replace_dict[m.group(2)] + '</spirit:configurableElementValue>\n'
+ out_file.write(w_line)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/fpga/usrp3/tools/scripts/viv_sim_project.tcl b/fpga/usrp3/tools/scripts/viv_sim_project.tcl
new file mode 100644
index 000000000..f2d071f10
--- /dev/null
+++ b/fpga/usrp3/tools/scripts/viv_sim_project.tcl
@@ -0,0 +1,149 @@
+#
+# Copyright 2014 Ettus Research
+#
+
+# ---------------------------------------
+# Gather all external parameters
+# ---------------------------------------
+set simulator $::env(VIV_SIMULATOR)
+set design_srcs $::env(VIV_DESIGN_SRCS)
+set sim_srcs $::env(VIV_SIM_SRCS)
+set inc_srcs $::env(VIV_INC_SRCS)
+set sim_top $::env(VIV_SIM_TOP)
+set part_name $::env(VIV_PART_NAME)
+set sim_runtime $::env(VIV_SIM_RUNTIME)
+set sim_fast $::env(VIV_SIM_FAST)
+set vivado_mode $::env(VIV_MODE)
+set working_dir [pwd]
+
+set sim_fileset "sim_1"
+set project_name "[string tolower $simulator]_proj"
+
+if [info exists ::env(VIV_SIM_COMPLIBDIR) ] {
+ set sim_complibdir $::env(VIV_SIM_COMPLIBDIR)
+ if [expr [file isdirectory $sim_complibdir] == 0] {
+ set sim_complibdir ""
+ }
+} else {
+ set sim_complibdir ""
+}
+if [expr ([string equal $simulator "XSim"] == 0) && ([string length $sim_complibdir] == 0)] {
+ puts "BUILDER: \[ERROR\]: Could not resolve the location for the compiled simulation libraries."
+ puts " Please build libraries for chosen simulator and set the env or"
+ puts " makefile variable SIM_COMPLIBDIR to point to the location."
+ exit 1
+}
+
+# ---------------------------------------
+# Vivado Commands
+# ---------------------------------------
+puts "BUILDER: Creating Vivado simulation project part $part_name"
+create_project -part $part_name -force $project_name/$project_name
+
+foreach src_file $design_srcs {
+ set src_ext [file extension $src_file ]
+ if [expr [lsearch {.vhd .vhdl} $src_ext] >= 0] {
+ puts "BUILDER: Adding VHDL : $src_file"
+ read_vhdl $src_file
+ } elseif [expr [lsearch {.v .vh} $src_ext] >= 0] {
+ puts "BUILDER: Adding Verilog : $src_file"
+ read_verilog $src_file
+ } elseif [expr [lsearch {.sv .svh} $src_ext] >= 0] {
+ puts "BUILDER: Adding SVerilog: $src_file"
+ read_verilog -sv $src_file
+ } elseif [expr [lsearch {.xdc} $src_ext] >= 0] {
+ puts "BUILDER: Adding XDC : $src_file"
+ read_xdc $src_file
+ } elseif [expr [lsearch {.xci} $src_ext] >= 0] {
+ puts "BUILDER: Adding IP : $src_file"
+ read_ip $src_file
+ } elseif [expr [lsearch {.ngc .edif} $src_ext] >= 0] {
+ puts "BUILDER: Adding Netlist : $src_file"
+ read_edif $src_file
+ } elseif [expr [lsearch {.bd} $src_ext] >= 0] {
+ puts "BUILDER: Adding Block Diagram: $src_file"
+ add_files -norecurse $src_file
+ } elseif [expr [lsearch {.bxml} $src_ext] >= 0] {
+ puts "BUILDER: Adding Block Diagram XML: $src_file"
+ add_files -norecurse $src_file
+ } else {
+ puts "BUILDER: \[WARNING\] File ignored!!!: $src_file"
+ }
+}
+
+foreach sim_src $sim_srcs {
+ puts "BUILDER: Adding Sim Src : $sim_src"
+ add_files -fileset $sim_fileset -norecurse $sim_src
+}
+
+foreach inc_src $inc_srcs {
+ puts "BUILDER: Adding Inc Src : $inc_src"
+ add_files -fileset $sim_fileset -norecurse $inc_src
+}
+
+# Simulator independent config
+set_property top $sim_top [get_filesets $sim_fileset]
+set_property default_lib xil_defaultlib [current_project]
+update_compile_order -fileset sim_1 -quiet
+
+# Select the simulator
+# WARNING: Do this first before setting simulator specific properties!
+set_property target_simulator $simulator [current_project]
+
+# Vivado quirk when passing options to external simulators
+if [expr [string equal $simulator "XSim"] == 1] {
+ set_property verilog_define "WORKING_DIR=\"$working_dir\"" [get_filesets $sim_fileset]
+} else {
+ set_property verilog_define "WORKING_DIR=$working_dir" [get_filesets $sim_fileset]
+}
+
+# XSim specific settings
+set_property xsim.simulate.runtime "${sim_runtime}us" -objects [get_filesets $sim_fileset]
+set_property xsim.elaborate.debug_level "all" -objects [get_filesets $sim_fileset]
+set_property xsim.elaborate.unifast $sim_fast -objects [get_filesets $sim_fileset]
+# Set default timescale to prevent bogus warnings
+set_property xsim.elaborate.xelab.more_options -value {-timescale 1ns/1ns} -objects [get_filesets $sim_fileset]
+
+# Modelsim specific settings
+if [expr [string equal $simulator "Modelsim"] == 1] {
+ set sim_64bit $::env(VIV_SIM_64BIT)
+
+ set_property compxlib.modelsim_compiled_library_dir $sim_complibdir [current_project]
+ # Does not work yet (as of Vivado 2015.2), but will be useful for 32-bit support
+ # See: http://www.xilinx.com/support/answers/62210.html
+ set_property modelsim.64bit $sim_64bit -objects [get_filesets $sim_fileset]
+ set_property modelsim.simulate.runtime "${sim_runtime}ns" -objects [get_filesets $sim_fileset]
+ set_property modelsim.elaborate.acc "true" -objects [get_filesets $sim_fileset]
+ set_property modelsim.simulate.log_all_signals "true" -objects [get_filesets $sim_fileset]
+ set_property modelsim.simulate.vsim.more_options -value "-c" -objects [get_filesets $sim_fileset]
+ set_property modelsim.elaborate.unifast $sim_fast -objects [get_filesets $sim_fileset]
+ if [info exists ::env(VIV_SIM_USER_DO) ] {
+ set_property modelsim.simulate.custom_udo -value "$::env(VIV_SIM_USER_DO)" -objects [get_filesets $sim_fileset]
+ }
+}
+
+# Launch simulation
+launch_simulation
+
+# Synthesize requested modules
+foreach synth_top "$::env(VIV_SYNTH_TOP)" {
+ set_property top $synth_top [current_fileset]
+ synth_design -mode out_of_context
+ # Perform a simple regex-based search for all clock signals and constrain
+ # them to 500 MHz for the timing report.
+ set clk_regexp "(?i)^(?!.*en.*).*(clk|clock).*"
+ foreach clk_inst [get_ports -regexp $clk_regexp] {
+ create_clock -name $clk_inst -period 2.0 [get_ports $clk_inst]
+ }
+ report_utilization -no_primitives -file ${working_dir}/${synth_top}_synth.rpt
+ report_timing_summary -setup -max_paths 3 -unique_pins -no_header -append -file ${working_dir}/${synth_top}_synth.rpt
+ write_checkpoint -force ${working_dir}/${synth_top}_synth.dcp
+}
+
+# Close project
+if [string equal $vivado_mode "batch"] {
+ puts "BUILDER: Closing project"
+ close_project
+} else {
+ puts "BUILDER: In GUI mode. Leaving project open."
+}
diff --git a/fpga/usrp3/tools/scripts/viv_strategies.tcl b/fpga/usrp3/tools/scripts/viv_strategies.tcl
new file mode 100644
index 000000000..cbf9ea913
--- /dev/null
+++ b/fpga/usrp3/tools/scripts/viv_strategies.tcl
@@ -0,0 +1,170 @@
+#
+# Copyright 2015 Ettus Research
+#
+
+# ---------------------------------------------------
+# Create namespace and initialize global parameters
+# ---------------------------------------------------
+namespace eval ::vivado_strategies {
+ # Export commands
+ namespace export \
+ get_preset \
+ implement_design \
+ check_strategy \
+ print_strategy
+
+ variable g_viv_version [version -short]
+}
+
+# ---------------------------------------------------
+# Return a preset strategy with the most commonly used options
+# ---------------------------------------------------
+proc ::vivado_strategies::get_impl_preset {preset} {
+ variable g_viv_version
+
+ set strategy [dict create]
+ switch -nocase $preset {
+ "Default" {
+ dict set strategy "opt_design.is_enabled" 1
+ dict set strategy "opt_design.directive" "Default"
+ dict set strategy "post_opt_power_opt_design.is_enabled" 0
+ dict set strategy "place_design.directive" "Default"
+ dict set strategy "post_place_power_opt_design.is_enabled" 0
+ dict set strategy "post_place_phys_opt_design.is_enabled" 0
+ dict set strategy "post_place_phys_opt_design.directive" "Default"
+ dict set strategy "route_design.directive" "Default"
+ dict set strategy "route_design.more_options" ""
+ dict set strategy "post_route_phys_opt_design.is_enabled" 0
+ dict set strategy "post_route_phys_opt_design.directive" "Default"
+ }
+ "Performance_Explore" {
+ dict set strategy "opt_design.is_enabled" 1
+ dict set strategy "opt_design.directive" "Explore"
+ dict set strategy "post_opt_power_opt_design.is_enabled" 0
+ dict set strategy "place_design.directive" "Explore"
+ dict set strategy "post_place_power_opt_design.is_enabled" 0
+ dict set strategy "post_place_phys_opt_design.is_enabled" 1
+ dict set strategy "post_place_phys_opt_design.directive" "Explore"
+ dict set strategy "route_design.directive" "Explore"
+ dict set strategy "route_design.more_options" ""
+ dict set strategy "post_route_phys_opt_design.is_enabled" 0
+ dict set strategy "post_route_phys_opt_design.directive" "Explore"
+ }
+ "Performance_ExplorePostRoutePhysOpt" {
+ dict set strategy "opt_design.is_enabled" 1
+ dict set strategy "opt_design.directive" "Explore"
+ dict set strategy "post_opt_power_opt_design.is_enabled" 0
+ dict set strategy "place_design.directive" "Explore"
+ dict set strategy "post_place_power_opt_design.is_enabled" 0
+ dict set strategy "post_place_phys_opt_design.is_enabled" 1
+ dict set strategy "post_place_phys_opt_design.directive" "Explore"
+ dict set strategy "route_design.directive" "Explore"
+ dict set strategy "route_design.more_options" "-tns_cleanup"
+ dict set strategy "post_route_phys_opt_design.is_enabled" 1
+ dict set strategy "post_route_phys_opt_design.directive" "Explore"
+ }
+ }
+ return $strategy
+}
+
+# ---------------------------------------------------
+# Execute the specified implementation strategy
+# ---------------------------------------------------
+proc ::vivado_strategies::implement_design {strategy} {
+ variable g_viv_version
+
+ # Check strategy for validity and print
+ vivado_strategies::check_strategy $strategy
+ puts "BUILDER: Running implementation strategy with:"
+ vivado_strategies::print_strategy $strategy
+
+ # Optimize the current netlist.
+ # This will perform the retarget, propconst, sweep and bram_power_opt optimizations by default.
+ if [dict get $strategy "opt_design.is_enabled"] {
+ set opt_dir [dict get $strategy "opt_design.directive"]
+ opt_design -directive $opt_dir
+ }
+
+ # Optimize dynamic power using intelligent clock gating after optimization
+ if [dict get $strategy "post_opt_power_opt_design.is_enabled"] {
+ power_opt_design
+ }
+
+ # Automatically place ports and leaf-level instances
+ set pla_dir [dict get $strategy "place_design.directive"]
+ place_design -directive $pla_dir
+
+ # Optimize dynamic power using intelligent clock gating after placement
+ if [dict get $strategy "post_place_power_opt_design.is_enabled"] {
+ power_opt_design
+ }
+
+ # Optimize the current placed netlist
+ if [dict get $strategy "post_place_phys_opt_design.is_enabled"] {
+ set pp_physopt_dir [dict get $strategy "post_place_phys_opt_design.directive"]
+ phys_opt_design -directive $pp_physopt_dir
+ }
+
+ # Route the current design
+ set rt_dir [dict get $strategy "route_design.directive"]
+ puts "BUILDER: Choosing routing directive: $rt_dir"
+ if {[dict get $strategy "route_design.more_options"] eq ""} {
+ route_design -directive $rt_dir
+ } else {
+ set rt_more [dict get $strategy "route_design.more_options"]
+ puts "BUILDER: Choosing additional routing options: $rt_more"
+ route_design -directive $rt_dir $rt_more
+ }
+
+ # Optimize the current routed netlist.
+ if [dict get $strategy "post_route_phys_opt_design.is_enabled"] {
+ set pr_physopt_dir [dict get $strategy "post_route_phys_opt_design.directive"]
+ phys_opt_design -directive $pr_physopt_dir
+ }
+}
+
+# ---------------------------------------------------
+# Sanity-check the specified strategy
+# ---------------------------------------------------
+proc ::vivado_strategies::check_strategy {strategy} {
+ variable g_viv_version
+
+ set strategy_options [dict keys $strategy]
+ set required_options {\
+ opt_design.is_enabled \
+ opt_design.directive \
+ post_opt_power_opt_design.is_enabled \
+ place_design.directive \
+ post_place_power_opt_design.is_enabled \
+ post_place_phys_opt_design.is_enabled \
+ post_place_phys_opt_design.directive \
+ route_design.directive \
+ post_route_phys_opt_design.is_enabled \
+ post_route_phys_opt_design.directive \
+ }
+
+ set invalid 0
+ foreach req $required_options {
+ if [expr [lsearch $strategy_options $req] < 0] {
+ puts "BUILDER: ERROR: Invalid strategy. Missing option $req"
+ set invalid 1
+ }
+ }
+ if $invalid {
+ error "Strategy check failed!"
+ }
+}
+
+# ---------------------------------------------------
+# Print strategy parameters to the console
+# ---------------------------------------------------
+proc ::vivado_strategies::print_strategy {strategy} {
+ variable g_viv_version
+
+ foreach opt [dict keys $strategy] {
+ set val [dict get $strategy $opt]
+ puts " * $opt = $val"
+ }
+}
+
+
diff --git a/fpga/usrp3/tools/scripts/viv_synth.tcl b/fpga/usrp3/tools/scripts/viv_synth.tcl
new file mode 100644
index 000000000..b93de3ca9
--- /dev/null
+++ b/fpga/usrp3/tools/scripts/viv_synth.tcl
@@ -0,0 +1,16 @@
+#
+# Copyright 2019 Ettus Research, a National Instruments Brand
+#
+
+source $::env(VIV_TOOLS_DIR)/scripts/viv_utils.tcl
+source $::env(VIV_TOOLS_DIR)/scripts/viv_strategies.tcl
+
+# STEP#1: Create project, add sources, refresh IP
+vivado_utils::initialize_project
+
+# STEP#2: Run synthesis
+vivado_utils::synthesize_design
+vivado_utils::generate_post_synth_reports
+
+# Cleanup
+vivado_utils::close_batch_project
diff --git a/fpga/usrp3/tools/scripts/viv_utils.tcl b/fpga/usrp3/tools/scripts/viv_utils.tcl
new file mode 100644
index 000000000..32c67e874
--- /dev/null
+++ b/fpga/usrp3/tools/scripts/viv_utils.tcl
@@ -0,0 +1,290 @@
+#
+# Copyright 2014-2015 Ettus Research
+#
+
+# ---------------------------------------------------
+# Create namespace and initialize global parameters
+# ---------------------------------------------------
+namespace eval ::vivado_utils {
+ # Export commands
+ namespace export \
+ initialize_project \
+ synthesize_design \
+ check_design \
+ generate_post_synth_reports \
+ generate_post_place_reports \
+ generate_post_route_reports \
+ write_implementation_outputs \
+ get_top_module \
+ get_part_name \
+ get_vivado_mode
+
+ # Required environment variables
+ variable g_tools_dir $::env(VIV_TOOLS_DIR)
+ variable g_top_module $::env(VIV_TOP_MODULE)
+ variable g_part_name $::env(VIV_PART_NAME)
+ variable g_output_dir $::env(VIV_OUTPUT_DIR)
+ variable g_source_files $::env(VIV_DESIGN_SRCS)
+ variable g_vivado_mode $::env(VIV_MODE)
+
+ # Optional environment variables
+ variable g_verilog_defs ""
+ if { [info exists ::env(VIV_VERILOG_DEFS) ] } {
+ set g_verilog_defs $::env(VIV_VERILOG_DEFS)
+ }
+ variable g_include_dirs ""
+ if { [info exists ::env(VIV_INCLUDE_DIRS) ] } {
+ set g_include_dirs $::env(VIV_INCLUDE_DIRS)
+ }
+}
+
+# ---------------------------------------------------
+# Create a new project in memory and add source files
+# ---------------------------------------------------
+proc ::vivado_utils::initialize_project { {save_to_disk 0} } {
+ variable g_top_module
+ variable g_part_name
+ variable g_output_dir
+ variable g_source_files
+
+ variable bd_files ""
+
+ file delete -force $g_output_dir/build.rpt
+
+ if {$save_to_disk == 1} {
+ puts "BUILDER: Creating Vivado project ${g_top_module}_project.xpr for part $g_part_name"
+ create_project -part $g_part_name ${g_top_module}_project
+ } else {
+ puts "BUILDER: Creating Vivado project in memory for part $g_part_name"
+ create_project -in_memory -part $g_part_name
+ }
+
+ foreach src_file $g_source_files {
+ set src_ext [file extension $src_file ]
+ if [expr [lsearch {.vhd .vhdl} $src_ext] >= 0] {
+ puts "BUILDER: Adding VHDL : $src_file"
+ read_vhdl -library work $src_file
+ } elseif [expr [lsearch {.v .vh .sv .svh} $src_ext] >= 0] {
+ puts "BUILDER: Adding Verilog : $src_file"
+ read_verilog $src_file
+ } elseif [expr [lsearch {.xdc} $src_ext] >= 0] {
+ puts "BUILDER: Adding XDC : $src_file"
+ read_xdc $src_file
+ } elseif [expr [lsearch {.xci} $src_ext] >= 0] {
+ puts "BUILDER: Adding IP : $src_file"
+ read_ip $src_file
+ set_property generate_synth_checkpoint true [get_files $src_file]
+ } elseif [expr [lsearch {.ngc .edif .edf} $src_ext] >= 0] {
+ puts "BUILDER: Adding Netlist : $src_file"
+ read_edif $src_file
+ } elseif [expr [lsearch {.bd} $src_ext] >= 0] {
+ puts "BUILDER: Adding Block Design to list (added after IP regeneration): $src_file"
+ append bd_files "$src_file "
+ } elseif [expr [lsearch {.bxml} $src_ext] >= 0] {
+ puts "BUILDER: Adding Block Design XML to list (added after IP regeneration): $src_file"
+ append bd_files "$src_file "
+ } elseif [expr [lsearch {.dat} $src_ext] >= 0] {
+ puts "BUILDER: Adding Data File : $src_file"
+ add_files $src_file
+ } else {
+ puts "BUILDER: \[WARNING\] File ignored!!!: $src_file"
+ }
+ }
+
+ # The 'synth_ip [get_ips *]' step causes builds in Windows to recompile various
+ # pieces of the IP. This is time-consuming and unnecessary behavior, thus is removed.
+ # These steps are redundant anyway since the IP builder performs both of them.
+ # puts "BUILDER: Refreshing IP"
+ # generate_target all [get_ips *]
+ # synth_ip [get_ips *]
+
+ #might seem silly, but we need to add the bd files after the ip regeneration.
+ foreach file $bd_files {
+ puts "BUILDER: Adding file from Block Design list: $file"
+ add_files -norecurse $file
+ }
+
+ puts "BUILDER: Setting $g_top_module as the top module"
+ set_property top $g_top_module [current_fileset]
+}
+
+# ---------------------------------------------------
+# Synthesize design (Shortcut for Vivado's synth_design)
+# ---------------------------------------------------
+proc ::vivado_utils::synthesize_design {args} {
+ variable g_top_module
+ variable g_part_name
+ variable g_verilog_defs
+ variable g_include_dirs
+
+ set vdef_args ""
+ foreach vdef $g_verilog_defs {
+ set vdef_args [concat $vdef_args "-verilog_define $vdef"]
+ }
+ set incdir_args ""
+ if { [string compare $g_include_dirs ""] != 0 } {
+ set incdir_args "-include_dirs $g_include_dirs"
+ }
+
+ set synth_cmd "synth_design -top $g_top_module -part $g_part_name"
+ set synth_cmd [concat $synth_cmd $vdef_args]
+ set synth_cmd [concat $synth_cmd $incdir_args]
+ set synth_cmd [concat $synth_cmd $args]
+ puts "BUILDER: Synthesizing design"
+ eval $synth_cmd
+}
+
+# ---------------------------------------------------
+# Check design (Shortcut for Vivado's synth_design -rtl)
+# ---------------------------------------------------
+proc ::vivado_utils::check_design {args} {
+ variable g_top_module
+ variable g_part_name
+ variable g_verilog_defs
+ variable g_include_dirs
+
+ set vdef_args ""
+ foreach vdef $g_verilog_defs {
+ set vdef_args [concat $vdef_args "-verilog_define $vdef"]
+ }
+ set incdir_args ""
+ if { [string compare $g_include_dirs ""] != 0 } {
+ set incdir_args "-include_dirs $g_include_dirs"
+ }
+
+ set synth_cmd "synth_design -top $g_top_module -part $g_part_name -rtl -rtl_skip_ip -rtl_skip_constraints"
+ set synth_cmd [concat $synth_cmd $vdef_args]
+ set synth_cmd [concat $synth_cmd $incdir_args]
+ set synth_cmd [concat $synth_cmd $args]
+ puts "BUILDER: Checking syntax and elaborating design"
+ eval $synth_cmd
+}
+
+# ---------------------------------------------------
+# Generate post synthesis reports and checkpoint
+# ---------------------------------------------------
+proc ::vivado_utils::generate_post_synth_reports {} {
+ variable g_output_dir
+
+ puts "BUILDER: Writing post-synthesis checkpoint"
+ write_checkpoint -force $g_output_dir/post_synth
+ puts "BUILDER: Writing post-synthesis reports"
+ report_utilization -file $g_output_dir/post_synth_util.rpt
+ report_utilization -hierarchical -file $g_output_dir/post_synth_util_hier.rpt
+ report_drc -ruledeck methodology_checks -file $g_output_dir/methodology.rpt
+ report_high_fanout_nets -file $g_output_dir/high_fanout_nets.rpt
+}
+
+# ---------------------------------------------------
+# Generate post placement reports and checkpoint
+# ---------------------------------------------------
+proc ::vivado_utils::generate_post_place_reports {} {
+ variable g_output_dir
+
+ puts "BUILDER: Writing post-placement checkpoint"
+ write_checkpoint -force $g_output_dir/post_place
+ puts "BUILDER: Writing post-placement reports"
+ report_clock_utilization -file $g_output_dir/clock_util.rpt
+ report_utilization -file $g_output_dir/post_place_util.rpt
+ report_utilization -hierarchical -file $g_output_dir/post_place_util_hier.rpt
+ report_timing -sort_by group -max_paths 5 -path_type summary -file $g_output_dir/post_place_timing.rpt
+}
+
+# ---------------------------------------------------
+# Generate post route reports and checkpoint
+# ---------------------------------------------------
+proc ::vivado_utils::generate_post_route_reports {} {
+ variable g_output_dir
+
+ puts "BUILDER: Writing post-route checkpoint"
+ write_checkpoint -force $g_output_dir/post_route
+ puts "BUILDER: Writing post-route reports"
+ if {[file exists "$g_output_dir/clock_util.rpt"] == 0} {
+ report_clock_utilization -file $g_output_dir/clock_util.rpt
+ }
+ report_timing_summary -file $g_output_dir/post_route_timing_summary.rpt
+ report_utilization -file $g_output_dir/post_route_util.rpt
+ report_utilization -hierarchical -file $g_output_dir/post_route_util_hier.rpt
+ report_power -file $g_output_dir/post_route_power.rpt
+ report_drc -file $g_output_dir/post_imp_drc.rpt
+ report_timing -sort_by group -max_paths 10 -path_type summary -file $g_output_dir/post_route_timing.rpt
+}
+
+# ---------------------------------------------------
+# Export implementation
+# ---------------------------------------------------
+proc ::vivado_utils::write_implementation_outputs { {byte_swap_bin 0} } {
+ variable g_output_dir
+ variable g_top_module
+ variable g_tools_dir
+
+ puts "BUILDER: Writing implementation netlist and XDC"
+ write_verilog -force $g_output_dir/${g_top_module}_impl_netlist.v
+ write_xdc -no_fixed_only -force $g_output_dir/${g_top_module}_impl.xdc
+ puts "BUILDER: Writing bitfile"
+ write_bitstream -force $g_output_dir/${g_top_module}.bit
+ puts "BUILDER: Writing config bitstream"
+ set binsize [expr [file size $g_output_dir/${g_top_module}.bit]/(1024*1024)]
+ set binsize_pow2 [expr {int(pow(2,ceil(log($binsize)/log(2))))}]
+ set bin_iface [expr $byte_swap_bin?"SMAPx32":"SMAPx8"]
+ write_cfgmem -force -quiet -interface $bin_iface -format BIN -size $binsize_pow2 -disablebitswap -loadbit "up 0x0 $g_output_dir/${g_top_module}.bit" $g_output_dir/${g_top_module}.bin
+ puts "BUILDER: Writing debug probes"
+ write_debug_probes -force $g_output_dir/${g_top_module}.ltx
+ puts "BUILDER: Writing export report"
+ report_utilization -omit_locs -file $g_output_dir/build.rpt
+ report_timing_summary -no_detailed_paths -file $g_output_dir/build.rpt -append
+ if {! [string match -nocase {*timing constraints are met*} [read [open $g_output_dir/build.rpt]]]} {
+ send_msg_id {Builder 0-0} error "The design did not satisfy timing constraints. (Implementation outputs were still generated)"
+ }
+}
+
+proc ::vivado_utils::write_netlist_outputs { {suffix ""} } {
+ variable g_output_dir
+ variable g_top_module
+
+ puts "BUILDER: Writing EDIF netlist and XDC"
+ set filename ${g_output_dir}/${g_top_module}
+ if { [expr [string length $suffix] > 0] } {
+ set filename ${filename}_${suffix}
+ }
+ write_edif -force ${filename}.edf
+ write_xdc -no_fixed_only -force ${filename}.xdc
+ puts "BUILDER: Writing export report"
+ report_utilization -omit_locs -file $g_output_dir/build.rpt
+ report_timing_summary -no_detailed_paths -file $g_output_dir/build.rpt -append
+ if {! [string match -nocase {*timing constraints are met*} [read [open $g_output_dir/build.rpt]]]} {
+ send_msg_id {Builder 0-0} error "The design did not meet all timing constraints. (Implementation outputs were still generated)"
+ }
+}
+
+# ---------------------------------------------------
+# Close project
+# ---------------------------------------------------
+proc ::vivado_utils::close_batch_project {} {
+ variable g_vivado_mode
+
+ if [string equal $g_vivado_mode "batch"] {
+ puts "BUILDER: Closing project"
+ close_project
+ } else {
+ puts "BUILDER: In GUI mode. Leaving project open."
+ }
+}
+
+# ---------------------------------------------------
+# Get state variables
+# ---------------------------------------------------
+proc ::vivado_utils::get_top_module {} {
+ variable g_top_module
+ return $g_top_module
+}
+
+proc ::vivado_utils::get_part_name {} {
+ variable g_part_name
+ return $g_part_name
+}
+
+proc ::vivado_utils::get_vivado_mode {} {
+ variable g_vivado_mode
+ return $g_vivado_mode
+}
diff --git a/fpga/usrp3/tools/scripts/xil_bitfile_parser.py b/fpga/usrp3/tools/scripts/xil_bitfile_parser.py
new file mode 100755
index 000000000..7201bde17
--- /dev/null
+++ b/fpga/usrp3/tools/scripts/xil_bitfile_parser.py
@@ -0,0 +1,84 @@
+#!/usr/bin/python
+
+import argparse
+import os, sys
+import struct
+import re
+
+# Parse command line options
+def get_options():
+ parser = argparse.ArgumentParser(description='Parser for the Xilinx FPGA Bitfile')
+ parser.add_argument("bitfile", help="Input bitfile path")
+ parser.add_argument("--bin_out", help="Output bin file path")
+ parser.add_argument('--flip', action='store_true', default=False, help='Flip 32-bit endianess')
+ parser.add_argument('--info', action='store_true', default=False, help='Print bitfile info')
+ args = parser.parse_args()
+ if (not os.path.isfile(args.bitfile)):
+ print('ERROR: Bitfile ' + args.bitfile + ' could not be accessed or is not a file.\n')
+ parser.print_help()
+ sys.exit(1)
+ return args
+
+short = struct.Struct('>H')
+ulong = struct.Struct('>I')
+KEYNAMES = {'a':'design_name', 'b':'part_name', 'c':'date', 'd':'time'}
+
+# Parse bitfile
+def parse_bitfile(bitfile_bytes):
+ header = dict()
+ ptr = 0
+ #Field 1
+ if short.unpack(bitfile_bytes[ptr:ptr+2])[0] == 9 and ulong.unpack(bitfile_bytes[ptr+2:ptr+6])[0] == 0x0ff00ff0:
+ #Headers
+ ptr += short.unpack(bitfile_bytes[ptr:ptr+2])[0] + 2
+ ptr += short.unpack(bitfile_bytes[ptr:ptr+2])[0] + 1
+ #Fields a-d
+ for keynum in range(0, 4):
+ key = bitfile_bytes[ptr]; ptr += 1
+ val_len = short.unpack(bitfile_bytes[ptr:ptr+2])[0]; ptr += 2
+ val = bitfile_bytes[ptr:ptr+val_len]; ptr += val_len
+ header[KEYNAMES[key]] = str(val).rstrip('\0')
+ #Field e
+ ptr += 1
+ length = ulong.unpack(bitfile_bytes[ptr:ptr+4])[0]; ptr += 4
+ header['bitstream_len'] = length
+ header['header_len'] = ptr
+ data = bitfile_bytes[ptr:ptr+length]
+ return (header, data)
+ else:
+ raise Exception('Bitfile header validation failed!')
+
+# Flip 32-bit endianess
+def flip32(data):
+ sl = struct.Struct('<I')
+ sb = struct.Struct('>I')
+ b = buffer(data)
+ d = bytearray(len(data))
+ for offset in xrange(0, len(data), 4):
+ sb.pack_into(d, offset, sl.unpack_from(b, offset)[0])
+ return d
+
+def main():
+ args = get_options();
+ with open(args.bitfile, 'rb') as bit_file:
+ # Parse bytes into a header map and data buffer
+ (header, data) = parse_bitfile(bit_file.read())
+ # Print bitfile info
+ if args.info:
+ m = re.search('(.+);UserID=(.+);COMPRESS=(.+);Version=(.+)', header['design_name'])
+ if m:
+ print 'Design Name: ' + m.group(1)
+ print 'User ID: ' + m.group(2)
+ print 'Compression: ' + m.group(3)
+ print 'Vivado Version: ' + m.group(4)
+ else:
+ print 'Design Name: ' + header['design_name']
+ print 'Part Name: ' + header['part_name']
+ print 'Datestamp: ' + header['date'] + ' ' + header['time']
+ print 'Bitstream Size: ' + str(header['bitstream_len'])
+ # Write a bin file
+ if args.bin_out:
+ open(args.bin_out, 'wb').write(flip32(data) if args.flip else data)
+
+if __name__ == '__main__':
+ main() \ No newline at end of file