mirror of https://github.com/lnis-uofu/SOFA.git
294 lines
11 KiB
Python
294 lines
11 KiB
Python
# ##############################################################################
|
|
# Tool: OpenFPGA-Physical
|
|
# Script: fpga_grid_gen.py
|
|
################################################################################
|
|
'''
|
|
fpga_grid_gen
|
|
-------------
|
|
|
|
This scripts read the layout section of the VPR architecture file and
|
|
create a 2D matrix of the FPGA grid.
|
|
'''
|
|
|
|
import argparse
|
|
import logging
|
|
import xml.etree.ElementTree as ET
|
|
from spydrnet_physical.util.openfpga_arch import OpenFPGA_Arch
|
|
|
|
logger = logging.getLogger('spydrnet_logs')
|
|
def formatter(prog): return argparse.HelpFormatter(prog, max_help_position=60)
|
|
|
|
|
|
help_msg = {
|
|
"design_name": "Design name, Generally in FPGAxxxx_xxxx format"
|
|
}
|
|
|
|
|
|
UP_ARROW = chr(8593)
|
|
RIGHT_ARROW = chr(8594)
|
|
|
|
|
|
def main() -> None:
|
|
"""
|
|
Execute when this file called as a script
|
|
"""
|
|
args = parse_argument()
|
|
grid = fpga_grid_gen(args.design_name, args.arch_file,
|
|
args.layout, args.release_root)
|
|
grid.enumerate_grid()
|
|
grid.print_grid()
|
|
|
|
|
|
def parse_argument() -> argparse.Namespace:
|
|
f"""
|
|
Parse commnad line arguement
|
|
{help_msg['design_name']}
|
|
"""
|
|
parser = argparse.ArgumentParser(formatter_class=formatter)
|
|
parser.add_argument('--design_name',
|
|
help="Design name, Generally in FPGAxxxx_xxxx format")
|
|
parser.add_argument('--arch_file', type=str,
|
|
help="VPR architecture file, It should atleast contain on fixed_layout")
|
|
parser.add_argument('--layout', type=str, default=None,
|
|
help="Specific layout name to render from the provided XML file")
|
|
parser.add_argument('--release_root', type=str, default=None,
|
|
help="Location to store pickled object of the 2D FPGA grid matrix")
|
|
return parser.parse_args()
|
|
|
|
|
|
class fpga_grid_gen():
|
|
'''
|
|
This class generates the 2D matrix of the FPGA grid.
|
|
|
|
**Example**:
|
|
|
|
python3.8 fpga_grid_gen.py **--design_name** FPGA66_flex
|
|
**--layout** dp
|
|
**--arch_file** example_files/vpr_arch_render_demo.xml
|
|
|
|
**Expected Output**:
|
|
|
|
.. code-block:: text
|
|
|
|
EMPTY io_top io_top io_top io_top io_top io_top EMPTY
|
|
io_left clb clb clb clb clb clb io_right
|
|
io_left clb clb clb clb clb clb io_right
|
|
io_left ram9k ram9k ram9k io_right
|
|
io_left clb clb clb clb clb clb io_right
|
|
io_left dsp dsp dsp io_right
|
|
io_left clb clb clb clb clb clb io_right
|
|
EMPTY io_bottom io_bottom io_bottom io_bottom io_bottom io_bottom EMPTY
|
|
|
|
'''
|
|
|
|
def __init__(self, design_name, arch_file, layout, release_root) -> None:
|
|
'''
|
|
args:
|
|
design_name (str): Design name
|
|
arch_file (str): Path to architecture file
|
|
layout (str): Fixed layout selection from architecture file
|
|
release_root (str): Directory to output bianries
|
|
'''
|
|
self.design_name = design_name
|
|
self.release_root = release_root
|
|
self.fpga_arch = OpenFPGA_Arch(arch_file, None, layout=layout)
|
|
# Parse values
|
|
self.clb = None
|
|
self.arch_tree = ET.parse(arch_file)
|
|
self.root = self.arch_tree.getroot()
|
|
self.layout = self.root.find(f".//fixed_layout[@name='{layout}']")
|
|
assert layout, "Specified layout not found in the architecture file"
|
|
self.width = self.fpga_arch.width
|
|
self.height = self.fpga_arch.height
|
|
self.pb_type = {}
|
|
self.grid = [[0 for x in range(self.width)]
|
|
for y in range(self.height)]
|
|
self.RIGHT_ARROW = RIGHT_ARROW
|
|
self.UP_ARROW = UP_ARROW
|
|
|
|
def get_width(self):
|
|
''' Get width of FPGA '''
|
|
return self.width-2
|
|
|
|
def get_height(self):
|
|
''' Get height of FPGA '''
|
|
return self.height-2
|
|
|
|
def get_block_size(self, block):
|
|
''' Get width of FPGA '''
|
|
return self.pb_type[block]
|
|
|
|
def print_grid(self):
|
|
"""
|
|
Prints the 2D FPGA grid on console
|
|
|
|
"""
|
|
for row in self.grid[::-1]:
|
|
for y in row:
|
|
print(f"{y:^10}", end=" ")
|
|
print("")
|
|
|
|
def get_block(self, x, y):
|
|
'''
|
|
This method returns the module present in specific x and y
|
|
cordinate. The return value contains module name and
|
|
adjusted X and Y cordianates
|
|
'''
|
|
value = self.grid[y][x]
|
|
while value in [self.RIGHT_ARROW, self.UP_ARROW]:
|
|
if value == self.UP_ARROW:
|
|
y -= 1
|
|
if value == self.RIGHT_ARROW:
|
|
x -= 1
|
|
if x < 1 and y < 1:
|
|
break
|
|
value = self.grid[y][x]
|
|
return value, x, y
|
|
|
|
@staticmethod
|
|
def _get_val(ele, param, default, vars={}):
|
|
"""
|
|
Parses the startx, starty, repeatx and repeaty variables to integer
|
|
"""
|
|
val = ele.attrib.get(param, str(default))
|
|
if val.isnumeric():
|
|
return int(val)
|
|
else:
|
|
val = val.replace("W", "{W}")
|
|
val = val.replace("H", "{H}")
|
|
return int(eval(val.format(**vars)))
|
|
|
|
def _set_value(self, x, y, value, width=1, height=1):
|
|
"""
|
|
Sets value in the FPGA grid
|
|
"""
|
|
try:
|
|
for xi in range(0, width):
|
|
for yi in range(0, height):
|
|
self.grid[y+yi][x+xi] = value if(xi, yi) == (0, 0) else \
|
|
RIGHT_ARROW if yi == 0 else UP_ARROW
|
|
return 1
|
|
except:
|
|
logger.warning(f"Trying to set grid location {(x, y)}")
|
|
return 0
|
|
|
|
def add_fill(self, ele):
|
|
ele_type = ele.attrib['type']
|
|
self.clb = ele_type
|
|
ele_w, ele_h = self.fpga_arch.tiles[ele_type]
|
|
for x in range(0, self.width, ele_w):
|
|
for y in range(0, self.height, ele_h):
|
|
self._set_value(x, y, ele_type, ele_w, ele_h)
|
|
|
|
def add_perimeter(self, ele):
|
|
ele_type = ele.attrib['type']
|
|
ele_w, ele_h = self.fpga_arch.tiles[ele_type]
|
|
|
|
for y in [0, self.fpga_arch.height-1]:
|
|
for x in range(0, self.fpga_arch.width):
|
|
self._set_value(x, y, ele_type, ele_w, ele_h)
|
|
for x in [0, self.fpga_arch.width-1]:
|
|
for y in range(0, self.fpga_arch.height):
|
|
self._set_value(x, y, ele_type, ele_w, ele_h)
|
|
|
|
def add_corners(self, ele):
|
|
ele_type = ele.attrib['type']
|
|
ele_w, ele_h = self.fpga_arch.tiles[ele_type]
|
|
self._set_value(0, 0, ele_type)
|
|
self._set_value(0, self.height-1, ele_type)
|
|
self._set_value(self.width-1, 0, ele_type)
|
|
self._set_value(self.width-1, self.height-1, ele_type, ele_w, ele_h)
|
|
|
|
def add_single(self, ele):
|
|
ele_type = ele.attrib['type']
|
|
ele_w, ele_h = self.fpga_arch.tiles[ele_type]
|
|
x = int(ele.attrib['x'])
|
|
y = int(ele.attrib['y'])
|
|
self._set_value(x-1, y-1, ele_type, ele_w, ele_h)
|
|
|
|
def add_row(self, ele):
|
|
ele_type = ele.attrib['type']
|
|
ele_w, ele_h = self.fpga_arch.tiles[ele_type]
|
|
var = {'w': ele_w, 'h': ele_h,
|
|
'W': self.fpga_arch.width, 'H': self.fpga_arch.height}
|
|
startx = self._get_val(ele, 'startx', ele_w, var)
|
|
incrx = self._get_val(ele, 'incrx', ele_w, var)
|
|
starty = self._get_val(ele, 'starty', 1, var)
|
|
repy = self._get_val(ele, 'repeaty', self.fpga_arch.height, var)
|
|
for x in range(startx, self.width, incrx):
|
|
for y in range(starty, self.height, repy):
|
|
self._set_value(x, y, ele_type, ele_w, ele_h)
|
|
|
|
def add_col(self, ele):
|
|
ele_type = ele.attrib['type']
|
|
ele_w, ele_h = self.fpga_arch.tiles[ele_type]
|
|
var = {'w': ele_w, 'h': ele_h,
|
|
'W': self.fpga_arch.width, 'H': self.fpga_arch.height}
|
|
startx = self._get_val(ele, 'startx', 0, var)
|
|
repeatx = self._get_val(ele, 'repeatx', self.fpga_arch.width, var)
|
|
starty = self._get_val(ele, 'starty', 1, var)
|
|
incry = self._get_val(ele, 'incry', ele_h, var)
|
|
for x in range(startx, self.width, repeatx):
|
|
for y in range(starty, self.height, incry):
|
|
self._set_value(x, y, ele_type, ele_w, ele_h)
|
|
|
|
def add_region(self, ele):
|
|
ele_type = ele.attrib['type']
|
|
ele_w, ele_h = self.fpga_arch.tiles[ele_type]
|
|
var = {'w': ele_w, 'h': ele_h,
|
|
'W': self.fpga_arch.width, 'H': self.fpga_arch.height}
|
|
startx = self._get_val(ele, "startx", 0, var)
|
|
endx = self._get_val(ele, "endx", self.fpga_arch.width, var)
|
|
incrx = self._get_val(ele, "incrx", ele_w, var)
|
|
repeatx = self._get_val(ele, "repeatx", self.fpga_arch.width, var)
|
|
starty = self._get_val(ele, "starty", 0, var)
|
|
endy = self._get_val(ele, "endy", self.fpga_arch.height, var)
|
|
incry = self._get_val(ele, "incry", ele_h, var)
|
|
repeaty = self._get_val(ele, "repeaty", self.fpga_arch.height, var)
|
|
|
|
for xstep in range(0, self.width, repeatx):
|
|
for ystep in range(0, self.height, repeaty):
|
|
for x in range(startx, endx, incrx):
|
|
for y in range(starty, endy, incry):
|
|
self._set_value(xstep+x, ystep+y,
|
|
ele_type, ele_w, ele_h)
|
|
|
|
def enumerate_grid(self):
|
|
'''
|
|
Enumerates the FPGA grid
|
|
|
|
Returns:
|
|
(list(list(str))): Returns 2D grid
|
|
'''
|
|
for element in sorted(self.layout, key=lambda x: int(x.attrib["priority"])):
|
|
tag = element.tag.lower()
|
|
ele_type = element.attrib["type"].lower()
|
|
if tag == "fill":
|
|
logger.debug("Adding Fill")
|
|
self.add_fill(element)
|
|
elif tag == "corners":
|
|
logger.debug("Adding Corners")
|
|
self.add_corners(element)
|
|
elif tag == "single":
|
|
logger.debug("Adding Single")
|
|
self.add_single(element)
|
|
elif tag == "perimeter":
|
|
logger.debug("Adding Perimeter")
|
|
self.add_perimeter(element)
|
|
elif tag == "row":
|
|
logger.debug("Adding Row")
|
|
self.add_row(element)
|
|
elif tag == "col":
|
|
logger.debug("Adding Col")
|
|
self.add_col(element)
|
|
elif tag == "region":
|
|
logger.debug("Adding region")
|
|
self.add_region(element)
|
|
else:
|
|
continue
|
|
return self.grid
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|