10
10
import time
11
11
from dataclasses import dataclass
12
12
from pathlib import Path
13
+ from random import shuffle
13
14
15
+ import numpy
16
+ import ray
14
17
from liberty .parser import parse_liberty
15
18
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
18
20
19
21
import helpers
22
+ from formula_converter_class import FormulaConverter
20
23
21
24
"""Part of the fault injection framework for the OpenTitan.
22
25
26
29
needed by the FI Injector.
27
30
28
31
Typical usage:
29
- >>> ./cell_lib_generator.py -l NangateOpenCellLibrary_typical.lib
32
+ >>> ./cell_lib_generator.py -l NangateOpenCellLibrary_typical.lib -n 16
30
33
-c examples/config.json
31
34
-o cell_lib_nangate45_autogen.py
32
35
"""
@@ -121,6 +124,18 @@ def parse_arguments(argv):
121
124
type = helpers .ap_check_file_exists ,
122
125
required = True ,
123
126
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" )
124
139
parser .add_argument ("-o" ,
125
140
"--output" ,
126
141
dest = "out_lib" ,
@@ -133,7 +148,7 @@ def parse_arguments(argv):
133
148
args = parser .parse_args (argv )
134
149
135
150
if args .version :
136
- helpers .show_and_exit (__file__ , ["sympy" ])
151
+ helpers .show_and_exit (__file__ , ["sympy" , "ray" , "numpy" ])
137
152
138
153
return args
139
154
@@ -152,173 +167,83 @@ def open_cfg_file(args) -> dict:
152
167
return cfg
153
168
154
169
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 .
157
172
158
173
Args:
159
174
args: The input arguments.
160
175
161
176
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.
204
178
"""
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
209
192
210
193
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.
236
196
237
197
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.
241
199
242
200
Returns:
243
- The boolean expression in CNF .
201
+ The cell library .
244
202
"""
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
254
203
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 ())
262
205
except :
263
- raise Exception (f"Failed to convert formula for { gate } ." )
206
+ raise Exception (f"Failed to parse the { args . cell_lib } library ." )
264
207
265
- return formula
208
+ return cell_lib
266
209
267
210
268
- def parse_cells (cell_lib ) -> list :
211
+ def parse_cells (cell_lib : dict , cell_types : list ) -> list :
269
212
""" Parse the cells in the cell library.
270
213
271
214
Args:
272
215
cell_lib: The opened cell library.
216
+ cell_bl: The cell black list.
273
217
274
218
Returns:
275
219
The cells list.
276
220
"""
277
221
cells = []
278
222
for cell_group in cell_lib .get_groups ("cell" ):
279
223
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 )
298
243
299
244
return cells
300
245
301
246
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
-
322
247
def build_cell_functions (cells : list ) -> list :
323
248
""" Creates the cell functions.
324
249
@@ -492,19 +417,51 @@ def write_cell_lib(cell_lib_template: Template, cell_lib_py: str,
492
417
out_file .write_text (cell_lib_template .render (cell_lib = cell_lib_py ))
493
418
494
419
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
+
495
446
def main (argv = None ):
496
447
tstp_begin = time .time ()
497
448
args = parse_arguments (argv )
498
449
450
+ num_cores = args .num_cores
451
+ ray .init (num_cpus = num_cores )
452
+
499
453
template_file = (Path ("template/cell_lib.py.tpl" ))
500
454
cell_lib_template = Template (template_file .read_text (),
501
455
strict_undefined = True )
502
456
# Open the cell library and config.
503
457
cell_lib = open_cell_lib (args )
504
458
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 )
505
461
# 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 )
508
465
# Assemble the output file and write.
509
466
cell_lib_py = build_cell_lib (cells , cell_cfg )
510
467
write_cell_lib (cell_lib_template , cell_lib_py , args .out_lib )
0 commit comments