# ##############################################################################
# Tool: OpenFPGA-Physical
# Script: FPGAGridGen.py
################################################################################
"""
FPGAGridGen
-------------
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 math
from itertools import accumulate
import svgwrite
from svgwrite.container import Group
from spydrnet_physical.util.openfpga_arch import OpenFPGA_Arch
from spydrnet_physical.ir.shaping_utils import shaping_utils
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 = FPGAGridGen(args.design_name, args.arch_file, args.layout, args.release_root)
grid.enumerate_grid()
grid.print_grid()
def parse_argument() -> argparse.Namespace:
"""
Parse commnad line arguement
"""
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()
CSS_STYLE = """
.boundary{stroke:grey; fill:none; stroke-width:0.2}
text{font-family: Lato; font-size:1.2px;}
symbol { stroke-width:0.1; stroke:black;}
.marker{stroke: red;opacity: 0.1;stroke-width: 0.1px;}
symbol[id="cbx"] * { fill:#d9d9f3;}
symbol[id="cby"] * { fill:#a8d0db;}
symbol[id="sb00"] * { fill:#ceefe4;}
symbol[id="sb01"] * { fill:#ceefe4;}
symbol[id="sb02"] * { fill:#ceefe4;}
symbol[id="sb03"] * { fill:#ceefe4;}
symbol[id="sb04"] * { fill:#ceefe4;}
symbol[id="sb05"] * { fill:#ceefe4;}
symbol[id="sb06"] * { fill:#ceefe4;}
symbol[id="sb07"] * { fill:#ceefe4;}
symbol[id="sb08"] * { fill:#ceefe4;}
symbol[id="sb09"] * { fill:#ceefe4;}
symbol[id="sb10"] * { fill:#ceefe4;}
rect[class="lb"] { fill:#f4f0e6; }
symbol[id*="io_"] * { fill:#f8b155;}
"""
[docs]class FPGAGridGen:
"""This class generates the 2D lsit of the FPGA grid,
based on the provided VPR architecture file.
This class generate two grid
**self.grid** : This is logic blocks grid including IOs (NxM)
**self.full_grid** : This is complete grid with a logic and routing blocks (N-1)x(M-1)
Where NxM is width and height of the FPGA, including IO
**Example execution**:
.. code-block:: bash
python FPGAGridGen.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
"""
design_name = ""
"""str: Design name"""
grid = None
"""list(list): 2-Dimentional grid for logic blocks grid[0] bottom most row"""
full_grid = None
"""list(list): 2-Dimentional grid for logic block and routing elements"""
fpga_arch = None
"""OpenFPGA_Arch: Instance of OpenFPGA_Arch class"""
def __init__(self, design_name, arch_file, layout, release_root=None) -> None:
"""
Initiliaze the FPGA grid generator class
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 binaries
"""
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.root = self.fpga_arch.vpr_arch
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.grid = [[0 for _ in range(self.width)] for _ in range(self.height)]
self.full_grid = [
[0 for _ in range(2 * (self.width) - 1)]
for _ in range(2 * (self.height) - 1)
]
self.RIGHT_ARROW = RIGHT_ARROW
self.UP_ARROW = UP_ARROW
self.default_parameters = self._default_shaping_param()
self.placement_db = {}
[docs] def get_width(self):
"""Get width of FPGA"""
return self.width - 2
[docs] def get_height(self):
"""Get height of FPGA"""
return self.height - 2
[docs] def print_grid(self, grid="grid"):
"""
Prints logic block grid
"""
grid = self.grid if grid == "grid" else self.full_grid
output = ""
for row in grid[::-1]:
for y in row:
output += f"{y:^10} "
output += "\n"
print(output)
return output
[docs] 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
the cordiante origin starts from the first element of top most list
and first element of the first element of list of list
"""
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 < 0 and y < 0:
x, y = 0, 0
break
value = self.grid[y][x]
return value, x, y
[docs] def get_top_instance(self, x, y):
"""
This method generates the grid instance information given the
cordinate points
"""
grid_lbl = self.get_block(int(x / 2), int(y / 2))
if y == 0 or ((self.height * 2) - 2 == y):
return (
"EMPTY"
if ((x % 2) or grid_lbl[0] == "EMPTY")
else ("%s_%d__%d_" % grid_lbl)
)
if x == 0 or ((self.width * 2) - 2 == x):
return (
"EMPTY"
if ((y % 2) or grid_lbl[0] == "EMPTY")
else ("%s_%d__%d_" % grid_lbl)
)
if (x % 2 == 0) and (y % 2 == 0):
return "%s_%d__%d_" % grid_lbl
module = {
True: "sb",
(x % 2 == 1) and (y % 2 == 0): "cby",
(x % 2 == 0) and (y % 2 == 1): "cbx",
}[True]
xi, yi = int(x / 2), int(y / 2)
# TODO : Square modules are not supported yet
if module == "sb":
if (self.get_block(xi + 1, yi + 1) == self.get_block(xi + 1, yi)) and (
self.get_block(xi + 1, yi + 1) == self.get_block(xi, yi + 1)
):
grid_lbl = self.get_block(xi, yi)
return "%s_%d__%d_" % grid_lbl
if module == "cby":
if self.get_block(xi, yi) == self.get_block(xi + 1, yi):
grid_lbl = self.get_block(xi, yi)
return "%s_%d__%d_" % grid_lbl
if module == "cbx":
if self.grid[yi + 1][xi] in [self.UP_ARROW]:
grid_lbl = self.get_block(xi, yi)
return "%s_%d__%d_" % grid_lbl
return f"{module}_{int(x/2)}__{int(y/2)}_"
@staticmethod
def _resolve_string(ele, param, default, variables={}):
"""
Parses the startx, starty, repeatx and repeaty variables to integer
Variables are passed in `variables` variable,
generally ``W`` and ``H`` values
"""
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(**variables)))
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 IndexError:
logger.warning("Trying to set grid location (%s %s)", x, y)
return 0
[docs] def add_fill(self, ele):
"""
Fill the grid with given element
Args:
ele (ET): Accepts Element Tree (element) as a input
"""
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)
[docs] def add_perimeter(self, ele):
"""
Add given element on the periphery of the grid
Args:
ele (ET): Accepts Element Tree (element) as a input
"""
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)
[docs] def add_corners(self, ele):
"""
Add given element on the corners of the grid
Args:
ele (ET): Accepts Element Tree (element) as a input
"""
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)
[docs] def add_single(self, ele):
"""
Add given element to the specifica location of the grid
Args:
ele (ET): Accepts Element Tree (element) as a input
ele.x (int): x locations to insert
ele.y (int): y locations to insert
"""
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, y, 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._resolve_string(ele, "startx", ele_w, var)
incrx = self._resolve_string(ele, "incrx", ele_w, var)
starty = self._resolve_string(ele, "starty", 1, var)
repy = self._resolve_string(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._resolve_string(ele, "startx", 0, var)
repeatx = self._resolve_string(ele, "repeatx", self.fpga_arch.width, var)
starty = self._resolve_string(ele, "starty", 1, var)
incry = self._resolve_string(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._resolve_string(ele, "startx", 0, var)
endx = self._resolve_string(ele, "endx", self.fpga_arch.width, var)
incrx = self._resolve_string(ele, "incrx", ele_w, var)
repeatx = self._resolve_string(ele, "repeatx", self.fpga_arch.width, var)
starty = self._resolve_string(ele, "starty", 0, var)
endy = self._resolve_string(ele, "endy", self.fpga_arch.height, var)
incry = self._resolve_string(ele, "incry", ele_h, var)
repeaty = self._resolve_string(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 + 1, incrx):
for y in range(starty, endy + 1, incry):
self._set_value(xstep + x, ystep + y, ele_type, ele_w, ele_h)
[docs] 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()
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
self._enumerate_full_grid()
return self.grid
def _enumerate_full_grid(self):
arrows = (self.RIGHT_ARROW, self.UP_ARROW)
for y in range((self.height * 2) - 1):
for x in range((self.width * 2) - 1):
grid_lbl = self.grid[math.floor(y / 2)][math.floor(x / 2)]
grid_lbl_r = (
self.grid[math.floor(y / 2)][math.floor(x / 2) + 1]
if (x + 2) < self.width * 2
else None
)
grid_lbl_l = (
self.grid[math.floor(y / 2)][math.floor(x / 2)] if x > 0 else None
)
grid_lbl_t = (
self.grid[math.floor(y / 2) + 1][math.floor(x / 2)]
if (y + 2) < self.height * 2
else None
)
grid_lbl_b = (
self.grid[math.floor(y / 2)][math.floor(x / 2)] if y > 0 else None
)
# print(
# f"{x:4} {y:4} {str(grid_lbl):10} " +
# f"{str(grid_lbl_t):10} {str(grid_lbl_b):10} " +
# f"{str(grid_lbl_l):10} {str(grid_lbl_r):10}")
module = {
True: self.UP_ARROW
if ((grid_lbl_r in arrows) and (grid_lbl_t in arrows))
else "sb",
(x % 2 == 1) and (y % 2 == 0): grid_lbl_r
if (grid_lbl_r == self.RIGHT_ARROW)
else self.UP_ARROW
if ((grid_lbl_l == self.UP_ARROW) and (grid_lbl_r == self.UP_ARROW))
else "cbx",
(x % 2 == 0) and (y % 2 == 1): grid_lbl_t
if (grid_lbl_t == self.UP_ARROW)
else "cby",
(x % 2 == 0) and (y % 2 == 0): grid_lbl,
(x == 0) and (y % 2 == 1): "EMPTY",
(x == ((self.width - 1) * 2)) and (y % 2 == 1): "EMPTY",
(x % 2 == 1) and (y == 0): "EMPTY",
(x % 2 == 1) and (y == ((self.height - 1) * 2)): "EMPTY",
}[True]
self.full_grid[y][x] = module
[docs] def validate_grid(self):
"""
Checks for the correctness of the grid
- if right and up arrows are placed correctly in the grid
- if the boundry blocks has correct grid value
"""
left_edge = [row[0] for row in self.grid]
bottom_edge = self.grid[0]
if RIGHT_ARROW in left_edge:
raise ValueError("Found right arrow on left edge")
if UP_ARROW in bottom_edge:
raise ValueError("Found up arrow on bottom edge")
@staticmethod
def _default_shaping_param():
"""
Returns default shaping parameters for rendering
"""
return {
"clb": [10, 10],
"cbx": [8, 4, None, 0],
"cby": [4, 8, 0, None],
"grid": [None, None],
"sb": [None, None, None, None, None, None, None, None],
}
def _expand_shaping_param(self):
params = self.default_parameters
params["cbx"][2] = (params["clb"][0] - params["cbx"][0]) * 0.5
params["cby"][3] = (params["clb"][1] - params["cby"][1]) * 0.5
params["grid"][0] = params["clb"][0] + params["cby"][0]
params["grid"][1] = params["clb"][1] + params["cbx"][1]
params["sb"][0] = params["cbx"][1]
params["sb"][1] = (params["clb"][0] - params["cbx"][0]) * 0.5
params["sb"][2] = (params["clb"][1] - params["cby"][1]) * 0.5
params["sb"][3] = params["cby"][0]
params["sb"][4] = (params["clb"][0] - params["cbx"][0]) * 0.5
params["sb"][5] = (params["clb"][1] - params["cby"][1]) * 0.5
params["sb"][6] = -1 * params["sb"][1]
params["sb"][7] = -1 * params["sb"][5]
return params
def add_render_symbols(self, dwg, params):
sym_map = {}
rect_symbols = {
"cbx": params["cbx"],
"cby": params["cby"],
}
clb_w = params["clb"][0]
clb_h = params["clb"][1]
grid_x = params["grid"][0]
grid_y = params["grid"][1]
for tile, pt in self.fpga_arch.pb_types.items():
rect_symbols[tile] = [
(clb_w * pt[0] if pt[0] == 1 else (grid_x * (pt[0] - 1) + clb_w))
]
rect_symbols[tile] += [
(clb_h * pt[1] if pt[1] == 1 else (grid_y * (pt[1] - 1) + clb_h))
]
rect_symbols[tile] += [0, 0] # add offset
for module, dims in rect_symbols.items():
symbol = dwg.symbol(id=module)
class_tag = "routing" if module in ("cbx", "cby") else "lb"
symbol.add(dwg.rect(size=dims[:2], class_=class_tag, insert=dims[2:]))
dwg.defs.add(symbol)
sym_map[module] = {
"symbol": symbol,
"center": (dims[0] / 2 + dims[2], dims[1] / 2 + dims[3]),
}
sb_map = {
# a, b, c, d, e, f
"sb": [1, 1, 1, 1, 1, 1, 0, 0],
"sb00": [1, 1, 1, 1, 1, 1, 1, 1], # ┿
"sb01": [1, 0, 1, 1, 1, 0, 0, 0], # ┗
"sb02": [1, 0, 1, 1, 1, 1, 0, 1], # ┝
"sb03": [1, 0, 0, 1, 1, 1, 0, 1], # ┏
"sb04": [1, 1, 0, 1, 1, 1, 1, 1], # ┯
"sb05": [1, 1, 0, 1, 0, 1, 1, 1], # ┓
"sb06": [1, 1, 1, 1, 0, 1, 1, 1], # ┫
"sb07": [1, 1, 1, 1, 0, 0, 1, 0], # ┛
"sb08": [1, 1, 1, 1, 1, 0, 1, 0], # ┷
"sb09": [1, 0, 1, 1, 0, 1, 0, 1], # ┃
"sb10": [1, 1, 0, 1, 1, 0, 1, 0], # ━
}
for module, dims in sb_map.items():
a, b, c, d, e, f, x, y = [a * b for a, b in zip(params["sb"], dims)]
symbol = dwg.symbol(id=module)
symbol["x"] = x
symbol["y"] = y
symbol.add(
dwg.path(
d=f"M {b} 0 "
+ f"v {f} h {-1*b} "
+ f"v {a} h {b} v {c} h {d} "
+ f"v {-1*c} h {e} v {-1*a} h {-1*e} "
+ f"v {-1*f}"
+ " z"
)
)
dwg.defs.add(symbol)
sym_map[module] = {"symbol": symbol, "center": ((d) / 2, (a) / 2)}
return sym_map
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 merge_symbol(self, inst_list, new_symbol_name):
points = []
def add_point(direction, distance):
if direction == "h":
new_pt = (points[-1][0] + distance, points[-1][1])
if direction == "v":
new_pt = (points[-1][0], points[-1][1] + distance)
points.append(new_pt)
for each in inst_list:
inst = self.get_instance(each)
symbol = self.get_symbol_of_instance(each)
pt = (inst.attribs["x"], inst.attribs["y"])
if symbol.elements[0].elementname == "rect":
ele = symbol.elements[0]
attrib = ele.attribs
points.append((pt[0] + attrib.get("x", 0), pt[1] + attrib.get("y", 0)))
add_point("v", float(attrib["height"]))
add_point("h", float(attrib["width"]))
add_point("v", -1 * float(attrib["height"]))
elif symbol.elements[0].elementname == "path":
ele = symbol.elements[0]
attrib = ele.attribs
pts = ele.attribs["d"].split()
print(pts)
print(pt)
points.append(
(
pt[0] + float(pts[1]) + attrib.get("x", 0),
pt[1] + float(pts[2]) + attrib.get("y", 0),
)
)
for direction, distance in zip(pts[3:-1:2], pts[4:-1:2]):
add_point(direction, float(distance))
else:
logger.error("Can not extract point from tag")
logger.debug(points)
_, points = shaping_utils.get_shapes_outline(points)
path_points = shaping_utils.points_to_path(points)
width_pt = list(accumulate(map(float, path_points.split()[3::2])))
height_pt = list(accumulate(map(float, path_points.split()[4::2])))
if path_points.split()[0] == "V":
width_pt, height_pt = height_pt, width_pt
min_x, max_x = min(width_pt), max(width_pt)
min_y, max_y = min(height_pt), max(height_pt)
pt = path_points.lower().split()
svg_path = ""
for eachpt in zip(pt[3::2], pt[4::2]):
svg_path += (
"v {} h {} ".format(*eachpt)
if pt[0] == "v"
else "h {} v {} ".format(*eachpt)
)
symbol = self.dwg.symbol(
id=new_symbol_name,
x=min_x,
y=min_y,
width=max_x - min_x,
height=max_y - min_y,
viewBox=f"{min_x} {min_y} {(max_x-min_x)} {(max_y-min_y)}",
)
symbol.add(self.dwg.path(d=f"M {pt[1]} {pt[2]} {svg_path} z"))
self.dwg.defs.add(symbol)
self.dwg_shapes.add(self.dwg.use(symbol, insert=points[0]))
# self.placement_db[symbol] = (points[0][0], points[0][1])
return symbol
def add_style(self, style):
for ele in self.dwg.defs.elements:
if ele.attribs.get("type", "") == "text/css":
ele.append(style)
ele.append("\n")
def get_instance(self, instance_name):
for ele in self.dwg_shapes.elements:
if instance_name in ele.attribs.get("id", ""):
return ele
logger.error("No matching instance found in the SVG %s", instance_name)
def get_symbol_of_instance(self, instance_name):
symbol = self.get_instance(instance_name)
try:
return self.get_symbol(symbol["xlink:href"][1:])
except TypeError:
logger.exception("%s instance not found", instance_name)
def get_symbol(self, symbol_name):
for ele in self.dwg.defs.elements:
if symbol_name in ele.attribs.get("id", ""):
return ele
[docs] def render_layout(self, filename=None, grid_io=False, markers=False):
"""
Renders the given layout
"""
params = self._expand_shaping_param()
grid_x = params["grid"][0]
grid_y = params["grid"][1]
bbox = (0, 0, grid_x * (self.width) - 2, grid_y * (self.height) - 2)
dwg = svgwrite.Drawing("_render.svg", bbox[2:], debug=False)
dwg.viewbox(0, -1 * bbox[3], bbox[2], bbox[3])
dwg.defs.add(dwg.style(CSS_STYLE))
symbol_map = self.add_render_symbols(dwg, params)
# Createing instances
dwg_main = dwg.add(Group(id="main", transform="scale(1,-1)"))
dwg_main.add(dwg.rect(size=bbox[2:], id="core_boundary", class_="boundary"))
dwg_shapes = dwg_main.add(Group(id="main_shapes"))
dwg_text = dwg_main.add(Group(id="main_text"))
visited = []
start_pt, end_pt = (0, 1) if grid_io else (1, 2)
for x_pt in range(start_pt, (2 * self.width) - end_pt):
for y_pt in range(start_pt, (2 * self.height) - end_pt):
module = self.get_top_instance(x_pt, y_pt)
inst_name = module
if module == "EMPTY":
continue
if module in visited:
continue
visited.append(module)
module = "_".join(module.split("_")[:-4])
if "sb" in module:
left = self.get_top_instance(x_pt - 1, y_pt).split("_")[0]
right = self.get_top_instance(x_pt + 1, y_pt).split("_")[0]
bottom = self.get_top_instance(x_pt, y_pt - 1).split("_")[0]
top = self.get_top_instance(x_pt, y_pt + 1).split("_")[0]
left = left if left in ("cbx", "cby") else "EMPTY"
right = right if right in ("cbx", "cby") else "EMPTY"
bottom = bottom if bottom in ("cbx", "cby") else "EMPTY"
top = top if top in ("cbx", "cby") else "EMPTY"
if (left, right, bottom, top) == ("cbx", "cbx", "cby", "cby"):
symbol = "sb00"
elif (left, right, bottom, top) == ("EMPTY", "cbx", "cby", "cby"):
symbol = "sb02"
elif (left, right, bottom, top) == ("EMPTY", "EMPTY", "cby", "cby"):
symbol = "sb09"
elif (left, right, bottom, top) == ("cbx", "cbx", "EMPTY", "EMPTY"):
symbol = "sb10"
elif (left, right, bottom) == ("cbx", "cbx", "cby"):
symbol = "sb04"
elif (left, top, bottom) == ("cbx", "cby", "cby"):
symbol = "sb06"
elif (left, right, top) == ("cbx", "cbx", "cby"):
symbol = "sb08"
elif (left, top) == ("cbx", "cby"):
symbol = "sb07"
elif (left, bottom) == ("cbx", "cby"):
symbol = "sb05"
elif (right, top) == ("cbx", "cby"):
symbol = "sb01"
elif (right, bottom) == ("cbx", "cby"):
symbol = "sb03"
else:
symbol = module
else:
symbol = module
x_pt_new = (
x_pt * 0.5 * grid_x
if (x_pt % 2) == 0
else (int(x_pt * 0.5) * grid_x) + params["clb"][0]
) # nopep8
y_pt_new = (
y_pt * 0.5 * grid_y
if (y_pt % 2) == 0
else (int(y_pt * 0.5) * grid_y) + params["clb"][1]
) # nopep8
# dwg_shapes.add(dwg.circle(r=0.02, stroke="red",
# center=(x_pt_new, y_pt_new)))
xct, yct = symbol_map[symbol]["center"]
dwg_text.add(
dwg.text(
inst_name,
insert=(x_pt_new + xct, (y_pt_new + yct) * -1),
transform="scale(1,-1)",
alignment_baseline="middle",
text_anchor="middle",
)
)
if markers:
dwg_shapes.add(
dwg.line(
start=(x_pt_new, 0),
end=(x_pt_new, bbox[3]),
class_="marker",
)
)
dwg_shapes.add(
dwg.line(
start=(0, y_pt_new),
end=(bbox[2], y_pt_new),
class_="marker",
)
)
if symbol:
dwg_shapes.add(
dwg.use(
symbol_map[symbol]["symbol"],
id=inst_name,
insert=(x_pt_new, y_pt_new),
)
)
else:
print(module)
if filename:
dwg.saveas(filename, pretty=True, indent=4)
self.dwg = dwg
self.dwg_shapes = dwg_shapes
self.dwg_text = dwg_text
return dwg
if __name__ == "__main__":
main()