Ejemplo n.º 1
0
    def __init__(self,
                 text,
                 blackboxes,
                 warnings=False,
                 error_on_warning=False):
        """
        Initializes a new transformer.

        Parameters
        ----------
        text: str
                The netlist that the transformer will be used on, used for
                error messages.
        blackboxes: list of circuitgraph.BlackBox
                The blackboxes present in the netlist that will be parsed.
        warnings: bool
                If True, warnings about unused nets will be printed.
        error_on_warning: bool
                If True, unused nets will cause raise `VerilogParsingWarning`
                exceptions.
        """
        self.c = Circuit()
        self.text = text
        self.blackboxes = blackboxes
        self.warnings = warnings
        self.error_on_warning = error_on_warning
        self.tie0 = self.c.add("tie0", "0")
        self.tie1 = self.c.add("tie1", "1")
        self.io = set()
        self.inputs = set()
        self.outputs = set()
        self.wires = set()
Ejemplo n.º 2
0
def copy(c):
    """
    Returns copy of a circuit.

    Parameters
    ----------
    c : Circuit
            Input circuit.

    Returns
    -------
    Circuit
            Circuit copy.

    """
    return Circuit(graph=c.graph.copy(),
                   name=c.name,
                   blackboxes=c.blackboxes.copy())
Ejemplo n.º 3
0
def relabel(c, mapping):
    """
    Builds copy with relabeled nodes.

    Parameters
    ----------
    c : Circuit
            Input circuit.
    mapping : dict of str:str
            Relabeling of nodes.

    Returns
    -------
    Circuit
            Circuit with removed blackboxes.
    """
    g = nx.relabel_nodes(c.graph, mapping)
    return Circuit(graph=g, name=c.name, blackboxes=c.blackboxes.copy())
Ejemplo n.º 4
0
def strip_blackboxes(c, ignore_pins=None):
    """
    Converts blackboxes to io.

    Parameters
    ----------
    c : Circuit
            Input circuit.
    ingnore_pins: str or list of str
            Pins to not create io for, just disconnect and delete.

    Returns
    -------
    Circuit
            Circuit with removed blackboxes.
    """
    if not ignore_pins:
        ignore_pins = []
    elif isinstance(ignore_pins, str):
        ignore_pins = [ignore_pins]
    g = c.graph.copy()
    bb_pins = []
    for n in c.filter_type("bb_input"):
        if n.split(".")[-1] in ignore_pins:
            g.remove_node(n)
        else:
            g.nodes[n]["type"] = "output"
            bb_pins.append(n)
    for n in c.filter_type("bb_output"):
        if n.split(".")[-1] in ignore_pins:
            g.remove_node(n)
        else:
            g.nodes[n]["type"] = "input"
            bb_pins.append(n)

    # rename nodes
    mapping = {n: n.replace(".", "_") for n in bb_pins}
    for k in mapping.values():
        if k in g:
            raise ValueError(f"Overlapping blackbox name: {k}")
    nx.relabel_nodes(g, mapping, copy=False)

    return Circuit(graph=g, name=c.name)
Ejemplo n.º 5
0
def strip_outputs(c):
    """
    Removes a circuit's outputs for easy
    instantiation.

    Parameters
    ----------
    c : Circuit
            Input circuit.

    Returns
    -------
    Circuit
            Circuit with removed io
    """
    g = c.graph.copy()
    for o in c.outputs():
        g.nodes[o]["type"] = "buf"

    return Circuit(graph=g, name=c.name, blackboxes=c.blackboxes.copy())
Ejemplo n.º 6
0
def strip_inputs(c):
    """
    Converts inputs to buffers for easy
    instantiation.

    Parameters
    ----------
    c : Circuit
            Input circuit.

    Returns
    -------
    Circuit
            Circuit with removed io
    """
    g = c.graph.copy()
    for i in c.inputs():
        g.nodes[i]["type"] = "buf"

    return Circuit(graph=g, name=c.name, blackboxes=c.blackboxes.copy())
Ejemplo n.º 7
0
def bench_to_circuit(bench, name):
    """
    Creates a new Circuit from a bench string.

    Parameters
    ----------
    bench: str
            bench code.
    name: str
            the module name.

    Returns
    -------
    Circuit
            the parsed circuit.
    """
    # create circuit
    c = Circuit(name=name)

    # get inputs
    in_regex = r"(?:INPUT|input)\s*\((.+?)\)"
    for net_str in re.findall(in_regex, bench, re.DOTALL):
        nets = net_str.replace(" ", "").replace("\n",
                                                "").replace("\t",
                                                            "").split(",")
        for n in nets:
            c.add(n, "input")

    # handle gates
    regex = r"(\S+)\s*=\s*(NOT|OR|NOR|AND|NAND|XOR|XNOR|not|or|nor|and|nand|not|xor|xnor)\((.+?)\)"
    for net, gate, input_str in re.findall(regex, bench):
        # parse all nets
        inputs = (input_str.replace(" ",
                                    "").replace("\n",
                                                "").replace("\t",
                                                            "").split(","))

    # get outputs
    in_regex = r"(?:OUTPUT|output)\s*\((.+?)\)"
    for net_str in re.findall(in_regex, bench, re.DOTALL):
        nets = net_str.replace(" ", "").replace("\n",
                                                "").replace("\t",
                                                            "").split(",")
        for n in nets:
            c.set_output(n)

    return c
