mirror of https://github.com/lnis-uofu/SOFA.git
242 lines
8.6 KiB
Python
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()
|