mirror of https://github.com/lnis-uofu/SOFA.git
887 lines
35 KiB
Python
887 lines
35 KiB
Python
# ##############################################################################
|
|
# Tool: OpenFPGA-Physical
|
|
# Script: fpga_grid_gen.py
|
|
################################################################################
|
|
'''
|
|
fpga_render_svg
|
|
---------------
|
|
|
|
TODO: Requires Restructuring
|
|
This scripts read the layout section of the VPR architecture file and
|
|
render the layout in SVG format. The outputs are stored in the release
|
|
directory (location passed as an argument) to this script.
|
|
|
|
python3.8 RenderArchitectureSVG.py \
|
|
--design_name FPGA66_flex \
|
|
--arch_file example_files/vpr_arch_render_demo.xml \
|
|
--layout dp \
|
|
--skip_channels \
|
|
--output_root _release
|
|
|
|
'''
|
|
|
|
import argparse
|
|
import csv
|
|
import logging
|
|
import math
|
|
import os
|
|
import pickle
|
|
import sys
|
|
import xml.etree.ElementTree as ET
|
|
from importlib import util
|
|
# Prinitng and logging related packages
|
|
from pprint import pprint
|
|
|
|
import matplotlib.pyplot as plt
|
|
import numpy as np
|
|
import pandas as pd
|
|
import svgwrite
|
|
import yaml
|
|
from svgwrite.container import Group
|
|
from openfpga_physical import fpga_grid_gen
|
|
if util.find_spec("coloredlogs"):
|
|
import coloredlogs
|
|
|
|
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
|
|
# Configure logging system
|
|
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
|
|
LOG_FORMAT = "%(levelname)5s (%(threadName)15s) - %(message)s"
|
|
if util.find_spec("coloredlogs"):
|
|
coloredlogs.install(level='INFO', stream=sys.stdout,
|
|
fmt=LOG_FORMAT)
|
|
else:
|
|
logging.basicConfig(level=logging.INFO, stream=sys.stdout,
|
|
format=LOG_FORMAT)
|
|
logger = logging.getLogger('RenderArchitecureSVG_logs')
|
|
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
|
|
|
|
|
|
def formatter(prog): return argparse.HelpFormatter(prog, max_help_position=60)
|
|
|
|
|
|
parser = argparse.ArgumentParser(formatter_class=formatter)
|
|
|
|
# Mandatory arguments
|
|
parser.add_argument('--design_name', type=str, default="FPGA22_QLAP3_SOFA_HD")
|
|
parser.add_argument('--arch_file', type=str, default="vpr_arch.xml")
|
|
parser.add_argument('--layout', type=str, default="dp")
|
|
|
|
# Configuration files
|
|
parser.add_argument('--shaping_conf_file', type=str, default="")
|
|
parser.add_argument('--area_file', type=str, default="")
|
|
parser.add_argument('--output_root', type=str, default="release")
|
|
# Rendering Related Parameters
|
|
parser.add_argument('--physical', action='store_true')
|
|
parser.add_argument('--clear_color', action='store_true')
|
|
parser.add_argument('--skip_channels', action='store_true')
|
|
parser.add_argument('--add_pads', action='store_true')
|
|
parser.add_argument('--cmap', action='store_true')
|
|
parser.add_argument('--debug', action='store_true')
|
|
args = parser.parse_args()
|
|
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
|
|
# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
|
|
|
|
|
|
area, width, height = 0, 1, 2
|
|
CPP = 4
|
|
SC_HEIGHT = 4
|
|
LOGICAL = not args.physical
|
|
|
|
|
|
def main():
|
|
grid = fpga_grid_gen(
|
|
design_name=args.design_name,
|
|
arch_file=args.arch_file,
|
|
layout=args.layout,
|
|
release_root="")
|
|
FPGA_SIZE = tuple([grid.get_width(), grid.get_height()])
|
|
|
|
DESIGN_NAME = args.design_name
|
|
SaveLocation = [args.output_root, ]
|
|
for eachLoc in ["data", "pickle", "TCL", "SVG", "ConnectNets", "PNG"]:
|
|
os.makedirs(os.path.join(*SaveLocation, eachLoc), exist_ok=True)
|
|
# = = = = = = Create dummy area file = = = = = = = = = = = = = = = = = = =
|
|
# incase of logical model create intermidiate file
|
|
if LOGICAL:
|
|
args.area_file = "_areafile.txt"
|
|
with open(args.area_file, "w") as fp:
|
|
CLBA, CBA = (32*32, 40)
|
|
fp.write(f"grid_clb_1__1_ {CLBA} 0 0 0\n")
|
|
fp.write(f"cbx_1__1_ {CBA} 0 0 0\n")
|
|
|
|
# = = = = = = Shaping Configuration file = = = = = = = = = = = = = = = = =
|
|
args.shaping_conf_file = "_shapingConf.yml"
|
|
with open(args.shaping_conf_file, "w") as file:
|
|
yaml.dump({"CLB_CHAN_X": 10,
|
|
"SC_RATIO": SC_HEIGHT/CPP,
|
|
"CPP": CPP,
|
|
"SC_HEIGHT": SC_HEIGHT,
|
|
"GRID_CLB_RATIO": 1,
|
|
"CBX_WIDTH_RATIO": 0.6,
|
|
"CBY_HEIGHT_RATIO": 0.6,
|
|
"GRID_RATIO_X": 1.4,
|
|
"GRID_RATIO_Y": 1.4,
|
|
"CBx_CHAN_X": 0,
|
|
"gridIO_HT": 10,
|
|
"gridIO_HB": 10,
|
|
"gridIO_WL": 12,
|
|
"gridIO_WR": 12,
|
|
"CBx_CHAN_Y": 0}, file)
|
|
|
|
# = = = = = = Read Pinmap = = = = = = = = = = = = = = = = = = = = = = = =
|
|
FPGAShape = FPGAShaping(FPGA_SIZE[0], FPGA_SIZE[1],
|
|
debug=args.debug,
|
|
areaFile=args.area_file,
|
|
shapingConf=args.shaping_conf_file,
|
|
gridIO=LOGICAL,
|
|
arch_file=args.arch_file,
|
|
layout_name=args.layout,
|
|
padFile=None)
|
|
|
|
FPGAShape.ComputeGrid(skipChannels=args.skip_channels)
|
|
FPGAShape.CreateDatabase()
|
|
# = = = = = = = = Setting up SVG Canvas = = = = = = = = = = = = = = = = = =
|
|
CoreBBox = (0, 0, int(FPGAShape.CLB_GRID_X*(FPGA_SIZE[0]+1))*CPP,
|
|
int(FPGAShape.CLB_GRID_Y*(FPGA_SIZE[1]+1))*SC_HEIGHT)
|
|
svgFilepath = os.path.join(
|
|
*SaveLocation, "SVG", DESIGN_NAME + '_Render.svg')
|
|
|
|
dwg = svgwrite.Drawing(svgFilepath, CoreBBox[2:])
|
|
dwg.viewbox(0, -1*CoreBBox[3], CoreBBox[2], CoreBBox[3])
|
|
|
|
dwgMain = dwg.add(Group(id="main", transform="scale(1,-1)"))
|
|
dwgMain.add(dwg.rect(size=CoreBBox[2:],
|
|
id="core_boundary",
|
|
class_="boundary",
|
|
stroke="grey",
|
|
fill="none",
|
|
stroke_width=2))
|
|
dwg.defs.add(dwg.style("""
|
|
text{font-family: Verdana; }
|
|
.cbx_1__0__def{fill:#d9d9f3}
|
|
.cbx_1__1__def{fill:#d9d9f3}
|
|
.cbx_1__2__def{fill:#d9d9f3}
|
|
|
|
.cby_0__1__def{fill:#a8d0db}
|
|
.cby_1__1__def{fill:#a8d0db}
|
|
.cby_2__1__def{fill:#a8d0db}
|
|
|
|
.sb_0__0__def{fill:#ceefe4}
|
|
.sb_0__1__def{fill:#ceefe4}
|
|
.sb_0__2__def{fill:#ceefe4}
|
|
.sb_1__0__def{fill:#ceefe4}
|
|
.sb_1__1__def{fill:#ceefe4}
|
|
.sb_1__2__def{fill:#ceefe4}
|
|
.sb_2__0__def{fill:#ceefe4}
|
|
.sb_2__1__def{fill:#ceefe4}
|
|
.sb_2__2__def{fill:#ceefe4}
|
|
|
|
.grid_def{fill:#f4f0e6;}
|
|
"""))
|
|
dwgShapes = dwgMain.add(Group(id="mainShapes"))
|
|
dwgText = dwgMain.add(Group(id="mainText"))
|
|
dwgGridShapes = dwgMain.add(Group(id="gridShapes"))
|
|
dwgGridText = dwgMain.add(Group(id="gridText"))
|
|
dwgMarker = dwgMain.add(Group(id="mainMarker"))
|
|
|
|
# addGrid markers
|
|
for i in range(1, FPGAShape.sizeX+1):
|
|
dwgMarker.add(dwg.line(start=(i*FPGAShape.CLB_GRID_X*CPP, CoreBBox[1]),
|
|
end=(i*FPGAShape.CLB_GRID_X*CPP, CoreBBox[3]),
|
|
class_="marker"))
|
|
for i in range(1, FPGAShape.sizeY+1):
|
|
dwgMarker.add(dwg.line(start=(CoreBBox[0],
|
|
i*FPGAShape.CLB_GRID_Y*SC_HEIGHT),
|
|
end=(CoreBBox[2], i *
|
|
FPGAShape.CLB_GRID_Y*SC_HEIGHT),
|
|
class_="marker"))
|
|
|
|
defList = {}
|
|
UniqueModules = [i["module"] for _, i in FPGAShape.PlacementDBKey.items()]
|
|
UniqueModules = set(UniqueModules)
|
|
UniqueModules = list(
|
|
filter(lambda x: not "grid_io" in x, set(UniqueModules)))
|
|
|
|
for eachModule in sorted(FPGAShape.PlacementDB, key=lambda x: "y" if "grid_clb" in x else 'z' if "grid" in x else x):
|
|
if (not args.add_pads) and ("grid_io" in eachModule):
|
|
continue
|
|
# Shape Definitions
|
|
mouleinfo = FPGAShape.PlacementDBKey[eachModule]
|
|
Module = mouleinfo["module"]
|
|
if not Module in defList.keys():
|
|
defList[Module] = dwg.symbol(id=Module)
|
|
xx = min([i[0] for i in mouleinfo["shape"]])
|
|
yy = min([i[1] for i in mouleinfo["shape"]])
|
|
|
|
if len(mouleinfo["shape"]) == 1:
|
|
llx, lly, w, h = mouleinfo["shape"][0]
|
|
defList[Module].add(dwg.rect(size=(float(w)*CPP, float(h)*SC_HEIGHT),
|
|
insert=((llx-xx)*CPP,
|
|
(lly-yy)*SC_HEIGHT),
|
|
# fill=mouleinfo["color"],
|
|
fill_opacity="1" if "grid" in Module else "0.7",
|
|
stroke="black"))
|
|
elif len(mouleinfo["shape"]) == 2:
|
|
Points = f"M "
|
|
Points += f"{mouleinfo['points'][0]*CPP} "
|
|
Points += f"{mouleinfo['points'][1]*SC_HEIGHT} "
|
|
Points += f"L "
|
|
|
|
for X, Y in np.reshape(np.array(mouleinfo['points'][2:]),
|
|
(int(len(mouleinfo['points'])*0.5)-1, 2)):
|
|
Points += f"{X*CPP} "
|
|
Points += f"{Y*SC_HEIGHT} "
|
|
defList[Module].add(dwg.path(d=Points + " z",
|
|
fill=mouleinfo["color"],
|
|
fill_opacity="0.7",
|
|
stroke="black"))
|
|
dwg.defs.add(defList[Module])
|
|
|
|
# Instantiation
|
|
x1, y1, w, h = mouleinfo["bbox"]
|
|
shapedwg = dwgGridShapes if "grid" in Module else dwgShapes
|
|
shapedwg.add(dwg.use(defList[Module],
|
|
class_=f"{Module}_def {eachModule}" +
|
|
(" grid_def" if "grid" in Module else ""),
|
|
insert=((x1*CPP), (y1*SC_HEIGHT))))
|
|
|
|
# # Uncomment following lines to print center of module
|
|
# dwgShapes.add(dwg.circle(
|
|
# center=(mouleinfo['center'][0]*CPP,
|
|
# mouleinfo['center'][1]*SC_HEIGHT),
|
|
# r=2,fill="red"))
|
|
|
|
x1, y1 = mouleinfo["center"]
|
|
textdwg = dwgGridText if "grid" in Module else dwgText
|
|
textdwg.add(dwg.text(f" {mouleinfo['short_name']} ",
|
|
insert=((x1*CPP), (-1*y1*SC_HEIGHT)),
|
|
transform="scale(1,-1)",
|
|
class_="moduleLabel",
|
|
fill="black",
|
|
alignment_baseline="middle",
|
|
text_anchor="middle"))
|
|
if LOGICAL:
|
|
logger.info("Printing Logical Model")
|
|
logger.info(f"Rendered image stored in {svgFilepath}")
|
|
dwg.save(pretty=True, indent=4)
|
|
pickle.dump(FPGAShape.get_variables(),
|
|
open(os.path.join(*SaveLocation, "pickle",
|
|
f"{DESIGN_NAME}_FPGAShapeVars.pickle"), 'wb'))
|
|
pickle.dump(FPGAShape.PlacementDBKey,
|
|
open(os.path.join(*SaveLocation, "pickle",
|
|
f"{DESIGN_NAME}_PlacementDBKey.pickle"), 'wb'))
|
|
pickle.dump(FPGAShape.PlacementDB,
|
|
open(os.path.join(*SaveLocation, "pickle",
|
|
f"{DESIGN_NAME}_PlacementDB.pickle"), 'wb'))
|
|
pickle.dump(dwg,
|
|
open(os.path.join(*SaveLocation, "pickle",
|
|
f"{DESIGN_NAME}_dwg.pickle"), 'wb'))
|
|
pickle.dump(FPGAShape.grid,
|
|
open(os.path.join(*SaveLocation, "pickle",
|
|
f"{DESIGN_NAME}_grid.pickle"), 'wb'))
|
|
return
|
|
|
|
|
|
class FPGAShaping():
|
|
"""
|
|
Accepts the architecture file and layout name to generate a tile database
|
|
to render as an SVG
|
|
|
|
args:
|
|
|
|
|
|
"""
|
|
|
|
def __init__(self, sizeX, sizeY,
|
|
debug=False,
|
|
areaFile=None,
|
|
padFile=None,
|
|
gridIO=False,
|
|
arch_file=None,
|
|
layout_name="",
|
|
shapingConf=None):
|
|
self.sizeX = sizeX
|
|
self.sizeY = sizeY
|
|
self.PlacementDB = []
|
|
self.PlacementDBKey = {}
|
|
self.GPIOPlacmentKey = []
|
|
self.debug = debug
|
|
|
|
self.get_default_configuration()
|
|
|
|
self.areaFile = areaFile
|
|
self.padFile = padFile
|
|
self.gridIO = gridIO
|
|
self.PadNames = {}
|
|
|
|
self.skipChannels = False
|
|
|
|
# Color Setting
|
|
self.CLB_COLOR = "#f4f0e6"
|
|
self.CBX_COLOR = "#d9d9f3"
|
|
self.CBY_COLOR = "#a8d0db"
|
|
self.SB_COLOR = "#ceefe4"
|
|
self.PAD_COLOR = "#204969"
|
|
self.GRID_IO_COLOR = "#ff8000"
|
|
|
|
# Pads Related
|
|
self.pad_w = 80
|
|
self.pad_h = 10
|
|
|
|
self.arch_file = arch_file
|
|
self.layout_name = layout_name
|
|
if shapingConf:
|
|
self.update_default_configuration(shapingConf)
|
|
|
|
self.grid = fpga_grid_gen("", arch_file, layout_name, "")
|
|
self.fpga_grid = self.grid.enumerate_grid()
|
|
self.grid.print_grid()
|
|
|
|
def update_default_configuration(self, shapingConf):
|
|
with open(shapingConf, "r") as file:
|
|
for eachKey, eachValue in yaml.load(file, Loader=yaml.FullLoader).items():
|
|
setattr(self, eachKey, eachValue)
|
|
|
|
def get_default_configuration(self):
|
|
# Grid clb shape
|
|
self.SC_RATIO = 8 # This is SC_HEIGHT/CPP of stadard cell
|
|
self.GRID_CLB_RATIO = 1 # This is actual dimension of the CLB unit width/height
|
|
|
|
# Connection box size
|
|
self.GRID_RATIO_X, self.GRID_RATIO_Y = 1.34, 1.48
|
|
self.CBX_WIDTH_RATIO, self.CBY_HEIGHT_RATIO = 0.6, 0.6
|
|
|
|
# Channel spacing between blocks
|
|
self.CLB_CHAN_T, self.CLB_CHAN_B = 1, 1
|
|
self.CLB_CHAN_L, self.CLB_CHAN_R = 4, 4
|
|
|
|
self.CBX_CHAN_T, self.CBX_CHAN_B = 0, 0
|
|
self.CBX_CHAN_L, self.CBX_CHAN_R = 4, 4
|
|
|
|
self.CBY_CHAN_T, self.CBY_CHAN_B = 1, 1
|
|
self.CBY_CHAN_L, self.CBY_CHAN_R = 0, 0
|
|
|
|
self.gridIO_MT, self.gridIO_MB = 1, 1
|
|
self.gridIO_ML, self.gridIO_MR = 2, 2
|
|
|
|
self.gridIO_HT, self.gridIO_HB = 10, 10
|
|
self.gridIO_WL, self.gridIO_WR = 48, 48
|
|
|
|
self.GRID_IOV_H_RATIO = 1
|
|
self.GRID_IOH_W_RATIO = 1
|
|
|
|
# TODO: Deprecate this
|
|
self.GPIO_CHAN_X, self.GPIO_CHAN_Y = 12, 4
|
|
self.GPIO_WIDTH, self.GPIO_HEIGHT = 40, 8
|
|
|
|
def get_variables(self):
|
|
return {
|
|
"CPP": self.CPP,
|
|
"SC_HEIGHT": self.SC_HEIGHT,
|
|
"CLB_COLOR": self.CLB_COLOR,
|
|
"CBX_COLOR": self.CBX_COLOR,
|
|
"CBY_COLOR": self.CBY_COLOR,
|
|
"SB_COLOR": self.SB_COLOR,
|
|
"PAD_COLOR": self.PAD_COLOR,
|
|
"GRID_IO_COLOR": self.GRID_IO_COLOR,
|
|
"CORE_BBOX": (0, 0, int(self.CLB_GRID_X*(self.sizeX+1)),
|
|
int(self.CLB_GRID_Y*(self.sizeY+1)))
|
|
}
|
|
|
|
def figSize(self):
|
|
size = (4+(1*self.sizeX), 4+(1*self.sizeY))
|
|
if self.sizeX < 16:
|
|
dpi = 300
|
|
elif self.sizeX < 64:
|
|
dpi = 100
|
|
else:
|
|
dpi = 50
|
|
return {"size": size, "dpi": dpi}
|
|
|
|
def snapDims(self, num, dim=2):
|
|
return math.ceil(num/dim)*dim
|
|
|
|
def setChannelSpacing(self, module, X, Y):
|
|
raise NotImplementedError
|
|
|
|
def ComputeGrid(self, skipChannels=False):
|
|
self.skipChannels = skipChannels
|
|
if self.areaFile:
|
|
BlockArea = {}
|
|
for eachLine in open(self.areaFile, "r"):
|
|
module, dims = eachLine.split(" ", 1)
|
|
BlockArea[module] = list(map(float, list(dims.split())))
|
|
self.CLB_DIM = BlockArea["grid_clb_1__1_"]
|
|
self.CB_DIM = BlockArea["cbx_1__1_"]
|
|
# self.CLB_DIM = math.floor(BlockArea["grid_clb_1__1_"][1]*0.5)*2
|
|
# self.CB_DIM = [self.CLB_DIM*0.6, 0, 0]
|
|
else:
|
|
self.CLB_DIM = [2500, 24*8, 24]
|
|
self.CB_DIM = [2500*0.6, 0, 0]
|
|
|
|
# Snap CLB Height and Width to next Multiple of 2
|
|
self.CLB_UNIT = math.sqrt(
|
|
self.CLB_DIM[area]/(self.GRID_CLB_RATIO*self.SC_RATIO))
|
|
|
|
self.CLB_H = self.snapDims(self.CLB_UNIT, 2)
|
|
self.CLB_W = self.snapDims(self.CLB_DIM[area]/self.CLB_H, 2)
|
|
|
|
self.CLB_GRID_X = self.snapDims(self.CLB_W*self.GRID_RATIO_X, 2)
|
|
self.CLB_GRID_Y = self.snapDims(self.CLB_H*self.GRID_RATIO_Y, 2)
|
|
|
|
self.CBX_W = self.snapDims(self.CLB_W*self.CBX_WIDTH_RATIO, 2)
|
|
self.CBX_H = self.CLB_GRID_Y-self.CLB_H
|
|
|
|
self.CBY_W = self.CLB_GRID_X-self.CLB_W
|
|
self.CBY_H = self.snapDims(self.CLB_H*self.CBY_HEIGHT_RATIO, 2)
|
|
|
|
self.SB_W = self.CLB_GRID_X - self.CBX_W
|
|
self.SB_H = self.CLB_GRID_Y - self.CBY_H
|
|
self.SIDE_X = self.CLB_GRID_X - self.CLB_W
|
|
self.SIDE_Y = self.CLB_GRID_Y - self.CLB_H
|
|
|
|
self.GRID_IOV_H = self.CLB_H*self.GRID_IOV_H_RATIO
|
|
self.GRID_IOH_W = self.CLB_W*self.GRID_IOH_W_RATIO
|
|
|
|
if self.debug:
|
|
print(f"self.CLB_W {self.CLB_W}")
|
|
print(f"self.CLB_H {self.CLB_H}")
|
|
print(f"self.CLB_GRID_X {self.CLB_GRID_X}")
|
|
print(f"self.CLB_GRID_Y {self.CLB_GRID_Y}")
|
|
print(f"self.CBX_W {self.CBX_W}")
|
|
print(f"self.CBX_H {self.CBX_H}")
|
|
print(f"self.CBY_W {self.CBY_W}")
|
|
print(f"self.CBY_H {self.CBY_H}")
|
|
print(f"self.SB_W {self.SB_W}")
|
|
print(f"self.SB_H {self.SB_H}")
|
|
|
|
if self.padFile:
|
|
if os.path.exists(self.padFile):
|
|
print(f"Found PinMapFile {self.padFile}")
|
|
df_pinMap = pd.read_csv(self.padFile)
|
|
df_pinMap.rename(columns=lambda x: x.strip(), inplace=True)
|
|
self.PadNames["L"] = df_pinMap["Remark"]
|
|
self.PadNames["T"] = df_pinMap["Remark.1"]
|
|
self.PadNames["R"] = df_pinMap["Remark.2"]
|
|
self.PadNames["B"] = df_pinMap["Remark.3"]
|
|
self.NumOfPads = len(df_pinMap.index)
|
|
|
|
def CreateDatabase(self):
|
|
# Create Blocks
|
|
grid_ele_size = {}
|
|
|
|
for x in range(self.sizeX+1):
|
|
for y in range(self.sizeY+1):
|
|
|
|
self.add_sb(x, y)
|
|
if x < self.sizeX:
|
|
self.add_cbx(x, y)
|
|
if y < self.sizeY:
|
|
self.add_cby(x, y)
|
|
if (x < self.sizeX) and (y < self.sizeY):
|
|
label = self.fpga_grid[y+1][x+1]
|
|
if not (label in [self.grid.RIGHT_ARROW, self.grid.UP_ARROW, "EMPTY"]):
|
|
if not label in grid_ele_size.keys():
|
|
ele_w, ele_h = self.grid.fpga_arch.tiles[label]
|
|
grid_ele_size[label] = (ele_w, ele_h)
|
|
else:
|
|
ele_w, ele_h = grid_ele_size[label]
|
|
self.add_clb(x, y, width=ele_w,
|
|
height=ele_h, lbl=label)
|
|
# Create gridIOs
|
|
if self.gridIO:
|
|
if (y == self.sizeY) and (x < self.sizeX):
|
|
self.add_gridIOH(x, y, side="top")
|
|
if (y == 0) and (x < self.sizeX):
|
|
self.add_gridIOH(x, y, side="bottom")
|
|
if (x == 0) and (y < self.sizeY):
|
|
self.add_gridIOV(x, y, side="left")
|
|
if (x == self.sizeX) and (y < self.sizeY):
|
|
self.add_gridIOV(x, y, side="right")
|
|
|
|
# Create Pins
|
|
if self.PadNames:
|
|
for side in ["L", "T", "R", "B"]:
|
|
for i in range(self.NumOfPads):
|
|
self.add_pad(side, i, self.PadNames[side][i])
|
|
return self.PlacementDB
|
|
|
|
def add_clb(self, xi, yi, width=1, height=1, lbl="grid_clb"):
|
|
x, y = (xi+1)*self.CLB_GRID_X, (yi+1)*self.CLB_GRID_Y
|
|
llx = x-self.snapDims(self.CLB_W*0.5)
|
|
lly = y-self.snapDims(self.CLB_H*0.5)
|
|
W1 = self.CLB_W + ((width-1) * self.CLB_GRID_X)
|
|
H1 = self.CLB_H + ((height-1) * self.CLB_GRID_Y)
|
|
initShape = [(llx, lly, W1, H1)]
|
|
x += ((width-1) * self.CLB_GRID_X)*0.5
|
|
y += ((height-1) * self.CLB_GRID_Y)*0.5
|
|
if not self.skipChannels:
|
|
llx += self.CLB_CHAN_L
|
|
lly += self.CLB_CHAN_B
|
|
W1 = self.CLB_W-self.CLB_CHAN_L-self.CLB_CHAN_R
|
|
H1 = self.CLB_H-self.CLB_CHAN_T-self.CLB_CHAN_B
|
|
block_name = f"grid_{lbl}_{xi+1}__{yi+1}_"
|
|
short_block_name = f"{lbl}_{xi+1}_{yi+1}"
|
|
COLOR = self.CLB_COLOR
|
|
points = [0, 0, 0, self.CLB_H, self.CLB_W, self.CLB_H, self.CLB_W, 0]
|
|
self.PlacementDB.append(block_name)
|
|
self.PlacementDBKey[block_name] = {"name": block_name,
|
|
"short_name": short_block_name,
|
|
"bbox": [llx, lly,
|
|
llx+W1, lly+H1],
|
|
"points": points,
|
|
"module": f"grid_{lbl}_1__1_",
|
|
"center": [x, y],
|
|
"color": COLOR,
|
|
"shape": [(llx, lly, W1, H1)],
|
|
"initShape": initShape,
|
|
"xi": xi,
|
|
"yi": yi}
|
|
|
|
def add_cbx(self, xi, yi, lbl=None):
|
|
x, y = (xi+1)*self.CLB_GRID_X, (yi+1)*self.CLB_GRID_Y
|
|
llx = x-self.snapDims((self.CBX_W)*0.5)
|
|
lly = y-self.snapDims((self.CLB_H*0.5)+self.CBX_H)
|
|
W1 = self.CBX_W
|
|
H1 = self.CBX_H
|
|
initShape = [(llx, lly, W1, H1)]
|
|
|
|
if not self.skipChannels:
|
|
llx += self.CBX_CHAN_L
|
|
lly += self.CBX_CHAN_B
|
|
W1 = self.CBX_W-self.CBX_CHAN_L-self.CBX_CHAN_R
|
|
H1 = self.CBX_H-self.CBX_CHAN_T-self.CBX_CHAN_B
|
|
|
|
block_name = f"cbx_{xi+1}__{yi}_"
|
|
short_block_name = f"CX_{xi+1}_{yi}"
|
|
points = [0, 0, 0, W1, H1, W1, H1, 0]
|
|
self.PlacementDB.append(block_name)
|
|
moduleName = "cbx_1__0_" if yi == 0 else "cbx_1__2_" if yi == self.sizeY else "cbx_1__1_"
|
|
self.PlacementDBKey[block_name] = {"name": block_name,
|
|
"short_name": short_block_name,
|
|
"bbox": [llx, lly, llx+W1, lly+H1],
|
|
"points": points,
|
|
"center": [llx+W1*0.5, lly+H1*0.5],
|
|
"module": moduleName,
|
|
"color": self.CBX_COLOR,
|
|
"shape": [(llx, lly, W1, H1)],
|
|
"initShape": initShape,
|
|
"xi": xi,
|
|
"yi": yi}
|
|
|
|
def add_cby(self, xi, yi, lbl=None):
|
|
x, y = (xi+1)*self.CLB_GRID_X, (yi+1)*self.CLB_GRID_Y
|
|
llx = x-self.snapDims((self.CLB_W*0.5)+self.CBY_W)
|
|
lly = y-self.snapDims(self.CBY_H)*0.5
|
|
W1 = self.CBY_W
|
|
H1 = self.CBY_H
|
|
initShape = [(llx, lly, W1, H1)]
|
|
|
|
if not self.skipChannels:
|
|
llx += self.CBY_CHAN_L
|
|
lly += self.CBY_CHAN_B
|
|
W1 = self.CBY_W-self.CBY_CHAN_L-self.CBY_CHAN_R
|
|
H1 = self.CBY_H-self.CBY_CHAN_T-self.CBY_CHAN_B
|
|
|
|
block_name = f"cby_{xi}__{yi+1}_"
|
|
short_block_name = f"CY_{xi}_{yi+1}"
|
|
points = [0, 0, 0, W1, H1, W1, H1, 0]
|
|
self.PlacementDB.append(block_name)
|
|
moduleName = "cby_0__1_" if xi == 0 else "cby_2__1_" if xi == self.sizeY else "cby_1__1_"
|
|
self.PlacementDBKey[block_name] = {"name": block_name,
|
|
"short_name": short_block_name,
|
|
"bbox": [llx, lly, llx+W1, lly+H1],
|
|
"points": points,
|
|
"center": [llx+W1*0.5, lly+H1*0.5],
|
|
"module": moduleName,
|
|
"color": self.CBY_COLOR,
|
|
"shape": [(llx, lly, W1, H1)],
|
|
"initShape": initShape,
|
|
"xi": xi,
|
|
"yi": yi}
|
|
|
|
def get_stype(self, x, y):
|
|
if x == 0:
|
|
if y == 0:
|
|
return 1
|
|
elif y == self.sizeY:
|
|
return 3
|
|
else:
|
|
return 2
|
|
elif x == self.sizeX:
|
|
if y == 0:
|
|
return 7
|
|
elif y == self.sizeY:
|
|
return 5
|
|
else:
|
|
return 6
|
|
else:
|
|
if y == 0:
|
|
return 8
|
|
elif y == self.sizeY:
|
|
return 4
|
|
else:
|
|
if self.grid.get_block(x, y+1) == self.grid.get_block(x+1, y+1):
|
|
return 4
|
|
if self.grid.get_block(x+1, y+1) == self.grid.get_block(x+1, y):
|
|
return 6
|
|
if self.grid.get_block(x, y) == self.grid.get_block(x+1, y):
|
|
return 8
|
|
if self.grid.get_block(x, y) == self.grid.get_block(x, y+1):
|
|
return 2
|
|
else:
|
|
return 0
|
|
|
|
def unique(self, sequence):
|
|
seen = set()
|
|
u = [x for x in sequence if not (x in seen or seen.add(x))]
|
|
return [val for sublist in u for val in sublist]
|
|
|
|
def add_sb(self, xi, yi):
|
|
'''
|
|
d
|
|
+----+
|
|
c | |
|
|
b | | e
|
|
+----+ +----+
|
|
a | | Cross Shape
|
|
| | -lengths {a b c d e f}
|
|
+----+ +----+
|
|
| |
|
|
| | f
|
|
+----+
|
|
'''
|
|
x = xi*self.CLB_GRID_X
|
|
y = yi*self.CLB_GRID_Y
|
|
|
|
llxB1 = x+(0.5*self.CLB_W)
|
|
llyB1 = y+(self.CBY_H*0.5)
|
|
WidthB1 = self.SIDE_X
|
|
HeightB1 = self.SB_H
|
|
|
|
llxB2 = x + (self.CBX_W*0.5)
|
|
llyB2 = y + (self.CLB_H*0.5)
|
|
WidthB2 = self.SB_W
|
|
HeightB2 = self.SIDE_Y
|
|
|
|
a = self.SIDE_Y
|
|
b = e = (WidthB2-self.SIDE_X) * 0.5
|
|
c = f = (HeightB1-self.SIDE_Y)*0.5
|
|
d = self.SIDE_X
|
|
|
|
Stype = self.get_stype(xi, yi)
|
|
if Stype == 1: # SB_0__0_
|
|
llyB1 += c
|
|
HeightB1 += -c
|
|
llxB2 += b
|
|
WidthB2 += -b
|
|
b = f = 0
|
|
elif Stype == 2: # SB_0__1_
|
|
llxB2 += b
|
|
WidthB2 -= b
|
|
b = 0
|
|
elif Stype == 3: # SB_0__2_
|
|
llxB2 += b
|
|
WidthB2 -= b
|
|
HeightB1 -= f
|
|
c = b = 0
|
|
elif Stype == 4: # SB_1__2_
|
|
HeightB1 -= c
|
|
c = 0
|
|
elif Stype == 5: # SB_2__2_
|
|
HeightB1 -= c
|
|
WidthB2 -= e
|
|
c = e = 0
|
|
elif Stype == 6: # SB_2__1_
|
|
WidthB2 -= e
|
|
e = 0
|
|
elif Stype == 7: # SB_2__0_
|
|
llyB1 += f
|
|
HeightB1 -= f
|
|
WidthB2 -= e
|
|
e = f = 0
|
|
elif Stype == 8: # SB_1__0_
|
|
llyB1 += f
|
|
HeightB1 -= f
|
|
f = 0
|
|
|
|
block_name = f"sb_{xi}__{yi}_"
|
|
short_block_name = f"SB_{xi}_{yi}"
|
|
initShape = [(llxB1, llyB1, WidthB1, HeightB1),
|
|
(llxB2, llyB2, WidthB2, HeightB2)]
|
|
# initShape = [(llxB1, llyB1, WidthB1, HeightB1), ]
|
|
# initShape = [(llxB2, llyB2, WidthB2, HeightB2), ]
|
|
|
|
points = self.unique([(b, 0), (b, f),
|
|
(0, f), (0, (f+a)),
|
|
(b, (f+a)), (b, (a+c+f)),
|
|
((b+d), (a+c+f)), ((b+d), (a+f)),
|
|
((b+d+e), (a+f)), ((b+d+e), f),
|
|
((b+d), f), ((b+d), 0)])
|
|
self.PlacementDB.append(block_name)
|
|
moduleNames = [
|
|
"sb_1__1_", "sb_0__0_", "sb_0__1_",
|
|
"sb_0__2_", "sb_1__2_", "sb_2__2_",
|
|
"sb_2__1_", "sb_2__0_", "sb_1__0_",
|
|
]
|
|
# ┿ ┗ ┝ ┏ ┯ ┓ ┫ ┛ ┷ ┃ ━
|
|
llx = min([i[0] for i in initShape])
|
|
lly = min([i[1] for i in initShape])
|
|
|
|
self.PlacementDBKey[block_name] = {"name": block_name,
|
|
"short_name": short_block_name,
|
|
"bbox": [llx, lly, llx+f+a+c, lly+b+d+e],
|
|
"points": points,
|
|
"center": [llx+(WidthB1*0.5)+b,
|
|
lly+(HeightB2*0.5)+f],
|
|
"module": moduleNames[Stype],
|
|
"color": self.SB_COLOR,
|
|
"shape": initShape,
|
|
"xi": xi,
|
|
"yi": yi,
|
|
"dims": [a, b, c, d, e, f],
|
|
"initShape": initShape}
|
|
|
|
def add_gridIOH(self, xi, yi, side, lbl=None):
|
|
x, y = (xi+1)*self.CLB_GRID_X, (yi+1)*self.CLB_GRID_Y
|
|
llx = x-self.snapDims((self.GRID_IOH_W)*0.5)
|
|
lly = y-self.snapDims((self.CLB_H*0.5)+self.CBX_H)
|
|
lly += (-1*self.gridIO_HB) if side == "bottom" else self.CBX_H
|
|
W1 = self.GRID_IOH_W
|
|
H1 = self.gridIO_HB
|
|
initShape = [(llx, lly, W1, H1)]
|
|
|
|
if not self.skipChannels:
|
|
llx += self.CBX_CHAN_L
|
|
lly += 0 if side == "bottom" else self.gridIO_MT
|
|
W1 = self.GRID_IOH_W-self.CBX_CHAN_L-self.CBX_CHAN_R
|
|
H1 = self.gridIO_HB-self.gridIO_MB
|
|
|
|
if side == "bottom":
|
|
moduleName = "grid_io_bottom_bottom"
|
|
block_name = f"grid_io_{side}_{side}_{xi+1}__{yi}_"
|
|
short_block_name = f"io{side}_{xi+1}_{yi}"
|
|
else:
|
|
moduleName = "grid_io_top_top"
|
|
block_name = f"grid_io_{side}_{side}_{xi+1}__{yi+1}_"
|
|
short_block_name = f"io{side}_{xi+1}_{yi+1}"
|
|
points = [0, 0, 0, W1, H1, W1, H1, 0]
|
|
self.PlacementDB.append(block_name)
|
|
|
|
self.PlacementDBKey[block_name] = {"name": block_name,
|
|
"short_name": short_block_name,
|
|
"bbox": [llx, lly, llx+W1, lly+H1],
|
|
"points": points,
|
|
"center": [llx+W1*0.5, lly+H1*0.5],
|
|
"module": moduleName,
|
|
"color": self.GRID_IO_COLOR,
|
|
"shape": [(llx, lly, W1, H1)],
|
|
"initShape": initShape}
|
|
|
|
def add_gridIOV(self, xi, yi, side, lbl=None):
|
|
x, y = (xi+1)*self.CLB_GRID_X, (yi+1)*self.CLB_GRID_Y
|
|
llx = x-self.snapDims((self.CLB_W*0.5)+self.CBY_W)
|
|
lly = y-self.snapDims(self.GRID_IOV_H)*0.5
|
|
llx += (-1*(self.gridIO_WL)) if side == "left" else self.CBY_W
|
|
W1 = self.gridIO_WL
|
|
H1 = self.GRID_IOV_H
|
|
initShape = [(llx, lly, W1, H1)]
|
|
|
|
if not self.skipChannels:
|
|
llx += self.CBY_CHAN_L
|
|
llx += (-1*self.gridIO_ML) if side == "left" else self.gridIO_MR
|
|
lly += self.CBY_CHAN_B
|
|
W1 = self.gridIO_WL-self.gridIO_ML
|
|
H1 = self.GRID_IOV_H-self.CBY_CHAN_T-self.CBY_CHAN_B
|
|
|
|
if side == "left":
|
|
block_name = f"grid_io_{side}_{side}_{xi}__{yi+1}_"
|
|
short_block_name = f"io{side}_{xi}_{yi+1}"
|
|
moduleName = "grid_io_left_left"
|
|
else:
|
|
block_name = f"grid_io_{side}_{side}_{xi+1}__{yi+1}_"
|
|
short_block_name = f"io{side}_{xi+1}_{yi+1}"
|
|
moduleName = "grid_io_right_right"
|
|
points = [0, 0, 0, W1, H1, W1, H1, 0]
|
|
self.PlacementDB.append(block_name)
|
|
|
|
self.PlacementDBKey[block_name] = {"name": block_name,
|
|
"short_name": short_block_name,
|
|
"bbox": [llx, lly, llx+W1, lly+H1],
|
|
"points": points,
|
|
"center": [llx+W1*0.5, lly+H1*0.5],
|
|
"module": moduleName,
|
|
"color": self.GRID_IO_COLOR,
|
|
"shape": [(llx, lly, W1, H1)],
|
|
"initShape": initShape}
|
|
|
|
def add_pad(self, side="L", number=0, padname="xx"):
|
|
CoreMinX, CoreMinY = (0.5*self.CLB_W), (0.5*self.CLB_H)
|
|
CoreMaxX, CoreMaxY = (((self.sizeX+0.5) * self.CLB_GRID_X)+0.5*self.CBY_W,
|
|
((self.sizeY+0.5) * self.CLB_GRID_Y)+0.5*self.CBX_H)
|
|
if side in ["L", "R"]:
|
|
pad_w = self.pad_w
|
|
pad_h = (((self.CLB_H+self.CBX_H)*self.sizeY+1) +
|
|
self.CBX_H)/self.NumOfPads
|
|
shift = (number*pad_h)
|
|
initialshitX = (self.CLB_GRID_Y - self.CBX_H-(self.CLB_H*0.5))
|
|
initialshitY = (self.CLB_GRID_X - self.CBY_W-(self.CLB_W*0.5))
|
|
pad_spacing = 24
|
|
if side == "L":
|
|
pad_x = CoreMinX - (pad_w*0.5) - pad_spacing
|
|
pad_y = initialshitX + shift + pad_h*0.5
|
|
pad_llx = pad_x - (pad_w*0.5)
|
|
pad_lly = pad_y - (pad_h*0.5)
|
|
pad_w, pad_h = pad_w, pad_h
|
|
rot = 0
|
|
t = 0.5
|
|
elif side == "R":
|
|
pad_x = CoreMaxX + (pad_w*0.5) + pad_spacing
|
|
pad_y = initialshitX + shift + pad_h*0.5
|
|
pad_llx = pad_x - (pad_w*0.5)
|
|
pad_lly = pad_y - (pad_h*0.5)
|
|
pad_w, pad_h = pad_w, pad_h
|
|
rot = 0
|
|
t = 0.5
|
|
else:
|
|
pad_w = (((self.CLB_W+self.CBY_W)*self.sizeX+1) +
|
|
self.CBY_W)/self.NumOfPads
|
|
pad_h = self.pad_h
|
|
shift = (number*pad_w)
|
|
initialshitY = (self.CLB_GRID_X - self.CBY_W-(self.CLB_W*0.5))
|
|
pad_spacing = 3
|
|
if side == "T":
|
|
pad_x = initialshitY + shift + pad_w*0.5
|
|
pad_y = CoreMaxY + pad_spacing + pad_h*0.5
|
|
pad_llx = pad_x - (0.5*pad_w)
|
|
pad_lly = pad_y - pad_h*0.5
|
|
pad_w, pad_h = pad_w, pad_h
|
|
rot = 90
|
|
t = 0.5
|
|
elif side == "B":
|
|
pad_x = initialshitY + shift + pad_w*0.5
|
|
pad_y = CoreMinY - pad_spacing - pad_h*0.5
|
|
pad_llx = pad_x - (0.5*pad_w)
|
|
pad_lly = pad_y - pad_h*0.5
|
|
pad_w, pad_h = pad_w, pad_h
|
|
rot = -90
|
|
t = 0.5
|
|
|
|
self.GPIOPlacmentKey.append(
|
|
{
|
|
"side": side,
|
|
"rot": rot,
|
|
"text": padname.strip(),
|
|
"shape": [(pad_llx, pad_lly, pad_w, pad_h)],
|
|
"color": self.PAD_COLOR,
|
|
"center": [pad_x, pad_y],
|
|
}
|
|
)
|
|
|
|
def moduleFmt(self, mod, X, Y):
|
|
return f"{mod}_{X}__{Y}_"
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|