fpga: tools: Remove uhd_image_builder
The image builder was replaced by rfnoc_image_builder, and has been obsolete since then. Original-commit: 676c3a37867f0e19476e9dc8495127bb3208a1a0
This commit is contained in:
@@ -1,22 +0,0 @@
|
|||||||
# 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:
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
# 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:
|
|
||||||
@@ -1,537 +0,0 @@
|
|||||||
#!/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())
|
|
||||||
@@ -1,656 +0,0 @@
|
|||||||
#!/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()
|
|
||||||
Reference in New Issue
Block a user