Import of icestorm-snapshot-150322.zip

This commit is contained in:
Clifford Wolf 2015-07-18 13:05:02 +02:00
commit dfeb92a46b
11 changed files with 5604 additions and 0 deletions

25
icebox/Makefile Normal file
View File

@ -0,0 +1,25 @@
all:
install:
cp icebox.py /usr/local/bin/icebox.py
cp iceboxdb.py /usr/local/bin/iceboxdb.py
cp icebox_chipdb.py /usr/local/bin/icebox_chipdb
cp icebox_diff.py /usr/local/bin/icebox_diff
cp icebox_explain.py /usr/local/bin/icebox_explain
cp icebox_html.py /usr/local/bin/icebox_html
cp icebox_maps.py /usr/local/bin/icebox_maps
cp icebox_vlog.py /usr/local/bin/icebox_vlog
uninstall:
rm -f /usr/local/bin/icebox.py
rm -f /usr/local/bin/iceboxdb.py
rm -f /usr/local/bin/icebox_chipdb
rm -f /usr/local/bin/icebox_diff
rm -f /usr/local/bin/icebox_explain
rm -f /usr/local/bin/icebox_html
rm -f /usr/local/bin/icebox_maps
rm -f /usr/local/bin/icebox_vlog
.PHONY: install uninstall

824
icebox/icebox.py Normal file
View File

