25
25
26
26
27
27
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).
34
48
"""
35
49
import pyparsing
36
50
import six
@@ -285,7 +299,11 @@ def _name_sorted(wires, name_mapper=lambda w: w.name):
285
299
def output_to_firrtl (open_file , rom_blocks = None , block = None ):
286
300
""" Output the block as FIRRTL code to the output file.
287
301
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, ...].
289
307
"""
290
308
block = working_block (block )
291
309
@@ -463,9 +481,9 @@ def net_graph(block=None, split_state=False):
463
481
:param split_state: if True, split connections to/from a register update net; this
464
482
means that registers will be appear as source nodes of the network, and
465
483
'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.
467
485
468
- Graph has the following form:
486
+ The graph has the following form:
469
487
{ node1: { nodeA: edge1A, nodeB: edge1B},
470
488
node2: { nodeB: edge2B, nodeC: edge2C},
471
489
...
@@ -474,8 +492,8 @@ def net_graph(block=None, split_state=False):
474
492
aka: edge = graph[source][dest]
475
493
476
494
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.).
479
497
Note that inputs, consts, and outputs will be both "node" and "edge".
480
498
WireVectors that are not connected to any nets are not returned as part
481
499
of the graph.
@@ -521,7 +539,18 @@ def net_graph(block=None, split_state=False):
521
539
522
540
523
541
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
+ """
525
554
graph = net_graph (block , split_state )
526
555
node_index_map = {} # map node -> index
527
556
@@ -549,7 +578,7 @@ def _default_edge_namer(edge, is_to_splitmerge=False, extra_edge_info=None):
549
578
:param is_to_splitmerge: if the node to which the edge points
550
579
is a select or concat operation
551
580
: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)
553
582
:return: a function that can be called by graph namer function you pass
554
583
in to block_to_graphviz_string
555
584
"""
@@ -569,9 +598,22 @@ def _default_edge_namer(edge, is_to_splitmerge=False, extra_edge_info=None):
569
598
570
599
571
600
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
+ """
572
614
def label (v ):
573
615
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 ]) + ")"
575
617
return v
576
618
577
619
if isinstance (node , Const ):
@@ -619,14 +661,27 @@ def label(v):
619
661
raise PyrtlError ('no naming rule for "%s"' % str (node ))
620
662
621
663
622
- def graphviz_default_namer (
664
+ def _graphviz_default_namer (
623
665
thing ,
624
666
is_edge ,
625
667
is_to_splitmerge ,
626
668
split_state ,
627
669
node_namer = _default_node_namer ,
628
670
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
+ """
630
685
if is_edge :
631
686
return edge_namer (thing , is_to_splitmerge = is_to_splitmerge )
632
687
else :
@@ -636,13 +691,18 @@ def graphviz_default_namer(
636
691
def graphviz_detailed_namer (
637
692
extra_node_info = None ,
638
693
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.
640
696
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
642
698
(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
644
700
(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.
646
706
"""
647
707
648
708
def node_namer (node , split_state ):
@@ -652,19 +712,55 @@ def edge_namer(edge, is_to_splitmerge):
652
712
return _default_edge_namer (edge , is_to_splitmerge , extra_edge_info )
653
713
654
714
def namer (thing , is_edge , is_to_splitmerge , split_state ):
655
- return graphviz_default_namer (
715
+ return _graphviz_default_namer (
656
716
thing , is_edge , is_to_splitmerge , split_state ,
657
717
node_namer = node_namer , edge_namer = edge_namer )
658
718
return namer
659
719
660
720
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
+ """
663
730
print (block_to_graphviz_string (block , namer , split_state ), file = file )
664
731
665
732
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
+ """
668
764
graph = net_graph (block , split_state )
669
765
node_index_map = {} # map node -> index
670
766
@@ -698,12 +794,22 @@ def block_to_graphviz_string(block=None, namer=graphviz_default_namer, split_sta
698
794
699
795
700
796
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
+ """
702
803
print (block_to_svg (block , split_state ), file = file )
703
804
704
805
705
806
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
+ """
707
813
try :
708
814
from graphviz import Source
709
815
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):
712
818
713
819
714
820
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
+ """
716
828
717
829
from .simulation import SimulationTrace , _trace_sort_key
718
830
if not isinstance (simtrace , SimulationTrace ):
0 commit comments