Module circuitgraph.logic

A collection of common logic elements as Circuit objects

Expand source code
"""A collection of common logic elements as `Circuit` objects"""

from itertools import product

from circuitgraph import Circuit
from circuitgraph.utils import clog2
from circuitgraph.transform import strip_io, relabel


def adder(w):
    """
    Create an adder.

    Parameters
    ----------
    w : int
            Input width of adder.

    Returns
    -------
    Circuit
            Adder circuit.
    """
    c = Circuit(name="adder")
    carry = c.add("null", "0")
    for i in range(w):
        # sum
        c.add(f"a_{i}", "input")
        c.add(f"b_{i}", "input")
        c.add(f"out_{i}", "xor", fanin=[f"a_{i}", f"b_{i}", carry], output=True)

        # carry
        c.add(f"and_ab_{i}", "and", fanin=[f"a_{i}", f"b_{i}"])
        c.add(f"and_ac_{i}", "and", fanin=[f"a_{i}", carry])
        c.add(f"and_bc_{i}", "and", fanin=[f"b_{i}", carry])
        carry = c.add(
            f"carry_{i}", "or", fanin=[f"and_ab_{i}", f"and_ac_{i}", f"and_bc_{i}",]
        )

    c.add(f"out_{w}", "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 = Circuit(name="mux")

    # create inputs
    for i in range(w):
        c.add(f"in_{i}", "input")
    sels = []
    for i in range(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 = Circuit(name="popcount")
    ps = [[c.add(f"in_{i}", "input")] for i in range(w)]

    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 += ["null"]
        while len(ns) < aw:
            ns += ["null"]

        # instantiate and connect adder
        a = strip_io(adder(aw))
        c.extend(relabel(a, {n: f"add_{i}_{n}" for n in a.nodes()}))
        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 "null" in c:
        c.set_type("null", "0")
        c.set_output("null", False)

    return c


def comb_lat():
    """
    Combinational model of a latch.

    Returns
    -------
    Circuit
            Latch model circuit.
    """
    lm = Circuit(name="lat")

    # mux
    m = strip_io(mux(2))
    lm.extend(relabel(m, {n: f"mux_{n}" for n in m.nodes()}))

    # inputs
    lm.add("si", "input", fanout="mux_in_0")
    lm.add("d", "input", fanout="mux_in_1")
    lm.add("clk", "input", fanout="mux_sel_0")
    lm.add("r", "input")
    lm.add("s", "input")

    # logic
    lm.add("r_b", "not", fanin="r")
    lm.add("qr", "and", fanin=["mux_out", "r_b"])
    lm.add("q", "or", fanin=["qr", "s"], output=True)
    lm.add("so", "buf", fanin="q", output=True)

    return lm


def comb_ff():
    """
    Combinational model of a flip-flop.

    Returns
    -------
    Circuit
            Flip-flop model circuit.
    """
    fm = Circuit(name="ff")

    # mux
    m = strip_io(mux(2))
    fm.extend(relabel(m, {n: f"mux_{n}" for n in m.nodes()}))

    # inputs
    fm.add("si", "input", fanout="mux_in_1")
    fm.add("d", "input", fanout="mux_in_0")
    fm.add("clk", "input", fanout="mux_sel_0")
    fm.add("r", "input")
    fm.add("s", "input")

    # logic
    fm.add("r_b", "not", fanin="r")
    fm.add("qr", "and", fanin=["si", "r_b"])
    fm.add("q", "or", fanin=["qr", "s"], output=True)
    fm.add("so", "buf", fanin="mux_out", output=True)

    return fm

Functions

def adder(w)

Create an adder.

Parameters

w : int
Input width of adder.

Returns

Circuit
Adder circuit.
Expand source code
def adder(w):
    """
    Create an adder.

    Parameters
    ----------
    w : int
            Input width of adder.

    Returns
    -------
    Circuit
            Adder circuit.
    """
    c = Circuit(name="adder")
    carry = c.add("null", "0")
    for i in range(w):
        # sum
        c.add(f"a_{i}", "input")
        c.add(f"b_{i}", "input")
        c.add(f"out_{i}", "xor", fanin=[f"a_{i}", f"b_{i}", carry], output=True)

        # carry
        c.add(f"and_ab_{i}", "and", fanin=[f"a_{i}", f"b_{i}"])
        c.add(f"and_ac_{i}", "and", fanin=[f"a_{i}", carry])
        c.add(f"and_bc_{i}", "and", fanin=[f"b_{i}", carry])
        carry = c.add(
            f"carry_{i}", "or", fanin=[f"and_ab_{i}", f"and_ac_{i}", f"and_bc_{i}",]
        )

    c.add(f"out_{w}", "buf", fanin=carry, output=True)
    return c
def comb_ff()

Combinational model of a flip-flop.

Returns

Circuit
Flip-flop model circuit.
Expand source code
def comb_ff():
    """
    Combinational model of a flip-flop.

    Returns
    -------
    Circuit
            Flip-flop model circuit.
    """
    fm = Circuit(name="ff")

    # mux
    m = strip_io(mux(2))
    fm.extend(relabel(m, {n: f"mux_{n}" for n in m.nodes()}))

    # inputs
    fm.add("si", "input", fanout="mux_in_1")
    fm.add("d", "input", fanout="mux_in_0")
    fm.add("clk", "input", fanout="mux_sel_0")
    fm.add("r", "input")
    fm.add("s", "input")

    # logic
    fm.add("r_b", "not", fanin="r")
    fm.add("qr", "and", fanin=["si", "r_b"])
    fm.add("q", "or", fanin=["qr", "s"], output=True)
    fm.add("so", "buf", fanin="mux_out", output=True)

    return fm
def comb_lat()

Combinational model of a latch.

Returns

Circuit
Latch model circuit.
Expand source code
def comb_lat():
    """
    Combinational model of a latch.

    Returns
    -------
    Circuit
            Latch model circuit.
    """
    lm = Circuit(name="lat")

    # mux
    m = strip_io(mux(2))
    lm.extend(relabel(m, {n: f"mux_{n}" for n in m.nodes()}))

    # inputs
    lm.add("si", "input", fanout="mux_in_0")
    lm.add("d", "input", fanout="mux_in_1")
    lm.add("clk", "input", fanout="mux_sel_0")
    lm.add("r", "input")
    lm.add("s", "input")

    # logic
    lm.add("r_b", "not", fanin="r")
    lm.add("qr", "and", fanin=["mux_out", "r_b"])
    lm.add("q", "or", fanin=["qr", "s"], output=True)
    lm.add("so", "buf", fanin="q", output=True)

    return lm
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 = Circuit(name="mux")

    # create inputs
    for i in range(w):
        c.add(f"in_{i}", "input")
    sels = []
    for i in range(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 = Circuit(name="popcount")
    ps = [[c.add(f"in_{i}", "input")] for i in range(w)]

    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 += ["null"]
        while len(ns) < aw:
            ns += ["null"]

        # instantiate and connect adder
        a = strip_io(adder(aw))
        c.extend(relabel(a, {n: f"add_{i}_{n}" for n in a.nodes()}))
        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 "null" in c:
        c.set_type("null", "0")
        c.set_output("null", False)

    return c