Beispiel #1
0
    def generate_kernel_boilerplate_pre(self, sdfg, state_id, kernel_name,
                                        parameters, bank_assignments,
                                        module_stream, kernel_stream,
                                        external_streams):

        # Write header
        module_stream.write(
            """#include <dace/xilinx/device.h>
#include <dace/math.h>
#include <dace/complex.h>""", sdfg)
        self._frame.generate_fileheader(sdfg, module_stream, 'xilinx_device')
        module_stream.write("\n", sdfg)

        argname_to_bank_assignment = {}
        # Build kernel signature
        kernel_args = []
        array_args = []
        for is_output, data_name, data, interface in parameters:
            is_assigned = data_name in bank_assignments and bank_assignments[
                data_name] is not None
            if is_assigned and isinstance(data, dt.Array):
                memory_bank = bank_assignments[data_name]
                if memory_bank[0] == "HBM":
                    lowest_bank_index, _ = fpga.get_multibank_ranges_from_subset(
                        memory_bank[1], sdfg)
                else:
                    lowest_bank_index = int(memory_bank[1])
                for bank, interface_id in fpga.iterate_hbm_interface_ids(
                        data, interface):
                    kernel_arg = self.make_kernel_argument(
                        data, data_name, bank, sdfg, is_output, True,
                        interface_id)
                    if kernel_arg:
                        kernel_args.append(kernel_arg)
                        array_args.append((kernel_arg, data_name))
                        argname_to_bank_assignment[kernel_arg] = (
                            memory_bank[0], lowest_bank_index + bank)
            else:
                kernel_arg = self.make_kernel_argument(data, data_name, None,
                                                       None, is_output, True,
                                                       interface)
                if kernel_arg:
                    kernel_args.append(kernel_arg)
                    if isinstance(data, dt.Array):
                        array_args.append((kernel_arg, data_name))
                        argname_to_bank_assignment[kernel_arg] = None

        stream_args = []
        for is_output, data_name, data, interface in external_streams:
            kernel_arg = self.make_kernel_argument(data, data_name, None, None,
                                                   is_output, True, interface)
            if kernel_arg:
                stream_args.append(kernel_arg)

        # Write kernel signature
        kernel_stream.write(
            "DACE_EXPORTED void {}({}) {{\n".format(
                kernel_name, ', '.join(kernel_args + stream_args)), sdfg,
            state_id)

        # Insert interface pragmas
        num_mapped_args = 0
        for arg, data_name in array_args:
            var_name = re.findall(r"\w+", arg)[-1]
            if "*" in arg:
                interface_name = "gmem{}".format(num_mapped_args)
                kernel_stream.write(
                    "#pragma HLS INTERFACE m_axi port={} "
                    "offset=slave bundle={}".format(var_name, interface_name),
                    sdfg, state_id)
                # Map this interface to the corresponding location
                # specification to be passed to the Xilinx compiler
                memory_bank = argname_to_bank_assignment[arg]
                self._bank_assignments[(kernel_name,
                                        interface_name)] = memory_bank
                num_mapped_args += 1

        for arg in kernel_args + ["return"]:
            var_name = re.findall(r"\w+", arg)[-1]
            kernel_stream.write(
                "#pragma HLS INTERFACE s_axilite port={} bundle=control".
                format(var_name))

        for _, var_name, _, _ in external_streams:
            kernel_stream.write(
                "#pragma HLS INTERFACE axis port={}".format(var_name))

        # TODO: add special case if there's only one module for niceness
        kernel_stream.write("\n#pragma HLS DATAFLOW")
        kernel_stream.write("\nHLSLIB_DATAFLOW_INIT();")
