Skip to content

Commit 120d457

Browse files
authored
Merge pull request #337 from pllab/typos-and-docs
Add documentation to inputoutput functions.
2 parents 7efa86a + 705b808 commit 120d457

File tree

5 files changed

+235
-44
lines changed

5 files changed

+235
-44
lines changed

docs/export.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,15 @@ Importing Verilog
2222
.. autofunction:: pyrtl.inputoutput.input_from_blif
2323

2424

25-
Outputing for Visualization
25+
Outputting for Visualization
2626
---------------------------
2727

2828
.. autofunction:: pyrtl.inputoutput.output_to_trivialgraph
2929
.. autofunction:: pyrtl.inputoutput.output_to_graphviz
30+
.. autofunction:: pyrtl.inputoutput.graphviz_detailed_namer
3031
.. autofunction:: pyrtl.inputoutput.output_to_svg
3132
.. autofunction:: pyrtl.inputoutput.block_to_graphviz_string
3233
.. autofunction:: pyrtl.inputoutput.block_to_svg
3334
.. autofunction:: pyrtl.inputoutput.trace_to_html
35+
.. autofunction:: pyrtl.inputoutput.net_graph
3436

pyrtl/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,14 @@
8585
# input and output to file format routines
8686
from .inputoutput import input_from_blif
8787
from .inputoutput import output_to_trivialgraph
88+
from .inputoutput import graphviz_detailed_namer
8889
from .inputoutput import output_to_graphviz
8990
from .inputoutput import output_to_svg
9091
from .inputoutput import output_to_firrtl
9192
from .inputoutput import block_to_graphviz_string
9293
from .inputoutput import block_to_svg
9394
from .inputoutput import trace_to_html
95+
from .inputoutput import net_graph
9496

9597
# extraction to verilog and verilog testbench
9698
from .verilog import output_to_verilog

pyrtl/helperfuncs.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -138,16 +138,16 @@ def log2(integer_val):
138138

139139

140140
def truncate(wirevector_or_integer, bitwidth):
141-
""" Returns a wirevector or integer truncated to the specified bitwidth
141+
""" Returns a WireVector or integer truncated to the specified bitwidth
142142
143-
:param wirevector_or_integer: Either a wirevector or an integer to be truncated
143+
:param wirevector_or_integer: Either a WireVector or an integer to be truncated.
144144
:param bitwidth: The length to which the first argument should be truncated.
145-
:return: Returns a tuncated wirevector or integer as appropriate
145+
:return: A truncated WireVector or integer as appropriate.
146146
147147
This function truncates the most significant bits of the input, leaving a result
148148
that is only "bitwidth" bits wide. For integers this is performed with a simple
149-
bitmask of size "bitwidth". For wirevectors the function calls WireVector.truncate
150-
and returns a wirevector of the specified bitwidth.
149+
bitmask of size "bitwidth". For WireVectors the function calls 'WireVector.truncate'
150+
and returns a WireVector of the specified bitwidth.
151151
152152
Examples: ::
153153
@@ -177,26 +177,26 @@ def __exit__(self, *execinfo):
177177

178178

