blob: c844385e5cb328c63a8969aa8838e518e93af353 [file] [log] [blame] [edit]
#!/usr/bin/env python3
import os
import argparse
import pickle
from collections import OrderedDict
import lxml.etree as ET
from data_structs import ConnectionType, Loc
from tile_import import make_top_level_pb_type
from tile_import import make_top_level_tile
# =============================================================================
def is_direct(connection):
"""
Returns True if the connections spans two tiles directly. Not necessarly
at the same location
"""
if connection.src.type == ConnectionType.TILE and \
connection.dst.type == ConnectionType.TILE and \
connection.is_direct is True:
return True
return False
# =============================================================================
def add_segment(xml_parent, segment):
"""
Adds a segment
"""
segment_type = "bidir"
# Make XML
xml_seg = ET.SubElement(
xml_parent, "segment", {
"name": segment.name,
"length": str(segment.length),
"freq": "1.0",
"type": segment_type,
"Rmetal": str(segment.r_metal),
"Cmetal": str(segment.c_metal),
}
)
if segment_type == "unidir":
ET.SubElement(xml_seg, "mux", {"name": "generic"})
elif segment_type == "bidir":
ET.SubElement(xml_seg, "wire_switch", {"name": "generic"})
ET.SubElement(xml_seg, "opin_switch", {"name": "generic"})
else:
assert False, segment_type
e = ET.SubElement(xml_seg, "sb", {"type": "pattern"})
e.text = " ".join(["1" for i in range(segment.length + 1)])
e = ET.SubElement(xml_seg, "cb", {"type": "pattern"})
e.text = " ".join(["1" for i in range(segment.length)])
def add_switch(xml_parent, switch):
"""
Adds a switch
"""
xml_switch = ET.SubElement(
xml_parent, "switch", {
"type": switch.type,
"name": switch.name,
"R": str(switch.r),
"Cin": str(switch.c_in),
"Cout": str(switch.c_out),
"Tdel": str(switch.t_del),
}
)
if switch.type in ["mux", "tristate"]:
xml_switch.attrib["Cinternal"] = str(switch.c_int)
def initialize_arch(xml_arch, switches, segments):
"""
Initializes the architecture definition from scratch.
"""
# .................................
# Device
xml_device = ET.SubElement(xml_arch, "device")
ET.SubElement(
xml_device, "sizing", {
"R_minW_nmos": "6000.0",
"R_minW_pmos": "18000.0",
}
)
ET.SubElement(xml_device, "area", {"grid_logic_tile_area": "15000.0"})
xml = ET.SubElement(xml_device, "chan_width_distr")
ET.SubElement(xml, "x", {"distr": "uniform", "peak": "1.0"})
ET.SubElement(xml, "y", {"distr": "uniform", "peak": "1.0"})
ET.SubElement(
xml_device, "connection_block", {"input_switch_name": "generic"}
)
ET.SubElement(xml_device, "switch_block", {
"type": "wilton",
"fs": "3",
})
ET.SubElement(
xml_device, "default_fc", {
"in_type": "frac",
"in_val": "1.0",
"out_type": "frac",
"out_val": "1.0",
}
)
# .................................
# Switchlist
xml_switchlist = ET.SubElement(xml_arch, "switchlist")
got_generic_switch = False
for switch in switches:
add_switch(xml_switchlist, switch)
# Check for the generic switch
if switch.name == "generic":
got_generic_switch = True
# No generic switch
assert got_generic_switch
# .................................
# Segmentlist
xml_seglist = ET.SubElement(xml_arch, "segmentlist")
for segment in segments:
add_segment(xml_seglist, segment)
def write_tiles(xml_arch, arch_tile_types, tile_types, equivalent_sites):
"""
Generates the "tiles" section of the architecture file
"""
# The "tiles" section
xml_tiles = xml_arch.find("tiles")
if xml_tiles is None:
xml_tiles = ET.SubElement(xml_arch, "tiles")
# Add tiles
for tile_type, sub_tiles in arch_tile_types.items():
xml = make_top_level_tile(
tile_type, sub_tiles, tile_types, equivalent_sites
)
xml_tiles.append(xml)
def write_pb_types(xml_arch, arch_pb_types, tile_types, nsmap):
"""
Generates the "complexblocklist" section.
"""
# Complexblocklist
xml_cplx = xml_arch.find("complexblocklist")
if xml_cplx is None:
xml_cplx = ET.SubElement(xml_arch, "complexblocklist")
# Add pb_types
for pb_type in arch_pb_types:
xml = make_top_level_pb_type(tile_types[pb_type], nsmap)
xml_cplx.append(xml)
def write_models(xml_arch, arch_models, nsmap):
"""
Generates the "models" section.
"""
# Models
xml_models = xml_arch.find("models")
if xml_models is None:
xml_models = ET.SubElement(xml_arch, "models")
# Include cell models
xi_include = "{{{}}}include".format(nsmap["xi"])
for model in arch_models:
name = model.lower()
# Be smart. Check if there is a file for that cell in the current
# directory. If not then use the one from "primitives" path
model_file = "./{}.model.xml".format(name)
if not os.path.isfile(model_file):
model_file = "../../primitives/{}/{}.model.xml".format(name, name)
ET.SubElement(
xml_models, xi_include, {
"href": model_file,
"xpointer": "xpointer(models/child::node())",
}
)
def write_tilegrid(xml_arch, arch_tile_grid, loc_map, layout_name):
"""
Generates the "layout" section of the arch XML and appends it to the
root given.
"""
# Remove the "layout" tag if any
xml_layout = xml_arch.find("layout")
if xml_layout is not None:
xml_arch.remove(xml_layout)
# Grid size
xs = [flat_loc[0] for flat_loc in arch_tile_grid]
ys = [flat_loc[1] for flat_loc in arch_tile_grid]
w = max(xs) + 1
h = max(ys) + 1
# Fixed layout
xml_layout = ET.SubElement(xml_arch, "layout")
xml_fixed = ET.SubElement(
xml_layout, "fixed_layout", {
"name": layout_name,
"width": str(w),
"height": str(h),
}
)
# Individual tiles
for flat_loc, tile in arch_tile_grid.items():
if tile is None:
continue
# Unpack
tile_type, capacity = tile
# Single tile
xml_sing = ET.SubElement(
xml_fixed,
"single",
{
"type": "TL-{}".format(tile_type.upper()),
"x": str(flat_loc[0]),
"y": str(flat_loc[1]),
"priority": str(10), # Not sure if we need this
}
)
# Gather metadata
metadata = []
for i in range(capacity):
loc = Loc(x=flat_loc[0], y=flat_loc[1], z=i)
if loc in loc_map.bwd:
phy_loc = loc_map.bwd[loc]
metadata.append("X{}Y{}".format(phy_loc.x, phy_loc.y))
# Emit metadata if any
if len(metadata):
xml_metadata = ET.SubElement(xml_sing, "metadata")
xml_meta = ET.SubElement(
xml_metadata, "meta", {
"name": "fasm_prefix",
}
)
xml_meta.text = " ".join(metadata)
def write_direct_connections(xml_arch, tile_grid, connections):
"""
"""
def get_tile(ep):
"""
Retireves tile for the given connection endpoint
"""
if ep.loc in tile_grid and tile_grid[ep.loc] is not None:
return tile_grid[ep.loc]
else:
print("ERROR: No tile found for the connection endpoint", ep)
return None
# Remove the "directlist" tag if any
xml_directlist = xml_arch.find("directlist")
if xml_directlist is not None:
xml_arch.remove(xml_directlist)
# Make a new one
xml_directlist = ET.SubElement(xml_arch, "directlist")
# Populate connections
conns = [c for c in connections if is_direct(c)]
for connection in conns:
src_tile = get_tile(connection.src)
dst_tile = get_tile(connection.dst)
if not src_tile or not dst_tile:
continue
src_name = "TL-{}.{}".format(src_tile.type, connection.src.pin)
dst_name = "TL-{}.{}".format(dst_tile.type, connection.dst.pin)
name = "{}_at_X{}Y{}Z{}_to_{}_at_X{}Y{}Z{}".format(
src_name,
connection.src.loc.x,
connection.src.loc.y,
connection.src.loc.z,
dst_name,
connection.dst.loc.x,
connection.dst.loc.y,
connection.dst.loc.z,
)
delta_loc = Loc(
x=connection.dst.loc.x - connection.src.loc.x,
y=connection.dst.loc.y - connection.src.loc.y,
z=connection.dst.loc.z - connection.src.loc.z,
)
# Format the direct connection tag
ET.SubElement(
xml_directlist, "direct", {
"name": name,
"from_pin": src_name,
"to_pin": dst_name,
"x_offset": str(delta_loc.x),
"y_offset": str(delta_loc.y),
"z_offset": str(delta_loc.z),
}
)
# =============================================================================
def main():
# Parse arguments
parser = argparse.ArgumentParser(
description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter
)
parser.add_argument(
"--vpr-db", type=str, required=True, help="VPR database file"
)
parser.add_argument(
"--arch-out",
type=str,
default="arch.xml",
help="Output arch XML file (def. arch.xml)"
)
parser.add_argument(
"--device",
type=str,
default="quicklogic",
help="Device name for the architecture"
)
args = parser.parse_args()
xi_url = "http://www.w3.org/2001/XInclude"
ET.register_namespace("xi", xi_url)
nsmap = {"xi": xi_url}
# Load data from the database
with open(args.vpr_db, "rb") as fp:
db = pickle.load(fp)
loc_map = db["loc_map"]
vpr_tile_types = db["vpr_tile_types"]
vpr_tile_grid = db["vpr_tile_grid"]
vpr_equivalent_sites = db["vpr_equivalent_sites"]
segments = db["segments"]
switches = db["switches"]
connections = db["connections"]
# Flatten the VPR tilegrid
flat_tile_grid = dict()
for vpr_loc, tile in vpr_tile_grid.items():
flat_loc = (vpr_loc.x, vpr_loc.y)
if flat_loc not in flat_tile_grid:
flat_tile_grid[flat_loc] = {}
if tile is not None:
flat_tile_grid[flat_loc][vpr_loc.z] = tile.type
# Create the arch tile grid and arch tile types
arch_tile_grid = dict()
arch_tile_types = dict()
arch_pb_types = set()
arch_models = set()
for flat_loc, tiles in flat_tile_grid.items():
if len(tiles):
# Group identical sub-tiles together, maintain their order
sub_tiles = OrderedDict()
for z, tile in tiles.items():
if tile not in sub_tiles:
sub_tiles[tile] = 0
sub_tiles[tile] += 1
# TODO: Make arch tile type name
tile_type = tiles[0]
# Create the tile type with sub tile types for the arch
arch_tile_types[tile_type] = sub_tiles
# Add each sub-tile to top-level pb_type list
for tile in sub_tiles:
arch_pb_types.add(tile)
# Add each cell of a sub-tile to the model list
for tile in sub_tiles:
for cell_type in vpr_tile_types[tile].cells.keys():
arch_models.add(cell_type)
# Add the arch tile type to the arch tile grid
arch_tile_grid[flat_loc] = (
tile_type,
len(tiles),
)
else:
# Add an empty location
arch_tile_grid[flat_loc] = None
# Initialize the arch XML if file not given
xml_arch = ET.Element("architecture", nsmap=nsmap)
initialize_arch(xml_arch, switches, segments)
# Add tiles
write_tiles(
xml_arch, arch_tile_types, vpr_tile_types, vpr_equivalent_sites
)
# Add pb_types
write_pb_types(xml_arch, arch_pb_types, vpr_tile_types, nsmap)
# Add models
write_models(xml_arch, arch_models, nsmap)
# Write the tilegrid to arch
write_tilegrid(xml_arch, arch_tile_grid, loc_map, args.device)
# Write direct connections
write_direct_connections(xml_arch, vpr_tile_grid, connections)
# Save the arch
ET.ElementTree(xml_arch).write(
args.arch_out,
pretty_print=True,
xml_declaration=True,
encoding="utf-8"
)
# =============================================================================
if __name__ == "__main__":
main()