Skip to content

Commit a2ae4c5

Browse files
committed
Clean up analysis and import/export documentation:
* Fix some type annotations * Fix some formatting * Add more links
1 parent d21b02c commit a2ae4c5

File tree

5 files changed

+308
-295
lines changed

5 files changed

+308
-295
lines changed

pyrtl/analysis.py

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -331,31 +331,31 @@ def print_critical_paths(critical_paths):
331331
# | \__/ .__/ | .__/
332332
#
333333

334-
def yosys_area_delay(library, abc_cmd=None, leave_in_dir=None, block=None):
335-
"""Synthesize with `Yosys <https://yosyshq.net/yosys/>`_ and return
336-
estimate of area and delay.
334+
def yosys_area_delay(library: str, abc_cmd: str = None, leave_in_dir: str = None,
335+
block: Block = None) -> tuple[float, float]:
336+
"""Synthesize with `Yosys <https://yosyshq.net/yosys/>`_ and return estimate of area
337+
and delay.
337338
338-
:param library: stdcell library file to target in liberty format
339-
:param abc_cmd: string of commands for :program:`yosys` to pass to
340-
:program:`abc` for synthesis
341-
:param dir: the directory where temporary files should be left
342-
:param block: PyRTL block to analyze
343-
:return: a tuple of numbers: area, delay
344-
345-
If `dir` is specified, that directory will be used to create any temporary
346-
files, and the resulting files will be left behind there (which can be
339+
If ``leave_in_dir`` is specified, that directory will be used to create any
340+
temporary files, and the resulting files will be left behind there (which can be
347341
useful for manual exploration or debugging)
348342
349-
The area and delay are returned in units as defined by the stdcell
350-
library. In the standard vsc 130nm library, the area is in a number of
351-
"tracks", each of which is about 1.74 square um (see area estimation
352-
for more details) and the delay is in ps.
343+
The area and delay are returned in units as defined by the stdcell library. In the
344+
standard vsc 130nm library, the area is in a number of "tracks", each of which is
345+
about 1.74 square um (see area estimation for more details) and the delay is in ps.
353346
354347
http://www.vlsitechnology.org/html/vsc_description.html
355348
356-
May raise :class:`PyrtlError` if :program:`yosys` is not configured correctly, and
357-
:class:`PyrtlInternalError` if the call to :program:`yosys` was not successful
349+
:param library: stdcell library file to target in liberty format
350+
:param abc_cmd: string of commands for :program:`yosys` to pass to :program:`abc`
351+
for synthesis
352+
:param leave_in_dir: the directory where temporary files should be left
353+
:param block: PyRTL block to analyze. Defaults to the :ref:`working_block`.
354+
355+
:raise PyrtlError: If :program:`yosys` is not configured correctly.
356+
:raise PyrtlInternalError: If the call to :program:`yosys` was not successful
358357
358+
:return: a tuple of numbers: area, delay
359359
"""
360360

361361
if abc_cmd is None:
@@ -546,19 +546,19 @@ def dfs(w, curr_path):
546546
return PathsResult(all_paths)
547547

548548

549-
def distance(src: WireVector, dst: WireVector, f: Callable[[LogicNet], int],
550-
block: Block = None) -> dict[list[LogicNet], int]:
551-
""" Calculate the 'distance' along each path from `src` to `dst` according to `f`
549+
def distance(src: WireVector, dst: WireVector, f: Callable[list[LogicNet], int],
550+
block: Block = None) -> dict[tuple[LogicNet], int]:
551+
"""Calculate the distance along each path from ``src`` to ``dst`` according to ``f``
552+
553+
This calls the given function ``f`` on each net in a path, summing the result.
552554
553555
:param src: wire to start from
554556
:param dst: wire to end on
555-
:param f: function from a net to number,
556-
representing the 'value' of a net that you want to sum
557-
across all nets in the path
557+
:param f: function from a net to number, representing the 'value' of a net that you
558+
want to sum across all nets in the path
558559
:param block: block to use (defaults to :ref:`working_block`)
559-
:return: a map from each path (a tuple) to its calculated distance
560560
561-
This calls the given function `f` on each net in a path, summing the result.
561+
:return: a map from each path (a tuple) to its calculated distance
562562
"""
563563
ps = paths(src, dst, block=block)
564564
ps = ps[src][dst]

pyrtl/importexport.py

Lines changed: 88 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import sys
1717
import functools
1818
import operator
19-
import typing
19+
from typing import Union, TYPE_CHECKING
2020

2121
from pyrtl.pyrtlexceptions import PyrtlError, PyrtlInternalError
2222
from pyrtl.core import working_block, _NameSanitizer, Block
@@ -25,6 +25,9 @@
2525
from pyrtl.memory import RomBlock
2626
from pyrtl.passes import two_way_concat, one_bit_selects
2727

28+
if TYPE_CHECKING:
29+
from pyrtl.simulation import SimulationTrace
30+
2831

2932
def _natural_sort_key(key):
3033
""" Convert the key into a form such that it will be sorted naturally,
@@ -103,31 +106,33 @@ def input_from_blif(
103106
clock_name: str = 'clk', top_model: str = None):
104107
"""Read an open BLIF file or string as input, updating the block appropriately.
105108
109+
If ``merge_io_vectors`` is ``True``, then given 1-bit :class:`Input` wires ``a[0]``
110+
and ``a[1]``, these wires will be combined into a single 2-bit :class:`Input` wire
111+
``a`` that can be accessed by name ``a`` in the block. Otherwise if
112+
``merge_io_vectors`` is ``False``, the original 1-bit wires will be :class:`Input`
113+
wires of the block. This holds similarly for :class:`Output`.
114+
115+
``input_from_blif`` assumes the following:
116+
117+
- There is only one single shared clock and reset
118+
119+
- Output is generated by Yosys with formals in a particular order
120+
121+
``input_from_blif`` currently supports multi-module (unflattened) BLIF, though we
122+
recommend importing a flattened BLIF with a single module when possible. It
123+
currently ignores the reset signal (which it assumes is input only to the flip
124+
flops).
125+
106126
:param blif: An open BLIF file to read.
107127
:param block: The block where the logic will be added. Defaults to the
108128
:ref:`working_block`.
109-
:param merge_io_vectors: If True, :class:`Input`/:class:`Output` wires whose
129+
:param merge_io_vectors: If ``True``, :class:`Input`/:class:`Output` wires whose
110130
names differ only by a indexing subscript (e.g. 1-bit wires ``a[0]`` and
111-
``a[1]``) will be combined into a single :class:`Input`/:class:`Output`
112-
(e.g. a 2-bit wire ``a``).
113-
:param clock_name: The name of the clock (defaults to ``clk``). :param top_model:
114-
name of top-level model to instantiate; if None, defaults to first model listed
115-
in the BLIF.
116-
117-
If ``merge_io_vectors`` is ``True``, then given 1-bit :class:`Input` wires
118-
``a[0]`` and ``a[1]``, these wires will be combined into a single 2-bit
119-
:class:`Input` wire ``a`` that can be accessed by name ``a`` in the block.
120-
Otherwise if ``merge_io_vectors`` is ``False``, the original 1-bit wires will be
121-
:class:`Input` wires of the block. This holds similarly for :class:`Output`.
122-
123-
This assumes the following:
124-
* There is only one single shared clock and reset
125-
* Output is generated by Yosys with formals in a particular order
126-
127-
It currently supports multi-module (unflattened) BLIF, though we recommend importing
128-
a flattened BLIF with a single module when possible. It currently ignores the reset
129-
signal (which it assumes is input only to the flip flops).
130-
131+
``a[1]``) will be combined into a single :class:`Input`/:class:`Output` (e.g. a
132+
2-bit wire ``a``).
133+
:param clock_name: The name of the clock (defaults to ``clk``).
134+
:param top_model: name of top-level model to instantiate; if ``None``, defaults to
135+
first model listed in the BLIF.
131136
"""
132137
import pyparsing
133138
from pyparsing import (Word, Literal, OneOrMore, ZeroOrMore,
@@ -536,23 +541,23 @@ def input_from_verilog(
536541
"""Read an open Verilog file or string as input via `Yosys
537542
<https://github.com/YosysHQ/yosys>`_ conversion, updating the block.
538543
539-
:param verilog: An open Verilog file to read.
540-
:param clock_name: The name of the clock (defaults to 'clk').
541-
:param toplevel: Name of top-level module to instantiate; if None, defaults to first
542-
model defined in the Verilog file.
543-
:param leave_in_dir: If True, save the intermediate BLIF file created in the given
544-
directory.
545-
:param block: The block where the logic will be added. Defaults to the
546-
:ref:`working_block`.
544+
This function is essentially a wrapper for :func:`input_from_blif`, with the added
545+
convenience of turning the Verilog into BLIF for import for you. This function
546+
passes a set of commands to Yosys as a script that normally produces BLIF files that
547+
can be successfully imported into PyRTL via :func:`input_from_blif`.
547548
548-
Note: This function is essentially a wrapper for :func:`input_from_blif`, with
549-
the added convenience of turning the Verilog into BLIF for import for you. This
550-
function passes a set of commands to Yosys as a script that normally produces BLIF
551-
files that can be successuflly imported into PyRTL via :func:`input_from_blif`. If the
552-
Yosys conversion fails here, we recommend you create your own custom Yosys script to
553-
try and produce BLIF yourself. Then you can import BLIF directly via
549+
If the Yosys conversion fails, we recommend you create your own custom Yosys script
550+
to try and produce BLIF yourself. Then you can import BLIF directly via
554551
:func:`input_from_blif`.
555552
553+
:param verilog: An open Verilog file to read.
554+
:param clock_name: The name of the clock (defaults to ``"clk"``).
555+
:param toplevel: Name of top-level module to instantiate; if ``None``, defaults to
556+
first model defined in the Verilog file.
557+
:param leave_in_dir: If ``True``, save the intermediate BLIF file created in the
558+
given directory.
559+
:param block: The block where the logic will be added. Defaults to the
560+
:ref:`working_block`.
556561
"""
557562

558563
# Dev Notes:
@@ -619,9 +624,16 @@ def input_from_verilog(
619624
os.remove(tmp_blif_path)
620625

621626

622-
def output_to_verilog(dest_file, add_reset: typing.Union[bool, str] = True,
627+
def output_to_verilog(dest_file, add_reset: Union[bool, str] = True,
623628
block: Block = None, initialize_registers: bool = False):
624-
"""A function to walk the block and output it in Verilog format to the open file.
629+
"""A function to walk the ``block`` and output it in Verilog format to the open
630+
file.
631+
632+
The Verilog module will be named ``toplevel``, with a clock input named ``clk``.
633+
634+
When possible, wires keep their names in the Verilog output. Wire names that do not
635+
satisfy Verilog's naming requirements, and wires that conflict with Verilog keywords
636+
are given new temporary names in the Verilog output.
625637
626638
:param dest_file: Open file where the Verilog output will be written.
627639
:param add_reset: If reset logic should be added. Allowable options are: ``False``
@@ -633,13 +645,6 @@ def output_to_verilog(dest_file, add_reset: typing.Union[bool, str] = True,
633645
When this argument is ``True``, a register like ``Register(name='foo',
634646
bitwidth=8, reset_value=4)`` generates Verilog like ``reg[7:0] foo = 8'd4;``.
635647
:param block: Block to be walked and exported. Defaults to the :ref:`working_block`.
636-
637-
The Verilog module will be named ``toplevel``, with a clock input named ``clk``.
638-
639-
When possible, wires keep their names in the Verilog output. Wire names that do not
640-
satisfy Verilog's naming requirements, and wires that conflict with Verilog keywords
641-
are given new temporary names in the Verilog output.
642-
643648
"""
644649

