# Copyright (C) 2020-2021 Arm Limited or its affiliates. All rights reserved. # # SPDX-License-Identifier: Apache-2.0 # # Licensed under the Apache License, Version 2.0 (the License); you may # not use this file except in compliance with the License. # You may obtain a copy of the License at # # www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an AS IS BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import csv import io from typing import Any from typing import Dict from typing import List from typing import Tuple from typing import Union import lxml.etree as xml from . import numeric_util from .operation import Operation from .shape4d import Shape4D class DebugDatabase: NULLREF = -1 show_warnings = False SOURCE_TABLE = "source" _sourceUID: Dict[Any, int] = {} _sourceHeaders = ["id", "operator", "kernel_w", "kernel_h", "ofm_w", "ofm_h", "ofm_d"] _sourceTable: List[List[Union[float, int, str]]] = [] OPTIMISED_TABLE = "optimised" _optimisedUID: Dict[Any, Tuple[int, int]] = {} _optimisedHeaders = ["id", "source_id", "operator", "kernel_w", "kernel_h", "ofm_w", "ofm_h", "ofm_d"] _optimisedTable: List[List[Union[float, int, str]]] = [] QUEUE_TABLE = "queue" _queueHeaders = ["offset", "cmdstream_id", "optimised_id"] _queueTable: List[List[int]] = [] STREAM_TABLE = "cmdstream" _streamUID: Dict[Any, int] = {} _streamHeaders = ["id", "file_offset"] _streamTable: List[List[int]] = [] @classmethod def add_source(cls, op: Operation): assert isinstance(op, Operation) uid = len(cls._sourceUID) cls._sourceUID[op] = uid ofm_shape = numeric_util.full_shape(3, op.outputs[0].shape, 1) cls._sourceTable.append( [uid, str(op.type), op.kernel.width, op.kernel.height, ofm_shape[-2], ofm_shape[-3], ofm_shape[-1]] ) @classmethod def add_optimised(cls, parent: Operation, op: Operation): assert isinstance(parent, Operation) and isinstance(op, Operation) if op not in cls._optimisedUID: if parent not in cls._sourceUID: # The the parent wasn't in the source network try to look it # up in the optimised network and use that op's source parent. if parent in cls._optimisedUID: src_uid = cls._optimisedUID[parent][1] else: if DebugDatabase.show_warnings: print("Debug Database: Associated parent '{0}' not in network".format(parent.type)) src_uid = DebugDatabase.NULLREF else: src_uid = cls._sourceUID[parent] uid = len(cls._optimisedUID) cls._optimisedUID[op] = (uid, src_uid) if len(op.ofm_shapes) == 0: ofm_shape = Shape4D(op.outputs[0].shape) else: ofm_shape = op.ofm_shapes[0] cls._optimisedTable.append( [ uid, src_uid, str(op.type), op.kernel.width, op.kernel.height, ofm_shape.width, ofm_shape.height, ofm_shape.depth, ] ) @classmethod def add_stream(cls, key): if key not in cls._streamUID: uid = len(cls._streamUID) cls._streamUID[key] = uid return uid @classmethod def set_stream_offset(cls, key, file_offset: int): assert key in cls._streamUID uid = cls._streamUID[key] cls._streamTable.append([uid, file_offset]) @classmethod def add_command(cls, stream_id: int, offset: int, op: Operation): assert stream_id < len(cls._streamUID) assert op in cls._optimisedUID, "Optimised operator must exist before code generation" optimised_id = cls._optimisedUID[op][0] cls._queueTable.append([offset, stream_id, optimised_id]) @classmethod def _write_table(cls, root: xml.Element, name: str, headers: List[str], table): # Convert table to CSV out = io.StringIO() writer = csv.writer(out, quoting=csv.QUOTE_NONNUMERIC) writer.writerow(headers) writer.writerows(table) # Package table into XML output table = xml.SubElement(root, "table", {"name": name}) table.text = xml.CDATA(out.getvalue()) @classmethod def write(cls, file_path: str, input_file: str, output_file: str): root = xml.Element("debug", {"source": input_file, "optimised": output_file}) cls._write_table(root, cls.SOURCE_TABLE, cls._sourceHeaders, cls._sourceTable) cls._write_table(root, cls.OPTIMISED_TABLE, cls._optimisedHeaders, cls._optimisedTable) cls._write_table(root, cls.QUEUE_TABLE, cls._queueHeaders, cls._queueTable) cls._write_table(root, cls.STREAM_TABLE, cls._streamHeaders, cls._streamTable) xml.ElementTree(root).write(file_path, encoding="utf-8", xml_declaration=True, pretty_print=True)