Module circuitgraph.utils
Various circuit related utilities
Expand source code
"""Various circuit related utilities"""
from pathlib import Path
from tempfile import NamedTemporaryFile
import subprocess
from circuitgraph import supported_types
from circuitgraph.io import circuit_to_verilog
def visualize(c, output_file):
"""
Visualize a circuit using Yosys.
Parameters
----------
c: Circuit
Circuit to visualize.
output_file: str
Where to write the image to.
"""
verilog = circuit_to_verilog(c)
output_file = Path(output_file)
fmt = output_file.suffix[1:]
prefix = output_file.with_suffix("")
with NamedTemporaryFile(
prefix="circuitgraph_synthesis_input", suffix=".v"
) as tmp_in:
tmp_in.write(bytes(verilog, "ascii"))
tmp_in.flush()
cmd = [
"yosys",
"-p",
f"read_verilog {tmp_in.name}; " f"show -format {fmt} -prefix {prefix}",
]
subprocess.run(cmd)
def clog2(num: int) -> int:
r"""Return the ceiling log base two of an integer :math:`\ge 1`.
This function tells you the minimum dimension of a Boolean space with at
least N points.
For example, here are the values of ``clog2(N)`` for :math:`1 \le N < 18`:
>>> [clog2(n) for n in range(1, 18)]
[0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5]
This function is undefined for non-positive integers:
>>> clog2(0)
Traceback (most recent call last):
...
ValueError: expected num >= 1
"""
if num < 1:
raise ValueError("expected num >= 1")
accum, shifter = 0, 1
while num > shifter:
shifter <<= 1
accum += 1
return accum
def int_to_bin(i, w, lend=False):
"""
Converts integer to binary tuple.
Parameters
----------
i : int
Integer to convert.
w : int
Width of conversion
lend : bool
Endianness of returned tuple, helpful for iterating.
Returns
-------
tuple of bool
Binary tuple.
"""
if not lend:
return tuple(v == "1" for v in bin(i)[2:].zfill(w))
else:
return tuple(reversed(tuple(v == "1" for v in bin(i)[2:].zfill(w))))
def bin_to_int(b, lend=False):
"""
Converts binary number to integer.
Parameters
----------
b : tuple of bool
Binary tuple.
lend : bool
Endianness of tuple.
Returns
-------
int
Value as integer.
"""
if not lend:
s = "".join("1" if v else "0" for v in b)
else:
s = "".join("1" if v else "0" for v in reversed(b))
return int(s, 2)
def lint(c, exhaustive=False, unloaded=False, undriven=True):
"""
Checks circuit for missing connections.
Parameters
----------
c: Circuit
The Circuit to lint.
"""
errors = []
def handle(s):
if exhaustive:
errors.append(s)
else:
raise ValueError(s)
# node types
for g in c.nodes():
if "type" not in c.graph.nodes[g]:
handle(f"no type for node {g}")
t = c.graph.nodes[g]["type"]
if t not in supported_types:
handle(f"node {g} has unsupported type {t}")
if "." in g and g.split(".")[0] not in c.blackboxes:
handle(f"node {g} has blackbox syntax with no instance")
# incorrect connections
for g in c.filter_type(["input", "0", "1", "bb_output"]):
if len(c.fanin(g)) > 0:
handle(f"{c.type(g)} {g} has fanin")
for g in c.filter_type(["buf", "not", "output", "bb_input"]):
if len(c.fanin(g)) > 1:
handle(f"{c.type(g)} {g} has fanin count > 1")
# dangling connections
if undriven:
for g in c.filter_type(
[
"buf",
"not",
"output",
"bb_input",
"and",
"nand",
"or",
"nor",
"xor",
"xnor",
]
):
if len(c.fanin(g)) < 1:
handle(f"{c.type(g)} {g} has no fanin")
if unloaded:
for g in c.nodes() - c.outputs():
if not c.fanout(g):
handle(f"{c.type(g)} {g} has no fanout")
# blackboxes
for name, bb in c.blackboxes.items():
for g in bb.inputs():
if f"{name}.{g}" not in c.graph.nodes:
handle(f"missing blackbox pin {name}.{g}")
else:
t = c.graph.nodes[f"{name}.{g}"]["type"]
if t != "bb_input":
handle(f"blackbox pin {name}.{g} has incorrect type {t}")
for g in bb.outputs():
if f"{name}.{g}" not in c.graph.nodes:
handle(f"missing blackbox pin {name}.{g}")
else:
t = c.graph.nodes[f"{name}.{g}"]["type"]
if t != "bb_output":
handle(f"blackbox pin {name}.{g} has incorrect type {t}")
if errors:
raise ValueError("\n".join(errors))
Functions
def bin_to_int(b, lend=False)
-
Converts binary number to integer.
Parameters
b
:tuple
ofbool
- Binary tuple.
lend
:bool
- Endianness of tuple.
Returns
int
- Value as integer.
Expand source code
def bin_to_int(b, lend=False): """ Converts binary number to integer. Parameters ---------- b : tuple of bool Binary tuple. lend : bool Endianness of tuple. Returns ------- int Value as integer. """ if not lend: s = "".join("1" if v else "0" for v in b) else: s = "".join("1" if v else "0" for v in reversed(b)) return int(s, 2)
def clog2(num: int) ‑> int
-
Return the ceiling log base two of an integer :math:
\ge 1
. This function tells you the minimum dimension of a Boolean space with at least N points. For example, here are the values ofclog2()(N)
for :math:1 \le N < 18
: >>> [clog2(n) for n in range(1, 18)] [0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5] This function is undefined for non-positive integers: >>> clog2(0) Traceback (most recent call last): … ValueError: expected num >= 1Expand source code
def clog2(num: int) -> int: r"""Return the ceiling log base two of an integer :math:`\ge 1`. This function tells you the minimum dimension of a Boolean space with at least N points. For example, here are the values of ``clog2(N)`` for :math:`1 \le N < 18`: >>> [clog2(n) for n in range(1, 18)] [0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5] This function is undefined for non-positive integers: >>> clog2(0) Traceback (most recent call last): ... ValueError: expected num >= 1 """ if num < 1: raise ValueError("expected num >= 1") accum, shifter = 0, 1 while num > shifter: shifter <<= 1 accum += 1 return accum
def int_to_bin(i, w, lend=False)
-
Converts integer to binary tuple.
Parameters
i
:int
- Integer to convert.
w
:int
- Width of conversion
lend
:bool
- Endianness of returned tuple, helpful for iterating.
Returns
tuple
ofbool
- Binary tuple.
Expand source code
def int_to_bin(i, w, lend=False): """ Converts integer to binary tuple. Parameters ---------- i : int Integer to convert. w : int Width of conversion lend : bool Endianness of returned tuple, helpful for iterating. Returns ------- tuple of bool Binary tuple. """ if not lend: return tuple(v == "1" for v in bin(i)[2:].zfill(w)) else: return tuple(reversed(tuple(v == "1" for v in bin(i)[2:].zfill(w))))
def lint(c, exhaustive=False, unloaded=False, undriven=True)
-
Checks circuit for missing connections.
Parameters
c
:Circuit
- The Circuit to lint.
Expand source code
def lint(c, exhaustive=False, unloaded=False, undriven=True): """ Checks circuit for missing connections. Parameters ---------- c: Circuit The Circuit to lint. """ errors = [] def handle(s): if exhaustive: errors.append(s) else: raise ValueError(s) # node types for g in c.nodes(): if "type" not in c.graph.nodes[g]: handle(f"no type for node {g}") t = c.graph.nodes[g]["type"] if t not in supported_types: handle(f"node {g} has unsupported type {t}") if "." in g and g.split(".")[0] not in c.blackboxes: handle(f"node {g} has blackbox syntax with no instance") # incorrect connections for g in c.filter_type(["input", "0", "1", "bb_output"]): if len(c.fanin(g)) > 0: handle(f"{c.type(g)} {g} has fanin") for g in c.filter_type(["buf", "not", "output", "bb_input"]): if len(c.fanin(g)) > 1: handle(f"{c.type(g)} {g} has fanin count > 1") # dangling connections if undriven: for g in c.filter_type( [ "buf", "not", "output", "bb_input", "and", "nand", "or", "nor", "xor", "xnor", ] ): if len(c.fanin(g)) < 1: handle(f"{c.type(g)} {g} has no fanin") if unloaded: for g in c.nodes() - c.outputs(): if not c.fanout(g): handle(f"{c.type(g)} {g} has no fanout") # blackboxes for name, bb in c.blackboxes.items(): for g in bb.inputs(): if f"{name}.{g}" not in c.graph.nodes: handle(f"missing blackbox pin {name}.{g}") else: t = c.graph.nodes[f"{name}.{g}"]["type"] if t != "bb_input": handle(f"blackbox pin {name}.{g} has incorrect type {t}") for g in bb.outputs(): if f"{name}.{g}" not in c.graph.nodes: handle(f"missing blackbox pin {name}.{g}") else: t = c.graph.nodes[f"{name}.{g}"]["type"] if t != "bb_output": handle(f"blackbox pin {name}.{g} has incorrect type {t}") if errors: raise ValueError("\n".join(errors))
def visualize(c, output_file)
-
Visualize a circuit using Yosys.
Parameters
c
:Circuit
- Circuit to visualize.
output_file
:str
- Where to write the image to.
Expand source code
def visualize(c, output_file): """ Visualize a circuit using Yosys. Parameters ---------- c: Circuit Circuit to visualize. output_file: str Where to write the image to. """ verilog = circuit_to_verilog(c) output_file = Path(output_file) fmt = output_file.suffix[1:] prefix = output_file.with_suffix("") with NamedTemporaryFile( prefix="circuitgraph_synthesis_input", suffix=".v" ) as tmp_in: tmp_in.write(bytes(verilog, "ascii")) tmp_in.flush() cmd = [ "yosys", "-p", f"read_verilog {tmp_in.name}; " f"show -format {fmt} -prefix {prefix}", ] subprocess.run(cmd)