Ejemplo n.º 8
0
def circuit_to_verilog(c):
    """
    Generates a str of Verilog code from a `CircuitGraph`.

    Parameters
    ----------
    c: Circuit
            the circuit to turn into Verilog.

    Returns
    -------
    str
        Verilog code.
    """
    c = Circuit(graph=c.graph.copy(),
                name=c.name,
                blackboxes=c.blackboxes.copy())
    # sanitize escaped nets
    for node in c.nodes():
        if node.startswith("\\"):
            c.relabel({node: node + " "})

    inputs = list(c.inputs())
    outputs = list(c.outputs())
    insts = []
    wires = []

    # remove outputs drivers
    driver_mapping = dict()
    for output in outputs:
        if len(c.fanin(output)) > 1:
            raise ValueError(f"Output {output} has multiple drivers.")
        elif len(c.fanin(output)) == 1:
            driver = c.fanin(output).pop()
            if c.type(driver) in ["input", "1", "0"]:
                driver = c.add(f"{output}_driver",
                               type="buf",
                               fanin=driver,
                               uid=True)
            driver_mapping[driver] = output
    c.remove(c.outputs())
    c.relabel(driver_mapping)

    # blackboxes
    output_map = {}
    for name, bb in c.blackboxes.items():
        io = []
        for n in bb.inputs():
            driver = c.fanin(f"{name}.{n}").pop()
            io += [f".{n}({driver})"]

        for n in bb.outputs():
            w = c.uid(f"{name}_{n}_load")
            wires.append(w)
            output_map[f"{name}.{n}"] = w
            io += [f".{n}({w})"]

        io_def = ", ".join(io)
        insts.append(f"{bb.name} {name} ({io_def})")

    # gates
    for n in c.nodes():
        if c.type(n) in [
                "xor", "xnor", "buf", "not", "nor", "or", "and", "nand"
        ]:
            fanin = [
                output_map[f] if f in output_map else f for f in c.fanin(n)
            ]
            fanin = ", ".join(fanin)
            insts.append(f"{c.type(n)} g_{len(insts)} " f"({n}, {fanin})")
            wires.append(n)
        elif c.type(n) in ["0", "1"]:
            insts.append(f"assign {n} = 1'b{c.type(n)}")
            wires.append(n)
        elif c.type(n) in ["input", "output", "bb_input", "bb_output"]:
            pass
        else:
            raise ValueError(f"unknown gate type: {c.type(n)}")

    verilog = f"module {c.name} ("
    verilog += ", ".join(inputs + outputs)
    verilog += ");\n"
    verilog += "".join(f"  input {inp};\n" for inp in inputs)
    verilog += "\n"
    verilog += "".join(f"  output {out};\n" for out in outputs)
    verilog += "\n"
    verilog += "".join(f"  wire {wire};\n" for wire in wires)
    verilog += "\n"
    verilog += "".join(f"  {inst};\n" for inst in insts)
    verilog += "endmodule\n"

    return verilog
Ejemplo n.º 9
0
def xor_hash(n, m):
    """
    Create a XOR hash function H_{xor}(n,m,3) as in:
    Chakraborty, Supratik, Kuldeep S. Meel, and Moshe Y. Vardi. "A scalable approximate model counter." International Conference on Principles and Practice of Constraint Programming. Springer, Berlin, Heidelberg, 2013.

    Each output of the hash is the xor/xnor of a random subset of the input.

    Parameters
    ----------
    n : int
            Input width of the hash function.
    m : int
            Output width of the hash function.

    Returns
    -------
    Circuit
            XOR hash function.
    """

    h = Circuit()

    for i in range(n):
        h.add(f"in_{i}", "input")

    for o in range(m):
        h.add(f"out_{o}", "output")

        # select inputs
        cons = [random() < 0.5 for i in range(n)]

        if sum(cons) == 0:
            # constant output
            h.add(f"c_{o}", "1" if random() < 0.5 else "0", fanout=f"out_{o}")
        else:
            # choose between xor/xnor
            h.add(f"xor_{o}", "xor", fanout=f"out_{o}")
            h.add(f"c_{o}", "1" if random() < 0.5 else "0", fanout=f"xor_{o}")
            for i, con in enumerate(cons):
                if con:
                    h.connect(f"in_{i}", f"xor_{o}")

    return h
