diff --git a/src/finn/analysis/fpgadataflow/post_synth_res.py b/src/finn/analysis/fpgadataflow/post_synth_res.py index 508c34aaed50f2935f4915cdcea29a3e92641b3c..72533b54ea7b2193a3a9e0d7ee7c8c60646ead0e 100644 --- a/src/finn/analysis/fpgadataflow/post_synth_res.py +++ b/src/finn/analysis/fpgadataflow/post_synth_res.py @@ -30,15 +30,20 @@ import os import xml.etree.ElementTree as ET from finn.transformation.move_reshape import _is_fpgadataflow_node +from finn.core.modelwrapper import ModelWrapper +from finn.custom_op.registry import getCustomOp -def post_synth_res(model): +def post_synth_res(model, override_synth_report_filename=None): """Extracts the FPGA resource results from the Vivado synthesis. Returns {node name : resources_dict}.""" res_dict = {} - synth_report_filename = model.get_metadata_prop("vivado_synth_rpt") + if override_synth_report_filename is not None: + synth_report_filename = override_synth_report_filename + else: + synth_report_filename = model.get_metadata_prop("vivado_synth_rpt") if os.path.isfile(synth_report_filename): tree = ET.parse(synth_report_filename) root = tree.getroot() @@ -51,30 +56,35 @@ def post_synth_res(model): for node in model.graph.node: if _is_fpgadataflow_node(node): - row = root.findall(".//*[@contents='%s']/.." % node.name) - if row != []: - node_dict = {} - row = row[0].getchildren() - """ Expected XML structure: -<tablerow class="" suppressoutput="0" wordwrap="0"> - <tableheader class="" contents="Instance" halign="3" width="-1"/> - <tableheader class="" contents="Module" halign="3" width="-1"/> - <tableheader class="" contents="Total LUTs" halign="3" width="-1"/> - <tableheader class="" contents="Logic LUTs" halign="3" width="-1"/> - <tableheader class="" contents="LUTRAMs" halign="3" width="-1"/> - <tableheader class="" contents="SRLs" halign="3" width="-1"/> - <tableheader class="" contents="FFs" halign="3" width="-1"/> - <tableheader class="" contents="RAMB36" halign="3" width="-1"/> - <tableheader class="" contents="RAMB18" halign="3" width="-1"/> - <tableheader class="" contents="DSP48 Blocks" halign="3" width="-1"/> -</tablerow> - """ - node_dict["LUT"] = int(row[2].attrib["contents"]) - node_dict["SRL"] = int(row[5].attrib["contents"]) - node_dict["FF"] = int(row[6].attrib["contents"]) - node_dict["BRAM_36K"] = int(row[7].attrib["contents"]) - node_dict["BRAM_18K"] = int(row[8].attrib["contents"]) - node_dict["DSP48"] = int(row[9].attrib["contents"]) - res_dict[node.name] = node_dict + if node.op_type == "StreamingDataflowPartition": + sdp_model = ModelWrapper(getCustomOp(node).get_nodeattr("model")) + sdp_res_dict = post_synth_res(sdp_model, synth_report_filename) + res_dict.update(sdp_res_dict) + else: + row = root.findall(".//*[@contents='%s']/.." % node.name) + if row != []: + node_dict = {} + row = row[0].getchildren() + """ Expected XML structure: + <tablerow class="" suppressoutput="0" wordwrap="0"> + <tableheader class="" contents="Instance" halign="3" width="-1"/> + <tableheader class="" contents="Module" halign="3" width="-1"/> + <tableheader class="" contents="Total LUTs" halign="3" width="-1"/> + <tableheader class="" contents="Logic LUTs" halign="3" width="-1"/> + <tableheader class="" contents="LUTRAMs" halign="3" width="-1"/> + <tableheader class="" contents="SRLs" halign="3" width="-1"/> + <tableheader class="" contents="FFs" halign="3" width="-1"/> + <tableheader class="" contents="RAMB36" halign="3" width="-1"/> + <tableheader class="" contents="RAMB18" halign="3" width="-1"/> + <tableheader class="" contents="DSP48 Blocks" halign="3" width="-1"/> + </tablerow> + """ + node_dict["LUT"] = int(row[2].attrib["contents"]) + node_dict["SRL"] = int(row[5].attrib["contents"]) + node_dict["FF"] = int(row[6].attrib["contents"]) + node_dict["BRAM_36K"] = int(row[7].attrib["contents"]) + node_dict["BRAM_18K"] = int(row[8].attrib["contents"]) + node_dict["DSP48"] = int(row[9].attrib["contents"]) + res_dict[node.name] = node_dict return res_dict diff --git a/src/finn/transformation/fpgadataflow/annotate_resources.py b/src/finn/transformation/fpgadataflow/annotate_resources.py index 62ee92df54eee2b63d84657515d7fbc3a8808b81..aba22d16caf715f3495055489c185d925a33f7e0 100644 --- a/src/finn/transformation/fpgadataflow/annotate_resources.py +++ b/src/finn/transformation/fpgadataflow/annotate_resources.py @@ -32,6 +32,8 @@ from finn.transformation.move_reshape import _is_fpgadataflow_node from finn.analysis.fpgadataflow.res_estimation import res_estimation from finn.analysis.fpgadataflow.hls_synth_res_estimation import hls_synth_res_estimation from finn.analysis.fpgadataflow.post_synth_res import post_synth_res +from finn.core.modelwrapper import ModelWrapper +from finn.custom_op.registry import getCustomOp class AnnotateResources(Transformation): @@ -44,9 +46,10 @@ class AnnotateResources(Transformation): chosen mode (e.g. HLSSynthIP for hls) was previously run. """ - def __init__(self, mode): + def __init__(self, mode, override_res_dict=None): super().__init__() self.mode = mode + self.res_dict = override_res_dict def apply(self, model): graph = model.graph @@ -58,10 +61,34 @@ class AnnotateResources(Transformation): res_fxn = post_synth_res else: raise Exception("Unrecognized mode for AnnotateResources") - res_dict = model.analysis(res_fxn) + if self.res_dict is None: + self.res_dict = model.analysis(res_fxn) + children_dict = {} + # annotate node resources + for node in graph.node: + if _is_fpgadataflow_node(node): + if node.name in self.res_dict.keys(): + op_inst = registry.getCustomOp(node) + op_inst.set_nodeattr( + "res_" + self.mode, str(self.res_dict[node.name]) + ) + children_dict[node.name] = self.res_dict[node.name] + elif node.op_type == "StreamingDataflowPartition": + # recurse into model to manually annotate per-layer resources + sdp_model_filename = getCustomOp(node).get_nodeattr("model") + sdp_model = ModelWrapper(sdp_model_filename) + sdp_model = sdp_model.transform( + AnnotateResources(self.mode, self.res_dict) + ) + sdp_dict = sdp_model.get_metadata_prop("res_total_" + self.mode) + # save transformed model + sdp_model.save(sdp_model_filename) + # set res attribute for sdp node + getCustomOp(node).set_nodeattr("res_" + self.mode, str(sdp_dict)) + children_dict[node.name] = sdp_dict total_dict = {} - for lname in res_dict.keys(): - layer_res_dict = res_dict[lname] + for lname in children_dict.keys(): + layer_res_dict = self.res_dict[lname] for r_type in layer_res_dict.keys(): r_amount = layer_res_dict[r_type] r_amount = float(r_amount) @@ -73,9 +100,4 @@ class AnnotateResources(Transformation): if "efficiency" in k: total_dict[k] = total_dict[k] / len(graph.node) model.set_metadata_prop("res_total_" + self.mode, str(total_dict)) - for node in graph.node: - if _is_fpgadataflow_node(node) and node.name in res_dict.keys(): - op_inst = registry.getCustomOp(node) - op_inst.set_nodeattr("res_" + self.mode, str(res_dict[node.name])) - return (model, False)