9
9
from __future__ import print_function , unicode_literals
10
10
import re
11
11
import collections
12
+ import tempfile
13
+ import os
14
+ import subprocess
15
+ import six
16
+ import sys
12
17
13
18
from .pyrtlexceptions import PyrtlError , PyrtlInternalError
14
19
from .core import working_block , _NameSanitizer
15
20
from .wire import WireVector , Input , Output , Const , Register , next_tempvar_name
16
- from .corecircuits import concat_list
21
+ from .corecircuits import concat_list , rtl_all , rtl_any
17
22
from .memory import RomBlock
18
23
from .passes import two_way_concat , one_bit_selects
19
24
@@ -108,10 +113,11 @@ def input_from_blif(blif, block=None, merge_io_vectors=True, clock_name='clk', t
108
113
wires will be Input wires of the block. This holds similarly for Outputs.
109
114
110
115
This assumes the following:
111
- * The BLIF has been flattened and there is only a single module
112
116
* There is only one single shared clock and reset
113
117
* Output is generated by Yosys with formals in a particular order
114
118
119
+ It currently supports multi-module (unflattened) BLIF, though we recommend importing a
120
+ flattened BLIF with a single module when possible.
115
121
It currently ignores the reset signal (which it assumes is input only to the flip flops).
116
122
"""
117
123
import pyparsing
@@ -315,26 +321,42 @@ def twire(w):
315
321
elif command ['cover_list' ].asList () == ['11' , '1' ]:
316
322
output_wire = twire (netio [2 ])
317
323
output_wire <<= twire (netio [0 ]) & twire (netio [1 ]) # and gate
318
- elif command ['cover_list' ].asList () == ['00' , '1' ]:
319
- output_wire = twire (netio [2 ])
320
- output_wire <<= ~ (twire (netio [0 ]) | twire (netio [1 ])) # nor gate
321
324
elif command ['cover_list' ].asList () == ['1-' , '1' , '-1' , '1' ]:
322
325
output_wire = twire (netio [2 ])
323
326
output_wire <<= twire (netio [0 ]) | twire (netio [1 ]) # or gate
327
+ elif command ['cover_list' ].asList () == ['0-' , '1' , '-0' , '1' ]:
328
+ output_wire = twire (netio [2 ])
329
+ output_wire <<= twire (netio [0 ]).nand (twire (netio [1 ])) # nand gate
324
330
elif command ['cover_list' ].asList () == ['10' , '1' , '01' , '1' ]:
325
331
output_wire = twire (netio [2 ])
326
332
output_wire <<= twire (netio [0 ]) ^ twire (netio [1 ]) # xor gate
327
- elif command ['cover_list' ].asList () == ['1-0' , '1' , '-11' , '1' ]:
328
- output_wire = twire (netio [3 ])
329
- output_wire <<= (twire (netio [0 ]) & ~ twire (netio [2 ])) \
330
- | (twire (netio [1 ]) & twire (netio [2 ])) # mux
331
- elif command ['cover_list' ].asList () == ['-00' , '1' , '0-0' , '1' ]:
332
- output_wire = twire (netio [3 ])
333
- output_wire <<= (~ twire (netio [1 ]) & ~ twire (netio [2 ])) \
334
- | (~ twire (netio [0 ]) & ~ twire (netio [2 ]))
335
333
else :
336
- raise PyrtlError ('Blif file with unknown logic cover set "%s"'
337
- '(currently gates are hard coded)' % command ['cover_list' ])
334
+ # Although the following is fully generic and thus encompasses all of the
335
+ # special cases after the simple wire case above, we leave the above in because
336
+ # they are commonly found and lead to a slightly cleaner (though equivalent) netlist,
337
+ # because we can use nand/xor primitives, or avoid the extra fluff of concat/select
338
+ # wires that might be created implicitly as part of rtl_all/rtl_any.
339
+ def convert_val (ix , val ):
340
+ wire = twire (netio [ix ])
341
+ if val == '0' :
342
+ wire = ~ wire
343
+ return wire
344
+
345
+ cover = command ['cover_list' ].asList ()
346
+ output_wire = twire (netio [- 1 ])
347
+ conjunctions = []
348
+ while cover :
349
+ if len (cover ) < 2 :
350
+ raise PyrtlError ('BLIF file with malformed cover set "%s" '
351
+ % command ['cover_list' ])
352
+ input_plane , output_plane , cover = cover [0 ], cover [1 ], cover [2 :]
353
+ if output_plane != '1' :
354
+ raise PyrtlError ('Off-set found in the output plane of BLIF cover set "%s" '
355
+ '(only on-sets are supported)' % command ['cover_list' ])
356
+ conj = rtl_all (* [convert_val (ix , val ) for ix , val
357
+ in enumerate (input_plane ) if val != '-' ])
358
+ conjunctions .append (conj )
359
+ output_wire <<= rtl_any (* conjunctions )
338
360
339
361
def extract_flop (subckt , command ):
340
362
@@ -411,6 +433,89 @@ def instantiate(subckt):
411
433
#
412
434
413
435
436
+ def input_from_verilog (verilog , clock_name = 'clk' , toplevel = None , leave_in_dir = None , block = None ):
437
+ """ Read an open Verilog file or string as input via Yosys conversion, updating the block.
438
+
439
+ :param verilog: An open Verilog file to read
440
+ :param clock_name: The name of the clock (defaults to 'clk')
441
+ :param toplevel: Name of top-level module to instantiate; if None, defaults to first model
442
+ defined in the Verilog file
443
+ :param bool leave_in_dir: If True, save the intermediate BLIF file created in
444
+ the given directory
445
+ :param block: The block where the logic will be added
446
+
447
+ Note: This function is essentially a wrapper for `input_from_blif()`, with the added convenience
448
+ of turning the Verilog into BLIF for import for you. This function passes a set of commands to
449
+ Yosys as a script that normally produces BLIF files that can be successuflly imported into
450
+ PyRTL via `input_from_blif()`. If the Yosys conversion fails here, we recommend you create your
451
+ own custom Yosys script to try and produce BLIF yourself. Then you can import BLIF directly via
452
+ `input_from_blif()`.
453
+ """
454
+
455
+ # Dev Notes:
456
+ # 1) We pass in an open file or string to keep the same API as input_from_blif (even though
457
+ # we are essentially putting that Verilog code back in a file for Yosys to operate on).
458
+ # 2) The Yosys script does not have a call to `flatten`, since we now have support for
459
+ # multi-module BLIFs. `flatten` replaces the .subckt (i.e. the module inclusions) with their
460
+ # actual contents, so if things aren't working for some reason, add this command for help.
461
+ # 3) The Yosys script *does* have a call to `setundef -zero -undriven`, which we use to set
462
+ # undriven nets to 0; we do this so PyRTL doesn't complain about used but undriven wires.
463
+
464
+ try :
465
+ verilog_string = verilog .read ()
466
+ except AttributeError :
467
+ if isinstance (verilog , six .string_types ):
468
+ verilog_string = verilog
469
+ else :
470
+ raise PyrtlError ('input_from_verilog expecting either open file or string' )
471
+
472
+ block = working_block (block )
473
+
474
+ # Create a temporary Verilog file
475
+ temp_vd , tmp_verilog_path = tempfile .mkstemp (prefix = 'pyrtl_verilog' , suffix = '.v' ,
476
+ dir = leave_in_dir , text = True )
477
+ with open (tmp_verilog_path , 'w' ) as f :
478
+ f .write (verilog_string )
479
+
480
+ # Create a temporary BLIF file
481
+ temp_bd , tmp_blif_path = tempfile .mkstemp (prefix = 'pyrtl_blif' , suffix = '.blif' ,
482
+ dir = leave_in_dir , text = True )
483
+
484
+ yosys_arg_template = (
485
+ "-p "
486
+ "read_verilog %s; "
487
+ "synth %s; "
488
+ "setundef -zero -undriven; "
489
+ "opt; "
490
+ "write_blif %s; "
491
+ )
492
+
493
+ yosys_arg = yosys_arg_template % (tmp_verilog_path ,
494
+ ('-top ' + toplevel ) if toplevel is not None else '-auto-top' ,
495
+ tmp_blif_path )
496
+
497
+ try :
498
+ os .close (temp_vd )
499
+ os .close (temp_bd )
500
+ # call yosys on the script, and grab the output
501
+ _yosys_output = subprocess .check_output (['yosys' , yosys_arg ])
502
+ with open (tmp_blif_path ) as blif :
503
+ input_from_blif (blif , block = block , clock_name = clock_name , top_model = toplevel )
504
+ except (subprocess .CalledProcessError , ValueError ) as e :
505
+ print ('Error with call to yosys...' , file = sys .stderr )
506
+ print ('---------------------------------------------' , file = sys .stderr )
507
+ print (str (e .output ).replace ('\\ n' , '\n ' ), file = sys .stderr )
508
+ print ('---------------------------------------------' , file = sys .stderr )
509
+ raise PyrtlError ('Yosys callfailed' )
510
+ except OSError as e :
511
+ print ('Error with call to yosys...' , file = sys .stderr )
512
+ raise PyrtlError ('Call to yosys failed (not installed or on path?)' )
513
+ finally :
514
+ os .remove (tmp_verilog_path )
515
+ if leave_in_dir is None :
516
+ os .remove (tmp_blif_path )
517
+
518
+
414
519
def output_to_verilog (dest_file , add_reset = True , block = None ):
415
520
""" A function to walk the block and output it in Verilog format to the open file.
416
521
@@ -432,6 +537,11 @@ def output_to_verilog(dest_file, add_reset=True, block=None):
432
537
file = dest_file
433
538
internal_names = _VerilogSanitizer ('_ver_out_tmp_' )
434
539
540
+ if add_reset : # True or 'asynchronous'
541
+ if block .get_wirevector_by_name ('rst' ) is not None :
542
+ raise PyrtlError ("Found a user-defined wire named 'rst'. Pass in "
543
+ "'add_reset=False' to use your existing reset logic." )
544
+
435
545
for wire in block .wirevector_set :
436
546
internal_names .make_valid_string (wire .name )
437
547
@@ -672,7 +782,7 @@ def _to_verilog_footer(file):
672
782
673
783
674
784
def output_verilog_testbench (dest_file , simulation_trace = None , toplevel_include = None ,
675
- vcd = "waveform.vcd" , cmd = None , block = None ):
785
+ vcd = "waveform.vcd" , cmd = None , add_reset = True , block = None ):
676
786
""" Output a Verilog testbench for the block/inputs used in the simulation trace.
677
787
678
788
:param dest_file: an open file to which the test bench will be printed.
@@ -692,6 +802,16 @@ def output_verilog_testbench(dest_file, simulation_trace=None, toplevel_include=
692
802
specific values out during testbench evaluation (e.g. cmd='$display("%d", out);'
693
803
will instruct the testbench to print the value of 'out' every cycle which can then
694
804
be compared easy with a reference).
805
+ :param add_reset: If reset logic should be added. Allowable options are:
806
+ False (meaning no reset logic is added), True (default, for adding synchronous
807
+ reset logic), and 'asynchronous' (for adding asynchronous reset logic).
808
+ The value passed in here should match the argument passed to `output_to_verilog()`.
809
+ :param block: Block containing design to test.
810
+
811
+ If `add_reset` is not False, a `rst` wire is added and will passed as an input to the
812
+ instantiated toplevel module. The `rst` wire will be held low in the testbench, because
813
+ initialization here occurs via the `initial` block. It is provided for consistency with
814
+ `output_to_verilog()`.
695
815
696
816
The test bench does not return any values.
697
817
@@ -707,8 +827,18 @@ def output_verilog_testbench(dest_file, simulation_trace=None, toplevel_include=
707
827
output_verilog_testbench(fp, sim.tracer, vcd=None, cmd='$display("%d", out);')
708
828
709
829
"""
830
+ if not isinstance (add_reset , bool ):
831
+ if add_reset != 'asynchronous' :
832
+ raise PyrtlError ("Invalid add_reset option %s. Acceptable options are "
833
+ "False, True, and 'asynchronous'" )
834
+
710
835
block = working_block (block )
711
836
837
+ if add_reset : # True or 'asynchronous'
838
+ if block .get_wirevector_by_name ('rst' ) is not None :
839
+ raise PyrtlError ("Found a user-defined wire named 'rst'. Pass in "
840
+ "'add_reset=False' to use your existing reset logic." )
841
+
712
842
inputs , outputs , registers , wires , memories = _verilog_block_parts (block )
713
843
714
844
ver_name = _VerilogSanitizer ('_ver_out_tmp_' )
@@ -760,6 +890,8 @@ def default_value():
760
890
761
891
# Declare all block inputs as reg
762
892
print (' reg clk;' , file = dest_file )
893
+ if add_reset :
894
+ print (' reg rst;' , file = dest_file )
763
895
for w in name_sorted (inputs ):
764
896
print (' reg{:s} {:s};' .format (_verilog_vector_decl (w ), ver_name [w .name ]),
765
897
file = dest_file )
@@ -775,6 +907,8 @@ def default_value():
775
907
776
908
# Instantiate logic block
777
909
io_list = ['clk' ] + name_list (name_sorted (inputs )) + name_list (name_sorted (outputs ))
910
+ if add_reset :
911
+ io_list .insert (1 , 'rst' )
778
912
io_list_str = ['.{0:s}({0:s})' .format (w ) for w in io_list ]
779
913
print (' toplevel block({:s});\n ' .format (', ' .join (io_list_str )), file = dest_file )
780
914
@@ -792,6 +926,8 @@ def default_value():
792
926
793
927
# Initialize clk, and all the registers and memories
794
928
print (' clk = 0;' , file = dest_file )
929
+ if add_reset :
930
+ print (' rst = 0;' , file = dest_file )
795
931
for r in name_sorted (registers ):
796
932
print (' block.%s = %d;' % (ver_name [r .name ], init_regvalue (r )), file = dest_file )
797
933
for m in sorted (memories , key = lambda m : m .id ):
0 commit comments