Skip to content

Commit 72e4745

Browse files
committed
Add combinatorial example generator to utils
Signed-off-by: Krzysztof Boronski <kboronski@antmicro.com>
1 parent 37a983b commit 72e4745

File tree

1 file changed

+279
-0
lines changed

1 file changed

+279
-0
lines changed

utils/generate_design.py

Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
#!/usr/bin/env python3
2+
3+
from copy import copy
4+
import random
5+
import networkx as nx
6+
from argparse import ArgumentParser
7+
8+
9+
def lut_param_randomizer(len):
10+
r = ['0', '1']
11+
12+
init = f'{len}\'b'
13+
for _ in range(0, len):
14+
init += random.choice(r)
15+
16+
return {'INIT': init}
17+
18+
19+
class Cell:
20+
name: str
21+
inputs: 'list[str]'
22+
outputs: 'list[str]'
23+
24+
def __init__(
25+
self,
26+
name: str,
27+
inputs: 'list[str]',
28+
outputs: 'list[str]',
29+
):
30+
self.name = name
31+
self.inputs = inputs
32+
self.outputs = outputs
33+
34+
35+
class PlaceableCell:
36+
cell: Cell
37+
probability: float
38+
param_randomizer: None
39+
40+
def __init__(
41+
self,
42+
name: str,
43+
inputs: 'list[str]',
44+
outputs: 'list[str]',
45+
probability: float,
46+
param_randomizer=None
47+
):
48+
self.cell = Cell(name, inputs, outputs)
49+
self.probability = probability
50+
self.param_randomizer = param_randomizer
51+
52+
53+
placeables = [
54+
PlaceableCell(
55+
name='LUT4',
56+
inputs=['I0', 'I1', 'I2', 'I3'],
57+
outputs=['O'],
58+
probability=1.0,
59+
param_randomizer=lambda: lut_param_randomizer(16)
60+
),
61+
PlaceableCell(
62+
name='LUT5',
63+
inputs=['I0', 'I1', 'I2', 'I3', 'I4'],
64+
outputs=['O'],
65+
probability=1.0,
66+
param_randomizer=lambda: lut_param_randomizer(32)
67+
),
68+
PlaceableCell(
69+
name='LUT6',
70+
inputs=['I0', 'I1', 'I2', 'I3', 'I4', 'I5'],
71+
outputs=['O'],
72+
probability=1.0,
73+
param_randomizer=lambda: lut_param_randomizer(64)
74+
)
75+
]
76+
77+
io_cells = {
78+
'in':
79+
{
80+
'cell': Cell(name='IBUF', inputs=['I'], outputs=['O']),
81+
'i': 'I',
82+
'o': 'O',
83+
'params': {}
84+
},
85+
'out':
86+
{
87+
'cell': Cell(name='OBUF', inputs=['I'], outputs=['O']),
88+
'i': 'I',
89+
'o': 'O',
90+
'params': {}
91+
}
92+
}
93+
94+
total_placeable_weight = 0.0
95+
for p in placeables:
96+
total_placeable_weight += p.probability
97+
98+
99+
def random_cell() -> PlaceableCell:
100+
global total_placeable_weight
101+
102+
v = random.uniform(0.0, total_placeable_weight)
103+
104+
acc = 0.0
105+
for c in placeables:
106+
if (v > acc) and (v <= acc + c.probability):
107+
return c
108+
else:
109+
acc += c.probability
110+
111+
raise RuntimeError('Random value out-of-range')
112+
113+
114+
class Netlist:
115+
cell_type_count: 'dict[str, int]'
116+
free_inpins: 'set[str]'
117+
free_outpins: 'set[str]'
118+
g: 'nx.DiGraph'
119+
cells: 'list[tuple[str, str, dict[str, str]]]'
120+
ports: 'list[str]'
121+
122+
def __init__(self):
123+
self.cell_type_count = {}
124+
self.g = nx.DiGraph()
125+
self.free_inpins = set()
126+
self.free_outpins = set()
127+
self.cells = []
128+
self.ports = []
129+
130+
def name_cell(self, c: Cell) -> str:
131+
if self.cell_type_count.get(c.name) is None:
132+
self.cell_type_count[c.name] = 0
133+
ccount = self.cell_type_count[c.name]
134+
self.cell_type_count[c.name] += 1
135+
return f'{c.name}_{ccount}'
136+
137+
def add_cell(self, cell: Cell, **properties: 'str') -> str:
138+
cell_name = self.name_cell(cell)
139+
self.cells.append((cell, cell_name, properties))
140+
141+
for input in cell.inputs:
142+
name = f'{cell_name}/{input}'
143+
self.g.add_node(name, cell_instance=cell_name)
144+
self.free_inpins.add(name)
145+
for output in cell.outputs:
146+
name = f'{cell_name}/{output}'
147+
self.g.add_node(name, cell_instance=cell_name)
148+
self.free_outpins.add(name)
149+
150+
return cell_name
151+
152+
def add_port(self, cell_pin: str, dir: str, name: str):
153+
self.ports.append(name)
154+
self.g.add_node(name)
155+
156+
if dir.lower() == 'in':
157+
self.g.nodes[name]['net'] = f'net_{name}'
158+
self.connect_driver_sink(name, cell_pin)
159+
elif dir.lower() == 'out':
160+
self.g.nodes[cell_pin]['net'] = f'net_{name}'
161+
self.connect_driver_sink(cell_pin, name)
162+
else:
163+
raise RuntimeError(f'Incorrect pin direction `{dir}`')
164+
165+
def connect_driver_sink(self, driver: str, sink: str):
166+
net = self.g.nodes.data('net', default=None)[driver]
167+
net_name = f'net_{driver}'
168+
if net is None:
169+
self.g.nodes[driver]['net'] = net_name
170+
self.g.nodes[sink]['net'] = net_name
171+
172+
self.g.add_edge(driver, sink)
173+
if sink in self.free_inpins:
174+
self.free_inpins.remove(sink)
175+
176+
def export_tcl(self) -> str:
177+
tcl = ''
178+
179+
for port in self.ports:
180+
dir = 'IN' if len(self.g.out_edges(port)) > 0 else 'OUT'
181+
tcl += f'create_port -direction {dir} {port}\n'
182+
183+
tcl += '\n'
184+
185+
for (cell, cell_name, properties) in self.cells:
186+
tcl += f'create_cell -ref {cell.name} {cell_name}\n'
187+
for (prop_name, prop_value) in properties.items():
188+
tcl += f'set_property {prop_name} ' + '{' + prop_value + '}' + f' [get_cell {cell_name}]\n'
189+
190+
tcl += '\n'
191+
192+
nets = {}
193+
for (driver, sink) in self.g.edges:
194+
net = self.g.nodes[driver]['net']
195+
if net not in nets:
196+
tcl += f'create_net {net.replace("/", "__")}\n'
197+
nets[net] = set()
198+
199+
nets[net].add(driver)
200+
nets[net].add(sink)
201+
202+
tcl += '\n'
203+
204+
for (net, objects) in nets.items():
205+
tcl += f'connect_net -net {net.replace("/", "__")} -objects ' + '{' + ' '.join(
206+
objects
207+
) + '}\n'
208+
209+
return tcl
210+
211+
212+
def add_and_connect_cells(netlist: Netlist, no_of_cells: int):
213+
# Add random cells
214+
for _ in range(0, no_of_cells):
215+
cell = random_cell()
216+
properties = cell.param_randomizer(
217+
) if cell.param_randomizer is not None else {}
218+
netlist.add_cell(cell.cell, **properties)
219+
220+
# Make random connections
221+
free_outpins = list(netlist.free_outpins)
222+
for sink in copy(netlist.free_inpins):
223+
if len(netlist.g.in_edges(sink)) != 0:
224+
continue
225+
driver = random.choice(free_outpins)
226+
netlist.connect_driver_sink(driver, sink)
227+
228+
229+
def add_io(netlist: Netlist, input_cnt: int, output_cnt: int):
230+
global io_cells
231+
232+
in_def = io_cells['in']
233+
out_def = io_cells['out']
234+
235+
for i in range(0, input_cnt):
236+
name = netlist.add_cell(in_def['cell'], **in_def['params'])
237+
netlist.add_port(f'{name}/{in_def["i"]}', 'IN', f'in{i}')
238+
for i in range(0, output_cnt):
239+
name = netlist.add_cell(out_def['cell'], **out_def['params'])
240+
outpin = f'{name}/{out_def["o"]}'
241+
netlist.add_port(outpin, 'OUT', f'out{i}')
242+
netlist.free_outpins.remove(outpin)
243+
244+
245+
def main():
246+
parser = ArgumentParser()
247+
parser.add_argument(
248+
'--cell-count',
249+
'-c',
250+
type=int,
251+
default=10,
252+
help='Number of cells to place'
253+
)
254+
parser.add_argument(
255+
'--input-count',
256+
'-I',
257+
type=int,
258+
default=8,
259+
help='Number of inputs to add'
260+
)
261+
parser.add_argument(
262+
'--output-count',
263+
'-O',
264+
type=int,
265+
default=8,
266+
help='Number of outputs to add'
267+
)
268+
269+
args = parser.parse_args()
270+
271+
netlist = Netlist()
272+
add_io(netlist, args.input_count, args.output_count)
273+
add_and_connect_cells(netlist, args.cell_count)
274+
275+
print(netlist.export_tcl())
276+
277+
278+
if __name__ == '__main__':
279+
main()

0 commit comments

Comments
 (0)