#!/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
GLB_NETWK_EXTERNAL_BLOCKS = [(13, 8, 1), (0, 8, 1), (7, 17, 0), (7, 0, 0),
(0, 9, 0), (13, 9, 0), (6, 0, 1), (6, 17, 1)]
GLB_NETWK_INTERNAL_TILES = [(7, 0), (7, 17), (13, 9), (0, 9),
(6, 17), (6, 0), (0, 8), (13, 8)]
## Get the global 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 net name
#
# \return the global name of the net if it is a span wire, otherwise
# the unmodified net name
#
# There are 46624 span wires on the 1k (not counting dummies):
#
# span4_x[1..12]_g[1..20]_[0..11]
# span4_y[1..16]_g[1..16]_[0..11]
# span12_x[1..12]_g[1..28]_[0..1]
# span12_y[1..16]_g[1..24]_[0..1]
#
# span4_left_g[3..16]_[0..3]
# span4_right_g[5..18]_[0..3]
# span4_bottom_g[3..12]_[0..3]
# span4_top_g[5..14]_[0..3]
#
# span4_topleft[2,4,6,8]_[0..3]
# span4_bottomright[2,4,6,8]_[0..3]
#
# dummy_y[1..16]_g[0..3]_[0..11]
#
# "Dummy" nets are horizontal accesses to non-existing vertical span
# wires on the right edge which are listed by icebox but don't
# actually connect to anything outside the tile itself.
def translate_netname(x, y, fw, fh, net):
def group_and_index(s, group_size):
n = int(s)
g = n // group_size
i = n % group_size
if g % 2 == 1:
i = i + 1 - (i % 2) * 2
return g, i
# logic and RAM tiles
match = re.match(r'sp4_h_r_(\d+)$', net)
if match is not None:
g, i = group_and_index(match.group(1), 12)
return 'span4_y%d_g%d_%d' % (y, x - g + 4, i)
match = re.match(r'sp4_h_l_(\d+)$', net)
if match is not None:
g, i = group_and_index(match.group(1), 12)
return 'span4_y%d_g%d_%d' % (y, x - g + 3, i)
match = re.match(r'sp4_v_b_(\d+)$', net)
if match is not None:
g, i = group_and_index(match.group(1), 12)
return 'span4_x%d_g%d_%d' % (x, y + g, i)
match = re.match(r'sp4_v_t_(\d+)$', net)
if match is not None:
g, i = group_and_index(match.group(1), 12)
return 'span4_x%d_g%d_%d' % (x, y + g + 1, i)
match = re.match(r'sp4_r_v_b_(\d+)$', net)
if match is not None:
g, i = group_and_index(match.group(1), 12)
if x == fw:
# this net doesn't connect anywhere
return 'dummy_y%d_g%d_%d' % (y, g, i)
else:
return 'span4_x%d_g%d_%d' % (x + 1, y + g, i)
match = re.match(r'sp12_h_r_(\d+)$', net)
if match is not None:
g, i = group_and_index(match.group(1), 2)
return 'span12_y%d_g%d_%d' % (y, x - g + 12, i)
match = re.match(r'sp12_h_l_(\d+)$', net)
if match is not None:
g, i = group_and_index(match.group(1), 2)
return 'span12_y%d_g%d_%d' % (y, x - g + 11, i)
match = re.match(r'sp12_v_b_(\d+)$', net)
if match is not None:
g, i = group_and_index(match.group(1), 2)
return 'span12_x%d_g%d_%d' % (x, y + g, i)
match = re.match(r'sp12_v_t_(\d+)$', net)
if match is not None:
g, i = group_and_index(match.group(1), 2)
return 'span12_x%d_g%d_%d' % (x, y + g + 1, i)
# I/O tiles
match = re.match(r'span4_horz_(\d+)$', net)
if match is not None:
g, i = group_and_index(match.group(1), 12)
if x == 0:
return 'span4_y%d_g%d_%d' % (y, x - g + 4, i)
else:
return 'span4_y%d_g%d_%d' % (y, x - g + 3, i)
match = re.match(r'span4_vert_(\d+)$', net)
if match is not None:
g, i = group_and_index(match.group(1), 12)
if y == 0:
return 'span4_x%d_g%d_%d' % (x, y + g + 1, i)
else:
return 'span4_x%d_g%d_%d' % (x, y + g, i)
match = re.match(r'span12_horz_(\d+)$', net)
if match is not None:
g, i = group_and_index(match.group(1), 2)
if x == 0:
return 'span12_y%d_g%d_%d' % (y, x - g + 12, i)
else:
return 'span12_y%d_g%d_%d' % (y, x - g + 11, i)
match = re.match(r'span12_vert_(\d+)$', net)
if match is not None:
g, i = group_and_index(match.group(1), 2)
if y == 0:
return 'span12_x%d_g%d_%d' % (x, y + g + 1, i)
else:
return 'span12_x%d_g%d_%d' % (x, y + g, i)
# I/O tiles - peripheral wires
match = re.match(r'span4_horz_r_(\d+)$', net)
if match is not None:
n = int(match.group(1)); g = n // 4; i = n % 4
if y == 0:
if fw - x + g - 4 < 0:
return 'span4_bottomright%d_%d' % ((fw - x + 1 + g) * 2, i)
elif x - g + 1 < 0:
return 'span4_left_g%d_%d' % (-x + 1 + g, i)
else:
return 'span4_bottom_g%d_%d' % (x + 4 - g, i)
else:
if x - g - 1 < 0:
return 'span4_topleft%d_%d' % ((x + 4 - g) * 2, i)
elif x - g + 1 >= fw:
return 'span4_right_g%d_%d' % (fh + fw - x + 1 + g, i)
else:
return 'span4_top_g%d_%d' % (x + 4 - g, i)
match = re.match(r'span4_horz_l_(\d+)$', net)
if match is not None:
n = int(match.group(1)); g = n // 4; i = n % 4
if y == 0:
if x - g < 0:
return 'span4_left_g%d_%d' % (-x + 2 + g, i)
else:
return 'span4_bottom_g%d_%d' % (x + 3 - g, i)
else:
if x - g - 2 < 0:
return 'span4_topleft%d_%d' % ((x + 3 - g) * 2, i)
else:
return 'span4_top_g%d_%d' % (x + 3 - g, i)
match = re.match(r'span4_vert_b_(\d+)$', net)
if match is not None:
n = int(match.group(1)); g = n // 4; i = n % 4
if x == 0:
if y + g - 3 < 0:
return 'span4_bottom_g%d_%d' % (-y + 5 - g, i)
if fh - y - g < 0:
return 'span4_topleft%d_%d' % ((fh + 5 - y - g) * 2, i)
else:
return 'span4_left_g%d_%d' % (y + g, i)
else:
if y + g - 5 < 0:
return 'span4_bottomright%d_%d' % ((y + g) * 2, i)
elif y + g >= fh + 3:
return 'span4_top_g%d_%d' % (fw + fh + 5 - y - g, i)
else:
return 'span4_right_g%d_%d' % (y + g, i)
match = re.match(r'span4_vert_t_(\d+)$', net)
if match is not None:
n = int(match.group(1)); g = n // 4; i = n % 4
if x == 0:
if fh - y - g - 1 < 0:
return 'span4_topleft%d_%d' % ((fh + 4 - y - g) * 2, i)
else:
return 'span4_left_g%d_%d' % (y + g + 1, i)
else:
if y + g >= fh + 2:
return 'span4_top_g%d_%d' % (fw + fh + 4 - y - g, i)
else:
return 'span4_right_g%d_%d' % (y + g + 1, i)
return net
## Return the human-readable name of the \c fabout net of IO tile
## (x, y).
def lookup_fabout(x, y):
if (x, y) in GLB_NETWK_INTERNAL_TILES:
return 'glb_netwk_%d' % GLB_NETWK_INTERNAL_TILES.index((x, y))
return 'fabout'
## Remove an argument from a LUT string and an associated list of
## argument names.
#
# This is a helper function for \ref lut_to_logic_expression.
#
# \param lut string of 2^N `0' or `1' characters representing the
# logic of an Nx1 look-up table
# \param args list of N strings containing the human-readable names
# of the arguments
# \param i index of the argument to remove
# \param keep boolean value indicating which value of the removed
# argument is to be assumed in the resulting LUT
#
# \return a new pair (lut, args) with the argument removed
def discard_argument(lut, args, i, keep):
assert len(lut) == 1 << len(args)
assert i >= 0 and i < len(args)
return ''.join(bit for j, bit in enumerate(lut)
if (j & (1 << i) != 0) == keep), \
args[:i] + args[i + 1:]
## Negate a tuple representation of a logic expression.
#
# This is a helper function for \ref lut_to_logic_expression.
def negate_expr(expr):
if len(expr) == 2:
op, a = expr
assert op == 'not'
return a
if len(expr) != 3:
return 'not', expr
a, op, b = expr
if op == 'and':
return negate_expr(a), 'or', negate_expr(b)
if op == 'or':
return negate_expr(a), 'and', negate_expr(b)
assert op == 'xor'
if len(a) == 2 and a[0] == 'not':
return a[1], op, b
if len(b) == 2 and b[0] == 'not':
return a, op, b[1]
return negate_expr(a), op, b
## Convert a tuple representation of a logic expression into a string.
#
# This is a helper function for \ref lut_to_logic_expression.
#
# \param expr the expression to convert
# \param parenthize whether a compound expression should be
# surrounded by parentheses
def stringify(expr, parenthize):
if type(expr) == str:
return expr
assert type(expr) == tuple
if len(expr) == 2:
op, a = expr
assert op == 'not'
assert type(a) == str
return "!" + a
if len(expr) == 5:
a, op0, b, op1, c = expr
assert op0 == '?' and op1 == ':'
s = '%s ? %s : %s' % (stringify(a, False), stringify(b, False),
stringify(c, False))
if parenthize:
return '(%s)' % s
return s
assert len(expr) == 3
a, op, b = expr
l = [a, b]
i = 0
while i < len(l):
if type(l[i]) == tuple and len(l[i]) == 3 and l[i][1] == op:
l = l[:i] + [l[i][0], l[i][2]] + l[i + 1:]
else:
i += 1
if op == 'and':
op = '&'
elif op == 'xor':
op = '^'
elif op == 'or':
op = '|'
s = (' %s ' % op).join(stringify(x, True) for x in l)
if parenthize:
return '(%s)' % s
return s
## Remove arguments which don't affect the result from a LUT string
## and an associated list of argument names.
#
# This is a helper function for \ref lut_to_logic_expression.
#
# \param lut string of 2^N `0' or `1' characters representing the
# logic of an Nx1 look-up table
# \param args list of N strings containing the human-readable names
# of the arguments
#
# \return a new pair (lut, args) with all unused arguments
# removed
def discard_unused_arguments(lut, args):
assert len(lut) == 1 << len(args)
i = 0
while i < len(args):
diff = False
for j in range(len(lut)):
if j & (1 << i) == 0 and lut[j] != lut[j | (1 << i)]:
diff = True
if not diff:
lut, args = discard_argument(lut, args, i, False)
else:
i += 1
return lut, args
## Convert a LUT string to a logic expression.
#
# \param lut string of 2^N `0' or `1' characters representing the
# logic of an Nx1 look-up table
# \param args list of N strings containing the human-readable names
# of the arguments
#
# \return a string containing a human-readable logic expression
# equivalent to the look-up table
#
# Example: lut_to_logic_expression('00010000', ['a', 'b', 'c']) -> 'a & b & !c'
def lut_to_logic_expression(lut, args):
lut, args = discard_unused_arguments(lut, args)
# filter out independent top-level arguments
toplevel_args = []
i = 0
while i < len(args) and len(args) >= 2:
ai_0 = set(bit for j, bit in enumerate(lut) if j & (1 << i) == 0)
ai_1 = set(bit for j, bit in enumerate(lut) if j & (1 << i) != 0)
assert len(ai_0) == 2 or len(ai_1) == 2
if len(ai_0) == 1:
# expression is constant if this argument is 0
# e = (...) & arg or e = (...) | !arg
if tuple(ai_0)[0] == '0':
toplevel_args.append(('and', args[i]))
else:
toplevel_args.append(('or', ('not', args[i])))
lut, args = discard_argument(lut, args, i, True)
i = 0
continue
if len(ai_1) == 1:
# expression is constant if this argument is 1
# e = (...) & !arg or e = (...) | arg
if tuple(ai_1)[0] == '0':
toplevel_args.append(('and', ('not', args[i])))
else:
toplevel_args.append(('or', args[i]))
lut, args = discard_argument(lut, args, i, False)
i = 0
continue
i += 1
i = 0
while i < len(args) and len(args) >= 2:
is_xor = True
for j in range(len(lut)):
if j & (1 << i) == 0 and lut[j] == lut[j | (1 << i)]:
is_xor = False
break
if is_xor:
toplevel_args.append(('xor', args[i]))
lut, args = discard_argument(lut, args, i, False)
continue
i += 1
# detect simple top-level ternary conditions
i = 0
while i < len(args) and len(args) >= 3:
j = i + 1
while j < len(args):
ai_0_aj_0 = set(bit for k, bit in enumerate(lut)
if k & (1 << i) == 0 and k & (1 << j) == 0)
ai_0_aj_1 = set(bit for k, bit in enumerate(lut)
if k & (1 << i) == 0 and k & (1 << j) != 0)
ai_1_aj_0 = set(bit for k, bit in enumerate(lut)
if k & (1 << i) != 0 and k & (1 << j) == 0)
ai_1_aj_1 = set(bit for k, bit in enumerate(lut)
if k & (1 << i) != 0 and k & (1 << j) != 0)
assert len(ai_0_aj_0) == 2 or len(ai_0_aj_1) == 2 or \
len(ai_1_aj_0) == 2 or len(ai_1_aj_1) == 2
if (len(ai_0_aj_0) == 2 or len(ai_0_aj_1) == 2) and \
(len(ai_1_aj_0) == 2 or len(ai_1_aj_1) == 2) and \
(len(ai_0_aj_0) == 2 or len(ai_1_aj_0) == 2) and \
(len(ai_0_aj_1) == 2 or len(ai_1_aj_1) == 2):
j += 1
continue
ai_doesnt_matter_for_aj_0 = True
ai_doesnt_matter_for_aj_1 = True
aj_doesnt_matter_for_ai_0 = True
aj_doesnt_matter_for_ai_1 = True
for k in range(len(lut)):
if k & (1 << i) != 0 or k & (1 << j) != 0:
continue
if lut[k] != lut[k | (1 << i)]:
ai_doesnt_matter_for_aj_0 = False
if lut[k | (1 << j)] != lut[k | (1 << i) | (1 << j)]:
ai_doesnt_matter_for_aj_1 = False
if lut[k] != lut[k | (1 << j)]:
aj_doesnt_matter_for_ai_0 = False
if lut[k | (1 << i)] != lut[k | (1 << i) | (1 << j)]:
aj_doesnt_matter_for_ai_1 = False
if len(ai_0_aj_0) == 1 and len(ai_0_aj_1) == 1 and \
aj_doesnt_matter_for_ai_1:
assert tuple(ai_0_aj_0)[0] != tuple(ai_0_aj_1)[0]
if tuple(ai_0_aj_0)[0] == '0':
toplevel_args.append((args[i], '?', ':', args[j]))
else:
toplevel_args.append((args[i], '?', ':', ('not', args[j])))
lut, args = discard_argument(lut, args, i, True)
# break loops
i = len(args)
j = len(args)
break
if len(ai_1_aj_0) == 1 and len(ai_1_aj_1) == 1 and \
aj_doesnt_matter_for_ai_0:
assert tuple(ai_1_aj_0)[0] != tuple(ai_1_aj_1)[0]
if tuple(ai_1_aj_0)[0] == '0':
toplevel_args.append((args[i], '?', args[j], ':'))
else:
toplevel_args.append((args[i], '?', ('not', args[j]), ':'))
lut, args = discard_argument(lut, args, i, False)
# break loops
i = len(args)
j = len(args)
break
if len(ai_0_aj_0) == 1 and len(ai_1_aj_0) == 1 and \
ai_doesnt_matter_for_aj_1:
assert tuple(ai_0_aj_0)[0] != tuple(ai_1_aj_0)[0]
if tuple(ai_0_aj_0)[0] == '0':
toplevel_args.append((args[j], '?', ':', args[i]))
else:
toplevel_args.append((args[j], '?', ':', ('not', args[i])))
lut, args = discard_argument(lut, args, j, True)
# break loops
i = len(args)
j = len(args)
break
if len(ai_0_aj_1) == 1 and len(ai_1_aj_1) == 1 and \
ai_doesnt_matter_for_aj_0:
assert tuple(ai_0_aj_1)[0] != tuple(ai_1_aj_1)[0]
if tuple(ai_0_aj_1)[0] == '0':
toplevel_args.append((args[j], '?', args[i], ':'))
else:
toplevel_args.append((args[j], '?', ('not', args[i]), ':'))
lut, args = discard_argument(lut, args, j, False)
# break loops
i = len(args)
j = len(args)
break
j += 1
i += 1
lut, args = discard_unused_arguments(lut, args)
# group pairwise isolated arguments
i = 0
while i < len(args):
j = i + 1
while j < len(args):
ai_doesnt_matter_for_aj_0 = True
ai_doesnt_matter_for_aj_1 = True
aj_doesnt_matter_for_ai_0 = True
aj_doesnt_matter_for_ai_1 = True
both_dont_matter_if_equal = True
both_dont_matter_if_unequal = True
for k in range(len(lut)):
if k & (1 << i) != 0 or k & (1 << j) != 0:
continue
if lut[k] != lut[k | (1 << i)]:
ai_doesnt_matter_for_aj_0 = False
if lut[k | (1 << j)] != lut[k | (1 << i) | (1 << j)]:
ai_doesnt_matter_for_aj_1 = False
if lut[k] != lut[k | (1 << j)]:
aj_doesnt_matter_for_ai_0 = False
if lut[k | (1 << i)] != lut[k | (1 << i) | (1 << j)]:
aj_doesnt_matter_for_ai_1 = False
if lut[k] != lut[k | (1 << i) | (1 << j)]:
both_dont_matter_if_equal = False
if lut[k | (1 << i)] != lut[k | (1 << j)]:
both_dont_matter_if_unequal = False
# There are five possibilities of coupled arguments: one
# of the four combinations differs from the other three,
# or they are xor'ed
if ai_doesnt_matter_for_aj_1 and \
aj_doesnt_matter_for_ai_1 and \
both_dont_matter_if_unequal:
# special case is ai=0 aj=0
args = args[:i] + ((args[i], 'or', args[j]), ) + args[i + 1:]
lut, args = discard_argument(lut, args, j, False)
j = i + 1
elif ai_doesnt_matter_for_aj_1 and \
aj_doesnt_matter_for_ai_0 and \
both_dont_matter_if_equal:
# special case is ai=1 aj=0
args = args[:i] + ((args[i], 'and', negate_expr(args[j])), ) + \
args[i + 1:]
lut, args = discard_argument(lut, args, j, False)
j = i + 1
elif ai_doesnt_matter_for_aj_0 and \
aj_doesnt_matter_for_ai_1 and \
both_dont_matter_if_equal:
# special case is ai=0 aj=1
args = args[:i] + ((args[i], 'or', negate_expr(args[j])), ) + \
args[i + 1:]
lut, args = discard_argument(lut, args, j, True)
j = i + 1
elif ai_doesnt_matter_for_aj_0 and \
aj_doesnt_matter_for_ai_0 and \
both_dont_matter_if_unequal:
# special case is ai=1 aj=1
args = args[:i] + ((args[i], 'and', args[j]), ) + args[i + 1:]
lut, args = discard_argument(lut, args, j, True)
j = i + 1
elif both_dont_matter_if_equal and \
both_dont_matter_if_unequal:
args = args[:i] + ((args[i], 'xor', args[j]), ) + args[i + 1:]
lut, args = discard_argument(lut, args, j, False)
j = i + 1
else:
j += 1
i += 1
# collect the result
if not args:
# constant expression
assert len(lut) == 1
return lut
negate_result = lut.count('1') > lut.count('0')
if negate_result:
lut = ''.join('1' if bit == '0' else '0' for bit in lut)
result = None
for i, bit in enumerate(lut):
if bit == '0':
continue
expr = None
for j, arg in enumerate(args):
if i & (1 << j) == 0:
arg = negate_expr(arg)
if expr is None:
expr = arg
else:
expr = (expr, 'and', arg)
if result is None:
result = expr
else:
result = (result, 'or', expr)
if negate_result:
result = negate_expr(result)
for toplevel_arg in reversed(toplevel_args):
if len(toplevel_arg) != 4:
result = tuple(reversed(toplevel_arg)) + (result, )
elif toplevel_arg[2] == ':':
result = toplevel_arg[0:2] + (result, ) + toplevel_arg[2:4]
else:
assert toplevel_arg[3] == ':'
result = toplevel_arg + (result, )
return stringify(result, False)
class Fabric:
def __init__(self, ic):
self.ic = ic
self.tiles = {}
#self.colbuf = set()
io_blocks = {}
ieren_blocks = {}
for x0, y0, b0, x1, y1, b1 in self.ic.ieren_db():
i = IOBlock()
assert (x0, y0, b0) not in io_blocks
io_blocks[x0, y0, b0] = i
assert (x1, y1, b1) not in ieren_blocks
ieren_blocks[x1, y1, b1] = i
for xy in ic.io_tiles:
assert xy not in self.tiles
self.tiles[xy] = IOTile(self, xy,
(io_blocks.pop((xy[0], xy[1], 0), None),
io_blocks.pop((xy[0], xy[1], 1), None)),
(ieren_blocks.pop((xy[0], xy[1], 0), None),
ieren_blocks.pop((xy[0], xy[1], 1), None)))
assert not io_blocks
assert not ieren_blocks
for xy in ic.logic_tiles:
assert xy not in self.tiles
self.tiles[xy] = LogicTile(self, xy)
for xy in ic.ramb_tiles:
assert xy not in self.tiles
self.tiles[xy] = RAMBTile(self, xy)
for xy in ic.ramt_tiles:
assert xy not in self.tiles
self.tiles[xy] = RAMTTile(self, xy)
for x, y in self.tiles:
assert x >= 0 and x <= self.ic.max_x
assert y >= 0 and y <= self.ic.max_y
for x in range(self.ic.max_x + 1):
for y in range(self.ic.max_y + 1):
should_exist = (x > 0 and x < self.ic.max_x) or \
(y > 0 and y < self.ic.max_y)
assert ((x, y) in self.tiles) == should_exist
for xy in ic.ram_data:
assert type(self.tiles.get(xy, None)) == RAMBTile
#colbuf_db = ic.colbuf_db()
#for x, y, i in self.colbuf:
# exists = False
# for src_x, src_y, dst_x, dst_y in colbuf_db:
# if src_x != x or src_y != y:
# continue
# assert (dst_x, dst_y) in self.tiles
# assert not self.tiles[dst_x, dst_y].colbuf[i]
# self.tiles[dst_x, dst_y].colbuf[i] = True
# exists = True
# assert exists
#
#for xy in self.tiles:
# for br in self.tiles[xy].buffer_and_routing:
# if br[0].startswith('glb_netwk_'):
# assert self.tiles[xy].colbuf[int(br[0][10:])]
for bit in self.ic.extra_bits:
directive, arg = self.ic.lookup_extra_bit(bit)
assert directive == 'padin_glb_netwk'
x, y, n = GLB_NETWK_EXTERNAL_BLOCKS[int(arg)]
assert type(self.tiles.get((x, y), None)) == IOTile
block = self.tiles[x, y].io_blocks[n]
assert block is not None
block.padin_glb_netwk = True
def printout(self, options):
print('device "%s" %d %d' % (self.ic.device, self.ic.max_x - 1,
self.ic.max_y - 1))
print('')
# internal_configuration_oscillator_frequency = low | medium | high
#print('coldboot = off')
print('warmboot = on') # IceStorm assumes this to be always on
for xy in sorted(self.tiles.keys(), key = lambda xy: (xy[1], xy[0])):
self.tiles[xy].printout(options)
class Tile:
def __init__(self, fabric, xy, data, is_logic_block):
self.fabric = fabric
self.ic = fabric.ic
self.xy = xy
self.data = data
self.buffer_and_routing = set()
self.used_buffer_and_routing = set()
self.text = set()
self.bitinfo = list()
self.unknown_bits = False
x, y = xy
db = self.ic.tile_db(x, y)
mapped_bits = set()
# 'data' is a list of strings containing a series of zeroes and
# ones. 'bits' is a set of strings containing an entry
# "B[]" or "!B[]" for each bit.
bits = set()
for k, line in enumerate(data):
for i in range(len(line)):
if line[i] == '1':
bits.add('B%d[%d]' % (k, i))
else:
bits.add('!B%d[%d]' % (k, i))
for entry in db:
# LC bits don't have a useful entry in the database; skip them
# for now
if re.match(r'LC_', entry[1]):
continue
# some nets have different names depending on the tile; filter
# out non-applicable net names
if entry[1] in ('routing', 'buffer') and (
not self.ic.tile_has_net(x, y, entry[2]) or
not self.ic.tile_has_net(x, y, entry[3])):
continue
# are all required bits set/unset?
match = True
for bit in entry[0]:
if not bit in bits:
match = False
if match:
for bit in entry[0]:
mapped_bits.add(bit)
if entry[1:] == ['IoCtrl', 'IE_0']:
if match != (self.ic.device == '1k'):
self.ieren_blocks[0].enable_input = True
continue
if entry[1:] == ['IoCtrl', 'REN_0']:
if match:
self.ieren_blocks[0].disable_pull_up = True
continue
if entry[1:] == ['IoCtrl', 'IE_1']:
if match != (self.ic.device == '1k'):
self.ieren_blocks[1].enable_input = True
continue
if entry[1:] == ['IoCtrl', 'REN_1']:
if match:
self.ieren_blocks[1].disable_pull_up = True
continue
if entry[1].startswith('IOB_') and entry[2].startswith('PINTYPE_'):
if match:
self.io_blocks[int(entry[1][4:])].pintype \
|= 1 << int(entry[2][8:])
continue
if entry[1:] == ['RamConfig', 'PowerUp']:
if match != (self.ic.device == '1k'):
self.text.add('power_up')
continue
if entry[1] == 'routing':
if match:
src = translate_netname(self.xy[0], self.xy[1],
self.ic.max_x - 1,
self.ic.max_y - 1, entry[2])
dst = translate_netname(self.xy[0], self.xy[1],
self.ic.max_x - 1,
self.ic.max_y - 1, entry[3])
if dst == 'fabout':
dst = lookup_fabout(*self.xy)
self.buffer_and_routing.add((src, '<->', dst))
continue
if entry[1] == 'buffer':
if match:
src = translate_netname(self.xy[0], self.xy[1],
self.ic.max_x - 1,
self.ic.max_y - 1, entry[2])
dst = translate_netname(self.xy[0], self.xy[1],
self.ic.max_x - 1,
self.ic.max_y - 1, entry[3])
if dst == 'fabout':
dst = lookup_fabout(*self.xy)
self.buffer_and_routing.add((src, '->', dst))
continue
if entry[1] == 'ColBufCtrl':
assert entry[2].startswith('glb_netwk_')
#if match:
# fabric.colbuf.add(self.xy + (int(entry[2][10:]), ))
continue
if match:
self.text.add(' '.join(entry[1:]))
for prefix in ('local_', 'glb2local_'):
for fst in [fst for fst in self.buffer_and_routing
if fst[-1].startswith(prefix)]:
used = False
for snd in [snd for snd in self.buffer_and_routing
if snd[0] == fst[-1]]:
self.buffer_and_routing.remove(snd)
self.buffer_and_routing.add(fst[:-1] + snd)
used = True
if used:
self.buffer_and_routing.remove(fst)
for k, line in enumerate(data):
self.bitinfo.append('')
extra_text = ''
for i in range(len(line)):
if 36 <= i <= 45 and is_logic_block:
self.bitinfo[-1] += '*' if line[i] == '1' else '-'
elif line[i] == '1' and 'B%d[%d]' % (k, i) not in mapped_bits:
self.unknown_bits = True
extra_text += ' B%d[%d]' % (k, i)
self.bitinfo[-1] += '?'
else:
self.bitinfo[-1] += '+' if line[i] == '1' else '-'
self.bitinfo[-1] += extra_text
def get_hlc(self):
return sorted(set.union(self.text,
set(' '.join(t)
for t in set.difference(
self.buffer_and_routing,
self.used_buffer_and_routing))))
def printout(self, stmt, options):
text = self.get_hlc()
if text or self.unknown_bits or options.print_all:
if self.unknown_bits or options.print_map:
print()
if self.unknown_bits:
print("; Warning: No DB entries for some bits:")
for k, line in enumerate(self.bitinfo):
print("; %4s %s" % ('B%d' % k, line))
print()
print("%s %d %d {" % (stmt, self.xy[0], self.xy[1]))
for line in text:
print(" " + line)
print("}")
class LogicCell:
def __init__(self, tile, lcidx):
self.lut = ''.join(icebox.get_lutff_lut_bits(tile.data, lcidx))
self.expr = lut_to_logic_expression(
self.lut, ('in_0', 'in_1', 'in_2', 'in_3'))
self.options = []
lutff_option_bits = ''.join(icebox.get_lutff_seq_bits(tile.data, lcidx))
if lutff_option_bits[0] == '1': self.options.append('enable_carry')
if lutff_option_bits[1] == '1': self.options.append('enable_dff')
if lutff_option_bits[2] == '1': self.options.append('set_noreset')
if lutff_option_bits[3] == '1': self.options.append('async_setreset')
self.buffer_and_routing0 = set()
self.buffer_and_routing1 = set()
for br in tuple(tile.buffer_and_routing):
if br[0] == 'lutff_%d/out' % lcidx:
self.buffer_and_routing1.add((br[0][8:], ) + br[1:])
tile.used_buffer_and_routing.add(br)
elif br[-1].startswith('lutff_%d/' % lcidx):
self.buffer_and_routing0.add(br[:-1] + (br[-1][8:], ))
tile.used_buffer_and_routing.add(br)
def get_hlc(self):
if self.lut == '0000000000000000' and not self.options:
t = []
elif len(self.expr) > 64:
t = ['lut ' + self.lut]
else:
t = ['out = ' + self.expr]
return [' '.join(t) for t in sorted(self.buffer_and_routing0,
key = lambda x: x[-1])] + \
t + self.options + \
[' '.join(t) for t in sorted(self.buffer_and_routing1,
key = lambda x: x[-1])]
class LogicTile(Tile):
def __init__(self, fabric, xy):
super().__init__(fabric, xy, fabric.ic.logic_tiles[xy], True)
self.cells = tuple(LogicCell(self, lcidx) for lcidx in range(8))
def get_hlc(self):
text = super().get_hlc()
for i, cell in reversed(tuple(enumerate(self.cells))):
t = cell.get_hlc()
if t:
text = ['lutff_%d {' % i] + \
[' %s' % s for s in t] + \
['}'] + \
text
return text
def printout(self, options):
super().printout('logic_tile', options)
class IOBlock:
def __init__(self):
# stored in the I/O tile where this block is located
self.pintype = 0
# stored in the I/O tile where this is an IE/REN block
self.enable_input = False
self.disable_pull_up = False
# stored as an extra bit
self.padin_glb_netwk = False
class IOTile(Tile):
def __init__(self, fabric, xy, io_blocks, ieren_blocks):
self.io_blocks = io_blocks
self.ieren_blocks = ieren_blocks
super().__init__(fabric, xy, fabric.ic.io_tiles[xy], False)
#self.cells = tuple(IOCell() for i in range(2))
for i, block in enumerate(io_blocks):
if block is None:
continue
block.buffer_and_routing0 = set()
block.buffer_and_routing1 = set()
for br in tuple(self.buffer_and_routing):
if br[0].startswith('io_%d/D_IN_' % i):
block.buffer_and_routing1.add((br[0][5:], ) + br[1:])
self.used_buffer_and_routing.add(br)
elif br[-1].startswith('io_%d/' % i):
block.buffer_and_routing0.add(br[:-1] + (br[-1][5:], ))
self.used_buffer_and_routing.add(br)
def get_hlc(self):
# if io_blocks[N] is None, this means there's no I/O pin there
text = super().get_hlc()
for n in (1, 0):
block = self.io_blocks[n]
if block is None:
continue
t = []
input_pt = block.pintype & 3
output_pt = block.pintype >> 2 & 15
unknown_pt = block.pintype >> 6
if input_pt != 0:
t.append('input_pin_type = %s' % (
'registered_pin',
'simple_input_pin',
'latched_registered_pin',
'latched_pin')[input_pt])
if output_pt != 0:
t.append('output_pin_type = %s' % (
'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')[output_pt])
if unknown_pt != 0:
t.append('unknown_pin_type = %d' % unknown_pt)
if block.enable_input:
t.append('enable_input')
if block.disable_pull_up:
t.append('disable_pull_up')
t += [' '.join(t) for t in sorted(block.buffer_and_routing0,
key = lambda x: x[-1])]
t += [' '.join(t) for t in sorted(block.buffer_and_routing1,
key = lambda x: x[0])]
if block.padin_glb_netwk:
t += ['GLOBAL_BUFFER_OUTPUT -> glb_netwk_%d'
% GLB_NETWK_EXTERNAL_BLOCKS.index(self.xy + (n, ))]
if t:
text = ['io_%d {' % n] + \
[' %s' % s for s in t] + \
['}'] + \
text
return text
def printout(self, options):
super().printout('io_tile', options)
class IOCell:
pass
class RAMBTile(Tile):
def __init__(self, fabric, xy):
super().__init__(fabric, xy, fabric.ic.ramb_tiles[xy], False)
if xy in fabric.ic.ram_data:
self.data = fabric.ic.ram_data[xy]
else:
self.data = None
def get_hlc(self):
text = super().get_hlc()
if self.data is not None:
text.append('')
text.append('data {')
for line in self.data:
text.append(' ' + line)
text.append('}')
return text
def printout(self, options):
super().printout('ramb_tile', options)
class RAMTTile(Tile):
def __init__(self, fabric, xy):
super().__init__(fabric, xy, fabric.ic.ramt_tiles[xy], False)
def printout(self, options):
super().printout('ramt_tile', options)
class Options:
def __init__(self):
self.print_map = False
self.print_all = False
def main():
program_short_name = os.path.basename(sys.argv[0])
options = Options()
try:
opts, args = getopt.getopt(sys.argv[1:], 'mA', ['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"
% sys.argv[0])
sys.exit(1)
for opt, arg in opts:
if opt == '--help':
sys.stderr.write("""\
Create a high-level representation from an ASCII bitstream.
Usage: %s [OPTION]... FILE
-m print tile config bitmaps
-A don't skip uninteresting tiles
--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
""" % sys.argv[0])
sys.exit(0)
if opt == '--version':
sys.stderr.write("""\
icebox_asc2hlc - create a high-level representation from an ASCII bitstream
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 opt == '-m':
options.print_map = True
elif opt == '-A':
options.print_all = True
if not args:
sys.stderr.write("%s: missing argument\n" % (program_short_name))
sys.stderr.write("Try `%s --help' for more information.\n"
% sys.argv[0])
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"
% sys.argv[0])
sys.exit(1)
ic = icebox.iceconfig()
if args[0] == '-':
ic.read_file('/dev/stdin')
else:
ic.read_file(args[0])
fabric = Fabric(ic)
fabric.printout(options)
if __name__ == '__main__':
main()