@ -0,0 +1,824 @@
#!/usr/bin/python
#
# 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.
#
from __future__ import division
from __future__ import print_function
import iceboxdb
import re, sys
class iceconfig:
def __init__(self):
self.clear()
def clear(self):
self.max_x = 0
self.max_y = 0
self.logic_tiles = dict()
self.io_tiles = dict()
self.ram_tiles = dict()
self.ram_init = dict()
def setup_empty_1k(self):
self.clear()
self.max_x = 13
self.max_y = 17
for x in range(1, self.max_x):
for y in range(1, self.max_y):
if x in (3, 10):
self.ram_tiles[(x, y)] = ["0" * 42 for i in range(16)]
else:
self.logic_tiles[(x, y)] = ["0" * 54 for i in range(16)]
for x in range(1, self.max_x):
self.io_tiles[(x, 0)] = ["0" * 18 for i in range(16)]
self.io_tiles[(x, self.max_y)] = ["0" * 18 for i in range(16)]
for y in range(1, self.max_y):
self.io_tiles[(0, y)] = ["0" * 18 for i in range(16)]
self.io_tiles[(self.max_x, y)] = ["0" * 18 for i in range(16)]
def tile(self, x, y):
if (x, y) in self.io_tiles: return self.io_tiles[(x, y)]
if (x, y) in self.logic_tiles: return self.logic_tiles[(x, y)]
if (x, y) in self.ram_tiles: return self.ram_tiles[(x, y)]
return None
def tile_db(self, x, y):
if x == 0: return iotile_l_db
if y == 0: return iotile_b_db
if x == self.max_x: return iotile_r_db
if y == self.max_y: return iotile_t_db
if (x, y) in self.ram_tiles: return ramtile_db
if (x, y) in self.logic_tiles: return logictile_db
assert False
def tile_type(self, x, y):
if x == 0: return "IO"
if y == 0: return "IO"
if x == self.max_x: return "IO"
if y == self.max_y: return "IO"
if (x, y) in self.ram_tiles: return "RAM"
if (x, y) in self.logic_tiles: return "LOGIC"
assert False
def tile_pos(self, x, y):
if x == 0 and 0 < y < self.max_y: return "l"
if y == 0 and 0 < x < self.max_x: return "b"
if x == self.max_x and 0 < y < self.max_y: return "r"
if y == self.max_y and 0 < x < self.max_x: return "t"
if 0 < x < self.max_x and 0 < y < self.max_y: return "x"
return None
def tile_has_entry(self, x, y, entry):
if entry[1] in ("routing", "buffer"):
return self.tile_has_net(x, y, entry[2]) and self.tile_has_net(x, y, entry[3])
return True
def tile_has_net(self, x, y, netname):
if netname.startswith("logic_op_"):
if netname.startswith("logic_op_bot_"):
if y == self.max_y and 0 < x < self.max_x: return True
if netname.startswith("logic_op_bnl_"):
if x == self.max_x and 1 < y < self.max_y: return True
if y == self.max_y and 1 < x < self.max_x: return True
if netname.startswith("logic_op_bnr_"):
if x == 0 and 1 < y < self.max_y: return True
if y == self.max_y and 0 < x < self.max_x-1: return True
if netname.startswith("logic_op_top_"):
if y == 0 and 0 < x < self.max_x: return True
if netname.startswith("logic_op_tnl_"):
if x == self.max_x and 0 < y < self.max_y-1: return True
if y == 0 and 1 < x < self.max_x: return True
if netname.startswith("logic_op_tnr_"):
if x == 0 and 0 < y < self.max_y-1: return True
if y == 0 and 0 < x < self.max_x-1: return True
if netname.startswith("logic_op_lft_"):
if x == self.max_x: return True
if netname.startswith("logic_op_rgt_"):
if x == 0: return True
return False
if not 0 <= x <= self.max_x: return False
if not 0 <= y <= self.max_y: return False
return pos_has_net(self.tile_pos(x, y), netname)
def tile_follow_net(self, x, y, direction, netname):
if x == 1 and y not in (0, self.max_y) and direction == 'l': return pos_follow_net("x", "L", netname)
if y == 1 and x not in (0, self.max_x) and direction == 'b': return pos_follow_net("x", "B", netname)
if x == self.max_x-1 and y not in (0, self.max_y) and direction == 'r': return pos_follow_net("x", "R", netname)
if y == self.max_y-1 and x not in (0, self.max_x) and direction == 't': return pos_follow_net("x", "T", netname)
return pos_follow_net(self.tile_pos(x, y), direction, netname)
def follow_funcnet(self, x, y, func):
neighbours = set()
def do_direction(name, nx, ny):
if 0 < nx < self.max_x and 0 < ny < self.max_y:
neighbours.add((nx, ny, "neigh_op_%s_%d" % (name, func)))
if nx in (0, self.max_x) and 0 < ny < self.max_y and nx != x:
neighbours.add((nx, ny, "logic_op_%s_%d" % (name, func)))
if ny in (0, self.max_y) and 0 < nx < self.max_x and ny != y:
neighbours.add((nx, ny, "logic_op_%s_%d" % (name, func)))
do_direction("bot", x, y+1)
do_direction("bnl", x+1, y+1)
do_direction("bnr", x-1, y+1)
do_direction("top", x, y-1)
do_direction("tnl", x+1, y-1)
do_direction("tnr", x-1, y-1)
do_direction("lft", x+1, y )
do_direction("rgt", x-1, y )
return neighbours
def lookup_funcnet(self, nx, ny, x, y, func):
npos = self.tile_pos(nx, ny)
pos = self.tile_pos(x, y)
if npos is not None and pos is not None:
if npos == "x":
return (nx, ny, "lutff_%d/out" % func)
elif pos == "x" and npos in ("l", "r", "t", "b"):
if func in (0, 4): return (nx, ny, "io_0/D_IN_0")
if func in (1, 5): return (nx, ny, "io_0/D_IN_1")
if func in (2, 6): return (nx, ny, "io_1/D_IN_0")
if func in (3, 7): return (nx, ny, "io_1/D_IN_1")
return None
def rlookup_funcnet(self, x, y, netname):
funcnets = set()
if netname == "io_0/D_IN_0":
for net in self.follow_funcnet(x, y, 0) | self.follow_funcnet(x, y, 4):
if self.tile_pos(net[0], net[1]) == "x": funcnets.add(net)
if netname == "io_0/D_IN_1":
for net in self.follow_funcnet(x, y, 1) | self.follow_funcnet(x, y, 5):
if self.tile_pos(net[0], net[1]) == "x": funcnets.add(net)
if netname == "io_1/D_IN_0":
for net in self.follow_funcnet(x, y, 2) | self.follow_funcnet(x, y, 6):
if self.tile_pos(net[0], net[1]) == "x": funcnets.add(net)
if netname == "io_1/D_IN_1":
for net in self.follow_funcnet(x, y, 3) | self.follow_funcnet(x, y, 7):
if self.tile_pos(net[0], net[1]) == "x": funcnets.add(net)
match = re.match(r"lutff_(\d+)/out", netname)
if match:
funcnets |= self.follow_funcnet(x, y, int(match.group(1)))
return funcnets
def follow_net(self, netspec):
x, y, netname = netspec
neighbours = self.rlookup_funcnet(x, y, netname)
if netname == "carry_in" and y > 1:
neighbours.add((x, y-1, "lutff_7/cout"))
if netname == "lutff_7/cout" and y+1 < self.max_y:
neighbours.add((x, y+1, "carry_in"))
if netname.startswith("glb_netwk_"):
for nx in range(self.max_x+1):
for ny in range(self.max_y+1):
if self.tile_pos(nx, ny) is not None:
neighbours.add((nx, ny, netname))
match = re.match(r"sp4_r_v_b_(\d+)", netname)
if match and 0 < x < self.max_x-1:
neighbours.add((x+1, y, sp4v_normalize("sp4_v_b_" + match.group(1))))
match = re.match(r"sp4_v_[bt]_(\d+)", netname)
if match and 1 < x < self.max_x:
n = sp4v_normalize(netname, "b")
if n is not None:
n = n.replace("sp4_", "sp4_r_")
neighbours.add((x-1, y, n))
match = re.match(r"(logic|neigh)_op_(...)_(\d+)", netname)
if match:
if match.group(2) == "bot": nx, ny = (x, y-1)
if match.group(2) == "bnl": nx, ny = (x-1, y-1)
if match.group(2) == "bnr": nx, ny = (x+1, y-1)
if match.group(2) == "top": nx, ny = (x, y+1)
if match.group(2) == "tnl": nx, ny = (x-1, y+1)
if match.group(2) == "tnr": nx, ny = (x+1, y+1)
if match.group(2) == "lft": nx, ny = (x-1, y )
if match.group(2) == "rgt": nx, ny = (x+1, y )
n = self.lookup_funcnet(nx, ny, x, y, int(match.group(3)))
if n is not None:
neighbours.add(n)
for direction in ["l", "r", "t", "b"]:
n = self.tile_follow_net(x, y, direction, netname)
if n is not None:
if direction == "l": s = (x-1, y, n)
if direction == "r": s = (x+1, y, n)
if direction == "t": s = (x, y+1, n)
if direction == "b": s = (x, y-1, n)
if s[0] in (0, self.max_x) and s[1] in (0, self.max_y):
if re.match("span4_(vert|horz)_[lrtb]_\d+$", n):
vert_net = n.replace("_l_", "_t_").replace("_r_", "_b_").replace("_horz_", "_vert_")
horz_net = n.replace("_t_", "_l_").replace("_b_", "_r_").replace("_vert_", "_horz_")
if s[0] == 0 and s[1] == 0:
if direction == "l": s = (0, 1, vert_net)
if direction == "b": s = (1, 0, horz_net)
if s[0] == self.max_x and s[1] == self.max_y:
if direction == "r": s = (self.max_x, self.max_y-1, vert_net)
if direction == "t": s = (self.max_x-1, self.max_y, horz_net)
vert_net = netname.replace("_l_", "_t_").replace("_r_", "_b_").replace("_horz_", "_vert_")
horz_net = netname.replace("_t_", "_l_").replace("_b_", "_r_").replace("_vert_", "_horz_")
if s[0] == 0 and s[1] == self.max_y:
if direction == "l": s = (0, self.max_y-1, vert_net)
if direction == "t": s = (1, self.max_y, horz_net)
if s[0] == self.max_x and s[1] == 0:
if direction == "r": s = (self.max_x, 1, vert_net)
if direction == "b": s = (self.max_x-1, 0, horz_net)
if self.tile_has_net(s[0], s[1], s[2]):
neighbours.add((s[0], s[1], s[2]))
return neighbours
def group_segments(self, all_from_tiles=set()):
seed_segments = set()
seen_segments = set()
connected_segments = dict()
grouped_segments = set()
for idx, tile in self.io_tiles.items():
tc = tileconfig(tile)
pintypes = [ list("000000"), list("000000") ]
for entry in self.tile_db(idx[0], idx[1]):
if entry[1].startswith("IOB_") and entry[2].startswith("PINTYPE_") and tc.match(entry[0]):
pintypes[int(entry[1][-1])][int(entry[2][-1])] = "1"
if "".join(pintypes[0][2:6]) != "0000":
seed_segments.add((idx[0], idx[1], "io_0/D_OUT_0"))
if "".join(pintypes[1][2:6]) != "0000":
seed_segments.add((idx[0], idx[1], "io_1/D_OUT_0"))
def add_seed_segments(idx, tile, db):
tc = tileconfig(tile)
for entry in db:
if entry[1] in ("routing", "buffer"):
config_match = tc.match(entry[0])
if idx in all_from_tiles or config_match:
if not self.tile_has_net(idx[0], idx[1], entry[2]): continue
if not self.tile_has_net(idx[0], idx[1], entry[3]): continue
s1 = (idx[0], idx[1], entry[2])
s2 = (idx[0], idx[1], entry[3])
if config_match:
connected_segments.setdefault(s1, set()).add(s2)
connected_segments.setdefault(s2, set()).add(s1)
seed_segments.add(s1)
seed_segments.add(s2)
for idx, tile in self.io_tiles.items():
add_seed_segments(idx, tile, self.tile_db(idx[0], idx[1]))
for idx, tile in self.logic_tiles.items():
if idx in all_from_tiles:
seed_segments.add((idx[0], idx[1], "lutff_7/cout"))
add_seed_segments(idx, tile, logictile_db)
for idx, tile in self.ram_tiles.items():
add_seed_segments(idx, tile, ramtile_db)
while seed_segments:
queue = set()
segments = set()
queue.add(seed_segments.pop())
while queue:
for s in self.expand_net(queue.pop()):
if s not in segments:
segments.add(s)
assert s not in seen_segments
seen_segments.add(s)
seed_segments.discard(s)
if s in connected_segments:
for cs in connected_segments[s]:
if not cs in segments:
queue.add(cs)
for s in segments:
assert s not in seed_segments
grouped_segments.add(tuple(sorted(segments)))
return grouped_segments
def expand_net(self, netspec):
queue = set()
segments = set()
queue.add(netspec)
while queue:
n = queue.pop()
segments.add(n)
for k in self.follow_net(n):
if k not in segments:
queue.add(k)
return segments
def read_file(self, filename, logprefix=""):
self.clear()
current_data = None
expected_data_lines = 0
with open(filename, "r") as f:
for linenum, linetext in enumerate(f):
# print("DEBUG: input line %d: %s" % (linenum, linetext.strip()))
line = linetext.strip().split()
if len(line) == 0:
assert expected_data_lines == 0
continue
if line[0][0] != ".":
if line[0][0] != "0" and line[0][0] != "1":
print("%sWarning: ignoring data block in line %d: %s" % (logprefix, linenum, linetext.strip()))
expected_data_lines = 0
continue
assert expected_data_lines != 0
current_data.append(line[0])
expected_data_lines -= 1
continue
assert expected_data_lines == 0
if line[0] in (".io_tile", ".logic_tile", ".ram_tile"):
current_data = list()
expected_data_lines = 16
self.max_x = max(self.max_x, int(line[1]))
self.max_y = max(self.max_y, int(line[2]))
if line[0] == ".io_tile":
self.io_tiles[(int(line[1]), int(line[2]))] = current_data
continue
if line[0] == ".logic_tile":
self.logic_tiles[(int(line[1]), int(line[2]))] = current_data
continue
if line[0] == ".ram_tile":
self.ram_tiles[(int(line[1]), int(line[2]))] = current_data
continue
if line[0] == ".device":
assert line[1] == "1k"
continue
print("%sWarning: ignoring line %d: %s" % (logprefix, linenum, linetext.strip()))
class tileconfig:
def __init__(self, tile):
self.bits = set()
for k, line in enumerate(tile):
for i in range(len(line)):
if line[i] == "1":
self.bits.add("B%d[%d]" % (k, i))
else:
self.bits.add("!B%d[%d]" % (k, i))
def match(self, pattern):
for bit in pattern:
if not bit in self.bits:
return False
return True
if False:
## Lattice span net name normalization
valid_sp4_h_l = set([1, 2, 4, 5, 7, 9, 10, 11, 15, 16, 17, 21, 24, 34, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47])
valid_sp4_h_r = set([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 19, 21, 24, 25, 27, 30, 31, 33, 34, 35, 36, 38, 39, 40, 41, 42, 43, 44, 45, 46])
valid_sp4_v_t = set([1, 3, 5, 9, 12, 14, 16, 17, 18, 21, 22, 23, 26, 28, 29, 30, 32, 33, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47])
valid_sp4_v_b = set([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 18, 19, 21, 22, 23, 24, 26, 30, 33, 36, 37, 38, 42, 46, 47])
valid_sp12_h_l = set([3, 4, 5, 12, 14, 16, 17, 18, 21, 22, 23])
valid_sp12_h_r = set([0, 1, 2, 3, 5, 8, 9, 10, 11, 12, 13, 14, 16, 20, 23])
valid_sp12_v_t = set([0, 1, 2, 3, 6, 9, 10, 12, 14, 21, 22, 23])
valid_sp12_v_b = set([0, 1, 6, 7, 8, 11, 12, 14, 16, 18, 19, 20, 21, 23])
else:
## IceStorm span net name normalization
valid_sp4_h_l = set(range(36, 48))
valid_sp4_h_r = set(range(48))
valid_sp4_v_t = set(range(36, 48))
valid_sp4_v_b = set(range(48))
valid_sp12_h_l = set(range(22, 24))
valid_sp12_h_r = set(range(24))
valid_sp12_v_t = set(range(22, 24))
valid_sp12_v_b = set(range(24))
def sp4h_normalize(netname, edge=""):
m = re.match("sp4_h_([lr])_(\d+)$", netname)
assert m
if not m: return None
cur_edge = m.group(1)
cur_index = int(m.group(2))
if cur_edge == edge:
return netname
if cur_edge == "r" and (edge == "l" or (edge == "" and cur_index not in valid_sp4_h_r)):
if cur_index < 12:
return None
return "sp4_h_l_%d" % ((cur_index-12)^1)
if cur_edge == "l" and (edge == "r" or (edge == "" and cur_index not in valid_sp4_h_l)):
if cur_index >= 36:
return None
return "sp4_h_r_%d" % ((cur_index+12)^1)
return netname
def sp4v_normalize(netname, edge=""):
m = re.match("sp4_v_([bt])_(\d+)$", netname)
assert m
if not m: return None
cur_edge = m.group(1)
cur_index = int(m.group(2))
if cur_edge == edge:
return netname
if cur_edge == "b" and (edge == "t" or (edge == "" and cur_index not in valid_sp4_v_b)):
if cur_index < 12:
return None
return "sp4_v_t_%d" % ((cur_index-12)^1)
if cur_edge == "t" and (edge == "b" or (edge == "" and cur_index not in valid_sp4_v_t)):
if cur_index >= 36:
return None
return "sp4_v_b_%d" % ((cur_index+12)^1)
return netname
def sp12h_normalize(netname, edge=""):
m = re.match("sp12_h_([lr])_(\d+)$", netname)
assert m
if not m: return None
cur_edge = m.group(1)
cur_index = int(m.group(2))
if cur_edge == edge:
return netname
if cur_edge == "r" and (edge == "l" or (edge == "" and cur_index not in valid_sp12_h_r)):
if cur_index < 2:
return None
return "sp12_h_l_%d" % ((cur_index-2)^1)
if cur_edge == "l" and (edge == "r" or (edge == "" and cur_index not in valid_sp12_h_l)):
if cur_index >= 22:
return None
return "sp12_h_r_%d" % ((cur_index+2)^1)
return netname
def sp12v_normalize(netname, edge=""):
m = re.match("sp12_v_([bt])_(\d+)$", netname)
assert m
if not m: return None
cur_edge = m.group(1)
cur_index = int(m.group(2))
if cur_edge == edge:
return netname
if cur_edge == "b" and (edge == "t" or (edge == "" and cur_index not in valid_sp12_v_b)):
if cur_index < 2:
return None
return "sp12_v_t_%d" % ((cur_index-2)^1)
if cur_edge == "t" and (edge == "b" or (edge == "" and cur_index not in valid_sp12_v_t)):
if cur_index >= 22:
return None
return "sp12_v_b_%d" % ((cur_index+2)^1)
return netname
def netname_normalize(netname, edge=""):
if netname.startswith("sp4_v_"): return sp4v_normalize(netname, edge)
if netname.startswith("sp4_h_"): return sp4h_normalize(netname, edge)
if netname.startswith("sp12_v_"): return sp12v_normalize(netname, edge)
if netname.startswith("sp12_h_"): return sp12h_normalize(netname, edge)
if netname.startswith("input_2_"): netname = netname.replace("input_2_", "wire_logic_cluster/lc_") + "/in_2"
netname = netname.replace("lc_trk_", "local_")
netname = netname.replace("lc_", "lutff_")
netname = netname.replace("wire_logic_cluster/", "")
netname = netname.replace("wire_io_cluster/", "")
match = re.match(r"(...)_op_(.*)", netname)
if match:
netname = "neigh_op_%s_%s" % (match.group(1), match.group(2))
if re.match(r"lutff_7/(cen|clk|s_r)", netname):
netname = netname.replace("lutff_7/", "lutff_global/")
if re.match(r"io_1/(cen|inclk|outclk)", netname):
netname = netname.replace("io_1/", "io_global/")
if netname == "carry_in_mux/cout":
return "carry_in_mux"
return netname
def pos_has_net(pos, netname):
if pos in ("l", "r"):
if re.search(r"_vert_\d+$", netname): return False
if re.search(r"_horz_[rl]_\d+$", netname): return False
if pos in ("t", "b"):
if re.search(r"_horz_\d+$", netname): return False
if re.search(r"_vert_[bt]_\d+$", netname): return False
return True
def pos_follow_net(pos, direction, netname):
if pos == "x":
m = re.match("sp4_h_[lr]_(\d+)$", netname)
if m and direction in ("l", "L"):
n = sp4h_normalize(netname, "l")
if n is not None:
if direction == "l":
n = re.sub("_l_", "_r_", n)
n = sp4h_normalize(n)
else:
n = re.sub("_l_", "_", n)
n = re.sub("sp4_h_", "span4_horz_", n)
return n
if m and direction in ("r", "R"):
n = sp4h_normalize(netname, "r")
if n is not None:
if direction == "r":
n = re.sub("_r_", "_l_", n)
n = sp4h_normalize(n)
else:
n = re.sub("_r_", "_", n)
n = re.sub("sp4_h_", "span4_horz_", n)
return n
m = re.match("sp4_v_[tb]_(\d+)$", netname)
if m and direction in ("t", "T"):
n = sp4v_normalize(netname, "t")
if n is not None:
if direction == "t":
n = re.sub("_t_", "_b_", n)
n = sp4v_normalize(n)
else:
n = re.sub("_t_", "_", n)
n = re.sub("sp4_v_", "span4_vert_", n)
return n
if m and direction in ("b", "B"):
n = sp4v_normalize(netname, "b")
if n is not None:
if direction == "b":
n = re.sub("_b_", "_t_", n)
n = sp4v_normalize(n)
else:
n = re.sub("_b_", "_", n)
n = re.sub("sp4_v_", "span4_vert_", n)
return n
m = re.match("sp12_h_[lr]_(\d+)$", netname)
if m and direction in ("l", "L"):
n = sp12h_normalize(netname, "l")
if n is not None:
if direction == "l":
n = re.sub("_l_", "_r_", n)
n = sp12h_normalize(n)
else:
n = re.sub("_l_", "_", n)
n = re.sub("sp12_h_", "span12_horz_", n)
return n
if m and direction in ("r", "R"):
n = sp12h_normalize(netname, "r")
if n is not None:
if direction == "r":
n = re.sub("_r_", "_l_", n)
n = sp12h_normalize(n)
else:
n = re.sub("_r_", "_", n)
n = re.sub("sp12_h_", "span12_horz_", n)
return n
m = re.match("sp12_v_[tb]_(\d+)$", netname)
if m and direction in ("t", "T"):
n = sp12v_normalize(netname, "t")
if n is not None:
if direction == "t":
n = re.sub("_t_", "_b_", n)
n = sp12v_normalize(n)
else:
n = re.sub("_t_", "_", n)
n = re.sub("sp12_v_", "span12_vert_", n)
return n
if m and direction in ("b", "B"):
n = sp12v_normalize(netname, "b")
if n is not None:
if direction == "b":
n = re.sub("_b_", "_t_", n)
n = sp12v_normalize(n)
else:
n = re.sub("_b_", "_", n)
n = re.sub("sp12_v_", "span12_vert_", n)
return n
if pos in ("l", "r" ):
m = re.match("span4_vert_([bt])_(\d+)$", netname)
if m:
case, idx = direction + m.group(1), int(m.group(2))
if case == "tt":
return "span4_vert_b_%d" % idx
if case == "tb" and idx >= 4:
return "span4_vert_b_%d" % (idx-4)
if case == "bb" and idx < 12:
return "span4_vert_b_%d" % (idx+4)
if case == "bb" and idx >= 12:
return "span4_vert_t_%d" % idx
if pos in ("t", "b" ):
m = re.match("span4_horz_([rl])_(\d+)$", netname)
if m:
case, idx = direction + m.group(1), int(m.group(2))
if case == "ll":
return "span4_horz_r_%d" % idx
if case == "lr" and idx >= 4:
return "span4_horz_r_%d" % (idx-4)
if case == "rr" and idx < 12:
return "span4_horz_r_%d" % (idx+4)
if case == "rr" and idx >= 12:
return "span4_horz_l_%d" % idx
if pos == "l" and direction == "r":
m = re.match("span4_horz_(\d+)$", netname)
if m: return sp4h_normalize("sp4_h_l_%s" % m.group(1))
m = re.match("span12_horz_(\d+)$", netname)
if m: return sp12h_normalize("sp12_h_l_%s" % m.group(1))
if pos == "r" and direction == "l":
m = re.match("span4_horz_(\d+)$", netname)
if m: return sp4h_normalize("sp4_h_r_%s" % m.group(1))
m = re.match("span12_horz_(\d+)$", netname)
if m: return sp12h_normalize("sp12_h_r_%s" % m.group(1))
if pos == "t" and direction == "b":
m = re.match("span4_vert_(\d+)$", netname)
if m: return sp4v_normalize("sp4_v_t_%s" % m.group(1))
m = re.match("span12_vert_(\d+)$", netname)
if m: return sp12v_normalize("sp12_v_t_%s" % m.group(1))
if pos == "b" and direction == "t":
m = re.match("span4_vert_(\d+)$", netname)
if m: return sp4v_normalize("sp4_v_b_%s" % m.group(1))
m = re.match("span12_vert_(\d+)$", netname)
if m: return sp12v_normalize("sp12_v_b_%s" % m.group(1))
return None
def get_lutff_bits(tile, index):
bits = list("--------------------")
for k, line in enumerate(tile):
for i in range(36, 46):
lutff_idx = k // 2
lutff_bitnum = (i-36) + 10*(k%2)
if lutff_idx == index:
bits[lutff_bitnum] = line[i];
return bits
def get_lutff_lut_bits(tile, index):
lutff_bits = get_lutff_bits(tile, index)
return [lutff_bits[i] for i in [4, 14, 15, 5, 6, 16, 17, 7, 3, 13, 12, 2, 1, 11, 10, 0]]
def get_lutff_seq_bits(tile, index):
lutff_bits = get_lutff_bits(tile, index)
return [lutff_bits[i] for i in [8, 9, 18, 19]]
def get_carry_cascade_bit(tile):
return tile[1][49]
def get_carry_bit(tile):
return tile[1][50]
def get_negclk_bit(tile):
return tile[0][0]
def cmp_netnames(a, b):
a = re.sub(r"\d+", lambda m: "%09d" % int(m.group(0)), a)
b = re.sub(r"\d+", lambda m: "%09d" % int(m.group(0)), b)
return cmp(a, b)
def run_checks_neigh():
print("Running consistency checks on neighbour finder..")
ic = iceconfig()
ic.max_x = 6
ic.max_y = 6
all_segments = set()
def add_segments(idx, db):
for entry in db:
if entry[1] in ("routing", "buffer"):
if not ic.tile_has_net(idx[0], idx[1], entry[2]): continue
if not ic.tile_has_net(idx[0], idx[1], entry[3]): continue
all_segments.add((idx[0], idx[1], entry[2]))
all_segments.add((idx[0], idx[1], entry[3]))
for x in range(ic.max_x+1):
for y in range(ic.max_x+1):
if x in (0, ic.max_x) and y in (0, ic.max_y):
continue
if x in (0, ic.max_x) or y in (0, ic.max_y):
add_segments((x, y), ic.tile_db(x, y))
else:
add_segments((x, y), logictile_db)
all_segments.add((x, y, "lutff_7/cout"))
for s1 in all_segments:
for s2 in ic.follow_net(s1):
if s1 not in ic.follow_net(s2):
print("ERROR: %s -> %s, but not vice versa!" % (s1, s2))
print("Neighbours of %s:" % (s1,))
for s in ic.follow_net(s1):
print(" ", s)
print("Neighbours of %s:" % (s2,))
for s in ic.follow_net(s2):
print(" ", s)
print()
def run_checks():
run_checks_neigh()
def parse_db(text):
db = list()
for line in text.split("\n"):
line = line.split("\t")
if len(line) == 0 or line[0] == "":
continue
line[0] = line[0].split(",")
db.append(line)
return db
iotile_full_db = parse_db(iceboxdb.database_io_txt)
logictile_db = parse_db(iceboxdb.database_logic_txt)
ramtile_db = parse_db(iceboxdb.database_ram_txt)
pinloc_db = [[int(s) for s in line.split()] for line in iceboxdb.pinloc_txt.split("\n") if line != ""]
iotile_l_db = list()
iotile_r_db = list()
iotile_t_db = list()
iotile_b_db = list()
for entry in iotile_full_db:
if entry[1] == "buffer" and entry[2].startswith("IO_L."):
new_entry = entry[:]
new_entry[2] = new_entry[2][5:]
iotile_l_db.append(new_entry)
elif entry[1] == "buffer" and entry[2].startswith("IO_R."):
new_entry = entry[:]
new_entry[2] = new_entry[2][5:]
iotile_r_db.append(new_entry)
elif entry[1] == "buffer" and entry[2].startswith("IO_T."):
new_entry = entry[:]
new_entry[2] = new_entry[2][5:]
iotile_t_db.append(new_entry)
elif entry[1] == "buffer" and entry[2].startswith("IO_B."):
new_entry = entry[:]
new_entry[2] = new_entry[2][5:]
iotile_b_db.append(new_entry)
else:
iotile_l_db.append(entry)
iotile_r_db.append(entry)
iotile_t_db.append(entry)
iotile_b_db.append(entry)
logictile_db.append([["B1[49]"], "buffer", "carry_in", "carry_in_mux"])
logictile_db.append([["B1[50]"], "CarryInSet"])
for db in [iotile_l_db, iotile_r_db, iotile_t_db, iotile_b_db, logictile_db, ramtile_db]:
for entry in db:
if entry[1] in ("buffer", "routing"):
entry[2] = netname_normalize(entry[2])
entry[3] = netname_normalize(entry[3])
unique_entries = dict()
while db:
entry = db.pop()
key = " ".join(entry[1:]) + str(entry)
unique_entries[key] = entry
for key in sorted(unique_entries):
db.append(unique_entries[key])
if __name__ == "__main__":
run_checks()