Ejemplo n.º 10
0
class VerilogCircuitGraphTransformer(Transformer):
    """
    A lark.Transformer that transforms a parsed verilog netlist into a
    circuitgraph.Circuit.
    """
    def __init__(self,
                 text,
                 blackboxes,
                 warnings=False,
                 error_on_warning=False):
        """
        Initializes a new transformer.

        Parameters
        ----------
        text: str
                The netlist that the transformer will be used on, used for
                error messages.
        blackboxes: list of circuitgraph.BlackBox
                The blackboxes present in the netlist that will be parsed.
        warnings: bool
                If True, warnings about unused nets will be printed.
        error_on_warning: bool
                If True, unused nets will cause raise `VerilogParsingWarning`
                exceptions.
        """
        self.c = Circuit()
        self.text = text
        self.blackboxes = blackboxes
        self.warnings = warnings
        self.error_on_warning = error_on_warning
        self.tie0 = self.c.add("tie0", "0")
        self.tie1 = self.c.add("tie1", "1")
        self.io = set()
        self.inputs = set()
        self.outputs = set()
        self.wires = set()

    # Helper functions
    def add_node(self, n, node_type, fanin=[], fanout=[], uid=False):
        """So that nodes are of type `str`, not `lark.Token`"""
        if type(fanin) not in [list, set]:
            fanin = [fanin]
        if type(fanout) not in [list, set]:
            fanout = [fanout]

        fanin = [str(i) for i in fanin]
        fanout = [str(i) for i in fanout]
        node_type = str(node_type)

        for i in fanout + fanin:
            if i not in self.c:
                self.c.add(i, "buf")

        return self.c.add(
            str(n),
            node_type,
            fanin=fanin,
            fanout=fanout,
            uid=uid,
        )

    def add_blackbox(self, blackbox, name, connections=dict()):
        formatted_connections = dict()
        for key in connections:
            formatted_connections[str(key)] = str(connections[key])
            if str(connections[key]) not in self.c:
                self.c.add(str(connections[key]), "buf")
        self.c.add_blackbox(blackbox, str(name), formatted_connections)

    def warn(self, message):
        if self.error_on_warning:
            raise VerilogParsingWarning(message)
        else:
            print(f"Warning: {message}")

    def check_for_warnings(self):
        for wire in self.wires:
            if wire not in self.c.nodes():
                self.warn(f"{wire} declared as wire but isn't connected.")

        for n in self.c.nodes():
            if self.c.type(n) not in ["output", "bb_input"
                                      ] and not self.c.fanout(n):
                self.warn(f"{n} doesn't drive any nets.")
            elif self.c.type(n) not in [
                    "input",
                    "0",
                    "1",
                    "bb_output",
            ] and not self.c.fanin(n):
                self.warn(f"{n} doesn't have any drivers.")

    # 1. Source text
    def start(self, description):
        return description

    def module(self, module_name_and_list_of_ports_and_module_items):
        self.c.name = str(module_name_and_list_of_ports_and_module_items[0])

        # Check if ports list matches with inputs and outputs
        if not self.inputs <= self.io:
            i = (self.inputs - self.io).pop()
            raise VerilogParsingError(
                f"{i} declared as output but not in port list", i, self.text)
        if not self.outputs <= self.io:
            o = (self.outputs - self.io).pop()
            raise VerilogParsingError(
                f"{o} declared as output but not in port list", o, self.text)
        if not self.io <= (self.inputs | self.outputs):
            v = (self.io - (self.inputs | self.outputs)).pop()
            raise VerilogParsingError(
                f"{v} in port list but was not declared as input or output",
                v,
                self.text,
            )

        # Relabel outputs using drivers
        for o in self.outputs:
            o = str(o)
            if self.c.fanin(o):
                o_driver = f"{o}_driver"
                while o_driver in self.c.nodes():
                    o_driver += "_0"
                self.c.relabel({o: o_driver})
                self.add_node(o, "output")
                self.c.connect(o_driver, o)

        # Remove tie0, tie1 if not used
        if not self.c.fanout(self.tie0):
            self.c.remove(self.tie0)
        if not self.c.fanout(self.tie1):
            self.c.remove(self.tie1)

        # Check for warnings
        if self.warnings:
            self.check_for_warnings()

        return self.c

    def list_of_ports(self, ports):
        for port in ports:
            self.io.add(port)

    # 2. Declarations
    def input_declaration(self, list_of_variables):
        [list_of_variables] = list_of_variables
        self.inputs.update(list_of_variables)
        for variable in list_of_variables:
            self.add_node(variable, "input")

    def output_declaration(self, list_of_variables):
        [list_of_variables] = list_of_variables
        self.outputs.update(list_of_variables)
        for variable in list_of_variables:
            self.add_node(variable, "output")

    def net_declaration(self, list_of_variables):
        [list_of_variables] = list_of_variables
        self.wires.update(list_of_variables)

    def list_of_variables(self, identifiers):
        return identifiers

    # 3. Primitive Instances
    # These are merged with module isntantiations

    # 4. Module Instantiations
    def module_instantiation(self, name_of_module_and_module_instances):
        name_of_module = name_of_module_and_module_instances[0]
        module_instances = name_of_module_and_module_instances[1:]
        # Check if this is a primitive gate
        if name_of_module in addable_types:
            for name, ports in module_instances:
                if isinstance(ports, dict):
                    raise VerilogParsingError(
                        "Primitive gates cannot use named port connections",
                        name,
                        self.text,
                    )
                self.add_node(ports[0],
                              name_of_module,
                              fanin=[p for p in ports[1:]])
        # Check if this is a GTECH gate
        elif name_of_module.startswith("GTECH_") and name_of_module.split("_")[
                -1].rstrip(digits).lower() in addable_types + ["zero", "one"]:
            gate = name_of_module.split("_")[-1].rstrip(digits).lower()
            if gate == "zero":
                gate = "0"
            if gate == "one":
                gate = "1"
            for name, connections in module_instances:
                if not isinstance(connections, dict):
                    raise VerilogParsingError(
                        "GTECH gates must use named port connections",
                        name,
                        self.text,
                    )
                output = connections["Z"]
                inputs = set(connections.values()) - {output}
                self.add_node(output, gate, fanin=inputs)
        # Otherwise, try to parse as blackbox
        else:
            try:
                bb = {i.name: i for i in self.blackboxes}[name_of_module]
            except KeyError:
                raise VerilogParsingError(
                    f"Blackbox {name_of_module} not in list of defined blackboxes.",
                    name_of_module,
                    self.text,
                )
            for name, connections in module_instances:
                if not isinstance(connections, dict):
                    raise VerilogParsingError(
                        "Blackbox instantiations must use named port connections",
                        name,
                        self.text,
                    )
                for output in bb.outputs():
                    if output in connections:
                        self.add_node(connections[output], "buf")
                self.add_blackbox(bb, name, connections)

    def module_instance(self,
                        name_of_instance_and_list_of_module_connecetions):
        (
            name_of_instance,
            list_of_module_connecetions,
        ) = name_of_instance_and_list_of_module_connecetions
        return (name_of_instance, list_of_module_connecetions)

    def list_of_module_connections(self, module_port_connections):
        if isinstance(module_port_connections[0], dict):
            d = dict()
            for m in module_port_connections:
                d.update(m)
            return d
        else:
            return module_port_connections

    def module_port_connection(self, expression):
        return expression[0]

    def named_port_connection(self, identifier_and_expression):
        [identifier, expression] = identifier_and_expression
        return {identifier: expression}

    # 5. Behavioral Statements
    def assignment(self, lvalue_and_expression):
        [lvalue, expression] = lvalue_and_expression
        if lvalue not in [self.tie0, self.tie1]:
            self.add_node(lvalue, "buf", fanin=expression)

    # 6. Specify Section

    # 7. Expressions
    def expression(self, s):
        return s[0]

    def constant_zero(self, value):
        return self.tie0

    def constant_one(self, value):
        return self.tie1

    def not_gate(self, items):
        io = "_".join(items)
        return self.add_node(f"not_{io}", "not", fanin=items[0], uid=True)

    def xor_gate(self, items):
        io = "_".join(items)
        return self.add_node(f"xor_{io}",
                             "xor",
                             fanin=[items[0], items[1]],
                             uid=True)

    def xnor_gate(self, items):
        io = "_".join(items)
        return self.add_node(f"xnor_{io}",
                             "xnor",
                             fanin=[items[0], items[1]],
                             uid=True)

    def and_gate(self, items):
        io = "_".join(items)
        return self.add_node(f"and_{io}",
                             "and",
                             fanin=[items[0], items[1]],
                             uid=True)

    def or_gate(self, items):
        io = "_".join(items)
        return self.add_node(f"or_{io}",
                             "or",
                             fanin=[items[0], items[1]],
                             uid=True)

    def ternary(self, items):
        io = "_".join(items)
        n = self.add_node(f"mux_n_{io}", "not", fanin=items[0], uid=True)
        a0 = self.add_node(f"mux_a0_{io}",
                           "and",
                           fanin=[n, items[2]],
                           uid=True)
        a1 = self.add_node(f"mux_a1_{io}",
                           "and",
                           fanin=[items[0], items[1]],
                           uid=True)
        return self.add_node(f"mux_o_{io}", "or", fanin=[a0, a1], uid=True)
