#!/usr/bin/env python3 # Copyright (C) 2017 Roland Lutz # # 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 getopt, os, re, sys import icebox from icebox import re_match_cached, re_sub_cached ## Get the tile-local name of a net. # # \param x, y coordinates of the tile to which the net belongs # \param fw, fh width and height of the tile fabric (excluding I/O tiles) # \param net global net name # # \return the tile-local name of the net if it is a span wire, # otherwise the unmodified net name def untranslate_netname(x, y, fw, fh, net): def index(g, i, group_size): if g % 2 == 1: i = i + 1 - (i % 2) * 2 return g * group_size + i match = re_match_cached(r'span4_y(\d+)_g(\d+)_(\d+)$', net) if match is not None: my = int(match.group(1)) mw = int(match.group(2)) mi = int(match.group(3)) assert my == y assert mi >= 0 and mi < 12 mg = x - mw + 4 assert mg >= 0 and mg <= 4 if x == 0: return 'span4_horz_%d' % index(mg, mi, 12) if x == fw + 1: return 'span4_horz_%d' % index(mg - 1, mi, 12) if mg == 4: return 'sp4_h_l_%d' % index(mg - 1, mi, 12) else: return 'sp4_h_r_%d' % index(mg, mi, 12) match = re_match_cached(r'span4_x(\d+)_g(\d+)_(\d+)$', net) if match is not None: mx = int(match.group(1)) mw = int(match.group(2)) mi = int(match.group(3)) assert mi >= 0 and mi < 12 mg = mw - y assert mg >= 0 and mg <= 4 if y == 0: return 'span4_vert_%d' % index(mg - 1, mi, 12) if y == fh + 1: return 'span4_vert_%d' % index(mg, mi, 12) if mx == x + 1: assert mg < 4 return 'sp4_r_v_b_%d' % index(mg, mi, 12) assert mx == x if mg == 4: return 'sp4_v_t_%d' % index(mg - 1, mi, 12) else: return 'sp4_v_b_%d' % index(mg, mi, 12) match = re_match_cached(r'dummy_y(\d+)_g(\d+)_(\d+)$', net) if match is not None: my = int(match.group(1)) mw = int(match.group(2)) mi = int(match.group(3)) assert my == y mg = mw assert mg >= 0 and mg < 4 return 'sp4_r_v_b_%d' % index(mg, mi, 12) match = re_match_cached(r'span12_y(\d+)_g(\d+)_(\d+)$', net) if match is not None: my = int(match.group(1)) mw = int(match.group(2)) mi = int(match.group(3)) assert my == y assert mi >= 0 and mi < 2 mg = x - mw + 12 assert mg >= 0 and mg <= 12 if x == 0: return 'span12_horz_%d' % index(mg, mi, 2) if x == fw + 1: return 'span12_horz_%d' % index(mg - 1, mi, 2) if mg == 12: return 'sp12_h_l_%d' % index(mg - 1, mi, 2) else: return 'sp12_h_r_%d' % index(mg, mi, 2) match = re_match_cached(r'span12_x(\d+)_g(\d+)_(\d+)$', net) if match is not None: mx = int(match.group(1)) mw = int(match.group(2)) mi = int(match.group(3)) assert mx == x assert mi >= 0 and mi < 2 mg = mw - y assert mg >= 0 and mg <= 12 if y == 0: return 'span12_vert_%d' % index(mg - 1, mi, 2) if y == fh + 1: return 'span12_vert_%d' % index(mg, mi, 2) if mg == 12: return 'sp12_v_t_%d' % index(mg - 1, mi, 2) else: return 'sp12_v_b_%d' % index(mg, mi, 2) match = re_match_cached(r'span4_bottom_g(\d+)_(\d+)$', net) if match is not None: mw = int(match.group(1)) mi = int(match.group(2)) assert mi >= 0 and mi < 4 if x == 0: assert y != 0 mg = -y + 5 - mw assert y + mg - 3 < 0 return 'span4_vert_b_%d' % (mg * 4 + mi) else: assert y == 0 mg = x + 4 - mw assert x - mg + 1 >= 0 if mg == 4: return 'span4_horz_l_%d' % (mg * 4 + mi - 4) else: assert fw - x + mg - 4 >= 0 return 'span4_horz_r_%d' % (mg * 4 + mi) match = re_match_cached(r'span4_left_g(\d+)_(\d+)$', net) if match is not None: mw = int(match.group(1)) mi = int(match.group(2)) assert mi >= 0 and mi < 4 if y == 0: assert x != 0 mg = mw + x - 1 assert x - mg + 1 < 0 if mg == 4: return 'span4_horz_l_%d' % (mg * 4 + mi - 4) else: assert fw - x + mg - 4 >= 0 return 'span4_horz_r_%d' % (mg * 4 + mi) else: assert x == 0 mg = mw - y assert fh - y - mg >= 0 if mg == 4: return 'span4_vert_t_%d' % (mg * 4 + mi - 4) else: assert y + mg - 3 >= 0 return 'span4_vert_b_%d' % (mg * 4 + mi) match = re_match_cached(r'span4_right_g(\d+)_(\d+)$', net) if match is not None: mw = int(match.group(1)) mi = int(match.group(2)) assert mi >= 0 and mi < 4 if y == fh + 1: mg = mw - fh - fw + x - 1 assert x - mg - 1 >= 0 assert x - mg + 1 >= fw return 'span4_horz_r_%d' % (mg * 4 + mi) assert x == fw + 1 mg = mw - y if mg == 4: assert y + mg - 1 < fh + 2 return 'span4_vert_t_%d' % (mg * 4 + mi - 4) else: assert y + mg - 5 >= 0 assert y + mg < fh + 3 return 'span4_vert_b_%d' % (mg * 4 + mi) match = re_match_cached(r'span4_top_g(\d+)_(\d+)$', net) if match is not None: mw = int(match.group(1)) mi = int(match.group(2)) assert mi >= 0 and mi < 4 if x == fw + 1: assert y != 0 mg = fw + fh + 5 - y - mw assert y + mg >= fh + 3 if mg == 4: return 'span4_vert_t_%d' % (mg * 4 + mi - 4) else: assert y + mg - 5 >= 0 return 'span4_vert_b_%d' % (mg * 4 + mi) assert y != 0 mg = x + 4 - mw assert x - mg - 1 >= 0 if mg == 4: return 'span4_horz_l_%d' % (mg * 4 + mi - 4) else: assert x - mg + 1 < fw return 'span4_horz_r_%d' % (mg * 4 + mi) match = re_match_cached(r'span4_bottomright(\d+)_(\d+)$', net) if match is not None: mw = int(match.group(1)) mi = int(match.group(2)) assert mw % 2 == 0 assert mi >= 0 and mi < 4 if y == 0: assert x != 0 mg = mw // 2 - fw + x - 1 assert fw - x + mg - 4 < 0 return 'span4_horz_r_%d' % (mg * 4 + mi) else: assert x == fw + 1 mg = mw // 2 - y assert y + mg - 5 < 0 return 'span4_vert_b_%d' % (mg * 4 + mi) match = re_match_cached(r'span4_topleft(\d+)_(\d+)$', net) if match is not None: mw = int(match.group(1)) mi = int(match.group(2)) assert mw % 2 == 0 assert mi >= 0 and mi < 4 if x == 0: assert y != 0 mg = fh + 5 - y - mw // 2 assert fh - y - mg < 0 if mg == 4: return 'span4_vert_t_%d' % (mg * 4 + mi - 4) else: return 'span4_vert_b_%d' % (mg * 4 + mi) else: assert y == fh + 1 mg = x + 4 - mw // 2 assert x - mg - 1 < 0 if mg == 4: return 'span4_horz_l_%d' % (mg * 4 + mi - 4) else: return 'span4_horz_r_%d' % (mg * 4 + mi) return net ## Check if the name of a destination net is the human-readable form ## of the \c fabout net of IO tile (x, y). # # \return \c 'fabout' if it is the \c fabout net, otherwise the # unchanged net name def revert_to_fabout(ic, x, y, net): if net.startswith('glb_netwk_'): for gx, gy, gi in ic.gbufin_db(): if net == 'glb_netwk_%d' % gi and (x, y) == (gx, gy): return 'fabout' raise ParseError("{} is a global network, but not at an expected location {} {}".format(net, gx, gy)) return net EXPR_AND, EXPR_XOR, EXPR_OR, EXPR_TERN, EXPR_NOT, EXPR_ZERO, EXPR_ONE = range(7) ## Evaluate a list representation of a logic expression for given ## input values. # # This is a helper function for \ref logic_expression_to_lut. # # \param expr list representation of a logic expression (see below) # \param args list of boolean values representing the input values # # \result \c False or \c True, depending on the expression and arguments # # Expression | Result # ---------------------------------------+---------------------------------- # i | value of argument \a i # (EXPR_AND, [expr, ...]) | AND operation of all expressions # (EXPR_XOR, [expr, ...]) | XOR operation of all expressions # (EXPR_OR, [expr, ...]) | OR operation of all expressions # (EXPR_TERN, ex_a, ex_b, ex_c) | result of \c ex_b if \c ex_a # | evaluates to \c True, otherwise # | result of \c ex_c # (EXPR_NOT, expr) | negated result of \a expr # (EXPR_ZERO, ) | \c False # (EXPR_ONE, ) | \c True def evaluate(expr, args): if type(expr) == int: return args[expr] op = expr[0] if op == EXPR_AND: assert len(expr) == 2 for o in expr[1]: if not evaluate(o, args): return False return True if op == EXPR_XOR: assert len(expr) == 2 result = False for o in expr[1]: if evaluate(o, args): result = not result return result if op == EXPR_OR: assert len(expr) == 2 for o in expr[1]: if evaluate(o, args): return True return False if op == EXPR_TERN: assert len(expr) == 4 if evaluate(expr[1], args): return evaluate(expr[2], args) else: return evaluate(expr[3], args) if op == EXPR_NOT: assert len(expr) == 2 return not evaluate(expr[1], args) if op == EXPR_ZERO: assert len(expr) == 1 return False if op == EXPR_ONE: assert len(expr) == 1 return True assert False # unknown operator ## Convert a logic expression to a LUT string. # # \param lut string containing a human-readable logic expression # \param args list of N strings containing the names of the arguments # # \return a string of 2^N `0' or `1' characters representing the logic # of an Nx1 look-up table equivalent to the logic expression # # Example: logic_expression_to_lut('a & b & !c', ['a', 'b', 'c']) -> '00010000' def logic_expression_to_lut(s, args): # make sure argument names are unique assert len(set(args)) == len(args) stack = [[None, None, None], [[], None, None]] stack[0][2] = l = []; stack[1][0].append((EXPR_OR, l)) stack[0][1] = l = []; stack[0][2].append((EXPR_XOR, l)) stack[0][0] = l = []; stack[0][1].append((EXPR_AND, l)) expect_expr = True negate_count = 0 i = 0 while i < len(s): if s[i] == ' ': pass elif s[i] == '!': assert expect_expr negate_count += 1 elif s[i] == '&': assert not expect_expr expect_expr = True negate_count = 0 elif s[i] == '^': assert not expect_expr stack[0][0] = l = []; stack[0][1].append((EXPR_AND, l)) expect_expr = True negate_count = 0 elif s[i] == '|': assert not expect_expr stack[0][1] = l = []; stack[0][2].append((EXPR_XOR, l)) stack[0][0] = l = []; stack[0][1].append((EXPR_AND, l)) expect_expr = True negate_count = 0 elif s[i] == '?': assert not expect_expr assert stack[1][0][-1][0] == EXPR_OR stack[1][0][-1] = (EXPR_TERN, stack[1][0][-1], (EXPR_OR, []), (EXPR_OR, [])) stack[0][2] = l = stack[1][0][-1][2][1] stack[0][1] = l = []; stack[0][2].append((EXPR_XOR, l)) stack[0][0] = l = []; stack[0][1].append((EXPR_AND, l)) expect_expr = True negate_count = 0 elif s[i] == ':': assert not expect_expr assert stack[1][0][-1][0] == EXPR_TERN stack[0][2] = l = stack[1][0][-1][3][1] stack[0][1] = l = []; stack[0][2].append((EXPR_XOR, l)) stack[0][0] = l = []; stack[0][1].append((EXPR_AND, l)) expect_expr = True negate_count = 0 elif s[i] == '(': assert expect_expr stack.insert(0, [None, None, None]) stack[0][2] = l = [] if negate_count % 2: stack[1][0].append((EXPR_NOT, (EXPR_OR, l))) else: stack[1][0].append((EXPR_OR, l)) stack[0][1] = l = []; stack[0][2].append((EXPR_XOR, l)) stack[0][0] = l = []; stack[0][1].append((EXPR_AND, l)) expect_expr = True negate_count = 0 elif s[i] == ')': assert not expect_expr stack.pop(0) elif s[i] == '0': assert expect_expr if negate_count % 2: stack[0][0].append((EXPR_ONE, )) else: stack[0][0].append((EXPR_ZERO, )) expect_expr = False negate_count = None elif s[i] == '1': assert expect_expr if negate_count % 2: stack[0][0].append((EXPR_ZERO, )) else: stack[0][0].append((EXPR_ONE, )) expect_expr = False negate_count = None else: assert expect_expr found = None for j, arg in enumerate(args): if s.startswith(arg, i): found = j i += len(arg) break assert found is not None if negate_count % 2: stack[0][0].append((EXPR_NOT, found)) else: stack[0][0].append(found) expect_expr = False negate_count = None continue i += 1 assert len(stack) == 2 return ''.join('1' if evaluate(stack[1][0][0], tuple(i & (1 << j) != 0 for j in range(len(args)))) else '0' for i in range(1 << len(args))) def parse_verilog_bitvector_to_bits(in_str): #replace x with 0 in_str = re_sub_cached('[xX]', '0', in_str) m = re_match_cached("([0-9]+)'([hdob])([0-9a-fA-F]+)", in_str) if m: num_bits = int(m.group(1)) prefix = m.group(2) val_str = m.group(3) if prefix == 'h': val = eval('0x' + val_str) elif prefix == 'd': val = eval(val_str.lstrip('0')) elif prefix == 'o' or prefix =='b': val = eval('0' + prefix + val_str) if val.bit_length() > num_bits: raise ParseError("Number of bits({}) given don't match expected ({})" .format(val.bit_length(), num_bits)) else: val = eval('0x' + in_str) num_bits = len(in_str) * 4 bit_str = bin(val)[2:] # zero pad nz = num_bits - len(bit_str) bit_vec = nz*['0'] + list(bit_str) return bit_vec def parse_verilog_bitvector_to_hex(in_str): if type(in_str) == str: bits = parse_verilog_bitvector_to_bits(in_str) else: bits = in_str # pad to 4 bits = ((4-len(bits)) % 4) * ['0'] + bits res = ''.join([hex(eval('0b' + ''.join(bits[ii:ii+4])))[2:] for ii in range(0, len(bits), 4)]) return res class ParseError(Exception): pass def parse_bool(s): if s == 'on': return True if s == 'off': return False raise ParseError("Unable to parse '{}' as boolean".format(s)) class Main: def __init__(self): self.ic = None self.device = None #self.coldboot = None self.warmboot = None self.tiles = {} def read(self, fields): if fields[0] == 'device' and len(fields) == 4 \ and len(fields[1]) >= 2 and fields[1][0] == '"' \ and fields[1][-1] == '"' \ and self.ic is None and self.device is None: self.device = fields[1][1:-1].lower() if self.device.startswith('lp') or self.device.startswith('hx'): self.device = self.device[2:] if self.device.startswith('1k'): self.ic = icebox.iceconfig() self.ic.setup_empty_1k() elif self.device.startswith('5k'): self.ic = icebox.iceconfig() self.ic.setup_empty_5k() elif self.device.startswith('8k'): self.ic = icebox.iceconfig() self.ic.setup_empty_8k() elif self.device.startswith('384'): self.ic = icebox.iceconfig() self.ic.setup_empty_384() else: raise ParseError("Unknown device {}".format(self.device)) #elif fields[0] == 'coldboot' and fields[1] == '=' \ # and self.coldboot is None: # # parsed but ignored (can't be represented in IceStorm .asc format) # self.coldboot = parse_bool(fields[2]) elif fields[0] == 'warmboot' and fields[1] == '=' \ and self.warmboot is None: # parsed but ignored (can't be represented in IceStorm .asc format) self.warmboot = parse_bool(fields[2]) else: raise ParseError("Unknown preamble directive {}".format(fields[0])) def new_block(self, fields): if len(fields) != 3: raise ParseError("Expect 3 fields for top block. Received: {}".format(fields)) x = int(fields[1]) y = int(fields[2]) if (x, y) in self.tiles: return self.tiles[x, y] if fields[0] == 'logic_tile': if (x, y) not in self.ic.logic_tiles: raise ParseError("{} position({},{}) not in defined list for device".format(fields[0], x, y)) tile = LogicTile(self.ic, x, y) elif fields[0] == 'ramb_tile': if (x, y) not in self.ic.ramb_tiles: raise ParseError("{} position({},{}) not in defined list for device".format(fields[0], x, y)) tile = RAMBTile(self.ic, x, y) elif fields[0] == 'ramt_tile': if (x, y) not in self.ic.ramt_tiles: raise ParseError("{} position({},{}) not in defined list for device".format(fields[0], x, y)) tile = RAMTTile(self.ic, x, y) elif fields[0] == 'io_tile': if (x, y) not in self.ic.io_tiles: raise ParseError("{} position({},{}) not in defined list for device".format(fields[0], x, y)) tile = IOTile(self.ic, x, y) else: raise ParseError("Unknown tile type {}".format(fields[0])) self.tiles[x, y] = tile return tile def writeout(self): if self.ic is None: raise ParseError("iceconfig not set") # fix up IE/REN bits unused_ieren = set() for x in range(1, self.ic.max_x): unused_ieren.add((x, 0, 0)) unused_ieren.add((x, 0, 1)) unused_ieren.add((x, self.ic.max_y, 0)) unused_ieren.add((x, self.ic.max_y, 1)) for y in range(1, self.ic.max_y): unused_ieren.add((0, y, 0)) unused_ieren.add((0, y, 1)) unused_ieren.add((self.ic.max_x, y, 0)) unused_ieren.add((self.ic.max_x, y, 1)) for x0, y0, b0, x1, y1, b1 in self.ic.ieren_db(): if (x0, y0) in self.tiles: io_tile = self.tiles[x0, y0] else: io_tile = IOTile(self.ic, x0, y0) self.tiles[x0, y0] = io_tile if io_tile.blocks[b0] is not None: io_block = io_tile.blocks[b0] else: io_block = IOBlock(io_tile, b0) io_tile.blocks[b0] = io_block if (x1, y1) in self.tiles: ieren_tile = self.tiles[x1, y1] else: ieren_tile = IOTile(self.ic, x1, y1) self.tiles[x1, y1] = ieren_tile if io_block.enable_input != (self.ic.device == '1k'): ieren_tile.apply_directive('IoCtrl', 'IE_%d' % b1) if io_block.disable_pull_up: ieren_tile.apply_directive('IoCtrl', 'REN_%d' % b1) unused_ieren.remove((x1, y1, b1)) if self.ic.device == '1k': for x1, y1, b1 in unused_ieren: if (x1, y1) in self.tiles: ieren_tile = self.tiles[x1, y1] else: ieren_tile = IOTile(self.ic, x1, y1) self.tiles[x1, y1] = ieren_tile ieren_tile.apply_directive('IoCtrl', 'IE_%d' % b1) # fix up RAMB power-up bits for x, y in self.ic.ramb_tiles: if (x, y) in self.tiles: tile = self.tiles[x, y] else: tile = RAMBTile(self.ic, x, y) self.tiles[x, y] = tile if tile.power_up != (self.ic.device == '1k'): tile.apply_directive('RamConfig', 'PowerUp') # enable column buffers colbuf_db = self.ic.colbuf_db() for x, y in list(self.tiles): for src, dst in self.tiles[x, y].buffers + \ self.tiles[x, y].routings: if not src.startswith('glb_netwk_'): continue driving_xy = [(src_x, src_y) for src_x, src_y, dst_x, dst_y in colbuf_db if dst_x == x and dst_y == y] assert len(driving_xy) == 1 driving_xy, = driving_xy if driving_xy not in self.tiles: if driving_xy in self.ic.logic_tiles: tile = LogicTile(self.ic, *driving_xy) elif driving_xy in self.ic.ramb_tiles: tile = RAMBTile(self.ic, *driving_xy) elif driving_xy in self.ic.ramt_tiles: tile = RAMTTile(self.ic, *driving_xy) elif driving_xy in self.ic.io_tiles: tile = IOTile(self.ic, *driving_xy) else: assert False self.tiles[driving_xy] = tile self.tiles[driving_xy].apply_directive('ColBufCtrl', src) self.ic.write_file('/dev/stdout') class Tile: def __init__(self, ic, x, y): self.ic = ic self.x = x self.y = y self.data = ic.tile(x, y) self.db = ic.tile_db(x, y) self.bits_lookup = {} def add_entry(entry, bits): entry = tuple(entry) if entry in self.bits_lookup: if self.bits_lookup[entry] == bits: logging.warn( "{} {} - {} (adding) != {} (existing)".format( (x, y), entry, bits, self.bits_lookup[entry])) self.bits_lookup[entry] = bits for bits, *entry in self.db: if not ic.tile_has_entry(x, y, (bits, *entry)): continue add_entry(entry, bits) self.symbols = {} self.buffers = [] self.routings = [] self.bits_set = set() self.bits_cleared = set() def __str__(self): return "{}({}, {}, {})".format(self.__class__.__name__, self.ic.device, self.x, self.y) def apply_directive(self, *fields): if fields not in self.bits_lookup: print("Possible matches:", file=sys.stderr) for bits, *entry in self.db: if entry[0] in ("routing", "buffer"): if entry[1] == fields[1]: print(" ", entry, bits, file=sys.stderr) elif entry[1] == fields[2]: print(" ", entry, bits, file=sys.stderr) elif entry[2] == fields[2]: print(" ", entry, bits, file=sys.stderr) elif entry[2] == fields[1]: print(" ", entry, bits, file=sys.stderr) raise ParseError("No bit pattern for {} in {}".format(fields, self)) bits = self.bits_lookup[fields] self.set_bits(bits) def set_bits(self, bits): bits_set = set() bits_clear = set() for bit in bits: match = re_match_cached(r'(!?)B(\d+)\[(\d+)\]$', bit) if not match: raise ValueError("invalid bit description: %s" % bit) if match.group(1): bits_clear.add((int(match.group(2)), int(match.group(3)))) else: bits_set.add((int(match.group(2)), int(match.group(3)))) if set.intersection(bits_set, bits_clear): raise ValueError( "trying to set/clear the same bit(s) at once set:{} clear:{}".format( bits_set, bits_clear)) if set.intersection(bits_set, self.bits_cleared) or \ set.intersection(bits_clear, self.bits_set): raise ParseError("""\ conflicting bits {} setting:{:<30} - current clear:{} clearing:{:<30} - current set :{}""".format( bits, str(bits_set), self.bits_cleared, str(bits_clear), self.bits_set)) self.bits_set.update(bits_set) self.bits_cleared.update(bits_clear) for row, col in bits_set: assert row < len(self.data) assert col < len(self.data[row]) self.data[row] = self.data[row][:col] + '1' + \ self.data[row][col + 1:] def read(self, fields): if len(fields) == 3 and fields[1] == '->': src = untranslate_netname(self.x, self.y, self.ic.max_x - 1, self.ic.max_y - 1, fields[0]) dst = untranslate_netname(self.x, self.y, self.ic.max_x - 1, self.ic.max_y - 1, fields[2]) dst = revert_to_fabout(self.ic, self.x, self.y, dst) if (src, dst) not in self.buffers: self.buffers.append((src, dst)) self.apply_directive('buffer', src, dst) elif len(fields) == 3 and fields[1] == '~>': src = untranslate_netname(self.x, self.y, self.ic.max_x - 1, self.ic.max_y - 1, fields[0]) dst = untranslate_netname(self.x, self.y, self.ic.max_x - 1, self.ic.max_y - 1, fields[2]) dst = revert_to_fabout(self.ic, self.x, self.y, dst) if (src, dst) not in self.routings: self.routings.append((src, dst)) self.apply_directive('routing', src, dst) elif len(fields) >= 5 and (fields[1] == '->' or fields[1] == '~>'): self.read(fields[:3]) self.read(fields[2:]) elif len(fields) == 3 and fields[1] == '.sym>': nn = untranslate_netname(self.x, self.y, self.ic.max_x - 1, self.ic.max_y - 1, fields[0]) net = self.ic.get_net_number( (self.x, self.y, nn) ) self.ic.symbols.setdefault(net, set()).add(fields[2]) else: raise ParseError("Unknown Tile specification format") def new_block(self, fields): raise ParseError("Unexpected new block in {}".format(type(self).__name__)) class LogicTile(Tile): def __init__(self, ic, x, y): super().__init__(ic, x, y) self.cells = [None, None, None, None, None, None, None, None] self.neg_clk = False self.carry_in_set = False # not in global bit list?! def read(self, fields): if fields == ['NegClk'] and not self.neg_clk: self.neg_clk = True self.apply_directive('NegClk') elif fields == ['CarryInSet'] and not self.carry_in_set: self.carry_in_set = True self.apply_directive('CarryInSet') else: super().read(fields) def new_block(self, fields): for i in range(8): if fields == ['lutff_%d' % i] and self.cells[i] is None: self.cells[i] = LogicCell(self, i) return self.cells[i] raise ParseError("Unexpected new block in {}".format(type(self).__name__)) class LogicCell: def __init__(self, tile, index): self.tile = tile self.index = index self.lut_bits = ['0'] * 16 self.seq_bits = ['0'] * 4 def read(self, fields): if fields[0] == 'lut' and len(fields) == 2: self.lut_bits = fields[1] elif fields[0] == 'out' and len(fields) >= 3 and fields[1] == '=': m = re_match_cached("([0-9]+)'b([01]+)", fields[2]) if m: lut_bits = parse_verilog_bitvector_to_bits(fields[2]) # Verilog 16'bXXXX is MSB first but the bitstream wants LSB. self.lut_bits = list(lut_bits[::-1]) else: self.lut_bits = logic_expression_to_lut( ' '.join(fields[2:]), ('in_0', 'in_1', 'in_2', 'in_3')) elif fields == ['enable_carry']: self.seq_bits[0] = '1' elif fields == ['enable_dff']: self.seq_bits[1] = '1' elif fields == ['set_noreset']: self.seq_bits[2] = '1' elif fields == ['async_setreset']: self.seq_bits[3] = '1' elif len(fields) > 3 and (fields[1] == '->' or fields[1] == '~>'): self.read(fields[:3]) self.read(fields[2:]) return elif len(fields) == 3 and (fields[1] == '->' or fields[1] == '~>'): prefix = 'lutff_%d/' % self.index # Strip prefix if it is given if fields[0].startswith(prefix): fields[0] = fields[0][len(prefix):] if fields[-1].startswith(prefix): fields[-1] = fields[-1][len(prefix):] if fields[0] == 'out': self.tile.read([prefix + fields[0]] + fields[1:]) elif fields[-1].startswith('in_'): self.tile.read(fields[:-1] + [prefix + fields[-1]]) else: self.tile.read(fields) return elif len(fields) == 3 and fields[1] == '.sym>': nn = untranslate_netname(self.tile.x, self.tile.y, self.tile.ic.max_x - 1, self.tile.ic.max_y - 1, fields[0]) net = self.tile.ic.get_net_number( (self.tile.x, self.tile.y, nn) ) self.tile.ic.symbols.setdefault(net, set()).add(fields[2]) bits = ''.join([ self.lut_bits[15], self.lut_bits[12], self.lut_bits[11], self.lut_bits[ 8], self.lut_bits[ 0], self.lut_bits[ 3], self.lut_bits[ 4], self.lut_bits[ 7], self.seq_bits[ 0], self.seq_bits[ 1], self.lut_bits[14], self.lut_bits[13], self.lut_bits[10], self.lut_bits[ 9], self.lut_bits[ 1], self.lut_bits[ 2], self.lut_bits[ 5], self.lut_bits[ 6], self.seq_bits[ 2], self.seq_bits[ 3] ]) self.tile.data[self.index * 2] = \ self.tile.data[self.index * 2][:36] + bits[:10] + \ self.tile.data[self.index * 2][46:] self.tile.data[self.index * 2 + 1] = \ self.tile.data[self.index * 2 + 1][:36] + bits[10:] + \ self.tile.data[self.index * 2 + 1][46:] def new_block(self, fields): raise ParseError("Unexpected new block in {}".format(type(self).__name__)) class RAMData: def __init__(self, data): self.data = data def read(self, fields): if len(fields) == 1: self.data.append(parse_verilog_bitvector_to_hex(fields[0])) else: raise ParseError("Unexpected format in {}".format(type(self).__name__)) def new_block(self, fields): raise ParseError("Unexpected new block in {}".format(type(self).__name__)) class RAMBTile(Tile): def __init__(self, ic, x, y): super().__init__(ic, x, y) self.power_up = False def read(self, fields): if fields == ['power_up'] and not self.power_up: self.power_up = True else: super().read(fields) def new_block(self, fields): if fields == ['data'] and (self.x, self.y) not in self.ic.ram_data: self.ic.ram_data[self.x, self.y] = data = [] return RAMData(data) raise ParseError("Unexpected new block in {}".format(type(self).__name__)) class RAMTTile(Tile): def __init__(self, ic, x, y): super().__init__(ic, x, y) def read(self, fields): if fields == ['NegClk'] or fields[0] == 'RamConfig': self.apply_directive(*fields) # TODO else: super().read(fields) class IOTile(Tile): def __init__(self, ic, x, y): super().__init__(ic, x, y) self.blocks = [None, None] def read(self, fields): if len(fields) == 2 and fields[0] == 'PLL': self.apply_directive(*fields) # TODO else: super().read(fields) def new_block(self, fields): if fields == ['io_0'] and self.blocks[0] is None: self.blocks[0] = IOBlock(self, 0) return self.blocks[0] if fields == ['io_1'] and self.blocks[1] is None: self.blocks[1] = IOBlock(self, 1) return self.blocks[1] raise ParseError("Unexpected new block in {}".format(type(self).__name__)) class IOBlock: def __init__(self, tile, index): self.tile = tile self.index = index self.input_pin_type = None self.output_pin_type = None self.enable_input = False self.disable_pull_up = False def read(self, fields): if fields[0] == 'input_pin_type' and fields[1] == '=' \ and len(fields) == 3 and self.input_pin_type is None: self.input_pin_type = [ 'registered_pin', 'simple_input_pin', 'latched_registered_pin', 'latched_pin'].index(fields[2]) for i in range(2): if self.input_pin_type & 1 << i: self.tile.apply_directive('IOB_%d' % self.index, 'PINTYPE_%d' % i) elif fields[0] == 'output_pin_type' and fields[1] == '=' \ and len(fields) == 3 and self.output_pin_type is None: self.output_pin_type = [ 'no_output', '1', '2', '3', 'DDR', 'REGISTERED', 'simple_output_pin', 'REGISTERED_INVERTED', 'DDR_ENABLE', 'REGISTERED_ENABLE', 'OUTPUT_TRISTATE', 'REGISTERED_ENABLE_INVERTED', 'DDR_ENABLE_REGISTERED', 'REGISTERED_ENABLE_REGISTERED', 'ENABLE_REGISTERED', 'REGISTERED_ENABLE_REGISTERED_INVERTED'].index(fields[2]) for i in range(4): if self.output_pin_type & 1 << i: self.tile.apply_directive('IOB_%d' % self.index, 'PINTYPE_%d' % (i + 2)) elif fields == ['enable_input'] and not self.enable_input: self.enable_input = True elif fields == ['disable_pull_up'] and not self.disable_pull_up: self.disable_pull_up = True elif fields[0] in ('GLOBAL_BUFFER_OUTPUT', 'io_%d/GLOBAL_BUFFER_OUTPUT' % self.index) \ and fields[1] == '->' \ and fields[2].startswith('glb_netwk_'): glb_net = int(fields[2][10:]) padin_loc = self.tile.ic.padin_pio_db()[glb_net] if padin_loc != (self.tile.x, self.tile.y, self.index): raise ParseError("""\ GLOBAL_BUFFER_OUTPUT not valid for glb_netwk_{} Driver at io_tile {},{} io{} Should be at io_tile {},{} io{} """.format(glb_net, self.tile.x, self.tile.y, self.index, *padin_loc)) bit = [bit for bit in self.tile.ic.extra_bits_db() if self.tile.ic.extra_bits_db()[bit] == ("padin_glb_netwk", fields[2][10:])] assert len(bit) == 1 self.tile.ic.extra_bits.add(bit[0]) elif len(fields) > 3 and (fields[1] == '->' or fields[1] == '~>'): self.read(fields[:3]) self.read(fields[2:]) elif len(fields) == 3 and (fields[1] == '->' or fields[1] == '~>'): prefix = 'io_%d/' % self.index # Strip prefix if it is given if fields[0].startswith(prefix): fields[0] = fields[0][len(prefix):] if fields[-1].startswith(prefix): fields[-1] = fields[-1][len(prefix):] if fields[0] in ('D_IN_0', 'D_IN_1'): self.tile.read([prefix + fields[0]] + fields[1:]) elif fields[-1] in ('cen', 'D_OUT_0', 'D_OUT_1', 'inclk', #'LATCH_INPUT_VALUE', 'outclk', 'OUT_ENB'): self.tile.read(fields[:-1] + [prefix + fields[-1]]) else: self.tile.read(fields) elif len(fields) == 3 and fields[1] == '.sym>': nn = untranslate_netname(self.tile.x, self.tile.y, self.tile.ic.max_x - 1, self.tile.ic.max_y - 1, fields[0]) net = self.tile.ic.get_net_number( (self.tile.x, self.tile.y, nn) ) self.tile.ic.symbols.setdefault(net, set()).add(fields[2]) else: raise ParseError("Unknown IOBlock specification format: {}".format(fields)) def new_block(self, fields): raise ParseError("Unexpected new block in {}".format(type(self).__name__)) def main1(path): f = open(path, 'r') stack = [Main()] for i, line in enumerate(f): fields = line.split('#')[0].split() try: if not fields: pass # empty line elif fields == ['}']: stack.pop() if not stack: raise ParseError("Parsing stack empty before expected") elif fields[-1] == '{': stack.append(stack[-1].new_block(fields[:-1])) else: stack[-1].read(fields) except ParseError as e: sys.stderr.write("Parse error in line %d:\n" % (i + 1)) sys.stderr.write(line) if e.args: sys.stderr.write("\n") print(*e.args, file=sys.stderr) sys.exit(1) if len(stack) != 1: sys.stderr.write("Parse error: unexpected end of file") sys.exit(1) f.close() stack[0].writeout() def main(): program_short_name = os.path.basename(sys.argv[0]) try: opts, args = getopt.getopt(sys.argv[1:], '', ['help', 'version']) except getopt.GetoptError as e: sys.stderr.write("%s: %s\n" % (program_short_name, e.msg)) sys.stderr.write("Try `%s --help' for more information.\n" % program_short_name) sys.exit(1) for opt, arg in opts: if opt == '--help': sys.stderr.write("""\ Create an ASCII bitstream from a high-level bitstream representation. Usage: %s [OPTION]... FILE --help display this help and exit --version output version information and exit If you have a bug report, please file an issue on github: https://github.com/rlutz/icestorm/issues """ % program_short_name) sys.exit(0) if opt == '--version': sys.stderr.write("""\ icebox_hlc2asc - create an ASCII bitstream from a high-level representation Copyright (C) 2017 Roland Lutz This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. """) sys.exit(0) if not args: sys.stderr.write("%s: missing argument\n" % (program_short_name)) sys.stderr.write("Try `%s --help' for more information.\n" % program_short_name) sys.exit(1) if len(args) != 1: sys.stderr.write("%s: too many arguments\n" % (program_short_name)) sys.stderr.write("Try `%s --help' for more information.\n" % program_short_name) sys.exit(1) if args[0] == '-': main1('/dev/stdin') else: main1(args[0]) if __name__ == '__main__': main()