121
icebox/icebox_chipdb.py Executable file
View File

@ -0,0 +1,121 @@
#!/usr/bin/python
#
# 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.
#
from __future__ import division
from __future__ import print_function
import icebox
import getopt, sys, re
ic = icebox.iceconfig()
ic.setup_empty_1k()
all_tiles = set()
for x in range(ic.max_x+1):
for y in range(ic.max_y+1):
if ic.tile(x, y) is not None:
all_tiles.add((x, y))
seg_to_net = dict()
net_to_segs = list()
print("""#
# IceBox Database Dump for iCE40 HX1K / LP1K
#
#
# Quick File Format Reference:
# ----------------------------
#
#
# .io_tile X Y
# .logic_tile X Y
# .ram_tile X Y
#
# declares the existence of a IO/LOGIC/RAM tile with the given coordinates
#
#
# .net NET_INDEX
# X1 Y1 name1
# X2 Y2 name2
# ...
#
# declares a net on the chip and lists its various names in different tiles
#
#
# .buffer X Y DST_NET_INDEX CONFIG_BITS_NAMES
# CONFIG_BITS_VALUES_1 SRC_NET_INDEX_1
# CONFIG_BITS_VALUES_2 SRC_NET_INDEX_2
# ...
#
# declares a buffer in the specified tile
#
#
# .routing X Y DST_NET_INDEX CONFIG_BITS_NAMES
# CONFIG_BITS_VALUES_1 SRC_NET_INDEX_1
# CONFIG_BITS_VALUES_2 SRC_NET_INDEX_2
# ...
#
# declares a routing switch in the specified tile
#
""")
for idx in sorted(ic.io_tiles):
print(".io_tile %d %d" % idx)
print()
for idx in sorted(ic.logic_tiles):
print(".logic_tile %d %d" % idx)
print()
for idx in sorted(ic.ram_tiles):
print(".ram_tile %d %d" % idx)
print()
for group in sorted(ic.group_segments(all_tiles)):
netidx = len(net_to_segs)
net_to_segs.append(group)
print(".net %d" % netidx)
for seg in group:
print("%d %d %s" % seg)
assert seg not in seg_to_net
seg_to_net[seg] = netidx
print()
for idx in sorted(all_tiles):
db = ic.tile_db(idx[0], idx[1])
db_by_bits = dict()
for entry in db:
if entry[1] in ("buffer", "routing") and ic.tile_has_net(idx[0], idx[1], entry[2]) and ic.tile_has_net(idx[0], idx[1], entry[3]):
bits = tuple([entry[1]] + sorted([bit.replace("!", "") for bit in entry[0]]))
db_by_bits.setdefault(bits, list()).append(entry)
for bits in sorted(db_by_bits):
dst_net = None
for entry in sorted(db_by_bits[bits]):
assert (idx[0], idx[1], entry[3]) in seg_to_net
if dst_net is None:
dst_net = seg_to_net[(idx[0], idx[1], entry[3])]
else:
assert dst_net == seg_to_net[(idx[0], idx[1], entry[3])]
print(".%s %d %d %d %s" % (bits[0], idx[0], idx[1], dst_net, " ".join(bits[1:])))
for entry in sorted(db_by_bits[bits]):
pattern = ""
for bit in bits[1:]:
pattern += "1" if bit in entry[0] else "0"
assert (idx[0], idx[1], entry[2]) in seg_to_net
print("%s %d" % (pattern, seg_to_net[(idx[0], idx[1], entry[2])]))
print()