Ejemplo n.º 11
0
def sensitivity_transform(c, n):
    """
    Creates a circuit to compute sensitivity.

    Parameters
    ----------
    c : Circuit
            Sequential circuit to unroll.
    n : str
            Node to compute sensitivity at.

    Returns
    -------
    Circuit
            Sensitivity circuit.

    """

    # check for blackboxes
    if c.blackboxes:
        raise ValueError(f"{c.name} contains a blackbox")

    # check for startpoints
    startpoints = c.startpoints(n)
    if len(startpoints) < 1:
        raise ValueError(f"{n} has no startpoints")

    # get input cone
    fi_nodes = c.transitive_fanin(n) | set([n])
    sub_c = Circuit(graph=c.graph.subgraph(fi_nodes).copy())

    # create sensitivity circuit
    sen = Circuit()
    sen.add_subcircuit(sub_c, "orig")
    for s in startpoints:
        sen.add(s, "input", fanout=f"orig_{s}")

    # add popcount
    sen.add_subcircuit(popcount(len(startpoints)), "pc")

    # add inverted input copies
    for i, s0 in enumerate(startpoints):
        sen.add_subcircuit(sub_c, f"inv_{s0}")

        # connect inputs
        for s1 in startpoints:
            if s0 != s1:
                sen.connect(s1, f"inv_{s0}_{s1}")
            else:
                # connect inverted input
                sen.set_type(f"inv_{s0}_{s1}", "not")
                sen.connect(s0, f"inv_{s0}_{s1}")

        # compare to orig
        sen.add(
            f"dif_{s0}",
            "xor",
            fanin=[f"orig_{n}", f"inv_{s0}_{n}"],
            fanout=f"pc_in_{i}",
        )
        sen.add(f"dif_out_{s0}", "output", fanin=f"dif_{s0}")

    # instantiate population count
    for o in range(clog2(len(startpoints) + 1)):
        sen.add(f"sen_out_{o}", "output", fanin=f"pc_out_{o}")

    return sen
