| #!/usr/bin/env python3 | |
| """Generate a HTML representation of the tile bit database""" | |
| import pytrellis | |
| import database | |
| import argparse | |
| import sys | |
| import re | |
| def find_bits(db): | |
| cwords = db.get_settings_words() | |
| for cword in cwords: | |
| wd = db.get_data_for_setword(cword) | |
| for i in range(len(wd.bits)): | |
| for bit in wd.bits[i].bits: | |
| bitmap[bit.frame, bit.bit] = "word_" + str(cword) | |
| if (bit.frame, bit.bit) not in labels: | |
| labels[bit.frame, bit.bit] = set() | |
| labels[bit.frame, bit.bit].add("{}[{}]".format(cword, i)) | |
| cenums = db.get_settings_enums() | |
| for cenum in cenums: | |
| ed = db.get_data_for_enum(cenum) | |
| for opt in ed.get_options(): | |
| for bit in ed.options[opt].bits: | |
| bitmap[bit.frame, bit.bit] = "enum_" + str(cenum) | |
| if (bit.frame, bit.bit) not in labels: | |
| labels[bit.frame, bit.bit] = set() | |
| labels[bit.frame, bit.bit].add(cenum) | |
| sinks = db.get_sinks() | |
| for sink in sinks: | |
| mux = db.get_mux_data_for_sink(sink) | |
| for src in mux.get_sources(): | |
| for bit in mux.arcs[src].bits.bits: | |
| bitmap[bit.frame, bit.bit] = "mux_" + str(sink) | |
| if (bit.frame, bit.bit) not in labels: | |
| labels[bit.frame, bit.bit] = set() | |
| labels[bit.frame, bit.bit].add(sink) | |
| def mux_html(mux, f): | |
| bitset = set() | |
| for src in mux.get_sources(): | |
| for bit in mux.arcs[src].bits.bits: | |
| bitset.add((bit.frame, bit.bit)) | |
| bitlist = list(sorted(bitset)) | |
| print('<h3 id="mux_{}">Mux driving <span class="mux_sink">{}</span></h3>'.format(mux.sink, mux.sink), file=f) | |
| print('<table class="mux"><tr><th>Source</th>', file=f) | |
| for bit in bitlist: | |
| print('<th style="padding-left: 10px; padding-right: 10px">F{}B{}</th>'.format(bit[0], bit[1]), file=f) | |
| print('</tr>', file=f) | |
| truthtable = [] | |
| for src in mux.get_sources(): | |
| ttrow = [] | |
| for blb in bitlist: | |
| found = False | |
| for ab in mux.arcs[src].bits.bits: | |
| if ab.frame == blb[0] and ab.bit == blb[1]: | |
| ttrow.append('0' if ab.inv else '1') | |
| found = True | |
| break | |
| if not found: | |
| ttrow.append("-") | |
| truthtable.append((mux.arcs[src], ttrow)) | |
| trstyle = "" | |
| for (arc, ttrow) in sorted(truthtable, key=lambda x: "".join(reversed(x[1])).replace("-", "0")): | |
| trstyle = " bgcolor=\"#dddddd\"" if trstyle == "" else "" | |
| print('<tr {}><td>{}</td>'.format(trstyle, arc.source), file=f) | |
| for bit in ttrow: | |
| print('<td style="text-align: center">{}</td>'.format(bit), file=f) | |
| print('</td>', file=f) | |
| print('</table>', file=f) | |
| def setword_html(word, f): | |
| print('<h3 id="word_{}">Configuration {} {}</h3>'.format(word.name, "bit" if len(word.bits) == 1 else "word", | |
| word.name), file=f) | |
| defval_bits = [] | |
| for i in range(len(word.defval)): | |
| if len(word.bits[i].bits) == 0: | |
| defval_bits.append("X") | |
| else: | |
| defval_bits.append("1" if word.defval[i] else "0") | |
| defval = "{}'b{}".format(len(defval_bits), "".join(reversed(defval_bits))) | |
| print('<p>Default value: {}</p>'.format(defval), file=f) | |
| print('<table class="setword">', file=f) | |
| trstyle = "" | |
| for idx in range(len(word.bits)): | |
| cbits = " ".join(["{}F{}B{}".format("!" if b.inv else "", b.frame, b.bit) for b in word.bits[idx].bits]) | |
| trstyle = " bgcolor=\"#dddddd\"" if trstyle == "" else "" | |
| print( | |
| '<tr {}><td style="padding-left: 10px; padding-right: 10px">{}[{}]</td><td style="padding-left: 10px; padding-right: 10px">{}</td></tr>'.format( | |
| trstyle, word.name, idx, cbits), file=f) | |
| print('</table>', file=f) | |
| def nice_sort(x): | |
| if x == "NONE": | |
| return -1, "" | |
| else: | |
| n = re.findall('\d+', x) | |
| if len(n) > 0: | |
| return int("".join(n)), x | |
| else: | |
| return 0, x | |
| def setenum_html(enum, f): | |
| print('<h3 id="enum_{}">Configuration Setting {}</h3>'.format(enum.name, enum.name), file=f) | |
| if enum.defval != "": | |
| print('<p>Default value: {}</p>'.format(enum.defval), file=f) | |
| bitset = set() | |
| for opt in enum.get_options(): | |
| for bit in enum.options[opt].bits: | |
| bitset.add((bit.frame, bit.bit)) | |
| bitlist = list(sorted(bitset)) | |
| print('<table class="setenum"><tr><th>Value</th>', file=f) | |
| for bit in bitlist: | |
| print('<th style="padding-left: 10px; padding-right: 10px">F{}B{}</th>'.format(bit[0], bit[1]), file=f) | |
| print("</tr>", file=f) | |
| truthtable = [] | |
| for opt in enum.get_options(): | |
| ttrow = [] | |
| for blb in bitlist: | |
| found = False | |
| for ab in enum.options[opt].bits: | |
| if ab.frame == blb[0] and ab.bit == blb[1]: | |
| ttrow.append('0' if ab.inv else '1') | |
| found = True | |
| break | |
| if not found: | |
| ttrow.append("-") | |
| truthtable.append((opt, ttrow)) | |
| trstyle = "" | |
| tt_sorted = sorted(truthtable, key=lambda x: nice_sort(x[0])) | |
| for (opt, ttrow) in tt_sorted: | |
| trstyle = " bgcolor=\"#dddddd\"" if trstyle == "" else "" | |
| print('<tr {}><td>{}</td>'.format(trstyle, opt), file=f) | |
| for bit in ttrow: | |
| print('<td style="text-align: center">{}</td>'.format(bit), file=f) | |
| print('</td>', file=f) | |
| print('</table>', file=f) | |
| def muxes_html(db, f): | |
| sinks = db.get_sinks() | |
| for sink in sorted(sinks): | |
| mux = db.get_mux_data_for_sink(sink) | |
| mux_html(mux, f) | |
| def setwords_html(db, f): | |
| words = db.get_settings_words() | |
| for name in sorted(words): | |
| word = db.get_data_for_setword(name) | |
| setword_html(word, f) | |
| def setenums_html(db, f): | |
| enums = db.get_settings_enums() | |
| for name in sorted(enums): | |
| enum = db.get_data_for_enum(name) | |
| setenum_html(enum, f) | |
| def fixed_conns_html(db, f): | |
| conns = db.get_fixed_conns() | |
| if len(conns) > 0: | |
| print("<h3>Fixed Connections</h3>", file=f) | |
| print('<table class="fconn" style="border-spacing:0"><tr><th>Source</th><th></th><th>Sink</th></tr>', file=f) | |
| trstyle = "" | |
| sorted_conns = sorted(conns, key=lambda x: re.sub(r"^([NESW]\d+)+_", "", x.sink)) | |
| for conn in sorted_conns: | |
| trstyle = " bgcolor=\"#dddddd\"" if trstyle == "" else "" | |
| print( | |
| """<tr {}><td style="padding-left: 10px; padding-right: 10px; margin-left: 0px;">{}</td><td>→</td> | |
| <td style="padding-left: 10px; padding-right: 10px">{}</td></tr>""".format( | |
| trstyle, conn.source, conn.sink), file=f) | |
| print('</table>', file=f) | |
| colours = {"A": "#88FFFF", "B": "#FF88FF", "C": "#8888FF", "D": "#FFFF88", "M": "#FFBBBB", "H": "#BBBBFF", | |
| "V": "#FFFFBB"} | |
| def get_bit_info(frame, bit): | |
| if (frame, bit) in bitmap: | |
| group = bitmap[(frame, bit)] | |
| if group.startswith("mux"): | |
| label = group.split("_")[-1][0] | |
| if label == "J": | |
| label = group.split("_")[-1][1] | |
| if label in colours: | |
| colour = colours[label] | |
| else: | |
| colour = "#FF8888" | |
| elif group.startswith("enum") or group.startswith("word"): | |
| label = re.split("[_.]", group)[-1][0] | |
| colour = "#88FF88" | |
| else: | |
| label = "?" | |
| colour = "#ffffff" | |
| return group, label, colour | |
| else: | |
| return "unknown", " ", "#FFFFFF" | |
| def bit_grid_html(tileinfo, f): | |
| print("<table style='font-size: 8pt; border: 2px solid black; text-align: center'>", file=f) | |
| for bit in range(tileinfo.bits_per_frame): | |
| print("<tr style='height: 20px'>", file=f) | |
| for frame in range(tileinfo.num_frames): | |
| group, label, colour = get_bit_info(frame, bit) | |
| print("<td style='height: 100%; border: 2px solid black;'>", file=f) | |
| title = "F{}B{}".format(frame, bit) | |
| if (frame, bit) in labels: | |
| for lab in sorted(labels[frame, bit]): | |
| title += " " + lab | |
| print("""<a href='#{}' title='{}' style='text-decoration: none; color: #000000'> | |
| <div id='f{}b{}' style='height: 100%; background-color: {}; width: 12px' class='grp_{}' | |
| onmouseover='mov(event);' onmouseout='mou(event);'>{}</div></a></td>""".format( | |
| group, title, frame, bit, colour, group, label), file=f) | |
| print("</tr>", file=f) | |
| print("</table>", file=f) | |
| parser = argparse.ArgumentParser(description=__doc__) | |
| parser.add_argument('family', type=str, | |
| help="FPGA family (e.g. ECP5)") | |
| parser.add_argument('device', type=str, | |
| help="FPGA device (e.g. LFE5U-85F)") | |
| parser.add_argument('tile', type=str, | |
| help="Tile type (e.g. PLC2)") | |
| parser.add_argument('outfile', type=argparse.FileType('w'), | |
| help="output HTML file") | |
| def main(argv): | |
| global bitmap, labels | |
| bitmap = dict(dict()) | |
| labels = dict(dict()) | |
| args = parser.parse_args(argv[1:]) | |
| f = args.outfile | |
| print( | |
| """<html> | |
| <head><title>{} Bit Data</title> | |
| """.format(args.tile), file=f) | |
| print(""" | |
| <script type="text/javascript"> | |
| origClr = {}; | |
| origClass = ""; | |
| function mov(event) { | |
| if (event.target.className != "unknown") { | |
| origClass = event.target.className; | |
| var elems = document.getElementsByClassName(origClass); | |
| for(var i = 0; i < elems.length; i++) { | |
| if(!(elems[i].id in origClr)) { | |
| origClr[elems[i].id] = elems[i].style.backgroundColor; | |
| } | |
| elems[i].style.backgroundColor = "white"; | |
| } | |
| } | |
| } | |
| function mou(event) { | |
| var elems = document.getElementsByClassName(origClass); | |
| for(var i = 0; i < elems.length; i++) { | |
| elems[i].style.backgroundColor = origClr[elems[i].id] || "#ffffff"; | |
| } | |
| } | |
| </script> | |
| </head> | |
| <body> | |
| """, file=f) | |
| print("""<h1>{} Bit Data</h1> | |
| """.format(args.tile), file=f) | |
| pytrellis.load_database(database.get_db_root()) | |
| tdb = pytrellis.get_tile_bitdata( | |
| pytrellis.TileLocator(args.family, args.device, args.tile)) | |
| ch = pytrellis.Chip(args.device) | |
| ti = ch.get_tiles_by_type(args.tile)[0].info | |
| find_bits(tdb) | |
| bit_grid_html(ti, f) | |
| muxes_html(tdb, f) | |
| setwords_html(tdb, f) | |
| setenums_html(tdb, f) | |
| fixed_conns_html(tdb, f) | |
| print("""</body></html>""", file=f) | |
| if __name__ == "__main__": | |
| main(sys.argv) |