|
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
|
@@ -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
|
@@ -427,6 +433,89 @@ def instantiate(subckt):
|
427 | 433 | #
|
428 | 434 |
|
429 | 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 | + |
430 | 519 | def output_to_verilog(dest_file, add_reset=True, block=None):
|
431 | 520 | """ A function to walk the block and output it in Verilog format to the open file.
|
432 | 521 |
|
|
0 commit comments