Ejemplo n.º 12
0
def banyan(bw):
    """
    Create a Banyan switching network

    Parameters
    ----------
    bw : int
            Input/output width of the network.

    Returns
    -------
    Circuit
            Network circuit.
    """

    b = Circuit()

    # generate switch
    m = mux(2)
    s = Circuit(name="switch")
    s.add_subcircuit(m, f"m0")
    s.add_subcircuit(m, f"m1")
    s.add("in_0", "buf", fanout=["m0_in_0", "m1_in_1"])
    s.add("in_1", "buf", fanout=["m0_in_1", "m1_in_0"])
    s.add("out_0", "buf", fanin="m0_out")
    s.add("out_1", "buf", fanin="m1_out")
    s.add("sel", "input", fanout=["m0_sel_0", "m1_sel_0"])

    # generate banyan
    I = int(2 * clog2(bw) - 2)
    J = int(bw / 2)

    # add switches
    for i in range(I * J):
        b.add_subcircuit(s, f"swb_{i}")

    # make connections
    swb_ins = [f"swb_{i//2}_in_{i%2}" for i in range(I * J * 2)]
    swb_outs = [f"swb_{i//2}_out_{i%2}" for i in range(I * J * 2)]

    # connect switches
    for i in range(clog2(J)):
        r = J / (2**i)
        for j in range(J):
            t = (j % r) >= (r / 2)
            # straight
            out_i = int((i * bw) + (2 * j) + t)
            in_i = int((i * bw + bw) + (2 * j) + t)
            b.connect(swb_outs[out_i], swb_ins[in_i])

            # cross
            out_i = int((i * bw) + (2 * j) + (1 - t) + ((r - 1) *
                                                        ((1 - t) * 2 - 1)))
            in_i = int((i * bw + bw) + (2 * j) + (1 - t))
            b.connect(swb_outs[out_i], swb_ins[in_i])

            if r > 2:
                # straight
                out_i = int(((I * J * 2) - ((2 + i) * bw)) + (2 * j) + t)
                in_i = int(((I * J * 2) - ((1 + i) * bw)) + (2 * j) + t)
                b.connect(swb_outs[out_i], swb_ins[in_i])

                # cross
                out_i = int(((I * J * 2) - ((2 + i) * bw)) + (2 * j) +
                            (1 - t) + ((r - 1) * ((1 - t) * 2 - 1)))
                in_i = int(((I * J * 2) - ((1 + i) * bw)) + (2 * j) + (1 - t))
                b.connect(swb_outs[out_i], swb_ins[in_i])

    # create banyan io
    net_ins = swb_ins[:bw]
    net_outs = swb_outs[-bw:]

    for i, net_in in enumerate(net_ins):
        b.add(f"in_{i}", "input", fanout=net_in)
    for i, net_out in enumerate(net_outs):
        b.add(f"out_{i}", "output", fanin=net_out)
    for i in range(I * J):
        b.add(f"sel_{i}", "input", fanout=f"swb_{i}_sel")

    return b
Ejemplo n.º 13
0
def miter(c0, c1=None, startpoints=None, endpoints=None):
    """
    Creates a miter circuit

    Parameters
    ----------
    c0 : Circuit
            First circuit.
    c1 : Circuit
            Optional second circuit, if None c0 is mitered with itself.
    startpoints : set of str
            Nodes to be tied together, must exist in both circuits.
    endpoints : set of str
            Nodes to be compared, must exist in both circuits.

    Returns
    -------
    Circuit
            Miter circuit.
    """
    # check for blackboxes
    if c0.blackboxes:
        raise ValueError(f"{c0.name} contains a blackbox")
    if c1 and c1.blackboxes:
        raise ValueError(f"{c1.name} contains a blackbox")

    # clean inputs
    if not c1:
        c1 = c0
    if not startpoints:
        startpoints = c0.startpoints() & c1.startpoints()
    if not endpoints:
        endpoints = c0.endpoints() & c1.endpoints()

    # create miter, relabel
    m = Circuit(name=f"miter_{c0.name}_{c1.name}")
    m.add_subcircuit(c0, "c0")
    m.add_subcircuit(c1, "c1")

    # tie inputs
    for n in startpoints:
        m.add(n, "input", fanout=[f"c0_{n}", f"c1_{n}"])

    # compare outputs
    m.add("miter", "or")
    m.add("sat", "output", fanin="miter")
    for n in endpoints:
        m.add(f"dif_{n}", "xor", fanin=[f"c0_{n}", f"c1_{n}"], fanout="miter")

    return m
