Exporting and Importing Designs#

Exporting Hardware Designs#

pyrtl.importexport.output_to_verilog(dest_file, add_reset=True, block=None)[source]#

A function to walk the block and output it in Verilog format to the open file.

Parameters:
  • dest_file – Open file where the Verilog output will be written

  • add_reset (Union[bool, str]) – If reset logic should be added. Allowable options are: False (meaning no reset logic is added), True (default, for adding synchronous reset logic), and asynchronous (for adding asynchronous reset logic).

  • block – Block to be walked and exported

The registers will be set to their reset_value, if specified, otherwise 0.

pyrtl.importexport.output_to_firrtl(open_file, rom_blocks=None, block=None)[source]#

Output the block as FIRRTL code to the output file.

Parameters:
  • open_file – File to write to

  • rom_blocks – List of ROM blocks to be initialized

  • block – Block to use (defaults to working block)

If ROM is initialized in PyRTL code, you can pass in the rom_blocks as a list [rom1, rom2, …].

Exporting Testbenches#

pyrtl.importexport.output_verilog_testbench(dest_file, simulation_trace=None, toplevel_include=None, vcd='waveform.vcd', cmd=None, add_reset=True, block=None)[source]#

Output a Verilog testbench for the block/inputs used in the simulation trace.

Parameters:
  • dest_file – an open file to which the test bench will be printed.

  • simulation_trace (SimulationTrace) – a simulation trace from which the inputs will be extracted for inclusion in the test bench. The test bench generated will just replay the inputs played to the simulation cycle by cycle. The default values for all registers and memories will be based on the trace, otherwise they will be initialized to 0.

  • toplevel_include (str) – name of the file containing the toplevel module this testbench is testing. If not None, an include directive will be added to the top.

  • vcd (str) – By default the testbench generator will include a command in the testbench to write the output of the testbench execution to a .vcd file (via $dumpfile), and this parameter is the string of the name of the file to use. If None is specified instead, then no dumpfile will be used.

  • cmd (str) – The string passed as cmd will be copied verbatim into the testbench just before the end of each cycle. This is useful for doing things like printing specific values out during testbench evaluation (e.g. cmd='$display("%d", out);' will instruct the testbench to print the value of out every cycle which can then be compared easy with a reference).

  • add_reset (Union[bool, str]) – If reset logic should be added. Allowable options are: False (meaning no reset logic is added), True (default, for adding synchronous reset logic), and asynchronous (for adding asynchronous reset logic). The value passed in here should match the argument passed to output_to_verilog().

  • block (Block) – Block containing design to test.

If add_reset is not False, a rst wire is added and will passed as an input to the instantiated toplevel module. The rst wire will be held low in the testbench, because initialization here occurs via the initial block. It is provided for consistency with output_to_verilog().

The test bench does not return any values.

Example 1 (writing testbench to a string):

with io.StringIO() as tbfile:
    pyrtl.output_verilog_testbench(dest_file=tbfile, simulation_trace=sim_trace)

Example 2 (testbench in same file as verilog):

with open('hardware.v', 'w') as fp:
    output_to_verilog(fp)
    output_verilog_testbench(fp, sim.tracer, vcd=None, cmd='$display("%d", out);')

Importing Verilog#

pyrtl.importexport.input_from_blif(blif, block=None, merge_io_vectors=True, clock_name='clk', top_model=None)[source]#

Read an open BLIF file or string as input, updating the block appropriately.

Parameters:
  • blif – An open BLIF file to read

  • block (Block) – The block where the logic will be added

  • merge_io_vectors (bool) – If True, Input/Output wires whose names differ only by a indexing subscript (e.g. 1-bit wires a[0] and a[1]) will be combined into a single Input/Output (e.g. a 2-bit wire a).

  • clock_name (str) – The name of the clock (defaults to clk)

  • top_model – name of top-level model to instantiate; if None, defaults to first model listed in the BLIF

If merge_io_vectors is True, then given 1-bit Input wires a[0] and a[1], these wires will be combined into a single 2-bit Input wire a that can be accessed by name a in the block. Otherwise if merge_io_vectors is False, the original 1-bit wires will be Input wires of the block. This holds similarly for Outputs.

This assumes the following:

  • There is only one single shared clock and reset

  • Output is generated by Yosys with formals in a particular order

It currently supports multi-module (unflattened) BLIF, though we recommend importing a flattened BLIF with a single module when possible. It currently ignores the reset signal (which it assumes is input only to the flip flops).

Outputting for Visualization#

pyrtl.visualization.output_to_trivialgraph(file, namer=<function _trivialgraph_default_namer>, block=None, split_state=False)[source]#

Walk the block and output it in trivial graph format to the open file.

Parameters:
  • file – Open file to write to

  • namer – A function that takes in an object (a wire or LogicNet) as the first argument and a boolean is_edge as the second that is set True if the object is a wire, and returns a string representing that object.

  • block (Block) – Block to use (defaults to current working block)

  • split_state (bool) – if True, split connections to/from a register update net; this means that registers will be appear as source nodes of the network, and r nets (i.e. the logic for setting a register’s next value) will be treated as sink nodes of the network.

pyrtl.visualization.output_to_graphviz(file, block=None, namer=<function _graphviz_default_namer>, split_state=True, maintain_arg_order=False)[source]#

Walk the block and output it in Graphviz format to the open file.

