""" Restructuring for castor Template : restructure_fabric.py [openfpga-physical] Author: Ganesh Gore Stages for tiling - *_raw_floorplan - *_original_floorplan - *_pre_tile floorplan - *_final_floorplan """ import gzip import json import logging import os import pickle import re import shutil import sys import time import xml.etree.ElementTree as ET from collections import OrderedDict from copy import deepcopy from fnmatch import fnmatch from glob import glob from itertools import chain from pathlib import Path import spydrnet as sdn import yaml from spydrnet_physical.util import ( ConnectPointList, FloorPlanViz, FPGAGridGen, OpenFPGA, Tile02, get_names, initial_hetero_placement, ) from spydrnet_physical.util.shell import launch_shell from svgwrite.container import Group logger = logging.getLogger("spydrnet_logs") sdn.enable_file_logging(LOG_LEVEL="INFO", filename="restructuring") # Constants Decalartion Section # = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = PROP = "VERILOG.InlineConstraints" LAYOUT = os.environ.get("LAYOUT") PROJ_NAME = os.environ.get("PROJ_NAME") TASK_DIR_NAME = os.environ.get("TASK_DIR_NAME") VERILOG_DIRECTORY = os.environ.get("VERILOG_PROJ_DIR") FPGA_WIDTH = int(os.environ.get("FPGA_SIZE_X")) FPGA_HEIGHT = int(os.environ.get("FPGA_SIZE_Y")) RELEASE_DIR = os.environ["RELEASE_DIRECTORY"] SVG_DIR = f"{RELEASE_DIR}/svg/" PICKLE_DIR = f"{RELEASE_DIR}/pickle/" XML_DIR = f"{RELEASE_DIR}/xml" CBX_COLOR = "#d9d9f3" CBY_COLOR = "#a8d0db" SB_COLOR = "#ceefe4" GRID_COLOR = "#ddd0b1" GLOBAL_SCALE = 1000 SC_HEIGHT = 480 CPP = 96 SC_GRID = SC_HEIGHT * CPP ADDITIONAL_STYLES = "" STYLE_SHEET = """ symbol {mix-blend-mode: difference;} symbol[id*='grid_dsp'] * {fill:#70AE98;} symbol[id*='grid_io'] * {fill:#e6a210;} symbol[id*='grid_bram'] * {fill:#BC85C3;} .over_util {fill:#b22222 !important} text{font-family: Lato; font-style: italic; font-size: 3500px;} rect.highlight_box { fill:none; stroke-width:40; stroke:green;} text.highlight_box { font-size:500px; font-weight:800; fill:red} line {stroke-width:20px !important;} """ mapping = {} script_start_time = time.time() # %% # Main method to perform restructuring # def main(): """ Main method """ global ADDITIONAL_STYLES Path(f"{RELEASE_DIR}/post_synth").mkdir(parents=True, exist_ok=True) Path(f"{RELEASE_DIR}/rpts/pre_pnr").mkdir(parents=True, exist_ok=True) try: VPR_ARCH_FILE = glob((f"{TASK_DIR_NAME}/arch/*vpr*.xml"))[0] except IndexError: logger.exception( "Arch file not found ['%s/arch/*vpr*.xml']", TASK_DIR_NAME) sys.exit() source_files = glob(f"{VERILOG_DIRECTORY}/SRCSynth/*.v") source_files += glob(f"{VERILOG_DIRECTORY}/SRCSynth/*/*.v") for file in source_files: logger.debug("Reading file: %s", file) fpga = OpenFPGA(grid=(FPGA_WIDTH, FPGA_HEIGHT), verilog_files=source_files) fpga.netlist.name = PROJ_NAME print_time_elpased("Finished netlist parsing") # = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Path(SVG_DIR).mkdir(parents=True, exist_ok=True) Path(PICKLE_DIR).mkdir(parents=True, exist_ok=True) # Read eaternal area information filename = f"{RELEASE_DIR}/post_synth/fpga_top_module_area.rpt" if os.path.isfile(filename): fpga.annotate_area_information(filename, skipline=1) # sump top level port information filename = f"{RELEASE_DIR}/post_synth/top_instances_ports.txt" dump_top_definition_ports(fpga, rpt_file=filename) # Visualize Floorplan fpga_grid = FPGAGridGen( design_name=PROJ_NAME, layout=LAYOUT, arch_file=VPR_ARCH_FILE, release_root=None ) fpga_grid.enumerate_grid() fpga.load_grid(fpga_grid) fpga.register_placement_creator( initial_hetero_placement, areaFile={}, ) fpga.merge_all_grid_ios() fpga.remove_direct_interc() fpga.placement_creator.CPP = CPP fpga.placement_creator.SC_HEIGHT = SC_HEIGHT fpga.placement_creator.SC_GRID = CPP * SC_HEIGHT top = fpga.top_module WSmall, HSmall = fpga.fpga_size W, H = fpga.fpga_size # ========================================================================== with open(r'shapes.yaml', 'r', encoding="UTF-8") as file: m = yaml.safe_load(file) # ========================================================================== fpga.placement_creator.update_shaping_param(m) fpga.placement_creator.derive_sb_paramters() auto_shaped_modules = fpga.placement_creator.create_shapes( w_override=WSmall, h_override=HSmall, shape_all=True) shapes = fpga.placement_creator.module_shapes print(auto_shaped_modules) for each in auto_shaped_modules: if "cbx" in each: if "__0_" in each: shapes[each] = deepcopy(shapes["cbx_1__0_"]) elif f"__{HSmall}_" in each: shapes[each] = deepcopy(shapes[f"cbx_1__{HSmall}_"]) else: shapes[each] = deepcopy(shapes["cbx_1__1_"]) if "cby" in each: if "_0__" in each: shapes[each] = deepcopy(shapes["cby_0__1_"]) elif f"_{WSmall}__" in each: shapes[each] = deepcopy(shapes[f"cby_{WSmall}__1_"]) else: shapes[each] = deepcopy(shapes["cby_1__1_"]) if "sb" in each: if f"__{HSmall}_" in each: shapes[each] = deepcopy(shapes[f"sb_1__{HSmall}_"]) else: shapes[each] = deepcopy(shapes["sb_1__1_"]) if "grid" in each: shapes[each] = deepcopy(shapes["grid_clb"]) fpga.create_placement() # fpga.update_module_label() fpga.save_shaping_data( "*", scale=1 / GLOBAL_SCALE, filename=f"{RELEASE_DIR}/rpts/pre_pnr/shaping.txt", ) filename = SVG_DIR + f"{PROJ_NAME}_raw_floorplan.svg" save_tiling_floorplan(fpga, filename, STYLE_SHEET=STYLE_SHEET) # Adding Narrow channels # %% # **Adding margin** # for module in shapes: if "grid_" in module: shapes[module]["POINTS"][0] -= 16 shapes[module]["POINTS"][1] -= 2 shapes[module]["PLACEMENT"][0] += 8 shapes[module]["PLACEMENT"][1] += 1 for module in shapes: if "cbx_" in module: shapes[module]["POINTS"][0] -= 16 shapes[module]["PLACEMENT"][0] += 8 for module in shapes: if "cby_" in module: shapes[module]["POINTS"][1] -= 2 shapes[module]["PLACEMENT"][1] += 1 fpga.create_placement() filename = SVG_DIR + f"{PROJ_NAME}_pre_tile_floorplan.svg" save_tiling_floorplan(fpga, filename, STYLE_SHEET=STYLE_SHEET) # Create tiles fpga.register_tile_generator(Tile02) fpga.create_tiles() save_netlist(fpga) filename = SVG_DIR + f"{PROJ_NAME}_floorplan.svg" save_tiling_floorplan(fpga, filename, STYLE_SHEET=STYLE_SHEET) # pickle.dump( # dwg, open(f"{PICKLE_DIR}/{PROJ_NAME}_floorplaned.pickle", "wb")) logger.info("Saved floorplan in %s", filename) # save_netlist_outline(fpga) def save_tiling_floorplan(fpga: OpenFPGA, filename: str, STYLE_SHEET=None): """ Save currnt tiling strategy to SVG file """ fp = FloorPlanViz(fpga.top_module) fp.compose(skip_connections=True, skip_pins=True) fp.custom_style_sheet = STYLE_SHEET dwg = fp.get_svg() dwg.add(fpga.placement_creator.design_grid.render_grid(return_group=True)) pattern = dwg.pattern(size=(4 * CPP, 2 * SC_HEIGHT), patternUnits="userSpaceOnUse") pattern.add(dwg.circle(center=(2, 2), r=1, fill="black")) pattern.add(dwg.circle(center=(2, SC_HEIGHT + 2), r=1, fill="red")) dwg.defs.add(pattern) dwg.defs.elements[0].elements[0].attribs["fill"] = pattern.get_funciri() dwg.saveas(filename, pretty=True, indent=4) def save_netlist(fpga: OpenFPGA): """ Save OpenFPGA netlist """ base_dir = (VERILOG_DIRECTORY, "SRC") shutil.rmtree( os.path.join(*base_dir), ignore_errors=True, ) Path(os.path.join(*base_dir)).mkdir(parents=True, exist_ok=True) options = { "skip_constraints": True, "sort_cables": True, "sort_print": True, "sort_instances": True, "sort_ports": True, } fpga.save_netlist("logical_tile*", os.path.join(*base_dir, "submodules"), **options) fpga.save_netlist("*tile*", os.path.join(*base_dir, "tile"), **options) fpga.save_netlist("fpga_core", os.path.join(*base_dir), **options) fpga.save_netlist("fpga_top", os.path.join(*base_dir), **options) fpga.save_netlist("[!BUFF][!TIE][!DFQ]*", os.path.join(*base_dir, "submodules"), write_blackbox=False, **options) include_file = os.path.join(*base_dir, "fabric_netlists.v") fpga.write_include_file(include_file) def dump_top_definition_ports(fpga: OpenFPGA, rpt_file, pattern=None): """ Create top level port """ portmap = OrderedDict() instances = {t.name: t for t in fpga.top_module.get_definitions()} instances = OrderedDict(sorted(instances.items(), reverse=True)) for def_name, defs in instances.items(): if "ASSIGN" in def_name: continue if def_name.startswith("const"): continue portmap[def_name] = sorted([f"{port.size:04d}_{port.direction:5s},{port.name}" for port in defs.get_ports("*")]) if pattern: portmap[def_name] = list(filter(pattern, portmap[def_name])) json.dump(portmap, open(rpt_file, "w", encoding="UTF-8"), indent=4) def print_time_elpased(msg): """ Prints runtime since previous call to this function """ global script_start_time script_start_time_new = time.time() logger.info("[%8.2d] %s", (script_start_time_new - script_start_time), msg) script_start_time = script_start_time_new if __name__ == "__main__": main()