Ejemplo n.º 14
0
def influence_transform(c, n, s):
    """
    Creates a circuit to compute sensitivity.

    Parameters
    ----------
    c : Circuit
            Sequential circuit to unroll.
    n : str
            Node to compute influence at.
    s : str
            Startpoint to compute influence for.

    Returns
    -------
    Circuit
            Influence circuit.

    """
    # check for blackboxes
    if c.blackboxes:
        raise ValueError(f"{c.name} contains a blackbox")

    # check if s is in startpoints
    sp = c.startpoints(n)
    if s not in sp:
        raise ValueError(f"{s} is not in startpoints of {n}")

    # get input cone
    fi_nodes = c.transitive_fanin(n) | set([n])
    sub_c = Circuit("sub_cone", c.graph.subgraph(fi_nodes).copy())

    # create two copies of sub circuit, share inputs except s
    infl = Circuit(name=f"infl_{s}_on_{n}")
    infl.add_subcircuit(sub_c, "c0")
    infl.add_subcircuit(sub_c, "c1")
    for g in sp:
        if g != s:
            infl.add(g, "input", fanout=[f"c0_{g}", f"c1_{g}"])
        else:
            infl.add(f"not_{g}", "not", fanout=f"c1_{s}")
            infl.add(g, "input", fanout=[f"c0_{g}", f"not_{g}"])
    infl.add("dif", "xor", fanin=[f"c0_{n}", f"c1_{n}"])
    infl.add("sat", "output", fanin="dif")

    return infl
Ejemplo n.º 15
0
def adder(w):
    """
    Create an adder.

    Parameters
    ----------
    w: the width of the adder.

    Returns
    -------
    a `CircuitGraph` addder.
    """
    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
