Skip to content

Commit 1b615f0

Browse files
Pascal Nasahlnasahlpa
Pascal Nasahl
authored andcommitted
Cell lib generator speed up.
As processing larger cell libraries takes time, this PR adds two speed ups: - Added the -n option to specify the number of cores used by the cell library generator. Similar to the FI injector, ray is used to spawn multiple processes. - Added optional -j argument to provide a JSON netlist. When the JSON netlist is provided by the user, the tools only processes the cell types, which occure in the netlist. Without the -j argument, all cells in the cell library are parsed. Signed-off-by: Pascal Nasahl <[email protected]>
1 parent f931331 commit 1b615f0

File tree

4 files changed

+242
-142
lines changed

4 files changed

+242
-142
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ The convert a cell library (e.g., the
3030
[NANGATE45](https://github.com/The-OpenROAD-Project/OpenROAD-flow-scripts/tree/master/flow/platforms/nangate45/lib) library),
3131
adapt the `examples/config.json` file and start the cell library generator:
3232
```console
33-
$ ./cell_lib_generator.py -l NangateOpenCellLibrary_typical.lib \
33+
$ ./cell_lib_generator.py -l NangateOpenCellLibrary_typical.lib -n 16 \
3434
-c examples/config.json -o cell_lib_nangate45_autogen.py
3535
```
3636
To start the preprocessing phase for this example netlist, create

cell_lib_generator.py

+97-140
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,16 @@
1010
import time
1111
from dataclasses import dataclass
1212
from pathlib import Path
13+
from random import shuffle
1314

15+
import numpy
16+
import ray
1417
from liberty.parser import parse_liberty
1518
from mako.template import Template
16-
from sympy import Symbol, false, sympify, true
17-
from sympy.logic.boolalg import is_cnf, simplify_logic, to_cnf
19+
from sympy import Symbol
1820

1921
import helpers
22+
from formula_converter_class import FormulaConverter
2023

2124
"""Part of the fault injection framework for the OpenTitan.
2225
@@ -26,7 +29,7 @@
2629
needed by the FI Injector.
2730
2831
Typical usage:
29-
>>> ./cell_lib_generator.py -l NangateOpenCellLibrary_typical.lib
32+
>>> ./cell_lib_generator.py -l NangateOpenCellLibrary_typical.lib -n 16
3033
-c examples/config.json
3134
-o cell_lib_nangate45_autogen.py
3235
"""
@@ -121,6 +124,18 @@ def parse_arguments(argv):
121124
type=helpers.ap_check_file_exists,
122125
required=True,
123126
help="Path of the cell library config file")
127+
parser.add_argument("-j",
128+
"--json",
129+
dest="netlist",
130+
type=helpers.ap_check_file_exists,
131+
required=False,
132+
help="Only parse cells which are also in the netlist")
133+
parser.add_argument("-n",
134+
"--num_cores",
135+
dest="num_cores",
136+
type=int,
137+
required=True,
138+
help="Number of cores to use")
124139
parser.add_argument("-o",
125140
"--output",
126141
dest="out_lib",
@@ -133,7 +148,7 @@ def parse_arguments(argv):
133148
args = parser.parse_args(argv)
134149

135150
if args.version:
136-
helpers.show_and_exit(__file__, ["sympy"])
151+
helpers.show_and_exit(__file__, ["sympy", "ray", "numpy"])
137152

138153
return args
139154

@@ -152,173 +167,83 @@ def open_cfg_file(args) -> dict:
152167
return cfg
153168

154169

155-
def open_cell_lib(args) -> dict:
156-
""" Opens the cell library in the liberty format.
170+
def open_netlist(args) -> list:
171+
""" Opens the JSON netlist and parses all cell types.
157172
158173
Args:
159174
args: The input arguments.
160175
161176
Returns:
162-
The cell library.
163-
"""
164-
try:
165-
cell_lib = parse_liberty(open(args.cell_lib).read())
166-
except:
167-
raise Exception(f"Failed to parse the {args.cell_lib} library.")
168-
169-
return cell_lib
170-
171-
172-
def simplify_expression(expr: Symbol) -> Symbol:
173-
""" Simplify the CNF expression.
174-
175-
The simplify_logic functionality of sympy is used to simplify the given
176-
expression. As the output needs to be in CNF, a check is conducted.
177-
178-
Args:
179-
expr: The boolean expression to simplify.
180-
181-
Returns:
182-
The simplified boolean expression in CNF.
183-
"""
184-
simplified = simplify_logic(expr, 'cnf', True)
185-
if is_cnf(simplified):
186-
return simplified
187-
else:
188-
return expr
189-
190-
191-
def convert_cnf(expr: Symbol, out_symbol: Symbol, gate: str) -> Symbol:
192-
""" Convert the given boolean expression to CNF.
193-
194-
The logical biconditional of the boolean expression of the gate is converted
195-
to CNF using the sympy library.
196-
197-
Args:
198-
expr: The boolean expression to convert.
199-
out_symbol: The output variable of the boolean expression.
200-
gate: The name of the current gate.
201-
202-
Returns:
203-
The boolean expression in CNF.
177+
The cell types.
204178
"""
205-
cnf = to_cnf((out_symbol & expr) | (~out_symbol & ~expr))
206-
if not is_cnf(cnf):
207-
raise Exception(f"Failed to convert {gate} to CNF.")
208-
return cnf
179+
modules = None
180+
cell_types = []
181+
if args.netlist:
182+
with open(args.netlist, "r") as circuit_json_file:
183+
circuit_json = json.load(circuit_json_file)
184+
modules = circuit_json["modules"]
185+
# Iterate over netlist and add all cell types to the list.
186+
for module, module_value in modules.items():
187+
for cell, cell_value in module_value["cells"].items():
188+
cell_types.append(cell_value["type"])
189+
# Remove duplicates from the list.
190+
cell_types = list(dict.fromkeys(cell_types))
191+
return cell_types
209192

210193

211-
def replace_pin(inputs: list, outputs: list, target_char: str,
212-
replace_char: str) -> str:
213-
""" Replace a pin name.
214-
215-
Sympy uses some predefined symbols (I, S), which need to be replaced in the
216-
input and output pins.
217-
218-
Args:
219-
inputs: The inputs of the cell.
220-
outputs: The outputs of the cell.
221-
target_char: The char to replace.
222-
replace_char: The rename char.
223-
224-
Returns:
225-
The formula, input, and output with the replaced pin name.
226-
"""
227-
inputs = [in_pin.replace(target_char, replace_char) for in_pin in inputs]
228-
for out_pin in outputs:
229-
out_pin.name.replace(target_char, replace_char)
230-
231-
return inputs, outputs
232-
233-
234-
def convert_string(formula: str, output: str, gate: str) -> Symbol:
235-
""" Convert the formula string to a sympy Symbol.
194+
def open_cell_lib(args) -> dict:
195+
""" Opens the cell library in the liberty format.
236196
237197
Args:
238-
formula: The boolean formula.
239-
output: The output in name of the boolean expression.
240-
gate: The current gate.
198+
args: The input arguments.
241199
242200
Returns:
243-
The boolean expression in CNF.
201+
The cell library.
244202
"""
245-
# As sympy requires ~ as a NOT, replace !.
246-
formula = formula.replace("!", "~")
247-
# "S" is predefined by sympy, replace with K.
248-
formula = formula.replace("S", "K")
249-
# "I" is predefined by sympy, replace with L.
250-
formula = formula.replace("I", "L")
251-
# Set 1/0 formula to true/false
252-
if formula == "1": formula = true
253-
if formula == "0": formula = false
254203
try:
255-
# Convert the string to sympy using sympify. The convert_xor=False
256-
# converts a ^ to a XOR.
257-
formula = sympify(formula, convert_xor=False)
258-
# Use the logical biconditional to induce the output.
259-
formula = convert_cnf(formula, Symbol(output), gate)
260-
# Simplify CNF formula.
261-
formula = simplify_expression(formula)
204+
cell_lib = parse_liberty(open(args.cell_lib).read())
262205
except:
263-
raise Exception(f"Failed to convert formula for {gate}.")
206+
raise Exception(f"Failed to parse the {args.cell_lib} library.")
264207

265-
return formula
208+
return cell_lib
266209

267210

268-
def parse_cells(cell_lib) -> list:
211+
def parse_cells(cell_lib: dict, cell_types: list) -> list:
269212
""" Parse the cells in the cell library.
270213
271214
Args:
272215
cell_lib: The opened cell library.
216+
cell_bl: The cell black list.
273217
274218
Returns:
275219
The cells list.
276220
"""
277221
cells = []
278222
for cell_group in cell_lib.get_groups("cell"):
279223
name = cell_group.args[0]
280-
inputs = []
281-
outputs = []
282-
for pin_group in cell_group.get_groups("pin"):
283-
pin_name = pin_group.args[0]
284-
if pin_group["direction"] == "input":
285-
inputs.append(pin_name)
286-
else:
287-
if pin_group["function"]:
288-
function = pin_group["function"].value
289-
out_pin = Output(name=pin_name,
290-
formula=function,
291-
formula_cnf="")
292-
outputs.append(out_pin)
293-
294-
# Ignore cells without outputs or inputs, e.g., filler cells.
295-
if inputs and outputs:
296-
cell = Cell(name=name, inputs=inputs, outputs=outputs)
297-
cells.append(cell)
224+
if (not cell_types) or (name in cell_types):
225+
inputs = []
226+
outputs = []
227+
for pin_group in cell_group.get_groups("pin"):
228+
pin_name = pin_group.args[0]
229+
if pin_group["direction"] == "input":
230+
inputs.append(pin_name)
231+
else:
232+
if pin_group["function"]:
233+
function = pin_group["function"].value
234+
out_pin = Output(name=pin_name,
235+
formula=function,
236+
formula_cnf="")
237+
outputs.append(out_pin)
238+
239+
# Ignore cells without outputs or inputs, e.g., filler cells.
240+
if inputs and outputs:
241+
cell = Cell(name=str(name), inputs=inputs, outputs=outputs)
242+
cells.append(cell)
298243

299244
return cells
300245

301246

302-
def convert_formula(cells: list):
303-
""" Converts the boolean function from a string to a clause.
304-
305-
Args:
306-
cells: The cells list.
307-
308-
"""
309-
for cell in cells:
310-
# "S" is predefined by sympy, replace with K.
311-
cell.inputs, cell.outputs = replace_pin(cell.inputs, cell.outputs, "S",
312-
"K")
313-
# "I" is predefined by sympy, replace with L.
314-
cell.inputs, cell.outputs = replace_pin(cell.inputs, cell.outputs, "I",
315-
"L")
316-
for output in cell.outputs:
317-
if output.formula:
318-
output.formula_cnf = convert_string(output.formula,
319-
output.name, cell.name)
320-
321-
322247
def build_cell_functions(cells: list) -> list:
323248
""" Creates the cell functions.
324249
@@ -492,19 +417,51 @@ def write_cell_lib(cell_lib_template: Template, cell_lib_py: str,
492417
out_file.write_text(cell_lib_template.render(cell_lib=cell_lib_py))
493418

494419

420+
def handle_cells(cells: dict, num_cores: int) -> list:
421+
""" Convert the cell formulas to sympy formulas using ray.
422+
423+
Args:
424+
cells: The cells of the library.
425+
num_cores: The number of ray processes.
426+
Returns:
427+
The list of cells with the sympy formulas.
428+
"""
429+
# Shuffle the list as some cell types take longer.
430+
shuffle(cells)
431+
# Split cells into num_core shares.
432+
cell_shares = numpy.array_split(numpy.array(cells), num_cores)
433+
# Use ray to distribute fault injection to num_cores processes.
434+
workers = [
435+
FormulaConverter.remote(cell_share) for cell_share in cell_shares
436+
]
437+
438+
# Perform the attack and collect the results.
439+
tasks = [worker.convert_formulas.remote() for worker in workers]
440+
results = ray.get(tasks)
441+
cell_formulas = [item for sublist in results for item in sublist]
442+
443+
return cell_formulas
444+
445+
495446
def main(argv=None):
496447
tstp_begin = time.time()
497448
args = parse_arguments(argv)
498449

450+
num_cores = args.num_cores
451+
ray.init(num_cpus=num_cores)
452+
499453
template_file = (Path("template/cell_lib.py.tpl"))
500454
cell_lib_template = Template(template_file.read_text(),
501455
strict_undefined=True)
502456
# Open the cell library and config.
503457
cell_lib = open_cell_lib(args)
504458
cell_cfg = open_cfg_file(args)
459+
# If provided, open the netlist. Only cells used in the netlist are parsed.
460+
cell_types = open_netlist(args)
505461
# Parse the cells of the lib and convert the formulas.
506-
cells = parse_cells(cell_lib)
507-
convert_formula(cells)
462+
cells = parse_cells(cell_lib, cell_types)
463+
# Distribute formula conversion to ray.
464+
cells = handle_cells(cells, num_cores)
508465
# Assemble the output file and write.
509466
cell_lib_py = build_cell_lib(cells, cell_cfg)
510467
write_cell_lib(cell_lib_template, cell_lib_py, args.out_lib)

doc/aes_rnd_cntr.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ a target gate is replaced during the FI process.
7979

8080
Then, convert the used cell library using the cell library generator script:
8181
```console
82-
$ ./cell_lib_generator.py -l NangateOpenCellLibrary_typical.lib \
82+
$ ./cell_lib_generator.py -l NangateOpenCellLibrary_typical.lib -n 16 \
8383
-c examples/config.json \
8484
-o cell_lib_nangate45_autogen.py
8585
```

0 commit comments

Comments
 (0)