blob: f7a72bbe1ebf9f0c2984534ca21308589cd39d59 [file] [log] [blame]
import pytrellis
import database
import itertools
import json
import argparse
# This mirrors center_map in libtrellis. Somehow expose center_map.
center_map = {
# 256HC
(7, 9): (3, 4),
# 640HC
(8, 17): (3, 7),
# 1200HC
(12, 21): (6, 12),
# 2000HC
(15, 25): (8, 13),
# 4000HC
(22, 31): (11, 15),
# 7000HC
(26, 40): (13, 18),
}
row_spans = {
# 1200HC
(12, 21): (5, 5),
}
start_stride = {
# 256HC
(7, 9): (0, 4),
# 640HC
(8, 17): (1, 5),
# 1200HC
(12, 21): (0, 4),
# 2000HC
(15, 25): (3, 7),
# 4000HC
(22, 31): (1, 5),
# 7000HC
(26, 40): (2, 6),
}
# There are 8 global nets. For a given column, globals are routed in pairs.
# Convert from a pair to an index of global pairs.
global_group = {
(0, 4): 0,
(1, 5): 1,
(2, 6): 2,
(3, 7): 3
}
inv_global_group = [(0, 4), (1, 5), (2, 6), (3, 7)]
# Generate which columns route which globals.
def column_routing(num_cols, col_1=(0, 4)):
i = 1
stride = (0, 4, 1, 5, 2, 6, 3, 7)
# Find which globals in column 0 will be routed, given which globals
# are routed in column 1.
#
# Column "0" in prjtrellis ("1" in Lattice numbering) always has six of
# the globals routed. The explanation for the final column applies here,
# except we are missing the four globals that would span from the right
# side of the U/D routing connection (and thus approach column 0 from the
# left).
col_0 = []
for g in stride:
if g not in col_1:
col_0.append(g)
yield tuple(col_0)
# Rotate the stride so the correct pair of globals are at the beginning
# of the list.
idx = global_group[col_1]*2
rotated_stride = stride[idx:] + stride[:idx]
# Take two at a time: https://docs.python.org/3/library/functions.html#zip
col_iter = itertools.cycle(zip(*[iter(rotated_stride)]*2))
for c in col_iter:
yield c
i = i + 1
if i >= num_cols:
break
# The final column will have 4 globals routed- the two expected globals
# for the column as well as the next two globals in the stride. This is
# because BRANCH wires that connect globals to CIBs span two columns to the
# right and one column to the left from where they connect to U/D routing.
# Since we are at the right bound of the chip, the globals we would expect
# to span from the left side of the U/D routing (and thus approach the
# final column from the right) don't physically exist! So we take care
# of them here.
yield next(col_iter) + next(col_iter)
# Generate how far branches span (exclusive span, in tiles) from u/d column
# routing.
def branch_spans(num_cols, col_1=(0, 4)):
i = 1
col_0 = [None, (0, 0), (0, 1), (0, 2)]
default = {
(0, 4): [(1, 2), None, None, None],
(1, 5): [None, (1, 2), None, None],
(2, 6): [None, None, (1, 2), None],
(3, 7): [None, None, None, (1, 2)]
}
idx = global_group[col_1]
# This works, somehow...
rotated_col0 = col_0[-idx:] + col_0[:-idx]
yield rotated_col0
col_iter = itertools.islice(column_routing(num_cols, col_1), 1, None)
for c in col_iter:
yield default[c]
i = i + 1
if i > (num_cols - 2):
break
# At the second-to-last row of the chip, the branch, which spans two
# columns to the right, will be truncated by the chip's edge.
second_last = [None, None, None, None]
curr_idx = global_group[next(col_iter)]
second_last[curr_idx] = (1, 1)
yield second_last
# At the last row of the chip, BRANCHES connecting to U/D routing (which
# which normally span two column to the right) will be truncated by the
# chip's edge.
last = [None, None, None, None]
curr_idx = (curr_idx + 1) % 4
last[curr_idx] = (1, 0)
# The remaining two globals should come from BRANCHES from the right.
# But since we run into the chip's edge, we route them to the current
# column (and only the current column!) here.
curr_idx = (curr_idx + 1) % 4
last[curr_idx] = (0, 0)
yield last
# 256: 9, (0, 4): L: 3, 7 has DCCs R: 0, 4 has DCCs
# 640: 17, (1, 5): L: 0, 4 has DCCs R: 1, 5 has DCCs
# 1200: 21, (0, 4): L: 3, 7 has DCCs R: 0, 4 has DCCs
# 2000: 25, (3, 7), L: 2, 6 has DCCs R: 3, 7 has DCCs
# 4000: 31, (1, 5), L: 0, 4 has DCCs R: 3, 7 has DCCs
# 7000: 40, (2, 6), L: 1, 5 has DCCs R: 1, 5 has DCCs (both top and bottom)
def main(args):
pytrellis.load_database(database.get_db_root())
ci = pytrellis.get_chip_info(pytrellis.find_device_by_name(args.device))
chip_size = (ci.max_row, ci.max_col)
globals_json = dict()
globals_json["lr-conns"] = {
"lr1" : {
"row" : center_map[chip_size][0],
"row-span" : row_spans[chip_size]
}
}
globals_json["ud-conns"] = {}
for n, c in enumerate(column_routing(chip_size[1], start_stride[chip_size])):
globals_json["ud-conns"][str(n)] = c
if n == chip_size[1] - 1:
last_stride = c
globals_json["branch-spans"] = {}
for col, grps in enumerate(branch_spans(chip_size[1], start_stride[chip_size])):
span_dict = {}
for gn, span in enumerate(grps):
if span:
for glb_no in inv_global_group[gn]:
span_dict[str(glb_no)] = span
globals_json["branch-spans"][str(col)] = span_dict
# For the first and last columns, globals at the stride's current
# position have DCCs when viewed in EPIC. These DCCs don't appear to
# physically exist on-chip. See minitests/machxo2/dcc/dcc2.v. However,
# in the bitstream (for the first and last columns) global conns going
# into "DCCs" have different bits controlling them as opposed to globals
# without DCC connections.
zero_col_dccs = set(inv_global_group[(global_group[start_stride[chip_size]] - 1) % 4])
zero_col_conns = set(globals_json["ud-conns"]["0"])
missing_dccs_l = tuple(zero_col_conns.difference(zero_col_dccs))
last_col_dccs = set(inv_global_group[(global_group[last_stride] + 1) % 4])
last_col_conns = set(globals_json["ud-conns"][str(chip_size[1])])
missing_dccs_r = tuple(last_col_conns.difference(last_col_dccs))
globals_json["missing-dccs"] = {
"0" : missing_dccs_l,
str(chip_size[1]) : missing_dccs_r
}
with args.outfile as jsonf:
jsonf.write(json.dumps(globals_json, indent=4, separators=(',', ': ')))
if __name__ == "__main__":
parser = argparse.ArgumentParser("Store MachXO2 global information into globals.json.")
parser.add_argument('device', type=str,
help="Device for which to generate globals.json.")
parser.add_argument('outfile', type=argparse.FileType('w'),
help="Output json file (globals.json in the database).")
args = parser.parse_args()
main(args)