From bbc9dab7ccaff54754bf76fa127c15aca3ea9974 Mon Sep 17 00:00:00 2001 From: Kelvin Chung Date: Mon, 26 Feb 2024 14:06:45 +0000 Subject: [PATCH] formatting --- FABulous.py | 447 +++--- fabric_cad/bit_gen.py | 216 +-- fabric_generator/code_generation_VHDL.py | 93 +- fabric_generator/code_generation_Verilog.py | 73 +- fabric_generator/code_generator.py | 33 +- fabric_generator/fabric.py | 375 +++-- fabric_generator/fabric_gen.py | 1496 ++++++++++++------- fabric_generator/file_parser.py | 368 +++-- fabric_generator/model_generation_npnr.py | 693 +++++++-- fabric_generator/model_generation_vpr.py | 1130 +++++++++----- fabric_generator/utilities.py | 936 +++++++----- geometry_generator/bel_geometry.py | 42 +- geometry_generator/fabric_geometry.py | 71 +- geometry_generator/geometry_gen.py | 2 - geometry_generator/geometry_obj.py | 4 +- geometry_generator/port_geometry.py | 45 +- geometry_generator/sm_geometry.py | 153 +- geometry_generator/tile_geometry.py | 242 ++- geometry_generator/wire_geometry.py | 104 +- 19 files changed, 4180 insertions(+), 2343 deletions(-) diff --git a/FABulous.py b/FABulous.py index acfe6123..c09e394e 100644 --- a/FABulous.py +++ b/FABulous.py @@ -44,33 +44,39 @@ from pathlib import PurePosixPath, PureWindowsPath import platform import traceback -readline.set_completer_delims(' \t\n') + +readline.set_completer_delims(" \t\n") histfile = "" histfile_size = 1000 -fabulousRoot = os.getenv('FAB_ROOT') +fabulousRoot = os.getenv("FAB_ROOT") if fabulousRoot is None: - print('FAB_ROOT environment variable not set!') + print("FAB_ROOT environment variable not set!") print("Use 'export FAB_ROOT='") sys.exit(-1) logger = logging.getLogger(__name__) logging.basicConfig( - format="[%(levelname)s]-%(asctime)s - %(message)s", level=logging.INFO) + format="[%(levelname)s]-%(asctime)s - %(message)s", level=logging.INFO +) # Create a FABulous Verilog project that contains all the required files def create_project(project_dir, type: Literal["verilog", "vhdl"] = "verilog"): if os.path.exists(project_dir): - print('Project directory already exists!') + print("Project directory already exists!") sys.exit() else: os.mkdir(f"{project_dir}") os.mkdir(f"{project_dir}/.FABulous") - shutil.copytree(f"{fabulousRoot}/fabric_files/FABulous_project_template_{type}/", - f"{project_dir}/", dirs_exist_ok=True) + shutil.copytree( + f"{fabulousRoot}/fabric_files/FABulous_project_template_{type}/", + f"{project_dir}/", + dirs_exist_ok=True, + ) + def get_path(path): system = platform.system() @@ -82,15 +88,19 @@ def get_path(path): else: raise NotImplementedError(f"Unsupported operating system: {system}") + class PlaceAndRouteError(Exception): """An exception to be thrown when place and route fails.""" + class SynthesisError(Exception): """An exception to be thrown when synthesis fails.""" + class BitstreamGenerationError(Exception): """An exception to be thrown when the bitstream generation fails.""" + class FABulous: fabricGenerator: FabricGenerator geometryGenerator: GeometryGenerator @@ -241,8 +251,10 @@ def inter(*args, **varargs): fun_to_wrap(*args, **varargs) except: import traceback + traceback.print_exc() sys.exit(1) + return inter tcl = tk.Tcl() @@ -253,7 +265,9 @@ def inter(*args, **varargs): for fun in dir(self.__class__): if fun.startswith("do_"): name = fun.strip("do_") - tcl.createcommand(name, wrap_with_except_handling(getattr(self, fun))) + tcl.createcommand( + name, wrap_with_except_handling(getattr(self, fun)) + ) # os.chdir(args.project_dir) tcl.eval(script) @@ -266,7 +280,12 @@ def postloop(self): readline.write_history_file(histfile) def precmd(self, line: str) -> str: - if ("gen" in line or "run" in line) and not self.fabricLoaded and "help" not in line and "?" not in line: + if ( + ("gen" in line or "run" in line) + and not self.fabricLoaded + and "help" not in line + and "?" not in line + ): logger.error("Fabric not loaded") return "" return line @@ -303,8 +322,7 @@ def do_shell(self, args): try: sp.run(args, shell=True) except sp.CalledProcessError: - logger.error( - "Could not execute the requested command.") + logger.error("Could not execute the requested command.") return def do_exit(self, *ignore): @@ -323,12 +341,14 @@ def do_load_fabric(self, args=""): self.fabricGen.loadFabric(self.csvFile) elif os.path.exists(f"{self.projectDir}/fabric.csv"): logger.info( - "Found fabric.csv in the project directory loading that file as the definition of the fabric") + "Found fabric.csv in the project directory loading that file as the definition of the fabric" + ) self.fabricGen.loadFabric(f"{self.projectDir}/fabric.csv") self.csvFile = f"{self.projectDir}/fabric.csv" else: logger.error( - "No argument is given and no csv file is set or the file does not exist") + "No argument is given and no csv file is set or the file does not exist" + ) return else: self.fabricGen.loadFabric(args[0]) @@ -336,12 +356,12 @@ def do_load_fabric(self, args=""): self.fabricLoaded = True # self.projectDir = os.path.split(self.csvFile)[0] - tileByPath = [f.name for f in os.scandir( - f"{self.projectDir}/Tile/") if f.is_dir()] + tileByPath = [ + f.name for f in os.scandir(f"{self.projectDir}/Tile/") if f.is_dir() + ] tileByFabric = list(self.fabricGen.fabric.tileDic.keys()) superTileByFabric = list(self.fabricGen.fabric.superTileDic.keys()) - self.allTile = list(set(tileByPath) & set( - tileByFabric + superTileByFabric)) + self.allTile = list(set(tileByPath) & set(tileByFabric + superTileByFabric)) logger.info("Complete") def complete_load_fabric(self, text, *ignored): @@ -363,9 +383,11 @@ def do_gen_config_mem(self, args): for i in args: logger.info(f"Generating configMem for {i}") self.fabricGen.setWriterOutputFile( - f"{self.projectDir}/Tile/{i}/{i}_ConfigMem.{self.extension}") + f"{self.projectDir}/Tile/{i}/{i}_ConfigMem.{self.extension}" + ) self.fabricGen.genConfigMem( - i, f"{self.projectDir}/Tile/{i}/{i}_ConfigMem.csv") + i, f"{self.projectDir}/Tile/{i}/{i}_ConfigMem.csv" + ) logger.info(f"Generating configMem complete") def complete_gen_config_mem(self, text, *ignored): @@ -378,7 +400,8 @@ def do_gen_switch_matrix(self, args): for i in args: logger.info(f"Generating switch matrix for {i}") self.fabricGen.setWriterOutputFile( - f"{self.projectDir}/Tile/{i}/{i}_switch_matrix.{self.extension}") + f"{self.projectDir}/Tile/{i}/{i}_switch_matrix.{self.extension}" + ) self.fabricGen.genSwitchMatrix(i) logger.info("Switch matrix generation complete") @@ -391,15 +414,19 @@ def do_gen_tile(self, args): args = self.parse(args) logger.info(f"Generating tile {' '.join(args)}") for t in args: - if subTiles := [f.name for f in os.scandir(f"{self.projectDir}/Tile/{t}") if f.is_dir()]: + if subTiles := [ + f.name for f in os.scandir(f"{self.projectDir}/Tile/{t}") if f.is_dir() + ]: logger.info( - f"{t} is a super tile, generating {t} with sub tiles {' '.join(subTiles)}") + f"{t} is a super tile, generating {t} with sub tiles {' '.join(subTiles)}" + ) for st in subTiles: # Gen switch matrix logger.info(f"Generating switch matrix for tile {t}") logger.info(f"Generating switch matrix for {st}") self.fabricGen.setWriterOutputFile( - f"{self.projectDir}/Tile/{t}/{st}/{st}_switch_matrix.{self.extension}") + f"{self.projectDir}/Tile/{t}/{st}/{st}_switch_matrix.{self.extension}" + ) self.fabricGen.genSwitchMatrix(st) logger.info(f"Generated switch matrix for {st}") @@ -407,23 +434,27 @@ def do_gen_tile(self, args): logger.info(f"Generating configMem for tile {t}") logger.info(f"Generating ConfigMem for {st}") self.fabricGen.setWriterOutputFile( - f"{self.projectDir}/Tile/{t}/{st}/{st}_ConfigMem.{self.extension}") + f"{self.projectDir}/Tile/{t}/{st}/{st}_ConfigMem.{self.extension}" + ) self.fabricGen.genConfigMem( - st, f"{self.projectDir}/Tile/{t}/{st}/{st}_ConfigMem.csv") + st, f"{self.projectDir}/Tile/{t}/{st}/{st}_ConfigMem.csv" + ) logger.info(f"Generated configMem for {st}") # Gen tile logger.info(f"Generating subtile for tile {t}") logger.info(f"Generating subtile {st}") self.fabricGen.setWriterOutputFile( - f"{self.projectDir}/Tile/{t}/{st}/{st}.{self.extension}") + f"{self.projectDir}/Tile/{t}/{st}/{st}.{self.extension}" + ) self.fabricGen.genTile(st) logger.info(f"Generated subtile {st}") # Gen super tile logger.info(f"Generating super tile {t}") self.fabricGen.setWriterOutputFile( - f"{self.projectDir}/Tile/{t}/{t}.{self.extension}") + f"{self.projectDir}/Tile/{t}/{t}.{self.extension}" + ) self.fabricGen.genSuperTile(t) logger.info(f"Generated super tile {t}") continue @@ -437,7 +468,8 @@ def do_gen_tile(self, args): logger.info(f"Generating tile {t}") # Gen tile self.fabricGen.setWriterOutputFile( - f"{self.projectDir}/Tile/{t}/{t}.{self.extension}") + f"{self.projectDir}/Tile/{t}/{t}.{self.extension}" + ) self.fabricGen.genTile(t) logger.info(f"Generated tile {t}") @@ -457,7 +489,8 @@ def do_gen_fabric(self, *ignored): logger.info(f"Generating fabric {self.fabricGen.fabric.name}") self.do_gen_all_tile() self.fabricGen.setWriterOutputFile( - f"{self.projectDir}/Fabric/{self.fabricGen.fabric.name}.{self.extension}") + f"{self.projectDir}/Fabric/{self.fabricGen.fabric.name}.{self.extension}" + ) self.fabricGen.genFabric() logger.info("Fabric generation complete") @@ -478,7 +511,9 @@ def do_gen_geometry(self, *vargs): padding = int(vargs[0]) logger.info(f"Setting padding to {padding}") except ValueError: - logger.warning(f"Faulty padding argument, defaulting to {paddingDefault}") + logger.warning( + f"Faulty padding argument, defaulting to {paddingDefault}" + ) padding = paddingDefault else: logger.info(f"No padding specified, defaulting to {paddingDefault}") @@ -491,19 +526,18 @@ def do_gen_geometry(self, *vargs): else: logger.error("padding has to be between 4 and 32 inclusively!") - def do_gen_bitStream_spec(self, *ignored): "Generate the bitstream specification of the fabric" logger.info("Generating bitstream specification") specObject = self.fabricGen.genBitStreamSpec() - logger.info( - f"output file: {self.projectDir}/{metaDataDir}/bitStreamSpec.bin") - with open(f"{self.projectDir}/{metaDataDir}/bitStreamSpec.bin", "wb") as outFile: + logger.info(f"output file: {self.projectDir}/{metaDataDir}/bitStreamSpec.bin") + with open( + f"{self.projectDir}/{metaDataDir}/bitStreamSpec.bin", "wb" + ) as outFile: pickle.dump(specObject, outFile) - logger.info( - f"output file: {self.projectDir}/{metaDataDir}/bitStreamSpec.csv") + logger.info(f"output file: {self.projectDir}/{metaDataDir}/bitStreamSpec.csv") with open(f"{self.projectDir}/{metaDataDir}/bitStreamSpec.csv", "w") as f: w = csv.writer(f) for key1 in specObject["TileSpecs"]: @@ -516,7 +550,8 @@ def do_gen_top_wrapper(self, *ignored): "Generate the top wrapper of the fabric" logger.info("Generating top wrapper") self.fabricGen.setWriterOutputFile( - f"{self.projectDir}/Fabric/{self.fabricGen.fabric.name}_top.{self.extension}") + f"{self.projectDir}/Fabric/{self.fabricGen.fabric.name}_top.{self.extension}" + ) self.fabricGen.genTopWrapper() logger.info("Generated top wrapper") @@ -535,8 +570,7 @@ def do_gen_model_npnr(self, *ignored): "Generate a npnr model of the fabric" logger.info("Generating npnr model") npnrModel = self.fabricGen.genModelNpnr() - logger.info( - f"output file: {self.projectDir}/{metaDataDir}/pips.txt") + logger.info(f"output file: {self.projectDir}/{metaDataDir}/pips.txt") with open(f"{self.projectDir}/{metaDataDir}/pips.txt", "w") as f: f.write(npnrModel[0]) @@ -544,13 +578,11 @@ def do_gen_model_npnr(self, *ignored): with open(f"{self.projectDir}/{metaDataDir}/bel.txt", "w") as f: f.write(npnrModel[1]) - logger.info( - f"output file: {self.projectDir}/{metaDataDir}/bel.v2.txt") + logger.info(f"output file: {self.projectDir}/{metaDataDir}/bel.v2.txt") with open(f"{self.projectDir}/{metaDataDir}/bel.v2.txt", "w") as f: f.write(npnrModel[2]) - logger.info( - f"output file: {self.projectDir}/{metaDataDir}/template.pcf") + logger.info(f"output file: {self.projectDir}/{metaDataDir}/template.pcf") with open(f"{self.projectDir}/{metaDataDir}/template.pcf", "w") as f: f.write(npnrModel[3]) @@ -561,15 +593,13 @@ def do_gen_model_npnr_pair(self): "Generate a pair npnr model of the fabric. (Currently not working)" logger.info("Generating pair npnr model") if self.csvFile: - FabricFile = [i.strip('\n').split(',') for i in open(self.csvFile)] + FabricFile = [i.strip("\n").split(",") for i in open(self.csvFile)] fabric = GetFabric(FabricFile) fabricObject = genFabricObject(fabric, FabricFile) pipFile = open(f"{self.projectDir}/{metaDataDir}/pips.txt", "w") belFile = open(f"{self.projectDir}/{metaDataDir}/bel.txt", "w") - pairFile = open( - f"{self.projectDir}/{metaDataDir}/wirePairs.csv", "w") - constraintFile = open( - f"{self.projectDir}/{metaDataDir}/template.pcf", "w") + pairFile = open(f"{self.projectDir}/{metaDataDir}/wirePairs.csv", "w") + constraintFile = open(f"{self.projectDir}/{metaDataDir}/template.pcf", "w") npnrModel = model_gen_npnr.genNextpnrModelOld(fabricObject, False) @@ -601,8 +631,7 @@ def do_gen_model_vpr(self, args): logger.info("Generating vpr model") vprModel = self.fabricGen.genModelVPR(args) - logger.info( - f"Output file: {self.projectDir}/{metaDataDir}/architecture.xml") + logger.info(f"Output file: {self.projectDir}/{metaDataDir}/architecture.xml") with open(f"{self.projectDir}/{metaDataDir}/architecture.xml", "w") as f: f.write(vprModel) @@ -610,20 +639,19 @@ def do_gen_model_vpr(self, args): # Write the routing resource graph vprRoutingResource = routingResourceInfo[0] logger.info( - f"Output file: {self.projectDir}/{metaDataDir}/routing_resource.xml") + f"Output file: {self.projectDir}/{metaDataDir}/routing_resource.xml" + ) with open(f"{self.projectDir}/{metaDataDir}/routing_resource.xml", "w") as f: f.write(vprRoutingResource) # Write maxWidth.txt vprMaxWidth = routingResourceInfo[1] - logger.info( - f"Output file: {self.projectDir}/{metaDataDir}/maxWidth.txt") + logger.info(f"Output file: {self.projectDir}/{metaDataDir}/maxWidth.txt") with open(f"{self.projectDir}/{metaDataDir}/maxWidth.txt", "w") as f: f.write(str(vprMaxWidth)) vprConstrain = self.fabricGen.genModelVPRConstrains() - logger.info( - f"Output file: {self.projectDir}/{metaDataDir}/fab_constraints.xml") + logger.info(f"Output file: {self.projectDir}/{metaDataDir}/fab_constraints.xml") with open(f"{self.projectDir}/{metaDataDir}/fab_constraints.xml", "w") as f: f.write(vprConstrain) @@ -642,8 +670,7 @@ def do_hls_create_project(self): name = get_path(self.projectDir).name with open(f"{self.projectDir}/HLS/Makefile", "w") as f: f.write(f"NAME = {name}\n") - f.write( - f"LOCAL_CONFIG = -legup-config=/root/{name}/config.tcl\n") + f.write(f"LOCAL_CONFIG = -legup-config=/root/{name}/config.tcl\n") f.write("LEVEL = /root/legup-4.0/examples\n") f.write("include /root/legup-4.0/examples/Makefile.common\n") f.write("include /root/legup-4.0/examples/Makefile.ancillary\n") @@ -651,7 +678,8 @@ def do_hls_create_project(self): with open(f"./HLS/{name}.c", "w") as f: f.write( - '#include \nint main() {\n printf("Hello World");\n return 0;\n}') + '#include \nint main() {\n printf("Hello World");\n return 0;\n}' + ) os.chmod(f"./HLS/config.tcl", 0o666) os.chmod(f"./HLS/Makefile", 0o666) @@ -663,26 +691,34 @@ def do_hls_generate_verilog(self): if not os.path.exists(f"./HLS/generated_file"): os.mkdir(f"{name}/generated_file") else: - os.system( - f"rm -rf ./create_HLS_project/generated_file/*") + os.system(f"rm -rf ./create_HLS_project/generated_file/*") client = docker.from_env() containers = client.containers.run( - "legup:latest", f'make -C /root/{name} ', - volumes=[f"{os.path.abspath(os.getcwd())}/{self.projectDir}/HLS/:/root/{name}"]) + "legup:latest", + f"make -C /root/{name} ", + volumes=[ + f"{os.path.abspath(os.getcwd())}/{self.projectDir}/HLS/:/root/{name}" + ], + ) print(containers.decode("utf-8")) # move all the generated files into a folder for f in os.listdir(f"./HLS"): - if not f.endswith(".v") and not f.endswith(".c") and not f.endswith(".h") and not f.endswith("_labeled.c") \ - and not f.endswith(".tcl") and f != "Makefile" and os.path.isfile(f"./HLS/{f}"): - shutil.move(f"./HLS/{f}", - f"./HLS/generated_file/") + if ( + not f.endswith(".v") + and not f.endswith(".c") + and not f.endswith(".h") + and not f.endswith("_labeled.c") + and not f.endswith(".tcl") + and f != "Makefile" + and os.path.isfile(f"./HLS/{f}") + ): + shutil.move(f"./HLS/{f}", f"./HLS/generated_file/") if f.endswith("_labeled.c"): - shutil.move(f"./HLS/{f}", - f"./HLS/generated_file/") + shutil.move(f"./HLS/{f}", f"./HLS/generated_file/") try: os.chmod(f"./HLS/{name}.v", 0o666) @@ -708,10 +744,8 @@ def do_hls_generate_verilog(self): "output wire finish;\n" "input waitrequest;\n" "output wire[31:0] return_val;\n" - "wire clk;\n" "(* keep *) Global_Clock inst_clk (.CLK(clk));\n" - "(* keep *) top inst_top(.clk(clk), .reset(reset), .start(start),\n" " .finish(finish), .waitrequest(waitrequest), .return_val(return_val));\n" "\n" @@ -722,7 +756,8 @@ def do_hls_generate_verilog(self): f.write(s) except: print( - "Verilog file is not generated, potentialy due to the error in the C code") + "Verilog file is not generated, potentialy due to the error in the C code" + ) exit(-1) def do_synthesis_npnr(self, args): @@ -730,9 +765,10 @@ def do_synthesis_npnr(self, args): args = self.parse(args) if len(args) != 1: logger.error("Usage: synthesis_npnr ") - raise TypeError(f"do_place_and_route_npnr takes exactly one argument ({len(args)} given)") - logger.info( - f"Running synthesis that targeting Nextpnr with design {args[0]}") + raise TypeError( + f"do_place_and_route_npnr takes exactly one argument ({len(args)} given)" + ) + logger.info(f"Running synthesis that targeting Nextpnr with design {args[0]}") path = get_path(args[0]) parent = path.parent verilog_file = path.name @@ -742,14 +778,18 @@ def do_synthesis_npnr(self, args): """ No verilog file provided. Usage: synthesis_npnr - """) + """ + ) return json_file = top_module_name + ".json" - runCmd = ["yosys", - "-p", f"synth_fabulous -top top_wrapper -json {self.projectDir}/{parent}/{json_file}", - f"{self.projectDir}/{parent}/{verilog_file}", - f"{self.projectDir}/{parent}/top_wrapper.v", ] + runCmd = [ + "yosys", + "-p", + f"synth_fabulous -top top_wrapper -json {self.projectDir}/{parent}/{json_file}", + f"{self.projectDir}/{parent}/{verilog_file}", + f"{self.projectDir}/{parent}/top_wrapper.v", + ] try: sp.run(runCmd, check=True) logger.info("Synthesis completed") @@ -765,9 +805,10 @@ def do_synthesis_blif(self, args): args = self.parse(args) if len(args) != 1: logger.error("Usage: synthesis_blif ") - raise TypeError(f"do_place_and_route_npnr takes exactly one argument ({len(args)} given)") - logger.info( - f"Running synthesis that targeting BLIF with design {args[0]}") + raise TypeError( + f"do_place_and_route_npnr takes exactly one argument ({len(args)} given)" + ) + logger.info(f"Running synthesis that targeting BLIF with design {args[0]}") path = get_path(args[0]) parent = path.parent @@ -778,14 +819,18 @@ def do_synthesis_blif(self, args): """ No verilog file provided. Usage: synthesis_blif - """) + """ + ) return blif_file = top_module_name + ".blif" - runCmd = ["yosys", - "-p", f"synth_fabulous -top top_wrapper -blif {self.projectDir}/{parent}/{blif_file} -vpr", - f"{self.projectDir}/{parent}/{verilog_file}", - f"{self.projectDir}/{parent}/top_wrapper.v", ] + runCmd = [ + "yosys", + "-p", + f"synth_fabulous -top top_wrapper -blif {self.projectDir}/{parent}/{blif_file} -vpr", + f"{self.projectDir}/{parent}/{verilog_file}", + f"{self.projectDir}/{parent}/top_wrapper.v", + ] try: sp.run(runCmd, check=True) logger.info("Synthesis completed.") @@ -804,10 +849,13 @@ def do_place_and_route_npnr(self, args): """ args = self.parse(args) if len(args) != 1: - logger.error("Usage: place_and_route_npnr ( is generated by Yosys. Generate it by running synthesis_npnr.)") - raise TypeError(f"do_place_and_route_npnr takes exactly one argument ({len(args)} given)") - logger.info( - f"Running Placement and Routing with Nextpnr for design {args[0]}") + logger.error( + "Usage: place_and_route_npnr ( is generated by Yosys. Generate it by running synthesis_npnr.)" + ) + raise TypeError( + f"do_place_and_route_npnr takes exactly one argument ({len(args)} given)" + ) + logger.info(f"Running Placement and Routing with Nextpnr for design {args[0]}") path = get_path(args[0]) parent = path.parent json_file = path.name @@ -818,7 +866,8 @@ def do_place_and_route_npnr(self, args): """ No json file provided. Usage: place_and_route_npnr ( is generated by Yosys. Generate it by running synthesis_npnr.) - """) + """ + ) return fasm_file = top_module_name + ".fasm" @@ -827,39 +876,51 @@ def do_place_and_route_npnr(self, args): if parent == "": parent = "." - if not os.path.exists(f"{self.projectDir}/.FABulous/pips.txt") or not os.path.exists( - f"{self.projectDir}/.FABulous/bel.txt"): + if not os.path.exists( + f"{self.projectDir}/.FABulous/pips.txt" + ) or not os.path.exists(f"{self.projectDir}/.FABulous/bel.txt"): logger.error( - "Pips and Bel files are not found, please run model_gen_npnr first") + "Pips and Bel files are not found, please run model_gen_npnr first" + ) raise FileNotFoundError if os.path.exists(f"{self.projectDir}/{parent}"): # TODO rewriting the fab_arch script so no need to copy file for work around if f"{json_file}" in os.listdir(f"{self.projectDir}/{parent}"): - runCmd = [f"FAB_ROOT={self.projectDir}", - f"nextpnr-generic", - "--uarch", "fabulous", - "--json", f"{self.projectDir}/{parent}/{json_file}", - "-o", f"fasm={self.projectDir}/{parent}/{fasm_file}", - "--verbose", - "--log", f"{self.projectDir}/{parent}/{log_file}"] + runCmd = [ + f"FAB_ROOT={self.projectDir}", + f"nextpnr-generic", + "--uarch", + "fabulous", + "--json", + f"{self.projectDir}/{parent}/{json_file}", + "-o", + f"fasm={self.projectDir}/{parent}/{fasm_file}", + "--verbose", + "--log", + f"{self.projectDir}/{parent}/{log_file}", + ] try: - sp.run(" ".join(runCmd), stdout=sys.stdout, - stderr=sp.STDOUT, check=True, shell=True) + sp.run( + " ".join(runCmd), + stdout=sys.stdout, + stderr=sp.STDOUT, + check=True, + shell=True, + ) except sp.CalledProcessError: - logger.error( - f"Placement and Routing failed.") + logger.error(f"Placement and Routing failed.") raise PlaceAndRouteError else: logger.error( - f"Cannot find file \"{json_file}\" in path \"./{parent}/\", which is generated by running Yosys with Nextpnr backend (e.g. synthesis_npnr).") + f'Cannot find file "{json_file}" in path "./{parent}/", which is generated by running Yosys with Nextpnr backend (e.g. synthesis_npnr).' + ) raise FileNotFoundError logger.info("Placement and Routing completed") else: - logger.error( - f"Directory {self.projectDir}/{parent} does not exist.") + logger.error(f"Directory {self.projectDir}/{parent} does not exist.") raise FileNotFoundError def complete_place_and_route_npnr(self, text, *ignored): @@ -870,35 +931,42 @@ def do_place_and_route_vpr(self, args): args = self.parse(args) if len(args) != 1: logger.error("Usage: place_and_route_vpr ") - raise TypeError(f"do_place_and_route_npnr takes exactly one argument ({len(args)} given)") - logger.info( - f"Running Placement and Routing with vpr for design {args[0]}") + raise TypeError( + f"do_place_and_route_npnr takes exactly one argument ({len(args)} given)" + ) + logger.info(f"Running Placement and Routing with vpr for design {args[0]}") path = get_path(args[0]) blif_file = path.name if os.path.exists(f"{self.projectDir}/user_design/{blif_file}"): - if not os.getenv('VTR_ROOT'): + if not os.getenv("VTR_ROOT"): logger.error( - "VTR_ROOT is not set, please set it to the VPR installation directory") + "VTR_ROOT is not set, please set it to the VPR installation directory" + ) exit(-1) - vtr_root = os.getenv('VTR_ROOT') - - runCmd = [f"{vtr_root}/vpr/vpr", - f"{self.projectDir}/.FABulous/architecture.xml", - f"{self.projectDir}/user_design/{blif_file}", - "--read_rr_graph", f".FABulous/routing_resources.xml", - "--echo_file", "on", - "--route_chan_width", "16"] + vtr_root = os.getenv("VTR_ROOT") + + runCmd = [ + f"{vtr_root}/vpr/vpr", + f"{self.projectDir}/.FABulous/architecture.xml", + f"{self.projectDir}/user_design/{blif_file}", + "--read_rr_graph", + f".FABulous/routing_resources.xml", + "--echo_file", + "on", + "--route_chan_width", + "16", + ] try: sp.run(runCmd, check=True) except sp.CalledProcessError: - logger.error( - "Placement and Routing failed.") + logger.error("Placement and Routing failed.") raise PlaceAndRouteError else: logger.error( - f"Cannot find {blif_file}, which is generated by running Yosys with blif backend") + f"Cannot find {blif_file}, which is generated by running Yosys with blif backend" + ) raise FileNotFoundError logger.info("Placement and Routing completed") @@ -921,35 +989,39 @@ def do_gen_bitStream_binary(self, args): """ No fasm file provided. Usage: gen_bitStream_binary - """) + """ + ) return bitstream_file = top_module_name + ".bin" if not os.path.exists(f"{self.projectDir}/.FABulous/bitStreamSpec.bin"): logger.error( - "Cannot find bitStreamSpec.bin file, which is generated by running gen_bitStream_spec") + "Cannot find bitStreamSpec.bin file, which is generated by running gen_bitStream_spec" + ) return if not os.path.exists(f"{self.projectDir}/{parent}/{fasm_file}"): logger.error( - f"Cannot find {self.projectDir}/{parent}/{fasm_file} file which is generated by running place_and_route_npnr or place_and_route_vpr. Potentially Place and Route Failed.") + f"Cannot find {self.projectDir}/{parent}/{fasm_file} file which is generated by running place_and_route_npnr or place_and_route_vpr. Potentially Place and Route Failed." + ) return - logger.info( - f"Generating Bitstream for design {self.projectDir}/{path}") + logger.info(f"Generating Bitstream for design {self.projectDir}/{path}") logger.info(f"Outputting to {self.projectDir}/{parent}/{bitstream_file}") - runCmd = ["python3", f"{fabulousRoot}/fabric_cad/bit_gen.py", - "-genBitstream", - f"{self.projectDir}/{parent}/{fasm_file}", - f"{self.projectDir}/.FABulous/bitStreamSpec.bin", - f"{self.projectDir}/{parent}/{bitstream_file}"] + runCmd = [ + "python3", + f"{fabulousRoot}/fabric_cad/bit_gen.py", + "-genBitstream", + f"{self.projectDir}/{parent}/{fasm_file}", + f"{self.projectDir}/.FABulous/bitStreamSpec.bin", + f"{self.projectDir}/{parent}/{bitstream_file}", + ] try: sp.run(runCmd, check=True) except sp.CalledProcessError: - logger.error( - "Bitstream generation failed") + logger.error("Bitstream generation failed") raise BitstreamGenerationError logger.info("Bitstream generated") @@ -963,11 +1035,11 @@ def do_run_FABulous_bitstream(self, *args): args = self.parse(args[0]) if len(args) != 2: logger.error( - "Usage: run_FABulous_bitstream ") + "Usage: run_FABulous_bitstream " + ) return elif len(args) != 2: - logger.error( - "Usage: run_FABulous_bitstream ") + logger.error("Usage: run_FABulous_bitstream ") return verilog_file_path = get_path(args[1]) @@ -978,7 +1050,8 @@ def do_run_FABulous_bitstream(self, *args): """ No verilog file provided. Usage: run_FABulous_bitstream - """) + """ + ) return json_file_path = file_path_no_suffix.with_suffix(".json") @@ -994,8 +1067,7 @@ def do_run_FABulous_bitstream(self, *args): self.do_place_and_route_npnr(str(json_file_path)) self.do_gen_bitStream_binary(str(fasm_file_path)) else: - logger.error( - "Usage: run_FABulous_bitstream ") + logger.error("Usage: run_FABulous_bitstream ") return return 0 @@ -1016,8 +1088,7 @@ def do_tcl(self, args): path = get_path(path_str) name = path.stem if not os.path.exists(path_str): - logger.error( - f"Cannot find {path_str}") + logger.error(f"Cannot find {path_str}") return logger.info(f"Execute TCL script {path_str}") @@ -1039,40 +1110,50 @@ def complete_tcl(self, text, *ignored): print("Need Python 3.9 or above to run FABulous") exit(-1) parser = argparse.ArgumentParser( - description='The command line interface for FABulous') + description="The command line interface for FABulous" + ) + + parser.add_argument("project_dir", help="The directory to the project folder") + + parser.add_argument( + "-c", + "--createProject", + default=False, + action="store_true", + help="Create a new project", + ) parser.add_argument( - "project_dir", help="The directory to the project folder") - - parser.add_argument('-c', '--createProject', - default=False, - action='store_true', - help='Create a new project') - - parser.add_argument('-csv', - default="", - nargs=1, - help="Log all the output from the terminal") - - parser.add_argument('-s', '--script', - default="", - help="Run FABulous with a FABulous script") - - parser.add_argument('-log', - default=False, - nargs='?', - const="FABulous.log", - help="Log all the output from the terminal") - - parser.add_argument('-w', '--writer', - default="verilog", - choices=['verilog', 'vhdl'], - help="Set the type of HDL code generated by the tool. Currently support Verilog and VHDL (Default using Verilog)") - - parser.add_argument('-md', '--metaDataDir', - default=".FABulous", - nargs=1, - help="Set the output directory for the meta data files eg. pip.txt, bel.txt") + "-csv", default="", nargs=1, help="Log all the output from the terminal" + ) + + parser.add_argument( + "-s", "--script", default="", help="Run FABulous with a FABulous script" + ) + + parser.add_argument( + "-log", + default=False, + nargs="?", + const="FABulous.log", + help="Log all the output from the terminal", + ) + + parser.add_argument( + "-w", + "--writer", + default="verilog", + choices=["verilog", "vhdl"], + help="Set the type of HDL code generated by the tool. Currently support Verilog and VHDL (Default using Verilog)", + ) + + parser.add_argument( + "-md", + "--metaDataDir", + default=".FABulous", + nargs=1, + help="Set the output directory for the meta data files eg. pip.txt, bel.txt", + ) args = parser.parse_args() @@ -1084,7 +1165,9 @@ def complete_tcl(self, text, *ignored): exit(0) if not os.path.exists(f"{args.project_dir}/.FABulous"): - print("The directory provided is not a FABulous project as it does not have a .FABulous folder") + print( + "The directory provided is not a FABulous project as it does not have a .FABulous folder" + ) exit(-1) else: writer = VerilogWriter() @@ -1093,14 +1176,16 @@ def complete_tcl(self, text, *ignored): if args.writer == "verilog": writer = VerilogWriter() - fabShell = FABulousShell( - FABulous(writer, fabricCSV=args.csv), args.project_dir, args.script) + FABulous(writer, fabricCSV=args.csv), args.project_dir, args.script + ) if args.metaDataDir: metaDataDir = args.metaDataDir - histfile = os.path.expanduser(f'{args.project_dir}/{metaDataDir}/.fabulous_history') + histfile = os.path.expanduser( + f"{args.project_dir}/{metaDataDir}/.fabulous_history" + ) if args.log: with open(args.log, "w") as log: diff --git a/fabric_cad/bit_gen.py b/fabric_cad/bit_gen.py index 7d423c05..d5dda196 100644 --- a/fabric_cad/bit_gen.py +++ b/fabric_cad/bit_gen.py @@ -9,90 +9,107 @@ import numpy import pickle import csv -from fasm import * #Remove this line if you do not have the fasm library installed and will not be generating a bitstream - +from fasm import * # Remove this line if you do not have the fasm library installed and will not be generating a bitstream + + def replace(string, substitutions): substrings = sorted(substitutions, key=len, reverse=True) - regex = re.compile('|'.join(map(re.escape, substrings))) + regex = re.compile("|".join(map(re.escape, substrings))) return regex.sub(lambda match: substitutions[match.group(0)], string) - + + def bitstring_to_bytes(s): - return int(s, 2).to_bytes((len(s) + 7) // 8, byteorder='big') + return int(s, 2).to_bytes((len(s) + 7) // 8, byteorder="big") -#CAD methods from summer vacation project 2020 +# CAD methods from summer vacation project 2020 -#Method to generate bitstream in the output format - more detail at the end + +# Method to generate bitstream in the output format - more detail at the end def genBitstream(fasmFile: str, specFile: str, bitstreamFile: str): lGen = parse_fasm_filename(fasmFile) canonStr = fasm_tuple_to_string(lGen, True) canonList = list(parse_fasm_string(canonStr)) - specDict = pickle.load(open(specFile,"rb")) + specDict = pickle.load(open(specFile, "rb")) tileDict = {} tileDict_No_Mask = {} FrameBitsPerRow = specDict["ArchSpecs"]["FrameBitsPerRow"] MaxFramesPerCol = specDict["ArchSpecs"]["MaxFramesPerCol"] - #Change this so it has the actual right dimensions, initialised as an empty bitstream + # Change this so it has the actual right dimensions, initialised as an empty bitstream for tile in specDict["TileMap"].keys(): - tileDict[tile] = [0]*(MaxFramesPerCol*FrameBitsPerRow) - tileDict_No_Mask[tile] = [0]*(MaxFramesPerCol*FrameBitsPerRow) + tileDict[tile] = [0] * (MaxFramesPerCol * FrameBitsPerRow) + tileDict_No_Mask[tile] = [0] * (MaxFramesPerCol * FrameBitsPerRow) ###NOTE: SOME OF THE FOLLOWING METHODS HAVE BEEN CHANGED DUE TO A MODIFIED BITSTREAM SPEC FORMAT - #Please bear in mind that the tilespecs are now mapped by tile loc and not by cell type + # Please bear in mind that the tilespecs are now mapped by tile loc and not by cell type for line in canonList: - if 'CLK' in set_feature_to_str(line.set_feature): + if "CLK" in set_feature_to_str(line.set_feature): continue - if (line.set_feature): + if line.set_feature: tileVals = set_feature_to_str(line.set_feature).split(".") tileLoc = tileVals[0] featureName = ".".join((tileVals[1], tileVals[2])) if tileLoc not in specDict["TileMap"].keys(): raise Exception("Tile found in fasm file not found in bitstream spec") - tileType = specDict["TileMap"][tileLoc] #Set the necessary bits high + tileType = specDict["TileMap"][tileLoc] # Set the necessary bits high if featureName in specDict["TileSpecs"][tileLoc].keys(): if specDict["TileSpecs"][tileLoc][featureName]: for bitIndex in specDict["TileSpecs"][tileLoc][featureName]: - tileDict[tileLoc][bitIndex] = int(specDict["TileSpecs"][tileLoc][featureName][bitIndex]) - for bitIndex_No_Mask in specDict["TileSpecs_No_Mask"][tileLoc][featureName]: - tileDict_No_Mask[tileLoc][bitIndex_No_Mask] = int(specDict["TileSpecs_No_Mask"][tileLoc][featureName][bitIndex_No_Mask]) + tileDict[tileLoc][bitIndex] = int( + specDict["TileSpecs"][tileLoc][featureName][bitIndex] + ) + for bitIndex_No_Mask in specDict["TileSpecs_No_Mask"][tileLoc][ + featureName + ]: + tileDict_No_Mask[tileLoc][bitIndex_No_Mask] = int( + specDict["TileSpecs_No_Mask"][tileLoc][featureName][ + bitIndex_No_Mask + ] + ) else: - #print(specDict["TileSpecs"][tileLoc].keys()) + # print(specDict["TileSpecs"][tileLoc].keys()) print(tileType) print(tileLoc) print(featureName) - raise Exception("Feature found in fasm file was not found in the bitstream spec") - + raise Exception( + "Feature found in fasm file was not found in the bitstream spec" + ) - #Write output string and introduce mask + # Write output string and introduce mask coordsRE = re.compile("X(\d*)Y(\d*)") num_columns = 0 num_rows = 0 for tileKey in tileDict: coordsMatch = coordsRE.match(tileKey) - num_columns = max(int(coordsMatch.group(1))+1,num_columns) - num_rows = max(int(coordsMatch.group(2))+1,num_rows) + num_columns = max(int(coordsMatch.group(1)) + 1, num_columns) + num_rows = max(int(coordsMatch.group(2)) + 1, num_rows) outStr = "" - bitStr = bytes.fromhex('00AAFF01000000010000000000000000FAB0FAB1') - bit_array = [[b'' for x in range(20)] for y in range(num_columns)] + bitStr = bytes.fromhex("00AAFF01000000010000000000000000FAB0FAB1") + bit_array = [[b"" for x in range(20)] for y in range(num_columns)] - verilog_str = '' - vhdl_str = 'library IEEE;\nuse IEEE.STD_LOGIC_1164.ALL;\n\npackage emulate_bitstream is\n' + verilog_str = "" + vhdl_str = ( + "library IEEE;\nuse IEEE.STD_LOGIC_1164.ALL;\n\npackage emulate_bitstream is\n" + ) for tileKey in tileDict_No_Mask: - if specDict["TileMap"][tileKey] == "NULL" or len(specDict["FrameMap"][specDict["TileMap"][tileKey]]) == 0: + if ( + specDict["TileMap"][tileKey] == "NULL" + or len(specDict["FrameMap"][specDict["TileMap"][tileKey]]) == 0 + ): continue verilog_str += f"// {tileKey}, {specDict['TileMap'][tileKey]}\n" verilog_str += f"`define Tile_{tileKey}_Emulate_Bitstream {MaxFramesPerCol*FrameBitsPerRow}'b" vhdl_str += f"--{tileKey}, {specDict['TileMap'][tileKey]}\n" - vhdl_str += f"constant Tile_{tileKey}_Emulate_Bitstream : std_logic_vector({MaxFramesPerCol*FrameBitsPerRow}-1 downto 0) := \"" + vhdl_str += f'constant Tile_{tileKey}_Emulate_Bitstream : std_logic_vector({MaxFramesPerCol*FrameBitsPerRow}-1 downto 0) := "' - for i in range((MaxFramesPerCol*FrameBitsPerRow)-1,-1,-1): + for i in range((MaxFramesPerCol * FrameBitsPerRow) - 1, -1, -1): verilog_str += str(tileDict_No_Mask[tileKey][i]) vhdl_str += str(tileDict_No_Mask[tileKey][i]) verilog_str += "\n" @@ -109,81 +126,99 @@ def genBitstream(fasmFile: str, specFile: str, bitstreamFile: str): bitPos = 0 for frameIndex in range(MaxFramesPerCol): - #print (tileDict[tileKey]) #:FrameBitsPerRow*frameIndex + # print (tileDict[tileKey]) #:FrameBitsPerRow*frameIndex if specDict["TileMap"][tileKey] == "NULL": - frame_bit_row = '0' * FrameBitsPerRow + frame_bit_row = "0" * FrameBitsPerRow else: - frame_bit_row = (''.join(map(str, (tileDict[tileKey][FrameBitsPerRow*frameIndex:(FrameBitsPerRow*frameIndex)+FrameBitsPerRow]))))[::-1] - curStr += ",".join((f"frame{frameIndex}", str(frameIndex), str(FrameBitsPerRow), frame_bit_row)) + frame_bit_row = ( + "".join( + map( + str, + ( + tileDict[tileKey][ + FrameBitsPerRow + * frameIndex : (FrameBitsPerRow * frameIndex) + + FrameBitsPerRow + ] + ), + ) + ) + )[::-1] + curStr += ",".join( + ( + f"frame{frameIndex}", + str(frameIndex), + str(FrameBitsPerRow), + frame_bit_row, + ) + ) curStr += "\n" bit_hex = bitstring_to_bytes(frame_bit_row) bit_array[x][frameIndex] += bit_hex - #concatenatedTileDict[tileKey] = curStr + # concatenatedTileDict[tileKey] = curStr outStr += curStr + "\n" - #print(num_columns) + # print(num_columns) for i in range(num_columns): for j in range(20): bin_temp = f"{i:05b}"[::-1] - frame_select = ['0' for k in range(32)] - #bitStr += "X"+str(i)+", frame"+str(j)+"\n" + frame_select = ["0" for k in range(32)] + # bitStr += "X"+str(i)+", frame"+str(j)+"\n" - for k in range(-5,0,1): + for k in range(-5, 0, 1): frame_select[k] = bin_temp[k] - frame_select[j] = '1' - frame_select_temp = (''.join(frame_select))[::-1] + frame_select[j] = "1" + frame_select_temp = ("".join(frame_select))[::-1] bitStr += bitstring_to_bytes(frame_select_temp) bitStr += bit_array[i][j] - - #Note - format in output file is line by line: - #Tile Loc, Tile Type, X, Y, bits...... \n - #Each line is one tile + # Note - format in output file is line by line: + # Tile Loc, Tile Type, X, Y, bits...... \n + # Each line is one tile # Write out bitstream CSV representation - print(outStr, file = open(bitstreamFile.replace("bin","csv"), "w+")) + print(outStr, file=open(bitstreamFile.replace("bin", "csv"), "w+")) # Write out HDL representations - print(verilog_str, file = open(bitstreamFile.replace("bin","vh"), "w+")) - print(vhdl_str, file = open(bitstreamFile.replace("bin","vhd"), "w+")) + print(verilog_str, file=open(bitstreamFile.replace("bin", "vh"), "w+")) + print(vhdl_str, file=open(bitstreamFile.replace("bin", "vhd"), "w+")) # Write out binary representation - with open(bitstreamFile, 'bw+') as f: - f.write(bitStr) + with open(bitstreamFile, "bw+") as f: + f.write(bitStr) -#This class represents individual tiles in the architecture +# This class represents individual tiles in the architecture class Tile: tileType = "" bels = [] - wires = [] - atomicWires = [] #For storing single wires (to handle cascading and termination) + wires = [] + atomicWires = [] # For storing single wires (to handle cascading and termination) pips = [] belPorts = set() matrixFileName = "" - pipMuxes_MapSourceToSinks= [] - pipMuxes_MapSinkToSources= [] + pipMuxes_MapSourceToSinks = [] + pipMuxes_MapSinkToSources = [] - x = -1 #Init with negative values to ease debugging + x = -1 # Init with negative values to ease debugging y = -1 - + def __init__(self, inType): self.tileType = inType - def genTileLoc(self, separate = False): - if (separate): - return("X" + str(self.x), "Y" + str(self.y)) + def genTileLoc(self, separate=False): + if separate: + return ("X" + str(self.x), "Y" + str(self.y)) return "X" + str(self.x) + "Y" + str(self.y) -#This class represents the fabric as a whole +# This class represents the fabric as a whole class Fabric: tiles = [] height = 0 width = 0 cellTypes = [] - def __init__(self, inHeight, inWidth): self.width = inWidth self.height = inHeight @@ -214,43 +249,50 @@ def getTileAndWireByWireDest(self, loc: str, dest: str, jumps: bool = True): destx = tile.x + int(wire["xoffset"]) desttileLoc = f"X{destx}Y{desty}" - if (desttileLoc == loc) and (wire["destination"] + str(i) == dest): + if (desttileLoc == loc) and ( + wire["destination"] + str(i) == dest + ): return (tile, wire, i) return None - + + ##################################################################################### # Main ##################################################################################### -#Strip arguments +# Strip arguments caseProcessedArguments = list(map(lambda x: x.strip(), sys.argv)) processedArguments = list(map(lambda x: x.lower(), caseProcessedArguments)) flagRE = re.compile("-\S*") -if ('-genBitstream'.lower() in str(sys.argv).lower()): - argIndex = processedArguments.index('-genBitstream'.lower()) - - if len(processedArguments) <= argIndex + 3: - raise ValueError('\nError: -genBitstream expect three file names - the fasm file, the spec file and the output file') - elif (flagRE.match(caseProcessedArguments[argIndex + 1]) - or flagRE.match(caseProcessedArguments[argIndex + 2]) - or flagRE.match(caseProcessedArguments[argIndex + 3])): - raise ValueError('\nError: -genBitstream expect three file names, but found a flag in the arguments:' - f' {caseProcessedArguments[argIndex + 1]}, {caseProcessedArguments[argIndex + 2]}, {caseProcessedArguments[argIndex + 3]}\n') +if "-genBitstream".lower() in str(sys.argv).lower(): + argIndex = processedArguments.index("-genBitstream".lower()) - FasmFileName = caseProcessedArguments[argIndex + 1] - SpecFileName = caseProcessedArguments[argIndex + 2] - OutFileName = caseProcessedArguments[argIndex + 3] + if len(processedArguments) <= argIndex + 3: + raise ValueError( + "\nError: -genBitstream expect three file names - the fasm file, the spec file and the output file" + ) + elif ( + flagRE.match(caseProcessedArguments[argIndex + 1]) + or flagRE.match(caseProcessedArguments[argIndex + 2]) + or flagRE.match(caseProcessedArguments[argIndex + 3]) + ): + raise ValueError( + "\nError: -genBitstream expect three file names, but found a flag in the arguments:" + f" {caseProcessedArguments[argIndex + 1]}, {caseProcessedArguments[argIndex + 2]}, {caseProcessedArguments[argIndex + 3]}\n" + ) + + FasmFileName = caseProcessedArguments[argIndex + 1] + SpecFileName = caseProcessedArguments[argIndex + 2] + OutFileName = caseProcessedArguments[argIndex + 3] genBitstream(FasmFileName, SpecFileName, OutFileName) - -if ('-help'.lower() in str(sys.argv).lower()) or ('-h' in str(sys.argv).lower()): - print('') - print('Options/Switches') - print(' -genBitstream foo.fasm spec.txt bitstream.txt - generates a bitstream - the first file is the fasm file, the second is the bitstream spec and the third is the fasm file to write to') - - - +if ("-help".lower() in str(sys.argv).lower()) or ("-h" in str(sys.argv).lower()): + print("") + print("Options/Switches") + print( + " -genBitstream foo.fasm spec.txt bitstream.txt - generates a bitstream - the first file is the fasm file, the second is the bitstream spec and the third is the fasm file to write to" + ) diff --git a/fabric_generator/code_generation_VHDL.py b/fabric_generator/code_generation_VHDL.py index 87a1fdf0..6cad3c1f 100644 --- a/fabric_generator/code_generation_VHDL.py +++ b/fabric_generator/code_generation_VHDL.py @@ -18,13 +18,11 @@ def addComment(self, comment, onNewLine=False, end="", indentLevel=0) -> None: if onNewLine: self._add("") if self._content: - self._content[-1] += f"{' ':<{indentLevel*4}}" + \ - f"-- {comment}"f"{end}" + self._content[-1] += f"{' ':<{indentLevel*4}}" + f"-- {comment}" f"{end}" else: - self._add(f"{' ':<{indentLevel*4}}" + - f"-- {comment}"f"{end}") + self._add(f"{' ':<{indentLevel*4}}" + f"-- {comment}" f"{end}") - def addHeader(self, name, package='', indentLevel=0): + def addHeader(self, name, package="", indentLevel=0): # library template self._add("library IEEE;", indentLevel) self._add("use IEEE.STD_LOGIC_1164.ALL;", indentLevel) @@ -58,9 +56,10 @@ def addPortStart(self, indentLevel=0): def addPortEnd(self, indentLevel=0): def deSemiColon(x): - cpos = x.rfind(';') + cpos = x.rfind(";") assert cpos != -1, x - return x[:cpos] + x[cpos+1:] + return x[:cpos] + x[cpos + 1 :] + temp = self._content.pop() if "--" in temp and ";" not in temp: temp2 = deSemiColon(self._content.pop()) @@ -76,8 +75,7 @@ def addPortScalar(self, name, io: IO, end=False, indentLevel=0): ioVHDL = "in" elif io.value.lower() == "output": ioVHDL = "out" - self._add(f"{name:<10} : {ioVHDL} STD_LOGIC;", - indentLevel=indentLevel) + self._add(f"{name:<10} : {ioVHDL} STD_LOGIC;", indentLevel=indentLevel) def addPortVector(self, name, io: IO, msbIndex, indentLevel=0): ioVHDL = "" @@ -86,11 +84,12 @@ def addPortVector(self, name, io: IO, msbIndex, indentLevel=0): elif io.value.lower() == "output": ioVHDL = "out" self._add( - f"{name:<10} : {ioVHDL} STD_LOGIC_VECTOR( {msbIndex} downto 0 );", indentLevel=indentLevel) + f"{name:<10} : {ioVHDL} STD_LOGIC_VECTOR( {msbIndex} downto 0 );", + indentLevel=indentLevel, + ) def addDesignDescriptionStart(self, name, indentLevel=0): - self._add( - f"architecture Behavioral of {name} is", indentLevel) + self._add(f"architecture Behavioral of {name} is", indentLevel) def addDesignDescriptionEnd(self, indentLevel=0): self._add(f"end architecture Behavioral;", indentLevel) @@ -103,72 +102,89 @@ def addConnectionScalar(self, name, indentLevel=0): def addConnectionVector(self, name, startIndex, endIndex=0, indentLevel=0): self._add( - f"signal {name} : STD_LOGIC_VECTOR( { startIndex } downto {endIndex} );", indentLevel) + f"signal {name} : STD_LOGIC_VECTOR( { startIndex } downto {endIndex} );", + indentLevel, + ) def addLogicStart(self, indentLevel=0): - self._add("\n"f"begin""\n", indentLevel) + self._add("\n" f"begin" "\n", indentLevel) def addLogicEnd(self, indentLevel=0): - self._add("\n"f"end""\n", indentLevel) + self._add("\n" f"end" "\n", indentLevel) def addAssignScalar(self, left, right, delay=0, indentLevel=0): if type(right) == list: self._add(f"{left} <= {' & '.join(right)} after {delay} ps;", indentLevel) else: - left = str(left).replace(":", " downto ").replace( - "[", "(").replace("]", ")") - right = str(right).replace(":", " downto ").replace( - "[", "(").replace("]", ")") + left = ( + str(left).replace(":", " downto ").replace("[", "(").replace("]", ")") + ) + right = ( + str(right).replace(":", " downto ").replace("[", "(").replace("]", ")") + ) self._add(f"{left} <= {right} after {delay} ps;", indentLevel) def addAssignVector(self, left, right, widthL, widthR, indentLevel=0): - self._add( - f"{left} <= {right}( {widthL} downto {widthR} );", indentLevel) - - def addInstantiation(self, compName, compInsName, portsPairs, paramPairs=[], emulateParamPairs=[], indentLevel=0): + self._add(f"{left} <= {right}( {widthL} downto {widthR} );", indentLevel) + + def addInstantiation( + self, + compName, + compInsName, + portsPairs, + paramPairs=[], + emulateParamPairs=[], + indentLevel=0, + ): self._add(f"{compInsName} : {compName}", indentLevel=indentLevel) if paramPairs: connectPair = [] - self._add(f"generic map (", indentLevel=indentLevel+1) + self._add(f"generic map (", indentLevel=indentLevel + 1) for i in paramPairs: - connectPair.append( - f"{i[0]} => {i[1]}") + connectPair.append(f"{i[0]} => {i[1]}") self._add( - (",\n"f"{' ':<{4*(indentLevel + 2)}}").join(connectPair), indentLevel=indentLevel + 2) - self._add(f")", indentLevel=indentLevel+1) + (",\n" f"{' ':<{4*(indentLevel + 2)}}").join(connectPair), + indentLevel=indentLevel + 2, + ) + self._add(f")", indentLevel=indentLevel + 1) self._add(f"Port map(", indentLevel=indentLevel + 1) connectPair = [] for i in portsPairs: if "[" in i[0]: - port = i[0].replace( - "[", "(").replace("]", ")").replace(":", " downto ") + port = i[0].replace("[", "(").replace("]", ")").replace(":", " downto ") else: port = i[0] if "[" in i[1]: - signal = i[1].replace( - "[", "(").replace("]", ")").replace(":", " downto ") + signal = ( + i[1].replace("[", "(").replace("]", ")").replace(":", " downto ") + ) else: signal = i[1] connectPair.append(f"{port} => {signal}") self._add( - (",\n"f"{' ':<{4*(indentLevel + 2)}}").join(connectPair), indentLevel=indentLevel + 2) + (",\n" f"{' ':<{4*(indentLevel + 2)}}").join(connectPair), + indentLevel=indentLevel + 2, + ) self._add(");", indentLevel=indentLevel + 1) self.addNewLine() def addComponentDeclarationForFile(self, fileName): configPortUsed = 0 # 1 means is used - with open(fileName, 'r') as f: + with open(fileName, "r") as f: data = f.read() - if result := re.search(r"NumberOfConfigBits.*?(\d+)", data, flags=re.IGNORECASE): + if result := re.search( + r"NumberOfConfigBits.*?(\d+)", data, flags=re.IGNORECASE + ): configPortUsed = 1 - if result.group(1) == '0': + if result.group(1) == "0": configPortUsed = 0 - if result := re.search(r"^entity.*?end entity.*?;", - data, flags=re.MULTILINE | re.DOTALL): + if result := re.search( + r"^entity.*?end entity.*?;", data, flags=re.MULTILINE | re.DOTALL + ): result = result.group(0) result = result.replace("entity", "component") @@ -212,7 +228,6 @@ def addShiftRegister(self, indentLevel=0): """ self._add(template, indentLevel) - def addPreprocIfDef(self, macro, indentLevel=0): assert False, "preprocessor not supported in VHDL" diff --git a/fabric_generator/code_generation_Verilog.py b/fabric_generator/code_generation_Verilog.py index 1c3c5530..5f465f6e 100644 --- a/fabric_generator/code_generation_Verilog.py +++ b/fabric_generator/code_generation_Verilog.py @@ -15,13 +15,11 @@ def addComment(self, comment, onNewLine=False, end="", indentLevel=0) -> None: if onNewLine: self._add("") if self._content: - self._content[-1] += f"{' ':<{indentLevel*4}}" + \ - f"//{comment}"f"{end}" + self._content[-1] += f"{' ':<{indentLevel*4}}" + f"//{comment}" f"{end}" else: - self._add(f"{' ':<{indentLevel*4}}" + - f"// {comment}"f"{end}") + self._add(f"{' ':<{indentLevel*4}}" + f"// {comment}" f"{end}") - def addHeader(self, name, package='', indentLevel=0): + def addHeader(self, name, package="", indentLevel=0): self._add(f"module {name}", indentLevel) def addHeaderEnd(self, name, indentLevel=0): @@ -51,9 +49,10 @@ def addPortStart(self, indentLevel=0): def addPortEnd(self, indentLevel=0): def deComma(x): - cpos = x.rfind(',') + cpos = x.rfind(",") assert cpos != -1, x - return x[:cpos] + x[cpos+1:] + return x[:cpos] + x[cpos + 1 :] + temp = self._content.pop() if "//" in temp and "," not in temp: temp2 = deComma(self._content.pop()) @@ -84,8 +83,7 @@ def addConnectionScalar(self, name, indentLevel=0): self._add(f"wire {name};", indentLevel) def addConnectionVector(self, name, startIndex, endIndex=0, indentLevel=0): - self._add( - f"wire[{startIndex}:{endIndex}] {name};", indentLevel) + self._add(f"wire[{startIndex}:{endIndex}] {name};", indentLevel) def addLogicStart(self, indentLevel=0): pass @@ -93,29 +91,39 @@ def addLogicStart(self, indentLevel=0): def addLogicEnd(self, indentLevel=0): pass - def addInstantiation(self, compName, compInsName, portsPairs, paramPairs=[], emulateParamPairs=[], indentLevel=0): + def addInstantiation( + self, + compName, + compInsName, + portsPairs, + paramPairs=[], + emulateParamPairs=[], + indentLevel=0, + ): if paramPairs: port = [f".{i[0]}({i[1]})" for i in paramPairs] + self._add(f"{compName}", indentLevel=indentLevel) + self._add("#(", indentLevel=indentLevel + 1) self._add( - f"{compName}", indentLevel=indentLevel) - self._add("#(", indentLevel=indentLevel+1) - self._add( - (",\n"f"{' ':<{4*(indentLevel + 1)}}").join(port), indentLevel=indentLevel + 1) - self._add(")", indentLevel=indentLevel+1) - self._add(f"{compInsName}", indentLevel=indentLevel+1) - self._add("(", indentLevel=indentLevel+1) + (",\n" f"{' ':<{4*(indentLevel + 1)}}").join(port), + indentLevel=indentLevel + 1, + ) + self._add(")", indentLevel=indentLevel + 1) + self._add(f"{compInsName}", indentLevel=indentLevel + 1) + self._add("(", indentLevel=indentLevel + 1) elif emulateParamPairs: port = [f".{i[0]}({i[1]})" for i in emulateParamPairs] - self._add( - f"{compName}", indentLevel=indentLevel) + self._add(f"{compName}", indentLevel=indentLevel) self._add("`ifdef EMULATION", indentLevel=0) - self._add("#(", indentLevel=indentLevel+1) + self._add("#(", indentLevel=indentLevel + 1) self._add( - (",\n"f"{' ':<{4*(indentLevel + 1)}}").join(port), indentLevel=indentLevel + 1) - self._add(")", indentLevel=indentLevel+1) + (",\n" f"{' ':<{4*(indentLevel + 1)}}").join(port), + indentLevel=indentLevel + 1, + ) + self._add(")", indentLevel=indentLevel + 1) self._add("`endif", indentLevel=0) - self._add(f"{compInsName}", indentLevel=indentLevel+1) - self._add("(", indentLevel=indentLevel+1) + self._add(f"{compInsName}", indentLevel=indentLevel + 1) + self._add("(", indentLevel=indentLevel + 1) else: self._add(f"{compName} {compInsName} (", indentLevel=indentLevel) @@ -128,18 +136,22 @@ def addInstantiation(self, compName, compInsName, portsPairs, paramPairs=[], emu connectPair.append(f".{i[0]}({tmp})") self._add( - (",\n"f"{' ':<{4*(indentLevel + 1)}}").join(connectPair), indentLevel=indentLevel + 1) + (",\n" f"{' ':<{4*(indentLevel + 1)}}").join(connectPair), + indentLevel=indentLevel + 1, + ) self._add(");", indentLevel=indentLevel) self.addNewLine() def addComponentDeclarationForFile(self, fileName): configPortUsed = 0 # 1 means is used - with open(fileName, 'r') as f: + with open(fileName, "r") as f: data = f.read() - if result := re.search(r"NumberOfConfigBits.*?(\d+)", data, flags=re.IGNORECASE): + if result := re.search( + r"NumberOfConfigBits.*?(\d+)", data, flags=re.IGNORECASE + ): configPortUsed = 1 - if result.group(1) == '0': + if result.group(1) == "0": configPortUsed = 0 return configPortUsed @@ -159,7 +171,7 @@ def addShiftRegister(self, configBits, indentLevel=0): self._add(template, indentLevel) def addFlipFlopChain(self, configBits, indentLevel=0): - cfgBit = int(math.ceil(configBits/2.0))*2 + cfgBit = int(math.ceil(configBits / 2.0)) * 2 template = f""" genvar k; assign ConfigBitsInput = {{ConfigBits[{cfgBit}-1-1:0], CONFin;}} @@ -187,8 +199,7 @@ def addAssignScalar(self, left, right, delay=0, indentLevel=0): self._add(f"assign {left} = {right};") def addAssignVector(self, left, right, widthL, widthR, indentLevel=0): - self._add( - f"assign {left} = {right}[{widthL}:{widthR}];", indentLevel) + self._add(f"assign {left} = {right}[{widthL}:{widthR}];", indentLevel) def addPreprocIfDef(self, macro, indentLevel=0): self._add(f"`ifdef {macro}", indentLevel) diff --git a/fabric_generator/code_generator.py b/fabric_generator/code_generator.py index ffe185f9..3f4dcc8e 100644 --- a/fabric_generator/code_generator.py +++ b/fabric_generator/code_generator.py @@ -23,7 +23,7 @@ def writeToFile(self): if self._outFileName == "": print("OutFileName is not set") exit(-1) - with open(self._outFileName, 'w') as f: + with open(self._outFileName, "w") as f: self._content = [i for i in self._content if i is not None] f.write("\n".join(self._content)) self._content = [] @@ -62,7 +62,7 @@ def addComment(self, comment: str, onNewLine=False, end="", indentLevel=0) -> No pass @abc.abstractmethod - def addHeader(self, name: str, package='', indentLevel=0): + def addHeader(self, name: str, package="", indentLevel=0): """ Add a header to the code. @@ -87,7 +87,7 @@ def addHeaderEnd(self, name: str, indentLevel=0): Add end to header. Only useful with VHDL. Examples : - | Verilog: + | Verilog: | VHDL: end entity **name**; Args: @@ -136,7 +136,7 @@ def addParameter(self, name: str, type, value, indentLevel=0): Args: name (str): name of the parameter type (_type_): type of the parameter. Only useful with VHDL. - value (_type_): value of the parameter. + value (_type_): value of the parameter. indentLevel (int, optional): The indentation Level. Defaults to 0. """ pass @@ -221,7 +221,7 @@ def addDesignDescriptionStart(self, name: str, indentLevel=0): @abc.abstractmethod def addDesignDescriptionEnd(self, indentLevel=0): """ - Add end of design description. + Add end of design description. Examples : | Verilog: endmodule @@ -274,7 +274,7 @@ def addConnectionVector(self, name: str, startIndex, endIndex=0, indentLevel=0): Args: name (str): name of the connection - startIndex : Start index of the vector. Can be a string. + startIndex : Start index of the vector. Can be a string. endIndex (int, optional): End index of the vector. Can be a string. Defaults to 0. indentLevel (int, optional): The indentation Level. Defaults to 0. """ @@ -286,7 +286,7 @@ def addLogicStart(self, indentLevel=0): Add start of logic. Only useful with VHDL. Examples : - | Verilog: + | Verilog: | VHDL: begin Args: @@ -309,7 +309,15 @@ def addLogicEnd(self, indentLevel=0): pass @abc.abstractmethod - def addInstantiation(self, compName: str, compInsName: str, portsPairs: List[Tuple[str, str]], paramPairs: List[Tuple[str, str]] = [], emulateParamPairs: List[Tuple[str, str]] = [], indentLevel=0): + def addInstantiation( + self, + compName: str, + compInsName: str, + portsPairs: List[Tuple[str, str]], + paramPairs: List[Tuple[str, str]] = [], + emulateParamPairs: List[Tuple[str, str]] = [], + indentLevel=0, + ): """ Add an instantiation. This will line up the ports and signals. So ports[0] will have signals[0] and so on. This is also the same case for paramPorts and paramSignals. @@ -319,7 +327,7 @@ def addInstantiation(self, compName: str, compInsName: str, portsPairs: List[Tup | . **paramPorts[1]** (**paramSignals[1]**), | ... | . **paramPorts[n]** (**paramSignals[n]**) - | ) ( + | ) ( | . **compPorts[0]** (**signals[0]**), | . **compPorts[1]** (**signals[1]**), | ... @@ -391,9 +399,9 @@ def addFlipFlopChain(self, configBits: int, indentLevel=0): @abc.abstractmethod def addAssignScalar(self, left, right, delay=0, indentLevel=0): """ - Add a scalar assign statement. Delay is provided by currently not being used by any of the code generator. - If **right** is a list, it will be concatenated. - Verilog will be concatenated with comma ','. + Add a scalar assign statement. Delay is provided by currently not being used by any of the code generator. + If **right** is a list, it will be concatenated. + Verilog will be concatenated with comma ','. VHDL will be concatenated with ampersand '&'. Examples : @@ -441,7 +449,6 @@ def addPreprocIfDef(self, macro, indentLevel=0): """ pass - @abc.abstractmethod def addPreprocIfNotDef(self, macro, indentLevel=0): """ diff --git a/fabric_generator/fabric.py b/fabric_generator/fabric.py index 00fcca5e..dda545a2 100644 --- a/fabric_generator/fabric.py +++ b/fabric_generator/fabric.py @@ -39,7 +39,7 @@ class ConfigBitMode(Enum): @dataclass(frozen=True, eq=True) -class Port(): +class Port: """ The port data class contains all the port information from the CSV file. The `name`, `inOut` and `sideOfTile` are added attributes to aid the generation of the fabric. @@ -60,6 +60,7 @@ class Port(): inOut (IO): The IO direction of the port sideOfTile (Side): The side which the port is physically located on the tile """ + wireDirection: Direction sourceName: str xOffset: int @@ -75,36 +76,45 @@ def __repr__(self) -> str: def expandPortInfoByName(self, indexed=False) -> List[str]: if self.sourceName == "NULL" or self.destinationName == "NULL": - wireCount = (abs(self.xOffset)+abs(self.yOffset)) * self.wireCount + wireCount = (abs(self.xOffset) + abs(self.yOffset)) * self.wireCount else: wireCount = self.wireCount if not indexed: return [f"{self.name}{i}" for i in range(wireCount) if self.name != "NULL"] else: - return [f"{self.name}[{i}]" for i in range(wireCount) if self.name != "NULL"] + return [ + f"{self.name}[{i}]" for i in range(wireCount) if self.name != "NULL" + ] def expandPortInfoByNameTop(self, indexed=False) -> List[str]: if self.sourceName == "NULL" or self.destinationName == "NULL": startIndex = 0 else: - startIndex = ( - (abs(self.xOffset)+abs(self.yOffset))-1) * self.wireCount + startIndex = ((abs(self.xOffset) + abs(self.yOffset)) - 1) * self.wireCount - wireCount = (abs(self.xOffset)+abs(self.yOffset)) * self.wireCount + wireCount = (abs(self.xOffset) + abs(self.yOffset)) * self.wireCount if not indexed: - return [f"{self.name}{i}" for i in range(startIndex, wireCount) if self.name != "NULL"] + return [ + f"{self.name}{i}" + for i in range(startIndex, wireCount) + if self.name != "NULL" + ] else: - return [f"{self.name}[{i}]" for i in range(startIndex, wireCount) if self.name != "NULL"] + return [ + f"{self.name}[{i}]" + for i in range(startIndex, wireCount) + if self.name != "NULL" + ] def expandPortInfo(self, mode="SwitchMatrix") -> Tuple[List[str], List[str]]: """ Expanding the port information to individual bit signal. If indexed is in the mode, then brackets are added to the signal name. Args: - mode (str, optional): mode for expansion. Defaults to "SwitchMatrix". - Possible modes are 'all', 'allIndexed', 'Top', 'TopIndexed', 'AutoTop', - 'AutoTopIndexed', 'SwitchMatrix', 'SwitchMatrixIndexed', 'AutoSwitchMatrix', + mode (str, optional): mode for expansion. Defaults to "SwitchMatrix". + Possible modes are 'all', 'allIndexed', 'Top', 'TopIndexed', 'AutoTop', + 'AutoTopIndexed', 'SwitchMatrix', 'SwitchMatrixIndexed', 'AutoSwitchMatrix', 'AutoSwitchMatrixIndexed' Returns: Tuple[List[str], List[str]]: A tuple of two lists. The first list contains the source names of the ports and the second list contains the destination names of the ports. @@ -124,46 +134,49 @@ def expandPortInfo(self, mode="SwitchMatrix") -> Tuple[List[str], List[str]]: elif mode == "AutoSwitchMatrix" or mode == "AutoSwitchMatrixIndexed": if self.sourceName == "NULL" or self.destinationName == "NULL": # the following line connects all wires to the switch matrix in the case one port is NULL (typically termination) - thisRange = ( - abs(self.xOffset)+abs(self.yOffset)) * self.wireCount + thisRange = (abs(self.xOffset) + abs(self.yOffset)) * self.wireCount else: # the following line connects all bottom wires to the switch matrix in the case begin and end ports are used thisRange = self.wireCount # range ((wires*distance)-1 downto 0) as connected to the tile top - elif mode in ['all', 'allIndexed', 'Top', 'TopIndexed', 'AutoTop', 'AutoTopIndexed']: - thisRange = ( - abs(self.xOffset)+abs(self.yOffset)) * self.wireCount + elif mode in [ + "all", + "allIndexed", + "Top", + "TopIndexed", + "AutoTop", + "AutoTopIndexed", + ]: + thisRange = (abs(self.xOffset) + abs(self.yOffset)) * self.wireCount # the following three lines are needed to get the top line[wires] that are actually the connection from a switch matrix to the routing fabric startIndex = 0 - if mode in ['Top', 'TopIndexed']: - startIndex = ( - (abs(self.xOffset)+abs(self.yOffset))-1) * self.wireCount + if mode in ["Top", "TopIndexed"]: + startIndex = ((abs(self.xOffset) + abs(self.yOffset)) - 1) * self.wireCount - elif mode in ['AutoTop', 'AutoTopIndexed']: - if self.sourceName == 'NULL' or self.destinationName == 'NULL': + elif mode in ["AutoTop", "AutoTopIndexed"]: + if self.sourceName == "NULL" or self.destinationName == "NULL": # in case one port is NULL, then the all the other port wires get connected to the switch matrix. startIndex = 0 else: # "normal" case as for the CLBs startIndex = ( - (abs(self.xOffset)+abs(self.yOffset))-1) * self.wireCount + (abs(self.xOffset) + abs(self.yOffset)) - 1 + ) * self.wireCount if startIndex == thisRange: thisRange = 1 for i in range(startIndex, thisRange): if self.sourceName != "NULL": - inputs.append( - f"{self.sourceName}{openIndex}{str(i)}{closeIndex}") + inputs.append(f"{self.sourceName}{openIndex}{str(i)}{closeIndex}") if self.destinationName != "NULL": - outputs.append( - f"{self.destinationName}{openIndex}{str(i)}{closeIndex}") + outputs.append(f"{self.destinationName}{openIndex}{str(i)}{closeIndex}") return inputs, outputs @dataclass(frozen=True, eq=True) -class Wire(): +class Wire: """ This class is for wire connection that span across multiple tiles. If working for connection between two adjacent tiles, the Port class should have all the required information. The main use of this class is to assist model generation, where information at individual wire level is needed. @@ -176,6 +189,7 @@ class Wire(): sourceTile (str): The source tile name of the wire destinationTile (str): The destination tile name of the wire """ + direction: Direction source: str xOffset: int @@ -194,14 +208,14 @@ def __eq__(self, __o: Any) -> bool: @dataclass -class Bel(): +class Bel: """ Contains all the information about a single BEL. The information is parsed from the directory of the BEL in the CSV - definition file. There are something to be noted. + definition file. There are something to be noted. - * The parsed name will contains the prefix of the bel. - * The `sharedPort` attribute is a list of Tuples with name of the port and IO information which is not expanded out yet. - * If a port is marked as both shared and external, the port is considered as shared. As a result signal like UserCLK will be in shared port list, but not in external port list. + * The parsed name will contains the prefix of the bel. + * The `sharedPort` attribute is a list of Tuples with name of the port and IO information which is not expanded out yet. + * If a port is marked as both shared and external, the port is considered as shared. As a result signal like UserCLK will be in shared port list, but not in external port list. Attributes: @@ -218,6 +232,7 @@ class Bel(): belFeatureMap (Dict[str, dict]) : The feature map of the BEL. withUserCLK (bool) : Whether the BEL has userCLK port. Default is False. """ + src: str prefix: str name: str @@ -231,7 +246,18 @@ class Bel(): belFeatureMap: Dict[str, dict] = field(default_factory=dict) withUserCLK: bool = False - def __init__(self, src: str, prefix: str, internal, external, configPort, sharedPort, configBit: int, belMap: Dict[str, dict], userCLK: bool) -> None: + def __init__( + self, + src: str, + prefix: str, + internal, + external, + configPort, + sharedPort, + configBit: int, + belMap: Dict[str, dict], + userCLK: bool, + ) -> None: self.src = src self.prefix = prefix self.name = src.split("/")[-1].split(".")[0] @@ -247,7 +273,7 @@ def __init__(self, src: str, prefix: str, internal, external, configPort, shared @dataclass(frozen=True, eq=True) -class ConfigMem(): +class ConfigMem: """ Data structure to store the information about a config memory. Each structure represent a row of entry in the config memory csv file. @@ -256,8 +282,9 @@ class ConfigMem(): frameIndex (int) : The index of the frame bitUsedInFrame (int) : The number of bits used in the frame usedBitMask (int) : The bit mask of the bits used in the frame - configBitRanges (List[int]) : A list of config bit mapping values + configBitRanges (List[int]) : A list of config bit mapping values """ + frameName: str frameIndex: int bitsUsedInFrame: int @@ -266,9 +293,9 @@ class ConfigMem(): @dataclass -class Tile(): +class Tile: """ - This class is for storing the information about a tile. + This class is for storing the information about a tile. attributes: name (str) : The name of the tile @@ -290,7 +317,15 @@ class Tile(): filePath: str = "." partOfSuperTile = False - def __init__(self, name: str, ports: List[Port], bels: List[Bel], matrixDir: str, userCLK: bool, configBit: int = 0) -> None: + def __init__( + self, + name: str, + ports: List[Port], + bels: List[Bel], + matrixDir: str, + userCLK: bool, + configBit: int = 0, + ) -> None: self.name = name self.portsInfo = ports self.bels = bels @@ -309,38 +344,74 @@ def __eq__(self, __o: Any) -> bool: return self.name == __o.name def getWestSidePorts(self) -> List[Port]: - return [p for p in self.portsInfo if p.sideOfTile == Side.WEST and p.name != "NULL"] + return [ + p for p in self.portsInfo if p.sideOfTile == Side.WEST and p.name != "NULL" + ] def getEastSidePorts(self) -> List[Port]: - return [p for p in self.portsInfo if p.sideOfTile == Side.EAST and p.name != "NULL"] + return [ + p for p in self.portsInfo if p.sideOfTile == Side.EAST and p.name != "NULL" + ] def getNorthSidePorts(self) -> List[Port]: - return [p for p in self.portsInfo if p.sideOfTile == Side.NORTH and p.name != "NULL"] + return [ + p for p in self.portsInfo if p.sideOfTile == Side.NORTH and p.name != "NULL" + ] def getSouthSidePorts(self) -> List[Port]: - return [p for p in self.portsInfo if p.sideOfTile == Side.SOUTH and p.name != "NULL"] + return [ + p for p in self.portsInfo if p.sideOfTile == Side.SOUTH and p.name != "NULL" + ] def getNorthPorts(self, io: IO) -> List[Port]: - return [p for p in self.portsInfo if p.wireDirection == Direction.NORTH and p.name != "NULL" and p.inOut == io] + return [ + p + for p in self.portsInfo + if p.wireDirection == Direction.NORTH and p.name != "NULL" and p.inOut == io + ] def getSouthPorts(self, io: IO) -> List[Port]: - return [p for p in self.portsInfo if p.wireDirection == Direction.SOUTH and p.name != "NULL" and p.inOut == io] + return [ + p + for p in self.portsInfo + if p.wireDirection == Direction.SOUTH and p.name != "NULL" and p.inOut == io + ] def getEastPorts(self, io: IO) -> List[Port]: - return [p for p in self.portsInfo if p.wireDirection == Direction.EAST and p.name != "NULL" and p.inOut == io] + return [ + p + for p in self.portsInfo + if p.wireDirection == Direction.EAST and p.name != "NULL" and p.inOut == io + ] def getWestPorts(self, io: IO) -> List[Port]: - return [p for p in self.portsInfo if p.wireDirection == Direction.WEST and p.name != "NULL" and p.inOut == io] + return [ + p + for p in self.portsInfo + if p.wireDirection == Direction.WEST and p.name != "NULL" and p.inOut == io + ] def getTileInputNames(self) -> List[str]: - return [p.destinationName for p in self.portsInfo if p.destinationName != "NULL" and p.wireDirection != Direction.JUMP and p.inOut == IO.INPUT] + return [ + p.destinationName + for p in self.portsInfo + if p.destinationName != "NULL" + and p.wireDirection != Direction.JUMP + and p.inOut == IO.INPUT + ] def getTileOutputNames(self) -> List[str]: - return [p.sourceName for p in self.portsInfo if p.sourceName != "NULL" and p.wireDirection != Direction.JUMP and p.inOut == IO.OUTPUT] + return [ + p.sourceName + for p in self.portsInfo + if p.sourceName != "NULL" + and p.wireDirection != Direction.JUMP + and p.inOut == IO.OUTPUT + ] -@ dataclass -class SuperTile(): +@dataclass +class SuperTile: """ This class is for storing the information about a super tile. @@ -371,13 +442,13 @@ def getPortsAroundTile(self) -> Dict[str, List[List[Port]]]: if self.tileMap[y][x] == None: continue ports[f"{x},{y}"] = [] - if y - 1 < 0 or self.tileMap[y-1][x] == None: + if y - 1 < 0 or self.tileMap[y - 1][x] == None: ports[f"{x},{y}"].append(tile.getNorthSidePorts()) - if x + 1 >= len(self.tileMap[y]) or self.tileMap[y][x+1] == None: + if x + 1 >= len(self.tileMap[y]) or self.tileMap[y][x + 1] == None: ports[f"{x},{y}"].append(tile.getEastSidePorts()) - if y + 1 >= len(self.tileMap) or self.tileMap[y+1][x] == None: + if y + 1 >= len(self.tileMap) or self.tileMap[y + 1][x] == None: ports[f"{x},{y}"].append(tile.getSouthSidePorts()) - if x - 1 < 0 or self.tileMap[y][x-1] == None: + if x - 1 < 0 or self.tileMap[y][x - 1] == None: ports[f"{x},{y}"].append(tile.getWestSidePorts()) return ports @@ -392,26 +463,22 @@ def getInternalConnections(self) -> List[Tuple[List[Port], int, int]]: internalConnections = [] for y, row in enumerate(self.tileMap): for x, tile in enumerate(row): - if 0 <= y - 1 < len(self.tileMap) and self.tileMap[y-1][x] != None: - internalConnections.append( - (tile.getNorthSidePorts(), x, y)) - if 0 <= x + 1 < len(self.tileMap[0]) and self.tileMap[y][x+1] != None: - internalConnections.append( - (tile.getEastSidePorts(), x, y)) - if 0 <= y + 1 < len(self.tileMap) and self.tileMap[y+1][x] != None: - internalConnections.append( - (tile.getSouthSidePorts(), x, y)) - if 0 <= x - 1 < len(self.tileMap[0]) and self.tileMap[y][x-1] != None: - internalConnections.append( - (tile.getWestSidePorts(), x, y)) + if 0 <= y - 1 < len(self.tileMap) and self.tileMap[y - 1][x] != None: + internalConnections.append((tile.getNorthSidePorts(), x, y)) + if 0 <= x + 1 < len(self.tileMap[0]) and self.tileMap[y][x + 1] != None: + internalConnections.append((tile.getEastSidePorts(), x, y)) + if 0 <= y + 1 < len(self.tileMap) and self.tileMap[y + 1][x] != None: + internalConnections.append((tile.getSouthSidePorts(), x, y)) + if 0 <= x - 1 < len(self.tileMap[0]) and self.tileMap[y][x - 1] != None: + internalConnections.append((tile.getWestSidePorts(), x, y)) return internalConnections -@ dataclass -class Fabric(): +@dataclass +class Fabric: """ - This class is for storing the information and hyper parameter of the fabric. All the information is parsed from the - CSV file. + This class is for storing the information and hyper parameter of the fabric. All the information is parsed from the + CSV file. Attributes: tile (List[List[Tile]]) : The tile map of the fabric @@ -421,15 +488,15 @@ class Fabric(): configMitMode (ConfigBitMode): The configuration bit mode of the fabric. Currently support frame based or ff chain frameBitsPerRow (int) : The number of frame bits per row of the fabric maxFramesPerCol (int) : The maximum number of frames per column of the fabric - package (str) : The extra package used by the fabric. Only useful for VHDL output. - generateDelayInSwitchMatrix (int) : The amount of delay in a switch matrix. + package (str) : The extra package used by the fabric. Only useful for VHDL output. + generateDelayInSwitchMatrix (int) : The amount of delay in a switch matrix. multiplexerStyle (MultiplexerStyle) : The style of the multiplexer used in the fabric. Currently support custom or generic frameSelectWidth (int) : The width of the frame select signal. rowSelectWidth (int) : The width of the row select signal. - desync_flag (int): + desync_flag (int): numberOfBRAMs (int) : The number of BRAMs in the fabric. superTileEnable (bool) : Whether the fabric has super tile. - tileDic (Dict[str, Tile]) : A dictionary of tiles used in the fabric. The key is the name of the tile and the value is the tile. + tileDic (Dict[str, Tile]) : A dictionary of tiles used in the fabric. The key is the name of the tile and the value is the tile. superTileDic (Dict[str, SuperTile]) : A dictionary of super tiles used in the fabric. The key is the name of the super tile and the value is the super tile. """ @@ -458,7 +525,7 @@ class Fabric(): def __post_init__(self) -> None: """ - Generate all the wire pair in the fabric and get all the wire in the fabric. + Generate all the wire pair in the fabric and get all the wire in the fabric. The wire pair are used during model generation when some of the signals have source or destination of "NULL". The wires are used during model generation to work with wire that going cross tile. """ @@ -467,81 +534,105 @@ def __post_init__(self) -> None: if tile == None: continue for port in tile.portsInfo: - self.commonWirePair.append( - (port.sourceName, port.destinationName)) + self.commonWirePair.append((port.sourceName, port.destinationName)) self.commonWirePair = list(dict.fromkeys(self.commonWirePair)) self.commonWirePair = [ - (i, j) for i, j in self.commonWirePair if i != "NULL" and j != "NULL"] + (i, j) for i, j in self.commonWirePair if i != "NULL" and j != "NULL" + ] for y, row in enumerate(self.tile): for x, tile in enumerate(row): if tile == None: continue for port in tile.portsInfo: - if abs(port.xOffset) <= 1 and abs(port.yOffset) <= 1 and port.sourceName != "NULL" and port.destinationName != "NULL": + if ( + abs(port.xOffset) <= 1 + and abs(port.yOffset) <= 1 + and port.sourceName != "NULL" + and port.destinationName != "NULL" + ): for i in range(port.wireCount): - tile.wireList.append(Wire(direction=port.wireDirection, - source=f"{port.sourceName}{i}", - xOffset=port.xOffset, - yOffset=port.yOffset, - destination=f"{port.destinationName}{i}", - sourceTile="", - destinationTile="")) + tile.wireList.append( + Wire( + direction=port.wireDirection, + source=f"{port.sourceName}{i}", + xOffset=port.xOffset, + yOffset=port.yOffset, + destination=f"{port.destinationName}{i}", + sourceTile="", + destinationTile="", + ) + ) elif port.sourceName != "NULL" and port.destinationName != "NULL": # clamp the xOffset to 1 or -1 value = min(max(port.xOffset, -1), 1) cascadedI = 0 - for i in range(port.wireCount*abs(port.xOffset)): + for i in range(port.wireCount * abs(port.xOffset)): if i < port.wireCount: - cascadedI = i + port.wireCount * \ - (abs(port.xOffset)-1) + cascadedI = i + port.wireCount * (abs(port.xOffset) - 1) else: cascadedI = i - port.wireCount - tile.wireList.append(Wire(direction=Direction.JUMP, - source=f"{port.destinationName}{i}", - xOffset=0, - yOffset=0, - destination=f"{port.sourceName}{i}", - sourceTile=f"X{x}Y{y}", - destinationTile=f"X{x}Y{y}")) - tile.wireList.append(Wire(direction=port.wireDirection, - source=f"{port.sourceName}{i}", - xOffset=value, - yOffset=port.yOffset, - destination=f"{port.destinationName}{cascadedI}", - sourceTile=f"X{x}Y{y}", - destinationTile=f"X{x+value}Y{y+port.yOffset}")) + tile.wireList.append( + Wire( + direction=Direction.JUMP, + source=f"{port.destinationName}{i}", + xOffset=0, + yOffset=0, + destination=f"{port.sourceName}{i}", + sourceTile=f"X{x}Y{y}", + destinationTile=f"X{x}Y{y}", + ) + ) + tile.wireList.append( + Wire( + direction=port.wireDirection, + source=f"{port.sourceName}{i}", + xOffset=value, + yOffset=port.yOffset, + destination=f"{port.destinationName}{cascadedI}", + sourceTile=f"X{x}Y{y}", + destinationTile=f"X{x+value}Y{y+port.yOffset}", + ) + ) # clamp the yOffset to 1 or -1 value = min(max(port.yOffset, -1), 1) cascadedI = 0 - for i in range(port.wireCount*abs(port.yOffset)): + for i in range(port.wireCount * abs(port.yOffset)): if i < port.wireCount: - cascadedI = i + port.wireCount * \ - (abs(port.yOffset)-1) + cascadedI = i + port.wireCount * (abs(port.yOffset) - 1) else: cascadedI = i - port.wireCount - tile.wireList.append(Wire(direction=Direction.JUMP, - source=f"{port.destinationName}{i}", - xOffset=0, - yOffset=0, - destination=f"{port.sourceName}{i}", - sourceTile=f"X{x}Y{y}", - destinationTile=f"X{x}Y{y}")) - tile.wireList.append(Wire(direction=port.wireDirection, - source=f"{port.sourceName}{i}", - xOffset=port.xOffset, - yOffset=value, - destination=f"{port.destinationName}{cascadedI}", - sourceTile=f"X{x}Y{y}", - destinationTile=f"X{x+port.xOffset}Y{y+value}")) + tile.wireList.append( + Wire( + direction=Direction.JUMP, + source=f"{port.destinationName}{i}", + xOffset=0, + yOffset=0, + destination=f"{port.sourceName}{i}", + sourceTile=f"X{x}Y{y}", + destinationTile=f"X{x}Y{y}", + ) + ) + tile.wireList.append( + Wire( + direction=port.wireDirection, + source=f"{port.sourceName}{i}", + xOffset=port.xOffset, + yOffset=value, + destination=f"{port.destinationName}{cascadedI}", + sourceTile=f"X{x}Y{y}", + destinationTile=f"X{x+port.xOffset}Y{y+value}", + ) + ) elif port.sourceName != "NULL" and port.destinationName == "NULL": sourceName = port.sourceName destName = "" try: index = [i for i, _ in self.commonWirePair].index( - port.sourceName) + port.sourceName + ) sourceName = self.commonWirePair[index][0] destName = self.commonWirePair[index][1] except: @@ -549,24 +640,32 @@ def __post_init__(self) -> None: destName = sourceName value = min(max(port.xOffset, -1), 1) - for i in range(port.wireCount*abs(port.xOffset)): - tile.wireList.append(Wire(direction=port.wireDirection, - source=f"{sourceName}{i}", - xOffset=value, - yOffset=port.yOffset, - destination=f"{destName}{i}", - sourceTile=f"X{x}Y{y}", - destinationTile=f"X{x+value}Y{y+port.yOffset}")) + for i in range(port.wireCount * abs(port.xOffset)): + tile.wireList.append( + Wire( + direction=port.wireDirection, + source=f"{sourceName}{i}", + xOffset=value, + yOffset=port.yOffset, + destination=f"{destName}{i}", + sourceTile=f"X{x}Y{y}", + destinationTile=f"X{x+value}Y{y+port.yOffset}", + ) + ) value = min(max(port.yOffset, -1), 1) - for i in range(port.wireCount*abs(port.yOffset)): - tile.wireList.append(Wire(direction=port.wireDirection, - source=f"{sourceName}{i}", - xOffset=port.xOffset, - yOffset=value, - destination=f"{destName}{i}", - sourceTile=f"X{x}Y{y}", - destinationTile=f"X{x+port.xOffset}Y{y+value}")) + for i in range(port.wireCount * abs(port.yOffset)): + tile.wireList.append( + Wire( + direction=port.wireDirection, + source=f"{sourceName}{i}", + xOffset=port.xOffset, + yOffset=value, + destination=f"{destName}{i}", + sourceTile=f"X{x}Y{y}", + destinationTile=f"X{x+port.xOffset}Y{y+value}", + ) + ) tile.wireList = list(dict.fromkeys(tile.wireList)) def __repr__(self) -> str: @@ -574,7 +673,7 @@ def __repr__(self) -> str: for i in range(self.numberOfRows): for j in range(self.numberOfColumns): if self.tile[i][j] is None: - fabric += "Null".ljust(15)+"\t" + fabric += "Null".ljust(15) + "\t" else: fabric += f"{str(self.tile[i][j].name).ljust(15)}\t" fabric += "\n" diff --git a/fabric_generator/fabric_gen.py b/fabric_generator/fabric_gen.py index e96955dd..659f3edd 100644 --- a/fabric_generator/fabric_gen.py +++ b/fabric_generator/fabric_gen.py @@ -50,6 +50,7 @@ class FabricGenerator: writer (codeGenerator): The code generator object to write the RTL files """ + fabric: Fabric writer: codeGenerator @@ -73,8 +74,7 @@ def bootstrapSwitchMatrix(tile: Tile, outputDir: str) -> None: tile (Tile): The tile to generate the switch matrix for outputDir (str): The output directory to write the switch matrix to """ - logger.info( - f"Generate matrix csv for {tile.name} # filename: {outputDir}") + logger.info(f"Generate matrix csv for {tile.name} # filename: {outputDir}") with open(f"{outputDir}", "w") as f: writer = csv.writer(f) sourceName, destName = [], [] @@ -127,7 +127,7 @@ def list2CSV(InFileName: str, OutFileName: str) -> None: file = re.sub(r"#.*", "", file) file = file.split("\n") - col = len(file[0].split(',')) + col = len(file[0].split(",")) rows = len(file) # create a 0 zero matrix as initialization @@ -135,35 +135,38 @@ def list2CSV(InFileName: str, OutFileName: str) -> None: # load the data from the original csv into the matrix for i in range(1, len(file)): - for j in range(1, len(file[i].split(','))): - value = file[i].split(',')[j] + for j in range(1, len(file[i].split(","))): + value = file[i].split(",")[j] if value == "": continue - matrix[i-1][j-1] = int(value) + matrix[i - 1][j - 1] = int(value) # get source and destination list in the csv - destination = file[0].strip("\n").split(',')[1:] + destination = file[0].strip("\n").split(",")[1:] source = [file[i].split(",")[0] for i in range(1, len(file))] # set the matrix value with the provided connection pair - for (s, d) in connectionPair: + for s, d in connectionPair: try: s_index = source.index(s) except ValueError: logger.critical( - f"{s} is not in the source column of the matrix csv file") + f"{s} is not in the source column of the matrix csv file" + ) exit(-1) try: d_index = destination.index(d) except ValueError: logger.critical( - f"{d} is not in the destination row of the matrix csv file") + f"{d} is not in the destination row of the matrix csv file" + ) exit(-1) if matrix[s_index][d_index] != 0: logger.warning( - f"connection ({s}, {d}) already exists in the original matrix") + f"connection ({s}, {d}) already exists in the original matrix" + ) matrix[s_index][d_index] = 1 # writing the matrix back to the given out file @@ -174,10 +177,10 @@ def list2CSV(InFileName: str, OutFileName: str) -> None: for j in range(len(destination)): f.write(str(matrix[i][j])) if j != len(destination) - 1: - f.write(',') + f.write(",") else: f.write(f",#,{matrix[i].count(1)}") - f.write('\n') + f.write("\n") colCount = [] for j in range(col): count = 0 @@ -196,14 +199,14 @@ def CSV2list(InFileName: str, OutFileName: str) -> None: InFileName (str): The input file name of the CSV file OutFileName (str): The directory of the list file to be written """ - InFile = [i.strip('\n').split(',') for i in open(InFileName)] + InFile = [i.strip("\n").split(",") for i in open(InFileName)] with open(OutFileName, "w") as f: # get the number of tiles in vertical direction rows = len(InFile) # get the number of tiles in horizontal direction cols = len(InFile[0]) # top-left should be the name - print('#', InFile[0][0], file=f) + print("#", InFile[0][0], file=f) # switch matrix inputs inputs = [] for item in InFile[0][1:]: @@ -211,9 +214,9 @@ def CSV2list(InFileName: str, OutFileName: str) -> None: # beginning from the second line, write out the list for line in InFile[1:]: for i in range(1, cols): - if line[i] != '0': + if line[i] != "0": # it is [i-1] because the beginning of the line is the destination port - print(line[0]+','+inputs[i-1], file=f) + print(line[0] + "," + inputs[i - 1], file=f) return def generateConfigMemInit(self, file: str, globalConfigBitsCounter: int) -> None: @@ -229,8 +232,13 @@ def generateConfigMemInit(self, file: str, globalConfigBitsCounter: int) -> None """ bitsLeftToPackInFrames = globalConfigBitsCounter - fieldName = ["frame_name", "frame_index", "bits_used_in_frame", - "used_bits_mask", "ConfigBits_ranges"] + fieldName = [ + "frame_name", + "frame_index", + "bits_used_in_frame", + "used_bits_mask", + "ConfigBits_ranges", + ] frameBitPerRow = self.fabric.frameBitsPerRow with open(file, "w") as f: @@ -249,14 +257,16 @@ def generateConfigMemInit(self, file: str, globalConfigBitsCounter: int) -> None frameBitsMask = f"{2**frameBitPerRow-1:_b}" entry.append(frameBitsMask) entry.append( - f"{bitsLeftToPackInFrames-1}:{bitsLeftToPackInFrames-frameBitPerRow}") + f"{bitsLeftToPackInFrames-1}:{bitsLeftToPackInFrames-frameBitPerRow}" + ) bitsLeftToPackInFrames -= frameBitPerRow else: entry.append(str(bitsLeftToPackInFrames)) # generate a string encoding a '1' for each flop used # this will allow us to kick out flops in the middle (e.g. for alignment padding) - frameBitsMask = (2**frameBitPerRow-1) - \ - (2**(frameBitPerRow-bitsLeftToPackInFrames)-1) + frameBitsMask = (2**frameBitPerRow - 1) - ( + 2 ** (frameBitPerRow - bitsLeftToPackInFrames) - 1 + ) frameBitsMask = f"{frameBitsMask:0{frameBitPerRow+7}_b}" entry.append(frameBitsMask) if bitsLeftToPackInFrames > 0: @@ -289,47 +299,63 @@ def generateConfigMem(self, tile: Tile, configMemCsv: str) -> None: configMemList: List[ConfigMem] = [] if os.path.exists(configMemCsv): logger.info( - f"Found bitstream mapping file {tile.name}_configMem.csv for tile {tile.name}") + f"Found bitstream mapping file {tile.name}_configMem.csv for tile {tile.name}" + ) logger.info(f"Parsing {tile.name}_configMem.csv") configMemList = parseConfigMem( - configMemCsv, self.fabric.maxFramesPerCol, self.fabric.frameBitsPerRow, tile.globalConfigBits) + configMemCsv, + self.fabric.maxFramesPerCol, + self.fabric.frameBitsPerRow, + tile.globalConfigBits, + ) else: logger.info(f"{tile.name}_configMem.csv does not exist") logger.info(f"Generating a default configMem for {tile.name}") - self.generateConfigMemInit( - configMemCsv, tile.globalConfigBits) + self.generateConfigMemInit(configMemCsv, tile.globalConfigBits) logger.info(f"Parsing {tile.name}_configMem.csv") configMemList = parseConfigMem( - configMemCsv, self.fabric.maxFramesPerCol, self.fabric.frameBitsPerRow, tile.globalConfigBits) + configMemCsv, + self.fabric.maxFramesPerCol, + self.fabric.frameBitsPerRow, + tile.globalConfigBits, + ) # start writing the file self.writer.addHeader(f"{tile.name}_ConfigMem") self.writer.addParameterStart(indentLevel=1) - if isinstance(self.writer, VerilogWriter): # emulation only in Verilog - maxBits = self.fabric.frameBitsPerRow*self.fabric.maxFramesPerCol + if isinstance(self.writer, VerilogWriter): # emulation only in Verilog + maxBits = self.fabric.frameBitsPerRow * self.fabric.maxFramesPerCol self.writer.addPreprocIfDef("EMULATION") - self.writer.addParameter("Emulate_Bitstream", f"[{maxBits-1}:0]", - f"{maxBits}'b0", indentLevel=2) + self.writer.addParameter( + "Emulate_Bitstream", f"[{maxBits-1}:0]", f"{maxBits}'b0", indentLevel=2 + ) self.writer.addPreprocEndif() if self.fabric.maxFramesPerCol != 0: - self.writer.addParameter("MaxFramesPerCol", "integer", - self.fabric.maxFramesPerCol, indentLevel=2) + self.writer.addParameter( + "MaxFramesPerCol", "integer", self.fabric.maxFramesPerCol, indentLevel=2 + ) if self.fabric.frameBitsPerRow != 0: - self.writer.addParameter("FrameBitsPerRow", "integer", - self.fabric.frameBitsPerRow, indentLevel=2) - self.writer.addParameter("NoConfigBits", "integer", - tile.globalConfigBits, indentLevel=2) + self.writer.addParameter( + "FrameBitsPerRow", "integer", self.fabric.frameBitsPerRow, indentLevel=2 + ) + self.writer.addParameter( + "NoConfigBits", "integer", tile.globalConfigBits, indentLevel=2 + ) self.writer.addParameterEnd(indentLevel=1) self.writer.addPortStart(indentLevel=1) # the port definitions are generic self.writer.addPortVector( - "FrameData", IO.INPUT, "FrameBitsPerRow - 1", indentLevel=2) - self.writer.addPortVector("FrameStrobe", IO.INPUT, - "MaxFramesPerCol - 1", indentLevel=2) - self.writer.addPortVector("ConfigBits", IO.OUTPUT, - "NoConfigBits - 1", indentLevel=2) - self.writer.addPortVector("ConfigBits_N", IO.OUTPUT, - "NoConfigBits - 1", indentLevel=2) + "FrameData", IO.INPUT, "FrameBitsPerRow - 1", indentLevel=2 + ) + self.writer.addPortVector( + "FrameStrobe", IO.INPUT, "MaxFramesPerCol - 1", indentLevel=2 + ) + self.writer.addPortVector( + "ConfigBits", IO.OUTPUT, "NoConfigBits - 1", indentLevel=2 + ) + self.writer.addPortVector( + "ConfigBits_N", IO.OUTPUT, "NoConfigBits - 1", indentLevel=2 + ) self.writer.addPortEnd(indentLevel=1) self.writer.addHeaderEnd(f"{tile.name}_ConfigMem") self.writer.addNewLine() @@ -339,18 +365,19 @@ def generateConfigMem(self, tile: Tile, configMemCsv: str) -> None: # instantiate latches for only the used frame bits for i in configMemList: if i.usedBitMask.count("1") > 0: - self.writer.addConnectionVector( - i.frameName, f"{i.bitsUsedInFrame}-1") + self.writer.addConnectionVector(i.frameName, f"{i.bitsUsedInFrame}-1") self.writer.addLogicStart() - if isinstance(self.writer, VerilogWriter): # emulation only in Verilog + if isinstance(self.writer, VerilogWriter): # emulation only in Verilog self.writer.addPreprocIfDef("EMULATION") for i in configMemList: counter = 0 for k in range(self.fabric.frameBitsPerRow): if i.usedBitMask[k] == "1": - self.writer.addAssignScalar(f"ConfigBits[{i.configBitRanges[counter]}]", - f"Emulate_Bitstream[{i.frameIndex*self.fabric.frameBitsPerRow + (self.fabric.frameBitsPerRow-1-k)}]") + self.writer.addAssignScalar( + f"ConfigBits[{i.configBitRanges[counter]}]", + f"Emulate_Bitstream[{i.frameIndex*self.fabric.frameBitsPerRow + (self.fabric.frameBitsPerRow-1-k)}]", + ) counter += 1 self.writer.addPreprocElse() self.writer.addNewLine() @@ -360,16 +387,18 @@ def generateConfigMem(self, tile: Tile, configMemCsv: str) -> None: counter = 0 for k in range(self.fabric.frameBitsPerRow): if i.usedBitMask[k] == "1": - self.writer.addInstantiation(compName="LHQD1", - compInsName=f"Inst_{i.frameName}_bit{self.fabric.frameBitsPerRow-1-k}", - portsPairs=[("D", f"FrameData[{self.fabric.frameBitsPerRow-1-k}]"), - ("E", - f"FrameStrobe[{i.frameIndex}]"), - ("Q", f"ConfigBits[{i.configBitRanges[counter]}]"), - ("QN", f"ConfigBits_N[{i.configBitRanges[counter]}]")] - ) + self.writer.addInstantiation( + compName="LHQD1", + compInsName=f"Inst_{i.frameName}_bit{self.fabric.frameBitsPerRow-1-k}", + portsPairs=[ + ("D", f"FrameData[{self.fabric.frameBitsPerRow-1-k}]"), + ("E", f"FrameStrobe[{i.frameIndex}]"), + ("Q", f"ConfigBits[{i.configBitRanges[counter]}]"), + ("QN", f"ConfigBits_N[{i.configBitRanges[counter]}]"), + ], + ) counter += 1 - if isinstance(self.writer, VerilogWriter): # emulation only in Verilog + if isinstance(self.writer, VerilogWriter): # emulation only in Verilog self.writer.addPreprocEndif() self.writer.addDesignDescriptionEnd() self.writer.writeToFile() @@ -396,24 +425,27 @@ def genTileSwitchMatrix(self, tile: Tile) -> None: elif tile.matrixDir.endswith(".list"): logger.info(f"{tile.name} matrix is a list file") logger.info( - f"bootstrapping {tile.name} to matrix form and adding the list file to the matrix") + f"bootstrapping {tile.name} to matrix form and adding the list file to the matrix" + ) matrixDir = tile.matrixDir.replace(".list", ".csv") self.bootstrapSwitchMatrix(tile, matrixDir) self.list2CSV(tile.matrixDir, matrixDir) logger.info( - f"Update matrix directory to {matrixDir} for Fabric Tile Dictionary") + f"Update matrix directory to {matrixDir} for Fabric Tile Dictionary" + ) tile.matrixDir = matrixDir connections = parseMatrix(tile.matrixDir, tile.name) elif tile.matrixDir.endswith(".v") or tile.matrixDir.endswith(".vhdl"): logger.info( - f"A switch matrix file is provided in {tile.name}, will skip the matrix generation process") + f"A switch matrix file is provided in {tile.name}, will skip the matrix generation process" + ) return else: raise ValueError("Invalid matrix file format") noConfigBits = 0 for i in connections: - noConfigBits += len(connections[i]).bit_length()-1 + noConfigBits += len(connections[i]).bit_length() - 1 # we pass the NumberOfConfigBits as a comment in the beginning of the file. # This simplifies it to generate the configuration port only if needed later when building the fabric where we are only working with the VHDL files @@ -422,8 +454,7 @@ def genTileSwitchMatrix(self, tile: Tile) -> None: self.writer.addComment(f"NumberOfConfigBits: {noConfigBits}") self.writer.addHeader(f"{tile.name}_switch_matrix") self.writer.addParameterStart(indentLevel=1) - self.writer.addParameter( - "NoConfigBits", "integer", noConfigBits, indentLevel=2) + self.writer.addParameter("NoConfigBits", "integer", noConfigBits, indentLevel=2) self.writer.addParameterEnd(indentLevel=1) self.writer.addPortStart(indentLevel=1) @@ -465,16 +496,17 @@ def genTileSwitchMatrix(self, tile: Tile) -> None: if noConfigBits > 0: if self.fabric.configBitMode == ConfigBitMode.FLIPFLOP_CHAIN: self.writer.addPortScalar("MODE", IO.INPUT, indentLevel=2) - self.writer.addComment( - "global signal 1: configuration, 0: operation") + self.writer.addComment("global signal 1: configuration, 0: operation") self.writer.addPortScalar("CONFin", IO.INPUT, indentLevel=2) self.writer.addPortScalar("CONFout", IO.OUTPUT, indentLevel=2) self.writer.addPortScalar("CLK", IO.INPUT, indentLevel=2) if self.fabric.configBitMode == ConfigBitMode.FRAME_BASED: - self.writer.addPortVector("ConfigBits", IO.INPUT, - "NoConfigBits-1", indentLevel=2) - self.writer.addPortVector("ConfigBits_N", IO.INPUT, - "NoConfigBits-1", indentLevel=2) + self.writer.addPortVector( + "ConfigBits", IO.INPUT, "NoConfigBits-1", indentLevel=2 + ) + self.writer.addPortVector( + "ConfigBits_N", IO.INPUT, "NoConfigBits-1", indentLevel=2 + ) self.writer.addPortEnd() self.writer.addHeaderEnd(f"{tile.name}_switch_matrix") self.writer.addDesignDescriptionStart(f"{tile.name}_switch_matrix") @@ -500,7 +532,8 @@ def genTileSwitchMatrix(self, tile: Tile) -> None: # signal declaration for portName in connections: self.writer.addConnectionVector( - f"{portName}_input", f"{len(connections[portName])}-1") + f"{portName}_input", f"{len(connections[portName])}-1" + ) ### SwitchMatrixDebugSignals ### SwitchMatrixDebugSignals ### ### SwitchMatrixDebugSignals ### SwitchMatrixDebugSignals ### @@ -510,27 +543,34 @@ def genTileSwitchMatrix(self, tile: Tile) -> None: muxSize = len(connections[portName]) if muxSize >= 2: self.writer.addConnectionVector( - f"DEBUG_select_{portName}", f"{int(math.ceil(math.log2(muxSize)))}-1") + f"DEBUG_select_{portName}", + f"{int(math.ceil(math.log2(muxSize)))}-1", + ) ### SwitchMatrixDebugSignals ### SwitchMatrixDebugSignals ### ### SwitchMatrixDebugSignals ### SwitchMatrixDebugSignals ### self.writer.addComment( - "The configuration bits (if any) are just a long shift register", onNewLine=True) + "The configuration bits (if any) are just a long shift register", + onNewLine=True, + ) self.writer.addComment( - "This shift register is padded to an even number of flops/latches", onNewLine=True) + "This shift register is padded to an even number of flops/latches", + onNewLine=True, + ) # we are only generate configuration bits, if we really need configurations bits # for example in terminating switch matrices at the fabric borders, we may just change direction without any switching if noConfigBits > 0: - if self.fabric.configBitMode == 'ff_chain': - self.writer.addConnectionVector( - "ConfigBits", noConfigBits) - if self.fabric.configBitMode == 'FlipFlopChain': + if self.fabric.configBitMode == "ff_chain": + self.writer.addConnectionVector("ConfigBits", noConfigBits) + if self.fabric.configBitMode == "FlipFlopChain": # print('DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG ConfigBitMode == FlipFlopChain') # we pad to an even number of bits: (int(math.ceil(ConfigBitCounter/2.0))*2) - self.writer.addConnectionVector("ConfigBits", int( - math.ceil(noConfigBits/2.0))*2) - self.writer.addConnectionVector("ConfigBitsInput", int( - math.ceil(noConfigBits/2.0))*2) + self.writer.addConnectionVector( + "ConfigBits", int(math.ceil(noConfigBits / 2.0)) * 2 + ) + self.writer.addConnectionVector( + "ConfigBitsInput", int(math.ceil(noConfigBits / 2.0)) * 2 + ) # begin architecture self.writer.addLogicStart() @@ -539,7 +579,7 @@ def genTileSwitchMatrix(self, tile: Tile) -> None: # again, we add this only if needed # TODO Should ff_chain be the same as FlipFlopChain? if noConfigBits > 0: - if self.fabric.configBitMode == 'ff_chain': + if self.fabric.configBitMode == "ff_chain": self.writer.addShiftRegister(noConfigBits) elif self.fabric.configBitMode == ConfigBitMode.FLIPFLOP_CHAIN: self.writer.addFlipFlopChain(noConfigBits) @@ -552,23 +592,29 @@ def genTileSwitchMatrix(self, tile: Tile) -> None: for portName in connections: muxSize = len(connections[portName]) self.writer.addComment( - f"switch matrix multiplexer {portName} MUX-{muxSize}", onNewLine=True) + f"switch matrix multiplexer {portName} MUX-{muxSize}", onNewLine=True + ) if muxSize == 0: logger.warning( - f"Input port {portName} of switch matrix in Tile {tile.name} is not used") + f"Input port {portName} of switch matrix in Tile {tile.name} is not used" + ) self.writer.addComment( - f"WARNING unused multiplexer MUX-{portName}", onNewLine=True) + f"WARNING unused multiplexer MUX-{portName}", onNewLine=True + ) elif muxSize == 1: # just route through : can be used for auxiliary wires or diagonal routing (Manhattan, just go to a switch matrix when turning # can also be used to tap a wire. A double with a mid is nothing else as a single cascaded with another single where the second single has only one '1' to cascade from the first single - if connections[portName][0] == '0': + if connections[portName][0] == "0": self.writer.addAssignScalar(portName, 0) - elif connections[portName][0] == '1': + elif connections[portName][0] == "1": self.writer.addAssignScalar(portName, 1) else: self.writer.addAssignScalar( - portName, connections[portName][0], delay=self.fabric.generateDelayInSwitchMatrix) + portName, + connections[portName][0], + delay=self.fabric.generateDelayInSwitchMatrix, + ) self.writer.addNewLine() elif muxSize >= 2: # this is the case for a configurable switch matrix multiplexer @@ -577,23 +623,30 @@ def genTileSwitchMatrix(self, tile: Tile) -> None: numGnd = 0 muxComponentName = "" - if (self.fabric.multiplexerStyle == MultiplexerStyle.CUSTOM) and (muxSize == 2): - muxComponentName = 'my_mux2' - elif (self.fabric.multiplexerStyle == MultiplexerStyle.CUSTOM) and (2 < muxSize <= 4): - muxComponentName = 'cus_mux41_buf' - numGnd = 4-muxSize - elif (self.fabric.multiplexerStyle == MultiplexerStyle.CUSTOM) and (4 < muxSize <= 8): - muxComponentName = 'cus_mux81_buf' - numGnd = 8-muxSize - elif (self.fabric.multiplexerStyle == MultiplexerStyle.CUSTOM) and (8 < muxSize <= 16): - muxComponentName = 'cus_mux161_buf' - numGnd = 16-muxSize + if (self.fabric.multiplexerStyle == MultiplexerStyle.CUSTOM) and ( + muxSize == 2 + ): + muxComponentName = "my_mux2" + elif (self.fabric.multiplexerStyle == MultiplexerStyle.CUSTOM) and ( + 2 < muxSize <= 4 + ): + muxComponentName = "cus_mux41_buf" + numGnd = 4 - muxSize + elif (self.fabric.multiplexerStyle == MultiplexerStyle.CUSTOM) and ( + 4 < muxSize <= 8 + ): + muxComponentName = "cus_mux81_buf" + numGnd = 8 - muxSize + elif (self.fabric.multiplexerStyle == MultiplexerStyle.CUSTOM) and ( + 8 < muxSize <= 16 + ): + muxComponentName = "cus_mux161_buf" + numGnd = 16 - muxSize portsPairs = [] start = 0 for start in range(muxSize): - portsPairs.append( - (f"A{start}", f"{portName}_input[{start}]")) + portsPairs.append((f"A{start}", f"{portName}_input[{start}]")) for end in range(start, numGnd): portsPairs.append((f"A{end}", "GND0")) @@ -601,36 +654,49 @@ def genTileSwitchMatrix(self, tile: Tile) -> None: if self.fabric.multiplexerStyle == MultiplexerStyle.CUSTOM: if muxSize == 2: portsPairs.append( - ("S", f"ConfigBits[{configBitstreamPosition}+0]")) + ("S", f"ConfigBits[{configBitstreamPosition}+0]") + ) else: - for i in range(muxSize.bit_length()-1): + for i in range(muxSize.bit_length() - 1): portsPairs.append( - (f"S{i}", f"ConfigBits[{configBitstreamPosition}+{i}]")) + (f"S{i}", f"ConfigBits[{configBitstreamPosition}+{i}]") + ) portsPairs.append( - (f"S{i}N", f"ConfigBits_N[{configBitstreamPosition}+{i}]")) + ( + f"S{i}N", + f"ConfigBits_N[{configBitstreamPosition}+{i}]", + ) + ) portsPairs.append((f"X", f"{portName}")) - if (self.fabric.multiplexerStyle == MultiplexerStyle.CUSTOM): + if self.fabric.multiplexerStyle == MultiplexerStyle.CUSTOM: # we add the input signal in reversed order # Changed it such that the left-most entry is located at the end of the concatenated vector for the multiplexing # This was done such that the index from left-to-right in the adjacency matrix corresponds with the multiplexer select input (index) self.writer.addAssignScalar( - f"{portName}_input", connections[portName][::-1], delay=self.fabric.generateDelayInSwitchMatrix) - self.writer.addInstantiation(compName=muxComponentName, - compInsName=f"inst_{muxComponentName}_{portName}", - portsPairs=portsPairs) + f"{portName}_input", + connections[portName][::-1], + delay=self.fabric.generateDelayInSwitchMatrix, + ) + self.writer.addInstantiation( + compName=muxComponentName, + compInsName=f"inst_{muxComponentName}_{portName}", + portsPairs=portsPairs, + ) if muxSize != 2 and muxSize != 4 and muxSize != 8 and muxSize != 16: print( - f"HINT: creating a MUX-{muxSize} for port {portName} using MUX-{muxSize} in switch matrix for tile {tile.name}") + f"HINT: creating a MUX-{muxSize} for port {portName} using MUX-{muxSize} in switch matrix for tile {tile.name}" + ) else: # generic multiplexer self.writer.addAssignScalar( - portName, f"{portName}_input[ConfigBits[{configBitstreamPosition-1}:{configBitstreamPosition}]]") + portName, + f"{portName}_input[ConfigBits[{configBitstreamPosition-1}:{configBitstreamPosition}]]", + ) # update the configuration bitstream position - configBitstreamPosition += len( - connections[portName]).bit_length()-1 + configBitstreamPosition += len(connections[portName]).bit_length() - 1 ### SwitchMatrixDebugSignals ### SwitchMatrixDebugSignals ### ### SwitchMatrixDebugSignals ### SwitchMatrixDebugSignals ### @@ -641,10 +707,13 @@ def genTileSwitchMatrix(self, tile: Tile) -> None: muxSize = len(connections[portName]) if muxSize >= 2: old_ConfigBitstreamPosition = configBitstreamPosition - configBitstreamPosition += int( - math.ceil(math.log2(muxSize))) + configBitstreamPosition += int(math.ceil(math.log2(muxSize))) self.writer.addAssignVector( - f"DEBUG_select_{portName:<15}", "ConfigBits", configBitstreamPosition-1, old_ConfigBitstreamPosition) + f"DEBUG_select_{portName:<15}", + "ConfigBits", + configBitstreamPosition - 1, + old_ConfigBitstreamPosition, + ) ### SwitchMatrixDebugSignals ### SwitchMatrixDebugSignals ### ### SwitchMatrixDebugSignals ### SwitchMatrixDebugSignals ### @@ -675,24 +744,32 @@ def generateTile(self, tile: Tile) -> None: # GenerateVHDL_Header(file, entity, NoConfigBits=str(GlobalConfigBitsCounter)) self.writer.addHeader(f"{tile.name}") self.writer.addParameterStart(indentLevel=1) - if isinstance(self.writer, VerilogWriter): # emulation only in Verilog - maxBits = self.fabric.frameBitsPerRow*self.fabric.maxFramesPerCol + if isinstance(self.writer, VerilogWriter): # emulation only in Verilog + maxBits = self.fabric.frameBitsPerRow * self.fabric.maxFramesPerCol self.writer.addPreprocIfDef("EMULATION") - self.writer.addParameter("Emulate_Bitstream", f"[{maxBits-1}:0]", - f"{maxBits}'b0", indentLevel=2) + self.writer.addParameter( + "Emulate_Bitstream", f"[{maxBits-1}:0]", f"{maxBits}'b0", indentLevel=2 + ) self.writer.addPreprocEndif() - self.writer.addParameter("MaxFramesPerCol", "integer", - self.fabric.maxFramesPerCol, indentLevel=2) - self.writer.addParameter("FrameBitsPerRow", "integer", - self.fabric.frameBitsPerRow, indentLevel=2) - self.writer.addParameter("NoConfigBits", "integer", - tile.globalConfigBits, indentLevel=2) + self.writer.addParameter( + "MaxFramesPerCol", "integer", self.fabric.maxFramesPerCol, indentLevel=2 + ) + self.writer.addParameter( + "FrameBitsPerRow", "integer", self.fabric.frameBitsPerRow, indentLevel=2 + ) + self.writer.addParameter( + "NoConfigBits", "integer", tile.globalConfigBits, indentLevel=2 + ) self.writer.addParameterEnd(indentLevel=1) self.writer.addPortStart(indentLevel=1) # holder for each direction of port string - portList = [tile.getNorthSidePorts(), tile.getEastSidePorts(), - tile.getWestSidePorts(), tile.getSouthSidePorts()] + portList = [ + tile.getNorthSidePorts(), + tile.getEastSidePorts(), + tile.getWestSidePorts(), + tile.getSouthSidePorts(), + ] for l in portList: if not l: continue @@ -700,11 +777,9 @@ def generateTile(self, tile: Tile) -> None: # destination port are input to the tile # source port are output of the tile for p in l: - wireSize = (abs(p.xOffset)+abs(p.yOffset)) * p.wireCount-1 - self.writer.addPortVector(p.name, p.inOut, - wireSize, indentLevel=2) - self.writer.addComment( - str(p), indentLevel=2, onNewLine=False) + wireSize = (abs(p.xOffset) + abs(p.yOffset)) * p.wireCount - 1 + self.writer.addPortVector(p.name, p.inOut, wireSize, indentLevel=2) + self.writer.addComment(str(p), indentLevel=2, onNewLine=False) # now we have to scan all BELs if they use external pins, because they have to be exported to the tile entity externalPorts = [] @@ -721,8 +796,7 @@ def generateTile(self, tile: Tile) -> None: for i in tile.bels: sharedExternalPorts.update(i.sharedPort) - self.writer.addComment("Tile IO ports from BELs", - onNewLine=True, indentLevel=1) + self.writer.addComment("Tile IO ports from BELs", onNewLine=True, indentLevel=1) self.writer.addPortScalar("UserCLK", IO.INPUT, indentLevel=2) self.writer.addPortScalar("UserCLKo", IO.OUTPUT, indentLevel=2) @@ -730,20 +804,26 @@ def generateTile(self, tile: Tile) -> None: if self.fabric.configBitMode == ConfigBitMode.FRAME_BASED: if tile.globalConfigBits > 0: self.writer.addPortVector( - "FrameData", IO.INPUT, "FrameBitsPerRow -1", indentLevel=2) + "FrameData", IO.INPUT, "FrameBitsPerRow -1", indentLevel=2 + ) self.writer.addComment("CONFIG_PORT", onNewLine=False, end="") - self.writer.addPortVector("FrameData_O", IO.OUTPUT, - "FrameBitsPerRow -1", indentLevel=2) - self.writer.addPortVector("FrameStrobe", IO.INPUT, - "MaxFramesPerCol -1", indentLevel=2) + self.writer.addPortVector( + "FrameData_O", IO.OUTPUT, "FrameBitsPerRow -1", indentLevel=2 + ) + self.writer.addPortVector( + "FrameStrobe", IO.INPUT, "MaxFramesPerCol -1", indentLevel=2 + ) self.writer.addComment("CONFIG_PORT", onNewLine=False, end="") - self.writer.addPortVector("FrameStrobe_O", IO.OUTPUT, - "MaxFramesPerCol -1", indentLevel=2) + self.writer.addPortVector( + "FrameStrobe_O", IO.OUTPUT, "MaxFramesPerCol -1", indentLevel=2 + ) else: - self.writer.addPortVector("FrameStrobe", IO.INPUT, - "MaxFramesPerCol -1", indentLevel=2) - self.writer.addPortVector("FrameStrobe_O", IO.OUTPUT, - "MaxFramesPerCol -1", indentLevel=2) + self.writer.addPortVector( + "FrameStrobe", IO.INPUT, "MaxFramesPerCol -1", indentLevel=2 + ) + self.writer.addPortVector( + "FrameStrobe_O", IO.OUTPUT, "MaxFramesPerCol -1", indentLevel=2 + ) elif self.fabric.configBitMode == ConfigBitMode.FLIPFLOP_CHAIN: self.writer.addPortScalar("MODE", IO.INPUT, indentLevel=2) @@ -774,24 +854,36 @@ def generateTile(self, tile: Tile) -> None: tileName = tile.name.rsplit("_", 1)[0] subFolder = tile.name - if os.path.exists(f"Tile/{tileName}/{subFolder}/{tile.name}_switch_matrix.vhdl"): + if os.path.exists( + f"Tile/{tileName}/{subFolder}/{tile.name}_switch_matrix.vhdl" + ): self.writer.addComponentDeclarationForFile( - f"Tile/{tileName}/{subFolder}/{tile.name}_switch_matrix.vhdl") + f"Tile/{tileName}/{subFolder}/{tile.name}_switch_matrix.vhdl" + ) else: raise ValueError( - f"Could not find {tile.name}_switch_matrix.vhdl in Tile/{tileName}/{subFolder}/ Need to run matrix generation first") + f"Could not find {tile.name}_switch_matrix.vhdl in Tile/{tileName}/{subFolder}/ Need to run matrix generation first" + ) - if os.path.exists(f"Tile/{tileName}/{subFolder}/{tile.name}_ConfigMem.vhdl"): + if os.path.exists( + f"Tile/{tileName}/{subFolder}/{tile.name}_ConfigMem.vhdl" + ): self.writer.addComponentDeclarationForFile( - f"Tile/{tileName}/{subFolder}/{tile.name}_ConfigMem.vhdl") + f"Tile/{tileName}/{subFolder}/{tile.name}_ConfigMem.vhdl" + ) else: raise ValueError( - f"Could not find {tile.name}_ConfigMem.vhdl in Tile/{tileName}/{subFolder}/ config_mem generation first") + f"Could not find {tile.name}_ConfigMem.vhdl in Tile/{tileName}/{subFolder}/ config_mem generation first" + ) - if self.fabric.configBitMode == ConfigBitMode.FRAME_BASED and tile.globalConfigBits > 0: + if ( + self.fabric.configBitMode == ConfigBitMode.FRAME_BASED + and tile.globalConfigBits > 0 + ): if os.path.exists(f"{tile.name}_ConfigMem.vhdl"): self.writer.addComponentDeclarationForFile( - f"{tile.name}_ConfigMem.vhdl") + f"{tile.name}_ConfigMem.vhdl" + ) # VHDL signal declarations self.writer.addComment("signal declarations", onNewLine=True) @@ -808,7 +900,11 @@ def generateTile(self, tile: Tile) -> None: self.writer.addComment("Jump wires", onNewLine=True) for p in tile.portsInfo: if p.wireDirection == Direction.JUMP: - if p.sourceName != "NULL" and p.destinationName != "NULL" and p.inOut == IO.OUTPUT: + if ( + p.sourceName != "NULL" + and p.destinationName != "NULL" + and p.inOut == IO.OUTPUT + ): self.writer.addConnectionVector(p.name, f"{p.wireCount}-1") for k in range(p.wireCount): @@ -816,7 +912,9 @@ def generateTile(self, tile: Tile) -> None: # internal configuration data signal to daisy-chain all BELs (if any and in the order they are listed in the fabric.csv) self.writer.addComment( - "internal configuration data signal to daisy-chain all BELs (if any and in the order they are listed in the fabric.csv)", onNewLine=True) + "internal configuration data signal to daisy-chain all BELs (if any and in the order they are listed in the fabric.csv)", + onNewLine=True, + ) # the signal has to be number of BELs+2 bits wide (Bel_counter+1 downto 0) # we chain switch matrices only to the configuration port, if they really contain configuration bits @@ -828,31 +926,32 @@ def generateTile(self, tile: Tile) -> None: # all the signal wire need to declare first for compatibility with VHDL self.writer.addConnectionVector("ConfigBits", "NoConfigBits-1", 0) - self.writer.addConnectionVector( - "ConfigBits_N", "NoConfigBits-1", 0) + self.writer.addConnectionVector("ConfigBits_N", "NoConfigBits-1", 0) self.writer.addNewLine() self.writer.addComment("Connection for outgoing wires", onNewLine=True) - self.writer.addConnectionVector( - "FrameData_i", "FrameBitsPerRow-1", 0) - self.writer.addConnectionVector( - "FrameData_O_i", "FrameBitsPerRow-1", 0) - self.writer.addConnectionVector( - "FrameStrobe_i", "MaxFramesPerCol-1", 0) - self.writer.addConnectionVector( - "FrameStrobe_O_i", "MaxFramesPerCol-1", 0) + self.writer.addConnectionVector("FrameData_i", "FrameBitsPerRow-1", 0) + self.writer.addConnectionVector("FrameData_O_i", "FrameBitsPerRow-1", 0) + self.writer.addConnectionVector("FrameStrobe_i", "MaxFramesPerCol-1", 0) + self.writer.addConnectionVector("FrameStrobe_O_i", "MaxFramesPerCol-1", 0) added = set() for port in tile.portsInfo: span = abs(port.xOffset) + abs(port.yOffset) if (port.sourceName, port.destinationName) in added: continue - if span >= 2 and port.sourceName != "NULL" and port.destinationName != "NULL": - highBoundIndex = span*port.wireCount - 1 + if ( + span >= 2 + and port.sourceName != "NULL" + and port.destinationName != "NULL" + ): + highBoundIndex = span * port.wireCount - 1 self.writer.addConnectionVector( - f"{port.destinationName}_i", highBoundIndex) + f"{port.destinationName}_i", highBoundIndex + ) self.writer.addConnectionVector( - f"{port.sourceName}_i", highBoundIndex - port.wireCount) + f"{port.sourceName}_i", highBoundIndex - port.wireCount + ) added.add((port.sourceName, port.destinationName)) self.writer.addNewLine() @@ -862,84 +961,114 @@ def generateTile(self, tile: Tile) -> None: self.writer.addAssignScalar("FrameData_O_i", "FrameData_i") self.writer.addNewLine() for i in range(self.fabric.frameBitsPerRow): - self.writer.addInstantiation("my_buf", - f"data_inbuf_{i}", - portsPairs=[("A", f"FrameData[{i}]"), - ("X", f"FrameData_i[{i}]")]) + self.writer.addInstantiation( + "my_buf", + f"data_inbuf_{i}", + portsPairs=[("A", f"FrameData[{i}]"), ("X", f"FrameData_i[{i}]")], + ) for i in range(self.fabric.frameBitsPerRow): - self.writer.addInstantiation("my_buf", - f"data_outbuf_{i}", - portsPairs=[("A", f"FrameData_O_i[{i}]"), - ("X", f"FrameData_O[{i}]")]) + self.writer.addInstantiation( + "my_buf", + f"data_outbuf_{i}", + portsPairs=[ + ("A", f"FrameData_O_i[{i}]"), + ("X", f"FrameData_O[{i}]"), + ], + ) # strobe is always added even when config bits are 0 self.writer.addAssignScalar("FrameStrobe_O_i", "FrameStrobe_i") self.writer.addNewLine() for i in range(self.fabric.maxFramesPerCol): - self.writer.addInstantiation("my_buf", - f"strobe_inbuf_{i}", - portsPairs=[("A", f"FrameStrobe[{i}]"), - ("X", f"FrameStrobe_i[{i}]")]) + self.writer.addInstantiation( + "my_buf", + f"strobe_inbuf_{i}", + portsPairs=[("A", f"FrameStrobe[{i}]"), ("X", f"FrameStrobe_i[{i}]")], + ) for i in range(self.fabric.maxFramesPerCol): - self.writer.addInstantiation("my_buf", - f"strobe_outbuf_{i}", - portsPairs=[("A", f"FrameStrobe_O_i[{i}]"), - ("X", f"FrameStrobe_O[{i}]")]) + self.writer.addInstantiation( + "my_buf", + f"strobe_outbuf_{i}", + portsPairs=[ + ("A", f"FrameStrobe_O_i[{i}]"), + ("X", f"FrameStrobe_O[{i}]"), + ], + ) added = set() for port in tile.portsInfo: span = abs(port.xOffset) + abs(port.yOffset) if (port.sourceName, port.destinationName) in added: continue - if span >= 2 and port.sourceName != "NULL" and port.destinationName != "NULL": - highBoundIndex = span*port.wireCount - 1 + if ( + span >= 2 + and port.sourceName != "NULL" + and port.destinationName != "NULL" + ): + highBoundIndex = span * port.wireCount - 1 # using scalar assignment to connect the two vectors # could replace with assign as vector, but will lose the - wireCount readability self.writer.addAssignScalar( - f"{port.sourceName}_i[{highBoundIndex}-{port.wireCount}:0]", f"{port.destinationName}_i[{highBoundIndex}:{port.wireCount}]") + f"{port.sourceName}_i[{highBoundIndex}-{port.wireCount}:0]", + f"{port.destinationName}_i[{highBoundIndex}:{port.wireCount}]", + ) self.writer.addNewLine() for i in range(highBoundIndex - port.wireCount + 1): - self.writer.addInstantiation("my_buf", - f"{port.destinationName}_inbuf_{i}", - portsPairs=[("A", f"{port.destinationName}[{i+port.wireCount}]"), - ("X", f"{port.destinationName}_i[{i+port.wireCount}]")]) + self.writer.addInstantiation( + "my_buf", + f"{port.destinationName}_inbuf_{i}", + portsPairs=[ + ("A", f"{port.destinationName}[{i+port.wireCount}]"), + ("X", f"{port.destinationName}_i[{i+port.wireCount}]"), + ], + ) for i in range(highBoundIndex - port.wireCount + 1): - self.writer.addInstantiation("my_buf", - f"{port.sourceName}_outbuf_{i}", - portsPairs=[("A", f"{port.sourceName}_i[{i}]"), - ("X", f"{port.sourceName}[{i}]")]) + self.writer.addInstantiation( + "my_buf", + f"{port.sourceName}_outbuf_{i}", + portsPairs=[ + ("A", f"{port.sourceName}_i[{i}]"), + ("X", f"{port.sourceName}[{i}]"), + ], + ) added.add((port.sourceName, port.destinationName)) - self.writer.addInstantiation("clk_buf", - f"inst_clk_buf", - portsPairs=[("A", f"UserCLK"), - ("X", f"UserCLKo")]) + self.writer.addInstantiation( + "clk_buf", + f"inst_clk_buf", + portsPairs=[("A", f"UserCLK"), ("X", f"UserCLKo")], + ) self.writer.addNewLine() # top configuration data daisy chaining if self.fabric.configBitMode == ConfigBitMode.FLIPFLOP_CHAIN: self.writer.addComment( - "top configuration data daisy chaining", onNewLine=True) + "top configuration data daisy chaining", onNewLine=True + ) self.writer.addAssignScalar("conf_data(conf_data'low)", "CONFin") - self.writer.addComment( - "conf_data'low=0 and CONFin is from tile entity") + self.writer.addComment("conf_data'low=0 and CONFin is from tile entity") self.writer.addAssignScalar("conf_data(conf_data'high)", "CONFout") self.writer.addComment("CONFout is from tile entity") # the _ConfigMem module is only parametrized through generics, so we hard code its instantiation here - if self.fabric.configBitMode == ConfigBitMode.FRAME_BASED and tile.globalConfigBits > 0: - self.writer.addComment( - "configuration storage latches", onNewLine=True) - self.writer.addInstantiation(compName=f"{tile.name}_ConfigMem", - compInsName=f"Inst_{tile.name}_ConfigMem", - portsPairs=[("FrameData", "FrameData"), - ("FrameStrobe", - "FrameStrobe"), - ("ConfigBits", "ConfigBits"), - ("ConfigBits_N", "ConfigBits_N")], - emulateParamPairs=[("Emulate_Bitstream", "Emulate_Bitstream")]) + if ( + self.fabric.configBitMode == ConfigBitMode.FRAME_BASED + and tile.globalConfigBits > 0 + ): + self.writer.addComment("configuration storage latches", onNewLine=True) + self.writer.addInstantiation( + compName=f"{tile.name}_ConfigMem", + compInsName=f"Inst_{tile.name}_ConfigMem", + portsPairs=[ + ("FrameData", "FrameData"), + ("FrameStrobe", "FrameStrobe"), + ("ConfigBits", "ConfigBits"), + ("ConfigBits_N", "ConfigBits_N"), + ], + emulateParamPairs=[("Emulate_Bitstream", "Emulate_Bitstream")], + ) # BEL component instantiations self.writer.addComment("BEL component instantiations", onNewLine=True) @@ -967,16 +1096,22 @@ def generateTile(self, tile: Tile) -> None: if self.fabric.configBitMode == ConfigBitMode.FRAME_BASED: if bel.configBit > 0: portsPairs.append( - ("ConfigBits", f"ConfigBits[{belConfigBitsCounter+bel.configBit}-1:{belConfigBitsCounter}]")) + ( + "ConfigBits", + f"ConfigBits[{belConfigBitsCounter+bel.configBit}-1:{belConfigBitsCounter}]", + ) + ) elif self.fabric.configBitMode == ConfigBitMode.FLIPFLOP_CHAIN: portsPairs.append(("MODE", "Mode")) portsPairs.append(("CONFin", f"conf_data({belCounter})")) portsPairs.append(("CONFout", f"conf_data({belCounter+1})")) portsPairs.append(("CLK", "CLK")) - self.writer.addInstantiation(compName=bel.name, - compInsName=f"Inst_{bel.prefix}{bel.name}", - portsPairs=portsPairs) + self.writer.addInstantiation( + compName=bel.name, + compInsName=f"Inst_{bel.prefix}{bel.name}", + portsPairs=portsPairs, + ) belCounter += 2 belConfigBitsCounter += bel.configBit @@ -1005,8 +1140,9 @@ def generateTile(self, tile: Tile) -> None: # normal input wire for i in tile.portsInfo: if i.wireDirection != Direction.JUMP and i.inOut == IO.INPUT: - portsPairs += list(zip(i.expandPortInfoByName(), - i.expandPortInfoByName(indexed=True))) + portsPairs += list( + zip(i.expandPortInfoByName(), i.expandPortInfoByName(indexed=True)) + ) # bel input wire (bel output is input to switch matrix) for bel in tile.bels: for p in bel.outputs: @@ -1025,8 +1161,12 @@ def generateTile(self, tile: Tile) -> None: # normal output wire for i in tile.portsInfo: if i.wireDirection != Direction.JUMP and i.inOut == IO.OUTPUT: - portsPairs += list(zip(i.expandPortInfoByName(), - i.expandPortInfoByNameTop(indexed=True))) + portsPairs += list( + zip( + i.expandPortInfoByName(), + i.expandPortInfoByNameTop(indexed=True), + ) + ) # bel output wire (bel input is input to switch matrix) for bel in tile.bels: @@ -1052,13 +1192,23 @@ def generateTile(self, tile: Tile) -> None: if self.fabric.configBitMode == ConfigBitMode.FRAME_BASED: if tile.globalConfigBits > 0: portsPairs.append( - ("ConfigBits", f"ConfigBits[{tile.globalConfigBits}-1:{belConfigBitsCounter}]")) + ( + "ConfigBits", + f"ConfigBits[{tile.globalConfigBits}-1:{belConfigBitsCounter}]", + ) + ) portsPairs.append( - ("ConfigBits_N", f"ConfigBits_N[{tile.globalConfigBits}-1:{belConfigBitsCounter}]")) - - self.writer.addInstantiation(compName=f"{tile.name}_switch_matrix", - compInsName=f"Inst_{tile.name}_switch_matrix", - portsPairs=portsPairs) + ( + "ConfigBits_N", + f"ConfigBits_N[{tile.globalConfigBits}-1:{belConfigBitsCounter}]", + ) + ) + + self.writer.addInstantiation( + compName=f"{tile.name}_switch_matrix", + compInsName=f"Inst_{tile.name}_switch_matrix", + portsPairs=portsPairs, + ) self.writer.addDesignDescriptionEnd() self.writer.writeToFile() @@ -1075,20 +1225,25 @@ def generateSuperTile(self, superTile: SuperTile) -> None: self.writer.addParameterStart(indentLevel=1) if isinstance(self.writer, VerilogWriter): self.writer.addPreprocIfDef("EMULATION") - maxBits = self.fabric.frameBitsPerRow*self.fabric.maxFramesPerCol + maxBits = self.fabric.frameBitsPerRow * self.fabric.maxFramesPerCol for y, row in enumerate(superTile.tileMap): for x, tile in enumerate(row): if not tile: continue - self.writer.addParameter(f"Tile_X{x}Y{y}_Emulate_Bitstream", f"[{maxBits-1}:0]", - f"{maxBits}'b0", indentLevel=2) + self.writer.addParameter( + f"Tile_X{x}Y{y}_Emulate_Bitstream", + f"[{maxBits-1}:0]", + f"{maxBits}'b0", + indentLevel=2, + ) self.writer.addPreprocEndif() - self.writer.addParameter("MaxFramesPerCol", "integer", - self.fabric.maxFramesPerCol, indentLevel=2) - self.writer.addParameter("FrameBitsPerRow", "integer", - self.fabric.frameBitsPerRow, indentLevel=2) self.writer.addParameter( - "NoConfigBits", "integer", 0, indentLevel=2) + "MaxFramesPerCol", "integer", self.fabric.maxFramesPerCol, indentLevel=2 + ) + self.writer.addParameter( + "FrameBitsPerRow", "integer", self.fabric.frameBitsPerRow, indentLevel=2 + ) + self.writer.addParameter("NoConfigBits", "integer", 0, indentLevel=2) self.writer.addParameterEnd(indentLevel=1) self.writer.addPortStart(indentLevel=1) @@ -1101,16 +1256,19 @@ def generateSuperTile(self, superTile: SuperTile) -> None: x, y = k.split(",") for pList in v: self.writer.addComment( - f"Tile_X{x}Y{y}_{pList[0].wireDirection}", onNewLine=True, indentLevel=1) + f"Tile_X{x}Y{y}_{pList[0].wireDirection}", + onNewLine=True, + indentLevel=1, + ) for p in pList: wire = (abs(p.xOffset) + abs(p.yOffset)) * p.wireCount - 1 self.writer.addPortVector( - f"Tile_X{x}Y{y}_{p.name}", p.inOut, wire, indentLevel=2) + f"Tile_X{x}Y{y}_{p.name}", p.inOut, wire, indentLevel=2 + ) self.writer.addComment(str(p), onNewLine=False) # add tile external bel port - self.writer.addComment("Tile IO ports from BELs", - onNewLine=True, indentLevel=1) + self.writer.addComment("Tile IO ports from BELs", onNewLine=True, indentLevel=1) for i in superTile.tiles: for b in i.bels: for p in b.externalInput: @@ -1130,28 +1288,57 @@ def generateSuperTile(self, superTile: SuperTile) -> None: if self.fabric.configBitMode == ConfigBitMode.FRAME_BASED: for y, row in enumerate(superTile.tileMap): for x, tile in enumerate(row): - if y - 1 < 0 or superTile.tileMap[y-1][x] == None: + if y - 1 < 0 or superTile.tileMap[y - 1][x] == None: self.writer.addPortVector( - f"Tile_X{x}Y{y}_FrameStrobe_O", IO.OUTPUT, "MaxFramesPerCol-1", indentLevel=2) + f"Tile_X{x}Y{y}_FrameStrobe_O", + IO.OUTPUT, + "MaxFramesPerCol-1", + indentLevel=2, + ) self.writer.addComment("CONFIG_PORT", onNewLine=False) - if x - 1 < 0 or superTile.tileMap[y][x-1] == None: + if x - 1 < 0 or superTile.tileMap[y][x - 1] == None: self.writer.addPortVector( - f"Tile_X{x}Y{y}_FrameData", IO.INPUT, "FrameBitsPerRow-1", indentLevel=2) + f"Tile_X{x}Y{y}_FrameData", + IO.INPUT, + "FrameBitsPerRow-1", + indentLevel=2, + ) self.writer.addComment("CONFIG_PORT", onNewLine=False) - if y + 1 >= len(superTile.tileMap) or superTile.tileMap[y+1][x] == None: + if ( + y + 1 >= len(superTile.tileMap) + or superTile.tileMap[y + 1][x] == None + ): self.writer.addPortVector( - f"Tile_X{x}Y{y}_FrameStrobe", IO.INPUT, "MaxFramesPerCol-1", indentLevel=2) + f"Tile_X{x}Y{y}_FrameStrobe", + IO.INPUT, + "MaxFramesPerCol-1", + indentLevel=2, + ) self.writer.addComment("CONFIG_PORT", onNewLine=False) - if x + 1 >= len(superTile.tileMap[y]) or superTile.tileMap[y][x+1] == None: + if ( + x + 1 >= len(superTile.tileMap[y]) + or superTile.tileMap[y][x + 1] == None + ): self.writer.addPortVector( - f"Tile_X{x}Y{y}_FrameData_O", IO.OUTPUT, "FrameBitsPerRow-1", indentLevel=2) + f"Tile_X{x}Y{y}_FrameData_O", + IO.OUTPUT, + "FrameBitsPerRow-1", + indentLevel=2, + ) self.writer.addComment("CONFIG_PORT", onNewLine=False) for y, row in enumerate(superTile.tileMap): for x, tile in enumerate(row): - if y - 1 < 0 or superTile.tileMap[y-1][x] == None: - self.writer.addPortScalar(f"Tile_X{x}Y{y}_UserCLKo", IO.OUTPUT, indentLevel=2) - if y + 1 >= len(superTile.tileMap) or superTile.tileMap[y+1][x] == None: - self.writer.addPortScalar(f"Tile_X{x}Y{y}_UserCLK", IO.INPUT, indentLevel=2) + if y - 1 < 0 or superTile.tileMap[y - 1][x] == None: + self.writer.addPortScalar( + f"Tile_X{x}Y{y}_UserCLKo", IO.OUTPUT, indentLevel=2 + ) + if ( + y + 1 >= len(superTile.tileMap) + or superTile.tileMap[y + 1][x] == None + ): + self.writer.addPortScalar( + f"Tile_X{x}Y{y}_UserCLK", IO.INPUT, indentLevel=2 + ) self.writer.addPortEnd() self.writer.addHeaderEnd(f"{superTile.name}") self.writer.addDesignDescriptionStart(f"{superTile.name}") @@ -1161,7 +1348,8 @@ def generateSuperTile(self, superTile: SuperTile) -> None: for t in superTile.tiles: # This is only relevant to VHDL code generation, will not affect Verilog code generation self.writer.addComponentDeclarationForFile( - f"Tile/{superTile.name}/{t.name}/{t.name}.vhdl") + f"Tile/{superTile.name}/{t.name}/{t.name}.vhdl" + ) # find all internal connections internalConnections = superTile.getInternalConnections() @@ -1171,26 +1359,38 @@ def generateSuperTile(self, superTile: SuperTile) -> None: for i, x, y in internalConnections: if i: self.writer.addComment( - f"Tile_X{x}Y{y}_{i[0].wireDirection}", onNewLine=True) + f"Tile_X{x}Y{y}_{i[0].wireDirection}", onNewLine=True + ) for p in i: if p.inOut == IO.OUTPUT: - wire = (abs(p.xOffset) + abs(p.yOffset)) * \ - p.wireCount - 1 + wire = (abs(p.xOffset) + abs(p.yOffset)) * p.wireCount - 1 self.writer.addConnectionVector( - f"Tile_X{x}Y{y}_{p.name}", wire, indentLevel=1) + f"Tile_X{x}Y{y}_{p.name}", wire, indentLevel=1 + ) self.writer.addComment(str(p), onNewLine=False) # declare internal connections for frameData, frameStrobe, and UserCLK for y, row in enumerate(superTile.tileMap): for x, tile in enumerate(row): - if 0 <= y - 1 < len(superTile.tileMap) and superTile.tileMap[y-1][x] != None: + if ( + 0 <= y - 1 < len(superTile.tileMap) + and superTile.tileMap[y - 1][x] != None + ): self.writer.addConnectionVector( - f"Tile_X{x}Y{y}_FrameStrobe_O", "MaxFramesPerCol-1", indentLevel=1) + f"Tile_X{x}Y{y}_FrameStrobe_O", + "MaxFramesPerCol-1", + indentLevel=1, + ) self.writer.addConnectionScalar( - f"Tile_X{x}Y{y}_userCLKo", indentLevel=1) - if 0 <= x - 1 < len(superTile.tileMap[y]) and superTile.tileMap[y][x-1] != None: + f"Tile_X{x}Y{y}_userCLKo", indentLevel=1 + ) + if ( + 0 <= x - 1 < len(superTile.tileMap[y]) + and superTile.tileMap[y][x - 1] != None + ): self.writer.addConnectionVector( - f"Tile_X{x}Y{y}_FrameData_O", "FrameBitsPerRow-1", indentLevel=1) + f"Tile_X{x}Y{y}_FrameData_O", "FrameBitsPerRow-1", indentLevel=1 + ) self.writer.addNewLine() @@ -1207,8 +1407,11 @@ def generateSuperTile(self, superTile: SuperTile) -> None: # north direction input connection northPort = [i.name for i in tile.getNorthPorts(IO.INPUT)] - if 0 <= y + 1 < len(superTile.tileMap) and superTile.tileMap[y+1][x] != None: - for p in superTile.tileMap[y+1][x].getNorthPorts(IO.OUTPUT): + if ( + 0 <= y + 1 < len(superTile.tileMap) + and superTile.tileMap[y + 1][x] != None + ): + for p in superTile.tileMap[y + 1][x].getNorthPorts(IO.OUTPUT): northInput.append(f"Tile_X{x}Y{y+1}_{p.name}") else: for p in tile.getNorthPorts(IO.INPUT): @@ -1217,8 +1420,11 @@ def generateSuperTile(self, superTile: SuperTile) -> None: portsPairs += list(zip(northPort, northInput)) # east direction input connection eastPort = [i.name for i in tile.getEastPorts(IO.INPUT)] - if 0 <= x - 1 < len(superTile.tileMap[0]) and superTile.tileMap[y][x-1] != None: - for p in superTile.tileMap[y][x-1].getEastPorts(IO.OUTPUT): + if ( + 0 <= x - 1 < len(superTile.tileMap[0]) + and superTile.tileMap[y][x - 1] != None + ): + for p in superTile.tileMap[y][x - 1].getEastPorts(IO.OUTPUT): eastInput.append(f"Tile_X{x-1}Y{y}_{p.name}") else: for p in tile.getEastPorts(IO.INPUT): @@ -1227,10 +1433,14 @@ def generateSuperTile(self, superTile: SuperTile) -> None: portsPairs += list(zip(eastPort, eastInput)) # south direction input connection - southPort = [i.name for i in tile.getSouthPorts(IO.INPUT) - if i.inOut == IO.INPUT] - if 0 <= y - 1 < len(superTile.tileMap) and superTile.tileMap[y-1][x] != None: - for p in superTile.tileMap[y-1][x].getSouthPorts(IO.OUTPUT): + southPort = [ + i.name for i in tile.getSouthPorts(IO.INPUT) if i.inOut == IO.INPUT + ] + if ( + 0 <= y - 1 < len(superTile.tileMap) + and superTile.tileMap[y - 1][x] != None + ): + for p in superTile.tileMap[y - 1][x].getSouthPorts(IO.OUTPUT): southInput.append(f"Tile_X{x}Y{y-1}_{p.name}") else: for p in tile.getSouthPorts(IO.INPUT): @@ -1239,10 +1449,14 @@ def generateSuperTile(self, superTile: SuperTile) -> None: portsPairs += list(zip(southPort, southInput)) # west direction input connection - westPort = [i.name for i in tile.getWestPorts(IO.INPUT) - if i.inOut == IO.INPUT] - if 0 <= x + 1 < len(superTile.tileMap[0]) and superTile.tileMap[y][x+1] != None: - for p in superTile.tileMap[y][x+1].getWestPorts(IO.OUTPUT): + westPort = [ + i.name for i in tile.getWestPorts(IO.INPUT) if i.inOut == IO.INPUT + ] + if ( + 0 <= x + 1 < len(superTile.tileMap[0]) + and superTile.tileMap[y][x + 1] != None + ): + for p in superTile.tileMap[y][x + 1].getWestPorts(IO.OUTPUT): westInput.append(f"Tile_X{x+1}Y{y}_{p.name}") else: for p in tile.getWestPorts(IO.INPUT): @@ -1250,49 +1464,57 @@ def generateSuperTile(self, superTile: SuperTile) -> None: portsPairs += list(zip(westPort, westInput)) - for p in tile.getNorthPorts(IO.OUTPUT) + tile.getEastPorts(IO.OUTPUT) + tile.getSouthPorts(IO.OUTPUT) + tile.getWestPorts(IO.OUTPUT): - portsPairs.append( - (p.name, f"Tile_X{x}Y{y}_{p.name}")) + for p in ( + tile.getNorthPorts(IO.OUTPUT) + + tile.getEastPorts(IO.OUTPUT) + + tile.getSouthPorts(IO.OUTPUT) + + tile.getWestPorts(IO.OUTPUT) + ): + portsPairs.append((p.name, f"Tile_X{x}Y{y}_{p.name}")) # add clock to tile - if 0 <= y + 1 < len(superTile.tileMap) and superTile.tileMap[y+1][x] != None: - portsPairs.append( - ("UserCLK", f"Tile_X{x}Y{y+1}_UserCLKo")) + if ( + 0 <= y + 1 < len(superTile.tileMap) + and superTile.tileMap[y + 1][x] != None + ): + portsPairs.append(("UserCLK", f"Tile_X{x}Y{y+1}_UserCLKo")) else: - portsPairs.append( - ("UserCLK", f"Tile_X{x}Y{y}_UserCLK")) - portsPairs.append( - ("UserCLKo", f"Tile_X{x}Y{y}_UserCLKo")) + portsPairs.append(("UserCLK", f"Tile_X{x}Y{y}_UserCLK")) + portsPairs.append(("UserCLKo", f"Tile_X{x}Y{y}_UserCLKo")) if self.fabric.configBitMode == ConfigBitMode.FRAME_BASED: # add connection for frameData, frameStrobe and UserCLK - if 0 <= x - 1 < len(superTile.tileMap[0]) and superTile.tileMap[y][x-1] != None: - portsPairs.append( - ("FrameData", f"Tile_X{x-1}Y{y}_FrameData_O")) + if ( + 0 <= x - 1 < len(superTile.tileMap[0]) + and superTile.tileMap[y][x - 1] != None + ): + portsPairs.append(("FrameData", f"Tile_X{x-1}Y{y}_FrameData_O")) else: - portsPairs.append( - ("FrameData", f"Tile_X{x}Y{y}_FrameData")) + portsPairs.append(("FrameData", f"Tile_X{x}Y{y}_FrameData")) - portsPairs.append( - ("FrameData_O", f"Tile_X{x}Y{y}_FrameData_O")) + portsPairs.append(("FrameData_O", f"Tile_X{x}Y{y}_FrameData_O")) - if 0 <= y + 1 < len(superTile.tileMap) and superTile.tileMap[y+1][x] != None: + if ( + 0 <= y + 1 < len(superTile.tileMap) + and superTile.tileMap[y + 1][x] != None + ): portsPairs.append( - ("FrameStrobe", f"Tile_X{x}Y{y+1}_FrameStrobe_O")) + ("FrameStrobe", f"Tile_X{x}Y{y+1}_FrameStrobe_O") + ) else: - portsPairs.append( - ("FrameStrobe", f"Tile_X{x}Y{y}_FrameStrobe")) + portsPairs.append(("FrameStrobe", f"Tile_X{x}Y{y}_FrameStrobe")) - portsPairs.append( - ("FrameStrobe_O", f"Tile_X{x}Y{y}_FrameStrobe_O")) + portsPairs.append(("FrameStrobe_O", f"Tile_X{x}Y{y}_FrameStrobe_O")) emulateParamPairs = [ ("Emulate_Bitstream", f"Tile_X{x}Y{y}_Emulate_Bitstream") ] - self.writer.addInstantiation(compName=tile.name, - compInsName=f"Tile_X{x}Y{y}_{tile.name}", - portsPairs=portsPairs, - emulateParamPairs=emulateParamPairs) + self.writer.addInstantiation( + compName=tile.name, + compInsName=f"Tile_X{x}Y{y}_{tile.name}", + portsPairs=portsPairs, + emulateParamPairs=emulateParamPairs, + ) self.writer.addDesignDescriptionEnd() self.writer.writeToFile() @@ -1313,12 +1535,13 @@ def generateFabric(self) -> None: fabricName = "eFPGA" self.writer.addHeader(fabricName) self.writer.addParameterStart(indentLevel=1) - self.writer.addParameter("MaxFramesPerCol", "integer", - self.fabric.maxFramesPerCol, indentLevel=2) - self.writer.addParameter("FrameBitsPerRow", "integer", - self.fabric.frameBitsPerRow, indentLevel=2) - self.writer.addParameter("NoConfigBits", "integer", - 0, indentLevel=2) + self.writer.addParameter( + "MaxFramesPerCol", "integer", self.fabric.maxFramesPerCol, indentLevel=2 + ) + self.writer.addParameter( + "FrameBitsPerRow", "integer", self.fabric.frameBitsPerRow, indentLevel=2 + ) + self.writer.addParameter("NoConfigBits", "integer", 0, indentLevel=2) self.writer.addParameterEnd(indentLevel=1) self.writer.addPortStart(indentLevel=1) for y, row in enumerate(self.fabric.tile): @@ -1327,19 +1550,29 @@ def generateFabric(self) -> None: for bel in tile.bels: for i in bel.externalInput: self.writer.addPortScalar( - f"Tile_X{x}Y{y}_{i}", IO.INPUT, indentLevel=2) + f"Tile_X{x}Y{y}_{i}", IO.INPUT, indentLevel=2 + ) self.writer.addComment("EXTERNAL", onNewLine=False) for i in bel.externalOutput: self.writer.addPortScalar( - f"Tile_X{x}Y{y}_{i}", IO.OUTPUT, indentLevel=2) + f"Tile_X{x}Y{y}_{i}", IO.OUTPUT, indentLevel=2 + ) self.writer.addComment("EXTERNAL", onNewLine=False) if self.fabric.configBitMode == ConfigBitMode.FRAME_BASED: self.writer.addPortVector( - "FrameData", IO.INPUT, f"(FrameBitsPerRow*{self.fabric.numberOfRows})-1", indentLevel=2) + "FrameData", + IO.INPUT, + f"(FrameBitsPerRow*{self.fabric.numberOfRows})-1", + indentLevel=2, + ) self.writer.addComment("CONFIG_PORT", onNewLine=False) self.writer.addPortVector( - "FrameStrobe", IO.INPUT, f"(MaxFramesPerCol*{self.fabric.numberOfColumns})-1", indentLevel=2) + "FrameStrobe", + IO.INPUT, + f"(MaxFramesPerCol*{self.fabric.numberOfColumns})-1", + indentLevel=2, + ) self.writer.addComment("CONFIG_PORT", onNewLine=False) self.writer.addPortScalar("UserCLK", IO.INPUT, indentLevel=2) @@ -1356,12 +1589,12 @@ def generateFabric(self) -> None: if name in added: continue if name not in self.fabric.superTileDic.keys(): - self.writer.addComponentDeclarationForFile( - f"Tile/{t}/{t}.vhdl") + self.writer.addComponentDeclarationForFile(f"Tile/{t}/{t}.vhdl") added.add(t) else: self.writer.addComponentDeclarationForFile( - f"Tile/{name}/{name}.vhdl") + f"Tile/{name}/{name}.vhdl" + ) added.add(name) # VHDL signal declarations @@ -1371,10 +1604,11 @@ def generateFabric(self) -> None: for x, tile in enumerate(row): self.writer.addConnectionScalar(f"Tile_X{x}Y{y}_UserCLKo") - self.writer.addComment("configuration signal declarations", - onNewLine=True, end="\n") + self.writer.addComment( + "configuration signal declarations", onNewLine=True, end="\n" + ) - if self.fabric.configBitMode == 'FlipFlopChain': + if self.fabric.configBitMode == "FlipFlopChain": tileCounter = 0 for row in self.fabric.tile: for t in row: @@ -1389,60 +1623,69 @@ def generateFabric(self) -> None: # FrameBitsPerRow : integer := 32; for y in range(self.fabric.numberOfRows): self.writer.addConnectionVector( - f"Tile_Y{y}_FrameData", "FrameBitsPerRow -1") + f"Tile_Y{y}_FrameData", "FrameBitsPerRow -1" + ) for x in range(self.fabric.numberOfColumns): self.writer.addConnectionVector( - f"Tile_X{x}_FrameStrobe", "MaxFramesPerCol - 1") + f"Tile_X{x}_FrameStrobe", "MaxFramesPerCol - 1" + ) for y in range(self.fabric.numberOfRows): for x in range(self.fabric.numberOfColumns): self.writer.addConnectionVector( - f"Tile_X{x}Y{y}_FrameData_O", "FrameBitsPerRow - 1") + f"Tile_X{x}Y{y}_FrameData_O", "FrameBitsPerRow - 1" + ) - for y in range(self.fabric.numberOfRows+1): + for y in range(self.fabric.numberOfRows + 1): for x in range(self.fabric.numberOfColumns): self.writer.addConnectionVector( - f"Tile_X{x}Y{y}_FrameStrobe_O", "MaxFramesPerCol - 1") + f"Tile_X{x}Y{y}_FrameStrobe_O", "MaxFramesPerCol - 1" + ) - self.writer.addComment( - "tile-to-tile signal declarations", onNewLine=True) + self.writer.addComment("tile-to-tile signal declarations", onNewLine=True) for y, row in enumerate(self.fabric.tile): for x, tile in enumerate(row): if tile != None: seenPorts = set() for p in tile.portsInfo: - wireLength = (abs(p.xOffset)+abs(p.yOffset) - ) * p.wireCount-1 + wireLength = (abs(p.xOffset) + abs(p.yOffset)) * p.wireCount - 1 if p.sourceName == "NULL" or p.wireDirection == Direction.JUMP: continue if p.sourceName in seenPorts: continue seenPorts.add(p.sourceName) self.writer.addConnectionVector( - f"Tile_X{x}Y{y}_{p.sourceName}", wireLength) + f"Tile_X{x}Y{y}_{p.sourceName}", wireLength + ) self.writer.addNewLine() # VHDL architecture body self.writer.addLogicStart() # top configuration data daisy chaining # this is copy and paste from tile code generation (so we can modify this here without side effects - if self.fabric.configBitMode == 'FlipFlopChain': - self.writer.addComment( - "configuration data daisy chaining", onNewLine=True) + if self.fabric.configBitMode == "FlipFlopChain": + self.writer.addComment("configuration data daisy chaining", onNewLine=True) self.writer.addAssignScalar("conf_dat'low", "CONFin") - self.writer.addComment( - "conf_data'low=0 and CONFin is from tile entity") + self.writer.addComment("conf_data'low=0 and CONFin is from tile entity") self.writer.addAssignScalar("CONFout", "conf_data'high") self.writer.addComment("CONFout is from tile entity") if self.fabric.configBitMode == ConfigBitMode.FRAME_BASED: - for y in range(1, len(self.fabric.tile)-1): + for y in range(1, len(self.fabric.tile) - 1): self.writer.addAssignVector( - f"Tile_Y{y}_FrameData", "FrameData", f"FrameBitsPerRow*({y}+1)-1", f"FrameBitsPerRow*{y}") + f"Tile_Y{y}_FrameData", + "FrameData", + f"FrameBitsPerRow*({y}+1)-1", + f"FrameBitsPerRow*{y}", + ) for x in range(len(self.fabric.tile[0])): self.writer.addAssignVector( - f"Tile_X{x}_FrameStrobe", "FrameStrobe", f"MaxFramesPerCol*({x}+1)-1", f"MaxFramesPerCol*{x}") + f"Tile_X{x}_FrameStrobe", + "FrameStrobe", + f"MaxFramesPerCol*({x}+1)-1", + f"MaxFramesPerCol*{x}", + ) instantiatedPosition = [] # Tile instantiations @@ -1472,12 +1715,14 @@ def generateFabric(self) -> None: if superTile: portsAround = superTile.getPortsAroundTile() - cord = [(i.split(",")[0], i.split(",")[1]) - for i in list(portsAround.keys())] - for (i, j) in cord: + cord = [ + (i.split(",")[0], i.split(",")[1]) + for i in list(portsAround.keys()) + ] + for i, j in cord: tileLocationOffset.append((int(i), int(j))) - instantiatedPosition.append((x+int(i), y+int(j))) - superTileLoc.append((x+int(i), y+int(j))) + instantiatedPosition.append((x + int(i), y + int(j))) + superTileLoc.append((x + int(i), y + int(j))) else: tileLocationOffset.append((0, 0)) @@ -1486,78 +1731,151 @@ def generateFabric(self) -> None: # if is a normal tile then the offset is (0, 0) for i, j in tileLocationOffset: # input connection from north side of the south tile - if 0 <= y + 1 < len(self.fabric.tile) and self.fabric.tile[y+j+1][x+i] != None and (x+i, y+j+1) not in superTileLoc: - if self.fabric.tile[y+j][x+i].partOfSuperTile: + if ( + 0 <= y + 1 < len(self.fabric.tile) + and self.fabric.tile[y + j + 1][x + i] != None + and (x + i, y + j + 1) not in superTileLoc + ): + if self.fabric.tile[y + j][x + i].partOfSuperTile: northPorts = [ - f"Tile_X{i}Y{j}_{p.name}" for p in self.fabric.tile[y+j][x+i].getNorthPorts(IO.INPUT)] + f"Tile_X{i}Y{j}_{p.name}" + for p in self.fabric.tile[y + j][x + i].getNorthPorts( + IO.INPUT + ) + ] else: northPorts = [ - i.name for i in self.fabric.tile[y+j][x+i].getNorthPorts(IO.INPUT)] + i.name + for i in self.fabric.tile[y + j][x + i].getNorthPorts( + IO.INPUT + ) + ] northInput = [ - f"Tile_X{x+i}Y{y+j+1}_{p.name}" for p in self.fabric.tile[y+j+1][x+i].getNorthPorts(IO.OUTPUT)] + f"Tile_X{x+i}Y{y+j+1}_{p.name}" + for p in self.fabric.tile[y + j + 1][x + i].getNorthPorts( + IO.OUTPUT + ) + ] portsPairs += list(zip(northPorts, northInput)) # input connection from east side of the west tile - if 0 <= x - 1 < len(self.fabric.tile[0]) and self.fabric.tile[y+j][x+i-1] != None and (x+i-1, y+j) not in superTileLoc: - if self.fabric.tile[y+j][x+i].partOfSuperTile: + if ( + 0 <= x - 1 < len(self.fabric.tile[0]) + and self.fabric.tile[y + j][x + i - 1] != None + and (x + i - 1, y + j) not in superTileLoc + ): + if self.fabric.tile[y + j][x + i].partOfSuperTile: eastPorts = [ - f"Tile_X{i}Y{j}_{p.name}" for p in self.fabric.tile[y+j][x+i].getEastPorts(IO.INPUT)] + f"Tile_X{i}Y{j}_{p.name}" + for p in self.fabric.tile[y + j][x + i].getEastPorts( + IO.INPUT + ) + ] else: eastPorts = [ - i.name for i in self.fabric.tile[y+j][x+i].getEastPorts(IO.INPUT)] + i.name + for i in self.fabric.tile[y + j][x + i].getEastPorts( + IO.INPUT + ) + ] eastInput = [ - f"Tile_X{x+i-1}Y{y+j}_{p.name}" for p in self.fabric.tile[y+j][x+i-1].getEastPorts(IO.OUTPUT)] + f"Tile_X{x+i-1}Y{y+j}_{p.name}" + for p in self.fabric.tile[y + j][x + i - 1].getEastPorts( + IO.OUTPUT + ) + ] portsPairs += list(zip(eastPorts, eastInput)) # input connection from south side of the north tile - if 0 <= y - 1 < len(self.fabric.tile) and self.fabric.tile[y+j-1][x+i] != None and (x+i, y+j-1) not in superTileLoc: - if self.fabric.tile[y+j][x+i].partOfSuperTile: + if ( + 0 <= y - 1 < len(self.fabric.tile) + and self.fabric.tile[y + j - 1][x + i] != None + and (x + i, y + j - 1) not in superTileLoc + ): + if self.fabric.tile[y + j][x + i].partOfSuperTile: southPorts = [ - f"Tile_X{i}Y{j}_{p.name}" for p in self.fabric.tile[y+j][x+i].getSouthPorts(IO.INPUT)] + f"Tile_X{i}Y{j}_{p.name}" + for p in self.fabric.tile[y + j][x + i].getSouthPorts( + IO.INPUT + ) + ] else: southPorts = [ - i.name for i in self.fabric.tile[y+j][x+i].getSouthPorts(IO.INPUT)] + i.name + for i in self.fabric.tile[y + j][x + i].getSouthPorts( + IO.INPUT + ) + ] southInput = [ - f"Tile_X{x+i}Y{y+j-1}_{p.name}" for p in self.fabric.tile[y+j-1][x+i].getSouthPorts(IO.OUTPUT)] + f"Tile_X{x+i}Y{y+j-1}_{p.name}" + for p in self.fabric.tile[y + j - 1][x + i].getSouthPorts( + IO.OUTPUT + ) + ] portsPairs += list(zip(southPorts, southInput)) # input connection from west side of the east tile - if 0 <= x + 1 < len(self.fabric.tile[0]) and self.fabric.tile[y+j][x+i+1] != None and (x+i+1, y+j) not in superTileLoc: - if self.fabric.tile[y+j][x+i].partOfSuperTile: + if ( + 0 <= x + 1 < len(self.fabric.tile[0]) + and self.fabric.tile[y + j][x + i + 1] != None + and (x + i + 1, y + j) not in superTileLoc + ): + if self.fabric.tile[y + j][x + i].partOfSuperTile: westPorts = [ - f"Tile_X{i}Y{j}_{p.name}" for p in self.fabric.tile[y+j][x+i].getWestPorts(IO.INPUT)] + f"Tile_X{i}Y{j}_{p.name}" + for p in self.fabric.tile[y + j][x + i].getWestPorts( + IO.INPUT + ) + ] else: westPorts = [ - i.name for i in self.fabric.tile[y+j][x+i].getWestPorts(IO.INPUT)] + i.name + for i in self.fabric.tile[y + j][x + i].getWestPorts( + IO.INPUT + ) + ] westInput = [ - f"Tile_X{x+i+1}Y{y+j}_{p.name}" for p in self.fabric.tile[y+j][x+i+1].getWestPorts(IO.OUTPUT)] + f"Tile_X{x+i+1}Y{y+j}_{p.name}" + for p in self.fabric.tile[y + j][x + i + 1].getWestPorts( + IO.OUTPUT + ) + ] portsPairs += list(zip(westPorts, westInput)) # output signal name is same as the output port name if superTile: portsAround = superTile.getPortsAroundTile() - cord = [(i.split(",")[0], i.split(",")[1]) - for i in list(portsAround.keys())] + cord = [ + (i.split(",")[0], i.split(",")[1]) + for i in list(portsAround.keys()) + ] cord = list(zip(cord, portsAround.values())) for (i, j), around in cord: for ports in around: for port in ports: if port.inOut == IO.OUTPUT and port.name != "NULL": portsPairs.append( - (f"Tile_X{int(i)}Y{int(j)}_{port.name}", f"Tile_X{x+int(i)}Y{y+int(j)}_{port.name}")) + ( + f"Tile_X{int(i)}Y{int(j)}_{port.name}", + f"Tile_X{x+int(i)}Y{y+int(j)}_{port.name}", + ) + ) else: for i in tile.getTileOutputNames(): portsPairs.append((i, f"Tile_X{x}Y{y}_{i}")) self.writer.addNewLine() self.writer.addComment( - "tile IO port will get directly connected to top-level tile module", onNewLine=True, indentLevel=0) - for (i, j) in tileLocationOffset: - for b in self.fabric.tile[y+j][x+i].bels: + "tile IO port will get directly connected to top-level tile module", + onNewLine=True, + indentLevel=0, + ) + for i, j in tileLocationOffset: + for b in self.fabric.tile[y + j][x + i].bels: for p in b.externalInput: portsPairs.append((p, f"Tile_X{x+i}Y{y+j}_{p}")) @@ -1570,16 +1888,18 @@ def generateFabric(self) -> None: if not superTile: # for userCLK - if y + 1 < self.fabric.numberOfRows and self.fabric.tile[y+1][x] != None: - portsPairs.append( - ("UserCLK", f"Tile_X{x}Y{y+1}_UserCLKo")) + if ( + y + 1 < self.fabric.numberOfRows + and self.fabric.tile[y + 1][x] != None + ): + portsPairs.append(("UserCLK", f"Tile_X{x}Y{y+1}_UserCLKo")) else: portsPairs.append(("UserCLK", "UserCLK")) # for userCLKo portsPairs.append(("UserCLKo", f"Tile_X{x}Y{y}_UserCLKo")) else: - for (i, j) in tileLocationOffset: + for i, j in tileLocationOffset: # prefix for super tile port if superTile: pre = f"Tile_X{i}Y{j}_" @@ -1587,24 +1907,27 @@ def generateFabric(self) -> None: pre = "" # UserCLK signal if y + 1 >= self.fabric.numberOfRows: - portsPairs.append( - (f"{pre}UserCLK", f"UserCLK")) + portsPairs.append((f"{pre}UserCLK", f"UserCLK")) - elif y + 1 < self.fabric.numberOfRows and self.fabric.tile[y+1][x] == None: - portsPairs.append( - (f"{pre}UserCLK", f"UserCLK")) + elif ( + y + 1 < self.fabric.numberOfRows + and self.fabric.tile[y + 1][x] == None + ): + portsPairs.append((f"{pre}UserCLK", f"UserCLK")) - elif (x+i, y+j+1) not in superTileLoc: + elif (x + i, y + j + 1) not in superTileLoc: portsPairs.append( - (f"{pre}UserCLK", f"Tile_X{x+i}Y{y+j+1}_UserCLKo")) + (f"{pre}UserCLK", f"Tile_X{x+i}Y{y+j+1}_UserCLKo") + ) # UserCLKo signal - if (x+i, y+j-1) not in superTileLoc: + if (x + i, y + j - 1) not in superTileLoc: portsPairs.append( - (f"{pre}UserCLKo", f"Tile_X{x+i}Y{y+j}_UserCLKo")) + (f"{pre}UserCLKo", f"Tile_X{x+i}Y{y+j}_UserCLKo") + ) if self.fabric.configBitMode == ConfigBitMode.FRAME_BASED: - for (i, j) in tileLocationOffset: + for i, j in tileLocationOffset: # prefix for super tile port if superTile: pre = f"Tile_X{i}Y{j}_" @@ -1614,22 +1937,32 @@ def generateFabric(self) -> None: # frameData signal if x == 0: portsPairs.append( - (f"{pre}FrameData", f"Tile_Y{y}_FrameData")) + (f"{pre}FrameData", f"Tile_Y{y}_FrameData") + ) - elif (x+i-1, y+j) not in superTileLoc: + elif (x + i - 1, y + j) not in superTileLoc: portsPairs.append( - (f"{pre}FrameData", f"Tile_X{x+i-1}Y{y+j}_FrameData_O")) + ( + f"{pre}FrameData", + f"Tile_X{x+i-1}Y{y+j}_FrameData_O", + ) + ) # frameData_O signal if x == len(self.fabric.tile[0]) - 1: portsPairs.append( - (f"{pre}FrameData_O", f"Tile_X{x}Y{y}_FrameData_O")) + (f"{pre}FrameData_O", f"Tile_X{x}Y{y}_FrameData_O") + ) - elif (x+i-1, y+j) not in superTileLoc: + elif (x + i - 1, y + j) not in superTileLoc: portsPairs.append( - (f"{pre}FrameData_O", f"Tile_X{x+i}Y{y+j}_FrameData_O")) + ( + f"{pre}FrameData_O", + f"Tile_X{x+i}Y{y+j}_FrameData_O", + ) + ) - for (i, j) in tileLocationOffset: + for i, j in tileLocationOffset: # prefix for super tile port if superTile: pre = f"Tile_X{i}Y{j}_" @@ -1638,37 +1971,59 @@ def generateFabric(self) -> None: # frameStrobe signal if y + 1 >= self.fabric.numberOfRows: portsPairs.append( - (f"{pre}FrameStrobe", f"Tile_X{x}_FrameStrobe")) + (f"{pre}FrameStrobe", f"Tile_X{x}_FrameStrobe") + ) - elif y + 1 < self.fabric.numberOfRows and self.fabric.tile[y+1][x] == None: + elif ( + y + 1 < self.fabric.numberOfRows + and self.fabric.tile[y + 1][x] == None + ): portsPairs.append( - (f"{pre}FrameStrobe", f"Tile_X{x}_FrameStrobe")) + (f"{pre}FrameStrobe", f"Tile_X{x}_FrameStrobe") + ) - elif (x+i, y+j+1) not in superTileLoc: + elif (x + i, y + j + 1) not in superTileLoc: portsPairs.append( - (f"{pre}FrameStrobe", f"Tile_X{x+i}Y{y+j+1}_FrameStrobe_O")) + ( + f"{pre}FrameStrobe", + f"Tile_X{x+i}Y{y+j+1}_FrameStrobe_O", + ) + ) # frameStrobe_O signal - if (x+i, y+j-1) not in superTileLoc: + if (x + i, y + j - 1) not in superTileLoc: portsPairs.append( - (f"{pre}FrameStrobe_O", f"Tile_X{x+i}Y{y+j}_FrameStrobe_O")) + ( + f"{pre}FrameStrobe_O", + f"Tile_X{x+i}Y{y+j}_FrameStrobe_O", + ) + ) name = "" emulateParamPairs = [] if superTile: name = superTile.name - for (i, j) in tileLocationOffset: - if (y+j) not in (0, self.fabric.numberOfRows-1): - emulateParamPairs.append((f"Tile_X{i}Y{j}_Emulate_Bitstream", f"`Tile_X{x+i}Y{y+j}_Emulate_Bitstream")) + for i, j in tileLocationOffset: + if (y + j) not in (0, self.fabric.numberOfRows - 1): + emulateParamPairs.append( + ( + f"Tile_X{i}Y{j}_Emulate_Bitstream", + f"`Tile_X{x+i}Y{y+j}_Emulate_Bitstream", + ) + ) else: name = tile.name - if y not in (0, self.fabric.numberOfRows-1): - emulateParamPairs.append((f"Emulate_Bitstream", f"`Tile_X{x}Y{y}_Emulate_Bitstream")) - - self.writer.addInstantiation(compName=name, - compInsName=f"Tile_X{x}Y{y}_{name}", - portsPairs=portsPairs, - emulateParamPairs=emulateParamPairs) + if y not in (0, self.fabric.numberOfRows - 1): + emulateParamPairs.append( + (f"Emulate_Bitstream", f"`Tile_X{x}Y{y}_Emulate_Bitstream") + ) + + self.writer.addInstantiation( + compName=name, + compInsName=f"Tile_X{x}Y{y}_{name}", + portsPairs=portsPairs, + emulateParamPairs=emulateParamPairs, + ) self.writer.addDesignDescriptionEnd() self.writer.writeToFile() @@ -1717,7 +2072,6 @@ def split_port(p): # Y is in reverse order return ((-y, x), tuple(indices), basename) - # determine external ports so we can group them externalPorts = [] portGroups = dict() @@ -1736,7 +2090,7 @@ def split_port(p): portGroups[port][1].append(name) # sort port groups according to vectorisation order for name, g in portGroups.items(): - g[1].sort(key=lambda x:split_port(x)) + g[1].sort(key=lambda x: split_port(x)) # header numberOfRows = self.fabric.numberOfRows - 2 @@ -1744,36 +2098,42 @@ def split_port(p): self.writer.addHeader(f"{self.fabric.name}_top") self.writer.addParameterStart(indentLevel=1) self.writer.addParameter("include_eFPGA", "integer", 1, indentLevel=2) - self.writer.addParameter("NumberOfRows", "integer", - numberOfRows, indentLevel=2) - self.writer.addParameter("NumberOfCols", "integer", - self.fabric.numberOfColumns, indentLevel=2) - self.writer.addParameter("FrameBitsPerRow", "integer", - self.fabric.frameBitsPerRow, indentLevel=2) - self.writer.addParameter("MaxFramesPerCol", "integer", - self.fabric.maxFramesPerCol, indentLevel=2) - self.writer.addParameter("desync_flag", "integer", - self.fabric.desync_flag, indentLevel=2) - self.writer.addParameter("FrameSelectWidth", "integer", - self.fabric.frameSelectWidth, indentLevel=2) - self.writer.addParameter("RowSelectWidth", "integer", - self.fabric.rowSelectWidth, indentLevel=2) + self.writer.addParameter("NumberOfRows", "integer", numberOfRows, indentLevel=2) + self.writer.addParameter( + "NumberOfCols", "integer", self.fabric.numberOfColumns, indentLevel=2 + ) + self.writer.addParameter( + "FrameBitsPerRow", "integer", self.fabric.frameBitsPerRow, indentLevel=2 + ) + self.writer.addParameter( + "MaxFramesPerCol", "integer", self.fabric.maxFramesPerCol, indentLevel=2 + ) + self.writer.addParameter( + "desync_flag", "integer", self.fabric.desync_flag, indentLevel=2 + ) + self.writer.addParameter( + "FrameSelectWidth", "integer", self.fabric.frameSelectWidth, indentLevel=2 + ) + self.writer.addParameter( + "RowSelectWidth", "integer", self.fabric.rowSelectWidth, indentLevel=2 + ) self.writer.addParameterEnd(indentLevel=1) self.writer.addPortStart(indentLevel=1) - self.writer.addComment( - "External IO port", onNewLine=True, indentLevel=2) - for name, group in sorted(portGroups.items(), key=lambda x:x[0]): - if self.fabric.numberOfBRAMs > 0 and ("RAM2FAB" in name or "FAB2RAM" in name): + self.writer.addComment("External IO port", onNewLine=True, indentLevel=2) + for name, group in sorted(portGroups.items(), key=lambda x: x[0]): + if self.fabric.numberOfBRAMs > 0 and ( + "RAM2FAB" in name or "FAB2RAM" in name + ): continue - self.writer.addPortVector(name, group[0], len(group[1])-1, indentLevel=2) - self.writer.addComment("Config related ports", - onNewLine=True, indentLevel=2) + self.writer.addPortVector(name, group[0], len(group[1]) - 1, indentLevel=2) + self.writer.addComment("Config related ports", onNewLine=True, indentLevel=2) self.writer.addPortScalar("CLK", IO.INPUT, indentLevel=2) self.writer.addPortScalar("resetn", IO.INPUT, indentLevel=2) self.writer.addPortScalar("SelfWriteStrobe", IO.INPUT, indentLevel=2) self.writer.addPortVector( - "SelfWriteData", IO.INPUT, self.fabric.frameBitsPerRow-1, indentLevel=2) + "SelfWriteData", IO.INPUT, self.fabric.frameBitsPerRow - 1, indentLevel=2 + ) self.writer.addPortScalar("Rx", IO.INPUT, indentLevel=2) self.writer.addPortScalar("ComActive", IO.OUTPUT, indentLevel=2) self.writer.addPortScalar("ReceiveLED", IO.OUTPUT, indentLevel=2) @@ -1795,13 +2155,15 @@ def split_port(p): self.writer.addNewLine() self.writer.addComment("Signal declarations", onNewLine=True) self.writer.addConnectionVector( - "FrameRegister", "(NumberOfRows*FrameBitsPerRow)-1") - self.writer.addConnectionVector( - "FrameSelect", "(MaxFramesPerCol*NumberOfCols)-1") + "FrameRegister", "(NumberOfRows*FrameBitsPerRow)-1" + ) self.writer.addConnectionVector( - "FrameData", "(FrameBitsPerRow*(NumberOfRows+2))-1") + "FrameSelect", "(MaxFramesPerCol*NumberOfCols)-1" + ) self.writer.addConnectionVector( - "FrameAddressRegister", "FrameBitsPerRow-1") + "FrameData", "(FrameBitsPerRow*(NumberOfRows+2))-1" + ) + self.writer.addConnectionVector("FrameAddressRegister", "FrameBitsPerRow-1") self.writer.addConnectionScalar("LongFrameStrobe") self.writer.addConnectionVector("LocalWriteData", 31) self.writer.addConnectionScalar("LocalWriteStrobe") @@ -1810,28 +2172,22 @@ def split_port(p): if isinstance(self.writer, VHDLWriter): if not os.path.exists("./Fabric/Frame_Data_Reg.vhdl"): - raise FileExistsError( - "Frame_Data_Reg.vhdl not found in Fabric folder") + raise FileExistsError("Frame_Data_Reg.vhdl not found in Fabric folder") if not os.path.exists("./Fabric/Frame_Select.vhdl"): - raise FileExistsError( - "Frame_Select.vhdl not found in Fabric folder") + raise FileExistsError("Frame_Select.vhdl not found in Fabric folder") if not os.path.exists("./Fabric/eFPGA_Config.vhdl"): raise FileExistsError("Config.vhdl not found in Fabric folder") if not os.path.exists("./Fabric/eFPGA.vhdl"): raise FileExistsError( - "eFPGA.vhdl not found in Fabric folder, need to generate the eFPGA first") + "eFPGA.vhdl not found in Fabric folder, need to generate the eFPGA first" + ) if not os.path.exists("./Fabric/BlockRAM_1KB.vhdl"): - raise FileExistsError( - "BlockRAM_1KB.vhdl not found in Fabric folder") - self.writer.addComponentDeclarationForFile( - "./Fabric/Frame_Data_Reg.vhdl") - self.writer.addComponentDeclarationForFile( - "./Fabric/Frame_Select.vhdl") - self.writer.addComponentDeclarationForFile( - "./Fabric/eFPGA_Config.vhdl") + raise FileExistsError("BlockRAM_1KB.vhdl not found in Fabric folder") + self.writer.addComponentDeclarationForFile("./Fabric/Frame_Data_Reg.vhdl") + self.writer.addComponentDeclarationForFile("./Fabric/Frame_Select.vhdl") + self.writer.addComponentDeclarationForFile("./Fabric/eFPGA_Config.vhdl") self.writer.addComponentDeclarationForFile("./Fabric/eFPGA.vhdl") - self.writer.addComponentDeclarationForFile( - "./Fabric/BlockRAM_1KB.vhdl") + self.writer.addComponentDeclarationForFile("./Fabric/BlockRAM_1KB.vhdl") self.writer.addLogicStart() @@ -1840,61 +2196,79 @@ def split_port(p): # the config module self.writer.addNewLine() - self.writer.addInstantiation(compName="eFPGA_Config", - compInsName="eFPGA_Config_inst", - portsPairs=[("CLK", "CLK"), - ("resetn", "resetn"), - ("Rx", "Rx"), - ("ComActive", "ComActive"), - ("ReceiveLED", "ReceiveLED"), - ("s_clk", "s_clk"), - ("s_data", "s_data"), - ("SelfWriteData", - "SelfWriteData"), - ("SelfWriteStrobe", - "SelfWriteStrobe"), - ("ConfigWriteData", - "LocalWriteData"), - ("ConfigWriteStrobe", - "LocalWriteStrobe"), - ("FrameAddressRegister", - "FrameAddressRegister"), - ("LongFrameStrobe", - "LongFrameStrobe"), - ("RowSelect", "RowSelect")], - paramPairs=[("RowSelectWidth", "RowSelectWidth"), - ("NumberOfRows", "NumberOfRows"), - ("desync_flag", "desync_flag"), - ("FrameBitsPerRow", - "FrameBitsPerRow"), ]) + self.writer.addInstantiation( + compName="eFPGA_Config", + compInsName="eFPGA_Config_inst", + portsPairs=[ + ("CLK", "CLK"), + ("resetn", "resetn"), + ("Rx", "Rx"), + ("ComActive", "ComActive"), + ("ReceiveLED", "ReceiveLED"), + ("s_clk", "s_clk"), + ("s_data", "s_data"), + ("SelfWriteData", "SelfWriteData"), + ("SelfWriteStrobe", "SelfWriteStrobe"), + ("ConfigWriteData", "LocalWriteData"), + ("ConfigWriteStrobe", "LocalWriteStrobe"), + ("FrameAddressRegister", "FrameAddressRegister"), + ("LongFrameStrobe", "LongFrameStrobe"), + ("RowSelect", "RowSelect"), + ], + paramPairs=[ + ("RowSelectWidth", "RowSelectWidth"), + ("NumberOfRows", "NumberOfRows"), + ("desync_flag", "desync_flag"), + ("FrameBitsPerRow", "FrameBitsPerRow"), + ], + ) self.writer.addNewLine() # the frame data reg module for row in range(numberOfRows): - self.writer.addInstantiation(compName=f"Frame_Data_Reg", - compInsName=f"inst_Frame_Data_Reg_{row}", - portsPairs=[("FrameData_I", "LocalWriteData"), - ("FrameData_O", f"FrameRegister[{row}*FrameBitsPerRow+FrameBitsPerRow-1:{row}*FrameBitsPerRow]"), - ("RowSelect", "RowSelect"), - ("CLK", "CLK")], - paramPairs=[("FrameBitsPerRow", "FrameBitsPerRow"), - ("RowSelectWidth", - "RowSelectWidth"), - ("Row", str(row+1))]) + self.writer.addInstantiation( + compName=f"Frame_Data_Reg", + compInsName=f"inst_Frame_Data_Reg_{row}", + portsPairs=[ + ("FrameData_I", "LocalWriteData"), + ( + "FrameData_O", + f"FrameRegister[{row}*FrameBitsPerRow+FrameBitsPerRow-1:{row}*FrameBitsPerRow]", + ), + ("RowSelect", "RowSelect"), + ("CLK", "CLK"), + ], + paramPairs=[ + ("FrameBitsPerRow", "FrameBitsPerRow"), + ("RowSelectWidth", "RowSelectWidth"), + ("Row", str(row + 1)), + ], + ) self.writer.addNewLine() # the frame select module for col in range(numberOfColumns): - self.writer.addInstantiation(compName=f"Frame_Select", - compInsName=f"inst_Frame_Select_{col}", - portsPairs=[("FrameStrobe_I", "FrameAddressRegister[MaxFramesPerCol-1:0]"), - ("FrameStrobe_O", f"FrameSelect[{col}*MaxFramesPerCol+MaxFramesPerCol-1:{col}*MaxFramesPerCol]"), - ("FrameSelect", "FrameAddressRegister[FrameBitsPerRow-1:FrameBitsPerRow-FrameSelectWidth]"), - ("FrameStrobe", "LongFrameStrobe")], - paramPairs=[("MaxFramesPerCol", "MaxFramesPerCol"), - ("FrameSelectWidth", - "FrameSelectWidth"), - ("Col", str(col))]) + self.writer.addInstantiation( + compName=f"Frame_Select", + compInsName=f"inst_Frame_Select_{col}", + portsPairs=[ + ("FrameStrobe_I", "FrameAddressRegister[MaxFramesPerCol-1:0]"), + ( + "FrameStrobe_O", + f"FrameSelect[{col}*MaxFramesPerCol+MaxFramesPerCol-1:{col}*MaxFramesPerCol]", + ), + ( + "FrameSelect", + "FrameAddressRegister[FrameBitsPerRow-1:FrameBitsPerRow-FrameSelectWidth]", + ), + ("FrameStrobe", "LongFrameStrobe"), + ], + paramPairs=[ + ("MaxFramesPerCol", "MaxFramesPerCol"), + ("FrameSelectWidth", "FrameSelectWidth"), + ("Col", str(col)), + ], + ) self.writer.addNewLine() if isinstance(self.writer, VerilogWriter): @@ -1905,7 +2279,7 @@ def split_port(p): signal = [] # external ports (IO, config access, BRAM, etc) - for name, group in sorted(portGroups.items(), key=lambda x:x[0]): + for name, group in sorted(portGroups.items(), key=lambda x: x[0]): for i, sig in enumerate(group[1]): portList.append(sig) signal.append(f"{name}[{i}]") @@ -1920,43 +2294,46 @@ def split_port(p): signal.append("FrameSelect") assert len(portList) == len(signal) - self.writer.addInstantiation(compName=self.fabric.name, - compInsName=f"{self.fabric.name}_inst", - portsPairs=list(zip(portList, signal))) + self.writer.addInstantiation( + compName=self.fabric.name, + compInsName=f"{self.fabric.name}_inst", + portsPairs=list(zip(portList, signal)), + ) self.writer.addNewLine() # the BRAM module if "RAM2FAB_D_I" in portGroups and self.fabric.numberOfBRAMs > 0: - data_cap = int((numberOfRows*4*4)/(self.fabric.numberOfBRAMs-1)) - addr_cap = int((numberOfRows*4*2)/(self.fabric.numberOfBRAMs-1)) - config_cap = int((numberOfRows*4)/(self.fabric.numberOfBRAMs-1)) - for i in range(self.fabric.numberOfBRAMs-1): - portsPairs = [("clk", "CLK"), - ("rd_addr", - f"FAB2RAM_A_O[{addr_cap*i+8-1}:{addr_cap*i}]"), - ("rd_data", - f"RAM2FAB_D_I[{data_cap*i+32-1}:{data_cap*i}]"), - ("wr_addr", - f"FAB2RAM_A_O[{addr_cap *i+16-1}:{addr_cap*i+8}]"), - ("wr_data", - f"FAB2RAM_D_O[{data_cap*i+32-1}:{data_cap*i}]"), - ("C0", f"FAB2RAM_C_O[{config_cap*i}]"), - ("C1", f"FAB2RAM_C_O[{config_cap*i+1}]"), - ("C2", f"FAB2RAM_C_O[{config_cap*i+2}]"), - ("C3", f"FAB2RAM_C_O[{config_cap*i+3}]"), - ("C4", f"FAB2RAM_C_O[{config_cap*i+4}]"), - ("C5", f"FAB2RAM_C_O[{config_cap*i+5}]") - ] - self.writer.addInstantiation(compName="BlockRAM_1KB", - compInsName=f"Inst_BlockRAM_{i}", - portsPairs=portsPairs) + data_cap = int((numberOfRows * 4 * 4) / (self.fabric.numberOfBRAMs - 1)) + addr_cap = int((numberOfRows * 4 * 2) / (self.fabric.numberOfBRAMs - 1)) + config_cap = int((numberOfRows * 4) / (self.fabric.numberOfBRAMs - 1)) + for i in range(self.fabric.numberOfBRAMs - 1): + portsPairs = [ + ("clk", "CLK"), + ("rd_addr", f"FAB2RAM_A_O[{addr_cap*i+8-1}:{addr_cap*i}]"), + ("rd_data", f"RAM2FAB_D_I[{data_cap*i+32-1}:{data_cap*i}]"), + ("wr_addr", f"FAB2RAM_A_O[{addr_cap *i+16-1}:{addr_cap*i+8}]"), + ("wr_data", f"FAB2RAM_D_O[{data_cap*i+32-1}:{data_cap*i}]"), + ("C0", f"FAB2RAM_C_O[{config_cap*i}]"), + ("C1", f"FAB2RAM_C_O[{config_cap*i+1}]"), + ("C2", f"FAB2RAM_C_O[{config_cap*i+2}]"), + ("C3", f"FAB2RAM_C_O[{config_cap*i+3}]"), + ("C4", f"FAB2RAM_C_O[{config_cap*i+4}]"), + ("C5", f"FAB2RAM_C_O[{config_cap*i+5}]"), + ] + self.writer.addInstantiation( + compName="BlockRAM_1KB", + compInsName=f"Inst_BlockRAM_{i}", + portsPairs=portsPairs, + ) if isinstance(self.writer, VHDLWriter): self.writer.addAssignScalar( - "FrameData", ['X"12345678"', "FrameRegister", 'X"12345678"']) + "FrameData", ['X"12345678"', "FrameRegister", 'X"12345678"'] + ) else: self.writer.addAssignScalar( - "FrameData", ["32'h12345678", "FrameRegister", "32'h12345678"]) + "FrameData", ["32'h12345678", "FrameRegister", "32'h12345678"] + ) self.writer.addDesignDescriptionEnd() self.writer.writeToFile() @@ -1968,13 +2345,17 @@ def generateBitsStreamSpec(self) -> Dict[str, Dict]: dict[str, dict]: The bits stream specification of the fabric """ - specData = {"TileMap": {}, - "TileSpecs": {}, - "TileSpecs_No_Mask": {}, - "FrameMap": {}, - "FrameMapEncode": {}, - "ArchSpecs": {"MaxFramesPerCol": self.fabric.maxFramesPerCol, - "FrameBitsPerRow": self.fabric.frameBitsPerRow}} + specData = { + "TileMap": {}, + "TileSpecs": {}, + "TileSpecs_No_Mask": {}, + "FrameMap": {}, + "FrameMapEncode": {}, + "ArchSpecs": { + "MaxFramesPerCol": self.fabric.maxFramesPerCol, + "FrameBitsPerRow": self.fabric.frameBitsPerRow, + }, + } tileMap = {} for y, row in enumerate(self.fabric.tile): @@ -1992,14 +2373,20 @@ def generateBitsStreamSpec(self) -> Dict[str, Dict]: continue if os.path.exists(f"{tile.filePath}/{tile.name}_ConfigMem.csv"): configMemList = parseConfigMem( - f"{tile.filePath}/{tile.name}_ConfigMem.csv", self.fabric.maxFramesPerCol, self.fabric.frameBitsPerRow, tile.globalConfigBits) + f"{tile.filePath}/{tile.name}_ConfigMem.csv", + self.fabric.maxFramesPerCol, + self.fabric.frameBitsPerRow, + tile.globalConfigBits, + ) elif tile.globalConfigBits > 0: logger.error( - f"No ConfigMem csv file found for {tile.name} which have config bits") + f"No ConfigMem csv file found for {tile.name} which have config bits" + ) exit(-1) - encodeDict = [-1] * (self.fabric.maxFramesPerCol * - self.fabric.frameBitsPerRow) + encodeDict = [-1] * ( + self.fabric.maxFramesPerCol * self.fabric.frameBitsPerRow + ) maskDic = {} for cfm in configMemList: maskDic[cfm.frameIndex] = cfm.usedBitMask @@ -2008,12 +2395,12 @@ def generateBitsStreamSpec(self) -> Dict[str, Dict]: for i, char in enumerate(cfm.usedBitMask): if char == "1": encodeDict[cfm.configBitRanges.pop(0)] = ( - self.fabric.frameBitsPerRow - 1 - i) + self.fabric.frameBitsPerRow * cfm.frameIndex + self.fabric.frameBitsPerRow - 1 - i + ) + self.fabric.frameBitsPerRow * cfm.frameIndex # filling the maskDic with the unused frames - for i in range(self.fabric.maxFramesPerCol-len(configMemList)): - maskDic[len(configMemList)+i] = '0' * \ - self.fabric.frameBitsPerRow + for i in range(self.fabric.maxFramesPerCol - len(configMemList)): + maskDic[len(configMemList) + i] = "0" * self.fabric.frameBitsPerRow specData["FrameMap"][tile.name] = maskDic if tile.globalConfigBits == 0: @@ -2030,10 +2417,16 @@ def generateBitsStreamSpec(self) -> Dict[str, Dict]: for entry in keyDict: if isinstance(entry, int): for v in keyDict[entry]: - curTileMap[f"{string.ascii_uppercase[i]}.{featureKey}"] = { - encodeDict[curBitOffset+v]: keyDict[entry][v]} - curTileMapNoMask[f"{string.ascii_uppercase[i]}.{featureKey}"] = { - encodeDict[curBitOffset+v]: keyDict[entry][v]} + curTileMap[ + f"{string.ascii_uppercase[i]}.{featureKey}" + ] = { + encodeDict[curBitOffset + v]: keyDict[entry][v] + } + curTileMapNoMask[ + f"{string.ascii_uppercase[i]}.{featureKey}" + ] = { + encodeDict[curBitOffset + v]: keyDict[entry][v] + } curBitOffset += len(keyDict[entry]) # All the generation will be working on the tile level with the tileDic @@ -2041,12 +2434,11 @@ def generateBitsStreamSpec(self) -> Dict[str, Dict]: if tile.matrixDir.endswith(".list"): tile.matrixDir = tile.matrixDir.replace(".list", ".csv") - result = parseMatrix( - tile.matrixDir, tile.name) + result = parseMatrix(tile.matrixDir, tile.name) for source, sinkList in result.items(): controlWidth = 0 for i, sink in enumerate(reversed(sinkList)): - controlWidth = len(sinkList).bit_length()-1 + controlWidth = len(sinkList).bit_length() - 1 controlValue = f"{len(sinkList) - 1 - i:0{controlWidth}b}" pip = f"{sink}.{source}" if len(sinkList) < 2: @@ -2059,8 +2451,10 @@ def generateBitsStreamSpec(self) -> Dict[str, Dict]: curTileMap[pip] = {} curTileMapNoMask[pip] = {} - curTileMap[pip][encodeDict[curBitOffset+c]] = curChar - curTileMapNoMask[pip][encodeDict[curBitOffset+c]] = curChar + curTileMap[pip][encodeDict[curBitOffset + c]] = curChar + curTileMapNoMask[pip][ + encodeDict[curBitOffset + c] + ] = curChar curBitOffset += controlWidth diff --git a/fabric_generator/file_parser.py b/fabric_generator/file_parser.py index 255572e5..da8a815c 100644 --- a/fabric_generator/file_parser.py +++ b/fabric_generator/file_parser.py @@ -11,8 +11,7 @@ # from fabric import IO, Direction, Side, MultiplexerStyle, ConfigBitMode -oppositeDic = {"NORTH": "SOUTH", "SOUTH": "NORTH", - "EAST": "WEST", "WEST": "EAST"} +oppositeDic = {"NORTH": "SOUTH", "SOUTH": "NORTH", "EAST": "WEST", "WEST": "EAST"} def parseFabricCSV(fileName: str) -> Fabric: @@ -47,30 +46,30 @@ def parseFabricCSV(fileName: str) -> Fabric: filePath, _ = os.path.split(os.path.abspath(fileName)) - with open(fileName, 'r') as f: + with open(fileName, "r") as f: file = f.read() file = re.sub(r"#.*", "", file) # read in the csv file and part them if fabricDescription := re.search( - r"FabricBegin(.*?)FabricEnd", file, re.MULTILINE | re.DOTALL): + r"FabricBegin(.*?)FabricEnd", file, re.MULTILINE | re.DOTALL + ): fabricDescription = fabricDescription.group(1) else: - raise ValueError( - 'Cannot find FabricBegin and FabricEnd in csv file') + raise ValueError("Cannot find FabricBegin and FabricEnd in csv file") if parameters := re.search( - r"ParametersBegin(.*?)ParametersEnd", file, re.MULTILINE | re.DOTALL): + r"ParametersBegin(.*?)ParametersEnd", file, re.MULTILINE | re.DOTALL + ): parameters = parameters.group(1) else: - raise ValueError( - 'Cannot find ParametersBegin and ParametersEnd in csv file') + raise ValueError("Cannot find ParametersBegin and ParametersEnd in csv file") - tilesData = re.findall(r"TILE(.*?)EndTILE", file, - re.MULTILINE | re.DOTALL) + tilesData = re.findall(r"TILE(.*?)EndTILE", file, re.MULTILINE | re.DOTALL) - superTile = re.findall(r"SuperTILE(.*?)EndSuperTILE", - file, re.MULTILINE | re.DOTALL) + superTile = re.findall( + r"SuperTILE(.*?)EndSuperTILE", file, re.MULTILINE | re.DOTALL + ) # parse the tile description fabricDescription = fabricDescription.split("\n") @@ -92,22 +91,65 @@ def parseFabricCSV(fileName: str) -> Fabric: if not temp or temp[0] == "": continue if temp[0] in ["NORTH", "SOUTH", "EAST", "WEST"]: - ports.append(Port(Direction[temp[0]], temp[1], int( - temp[2]), int(temp[3]), temp[4], int(temp[5]), temp[1], IO.OUTPUT, Side[temp[0]])) - - ports.append(Port(Direction[temp[0]], temp[1], int( - temp[2]), int(temp[3]), temp[4], int(temp[5]), temp[4], IO.INPUT, Side[oppositeDic[temp[0]].upper()])) + ports.append( + Port( + Direction[temp[0]], + temp[1], + int(temp[2]), + int(temp[3]), + temp[4], + int(temp[5]), + temp[1], + IO.OUTPUT, + Side[temp[0]], + ) + ) + + ports.append( + Port( + Direction[temp[0]], + temp[1], + int(temp[2]), + int(temp[3]), + temp[4], + int(temp[5]), + temp[4], + IO.INPUT, + Side[oppositeDic[temp[0]].upper()], + ) + ) # wireCount = (abs(int(temp[2])) + # abs(int(temp[3])))*int(temp[5]) # for i in range(wireCount): - commonWirePair.append( - (f"{temp[1]}", f"{temp[4]}")) + commonWirePair.append((f"{temp[1]}", f"{temp[4]}")) elif temp[0] == "JUMP": - ports.append(Port(Direction.JUMP, temp[1], int( - temp[2]), int(temp[3]), temp[4], int(temp[5]), temp[1], IO.OUTPUT, Side.ANY)) - ports.append(Port(Direction.JUMP, temp[1], int( - temp[2]), int(temp[3]), temp[4], int(temp[5]), temp[4], IO.INPUT, Side.ANY)) + ports.append( + Port( + Direction.JUMP, + temp[1], + int(temp[2]), + int(temp[3]), + temp[4], + int(temp[5]), + temp[1], + IO.OUTPUT, + Side.ANY, + ) + ) + ports.append( + Port( + Direction.JUMP, + temp[1], + int(temp[2]), + int(temp[3]), + temp[4], + int(temp[5]), + temp[4], + IO.INPUT, + Side.ANY, + ) + ) elif temp[0] == "BEL": belFilePath = os.path.join(filePath, temp[1]) if temp[1].endswith(".vhdl"): @@ -116,10 +158,22 @@ def parseFabricCSV(fileName: str) -> Fabric: result = parseFileVerilog(belFilePath, temp[2]) else: raise ValueError( - "Invalid file type, only .vhdl and .v are supported") + "Invalid file type, only .vhdl and .v are supported" + ) internal, external, config, shared, configBit, userClk, belMap = result - bels.append(Bel(belFilePath, temp[2], internal, - external, config, shared, configBit, belMap, userClk)) + bels.append( + Bel( + belFilePath, + temp[2], + internal, + external, + config, + shared, + configBit, + belMap, + userClk, + ) + ) withUserCLK |= userClk elif temp[0] == "MATRIX": matrixDir = os.path.join(filePath, temp[1]) @@ -128,12 +182,12 @@ def parseFabricCSV(fileName: str) -> Fabric: for _, v in parseList(matrixDir, "source").items(): muxSize = len(v) if muxSize >= 2: - configBit += muxSize.bit_length()-1 + configBit += muxSize.bit_length() - 1 elif temp[1].endswith("_matrix.csv"): for _, v in parseMatrix(matrixDir, tileName).items(): muxSize = len(v) if muxSize >= 2: - configBit += muxSize.bit_length()-1 + configBit += muxSize.bit_length() - 1 elif temp[1].endswith(".vhdl") or temp[1].endswith(".v"): with open(matrixDir, "r") as f: f = f.read() @@ -142,17 +196,15 @@ def parseFabricCSV(fileName: str) -> Fabric: else: configBit = 0 print( - f"Cannot find NumberOfConfigBits in {matrixDir} assume 0 config bits") + f"Cannot find NumberOfConfigBits in {matrixDir} assume 0 config bits" + ) else: - raise ValueError( - 'Unknown file extension for matrix') + raise ValueError("Unknown file extension for matrix") else: - raise ValueError( - f"Unknown tile description {temp[0]} in tile {t}") + raise ValueError(f"Unknown tile description {temp[0]} in tile {t}") - tileDefs.append(Tile(tileName, ports, bels, - matrixDir, withUserCLK, configBit)) + tileDefs.append(Tile(tileName, ports, bels, matrixDir, withUserCLK, configBit)) fabricTiles = [] tileDic = dict(zip(tileTypes, tileDefs)) @@ -178,8 +230,19 @@ def parseFabricCSV(fileName: str) -> Fabric: else: result = parseFileVerilog(belFilePath, line[2]) internal, external, config, shared, configBit, userClk, belMap = result - bels.append(Bel(belFilePath, line[2], internal, - external, config, shared, configBit, belMap, userClk)) + bels.append( + Bel( + belFilePath, + line[2], + internal, + external, + config, + shared, + configBit, + belMap, + userClk, + ) + ) withUserCLK |= userClk continue @@ -195,7 +258,8 @@ def parseFabricCSV(fileName: str) -> Fabric: row.append(None) else: raise ValueError( - f"The super tile {name} contains definitions that are not tiles or Null.") + f"The super tile {name} contains definitions that are not tiles or Null." + ) tileMap.append(row) superTileDic[name] = SuperTile(name, tiles, tileMap, bels, withUserCLK) @@ -220,13 +284,13 @@ def parseFabricCSV(fileName: str) -> Fabric: for i in list(tileDic.keys()): if i not in usedTile: - print( - f"Tile {i} is not used in the fabric. Removing from tile dictionary.") + print(f"Tile {i} is not used in the fabric. Removing from tile dictionary.") del tileDic[i] for i in list(superTileDic.keys()): if any(j.name not in usedTile for j in superTileDic[i].tiles): print( - f"Supertile {i} is not used in the fabric. Removing from tile dictionary.") + f"Supertile {i} is not used in the fabric. Removing from tile dictionary." + ) del superTileDic[i] # parse the parameters @@ -252,7 +316,8 @@ def parseFabricCSV(fileName: str) -> Fabric: configBitMode = ConfigBitMode.FLIPFLOP_CHAIN else: raise ValueError( - f"Invalid config bit mode {i[1]} in parameters. Valid options are frame_based and FlipFlopChain") + f"Invalid config bit mode {i[1]} in parameters. Valid options are frame_based and FlipFlopChain" + ) elif i[0].startswith("FrameBitsPerRow"): frameBitsPerRow = int(i[1]) elif i[0].startswith("MaxFramesPerCol"): @@ -268,7 +333,8 @@ def parseFabricCSV(fileName: str) -> Fabric: multiplexerStyle = MultiplexerStyle.GENERIC else: raise ValueError( - f"Invalid multiplexer style {i[1]} in parameters. Valid options are custom and generic") + f"Invalid multiplexer style {i[1]} in parameters. Valid options are custom and generic" + ) elif i[0].startswith("SuperTileEnable"): superTileEnable = i[1] == "TRUE" else: @@ -278,38 +344,47 @@ def parseFabricCSV(fileName: str) -> Fabric: width = len(fabricTiles[0]) commonWirePair = list(dict.fromkeys(commonWirePair)) - commonWirePair = [(i, j) for ( - i, j) in commonWirePair if "NULL" not in i and "NULL" not in j] - - return Fabric(tile=fabricTiles, - numberOfColumns=width, - numberOfRows=height, - configBitMode=configBitMode, - frameBitsPerRow=frameBitsPerRow, - maxFramesPerCol=maxFramesPerCol, - package=package, - generateDelayInSwitchMatrix=generateDelayInSwitchMatrix, - multiplexerStyle=multiplexerStyle, - numberOfBRAMs=int(height/2), - superTileEnable=superTileEnable, - tileDic=tileDic, - superTileDic=superTileDic, - commonWirePair=commonWirePair) + commonWirePair = [ + (i, j) for (i, j) in commonWirePair if "NULL" not in i and "NULL" not in j + ] + + return Fabric( + tile=fabricTiles, + numberOfColumns=width, + numberOfRows=height, + configBitMode=configBitMode, + frameBitsPerRow=frameBitsPerRow, + maxFramesPerCol=maxFramesPerCol, + package=package, + generateDelayInSwitchMatrix=generateDelayInSwitchMatrix, + multiplexerStyle=multiplexerStyle, + numberOfBRAMs=int(height / 2), + superTileEnable=superTileEnable, + tileDic=tileDic, + superTileDic=superTileDic, + commonWirePair=commonWirePair, + ) @overload -def parseList(fileName: str, collect: Literal["pair"] = "pair") -> List[Tuple[str, str]]: +def parseList( + fileName: str, collect: Literal["pair"] = "pair" +) -> List[Tuple[str, str]]: pass @overload -def parseList(fileName: str, collect: Literal["source", "sink"]) -> Dict[str, List[str]]: +def parseList( + fileName: str, collect: Literal["source", "sink"] +) -> Dict[str, List[str]]: pass -def parseList(fileName: str, collect: Literal["pair", "source", "sink"] = "pair") -> Union[List[Tuple[str, str]], Dict[str, List[str]]]: +def parseList( + fileName: str, collect: Literal["pair", "source", "sink"] = "pair" +) -> Union[List[Tuple[str, str]], Dict[str, List[str]]]: """ - parse a list file and expand the list file information into a list of tuples. + parse a list file and expand the list file information into a list of tuples. Args: fileName (str): "" @@ -327,7 +402,7 @@ def parseList(fileName: str, collect: Literal["pair", "source", "sink"] = "pair" raise ValueError(f"The file {fileName} does not exist.") resultList = [] - with open(fileName, 'r') as f: + with open(fileName, "r") as f: file = f.read() file = re.sub(r"#.*", "", file) file = file.split("\n") @@ -338,8 +413,7 @@ def parseList(fileName: str, collect: Literal["pair", "source", "sink"] = "pair" continue if len(line) != 2: print(line) - raise ValueError( - f"Invalid list formatting in file: {fileName} at line {i}") + raise ValueError(f"Invalid list formatting in file: {fileName} at line {i}") left, right = line[0], line[1] leftList = [] @@ -376,17 +450,18 @@ def _expandListPorts(port, PortList): if "[" in port: if "]" not in port: raise ValueError( - '\nError in function ExpandListPorts: cannot find closing ]\n') + "\nError in function ExpandListPorts: cannot find closing ]\n" + ) # port.find gives us the first occurrence index in a string left_index = port.find("[") right_index = port.find("]") before_left_index = port[0:left_index] # right_index is the position of the ']' so we need everything after that - after_right_index = port[(right_index+1):] + after_right_index = port[(right_index + 1) :] ExpandList = [] - ExpandList = re.split(r"\|", port[left_index+1:right_index]) + ExpandList = re.split(r"\|", port[left_index + 1 : right_index]) for entry in ExpandList: - ExpandListItem = (before_left_index+entry+after_right_index) + ExpandListItem = before_left_index + entry + after_right_index _expandListPorts(ExpandListItem, PortList) else: @@ -395,7 +470,15 @@ def _expandListPorts(port, PortList): return -def parseFileVHDL(filename: str, belPrefix: str = "") -> Tuple[List[Tuple[str, IO]], List[Tuple[str, IO]], List[Tuple[str, IO]], List[Tuple[str, IO]], int, bool, Dict[str, int]]: +def parseFileVHDL(filename: str, belPrefix: str = "") -> Tuple[ + List[Tuple[str, IO]], + List[Tuple[str, IO]], + List[Tuple[str, IO]], + List[Tuple[str, IO]], + int, + bool, + Dict[str, int], +]: """ Parse a VHDL bel file and return all the related information of the bel. The tuple returned for relating to ports will be a list of (belName, IO) pair. @@ -412,9 +495,9 @@ def parseFileVHDL(filename: str, belPrefix: str = "") -> Tuple[List[Tuple[str, I ValueError: Cannot find the port section in the file which defines the bel ports. Returns: - Tuple[List[Tuple[str, IO]], List[Tuple[str, IO]], List[Tuple[str, IO]], List[Tuple[str, IO]], int, bool, Dict[str, int]]: + Tuple[List[Tuple[str, IO]], List[Tuple[str, IO]], List[Tuple[str, IO]], List[Tuple[str, IO]], int, bool, Dict[str, int]]: Bel internal ports, bel external ports, bel config ports, bel shared ports, number of configuration bit in the bel, - whether the bel have UserCLK, and the bel config bit mapping. + whether the bel have UserCLK, and the bel config bit mapping. """ internal: List[Tuple[str, IO]] = [] external: List[Tuple[str, IO]] = [] @@ -446,15 +529,16 @@ def parseFileVHDL(filename: str, belPrefix: str = "") -> Tuple[List[Tuple[str, I if len(belMapDic) != noConfigBits: raise ValueError( - f"NoConfigBits does not match with the BEL map in file {filename}, length of BelMap is {len(belMapDic)}, but with {noConfigBits} config bits") + f"NoConfigBits does not match with the BEL map in file {filename}, length of BelMap is {len(belMapDic)}, but with {noConfigBits} config bits" + ) portSection = "" - if result := re.search(r"port.*?\((.*?)\);", file, - re.MULTILINE | re.DOTALL | re.IGNORECASE): + if result := re.search( + r"port.*?\((.*?)\);", file, re.MULTILINE | re.DOTALL | re.IGNORECASE + ): portSection = result.group(1) else: - raise ValueError( - f"Could not find port section in file {filename}") + raise ValueError(f"Could not find port section in file {filename}") preGlobal, postGlobal = portSection.split("-- GLOBAL") @@ -497,7 +581,8 @@ def parseFileVHDL(filename: str, belPrefix: str = "") -> Tuple[List[Tuple[str, I shared.append((result.group(1), IO.INOUT)) else: raise ValueError( - f"Invalid port type {result.group(2)} in file {filename}") + f"Invalid port type {result.group(2)} in file {filename}" + ) else: if result.group(2).lower() == "in": internal.append((portName, IO.INPUT)) @@ -507,7 +592,8 @@ def parseFileVHDL(filename: str, belPrefix: str = "") -> Tuple[List[Tuple[str, I internal.append((portName, IO.INOUT)) else: raise ValueError( - f"Invalid port type {result.group(2)} in file {filename}") + f"Invalid port type {result.group(2)} in file {filename}" + ) if "UserCLK" in portName: userClk = True @@ -517,7 +603,8 @@ def parseFileVHDL(filename: str, belPrefix: str = "") -> Tuple[List[Tuple[str, I isShared = False result = re.search( - r"NoConfigBits\s*:\s*integer\s*:=\s*(\w+)", file, re.IGNORECASE | re.DOTALL) + r"NoConfigBits\s*:\s*integer\s*:=\s*(\w+)", file, re.IGNORECASE | re.DOTALL + ) if result: try: noConfigBits = int(result.group(1)) @@ -533,9 +620,17 @@ def parseFileVHDL(filename: str, belPrefix: str = "") -> Tuple[List[Tuple[str, I return internal, external, config, shared, noConfigBits, userClk, belMapDic -def parseFileVerilog(filename: str, belPrefix: str = "") -> Tuple[List[Tuple[str, IO]], List[Tuple[str, IO]], List[Tuple[str, IO]], List[Tuple[str, IO]], int, bool, Dict[str, Dict]]: +def parseFileVerilog(filename: str, belPrefix: str = "") -> Tuple[ + List[Tuple[str, IO]], + List[Tuple[str, IO]], + List[Tuple[str, IO]], + List[Tuple[str, IO]], + int, + bool, + Dict[str, Dict], +]: """ - Parse a Verilog bel file and return all the related information of the bel. The tuple returned for relating to ports + Parse a Verilog bel file and return all the related information of the bel. The tuple returned for relating to ports will be a list of (belName, IO) pair. The function will also parse and record all the FABulous attribute which all starts with :: @@ -550,26 +645,26 @@ def parseFileVerilog(filename: str, belPrefix: str = "") -> Tuple[List[Tuple[str * **GLOBAL** * **CONFIG_PORT** - The **BelMap** attribute will specify the bel mapping for the bel. This attribute should be placed before the start of + The **BelMap** attribute will specify the bel mapping for the bel. This attribute should be placed before the start of the module The bel mapping is then used for generating the bitstream specification. Each of the entry in the attribute will have the following format:: = ```` is the name of the feature and ```` will be the bit position of the feature. ie. ``INIT=0`` will specify that the feature ``INIT`` is located at bit 0. - Since a single feature can be mapped to multiple bits, this is currently done by specifying multiple entries for the same feature. This will be changed in the future. + Since a single feature can be mapped to multiple bits, this is currently done by specifying multiple entries for the same feature. This will be changed in the future. The bit specification is done in the following way:: INIT_a_1=1, INIT_a_2=2, ... - The name of the feature will be converted to ``INIT_a[1]``, ``INIT_a[2]`` for the above example. This is necessary - because Verilog does not allow square brackets as part of the attribute name. + The name of the feature will be converted to ``INIT_a[1]``, ``INIT_a[2]`` for the above example. This is necessary + because Verilog does not allow square brackets as part of the attribute name. **EXTERNAL** attribute will notify FABulous to put the pin in the top module during the fabric generation. **SHARED_PORT** attribute will notify FABulous this the pin is shared between multiple bels. Attribute need to go with the **EXTERNAL** attribute. - **GLOBAL** attribute will notify FABulous to stop parsing any pin after this attribute. + **GLOBAL** attribute will notify FABulous to stop parsing any pin after this attribute. **CONFIG_PORT** attribute will notify FABulous the port is for configuration. @@ -598,9 +693,9 @@ def parseFileVerilog(filename: str, belPrefix: str = "") -> Tuple[List[Tuple[str ValueError: No permission to access the file Returns: - Tuple[List[Tuple[str, IO]], List[Tuple[str, IO]], List[Tuple[str, IO]], List[Tuple[str, IO]], int, bool, Dict[str, Dict]]: + Tuple[List[Tuple[str, IO]], List[Tuple[str, IO]], List[Tuple[str, IO]], List[Tuple[str, IO]], int, bool, Dict[str, Dict]]: Bel internal ports, bel external ports, bel config ports, bel shared ports, number of configuration bit in the bel, - whether the bel have UserCLK, and the bel config bit mapping. + whether the bel have UserCLK, and the bel config bit mapping. """ internal: List[Tuple[str, IO]] = [] external: List[Tuple[str, IO]] = [] @@ -633,7 +728,8 @@ def parseFileVerilog(filename: str, belPrefix: str = "") -> Tuple[List[Tuple[str if len(belMapDic) != noConfigBits: raise ValueError( - f"NoConfigBits does not match with the BEL map in file {filename}, length of BelMap is {len(belMapDic)}, but with {noConfigBits} config bits") + f"NoConfigBits does not match with the BEL map in file {filename}, length of BelMap is {len(belMapDic)}, but with {noConfigBits} config bits" + ) file = file.split("\n") @@ -675,13 +771,17 @@ def parseFileVerilog(filename: str, belPrefix: str = "") -> Tuple[List[Tuple[str return internal, external, config, shared, noConfigBits, userClk, belMapDic -def _belMapProcessing(file: str, filename: str, syntax: Literal["vhdl", "verilog"]) -> Dict: +def _belMapProcessing( + file: str, filename: str, syntax: Literal["vhdl", "verilog"] +) -> Dict: pre = "" if syntax == "vhdl": pre = "--.*?" belEnumsDic = {} - if belEnums := re.findall(pre+r"\(\*.*?FABulous,.*?BelEnum,(.*?)\*\)", file, re.DOTALL | re.MULTILINE): + if belEnums := re.findall( + pre + r"\(\*.*?FABulous,.*?BelEnum,(.*?)\*\)", file, re.DOTALL | re.MULTILINE + ): for enums in belEnums: enums = enums.replace("\n", "").replace(" ", "").replace("\t", "") enums = enums.split(",") @@ -691,8 +791,7 @@ def _belMapProcessing(file: str, filename: str, syntax: Literal["vhdl", "verilog start = int(enumParse.group(2)) end = int(enumParse.group(3)) else: - raise ValueError( - f"Invalid enum {enums[0]} in file {filename}") + raise ValueError(f"Invalid enum {enums[0]} in file {filename}") belEnumsDic[name] = {} for i in enums[1:]: key, value = i.split("=") @@ -706,7 +805,9 @@ def _belMapProcessing(file: str, filename: str, syntax: Literal["vhdl", "verilog belEnumsDic[name][key][j] = bitValue.pop(0) belMapDic = {} - if belMap := re.search(pre+r"\(\*.*FABulous,.*?BelMap,(.*?)\*\)", file, re.DOTALL | re.MULTILINE): + if belMap := re.search( + pre + r"\(\*.*FABulous,.*?BelMap,(.*?)\*\)", file, re.DOTALL | re.MULTILINE + ): belMap = belMap.group(1) belMap = belMap.replace("\n", "").replace(" ", "").replace("\t", "") belMap = belMap.split(",") @@ -718,7 +819,7 @@ def _belMapProcessing(file: str, filename: str, syntax: Literal["vhdl", "verilog if len(belNameTemp) > 1 and belNameTemp[1].isnumeric(): bel[0] = f"{belNameTemp[0]}[{belNameTemp[1]}]" belMapDic[bel[0]] = {} - if bel == ['']: + if bel == [""]: continue # process enum data type if bel[0] in list(belEnumsDic.keys()): @@ -729,21 +830,20 @@ def _belMapProcessing(file: str, filename: str, syntax: Literal["vhdl", "verilog start, end = int(start), int(end) if start > end: length = start - end + 1 - for i in range(2**length-1, -1, -1): + for i in range(2**length - 1, -1, -1): belMapDic[bel[0]][i] = {} bitMap = list(f"{i:0{length.bit_length()}b}") - for v in range(len(bitMap)-1, -1, -1): + for v in range(len(bitMap) - 1, -1, -1): belMapDic[bel[0]][i][v] = bitMap.pop(0) else: length = end - start + 1 for i in range(0, 2**length): belMapDic[bel[0]][i] = {} - bitMap = list( - f"{2**length-i-1:0{length.bit_length()}b}") - for v in range(len(bitMap)-1, -1, -1): + bitMap = list(f"{2**length-i-1:0{length.bit_length()}b}") + for v in range(len(bitMap) - 1, -1, -1): belMapDic[bel[0]][i][v] = bitMap.pop(0) else: - belMapDic[bel[0]][0] = {0: '1'} + belMapDic[bel[0]][0] = {0: "1"} return belMapDic @@ -763,7 +863,7 @@ def parseMatrix(fileName: str, tileName: str) -> Dict[str, List[str]]: """ connectionsDic = {} - with open(fileName, 'r') as f: + with open(fileName, "r") as f: file = f.read() file = re.sub(r"#.*", "", file) file = file.split("\n") @@ -773,7 +873,8 @@ def parseMatrix(fileName: str, tileName: str) -> Dict[str, List[str]]: print(file[0].split(",")) print(tileName) raise ValueError( - 'Tile name (top left element) in csv file does not match tile name in tile object') + "Tile name (top left element) in csv file does not match tile name in tile object" + ) destList = file[0].split(",")[1:] @@ -787,7 +888,9 @@ def parseMatrix(fileName: str, tileName: str) -> Dict[str, List[str]]: return connectionsDic -def parseConfigMem(fileName: str, maxFramePerCol: int, frameBitPerRow: int, globalConfigBits: int) -> List[ConfigMem]: +def parseConfigMem( + fileName: str, maxFramePerCol: int, frameBitPerRow: int, globalConfigBits: int +) -> List[ConfigMem]: """ Parse the config memory csv file into a list of ConfigMem objects @@ -815,37 +918,43 @@ def parseConfigMem(fileName: str, maxFramePerCol: int, frameBitPerRow: int, glob # remove the pretty print from used_bits_mask for i, _ in enumerate(mappingFile): mappingFile[i]["used_bits_mask"] = mappingFile[i]["used_bits_mask"].replace( - "_", "") + "_", "" + ) # we should have as many lines as we have frames (=framePerCol) if len(mappingFile) != maxFramePerCol: raise ValueError( - f"WARNING: the bitstream mapping file has only {len(mappingFile)} entries but MaxFramesPerCol is {maxFramePerCol}") + f"WARNING: the bitstream mapping file has only {len(mappingFile)} entries but MaxFramesPerCol is {maxFramePerCol}" + ) # we also check used_bits_mask (is a vector that is as long as a frame and contains a '1' for a bit used and a '0' if not used (padded) usedBitsCounter = 0 for entry in mappingFile: if entry["used_bits_mask"].count("1") > frameBitPerRow: raise ValueError( - f"bitstream mapping file {fileName} has to many 1-elements in bitmask for frame : {entry['frame_name']}") + f"bitstream mapping file {fileName} has to many 1-elements in bitmask for frame : {entry['frame_name']}" + ) if len(entry["used_bits_mask"]) != frameBitPerRow: raise ValueError( - f"bitstream mapping file {fileName} has has a too long or short bitmask for frame : {entry['frame_name']}") + f"bitstream mapping file {fileName} has has a too long or short bitmask for frame : {entry['frame_name']}" + ) usedBitsCounter += entry["used_bits_mask"].count("1") if usedBitsCounter != globalConfigBits: raise ValueError( - f"bitstream mapping file {fileName} has a bitmask miss match; bitmask has in total {usedBitsCounter} 1-values for {globalConfigBits} bits") + f"bitstream mapping file {fileName} has a bitmask miss match; bitmask has in total {usedBitsCounter} 1-values for {globalConfigBits} bits" + ) allConfigBitsOrder = [] configMemEntry = [] for entry in mappingFile: configBitsOrder = [] - entry["ConfigBits_ranges"] = entry["ConfigBits_ranges"].replace( - " ", "").replace("\t", "") + entry["ConfigBits_ranges"] = ( + entry["ConfigBits_ranges"].replace(" ", "").replace("\t", "") + ) if ":" in entry["ConfigBits_ranges"]: - left, right = re.split(':', entry["ConfigBits_ranges"]) + left, right = re.split(":", entry["ConfigBits_ranges"]) # check the order of the number, if right is smaller than left, then we swap them left, right = int(left), int(right) if right < left: @@ -857,14 +966,16 @@ def parseConfigMem(fileName: str, maxFramePerCol: int, frameBitPerRow: int, glob for i in numList: if i in allConfigBitsOrder: raise ValueError( - f"Configuration bit index {i} already allocated in {fileName}, {entry['frame_name']}") + f"Configuration bit index {i} already allocated in {fileName}, {entry['frame_name']}" + ) configBitsOrder.append(i) elif ";" in entry["ConfigBits_ranges"]: for item in entry["ConfigBits_ranges"].split(";"): if int(item) in allConfigBitsOrder: raise ValueError( - f"Configuration bit index {item} already allocated in {fileName}, {entry['frame_name']}") + f"Configuration bit index {item} already allocated in {fileName}, {entry['frame_name']}" + ) configBitsOrder.append(int(item)) elif "NULL" in entry["ConfigBits_ranges"]: @@ -872,23 +983,26 @@ def parseConfigMem(fileName: str, maxFramePerCol: int, frameBitPerRow: int, glob else: raise ValueError( - f"Range {entry['ConfigBits_ranges']} is not a valid format. It should be in the form [int]:[int] or [int]. If there are multiple ranges it should be separated by ';'") + f"Range {entry['ConfigBits_ranges']} is not a valid format. It should be in the form [int]:[int] or [int]. If there are multiple ranges it should be separated by ';'" + ) allConfigBitsOrder += configBitsOrder if entry["used_bits_mask"].count("1") > 0: - configMemEntry.append(ConfigMem(frameName=entry["frame_name"], - frameIndex=int( - entry["frame_index"]), - bitsUsedInFrame=entry["used_bits_mask"].count( - "1"), - usedBitMask=entry["used_bits_mask"], - configBitRanges=configBitsOrder)) + configMemEntry.append( + ConfigMem( + frameName=entry["frame_name"], + frameIndex=int(entry["frame_index"]), + bitsUsedInFrame=entry["used_bits_mask"].count("1"), + usedBitMask=entry["used_bits_mask"], + configBitRanges=configBitsOrder, + ) + ) return configMemEntry -if __name__ == '__main__': +if __name__ == "__main__": # result = parseFabricCSV('fabric.csv') # result1 = parseList('RegFile_switch_matrix.list', collect="source") # result = parseFileVerilog('./LUT4c_frame_config_dffesr.v') diff --git a/fabric_generator/model_generation_npnr.py b/fabric_generator/model_generation_npnr.py index 316174e6..61102d79 100644 --- a/fabric_generator/model_generation_npnr.py +++ b/fabric_generator/model_generation_npnr.py @@ -10,9 +10,11 @@ def genNextpnrModel(fabric: Fabric): belStr = [] belv2Str = [] belStr.append( - f"# BEL descriptions: top left corner Tile_X0Y0, bottom right Tile_X{fabric.numberOfColumns}Y{fabric.numberOfRows}") + f"# BEL descriptions: top left corner Tile_X0Y0, bottom right Tile_X{fabric.numberOfColumns}Y{fabric.numberOfRows}" + ) belv2Str.append( - f"# BEL descriptions: top left corner Tile_X0Y0, bottom right Tile_X{fabric.numberOfColumns}Y{fabric.numberOfRows}") + f"# BEL descriptions: top left corner Tile_X0Y0, bottom right Tile_X{fabric.numberOfColumns}Y{fabric.numberOfRows}" + ) constrainStr = [] for y, row in enumerate(fabric.tile): @@ -25,65 +27,96 @@ def genNextpnrModel(fabric: Fabric): for source, sinkList in connection.items(): for sink in sinkList: pipStr.append( - f"X{x}Y{y},{sink},X{x}Y{y},{source},{8},{sink}.{source}") + f"X{x}Y{y},{sink},X{x}Y{y},{source},{8},{sink}.{source}" + ) elif tile.matrixDir.endswith(".list"): connection = parseList(tile.matrixDir) for sink, source in connection: pipStr.append( - f"X{x}Y{y},{source},X{x}Y{y},{sink},{8},{source}.{sink}") + f"X{x}Y{y},{source},X{x}Y{y},{sink},{8},{source}.{sink}" + ) else: raise ValueError( - f"For model generation {tile.matrixDir} need to a csv or list file") + f"For model generation {tile.matrixDir} need to a csv or list file" + ) pipStr.append(f"#Tile-external pips on tile X{x}Y{y}:") for wire in tile.wireList: pipStr.append( - f"X{x}Y{y},{wire.source},X{x+wire.xOffset}Y{y+wire.yOffset},{wire.destination},{8},{wire.source}.{wire.destination}") + f"X{x}Y{y},{wire.source},X{x+wire.xOffset}Y{y+wire.yOffset},{wire.destination},{8},{wire.source}.{wire.destination}" + ) # Old style bel definition belStr.append(f"#Tile_X{x}Y{y}") for i, bel in enumerate(tile.bels): belPort = bel.inputs + bel.outputs cType = bel.name - if bel.name == "LUT4c_frame_config" or bel.name == "LUT4c_frame_config_dffesr": + if ( + bel.name == "LUT4c_frame_config" + or bel.name == "LUT4c_frame_config_dffesr" + ): cType = "FABULOUS_LC" letter = string.ascii_uppercase[i] belStr.append( - f"X{x}Y{y},X{x},Y{y},{letter},{cType},{','.join(belPort)}") + f"X{x}Y{y},X{x},Y{y},{letter},{cType},{','.join(belPort)}" + ) - if bel.name in ["IO_1_bidirectional_frame_config_pass", "InPass4_frame_config", "OutPass4_frame_config", "InPass4_frame_config_mux", "OutPass4_frame_config_mux"]: + if bel.name in [ + "IO_1_bidirectional_frame_config_pass", + "InPass4_frame_config", + "OutPass4_frame_config", + "InPass4_frame_config_mux", + "OutPass4_frame_config_mux", + ]: constrainStr.append( - f"set_io Tile_X{x}Y{y}_{letter} Tile_X{x}Y{y}.{letter}") + f"set_io Tile_X{x}Y{y}_{letter} Tile_X{x}Y{y}.{letter}" + ) # New style bel definition belv2Str.append(f"#Tile_X{x}Y{y}") for i, bel in enumerate(tile.bels): cType = bel.name - if bel.name == "LUT4c_frame_config" or bel.name == "LUT4c_frame_config_dffesr": + if ( + bel.name == "LUT4c_frame_config" + or bel.name == "LUT4c_frame_config_dffesr" + ): cType = "FABULOUS_LC" letter = string.ascii_uppercase[i] belv2Str.append(f"BelBegin,X{x}Y{y},{letter},{cType},{bel.prefix}") + def strip_prefix(x): if x.startswith(bel.prefix): - return x[len(bel.prefix):] + return x[len(bel.prefix) :] else: return x + for inp in bel.inputs: - belv2Str.append(f"I,{strip_prefix(inp)},X{x}Y{y}.{inp}") # I,, + belv2Str.append( + f"I,{strip_prefix(inp)},X{x}Y{y}.{inp}" + ) # I,, for outp in bel.outputs: - belv2Str.append(f"O,{strip_prefix(outp)},X{x}Y{y}.{outp}") # O,, - for feat, cfg in sorted(bel.belFeatureMap.items(), key=lambda x:x[0]): + belv2Str.append( + f"O,{strip_prefix(outp)},X{x}Y{y}.{outp}" + ) # O,, + for feat, cfg in sorted(bel.belFeatureMap.items(), key=lambda x: x[0]): belv2Str.append(f"CFG,{feat}") if bel.withUserCLK: belv2Str.append(f"GlobalClk") belv2Str.append(f"BelEnd") - return "\n".join(pipStr), "\n".join(belStr), "\n".join(belv2Str), "\n".join(constrainStr) + return ( + "\n".join(pipStr), + "\n".join(belStr), + "\n".join(belv2Str), + "\n".join(constrainStr), + ) def genNextpnrPairModel(fabric: Fabric): pass -def genNextpnrModelOld(archObject: FabricModelGen, generatePairs=True) -> Tuple[str, str, str]: +def genNextpnrModelOld( + archObject: FabricModelGen, generatePairs=True +) -> Tuple[str, str, str]: pipsStr = "" belsStr = f"# BEL descriptions: bottom left corner Tile_X0Y0, top right {archObject.tiles[0][archObject.width - 1].genTileLoc()}\n" pairStr = "" @@ -96,8 +129,16 @@ def genNextpnrModelOld(archObject: FabricModelGen, generatePairs=True) -> Tuple[ pipsStr += f"#Tile-internal pips on tile {tileLoc}:\n" for pip in tile.pips: # Add the pips (also delay should be done here later, sDelay is a filler) - pipsStr += ",".join((tileLoc, pip[0], tileLoc, - pip[1], sDelay, ".".join((pip[0], pip[1])))) + pipsStr += ",".join( + ( + tileLoc, + pip[0], + tileLoc, + pip[1], + sDelay, + ".".join((pip[0], pip[1])), + ) + ) pipsStr += "\n" # Wires between tiles @@ -107,13 +148,35 @@ def genNextpnrModelOld(archObject: FabricModelGen, generatePairs=True) -> Tuple[ destx = tile.x + int(wire["xoffset"]) desttileLoc = f"X{destx}Y{desty}" for i in range(int(wire["wire-count"])): - pipsStr += ",".join((tileLoc, wire["source"]+str(i), desttileLoc, wire["destination"]+str( - i), sDelay, ".".join((wire["source"]+str(i), wire["destination"]+str(i))))) + pipsStr += ",".join( + ( + tileLoc, + wire["source"] + str(i), + desttileLoc, + wire["destination"] + str(i), + sDelay, + ".".join( + (wire["source"] + str(i), wire["destination"] + str(i)) + ), + ) + ) pipsStr += "\n" - for wire in tile.atomicWires: # Very simple - just add wires using values directly from the atomic wire structure + for ( + wire + ) in ( + tile.atomicWires + ): # Very simple - just add wires using values directly from the atomic wire structure desttileLoc = wire["destTile"] - pipsStr += ",".join((tileLoc, wire["source"], desttileLoc, wire["destination"], - sDelay, ".".join((wire["source"], wire["destination"])))) + pipsStr += ",".join( + ( + tileLoc, + wire["source"], + desttileLoc, + wire["destination"], + sDelay, + ".".join((wire["source"], wire["destination"])), + ) + ) pipsStr += "\n" # Add BELs belsStr += "#Tile_" + tileLoc + "\n" # Tile declaration as a comment @@ -136,10 +199,24 @@ def genNextpnrModelOld(archObject: FabricModelGen, generatePairs=True) -> Tuple[ # cType = "IOBUF" else: cType = bel - belsStr += ",".join((tileLoc, ",".join(tile.genTileLoc(True)), - let, cType, ",".join(nports))) + "\n" + belsStr += ( + ",".join( + ( + tileLoc, + ",".join(tile.genTileLoc(True)), + let, + cType, + ",".join(nports), + ) + ) + + "\n" + ) # Add constraints to fix pin location (based on template generated in genVerilogTemplate) - if bel in ["IO_1_bidirectional_frame_config_pass", "InPass4_frame_config", "OutPass4_frame_config"]: + if bel in [ + "IO_1_bidirectional_frame_config_pass", + "InPass4_frame_config", + "OutPass4_frame_config", + ]: belName = f"Tile_{tileLoc}_{let}" constraintStr += f"set_io {belName} {tileLoc}.{let}\n" @@ -153,61 +230,115 @@ def genNextpnrModelOld(archObject: FabricModelGen, generatePairs=True) -> Tuple[ destx = tile.x + int(wire["xoffset"]) destTile = archObject.getTileByCoords(destx, desty) desttileLoc = f"X{destx}Y{desty}" - if (wire["destination"] + str(i)) not in destTile.pipMuxes_MapSourceToSinks.keys(): + if ( + wire["destination"] + str(i) + ) not in destTile.pipMuxes_MapSourceToSinks.keys(): continue - for pipSink in destTile.pipMuxes_MapSourceToSinks[wire["destination"] + str(i)]: + for pipSink in destTile.pipMuxes_MapSourceToSinks[ + wire["destination"] + str(i) + ]: # If there is a multiplexer here, then we can simply add this pair if len(destTile.pipMuxes_MapSinkToSources[pipSink]) > 1: - pairStr += ",".join((".".join((tileLoc, wire["source"] + f"[{str(i)}]")), ".".join( - (desttileLoc, addBrackets(pipSink, tile))))) + "\n" # TODO: add square brackets to end + pairStr += ( + ",".join( + ( + ".".join( + ( + tileLoc, + wire["source"] + f"[{str(i)}]", + ) + ), + ".".join( + ( + desttileLoc, + addBrackets(pipSink, tile), + ) + ), + ) + ) + + "\n" + ) # TODO: add square brackets to end # otherwise, there is no physical pair in the ASIC netlist, so we must propagate back until we hit a multiplexer else: finalDestination = ".".join( - (desttileLoc, addBrackets(pipSink, tile))) + (desttileLoc, addBrackets(pipSink, tile)) + ) foundPhysicalPairs = False curWireTuple = (tile, wire, i) potentialStarts = [] stopOffs = [] - while (not foundPhysicalPairs): + while not foundPhysicalPairs: cTile = curWireTuple[0] cWire = curWireTuple[1] cIndex = curWireTuple[2] - if len(cTile.pipMuxes_MapSinkToSources[cWire["source"] + str(cIndex)]) > 1: - for wireEnd in cTile.pipMuxes_MapSinkToSources[cWire["source"] + str(cIndex)]: + if ( + len( + cTile.pipMuxes_MapSinkToSources[ + cWire["source"] + str(cIndex) + ] + ) + > 1 + ): + for wireEnd in cTile.pipMuxes_MapSinkToSources[ + cWire["source"] + str(cIndex) + ]: if wireEnd in cTile.belPorts: continue cPair = archObject.getTileAndWireByWireDest( - cTile.genTileLoc(), wireEnd) + cTile.genTileLoc(), wireEnd + ) if cPair == None: continue - potentialStarts.append(cPair[0].genTileLoc( - ) + "." + cPair[1]["source"] + "[" + str(cPair[2]) + "]") + potentialStarts.append( + cPair[0].genTileLoc() + + "." + + cPair[1]["source"] + + "[" + + str(cPair[2]) + + "]" + ) foundPhysicalPairs = True else: - destPort = cTile.pipMuxes_MapSinkToSources[cWire["source"] + str( - cIndex)][0] + destPort = cTile.pipMuxes_MapSinkToSources[ + cWire["source"] + str(cIndex) + ][0] destLoc = cTile.genTileLoc() if destPort in cTile.belPorts: foundPhysicalPairs = True # This means it's connected to a BEL continue - if GNDRE.match(destPort) or VCCRE.match(destPort) or VDDRE.match(destPort): + if ( + GNDRE.match(destPort) + or VCCRE.match(destPort) + or VDDRE.match(destPort) + ): foundPhysicalPairs = True continue - stopOffs.append( - destLoc + "." + destPort) - curWireTuple = archObject.getTileAndWireByWireDest( - destLoc, destPort) - pairStr += "#Propagated route for " + finalDestination + "\n" + stopOffs.append(destLoc + "." + destPort) + curWireTuple = ( + archObject.getTileAndWireByWireDest( + destLoc, destPort + ) + ) + pairStr += ( + "#Propagated route for " + finalDestination + "\n" + ) for index, start in enumerate(potentialStarts): pairStr += start + "," + finalDestination + "\n" - pairStr += "#Stopoffs: " + \ - ",".join(stopOffs) + "\n" + pairStr += "#Stopoffs: " + ",".join(stopOffs) + "\n" # Generate pairs for bels: pairStr += "#Atomic wire pairs\n" for wire in tile.atomicWires: - pairStr += wire["sourceTile"] + "." + addBrackets( - wire["source"], tile) + "," + wire["destTile"] + "." + addBrackets(wire["destination"], tile) + "\n" + pairStr += ( + wire["sourceTile"] + + "." + + addBrackets(wire["source"], tile) + + "," + + wire["destTile"] + + "." + + addBrackets(wire["destination"], tile) + + "\n" + ) for num, belpair in enumerate(tile.bels): pairStr += "#Bel pairs" + "\n" bel = belpair[0] @@ -216,135 +347,393 @@ def genNextpnrModelOld(archObject: FabricModelGen, generatePairs=True) -> Tuple[ nports = belpair[2] if bel == "LUT4c_frame_config": for i in range(4): - pairStr += tileLoc + "." + prefix + \ - f"D[{i}]," + tileLoc + "." + \ - addBrackets(outPip, tile) + "\n" + pairStr += ( + tileLoc + + "." + + prefix + + f"D[{i}]," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) for outPip in tile.pipMuxes_MapSourceToSinks[prefix + "O"]: for i in range(4): - pairStr += tileLoc + "." + prefix + \ - f"I[{i}]," + tileLoc + "." + \ - addBrackets(outPip, tile) + "\n" - pairStr += tileLoc + "." + prefix + \ - f"Q[{i}]," + tileLoc + "." + \ - addBrackets(outPip, tile) + "\n" + pairStr += ( + tileLoc + + "." + + prefix + + f"I[{i}]," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) + pairStr += ( + tileLoc + + "." + + prefix + + f"Q[{i}]," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) elif bel == "MUX8LUT_frame_config": for outPip in tile.pipMuxes_MapSourceToSinks["M_AB"]: - pairStr += tileLoc + ".A," + tileLoc + \ - "." + addBrackets(outPip, tile) + "\n" - pairStr += tileLoc + ".B," + tileLoc + \ - "." + addBrackets(outPip, tile) + "\n" - pairStr += tileLoc + ".S0," + tileLoc + \ - "." + addBrackets(outPip, tile) + "\n" + pairStr += ( + tileLoc + + ".A," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) + pairStr += ( + tileLoc + + ".B," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) + pairStr += ( + tileLoc + + ".S0," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) for outPip in tile.pipMuxes_MapSourceToSinks["M_AD"]: - pairStr += tileLoc + ".A," + tileLoc + \ - "." + addBrackets(outPip, tile) + "\n" - pairStr += tileLoc + ".B," + tileLoc + \ - "." + addBrackets(outPip, tile) + "\n" - pairStr += tileLoc + ".C," + tileLoc + \ - "." + addBrackets(outPip, tile) + "\n" - pairStr += tileLoc + ".D," + tileLoc + \ - "." + addBrackets(outPip, tile) + "\n" - pairStr += tileLoc + ".S0," + tileLoc + \ - "." + addBrackets(outPip, tile) + "\n" - pairStr += tileLoc + ".S1," + tileLoc + \ - "." + addBrackets(outPip, tile) + "\n" + pairStr += ( + tileLoc + + ".A," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) + pairStr += ( + tileLoc + + ".B," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) + pairStr += ( + tileLoc + + ".C," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) + pairStr += ( + tileLoc + + ".D," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) + pairStr += ( + tileLoc + + ".S0," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) + pairStr += ( + tileLoc + + ".S1," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) for outPip in tile.pipMuxes_MapSourceToSinks["M_AH"]: - pairStr += tileLoc + ".A," + tileLoc + \ - "." + addBrackets(outPip, tile) + "\n" - pairStr += tileLoc + ".B," + tileLoc + \ - "." + addBrackets(outPip, tile) + "\n" - pairStr += tileLoc + ".C," + tileLoc + \ - "." + addBrackets(outPip, tile) + "\n" - pairStr += tileLoc + ".D," + tileLoc + \ - "." + addBrackets(outPip, tile) + "\n" - pairStr += tileLoc + ".E," + tileLoc + \ - "." + addBrackets(outPip, tile) + "\n" - pairStr += tileLoc + ".F," + tileLoc + \ - "." + addBrackets(outPip, tile) + "\n" - pairStr += tileLoc + ".G," + tileLoc + \ - "." + addBrackets(outPip, tile) + "\n" - pairStr += tileLoc + ".H," + tileLoc + \ - "." + addBrackets(outPip, tile) + "\n" - pairStr += tileLoc + ".S0," + tileLoc + \ - "." + addBrackets(outPip, tile) + "\n" - pairStr += tileLoc + ".S1," + tileLoc + \ - "." + addBrackets(outPip, tile) + "\n" - pairStr += tileLoc + ".S2," + tileLoc + \ - "." + addBrackets(outPip, tile) + "\n" - pairStr += tileLoc + ".S3," + tileLoc + \ - "." + addBrackets(outPip, tile) + "\n" + pairStr += ( + tileLoc + + ".A," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) + pairStr += ( + tileLoc + + ".B," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) + pairStr += ( + tileLoc + + ".C," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) + pairStr += ( + tileLoc + + ".D," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) + pairStr += ( + tileLoc + + ".E," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) + pairStr += ( + tileLoc + + ".F," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) + pairStr += ( + tileLoc + + ".G," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) + pairStr += ( + tileLoc + + ".H," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) + pairStr += ( + tileLoc + + ".S0," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) + pairStr += ( + tileLoc + + ".S1," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) + pairStr += ( + tileLoc + + ".S2," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) + pairStr += ( + tileLoc + + ".S3," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) for outPip in tile.pipMuxes_MapSourceToSinks["M_EF"]: - pairStr += tileLoc + ".E," + tileLoc + \ - "." + addBrackets(outPip, tile) + "\n" - pairStr += tileLoc + ".F," + tileLoc + \ - "." + addBrackets(outPip, tile) + "\n" - pairStr += tileLoc + ".S0," + tileLoc + \ - "." + addBrackets(outPip, tile) + "\n" - pairStr += tileLoc + ".S2," + tileLoc + \ - "." + addBrackets(outPip, tile) + "\n" + pairStr += ( + tileLoc + + ".E," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) + pairStr += ( + tileLoc + + ".F," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) + pairStr += ( + tileLoc + + ".S0," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) + pairStr += ( + tileLoc + + ".S2," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) elif bel == "MULADD": for i in range(20): for outPip in tile.pipMuxes_MapSourceToSinks[f"Q{i}"]: for i in range(8): - pairStr += tileLoc + \ - f".A[{i}]," + tileLoc + "." + \ - addBrackets(outPip, tile) + "\n" + pairStr += ( + tileLoc + + f".A[{i}]," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) for i in range(8): - pairStr += tileLoc + \ - f".B[{i}]," + tileLoc + "." + \ - addBrackets(outPip, tile) + "\n" + pairStr += ( + tileLoc + + f".B[{i}]," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) for i in range(20): - pairStr += tileLoc + \ - f".C[{i}]," + tileLoc + "." + \ - addBrackets(outPip, tile) + "\n" + pairStr += ( + tileLoc + + f".C[{i}]," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) elif bel == "RegFile_32x4": for i in range(4): for outPip in tile.pipMuxes_MapSourceToSinks[f"AD{i}"]: - pairStr += tileLoc + ".W_en," + tileLoc + \ - "." + addBrackets(outPip, tile) + "\n" + pairStr += ( + tileLoc + + ".W_en," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) for j in range(4): - pairStr += tileLoc + \ - f".D[{j}]," + tileLoc + "." + \ - addBrackets(outPip, tile) + "\n" - pairStr += tileLoc + \ - f".W_ADR[{j}]," + tileLoc + "." + \ - addBrackets(outPip, tile) + "\n" - pairStr += tileLoc + \ - f".A_ADR[{j}]," + tileLoc + "." + \ - addBrackets(outPip, tile) + "\n" + pairStr += ( + tileLoc + + f".D[{j}]," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) + pairStr += ( + tileLoc + + f".W_ADR[{j}]," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) + pairStr += ( + tileLoc + + f".A_ADR[{j}]," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) for outPip in tile.pipMuxes_MapSourceToSinks[f"BD{i}"]: - pairStr += tileLoc + ".W_en," + tileLoc + \ - "." + addBrackets(outPip, tile) + "\n" + pairStr += ( + tileLoc + + ".W_en," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) for j in range(4): - pairStr += tileLoc + \ - f".D[{j}]," + tileLoc + "." + \ - addBrackets(outPip, tile) + "\n" - pairStr += tileLoc + \ - f".W_ADR[{j}]," + tileLoc + "." + \ - addBrackets(outPip, tile) + "\n" - pairStr += tileLoc + \ - f".B_ADR[{j}]," + tileLoc + "." + \ - addBrackets(outPip, tile) + "\n" + pairStr += ( + tileLoc + + f".D[{j}]," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) + pairStr += ( + tileLoc + + f".W_ADR[{j}]," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) + pairStr += ( + tileLoc + + f".B_ADR[{j}]," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) elif bel == "IO_1_bidirectional_frame_config_pass": # inPorts go into the fabric, outPorts go out for inPort in ("O", "Q"): - for outPip in tile.pipMuxes_MapSourceToSinks[prefix + inPort]: - pairStr += tileLoc + "." + prefix + inPort + "," + \ - tileLoc + "." + \ - addBrackets(outPip, tile) + "\n" + for outPip in tile.pipMuxes_MapSourceToSinks[ + prefix + inPort + ]: + pairStr += ( + tileLoc + + "." + + prefix + + inPort + + "," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) # Outputs are covered by the wire code, as pips will link to them elif bel == "InPass4_frame_config": for i in range(4): - for outPip in tile.pipMuxes_MapSourceToSinks[prefix + "O" + str(i)]: - pairStr += tileLoc + "." + prefix + \ - f"O{i}" + "," + tileLoc + "." + \ - addBrackets(outPip, tile) + "\n" + for outPip in tile.pipMuxes_MapSourceToSinks[ + prefix + "O" + str(i) + ]: + pairStr += ( + tileLoc + + "." + + prefix + + f"O{i}" + + "," + + tileLoc + + "." + + addBrackets(outPip, tile) + + "\n" + ) elif bel == "OutPass4_frame_config": for i in range(4): - for inPip in tile.pipMuxes_MapSinkToSources[prefix + "I" + str(i)]: - pairStr += tileLoc + "." + \ - addBrackets(inPip, tile) + "," + \ - tileLoc + "." + prefix + f"I{i}" + "\n" + for inPip in tile.pipMuxes_MapSinkToSources[ + prefix + "I" + str(i) + ]: + pairStr += ( + tileLoc + + "." + + addBrackets(inPip, tile) + + "," + + tileLoc + + "." + + prefix + + f"I{i}" + + "\n" + ) if generatePairs: return (pipsStr, belsStr, constraintStr, pairStr) else: diff --git a/fabric_generator/model_generation_vpr.py b/fabric_generator/model_generation_vpr.py index 040d7d09..ea24e6f9 100644 --- a/fabric_generator/model_generation_vpr.py +++ b/fabric_generator/model_generation_vpr.py @@ -9,21 +9,21 @@ from xml.dom import minidom from fabric_generator.file_parser import parseMatrix, parseList import logging + logger = logging.getLogger(__name__) def genVPRModel(fabric: Fabric, customXMLfile: str = "") -> str: if customXMLfile != "": - with open(customXMLfile, 'r') as f: + with open(customXMLfile, "r") as f: customXML = ET.fromstring(f.read()) else: customXML = ET.Element("EMPTY") allBelVariant: List[Bel] = [] belName: set[str] = set() - allCustomXMLBelName = [i.get("name") - for i in customXML.findall("bel_info")] + allCustomXMLBelName = [i.get("name") for i in customXML.findall("bel_info")] for name, tile in fabric.tileDic.items(): for bel in tile.bels: if bel.name not in belName: @@ -34,29 +34,43 @@ def genVPRModel(fabric: Fabric, customXMLfile: str = "") -> str: # device block device = ET.SubElement(root, "device") - ET.SubElement(device, "sizing", R_minW_nmos="6065.520020", - R_minW_pmos="18138.500000") + ET.SubElement( + device, "sizing", R_minW_nmos="6065.520020", R_minW_pmos="18138.500000" + ) ET.SubElement(device, "area", grid_logic_tile_area="14813.392") chanWidthDistr = ET.SubElement(device, "chan_width_distr") ET.SubElement(chanWidthDistr, "x", distr="uniform", peak="1.000000") ET.SubElement(chanWidthDistr, "y", distr="uniform", peak="1.000000") ET.SubElement(device, "switch_block", type="universal", fs="3") ET.SubElement(device, "connection_block", input_switch_name="ipin_cblock") - ET.SubElement(device, "default_fc", in_type="frac", - in_val="1", out_type="frac", out_val="1") + ET.SubElement( + device, "default_fc", in_type="frac", in_val="1", out_type="frac", out_val="1" + ) # layout block layout = ET.SubElement(root, "layout") - fixedLayout = ET.SubElement(layout, "fixed_layout", name="FABulous", - width=str(fabric.numberOfColumns-2), height=str(fabric.numberOfRows-2)) - ET.SubElement(fixedLayout, "single", type="clock_primitive", - priority="1", x="1", y="1") + fixedLayout = ET.SubElement( + layout, + "fixed_layout", + name="FABulous", + width=str(fabric.numberOfColumns - 2), + height=str(fabric.numberOfRows - 2), + ) + ET.SubElement( + fixedLayout, "single", type="clock_primitive", priority="1", x="1", y="1" + ) for y, row in enumerate(fabric.tile): for x, tile in enumerate(row): if tile == None: continue - single = ET.SubElement(fixedLayout, "single", - type=tile.name, priority="1", x=str(x+1), y=str(y+1)) + single = ET.SubElement( + fixedLayout, + "single", + type=tile.name, + priority="1", + x=str(x + 1), + y=str(y + 1), + ) metaData = ET.SubElement(single, "metadata") prefixList = [] if tile.bels == []: @@ -70,16 +84,18 @@ def genVPRModel(fabric: Fabric, customXMLfile: str = "") -> str: prefixList.append(f"X{x}Y{y}.hanging") break - ET.SubElement(metaData, "meta", - name="fasm_prefix").text = " ".join(prefixList) + ET.SubElement(metaData, "meta", name="fasm_prefix").text = " ".join( + prefixList + ) # Tiles block tiles = ET.SubElement(root, "tiles") tile = ET.SubElement(tiles, "tile", name="clock_primitive") subTile = ET.SubElement(tile, "sub_tile", name="clock_sub") equivalentSite = ET.SubElement(subTile, "equivalent_sites") - ET.SubElement(equivalentSite, "site", - pb_type="clock_primitive", pin_mapping="direct") + ET.SubElement( + equivalentSite, "site", pb_type="clock_primitive", pin_mapping="direct" + ) ET.SubElement(subTile, "output", name="clock_out", num_pins="1") for name, tile in fabric.tileDic.items(): @@ -87,39 +103,55 @@ def genVPRModel(fabric: Fabric, customXMLfile: str = "") -> str: # tile without bel if tile.bels == []: - subTile = ET.SubElement( - t, "sub_tile", name=f"{name}_dummy", capacity="1") + subTile = ET.SubElement(t, "sub_tile", name=f"{name}_dummy", capacity="1") equivalentSite = ET.SubElement(subTile, "equivalent_sites") - ET.SubElement(equivalentSite, "site", - pb_type="reserved_dummy", pin_mapping="direct") + ET.SubElement( + equivalentSite, "site", pb_type="reserved_dummy", pin_mapping="direct" + ) ET.SubElement(subTile, "input", name="UserCLK", num_pins="1") # tile with bel for bel in tile.bels: if bel.inputs + bel.outputs == []: subTile = ET.SubElement( - t, "sub_tile", name=f"{bel.prefix}{bel.name}_dummy", capacity="1") + t, "sub_tile", name=f"{bel.prefix}{bel.name}_dummy", capacity="1" + ) equivalentSite = ET.SubElement(subTile, "equivalent_sites") - ET.SubElement(equivalentSite, "site", - pb_type="reserved_dummy", pin_mapping="direct") + ET.SubElement( + equivalentSite, + "site", + pb_type="reserved_dummy", + pin_mapping="direct", + ) ET.SubElement(subTile, "input", name="UserCLK", num_pins="1") else: subTile = ET.SubElement( - t, "sub_tile", name=f"{bel.prefix}{bel.name}", capacity="1") + t, "sub_tile", name=f"{bel.prefix}{bel.name}", capacity="1" + ) equivalentSite = ET.SubElement(subTile, "equivalent_sites") - site = ET.SubElement(equivalentSite, "site", - pb_type=f"{bel.name}_wrapper", pin_mapping="custom") + site = ET.SubElement( + equivalentSite, + "site", + pb_type=f"{bel.name}_wrapper", + pin_mapping="custom", + ) for port in bel.inputs: - ET.SubElement(site, "direct").attrib = {"from": f"{bel.prefix}{bel.name}.{port}", - "to": f"{bel.name}_wrapper.{port.removeprefix(bel.prefix)}"} + ET.SubElement(site, "direct").attrib = { + "from": f"{bel.prefix}{bel.name}.{port}", + "to": f"{bel.name}_wrapper.{port.removeprefix(bel.prefix)}", + } for port in bel.outputs: - ET.SubElement(site, "direct").attrib = {"to": f"{bel.name}_wrapper.{port.removeprefix(bel.prefix)}", - "from": f"{bel.prefix}{bel.name}.{port}"} + ET.SubElement(site, "direct").attrib = { + "to": f"{bel.name}_wrapper.{port.removeprefix(bel.prefix)}", + "from": f"{bel.prefix}{bel.name}.{port}", + } if bel.withUserCLK: - ET.SubElement(site, "direct").attrib = {"from": f"{bel.prefix}{bel.name}.UserCLK", - "to": f"{bel.name}_wrapper.UserCLK"} + ET.SubElement(site, "direct").attrib = { + "from": f"{bel.prefix}{bel.name}.UserCLK", + "to": f"{bel.name}_wrapper.UserCLK", + } ET.SubElement(subTile, "input", name="UserCLK", num_pins="1") for i in bel.inputs: @@ -127,12 +159,16 @@ def genVPRModel(fabric: Fabric, customXMLfile: str = "") -> str: for i in bel.outputs: ET.SubElement(subTile, "output", name=i, num_pins="1") - if hangingPort := [i.destinationName for i in tile.portsInfo if "GND" in i.destinationName or "VCC" in i.destinationName]: - subTile = ET.SubElement( - t, "sub_tile", name=f"{name}_hanging", capacity="1") + if hangingPort := [ + i.destinationName + for i in tile.portsInfo + if "GND" in i.destinationName or "VCC" in i.destinationName + ]: + subTile = ET.SubElement(t, "sub_tile", name=f"{name}_hanging", capacity="1") equivalentSite = ET.SubElement(subTile, "equivalent_sites") - ET.SubElement(equivalentSite, "site", - pb_type=f"{name}_hanging", pin_mapping="direct") + ET.SubElement( + equivalentSite, "site", pb_type=f"{name}_hanging", pin_mapping="direct" + ) for i in set(hangingPort): ET.SubElement(subTile, "output", name=i, num_pins="1") @@ -156,27 +192,40 @@ def genVPRModel(fabric: Fabric, customXMLfile: str = "") -> str: model = ET.SubElement(models, "model", name=bel.name) inputPorts = ET.SubElement(model, "input_ports") for input in inputStriped: - ET.SubElement(inputPorts, "port", - name=input, combinational_sink_ports=" ".join(outputStriped)) + ET.SubElement( + inputPorts, + "port", + name=input, + combinational_sink_ports=" ".join(outputStriped), + ) ET.SubElement(inputPorts, "port", name="UserCLK") outputPorts = ET.SubElement(model, "output_ports") for output in outputStriped: - ET.SubElement(outputPorts, "port", name=output) + ET.SubElement(outputPorts, "port", name=output) # complex block list block complexBlockList = ET.SubElement(root, "complexblocklist") # clock primitive - pbTypeClockPrim = ET.SubElement( - complexBlockList, "pb_type", name="clock_primitive") - pbTypeClock_input = ET.SubElement(pbTypeClockPrim, "pb_type", name="clock_input", - blif_model=".subckt Global_Clock", num_pb="1") + pbTypeClockPrim = ET.SubElement(complexBlockList, "pb_type", name="clock_primitive") + pbTypeClock_input = ET.SubElement( + pbTypeClockPrim, + "pb_type", + name="clock_input", + blif_model=".subckt Global_Clock", + num_pb="1", + ) ET.SubElement(pbTypeClock_input, "output", name="CLK", num_pins="1") ET.SubElement(pbTypeClockPrim, "output", name="clock_out", num_pins="1") interConnect = ET.SubElement(pbTypeClockPrim, "interconnect") - ET.SubElement(interConnect, "direct", name="clock_prim_to_top", - input="clock_input.CLK", output="clock_primitive.clock_out") + ET.SubElement( + interConnect, + "direct", + name="clock_prim_to_top", + input="clock_input.CLK", + output="clock_primitive.clock_out", + ) # reserved dummy pbTypeDummy = ET.SubElement(complexBlockList, "pb_type", name="reserved_dummy") @@ -184,9 +233,15 @@ def genVPRModel(fabric: Fabric, customXMLfile: str = "") -> str: ET.SubElement(pbTypeDummy, "interconnect") for name, tile in fabric.tileDic.items(): - hangingPort = [i.destinationName for i in tile.portsInfo if "GND" in i.destinationName or "VCC" in i.destinationName] + hangingPort = [ + i.destinationName + for i in tile.portsInfo + if "GND" in i.destinationName or "VCC" in i.destinationName + ] if hangingPort: - pbTypeHang = ET.SubElement(complexBlockList, "pb_type", name=f"{tile.name}_hanging") + pbTypeHang = ET.SubElement( + complexBlockList, "pb_type", name=f"{tile.name}_hanging" + ) ET.SubElement(pbTypeHang, "interconnect") for p in list(set(hangingPort)): ET.SubElement(pbTypeHang, "output", name=p, num_pins="1") @@ -196,59 +251,113 @@ def genVPRModel(fabric: Fabric, customXMLfile: str = "") -> str: if bel.inputs + bel.outputs == []: continue pbTypeWrapper = ET.SubElement( - complexBlockList, "pb_type", name=f"{bel.name}_wrapper") - + complexBlockList, "pb_type", name=f"{bel.name}_wrapper" + ) + if bel.name in allCustomXMLBelName: - element = customXML.find(f".//bel_info[@name='{bel.name}']//pb_type") + element = customXML.find(f".//bel_info[@name='{bel.name}']//pb_type") pbTypeWrapper.append(element) else: pbType = ET.SubElement( - pbTypeWrapper, "pb_type", name=f"{bel.name}", num_pb="1", blif_model=f".subckt {bel.name}") + pbTypeWrapper, + "pb_type", + name=f"{bel.name}", + num_pb="1", + blif_model=f".subckt {bel.name}", + ) for i in bel.inputs: for j in bel.outputs: - ET.SubElement(pbType, "delay_constant", max="300e-12", - in_port=f"{bel.name}.{i.removeprefix(bel.prefix)}", out_port=f"{bel.name}.{j.removeprefix(bel.prefix)}") + ET.SubElement( + pbType, + "delay_constant", + max="300e-12", + in_port=f"{bel.name}.{i.removeprefix(bel.prefix)}", + out_port=f"{bel.name}.{j.removeprefix(bel.prefix)}", + ) interConnect = ET.SubElement(pbTypeWrapper, "interconnect") if "UserCLK" in [i[0] for i in bel.sharedPort]: - ET.SubElement(pbTypeWrapper, "input", - name="UserCLK", num_pins="1") + ET.SubElement(pbTypeWrapper, "input", name="UserCLK", num_pins="1") if pbType != None: - ET.SubElement(pbType, "input", - name="UserCLK", num_pins="1") - ET.SubElement(interConnect, "direct", - name=f"{bel.name}_UserCLK_to_top_child", input=f"{bel.name}_wrapper.UserCLK", output=f"{bel.name}.UserCLK") + ET.SubElement(pbType, "input", name="UserCLK", num_pins="1") + ET.SubElement( + interConnect, + "direct", + name=f"{bel.name}_UserCLK_to_top_child", + input=f"{bel.name}_wrapper.UserCLK", + output=f"{bel.name}.UserCLK", + ) for i in bel.inputs: - ET.SubElement(pbTypeWrapper, "input", - name=i.removeprefix(bel.prefix), num_pins="1") + ET.SubElement( + pbTypeWrapper, "input", name=i.removeprefix(bel.prefix), num_pins="1" + ) if pbType != None: - ET.SubElement(pbType, "input", - name=i.removeprefix(bel.prefix), num_pins="1") - ET.SubElement(interConnect, "direct", - name=f"{bel.name}_{i.removeprefix(bel.prefix)}_top_to_child", input=f"{bel.name}_wrapper.{i.removeprefix(bel.prefix)}", output=f"{bel.name}.{i.removeprefix(bel.prefix)}") + ET.SubElement( + pbType, "input", name=i.removeprefix(bel.prefix), num_pins="1" + ) + ET.SubElement( + interConnect, + "direct", + name=f"{bel.name}_{i.removeprefix(bel.prefix)}_top_to_child", + input=f"{bel.name}_wrapper.{i.removeprefix(bel.prefix)}", + output=f"{bel.name}.{i.removeprefix(bel.prefix)}", + ) for i in bel.outputs: - ET.SubElement(pbTypeWrapper, "output", - name=i.removeprefix(bel.prefix), num_pins="1") + ET.SubElement( + pbTypeWrapper, "output", name=i.removeprefix(bel.prefix), num_pins="1" + ) if pbType != None: - ET.SubElement(pbType, "output", - name=i.removeprefix(bel.prefix), num_pins="1") - ET.SubElement(interConnect, "direct", - name=f"{bel.name}_{i.removeprefix(bel.prefix)}_child_to_top", input=f"{bel.name}.{i.removeprefix(bel.prefix)}", output=f"{bel.name}_wrapper.{i.removeprefix(bel.prefix)}") - + ET.SubElement( + pbType, "output", name=i.removeprefix(bel.prefix), num_pins="1" + ) + ET.SubElement( + interConnect, + "direct", + name=f"{bel.name}_{i.removeprefix(bel.prefix)}_child_to_top", + input=f"{bel.name}.{i.removeprefix(bel.prefix)}", + output=f"{bel.name}_wrapper.{i.removeprefix(bel.prefix)}", + ) # switch list block switchList = ET.SubElement(root, "switchlist") - ET.SubElement(switchList, "switch", type="buffer", name="ipin_cblock", - R="551", Cin=".77e-15", Cout="4e-15", Tdel="58e-12", buf_size="27.645901") - ET.SubElement(switchList, "switch", type="mux", name="buffer", R="2e-12", Cin=".77e-15", - Cout="4e-15", Tdel="58e-12", mux_trans_size="2.630740", buf_size="27.645901") + ET.SubElement( + switchList, + "switch", + type="buffer", + name="ipin_cblock", + R="551", + Cin=".77e-15", + Cout="4e-15", + Tdel="58e-12", + buf_size="27.645901", + ) + ET.SubElement( + switchList, + "switch", + type="mux", + name="buffer", + R="2e-12", + Cin=".77e-15", + Cout="4e-15", + Tdel="58e-12", + mux_trans_size="2.630740", + buf_size="27.645901", + ) # segment list block segmentList = ET.SubElement(root, "segmentlist") - segment = ET.SubElement(segmentList, "segment", name="dummy", length="1", - freq="1.000000", type="unidir", Rmetal="1e-12", Cmetal="22.5e-15") + segment = ET.SubElement( + segmentList, + "segment", + name="dummy", + length="1", + freq="1.000000", + type="unidir", + Rmetal="1e-12", + Cmetal="22.5e-15", + ) ET.SubElement(segment, "sb", type="pattern").text = "0 0" ET.SubElement(segment, "cb", type="pattern").text = "0" ET.SubElement(segment, "mux", name="buffer") @@ -260,9 +369,11 @@ def genVPRModel(fabric: Fabric, customXMLfile: str = "") -> str: def genVPRRoutingResourceGraph(fabric: Fabric) -> (str, int): root = ET.Element("rr_graph") - root.attrib = {"tool_name": "vpr", - "tool_version": "82a3c72", - "tool_comment": "Based on FABulous output"} + root.attrib = { + "tool_name": "vpr", + "tool_version": "82a3c72", + "tool_comment": "Based on FABulous output", + } clockX = 1 clockY = 1 @@ -278,70 +389,79 @@ def genVPRRoutingResourceGraph(fabric: Fabric) -> (str, int): ptcMap: dict[str, dict[str, int]] = {} blockTypes = ET.SubElement(root, "block_types") - ET.SubElement(blockTypes, "block_type", - id=f"{curId}", name="EMPTY", width="1", height="1") + ET.SubElement( + blockTypes, "block_type", id=f"{curId}", name="EMPTY", width="1", height="1" + ) blockIdMap["EMPTY"] = curId curId += 1 - blockType = ET.SubElement(blockTypes, "block_type", - id=f"{curId}", name="clock_primitive", width="1", height="1") + blockType = ET.SubElement( + blockTypes, + "block_type", + id=f"{curId}", + name="clock_primitive", + width="1", + height="1", + ) pinClass = ET.SubElement(blockType, "pin_class", type="OUTPUT") - ET.SubElement(pinClass, "pin", - ptc=f"{ptc}").text = "clock_primitive.clock_out[0]" + ET.SubElement(pinClass, "pin", ptc=f"{ptc}").text = "clock_primitive.clock_out[0]" ptc += 1 blockIdMap["clock_primitive"] = curId curId += 1 for name, tile in fabric.tileDic.items(): tilePtcMap: dict[str, int] = {} - blockType = ET.SubElement(blockTypes, "block_type", - id=f"{curId}", name=f"{name}", width="1", height="1") + blockType = ET.SubElement( + blockTypes, + "block_type", + id=f"{curId}", + name=f"{name}", + width="1", + height="1", + ) ptc = 0 # If no Bels if tile.bels == []: pinClass = ET.SubElement(blockType, "pin_class", type="INPUT") - ET.SubElement(pinClass, "pin", - ptc=f"{ptc}").text = f"{name}.UserCLK[0]" + ET.SubElement(pinClass, "pin", ptc=f"{ptc}").text = f"{name}.UserCLK[0]" ptc += 1 for bel in tile.bels: # if bel have no inputs and outputs or have clock input if bel.inputs == bel.outputs == [] or tile.withUserCLK: pinClass = ET.SubElement(blockType, "pin_class", type="INPUT") - ET.SubElement(pinClass, "pin", - ptc=f"{ptc}").text = f"{name}.UserCLK[0]" + ET.SubElement(pinClass, "pin", ptc=f"{ptc}").text = f"{name}.UserCLK[0]" ptc += 1 # bel input for i in bel.inputs: pinClass = ET.SubElement(blockType, "pin_class", type="INPUT") - ET.SubElement( - pinClass, "pin", ptc=f"{ptc}").text = f"{name}.{i}[0]" + ET.SubElement(pinClass, "pin", ptc=f"{ptc}").text = f"{name}.{i}[0]" tilePtcMap[i] = ptc ptc += 1 # bel output for i in bel.outputs: - pinClass = ET.SubElement( - blockType, "pin_class", type="OUTPUT") - ET.SubElement( - pinClass, "pin", ptc=f"{ptc}").text = f"{name}.{i}[0]" + pinClass = ET.SubElement(blockType, "pin_class", type="OUTPUT") + ET.SubElement(pinClass, "pin", ptc=f"{ptc}").text = f"{name}.{i}[0]" tilePtcMap[i] = ptc ptc += 1 - if hangingPort := [i for i in tile.portsInfo if "GND" in i.destinationName or "VCC" in i.destinationName]: + if hangingPort := [ + i + for i in tile.portsInfo + if "GND" in i.destinationName or "VCC" in i.destinationName + ]: expandedList = [] # get expended hanging port list for port in hangingPort: expandedList += port.expandPortInfo()[1] for i in set(expandedList): - pinClass = ET.SubElement( - blockType, "pin_class", type="OUTPUT") - ET.SubElement( - pinClass, "pin", ptc=f"{ptc}").text = f"{name}.{i}[0]" + pinClass = ET.SubElement(blockType, "pin_class", type="OUTPUT") + ET.SubElement(pinClass, "pin", ptc=f"{ptc}").text = f"{name}.{i}[0]" tilePtcMap[i] = ptc ptc += 1 @@ -362,18 +482,30 @@ def genVPRRoutingResourceGraph(fabric: Fabric) -> (str, int): nodes = ET.SubElement(root, "rr_nodes") nodes.append(ET.Comment("Clock output: clock_primitive.clock_out")) - node = ET.SubElement( - nodes, "node", id=f"{curNodeId}", type="SOURCE", capacity="1") + node = ET.SubElement(nodes, "node", id=f"{curNodeId}", type="SOURCE", capacity="1") ET.SubElement( - node, "loc", xlow=f"{clockX}", ylow=f"{clockY}", xhigh=f"{clockX}", yhigh=f"{clockY}", ptc="0") + node, + "loc", + xlow=f"{clockX}", + ylow=f"{clockY}", + xhigh=f"{clockX}", + yhigh=f"{clockY}", + ptc="0", + ) curNodeId += 1 - node = ET.SubElement( - nodes, "node", id=f"{curNodeId}", type="OPIN", capacity="1") + node = ET.SubElement(nodes, "node", id=f"{curNodeId}", type="OPIN", capacity="1") ET.SubElement( - node, "loc", xlow=f"{clockX}", ylow=f"{clockY}", xhigh=f"{clockX}", yhigh=f"{clockY}", ptc="0") + node, + "loc", + xlow=f"{clockX}", + ylow=f"{clockY}", + xhigh=f"{clockX}", + yhigh=f"{clockY}", + ptc="0", + ) destToWireIDMap[f"X{clockX}Y{clockY}.clock_out"] = curNodeId - edgeSourceSinkPair.append((curNodeId, curNodeId-1)) + edgeSourceSinkPair.append((curNodeId, curNodeId - 1)) curNodeId += 1 clockPtc += 1 @@ -383,26 +515,44 @@ def genVPRRoutingResourceGraph(fabric: Fabric) -> (str, int): continue node = ET.SubElement( - nodes, "node", id=f"{curNodeId}", type="IPIN", capacity="1") - ET.SubElement(node, "loc", xlow=f"{x + 1}", ylow=f"{y + 1}", - xhigh=f"{x + 1}", yhigh=f"{y + 1}", ptc="0", side="BOTTOM") + nodes, "node", id=f"{curNodeId}", type="IPIN", capacity="1" + ) + ET.SubElement( + node, + "loc", + xlow=f"{x + 1}", + ylow=f"{y + 1}", + xhigh=f"{x + 1}", + yhigh=f"{y + 1}", + ptc="0", + side="BOTTOM", + ) sourceToWireIDMap[f"X{x}Y{y}.UserCLK"] = curNodeId curNodeId += 1 node = ET.SubElement( - nodes, "node", id=f"{curNodeId}", type="SINK", capacity="1") - ET.SubElement(node, "loc", xlow=f"{x + 1}", ylow=f"{y + 1}", - xhigh=f"{x + 1}", yhigh=f"{y + 1}", ptc="0") + nodes, "node", id=f"{curNodeId}", type="SINK", capacity="1" + ) + ET.SubElement( + node, + "loc", + xlow=f"{x + 1}", + ylow=f"{y + 1}", + xhigh=f"{x + 1}", + yhigh=f"{y + 1}", + ptc="0", + ) curNodeId += 1 - edgeSourceSinkPair.append((curNodeId-1, curNodeId)) + edgeSourceSinkPair.append((curNodeId - 1, curNodeId)) nodeType = "" doneWire = set() for wire in tile.wireList: if wire.xOffset != 0 and wire.yOffset != 0: raise ValueError( - "Diagonal wires not currently supported for VPR routing resource model") + "Diagonal wires not currently supported for VPR routing resource model" + ) if (wire.source, wire.destination) in doneWire: continue doneWire.add((wire.source, wire.destination)) @@ -414,7 +564,9 @@ def genVPRRoutingResourceGraph(fabric: Fabric) -> (str, int): nodeType = "CHANY" # Check wire direction and set appropriate values - if (nodeType == "CHANX" and wire.xOffset > 0) or (nodeType == "CHANY" and wire.yOffset > 0): + if (nodeType == "CHANX" and wire.xOffset > 0) or ( + nodeType == "CHANY" and wire.yOffset > 0 + ): direction = "INC_DIR" yLow = y xLow = x @@ -435,19 +587,34 @@ def genVPRRoutingResourceGraph(fabric: Fabric) -> (str, int): colPtcArr[x] += 1 if wirePtc > colMaxPtc: raise ValueError( - "Channel PTC value too high - FABulous' VPR flow may not currently be able to support this many overlapping wires.") + "Channel PTC value too high - FABulous' VPR flow may not currently be able to support this many overlapping wires." + ) else: # i.e. if nodeType == "CHANX" wirePtc = rowPtcArr[y] rowPtcArr[y] += 1 if wirePtc > rowMaxPtc: raise ValueError( - "Channel PTC value too high - FABulous' VPR flow may not currently be able to support this many overlapping wires.") + "Channel PTC value too high - FABulous' VPR flow may not currently be able to support this many overlapping wires." + ) nodes.append(ET.Comment(f"Wire {wireSource} -> {wireDest}")) node = ET.SubElement( - nodes, "node", id=f"{curNodeId}", type=f"{nodeType}", capacity="1", direction=f"{direction}") - ET.SubElement(node, "loc", xlow=f"{xLow+1}", ylow=f"{yLow+1}", - xhigh=f"{xHigh+1}", yhigh=f"{yHigh+1}", ptc=f"{wirePtc}") + nodes, + "node", + id=f"{curNodeId}", + type=f"{nodeType}", + capacity="1", + direction=f"{direction}", + ) + ET.SubElement( + node, + "loc", + xlow=f"{xLow+1}", + ylow=f"{yLow+1}", + xhigh=f"{xHigh+1}", + yhigh=f"{yHigh+1}", + ptc=f"{wirePtc}", + ) ET.SubElement(node, "segment", segment_id="0") sourceToWireIDMap[wireSource] = curNodeId @@ -463,20 +630,38 @@ def genVPRRoutingResourceGraph(fabric: Fabric) -> (str, int): thisPtc = ptcMap[tile.name][input] else: raise Exception( - f"Could not find pin {input} of ptc in block_type {tile.name} designation for RR Graph generation.") + f"Could not find pin {input} of ptc in block_type {tile.name} designation for RR Graph generation." + ) nodes.append(ET.Comment(f"Bel input: {input}")) node = ET.SubElement( - nodes, "node", id=f"{curNodeId}", type="IPIN", capacity="1") - ET.SubElement(node, "loc", xlow=f"{x + 1}", ylow=f"{y + 1}", - xhigh=f"{x + 1}", yhigh=f"{y + 1}", ptc=f"{thisPtc}", side="BOTTOM") + nodes, "node", id=f"{curNodeId}", type="IPIN", capacity="1" + ) + ET.SubElement( + node, + "loc", + xlow=f"{x + 1}", + ylow=f"{y + 1}", + xhigh=f"{x + 1}", + yhigh=f"{y + 1}", + ptc=f"{thisPtc}", + side="BOTTOM", + ) sourceToWireIDMap[f"X{x}Y{y}.{input}"] = curNodeId curNodeId += 1 node = ET.SubElement( - nodes, "node", id=f"{curNodeId}", type="SINK", capacity="1") - ET.SubElement(node, "loc", xlow=f"{x + 1}", ylow=f"{y + 1}", - xhigh=f"{x + 1}", yhigh=f"{y + 1}", ptc=f"{thisPtc}") + nodes, "node", id=f"{curNodeId}", type="SINK", capacity="1" + ) + ET.SubElement( + node, + "loc", + xlow=f"{x + 1}", + ylow=f"{y + 1}", + xhigh=f"{x + 1}", + yhigh=f"{y + 1}", + ptc=f"{thisPtc}", + ) edgeSourceSinkPair.append((curNodeId, curNodeId - 1)) @@ -485,44 +670,82 @@ def genVPRRoutingResourceGraph(fabric: Fabric) -> (str, int): thisPtc = ptcMap[tile.name][output] else: raise Exception( - "Could not find pin ptc in block_type designation for RR Graph generation.") + "Could not find pin ptc in block_type designation for RR Graph generation." + ) nodes.append(ET.Comment(f"Bel output: {output}")) node = ET.SubElement( - nodes, "node", id=f"{curNodeId}", type="OPIN", capacity="1") - ET.SubElement(node, "loc", xlow=f"{x + 1}", ylow=f"{y + 1}", - xhigh=f"{x + 1}", yhigh=f"{y + 1}", ptc=f"{thisPtc}", side="BOTTOM") + nodes, "node", id=f"{curNodeId}", type="OPIN", capacity="1" + ) + ET.SubElement( + node, + "loc", + xlow=f"{x + 1}", + ylow=f"{y + 1}", + xhigh=f"{x + 1}", + yhigh=f"{y + 1}", + ptc=f"{thisPtc}", + side="BOTTOM", + ) destToWireIDMap[f"X{x}Y{y}.{output}"] = curNodeId curNodeId += 1 node = ET.SubElement( - nodes, "node", id=f"{curNodeId}", type="SOURCE", capacity="1") - ET.SubElement(node, "loc", xlow=f"{x + 1}", ylow=f"{y + 1}", - xhigh=f"{x + 1}", yhigh=f"{y + 1}", ptc=f"{thisPtc}") - - edgeSourceSinkPair.append((curNodeId-1, curNodeId)) + nodes, "node", id=f"{curNodeId}", type="SOURCE", capacity="1" + ) + ET.SubElement( + node, + "loc", + xlow=f"{x + 1}", + ylow=f"{y + 1}", + xhigh=f"{x + 1}", + yhigh=f"{y + 1}", + ptc=f"{thisPtc}", + ) + + edgeSourceSinkPair.append((curNodeId - 1, curNodeId)) curNodeId += 1 - if hangingPort := [i for i in tile.portsInfo if "GND" in i.destinationName or "VCC" in i.destinationName]: + if hangingPort := [ + i + for i in tile.portsInfo + if "GND" in i.destinationName or "VCC" in i.destinationName + ]: expandedList = [] # get expended hanging port list for port in hangingPort: expandedList += port.expandPortInfo()[1] for p in set(expandedList): thisPtc = ptcMap[tile.name][p] - nodes.append(ET.Comment( - f"Source: X{x}Y{y}.{p}")) + nodes.append(ET.Comment(f"Source: X{x}Y{y}.{p}")) node = ET.SubElement( - nodes, "node", id=f"{curNodeId}", type="SOURCE", capacity="1") - ET.SubElement(node, "loc", xlow=f"{x + 1}", ylow=f"{y + 1}", - xhigh=f"{x + 1}", yhigh=f"{y + 1}", ptc=f"{thisPtc}") + nodes, "node", id=f"{curNodeId}", type="SOURCE", capacity="1" + ) + ET.SubElement( + node, + "loc", + xlow=f"{x + 1}", + ylow=f"{y + 1}", + xhigh=f"{x + 1}", + yhigh=f"{y + 1}", + ptc=f"{thisPtc}", + ) curNodeId += 1 node = ET.SubElement( - nodes, "node", id=f"{curNodeId}", type="OPIN", capacity="1") - ET.SubElement(node, "loc", xlow=f"{x + 1}", ylow=f"{y + 1}", - xhigh=f"{x + 1}", yhigh=f"{y + 1}", ptc=f"{thisPtc}", side="BOTTOM") + nodes, "node", id=f"{curNodeId}", type="OPIN", capacity="1" + ) + ET.SubElement( + node, + "loc", + xlow=f"{x + 1}", + ylow=f"{y + 1}", + xhigh=f"{x + 1}", + yhigh=f"{y + 1}", + ptc=f"{thisPtc}", + side="BOTTOM", + ) destToWireIDMap[f"X{x}Y{y}.{p}"] = curNodeId edgeSourceSinkPair.append((curNodeId, curNodeId - 1)) @@ -530,33 +753,42 @@ def genVPRRoutingResourceGraph(fabric: Fabric) -> (str, int): # edge block edges = ET.SubElement(root, "rr_edges") - for (source, sink) in set(edgeSourceSinkPair): + for source, sink in set(edgeSourceSinkPair): ET.SubElement( - edges, "edge", src_node=f"{source}", sink_node=f"{sink}", switch_id="1") + edges, "edge", src_node=f"{source}", sink_node=f"{sink}", switch_id="1" + ) for y, row in enumerate(fabric.tile): for x, tile in enumerate(row): if tile == None: continue - ET.SubElement(edges, "edge", src_node=f"{destToWireIDMap[f'X{clockX}Y{clockY}.clock_out']}", - sink_node=f"{sourceToWireIDMap[f'X{clockX}Y{clockY}.UserCLK']}", switch_id="1") + ET.SubElement( + edges, + "edge", + src_node=f"{destToWireIDMap[f'X{clockX}Y{clockY}.clock_out']}", + sink_node=f"{sourceToWireIDMap[f'X{clockX}Y{clockY}.UserCLK']}", + switch_id="1", + ) if tile.matrixDir.endswith(".csv"): connections = parseMatrix(tile.matrixDir, tile.name) elif tile.matrixDir.endswith(".list"): logger.info(f"TileX{x}Y{y}_{tile.name} matrix is a list file") logger.info( - f"bootstrapping TileX{x}Y{y}_{tile.name} to matrix form and adding the list file to the matrix") + f"bootstrapping TileX{x}Y{y}_{tile.name} to matrix form and adding the list file to the matrix" + ) matrixDir = tile.matrixDir.replace(".list", ".csv") FabricGenerator.bootstrapSwitchMatrix(tile, matrixDir) FabricGenerator.list2CSV(tile.matrixDir, matrixDir) logger.info( - f"Update matrix directory to {matrixDir} for Fabric TileX{x}Y{y}_{tile.name}") + f"Update matrix directory to {matrixDir} for Fabric TileX{x}Y{y}_{tile.name}" + ) tile.matrixDir = matrixDir connections = parseMatrix(tile.matrixDir, tile.name) else: raise ValueError( - f"For model generation {tile.matrixDir} need to a csv or list file") + f"For model generation {tile.matrixDir} need to a csv or list file" + ) doneEdge = set() for source, sinkList in connections.items(): @@ -567,16 +799,29 @@ def genVPRRoutingResourceGraph(fabric: Fabric) -> (str, int): continue doneEdge.add((sinkName, sourceName)) edge = ET.SubElement( - edges, "edge", src_node=f"{destToWireIDMap[sourceName]}", sink_node=f"{sourceToWireIDMap[sinkName]}", switch_id="1") + edges, + "edge", + src_node=f"{destToWireIDMap[sourceName]}", + sink_node=f"{sourceToWireIDMap[sinkName]}", + switch_id="1", + ) metadata = ET.SubElement(edge, "metadata") - ET.SubElement( - metadata, "meta", name="fasm_features").text = f"X{x}Y{y}.{sink}.{source}" + ET.SubElement(metadata, "meta", name="fasm_features").text = ( + f"X{x}Y{y}.{sink}.{source}" + ) # channel block channels = ET.SubElement(root, "channels") maxWidth = max(rowPtcArr + colPtcArr) - channel = ET.SubElement(channels, "channel", chan_width_max=f"{maxWidth}", x_min="0", - y_min="0", x_max=f"{fabric.numberOfColumns + 1}", y_max=f"{fabric.numberOfRows + 1}") + channel = ET.SubElement( + channels, + "channel", + chan_width_max=f"{maxWidth}", + x_min="0", + y_min="0", + x_max=f"{fabric.numberOfColumns + 1}", + y_max=f"{fabric.numberOfRows + 1}", + ) for i in range(fabric.numberOfRows + 2): ET.SubElement(channels, "x_list", index=f"{i}", info=f"{maxWidth}") @@ -589,54 +834,109 @@ def genVPRRoutingResourceGraph(fabric: Fabric) -> (str, int): for y, row in list(enumerate(fabric.tile))[::-1]: for x, tile in enumerate(row): # skip clock tile - if x+1 == clockX and y+1 == clockY: + if x + 1 == clockX and y + 1 == clockY: continue if tile == None: - ET.SubElement(grid, "grid_loc", - x=f"{x+1}", y=f"{y+1}", block_type_id=f"{blockIdMap['EMPTY']}", width_offset="0", height_offset="0") + ET.SubElement( + grid, + "grid_loc", + x=f"{x+1}", + y=f"{y+1}", + block_type_id=f"{blockIdMap['EMPTY']}", + width_offset="0", + height_offset="0", + ) continue - ET.SubElement(grid, "grid_loc", - x=f"{x+1}", y=f"{y+1}", block_type_id=f"{blockIdMap[tile.name]}", width_offset="0", height_offset="0") + ET.SubElement( + grid, + "grid_loc", + x=f"{x+1}", + y=f"{y+1}", + block_type_id=f"{blockIdMap[tile.name]}", + width_offset="0", + height_offset="0", + ) grid.append(ET.Comment(f"EMPTY padding around chip")) for i in range(fabric.numberOfRows + 2): - if clockX+1 != 0 or clockY+1 != i: - ET.SubElement(grid, "grid_loc", - x="0", y=f"{i}", block_type_id=f"{blockIdMap['EMPTY']}", width_offset="0", height_offset="0") - if clockX+1 != fabric.numberOfColumns + 1 or clockY+1 != i: - ET.SubElement(grid, "grid_loc", - x=f"{fabric.numberOfColumns+1}", y=f"{i}", block_type_id=f"{blockIdMap['EMPTY']}", width_offset="0", height_offset="0") + if clockX + 1 != 0 or clockY + 1 != i: + ET.SubElement( + grid, + "grid_loc", + x="0", + y=f"{i}", + block_type_id=f"{blockIdMap['EMPTY']}", + width_offset="0", + height_offset="0", + ) + if clockX + 1 != fabric.numberOfColumns + 1 or clockY + 1 != i: + ET.SubElement( + grid, + "grid_loc", + x=f"{fabric.numberOfColumns+1}", + y=f"{i}", + block_type_id=f"{blockIdMap['EMPTY']}", + width_offset="0", + height_offset="0", + ) for i in range(1, fabric.numberOfColumns + 1): - if clockX+1 != i or clockY+1 != 0: - ET.SubElement(grid, "grid_loc", - x=f"{i}", y=f"{0}", block_type_id=f"{blockIdMap['EMPTY']}", width_offset="0", height_offset="0") - if clockX+1 != i or clockY+1 != fabric.numberOfRows + 1: - ET.SubElement(grid, "grid_loc", - x=f"{i}", y=f"{fabric.numberOfRows+1}", block_type_id=f"{blockIdMap['EMPTY']}", width_offset="0", height_offset="0") + if clockX + 1 != i or clockY + 1 != 0: + ET.SubElement( + grid, + "grid_loc", + x=f"{i}", + y=f"{0}", + block_type_id=f"{blockIdMap['EMPTY']}", + width_offset="0", + height_offset="0", + ) + if clockX + 1 != i or clockY + 1 != fabric.numberOfRows + 1: + ET.SubElement( + grid, + "grid_loc", + x=f"{i}", + y=f"{fabric.numberOfRows+1}", + block_type_id=f"{blockIdMap['EMPTY']}", + width_offset="0", + height_offset="0", + ) # lastly add clock tile - ET.SubElement(grid, "grid_loc", - x=f"{clockX}", y=f"{clockY}", block_type_id=f"{blockIdMap['clock_primitive']}", width_offset="0", height_offset="0") + ET.SubElement( + grid, + "grid_loc", + x=f"{clockX}", + y=f"{clockY}", + block_type_id=f"{blockIdMap['clock_primitive']}", + width_offset="0", + height_offset="0", + ) # switches block switches = ET.SubElement(root, "switches") - switch = ET.SubElement(switches, "switch", id="0", - type="mux", name="__vpr_delayless_switch__") + switch = ET.SubElement( + switches, "switch", id="0", type="mux", name="__vpr_delayless_switch__" + ) ET.SubElement(switch, "timing", R="0", Cin="0", Cout="0", Tdel="0") ET.SubElement(switch, "sizing", mux_trans_size="0", buf_size="0") - switch = ET.SubElement(switches, "switch", id="1", - type="mux", name="buffer") - ET.SubElement(switch, "timing", R="1.99999999e-12", - Cin="7.70000012e-16", Cout="4.00000001e-15", Tdel="5.80000006e-11") - ET.SubElement(switch, "sizing", mux_trans_size="2.63073993", - buf_size="27.6459007") + switch = ET.SubElement(switches, "switch", id="1", type="mux", name="buffer") + ET.SubElement( + switch, + "timing", + R="1.99999999e-12", + Cin="7.70000012e-16", + Cout="4.00000001e-15", + Tdel="5.80000006e-11", + ) + ET.SubElement(switch, "sizing", mux_trans_size="2.63073993", buf_size="27.6459007") # segment block segments = ET.SubElement(root, "segments") segment = ET.SubElement(segments, "segment", id="0", name="dummy") - ET.SubElement(segment, "timing", R_per_meter="9.99999996e-13", - C_per_meter="2.25000005e-14") + ET.SubElement( + segment, "timing", R_per_meter="9.99999996e-13", C_per_meter="2.25000005e-14" + ) ET.indent(root, space=" ") @@ -656,28 +956,61 @@ def genVPRConstrainsXML(fabric: Fabric) -> str: for i, bel in enumerate(tile.bels): if bel.name == "IO_1_bidirectional_frame_config_pass": partition = ET.SubElement( - partitionList, "partition", name=f"Tile_X{x}Y{y}_{letter[i]}") - ET.SubElement(partition, "add_atom", - name_pattern=f"Tile_X{x}Y{y}_{bel.prefix}O") + partitionList, "partition", name=f"Tile_X{x}Y{y}_{letter[i]}" + ) ET.SubElement( - partition, "add_region", x_low=f"{x+1}", y_low=f"{y+1}", x_high=f"{x+1}", y_high=f"{y+1}", subtile=f"{i}") + partition, + "add_atom", + name_pattern=f"Tile_X{x}Y{y}_{bel.prefix}O", + ) + ET.SubElement( + partition, + "add_region", + x_low=f"{x+1}", + y_low=f"{y+1}", + x_high=f"{x+1}", + y_high=f"{y+1}", + subtile=f"{i}", + ) if bel.name == "InPass4_frame_config": partition = ET.SubElement( - partitionList, "partition", name=f"Tile_X{x}Y{y}_{letter[i]}") - ET.SubElement(partition, "add_atom", - name_pattern=f"Tile_X{x}Y{y}_{bel.prefix}O0") + partitionList, "partition", name=f"Tile_X{x}Y{y}_{letter[i]}" + ) + ET.SubElement( + partition, + "add_atom", + name_pattern=f"Tile_X{x}Y{y}_{bel.prefix}O0", + ) ET.SubElement( - partition, "add_region", x_low=f"{x+1}", y_low=f"{y+1}", x_high=f"{x+1}", y_high=f"{y+1}", subtile=f"{i}") + partition, + "add_region", + x_low=f"{x+1}", + y_low=f"{y+1}", + x_high=f"{x+1}", + y_high=f"{y+1}", + subtile=f"{i}", + ) if bel.name == "OutPass4_frame_config": partition = ET.SubElement( - partitionList, "partition", name=f"Tile_X{x}Y{y}_{letter[i]}") - ET.SubElement(partition, "add_atom", - name_pattern=f"unnamed_subckt{unnamedCount}") + partitionList, "partition", name=f"Tile_X{x}Y{y}_{letter[i]}" + ) + ET.SubElement( + partition, + "add_atom", + name_pattern=f"unnamed_subckt{unnamedCount}", + ) unnamedCount += 1 ET.SubElement( - partition, "add_region", x_low=f"{x+1}", y_low=f"{y+1}", x_high=f"{x+1}", y_high=f"{y+1}", subtile=f"{i}") + partition, + "add_region", + x_low=f"{x+1}", + y_low=f"{y+1}", + x_high=f"{x+1}", + y_high=f"{y+1}", + subtile=f"{i}", + ) ET.indent(root, space=" ") return ET.tostring(root, encoding="unicode") @@ -686,7 +1019,7 @@ def genVPRConstrainsXML(fabric: Fabric) -> str: # Generates constraint XML for VPR flow def genVPRModelConstraints(archObject: FabricModelGen): constraintString = '\n' - constraintString += ' \n' + constraintString += " \n" unnamedCount = 0 for row in archObject.tiles: @@ -703,15 +1036,19 @@ def genVPRModelConstraints(archObject: FabricModelGen): # VPR names primitives after the first wire they drive # So we use the wire names assigned in genVerilogTemplate constraintString += f' \n' - constraintString += f' \n' + constraintString += ( + f' \n' + ) constraintString += f' \n' - constraintString += f' \n' + constraintString += f" \n" if bel == "InPass4_frame_config": constraintString += f' \n' - constraintString += f' \n' + constraintString += ( + f' \n' + ) constraintString += f' \n' - constraintString += f' \n' + constraintString += f" \n" # Frustratingly, since VPR names blocks after BELs they drive, BELs that drive no wires have auto-generated names # These names are, at time of writing, generated with unique_subckt_name() in vpr/src/base/read_blif.cpp @@ -721,10 +1058,10 @@ def genVPRModelConstraints(archObject: FabricModelGen): constraintString += f' \n' unnamedCount += 1 constraintString += f' \n' - constraintString += f' \n' + constraintString += f" \n" - constraintString += ' \n' - constraintString += '' + constraintString += " \n" + constraintString += "" return constraintString @@ -755,38 +1092,39 @@ def genVPRModelXML(archObject: FabricModelGen, customXmlFilename, generatePairs= for bel_info in root: # Check that the tag is valid if bel_info.tag != "bel_info": - raise ValueError( - f"Error: Unknown tag in custom XML file: {bel_info.tag}") + raise ValueError(f"Error: Unknown tag in custom XML file: {bel_info.tag}") - bel_name = bel_info.attrib['name'] + bel_name = bel_info.attrib["name"] # Check only one of each tag is present - if len(bel_info.findall('bel_pb')) > 1: + if len(bel_info.findall("bel_pb")) > 1: raise ValueError( - "Error: Found multiple bel_pb tags within one bel_info tag in custom XML file. Please provide only one.") + "Error: Found multiple bel_pb tags within one bel_info tag in custom XML file. Please provide only one." + ) - if len(bel_info.findall('bel_model')) > 1: + if len(bel_info.findall("bel_model")) > 1: raise ValueError( - "Error: Found multiple bel_model tags within one bel_info tag in custom XML file. Please provide at most one.") + "Error: Found multiple bel_model tags within one bel_info tag in custom XML file. Please provide at most one." + ) - if len(bel_info.findall('bel_interconnect')) > 1: + if len(bel_info.findall("bel_interconnect")) > 1: raise ValueError( - "Error: Found multiple bel_interconnect tags within one bel_info tag in custom XML file. Please provide at most one.") + "Error: Found multiple bel_interconnect tags within one bel_info tag in custom XML file. Please provide at most one." + ) # Fetch data and store in appropriate dicts - if bel_info.find('bel_pb'): - for bel_pb in bel_info.find('bel_pb'): - specialBelDict[bel_name] = ET.tostring( - bel_pb, encoding='unicode') - if bel_info.find('bel_model'): - for bel_model in bel_info.find('bel_model'): - specialModelDict[bel_name] = ET.tostring( - bel_model, encoding='unicode') - if bel_info.find('bel_interconnect'): - for bel_interconnect in bel_info.find('bel_interconnect'): + if bel_info.find("bel_pb"): + for bel_pb in bel_info.find("bel_pb"): + specialBelDict[bel_name] = ET.tostring(bel_pb, encoding="unicode") + if bel_info.find("bel_model"): + for bel_model in bel_info.find("bel_model"): + specialModelDict[bel_name] = ET.tostring(bel_model, encoding="unicode") + if bel_info.find("bel_interconnect"): + for bel_interconnect in bel_info.find("bel_interconnect"): specialInterconnectDict[bel_name] = ET.tostring( - bel_interconnect, encoding='unicode') + bel_interconnect, encoding="unicode" + ) # Calculate clock X and Y coordinates considering variations in coordinate systems and EMPTY padding around VPR model newClockX = clockX + 1 @@ -813,12 +1151,12 @@ def genVPRModelXML(archObject: FabricModelGen, customXmlFilename, generatePairs= # COMPLEX BLOCKS, MODELS & TILES # String to store all the different kinds of pb_types needed - first we populate it with a dummy for tiles without BELs (as they still require a subtile) - pb_typesString = ''' + pb_typesString = """ - ''' + """ modelsString = "" # String to store different models @@ -844,15 +1182,19 @@ def genVPRModelXML(archObject: FabricModelGen, customXmlFilename, generatePairs= tileInputs = [] tileOutputs = [] - if cTile.belsWithIO == []: # VPR requires a sub-tile tag, so if we can't create one for a BEL we make a dummy one + if ( + cTile.belsWithIO == [] + ): # VPR requires a sub-tile tag, so if we can't create one for a BEL we make a dummy one tilesString += f'\n' - tilesString += f' \n' - tilesString += f' \n' - tilesString += f' \n' + tilesString += f" \n" + tilesString += ( + f' \n' + ) + tilesString += f" \n" tilesString += f'' - tilesString += f'\n' + tilesString += f"\n" - hangingPortStr = '' + hangingPortStr = "" # Create second layer (leaf) blocks for each bel for bel in cTile.belsWithIO: @@ -863,12 +1205,16 @@ def genVPRModelXML(archObject: FabricModelGen, customXmlFilename, generatePairs= # If the BEL has no inputs or outputs then we need another dummy as VPR (understandably) doesn't like BELs with no pins # We could probably just omit them from the model, but this avoids any inconsistencies between subtile number and the fabric.csv if bel[2] == bel[3] == []: - tilesString += f'\n' - tilesString += f' \n' - tilesString += f' \n' - tilesString += f' \n' + tilesString += ( + f'\n' + ) + tilesString += f" \n" + tilesString += ( + f' \n' + ) + tilesString += f" \n" tilesString += f'' - tilesString += f'\n' + tilesString += f"\n" continue # We generate a separate subtile for each BEL instance (so that we can wire them differently) @@ -877,28 +1223,36 @@ def genVPRModelXML(archObject: FabricModelGen, customXmlFilename, generatePairs= # Add subtile to represent this BEL (specific instance) # Add sub_tile declaration to meet VTR 8.1.0 requirements tilesString += f' \n' - tilesString += ' \n' + tilesString += " \n" - pinMappingStr = '' + pinMappingStr = "" # Generate interconnect from wrapper pb_type to primitive # Add direct connections from top level tile to the corresponding child port for cInput in bel[2]: pinMappingStr += f' \n' - for cOutput in bel[3]: # Add direct connections from child port to top level tile + for cOutput in bel[ + 3 + ]: # Add direct connections from child port to top level tile pinMappingStr += f' \n' if bel[4]: # If the BEL has a clock input then route it in pinMappingStr += f' \n' - if pinMappingStr == '': # If no connections then direct mapping so VPR doesn't insist on subchildren that clarify mapping - tilesString += f' \n' + if ( + pinMappingStr == "" + ): # If no connections then direct mapping so VPR doesn't insist on subchildren that clarify mapping + tilesString += ( + f' \n' + ) else: - tilesString += f' \n' + tilesString += ( + f' \n' + ) tilesString += pinMappingStr - tilesString += f' \n' - tilesString += ' \n' + tilesString += f" \n" + tilesString += " \n" # If the BEL has an external clock connection then add this to the tile string if bel[4]: @@ -911,7 +1265,7 @@ def genVPRModelXML(archObject: FabricModelGen, customXmlFilename, generatePairs= for cOutput in bel[3]: tilesString += f' \n' - tilesString += f' \n' # Close subtile tag for this BEL + tilesString += f" \n" # Close subtile tag for this BEL # We only want one pb_type for each kind of bel so we track which ones we have already done if bel[0] in doneBels: @@ -925,14 +1279,14 @@ def genVPRModelXML(archObject: FabricModelGen, customXmlFilename, generatePairs= # Prepare custom passthrough interconnect from wrapper to primitive pb_type # Create lists of ports without prefixes for our generic modelling - unprefixedInputs = [removeStringPrefix( - cInput, bel[1]) for cInput in bel[2]] - unprefixedOutputs = [removeStringPrefix( - cOutput, bel[1]) for cOutput in bel[3]] + unprefixedInputs = [removeStringPrefix(cInput, bel[1]) for cInput in bel[2]] + unprefixedOutputs = [ + removeStringPrefix(cOutput, bel[1]) for cOutput in bel[3] + ] # String to connect ports in primitive pb to same-named ports on top-level pb - passthroughInterconnectStr = '' - pbPortsStr = '' + passthroughInterconnectStr = "" + pbPortsStr = "" for cInput in unprefixedInputs: # Add input and outputs @@ -953,19 +1307,25 @@ def genVPRModelXML(archObject: FabricModelGen, customXmlFilename, generatePairs= # Get the custom XML string thisPbString = specialBelDict[bel[0]] - customInterconnectStr = "" # Just so it's defined if there's no custom interconnect - if bel[0] in specialInterconnectDict: # And if it has any custom interconnects + customInterconnectStr = ( + "" # Just so it's defined if there's no custom interconnect + ) + if ( + bel[0] in specialInterconnectDict + ): # And if it has any custom interconnects # Add them to this string to be added in at the end of the pb_type customInterconnectStr = specialInterconnectDict[bel[0]] pb_typesString += f' \n' - pb_typesString += thisPbString # Add the custom pb_type XML with the list inserted + pb_typesString += ( + thisPbString # Add the custom pb_type XML with the list inserted + ) pb_typesString += pbPortsStr - pb_typesString += ' \n' + pb_typesString += " \n" pb_typesString += customInterconnectStr pb_typesString += passthroughInterconnectStr - pb_typesString += ' \n' - pb_typesString += f' \n' + pb_typesString += " \n" + pb_typesString += f" \n" if bel[0] in specialModelDict: # If it also has custom model XML # Then add in this XML @@ -981,7 +1341,9 @@ def genVPRModelXML(archObject: FabricModelGen, customXmlFilename, generatePairs= pb_typesString += f' \n' modelsString += f' \n' # Add model tag - modelsString += ' \n' # open tag for input ports in model list + modelsString += ( + " \n" # open tag for input ports in model list + ) # Generate space-separated list of all outputs for combinational sink ports allOutsStr = " ".join(unprefixedOutputs) @@ -994,14 +1356,14 @@ def genVPRModelXML(archObject: FabricModelGen, customXmlFilename, generatePairs= # Add all outputs as combinational sinks modelsString += f' \n' - modelsString += ' \n' # close input ports tag - modelsString += ' \n' # open output ports tag + modelsString += " \n" # close input ports tag + modelsString += " \n" # open output ports tag for cOutput in unprefixedOutputs: modelsString += f' \n' - modelsString += f' \n' # close output ports tag - modelsString += ' \n' + modelsString += f" \n" # close output ports tag + modelsString += " \n" # Generate delay constants - for the time being, we will assume that all inputs are combinatorially connected to all outputs @@ -1011,12 +1373,12 @@ def genVPRModelXML(archObject: FabricModelGen, customXmlFilename, generatePairs= pb_typesString += f' \n' pb_typesString += pbPortsStr - pb_typesString += ' \n' # Close inner tag - pb_typesString += ' \n' + pb_typesString += " \n" # Close inner tag + pb_typesString += " \n" pb_typesString += passthroughInterconnectStr - pb_typesString += ' \n' + pb_typesString += " \n" pb_typesString += pbPortsStr - pb_typesString += f' \n' # Close wrapper tag + pb_typesString += f" \n" # Close wrapper tag doneBels.append(bel[0]) # Make sure we don't repeat similar BELs @@ -1031,21 +1393,23 @@ def genVPRModelXML(archObject: FabricModelGen, customXmlFilename, generatePairs= if not (len(hangingSources) == len(hangingSinks) == 0): # First create sub_tile tilesString += f'\n' - tilesString += f' \n' - tilesString += f' \n' - tilesString += f' \n' + tilesString += f" \n" + tilesString += ( + f' \n' + ) + tilesString += f" \n" for sink in hangingSinks: tilesString += f'\n' for source in hangingSources: tilesString += f'\n' - tilesString += f'\n' + tilesString += f"\n" # Now create the pb_type pb_typesString += f'\n' - pb_typesString += f' \n' + pb_typesString += f" \n" for sink in hangingSinks: pb_typesString += f'\n' @@ -1053,9 +1417,9 @@ def genVPRModelXML(archObject: FabricModelGen, customXmlFilename, generatePairs= for source in hangingSources: pb_typesString += f'\n' - pb_typesString += f'\n' + pb_typesString += f"\n" - tilesString += ' \n' + tilesString += " \n" # LAYOUT @@ -1068,11 +1432,13 @@ def genVPRModelXML(archObject: FabricModelGen, customXmlFilename, generatePairs= for line in archObject.tiles: for tile in line: - if tile.tileType != "NULL": # We do not need to specify if the tile is empty as all tiles default to EMPTY in VPR + if ( + tile.tileType != "NULL" + ): # We do not need to specify if the tile is empty as all tiles default to EMPTY in VPR # Add single tag for each tile - add 1 to x and y (cancels out in y conversion) for padding layoutString += f' \n' # Now add metadata for fasm generation - layoutString += ' \n' + layoutString += " \n" # We need different numbers of prefixes depending on the subtiles tileLoc = tile.genTileLoc() @@ -1093,10 +1459,10 @@ def genVPRModelXML(archObject: FabricModelGen, customXmlFilename, generatePairs= # Only metadata required is the tile name for the prefix layoutString += f' {prefixList} \n' - layoutString += ' \n' - layoutString += ' \n' + layoutString += " \n" + layoutString += " \n" - layoutString += ' \n' + layoutString += " \n" # SWITCHLIST @@ -1137,7 +1503,7 @@ def genVPRModelXML(archObject: FabricModelGen, customXmlFilename, generatePairs= # OUTPUT - outputString = f''' + outputString = f""" {deviceString} @@ -1175,7 +1541,7 @@ def genVPRModelXML(archObject: FabricModelGen, customXmlFilename, generatePairs= -''' +""" return outputString @@ -1187,9 +1553,11 @@ def genVPRModelRRGraph(archObject: FabricModelGen, generatePairs=True): # BLOCKS - blocksString = '' # Initialise string for block types + blocksString = "" # Initialise string for block types curId = 0 # Increment id from 0 as we work through - blockIdMap = {} # Dictionary to record IDs for different tile types when generating grid + blockIdMap = ( + {} + ) # Dictionary to record IDs for different tile types when generating grid ptcMap = {} # Dict to map tiles to individual dicts that map pin name to PTC # Get sources and sinks for fabric - more info in method sourceSinkMap = getFabricSourcesAndSinks(archObject) @@ -1203,17 +1571,19 @@ def genVPRModelRRGraph(archObject: FabricModelGen, generatePairs=True): # And add clock tile (as this is a dummy to represent deeper FABulous functionality, so will not be in our csv files) - blocksString += f' \n' + blocksString += ( + f' \n' + ) ptc = 0 blocksString += f' \n' # Add output tag for each tile blocksString += f' clock_primitive.clock_out[0]\n' - blocksString += f' \n' + blocksString += f" \n" ptc += 1 - blocksString += '\n' + blocksString += "\n" # Store that the clock_primitive block has this ID blockIdMap["clock_primitive"] = curId curId += 1 @@ -1221,7 +1591,9 @@ def genVPRModelRRGraph(archObject: FabricModelGen, generatePairs=True): for cellType in archObject.cellTypes: tilePtcMap = {} # Dict to map each pin on this tile to its ptc # Generate block type tile for each type of tile - we assume 1x1 tiles here - blocksString += f' \n' + blocksString += ( + f' \n' + ) cTile = getTileByType(archObject, cellType) # Fetch tile of this type @@ -1234,7 +1606,7 @@ def genVPRModelRRGraph(archObject: FabricModelGen, generatePairs=True): if cTile.belsWithIO == []: # If no BELs then we need UserCLK as a dummy pin blocksString += f' \n' # Generate the tags blocksString += f' {cellType}.UserCLK[0]\n' - blocksString += f' \n' + blocksString += f" \n" ptc += 1 for bel in cTile.belsWithIO: # For each bel on the tile @@ -1242,38 +1614,38 @@ def genVPRModelRRGraph(archObject: FabricModelGen, generatePairs=True): if bel[4] or (bel[2] == bel[3] == []): blocksString += f' \n' # Generate the tags blocksString += f' {cellType}.UserCLK[0]\n' - blocksString += f' \n' + blocksString += f" \n" ptc += 1 # And increment the ptc for cInput in bel[2]: # Take each input and output blocksString += f' \n' # Generate the tags blocksString += f' {cellType}.{cInput}[0]\n' - blocksString += f' \n' + blocksString += f" \n" tilePtcMap[cInput] = ptc # Note the ptc in the tile's ptc map ptc += 1 # And increment the ptc for cOutput in bel[3]: blocksString += f' \n' # Same as above blocksString += f' {cellType}.{cOutput}[0]\n' - blocksString += f' \n' + blocksString += f" \n" tilePtcMap[cOutput] = ptc ptc += 1 for sink in sourceSinkMap[cTile.genTileLoc()][1]: blocksString += f' \n' # Generate the tags blocksString += f' {cellType}.{sink}[0]\n' - blocksString += f' \n' + blocksString += f" \n" tilePtcMap[sink] = ptc # Note the ptc in the tile's ptc map ptc += 1 # And increment the ptc for source in sourceSinkMap[cTile.genTileLoc()][0]: blocksString += f' \n' # Same as above blocksString += f' {cellType}.{source}[0]\n' - blocksString += f' \n' + blocksString += f" \n" tilePtcMap[source] = ptc ptc += 1 - blocksString += ' \n' + blocksString += " \n" # Create copy of ptc map for this tile and add to larger dict (passing by reference would have undesired effects) ptcMap[cellType] = dict(tilePtcMap) @@ -1284,7 +1656,7 @@ def genVPRModelRRGraph(archObject: FabricModelGen, generatePairs=True): # NODES - nodesString = '' + nodesString = "" curNodeId = 0 # Start indexing nodes at 0 and increment each time a node is added sourceToWireIDMap = {} # Dictionary to map a wire source to the relevant wire ID @@ -1292,19 +1664,19 @@ def genVPRModelRRGraph(archObject: FabricModelGen, generatePairs=True): max_width = 1 # Initialise value to find maximum channel width for channels tag - start as 1 as you can't have a thinner wire! - srcToOpinStr = '' - IpinToSinkStr = '' + srcToOpinStr = "" + IpinToSinkStr = "" clockPtc = 0 - clockLoc = f'X{clockX}Y{clockY}' + clockLoc = f"X{clockX}Y{clockY}" # Add node for clock out - nodesString += f' \n' + nodesString += f" \n" # Generate tag for each node nodesString += f' \n' # Add loc tag nodesString += f' \n' - nodesString += ' \n' # Close node tag + nodesString += " \n" # Close node tag curNodeId += 1 # Increment id so all nodes have different ids @@ -1312,8 +1684,10 @@ def genVPRModelRRGraph(archObject: FabricModelGen, generatePairs=True): nodesString += f' \n' # Add loc tag nodesString += f' \n' - nodesString += ' \n' # Close node tag - srcToOpinStr += f' \n' + nodesString += " \n" # Close node tag + srcToOpinStr += ( + f' \n' + ) # Add to dest map as equivalent to a wire destination destToWireIDMap[clockLoc + "." + "clock_out"] = curNodeId curNodeId += 1 @@ -1349,7 +1723,7 @@ def genVPRModelRRGraph(archObject: FabricModelGen, generatePairs=True): nodesString += f' \n' # Add loc tag nodesString += f' \n' - nodesString += ' \n' # Close node tag + nodesString += " \n" # Close node tag # Add to dest map as equivalent to a wire destination sourceToWireIDMap[tileLoc + ".UserCLK"] = curNodeId curNodeId += 1 @@ -1358,7 +1732,7 @@ def genVPRModelRRGraph(archObject: FabricModelGen, generatePairs=True): nodesString += f' \n' # Add loc tag nodesString += f' \n' - nodesString += ' \n' # Close node tag + nodesString += " \n" # Close node tag IpinToSinkStr += f' \n' curNodeId += 1 @@ -1367,10 +1741,13 @@ def genVPRModelRRGraph(archObject: FabricModelGen, generatePairs=True): if wire["yoffset"] != "0" and wire["xoffset"] != "0": # Stop if there are diagonal wires just in case they get put in a fabric raise Exception( - "Diagonal wires not currently supported for VPR routing resource model") + "Diagonal wires not currently supported for VPR routing resource model" + ) # Then we check which one isn't zero and take that as the length if wire["yoffset"] != "0": - nodeType = "CHANY" # Set node type as vertical channel if wire is vertical + nodeType = ( + "CHANY" # Set node type as vertical channel if wire is vertical + ) elif wire["xoffset"] != "0": nodeType = "CHANX" # Set as horizontal if moving along X else: # If we get to here then both offsets are zero and so this must be a jump wire @@ -1382,7 +1759,9 @@ def genVPRModelRRGraph(archObject: FabricModelGen, generatePairs=True): desttileLoc = f"X{destx}Y{desty}" # Check wire direction and set appropriate valuesz - if (nodeType == "CHANX" and int(wire["xoffset"]) > 0) or (nodeType == "CHANY" and int(wire["yoffset"]) > 0): + if (nodeType == "CHANX" and int(wire["xoffset"]) > 0) or ( + nodeType == "CHANY" and int(wire["yoffset"]) > 0 + ): direction = "INC_DIR" yLow = tile.y xLow = tile.x @@ -1406,27 +1785,31 @@ def genVPRModelRRGraph(archObject: FabricModelGen, generatePairs=True): colPtcArr[tile.x] += 1 if wirePtc > colMaxPtc: raise ValueError( - "Channel PTC value too high - FABulous' VPR flow may not currently be able to support this many overlapping wires.") + "Channel PTC value too high - FABulous' VPR flow may not currently be able to support this many overlapping wires." + ) else: # i.e. if nodeType == "CHANX" wirePtc = rowPtcArr[tile.y] rowPtcArr[tile.y] += 1 if wirePtc > rowMaxPtc: raise ValueError( - "Channel PTC value too high - FABulous' VPR flow may not currently be able to support this many overlapping wires.") + "Channel PTC value too high - FABulous' VPR flow may not currently be able to support this many overlapping wires." + ) # Coordinates until now have been relative to the fabric - only account for padding when formatting actual string # Comment destination for clarity - nodesString += f' \n' + nodesString += ( + f" \n" + ) # Generate tag for each node nodesString += f' \n' nodesString += ' \n' # Add loc tag with the information we just calculated nodesString += f' \n' - nodesString += ' \n' # Close node tag + nodesString += " \n" # Close node tag - sourceToWireIDMap[wireSource+str(i)] = curNodeId - destToWireIDMap[wireDest+str(i)] = curNodeId + sourceToWireIDMap[wireSource + str(i)] = curNodeId + destToWireIDMap[wireDest + str(i)] = curNodeId curNodeId += 1 # Increment id so all nodes have different ids # If our current width is greater than the previous max, take the new one @@ -1439,10 +1822,13 @@ def genVPRModelRRGraph(archObject: FabricModelGen, generatePairs=True): print(wire["yoffset"], wire["xoffset"]) # Stop if there are diagonal wires just in case they get put in a fabric raise Exception( - "Diagonal wires not currently supported for VPR routing resource model") + "Diagonal wires not currently supported for VPR routing resource model" + ) # Then we check which one isn't zero and take that as the length if wire["yoffset"] != "0": - nodeType = "CHANY" # Set node type as vertical channel if wire is vertical + nodeType = ( + "CHANY" # Set node type as vertical channel if wire is vertical + ) elif wire["xoffset"] != "0": nodeType = "CHANX" # Set as horizontal if moving along X @@ -1452,7 +1838,9 @@ def genVPRModelRRGraph(archObject: FabricModelGen, generatePairs=True): destTile = archObject.getTileByLoc(wire["destTile"]) - if (nodeType == "CHANX" and int(wire["xoffset"]) > 0) or (nodeType == "CHANY" and int(wire["yoffset"]) > 0): + if (nodeType == "CHANX" and int(wire["xoffset"]) > 0) or ( + nodeType == "CHANY" and int(wire["yoffset"]) > 0 + ): direction = "INC_DIR" yLow = tile.y xLow = tile.x @@ -1470,16 +1858,18 @@ def genVPRModelRRGraph(archObject: FabricModelGen, generatePairs=True): colPtcArr[tile.x] += 1 if wirePtc > colMaxPtc: raise ValueError( - "Channel PTC value too high - FABulous' VPR flow may not currently be able to support this many overlapping wires.") + "Channel PTC value too high - FABulous' VPR flow may not currently be able to support this many overlapping wires." + ) else: # i.e. if nodeType == "CHANX" wirePtc = rowPtcArr[tile.y] rowPtcArr[tile.y] += 1 if wirePtc > rowMaxPtc: raise ValueError( - "Channel PTC value too high - FABulous' VPR flow may not currently be able to support this many overlapping wires.") + "Channel PTC value too high - FABulous' VPR flow may not currently be able to support this many overlapping wires." + ) # Comment destination for clarity - nodesString += f' \n' + nodesString += f" \n" # Generate tag for each node nodesString += f' \n' @@ -1487,7 +1877,7 @@ def genVPRModelRRGraph(archObject: FabricModelGen, generatePairs=True): nodesString += f' \n' nodesString += ' \n' - nodesString += f' \n' # Close node tag + nodesString += f" \n" # Close node tag sourceToWireIDMap[wireSource] = curNodeId destToWireIDMap[wireDest] = curNodeId @@ -1501,13 +1891,16 @@ def genVPRModelRRGraph(archObject: FabricModelGen, generatePairs=True): thisPtc = ptcMap[tile.tileType][cInput] else: raise Exception( - "Could not find pin ptc in block_type designation for RR Graph generation.") - nodesString += f' \n' + "Could not find pin ptc in block_type designation for RR Graph generation." + ) + nodesString += f" \n" # Generate tag for each node - nodesString += f' \n' + nodesString += ( + f' \n' + ) # Add loc tag - same high and low vals as no movement between tiles nodesString += f' \n' - nodesString += ' \n' # Close node tag + nodesString += " \n" # Close node tag # Add to source map as it is the equivalent of a wire source sourceToWireIDMap[tileLoc + "." + cInput] = curNodeId @@ -1515,10 +1908,12 @@ def genVPRModelRRGraph(archObject: FabricModelGen, generatePairs=True): curNodeId += 1 # Increment id so all nodes have different ids # Generate tag for each node - nodesString += f' \n' + nodesString += ( + f' \n' + ) # Add loc tag - same high and low vals as no movement between tiles nodesString += f' \n' - nodesString += ' \n' # Close node tag + nodesString += " \n" # Close node tag IpinToSinkStr += f' \n' curNodeId += 1 # Increment id so all nodes have different ids @@ -1528,36 +1923,41 @@ def genVPRModelRRGraph(archObject: FabricModelGen, generatePairs=True): thisPtc = ptcMap[tile.tileType][cOutput] else: raise Exception( - "Could not find pin ptc in block_type designation for RR Graph generation.") - nodesString += f' \n' + "Could not find pin ptc in block_type designation for RR Graph generation." + ) + nodesString += f" \n" # Generate tag for each node - nodesString += f' \n' + nodesString += ( + f' \n' + ) # Add loc tag nodesString += f' \n' - nodesString += ' \n' # Close node tag + nodesString += " \n" # Close node tag # Add to dest map as equivalent to a wire destination destToWireIDMap[tileLoc + "." + cOutput] = curNodeId curNodeId += 1 # Increment id so all nodes have different ids # Generate tag for each node - nodesString += f' \n' + nodesString += ( + f' \n' + ) # Add loc tag nodesString += f' \n' - nodesString += ' \n' # Close node tag + nodesString += " \n" # Close node tag srcToOpinStr += f' \n' curNodeId += 1 # Increment id so all nodes have different ids for source in sourceSinkMap[tile.genTileLoc()][0]: thisPtc = ptcMap[tile.tileType][source] - nodesString += f' \n' + nodesString += f" \n" # Generate tag for each node nodesString += f' \n' # Add loc tag nodesString += f' \n' - nodesString += ' \n' # Close node tag + nodesString += " \n" # Close node tag curNodeId += 1 # Increment id so all nodes have different ids @@ -1565,7 +1965,7 @@ def genVPRModelRRGraph(archObject: FabricModelGen, generatePairs=True): nodesString += f' \n' # Add loc tag nodesString += f' \n' - nodesString += ' \n' # Close node tag + nodesString += " \n" # Close node tag srcToOpinStr += f' \n' # Add to dest map as equivalent to a wire destination destToWireIDMap[tileLoc + "." + source] = curNodeId @@ -1575,20 +1975,20 @@ def genVPRModelRRGraph(archObject: FabricModelGen, generatePairs=True): for sink in sourceSinkMap[tile.genTileLoc()][1]: thisPtc = ptcMap[tile.tileType][sink] - nodesString += f' \n' + nodesString += f" \n" # Generate tag for each node nodesString += f' \n' # Add loc tag nodesString += f' \n' - nodesString += ' \n' # Close node tag + nodesString += " \n" # Close node tag curNodeId += 1 # Increment id so all nodes have different ids # Generate tag for each node nodesString += f' \n' # Add loc tag nodesString += f' \n' - nodesString += ' \n' # Close node tag + nodesString += " \n" # Close node tag IpinToSinkStr += f' \n' @@ -1605,7 +2005,9 @@ def genVPRModelRRGraph(archObject: FabricModelGen, generatePairs=True): for row in archObject.tiles: for tile in row: - if tile.tileType != "NULL": # If the tile isn't NULL then create an edge for the clock primitive connection + if ( + tile.tileType != "NULL" + ): # If the tile isn't NULL then create an edge for the clock primitive connection tileLoc = tile.genTileLoc() edgeStr += f' \n' @@ -1616,10 +2018,10 @@ def genVPRModelRRGraph(archObject: FabricModelGen, generatePairs=True): # And create node edgeStr += f' \n' # Generate metadata tag that tells us which switch matrix connection to activate - edgeStr += ' \n' + edgeStr += " \n" edgeStr += f' {".".join([tileLoc, pip[0], pip[1]])}\n' - edgeStr += ' \n' - edgeStr += ' \n' + edgeStr += " \n" + edgeStr += " \n" # CHANNELS @@ -1638,22 +2040,28 @@ def genVPRModelRRGraph(archObject: FabricModelGen, generatePairs=True): # GRID - gridString = '' + gridString = "" for row in archObject.tiles[::-1]: for tile in row: - if tile.x == clockX and tile.y == clockY: # We add the clock tile at the end so ignore it for now + if ( + tile.x == clockX and tile.y == clockY + ): # We add the clock tile at the end so ignore it for now continue - if tile.tileType == "NULL": # The method that generates cellTypes ignores NULL, so it was never in our map - we'll just use EMPTY instead as we did for the main XML model + if ( + tile.tileType == "NULL" + ): # The method that generates cellTypes ignores NULL, so it was never in our map - we'll just use EMPTY instead as we did for the main XML model gridString += f' \n' continue gridString += f' \n' # Create padding of EMPTY tiles around chip - gridString += ' \n' + gridString += " \n" for i in range(archObject.height + 2): # Add vertical padding - if newClockX != 0 or newClockY != i: # Make sure that this isn't the clock tile as we add this at the end + if ( + newClockX != 0 or newClockY != i + ): # Make sure that this isn't the clock tile as we add this at the end gridString += f' \n' if newClockX != archObject.width + 1 or newClockY != i: # Check not clock tile gridString += f' \n' @@ -1671,18 +2079,18 @@ def genVPRModelRRGraph(archObject: FabricModelGen, generatePairs=True): # Largely filler info - again, FABulous does not deal with this level of hardware detail currently - switchesString = ''' + switchesString = """ - \n''' + \n""" # OUTPUT - outputString = f''' + outputString = f""" @@ -1716,7 +2124,7 @@ def genVPRModelRRGraph(archObject: FabricModelGen, generatePairs=True): -''' # Same point as in main XML generation applies here regarding outsourcing indentation +""" # Same point as in main XML generation applies here regarding outsourcing indentation - print(f'Max Width: {max_width}') + print(f"Max Width: {max_width}") return outputString diff --git a/fabric_generator/utilities.py b/fabric_generator/utilities.py index b56b720c..1fec1eac 100644 --- a/fabric_generator/utilities.py +++ b/fabric_generator/utilities.py @@ -1,18 +1,18 @@ import re # Default parameters (will be overwritten if defined in fabric between 'ParametersBegin' and 'ParametersEnd' -#Parameters = [ 'ConfigBitMode', 'FrameBitsPerRow' ] -CONFIG_BIT_MODE = 'FlipFlopChain' +# Parameters = [ 'ConfigBitMode', 'FrameBitsPerRow' ] +CONFIG_BIT_MODE = "FlipFlopChain" FRAME_BITS_PER_ROW = 32 MAX_FRAMES_PER_COL = 20 -PACKAGE = 'use work.my_package.all;' +PACKAGE = "use work.my_package.all;" # time in ps - this is needed for simulation as a fabric configuration can result in loops crashing the simulator -GENERATE_DELAY_IN_SWITCH_MATRIX = '100' +GENERATE_DELAY_IN_SWITCH_MATRIX = "100" # 'custom': using our hard-coded MUX-4 and MUX-16; 'generic': using standard generic RTL code -MULTIPLEXER_STYLE = 'custom' +MULTIPLEXER_STYLE = "custom" # generate switch matrix select signals (index) which is useful to verify if bitstream matches bitstream SWITCH_MATRIX_DEBUG_SIGNAL = True -SUPER_TILE_ENABLE = True # enable SuperTile generation +SUPER_TILE_ENABLE = True # enable SuperTile generation src_dir = "./" @@ -38,9 +38,13 @@ # BEL prefix field (needed to allow multiple instantiations of the same BEL inside the same tile) BEL_prefix = 2 # MISC -All_Directions = ['NORTH', 'EAST', 'SOUTH', 'WEST'] -Opposite_Directions = {"NORTH": "SOUTH", - "EAST": "WEST", "SOUTH": "NORTH", "WEST": "EAST"} +All_Directions = ["NORTH", "EAST", "SOUTH", "WEST"] +Opposite_Directions = { + "NORTH": "SOUTH", + "EAST": "WEST", + "SOUTH": "NORTH", + "WEST": "EAST", +} sDelay = "8" @@ -48,8 +52,31 @@ VCCRE = re.compile("VCC(\d*)") VDDRE = re.compile("VDD(\d*)") BracketAddingRE = re.compile(r"^(\S+?)(\d+)$") -letters = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", - "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W"] # For LUT labelling +letters = [ + "A", + "B", + "C", + "D", + "E", + "F", + "G", + "H", + "I", + "J", + "K", + "L", + "M", + "N", + "O", + "P", + "Q", + "R", + "S", + "T", + "U", + "V", + "W", +] # For LUT labelling # Given a fabric array description, return all uniq cell types @@ -66,8 +93,8 @@ def GetCellTypes(list): output.append(item) # we use the keyword 'NULL' for padding tiles that we don't return - if ('NULL' in output): - output.remove('NULL') + if "NULL" in output: + output.remove("NULL") return output @@ -78,32 +105,32 @@ def RemoveComments(list): for line in list: templine = [] - marker = False # we use this marker to remember if we had an '#' element before + marker = False # we use this marker to remember if we had an '#' element before for item in line: - if item.startswith('#'): + if item.startswith("#"): marker = True - if not (item.startswith('#') or marker == True): + if not (item.startswith("#") or marker == True): # marker = True templine.append(item) - if item == '': - templine.remove('') + if item == "": + templine.remove("") if templine != []: output.append(templine) return output -def GetFabric(list, filter='Fabric'): +def GetFabric(list, filter="Fabric"): templist = [] # output = [] marker = False for sublist in list: - if filter+'End' in sublist: # was FabricEnd + if filter + "End" in sublist: # was FabricEnd marker = False if marker == True: templist.append(sublist) # we place this conditional after the append such that the 'FabricBegin' will be kicked out - if filter+'Begin' in sublist: # was FabricBegin + if filter + "Begin" in sublist: # was FabricBegin marker = True return RemoveComments(templist) @@ -114,30 +141,30 @@ def GetTileFromFile(list, TileType): marker = False for sublist in list: - if ('EndTILE' in sublist): + if "EndTILE" in sublist: marker = False - if ('TILE' in sublist) and (TileType in sublist): + if ("TILE" in sublist) and (TileType in sublist): marker = True if marker == True: templist.append(sublist) # we place this conditional after the append such that the 'FabricBegin' will be kicked out # if ('TILE' in sublist) and (type in sublist): # if ('TILE' in sublist) and (TileType in sublist): - # marker = True + # marker = True return RemoveComments(templist) def GetSuperTileFromFile(list): templist = [] tempdict = {} - superTile_type = '' + superTile_type = "" marker = False for sublist in list: - if 'SuperTILE' in sublist: + if "SuperTILE" in sublist: marker = True superTile_type = sublist[1] continue - elif 'EndSuperTILE' in sublist: + elif "EndSuperTILE" in sublist: marker = False tempdict[superTile_type] = RemoveComments(templist) templist = [] @@ -148,35 +175,60 @@ def GetSuperTileFromFile(list): def PrintTileComponentPort(tile_description, entity, direction, file): - print('\t-- ', direction, file=file) + print("\t-- ", direction, file=file) for line in tile_description: if line[0] == direction: - print('\t\t', line[source_name], - '\t: out \tSTD_LOGIC_VECTOR( ', end='', file=file) - print(((abs(int(line[X_offset]))+abs(int(line[Y_offset]))) - * int(line[wires]))-1, end='', file=file) - print(' downto 0 );', end='', file=file) - print('\t -- wires: ', line[wires], file=file) + print( + "\t\t", + line[source_name], + "\t: out \tSTD_LOGIC_VECTOR( ", + end="", + file=file, + ) + print( + ( + (abs(int(line[X_offset])) + abs(int(line[Y_offset]))) + * int(line[wires]) + ) + - 1, + end="", + file=file, + ) + print(" downto 0 );", end="", file=file) + print("\t -- wires: ", line[wires], file=file) for line in tile_description: if line[0] == direction: - print('\t\t', line[destination_name], - '\t: in \tSTD_LOGIC_VECTOR( ', end='', file=file) - print(((abs(int(line[X_offset]))+abs(int(line[Y_offset]))) - * int(line[wires]))-1, end='', file=file) - print(' downto 0 );', end='', file=file) - print('\t -- wires: ', line[wires], file=file) + print( + "\t\t", + line[destination_name], + "\t: in \tSTD_LOGIC_VECTOR( ", + end="", + file=file, + ) + print( + ( + (abs(int(line[X_offset])) + abs(int(line[Y_offset]))) + * int(line[wires]) + ) + - 1, + end="", + file=file, + ) + print(" downto 0 );", end="", file=file) + print("\t -- wires: ", line[wires], file=file) return def replace(string, substitutions): substrings = sorted(substitutions, key=len, reverse=True) - regex = re.compile('|'.join(map(re.escape, substrings))) + regex = re.compile("|".join(map(re.escape, substrings))) return regex.sub(lambda match: substitutions[match.group(0)], string) -def GetComponentPortsFromFile(VHDL_file_name, filter='ALL', port='internal', BEL_Prefix=''): - VHDLfile = [line.rstrip('\n') - for line in open(f"{src_dir}/{VHDL_file_name}")] +def GetComponentPortsFromFile( + VHDL_file_name, filter="ALL", port="internal", BEL_Prefix="" +): + VHDLfile = [line.rstrip("\n") for line in open(f"{src_dir}/{VHDL_file_name}")] Inputs = [] Outputs = [] @@ -184,37 +236,41 @@ def GetComponentPortsFromFile(VHDL_file_name, filter='ALL', port='internal', BEL marker = False FoundEntityMarker = False DoneMarker = False - direction = '' + direction = "" for line in VHDLfile: # the order of the if-statements are important ; - if re.search('^entity', line, flags=re.IGNORECASE): + if re.search("^entity", line, flags=re.IGNORECASE): FoundEntityMarker = True # detect the direction from comments, like "--NORTH" # we need this to filter for a specific direction # this implies of course that this information is provided in the VHDL entity - if re.search('NORTH', line, flags=re.IGNORECASE): - direction = 'NORTH' - if re.search('EAST', line, flags=re.IGNORECASE): - direction = 'EAST' - if re.search('SOUTH', line, flags=re.IGNORECASE): - direction = 'SOUTH' - if re.search('WEST', line, flags=re.IGNORECASE): - direction = 'WEST' + if re.search("NORTH", line, flags=re.IGNORECASE): + direction = "NORTH" + if re.search("EAST", line, flags=re.IGNORECASE): + direction = "EAST" + if re.search("SOUTH", line, flags=re.IGNORECASE): + direction = "SOUTH" + if re.search("WEST", line, flags=re.IGNORECASE): + direction = "WEST" # all primitive pins that are connected to the switch matrix have to go before the GLOBAL label - if re.search('-- global', line, flags=re.IGNORECASE): + if re.search("-- global", line, flags=re.IGNORECASE): FoundEntityMarker = False marker = False DoneMarker = True - if (marker == True) and (DoneMarker == False) and (direction == filter or filter == 'ALL'): + if ( + (marker == True) + and (DoneMarker == False) + and (direction == filter or filter == "ALL") + ): # detect if the port has to be exported as EXTERNAL which is flagged by the comment - if re.search('EXTERNAL', line): + if re.search("EXTERNAL", line): External = True else: External = False - if re.search('CONFIG_PORT', line): + if re.search("CONFIG_PORT", line): Config = True else: Config = False @@ -222,86 +278,109 @@ def GetComponentPortsFromFile(VHDL_file_name, filter='ALL', port='internal', BEL # substitutions = {';.*', '', '--.*', '', ',.*', ''} # tmp_line=(replace(line, substitutions)) # tmp_line = (re.sub(';.*', '',(re.sub('--.*', '',line, flags=re.IGNORECASE)), flags=re.IGNORECASE)) - tmp_line = (re.sub(';.*', '', (re.sub('--.*', '', (re.sub(',.*', '', line, - flags=re.IGNORECASE)), flags=re.IGNORECASE)), flags=re.IGNORECASE)) - std_vector = '' - if re.search('std_logic_vector', tmp_line, flags=re.IGNORECASE): - std_vector = (re.sub('.*std_logic_vector', '', - tmp_line, flags=re.IGNORECASE)) - tmp_line = (re.sub('STD_LOGIC.*', '', - tmp_line, flags=re.IGNORECASE)) + tmp_line = re.sub( + ";.*", + "", + ( + re.sub( + "--.*", + "", + (re.sub(",.*", "", line, flags=re.IGNORECASE)), + flags=re.IGNORECASE, + ) + ), + flags=re.IGNORECASE, + ) + std_vector = "" + if re.search("std_logic_vector", tmp_line, flags=re.IGNORECASE): + std_vector = re.sub( + ".*std_logic_vector", "", tmp_line, flags=re.IGNORECASE + ) + tmp_line = re.sub("STD_LOGIC.*", "", tmp_line, flags=re.IGNORECASE) substitutions = {" ": "", "\t": ""} - tmp_line = (replace(tmp_line, substitutions)) + tmp_line = replace(tmp_line, substitutions) # at this point, we get clean port names, like # A0:in # A1:in # A2:in # The following is for internal fabric signal ports (e.g., a CLB/LUT) - if (port == 'internal') and (External == False) and (Config == False): - if re.search(':in', tmp_line, flags=re.IGNORECASE): + if (port == "internal") and (External == False) and (Config == False): + if re.search(":in", tmp_line, flags=re.IGNORECASE): Inputs.append( - BEL_Prefix+(re.sub(':in.*', '', tmp_line, flags=re.IGNORECASE))+std_vector) - if re.search(':out', tmp_line, flags=re.IGNORECASE): + BEL_Prefix + + (re.sub(":in.*", "", tmp_line, flags=re.IGNORECASE)) + + std_vector + ) + if re.search(":out", tmp_line, flags=re.IGNORECASE): Outputs.append( - BEL_Prefix+(re.sub(':out.*', '', tmp_line, flags=re.IGNORECASE))+std_vector) + BEL_Prefix + + (re.sub(":out.*", "", tmp_line, flags=re.IGNORECASE)) + + std_vector + ) # The following is for ports that have to go all the way up to the top-level entity (e.g., from an I/O cell) - if (port == 'external') and (External == True): + if (port == "external") and (External == True): # .lstrip() removes leading white spaces including ' ', '\t' - ExternalPorts.append(BEL_Prefix+line.lstrip()) + ExternalPorts.append(BEL_Prefix + line.lstrip()) # frame reconfiguration needs a port for writing in frame data - if (port == 'frame_config') and (Config == True): + if (port == "frame_config") and (Config == True): # .lstrip() removes leading white spaces including ' ', '\t' - ExternalPorts.append(BEL_Prefix+line.lstrip()) + ExternalPorts.append(BEL_Prefix + line.lstrip()) - if re.search('port', line, flags=re.IGNORECASE): + if re.search("port", line, flags=re.IGNORECASE): marker = True - if port == 'internal': # default + if port == "internal": # default return Inputs, Outputs else: return ExternalPorts -def GetComponentPortsFromVerilog(Verilog_file_name, filter='ALL', port='internal', BEL_Prefix=''): - Verilogfile = [line.rstrip('\n') for line in open(Verilog_file_name)] +def GetComponentPortsFromVerilog( + Verilog_file_name, filter="ALL", port="internal", BEL_Prefix="" +): + Verilogfile = [line.rstrip("\n") for line in open(Verilog_file_name)] Inputs = [] Outputs = [] ExternalPorts = [] marker = False FoundEntityMarker = False DoneMarker = False - direction = '' + direction = "" for line in Verilogfile: # the order of the if-statements are important ; - if re.search('^module', line, flags=re.IGNORECASE): + if re.search("^module", line, flags=re.IGNORECASE): FoundEntityMarker = True # detect the direction from comments, like "--NORTH" # we need this to filter for a specific direction # this implies of course that this information is provided in the VHDL entity - if re.search('NORTH', line, flags=re.IGNORECASE): - direction = 'NORTH' - if re.search('EAST', line, flags=re.IGNORECASE): - direction = 'EAST' - if re.search('SOUTH', line, flags=re.IGNORECASE): - direction = 'SOUTH' - if re.search('WEST', line, flags=re.IGNORECASE): - direction = 'WEST' + if re.search("NORTH", line, flags=re.IGNORECASE): + direction = "NORTH" + if re.search("EAST", line, flags=re.IGNORECASE): + direction = "EAST" + if re.search("SOUTH", line, flags=re.IGNORECASE): + direction = "SOUTH" + if re.search("WEST", line, flags=re.IGNORECASE): + direction = "WEST" # all primitive pins that are connected to the switch matrix have to go before the GLOBAL label - if re.search('// global', line, flags=re.IGNORECASE): + if re.search("// global", line, flags=re.IGNORECASE): FoundEntityMarker = False marker = False DoneMarker = True - if (marker == True) and (DoneMarker == False) and (direction == filter or filter == 'ALL'): + if ( + (marker == True) + and (DoneMarker == False) + and (direction == filter or filter == "ALL") + ): # detect if the port has to be exported as EXTERNAL which is flagged by the comment - if re.search('EXTERNAL', line): + if re.search("EXTERNAL", line): External = True else: External = False - if re.search('CONFIG_PORT', line): + if re.search("CONFIG_PORT", line): Config = True else: Config = False @@ -309,52 +388,75 @@ def GetComponentPortsFromVerilog(Verilog_file_name, filter='ALL', port='internal # substitutions = {';.*', '', '--.*', '', ',.*', ''} # tmp_line=(replace(line, substitutions)) # tmp_line = (re.sub(';.*', '',(re.sub('--.*', '',line, flags=re.IGNORECASE)), flags=re.IGNORECASE)) - tmp_line = (re.sub(';.*', '', (re.sub('//.*', '', (re.sub(',.*', '', line, - flags=re.IGNORECASE)), flags=re.IGNORECASE)), flags=re.IGNORECASE)) - std_vector = '' - if re.search('input', tmp_line, flags=re.IGNORECASE) or re.search('output', tmp_line, flags=re.IGNORECASE): - std_vector = (re.sub('.*std_logic_vector', '', - tmp_line, flags=re.IGNORECASE)) - tmp_line = (re.sub('STD_LOGIC.*', '', - tmp_line, flags=re.IGNORECASE)) + tmp_line = re.sub( + ";.*", + "", + ( + re.sub( + "//.*", + "", + (re.sub(",.*", "", line, flags=re.IGNORECASE)), + flags=re.IGNORECASE, + ) + ), + flags=re.IGNORECASE, + ) + std_vector = "" + if re.search("input", tmp_line, flags=re.IGNORECASE) or re.search( + "output", tmp_line, flags=re.IGNORECASE + ): + std_vector = re.sub( + ".*std_logic_vector", "", tmp_line, flags=re.IGNORECASE + ) + tmp_line = re.sub("STD_LOGIC.*", "", tmp_line, flags=re.IGNORECASE) substitutions = {" ": "", "\t": ""} - tmp_line = (replace(tmp_line, substitutions)) + tmp_line = replace(tmp_line, substitutions) # at this point, we get clean port names, like # A0:in # A1:in # A2:in # The following is for internal fabric signal ports (e.g., a CLB/LUT) - if (port == 'internal') and (External == False) and (Config == False): - if re.search(':in', tmp_line, flags=re.IGNORECASE) and 'integer' not in tmp_line: + if (port == "internal") and (External == False) and (Config == False): + if ( + re.search(":in", tmp_line, flags=re.IGNORECASE) + and "integer" not in tmp_line + ): Inputs.append( - BEL_Prefix+(re.sub(':in.*', '', tmp_line, flags=re.IGNORECASE))+std_vector) - if re.search(':out', tmp_line, flags=re.IGNORECASE): + BEL_Prefix + + (re.sub(":in.*", "", tmp_line, flags=re.IGNORECASE)) + + std_vector + ) + if re.search(":out", tmp_line, flags=re.IGNORECASE): Outputs.append( - BEL_Prefix+(re.sub(':out.*', '', tmp_line, flags=re.IGNORECASE))+std_vector) + BEL_Prefix + + (re.sub(":out.*", "", tmp_line, flags=re.IGNORECASE)) + + std_vector + ) # The following is for ports that have to go all the way up to the top-level entity (e.g., from an I/O cell) - if (port == 'external') and (External == True): + if (port == "external") and (External == True): # .lstrip() removes leading white spaces including ' ', '\t' - ExternalPorts.append(BEL_Prefix+line.lstrip()) + ExternalPorts.append(BEL_Prefix + line.lstrip()) # frame reconfiguration needs a port for writing in frame data - if (port == 'frame_config') and (Config == True): + if (port == "frame_config") and (Config == True): # .lstrip() removes leading white spaces including ' ', '\t' - ExternalPorts.append(BEL_Prefix+line.lstrip()) + ExternalPorts.append(BEL_Prefix + line.lstrip()) - if re.search('port', line, flags=re.IGNORECASE): + if re.search("port", line, flags=re.IGNORECASE): marker = True - if port == 'internal': # default + if port == "internal": # default return Inputs, Outputs else: return ExternalPorts def GetNoConfigBitsFromFile(VHDL_file_name): - with open(VHDL_file_name, 'r') as f: + with open(VHDL_file_name, "r") as f: file = f.read() result = re.search( - r"NoConfigBits\s*:\s*integer\s*:=\s*(\w+)", file, flags=re.IGNORECASE) + r"NoConfigBits\s*:\s*integer\s*:=\s*(\w+)", file, flags=re.IGNORECASE + ) if result: try: return int(result.group(1)) @@ -363,72 +465,106 @@ def GetNoConfigBitsFromFile(VHDL_file_name): def GetComponentEntityNameFromFile(VHDL_file_name): - VHDLfile = [line.rstrip('\n') - for line in open(f"{src_dir}/{VHDL_file_name}")] + VHDLfile = [line.rstrip("\n") for line in open(f"{src_dir}/{VHDL_file_name}")] for line in VHDLfile: # the order of the if-statements is important - if re.search('^entity', line, flags=re.IGNORECASE): - result = (re.sub(' ', '', (re.sub('entity', '', (re.sub( - ' is.*', '', line, flags=re.IGNORECASE)), flags=re.IGNORECASE)))) + if re.search("^entity", line, flags=re.IGNORECASE): + result = re.sub( + " ", + "", + ( + re.sub( + "entity", + "", + (re.sub(" is.*", "", line, flags=re.IGNORECASE)), + flags=re.IGNORECASE, + ) + ), + ) return result def GetComponentEntityNameFromVerilog(Verilog_file_name): - Verilogfile = [line.rstrip('\n') for line in open( - f"{src_dir}/{Verilog_file_name}")] + Verilogfile = [line.rstrip("\n") for line in open(f"{src_dir}/{Verilog_file_name}")] for line in Verilogfile: # the order of the if-statements is important - if re.search('^module', line, flags=re.IGNORECASE): - result = (re.sub(' ', '', (re.sub('module', '', (re.sub( - ' (.*', '', line, flags=re.IGNORECASE)), flags=re.IGNORECASE)))) + if re.search("^module", line, flags=re.IGNORECASE): + result = re.sub( + " ", + "", + ( + re.sub( + "module", + "", + (re.sub(" (.*", "", line, flags=re.IGNORECASE)), + flags=re.IGNORECASE, + ) + ), + ) return result -def GetTileComponentPorts(tile_description, mode='SwitchMatrix'): +def GetTileComponentPorts(tile_description, mode="SwitchMatrix"): Inputs = [] Outputs = [] - OpenIndex = '' - CloseIndex = '' - if re.search('Indexed', mode, flags=re.IGNORECASE): - OpenIndex = '(' - CloseIndex = ')' + OpenIndex = "" + CloseIndex = "" + if re.search("Indexed", mode, flags=re.IGNORECASE): + OpenIndex = "(" + CloseIndex = ")" for line in tile_description: - if (line[direction] == 'NORTH') or (line[direction] == 'EAST') or (line[direction] == 'SOUTH') or (line[direction] == 'WEST'): + if ( + (line[direction] == "NORTH") + or (line[direction] == "EAST") + or (line[direction] == "SOUTH") + or (line[direction] == "WEST") + ): # range (wires-1 downto 0) as connected to the switch matrix - if mode in ['SwitchMatrix', 'SwitchMatrixIndexed']: + if mode in ["SwitchMatrix", "SwitchMatrixIndexed"]: ThisRange = int(line[wires]) - if mode in ['AutoSwitchMatrix', 'AutoSwitchMatrixIndexed']: - if line[source_name] == 'NULL' or line[destination_name] == 'NULL': + if mode in ["AutoSwitchMatrix", "AutoSwitchMatrixIndexed"]: + if line[source_name] == "NULL" or line[destination_name] == "NULL": # the following line connects all wires to the switch matrix in the case one port is NULL (typically termination) ThisRange = ( - abs(int(line[X_offset]))+abs(int(line[Y_offset]))) * int(line[wires]) + abs(int(line[X_offset])) + abs(int(line[Y_offset])) + ) * int(line[wires]) else: # the following line connects all bottom wires to the switch matrix in the case begin and end ports are used ThisRange = int(line[wires]) # range ((wires*distance)-1 downto 0) as connected to the tile top - if mode in ['all', 'allIndexed', 'Top', 'TopIndexed', 'AutoTop', 'AutoTopIndexed']: - ThisRange = ( - abs(int(line[X_offset]))+abs(int(line[Y_offset]))) * int(line[wires]) + if mode in [ + "all", + "allIndexed", + "Top", + "TopIndexed", + "AutoTop", + "AutoTopIndexed", + ]: + ThisRange = (abs(int(line[X_offset])) + abs(int(line[Y_offset]))) * int( + line[wires] + ) # the following three lines are needed to get the top line[wires] that are actually the connection from a switch matrix to the routing fabric StartIndex = 0 - if mode in ['Top', 'TopIndexed']: + if mode in ["Top", "TopIndexed"]: StartIndex = ( - (abs(int(line[X_offset]))+abs(int(line[Y_offset])))-1) * int(line[wires]) - if mode in ['AutoTop', 'AutoTopIndexed']: - if line[source_name] == 'NULL' or line[destination_name] == 'NULL': + (abs(int(line[X_offset])) + abs(int(line[Y_offset]))) - 1 + ) * int(line[wires]) + if mode in ["AutoTop", "AutoTopIndexed"]: + if line[source_name] == "NULL" or line[destination_name] == "NULL": # in case one port is NULL, then the all the other port wires get connected to the switch matrix. StartIndex = 0 else: # "normal" case as for the CLBs StartIndex = ( - (abs(int(line[X_offset]))+abs(int(line[Y_offset])))-1) * int(line[wires]) + (abs(int(line[X_offset])) + abs(int(line[Y_offset]))) - 1 + ) * int(line[wires]) for i in range(StartIndex, ThisRange): - if line[destination_name] != 'NULL': - Inputs.append(line[destination_name] + - OpenIndex+str(i)+CloseIndex) - if line[source_name] != 'NULL': - Outputs.append(line[source_name] + - OpenIndex+str(i)+CloseIndex) + if line[destination_name] != "NULL": + Inputs.append( + line[destination_name] + OpenIndex + str(i) + CloseIndex + ) + if line[source_name] != "NULL": + Outputs.append(line[source_name] + OpenIndex + str(i) + CloseIndex) return Inputs, Outputs @@ -438,30 +574,38 @@ def GetTileComponentPortsVectors(tile_description, mode): Outputs = [] MaxIndex = 0 for line in tile_description: - if (line[direction] == 'NORTH') or (line[direction] == 'EAST') or (line[direction] == 'SOUTH') or (line[direction] == 'WEST'): + if ( + (line[direction] == "NORTH") + or (line[direction] == "EAST") + or (line[direction] == "SOUTH") + or (line[direction] == "WEST") + ): # range (wires-1 downto 0) as connected to the switch matrix - if mode in ['SwitchMatrix', 'SwitchMatrixIndexed']: + if mode in ["SwitchMatrix", "SwitchMatrixIndexed"]: MaxIndex = int(line[wires]) # range ((wires*distance)-1 downto 0) as connected to the tile top - if mode in ['all', 'allIndexed']: - MaxIndex = ( - abs(int(line[X_offset]))+abs(int(line[Y_offset]))) * int(line[wires]) - if line[destination_name] != 'NULL': + if mode in ["all", "allIndexed"]: + MaxIndex = (abs(int(line[X_offset])) + abs(int(line[Y_offset]))) * int( + line[wires] + ) + if line[destination_name] != "NULL": Inputs.append( - str(line[destination_name]+'('+str(MaxIndex)+' downto 0)')) - if line[source_name] != 'NULL': + str(line[destination_name] + "(" + str(MaxIndex) + " downto 0)") + ) + if line[source_name] != "NULL": Outputs.append( - str(line[source_name]+'('+str(MaxIndex)+' downto 0)')) + str(line[source_name] + "(" + str(MaxIndex) + " downto 0)") + ) return Inputs, Outputs def PrintCSV_FileInfo(CSV_FileName): - CSVFile = [i.strip('\n').split(',') for i in open(CSV_FileName)] - print('Tile: ', str(CSVFile[0][0]), '\n') + CSVFile = [i.strip("\n").split(",") for i in open(CSV_FileName)] + print("Tile: ", str(CSVFile[0][0]), "\n") # print('DEBUG:',CSVFile) - print('\nInputs: \n') + print("\nInputs: \n") CSVFileRows = len(CSVFile) # for port in CSVFile[0][1:]: line = CSVFile[0] @@ -469,12 +613,12 @@ def PrintCSV_FileInfo(CSV_FileName): PortList = [] PortCount = 0 for j in range(1, len(CSVFile)): - if CSVFile[j][k] != '0': + if CSVFile[j][k] != "0": PortList.append(CSVFile[j][0]) PortCount += 1 - print(line[k], ' connects to ', PortCount, ' ports: ', PortList) + print(line[k], " connects to ", PortCount, " ports: ", PortList) - print('\nOutputs: \n') + print("\nOutputs: \n") for line in CSVFile[1:]: # we first count the number of multiplexer inputs mux_size = 0 @@ -482,29 +626,30 @@ def PrintCSV_FileInfo(CSV_FileName): # for port in line[1:]: # if port != '0': for k in range(1, len(line)): - if line[k] != '0': + if line[k] != "0": mux_size += 1 PortList.append(CSVFile[0][k]) - print(line[0], ',', str(mux_size), ', Source port list: ', PortList) + print(line[0], ",", str(mux_size), ", Source port list: ", PortList) return def ExpandListPorts(port, PortList): # a leading '[' tells us that we have to expand the list - if re.search('\[', port): - if not re.search('\]', port): + if re.search("\[", port): + if not re.search("\]", port): raise ValueError( - '\nError in function ExpandListPorts: cannot find closing ]\n') + "\nError in function ExpandListPorts: cannot find closing ]\n" + ) # port.find gives us the first occurrence index in a string - left_index = port.find('[') - right_index = port.find(']') + left_index = port.find("[") + right_index = port.find("]") before_left_index = port[0:left_index] # right_index is the position of the ']' so we need everything after that - after_right_index = port[(right_index+1):] + after_right_index = port[(right_index + 1) :] ExpandList = [] - ExpandList = re.split('\|', port[left_index+1:right_index]) + ExpandList = re.split("\|", port[left_index + 1 : right_index]) for entry in ExpandList: - ExpandListItem = (before_left_index+entry+after_right_index) + ExpandListItem = before_left_index + entry + after_right_index ExpandListPorts(ExpandListItem, PortList) else: @@ -514,25 +659,24 @@ def ExpandListPorts(port, PortList): def takes_list(a_string, a_list): - print('first debug (a_list):', a_list, 'string:', a_string) + print("first debug (a_list):", a_list, "string:", a_string) for item in a_list: - print('hello debug:', item, 'string:', a_string) + print("hello debug:", item, "string:", a_string) def GetVerilogDeclarationForFile(VHDL_file_name): ConfigPortUsed = 0 # 1 means is used - VHDLfile = [line.rstrip('\n') - for line in open(f"{src_dir}/{VHDL_file_name}")] + VHDLfile = [line.rstrip("\n") for line in open(f"{src_dir}/{VHDL_file_name}")] templist = [] # for item in VHDLfile: # print(item) for line in VHDLfile: # NumberOfConfigBits:0 means no configuration port - if re.search('NumberOfConfigBits', line, flags=re.IGNORECASE): + if re.search("NumberOfConfigBits", line, flags=re.IGNORECASE): # NumberOfConfigBits appears, so we may have a config port ConfigPortUsed = 1 # but only if the following is not true - if re.search('NumberOfConfigBits:0', line, flags=re.IGNORECASE): + if re.search("NumberOfConfigBits:0", line, flags=re.IGNORECASE): ConfigPortUsed = 0 # print('', file=file) return ConfigPortUsed @@ -542,7 +686,9 @@ def GetVerilogDeclarationForFile(VHDL_file_name): class TileModelGen: tileType = "" bels = [] - belsWithIO = [] # Currently the plan is to deprecate bels and replace it with this. However, this would require nextpnr model generation changes, so I won't do that until the VPR foundations are established + belsWithIO = ( + [] + ) # Currently the plan is to deprecate bels and replace it with this. However, this would require nextpnr model generation changes, so I won't do that until the VPR foundations are established # Format for belsWithIO is [bel name, prefix, inputs, outputs, whether it has a clock input] # Format for bels is [bel name, prefix, ports, whether it has a clock input] wires = [] @@ -560,8 +706,8 @@ def __init__(self, inType): self.tileType = inType def genTileLoc(self, separate=False): - if (separate): - return("X" + str(self.x), "Y" + str(self.y)) + if separate: + return ("X" + str(self.x), "Y" + str(self.y)) return "X" + str(self.x) + "Y" + str(self.y) @@ -601,7 +747,9 @@ def getTileAndWireByWireDest(self, loc: str, dest: str, jumps: bool = True): desty = tile.y + int(wire["yoffset"]) destx = tile.x + int(wire["xoffset"]) desttileLoc = f"X{destx}Y{desty}" - if (desttileLoc == loc) and (wire["destination"] + str(i) == dest): + if (desttileLoc == loc) and ( + wire["destination"] + str(i) == dest + ): return (tile, wire, i) return None @@ -626,7 +774,9 @@ def getTileByType(fabricObject: FabricModelGen, cellType: str): # This function parses the contents of a CSV with comments removed to get where potential interconnects are # The current implementation has two potential outputs: pips is a list of pairings (designed for single PIPs), whereas pipsdict maps each source to all possible sinks (designed with multiplexers in mind) -def findPipList(csvFile: list, returnDict: bool = False, mapSourceToSinks: bool = False): +def findPipList( + csvFile: list, returnDict: bool = False, mapSourceToSinks: bool = False +): sinks = [line[0] for line in csvFile] sources = csvFile[0] pips = [] @@ -635,17 +785,17 @@ def findPipList(csvFile: list, returnDict: bool = False, mapSourceToSinks: bool for x, value in enumerate(row[1::]): # Remember that x and y are offset if value == "1": - pips.append([sources[x+1], sinks[y+1]]) + pips.append([sources[x + 1], sinks[y + 1]]) if mapSourceToSinks: - if sources[x+1] in pipsdict.keys(): - pipsdict[sources[x+1]].append(sinks[y+1]) + if sources[x + 1] in pipsdict.keys(): + pipsdict[sources[x + 1]].append(sinks[y + 1]) else: - pipsdict[sources[x+1]] = [sinks[y+1]] + pipsdict[sources[x + 1]] = [sinks[y + 1]] else: - if sinks[y+1] in pipsdict.keys(): - pipsdict[sinks[y+1]].append(sources[x+1]) + if sinks[y + 1] in pipsdict.keys(): + pipsdict[sinks[y + 1]].append(sources[x + 1]) else: - pipsdict[sinks[y+1]] = [sources[x+1]] + pipsdict[sinks[y + 1]] = [sources[x + 1]] if returnDict: return pipsdict return pips @@ -653,8 +803,8 @@ def findPipList(csvFile: list, returnDict: bool = False, mapSourceToSinks: bool # Method to remove a known prefix from a string if it is present at the start - this is provided as str.removeprefix in Python 3.9 but has been implemented for compatibility def removeStringPrefix(mainStr: str, prefix: str): - if mainStr[0:len(prefix)] == prefix: - return mainStr[len(prefix):] + if mainStr[0 : len(prefix)] == prefix: + return mainStr[len(prefix) :] else: return mainStr @@ -674,9 +824,11 @@ def getFabricSourcesAndSinks(archObject: FabricModelGen, assumeSourceSinkNames=T for bel in tile.belsWithIO: allFabricInputs.extend( - [(tileLoc + "." + cInput) for cInput in bel[2]]) + [(tileLoc + "." + cInput) for cInput in bel[2]] + ) allFabricOutputs.extend( - [(tileLoc + "." + cOutput) for cOutput in bel[3]]) + [(tileLoc + "." + cOutput) for cOutput in bel[3]] + ) for wire in tile.wires: # Calculate destination location of the wire at hand @@ -686,17 +838,17 @@ def getFabricSourcesAndSinks(archObject: FabricModelGen, assumeSourceSinkNames=T # For every individual wire for i in range(int(wire["wire-count"])): - allFabricInputs.append( - tileLoc + "." + wire["source"] + str(i)) + allFabricInputs.append(tileLoc + "." + wire["source"] + str(i)) allFabricOutputs.append( - desttileLoc + "." + wire["destination"] + str(i)) + desttileLoc + "." + wire["destination"] + str(i) + ) for wire in tile.atomicWires: # Generate location strings for the source and destination - allFabricInputs.append( - wire["sourceTile"] + "." + wire["source"]) + allFabricInputs.append(wire["sourceTile"] + "." + wire["source"]) allFabricOutputs.append( - wire["destTile"] + "." + wire["destination"]) + wire["destTile"] + "." + wire["destination"] + ) # Now we go through all the pips, and if a source/sink doesn't appear in the list we keep it for row in archObject.tiles: @@ -706,7 +858,11 @@ def getFabricSourcesAndSinks(archObject: FabricModelGen, assumeSourceSinkNames=T sinkSet = set() for pip in tile.pips: if assumeSourceSinkNames: - if GNDRE.match(pip[0]) or VCCRE.match(pip[0]) or VDDRE.match(pip[0]): + if ( + GNDRE.match(pip[0]) + or VCCRE.match(pip[0]) + or VDDRE.match(pip[0]) + ): sourceSet.add(pip[0]) else: if (tileLoc + "." + pip[0]) not in allFabricOutputs: @@ -756,13 +912,16 @@ def genFabricObject(fabric: list, FabricFile): cTile.matrixFileName = csvLoc try: csvFile = RemoveComments( - [i.strip('\n').split(',') for i in open(csvLoc)]) + [i.strip("\n").split(",") for i in open(csvLoc)] + ) cTile.pips = findPipList(csvFile) cTile.pipMuxes_MapSourceToSinks = findPipList( - csvFile, returnDict=True, mapSourceToSinks=True) + csvFile, returnDict=True, mapSourceToSinks=True + ) cTile.pipMuxes_MapSinkToSources = findPipList( - csvFile, returnDict=True, mapSourceToSinks=False) + csvFile, returnDict=True, mapSourceToSinks=False + ) except: raise Exception("CSV File not found.") @@ -773,14 +932,17 @@ def genFabricObject(fabric: list, FabricFile): ports = GetComponentPortsFromFile(wire[1]) # We also want to check whether the component has a clock input # Get all external (routed to top) ports - externalPorts = (GetComponentPortsFromFile( - wire[1], port="external")) + externalPorts = GetComponentPortsFromFile( + wire[1], port="external" + ) for port in externalPorts: # Get port name - PortName = re.sub('\:.*', '', port) + PortName = re.sub("\:.*", "", port) substitutions = {" ": "", "\t": ""} # Strip - PortName = (replace(PortName, substitutions)) - if PortName == "UserCLK": # And if UserCLK is in there then we have a clock input + PortName = replace(PortName, substitutions) + if ( + PortName == "UserCLK" + ): # And if UserCLK is in there then we have a clock input belHasClockInput = True except: @@ -795,22 +957,24 @@ def genFabricObject(fabric: list, FabricFile): outputPorts = [] for port in ports[0]: - nports.append( - prefix + re.sub(" *\(.*\) *", "", str(port))) + nports.append(prefix + re.sub(" *\(.*\) *", "", str(port))) # Also add to distinct input/output lists - inputPorts.append( - prefix + re.sub(" *\(.*\) *", "", str(port))) + inputPorts.append(prefix + re.sub(" *\(.*\) *", "", str(port))) for port in ports[1]: - nports.append( - prefix + re.sub(" *\(.*\) *", "", str(port))) - outputPorts.append( - prefix + re.sub(" *\(.*\) *", "", str(port))) + nports.append(prefix + re.sub(" *\(.*\) *", "", str(port))) + outputPorts.append(prefix + re.sub(" *\(.*\) *", "", str(port))) cTile.belPorts.update(nports) belListWithIO.append( - [wire[1][0:-5:], prefix, inputPorts, outputPorts, belHasClockInput]) - belList.append( - [wire[1][0:-5:], prefix, nports, belHasClockInput]) + [ + wire[1][0:-5:], + prefix, + inputPorts, + outputPorts, + belHasClockInput, + ] + ) + belList.append([wire[1][0:-5:], prefix, nports, belHasClockInput]) elif wire[0] in ["NORTH", "SOUTH", "EAST", "WEST"]: # Wires are added in next pass - this pass generates port lists to be used for wire generation @@ -818,13 +982,29 @@ def genFabricObject(fabric: list, FabricFile): portList.append(wire[1]) if wire[4] != "NULL": portList.append(wire[4]) - wireTextList.append({"direction": wire[0], "source": wire[1], "xoffset": wire[2], - "yoffset": wire[3], "destination": wire[4], "wire-count": wire[5]}) + wireTextList.append( + { + "direction": wire[0], + "source": wire[1], + "xoffset": wire[2], + "yoffset": wire[3], + "destination": wire[4], + "wire-count": wire[5], + } + ) # We just treat JUMPs as normal wires - however they're only on one tile so we can add them directly elif wire[0] == "JUMP": if "NULL" not in wire: - wires.append({"direction": wire[0], "source": wire[1], "xoffset": wire[2], - "yoffset": wire[3], "destination": wire[4], "wire-count": wire[5]}) + wires.append( + { + "direction": wire[0], + "source": wire[1], + "xoffset": wire[2], + "yoffset": wire[3], + "destination": wire[4], + "wire-count": wire[5], + } + ) cTile.wires = wires cTile.x = j # cTile.y = archFabric.height - i -1 @@ -850,8 +1030,13 @@ def genFabricObject(fabric: list, FabricFile): yOffset = int(wire["yoffset"]) wireCount = int(wire["wire-count"]) destinationTile = archFabric.getTileByCoords( - tile.x + xOffset, tile.y + yOffset) - if abs(xOffset) <= 1 and abs(yOffset) <= 1 and not ("NULL" in wire.values()): + tile.x + xOffset, tile.y + yOffset + ) + if ( + abs(xOffset) <= 1 + and abs(yOffset) <= 1 + and not ("NULL" in wire.values()) + ): wires.append(wire) portMap[destinationTile].remove(wire["destination"]) portMap[tile].remove(wire["source"]) @@ -860,172 +1045,225 @@ def genFabricObject(fabric: list, FabricFile): if xOffset != 0: # If we're moving in the x axis if xOffset > 1: cTile = archFabric.getTileByCoords( - tile.x + 1, tile.y + yOffset) # destination tile - for i in range(wireCount*abs(xOffset)): + tile.x + 1, tile.y + yOffset + ) # destination tile + for i in range(wireCount * abs(xOffset)): if i < wireCount: - cascaded_i = i + \ - wireCount * \ - (abs(xOffset)-1) + cascaded_i = i + wireCount * (abs(xOffset) - 1) else: cascaded_i = i - wireCount - tempAtomicWires.append({"direction": "JUMP", - "source": wire["destination"] + str(i), - "xoffset": '0', - "yoffset": '0', - "destination": wire["source"] + str(i), - "sourceTile": tile.genTileLoc(), - "destTile": tile.genTileLoc()}) - tempAtomicWires.append({"direction": wire["direction"], - "source": wire["source"] + str(i), - "xoffset": '1', - "yoffset": wire["yoffset"], - "destination": wire["destination"] + str(cascaded_i), - "sourceTile": tile.genTileLoc(), - "destTile": cTile.genTileLoc()}) # Add atomic wire names + tempAtomicWires.append( + { + "direction": "JUMP", + "source": wire["destination"] + str(i), + "xoffset": "0", + "yoffset": "0", + "destination": wire["source"] + str(i), + "sourceTile": tile.genTileLoc(), + "destTile": tile.genTileLoc(), + } + ) + tempAtomicWires.append( + { + "direction": wire["direction"], + "source": wire["source"] + str(i), + "xoffset": "1", + "yoffset": wire["yoffset"], + "destination": wire["destination"] + + str(cascaded_i), + "sourceTile": tile.genTileLoc(), + "destTile": cTile.genTileLoc(), + } + ) # Add atomic wire names portMap[cTile].remove(wire["destination"]) portMap[tile].remove(wire["source"]) elif xOffset < -1: cTile = archFabric.getTileByCoords( - tile.x - 1, tile.y + yOffset) # destination tile - for i in range(wireCount*abs(xOffset)): + tile.x - 1, tile.y + yOffset + ) # destination tile + for i in range(wireCount * abs(xOffset)): if i < wireCount: - cascaded_i = i + \ - wireCount * \ - (abs(xOffset)-1) + cascaded_i = i + wireCount * (abs(xOffset) - 1) else: cascaded_i = i - wireCount - tempAtomicWires.append({"direction": "JUMP", - "source": wire["destination"] + str(i), - "xoffset": '0', - "yoffset": '0', - "destination": wire["source"] + str(i), - "sourceTile": tile.genTileLoc(), - "destTile": tile.genTileLoc()}) - tempAtomicWires.append({"direction": wire["direction"], - "source": wire["source"] + str(i), - "xoffset": '-1', - "yoffset": wire["yoffset"], - "destination": wire["destination"] + str(cascaded_i), - "sourceTile": tile.genTileLoc(), - "destTile": cTile.genTileLoc()}) # Add atomic wire names + tempAtomicWires.append( + { + "direction": "JUMP", + "source": wire["destination"] + str(i), + "xoffset": "0", + "yoffset": "0", + "destination": wire["source"] + str(i), + "sourceTile": tile.genTileLoc(), + "destTile": tile.genTileLoc(), + } + ) + tempAtomicWires.append( + { + "direction": wire["direction"], + "source": wire["source"] + str(i), + "xoffset": "-1", + "yoffset": wire["yoffset"], + "destination": wire["destination"] + + str(cascaded_i), + "sourceTile": tile.genTileLoc(), + "destTile": cTile.genTileLoc(), + } + ) # Add atomic wire names portMap[cTile].remove(wire["destination"]) portMap[tile].remove(wire["source"]) elif yOffset != 0: # If we're moving in the y axis if yOffset > 1: cTile = archFabric.getTileByCoords( - tile.x + xOffset, tile.y + 1) # destination tile - for i in range(wireCount*abs(yOffset)): + tile.x + xOffset, tile.y + 1 + ) # destination tile + for i in range(wireCount * abs(yOffset)): if i < wireCount: - cascaded_i = i + \ - wireCount * \ - (abs(yOffset)-1) + cascaded_i = i + wireCount * (abs(yOffset) - 1) else: cascaded_i = i - wireCount - tempAtomicWires.append({"direction": "JUMP", - "source": wire["destination"] + str(i), - "xoffset": '0', - "yoffset": '0', - "destination": wire["source"] + str(i), - "sourceTile": tile.genTileLoc(), - "destTile": tile.genTileLoc()}) - tempAtomicWires.append({"direction": wire["direction"], - "source": wire["source"] + str(i), - "xoffset": wire["xoffset"], - "yoffset": '1', - "destination": wire["destination"] + str(cascaded_i), - "sourceTile": tile.genTileLoc(), - "destTile": cTile.genTileLoc()}) # Add atomic wire names + tempAtomicWires.append( + { + "direction": "JUMP", + "source": wire["destination"] + str(i), + "xoffset": "0", + "yoffset": "0", + "destination": wire["source"] + str(i), + "sourceTile": tile.genTileLoc(), + "destTile": tile.genTileLoc(), + } + ) + tempAtomicWires.append( + { + "direction": wire["direction"], + "source": wire["source"] + str(i), + "xoffset": wire["xoffset"], + "yoffset": "1", + "destination": wire["destination"] + + str(cascaded_i), + "sourceTile": tile.genTileLoc(), + "destTile": cTile.genTileLoc(), + } + ) # Add atomic wire names portMap[cTile].remove(wire["destination"]) portMap[tile].remove(wire["source"]) elif yOffset < -1: cTile = archFabric.getTileByCoords( - tile.x + xOffset, tile.y - 1) # destination tile - for i in range(wireCount*abs(yOffset)): + tile.x + xOffset, tile.y - 1 + ) # destination tile + for i in range(wireCount * abs(yOffset)): if i < wireCount: - cascaded_i = i + \ - wireCount * \ - (abs(yOffset)-1) + cascaded_i = i + wireCount * (abs(yOffset) - 1) else: cascaded_i = i - wireCount - tempAtomicWires.append({"direction": "JUMP", - "source": wire["destination"] + str(i), - "xoffset": '0', - "yoffset": '0', - "destination": wire["source"] + str(i), - "sourceTile": tile.genTileLoc(), - "destTile": tile.genTileLoc()}) - tempAtomicWires.append({"direction": wire["direction"], - "source": wire["source"] + str(i), - "xoffset": wire["xoffset"], - "yoffset": '-1', - "destination": wire["destination"] + str(cascaded_i), - "sourceTile": tile.genTileLoc(), - "destTile": cTile.genTileLoc()}) # Add atomic wire names + tempAtomicWires.append( + { + "direction": "JUMP", + "source": wire["destination"] + str(i), + "xoffset": "0", + "yoffset": "0", + "destination": wire["source"] + str(i), + "sourceTile": tile.genTileLoc(), + "destTile": tile.genTileLoc(), + } + ) + tempAtomicWires.append( + { + "direction": wire["direction"], + "source": wire["source"] + str(i), + "xoffset": wire["xoffset"], + "yoffset": "-1", + "destination": wire["destination"] + + str(cascaded_i), + "sourceTile": tile.genTileLoc(), + "destTile": cTile.genTileLoc(), + } + ) # Add atomic wire names portMap[cTile].remove(wire["destination"]) portMap[tile].remove(wire["source"]) elif wire["source"] != "NULL" and wire["destination"] == "NULL": source_wire_name = wire["source"] - if source_wire_name == 'Co': - dest_wire_name = 'Ci' - elif source_wire_name[1] == '2' and source_wire_name[-1] == 'b': + if source_wire_name == "Co": + dest_wire_name = "Ci" + elif source_wire_name[1] == "2" and source_wire_name[-1] == "b": dest_wire_name = wire["source"].replace("BEGb", "END") - elif source_wire_name[1] == '2' and source_wire_name[-1] != 'b': + elif source_wire_name[1] == "2" and source_wire_name[-1] != "b": dest_wire_name = wire["source"].replace("BEG", "MID") else: dest_wire_name = wire["source"].replace("BEG", "END") if xOffset != 0: # If we're moving in the x axis if xOffset > 0: cTile = archFabric.getTileByCoords( - tile.x + 1, tile.y + yOffset) # destination tile - for i in range(wireCount*abs(xOffset)): - tempAtomicWires.append({"direction": wire["direction"], - "source": wire["source"] + str(i), - "xoffset": '1', "yoffset": wire["yoffset"], - "destination": dest_wire_name + str(i), - "sourceTile": tile.genTileLoc(), - "destTile": cTile.genTileLoc()}) # Add atomic wire names + tile.x + 1, tile.y + yOffset + ) # destination tile + for i in range(wireCount * abs(xOffset)): + tempAtomicWires.append( + { + "direction": wire["direction"], + "source": wire["source"] + str(i), + "xoffset": "1", + "yoffset": wire["yoffset"], + "destination": dest_wire_name + str(i), + "sourceTile": tile.genTileLoc(), + "destTile": cTile.genTileLoc(), + } + ) # Add atomic wire names portMap[cTile].remove(dest_wire_name) portMap[tile].remove(wire["source"]) elif xOffset < 0: cTile = archFabric.getTileByCoords( - tile.x - 1, tile.y + yOffset) # destination tile - for i in range(wireCount*abs(xOffset)): - tempAtomicWires.append({"direction": wire["direction"], - "source": wire["source"] + str(i), - "xoffset": '-1', - "yoffset": wire["yoffset"], - "destination": dest_wire_name + str(i), - "sourceTile": tile.genTileLoc(), - "destTile": cTile.genTileLoc()}) # Add atomic wire names + tile.x - 1, tile.y + yOffset + ) # destination tile + for i in range(wireCount * abs(xOffset)): + tempAtomicWires.append( + { + "direction": wire["direction"], + "source": wire["source"] + str(i), + "xoffset": "-1", + "yoffset": wire["yoffset"], + "destination": dest_wire_name + str(i), + "sourceTile": tile.genTileLoc(), + "destTile": cTile.genTileLoc(), + } + ) # Add atomic wire names portMap[cTile].remove(dest_wire_name) portMap[tile].remove(wire["source"]) elif yOffset != 0: # If we're moving in the y axis if yOffset > 0: cTile = archFabric.getTileByCoords( - tile.x + xOffset, tile.y + 1) # destination tile - for i in range(wireCount*abs(yOffset)): - tempAtomicWires.append({"direction": wire["direction"], - "source": wire["source"] + str(i), - "xoffset": wire["xoffset"], - "yoffset": '1', - "destination": dest_wire_name + str(i), - "sourceTile": tile.genTileLoc(), - "destTile": cTile.genTileLoc()}) # Add atomic wire names + tile.x + xOffset, tile.y + 1 + ) # destination tile + for i in range(wireCount * abs(yOffset)): + tempAtomicWires.append( + { + "direction": wire["direction"], + "source": wire["source"] + str(i), + "xoffset": wire["xoffset"], + "yoffset": "1", + "destination": dest_wire_name + str(i), + "sourceTile": tile.genTileLoc(), + "destTile": cTile.genTileLoc(), + } + ) # Add atomic wire names portMap[cTile].remove(dest_wire_name) portMap[tile].remove(wire["source"]) elif yOffset < 0: cTile = archFabric.getTileByCoords( - tile.x + xOffset, tile.y - 1) # destination tile - for i in range(wireCount*abs(yOffset)): - tempAtomicWires.append({"direction": wire["direction"], - "source": wire["source"] + str(i), - "xoffset": wire["xoffset"], - "yoffset": '-1', - "destination": dest_wire_name + str(i), - "sourceTile": tile.genTileLoc(), - "destTile": cTile.genTileLoc()}) # Add atomic wire names + tile.x + xOffset, tile.y - 1 + ) # destination tile + for i in range(wireCount * abs(yOffset)): + tempAtomicWires.append( + { + "direction": wire["direction"], + "source": wire["source"] + str(i), + "xoffset": wire["xoffset"], + "yoffset": "-1", + "destination": dest_wire_name + str(i), + "sourceTile": tile.genTileLoc(), + "destTile": cTile.genTileLoc(), + } + ) # Add atomic wire names portMap[cTile].remove(dest_wire_name) portMap[tile].remove(wire["source"]) @@ -1038,9 +1276,9 @@ def genFabricObject(fabric: list, FabricFile): def genVerilogTemplate(archObject: FabricModelGen): - templateStr = '// IMPORTANT NOTE: if using VPR, any instantiated BELs with no outputs MUST be instantiated after IO\n' - templateStr += '// This is because VPR auto-generates names for primitives with no outputs, and we assume OutPass BELs\n' - templateStr += '// are the first BELs to be auto-named in our constraints file.\n\n' + templateStr = "// IMPORTANT NOTE: if using VPR, any instantiated BELs with no outputs MUST be instantiated after IO\n" + templateStr += "// This is because VPR auto-generates names for primitives with no outputs, and we assume OutPass BELs\n" + templateStr += "// are the first BELs to be auto-named in our constraints file.\n\n" templateStr += "module template ();\n" for line in archObject.tiles: @@ -1086,5 +1324,5 @@ def genVerilogTemplate(archObject: FabricModelGen): return templateStr -if __name__ == '__main__': +if __name__ == "__main__": print(GetComponentPortsFromFile("MULADD.vhdl", port="external")) diff --git a/geometry_generator/bel_geometry.py b/geometry_generator/bel_geometry.py index 210f1612..32c2ba33 100644 --- a/geometry_generator/bel_geometry.py +++ b/geometry_generator/bel_geometry.py @@ -23,6 +23,7 @@ class BelGeometry: externalPortGeoms (List[PortGeometry]): List of geometries of the external ports of the bel """ + name: str src: str width: int @@ -36,7 +37,6 @@ class BelGeometry: internalPortGeoms: List[PortGeometry] externalPortGeoms: List[PortGeometry] - def __init__(self): self.name = None self.src = None @@ -51,7 +51,6 @@ def __init__(self): self.internalPortGeoms = [] self.externalPortGeoms = [] - def generateGeometry(self, bel: Bel, padding: int) -> None: self.name = bel.name self.src = bel.src @@ -65,11 +64,10 @@ def generateGeometry(self, bel: Bel, padding: int) -> None: maxAmountVerticalPorts = max(internalPortsAmount, externalPortsAmount) self.height = maxAmountVerticalPorts + padding - self.width = 32 # TODO: Deduce width in a meaningful way? + self.width = 32 # TODO: Deduce width in a meaningful way? self.generatePortsGeometry(bel, padding) - def generatePortsGeometry(self, bel: Bel, padding: int) -> None: internalPortX = 0 internalPortY = padding // 2 @@ -82,7 +80,8 @@ def generatePortsGeometry(self, bel: Bel, padding: int) -> None: portName, PortType.BEL, IO.INPUT, - internalPortX, internalPortY + internalPortX, + internalPortY, ) self.internalPortGeoms.append(portGeom) internalPortY += 1 @@ -96,7 +95,8 @@ def generatePortsGeometry(self, bel: Bel, padding: int) -> None: portName, PortType.BEL, IO.OUTPUT, - internalPortX, internalPortY + internalPortX, + internalPortY, ) self.internalPortGeoms.append(portGeom) internalPortY += 1 @@ -112,7 +112,8 @@ def generatePortsGeometry(self, bel: Bel, padding: int) -> None: portName, PortType.BEL, IO.INPUT, - externalPortX, externalPortY + externalPortX, + externalPortY, ) self.externalPortGeoms.append(portGeom) externalPortY += 1 @@ -126,28 +127,29 @@ def generatePortsGeometry(self, bel: Bel, padding: int) -> None: portName, PortType.BEL, IO.OUTPUT, - externalPortX, externalPortY + externalPortX, + externalPortY, ) self.externalPortGeoms.append(portGeom) externalPortY += 1 - def adjustPos(self, relX: int, relY: int) -> None: self.relX = relX self.relY = relY - def saveToCSV(self, writer: csvWriter) -> None: - writer.writerows([ - ["BEL"], - ["Name"] + [self.name], - ["Src"] + [self.src], - ["RelX"] + [str(self.relX)], - ["RelY"] + [str(self.relY)], - ["Width"] + [str(self.width)], - ["Height"] + [str(self.height)], - [] - ]) + writer.writerows( + [ + ["BEL"], + ["Name"] + [self.name], + ["Src"] + [self.src], + ["RelX"] + [str(self.relX)], + ["RelY"] + [str(self.relY)], + ["Width"] + [str(self.width)], + ["Height"] + [str(self.height)], + [], + ] + ) for portGeom in self.internalPortGeoms: portGeom.saveToCSV(writer) diff --git a/geometry_generator/fabric_geometry.py b/geometry_generator/fabric_geometry.py index 8377553e..ec3b0d04 100644 --- a/geometry_generator/fabric_geometry.py +++ b/geometry_generator/fabric_geometry.py @@ -21,8 +21,9 @@ class FabricGeometry: padding (int) : Padding used throughout the geometry, in multiples of the width between wires width (int) : Width of the fabric height (int) : Height of the fabric - + """ + fabric: Fabric tileNames: Set[str] tileGeomMap: Dict[str, TileGeometry] @@ -31,7 +32,6 @@ class FabricGeometry: width: int height: int - def __init__(self, fabric: Fabric, padding: int = 8): self.fabric = fabric self.tileNames = set() @@ -43,18 +43,17 @@ def __init__(self, fabric: Fabric, padding: int = 8): self.generateGeometry() - def generateGeometry(self) -> None: """ Generates the geometric information from the given fabric object - + """ # here, the border attribute is set for tiles that are - # located at a border of the tile. This is done to + # located at a border of the tile. This is done to # ensure no stair-like wires being generated for these tiles. - # The distinction left/right and top/bottom is made, to - # prevent generation of horizontal and vertical stair-like + # The distinction left/right and top/bottom is made, to + # prevent generation of horizontal and vertical stair-like # wires respectively. for i in range(self.fabric.numberOfRows): for j in range(self.fabric.numberOfColumns): @@ -67,12 +66,15 @@ def generateGeometry(self) -> None: self.tileGeomMap[tile.name] = TileGeometry() tileGeom = self.tileGeomMap[tile.name] - northSouth = (i == 0 or i+1 == self.fabric.numberOfRows) - eastWest = (j == 0 or j+1 == self.fabric.numberOfColumns) + northSouth = i == 0 or i + 1 == self.fabric.numberOfRows + eastWest = j == 0 or j + 1 == self.fabric.numberOfColumns - if northSouth and eastWest : tileGeom.border = Border.CORNER - elif northSouth : tileGeom.border = Border.NORTHSOUTH - elif eastWest : tileGeom.border = Border.EASTWEST + if northSouth and eastWest: + tileGeom.border = Border.CORNER + elif northSouth: + tileGeom.border = Border.NORTHSOUTH + elif eastWest: + tileGeom.border = Border.EASTWEST for tileName in self.tileNames: tile = self.fabric.getTileByName(tileName) @@ -137,7 +139,7 @@ def generateGeometry(self) -> None: maxWidthInColumn, maxHeightInRow, maxSmWidthInColumn, - maxSmRelXInColumn + maxSmRelXInColumn, ) for i in range(self.fabric.numberOfRows): @@ -178,7 +180,7 @@ def generateGeometry(self) -> None: self.width = rightMostX self.height = bottomMostY - # this step is for rearranging the switch matrices by setting + # this step is for rearranging the switch matrices by setting # the relX/relY appropriately. This is done to ensure that # all inter-tile wires line up correctly. adjustedTileNames = set() @@ -202,47 +204,56 @@ def generateGeometry(self) -> None: tileGeom = self.tileGeomMap[tileName] tileGeom.generateWires(self.padding) - def saveToCSV(self, fileName: str) -> None: """ - Saves the generated geometric information of the + Saves the generated geometric information of the given fabric to a .csv file that can be imported into the graphical frontend. Args: fileName (str): the name of the csv file - + """ - logger.info(f"Generating geometry csv file for {self.fabric.name} # file name: {fileName}") + logger.info( + f"Generating geometry csv file for {self.fabric.name} # file name: {fileName}" + ) with open(f"{fileName}", "w", newline="", encoding="utf-8") as file: writer = csvWriter(file) - writer.writerows([ - ["PARAMS"], - ["Name"] + [self.fabric.name], - ["Rows"] + [str(self.fabric.numberOfRows)], - ["Columns"] + [str(self.fabric.numberOfColumns)], - ["Width"] + [str(self.width)], - ["Height"] + [str(self.height)], - [] - ]) + writer.writerows( + [ + ["PARAMS"], + ["Name"] + [self.fabric.name], + ["Rows"] + [str(self.fabric.numberOfRows)], + ["Columns"] + [str(self.fabric.numberOfColumns)], + ["Width"] + [str(self.width)], + ["Height"] + [str(self.height)], + [], + ] + ) writer.writerow(["FABRIC_DEF"]) for i in range(self.fabric.numberOfRows): - writer.writerow([tile.name if tile is not None else "Null" for tile in self.fabric.tile[i]]) + writer.writerow( + [ + tile.name if tile is not None else "Null" + for tile in self.fabric.tile[i] + ] + ) writer.writerow([]) writer.writerow(["FABRIC_LOCS"]) for i in range(self.fabric.numberOfRows): - writer.writerow([loc if loc is not None else "Null" for loc in self.tileLocs[i]]) + writer.writerow( + [loc if loc is not None else "Null" for loc in self.tileLocs[i]] + ) writer.writerows([[], []]) for tileName in self.tileNames: tileGeometry = self.tileGeomMap[tileName] tileGeometry.saveToCSV(writer) - def __repr__(self) -> str: geometry = "Respective dimensions of tiles: \n" for i in range(self.fabric.numberOfRows): diff --git a/geometry_generator/geometry_gen.py b/geometry_generator/geometry_gen.py index 9f15a724..24a2ab32 100644 --- a/geometry_generator/geometry_gen.py +++ b/geometry_generator/geometry_gen.py @@ -33,7 +33,6 @@ def __init__(self, fabric: Fabric): self.fabric = fabric self.fabricGeometry = None - def generateGeometry(self, padding: int = 8) -> None: """ Starts generation of the geometry for the given fabric. @@ -41,7 +40,6 @@ def generateGeometry(self, padding: int = 8) -> None: """ self.fabricGeometry = FabricGeometry(self.fabric, padding) - def saveToCSV(self, fileName: str) -> None: """ Saves the generated geometry into a file specified by the given file name. diff --git a/geometry_generator/geometry_obj.py b/geometry_generator/geometry_obj.py index 1985817e..e8b32e68 100644 --- a/geometry_generator/geometry_obj.py +++ b/geometry_generator/geometry_obj.py @@ -10,20 +10,18 @@ class Location: y (int): Y coordinate """ + x: int y: int - def __init__(self, x: int, y: int): self.x = x self.y = y - def __repr__(self) -> str: return f"{self.x}/{self.y}" - class Border(Enum): NORTHSOUTH = "NORTHSOUTH" EASTWEST = "EASTWEST" diff --git a/geometry_generator/port_geometry.py b/geometry_generator/port_geometry.py index e2182098..2429f802 100644 --- a/geometry_generator/port_geometry.py +++ b/geometry_generator/port_geometry.py @@ -9,7 +9,6 @@ class PortType(Enum): BEL = "BEL_PORT" - class PortGeometry: """ A datastruct representing the geometry of a Port @@ -29,6 +28,7 @@ class PortGeometry: relY (int) : Y coordinate of the port, relative to its parent (bel, switch matrix) """ + name: str sourceName: str destName: str @@ -43,7 +43,6 @@ class PortGeometry: nextId = 1 - def __init__(self): self.name = None self.sourceName = None @@ -58,15 +57,16 @@ def __init__(self): self.relX = 0 self.relY = 0 - - def generateGeometry(self, - name: str, - sourceName: str, - destName: str, - type: PortType, - ioDirection: IO, - relX: int, - relY: int) -> None: + def generateGeometry( + self, + name: str, + sourceName: str, + destName: str, + type: PortType, + ioDirection: IO, + relX: int, + relY: int, + ) -> None: self.name = name self.sourceName = sourceName self.destName = destName @@ -75,15 +75,16 @@ def generateGeometry(self, self.relX = relX self.relY = relY - def saveToCSV(self, writer: csvWriter) -> None: - writer.writerows([ - [self.type.value], - ["Name"] + [self.name], - ["Source"] + [self.sourceName], - ["Dest"] + [self.destName], - ["IO"] + [self.ioDirection.value], - ["RelX"] + [str(self.relX)], - ["RelY"] + [str(self.relY)], - [] - ]) + writer.writerows( + [ + [self.type.value], + ["Name"] + [self.name], + ["Source"] + [self.sourceName], + ["Dest"] + [self.destName], + ["IO"] + [self.ioDirection.value], + ["RelX"] + [str(self.relX)], + ["RelY"] + [str(self.relY)], + [], + ] + ) diff --git a/geometry_generator/sm_geometry.py b/geometry_generator/sm_geometry.py index 5fdb6266..9b83ebd8 100644 --- a/geometry_generator/sm_geometry.py +++ b/geometry_generator/sm_geometry.py @@ -35,6 +35,7 @@ class SmGeometry: westPortsRightX (int) : Right most x coord of any west port, reference for stair-wires """ + name: str src: str csv: str @@ -52,7 +53,7 @@ class SmGeometry: southWiresReservedWidth: int eastWiresReservedHeight: int westWiresReservedHeight: int - southPortsTopY: int + southPortsTopY: int westPortsRightX: int def __init__(self): @@ -76,7 +77,6 @@ def __init__(self): self.southPortsTopY = 0 self.westPortsRightX = 0 - def preprocessPorts(self, tileBorder: Border) -> None: """ Ensures that ports are ordered correctly, @@ -95,11 +95,14 @@ def preprocessPorts(self, tileBorder: Border) -> None: if abs(southPort.yOffset) > 1: augmentedPort = Port( southPort.wireDirection, - southPort.sourceName, 0, 1, southPort.destinationName, + southPort.sourceName, + 0, + 1, + southPort.destinationName, southPort.wireCount * abs(southPort.yOffset), southPort.name, southPort.inOut, - southPort.sideOfTile + southPort.sideOfTile, ) augmentedSouthPorts.append(augmentedPort) else: @@ -111,11 +114,14 @@ def preprocessPorts(self, tileBorder: Border) -> None: if abs(northPort.yOffset) > 1: augmentedPort = Port( northPort.wireDirection, - northPort.sourceName, 0, 1, northPort.destinationName, + northPort.sourceName, + 0, + 1, + northPort.destinationName, northPort.wireCount * abs(northPort.yOffset), northPort.name, northPort.inOut, - northPort.sideOfTile + northPort.sideOfTile, ) augmentedNorthPorts.append(augmentedPort) else: @@ -128,11 +134,14 @@ def preprocessPorts(self, tileBorder: Border) -> None: if abs(eastPort.xOffset) > 1: augmentedPort = Port( eastPort.wireDirection, - eastPort.sourceName, 1, 0, eastPort.destinationName, + eastPort.sourceName, + 1, + 0, + eastPort.destinationName, eastPort.wireCount * abs(eastPort.xOffset), eastPort.name, eastPort.inOut, - eastPort.sideOfTile + eastPort.sideOfTile, ) augmentedEastPorts.append(augmentedPort) else: @@ -144,11 +153,14 @@ def preprocessPorts(self, tileBorder: Border) -> None: if abs(westPort.xOffset) > 1: augmentedPort = Port( westPort.wireDirection, - westPort.sourceName, 1, 0, westPort.destinationName, + westPort.sourceName, + 1, + 0, + westPort.destinationName, westPort.wireCount * abs(westPort.xOffset), westPort.name, westPort.inOut, - westPort.sideOfTile + westPort.sideOfTile, ) augmentedWestPorts.append(augmentedPort) else: @@ -182,12 +194,13 @@ def preprocessPorts(self, tileBorder: Border) -> None: mergedPort = Port( Direction.JUMP, firstPort.sourceName, - 0, 0, + 0, + 0, firstPort.destinationName, firstPort.wireCount, firstPortName, IO.INOUT, - firstPort.sideOfTile + firstPort.sideOfTile, ) mergedJumpPorts.append(mergedPort) del portNameMap[firstPortName] @@ -202,14 +215,16 @@ def preprocessPorts(self, tileBorder: Border) -> None: self.jumpPorts = mergedJumpPorts - - def generateGeometry(self, tile: Tile, tileBorder: Border, - belGeoms: List[BelGeometry], padding: int) -> None: + def generateGeometry( + self, tile: Tile, tileBorder: Border, belGeoms: List[BelGeometry], padding: int + ) -> None: self.name = tile.name + "_switch_matrix" self.src = tile.filePath + "/" + self.name + ".v" self.csv = tile.filePath + "/" + self.name + ".csv" - self.jumpPorts = [port for port in tile.portsInfo if port.wireDirection == Direction.JUMP] + self.jumpPorts = [ + port for port in tile.portsInfo if port.wireDirection == Direction.JUMP + ] self.northPorts = tile.getNorthSidePorts() self.southPorts = tile.getSouthSidePorts() self.eastPorts = tile.getEastSidePorts() @@ -223,12 +238,23 @@ def generateGeometry(self, tile: Tile, tileBorder: Border, westWires = sum([port.wireCount for port in self.westPorts]) jumpWires = sum([port.wireCount for port in self.jumpPorts]) - self.northWiresReservedWidth = sum([abs(port.yOffset) * port.wireCount for port in self.northPorts]) - self.southWiresReservedWidth = sum([abs(port.yOffset) * port.wireCount for port in self.southPorts]) - self.eastWiresReservedHeight = sum([abs(port.xOffset) * port.wireCount for port in self.eastPorts]) - self.westWiresReservedHeight = sum([abs(port.xOffset) * port.wireCount for port in self.westPorts]) - - self.relX = max(self.northWiresReservedWidth, self.southWiresReservedWidth) + 2 * padding + self.northWiresReservedWidth = sum( + [abs(port.yOffset) * port.wireCount for port in self.northPorts] + ) + self.southWiresReservedWidth = sum( + [abs(port.yOffset) * port.wireCount for port in self.southPorts] + ) + self.eastWiresReservedHeight = sum( + [abs(port.xOffset) * port.wireCount for port in self.eastPorts] + ) + self.westWiresReservedHeight = sum( + [abs(port.xOffset) * port.wireCount for port in self.westPorts] + ) + + self.relX = ( + max(self.northWiresReservedWidth, self.southWiresReservedWidth) + + 2 * padding + ) self.relY = padding # These gaps are for the stair-like wires, @@ -237,13 +263,25 @@ def generateGeometry(self, tile: Tile, tileBorder: Border, if tileBorder == Border.NORTHSOUTH or tileBorder == Border.CORNER: portsGapWest = 0 else: - portsGapWest = sum([port.wireCount for port in (self.northPorts + self.southPorts) if abs(port.yOffset) > 1]) + portsGapWest = sum( + [ + port.wireCount + for port in (self.northPorts + self.southPorts) + if abs(port.yOffset) > 1 + ] + ) portsGapWest += padding if tileBorder == Border.EASTWEST or tileBorder == Border.CORNER: portsGapSouth = 0 else: - portsGapSouth = sum([port.wireCount for port in (self.eastPorts + self.westPorts) if abs(port.xOffset) > 1]) + portsGapSouth = sum( + [ + port.wireCount + for port in (self.eastPorts + self.westPorts) + if abs(port.xOffset) > 1 + ] + ) portsGapSouth += padding belsHeightTotal = sum([belGeom.height for belGeom in belGeoms]) @@ -252,12 +290,18 @@ def generateGeometry(self, tile: Tile, tileBorder: Border, belsReservedSpace = belsHeightTotal + belsPaddingTotal self.width = max(eastWires + westWires + portsGapSouth, jumpWires) + 2 * padding - self.height = max(southWires + northWires + portsGapWest + 2 * padding, belsReservedSpace) + self.height = max( + southWires + northWires + portsGapWest + 2 * padding, belsReservedSpace + ) self.generatePortsGeometry(padding) - self.southPortsTopY = min([geom.relY for geom in self.portGeoms if geom.sideOfTile == Side.SOUTH] + [self.height]) - self.westPortsRightX = max([geom.relX for geom in self.portGeoms if geom.sideOfTile == Side.WEST] + [0]) - + self.southPortsTopY = min( + [geom.relY for geom in self.portGeoms if geom.sideOfTile == Side.SOUTH] + + [self.height] + ) + self.westPortsRightX = max( + [geom.relX for geom in self.portGeoms if geom.sideOfTile == Side.WEST] + [0] + ) def generatePortsGeometry(self, padding: int) -> None: jumpPortX = padding @@ -271,7 +315,8 @@ def generatePortsGeometry(self, padding: int) -> None: f"{port.destinationName}{i}", PortType.JUMP, port.inOut, - jumpPortX, jumpPortY + jumpPortX, + jumpPortY, ) self.portGeoms.append(portGeom) jumpPortX += 1 @@ -287,7 +332,8 @@ def generatePortsGeometry(self, padding: int) -> None: f"{port.destinationName}{i}", PortType.SWITCH_MATRIX, port.inOut, - northPortX, northPortY + northPortX, + northPortY, ) portGeom.sideOfTile = port.sideOfTile portGeom.offset = port.yOffset @@ -297,7 +343,7 @@ def generatePortsGeometry(self, padding: int) -> None: self.portGeoms.append(portGeom) northPortY += 1 - PortGeometry.nextId += 1 + PortGeometry.nextId += 1 southPortX = 0 southPortY = self.height - padding @@ -310,7 +356,8 @@ def generatePortsGeometry(self, padding: int) -> None: f"{port.destinationName}{i}", PortType.SWITCH_MATRIX, port.inOut, - southPortX, southPortY + southPortX, + southPortY, ) portGeom.sideOfTile = port.sideOfTile portGeom.offset = port.yOffset @@ -320,7 +367,7 @@ def generatePortsGeometry(self, padding: int) -> None: self.portGeoms.append(portGeom) southPortY -= 1 - PortGeometry.nextId += 1 + PortGeometry.nextId += 1 eastPortX = self.width - padding eastPortY = self.height @@ -333,8 +380,9 @@ def generatePortsGeometry(self, padding: int) -> None: f"{port.destinationName}{i}", PortType.SWITCH_MATRIX, port.inOut, - eastPortX, eastPortY - ) + eastPortX, + eastPortY, + ) portGeom.sideOfTile = port.sideOfTile portGeom.offset = port.xOffset portGeom.wireDirection = port.wireDirection @@ -343,7 +391,7 @@ def generatePortsGeometry(self, padding: int) -> None: self.portGeoms.append(portGeom) eastPortX -= 1 - PortGeometry.nextId += 1 + PortGeometry.nextId += 1 westPortX = padding westPortY = self.height @@ -356,7 +404,8 @@ def generatePortsGeometry(self, padding: int) -> None: f"{port.destinationName}{i}", PortType.SWITCH_MATRIX, port.inOut, - westPortX, westPortY + westPortX, + westPortY, ) portGeom.sideOfTile = port.sideOfTile portGeom.offset = port.xOffset @@ -368,7 +417,6 @@ def generatePortsGeometry(self, padding: int) -> None: westPortX += 1 PortGeometry.nextId += 1 - def generateBelPorts(self, belGeomList: List[BelGeometry]) -> None: for belGeom in belGeomList: for belPortGeom in belGeom.internalPortGeoms: @@ -382,24 +430,25 @@ def generateBelPorts(self, belGeomList: List[BelGeometry]) -> None: belPortGeom.destName, PortType.SWITCH_MATRIX, belPortGeom.ioDirection, - portX, portY + portX, + portY, ) self.portGeoms.append(portGeom) - def saveToCSV(self, writer: csvWriter) -> None: - writer.writerows([ - ["SWITCH_MATRIX"], - ["Name"] + [self.name], - ["Src"] + [self.src], - ["Csv"] + [self.csv], - ["RelX"] + [str(self.relX)], - ["RelY"] + [str(self.relY)], - ["Width"] + [str(self.width)], - ["Height"] + [str(self.height)], - [] - ]) + writer.writerows( + [ + ["SWITCH_MATRIX"], + ["Name"] + [self.name], + ["Src"] + [self.src], + ["Csv"] + [self.csv], + ["RelX"] + [str(self.relX)], + ["RelY"] + [str(self.relY)], + ["Width"] + [str(self.width)], + ["Height"] + [str(self.height)], + [], + ] + ) for portGeom in self.portGeoms: portGeom.saveToCSV(writer) - \ No newline at end of file diff --git a/geometry_generator/tile_geometry.py b/geometry_generator/tile_geometry.py index c0a47efd..118e5231 100644 --- a/geometry_generator/tile_geometry.py +++ b/geometry_generator/tile_geometry.py @@ -21,8 +21,9 @@ class TileGeometry: belGeomList (List[BelGeometry]) : List of the geometries of the tiles bels wireGeomList (List[WireGeometry]): List of the geometries of the tiles wires stairWiresList (List[StairWires]) : List of the stair-like wires of the tile - + """ + name: str width: int height: int @@ -31,7 +32,6 @@ class TileGeometry: belGeomList: List[BelGeometry] wireGeomList: List[WireGeometry] stairWiresList: List[StairWires] - def __init__(self): self.name = None @@ -43,42 +43,46 @@ def __init__(self): self.wireGeomList = [] self.stairWiresList = [] - def generateGeometry(self, tile: Tile, padding: int) -> None: self.name = tile.name - + for bel in tile.bels: belGeom = BelGeometry() belGeom.generateGeometry(bel, padding) - self.belGeomList.append(belGeom) - + self.belGeomList.append(belGeom) + self.smGeometry.generateGeometry(tile, self.border, self.belGeomList, padding) maxBelWidth = max([belGeom.width for belGeom in self.belGeomList] + [0]) - self.width = (self.smGeometry.relX - + self.smGeometry.width - + 2 * padding + maxBelWidth) - - self.height = (self.smGeometry.relY - + self.smGeometry.height - + max(self.smGeometry.eastWiresReservedHeight, - self.smGeometry.westWiresReservedHeight) - + 2 * padding) - - - def adjustDimensions(self, maxWidthInColumn: int, - maxHeightInRow: int, - maxSmWidthInColumn: int, - maxSmRelXInColumn: int) -> None: - + self.width = ( + self.smGeometry.relX + self.smGeometry.width + 2 * padding + maxBelWidth + ) + + self.height = ( + self.smGeometry.relY + + self.smGeometry.height + + max( + self.smGeometry.eastWiresReservedHeight, + self.smGeometry.westWiresReservedHeight, + ) + + 2 * padding + ) + + def adjustDimensions( + self, + maxWidthInColumn: int, + maxHeightInRow: int, + maxSmWidthInColumn: int, + maxSmRelXInColumn: int, + ) -> None: + self.width = maxWidthInColumn self.height = maxHeightInRow - self.smGeometry.width = maxSmWidthInColumn # TODO: needed? + self.smGeometry.width = maxSmWidthInColumn # TODO: needed? self.smGeometry.relX = maxSmRelXInColumn # TODO: - #dim.smWidth = dim.smWidth*2 if dim.smWidth*2 < maxSmWidths[j] else dim.smWidth - + # dim.smWidth = dim.smWidth*2 if dim.smWidth*2 < maxSmWidths[j] else dim.smWidth def adjustSmPos(self, lowestSmYInRow: int, padding: int) -> None: """ @@ -86,26 +90,25 @@ def adjustSmPos(self, lowestSmYInRow: int, padding: int) -> None: the lowest Y coordinate of any switch matrix in the same row for reference. After this step is completed for all switch matrices, their southern - edge will be on the same Y coordinate, allowing + edge will be on the same Y coordinate, allowing for easier inter-tile routing. - + """ currentSmY = self.smGeometry.relY + self.smGeometry.height - additionalOffset = (lowestSmYInRow - currentSmY) + additionalOffset = lowestSmYInRow - currentSmY self.smGeometry.relY += additionalOffset - + self.setBelPositions(padding) - # Bel positions are set by now, so the bel ports + # Bel positions are set by now, so the bel ports # of the switch matrix can be generated now. self.smGeometry.generateBelPorts(self.belGeomList) - def setBelPositions(self, padding: int) -> None: """ The position of the switch matrix is final when this is called, thus bel positions can be set. - + """ belPadding = padding // 2 belX = self.smGeometry.relX + self.smGeometry.width + padding @@ -115,48 +118,46 @@ def setBelPositions(self, padding: int) -> None: belY += belGeom.height belY += belPadding - def generateWires(self, padding: int) -> None: self.generateBelWires() self.generateDirectWires(padding) - # This adjustment is done to ensure that wires - # in tiles with less/more direct north than - # south wires (and the same with east/west) + # This adjustment is done to ensure that wires + # in tiles with less/more direct north than + # south wires (and the same with east/west) # align, such as in some super-tiles. - self.northMiddleX = min(self.northMiddleX, self.southMiddleX) - self.southMiddleX = min(self.northMiddleX, self.southMiddleX) + self.northMiddleX = min(self.northMiddleX, self.southMiddleX) + self.southMiddleX = min(self.northMiddleX, self.southMiddleX) self.eastMiddleY = max(self.eastMiddleY, self.westMiddleY) self.westMiddleY = max(self.eastMiddleY, self.westMiddleY) self.generateIndirectWires(padding) - def generateBelWires(self) -> None: """ Generates the wires between the switch matrix and its bels. - + """ for belGeom in self.belGeomList: - belToSmDistanceX = belGeom.relX - (self.smGeometry.relX + self.smGeometry.width) + belToSmDistanceX = belGeom.relX - ( + self.smGeometry.relX + self.smGeometry.width + ) for portGeom in belGeom.internalPortGeoms: wireName = f"{portGeom.sourceName} ⟶ {portGeom.destName}" wireGeom = WireGeometry(wireName) start = Location( - portGeom.relX + belGeom.relX, - portGeom.relY + belGeom.relY + portGeom.relX + belGeom.relX, portGeom.relY + belGeom.relY ) end = Location( portGeom.relX + belGeom.relX - belToSmDistanceX, - portGeom.relY + belGeom.relY + portGeom.relY + belGeom.relY, ) wireGeom.addPathLoc(start) wireGeom.addPathLoc(end) self.wireGeomList.append(wireGeom) - northMiddleX = None southMiddleX = None eastMiddleY = None @@ -167,14 +168,15 @@ def generateDirectWires(self, padding: int) -> None: Generates wires to neigbouring tiles, which are straightforward to generate. - """ + """ self.northMiddleX = self.smGeometry.relX - padding self.southMiddleX = self.smGeometry.relX - padding self.eastMiddleY = self.smGeometry.relY + self.smGeometry.height + padding self.westMiddleY = self.smGeometry.relY + self.smGeometry.height + padding for portGeom in self.smGeometry.portGeoms: - if abs(portGeom.offset) != 1: continue + if abs(portGeom.offset) != 1: + continue wireName = f"{portGeom.sourceName} ⟶ {portGeom.destName}" wireGeom = WireGeometry(wireName) @@ -232,7 +234,7 @@ def generateDirectWires(self, padding: int) -> None: else: raise Exception("port with offset 1 and no tile side!") - + self.wireGeomList.append(wireGeom) currPortGroupId = 0 @@ -246,10 +248,11 @@ def generateIndirectWires(self, padding: int): Generates wires to non-neighbouring tiles. These are not straightforward to generate, as they require a staircase-like shape. - + """ for portGeom in self.smGeometry.portGeoms: - if abs(portGeom.offset) < 2: continue + if abs(portGeom.offset) < 2: + continue if portGeom.sideOfTile == Side.NORTH: self.indirectNorthSideWire(portGeom, padding) @@ -262,16 +265,17 @@ def generateIndirectWires(self, padding: int): else: raise Exception("port with abs(offset) > 1 and no tile side!") - def indirectNorthSideWire(self, portGeom: PortGeometry, padding: int) -> None: """ generates indirect wires on the north side of the tile, along with the stair-like wires needed. """ - generateNorthSouthStairWire = (self.border != Border.NORTHSOUTH and self.border != Border.CORNER) + generateNorthSouthStairWire = ( + self.border != Border.NORTHSOUTH and self.border != Border.CORNER + ) - # with a new group of ports, there will be the + # with a new group of ports, there will be the # need for a new stair-like wire for that group if generateNorthSouthStairWire and self.currPortGroupId != portGeom.groupId: self.currPortGroupId = portGeom.groupId @@ -290,10 +294,13 @@ def indirectNorthSideWire(self, portGeom: PortGeometry, padding: int) -> None: stairWiresName = f"({portGeom.sourceName} ⟶ {portGeom.destName})" stairWires = StairWires(stairWiresName) stairWires.generateGeometry( - self.northMiddleX - xOffset, + self.northMiddleX - xOffset, self.smGeometry.southPortsTopY + self.smGeometry.relY - padding, - portGeom.offset, portGeom.wireDirection, portGeom.groupWires, - self.width, self.height + portGeom.offset, + portGeom.wireDirection, + portGeom.groupWires, + self.width, + self.height, ) self.stairWiresList.append(stairWires) @@ -303,35 +310,27 @@ def indirectNorthSideWire(self, portGeom: PortGeometry, padding: int) -> None: wireName = f"{portGeom.sourceName} ⟶ {portGeom.destName}" wireGeom = WireGeometry(wireName) - start = Location( - self.northMiddleX, - 0 - ) - middle = Location( - self.northMiddleX, - self.smGeometry.relY + portGeom.relY - ) - end = Location( - self.smGeometry.relX, - self.smGeometry.relY + portGeom.relY - ) + start = Location(self.northMiddleX, 0) + middle = Location(self.northMiddleX, self.smGeometry.relY + portGeom.relY) + end = Location(self.smGeometry.relX, self.smGeometry.relY + portGeom.relY) wireGeom.addPathLoc(start) wireGeom.addPathLoc(middle) wireGeom.addPathLoc(end) self.wireGeomList.append(wireGeom) - self.northMiddleX -= 1 - - + self.northMiddleX -= 1 + def indirectSouthSideWire(self, portGeom: PortGeometry) -> None: """ - In contrast to indirectNorthSideWire(), this method - generates only indirect wires on the south side of + In contrast to indirectNorthSideWire(), this method + generates only indirect wires on the south side of the tile, but no stair-like wires. """ - generateNorthSouthStairWire = (self.border != Border.NORTHSOUTH and self.border != Border.CORNER) + generateNorthSouthStairWire = ( + self.border != Border.NORTHSOUTH and self.border != Border.CORNER + ) - # with a new group of ports, there will be the + # with a new group of ports, there will be the # need for space for the generated stair-like wire if generateNorthSouthStairWire and self.currPortGroupId != portGeom.groupId: self.currPortGroupId = portGeom.groupId @@ -344,41 +343,33 @@ def indirectSouthSideWire(self, portGeom: PortGeometry) -> None: # of the stair-like wire into account. if portGeom.wireDirection == Direction.NORTH: self.queuedAdjustmentLeft = stairReservedWidth - + if portGeom.wireDirection == Direction.SOUTH: self.southMiddleX -= stairReservedWidth self.queuedAdjustmentLeft = 0 wireName = f"{portGeom.sourceName} ⟶ {portGeom.destName}" wireGeom = WireGeometry(wireName) - start = Location( - self.southMiddleX, - self.height - ) - middle = Location( - self.southMiddleX, - self.smGeometry.relY + portGeom.relY - ) - end = Location( - self.smGeometry.relX, - self.smGeometry.relY + portGeom.relY - ) + start = Location(self.southMiddleX, self.height) + middle = Location(self.southMiddleX, self.smGeometry.relY + portGeom.relY) + end = Location(self.smGeometry.relX, self.smGeometry.relY + portGeom.relY) wireGeom.addPathLoc(start) wireGeom.addPathLoc(middle) wireGeom.addPathLoc(end) self.wireGeomList.append(wireGeom) self.southMiddleX -= 1 - def indirectEastSideWire(self, portGeom: PortGeometry, padding: int) -> None: """ generates indirect wires on the east side of the tile, along with the stair-like wires needed. """ - generateEastWestStairWire = (self.border != Border.EASTWEST and self.border != Border.CORNER) + generateEastWestStairWire = ( + self.border != Border.EASTWEST and self.border != Border.CORNER + ) - # with a new group of ports, there will be the + # with a new group of ports, there will be the # need for a new stair-like wire for that group if generateEastWestStairWire and self.currPortGroupId != portGeom.groupId: self.currPortGroupId = portGeom.groupId @@ -397,10 +388,13 @@ def indirectEastSideWire(self, portGeom: PortGeometry, padding: int) -> None: stairWiresName = f"({portGeom.sourceName} ⟶ {portGeom.destName})" stairWires = StairWires(stairWiresName) stairWires.generateGeometry( - self.smGeometry.westPortsRightX + self.smGeometry.relX + padding, + self.smGeometry.westPortsRightX + self.smGeometry.relX + padding, self.eastMiddleY + yOffset, - portGeom.offset, portGeom.wireDirection, portGeom.groupWires, - self.width, self.height + portGeom.offset, + portGeom.wireDirection, + portGeom.groupWires, + self.width, + self.height, ) self.stairWiresList.append(stairWires) @@ -411,34 +405,28 @@ def indirectEastSideWire(self, portGeom: PortGeometry, padding: int) -> None: wireName = f"{portGeom.sourceName} ⟶ {portGeom.destName}" wireGeom = WireGeometry(wireName) start = Location( - self.smGeometry.relX + portGeom.relX, - self.smGeometry.relY + portGeom.relY - ) - middle = Location( - self.smGeometry.relX + portGeom.relX, - self.eastMiddleY - ) - end = Location( - self.width, - self.eastMiddleY + self.smGeometry.relX + portGeom.relX, self.smGeometry.relY + portGeom.relY ) + middle = Location(self.smGeometry.relX + portGeom.relX, self.eastMiddleY) + end = Location(self.width, self.eastMiddleY) wireGeom.addPathLoc(start) wireGeom.addPathLoc(middle) wireGeom.addPathLoc(end) self.wireGeomList.append(wireGeom) - self.eastMiddleY += 1 - + self.eastMiddleY += 1 def indirectWestSideWire(self, portGeom: PortGeometry) -> None: """ - In contrast to indirectEastSideWire(), this method - generates only indirect wires on the south side of + In contrast to indirectEastSideWire(), this method + generates only indirect wires on the south side of the tile, but no stair-like wires. """ - generateEastWestStairWire = (self.border != Border.EASTWEST and self.border != Border.CORNER) + generateEastWestStairWire = ( + self.border != Border.EASTWEST and self.border != Border.CORNER + ) - # with a new group of ports, there will be the + # with a new group of ports, there will be the # need for space for the generated stair-like wire if generateEastWestStairWire and self.currPortGroupId != portGeom.groupId: self.currPortGroupId = portGeom.groupId @@ -451,24 +439,17 @@ def indirectWestSideWire(self, portGeom: PortGeometry) -> None: # of the stair-like wire into account. if portGeom.wireDirection == Direction.EAST: self.queuedAdjustmentBottom = stairReservedHeight - + if portGeom.wireDirection == Direction.WEST: self.westMiddleY += stairReservedHeight self.queuedAdjustmentBottom = 0 wireName = f"{portGeom.sourceName} ⟶ {portGeom.destName}" wireGeom = WireGeometry(wireName) - start = Location( - 0, - self.westMiddleY - ) - middle = Location( - self.smGeometry.relX + portGeom.relX, - self.westMiddleY - ) + start = Location(0, self.westMiddleY) + middle = Location(self.smGeometry.relX + portGeom.relX, self.westMiddleY) end = Location( - self.smGeometry.relX + portGeom.relX, - self.smGeometry.relY + portGeom.relY + self.smGeometry.relX + portGeom.relX, self.smGeometry.relY + portGeom.relY ) wireGeom.addPathLoc(start) wireGeom.addPathLoc(middle) @@ -476,22 +457,23 @@ def indirectWestSideWire(self, portGeom: PortGeometry) -> None: self.wireGeomList.append(wireGeom) self.westMiddleY += 1 - def saveToCSV(self, writer: csvWriter) -> None: - writer.writerows([ - ["TILE"], - ["Name"] + [self.name], - ["Width"] + [str(self.width)], - ["Height"] + [str(self.height)], - [] - ]) + writer.writerows( + [ + ["TILE"], + ["Name"] + [self.name], + ["Width"] + [str(self.width)], + ["Height"] + [str(self.height)], + [], + ] + ) self.smGeometry.saveToCSV(writer) for belGeometry in self.belGeomList: belGeometry.saveToCSV(writer) for wireGeometry in self.wireGeomList: - wireGeometry.saveToCSV(writer) + wireGeometry.saveToCSV(writer) for stairWires in self.stairWiresList: stairWires.saveToCSV(writer) diff --git a/geometry_generator/wire_geometry.py b/geometry_generator/wire_geometry.py index 10ce7d14..ae366d7c 100644 --- a/geometry_generator/wire_geometry.py +++ b/geometry_generator/wire_geometry.py @@ -13,33 +13,26 @@ class WireGeometry: path List[Location] : Path of the wire """ + name: str path: List[Location] - def __init__(self, name: str): self.name = name self.path = [] - def addPathLoc(self, pathLoc: Location) -> None: self.path.append(pathLoc) - def saveToCSV(self, writer: csvWriter) -> None: - writer.writerows([ - ["WIRE"], - ["Name"] + [self.name] - ]) + writer.writerows([["WIRE"], ["Name"] + [self.name]]) for pathPoint in self.path: - writer.writerows([ - ["RelX"] + [str(pathPoint.x)], - ["RelY"] + [str(pathPoint.y)] - ]) + writer.writerows( + [["RelX"] + [str(pathPoint.x)], ["RelY"] + [str(pathPoint.y)]] + ) writer.writerow([]) - class StairWires: """ A datastruct representing a stair-like collection of wires @@ -48,24 +41,24 @@ class StairWires: name (str) : Name of the structure refX (int) : Reference point x coord of the stair structure refY (int) : Reference point y coord of the stair structure - offset (int) : Offset of the wires - direction (Direction) : Direction of the wires + offset (int) : Offset of the wires + direction (Direction) : Direction of the wires groupWires (int) : Amount of wires of a single "strand" tileWidth (int) : Width of the tile containing the wires tileHeight (int) : Height of the tile containing the wires - wireGeoms List[WireGeometry] : List of the wires geometries + wireGeoms List[WireGeometry] : List of the wires geometries The (refX, refY) point refers to the following location(s) of the stair-like structure: - - @ @ @ @@ @@ @@ - @ @ @ @@ @@ @@ - @ @ @ @@ @@ @@ - @ @ @ @@ @@ @@ - @ @ @ @@ @@ @@ - @ @ @@@@@@@@ @@@@@@@@ @@ @@ - @ @ @@ @ @@ @@ - @ @@@@@@@% @@ @ @@@@@@@@ @@ - @ @% @@ @ @@ @@ + + @ @ @ @@ @@ @@ + @ @ @ @@ @@ @@ + @ @ @ @@ @@ @@ + @ @ @ @@ @@ @@ + @ @ @ @@ @@ @@ + @ @ @@@@@@@@ @@@@@@@@ @@ @@ + @ @ @@ @ @@ @@ + @ @@@@@@@% @@ @ @@@@@@@@ @@ + @ @% @@ @ @@ @@ --> @@@@@@%@ @% @@ @ @# #@@@@@@@@ <-- (refX, refY) %@ @% @@ @ @# #@ %@ @% @@ @ @# #@ @@ -73,23 +66,23 @@ class StairWires: %@ @% @@ @ @# #@ %@ @@ @@ @ @# #@ - Depending on the orientation of the structure. Rotate right by 90° to get + Depending on the orientation of the structure. Rotate right by 90° to get the image for the corresponding left-right stair-ike wire structure. The right stair-like structure represents a north stair, the left one represents a south stair (These being the directions of the wires). """ + name: str refX: int refY: int offset: int direction: Direction groupWires: int - tileWidth: int + tileWidth: int tileHeight: int wireGeoms: List[WireGeometry] - def __init__(self, name: str): self.name = name self.refX = 0 @@ -101,10 +94,16 @@ def __init__(self, name: str): self.tileHeight = 0 self.wireGeoms = [] - - def generateGeometry(self, refX: int, refY: int, - offset: int, direction: Direction, groupWires: int, - tileWidth: int, tileHeight: int) -> None: + def generateGeometry( + self, + refX: int, + refY: int, + offset: int, + direction: Direction, + groupWires: int, + tileWidth: int, + tileHeight: int, + ) -> None: self.refX = refX self.refY = refY self.offset = offset @@ -124,56 +123,53 @@ def generateGeometry(self, refX: int, refY: int, else: raise Exception("Invalid direction!") - def generateNorthStairWires(self) -> None: totalWires = self.groupWires * (abs(self.offset) - 1) for i in range(totalWires): - wireGeom = WireGeometry(f"{self.name} #{i}") - start = Location(self.refX, 0) + wireGeom = WireGeometry(f"{self.name} #{i}") + start = Location(self.refX, 0) nextToStart = Location(self.refX, self.refY) - nextToEnd = Location(self.refX - self.groupWires, self.refY) - end = Location(self.refX - self.groupWires, self.tileHeight) + nextToEnd = Location(self.refX - self.groupWires, self.refY) + end = Location(self.refX - self.groupWires, self.tileHeight) wireGeom.addPathLoc(start) wireGeom.addPathLoc(nextToStart) wireGeom.addPathLoc(nextToEnd) wireGeom.addPathLoc(end) self.wireGeoms.append(wireGeom) - + self.refX -= 1 self.refY -= 1 - def generateSouthStairWires(self) -> None: totalWires = self.groupWires * (abs(self.offset) - 1) for i in range(totalWires): - wireGeom = WireGeometry(f"{self.name} #{i}") - start = Location(self.refX, 0) + wireGeom = WireGeometry(f"{self.name} #{i}") + start = Location(self.refX, 0) nextToStart = Location(self.refX, self.refY) - nextToEnd = Location(self.refX + self.groupWires, self.refY) - end = Location(self.refX + self.groupWires, self.tileHeight) + nextToEnd = Location(self.refX + self.groupWires, self.refY) + end = Location(self.refX + self.groupWires, self.tileHeight) wireGeom.addPathLoc(start) wireGeom.addPathLoc(nextToStart) wireGeom.addPathLoc(nextToEnd) wireGeom.addPathLoc(end) self.wireGeoms.append(wireGeom) - + self.refX += 1 self.refY -= 1 - def generateEastStairWires(self) -> None: totalWires = self.groupWires * (abs(self.offset) - 1) for i in range(totalWires): - wireGeom = WireGeometry(f"{self.name} #{i}") - start = Location(self.tileWidth, self.refY) + wireGeom = WireGeometry(f"{self.name} #{i}") + start = Location(self.tileWidth, self.refY) nextToStart = Location(self.refX, self.refY) - nextToEnd = Location(self.refX, self.refY + self.groupWires) - end = Location(0, self.refY + self.groupWires) + nextToEnd = Location(self.refX, self.refY + self.groupWires) + end = Location(0, self.refY + self.groupWires) wireGeom.addPathLoc(start) wireGeom.addPathLoc(nextToStart) @@ -184,16 +180,15 @@ def generateEastStairWires(self) -> None: self.refX += 1 self.refY += 1 - def generateWestStairWires(self) -> None: totalWires = self.groupWires * (abs(self.offset) - 1) for i in range(totalWires): - wireGeom = WireGeometry(f"{self.name} #{i}") - start = Location(self.tileWidth, self.refY) + wireGeom = WireGeometry(f"{self.name} #{i}") + start = Location(self.tileWidth, self.refY) nextToStart = Location(self.refX, self.refY) - nextToEnd = Location(self.refX, self.refY - self.groupWires) - end = Location(0, self.refY - self.groupWires) + nextToEnd = Location(self.refX, self.refY - self.groupWires) + end = Location(0, self.refY - self.groupWires) wireGeom.addPathLoc(start) wireGeom.addPathLoc(nextToStart) @@ -204,7 +199,6 @@ def generateWestStairWires(self) -> None: self.refX += 1 self.refY -= 1 - def saveToCSV(self, writer: csvWriter) -> None: for wireGeom in self.wireGeoms: wireGeom.saveToCSV(writer)