57
icebox/icebox_diff.py Executable file
View File

@ -0,0 +1,57 @@
#!/usr/bin/python
#
# 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.
#
from __future__ import division
from __future__ import print_function
import icebox
import sys
print("Reading file '%s'.." % sys.argv[1])
ic1 = icebox.iceconfig()
ic1.read_file(sys.argv[1])
print("Reading file '%s'.." % sys.argv[2])
ic2 = icebox.iceconfig()
ic2.read_file(sys.argv[2])
def diff_tiles(stmt, tiles1, tiles2):
for i in sorted(set(tiles1.keys() + tiles2.keys())):
if not i in tiles1:
print("+ %s %d %d" % (stmt, i[0], i[1]))
for line in tiles2[i]:
print("+ %s" % line)
continue
if not i in tiles2:
print("- %s %d %d" % (stmt, i[0], i[1]))
for line in tiles1[i]:
print("- %s" % line)
continue
if tiles1[i] == tiles2[i]:
continue
print(" %s %d %d" % (stmt, i[0], i[1]))
for c in range(len(tiles1[i])):
if tiles1[i][c] == tiles2[i][c]:
print(" %s" % tiles1[i][c])
else:
print("- %s" % tiles1[i][c])
print("+ %s" % tiles2[i][c])
diff_tiles(".io_tile", ic1.io_tiles, ic2.io_tiles)
diff_tiles(".logic_tile", ic1.logic_tiles, ic2.logic_tiles)
diff_tiles(".ram_tile", ic1.ram_tiles, ic2.ram_tiles)