Ejemplo n.º 16
0
def comb_ff():
    lm = Circuit(name="ff")

    # mux
    m = mux(2).strip_io()
    lm.extend(m.relabel({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
Ejemplo n.º 17
0
def fast_parse_verilog_netlist(netlist, blackboxes):
    """
    A fast version of `parse_verilog_netlist` that can speed up parsing on
    very large netlists by making a handful of assumptions. It is much
    safer to use `parse_verilog_netlist`. This function should only be used
    if necessary.

    The input netlist must conform to the following rules:
        - Only one module definition is present
        - There are no comments
        - Assign statements must have a single net as the LHS, and the RHS
          must be a constant
        - The only constants that may be used are `1'b0` and `1'b1` (or h/d)
        - Primitive gates can only have one output
        - Instantiations must be named.
        - Only one instantation per line (e.g. `buf b1(a, b) b2(c, d);` is
          not allowed)
        - No expressions (e.g. `buf (a, b & c);` is not allowed)
        - No escaped identifiers
    
    The code does not overtly check that these rules are satisfied, and if
    they are not this function may still return a malformed Circuit object.
    It is up to the caller of the function to assure that these rules are
    followed.

    If an output is undriven, a driver for the output will still be added to
    the circuit, which is a discrepancy with `parse_verilog_netlist` (in which
    no output drive will be added).

    Note that thorough error checking that is done in `parse_verilog_netlist`
    is skipped in this function (e.g. checking if nets are declared as wires,
    checking if the portlist matches the input/output declarations, etc.).

    Note also that wires that are declared but not used will not be added to
    the circuit.

    Parameters
    ----------
    netlist: str
            Verilog code.
    blackboxes: seq of BlackBox
            Blackboxes in module.
    Returns
    -------
    Circuit
            Parsed circuit.
    """

    regex = f"module\s+(.+?)\s*\(.*?\);"
    m = re.search(regex, netlist, re.DOTALL)
    name = m.group(1)
    module = netlist[m.end():]

    regex = f"endmodule"
    m = re.search(regex, netlist, re.DOTALL)
    module = module[:m.start()]

    # create graph
    g = nx.DiGraph()

    # parse io
    regex = "(input)\s(.+?);"
    inputs = set()
    for net_type, net_str in re.findall(regex, module, re.DOTALL):
        nets = net_str.split(",")
        for net in nets:
            inputs.add(net.strip())
    g.add_nodes_from(inputs, type="input")

    regex = "(output)\s(.+?);"
    outputs = set()
    for net_type, net_str in re.findall(regex, module, re.DOTALL):
        nets = net_str.split(",")
        for net in nets:
            outputs.add(net.strip())
    g.add_nodes_from(outputs, type="output")

    # create output drivers, ensure unique names
    output_drivers = dict()
    for o in outputs:
        driver = f"{o}_driver"
        while driver in g:
            driver = f"{o}_driver_{random.randint(1111, 9999)}"
        output_drivers[o] = driver

    g.add_nodes_from(output_drivers.values(), type="wire")
    g.add_edges_from((v, k) for k, v in output_drivers.items())

    # create constants, (will be removed if unused)
    tie_0 = "tie0"
    while tie_0 in g:
        tie_0 = "tie0_{random.randint(1111, 9999)}"
    tie_1 = "tie1"
    while tie_1 in g:
        tie_1 = "tie1_{random.randint(1111, 9999)}"
    g.add_node(tie_0, type="0")
    g.add_node(tie_1, type="1")

    # parse insts
    regex = "([a-zA-Z][a-zA-Z\d_]*)\s+([a-zA-Z][a-zA-Z\d_]*)\s*\(([^;]+)\);"

    all_nets = defaultdict(list)
    all_edges = list()
    blackboxes_to_add = dict()
    for gate, inst, net_str in re.findall(regex, module, re.DOTALL):

        # parse generics
        if gate in addable_types:
            # parse nets
            nets = [n.strip() for n in net_str.split(",")]

            # check for outputs, replace constants
            nets = [
                output_drivers[n] if n in output_drivers else
                tie_0 if n == "1'b0" else tie_1 if n == "1'b1" else n
                for n in nets
            ]

            all_nets[gate].append(nets[0])
            all_edges += [(i, nets[0]) for i in nets[1:]]

        # parse non-generics
        else:
            # get blackbox definition
            try:
                bb = next(bb for bb in blackboxes if bb.name == gate)
            except:
                raise ValueError(f"blackbox {gate} not defined")

            # parse pins
            all_nets["bb_input"] += [f"{inst}.{n}" for n in bb.inputs()]
            all_nets["bb_output"] += [f"{inst}.{n}" for n in bb.outputs()]

            regex = "\.\s*(\S+)\s*\(\s*(\S+)\s*\)"
            connections = {}
            for pin, net in re.findall(regex, net_str):
                # check for outputs
                if net in output_drivers:
                    net = output_drivers[net]
                elif net == "1'b1":
                    net = tie_1
                elif net == "1'b0":
                    net = tie_0

                if pin in bb.inputs():
                    all_edges.append((net, f"{inst}.{pin}"))
                elif pin in bb.outputs():
                    # add intermediate net for outputs
                    all_nets["buf"].append(net)
                    all_edges.append((f"{inst}.{pin}", net))
                else:
                    raise ValueError(
                        f"node {pin} not defined for blackbox {gate}")

            blackboxes_to_add[inst] = bb

    regex = "assign\s+([a-zA-Z][a-zA-Z\d_]*)\s*=\s*([a-zA-Z\d][a-zA-Z\d_']*)\s*;"
    for n0, n1 in re.findall(regex, module):
        if n0 in output_drivers:
            n0 = output_drivers[n0]
        if n1 in output_drivers:
            n1 = output_drivers[n1]
        all_nets["buf"].append(n0)
        if n1 in ["1'b0", "1'h0", "1'd0"]:
            all_edges.append((tie_0, n0))
        elif n1 in ["1'b1", "1'h1", "1'd1"]:
            all_edges.append((tie_1, n0))
        else:
            all_edges.append((n1, n0))

    for k, v in all_nets.items():
        g.add_nodes_from(v, type=k)
    g.add_edges_from(all_edges)

    try:
        next(g.successors(tie_0))
    except StopIteration:
        g.remove_node(tie_0)

    try:
        next(g.successors(tie_1))
    except StopIteration:
        g.remove_node(tie_1)

    return Circuit(name=name, graph=g, blackboxes=blackboxes_to_add)
Ejemplo n.º 18
0
def popcount(w):
    """
    Create a population count circuit.

    Parameters
    ----------
    w: the width of the adder.

    Returns
    -------
    a `CircuitGraph` addder.
    """
    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 = adder(aw).strip_io()
        c.extend(a.relabel({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
Ejemplo n.º 19
0
def verilog_to_circuit(verilog, name, seq_types=None):
    """
    Creates a new Circuit from a verilog file.

    Parameters
    ----------
    path: str
            verilog code.
    name: str
            the module name.
    seq_types: list of dicts of str:str
            the sequential element types.

    Returns
    -------
    Circuit
            the parsed circuit.
    """
    if seq_types is None:
        seq_types = default_seq_types

    c = Circuit(name=name)
    with tempfile.TemporaryDirectory(prefix="circuitgraph") as d:
        codeparser = VerilogParser(outputdir=d, debug=False)
        ast = codeparser.parse(verilog, debug=False)
        description = ast.children()[0]

        module_def = [d for d in description.children() if d.name == name]
        if not module_def:
            raise ValueError(f"Module {name} not found")
        module_def = module_def[0]
        outputs = set()
        widths = dict()
        for child in module_def.children():
            if type(child) == ast_types.Paramlist:
                if child.children():
                    raise ValueError(
                        f"circuitgraph cannot parse parameters (line {child.lineno})"
                    )
            # Parse portlist
            elif type(child) == ast_types.Portlist:
                for cip in [
                        i for i in child.children()
                        if type(i) == ast_types.Ioport
                ]:
                    for ci in cip.children():
                        parse_io(c, ci, outputs)
            # Parse declarations
            elif type(child) == ast_types.Decl:
                if ast_types.Parameter in [type(i) for i in child.children()]:
                    raise ValueError(
                        f"circuitgraph cannot parse parameters (line {child.lineno})"
                    )
                for ci in [
                        i for i in child.children()
                        if type(i) in [ast_types.Input, ast_types.Output]
                ]:
                    parse_io(c, ci, outputs)
            # Parse instances
            elif type(child) == ast_types.InstanceList:
                for instance in child.instances:
                    if instance.module in [
                            "buf",
                            "not",
                            "and",
                            "nand",
                            "or",
                            "nor",
                            "xor",
                            "xnor",
                    ]:
                        gate = instance.module
                        dest = parse_argument(instance.portlist[0].argname, c)
                        sources = [
                            parse_argument(i.argname, c)
                            for i in instance.portlist[1:]
                        ]
                        c.add(dest,
                              gate,
                              fanin=sources,
                              output=dest in outputs)
                    elif instance.module in [i.name for i in seq_types]:
                        if instance.portlist[0].portname is None:
                            raise ValueError("circuitgraph can only parse "
                                             "sequential instances declared "
                                             "with port argument notation "
                                             f"(line {instance.lineno})")
                        seq_type = [
                            i for i in seq_types if i.name == instance.module
                        ][0]
                        ports = {
                            p.portname: parse_argument(p.argname, c)
                            for p in instance.portlist
                        }
                        c.add(
                            ports.get(seq_type.io.get("q")),
                            seq_type.seq_type,
                            fanin=ports.get(seq_type.io.get("d")),
                            clk=ports.get(seq_type.io.get("clk")),
                            r=ports.get(seq_type.io.get("r")),
                            s=ports.get(seq_type.io.get("s")),
                            output=ports.get(seq_type.io.get("q")) in outputs,
                        )
                    else:
                        raise ValueError(
                            "circuitgraph cannot parse instance of "
                            f"type {instance.module} (line "
                            f"{instance.lineno})")
            # Parse assigns
            elif type(child) == ast_types.Assign:
                dest = child.left.var
                dest = parse_argument(dest, c)
                if type(child.right.var) == ast_types.IntConst:
                    c.add(
                        dest,
                        f"{child.right.var.value[-1]}",
                        output=dest in outputs,
                    )
                elif issubclass(type(child.right.var), ast_types.Operator):
                    parse_operator(child.right.var, c, outputs, dest=dest)
                elif issubclass(type(child.right.var), ast_types.Concat):
                    raise ValueError(
                        "circuitgraph cannot parse concatenations "
                        f"(line {child.right.var.lineno})")
            else:
                raise ValueError(
                    "circuitgraph cannot parse statements of type "
                    f"{type(child)} (line {child.lineno})")

    return c
Ejemplo n.º 20
0
def bench_to_circuit(netlist, name):
    """
    Creates a new Circuit from a netlist string.

    Parameters
    ----------
    netlist: str
            netlist code.
    name: str
            the module name.

    Returns
    -------
    Circuit
            the parsed circuit.
    """
    # create circuit
    c = Circuit(name=name)

    # get inputs
    in_regex = r"(?:INPUT|input)\s*\(\s*([a-zA-Z][a-zA-Z\d_]*)\s*\)"
    for net_str in re.findall(in_regex, netlist, re.DOTALL):
        nets = net_str.replace(" ", "").replace("\n",
                                                "").replace("\t",
                                                            "").split(",")
        for n in nets:
            c.add(n, "input")

    # handle gates
    regex = r"([a-zA-Z][a-zA-Z\d_]*)\s*=\s*(BUF|NOT|OR|NOR|AND|NAND|XOR|XNOR|buf|not|or|nor|and|nand|not|xor|xnor)\(([^\)]+)\)"
    for net, gate, input_str in re.findall(regex, netlist):
        # parse all nets
        inputs = (input_str.replace(" ",
                                    "").replace("\n",
                                                "").replace("\t",
                                                            "").split(","))
        c.add(net, gate.lower(), fanin=inputs)

    # get outputs
    in_regex = r"(?:OUTPUT|output)\s*\(\s*([a-zA-Z][a-zA-Z\d_]*)\s*\)"
    for net_str in re.findall(in_regex, netlist, re.DOTALL):
        nets = net_str.replace(" ", "").replace("\n",
                                                "").replace("\t",
                                                            "").split(",")
        for n in nets:
            driver = c.uid(f"{n}_driver")
            c.relabel({n: driver})
            c.add(n, "output", fanin=driver)

    return c
Ejemplo n.º 21
0
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)]
    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), f"add_{i}")
        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}", "output", fanin=o)

    if not c.fanout("tie0"):
        c.remove("tie0")

    return c
Ejemplo n.º 22
0
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"sum_{i}", "xor", fanin=[f"a_{i}", f"b_{i}", carry])
        c.add(f"out_{i}", "output", fanin=f"sum_{i}")

        # 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}", "output", fanin=carry)
    return c
Ejemplo n.º 23
0
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("or", "or")
    c.add("out", "output", fanin="or")

    i = 0
    for sel in product(*sels[::-1]):
        c.add(f"and_{i}", "and", fanin=[*sel, f"in_{i}"], fanout="or")

        i += 1
        if i == w:
            break

    return c