Module circuitgraph.logic
A collection of common logic elements as Circuit
objects.
They can be added to existing circuits using Circuit.add_subcircuit
Examples
Either add a
and c
or b
and c
depending on sel
.
>>> import circuitgraph as cg
>>> a = cg.logic.half_adder()
>>> m = cg.logic.mux(2)
>>> c = cg.Circuit()
>>> c.add("a", "input")
'a'
>>> c.add("b", "input")
'b'
>>> c.add("c", "input")
'c'
>>> c.add("sel", "input")
'sel'
>>> c.add("d", "buf")
'd'
>>> c.add("sum", "buf", output=True)
'sum'
>>> c.add("carry", "buf", output=True)
'carry'
Add mux subcircuit
>>> mux_conns = {"in_0": "a", "in_1": "b", "sel_0": "sel", "out": "d"}
>>> c.add_subcircuit(m, "mux", mux_conns)
Add adder subcircuit
>>> add_conns = {"x": "c", "y": "d", "c": "carry", "s": "sum"}
>>> c.add_subcircuit(a, "adder", add_conns)
Simulate to verify
>>> res = cg.sat.solve(c, {"a": 0, "b": 1, "c": 1, "sel": 0}) # a + c
>>> res["sum"]
True
>>> res["carry"]
False
>>> res = cg.sat.solve(c, {"a": 0, "b": 1, "c": 1, "sel": 1}) # b + c
>>> res["sum"]
False
>>> res["carry"]
True
Expand source code
"""
A collection of common logic elements as `Circuit` objects.
They can be added to existing circuits using `Circuit.add_subcircuit`
Examples
--------
Either add `a` and `c` or `b` and `c` depending on `sel`.
>>> import circuitgraph as cg
>>> a = cg.logic.half_adder()
>>> m = cg.logic.mux(2)
>>> c = cg.Circuit()
>>> c.add("a", "input")
'a'
>>> c.add("b", "input")
'b'
>>> c.add("c", "input")
'c'
>>> c.add("sel", "input")
'sel'
>>> c.add("d", "buf")
'd'
>>> c.add("sum", "buf", output=True)
'sum'
>>> c.add("carry", "buf", output=True)
'carry'
Add mux subcircuit
>>> mux_conns = {"in_0": "a", "in_1": "b", "sel_0": "sel", "out": "d"}
>>> c.add_subcircuit(m, "mux", mux_conns)
Add adder subcircuit
>>> add_conns = {"x": "c", "y": "d", "c": "carry", "s": "sum"}
>>> c.add_subcircuit(a, "adder", add_conns)
Simulate to verify
>>> res = cg.sat.solve(c, {"a": 0, "b": 1, "c": 1, "sel": 0}) # a + c
>>> res["sum"]
True
>>> res["carry"]
False
>>> res = cg.sat.solve(c, {"a": 0, "b": 1, "c": 1, "sel": 1}) # b + c
>>> res["sum"]
False
>>> res["carry"]
True
"""
from itertools import product
import circuitgraph as cg
def half_adder():
"""
Create an AND/XOR half adder.
Returns
-------
Circuit
Half adder circuit.
"""
c = cg.Circuit(name="half_adder")
ins = [c.add("x", "input"), c.add("y", "input")]
c.add("c", "and", fanin=ins, output=True)
c.add("s", "xor", fanin=ins, output=True)
return c
def full_adder():
"""
Create a full adder from two half adders.
Returns
-------
Circuit
Full adder circuit.
"""
c = cg.Circuit("full_adder")
c.add("x", "input")
c.add("y", "input")
c.add("cin", "input")
c.add_subcircuit(half_adder(), "x_y_ha", connections={"x": "x", "y": "y"})
c.add_subcircuit(
half_adder(), "cin_s_ha", connections={"x": "x_y_ha_s", "y": "cin"}
)
c.add("cout", "or", fanin=["x_y_ha_c", "cin_s_ha_c"], output=True)
c.add("s", "buf", fanin="cin_s_ha_s", output=True)
return c
def adder(width, carry_in=False, carry_out=False):
"""
Create a ripple carry adder.
Parameters
----------
width : int
Input width of adder.
carry_in: bool
Add a carry input.
carry_out: bool
Add a carry output.
Returns
-------
Circuit
Adder circuit.
"""
c = cg.Circuit(name="adder")
carry = c.add("cin", "input" if carry_in else "0")
for bit in range(width):
a = c.add(f"a_{bit}", "input")
b = c.add(f"b_{bit}", "input")
out = c.add(f"out_{bit}", "buf", output=True)
c.add_subcircuit(
full_adder(), f"fa_{bit}", {"x": a, "y": b, "cin": carry, "s": out}
)
carry = f"fa_{bit}_cout"
if carry_out:
c.add("cout", "buf", fanin=carry, output=True)
return c
def mux(w):
"""
Create a mux.
Parameters
----------
w : int
Input width of the mux.
Returns
-------
Circuit
Mux circuit.
"""
c = cg.Circuit(name="mux")
# create inputs
for i in range(w):
c.add(f"in_{i}", "input")
sels = []
for i in range(cg.utils.clog2(w)):
c.add(f"sel_{i}", "input")
c.add(f"not_sel_{i}", "not", fanin=f"sel_{i}")
sels.append([f"not_sel_{i}", f"sel_{i}"])
# create output or
c.add("out", "or", output=True)
i = 0
for sel in product(*sels[::-1]):
c.add(f"and_{i}", "and", fanin=[*sel, f"in_{i}"], fanout="out")
i += 1
if i == w:
break
return c
def popcount(w):
"""
Create a population count circuit.
Parameters
----------
w : int
Input width of the circuit.
Returns
-------
Circuit
Population count circuit.
"""
c = cg.Circuit(name="popcount")
ps = [[c.add(f"in_{i}", "input")] for i in range(w)]
c.add("tie0", "0")
i = 0
while len(ps) > 1:
# get values
ns = ps.pop(0)
ms = ps.pop(0)
# pad
aw = max(len(ns), len(ms))
while len(ms) < aw:
ms += ["tie0"]
while len(ns) < aw:
ns += ["tie0"]
# instantiate and connect adder
c.add_subcircuit(adder(aw, carry_out=True), f"add_{i}")
c.relabel({f"add_{i}_cout": f"add_{i}_out_{aw}"})
for j, (n, m) in enumerate(zip(ns, ms)):
c.connect(n, f"add_{i}_a_{j}")
c.connect(m, f"add_{i}_b_{j}")
# add adder outputs
ps.append([f"add_{i}_out_{j}" for j in range(aw + 1)])
i += 1
# connect outputs
for i, o in enumerate(ps[0]):
c.add(f"out_{i}", "buf", fanin=o, output=True)
if not c.fanout("tie0"):
c.remove("tie0")
return c
Functions
def adder(width, carry_in=False, carry_out=False)
-
Create a ripple carry adder.
Parameters
width
:int
- Input width of adder.
carry_in
:bool
- Add a carry input.
carry_out
:bool
- Add a carry output.
Returns
Circuit
- Adder circuit.
Expand source code
def adder(width, carry_in=False, carry_out=False): """ Create a ripple carry adder. Parameters ---------- width : int Input width of adder. carry_in: bool Add a carry input. carry_out: bool Add a carry output. Returns ------- Circuit Adder circuit. """ c = cg.Circuit(name="adder") carry = c.add("cin", "input" if carry_in else "0") for bit in range(width): a = c.add(f"a_{bit}", "input") b = c.add(f"b_{bit}", "input") out = c.add(f"out_{bit}", "buf", output=True) c.add_subcircuit( full_adder(), f"fa_{bit}", {"x": a, "y": b, "cin": carry, "s": out} ) carry = f"fa_{bit}_cout" if carry_out: c.add("cout", "buf", fanin=carry, output=True) return c
def full_adder()
-
Create a full adder from two half adders.
Returns
Circuit
- Full adder circuit.
Expand source code
def full_adder(): """ Create a full adder from two half adders. Returns ------- Circuit Full adder circuit. """ c = cg.Circuit("full_adder") c.add("x", "input") c.add("y", "input") c.add("cin", "input") c.add_subcircuit(half_adder(), "x_y_ha", connections={"x": "x", "y": "y"}) c.add_subcircuit( half_adder(), "cin_s_ha", connections={"x": "x_y_ha_s", "y": "cin"} ) c.add("cout", "or", fanin=["x_y_ha_c", "cin_s_ha_c"], output=True) c.add("s", "buf", fanin="cin_s_ha_s", output=True) return c
def half_adder()
-
Create an AND/XOR half adder.
Returns
Circuit
- Half adder circuit.
Expand source code
def half_adder(): """ Create an AND/XOR half adder. Returns ------- Circuit Half adder circuit. """ c = cg.Circuit(name="half_adder") ins = [c.add("x", "input"), c.add("y", "input")] c.add("c", "and", fanin=ins, output=True) c.add("s", "xor", fanin=ins, output=True) return c
def mux(w)
-
Create a mux.
Parameters
w
:int
- Input width of the mux.
Returns
Circuit
- Mux circuit.
Expand source code
def mux(w): """ Create a mux. Parameters ---------- w : int Input width of the mux. Returns ------- Circuit Mux circuit. """ c = cg.Circuit(name="mux") # create inputs for i in range(w): c.add(f"in_{i}", "input") sels = [] for i in range(cg.utils.clog2(w)): c.add(f"sel_{i}", "input") c.add(f"not_sel_{i}", "not", fanin=f"sel_{i}") sels.append([f"not_sel_{i}", f"sel_{i}"]) # create output or c.add("out", "or", output=True) i = 0 for sel in product(*sels[::-1]): c.add(f"and_{i}", "and", fanin=[*sel, f"in_{i}"], fanout="out") i += 1 if i == w: break return c
def popcount(w)
-
Create a population count circuit.
Parameters
w
:int
- Input width of the circuit.
Returns
Circuit
- Population count circuit.
Expand source code
def popcount(w): """ Create a population count circuit. Parameters ---------- w : int Input width of the circuit. Returns ------- Circuit Population count circuit. """ c = cg.Circuit(name="popcount") ps = [[c.add(f"in_{i}", "input")] for i in range(w)] c.add("tie0", "0") i = 0 while len(ps) > 1: # get values ns = ps.pop(0) ms = ps.pop(0) # pad aw = max(len(ns), len(ms)) while len(ms) < aw: ms += ["tie0"] while len(ns) < aw: ns += ["tie0"] # instantiate and connect adder c.add_subcircuit(adder(aw, carry_out=True), f"add_{i}") c.relabel({f"add_{i}_cout": f"add_{i}_out_{aw}"}) for j, (n, m) in enumerate(zip(ns, ms)): c.connect(n, f"add_{i}_a_{j}") c.connect(m, f"add_{i}_b_{j}") # add adder outputs ps.append([f"add_{i}_out_{j}" for j in range(aw + 1)]) i += 1 # connect outputs for i, o in enumerate(ps[0]): c.add(f"out_{i}", "buf", fanin=o, output=True) if not c.fanout("tie0"): c.remove("tie0") return c