645650
if not isinstance(add_reset, bool):
@@ -906,39 +911,12 @@ def _to_verilog_footer(file):
906911

907912

908913
def output_verilog_testbench(
909-
dest_file, simulation_trace=None, toplevel_include: str = None,
910-
vcd: str = "waveform.vcd", cmd: str = None,
911-
add_reset: typing.Union[bool, str] = True, block: Block = None):
912-
"""Output a Verilog testbench for the block/inputs used in the simulation
913-
trace.
914-
915-
:param dest_file: an open file to which the test bench will be printed.
916-
:param SimulationTrace simulation_trace: a simulation trace from which the inputs
917-
will be extracted for inclusion in the test bench. The test bench generated will
918-
just replay the inputs played to the simulation cycle by cycle. The default
919-
values for all registers and memories will be based on the trace, otherwise they
920-
will be initialized to 0.
921-
:param toplevel_include: name of the file containing the toplevel module this
922-
testbench is testing. If not ``None``, an `include` directive will be added to
923-
the top.
924-
:param vcd: By default the testbench generator will include a command in the
925-
testbench to write the output of the testbench execution to a ``.vcd`` file (via
926-
`$dumpfile`), and this parameter is the name of the file to write. If ``None``
927-
is specified, then no `dumpfile` will be used.
928-
:param cmd: The string passed as ``cmd`` will be copied verbatim into the testbench
929-
just before the end of each cycle. This is useful for doing things like printing
930-
specific values during testbench evaluation. For example, ``cmd='$display("%d",
931-
out);'`` will instruct the testbench to print the value of `out` every cycle,
932-
which can be compared with a reference.
933-
:param add_reset: If reset logic should be added. Allowable options are: ``False``
934-
(meaning no reset logic is added), ``True`` (default, for adding synchronous
935-
reset logic), and ``'asynchronous'`` (for adding asynchronous reset logic). The
936-
value passed in here should match the argument passed to
937-
:func:`output_to_verilog`.
914+
dest_file, simulation_trace: SimulationTrace = None,
915+
toplevel_include: str = None, vcd: str = "waveform.vcd", cmd: str = None,
916+
add_reset: Union[bool, str] = True, block: Block = None):
917+
"""Output a Verilog testbench for the block/inputs used in the simulation trace.
938918
939-
:param block: Block containing design to test. Defaults to the :ref:`working_block`.
940-
941-
If ``add_reset`` is not False, a ``rst`` input wire is added to the instantiated
919+
If ``add_reset`` is ``True``, a ``rst`` input wire is added to the instantiated
942920
``toplevel`` module. The ``rst`` wire will be held low in the testbench, because
943921
initialization here occurs via the ``initial`` block. ``add_reset`` is provided for
944922
consistency with :func:`output_to_verilog`.
@@ -949,17 +927,44 @@ def output_verilog_testbench(
949927
950928
The test bench does not return any values.
951929
952-
Example 1 (writing testbench to a string)::
930+
Example 1, writing testbench to a string::
953931
954932
with io.StringIO() as tbfile:
955933
pyrtl.output_verilog_testbench(dest_file=tbfile, simulation_trace=sim_trace)
956934
957-
Example 2 (testbench in same file as verilog)::
935+
Example 2, testbench in same file as Verilog::
958936
959937
with open('hardware.v', 'w') as fp:
960938
output_to_verilog(fp)
961939
output_verilog_testbench(fp, sim.tracer, vcd=None, cmd='$display("%d", out);')
962940
941+
:param dest_file: An open file to which the test bench will be printed.
942+
:param simulation_trace: A trace from which the inputs will be extracted for
943+
inclusion in the test bench. This is typically :attr:`Simulation.tracer`. The
944+
generated test bench will replay the :class:`Simulation`'s
945+
:class:`Inputs<Input>` cycle by cycle. The default values for all registers and
946+
memories will be based on the trace, otherwise they will be initialized to 0.
947+
:param toplevel_include: Name of the file containing the ``toplevel`` module this
948+
testbench is testing. If not ``None``, an ``include`` directive will be added
949+
for this file.
950+
:param vcd: By default, the testbench generator will generate a command to write the
951+
output of the testbench execution to a ``.vcd`` file, via ``$dumpfile``, and
952+
``vcd`` is the name of the file to write. If ``None``, then no ``dumpfile`` will
953+
be used.
954+
:param cmd: The string passed as ``cmd`` will be copied verbatim into the testbench
955+
just before the end of each cycle. This is useful for doing things like printing
956+
specific values during testbench evaluation. For example::
957+
958+
cmd='$display("%d", out);'
959+
960+
will instruct the testbench to print the value of ``out`` every cycle.
961+
962+
:param add_reset: If reset logic should be added. Allowable options are: ``False``
963+
(meaning no reset logic is added), ``True`` (default, for adding synchronous
964+
reset logic), and ``'asynchronous'`` (for adding asynchronous reset logic). The
965+
value passed in here should match the argument passed to
966+
:func:`output_to_verilog`.
967+
:param block: Block containing design to test. Defaults to the :ref:`working_block`.
963968
"""
964969
if not isinstance(add_reset, bool):
965970
if add_reset != 'asynchronous':
@@ -1103,13 +1108,12 @@ def default_value():
11031108
def output_to_firrtl(open_file, rom_blocks: list[RomBlock] = None, block: Block = None):
11041109
"""Output the block as FIRRTL code to the output file.
11051110
1106-
:param open_file: File to write to.
1107-
:param rom_blocks: List of ROM blocks to be initialized.
1108-
:param block: Block to use (defaults to :ref:`working_block`).
1109-
1110-
If ROM is initialized in PyRTL code, you can pass in the ``rom_blocks`` as a
1111+
If ROM is initialized in PyRTL code, you can pass in the :class:`RomBlocks` as a
11111112
list ``[rom1, rom2, ...]``.
11121113
1114+
:param open_file: File to write to.
1115+
:param rom_blocks: List of :class:`RomBlocks<RomBlock>` to be initialized.
1116+
:param block: Block to use (defaults to :ref:`working_block`).
11131117
"""
11141118
block = working_block(block)
11151119

0 commit comments

Comments
 (0)