| #!/usr/bin/env python3 | |
| """ | |
| This utility script allows to visualize VPR placement. It reads a VPR .place | |
| file and generates a bitmap with the visualization. | |
| """ | |
| import argparse | |
| import re | |
| import itertools | |
| from PIL import Image, ImageDraw | |
| # ============================================================================= | |
| def load_placement(placement_file): | |
| """ | |
| Loads VPR placement file. Returns a tuple with the grid size and a dict | |
| indexed by locations that contains top-level block names. | |
| """ | |
| RE_PLACEMENT = re.compile( | |
| r"^\s*(?P<net>\S+)\s+(?P<x>[0-9]+)\s+(?P<y>[0-9]+)\s+(?P<z>[0-9]+)" | |
| ) | |
| RE_GRID_SIZE = re.compile( | |
| r"Array size:\s+(?P<x>[0-9]+)\s+x\s+(?P<y>[0-9]+)\s+logic blocks" | |
| ) | |
| # Load the file | |
| with open(placement_file, "r") as fp: | |
| lines = fp.readlines() | |
| # Parse | |
| grid_size = None | |
| placement = {} | |
| for line in lines: | |
| line = line.strip() | |
| if line.startswith("#"): | |
| continue | |
| # Placement | |
| match = RE_PLACEMENT.match(line) | |
| if match is not None: | |
| loc = (int(match.group("x")), int(match.group("y"))) | |
| placement[loc] = match.group("net") | |
| # Grid size | |
| match = RE_GRID_SIZE.match(line) | |
| if match is not None: | |
| grid_size = (int(match.group("x")), int(match.group("y"))) | |
| return grid_size, placement | |
| def generate_image(grid_size, placement, block_size=8, colormap=None): | |
| """ | |
| Generates a visualization of the placement. | |
| """ | |
| block_size = max(block_size, 3) | |
| gap_size = 1 | |
| cell_size = block_size + 2 * gap_size | |
| # Create new image | |
| dx = grid_size[0] * cell_size + 1 | |
| dy = grid_size[1] * cell_size + 1 | |
| image = Image.new("RGB", (dx, dy), color="#FFFFFF") | |
| # Draw stuff | |
| draw = ImageDraw.Draw(image) | |
| for cx, cy in itertools.product(range(grid_size[0]), range(grid_size[1])): | |
| x0 = cx * cell_size + gap_size | |
| y0 = cy * cell_size + gap_size | |
| x1 = x0 + block_size | |
| y1 = y0 + block_size | |
| if (cx, cy) in placement: | |
| name = placement[(cx, cy)] | |
| if colormap is not None and name in colormap: | |
| color = colormap[name] | |
| else: | |
| color = "#2080C0" | |
| else: | |
| color = "#FFFFFF" | |
| draw.rectangle((x0, y0, x1, y1), color, "#000000") | |
| return image | |
| # ============================================================================= | |
| def main(): | |
| # Parse arguments | |
| parser = argparse.ArgumentParser( | |
| description=__doc__, | |
| formatter_class=argparse.RawDescriptionHelpFormatter | |
| ) | |
| parser.add_argument("place", type=str, help="A VPR .place file") | |
| parser.add_argument( | |
| "-o", type=str, default="placement.png", help="Output image file" | |
| ) | |
| parser.add_argument( | |
| "--block-size", type=int, default=4, help="Block size (def. 4)" | |
| ) | |
| args = parser.parse_args() | |
| # Load placement | |
| grid_size, placement = load_placement(args.place) | |
| # Colormap | |
| colormap = { | |
| "$true": "#C02020", | |
| "$false": "#000000", | |
| } | |
| # Generate the image | |
| image = generate_image(grid_size, placement, args.block_size, colormap) | |
| image.save(args.o) | |
| # ============================================================================= | |
| if __name__ == "__main__": | |
| main() |