157
icebox/icebox_explain.py Executable file
View File

@ -0,0 +1,157 @@
#!/usr/bin/python
#
# 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.
#
from __future__ import division
from __future__ import print_function
import icebox
import getopt, sys, re
print_bits = False
print_map = False
single_tile = None
def usage():
print("""
Usage: icebox_explain [options] <bitmap.txt>
-b
print config bit names for each config statement
-m
print tile config bitmaps
-t '<x-coordinate> <y-coordinate>'
print only the specified tile
""")
sys.exit(0)
try:
opts, args = getopt.getopt(sys.argv[1:], "bmt:")
except:
usage()
for o, a in opts:
if o == "-b":
print_bits = True
elif o == "-m":
print_map = True
elif o == "-t":
single_tile = tuple([int(s) for s in a.split()])
else:
usage()
print("Reading file '%s'.." % args[0])
ic = icebox.iceconfig()
ic.read_file(args[0])
print("Fabric size (without IO tiles): %d x %d" % (ic.max_x-1, ic.max_y-1))
def print_tile(stmt, ic, x, y, tile, db):
if single_tile is not None and single_tile != (x, y):
return
bits = set()
mapped_bits = set()
for k, line in enumerate(tile):
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))
if re.search(r"logic_tile", stmt):
active_luts = set([i for i in range(8) if "1" in icebox.get_lutff_bits(tile, i)])
text = set()
used_lc = set()
text_default_mask = 0
for entry in db:
if re.match(r"LC_", entry[1]):
continue
if entry[1] in ("routing", "buffer"):
if not ic.tile_has_net(x, y, entry[2]): continue
if not ic.tile_has_net(x, y, entry[3]): continue
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" and entry[2] == "IE_0":
text_default_mask |= 1
if entry[1] == "IoCtrl" and entry[2] == "IE_1":
text_default_mask |= 2
if entry[1] == "RamConfig" and entry[2] == "MEMB_Power_Up_Control":
text_default_mask |= 4
if print_bits:
text.add("<%s> %s" % (" ".join(entry[0]), " ".join(entry[1:])))
else:
text.add(" ".join(entry[1:]))
bitinfo = list()
print_bitinfo = False
for k, line in enumerate(tile):
bitinfo.append("")
extra_text = ""
for i in range(len(line)):
if 36 <= i <= 45 and re.search(r"logic_tile", stmt):
lutff_idx = k // 2
lutff_bitnum = (i-36) + 10*(k%2)
if line[i] == "1":
used_lc.add(lutff_idx)
bitinfo[-1] += "*"
else:
bitinfo[-1] += "-"
elif line[i] == "1" and "B%d[%d]" % (k, i) not in mapped_bits:
print_bitinfo = True
extra_text += " B%d[%d]" % (k, i)
bitinfo[-1] += "?"
else:
bitinfo[-1] += "+" if line[i] == "1" else "-"
bitinfo[-1] += extra_text
for lcidx in sorted(used_lc):
lutff_options = "".join(icebox.get_lutff_seq_bits(tile, lcidx))
if lutff_options[0] == "1": lutff_options += " CarryEnable"
if lutff_options[1] == "1": lutff_options += " DffEnable"
if lutff_options[2] == "1": lutff_options += " Set_NoReset"
if lutff_options[3] == "1": lutff_options += " AsyncSetReset"
text.add("LC_%d %s %s" % (lcidx, "".join(icebox.get_lutff_lut_bits(tile, lcidx)), lutff_options))
if not print_bitinfo:
if text_default_mask == 3 and len(text) == 2:
return
if text_default_mask == 4 and len(text) == 1:
return
if len(text) or print_bitinfo:
print("\n%s" % stmt)
if print_bitinfo:
print("Warning: No DB entries for some bits:")
if print_bitinfo or print_map:
for k, line in enumerate(bitinfo):
print("%4s %s" % ("B%d" % k, line))
for line in sorted(text):
print(line)
for idx in ic.io_tiles:
print_tile(".io_tile %d %d" % idx, ic, idx[0], idx[1], ic.io_tiles[idx], ic.tile_db(idx[0], idx[1]))
for idx in ic.logic_tiles:
print_tile(".logic_tile %d %d" % idx, ic, idx[0], idx[1], ic.logic_tiles[idx], ic.tile_db(idx[0], idx[1]))
for idx in ic.ram_tiles:
print_tile(".ram_tile %d %d" % idx, ic, idx[0], idx[1], ic.ram_tiles[idx], ic.tile_db(idx[0], idx[1]))
print()

523
icebox/icebox_html.py Executable file
View File