Parameters:
  • file – Open file to write to

  • block (Block) – Block to use (defaults to current working block)

  • namer – Function used to label each edge and node; see block_to_graphviz_string() for more information.

  • split_state (bool) – If True, visually split the connections to/from a register update net.

  • maintain_arg_order (bool) – If True, will add ordering constraints so that that incoming edges are ordered left-to-right for nets where argument order matters (e.g. <). Keeping this as False results in a cleaner, though less visually precise, graphical output.

The file written by the this function should be a directed graph in the format expected by the Graphviz package, specifically in the dot format. Once Graphviz is installed, the resulting graph file can be rendered to a .png file with:

dot -Tps output.dot > output.ps
pyrtl.visualization.graphviz_detailed_namer(extra_node_info=None, extra_edge_info=None)[source]#

Returns a detailed Graphviz namer that prints extra information about nodes/edges in the given maps.

Parameters:
  • extra_node_info – A dict from node to some object about that node (its string representation will be printed next to the node’s label)

  • extra_edge_info – A dict from edge to some object about that edge (its string representation will be printed next to the edge’s label)

Returns:

A function that knows how to label each element in the graph, which can be passed to output_to_graphviz() or block_to_graphviz_string()

If both dict arguments are None, the returned namer behaves identically to the default Graphviz namer.

pyrtl.visualization.output_to_svg(file, block=None, split_state=True)[source]#

Output the block as an SVG to the open file.

Parameters:
  • file – Open file to write to

  • block (Block) – Block to use (defaults to current working block)

  • split_state (bool) – If True, visually split the connections to/from a register update net.

pyrtl.visualization.block_to_graphviz_string(block=None, namer=<function _graphviz_default_namer>, split_state=True, maintain_arg_order=False)[source]#

Return a Graphviz string for the block.

Parameters:
  • namer – A function mapping graph objects (wires/logic nets) to labels. If you want a more detailed namer, pass in a call to graphviz_detailed_namer() (see below).

  • block (Block) – Block to use (defaults to current working block)

  • split_state (bool) – If True, split connections to/from a register update net; this means that registers will be appear as source nodes of the network, and r nets (i.e. the logic for setting a register’s next value) will be treated as sink nodes of the network.

  • maintain_arg_order (bool) – If True, will add ordering constraints so that that incoming edges are ordered left-to-right for nets where argument order matters (e.g. <). Keeping this as False results in a cleaner, though less visually precise, graphical output.

The normal namer function will label user-named wires with their names and label the nodes (logic nets or Input/Output/Const terminals) with their operator symbol or name/value, respectively. If custom information about each node in the graph is desired, you can pass in a custom namer function which must have the same signature as the default namer, _graphviz_default_namer(). However, we recommend you instead pass in a call to graphviz_detailed_namer(), supplying it with your own dicts mapping wires and nodes to labels. For any wire/node found in these maps, that additional information will be printed in parentheses alongside the node in the graphviz graph.

For example, if you wanted to print the delay of each wire and the fanout of each gate, you could pass in two maps to the graphviz_detailed_namer() call, which returns a namer function that can subsequently be passed to output_to_graphviz() or block_to_graphviz_string().

node_fanout = {n: "Fanout: %d" % my_fanout_func(n) for n in working_block().logic}
wire_delay = {w: "Delay: %.2f" % my_delay_func(w) for w in working_block().wirevector_set}

with open("out.gv", "w") as f:
    output_to_graphviz(f, namer=graphviz_detailed_namer(node_fanout, wire_delay))
pyrtl.visualization.block_to_svg(block=None, split_state=True, maintain_arg_order=False)[source]#

Return an SVG for the block.

Parameters:
  • block (Block) – Block to use (defaults to current working block)

  • split_state (bool) – If True, visually split the connections to/from a register update net.

  • maintain_arg_order (bool) – If True, will add ordering constraints so that that incoming edges are ordered left-to-right for nets where argument order matters (e.g. <). Keeping this as False results in a cleaner, though less visually precise, graphical output.

Returns:

The SVG representation of the block

pyrtl.visualization.trace_to_html(simtrace, trace_list=None, sortkey=None, repr_func=<built-in function hex>, repr_per_name={})[source]#

Return a HTML block showing the trace.

Parameters:
  • simtrace (SimulationTrace) – A SimulationTrace object

  • trace_list (list[str]) – (optional) A list of wires to display

  • sortkey – (optional) The key with which to sort the trace_list

  • repr_func – function to use for representing the current_val; examples are hex, oct, bin, str (for decimal), or even the name of an IntEnum class you know the value will belong to. Defaults to hex.

  • repr_per_name – Map from signal name to a function that takes in the signal’s value and returns a user-defined representation. If a signal name is not found in the map, the argument repr_func will be used instead.

Returns:

An HTML block showing the trace

pyrtl.visualization.net_graph(block=None, split_state=False)[source]#

Return a graph representation of the given block.

Parameters:
  • block (Block) – block to use (defaults to current working block)

  • split_state (bool) – if True, split connections to/from a register update net; this means that registers will be appear as source nodes of the network, and r nets (i.e. the logic for setting a register’s next value) will be treated as sink nodes of the network.

The graph has the following form:

{
  node1: { nodeA: [edge1A_1, edge1A_2], nodeB: [edge1B]},
  node2: { nodeB: [edge2B],             nodeC: [edge2C_1, edge2C_2]},
  ...
}

aka: edges = graph[source][dest]

Each node can be either a LogicNet or a WireVector (e.g. an Input, an Output, a Const or even an undriven WireVector (which acts as a source or sink in the network). Each edge is a WireVector or derived type (Input, Output, Register, etc.). Note that inputs, consts, and outputs will be both “node” and “edge”. WireVectors that are not connected to any nets are not returned as part of the graph.