|
141 | 141 | from finn.transformation.streamline import Streamline |
142 | 142 | from finn.transformation.streamline.reorder import MakeMaxPoolNHWC |
143 | 143 | from finn.transformation.streamline.round_thresholds import RoundAndClipThresholds |
144 | | -from finn.util.basic import get_liveness_threshold_cycles, get_rtlsim_trace_depth |
| 144 | +from finn.util.basic import ( |
| 145 | + get_liveness_threshold_cycles, |
| 146 | + get_metadata_prop_path, |
| 147 | + get_metadata_prop_safe, |
| 148 | + get_rtlsim_trace_depth, |
| 149 | +) |
145 | 150 | from finn.util.config import extract_model_config_consolidate_shuffles, extract_model_config_to_json |
146 | 151 | from finn.util.exception import FINNMultiFPGAUserError, FINNUserError |
147 | 152 | from finn.util.execution import execute_parent |
@@ -1604,59 +1609,94 @@ def step_vivado_power_estimation(model: ModelWrapper, cfg: DataflowBuildConfig): |
1604 | 1609 | def step_synthesize_bitfile(model: ModelWrapper, cfg: DataflowBuildConfig): |
1605 | 1610 | """Synthesize a bitfile for the using the specified shell flow, using either |
1606 | 1611 | Vivado or Vitis, to target the specified board.""" |
| 1612 | + if DataflowOutputType.BITFILE not in cfg.generate_outputs: |
| 1613 | + log.warning( |
| 1614 | + "DataflowOutputType.BITFILE not in requested outputs, skipping step_synthesize_bitfile." |
| 1615 | + ) |
| 1616 | + return model |
1607 | 1617 |
|
1608 | | - if DataflowOutputType.BITFILE in cfg.generate_outputs: |
1609 | | - bitfile_dir = cfg.output_dir + "/bitfile" |
1610 | | - os.makedirs(bitfile_dir, exist_ok=True) |
1611 | | - report_dir = cfg.output_dir + "/report" |
1612 | | - os.makedirs(report_dir, exist_ok=True) |
1613 | | - partition_model_dir = cfg.output_dir + "/intermediate_models/kernel_partitions" |
1614 | | - if cfg.shell_flow_type == ShellFlowType.VIVADO_ZYNQ: |
1615 | | - model = model.transform( |
1616 | | - ZynqBuild( |
1617 | | - cfg.board, |
1618 | | - cfg.synth_clk_period_ns, |
1619 | | - cfg.enable_hw_debug, |
1620 | | - cfg.enable_instrumentation, |
1621 | | - cfg.instrumentation_no_dma, |
1622 | | - cfg.live_fifo_sizing, |
1623 | | - partition_model_dir=partition_model_dir, |
1624 | | - ) |
1625 | | - ) |
1626 | | - |
1627 | | - bitfile_path = os.path.join(bitfile_dir, "finn-accel.bit") |
1628 | | - copy(model.get_metadata_prop("bitfile"), bitfile_path) |
1629 | | - copy(model.get_metadata_prop("hw_handoff"), bitfile_dir + "/finn-accel.hwh") |
1630 | | - copy( |
1631 | | - model.get_metadata_prop("vivado_synth_rpt"), |
1632 | | - report_dir + "/post_synth_resources.xml", |
1633 | | - ) |
1634 | | - |
1635 | | - model.set_metadata_prop("bitfile_output", os.path.abspath(bitfile_path)) |
1636 | | - |
1637 | | - post_synth_resources = model.analysis(post_synth_res) |
1638 | | - with open(report_dir + "/post_synth_resources.json", "w") as f: |
1639 | | - json.dump(post_synth_resources, f, indent=2) |
| 1618 | + # Create some directories for later |
| 1619 | + bitfile_dir = Path(cfg.output_dir) / "bitfile" |
| 1620 | + bitfile_dir.mkdir(exist_ok=True) |
| 1621 | + report_dir = Path(cfg.output_dir) / "report" |
| 1622 | + report_dir.mkdir(exist_ok=True) |
| 1623 | + partition_model_dir = Path(cfg.output_dir) / "intermediate_models/kernel_partitions" |
1640 | 1624 |
|
1641 | | - vivado_pynq_proj_dir = model.get_metadata_prop("vivado_pynq_proj") |
1642 | | - timing_rpt = ( |
1643 | | - "%s/finn_zynq_link.runs/impl_1/top_wrapper_timing_summary_routed.rpt" |
1644 | | - % vivado_pynq_proj_dir |
| 1625 | + # The actual synthesis step! |
| 1626 | + if cfg.shell_flow_type == ShellFlowType.VIVADO_ZYNQ: |
| 1627 | + model = model.transform( |
| 1628 | + ZynqBuild( |
| 1629 | + cfg.board, |
| 1630 | + cfg.synth_clk_period_ns, |
| 1631 | + cfg.enable_hw_debug, |
| 1632 | + cfg.enable_instrumentation, |
| 1633 | + cfg.instrumentation_no_dma, |
| 1634 | + cfg.live_fifo_sizing, |
| 1635 | + partition_model_dir=partition_model_dir, |
1645 | 1636 | ) |
1646 | | - copy(timing_rpt, report_dir + "/post_route_timing.rpt") |
1647 | | - |
1648 | | - elif cfg.shell_flow_type == ShellFlowType.VITIS_ALVEO: |
1649 | | - # TODO: Rework missing parts here |
1650 | | - model = model.transform(VitisBuild(cfg)) |
1651 | | - |
| 1637 | + ) |
| 1638 | + elif cfg.shell_flow_type == ShellFlowType.VITIS_ALVEO: |
| 1639 | + model = model.transform(VitisBuild(cfg)) |
| 1640 | + else: |
| 1641 | + raise Exception("Unrecognized shell_flow_type: " + str(cfg.shell_flow_type)) |
| 1642 | + |
| 1643 | + # Store bitstreams into the output directory. This is the same regardless of flow. |
| 1644 | + bitfile_json = json.loads(get_metadata_prop_safe(model, "bitfile")) |
| 1645 | + suffix = "" |
| 1646 | + if cfg.shell_flow_type == ShellFlowType.VIVADO_ZYNQ: |
| 1647 | + suffix = ".bit" |
| 1648 | + elif cfg.shell_flow_type == ShellFlowType.VITIS_ALVEO: |
| 1649 | + suffix = ".xclbin" |
| 1650 | + |
| 1651 | + # If only one device, omit the device suffix |
| 1652 | + if len(list(bitfile_json.keys())) == 1: |
| 1653 | + bitfile_path = bitfile_dir / f"finn-accel{suffix}" |
| 1654 | + copy(bitfile_json[0], bitfile_path) |
| 1655 | + # For the single-fpga case, store in bitfile_output |
| 1656 | + # TODO: Also adapt this for Multi-FGPA |
| 1657 | + model.set_metadata_prop("bitfile_output", str(bitfile_path.absolute())) |
| 1658 | + else: |
| 1659 | + for device, path in bitfile_json.items(): |
| 1660 | + copy(path, bitfile_dir / f"finn-accel-{device}{suffix}") |
| 1661 | + |
| 1662 | + # Store synth reports |
| 1663 | + rpt_json = json.loads(get_metadata_prop_safe(model, "vivado_synth_rpt")) |
| 1664 | + if len(list(rpt_json.keys())) == 1: |
| 1665 | + res_report = Path(rpt_json[0]) |
| 1666 | + if res_report.exists(): |
| 1667 | + copy(res_report, report_dir / "post_synth_resources.xml") |
1652 | 1668 | else: |
1653 | | - raise Exception("Unrecognized shell_flow_type: " + str(cfg.shell_flow_type)) |
1654 | | - log.info(f"Bitfile written into {bitfile_dir}") |
1655 | | - |
| 1669 | + log.warning(f"Resource report XML not found: {res_report}") |
1656 | 1670 | else: |
1657 | | - log.info( |
1658 | | - "DataflowOutputType.BITFILE not in requested outputs, skipping step_synthesize_bitfile." |
| 1671 | + for device, path in rpt_json.items(): |
| 1672 | + if path.exists(): |
| 1673 | + copy(path, report_dir / f"post_synth_resources_{device}.xml") |
| 1674 | + else: |
| 1675 | + log.warning(f"Resource report XML not found: {path}") |
| 1676 | + |
| 1677 | + # Store artifacts, depending on flow |
| 1678 | + if cfg.shell_flow_type == ShellFlowType.VIVADO_ZYNQ: |
| 1679 | + # Store synthesis artifacts |
| 1680 | + hw_handoff = get_metadata_prop_path(model, "hw_handoff", must_exist=True) |
| 1681 | + copy(hw_handoff, bitfile_dir / "finn-accel.hwh") |
| 1682 | + |
| 1683 | + # Log post synthesis resource as a JSON file |
| 1684 | + post_synth_resources = model.analysis(post_synth_res) |
| 1685 | + (report_dir / "post_synth_resources.json").write_text( |
| 1686 | + json.dumps(post_synth_resources, indent=2) |
| 1687 | + ) |
| 1688 | + |
| 1689 | + # Store the post-route timing report |
| 1690 | + timing_report = ( |
| 1691 | + get_metadata_prop_path(model, "vivado_pynq_proj", must_exist=True) |
| 1692 | + / "finn_zynq_link.runs" |
| 1693 | + / "impl_1" |
| 1694 | + / "top_wrapper_timing_summary_routed.rpt" |
1659 | 1695 | ) |
| 1696 | + copy(timing_report, report_dir / "post_route_timing.rpt") |
| 1697 | + |
| 1698 | + elif cfg.shell_flow_type == ShellFlowType.VITIS_ALVEO: |
| 1699 | + pass |
1660 | 1700 |
|
1661 | 1701 | return model |
1662 | 1702 |
|
|
0 commit comments