generated from efabless/caravel_user_project
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathmanipulate_macro_cfg.py
executable file
·331 lines (278 loc) · 11.2 KB
/
manipulate_macro_cfg.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
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
#!/usr/bin/env python3
import math
import re
#TODO: Improve this script so e.g. arguments can be given from the command line
# ignore_strings = ["BlockRAM", "cvxif_pau", "data_mem", "LUT4AB", "W_IO"]
# ignore_strings = ["BlockRAM", "cvxif_pau", "data_mem"]
ignore_strings = []
def rotate(origin, point, angle):
"""
Rotate a point counterclockwise by a given angle around a given origin.
The angle should be given in radians.
"""
ox, oy = origin
px, py = point
qx = ox + math.cos(angle) * (px - ox) - math.sin(angle) * (py - oy)
qy = oy + math.sin(angle) * (px - ox) + math.cos(angle) * (py - oy)
return qx, qy
def clip_to_next_multiple(offset, pitch):
"""
Clips the given offset to the next multiple of pitch.
Parameters:
offset (int): The offset value to be clipped.
pitch (int): The pitch value (step size).
Returns:
int: The smallest multiple of pitch greater than or equal to the offset.
"""
# if offset < 0 or pitch <= 0:
if pitch <= 0:
raise ValueError("Offset must be non-negative and pitch must be positive.")
return ((offset + pitch - 1) // pitch) * pitch
def parse_coordinates(s):
"""
Extracts the numeric coordinates following 'X' and 'Y' from a given string.
Parameters:
s (str): The input string containing the coordinates. The string must
contain 'X' and 'Y' exactly once, each followed by digits.
Returns:
tuple: A tuple (x, y), where:
- x (int): The number following 'X'.
- y (int): The number following 'Y'.
Raises:
ValueError: If the required format (X<number>Y<number>) is not found in the string.
"""
match = re.search(r'X(\d+)Y(\d+)', s)
if match:
return int(match.group(1)), int(match.group(2))
raise ValueError("No coordinates found in the string")
def read_tiles(file_path):
"""
Reads the input file and extracts tile information into a list of dictionaries.
Parameters:
file_path (str): Path to the input file.
Returns:
list: A list of dictionaries with tile data (e.g., [{'name': value, 'x': value, ...}]).
"""
tiles = []
with open(file_path, 'r') as f:
lines = f.readlines()
for line in lines:
if line.strip() and not line.startswith(" "):
parts = line.strip().split()
name = parts[0]
x, y = map(float, parts[1:3]) # Assuming x, y coordinates are the 2nd and 3rd columns
flip = parts[3]
tiles.append({'name': name, 'x': x, 'y': y, "flip": flip})
return tiles
def print_tiles(tiles):
"""
Prints the tile data in a readable format.
Parameters:
tiles (list): A list of dictionaries with tile data.
"""
for tile in tiles:
print(f"TILE name: {tile['name']} x: {tile['x']}, y: {tile['y']}, flip: {tile['flip']}")
def rotate_tiles(tiles, origin, angle_degrees):
"""
Rotates all tiles around a given origin by a specified angle.
Parameters:
tiles (list): A list of dictionaries with tile data.
origin (tuple): The (x, y) coordinates of the rotation origin.
angle_degrees (float): The rotation angle in degrees.
Returns:
list: A list of rotated tile data.
"""
# Convert the angle from degrees to radians
angle = math.radians(angle_degrees)
for tile in tiles:
if any(ignore_string in tile["name"] for ignore_string in ignore_strings):
continue
flip = "N"
x, y = rotate(origin, (tile["x"], tile["y"]), angle)
x += 2000
tile.update({"x": x, "y": y, "flip": flip})
return tiles
def move_tiles(tiles, x_offset, y_offset):
for tile in tiles:
if any(ignore_string in tile["name"] for ignore_string in ignore_strings):
continue
x = tile["x"] + x_offset
y = tile["y"] + y_offset
tile.update({"x": x, "y": y})
return tiles
def change_space_between_tiles_vertical(tiles, space, pdn_pitch, start_bot=True):
"""
Adjusts the vertical space between tiles in each row.
Parameters:
tiles (list): A list of dictionaries containing tile data.
space (float): The amount of space to add between tiles vertically.
pdn_pitch (float): The vertical pitch of the PDN.
start_top (bool): Whether to start adjusting from the topmost row (True)
or the bottommost row (False).
Returns:
list: The modified list of tiles with updated y-coordinates.
"""
print(f"""\033[93mWARNING: Changed vertical space between tiles. Make sure to
adjust the height of all supertiles by {space:.1f}!\033[0m""")
# Group tiles by their y-coordinate (rows)
rows = {}
rams = {}
for tile in tiles:
if any(ignore_string in tile["name"] for ignore_string in ignore_strings):
continue
if "BlockRAM" in tile["name"]:
ram = tile['y']
if ram not in rams:
rams[ram] = []
rams[ram].append(tile)
else:
y = tile['y']
if y not in rows:
rows[y] = []
rows[y].append(tile)
sorted_rams = sorted(rams.keys(), reverse=not start_bot)
y_offset_ram = 0
# Handle RAM separately
for ram in sorted_rams:
ram_cell = rams[ram][0]
if start_bot:
ram_cell['y'] += y_offset_ram
else:
ram_cell['y'] -= y_offset_ram
# The ram spans over two tiles, so it has to be shifted by two spaces
#TODO: this is not entirely correct and should be read from the tiles
#somehow instead
y_offset_ram += 2*space
sorted_rows = sorted(rows.keys(), reverse=not start_bot)
# Update the horizontal spacing for each column
y_offset = 0
for row_y in sorted_rows:
for tile in sorted(rows[row_y], key=lambda t: t['x']):
if any(ignore_string in tile["name"] for ignore_string in ignore_strings):
continue
elif "N_term" in tile["name"] and start_bot:
n_term_offset = clip_to_next_multiple(y_offset, pdn_pitch)
tile['y'] += n_term_offset
elif "S_term" in tile["name"] and not start_bot:
s_term_offset = clip_to_next_multiple(y_offset, pdn_pitch)
tile['y'] -= s_term_offset
else:
if start_bot:
tile['y'] += y_offset
else:
tile['y'] -= y_offset
y_offset += space # Increment the x_offset by the space value
return tiles
def change_space_between_tiles_horizontal(tiles, space, start_left=True, start=None, stop=None):
"""
Adjusts the horizontal space between tiles in each column.
Parameters:
tiles (list): A list of dictionaries containing tile data.
space (float): The amount of space to add between tiles horizontally.
start_top (bool): Whether to start adjusting from the topleftmost column (True)
or the bottommost column (False).
Returns:
list: The modified list of tiles with updated x-coordinates.
"""
# Group tiles by their x-coordinate (columns)
column = {}
for tile in tiles:
if any(ignore_string in tile["name"] for ignore_string in ignore_strings):
continue
x = tile['x']
if x not in column:
column[x] = []
column[x].append(tile)
sorted_rows = sorted(column.keys(), reverse=not start_left)
# Update the horizontal spacing for each column
x_offset = 0
reached_end = False
reached_start = False
for column_x in sorted_rows:
for tile in sorted(column[column_x], key=lambda t: t['y']):
if any(ignore_string in tile["name"] for ignore_string in ignore_strings):
continue
if stop != None and start != None:
# BlockRAM has no XY fabric coordinate
if "BlockRAM" in tile["name"]:
continue
x_coord, _ = parse_coordinates(tile["name"])
if start_left:
if x_coord < start:
continue
reached_start = True
if x_coord == stop:
reached_end = True
else:
if x_coord > start:
continue
reached_start = True
if x_coord == stop:
reached_end = True
if start_left:
tile['x'] += x_offset
else:
tile['x'] -= x_offset
if reached_start and not reached_end:
x_offset += space # Increment the x_offset by the space value
return tiles
def write_tiles_to_file_flip(tiles, file_path):
"""
Writes the tile data to a specified file.
Parameters:
tiles (list): A list of dictionaries with tile data.
file_path (str): Path to the output file.
"""
with open(file_path, "w") as f:
for tile in tiles:
if tile['flip'] == "N":
line = f"{tile['name']} {tile['x']} {tile['y']} W\n"
else:
line = f"{tile['name']} {tile['x']} {tile['y']} {tile['flip']}\n"
f.write(line)
def write_tiles_to_file(tiles, file_path):
"""
Writes the tile data to a specified file.
Parameters:
tiles (list): A list of dictionaries with tile data.
file_path (str): Path to the output file.
"""
with open(file_path, "w") as f:
for tile in tiles:
line = f"{tile['name']} {tile['x']:.1f} {tile['y']:.1f} {tile['flip']}\n"
f.write(line)
def main():
#TODO: add these as command line parameters
input_path = "../openlane/user_project_wrapper/macro_tmp_test.cfg" # Path to your input configuration file
output_path = "../openlane/user_project_wrapper/macro_manipulated_test.cfg"
# origin = (2400, 245) # Rotation origin (x, y)
origin = (150, 50) # Rotation origin (x, y)
angle = -90 # Rotation angle in degrees
x_offset = -113
y_offset = 0
x_space_offset = 5
y_space_offset = -3.5
pdn_pitch_vertical = 75
# x_start = 6
# x_stop = 5
#
# # Read the tiles from the input file
tiles = read_tiles(input_path)
#
# tiles = change_space_between_tiles_horizontal(tiles, x_space_offset, False,
# x_start, x_stop)
# x_start = 7
# x_stop = 5
# tiles = change_space_between_tiles_horizontal(tiles, x_space_offset, False,
# x_start, x_stop)
# tiles = change_space_between_tiles_horizontal(tiles, x_space_offset, False,
# x_start, x_stop)
move_tiles(tiles, x_offset, y_offset)
# tiles = change_space_between_tiles_vertical(tiles, y_space_offset,
# pdn_pitch_vertical)
# Rotate the tiles around the given origin
# tiles = rotate_tiles(tiles, origin, angle)
# Write the rotated tiles to the output file
write_tiles_to_file(tiles, output_path)
if __name__ == "__main__":
main()