Beispiel #2
0
def validate_sdfg(sdfg: 'dace.sdfg.SDFG'):
    """ Verifies the correctness of an SDFG by applying multiple tests.
        :param sdfg: The SDFG to verify.

        Raises an InvalidSDFGError with the erroneous node/edge
        on failure.
    """
    # Avoid import loop
    from dace.codegen.targets import fpga

    try:
        # SDFG-level checks
        if not dtypes.validate_name(sdfg.name):
            raise InvalidSDFGError("Invalid name", sdfg, None)

        if len(sdfg.source_nodes()) > 1 and sdfg.start_state is None:
            raise InvalidSDFGError("Starting state undefined", sdfg, None)

        if len(set([s.label for s in sdfg.nodes()])) != len(sdfg.nodes()):
            raise InvalidSDFGError("Found multiple states with the same name",
                                   sdfg, None)

        # Validate data descriptors
        for name, desc in sdfg._arrays.items():
            # Validate array names
            if name is not None and not dtypes.validate_name(name):
                raise InvalidSDFGError("Invalid array name %s" % name, sdfg,
                                       None)
            # Allocation lifetime checks
            if (desc.lifetime is dtypes.AllocationLifetime.Persistent
                    and desc.storage is dtypes.StorageType.Register):
                raise InvalidSDFGError(
                    "Array %s cannot be both persistent and use Register as "
                    "storage type. Please use a different storage location." %
                    name, sdfg, None)

            # Check for valid bank assignments
            try:
                bank_assignment = fpga.parse_location_bank(desc)
            except ValueError as e:
                raise InvalidSDFGError(str(e), sdfg, None)
            if bank_assignment is not None:
                if bank_assignment[0] == "DDR" or bank_assignment[0] == "HBM":
                    try:
                        tmp = subsets.Range.from_string(bank_assignment[1])
                    except SyntaxError:
                        raise InvalidSDFGError(
                            "Memory bank specifier must be convertible to subsets.Range"
                            f" for array {name}", sdfg, None)
                    try:
                        low, high = fpga.get_multibank_ranges_from_subset(
                            bank_assignment[1], sdfg)
                    except ValueError as e:
                        raise InvalidSDFGError(str(e), sdfg, None)
                    if (high - low < 1):
                        raise InvalidSDFGError(
                            "Memory bank specifier must at least define one bank to be used"
                            f" for array {name}", sdfg, None)
                    if (high - low > 1 and
                        (high - low != desc.shape[0] or len(desc.shape) < 2)):
                        raise InvalidSDFGError(
                            "Arrays that use a multibank access pattern must have the size of the first dimension equal"
                            f" the number of banks and have at least 2 dimensions for array {name}",
                            sdfg, None)

        # Check every state separately
        start_state = sdfg.start_state
        initialized_transients = {'__pystate'}
        symbols = copy.deepcopy(sdfg.symbols)
        symbols.update(sdfg.arrays)
        symbols.update({
            k: dt.create_datadescriptor(v)
            for k, v in sdfg.constants.items()
        })
        for desc in sdfg.arrays.values():
            for sym in desc.free_symbols:
                symbols[str(sym)] = sym.dtype
        visited = set()
        visited_edges = set()
        # Run through states via DFS, ensuring that only the defined symbols
        # are available for validation
        for edge in sdfg.dfs_edges(start_state):
            # Source -> inter-state definition -> Destination
            ##########################################
            visited_edges.add(edge)
            # Source
            if edge.src not in visited:
                visited.add(edge.src)
                validate_state(edge.src, sdfg.node_id(edge.src), sdfg, symbols,
                               initialized_transients)

            ##########################################
            # Edge
            # Check inter-state edge for undefined symbols
            undef_syms = set(edge.data.free_symbols) - set(symbols.keys())
            if len(undef_syms) > 0:
                eid = sdfg.edge_id(edge)
                raise InvalidSDFGInterstateEdgeError(
                    "Undefined symbols in edge: %s" % undef_syms, sdfg, eid)

            # Validate inter-state edge names
            issyms = edge.data.new_symbols(sdfg, symbols)
            if any(not dtypes.validate_name(s) for s in issyms):
                invalid = next(s for s in issyms
                               if not dtypes.validate_name(s))
                eid = sdfg.edge_id(edge)
                raise InvalidSDFGInterstateEdgeError(
                    "Invalid interstate symbol name %s" % invalid, sdfg, eid)

            # Add edge symbols into defined symbols
            symbols.update(issyms)

            ##########################################
            # Destination
            if edge.dst not in visited:
                visited.add(edge.dst)
                validate_state(edge.dst, sdfg.node_id(edge.dst), sdfg, symbols,
                               initialized_transients)
        # End of state DFS

        # If there is only one state, the DFS will miss it
        if start_state not in visited:
            validate_state(start_state, sdfg.node_id(start_state), sdfg,
                           symbols, initialized_transients)

        # Validate all inter-state edges (including self-loops not found by DFS)
        for eid, edge in enumerate(sdfg.edges()):
            if edge in visited_edges:
                continue
            issyms = edge.data.assignments.keys()
            if any(not dtypes.validate_name(s) for s in issyms):
                invalid = next(s for s in issyms
                               if not dtypes.validate_name(s))
                raise InvalidSDFGInterstateEdgeError(
                    "Invalid interstate symbol name %s" % invalid, sdfg, eid)

    except InvalidSDFGError as ex:
        # If the SDFG is invalid, save it
        sdfg.save(os.path.join('_dacegraphs', 'invalid.sdfg'), exception=ex)
        raise