179179
def match_bitpattern(w, bitpattern, field_map=None):
180-
""" Returns a single-bit wirevector that is 1 if and only if 'w' matches the bitpattern,
180+
""" Returns a single-bit WireVector that is 1 if and only if 'w' matches the bitpattern,
181181
and a tuple containining the matched fields, if any. Compatible with the 'with' statement.
182182
183-
:param w: The wirevector to be compared to the bitpattern
183+
:param w: The WireVector to be compared to the bitpattern
184184
:param bitpattern: A string holding the pattern (of bits and wildcards) to match
185185
:param field_map: (optional) A map from single-character field name in the bitpattern
186186
to the desired name of field in the returned namedtuple. If given, all non-"1"/"0"/"?"
187187
characters in the bitpattern must be present in the map.
188-
:return: A tuple of 1-bit wirevector carrying the result of the comparison, followed
188+
:return: A tuple of 1-bit WireVector carrying the result of the comparison, followed
189189
by a named tuple containing the matched fields, if any.
190190
191-
This function will compare a multi-bit wirevector to a specified pattern of bits, where some
191+
This function will compare a multi-bit WireVector to a specified pattern of bits, where some
192192
of the pattern can be "wildcard" bits. If any of the "1" or "0" values specified in the
193-
bitpattern fail to match the wirevector during execution, a "0" will be produced, otherwise
193+
bitpattern fail to match the WireVector during execution, a "0" will be produced, otherwise
194194
the value carried on the wire will be "1". The wildcard characters can be any other
195195
alphanumeric character, with characters other than "?" having special functionality (see below).
196196
The string must have length equal to the wirevector specified, although whitespace and
197197
underscore characters will be ignored and can be used for pattern readability.
198198
199-
For all other characters besides "1", "0", or "?", a tuple of wirevectors will be returned as
199+
For all other characters besides "1", "0", or "?", a tuple of WireVectors will be returned as
200200
the second return value. Each character will be treated as the name of a field,
201201
and non-consecutive fields with the same name will be concatenated together, left-to-right, into
202202
a single field in the resultant tuple. For example, "01aa1?bbb11a" will match a string such
@@ -267,13 +267,13 @@ def field_name(name):
267267

268268

269269
def chop(w, *segment_widths):
270-
""" Returns a list of wirevectors each a slice of the original 'w'
270+
""" Returns a list of WireVectors each a slice of the original 'w'
271271
272-
:param w: The wirevector to be chopped up into segments
272+
:param w: The WireVector to be chopped up into segments
273273
:param segment_widths: Additional arguments are integers which are bitwidths
274-
:return: A list of wirevectors each with a proper segment width
274+
:return: A list of WireVectors each with a proper segment width
275275
276-
This function chops a wirevector into a set of smaller wirevectors of different
276+
This function chops a WireVector into a set of smaller WireVectors of different
277277
lengths. It is most useful when multiple "fields" are contained with a single
278278
wirevector, for example when breaking apart an instruction. For example, if
279279
you wish to break apart a 32-bit MIPS I-type (Immediate) instruction you know

pyrtl/inputoutput.py

Lines changed: 140 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,26 @@
2525

2626

