forked from YosysHQ/prjtrellis
-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathgen_globals.py
216 lines (179 loc) · 6.8 KB
/
gen_globals.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
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)