SOFA/openfpga-physical/python-lib/openfpga_physical/fabric_key_generator.py

242 lines
8.6 KiB
Python

# ##############################################################################
# Tool: OpenFPGA-Physical
# Script: generate_fabric_key.py
# Description : This script cretes a fabric_key.xml file for give size of FPGA
# Currently this script generate pattern which routes configuration chain
# from right top corner to left bottom corner byt traversing horizontally
# in every row of the FPGA grid
################################################################################
'''
File Title
'''
import argparse
import os
import pickle
import xml.etree.ElementTree as ET
from pprint import pprint
from xml.dom import minidom
import svgwrite
from svgwrite.container import Group
from openfpga_physical import fpga_grid_gen
def formatter(prog): return argparse.HelpFormatter(prog, max_help_position=60)
# Mandatory arguments
def parse_argument():
parser = argparse.ArgumentParser(formatter_class=formatter)
parser.add_argument('--design_name')
parser.add_argument('--arch_file', type=str)
parser.add_argument('--layout', type=str)
parser.add_argument('--show_gridIO', action="store_true")
parser.add_argument('--out_file', type=str, default="fabric_key.xml")
parser.add_argument('--release_root', type=str, default="release")
parser.add_argument('--pattern_type', type=str,
choices=['single', 'vertical', 'horizontal'],
default="horizontal")
return parser.parse_args()
def print_grid(grid):
"""
Prints the 2D FPGA grid on console
"""
for row in grid[::-1]:
for y in row:
print(f"{y:^10}", end=" ")
print("")
def _get_val(root, param, default, vars={}):
"""
Parses the startx, starty, repeatx and repeaty variables to integer
"""
val = root.attrib.get(param, str(default))
if val.isnumeric():
return int(val)
else:
val = val.replace("W", "{W}")
val = val.replace("H", "{H}")
return eval(val.format(**vars))
def _set_value(grid, x, y, value):
"""
Sets value in the FPGA grid
"""
try:
grid[y][x] = value
return 1
except:
return 0
def enumerate_grid(root, layout, width, height):
'''
Enumerate FPGA grid
'''
block_grid = [[0 for x in range(width)] for y in range(height)]
# return block_grid
for each in sorted(layout, key=lambda x: int(x.attrib["priority"])):
tag = each.tag
ele_type = each.attrib["type"]
ele = root.find(f".//tile[@name='{ele_type}']")
ele_w, ele_h = (int(ele.attrib.get("width", 1)),
int(ele.attrib.get("height", 1))) if ele else (1, 1)
vars = {"W": width, "H": height}
startx = _get_val(each, "startx", 0, vars)
starty = _get_val(each, "starty", 0, vars)
repx = _get_val(each, "repeatx", ele_w if tag ==
"row" else width, vars)
repy = _get_val(each, "repeaty", ele_h if tag ==
"col" else height, vars)
repx, repy = (ele_w, ele_h) if tag == "fill" else (repx, repy)
repx, repy = (width-1, height-1) if tag == "corners" else (repx, repy)
for x in range(startx, width, repx):
for y in range(starty, height, repy):
_set_value(block_grid, x, y, ele_type)
for i in range(1, ele_w):
_set_value(block_grid, x+1, y, "")
for i in range(1, ele_h):
_set_value(block_grid, x, y+1, "")
return block_grid
def create_vertical_cc(fpga, key):
grid = fpga.grid
width = len(grid[0])
height = len(grid)
print(f"Creating {width-2} configuration chains")
start = 0
for x in range(1,width):
ord_mod = []
# These are bottom up connections
for y in range(0, height):
if y < (height-1):
ord_mod.append(f"sb_{x-1}__{y}_")
if y < (height-2):
if not grid[y+1][x] in [fpga.RIGHT_ARROW, fpga.UP_ARROW]:
ord_mod.append(f"cby_{x-1}__{y+1}_")
if (x == 1):
label = grid[y+1][x-1]
ord_mod.append(f"grid_{label}_left_{x-1}__{y+1}_")
# These are top down connections
for y in range(height-1,-1,-1):
if y < (height-1):
ord_mod.append(f"cbx_{x}__{y}_")
# if x == (width-2):
# ord_mod.append(f"sb_{x}__{y}_")
# if y > 0:
# label = grid[y][x+1]
# ord_mod.append(f"cby_{x}__{y}_")
# ord_mod.append(f"grid_{label}_right_{x+1}__{y}_")
if y == (height-1):
label = grid[y][x]
ord_mod.append(f"grid_{label}_top_{x}__{y}_")
if y < (height-1) and (y > 0):
label = grid[y][x]
if not label in [fpga.RIGHT_ARROW, fpga.UP_ARROW, "EMPTY"]:
label = f"grid_{label}"
ord_mod.append(f"{label}_{x}__{y}_")
label = grid[y][x]
ord_mod.append(f"grid_{label}_bottom_{x}__0_")
# EAdd last chain
if x == width-1:
ord_mod = []
ord_mod.append(f"sb_{width-2}__0_")
for y in range(1, height-1):
ord_mod.append(f"cby_{width-2}__{y}_")
ord_mod.append(f"grid_{grid[y][width-1]}_right_{width-1}__{y}_")
ord_mod.append(f"sb_{width-2}__{y}_")
region = ET.SubElement(key, "region", {'id': str(x-1)})
for i, each in enumerate(ord_mod):
ET.SubElement(region, 'key', {'id': str(start+i), 'alias': each})
start += i+1
def save_fabric_key(key, filename):
with open(filename, "w") as fp:
rough_string = ET.tostring(key, 'utf-8')
reparsed = minidom.parseString(rough_string)
fp.write(reparsed.toprettyxml(indent=" "))
def main():
'''
Main method to execute function
'''
# Parse architecture file and get layput block
args = parse_argument()
arch = ET.parse(args.arch_file)
root = arch.getroot()
layout = root.find(f".//fixed_layout[@name='{args.layout}']")
assert layout, "Specified layput not found in the architecture file"
# Extract height and width paramter and create grid layout
width = int(layout.attrib["width"])
height = int(layout.attrib["height"])
# fpga_grid = enumerate_grid(root, layout, width, height)
grid = fpga_grid_gen(args.design_name, args.arch_file, args.layout, "")
grid.enumerate_grid()
grid.print_grid()
key = ET.Element('fabric_key')
if args.pattern_type == "vertical":
create_vertical_cc(grid, key)
save_fabric_key(key, args.out_file)
RenderSVG(key, args.design_name, args.release_root)
def getGroup(dwg, id):
for ele in dwg.elements:
if ele.get_id() == id:
return ele
def RenderSVG(FKey, DESIGN_NAME, SaveLocation, show_gridIO=False):
PlacementDBKey = pickle.load(open(os.path.join(SaveLocation, "pickle",
f"{DESIGN_NAME}_PlacementDBKey.pickle"), 'rb'))
FPGAShapeVars = pickle.load(open(os.path.join(SaveLocation, "pickle",
f"{DESIGN_NAME}_FPGAShapeVars.pickle"), 'rb'))
dwg = pickle.load(open(os.path.join(SaveLocation, "pickle",
f"{DESIGN_NAME}_dwg.pickle"), 'rb'))
ccffSVGPath = os.path.join(
SaveLocation, "SVG", DESIGN_NAME + '_CCFF_Chain.svg')
DRMarker = dwg.marker(refX="30", refY="30",
viewBox="0 0 120 120",
markerUnits="strokeWidth",
markerWidth="8", markerHeight="10", orient="auto")
DRMarker.add(dwg.path(d="M 0 0 L 60 30 L 0 60 z", fill="blue"))
dwg.defs.add(DRMarker)
# Add Content
dwgMain = getGroup(dwg, "main")
dwgChain = dwgMain.add(Group(id="ffChains"))
for region in FKey.iter("region"):
CCFFChainCenter = []
for eachEle in region:
module = eachEle.attrib["alias"]
if ("io" in module) and (not show_gridIO):
continue
C = PlacementDBKey[module]["center"]
CCFFChainCenter.append(str(C[0]*FPGAShapeVars['CPP']))
CCFFChainCenter.append(str(C[1]*FPGAShapeVars['SC_HEIGHT']))
dwgChain.add(dwg.path(stroke="red", fill="none", stroke_width=3,
marker_mid=DRMarker.get_funciri(),
d=f"M{CCFFChainCenter[0]} {CCFFChainCenter[1]} " + " ".join(CCFFChainCenter)))
dwg.saveas(ccffSVGPath, pretty=True, indent=4)
print(f"SVG file saved as {ccffSVGPath}")
if __name__ == '__main__':
main()