diff options
author | Martin Braun <martin.braun@ettus.com> | 2020-05-01 14:31:11 -0700 |
---|---|---|
committer | Aaron Rossetto <aaron.rossetto@ni.com> | 2020-05-05 08:04:50 -0500 |
commit | 6a488d32ebd128b0f320863723a6877113bde6dd (patch) | |
tree | 81a3bcb6d7d76d86c4406b62bc8ad17ac0b18f5d /host/utils | |
parent | 24527b39103b756581287f8e9e6082673645b1cd (diff) | |
download | uhd-6a488d32ebd128b0f320863723a6877113bde6dd.tar.gz uhd-6a488d32ebd128b0f320863723a6877113bde6dd.tar.bz2 uhd-6a488d32ebd128b0f320863723a6877113bde6dd.zip |
python: Move the rfnoc image builder module under the uhd module
Note: This commit changes nothing to the way the image builder is being
called. One can still run
rfnoc_image_builder [...]
as before. The difference is in the Python guts:
Where previously one had to do
import rfnoc
now the incantation becomes:
from uhd import imgbuilder
(Note that the submodule uhd.rfnoc already exists for wrapping the RFNoC
API into Python, hence the renaming from rfnoc to imgbuilder).
This is done for a variety of reasons:
- Now, there is only one and exactly one Python module for UHD that
contains all the things, as opposed to before where there were two.
- The rfnoc and uhd modules were installed in different ways (setuptools
vs. CMake); that is now harmonized. This also removes a lot of CMake
plumbing.
- It is not common to import the rfnoc module for anyone other than
rfnoc_image_builder
Diffstat (limited to 'host/utils')
20 files changed, 21 insertions, 1563 deletions
diff --git a/host/utils/CMakeLists.txt b/host/utils/CMakeLists.txt index 958bffb97..08ace6a0c 100644 --- a/host/utils/CMakeLists.txt +++ b/host/utils/CMakeLists.txt @@ -26,6 +26,18 @@ foreach(util_source ${util_runtime_sources}) UHD_INSTALL(TARGETS ${util_name} RUNTIME DESTINATION ${RUNTIME_DIR} COMPONENT utilities) endforeach(util_source) +set(CONFIG_PATH "${CMAKE_INSTALL_PREFIX}/${PKG_DATA_DIR}") +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/rfnoc_image_builder.py" + "${CMAKE_CURRENT_BINARY_DIR}/rfnoc_image_builder" +) +UHD_INSTALL(PROGRAMS + ${CMAKE_CURRENT_BINARY_DIR}/rfnoc_image_builder + RENAME rfnoc_image_builder + DESTINATION ${RUNTIME_DIR} + COMPONENT utilities +) + ######################################################################## # Utilities that get installed into the share path ######################################################################## @@ -105,6 +117,7 @@ UHD_INSTALL(PROGRAMS DESTINATION ${PKG_LIB_DIR}/utils COMPONENT utilities ) + if(LINUX) UHD_INSTALL(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/uhd_images_downloader.py @@ -158,38 +171,6 @@ if(ENABLE_USRP2) endif(ENABLE_USRP2) ######################################################################## -# Python rfnoc package -######################################################################## -set(RFNOC_PKG_FILES "") -add_subdirectory(rfnoc) - -set(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/build/timestamp") -set(SETUP_PY_IN "${CMAKE_CURRENT_SOURCE_DIR}/setup.py.in") -set(SETUP_PY "${CMAKE_CURRENT_BINARY_DIR}/setup.py") -set(CONFIG_PATH "${CMAKE_INSTALL_PREFIX}/${PKG_DATA_DIR}") - -# construct site-package folder from install prefix -execute_process(COMMAND ${PYTHON_EXECUTABLE} -c - "from __future__ import print_function; from distutils import sysconfig; print(sysconfig.get_python_lib(plat_specific=True, prefix='${CMAKE_INSTALL_PREFIX}'))" - OUTPUT_VARIABLE RFNOC_PACKAGE_DIR OUTPUT_STRIP_TRAILING_WHITESPACE -) - -configure_file(${SETUP_PY_IN} ${SETUP_PY}) -# rfnoc_image_builder uses RFNOC_PACKAGE_DIR to find python package location -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/bin/rfnoc_image_builder" "${CMAKE_CURRENT_BINARY_DIR}/bin/rfnoc_image_builder") - -add_custom_command(OUTPUT ${OUTPUT} - COMMENT "build rfnoc package" - COMMAND ${CMAKE_COMMAND} -DSOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}" -DBINARY_DIR="${CMAKE_CURRENT_BINARY_DIR}" -P ${CMAKE_CURRENT_SOURCE_DIR}/copy_python_module.cmake - COMMAND ${PYTHON_EXECUTABLE} ${SETUP_PY} -q build - COMMAND ${CMAKE_COMMAND} -E touch ${OUTPUT} - DEPENDS ${RFNOC_PKG_FILES}) -add_custom_target(rfnoc ALL DEPENDS ${OUTPUT}) - -get_filename_component(SETUP_FOLDER ${SETUP_PY} DIRECTORY) -install(CODE "execute_process(COMMAND ${PYTHON_EXECUTABLE} ${SETUP_PY} install --force --prefix ${CMAKE_INSTALL_PREFIX} WORKING_DIRECTORY ${SETUP_FOLDER})") - -######################################################################## # Other files that are not utilities or executables ######################################################################## if(WIN32) diff --git a/host/utils/rfnoc/CMakeLists.txt b/host/utils/rfnoc/CMakeLists.txt deleted file mode 100644 index baf65f7e7..000000000 --- a/host/utils/rfnoc/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -# -# Copyright 2019 Ettus Research, A National Instrument Brand -# -# SPDX-License-Identifier: GPL-3.0-or-later -# - -######################################################################## -# This file included, use CMake directory variables -######################################################################## -set(RFNOC_PKG_FILES ${RFNOC_PKG_FILES}) -set(RFNOC_PKG_TOP_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/__init__.py - ${CMAKE_CURRENT_SOURCE_DIR}/image_builder.py - ${CMAKE_CURRENT_SOURCE_DIR}/yaml_utils.py -) -list(APPEND RFNOC_PKG_FILES ${RFNOC_PKG_TOP_FILES}) -add_subdirectory(templates) -set(RFNOC_PKG_FILES ${RFNOC_PKG_FILES} PARENT_SCOPE) diff --git a/host/utils/rfnoc/__init__.py b/host/utils/rfnoc/__init__.py deleted file mode 100644 index e69de29bb..000000000 --- a/host/utils/rfnoc/__init__.py +++ /dev/null diff --git a/host/utils/rfnoc/image_builder.py b/host/utils/rfnoc/image_builder.py deleted file mode 100755 index 4f5f416d6..000000000 --- a/host/utils/rfnoc/image_builder.py +++ /dev/null @@ -1,847 +0,0 @@ -""" -Copyright 2019 Ettus Research, A National Instrument Brand - -SPDX-License-Identifier: GPL-3.0-or-later - -RFNoC image builder: All the algorithms required to turn either a YAML -description or a GRC file into an rfnoc_image_core.v file. -""" - -from collections import deque -from collections import OrderedDict - -import logging -import os -import re -import sys - -import six -import mako.lookup -import mako.template -from mako import exceptions -from ruamel import yaml - -### DATA ###################################################################### -# Directory under the FPGA repo where the device directories are -USRP3_TOP_DIR = os.path.join('usrp3', 'top') - -USRP3_LIB_RFNOC_DIR = os.path.join('usrp3', 'lib', 'rfnoc') - -# Subdirectory for the core YAML files -RFNOC_CORE_DIR = os.path.join('rfnoc', 'core') - -# Path to the system's bash executable -BASH_EXECUTABLE = '/bin/bash' # FIXME this should come from somewhere - -# Map device names to the corresponding directory under usrp3/top -DEVICE_DIR_MAP = { - 'x300': 'x300', - 'x310': 'x300', - 'e300': 'e300', - 'e310': 'e31x', - 'e320': 'e320', - 'n300': 'n3xx', - 'n310': 'n3xx', - 'n320': 'n3xx', -} - -# Picks the default make target per device -DEVICE_DEFAULTTARGET_MAP = { - 'x300': 'X300_HG', - 'x310': 'X310_HG', - 'e310': 'E310_SG3', - 'e320': 'E320_1G', - 'n300': 'N300_HG', - 'n310': 'N310_HG', - 'n320': 'N320_XG', -} - - -# Adapted from code found at -# https://stackoverflow.com/questions/5121931/ -# in-python-how-can-you-load-yaml-mappings-as-ordereddicts -# (Accessed 17 October 2019) -def ordered_load(stream, Loader=yaml.SafeLoader, object_pairs_hook=OrderedDict): - """ - In Python 3.5, element insertion order into dictionaries is not preserved. - This function uses an OrderedDict to read a YAML file, which does preserve order. - """ - class OrderedLoader(Loader): - pass - def construct_mapping(loader, node): - loader.flatten_mapping(node) - return object_pairs_hook(loader.construct_pairs(node)) - OrderedLoader.add_constructor( - yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, - construct_mapping) - return yaml.load(stream, OrderedLoader) - -def split(iterable, function): - """ - Split an iterable by condition. Matching items are returned in the first - deque of the returned tuple unmatched in the second - :param iterable: an iterable to split - :param function: an expression that returns True/False for iterable values - :return: 2-tuple with deque for matching/non-matching items - """ - dq_true = deque() - dq_false = deque() - - deque(((dq_true if function(item) else dq_false).append(item) - for item in iterable), maxlen=0) - - return dq_true, dq_false - - -def expand_io_port_desc(io_ports, signatures): - """ - Add a wires entry to each io port dictionary entry which contains a - complete list of wires for the specific port according to the information - in signature file. Each wire entry contains: - * fully resolved wire name (wire name as in signature or replaced name - with respect to regular expression if some is given, regular expression - should use back references to retain original wire name). - * width in bits - * direction as input/output depending on whether the port is a - master/broadcaster or slave/listener and the wire is described as from - or to master - :param io_ports: io port dictionary from yml configuration - :param signatures: signature description from yml configuration - :return: None - """ - for name, io_port in six.iteritems(io_ports): - wires = [] - for signature in signatures[io_port["type"]]["ports"]: - width = signature.get("width", 1) - wire_type = signature.get("type", None) - drive = io_port["drive"] - direction = {"master": {"from-master": "input ", "to-master": "output"}, - "slave": {"from-master": "output", "to-master": "input "}, - "broadcaster": {None: "input "}, - "listener": {None: "output"}}[drive][wire_type] - - signature_name = signature["name"] - if "rename" in io_port: - signature_name = re.sub(io_port["rename"]["pattern"], - io_port["rename"]["repl"], - signature_name, 1) - - wires.append({"direction": direction, - "width": width, - "name": signature_name}) - io_port["wires"] = wires - -# pylint: disable=too-few-public-methods -class IOConfig: - """ - Class containing configuration from a yml file. - - Each top level entry is translated into a class member variable. If the - configuration contains an io_ports section the ports get a wire list which - is derived from the signature file. This allows easier processing of IO - ports in the mako templates and failures from yml configuration files fail - in this script rather than during template processing which is easier to - track and debug. - """ - def __init__(self, config, signatures): - # read configuration from config dictionary - # TODO: Is this guaranteed ordered? - self.__dict__.update(**config) - if hasattr(self, "io_ports"): - expand_io_port_desc(getattr(self, "io_ports"), signatures) - - -class ImageBuilderConfig: - """ - Holds the complete image configuration settings. This includes - * the image configuration itself as it is passed to the script - * all noc block configurations found by the script - * device configuration information as found in the bsp.yml of the device - information passed to the script. - """ - # pylint: disable=too-many-instance-attributes - def __init__(self, config, blocks, device): - self.noc_blocks = OrderedDict() - self.stream_endpoints = OrderedDict() - self.connections = [] - self.clk_domains = [] - self.block_ports = OrderedDict() - self.io_ports = OrderedDict() - self.clocks = OrderedDict() - self.block_con = [] - self.io_port_con_ms = [] - self.io_port_con_bl = [] - self.clk_domain_con = [] - # read configuration from config dictionary - self.__dict__.update(**config) - self.blocks = blocks - self.device = device - self._update_sep_defaults() - self._set_indices() - self._collect_noc_ports() - self._collect_io_ports() - self._collect_clocks() - self.pick_connections() - self.pick_clk_domains() - - def _update_sep_defaults(self): - """ - Update any missing stream endpoint attributes with default values - """ - for sep in self.stream_endpoints: - if "num_data_i" not in self.stream_endpoints[sep]: - self.stream_endpoints[sep]["num_data_i"] = 1 - if "num_data_o" not in self.stream_endpoints[sep]: - self.stream_endpoints[sep]["num_data_o"] = 1 - - def _set_indices(self): - """ - Add an index for each port of each stream endpoint and noc block. - These indices are used to generate static_router.hex - """ - start = 1 - i = 0 - for i, sep in enumerate(self.stream_endpoints.values()): - sep["index"] = i + start - start = start + i + 1 - for i, block in enumerate(self.noc_blocks.values()): - block["index"] = start + i - - def _collect_noc_ports(self): - """ - Create lookup table for noc blocks. The key is a tuple of block - name, port name and flow direction. If any block port has num_ports > 1 - then unroll that port into multiple ports of the same name plus a - number to make its name unique. - """ - for name, block in six.iteritems(self.noc_blocks): - desc = self.blocks[block["block_desc"]] - # Update per-instance parameters - if not hasattr(desc, "parameters"): - setattr(desc, "parameters", {}) - if "parameters" not in block: - block["parameters"] = OrderedDict() - for key in block["parameters"].keys(): - if key not in desc.parameters: - logging.error("Unknown parameter %s for block %s", key, name) - del block["parameters"][key] - for param, value in six.iteritems(desc.parameters): - if param not in block["parameters"]: - block["parameters"][param] = value - # Generate list of block ports, adding 'index' to each port's dict - for direction in ("inputs", "outputs"): - index = 0 - for port_name, port_info in desc.data[direction].items(): - num_ports = 1 - if "num_ports" in port_info: - parameter = port_info["num_ports"] - num_ports = parameter - if parameter in block["parameters"]: - num_ports = block["parameters"][parameter] - # Make sure the parameter resolved to a number - if not isinstance(num_ports, int): - logging.error( - "'num_ports' of port '%s' on block '%s' " - "resolved to invalid value of '%s'", - port_name, name, str(num_ports)) - sys.exit(1) - if num_ports < 1 or num_ports > 64: - logging.error( - "'num_ports' of port '%s' on block '%s' " - "has invalid value '%s', must be in [1, 64]", - port_name, name, str(num_ports)) - sys.exit(1) - if "num_ports" in port_info: - # If num_ports was a variable in the YAML, unroll into - # multiple ports - for i in range(num_ports): - new_port_info = port_info.copy() - new_port_info['index'] = index - index = index + 1 - self.block_ports.update({(name, port_name + "_" \ - + str(i), direction[:-1]) : new_port_info}) - else: - port_info['index'] = index - self.block_ports.update( - {(name, port_name, direction[:-1]) : port_info}) - index = index + 1 - ports = self.stream_endpoints - for sep in self.stream_endpoints: - inputs = {(sep, "in%d" % port, "input") : - ports[sep] for port in range(ports[sep]["num_data_i"])} - self.block_ports.update(inputs) - outputs = {(sep, "out%d" % port, "output") : - ports[sep] for port in range(ports[sep]["num_data_o"])} - self.block_ports.update(outputs) - - def _collect_io_ports(self): - """ - Create lookup table for io ports. The key is a tuple of block name - (_device_ for io ports of the bsp), the io port name and flow - direction. - """ - for name, block in six.iteritems(self.noc_blocks): - desc = self.blocks[block["block_desc"]] - if hasattr(desc, "io_ports"): - self.io_ports.update({ - (name, io, desc.io_ports[io]["drive"]): - desc.io_ports[io] for io in desc.io_ports}) - self.io_ports.update({ - ("_device_", io, self.device.io_ports[io]["drive"]): - self.device.io_ports[io] for io in self.device.io_ports}) - - def _collect_clocks(self): - """ - Create lookup table for clocks. The key is a tuple of block name - (_device_ for clocks of the bsp), the clock name and flow - direction - """ - for name, block in six.iteritems(self.noc_blocks): - desc = self.blocks[block["block_desc"]] - if hasattr(desc, "clocks"): - self.clocks.update({ - (name, clk["name"]): clk for clk in desc.clocks}) - if hasattr(self.device, "clocks"): - self.clocks.update({ - ("_device_", clk["name"]): clk for clk in self.device.clocks}) - # Add the implied clocks for the BSP - self.clocks[("_device_", "rfnoc_ctrl")] = {"freq": '[]', "name": "rfnoc_ctrl"} - self.clocks[("_device_", "rfnoc_chdr")] = {"freq": '[]', "name": "rfnoc_chdr"} - - def pick_clk_domains(self): - """ - Filter clock domain list into a local list for easier access. - Remaining connection items are printed as error and execution is - aborted. Likewise, checks for unconnected clocks. - """ - (self.clk_domain_con, self.clk_domains) = split( - self.clk_domains, lambda con: - (con["srcblk"], con["srcport"]) in self.clocks and - (con["dstblk"], con["dstport"]) in self.clocks) - - # Check if there are unconnected clocks - connected = [(con["dstblk"], con["dstport"]) for con in self.clk_domain_con] - unconnected = [] - for clk in self.clocks: - if clk[0] != "_device_" and \ - clk[1] not in ["rfnoc_ctrl", "rfnoc_chdr"] and \ - clk not in connected: - unconnected.append(clk) - if unconnected: - logging.error("%d unresolved clk domain(s)", len(unconnected)) - for clk in unconnected: - logging.error(" %s:%s", clk[0], clk[1]) - logging.error("Please specify the clock(s) to connect") - sys.exit(1) - - if self.clk_domains: - logging.error("%d Unresolved clk domain(s)", len(self.clk_domains)) - - for connection in self.clk_domains: - logging.error(" (%s-%s -> %s-%s)", - connection["srcblk"], connection["srcport"], - connection["dstblk"], connection["dstport"]) - logging.error("Source or destination domain not found") - sys.exit(1) - - def pick_connections(self): - """ - Sort connection list into three local lists for - * input => output (block port to block port) - * master => slave (io port to io port) - * broadcaster => listener (io port to io port) - Remaining connection items are printed as error and execution is - aborted. Possible reasons are - * undeclared block or io port - * connection direction wrong (e.g. output => input) - * mixed connection type (e.g. master => listener) - """ - block_types = lambda type: filter(lambda key: key[2] == type, self.block_ports) - io_types = lambda type: filter(lambda key: key[2] == type, self.io_ports) - (self.block_con, self.connections) = split( - self.connections, lambda con: - (con["srcblk"], con["srcport"], "output") in block_types("output") and - (con["dstblk"], con["dstport"], "input") in block_types("input")) - (self.io_port_con_ms, self.connections) = split( - self.connections, lambda con: - (con["srcblk"], con["srcport"], "master") in io_types("master") and - (con["dstblk"], con["dstport"], "slave") in io_types("slave")) - (self.io_port_con_bl, self.connections) = split( - self.connections, lambda con: - (con["srcblk"], con["srcport"], "broadcaster") in io_types("broadcaster") and - (con["dstblk"], con["dstport"], "listener") in io_types("listener")) - - if self.connections: - logging.error("%d Unresolved connection(s)", len(self.connections)) - - for connection in self.connections: - logging.error(" (%s-%s -> %s-%s)", - connection["srcblk"], connection["srcport"], - connection["dstblk"], connection["dstport"]) - logging.debug(" Make sure block ports are connected output " - "(src) to input (dst)") - logging.debug(" Available block ports for connections:") - for block in self.block_ports: - logging.debug(" %s", (block,)) - logging.debug(" Make sure io ports are connected master " - "(src) to slave (dst)") - logging.debug(" or broadcaster " - "(src) to listener (dst)") - logging.debug(" Available io ports for connections:") - for io_port in self.io_ports: - logging.info(" %s", (io_port,)) - sys.exit(1) - -def load_config(filename): - """ - Loads yml configuration from filename. - - Configuration files are searched in folder returned by get_get_config_path. - This method logs error and exits on IO failure - - :param filename: yml configuration to load - :return: IO signatures as dictionary - """ - dirname, basename = os.path.split(filename) - try: - with open(filename) as stream: - logging.info( - "Using %s from %s.", basename, os.path.normpath(dirname)) - config = ordered_load(stream) - return config - except IOError: - logging.error("%s misses %s", os.path.normpath(dirname), basename) - sys.exit(1) - -def device_config(config_path, device): - """ - Load device config from bsp.yml - - Location of bsp.yml is derived from the device chosen in the arguments - - :param config_path: location of core configuration files - :param device: device to build for - :return: device configuration as dictionary - """ - return load_config(os.path.join(config_path, "%s_bsp.yml" % device.lower())) - - -def io_signatures(config_path): - """ - Load IO signatures from io_signatures.yml - - :param config_path: location of core configuration files - :return: IO signatures as dictionary - """ - return load_config(os.path.join(config_path, "io_signatures.yml")) - - -def read_grc_block_configs(path): - """ - Reads RFNoC config block used by Gnuradio Companion - :param path: location of grc block configuration files - :return: dictionary of block (id mapped to description) - """ - result = {} - - for root, dirs, names in os.walk(path): - for name in names: - if re.match(r".*\.block\.yml", name): - with open (os.path.join(root, name)) as stream: - config = ordered_load(stream) - result[config["id"]] = config - - return result - - -def convert_to_image_config(grc, grc_config_path): - """ - Converts Gnuradio Companion grc into image configuration. - :param grc: - :return: image configuration as it would be returned by image_config(args) - """ - grc_blocks = read_grc_block_configs(grc_config_path) - #filter all blocks that have no block representation - all = {item["name"]: item for item in grc["blocks"]} - seps = {item["name"]: item for item in grc["blocks"] if item["parameters"]["type"] == 'sep'} - blocks = {item["name"]: item for item in grc["blocks"] if item["parameters"]["type"] == 'block'} - device = [item for item in grc["blocks"] if item["parameters"]["type"] == 'device'] - if len(device) == 1: - device = device[0] - else: - logging.error("More than one or no device found in grc file") - return None - - result = { - "schema": "rfnoc_imagebuilder", - "copyright": "Ettus Research, A National Instruments Brand", - "license": "SPDX-License-Identifier: LGPL-3.0-or-later", - "version": 1.0, - "rfnoc_version": 1.0} - # for param in [item for item in grc["blocks"] if item["id"] == "parameter"]: - # result[param["name"]] = { - # "str": lambda value: str, - # "": lambda value: str, - # "complex": str, - # "intx": int, - # "long": int, - # }[param["parameters"]["type"]](param["parameters"]["value"]) - - result["stream_endpoints"] = {} - for sep in seps.values(): - result["stream_endpoints"][sep["name"]] = { "ctrl": bool(sep["parameters"]["ctrl"]), - "data": bool(sep["parameters"]["data"]), - "buff_size": int(sep["parameters"]["buff_size"]) } - - result["noc_blocks"] = {} - for block in blocks.values(): - result["noc_blocks"][block["name"]] = { "block_desc": block["parameters"]["desc"] } - - device_clocks = {port["id"]: port for port in grc_blocks[device['id']]["outputs"] if port["dtype"] == "message"} - - for connection in grc["connections"]: - if connection[0] == device["name"]: - connection[0] = "_device_" - if connection[2] == device["name"]: - connection[2] = "_device_" - device["name"] = "_device_" - - (clk_connections, connections) = split( - grc["connections"], lambda con: - con[0] == device["name"] and con[1] in device_clocks) - - result["connections"] = [] - for connection in connections: - result["connections"].append( - {"srcblk": connection[0], - "srcport": connection[1], - "dstblk": connection[2], - "dstport": connection[3]} - ) - - result["clk_domains"] = [] - for connection in clk_connections: - result["clk_domains"].append( - {"srcblk": connection[0], - "srcport": connection[1], - "dstblk": connection[2], - "dstport": connection[3]} - ) - - return result - - -def collect_module_paths(config_path, include_paths): - """ - Create a list of directories that contain noc block configuration files. - :param config_path: root path holding configuration files - :return: list of noc block directories - """ - # rfnoc blocks - result = [os.path.join(config_path, 'rfnoc', 'blocks')] + \ - [os.path.join(x, 'blocks') for x in include_paths] - return result - - -def read_block_descriptions(signatures, *paths): - """ - Recursive search all pathes for block definitions. - :param signatures: signature passed to IOConfig initialization - :param paths: paths to be searched - :return: dictionary of noc blocks. Key is filename of the block, value - is an IOConfig object - """ - blocks = OrderedDict() - for path in paths: - for root, dirs, files, in os.walk(path): - for filename in files: - if re.match(r".*\.yml$", filename): - with open(os.path.join(root, filename)) as stream: - block = ordered_load(stream) - if "schema" in block and \ - block["schema"] == "rfnoc_modtool_args": - logging.info("Adding block description from " - "%s (%s).", filename, os.path.normpath(root)) - blocks[filename] = IOConfig(block, signatures) - for dirname in dirs: - blocks.update(read_block_descriptions( - os.path.join(root, dirname))) - return blocks - - -def write_edges(config, destination): - """ - Write edges description files. The file is a simple text file. Each line - contains 8 hexadecimal digits. - First line is the number of following entries. - Starting with the second line each line describes a port to port connection - The 32 bit value has 16 bit for each node where the node is represented by - 10 bit for the block number and 6 bit for the port number. - :param config: ImageBuilderConfig derived from script parameter - :param destination: folder to write the file (next to device top level files - :return: None - """ - logging.info("Writing static routing table to %s", destination) - with open(destination, "w") as stream: - stream.write("%08X\n" % len(config.block_con)) - for connection in config.block_con: - if connection["srcblk"] in config.stream_endpoints: - sep = config.stream_endpoints[connection["srcblk"]] - index_match = re.match(r"out(\d)", connection["srcport"]) - if not index_match: - logging.error("Port %s is invalid on endpoint %s", - connection["srcport"], connection["srcblk"]) - port_index = int(index_match.group(1)) - # Verify index < num_data_o - if port_index >= sep["num_data_o"]: - logging.error("Port %s exceeds num_data_o for endpoint %s", - connection["srcport"], connection["srcblk"]) - src = (sep["index"], port_index) - else: - key = (connection["srcblk"], connection["srcport"], "output") - src = (config.noc_blocks[connection["srcblk"]]["index"], - config.block_ports[key]["index"]) - if connection["dstblk"] in config.stream_endpoints: - sep = config.stream_endpoints[connection["dstblk"]] - index_match = re.match(r"in(\d)", connection["dstport"]) - if not index_match: - logging.error("Port %s is invalid on endpoint %s", - connection["dstport"], connection["dstblk"]) - # Verify index < num_data_i - port_index = int(index_match.group(1)) - if port_index >= sep["num_data_i"]: - logging.error("Port %s exceeds num_data_i for endpoint %s", - connection["dstport"], connection["dstblk"]) - dst = (sep["index"], port_index) - else: - key = (connection["dstblk"], connection["dstport"], "input") - dst = (config.noc_blocks[connection["dstblk"]]["index"], - config.block_ports[key]["index"]) - logging.debug("%s-%s (%d,%d) => %s-%s (%d,%d)", - connection["srcblk"], connection["srcport"], - src[0], src[1], - connection["dstblk"], connection["dstport"], - dst[0], dst[1]) - stream.write("%08x\n" % - ((((src[0] << 6) | src[1]) << 16) | - ((dst[0] << 6) | dst[1]))) - - -def write_verilog(config, destination, source, source_hash): - """ - Generates rfnoc_image_core.v file for the device. - - Mako templates from local template folder are used to generate the image - core file. The template engine does not do any computation on the script - parameter. Instead all necessary dependencies are resolved in this script - to enforce early failure which is easier to track than errors in the - template engine. - :param config: ImageBuilderConfig derived from script parameter - :param destination: Filepath to write to - :return: None - """ - template_dir = os.path.join(os.path.dirname(__file__), "templates") - lookup = mako.lookup.TemplateLookup(directories=[template_dir]) - tpl_filename = os.path.join(template_dir, "rfnoc_image_core.v.mako") - tpl = mako.template.Template( - filename=tpl_filename, - lookup=lookup, - strict_undefined=True) - - try: - block = tpl.render(**{ - "config": config, - "source": source, - "source_hash": source_hash, - }) - except: - print(exceptions.text_error_template().render()) - sys.exit(1) - - logging.info("Writing image core to %s", destination) - with open(destination, "w") as image_core_file: - image_core_file.write(block) - - -def write_build_env(): - """ - # TODO update Makefile entries according to used blocks - :return: - """ - - -def build(fpga_path, device, image_core_path, edge_file, **args): - """ - Call FPGA toolchain to actually build the image - - :param fpga_path: A path that holds the FPGA IP sources. - :param device: The device to build for. - :param **args: Additional options - target: The target to build (leave empty for default). - clean_all: passed to Makefile - GUI: passed to Makefile - source: The source of the build (YAML or GRC file path) - include_paths: List of paths to OOT modules - extra_makefile_srcs: An additional list of paths to modules - that don't follow the OOT module layout. These paths must - point directly to a Makefile.srcs file. - :return: exit value of build process - """ - ret_val = 0 - cwd = os.path.dirname(__file__) - build_dir = os.path.join(get_top_path(fpga_path), target_dir(device)) - if not os.path.isdir(build_dir): - logging.error("Not a valid directory: %s", build_dir) - return 1 - makefile_src_paths = [ - os.path.join( - os.path.abspath(os.path.normpath(x)), - os.path.join('fpga', 'Makefile.srcs')) - for x in args.get("include_paths", []) - ] + args.get("extra_makefile_srcs", []) - logging.debug("Temporarily changing working directory to %s", build_dir) - os.chdir(build_dir) - make_cmd = ". ./setupenv.sh " - if "clean_all" in args and args["clean_all"]: - make_cmd = make_cmd + "&& make cleanall " - target = args["target"] if "target" in args else "" - make_cmd = make_cmd + "&& make " + default_target(device, target) - make_cmd += " IMAGE_CORE={} EDGE_FILE={}".format(image_core_path, - edge_file) - if makefile_src_paths: - make_cmd += " RFNOC_OOT_MAKEFILE_SRCS=" + "\\ ".join(makefile_src_paths) - if "GUI" in args and args["GUI"]: - make_cmd = make_cmd + " GUI=1" - logging.info("Launching build with the following settings:") - logging.info(" * Build Directory: %s", build_dir) - logging.info(" * Target: %s", target) - logging.info(" * Image Core File: %s", image_core_path) - logging.info(" * Edge Table File: %s", edge_file) - # Wrap it into a bash call: - make_cmd = '{bash} -c "{cmd}"'.format(bash=BASH_EXECUTABLE, cmd=make_cmd) - logging.debug("Executing the following command: %s", make_cmd) - ret_val = os.system(make_cmd) - os.chdir(cwd) - return ret_val - - -def target_dir(device): - """ - Target directory derived from chosen device - :param device: device to build for - :return: target directory (relative path) - """ - if not device.lower() in DEVICE_DIR_MAP: - logging.error("Unsupported device %s. Supported devices are %s", - device, DEVICE_DIR_MAP.keys()) - sys.exit(1) - return DEVICE_DIR_MAP[device.lower()] - -def default_target(device, target): - """ - If no target specified, selects the default building target based on the - targeted device - """ - if target is None: - return DEVICE_DEFAULTTARGET_MAP.get(device.lower()) - return target - -def get_top_path(fpga_root): - """ - returns the path where FPGA top level sources reside - """ - return os.path.join(fpga_root, USRP3_TOP_DIR) - -def get_core_config_path(config_path): - """ - returns the path where core configuration files are stored - """ - return os.path.join(config_path, RFNOC_CORE_DIR) - -def generate_image_core_path(output_path, source): - """ - Creates the path where the image core file gets to be stored. - - output_path: If not None, this is returned - source: Otherwise, this path is returned with a .v argument - """ - if output_path is not None: - return output_path - source = os.path.splitext(os.path.normpath(source))[0] - return source + '.v' - -def generate_edge_file_path(output_path, device, source): - """ - Creates a valid path for the edge file to get stored. - """ - if output_path is not None: - return output_path - edge_path = os.path.split(os.path.abspath(os.path.normpath(source)))[0] - return os.path.join(edge_path, "{}_static_router.hex".format(device)) - - -def build_image(config, fpga_path, config_path, device, **args): - """ - Generate image dependent Verilog code and trigger Xilinx toolchain, if - requested. - - :param config: A dictionary containing the image configuration options. - This must obey the rfnoc_imagebuilder_args schema. - :param fpga_path: A path that holds the FPGA IP sources. - :param device: The device to build for. - :param **args: Additional options including - target: The target to build (leave empty for default). - generate_only: Do not build the code after generation. - clean_all: passed to Makefile - GUI: passed to Makefile - include_paths: Paths to additional blocks - :return: Exit result of build process or 0 if generate-only is given. - """ - logging.info("Selected device %s", device) - image_core_path = \ - generate_image_core_path(args.get('output_path'), args.get('source')) - edge_file = \ - generate_edge_file_path( - args.get('router_hex_path'), device, args.get('source')) - - logging.debug("Image core output file: %s", image_core_path) - logging.debug("Edge output file: %s", edge_file) - - core_config_path = get_core_config_path(config_path) - signatures_conf = io_signatures(core_config_path) - device_conf = IOConfig(device_config(core_config_path, device), - signatures_conf) - - block_paths = collect_module_paths(config_path, args.get('include_paths', [])) - logging.debug("Looking for block descriptors in:") - for path in block_paths: - logging.debug(" %s", os.path.normpath(path)) - blocks = read_block_descriptions(signatures_conf, *block_paths) - - builder_conf = ImageBuilderConfig(config, blocks, device_conf) - - write_edges(builder_conf, edge_file) - write_verilog( - builder_conf, - image_core_path, - source=args.get('source'), - source_hash=args.get('source_hash')) - write_build_env() - - if "generate_only" in args and args["generate_only"]: - logging.info("Skip build (generate only option given)") - return 0 - - # Check if the YAML files require additional Makefile.srcs - extra_makefile_srcs = set() - for block_info in builder_conf.noc_blocks.values(): - block_desc = blocks[block_info['block_desc']] - if hasattr(block_desc, 'makefile_srcs'): - extra_path = mako.template.Template(block_desc.makefile_srcs).render(**{ - "fpga_lib_dir": os.path.join(fpga_path, USRP3_LIB_RFNOC_DIR), - }) - if extra_path not in extra_makefile_srcs: - logging.debug("Adding additional Makefile.srcs path: %s", extra_path) - extra_makefile_srcs.add(extra_path) - args['extra_makefile_srcs'] = list(extra_makefile_srcs) - return build(fpga_path, device, image_core_path, edge_file, **args) diff --git a/host/utils/rfnoc/templates/CMakeLists.txt b/host/utils/rfnoc/templates/CMakeLists.txt deleted file mode 100644 index 88c5e3bd4..000000000 --- a/host/utils/rfnoc/templates/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -# -# Copyright 2019 Ettus Research, A National Instrument Brand -# -# SPDX-License-Identifier: GPL-3.0-or-later -# - -######################################################################## -# This file included, use CMake directory variables -######################################################################## -set(RFNOC_PKG_FILES ${RFNOC_PKG_FILES}) -set(RFNOC_PKG_TOP_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/rfnoc_image_core.v.mako -) -list(APPEND RFNOC_PKG_FILES ${RFNOC_PKG_TOP_FILES}) -add_subdirectory(modules) -set(RFNOC_PKG_FILES ${RFNOC_PKG_FILES} PARENT_SCOPE) diff --git a/host/utils/rfnoc/templates/modules/CMakeLists.txt b/host/utils/rfnoc/templates/modules/CMakeLists.txt deleted file mode 100644 index ee69b63b5..000000000 --- a/host/utils/rfnoc/templates/modules/CMakeLists.txt +++ /dev/null @@ -1,24 +0,0 @@ -# -# Copyright 2017 Ettus Research, National Instruments Company -# -# SPDX-License-Identifier: GPL-3.0-or-later -# - -######################################################################## -# This file included, use CMake directory variables -######################################################################## -set(RFNOC_PKG_FILES ${RFNOC_PKG_FILES}) -set(RFNOC_PKG_TOP_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/chdr_xb_sep_transport.v.mako - ${CMAKE_CURRENT_SOURCE_DIR}/connect_io_ports.v.mako - ${CMAKE_CURRENT_SOURCE_DIR}/ctrl_crossbar.v.mako - ${CMAKE_CURRENT_SOURCE_DIR}/device_io_ports.v.mako - ${CMAKE_CURRENT_SOURCE_DIR}/device_transport.v.mako - ${CMAKE_CURRENT_SOURCE_DIR}/drive_unused_ports.v.mako - ${CMAKE_CURRENT_SOURCE_DIR}/rfnoc_block.v.mako - ${CMAKE_CURRENT_SOURCE_DIR}/sep_xb_wires.v.mako - ${CMAKE_CURRENT_SOURCE_DIR}/static_router.v.mako - ${CMAKE_CURRENT_SOURCE_DIR}/stream_endpoints.v.mako -) -list(APPEND RFNOC_PKG_FILES ${RFNOC_PKG_TOP_FILES}) -set(RFNOC_PKG_FILES ${RFNOC_PKG_FILES} PARENT_SCOPE) diff --git a/host/utils/rfnoc/templates/modules/chdr_xb_sep_transport.v.mako b/host/utils/rfnoc/templates/modules/chdr_xb_sep_transport.v.mako deleted file mode 100644 index 0862a1ac1..000000000 --- a/host/utils/rfnoc/templates/modules/chdr_xb_sep_transport.v.mako +++ /dev/null @@ -1,23 +0,0 @@ -<%page args="seps, transports"/>\ -\ -<% - import re - sep2xb = "" - xb2sep = "" - for sep in reversed(list(seps.keys())): - sep2xb += "%s_to_xb_wire, " % sep - xb2sep += "xb_to_%s_wire, " % sep - for transport in reversed(transports): - sep2xb += "s_%s_wire, " % transport["name"] - xb2sep += "m_%s_wire, " % transport["name"] - sep2xb = sep2xb[:-2] - xb2sep = xb2sep[:-2] -%>\ - .s_axis_tdata ({${re.sub("wire", "tdata", sep2xb)}}), - .s_axis_tlast ({${re.sub("wire", "tlast", sep2xb)}}), - .s_axis_tvalid ({${re.sub("wire", "tvalid", sep2xb)}}), - .s_axis_tready ({${re.sub("wire", "tready", sep2xb)}}), - .m_axis_tdata ({${re.sub("wire", "tdata", xb2sep)}}), - .m_axis_tlast ({${re.sub("wire", "tlast", xb2sep)}}), - .m_axis_tvalid ({${re.sub("wire", "tvalid", xb2sep)}}), - .m_axis_tready ({${re.sub("wire", "tready", xb2sep)}}), diff --git a/host/utils/rfnoc/templates/modules/connect_clk_domains.v.mako b/host/utils/rfnoc/templates/modules/connect_clk_domains.v.mako deleted file mode 100644 index df055645c..000000000 --- a/host/utils/rfnoc/templates/modules/connect_clk_domains.v.mako +++ /dev/null @@ -1,12 +0,0 @@ -<%page args="connections, clocks"/>\ -\ -%for connection in connections: -<% - src_name = connection["srcblk"] # Should always be "_device_" - src = clocks[(src_name, connection["srcport"])] - dst_name = connection["dstblk"] - dst = clocks[(dst_name, connection["dstport"])] -%>\ - assign ${dst_name}_${dst["name"]}_clk = ${src["name"]}_clk; -%endfor - diff --git a/host/utils/rfnoc/templates/modules/connect_io_ports.v.mako b/host/utils/rfnoc/templates/modules/connect_io_ports.v.mako deleted file mode 100644 index c37697d88..000000000 --- a/host/utils/rfnoc/templates/modules/connect_io_ports.v.mako +++ /dev/null @@ -1,20 +0,0 @@ -<%page args="connections, io_ports, names"/>\ -\ -%for connection in connections: -<% - src_name = connection["srcblk"] - src = io_ports[(src_name, connection["srcport"], names[0])] - dst_name = connection["dstblk"] - dst = io_ports[(dst_name, connection["dstport"], names[1])] -%>\ - %for src_wire, dst_wire in zip(src["wires"], dst["wires"]): -<% - swire = src_wire["name"] if src_name == "_device_" else "%s_%s" % (src_name, src_wire["name"]) - dwire = dst_wire["name"] if dst_name == "_device_" else "%s_%s" % (dst_name, dst_wire["name"]) - if src_wire["direction"] == "output": - swire, dwire = dwire, swire -%>\ - assign ${dwire} = ${swire}; - %endfor - -%endfor diff --git a/host/utils/rfnoc/templates/modules/ctrl_crossbar.v.mako b/host/utils/rfnoc/templates/modules/ctrl_crossbar.v.mako deleted file mode 100644 index 5872e270e..000000000 --- a/host/utils/rfnoc/templates/modules/ctrl_crossbar.v.mako +++ /dev/null @@ -1,39 +0,0 @@ -<%page args="seps, blocks"/>\ -\ -<% - import re - axisstr = "" - for block in reversed(list(blocks.keys())): - axisstr += "{0}_%s_ctrl_{1}, " % block - for sep in reversed(list(seps.keys())): - axisstr += "{0}_%s_ctrl_{1}, " % sep - axisstr += "{0}_core_ctrl_{1}" -%>\ -%for block in blocks: - wire [31:0] m_${block}_ctrl_tdata , s_${block}_ctrl_tdata ; - wire m_${block}_ctrl_tlast , s_${block}_ctrl_tlast ; - wire m_${block}_ctrl_tvalid, s_${block}_ctrl_tvalid; - wire m_${block}_ctrl_tready, s_${block}_ctrl_tready; -%endfor - - axis_ctrl_crossbar_nxn #( - .WIDTH (32), - .NPORTS (${len(seps) + len(blocks) + 1}), - .TOPOLOGY ("TORUS"), - .INGRESS_BUFF_SIZE(5), - .ROUTER_BUFF_SIZE (5), - .ROUTING_ALLOC ("WORMHOLE"), - .SWITCH_ALLOC ("PRIO") - ) ctrl_xb_i ( - .clk (rfnoc_ctrl_clk), - .reset (rfnoc_ctrl_rst), - .s_axis_tdata ({${axisstr.format("m", "tdata ")}}), - .s_axis_tvalid ({${axisstr.format("m", "tvalid")}}), - .s_axis_tlast ({${axisstr.format("m", "tlast ")}}), - .s_axis_tready ({${axisstr.format("m", "tready")}}), - .m_axis_tdata ({${axisstr.format("s", "tdata ")}}), - .m_axis_tvalid ({${axisstr.format("s", "tvalid")}}), - .m_axis_tlast ({${axisstr.format("s", "tlast ")}}), - .m_axis_tready ({${axisstr.format("s", "tready")}}), - .deadlock_detected() - ); diff --git a/host/utils/rfnoc/templates/modules/device_io_ports.v.mako b/host/utils/rfnoc/templates/modules/device_io_ports.v.mako deleted file mode 100644 index abfb86c98..000000000 --- a/host/utils/rfnoc/templates/modules/device_io_ports.v.mako +++ /dev/null @@ -1,9 +0,0 @@ -<%page args="io_ports"/>\ -<%import six%>\ -//// IO ports ////////////////////////////////// -%for name, io_port in six.iteritems(io_ports): -// ${name} - %for wire in io_port["wires"]: - ${wire["direction"]} wire [${"%3d" % wire["width"]}-1:0] ${wire["name"]}, - %endfor -%endfor diff --git a/host/utils/rfnoc/templates/modules/device_transport.v.mako b/host/utils/rfnoc/templates/modules/device_transport.v.mako deleted file mode 100644 index 3d752ce13..000000000 --- a/host/utils/rfnoc/templates/modules/device_transport.v.mako +++ /dev/null @@ -1,13 +0,0 @@ -<%page args="transports"/>\ -\ -%for i, transport in enumerate(transports): - // Transport ${i} (${transport["name"]} ${transport["type"]}) - input wire [${transport["width"]}-1:0] s_${transport["name"]}_tdata, - input wire s_${transport["name"]}_tlast, - input wire s_${transport["name"]}_tvalid, - output wire s_${transport["name"]}_tready, - output wire [${transport["width"]}-1:0] m_${transport["name"]}_tdata, - output wire m_${transport["name"]}_tlast, - output wire m_${transport["name"]}_tvalid, - input wire m_${transport["name"]}_tready${"," if i < len(transports) - 1 else ""} -%endfor diff --git a/host/utils/rfnoc/templates/modules/drive_unused_ports.v.mako b/host/utils/rfnoc/templates/modules/drive_unused_ports.v.mako deleted file mode 100644 index dfa1e7eb6..000000000 --- a/host/utils/rfnoc/templates/modules/drive_unused_ports.v.mako +++ /dev/null @@ -1,21 +0,0 @@ -<%page args="connections, block_ports"/>\ -<% - sources = [] - destinations = [] - for connection in connections: - sources.append((connection["srcblk"], connection["srcport"])) - destinations.append((connection["dstblk"], connection["dstport"])) -%>\ -%for (block_name, port_name, direction) in block_ports: - %if direction == "input": - %if not (block_name, port_name) in destinations: - assign s_${block_name}_${port_name}_tdata = {CHDR_W{1'b0}}; - assign s_${block_name}_${port_name}_tlast = 1'b0; - assign s_${block_name}_${port_name}_tvalid = 1'b0; - %endif - %elif direction == "output": - %if not (block_name, port_name) in sources: - assign m_${block_name}_${port_name}_tready = 1'b1; - %endif - %endif -%endfor diff --git a/host/utils/rfnoc/templates/modules/rfnoc_block.v.mako b/host/utils/rfnoc/templates/modules/rfnoc_block.v.mako deleted file mode 100644 index ee4a811a8..000000000 --- a/host/utils/rfnoc/templates/modules/rfnoc_block.v.mako +++ /dev/null @@ -1,91 +0,0 @@ -<%page args="block_id, block_number, block_name, block, block_params, block_ports"/>\ -\ -<% - import re - import six - - # Create two strings, one for the input and one for the output, that each - # contains all the signal names to be connected to the input or output - # AXIS-CHDR ports of this block. - axis_inputs = "" - axis_outputs = "" - for port_desc in block_ports: - if port_desc[0] == block_name: - port_name = port_desc[1] - if port_desc[2] == "input": - axis_inputs = "{0}_%s_%s_{1}, " % (block_name, port_name) + axis_inputs - elif port_desc[2] == "output": - axis_outputs = "{0}_%s_%s_{1}, " % (block_name, port_name) + axis_outputs - axis_inputs = axis_inputs[:-2] - axis_outputs = axis_outputs[:-2] -%>\ - - // ---------------------------------------------------- - // ${block_name} - // ---------------------------------------------------- -%for clock in block.clocks: - %if not clock["name"] in ["rfnoc_chdr", "rfnoc_ctrl"]: - wire ${block_name}_${clock["name"]}_clk; - %endif -%endfor - wire [CHDR_W-1:0] ${axis_inputs.format("s", "tdata ")}; - wire ${axis_inputs.format("s", "tlast ")}; - wire ${axis_inputs.format("s", "tvalid")}; - wire ${axis_inputs.format("s", "tready")}; - wire [CHDR_W-1:0] ${axis_outputs.format("m", "tdata ")}; - wire ${axis_outputs.format("m", "tlast ")}; - wire ${axis_outputs.format("m", "tvalid")}; - wire ${axis_outputs.format("m", "tready")}; - -%if hasattr(block, "io_ports"): - %for name, io_port in six.iteritems(block.io_ports): - // ${name} - %for wire in io_port["wires"]: - wire [${"%3d" % wire["width"]}-1:0] ${block_name}_${wire["name"]}; - %endfor - %endfor -%endif - - rfnoc_block_${block.module_name} #( - .THIS_PORTID(${block_id}), - .CHDR_W(CHDR_W), -%for name, value in six.iteritems(block_params): - .${name}(${value}), -%endfor - .MTU(MTU) - ) b_${block_name}_${block_number} ( - .rfnoc_chdr_clk (rfnoc_chdr_clk), - .rfnoc_ctrl_clk (rfnoc_ctrl_clk), -%for clock in block.clocks: - %if not clock["name"] in ["rfnoc_chdr", "rfnoc_ctrl"]: - .${clock["name"]}_clk(${block_name}_${clock["name"]}_clk), - %endif -%endfor - .rfnoc_core_config (rfnoc_core_config[512*${block_number + 1}-1:512*${block_number}]), - .rfnoc_core_status (rfnoc_core_status[512*${block_number + 1}-1:512*${block_number}]), - -%if hasattr(block, "io_ports"): - %for name, io_port in six.iteritems(block.io_ports): - %for wire in io_port["wires"]: - .${wire["name"]}(${block_name}_${wire["name"]}), - %endfor - %endfor -%endif - - .s_rfnoc_chdr_tdata ({${axis_inputs.format("s", "tdata ")}}), - .s_rfnoc_chdr_tlast ({${axis_inputs.format("s", "tlast ")}}), - .s_rfnoc_chdr_tvalid({${axis_inputs.format("s", "tvalid")}}), - .s_rfnoc_chdr_tready({${axis_inputs.format("s", "tready")}}), - .m_rfnoc_chdr_tdata ({${axis_outputs.format("m", "tdata ")}}), - .m_rfnoc_chdr_tlast ({${axis_outputs.format("m", "tlast ")}}), - .m_rfnoc_chdr_tvalid({${axis_outputs.format("m", "tvalid")}}), - .m_rfnoc_chdr_tready({${axis_outputs.format("m", "tready")}}), - .s_rfnoc_ctrl_tdata (s_${block_name}_ctrl_tdata ), - .s_rfnoc_ctrl_tlast (s_${block_name}_ctrl_tlast ), - .s_rfnoc_ctrl_tvalid(s_${block_name}_ctrl_tvalid), - .s_rfnoc_ctrl_tready(s_${block_name}_ctrl_tready), - .m_rfnoc_ctrl_tdata (m_${block_name}_ctrl_tdata ), - .m_rfnoc_ctrl_tlast (m_${block_name}_ctrl_tlast ), - .m_rfnoc_ctrl_tvalid(m_${block_name}_ctrl_tvalid), - .m_rfnoc_ctrl_tready(m_${block_name}_ctrl_tready) - ); diff --git a/host/utils/rfnoc/templates/modules/sep_xb_wires.v.mako b/host/utils/rfnoc/templates/modules/sep_xb_wires.v.mako deleted file mode 100644 index 4aa7d56bb..000000000 --- a/host/utils/rfnoc/templates/modules/sep_xb_wires.v.mako +++ /dev/null @@ -1,12 +0,0 @@ -<%page args="seps"/>\ -\ -%for sep in seps: - wire [CHDR_W-1:0] xb_to_${sep}_tdata ; - wire xb_to_${sep}_tlast ; - wire xb_to_${sep}_tvalid; - wire xb_to_${sep}_tready; - wire [CHDR_W-1:0] ${sep}_to_xb_tdata ; - wire ${sep}_to_xb_tlast ; - wire ${sep}_to_xb_tvalid; - wire ${sep}_to_xb_tready; -%endfor diff --git a/host/utils/rfnoc/templates/modules/static_router.v.mako b/host/utils/rfnoc/templates/modules/static_router.v.mako deleted file mode 100644 index 3649c278b..000000000 --- a/host/utils/rfnoc/templates/modules/static_router.v.mako +++ /dev/null @@ -1,15 +0,0 @@ -<%page args="connections"/>\ -\ -%for connection in connections: -<% - srcblk = connection["srcblk"] - dstblk = connection["dstblk"] - srcport = "in" if connection["srcport"] == None else connection["srcport"] - dstport = "out" if connection["dstport"] == None else connection["dstport"] -%>\ - assign s_${dstblk}_${dstport}_tdata = m_${srcblk}_${srcport}_tdata ; - assign s_${dstblk}_${dstport}_tlast = m_${srcblk}_${srcport}_tlast ; - assign s_${dstblk}_${dstport}_tvalid = m_${srcblk}_${srcport}_tvalid; - assign m_${srcblk}_${srcport}_tready = s_${dstblk}_${dstport}_tready; - -%endfor
\ No newline at end of file diff --git a/host/utils/rfnoc/templates/modules/stream_endpoints.v.mako b/host/utils/rfnoc/templates/modules/stream_endpoints.v.mako deleted file mode 100644 index f8ecccb77..000000000 --- a/host/utils/rfnoc/templates/modules/stream_endpoints.v.mako +++ /dev/null @@ -1,92 +0,0 @@ -<%page args="seps"/>\ -<% - import math - import re - import six - - axis_inputs = {} - axis_outputs = {} - for i, sep in enumerate(seps): - inputs = "" - outputs = "" - for data_i in range(0,seps[sep]["num_data_i"]): - inputs = "s_{0}_in%d_{1}, " % (data_i) + inputs - axis_inputs[sep] = inputs[:-2] - for data_o in range(0,seps[sep]["num_data_o"]): - outputs = "m_{0}_out%d_{1}, " % (data_o) + outputs - axis_outputs[sep] = outputs[:-2] -%>\ -\ -%for i, sep in enumerate(seps): -<% -# If buff_size == 0, then we assume that we will never transmit through this SEP -buff_size = seps[sep]["buff_size"] -if buff_size > 0: - buff_size = int(math.ceil(math.log(buff_size, 2))) - # FIXME MTU assumed to be 10 here -- forcing to at least accommodate 2 pkts - buff_size = max(buff_size, 10+1) -else: - buff_size = 5 -%>\ - wire [CHDR_W-1:0] ${axis_outputs[sep].format(sep,"tdata")}; - wire ${axis_outputs[sep].format(sep,"tlast")}; - wire ${axis_outputs[sep].format(sep,"tvalid")}; - wire ${axis_outputs[sep].format(sep,"tready")}; - wire [CHDR_W-1:0] ${axis_inputs[sep].format(sep,"tdata")}; - wire ${axis_inputs[sep].format(sep,"tlast")}; - wire ${axis_inputs[sep].format(sep,"tvalid")}; - wire ${axis_inputs[sep].format(sep,"tready")}; - wire [31:0] m_${sep}_ctrl_tdata , s_${sep}_ctrl_tdata ; - wire m_${sep}_ctrl_tlast , s_${sep}_ctrl_tlast ; - wire m_${sep}_ctrl_tvalid, s_${sep}_ctrl_tvalid; - wire m_${sep}_ctrl_tready, s_${sep}_ctrl_tready; - - chdr_stream_endpoint #( - .PROTOVER (PROTOVER), - .CHDR_W (CHDR_W), - .AXIS_CTRL_EN (${int(seps[sep]["ctrl"])}), - .AXIS_DATA_EN (${int(seps[sep]["data"])}), - .NUM_DATA_I (${int(seps[sep]["num_data_i"])}), - .NUM_DATA_O (${int(seps[sep]["num_data_o"])}), - .INST_NUM (${i}), - .CTRL_XBAR_PORT (${i+1}), - .INGRESS_BUFF_SIZE (${buff_size}), - .MTU (MTU), - .REPORT_STRM_ERRS (1) - ) ${sep}_i ( - .rfnoc_chdr_clk (rfnoc_chdr_clk ), - .rfnoc_chdr_rst (rfnoc_chdr_rst ), - .rfnoc_ctrl_clk (rfnoc_ctrl_clk ), - .rfnoc_ctrl_rst (rfnoc_ctrl_rst ), - .device_id (device_id ), - .s_axis_chdr_tdata (xb_to_${sep}_tdata ), - .s_axis_chdr_tlast (xb_to_${sep}_tlast ), - .s_axis_chdr_tvalid (xb_to_${sep}_tvalid ), - .s_axis_chdr_tready (xb_to_${sep}_tready ), - .m_axis_chdr_tdata (${sep}_to_xb_tdata ), - .m_axis_chdr_tlast (${sep}_to_xb_tlast ), - .m_axis_chdr_tvalid (${sep}_to_xb_tvalid ), - .m_axis_chdr_tready (${sep}_to_xb_tready ), - .s_axis_data_tdata ({${axis_inputs[sep].format(sep,"tdata")}}), - .s_axis_data_tlast ({${axis_inputs[sep].format(sep,"tlast")}}), - .s_axis_data_tvalid ({${axis_inputs[sep].format(sep,"tvalid")}}), - .s_axis_data_tready ({${axis_inputs[sep].format(sep,"tready")}}), - .m_axis_data_tdata ({${axis_outputs[sep].format(sep,"tdata")}}), - .m_axis_data_tlast ({${axis_outputs[sep].format(sep,"tlast")}}), - .m_axis_data_tvalid ({${axis_outputs[sep].format(sep,"tvalid")}}), - .m_axis_data_tready ({${axis_outputs[sep].format(sep,"tready")}}), - .s_axis_ctrl_tdata (s_${sep}_ctrl_tdata ), - .s_axis_ctrl_tlast (s_${sep}_ctrl_tlast ), - .s_axis_ctrl_tvalid (s_${sep}_ctrl_tvalid), - .s_axis_ctrl_tready (s_${sep}_ctrl_tready), - .m_axis_ctrl_tdata (m_${sep}_ctrl_tdata ), - .m_axis_ctrl_tlast (m_${sep}_ctrl_tlast ), - .m_axis_ctrl_tvalid (m_${sep}_ctrl_tvalid), - .m_axis_ctrl_tready (m_${sep}_ctrl_tready), - .strm_seq_err_stb ( ), - .strm_data_err_stb ( ), - .strm_route_err_stb ( ), - .signal_data_err (1'b0 ) - ); - -%endfor diff --git a/host/utils/rfnoc/templates/rfnoc_image_core.v.mako b/host/utils/rfnoc/templates/rfnoc_image_core.v.mako deleted file mode 100644 index a18ff53a1..000000000 --- a/host/utils/rfnoc/templates/rfnoc_image_core.v.mako +++ /dev/null @@ -1,159 +0,0 @@ -<% - import datetime -%>// -// Copyright ${datetime.datetime.now().year} ${config.copyright} -// -// ${config.license} -// - -// Module: rfnoc_image_core (for ${config.device.type}) -// This file was autogenerated by UHD's image builder tool (rfnoc_image_builder) -// Re-running that tool will overwrite this file! -// File generated on: ${datetime.datetime.now().isoformat()} -% if source: -// Source: ${source} -% endif -% if source_hash: -// Source SHA256: ${source_hash} -% endif - -module rfnoc_image_core #( - parameter [15:0] PROTOVER = {8'd1, 8'd0} -)( - // Clocks - input wire chdr_aclk, - input wire ctrl_aclk, - input wire core_arst, -%for clock in config.device.clocks: - input wire ${clock["name"]}_clk, -%endfor - // Basic - input wire [15:0] device_id, -<%include file="/modules/device_io_ports.v.mako" args="io_ports=config.device.io_ports"/>\ -<%include file="/modules/device_transport.v.mako" args="transports=config.device.transports"/>\ -); - - localparam CHDR_W = 64; - localparam MTU = 10; - localparam EDGE_TBL_FILE = `"`RFNOC_EDGE_TBL_FILE`"; - - wire rfnoc_chdr_clk, rfnoc_chdr_rst; - wire rfnoc_ctrl_clk, rfnoc_ctrl_rst; - - // ---------------------------------------------------- - // CHDR Crossbar - // ---------------------------------------------------- -<%include file="/modules/sep_xb_wires.v.mako" args="seps=config.stream_endpoints"/>\ - - chdr_crossbar_nxn #( - .CHDR_W (CHDR_W), - .NPORTS (${len(config.stream_endpoints) + len(config.device.transports)}), - .DEFAULT_PORT (0), - .MTU (MTU), - .ROUTE_TBL_SIZE (6), - .MUX_ALLOC ("ROUND-ROBIN"), - .OPTIMIZE ("AREA"), - .NPORTS_MGMT (${len(config.device.transports)}), - .EXT_RTCFG_PORT (0), - .PROTOVER (PROTOVER) - ) chdr_xb_i ( - .clk (rfnoc_chdr_clk), - .reset (rfnoc_chdr_rst), - .device_id (device_id), -<%include file="/modules/chdr_xb_sep_transport.v.mako" args="seps=config.stream_endpoints, transports=config.device.transports"/>\ - .ext_rtcfg_stb (1'h0), - .ext_rtcfg_addr (16'h0), - .ext_rtcfg_data (32'h0), - .ext_rtcfg_ack () - ); - - // ---------------------------------------------------- - // Stream Endpoints - // ---------------------------------------------------- - -<%include file="/modules/stream_endpoints.v.mako" args="seps=config.stream_endpoints"/>\ - -<% - from collections import OrderedDict - ctrl_seps = OrderedDict((k, v) for k, v in config.stream_endpoints.items() if v.get('ctrl')) -%> - // ---------------------------------------------------- - // Control Crossbar - // ---------------------------------------------------- - - wire [31:0] m_core_ctrl_tdata , s_core_ctrl_tdata ; - wire m_core_ctrl_tlast , s_core_ctrl_tlast ; - wire m_core_ctrl_tvalid, s_core_ctrl_tvalid; - wire m_core_ctrl_tready, s_core_ctrl_tready; -<%include file="/modules/ctrl_crossbar.v.mako" args="seps=ctrl_seps, blocks=config.noc_blocks"/>\ - - // ---------------------------------------------------- - // RFNoC Core Kernel - // ---------------------------------------------------- - wire [(512*${len(config.noc_blocks)})-1:0] rfnoc_core_config, rfnoc_core_status; - - rfnoc_core_kernel #( - .PROTOVER (PROTOVER), - .DEVICE_TYPE (16'h${config.device.type_id}), - .DEVICE_FAMILY ("${config.device.family}"), - .SAFE_START_CLKS (0), - .NUM_BLOCKS (${len(config.noc_blocks)}), - .NUM_STREAM_ENDPOINTS(${len(config.stream_endpoints)}), - .NUM_ENDPOINTS_CTRL (${len(ctrl_seps)}), - .NUM_TRANSPORTS (${len(config.device.transports)}), - .NUM_EDGES (${len(config.block_con)}), - .CHDR_XBAR_PRESENT (1), - .EDGE_TBL_FILE (EDGE_TBL_FILE) - ) core_kernel_i ( - .chdr_aclk (chdr_aclk), - .chdr_aclk_locked (1'b1), - .ctrl_aclk (ctrl_aclk), - .ctrl_aclk_locked (1'b1), - .core_arst (core_arst), - .core_chdr_clk (rfnoc_chdr_clk), - .core_chdr_rst (rfnoc_chdr_rst), - .core_ctrl_clk (rfnoc_ctrl_clk), - .core_ctrl_rst (rfnoc_ctrl_rst), - .s_axis_ctrl_tdata (s_core_ctrl_tdata ), - .s_axis_ctrl_tlast (s_core_ctrl_tlast ), - .s_axis_ctrl_tvalid (s_core_ctrl_tvalid), - .s_axis_ctrl_tready (s_core_ctrl_tready), - .m_axis_ctrl_tdata (m_core_ctrl_tdata ), - .m_axis_ctrl_tlast (m_core_ctrl_tlast ), - .m_axis_ctrl_tvalid (m_core_ctrl_tvalid), - .m_axis_ctrl_tready (m_core_ctrl_tready), - .device_id (device_id), - .rfnoc_core_config (rfnoc_core_config), - .rfnoc_core_status (rfnoc_core_status) - ); - - // ---------------------------------------------------- - // Blocks - // ---------------------------------------------------- -%for i, name in enumerate(config.noc_blocks): -<%include file="/modules/rfnoc_block.v.mako" args="block_id=i + len(ctrl_seps) + 1, block_number=i, block_name=name, block=config.blocks[config.noc_blocks[name]['block_desc']], block_params=config.noc_blocks[name]['parameters'], block_ports=config.block_ports"/> -%endfor - - // ---------------------------------------------------- - // Static Router - // ---------------------------------------------------- -<%include file="/modules/static_router.v.mako" args="connections=config.block_con"/>\ - - // ---------------------------------------------------- - // Unused Ports - // ---------------------------------------------------- -<%include file="/modules/drive_unused_ports.v.mako" args="connections=config.block_con, block_ports=config.block_ports"/>\ - - // ---------------------------------------------------- - // Clock Domains - // ---------------------------------------------------- -<%include file="/modules/connect_clk_domains.v.mako" args="connections=config.clk_domain_con, clocks=config.clocks"/>\ - - // ---------------------------------------------------- - // IO Port Connection - // ---------------------------------------------------- - // Master/Slave Connections: -<%include file="/modules/connect_io_ports.v.mako" args="connections=config.io_port_con_ms, io_ports=config.io_ports, names=('master', 'slave')"/>\ - // Broadcaster/Listener Connections: -<%include file="/modules/connect_io_ports.v.mako" args="connections=config.io_port_con_bl, io_ports=config.io_ports, names=('broadcaster', 'listener')"/>\ -endmodule diff --git a/host/utils/rfnoc/yaml_utils.py b/host/utils/rfnoc/yaml_utils.py deleted file mode 100644 index e64556334..000000000 --- a/host/utils/rfnoc/yaml_utils.py +++ /dev/null @@ -1,107 +0,0 @@ -""" -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/>. -""" - -import json -import logging -import os -import sys -from ruamel import yaml - -# Allow jsonschema import to fail. If not available no schema validation will -# be done (but warning will be printed for each skipped validation). -try: - import jsonschema -except ImportError: - logging.warning("Module jsonschema is not installed. Configuration files " - "will not be validated against their schema.") - - -def find_schema(schema_name, config_path): - """ - Recursive search for schema file. Only looks for a file with appropriate - name without checking for content or accessibility. - This check will be performed later when trying to load the schema. - :param schema_name: name of schema file to search for - :param config_path: root path to start search in - :return: full path to schema file if found, None else - """ - for root, _, _ in os.walk(config_path): - filename = os.path.join(root, schema_name) - if os.path.isfile(filename): - return filename - return None - - -def validate_config(config, config_path): - """ - Try to validate config. - - config contains a configuration loaded from a yaml file. config is assumed - to be a dictionary which contains a key 'schema' which determines - which schema to validate against. The schema (json formatted) needs to be - located in config_path or any sub folder of it. - If "jsonschema" module cannot be loaded validation is skipped and config - is assumed to be valid. This way a configuration can also be loaded if - "jsonschema" is not available. - The method raises ValueError if no schema is defined in config or the - schema defined in config cannot be found. The validation itself may throw - a jsonschema.exceptions.ValidationError if config does not confirm to its - schema. - :param config: a dictionary to validate (loaded from yaml file). - :param config_path: a path holding schema definitions - """ - if "jsonschema" not in sys.modules: - logging.warning("Skip schema validation (missing module jsonschema).") - return - - if not "schema" in config: - raise ValueError("Missing schema in configuration.") - - schema_name = config["schema"] - logging.debug("Validate against schema %s.", schema_name) - - schema_file = find_schema('%s.json' % schema_name, config_path) - if not schema_file: - raise ValueError("Unknown schema %s." % schema_name) - - logging.debug("Using schema file %s.", schema_file) - - with open(schema_file) as stream: - jsonschema.validate(instance=config, schema=json.load(stream)) - logging.debug("Configuration successful validated.") - - -def load_config(config_file, config_path): - """ - Wrapper method to unify loading of configuration files. - Beside loading the configuration (yaml file format) itself from config_file - this method also validates the configuration against a schema. The root - element of the configuration must load to a dictionary which contains a - "schema" key. The value of schema points to a file named {value}.json which - must be located in config_path or one of its sub folders. - .. seealso:: validate_config. - :param config_file: configuration to load - :param config_path: root path of schema definition files - :return: - """ - logging.debug("Load configuration %s.", config_file) - with open(config_file) as stream: - rt_yaml = yaml.YAML(typ='rt') - config = rt_yaml.load(stream) - logging.debug("Configuration successful loaded.") - validate_config(config, config_path) - return config diff --git a/host/utils/bin/rfnoc_image_builder b/host/utils/rfnoc_image_builder.py index e221787c1..a031621ca 100755 --- a/host/utils/bin/rfnoc_image_builder +++ b/host/utils/rfnoc_image_builder.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """ Copyright 2019 Ettus Research, A National Instrument Brand @@ -27,13 +27,9 @@ import re import yaml logging.basicConfig(format='[%(levelname).3s] %(message)s') -# CMAKE will dump final site-package folder into this to allow the script find -# its package folder -sys.path.insert(0, "@RFNOC_PACKAGE_DIR@") -#pylint : disable = wrong - import - position -import rfnoc.image_builder -import rfnoc.yaml_utils +from uhd.imgbuilder import image_builder +from uhd.imgbuilder import yaml_utils def setup_parser(): """ @@ -114,15 +110,14 @@ def image_config(args): :return: image configuration as dictionary """ if args.yaml_config: - config = \ - rfnoc.yaml_utils.load_config(args.yaml_config, get_config_path()) + config = yaml_utils.load_config(args.yaml_config, get_config_path()) device = config.get('device') if args.device is None else args.device target = config.get('default_target') if args.target is None else args.target return config, args.yaml_config, device, target with open(args.grc_config) as grc_file: config = yaml.load(grc_file) logging.info("Converting GNU Radio Companion file to image builder format") - config = rfnoc.image_builder.convert_to_image_config(config, args.grc_blocks) + config = image_builder.convert_to_image_config(config, args.grc_blocks) return config, args.grc_config, args.device, args.target @@ -164,7 +159,7 @@ def get_config_path(): def main(): """ - Wrapper for rfnoc.image_builder.build_image. + Wrapper for image_builder.build_image. :return: exit code """ args = setup_parser().parse_args() @@ -176,7 +171,7 @@ def main(): with open(source, "rb") as source_file: source_hash.update(source_file.read()) - rfnoc.image_builder.build_image( + image_builder.build_image( config=config, fpga_path=args.fpga_dir, config_path=get_config_path(), @@ -193,4 +188,4 @@ def main(): ) if __name__ == "__main__": - exit(main()) + sys.exit(main()) |