|  | #!/usr/bin/env python3 | 
|  | # | 
|  | #  Copyright (C) 2015  Clifford Wolf <clifford@clifford.at> | 
|  | # | 
|  | #  Permission to use, copy, modify, and/or distribute this software for any | 
|  | #  purpose with or without fee is hereby granted, provided that the above | 
|  | #  copyright notice and this permission notice appear in all copies. | 
|  | # | 
|  | #  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | 
|  | #  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | 
|  | #  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | 
|  | #  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | 
|  | #  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | 
|  | #  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | 
|  | #  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | 
|  | # | 
|  |  | 
|  | import icebox | 
|  | import getopt, sys, os, re | 
|  |  | 
|  | chipname = "iCE40 HX1K" | 
|  | chipdbfile = "chipdb-1k.txt" | 
|  | outdir = None | 
|  | mode8k = False | 
|  | mode384 = False | 
|  | tx, ty = 0, 0 | 
|  |  | 
|  | def usage(): | 
|  | print("Usage: %s [options]" % sys.argv[0]) | 
|  | print("  -x tile_x_coordinate") | 
|  | print("  -y tile_y_coordinate") | 
|  | print("  -d outdir") | 
|  | print("  -8") | 
|  | print("  -3") | 
|  | sys.exit(0) | 
|  |  | 
|  | try: | 
|  | opts, args = getopt.getopt(sys.argv[1:], "x:y:d:83") | 
|  | except: | 
|  | usage() | 
|  |  | 
|  | for o, a in opts: | 
|  | if o == "-x": | 
|  | tx = int(a) | 
|  | elif o == "-y": | 
|  | ty = int(a) | 
|  | elif o == "-d": | 
|  | outdir = a | 
|  | elif o == "-8": | 
|  | mode8k = True | 
|  | chipname = "iCE40 HX8K" | 
|  | chipdbfile = "chipdb-8k.txt" | 
|  | elif o == "-3": | 
|  | mode384 = True | 
|  | chipname = "iCE40 LP384" | 
|  | chipdbfile = "chipdb-384.txt" | 
|  | else: | 
|  | usage() | 
|  |  | 
|  | if len(args) != 0: | 
|  | usage() | 
|  |  | 
|  | ic = icebox.iceconfig() | 
|  |  | 
|  | mktiles = set() | 
|  |  | 
|  | if mode384: | 
|  | ic.setup_empty_384() | 
|  |  | 
|  | for x in range(1, 7): # IO top/bottom | 
|  | mktiles.add((x, 0)) | 
|  | mktiles.add((x, 9)) | 
|  |  | 
|  | for x in list(range(1, 3)) + list(range(5, 7)): # corners | 
|  | mktiles.add((x, 1)) | 
|  | mktiles.add((x, 8)) | 
|  |  | 
|  | for x in [1,6]: | 
|  | mktiles.add((x, 2)) | 
|  | mktiles.add((x, 7)) | 
|  |  | 
|  | for y in range(1, 9): # left/right IO | 
|  | mktiles.add((0, y)) | 
|  | mktiles.add((7, y)) | 
|  |  | 
|  | for x in range(3, 5): # middle square | 
|  | for y in range(4, 6): | 
|  | mktiles.add((x, y)) | 
|  |  | 
|  | elif mode8k: | 
|  | ic.setup_empty_8k() | 
|  |  | 
|  | for x in list(range(1, 3)) + list(range(8-2, 8+3)) + list(range(15, 19)) + list(range(25-2, 25+3)) + list(range(33-2, 33)): | 
|  | mktiles.add((x, 0)) | 
|  | mktiles.add((x, 33)) | 
|  |  | 
|  | for x in list(range(0, 3)) + list(range(8-1, 8+2)) + list(range(25-1, 25+2)) + list(range(33-2, 34)): | 
|  | mktiles.add((x, 1)) | 
|  | mktiles.add((x, 32)) | 
|  |  | 
|  | for x in list(range(0, 2)) + list(range(8-1, 8+2)) + list(range(25-1, 25+2)) + list(range(34-2, 34)): | 
|  | mktiles.add((x, 2)) | 
|  | mktiles.add((x, 31)) | 
|  |  | 
|  | for x in [0, 33]: | 
|  | mktiles.add((x, 15)) | 
|  | mktiles.add((x, 16)) | 
|  | mktiles.add((x, 17)) | 
|  | mktiles.add((x, 18)) | 
|  |  | 
|  | for x in [16, 17]: | 
|  | mktiles.add((x, 16)) | 
|  | mktiles.add((x, 17)) | 
|  |  | 
|  | else: | 
|  | ic.setup_empty_1k() | 
|  |  | 
|  | for x in range(1, 13): | 
|  | mktiles.add((x, 0)) | 
|  | mktiles.add((x, 17)) | 
|  |  | 
|  | for x in list(range(0, 6)) + list(range(8, 14)): | 
|  | mktiles.add((x, 1)) | 
|  | mktiles.add((x, 16)) | 
|  |  | 
|  | for x in list(range(0, 5)) + list(range(9, 14)): | 
|  | mktiles.add((x, 2)) | 
|  | mktiles.add((x, 15)) | 
|  |  | 
|  | for y in range(7, 11): | 
|  | mktiles.add((0, y)) | 
|  | mktiles.add((13, y)) | 
|  |  | 
|  | for x in range(6, 8): | 
|  | for y in range(8, 10): | 
|  | mktiles.add((x, y)) | 
|  |  | 
|  | expand_count=[0] | 
|  |  | 
|  | def print_expand_div(title): | 
|  | print('<a id="exph%d" href="#" onclick="document.getElementById(\'exph%d\').style.display=\'none\'; document.getElementById(\'exp%d\').style.display=\'block\'; return false">[+] Show %s</a><div id="exp%d" style="display:none">' % (expand_count[0], expand_count[0], expand_count[0], title, expand_count[0])) | 
|  | expand_count[0] += 1 | 
|  |  | 
|  | def print_expand_end(): | 
|  | print('</div>') | 
|  |  | 
|  | def print_expand_all(): | 
|  | print('<a id="exph%d" href="#" onclick="for (i = 0; i < 100; i++) { document.getElementById(\'exph\'+i).style.display=\'none\'; document.getElementById(\'exp\'+i).style.display=\'block\'; }; return false">[+] Expand All</a><span id="exp%d" style="display:none"></span>' % (expand_count[0], expand_count[0])) | 
|  | expand_count[0] += 1 | 
|  |  | 
|  | def print_index(): | 
|  | print("<title>Project IceStorm – %s Overview</title>" % chipname) | 
|  | print("<h1>Project IceStorm – %s Overview</h1>" % chipname) | 
|  |  | 
|  | print("""<i><a href="http://www.clifford.at/icestorm/">Project IceStorm</a> aims at documenting the bitstream format of Lattice iCE40 FPGAs | 
|  | and providing simple tools for analyzing and creating bitstream files. This is work in progress.</i>""") | 
|  |  | 
|  | print("""<p>This documentation is auto-generated by <tt>icebox_html.py</tt> from IceBox.<br/> | 
|  | A machine-readable form of the database can be downloaded <a href="%s">here</a>.</p>""" % chipdbfile) | 
|  |  | 
|  | print("""<p>The iCE40 FPGA fabric is organized into tiles. The configuration bits | 
|  | themselves have the same meaning in all tiles of the same type. But the way the tiles | 
|  | are connected to each other depends on the types of neighbouring cells. Furthermore, | 
|  | some wire names are different for e.g. an IO tile on the left border and an IO tile on | 
|  | the top border.</p>""") | 
|  |  | 
|  | print("""<p>Click on a highlighted tile below to view the bitstream details for the | 
|  | tile. The highlighted tiles cover all combinations of neighbouring cells that can be found | 
|  | in iCE40 FPGAs.</p>""") | 
|  |  | 
|  | print('<p><table border="1">') | 
|  | for y in range(ic.max_y, -1, -1): | 
|  | print("<tr>") | 
|  | for x in range(ic.max_x + 1): | 
|  | if mode8k: | 
|  | fontsize="8px" | 
|  | print('<td style="width:25px; height:20px;" align="center" valign="center"', end="") | 
|  | else: | 
|  | fontsize="10px" | 
|  | print('<td style="width:40px; height:40px;" align="center" valign="center"', end="") | 
|  | if ic.tile_pos(x, y) == None: | 
|  | print('> </td>') | 
|  | elif (x, y) in mktiles: | 
|  | if ic.tile_type(x, y) == "IO": color = "#aee" | 
|  | if ic.tile_type(x, y) == "LOGIC": color = "#eae" | 
|  | if ic.tile_type(x, y) == "RAMB": color = "#eea" | 
|  | if ic.tile_type(x, y) == "RAMT": color = "#eea" | 
|  | print('bgcolor="%s"><span style="font-size:%s"><a style="color:#000; text-decoration:none" href="tile_%d_%d.html"><b>%s<br/>(%d %d)</b></a></span></td>' % (color, fontsize, x, y, ic.tile_type(x, y), x, y)) | 
|  | else: | 
|  | if ic.tile_type(x, y) == "IO": color = "#8aa" | 
|  | if ic.tile_type(x, y) == "LOGIC": color = "#a8a" | 
|  | if ic.tile_type(x, y) == "RAMB": color = "#aa8" | 
|  | if ic.tile_type(x, y) == "RAMT": color = "#aa8" | 
|  | print('bgcolor="%s"><span style="font-size:%s">%s<br/>(%d %d)</span></td>' % (color, fontsize, ic.tile_type(x, y), x, y)) | 
|  | print("</tr>") | 
|  | print("</table></p>") | 
|  |  | 
|  | def print_tile(tx, ty): | 
|  | tile = ic.tile(tx, ty) | 
|  | tile_type = ic.tile_type(tx, ty) | 
|  |  | 
|  | print("<title>Project IceStorm – %s %s Tile (%d %d)</title>" % (chipname, tile_type, tx, ty)) | 
|  | print("<h1>Project IceStorm – %s %s Tile (%d %d)</h1>" % (chipname, tile_type, tx, ty)) | 
|  |  | 
|  | print("""<i><a href="http://www.clifford.at/icestorm/">Project IceStorm</a> aims at documenting the bitstream format of Lattice iCE40 FPGAs | 
|  | and providing simple tools for analyzing and creating bitstream files. This is work in progress.</i>""") | 
|  |  | 
|  | print("""<p>This page describes the %s Tile (%d %d), what nets and | 
|  | configuration bits it has and how it is connected to its neighbourhood.</p>""" % (tile_type, tx, ty)) | 
|  |  | 
|  | visible_tiles = set() | 
|  |  | 
|  | print('<p><table border="1">') | 
|  | for y in range(ty+2, ty-3, -1): | 
|  | print("<tr>") | 
|  | for x in range(tx-2, tx+3): | 
|  | print('<td style="width:100px; height:70px;" align="center" valign="center"', end="") | 
|  | if ic.tile_pos(x, y) == None: | 
|  | print('> </td>') | 
|  | else: | 
|  | if (x, y) in mktiles: | 
|  | if ic.tile_type(x, y) == "IO": color = "#aee" | 
|  | if ic.tile_type(x, y) == "LOGIC": color = "#eae" | 
|  | if ic.tile_type(x, y) == "RAMB": color = "#eea" | 
|  | if ic.tile_type(x, y) == "RAMT": color = "#eea" | 
|  | print('bgcolor="%s"><a style="color:#000; text-decoration:none" href="tile_%d_%d.html"><b>%s Tile<br/>(%d %d)</b></a></td>' % (color, x, y, ic.tile_type(x, y), x, y)) | 
|  | else: | 
|  | if ic.tile_type(x, y) == "IO": color = "#8aa" | 
|  | if ic.tile_type(x, y) == "LOGIC": color = "#a8a" | 
|  | if ic.tile_type(x, y) == "RAMB": color = "#aa8" | 
|  | if ic.tile_type(x, y) == "RAMT": color = "#aa8" | 
|  | print('bgcolor="%s">%s Tile<br/>(%d %d)</td>' % (color, ic.tile_type(x, y), x, y)) | 
|  | visible_tiles.add((x, y)) | 
|  | print("</tr>") | 
|  | print("</table></p>") | 
|  |  | 
|  | # print_expand_all() | 
|  |  | 
|  | print("<h2>Configuration Bitmap</h2>") | 
|  |  | 
|  | print("<p>A %s Tile has %d config bits in %d groups of %d bits each:<br/>" % (tile_type, len(tile)*len(tile[0]), len(tile), len(tile[0]))) | 
|  | print(("<tt>%s</tt></p>" % (", ".join(['%sB%d[%d:0]' % (" " if i < 10 else "", i, len(tile[i])-1) for i in range(len(tile))]))).replace(" B8", "<br/> B8")) | 
|  |  | 
|  | bitmap_cells = list() | 
|  | for line_nr in range(len(tile)): | 
|  | line = list() | 
|  | bitmap_cells.append(line) | 
|  | for bit_nr in range(len(tile[line_nr])): | 
|  | line.append({"bgcolor": "#aaa", "label": "?"}) | 
|  |  | 
|  | for entry in ic.tile_db(tx, ty): | 
|  | if not ic.tile_has_entry(tx, ty, entry): | 
|  | continue | 
|  | for bit in [bit.replace("!", "") for bit in entry[0]]: | 
|  | match = re.match(r"B(\d+)\[(\d+)\]$", bit) | 
|  | idx1 = int(match.group(1)) | 
|  | idx2 = int(match.group(2)) | 
|  | if entry[1] == "routing": | 
|  | bitmap_cells[idx1][idx2]["bgcolor"] = "#faa" | 
|  | bitmap_cells[idx1][idx2]["label"] = "R" | 
|  | bitmap_cells[idx1][idx2]["is_routing"] = True | 
|  | elif entry[1] == "buffer": | 
|  | bitmap_cells[idx1][idx2]["bgcolor"] = "#afa" | 
|  | bitmap_cells[idx1][idx2]["label"] = "B" | 
|  | bitmap_cells[idx1][idx2]["is_routing"] = True | 
|  | else: | 
|  | bitmap_cells[idx1][idx2]["bgcolor"] = "#aaf" | 
|  | if entry[1] == "ColBufCtrl": | 
|  | bitmap_cells[idx1][idx2]["label"] = "O" | 
|  | elif entry[1].startswith("LC_"): | 
|  | bitmap_cells[idx1][idx2]["label"] = "L" | 
|  | elif entry[1].startswith("NegClk"): | 
|  | bitmap_cells[idx1][idx2]["label"] = "N" | 
|  | elif entry[1].startswith("CarryInSet"): | 
|  | bitmap_cells[idx1][idx2]["label"] = "C" | 
|  | elif entry[1].startswith("IOB_"): | 
|  | bitmap_cells[idx1][idx2]["label"] = "I" | 
|  | elif entry[1].startswith("IoCtrl"): | 
|  | bitmap_cells[idx1][idx2]["label"] = "T" | 
|  | elif entry[1] == "Icegate": | 
|  | bitmap_cells[idx1][idx2]["label"] = "G" | 
|  | elif entry[1].startswith("Cascade"): | 
|  | bitmap_cells[idx1][idx2]["label"] = "A" | 
|  | elif entry[1].startswith("RamConfig"): | 
|  | bitmap_cells[idx1][idx2]["label"] = "M" | 
|  | elif entry[1].startswith("RamCascade"): | 
|  | bitmap_cells[idx1][idx2]["label"] = "M" | 
|  | elif entry[1].startswith("PLL"): | 
|  | bitmap_cells[idx1][idx2]["label"] = "P" | 
|  | else: | 
|  | assert False | 
|  | bitmap_cells[idx1][idx2]["label"] = '<a style="color:#666; text-decoration:none" href="#B.%d.%d">%s</a>' % (idx1, idx2, bitmap_cells[idx1][idx2]["label"]) | 
|  |  | 
|  | print('<table style="font-size:small">') | 
|  | print("<tr><td></td>") | 
|  | for cell_nr in range(len(line)): | 
|  | print('<td align="center" width="15">%d</td>' % cell_nr) | 
|  | print("<td></td></tr>") | 
|  | for line_nr, line in enumerate(bitmap_cells): | 
|  | print("<tr>") | 
|  | print('<td>B%d</td>' % line_nr) | 
|  | for cell in line: | 
|  | print('<td align="center" bgcolor="%s" style="color:#666;">%s</td>' % (cell["bgcolor"], cell["label"])) | 
|  | print('<td>B%d</td>' % line_nr) | 
|  | print("</tr>") | 
|  | print("<tr><td></td>") | 
|  | for cell_nr in range(len(line)): | 
|  | print('<td align="center">%d</td>' % cell_nr) | 
|  | print("<td></td></tr>") | 
|  | print("</table>") | 
|  |  | 
|  | print("<h2>Nets and Connectivity</h2>") | 
|  |  | 
|  | print("""<p>This section lists all nets in the tile and how this | 
|  | nets are connected with nets from cells in its neighbourhood.</p>""") | 
|  |  | 
|  | grouped_segs = ic.group_segments(set([(tx, ty)])) | 
|  | groups_indexed = dict() | 
|  | this_tile_nets = dict() | 
|  |  | 
|  | for segs in sorted(grouped_segs): | 
|  | this_segs = list() | 
|  | neighbour_segs = dict() | 
|  | for s in segs: | 
|  | if s[0] == tx and s[1] == ty: | 
|  | this_segs.append(s[2]) | 
|  | match = re.match(r"(.*?_)(\d+)(.*)", s[2]) | 
|  | if match: | 
|  | this_tile_nets.setdefault(match.group(1) + "*" + match.group(3), set()).add(int(match.group(2))) | 
|  | else: | 
|  | this_tile_nets.setdefault(s[2], set()).add(-1) | 
|  | if (s[0], s[1]) in visible_tiles: | 
|  | neighbour_segs.setdefault((s[0], s[1]), list()).append(s[2]) | 
|  | if this_segs: | 
|  | this_name = ", ".join(sorted(this_segs)) | 
|  | assert this_name not in groups_indexed | 
|  | groups_indexed[this_name] = neighbour_segs | 
|  |  | 
|  | print("<h3>List of nets in %s Tile (%d %d)</h3>" % (tile_type, tx, ty)) | 
|  |  | 
|  | def net2cat(netname): | 
|  | cat = (99, "Unsorted") | 
|  | if netname.startswith("glb_netwk_"): cat = (10, "Global Networks") | 
|  | if netname.startswith("glb2local_"): cat = (10, "Global Networks") | 
|  | if netname.startswith("fabout"): cat = (10, "Global Networks") | 
|  | if netname.startswith("local_"): cat = (20, "Local Tracks") | 
|  | if netname.startswith("carry_in"): cat = (25, "Logic Block") | 
|  | if netname.startswith("io_"): cat = (25, "IO Block") | 
|  | if netname.startswith("ram"): cat = (25, "RAM Block") | 
|  | if netname.startswith("lutff_"): cat = (25, "Logic Block") | 
|  | if netname.startswith("lutff_0"): cat = (30, "Logic Unit 0") | 
|  | if netname.startswith("lutff_1"): cat = (30, "Logic Unit 1") | 
|  | if netname.startswith("lutff_2"): cat = (30, "Logic Unit 2") | 
|  | if netname.startswith("lutff_3"): cat = (30, "Logic Unit 3") | 
|  | if netname.startswith("lutff_4"): cat = (30, "Logic Unit 4") | 
|  | if netname.startswith("lutff_5"): cat = (30, "Logic Unit 5") | 
|  | if netname.startswith("lutff_6"): cat = (30, "Logic Unit 6") | 
|  | if netname.startswith("lutff_7"): cat = (30, "Logic Unit 7") | 
|  | if netname.startswith("neigh_op_"): cat = (40, "Neighbourhood") | 
|  | if netname.startswith("logic_op_"): cat = (40, "Neighbourhood") | 
|  | if netname.startswith("sp4_v_"): cat = (50, "Span-4 Vertical") | 
|  | if netname.startswith("span4_vert_"): cat = (50, "Span-4 Vertical") | 
|  | if netname.startswith("sp4_r_v_"): cat = (55, "Span-4 Right Vertical") | 
|  | if netname.startswith("sp4_h_"): cat = (60, "Span-4 Horizontal") | 
|  | if netname.startswith("span4_horz_"): cat = (60, "Span-4 Horizontal") | 
|  | if netname.startswith("sp12_v_"): cat = (70, "Span-12 Vertical") | 
|  | if netname.startswith("span12_vert_"): cat = (70, "Span-12 Vertical") | 
|  | if netname.startswith("sp12_h_"): cat = (80, "Span-12 Horizontal") | 
|  | if netname.startswith("span12_horz_"): cat = (80, "Span-12 Horizontal") | 
|  | return cat | 
|  |  | 
|  | nets_in_cats = dict() | 
|  |  | 
|  | for this_name in sorted(this_tile_nets): | 
|  | nets_in_cats.setdefault(net2cat(this_name), list()).append(this_name) | 
|  |  | 
|  | for cat in sorted(nets_in_cats): | 
|  | print('<h4>%s</h4>' % cat[1]) | 
|  | print('<p><ul style="margin:0">') | 
|  | for this_name in sorted(nets_in_cats[cat]): | 
|  | indices = [i for i in this_tile_nets[this_name] if i >= 0] | 
|  | if -1 in this_tile_nets[this_name]: | 
|  | print("<li><tt>%s</tt></li>" % this_name) | 
|  | if len(indices) == 1: | 
|  | print("<li><tt>%s</tt></li>" % this_name.replace("*", "%d" % indices[0])) | 
|  | elif len(indices) > 0: | 
|  | print("<li><tt>%s</tt></li>" % this_name.replace("*", "{" + ",".join(["%d" % i for i in sorted(indices)]) + "}")) | 
|  | print("</ul></p>") | 
|  |  | 
|  | print("<h3>Nets and their permanent connections to nets in neighbour tiles</h3>") | 
|  |  | 
|  | # print_expand_div("connection details") | 
|  |  | 
|  | all_cats = set() | 
|  | for this_name in sorted(groups_indexed): | 
|  | all_cats.add(net2cat(this_name)) | 
|  |  | 
|  | for cat in sorted(all_cats): | 
|  | print('<h4>%s</h4>' % cat[1]) | 
|  | print('<p><div style="-webkit-column-count: 3; -moz-column-count: 3; column-count: 3;"><ul style="margin:0">') | 
|  | for this_name in sorted(groups_indexed): | 
|  | if net2cat(this_name) == cat: | 
|  | neighbour_segs = groups_indexed[this_name] | 
|  | print("<li><tt><b>%s</b></tt>" % this_name) | 
|  | if neighbour_segs: | 
|  | print("<ul>") | 
|  | for nidx in sorted(neighbour_segs): | 
|  | if nidx == (tx, ty): | 
|  | print("<li><tt><b>(%d %d)</b> %s</tt></li>" % (nidx[0], nidx[1], ", ".join(sorted(neighbour_segs[nidx])))) | 
|  | else: | 
|  | print("<li><tt>(%d %d) %s</tt></li>" % (nidx[0], nidx[1], ", ".join(sorted(neighbour_segs[nidx])))) | 
|  | print("</ul>") | 
|  | print("</li>") | 
|  | print("</ul></div></p>") | 
|  |  | 
|  | # print_expand_end() | 
|  |  | 
|  | print("<h2>Routing Configuration</h2>") | 
|  |  | 
|  | print("""<p>This section lists the routing configuration bits in the tile. | 
|  | All routing resources are directional tristate buffers that are in tristate mode | 
|  | in the all-zeros configuration.</p>""") | 
|  |  | 
|  | grpgrp = dict() | 
|  | config_groups = dict() | 
|  | other_config_groups = dict() | 
|  |  | 
|  | for entry in ic.tile_db(tx, ty): | 
|  | if not ic.tile_has_entry(tx, ty, entry): | 
|  | continue | 
|  | if entry[1] in ("routing", "buffer"): | 
|  | cfggrp = entry[1] + " " + entry[3] + "," + ",".join(sorted([bit.replace("!", "") for bit in entry[0]])) | 
|  | config_groups.setdefault(cfggrp, list()).append(entry) | 
|  | grpgrp.setdefault(net2cat(entry[3]), set()).add(cfggrp) | 
|  | else: | 
|  | grp = other_config_groups.setdefault(" ".join(entry[1:]), set()) | 
|  | for bit in entry[0]: grp.add(bit) | 
|  |  | 
|  | for cat in sorted(grpgrp): | 
|  | print('<h4>%s</h4>' % cat[1]) | 
|  |  | 
|  | bits_in_cat = set() | 
|  |  | 
|  | for cfggrp in sorted(grpgrp[cat]): | 
|  | grp = config_groups[cfggrp] | 
|  | for bit in cfggrp.split(",")[1:]: | 
|  | match = re.match(r"B(\d+)\[(\d+)\]", bit) | 
|  | bits_in_cat.add((int(match.group(1)), int(match.group(2)))) | 
|  |  | 
|  | print('<table style="font-size:x-small">') | 
|  | print("<tr><td></td>") | 
|  | for cell_nr in range(len(bitmap_cells[0])): | 
|  | print('<td align="center" width="15">%d</td>' % cell_nr) | 
|  | print("<td></td></tr>") | 
|  | for line_nr, line in enumerate(bitmap_cells): | 
|  | print("<tr>") | 
|  | print('<td>B%d</td>' % line_nr) | 
|  | for cell_nr, cell in enumerate(line): | 
|  | color = cell["bgcolor"] | 
|  | if (line_nr, cell_nr) not in bits_in_cat: color="#aaa" | 
|  | print('<td align="center" bgcolor="%s" style="color:#666;">%s</td>' % (color, cell["label"])) | 
|  | print('<td>B%d</td>' % line_nr) | 
|  | print("</tr>") | 
|  | print("<tr><td></td>") | 
|  | for cell_nr in range(len(line)): | 
|  | print('<td align="center">%d</td>' % cell_nr) | 
|  | print("<td></td></tr>") | 
|  | print("</table>") | 
|  |  | 
|  | # print_expand_div("details") | 
|  |  | 
|  | src_nets = set() | 
|  | dst_nets = set() | 
|  | links = dict() | 
|  |  | 
|  | for cfggrp in sorted(grpgrp[cat]): | 
|  | grp = config_groups[cfggrp] | 
|  | for entry in grp: | 
|  | src_nets.add(entry[2]) | 
|  | dst_nets.add(entry[3]) | 
|  | if entry[1] == "buffer": | 
|  | assert (entry[2], entry[3]) not in links | 
|  | links[(entry[2], entry[3])] = '<td align="center" bgcolor="#afa" style="color:#666;">B</td>' | 
|  | else: | 
|  | assert (entry[2], entry[3]) not in links | 
|  | links[(entry[2], entry[3])] = '<td align="center" bgcolor="#faa" style="color:#666;">R</td>' | 
|  |  | 
|  | print('<h5>Connectivity Matrix</h5>') | 
|  | print('<table style="font-size:x-small">') | 
|  | dst_net_prefix = "" | 
|  | dst_net_list = sorted(dst_nets, key=icebox.key_netname) | 
|  | if len(dst_net_list) > 1: | 
|  | while len(set([n[0] for n in dst_net_list])) == 1: | 
|  | dst_net_prefix += dst_net_list[0][0] | 
|  | for i in range(len(dst_net_list)): | 
|  | dst_net_list[i] = dst_net_list[i][1:] | 
|  | while dst_net_prefix != "" and dst_net_prefix[-1] != "_": | 
|  | for i in range(len(dst_net_list)): | 
|  | dst_net_list[i] = dst_net_prefix[-1] + dst_net_list[i] | 
|  | dst_net_prefix = dst_net_prefix[0:-1] | 
|  | print('<tr><td></td><td colspan="%d" align="center">%s</td></tr>' % (len(dst_net_list), dst_net_prefix)) | 
|  | print('<tr><td></td>') | 
|  | for dn in dst_net_list: | 
|  | print('<td>%s</td>' % dn) | 
|  | print("</tr>") | 
|  | for sn in sorted(src_nets, key=icebox.key_netname): | 
|  | print("<tr>") | 
|  | print('<td>%s</td>' % sn) | 
|  | for dn in sorted(dst_nets, key=icebox.key_netname): | 
|  | if (sn, dn) in links: | 
|  | print(links[(sn, dn)]) | 
|  | else: | 
|  | print('<td align="center" bgcolor="#aaa" style="color:#666;"> </td>') | 
|  | print("</tr>") | 
|  | print("</table>") | 
|  |  | 
|  | print('<h5>Configuration Stamps</h5>') | 
|  | for cfggrp in sorted(grpgrp[cat]): | 
|  | grp = config_groups[cfggrp] | 
|  | bits = cfggrp.split(",")[1:] | 
|  | print('<p><table style="font-size:small" border><tr>') | 
|  | for bit in bits: | 
|  | print('<th style="width:5em"><a name="%s">%s</a></th>' % (re.sub(r"B(\d+)\[(\d+)\]", r"B.\1.\2", bit), bit)) | 
|  | group_lines = list() | 
|  | is_buffer = True | 
|  | for entry in grp: | 
|  | line = '<tr>' | 
|  | for bit in bits: | 
|  | if bit in entry[0]: | 
|  | line += '<td align="center">1</td>' | 
|  | else: | 
|  | line += '<td align="center">0</td>' | 
|  | is_buffer = entry[1] == "buffer" | 
|  | line += '<td align="center">%s</td><td><tt>%s</tt></td><td><tt>%s</tt></td></tr>' % (entry[1], entry[2], entry[3]) | 
|  | group_lines.append(line) | 
|  | if is_buffer: | 
|  | print('<th style="width:5em">Function</th><th style="width:15em">Source-Net</th><th style="width:15em">Destination-Net</th></tr>') | 
|  | else: | 
|  | print('<th style="width:5em">Function</th><th style="width:15em">Net</th><th style="width:15em">Net</th></tr>') | 
|  | for line in sorted(group_lines): | 
|  | print(line) | 
|  | print('</table></p>') | 
|  |  | 
|  | # print_expand_end() | 
|  |  | 
|  | print("<h2>Non-routing Configuration</h2>") | 
|  |  | 
|  | print("<p>This section lists the non-routing configuration bits in the tile.</p>") | 
|  |  | 
|  | print('<table style="font-size:x-small">') | 
|  | print("<tr><td></td>") | 
|  | for cell_nr in range(len(bitmap_cells[0])): | 
|  | print('<td align="center" width="15">%d</td>' % cell_nr) | 
|  | print("<td></td></tr>") | 
|  | for line_nr, line in enumerate(bitmap_cells): | 
|  | print("<tr>") | 
|  | print('<td>B%d</td>' % line_nr) | 
|  | for cell_nr, cell in enumerate(line): | 
|  | color = cell["bgcolor"] | 
|  | if "is_routing" in cell: color="#aaa" | 
|  | print('<td align="center" bgcolor="%s" style="color:#666;">%s</td>' % (color, cell["label"])) | 
|  | print('<td>B%d</td>' % line_nr) | 
|  | print("</tr>") | 
|  | print("<tr><td></td>") | 
|  | for cell_nr in range(len(line)): | 
|  | print('<td align="center">%d</td>' % cell_nr) | 
|  | print("<td></td></tr>") | 
|  | print("</table>") | 
|  |  | 
|  | print('<p><table style="font-size:small" border><tr><th>Function</th><th>Bits</th></tr>') | 
|  | for cfggrp in sorted(other_config_groups): | 
|  | bits = " ".join(['<a name="%s">%s</a>' % (re.sub(r"B(\d+)\[(\d+)\]", r"B.\1.\2", bit), bit) for bit in sorted(other_config_groups[cfggrp])]) | 
|  | cfggrp = cfggrp.replace(" " + list(other_config_groups[cfggrp])[0], "") | 
|  | print('<tr><td>%s</td><td>%s</td></tr>' % (cfggrp, bits)) | 
|  | print('</table></p>') | 
|  |  | 
|  |  | 
|  | if outdir is not None: | 
|  | stdout = sys.stdout | 
|  |  | 
|  | if not os.path.exists(outdir): | 
|  | print("Creating %s/" % outdir, file=stdout) | 
|  | os.makedirs(outdir) | 
|  |  | 
|  | print("Writing %s/index.html.." % outdir, file=stdout) | 
|  | sys.stdout = open("%s/index.html" % outdir, "w") | 
|  | print_index() | 
|  |  | 
|  | for x in range(ic.max_x+1): | 
|  | for y in range(ic.max_y+1): | 
|  | if (x, y) in mktiles: | 
|  | print("Writing %s/tile_%d_%d.html.." % (outdir, x, y), file=stdout) | 
|  | sys.stdout = open("%s/tile_%d_%d.html" % (outdir, x, y), "w") | 
|  | print_tile(x, y) | 
|  |  | 
|  | print("Writing %s/%s..." % (outdir, chipdbfile), file=stdout) | 
|  | if os.access("icebox_chipdb.py", os.R_OK): | 
|  | os.system("python3 icebox_chipdb.py %s > %s/%s" % ("-8" if mode8k else "", outdir, chipdbfile)) | 
|  | else: | 
|  | os.system("icebox_chipdb %s > %s/%s" % ("-8" if mode8k else "", outdir, chipdbfile)) | 
|  |  | 
|  | sys.stdout = stdout | 
|  |  | 
|  | elif (tx, ty) == (0, 0): | 
|  | print_index() | 
|  |  | 
|  | else: | 
|  | print_tile(tx, ty) |