2727
def input_from_blif(blif, block=None, merge_io_vectors=True, clock_name='clk'):
28-
""" Read an open blif file or string as input, updating the block appropriately.
29-
30-
Assumes the blif has been flattened and their is only a single module.
31-
Assumes that there is only one single shared clock and reset.
32-
Assumes that output is generated by Yosys with formals in a particular order.
33-
Ignores reset signal (which it assumes is input only to the flip flops).
28+
""" Read an open BLIF file or string as input, updating the block appropriately.
29+
30+
:param blif: An open BLIF file to read
31+
:param block: The block where the logic will be added
32+
:param merge_io_vectors: If True, Input/Output wires whose names differ only
33+
by a indexing subscript (e.g. 1-bit wires 'a[0]' and 'a[1]') will be combined
34+
into a single Input/Output (e.g. a 2-bit wire 'a').
35+
:param clock_name: The name of the clock (defaults to 'clk')
36+
37+
If merge_io_vectors is True, then given 1-bit Input wires 'a[0]' and 'a[1]', these
38+
wires will be combined into a single 2-bit Input wire 'a' that can be accessed
39+
by name 'a' in the block. Otherwise if merge_io_vectors is False, the original 1-bit
40+
wires will be Input wires of the block. This holds similarly for Outputs.
41+
42+
This assumes the following:
43+
* The BLIF has been flattened and there is only a single module
44+
* There is only one single shared clock and reset
45+
* Output is generated by Yosys with formals in a particular order
46+
47+
It currently ignores the reset signal (which it assumes is input only to the flip flops).
3448
"""
3549
import pyparsing
3650
import six
@@ -285,7 +299,11 @@ def _name_sorted(wires, name_mapper=lambda w: w.name):
285299
def output_to_firrtl(open_file, rom_blocks=None, block=None):
286300
""" Output the block as FIRRTL code to the output file.
287301
288-
If rom is intialized in PyRTL code, you can pass in the rom_blocks as a list [rom1, rom2, ...].
302+
:param open_file: File to write to
303+
:param rom_blocks: List of ROM blocks to be initialized
304+
:param block: Block to use (defaults to working block)
305+
306+
If ROM is initialized in PyRTL code, you can pass in the rom_blocks as a list [rom1, rom2, ...].
289307
"""
290308
block = working_block(block)
291309

@@ -463,9 +481,9 @@ def net_graph(block=None, split_state=False):
463481
:param split_state: if True, split connections to/from a register update net; this
464482
means that registers will be appear as source nodes of the network, and
465483
'r' nets (i.e. the logic for setting a register's next value) will
466-
be treated as sink nodes of the network
484+
be treated as sink nodes of the network.
467485
468-
Graph has the following form:
486+
The graph has the following form:
469487
{ node1: { nodeA: edge1A, nodeB: edge1B},
470488
node2: { nodeB: edge2B, nodeC: edge2C},
471489
...
@@ -474,8 +492,8 @@ def net_graph(block=None, split_state=False):
474492
aka: edge = graph[source][dest]
475493
476494
Each node can be either a logic net or a WireVector (e.g. an Input, an Output, a
477-
Const or even an undriven WireVector (which acts as a source or sink in the network)
478-
Each edge is a WireVector or derived type (Input, Output, Register, etc.)
495+
Const or even an undriven WireVector (which acts as a source or sink in the network).
496+
Each edge is a WireVector or derived type (Input, Output, Register, etc.).
479497
Note that inputs, consts, and outputs will be both "node" and "edge".
480498
WireVectors that are not connected to any nets are not returned as part
481499
of the graph.
@@ -521,7 +539,18 @@ def net_graph(block=None, split_state=False):
521539

522540

523541
def output_to_trivialgraph(file, namer=_trivialgraph_default_namer, block=None, split_state=False):
524-
""" Walk the block and output it in trivial graph format to the open file. """
542+
""" Walk the block and output it in trivial graph format to the open file.
543+
544+
:param file: Open file to write to
545+
:param namer: A function that takes in an object (a wire or logicnet) as the first argument and
546+
a boolean `is_edge` as the second that is set True if the object is a wire, and returns
547+
a string representing that object.
548+
:param block: Block to use (defaults to current working block)
549+
:param split_state: if True, split connections to/from a register update net; this
550+
means that registers will be appear as source nodes of the network, and
551+
'r' nets (i.e. the logic for setting a register's next value) will
552+
be treated as sink nodes of the network.
553+
"""
525554
graph = net_graph(block, split_state)
526555
node_index_map = {} # map node -> index
527556

@@ -549,7 +578,7 @@ def _default_edge_namer(edge, is_to_splitmerge=False, extra_edge_info=None):
549578
:param is_to_splitmerge: if the node to which the edge points
550579
is a select or concat operation
551580
:param extra_edge_info: a map from edge to any additional data you want
552-
to print associated with it (e.g. timing data).
581+
to print associated with it (e.g. timing data)
553582
:return: a function that can be called by graph namer function you pass
554583
in to block_to_graphviz_string
555584
"""
@@ -569,9 +598,22 @@ def _default_edge_namer(edge, is_to_splitmerge=False, extra_edge_info=None):
569598

570599

571600
def _default_node_namer(node, split_state=False, extra_node_info=None):
601+
"""
602+
A function for naming a node for use in the graphviz graph.
603+
604+
:param node: the node (i.e. WireVector or deriving class, or a logic net)
605+
:param split_state: if True, split connections to/from a register update net; this
606+
means that registers will be appear as source nodes of the network, and
607+
'r' nets (i.e. the logic for setting a register's next value) will
608+
be treated as sink nodes of the network.
609+
:param extra_node_info: a map from node to any additional data you want
610+
to print associated with it (e.g. delay data)
611+
:return: a function that can be called by graph namer function you pass
612+
in to block_to_graphviz_string
613+
"""
572614
def label(v):
573615
if extra_node_info and node in extra_node_info:
574-
v = v + "(" + str(extra_node_info[node]) + ")"
616+
v = v + " (" + str(extra_node_info[node]) + ")"
575617
return v
576618

577619
if isinstance(node, Const):
@@ -619,14 +661,27 @@ def label(v):
619661
raise PyrtlError('no naming rule for "%s"' % str(node))
620662

621663

622-
def graphviz_default_namer(
664+
def _graphviz_default_namer(
623665
thing,
624666
is_edge,
625667
is_to_splitmerge,
626668
split_state,
627669
node_namer=_default_node_namer,
628670
edge_namer=_default_edge_namer):
629-
""" Returns a "good" graphviz label for thing. """
671+
""" Returns a "good" Graphviz label for thing.
672+
673+
:param thing: The edge (wire) or node (logic net or Input/Output/Const) to name
674+
:param is_edge: True if thing is an edge
675+
:param is_to_splitmerge: if the node to which the edge points
676+
is a select or concat operation
677+
:param split_state: If True, visually split the connections to/from a register update net.
678+
:param node_namer: A function mapping a node to a label; one of its arguments
679+
is a dict mapping nodes to nodes to additional user-supplied information.
680+
:param edge_namer: A function mapping an edge to a label; one of its arguments
681+
is a dict mapping nodes to nodes to additional user-supplied information.
682+
:return: A function that knows how to label each element in the graph, which
683+
can be passed to 'output_to_graphviz' or 'block_to_graphviz_string'
684+
"""
630685
if is_edge:
631686
return edge_namer(thing, is_to_splitmerge=is_to_splitmerge)
632687
else:
@@ -636,13 +691,18 @@ def graphviz_default_namer(
636691
def graphviz_detailed_namer(
637692
extra_node_info=None,
638693
extra_edge_info=None):
639-
""" Returns a detailed namer that prints extra information about nodes/edges in the given maps.
694+
""" Returns a detailed Graphviz namer that prints extra information
695+
about nodes/edges in the given maps.
640696
641-
:param extra_node_info: A map from node to some object about that node
697+
:param extra_node_info: A dict from node to some object about that node
642698
(its string representation will be printed next to the node's label)
643-
:param extra_edge_info: A map from edge to some object about that edge
699+
:param extra_edge_info: A dict from edge to some object about that edge
644700
(its string representation will be printed next to the edge's label)
645-
:return: a function that can be used as the namer function for block_to_graphviz_string
701+
:return: A function that knows how to label each element in the graph, which
702+
can be passed to 'output_to_graphviz' or 'block_to_graphviz_string'
703+
704+
If both dict arguments are None, the returned namer behaves identically
705+
to the default Graphviz namer.
646706
"""
647707

648708
def node_namer(node, split_state):
@@ -652,19 +712,55 @@ def edge_namer(edge, is_to_splitmerge):
652712
return _default_edge_namer(edge, is_to_splitmerge, extra_edge_info)
653713

654714
def namer(thing, is_edge, is_to_splitmerge, split_state):
655-
return graphviz_default_namer(
715+
return _graphviz_default_namer(
656716
thing, is_edge, is_to_splitmerge, split_state,
657717
node_namer=node_namer, edge_namer=edge_namer)
658718
return namer
659719

660720

661-
def output_to_graphviz(file, namer=graphviz_default_namer, block=None, split_state=True):
662-
""" Walk the block and output it in graphviz format to the open file. """
721+
def output_to_graphviz(file, block=None, namer=_graphviz_default_namer, split_state=True):
722+
""" Walk the block and output it in Graphviz format to the open file.
723+
724+
:param file: Open file to write to
725+
:param block: Block to use (defaults to current working block)
726+
:param namer: Function used to label each edge and node; see 'block_to_graphviz_string'
727+
for more information.
728+
:param split_state: If True, visually split the connections to/from a register update net.
729+
"""
663730
print(block_to_graphviz_string(block, namer, split_state), file=file)
664731

665732

666-
def block_to_graphviz_string(block=None, namer=graphviz_default_namer, split_state=True):
667-
""" Return a graphviz string for the block. """
733+
def block_to_graphviz_string(block=None, namer=_graphviz_default_namer, split_state=True):
734+
""" Return a Graphviz string for the block.
735+
736+
:param namer: A function mapping graph objects (wires/logic nets) to labels.
737+
If you want a more detailed namer, pass in a call to `graphviz_detailed_namer` (see below).
738+
:param block: Block to use (defaults to current working block)
739+
:param split_state: If True, split connections to/from a register update net; this
740+
means that registers will be appear as source nodes of the network, and
741+
'r' nets (i.e. the logic for setting a register's next value) will
742+
be treated as sink nodes of the network.
743+
744+
The normal namer function will label user-named wires with their names and label the nodes
745+
(logic nets or Input/Output/Const terminals) with their operator symbol or name/value,
746+
respectively. If custom information about each node in the graph is desired, you can pass
747+
in a custom namer function which must have the same signature as the default namer,
748+
`_graphviz_default_namer`. However, we recommend you instead pass in a call to
749+
`graphviz_detailed_namer`, supplying it with your own dicts mapping wires and nodes to labels.
750+
For any wire/node found in these maps, that additional information will be printed in
751+
parentheses alongside the node in the graphviz graph.
752+
753+
For example, if you wanted to print the delay of each wire and the fanout of each
754+
gate, you could pass in two maps to the `graphviz_detailed_namer` call, which returns a namer
755+
function that can subsequently be passed to 'output_to_graphviz' or
756+
'block_to_graphviz_string'. ::
757+
758+
node_fanout = {n: "Fanout: %d" % my_fanout_func(n) for n in working_block().logic}
759+
wire_delay = {w: "Delay: %.2f" % my_delay_func(w) for w in working_block().wirevector_set}
760+
761+
with open("out.gv", "w") as f:
762+
output_to_graphviz(f, namer=graphviz_detailed_namer(node_fanout, wire_delay))
763+
"""
668764
graph = net_graph(block, split_state)
669765
node_index_map = {} # map node -> index
670766

@@ -698,12 +794,22 @@ def block_to_graphviz_string(block=None, namer=graphviz_default_namer, split_sta
698794

699795

700796
def output_to_svg(file, block=None, split_state=True):
701-
""" Output the block as an SVG to the open file. """
797+
""" Output the block as an SVG to the open file.
798+
799+
:param file: Open file to write to
800+
:param block: Block to use (defaults to current working block)
801+
:param split_state: If True, visually split the connections to/from a register update net.
802+
"""
702803
print(block_to_svg(block, split_state), file=file)
703804

704805

705806
def block_to_svg(block=None, split_state=True):
706-
""" Return an SVG for the block. """
807+
""" Return an SVG for the block.
808+
809+
:param block: Block to use (defaults to current working block)
810+
:param split_state: If True, visually split the connections to/from a register update net.
811+
:return: The SVG representation of the block
812+
"""
707813
try:
708814
from graphviz import Source
709815
return Source(block_to_graphviz_string(block, split_state=split_state))._repr_svg_()
@@ -712,7 +818,13 @@ def block_to_svg(block=None, split_state=True):
712818

713819

714820
def trace_to_html(simtrace, trace_list=None, sortkey=None):
715-
""" Return a HTML block showing the trace. """
821+
""" Return a HTML block showing the trace.
822+
823+
:param simtrace: A SimulationTrace object
824+
:param trace_list: (optional) A list of wires to display
825+
:param sortkey: (optional) The key with which to sort the trace_list
826+
:return: An HTML block showing the trace
827+
"""
716828

717829
from .simulation import SimulationTrace, _trace_sort_key
718830
if not isinstance(simtrace, SimulationTrace):

0 commit comments

Comments
 (0)