@ -0,0 +1,523 @@
#!/usr/bin/python
#
# 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.
#
from __future__ import division
from __future__ import print_function
import icebox
import getopt, sys, os, re
chipname = "iCE40 HX1K"
outdir = None
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")
sys.exit(0)
try:
opts, args = getopt.getopt(sys.argv[1:], "x:y:d:")
except:
usage()
for o, a in opts:
if o == "-x":
tx = int(a)
elif o == "-y":
ty = int(a)
elif o == "-d":
outdir = a
else:
usage()
ic = icebox.iceconfig()
ic.setup_empty_1k()
mktiles = set()
for x in range(1, 6) + range(8, 13):
mktiles.add((x, 0))
mktiles.add((x, 17))
for x in range(0, 6) + range(8, 14):
mktiles.add((x, 1))
mktiles.add((x, 16))
for x in range(0, 5) + range(9, 14):
mktiles.add((x, 2))
mktiles.add((x, 15))
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 &ndash; %s Overview</title>" % chipname)
print("<h1>Project IceStorm &ndash; %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="chipdb.txt">here</a>.</p>""")
print("""<p>The iCE40 FPGA fabric is organized into tiles. The configuration bits
themself 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.) a 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):
print('<td style="width:50px; height:50px;" align="center" valign="center"', end="")
if ic.tile_pos(x, y) == None:
print('>&nbsp;</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) == "RAM": color = "#eea"
print('bgcolor="%s"><small><a style="color:#000; text-decoration:none" href="tile_%d_%d.html"><b>%s<br/>(%d %d)</b></a></small></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) == "RAM": color = "#aa8"
print('bgcolor="%s"><small>%s<br/>(%d %d)</small></td>' % (color, 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 &ndash; %s %s Tile (%d %d)</title>" % (chipname, tile_type, tx, ty))
print("<h1>Project IceStorm &ndash; %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('>&nbsp;</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) == "RAM": 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) == "RAM": 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]' % ("&nbsp;" if i < 10 else "", i, len(tile[i])-1) for i in range(len(tile))]))).replace("&nbsp;B8", "<br/>&nbsp;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"] = "I"
elif entry[1].startswith("Cascade"):
bitmap_cells[idx1][idx2]["label"] = "A"
elif entry[1].startswith("RamConfig"):
bitmap_cells[idx1][idx2]["label"] = "R"
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("wire_gbuf"): 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("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.
The entries titled "routing" configure transfer gates, the entries titled
"buffer" configure tri-state drivers.</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("&nbsp;".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, icebox.cmp_netnames)
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, icebox.cmp_netnames):
print("<tr>")
print('<td>%s</td>' % sn)
for dn in sorted(dst_nets, icebox.cmp_netnames):
if (sn, dn) in links:
print(links[(sn, dn)])
else:
print('<td align="center" bgcolor="#aaa" style="color:#666;">&nbsp;</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("&nbsp;" + 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/chipdb.txt..." % outdir, file=stdout)
os.system("python icebox_chipdb.py > %s/chipdb.txt" % outdir)
sys.stdout = stdout
elif (tx, ty) == (0, 0):
print_index()
else:
print_tile(tx, ty)

152
icebox/icebox_maps.py Executable file
View File

@ -0,0 +1,152 @@
#!/usr/bin/python
#
# 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.
#
from __future__ import division
from __future__ import print_function
import icebox
import getopt, sys, re
mode = None
def usage():
print("Usage:")
print(" icebox_maps -m bitmaps")
print(" icebox_maps -m io_tile_nets_l")
print(" icebox_maps -m io_tile_nets_r")
print(" icebox_maps -m io_tile_nets_t")
print(" icebox_maps -m io_tile_nets_b")
print(" icebox_maps -m logic_tile_nets")
print(" icebox_maps -m ram_tile_nets")
sys.exit(0)
try:
opts, args = getopt.getopt(sys.argv[1:], "m:")
except:
usage()
for o, a in opts:
if o == "-m":
mode = a.strip()
else:
usage()
def get_bit_group(x, y, db):
bit = "B%d[%d]" % (y, x)
nbit = "!B%d[%d]" % (y, x)
funcs = set()
for entry in db:
if bit in entry[0] or nbit in entry[0]:
if entry[1] in ("IOB_0", "IOB_1", "IoCtrl"):
funcs.add("i")
elif entry[1] == "routing":
funcs.add("r")
elif entry[1] == "buffer":
funcs.add("b")
elif re.match("LC_", entry[1]):
funcs.add("l")
elif entry[1] == "NegClk":
funcs.add("N")
elif entry[1] == "ColBufCtrl":
funcs.add("o")
elif entry[1] == "CarryInSet":
funcs.add("C")
elif entry[1] == "Cascade":
funcs.add("a")
else:
funcs.add("?")
if len(funcs) == 1:
return funcs.pop()
if len(funcs) > 1:
return "X"
return "-"
def print_tilemap(stmt, db, n):
print()
print(stmt)
for y in range(16):
for x in range(n):
print(get_bit_group(x, y, db), end="")
print()
def print_db_nets(stmt, db, pos):
print()
print(stmt, end="")
netnames = set()
for entry in db:
if entry[1] in ("routing", "buffer"):
if icebox.pos_has_net(pos[0], entry[2]): netnames.add(entry[2])
if icebox.pos_has_net(pos[0], entry[3]): netnames.add(entry[3])
last_prefix = ""
for net in sorted(netnames, icebox.cmp_netnames):
match = re.match(r"(.*?)(\d+)$", net)
if match:
if last_prefix == match.group(1):
print(",%s" % match.group(2), end="")
else:
print()
print(net, end="")
last_prefix = match.group(1)
else:
print()
print(net, end="")
last_prefix = "*"
print()
if mode == "bitmaps":
print_tilemap(".io_tile_bitmap_l", icebox.iotile_l_db, 18)
print_tilemap(".io_tile_bitmap_r", icebox.iotile_r_db, 18)
print_tilemap(".io_tile_bitmap_t", icebox.iotile_t_db, 18)
print_tilemap(".io_tile_bitmap_b", icebox.iotile_b_db, 18)
print_tilemap(".logic_tile_bitmap", icebox.logictile_db, 54)
print_tilemap(".ram_tile_bitmap", icebox.ramtile_db, 42)
print()
print(".bitmap_legend")
print("- ... unknown bit")
print("? ... unknown bit type")
print("X ... database conflict")
print("i ... IOB_0 IOB_1 IoCtrl")
print("a ... Carry_In_Mux Cascade")
print("r ... routing")
print("b ... buffer")
print("l ... logic bits")
print("o ... ColBufCtrl")
print("C ... CarryInSet")
print("N ... NegClk")
print()
elif mode == "io_tile_nets_l":
print_db_nets(".io_tile_nets_l", icebox.iotile_l_db, "l")
elif mode == "io_tile_nets_r":
print_db_nets(".io_tile_nets_r", icebox.iotile_r_db, "r")
elif mode == "io_tile_nets_t":
print_db_nets(".io_tile_nets_t", icebox.iotile_t_db, "t")
elif mode == "io_tile_nets_b":
print_db_nets(".io_tile_nets_b", icebox.iotile_b_db, "b")
elif mode == "logic_tile_nets":
print_db_nets(".logic_tile_nets", icebox.logictile_db, "c")
elif mode == "ram_tile_nets":
print_db_nets(".ram_tile_nets", icebox.ramtile_db, "c")
else:
usage()

367
icebox/icebox_vlog.py Executable file
View File

@ -0,0 +1,367 @@
#!/usr/bin/python
#
# 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.
#
from __future__ import division
from __future__ import print_function
import icebox
import getopt, sys, re
strip_comments = False
strip_interconn = False
lookup_pins = False
pcf_data = dict()
portnames = set()
unmatched_ports = set()
auto_clk = False
auto_en = False
def usage():
print("""
Usage: icebox_vlog [options] <bitmap.txt>
-s
strip comments from output
-S
strip comments about interconn wires from output
-a
auto-detect global clock and enable signals
(require ports "clk" and "en" in pcf file)
-l
convert io tile port names to chip pin numbers
-p <pcf-file>
use the set_io command from the specified pcf file
-P <pcf-file>
like -p, enable some hacks for pcf files created
by the iCEcube2 placer.
""")
sys.exit(0)
try:
opts, args = getopt.getopt(sys.argv[1:], "sSlap:P:")
except:
usage()
for o, a in opts:
if o == "-s":
strip_comments = True
elif o == "-S":
strip_interconn = True
elif o == "-l":
lookup_pins = True
elif o == "-a":
auto_clk = True
auto_en = True
elif o in ("-p", "-P"):
with open(a, "r") as f:
for line in f:
line = re.sub(r"#.*", "", line.strip()).split()
if len(line) and line[0] == "set_io":
p = line[1]
if o == "-P":
p = p.lower()
p = p.replace("_ibuf", "")
p = p.replace("_obuf", "")
p = p.replace("_gb_io", "")
portnames.add(p)
if not re.match(r"[a-zA-Z_][a-zA-Z0-9_]*$", p):
p = "\\%s " % p
unmatched_ports.add(p)
pinloc = tuple([int(s) for s in line[2:]])
pcf_data[pinloc] = p
else:
usage()
if not strip_comments:
print("// Reading file '%s'.." % args[0])
ic = icebox.iceconfig()
ic.read_file(args[0])
print()
text_wires = list()
text_ports = list()
luts_queue = set()
text_func = list()
netidx = [0]
nets = dict()
seg2net = dict()
auto_clk_nets = set()
auto_en_nets = set()
def is_interconn(netname):
if netname.startswith("sp4_"): return True
if netname.startswith("sp12_"): return True
if netname.startswith("span4_"): return True
if netname.startswith("span12_"): return True
if netname.startswith("logic_op_"): return True
if netname.startswith("neigh_op_"): return True
if netname.startswith("local_"): return True
return False
for segs in sorted(ic.group_segments()):
while True:
netidx[0] += 1
n = "n%d" % netidx[0]
if n not in portnames: break
net_segs = set()
renamed_net_to_port = False
for s in segs:
match = re.match("io_(\d+)/D_(IN|OUT)_(\d+)$", s[2])
if match:
if match.group(2) == "IN":
p = "io_%d_%d_%s_din_%s" % (s[0], s[1], match.group(1), match.group(3))
net_segs.add(p)
else:
p = "io_%d_%d_%s_dout_%s" % (s[0], s[1], match.group(1), match.group(3))
net_segs.add(p)
if lookup_pins or pcf_data:
for entry in icebox.pinloc_db:
if s[0] == entry[1] and s[1] == entry[2] and int(match.group(1)) == entry[3]:
if (entry[0],) in pcf_data:
p = pcf_data[(entry[0],)]
unmatched_ports.discard(p)
elif (entry[1], entry[2], entry[3]) in pcf_data:
p = pcf_data[(entry[1], entry[2], entry[3])]
unmatched_ports.discard(p)
elif lookup_pins:
p = "pin_%d" % entry[0]
if p == "clk":
auto_clk = False
if p == "en":
auto_en = False
if not renamed_net_to_port:
n = p
if match.group(2) == "IN":
text_ports.append("input %s" % p)
else:
text_ports.append("output %s" % p)
text_wires.append("wire %s;" % n)
renamed_net_to_port = True
elif match.group(2) == "IN":
text_ports.append("input %s" % p)
text_wires.append("assign %s = %s;" % (n, p))
else:
text_ports.append("output %s" % p)
text_wires.append("assign %s = %s;" % (p, n))
match = re.match("lutff_(\d+)/", s[2])
if match:
luts_queue.add((s[0], s[1], int(match.group(1))))
nets[n] = segs
for s in segs:
seg2net[s] = n
if not renamed_net_to_port:
text_wires.append("wire %s;" % n)
for s in segs:
if not strip_interconn or not is_interconn(s[2]):
if s[2].startswith("glb_netwk_"):
net_segs.add((0, 0, s[2]))
else:
net_segs.add(s)
if not renamed_net_to_port:
has_clk = False
has_cen = False
has_global = False
has_driver = False
for s in sorted(net_segs):
if s[2].startswith("glb_netwk_"):
has_global = True
elif re.search(r"/out", s[2]):
has_driver = True
elif s[2] == "lutff_global/clk":
has_clk = True
elif s[2] == "lutff_global/cen":
has_cen = True
if has_global and not has_driver:
if has_clk:
auto_clk_nets.add(n)
if has_cen and not has_clk:
auto_en_nets.add(n)
if not strip_comments:
for s in sorted(net_segs):
text_wires.append("// %s" % (s,))
text_wires.append("")
for p in unmatched_ports:
text_ports.append("input %s" % p)
if auto_clk and auto_clk_nets and "clk" in unmatched_ports:
assert len(auto_clk_nets) == 1
if not strip_comments:
text_wires.append("// automatically detected clock network")
text_wires.append("assign %s = clk;" % auto_clk_nets.pop())
if not strip_comments:
text_wires.append("")
unmatched_ports.remove("clk")
if auto_en and auto_en_nets and "en" in unmatched_ports:
assert len(auto_en_nets) == 1
if not strip_comments:
text_wires.append("// automatically detected enable network")
text_wires.append("assign %s = en;" % auto_en_nets.pop())
if not strip_comments:
text_wires.append("")
unmatched_ports.remove("en")
def seg_to_net(seg, default=None):
if seg not in seg2net:
if default is not None:
return default
while True:
netidx[0] += 1
n = "n%d" % netidx[0]
if n not in portnames: break
nets[n] = set([seg])
seg2net[seg] = n
text_wires.append("wire %s;" % n)
if not strip_comments:
if not strip_interconn or not is_interconn(seg[2]):
text_wires.append("// %s" % (seg,))
text_wires.append("")
return seg2net[seg]
wire_to_reg = set()
lut_assigns = list()
const_assigns = list()
carry_assigns = list()
always_stmts = list()
max_net_len = 0
for lut in luts_queue:
seq_bits = icebox.get_lutff_seq_bits(ic.logic_tiles[(lut[0], lut[1])], lut[2])
if seq_bits[0] == "1":
seg_to_net((lut[0], lut[1], "lutff_%d/cout" % lut[2]))
for lut in luts_queue:
tile = ic.logic_tiles[(lut[0], lut[1])]
lut_bits = icebox.get_lutff_lut_bits(tile, lut[2])
seq_bits = icebox.get_lutff_seq_bits(tile, lut[2])
net_in0 = seg_to_net((lut[0], lut[1], "lutff_%d/in_0" % lut[2]), "0")
net_in1 = seg_to_net((lut[0], lut[1], "lutff_%d/in_1" % lut[2]), "0")
net_in2 = seg_to_net((lut[0], lut[1], "lutff_%d/in_2" % lut[2]), "0")
net_in3 = seg_to_net((lut[0], lut[1], "lutff_%d/in_3" % lut[2]), "0")
net_out = seg_to_net((lut[0], lut[1], "lutff_%d/out" % lut[2]))
if seq_bits[0] == "1":
net_cout = seg_to_net((lut[0], lut[1], "lutff_%d/cout" % lut[2]))
net_in1 = seg_to_net((lut[0], lut[1], "lutff_%d/in_1" % lut[2]), "0")
net_in2 = seg_to_net((lut[0], lut[1], "lutff_%d/in_2" % lut[2]), "0")
if lut[2] == 0:
net_cin = seg_to_net((lut[0], lut[1], "carry_in_mux"))
if icebox.get_carry_cascade_bit(tile) == "0":
if not strip_comments:
text_wires.append("// Carry-In for (%d %d)" % (lut[0], lut[1]))
text_wires.append("assign %s = %s;" % (net_cin, icebox.get_carry_bit(tile)))
if not strip_comments:
text_wires.append("")
else:
net_cin = seg_to_net((lut[0], lut[1], "lutff_%d/cout" % (lut[2]-1)), "0")
carry_assigns.append([net_cout, "/* CARRY %2d %2d %2d */ (%s & %s) | ((%s | %s) & %s)" %
(lut[0], lut[1], lut[2], net_in1, net_in2, net_in1, net_in2, net_cin)])
if seq_bits[1] == "1":
while True:
netidx[0] += 1
n = "n%d" % netidx[0]
if n not in portnames: break
text_wires.append("wire %s;" % n)
if not strip_comments:
text_wires.append("// FF %s" % (lut,))
text_wires.append("")
net_cen = seg_to_net((lut[0], lut[1], "lutff_global/cen"), "1")
net_clk = seg_to_net((lut[0], lut[1], "lutff_global/clk"), "0")
net_sr = seg_to_net((lut[0], lut[1], "lutff_global/s_r"), "0")
if seq_bits[3] == "0":
always_stmts.append("/* FF %2d %2d %2d */ always @(%sedge %s) if (%s) %s <= %s ? %s : %s;" %
(lut[0], lut[1], lut[2], "neg" if icebox.get_negclk_bit(tile) == "1" else "pos",
net_clk, net_cen, net_out, net_sr, seq_bits[2], n))
else:
always_stmts.append("/* FF %2d %2d %2d */ always @(%sedge %s, posedge %s) if (%s) %s <= %s; else if (%s) %s <= %s;" %
(lut[0], lut[1], lut[2], "neg" if icebox.get_negclk_bit(tile) == "1" else "pos",
net_clk, net_sr, net_sr, net_out, seq_bits[2], net_cen, net_out, n))
wire_to_reg.add(net_out)
net_out = n
if not "1" in lut_bits:
const_assigns.append([net_out, "1'b0"])
elif not "0" in lut_bits:
const_assigns.append([net_out, "1'b1"])
else:
def make_lut_expr(bits, sigs):
if not sigs:
return "%s" % bits[0]
l_expr = make_lut_expr(bits[0:len(bits)//2], sigs[1:])
h_expr = make_lut_expr(bits[len(bits)//2:len(bits)], sigs[1:])
if h_expr == l_expr: return h_expr
if sigs[0] == "0": return l_expr
if sigs[0] == "1": return h_expr
if h_expr == "1" and l_expr == "0": return sigs[0]
if h_expr == "0" and l_expr == "1": return "!" + sigs[0]
return "%s ? %s : %s" % (sigs[0], h_expr, l_expr)
lut_expr = make_lut_expr(lut_bits, [net_in3, net_in2, net_in1, net_in0])
lut_assigns.append([net_out, "/* LUT %2d %2d %2d */ %s" % (lut[0], lut[1], lut[2], lut_expr)])
max_net_len = max(max_net_len, len(net_out))
for a in const_assigns + lut_assigns + carry_assigns:
text_func.append("assign %-*s = %s;" % (max_net_len, a[0], a[1]))
print("module chip (%s);\n" % ", ".join(text_ports))
new_text_wires = list()
for line in text_wires:
match = re.match(r"wire ([^ ;]+)(.*)", line)
if match and match.group(1) in wire_to_reg:
line = "reg " + match.group(1) + match.group(2)
if strip_comments:
if new_text_wires and new_text_wires[-1].split()[0] == line.split()[0] and new_text_wires[-1][-1] == ";":
new_text_wires[-1] = new_text_wires[-1][0:-1] + "," + line[len(line.split()[0]):]
else:
new_text_wires.append(line)
else:
print(line)
for line in new_text_wires:
print(line)
if strip_comments:
print()
for line in text_func:
print(line)
for line in always_stmts:
print(line)
print()
for p in unmatched_ports:
print("// Warning: unmatched port '%s'" %p)
if unmatched_ports:
print()
print("endmodule")
print()

2985
icebox/iceboxdb.py Normal file

File diff suppressed because it is too large Load Diff

25
icepack/Makefile Normal file
View File

@ -0,0 +1,25 @@
CC = clang
CXX = clang
LDFLAGS = -lm -lstdc++
CFLAGS = -MD -Os -Wall -std=c99
CXXFLAGS = -MD -Os -Wall -std=c99
all: iceunpack
iceunpack: iceunpack.o
install: all
cp iceunpack /usr/local/bin/iceunpack
uninstall:
rm -f /usr/local/bin/iceunpack
clean:
rm -f iceunpack
rm -f *.o *.d
-include *.d
.PHONY: install uninstall clean

368
icepack/iceunpack.c Normal file
View File

@ -0,0 +1,368 @@
// Written by Mathias Lasser
// License terms are unclear at the moment
#include<stdio.h>
#include<stdbool.h>
#include<stdlib.h>
#include<stdint.h>
#include<string.h>
int enable_debug=0;
#define debugf(...) do{if(enable_debug)fprintf(stderr,__VA_ARGS__);}while(0)
#pragma pack(2)
typedef struct
{
uint16_t bfType;
uint32_t bfSize;
uint16_t bfReserved1;
uint16_t bfReserved2;
uint32_t bfOffBits;
}BITMAPFILEHEADER;
typedef struct
{
uint32_t biSize;
uint32_t biWidth;
uint32_t biHeight;
uint16_t biPlanes;
uint16_t biBitCount;
uint32_t biCompression;
uint32_t biSizeImage;
uint32_t biXPelsPerMeter;
uint32_t biYPelsPerMeter;
uint32_t biClrUsed;
uint32_t biClrImportant;
}BITMAPINFOHEADER;
typedef struct{
uint16_t boot_mode;
uint16_t crc;
uint8_t freq;
uint8_t bank;
uint32_t write_pointer;
uint32_t CRAM_rowsize;
uint32_t CRAM_colsize;
uint8_t* CRAM[4];
uint32_t BRAM_rowsize;
uint32_t BRAM_colsize;
uint8_t* BRAM[4];
}FPGA_t;
typedef struct{
uint32_t len;
uint32_t pointer;
uint16_t crc;
uint8_t payload[0];
}bitstream_t;
typedef union{
struct{
uint8_t lo:4;
uint8_t hi:4;
};
uint8_t full;
}nibble_t;
const char* freq_settings[]={"low","medium","high"};
const char* boot_settings[]={"Disable","Enable"};
uint16_t crc16(uint32_t crc,uint8_t in){
for(int i=7;i>=0;i--){
crc<<=1;
if((crc^(in<<(16-i)))&0x10000)
crc^=0x1021;
}
return crc;
}
uint32_t get_byte(bitstream_t* bitstream){
if(bitstream->pointer>=bitstream->len)return 0xFFFFFF;
uint8_t data=bitstream->payload[bitstream->pointer++];
bitstream->crc=crc16(bitstream->crc,data);
return data;
}
uint32_t get_payload(bitstream_t* bitstream,int len){
uint32_t ret=get_byte(bitstream);
for(int i=1;i<len&&ret!=0xFFFFFFFF;i++){
ret<<=8;
ret|=get_byte(bitstream);
}
return ret;
}
void FPGA_write(FPGA_t* FPGA,bitstream_t* bitstream){
int n=FPGA->CRAM_colsize*FPGA->CRAM_rowsize/8;
uint8_t* temp=(uint8_t*)malloc(n);
for(int i=0;i<n;i++)
temp[i]=get_byte(bitstream);
FPGA->CRAM[FPGA->bank]=temp;
}
void FPGA_EBR_write(FPGA_t* FPGA,bitstream_t* bitstream){
int n=FPGA->BRAM_colsize*FPGA->BRAM_rowsize/8;
uint8_t* temp=(uint8_t*)malloc(FPGA->write_pointer+n);
if(FPGA->write_pointer!=0){
uint8_t* old_data=FPGA->BRAM[FPGA->bank];
memcpy(temp,old_data,FPGA->write_pointer);
free(old_data);
}
for(int i=0;i<n;i++)temp[FPGA->write_pointer++]=get_byte(bitstream);
FPGA->BRAM[FPGA->bank]=temp;
}
int parse(bitstream_t* bitstream, FPGA_t* FPGA) {
uint32_t preamble=0;
while(1){
preamble<<=8;
preamble|=get_byte(bitstream);
if(preamble==0x7EAA997E){
debugf("Got preamble\n");
break;
}
if(preamble==0xFFFFFFFF){
fprintf(stderr,"Error: could not find preamble...\n");
return -1;
}
}
nibble_t command;
uint32_t payload;
uint16_t crc;
while(1){
crc=bitstream->crc;
command.full=get_byte(bitstream);
if(command.full==0xFF){
payload=get_byte(bitstream);
if(payload!=0x00)goto err;
char*comment=(char*)&bitstream->payload[bitstream->pointer];
debugf("Got comment section start\n");
while(1){
payload<<=8;
payload|=get_byte(bitstream);
if((payload&0xFFFF)==0x00FF)break;
if(payload==0xFFFFFFFF){
fprintf(stderr,"Error: could not find comment section end\n");
return -1;
}
}
debugf("\n%s\n\n",comment);
debugf("Got comment section end\n");
continue;
}
payload=get_payload(bitstream,command.lo);
switch(command.hi){
case 0x0:
if(command.lo!=0x01)goto err;
switch(payload){
case 0x01:
debugf("Write to CRAM!!!\n");
FPGA_write(FPGA,bitstream);
get_payload(bitstream,2);
break;
case 0x03:
debugf("Write to BRAM!!!\n");
FPGA_EBR_write(FPGA,bitstream);
get_payload(bitstream,2);
break;
case 0x05:
debugf("Resetting CRC\n");
bitstream->crc=0;
break;
case 0x06:
debugf("Wake up\n");
return 0;
default:
goto err;
}
break;
case 0x1:
if(command.lo!=0x01)goto err;
if(payload>3){
fprintf(stderr,"Error: bank %u does not exist...\n",payload);
}
debugf("Set bank to %u\n",payload);
FPGA->bank=payload;
break;
case 0x2:
if(command.lo!=0x02)goto err;
debugf("CRC check: %04X %04X\n",payload,crc);
break;
case 0x5:
if(command.lo!=0x01)goto err;
if(payload>2){
fprintf(stderr,"Error: unknown frequency setting...\n");
return -1;
}
debugf("Boot frequency set to %s\n",freq_settings[payload]);
FPGA->freq=payload;
break;
case 0x6:
if(command.lo!=0x02)goto err;
payload++;
debugf("Row size: %i\n",payload);
if(FPGA->CRAM_rowsize)FPGA->BRAM_rowsize=payload;
else FPGA->CRAM_rowsize=payload;
break;
case 0x7:
if(command.lo!=0x02)goto err;
debugf("Column size: %i\n",payload);
if(FPGA->CRAM_colsize)FPGA->BRAM_colsize=payload;
else FPGA->CRAM_colsize=payload;
break;
case 0x8:
if(command.lo!=0x02)goto err;
if(payload==0x0000){
debugf("Reset write pointer\n");
FPGA->write_pointer=0;
}
else if(payload==0x0080){
debugf("Don't reset write pointer\n");
}
else goto err;
break;
case 0x9:
if(command.lo!=0x02)goto err;
if(payload&0xFFDF){
fprintf(stderr,"Error: Unknown warmboot setting... %04X\n",payload);
return -1;
}
debugf("%s warmboot\n",boot_settings[payload?1:0]);
FPGA->boot_mode=payload;
break;
default:
goto err;
}
}
err:
fprintf(stderr,"Error: unkown command... %08X\n",bitstream->pointer);
return -1;
}
uint8_t get_CRAM_bit_from_sector(FPGA_t* FPGA,int bank,int x,int y){
if(x<0||x>=FPGA->CRAM_rowsize)return 0xFF;
if(y<0||y>=FPGA->CRAM_colsize)return 0xFF;
if(bank<0||bank>=4)return 0xFF;
int bit=y*FPGA->CRAM_rowsize+x;
int pointer=bit>>3;
int shifts=7-(bit&7);
return (FPGA->CRAM[bank][pointer]>>shifts)&1;
}
int tiles[2][20]={
{18,54,54,42,54,54,54},
{18,2,54,54,54,54,54,54,54,42,54,54,54,54,54,54,54,54}
};
int permx[2][18]={
{23,25,26,27,16,17,18,19,20,14,32,33,34,35,36,37,4,5},
{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17}};
int permy[4][16]={
{0,1,3,2,4,5,7,6,8,9,11,10,12,13,15,14},
{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15},
};
int tile_row_size[2]={7,17};
int tile_col_size[2]={9,17};
void print_tile(FPGA_t* FPGA,int x,int y){
int type=FPGA->CRAM_rowsize==872;
int dirx=0;
int diry=0;
int tx=x;
int ty=y;
int corner_flags=0;
if(x==0)corner_flags|=1;
if(y==0)corner_flags|=2;
if(x==tile_row_size[type]*2-1)corner_flags|=4;
if(y==tile_col_size[type]*2-1)corner_flags|=8;
if(corner_flags&(corner_flags-1))
return;
if(x>=tile_row_size[type]){
dirx=1;
tx=tile_row_size[type]*2-1-x;
}
if(y>=tile_col_size[type]){
diry=1;
ty=tile_col_size[type]*2-1-y;
}
int sector=(diry|dirx<<1);
int offx=0;for(int i=0;i<tx;i++)offx+=tiles[type][i];
if(corner_flags){
printf(".io_tile %i %i\n",x,y);
for(int cy=0;cy<16;cy++){
for(int cx=0;cx<18;cx++){
int val;
if(corner_flags&5){
if(diry){
val=get_CRAM_bit_from_sector(FPGA,sector,offx+tiles[type][tx]-1-permx[1][cx],ty*16+15-permy[1][cy]);
}else{
val=get_CRAM_bit_from_sector(FPGA,sector,offx+tiles[type][tx]-1-permx[1][cx],ty*16+permy[1][cy]);
}
}else{
if(dirx){
val=get_CRAM_bit_from_sector(FPGA,sector,offx+tiles[type][tx]-1-permx[0][cx],ty*16+15-permy[0][cy]);
}else{
val=get_CRAM_bit_from_sector(FPGA,sector,offx+permx[0][cx],ty*16+15-permy[0][cy]);
}
}
printf("%i",val);
}
printf("\n");
}
}
else{
if(tiles[type][tx]==20)printf(".io_tile %i %i\n",x,y);
if(tiles[type][tx]==42)printf(".ram_tile %i %i\n",x,y);
if(tiles[type][tx]==54)printf(".logic_tile %i %i\n",x,y);
for(int cy=0;cy<16;cy++){
for(int cx=0;cx<tiles[type][tx];cx++){
printf("%i",get_CRAM_bit_from_sector(FPGA,sector,(dirx?(offx+tiles[type][tx]-1-cx):(offx+cx)),(diry?(ty*16+(15-cy)):(ty*16+cy))));
}
printf("\n");
}
}
}
int main(int argc,char**argv) {
if(argc>=2&&!strcmp(argv[1], "-v")) {
enable_debug=1;
argc--;
argv++;
}
if(argc!=2) {
fprintf(stderr,"iceunpack [-v] input\n");
return 1;
}
FILE*r_file=fopen(argv[1],"rb");
if(r_file==NULL) {
fprintf(stderr,"could not open %s\n",argv[1]);
return 1;
}
fseek(r_file,0,SEEK_END);
size_t r_size=ftell(r_file);
fseek(r_file,0,SEEK_SET);
bitstream_t* bitstream=(bitstream_t*)malloc(sizeof(bitstream_t)+r_size);
bitstream->len=r_size;
bitstream->pointer=0;
fread(bitstream->payload,1,r_size,r_file);
fclose(r_file);
FPGA_t FPGA;
memset(&FPGA,0,sizeof(FPGA));
parse(bitstream,&FPGA);
free(bitstream);
printf(".device 1k\n");
for(int y=0;y<18;y++)for(int x=0;x<14;x++)print_tile(&FPGA,x,y);
return 0;
}