Module circuitgraph.circuitgraph

Holds a graphical representation of a circuit

Expand source code
"""Holds a graphical representation of a circuit"""
import re
import code
import tempfile
import networkx as nx
from pysat.formula import CNF,IDPool
from pysat.solvers import Cadical
from subprocess import PIPE,Popen

class Circuit:

        def __init__(self,verilog=None,path=None,name=None,graph=None):
                """Create a new Circuit"""
                if name:
                        self.name = name
                elif path:
                        self.name = path.split('/')[-1].replace('.v','')
                else:
                        self.name = 'circuit'

                if verilog or path:
                        self.graph = self.parseModule(verilog,path)
                elif graph:
                        self.graph = graph
                else:
                        self.graph = nx.DiGraph()

        def __contains__(self,n):
                return self.graph.__contains__(n)

        def __len__(self):
                return self.graph.__len__()

        def __iter__(self):
                return self.graph.__iter__()

        def type(self,n):
                return self.graph.nodes[n]['type']

        def nodes(self,types=None):
                if types is None:
                        return self.graph.nodes
                else:
                        if isinstance(types,str): types = [types]
                        return set(n for n in self.nodes() if self.type(n) in types)

        def edges(self):
                return self.graph.edges

        def relabel(self,mapping,name=None):
                return Circuit(graph=nx.relabel_nodes(self.graph,mapping),name=name)

        def add(self,n,type,fanin=None,fanout=None):
                if fanin is None: fanin=[]
                if fanout is None: fanout=[]
                self.graph.add_node(n,type=type)
                self.graph.add_edges_from((f,n) for f in fanin)
                self.graph.add_edges_from((n,f) for f in fanout)

        def extend(self,c):
                self.graph.update(c.graph)

        def transitiveFanout(self,ns,stopat=['d'],gates=None):
                if gates is None: gates=set()
                if isinstance(ns,str): ns = [ns]
                for n in ns:
                        for s in self.graph.successors(n):
                                if s not in gates:
                                        gates.add(s)
                                        if self.type(s) not in stopat:
                                                self.transitiveFanout(s,stopat,gates)
                return gates

        def transitiveFanin(self,ns,stopat=['ff','lat'],gates=None):
                if gates is None: gates=set()
                if isinstance(ns,str): ns = [ns]
                for n in ns:
                        for p in self.graph.predecessors(n):
                                if p not in gates:
                                        gates.add(p)
                                        if self.type(p) not in stopat:
                                                self.transitiveFanin(p,stopat,gates)
                return gates

        def fanout(self,ns):
                gates = set()
                if isinstance(ns,str): ns = [ns]
                for n in ns:
                        gates |= set(self.graph.successors(n))
                return gates

        def fanin(self,ns):
                gates = set()
                if isinstance(ns,str): ns = [ns]
                for n in ns:
                        gates |= set(self.graph.predecessors(n))
                return gates

        def lats(self): return self.nodes('lat')
        def ffs(self): return self.nodes('ff')
        def seq(self): return self.nodes(['ff','lat'])
        def inputs(self): return self.nodes('input')
        def outputs(self): return self.nodes('input')
        def io(self): return self.nodes(['output','input'])

        def startpoints(self,ns=None):
                if ns:
                        return set(n for n in self.transitiveFanin(ns) if self.type(n) in ['ff','lat','input'])
                else:
                        return set(n for n in self.graph if self.type(n) in ['ff','lat','input'])

        def endpoints(self,ns=None):
                if ns:
                        return set(n for n in self.transitiveFanout(ns) if self.type(n) in ['d','output'])
                else:
                        return set(n for n in self.graph if self.type(n) in ['d','output'])

        def seqGraph(self):
                graph = nx.DiGraph()

                # add nodes
                for n in self.io()|self.seq():
                        graph.add_node(n,gate=self.type(n))

                # add edges
                for n in graph.nodes:
                        graph.add_edges_from((s,n) for s in self.startpoints(n))

                return graph

        def sat(self,true=None,false=None):
                solver,clauses,variables = self.solver(true,false)
                if solver.solve():
                        model = solver.get_model()
                        return {n:model[variables.id(n)-1]>0 for n in self.nodes()}
                else:
                        return False

        def solver(self,true=None,false=None):
                if true is None: true = set()
                if false is None: false = set()
                clauses,variables = self.cnf()
                for n in true: clauses.append([variables.id(n)])
                for n in false: clauses.append([-variables.id(n)])
                solver = Cadical(bootstrap_with=clauses)
                return solver,clauses,variables

        def cnf(self):
                variables = IDPool()
                clauses = CNF()

                for n in self.nodes():
                        variables.id(n)
                        if self.type(n) == 'and':
                                for f in self.fanin(n):
                                        clauses.append([-variables.id(n),variables.id(f)])
                                clauses.append([variables.id(n)] + [-variables.id(f) for f in self.fanin(n)])
                        elif self.type(n) == 'nand':
                                for f in self.fanin(n):
                                        clauses.append([variables.id(n),variables.id(f)])
                                clauses.append([-variables.id(n)] + [-variables.id(f) for f in self.fanin(n)])
                        elif self.type(n) == 'or':
                                for f in self.fanin(n):
                                        clauses.append([variables.id(n),-variables.id(f)])
                                clauses.append([-variables.id(n)] + [variables.id(f) for f in self.fanin(n)])
                        elif self.type(n) == 'nor':
                                for f in self.fanin(n):
                                        clauses.append([-variables.id(n),-variables.id(f)])
                                clauses.append([variables.id(n)] + [variables.id(f) for f in self.fanin(n)])
                        elif self.type(n) == 'not':
                                f = self.fanin(n).pop()
                                clauses.append([variables.id(n),variables.id(f)])
                                clauses.append([-variables.id(n),-variables.id(f)])
                        elif self.type(n) in ['output','d','r','buf','clk']:
                                f = self.fanin(n).pop()
                                clauses.append([variables.id(n),-variables.id(f)])
                                clauses.append([-variables.id(n),variables.id(f)])
                        elif self.type(n) in ['xor','xnor']:
                                # break into heirarchical xors
                                nets = list(self.fanin(n))

                                # xor gen
                                def xorClauses(a,b,c):
                                        clauses.append([-variables.id(c),-variables.id(b),-variables.id(a)])
                                        clauses.append([-variables.id(c),variables.id(b),variables.id(a)])
                                        clauses.append([variables.id(c),-variables.id(b),variables.id(a)])
                                        clauses.append([variables.id(c),variables.id(b),-variables.id(a)])

                                while len(nets)>2:
                                        #create new net
                                        new_net = 'xor_'+nets[-2]+'_'+nets[-1]
                                        variables.id(new_net)

                                        # add sub xors
                                        xorClauses(nets[-2],nets[-1],new_net)

                                        # remove last 2 nets
                                        nets = nets[:-2]

                                        # insert before out
                                        nets.insert(0,new_net)

                                # add final xor
                                if self.type(n) == 'xor':
                                        xorClauses(nets[-2],nets[-1],n)
                                else:
                                        # invert xor
                                        variables.id(f'xor_inv_{n}')
                                        xorClauses(nets[-2],nets[-1],f'xor_inv_{n}')
                                        clauses.append([variables.id(n),variables.id(f'xor_inv_{n}')])
                                        clauses.append([-variables.id(n),-variables.id(f'xor_inv_{n}')])
                        elif self.type(n) == '0':
                                clauses.append([-variables.id(n)])
                        elif self.type(n) == '1':
                                clauses.append([variables.id(n)])
                        elif self.type(n) in ['ff','lat','input']:
                                pass
                        else:
                                print(f"unknown gate type: {self.type(n)}")
                                code.interact(local=dict(globals(), **locals()))

                return clauses,variables

        def verilog(self):
                inputs = []
                outputs = []
                insts = []
                wires = []

                for n in self.nodes():
                        if c.type(n) in ['xor','xnor','buf','not','nor','or','and','nand']:
                                fanin = ','.join(p for p in c.fanin(n))
                                insts.append(f"{c.type(n)} g_{n} ({n},{fanin})")
                                wires.append(n)
                        elif c.type(n) in ['0','1']:
                                insts.append(f"assign {n} = 1'b{c.type()}")
                        elif c.type(n) in ['input']:
                                inputs.append(n)
                                wires.append(n)
                        elif c.type(n) in ['output']:
                                outputs.append(n.replace('output[','')[:-1])
                        elif c.type(n) in ['ff']:
                                d = c.fanin(f'd[{n}]').pop()
                                clk = c.fanin(f'clk[{n}]').pop()
                                insts.append(f"fflopd g_{n} (.CK({clk}),.D({d}),.Q({n}))")
                        elif c.type(n) in ['lat']:
                                d = c.fanin(f'd[{n}]').pop()
                                clk = c.fanin(f'clk[{n}]').pop()
                                r = c.fanin(f'r[{n}]').pop()
                                insts.append(f"latchdrs g_{n} (.ENA({clk}),.D({d}),.R({r}),.S(1'b1),.Q({n}))")
                        elif c.type(n) in ['clk','d','r']:
                                pass
                        else:
                                print(f"unknown gate type: {c.type(n)}")
                                return

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

                return verilog

        def syn(self,printOutput=False):
                verilog = self.verilog()

                with tempfile.NamedTemporaryFile() as tmp:
                        cmd = ['genus','-execute',f"""set_db / .library $env(GENUS_DIR)/share/synth/tutorials/tech/tutorial.lib;
                                        read_hdl -sv {tmp.name};
                                        elaborate;
                                        set_db syn_generic_effort high
                                        syn_generic;
                                        syn_map;
                                        syn_opt;
                                        write_hdl -generic;
                                        exit;"""]
                        tmp.write(bytes(verilog,'ascii'))
                        tmp.flush()
                        #code.interact(local=dict(globals(), **locals()))

                        process = Popen(cmd,stdout=PIPE,stderr=PIPE,universal_newlines=True)
                        output = ''
                        while True:
                                line = process.stdout.readline()
                                if line == '' and process.poll() is not None:
                                        break
                                if line:
                                        if printOutput:
                                                print(line.strip())
                                        output += line

                regex = "(module.*endmodule)"
                m = re.search(regex,output,re.DOTALL)
                syn_verilog = m.group(1)

                c = Circuit(verilog=syn_verilog,name=self.name)
                c.name = f'{self.name}_syn'
                return c

        def two_input(self):
                two_inp_c = nx.DiGraph()

                # create nodes
                for n in self.nodes():
                        two_inp_c.add_node(n)
                        for a,v in c.nodes[n].items():
                                two_inp_c.nodes[n][a] = v

                # connect nodes
                for n in self.nodes():
                        # handle fanin
                        pred = list(c.predecessors(n))

                        # select new gate type
                        if c.nodes[n]['type'] in ['and','nand']:
                                t = 'and'
                        elif c.nodes[n]['type'] in ['or','nor']:
                                t = 'or'
                        elif c.nodes[n]['type'] in ['xor','xnor']:
                                t = 'xor'

                        while len(pred)>2:
                                # create new, connect
                                two_inp_c.add_node(f'{n}_add_{len(pred)}',output=False,gate=t)
                                two_inp_c.add_edges_from((p,f'{n}_add_{len(pred)}') for p in pred[0:2])

                                # update list
                                pred.append(f'{n}_add_{len(pred)}')
                                pred = pred[2:]

                        # add final input connections
                        two_inp_c.add_edges_from((p,n) for p in pred)

                        # ensure all are two inputs
                        for n in two_inp_c.nodes():
                                if two_inp_c.in_degree(n)>2:
                                        print(f"gate with more than 2 inputs")
                                        code.interact(local=dict(globals(), **locals()))

                return two_inp_c

        def dual_rail_ternary_encoding(self):
                d = deepcopy(c)

                # add dual nodes
                for n in c:
                        if c.nodes[n]['type'] in ['and','nand']:
                                d.add_node(f'{n}_x',gate='and',output=c.nodes[n]['output'])
                                d.add_node(f'{n}_x_in_fi',gate='or',output=False)
                                d.add_node(f'{n}_0_not_in_fi',gate='nor',output=False)
                                d.add_edges_from([(f'{n}_x_in_fi',f'{n}_x'),(f'{n}_0_not_in_fi',f'{n}_x')])
                                d.add_edges_from((f'{p}_x',f'{n}_x_in_fi') for p in c.predecessors(n))
                                for p in c.predecessors(n):
                                        d.add_node(f'{p}_is_0',gate='nor',output=False)
                                        d.add_edge(f'{p}_is_0',f'{n}_0_not_in_fi')
                                        d.add_edge(f'{p}_x',f'{p}_is_0')
                                        d.add_edge(p,f'{p}_is_0')

                        elif c.nodes[n]['type'] in ['or','nor']:
                                d.add_node(f'{n}_x',gate='and',output=c.nodes[n]['output'])
                                d.add_node(f'{n}_x_in_fi',gate='or',output=False)
                                d.add_node(f'{n}_1_not_in_fi',gate='nor',output=False)
                                d.add_edges_from([(f'{n}_x_in_fi',f'{n}_x'),(f'{n}_1_not_in_fi',f'{n}_x')])
                                d.add_edges_from((f'{p}_x',f'{n}_x_in_fi') for p in c.predecessors(n))
                                for p in c.predecessors(n):
                                        d.add_node(f'{p}_is_1',gate='and',output=False)
                                        d.add_edge(f'{p}_is_1',f'{n}_1_not_in_fi')
                                        d.add_node(f'{p}_not_x',gate='not',output=False)
                                        d.add_edge(f'{p}_x',f'{p}_not_x')
                                        d.add_edge(f'{p}_not_x',f'{p}_is_1')
                                        d.add_edge(p,f'{p}_is_1')

                        elif c.nodes[n]['type'] in ['buf','not']:
                                d.add_node(f'{n}_x',gate='buf',output=c.nodes[n]['output'])
                                p = list(c.predecessors(n))[0]
                                d.add_edge(f'{p}_x',f'{n}_x')

                        elif c.nodes[n]['type'] in ['xor','xnor']:
                                d.add_node(f'{n}_x',gate='or',output=c.nodes[n]['output'])
                                d.add_edges_from((f'{p}_x',f'{n}_x') for p in c.predecessors(n))

                        elif c.nodes[n]['type'] in ['0','1']:
                                d.add_node(f'{n}_x',gate='0',output=c.nodes[n]['output'])

                        elif c.nodes[n]['type'] in ['input']:
                                d.add_node(f'{n}_x',gate='input',output=c.nodes[n]['output'])

                        elif c.nodes[n]['type'] in ['dff']:
                                d.add_node(f'{n}_x',gate='dff',output=c.nodes[n]['output'],clk=c.nodes[n]['clk'])
                                p = list(c.predecessors(n))[0]
                                d.add_edge(f'{p}_x',f'{n}_x')

                        elif c.nodes[n]['type'] in ['lat']:
                                d.add_node(f'{n}_x',gate='lat',output=c.nodes[n]['output'],clk=c.nodes[n]['clk'],rst=c.nodes[n]['rst'])
                                p = list(c.predecessors(n))[0]
                                d.add_edge(f'{p}_x',f'{n}_x')

                        else:
                                print(f"unknown gate type: {c.nodes[n]['type']}")
                                code.interact(local=locals())

                for n in d:
                        if 'type' not in d.nodes[n]:
                                print(f"empty gate type: {n}")
                                code.interact(local=locals())

                return d

        def parseModule(self,verilog,path):
                # read verilog
                if path:
                        with open(path, 'r') as f:
                                verilog = f.read()

                # find module
                regex = f"module\s+{self.name}\s*\(.*?\);(.*?)endmodule"
                m = re.search(regex,verilog,re.DOTALL)
                module =  m.group(1)

                # create graph
                G = nx.DiGraph()

                # handle gates
                regex = "(or|nor|and|nand|not|xor|xnor)\s+\S+\s*\((.+?)\);"
                for gate, net_str in re.findall(regex,module,re.DOTALL):

                        # parse all nets
                        nets = net_str.replace(" ","").replace("\n","").replace("\t","").split(",")
                        output = nets[0]
                        inputs = nets[1:]

                        # add to graph
                        G.add_edges_from((net,output) for net in inputs)
                        G.nodes[output]['type'] = gate

                # handle ffs
                regex = "fflopd\s+\S+\s*\(\.CK\s*\((.+?)\),\s*.D\s*\((.+?)\),\s*.Q\s*\((.+?)\)\);"
                for clk,d,q in re.findall(regex,module,re.DOTALL):

                        # add to graph
                        G.add_node(q,type='ff')

                        G.add_edge(d,f'd[{q}]')
                        G.nodes[f'd[{q}]']['type'] = 'd'
                        G.add_edge(f'd[{q}]',q)

                        G.add_edge(clk,f'clk[{q}]')
                        G.nodes[f'clk[{q}]']['type'] = 'clk'
                        G.add_edge(f'clk[{q}]',q)

                # handle lats
                regex = "latchdrs\s+\S+\s*\(\s*\.R\s*\((.+?)\),\s*\.S\s*\((.+?)\),\s*\.ENA\s*\((.+?)\),\s*.D\s*\((.+?)\),\s*.Q\s*\((.+?)\)\s*\);"
                for r,s,c,d,q in re.findall(regex,module,re.DOTALL):

                        # add to graph
                        G.add_node(q,type='lat')

                        G.add_edge(d,f'd[{q}]')
                        G.nodes[f'd[{q}]']['type'] = 'd'
                        G.add_edge(f'd[{q}]',q)

                        G.add_edge(clk,f'clk[{q}]')
                        G.nodes[f'clk[{q}]']['type'] = 'clk'
                        G.add_edge(f'clk[{q}]',q)

                        G.add_edge(d,f'r[{q}]')
                        G.nodes[f'r[{q}]']['type'] = 'r'
                        G.add_edge(f'r[{q}]',q)

                # handle assigns
                assign_regex = "assign\s+(.+?)\s*=\s*(.+?);"
                for n0, n1 in re.findall(assign_regex,module):
                        output = n0.replace(' ','')
                        inpt = n1.replace(' ','')
                        G.add_edge(inpt,output)
                        G.nodes[output]['type'] = 'buf'

                for n in G.nodes():
                        if 'type' not in G.nodes[n]:
                                if n == "1'b0":
                                        G.nodes[n]['type'] = '0'
                                elif n == "1'b1":
                                        G.nodes[n]['type'] = '1'
                                else:
                                        G.nodes[n]['type'] = 'input'

                # get outputs
                out_regex = "output\s(.+?);"
                for net_str in re.findall(out_regex,module,re.DOTALL):
                        nets = net_str.replace(" ","").replace("\n","").replace("\t","").split(",")
                        for net in nets:
                                G.add_edge(net,f'output[{net}]')
                                G.nodes[f'output[{net}]']['type'] = 'output'

                return G

        def miter(self,c=None,inputs=None,outputs=None):
                if not c:
                        c = self
                if not inputs:
                        inputs = self.startpoints()
                if not outputs:
                        outputs = self.endpoints()

                # get inputs to be used in miter
                common_inputs = self.startpoints()&inputs
                common_outputs = self.endpoints()&outputs

                # create miter
                m = c.relabel({n:f'c0_{n}' for n in c.nodes()-common_inputs})
                m.extend(self.relabel({n:f'c1_{n}' for n in c.nodes()-common_inputs}))

                # compare outputs
                m.add('sat','or')
                for o in common_outputs:
                        m.add(f'miter_{o}','xor',fanin=[f'c0_{o}',f'c1_{o}'],fanout=['sat'])

                return m

        def unroll(self,cycles):
                pass


if __name__=='__main__':
        # test class

        verilog = """
// Generated by Cadence Genus(TM) Synthesis Solution 16.22-s033_1
// Generated on: Jun 19 2020 15:25:45 EDT (Jun 19 2020 19:25:45 UTC)

// Verification Directory fv/s27

module s27(clk, G0, G1, G17, G2, G3);
  input clk, G0, G1, G2, G3;
  output G17;
  wire clk, G0, G1, G2, G3;
  wire G17;
  wire G5, G6, G7, n_0, n_1, n_2, n_3, n_4;
  wire n_5, n_6, n_7, n_8, n_9, n_10, n_11, n_12;
  wire n_20, n_21;
  fflopd DFF_0_Q_reg(.CK (clk), .D (n_12), .Q (G5));
  fflopd DFF_1_Q_reg(.CK (clk), .D (n_21), .Q (G6));
  not g543 (G17, n_20);
  not g545 (n_12, n_11);
  nand g546__7837 (n_11, G0, n_9);
  nor g548__7557 (n_10, n_7, n_8);
  nand g549__7654 (n_9, n_1, n_8);
  fflopd DFF_2_Q_reg(.CK (clk), .D (n_6), .Q (G7));
  nor g551__8867 (n_8, G7, n_4);
  not g553 (n_7, n_5);
  nor g550__1377 (n_6, G2, n_3);
  nand g554__3717 (n_5, n_2, G6);
  nand g552__4599 (n_4, G3, n_0);
  nor g555__3779 (n_3, G1, G7);
  not g557 (n_2, G0);
  not g556 (n_1, G5);
  not g558 (n_0, G1);
  nor g562__2007 (n_20, G5, n_10);
  nor g544_dup__1237 (n_21, G5, n_10);
endmodule


module fflopd(CK, D, Q);
  input CK, D;
  output Q;
  wire CK, D;
  wire Q;
  wire next_state;
  reg  qi;
  assign #1 Q = qi;
  assign next_state = D;
  always
    @(posedge CK)
      qi <= next_state;
  initial
    qi <= 1'b0;
endmodule

"""

        # parse circuit
        c = Circuit(verilog=verilog,name='s27')
        print(f'parsed: {c.name}')
        print(f'nodes: {c.nodes()}')
        print(f'edges: {c.edges()}')
        for n in c: print(f'type of {n}: {c.type(n)}')
        print(f'len: {len(c)}')
        print(f"contains: {'G1' in c}")

        # check self equivalence
        m = c.miter()
        live = m.sat()
        print(f'self live: {live}')
        equiv = m.sat(true=['sat'])
        print(f'self equiv: {not equiv}')
        #code.interact(local=dict(globals(), **locals()))

        # synthesize and check equiv
        s = c.syn(True)
        m = c.miter(s)
        live = m.sat()
        print(f'syn live: {live}')
        equiv = m.sat(true=['sat'])
        print(f'syn equiv: {not equiv}')

Classes

class Circuit (verilog=None, path=None, name=None, graph=None)

Create a new Circuit

Expand source code
class Circuit:

        def __init__(self,verilog=None,path=None,name=None,graph=None):
                """Create a new Circuit"""
                if name:
                        self.name = name
                elif path:
                        self.name = path.split('/')[-1].replace('.v','')
                else:
                        self.name = 'circuit'

                if verilog or path:
                        self.graph = self.parseModule(verilog,path)
                elif graph:
                        self.graph = graph
                else:
                        self.graph = nx.DiGraph()

        def __contains__(self,n):
                return self.graph.__contains__(n)

        def __len__(self):
                return self.graph.__len__()

        def __iter__(self):
                return self.graph.__iter__()

        def type(self,n):
                return self.graph.nodes[n]['type']

        def nodes(self,types=None):
                if types is None:
                        return self.graph.nodes
                else:
                        if isinstance(types,str): types = [types]
                        return set(n for n in self.nodes() if self.type(n) in types)

        def edges(self):
                return self.graph.edges

        def relabel(self,mapping,name=None):
                return Circuit(graph=nx.relabel_nodes(self.graph,mapping),name=name)

        def add(self,n,type,fanin=None,fanout=None):
                if fanin is None: fanin=[]
                if fanout is None: fanout=[]
                self.graph.add_node(n,type=type)
                self.graph.add_edges_from((f,n) for f in fanin)
                self.graph.add_edges_from((n,f) for f in fanout)

        def extend(self,c):
                self.graph.update(c.graph)

        def transitiveFanout(self,ns,stopat=['d'],gates=None):
                if gates is None: gates=set()
                if isinstance(ns,str): ns = [ns]
                for n in ns:
                        for s in self.graph.successors(n):
                                if s not in gates:
                                        gates.add(s)
                                        if self.type(s) not in stopat:
                                                self.transitiveFanout(s,stopat,gates)
                return gates

        def transitiveFanin(self,ns,stopat=['ff','lat'],gates=None):
                if gates is None: gates=set()
                if isinstance(ns,str): ns = [ns]
                for n in ns:
                        for p in self.graph.predecessors(n):
                                if p not in gates:
                                        gates.add(p)
                                        if self.type(p) not in stopat:
                                                self.transitiveFanin(p,stopat,gates)
                return gates

        def fanout(self,ns):
                gates = set()
                if isinstance(ns,str): ns = [ns]
                for n in ns:
                        gates |= set(self.graph.successors(n))
                return gates

        def fanin(self,ns):
                gates = set()
                if isinstance(ns,str): ns = [ns]
                for n in ns:
                        gates |= set(self.graph.predecessors(n))
                return gates

        def lats(self): return self.nodes('lat')
        def ffs(self): return self.nodes('ff')
        def seq(self): return self.nodes(['ff','lat'])
        def inputs(self): return self.nodes('input')
        def outputs(self): return self.nodes('input')
        def io(self): return self.nodes(['output','input'])

        def startpoints(self,ns=None):
                if ns:
                        return set(n for n in self.transitiveFanin(ns) if self.type(n) in ['ff','lat','input'])
                else:
                        return set(n for n in self.graph if self.type(n) in ['ff','lat','input'])

        def endpoints(self,ns=None):
                if ns:
                        return set(n for n in self.transitiveFanout(ns) if self.type(n) in ['d','output'])
                else:
                        return set(n for n in self.graph if self.type(n) in ['d','output'])

        def seqGraph(self):
                graph = nx.DiGraph()

                # add nodes
                for n in self.io()|self.seq():
                        graph.add_node(n,gate=self.type(n))

                # add edges
                for n in graph.nodes:
                        graph.add_edges_from((s,n) for s in self.startpoints(n))

                return graph

        def sat(self,true=None,false=None):
                solver,clauses,variables = self.solver(true,false)
                if solver.solve():
                        model = solver.get_model()
                        return {n:model[variables.id(n)-1]>0 for n in self.nodes()}
                else:
                        return False

        def solver(self,true=None,false=None):
                if true is None: true = set()
                if false is None: false = set()
                clauses,variables = self.cnf()
                for n in true: clauses.append([variables.id(n)])
                for n in false: clauses.append([-variables.id(n)])
                solver = Cadical(bootstrap_with=clauses)
                return solver,clauses,variables

        def cnf(self):
                variables = IDPool()
                clauses = CNF()

                for n in self.nodes():
                        variables.id(n)
                        if self.type(n) == 'and':
                                for f in self.fanin(n):
                                        clauses.append([-variables.id(n),variables.id(f)])
                                clauses.append([variables.id(n)] + [-variables.id(f) for f in self.fanin(n)])
                        elif self.type(n) == 'nand':
                                for f in self.fanin(n):
                                        clauses.append([variables.id(n),variables.id(f)])
                                clauses.append([-variables.id(n)] + [-variables.id(f) for f in self.fanin(n)])
                        elif self.type(n) == 'or':
                                for f in self.fanin(n):
                                        clauses.append([variables.id(n),-variables.id(f)])
                                clauses.append([-variables.id(n)] + [variables.id(f) for f in self.fanin(n)])
                        elif self.type(n) == 'nor':
                                for f in self.fanin(n):
                                        clauses.append([-variables.id(n),-variables.id(f)])
                                clauses.append([variables.id(n)] + [variables.id(f) for f in self.fanin(n)])
                        elif self.type(n) == 'not':
                                f = self.fanin(n).pop()
                                clauses.append([variables.id(n),variables.id(f)])
                                clauses.append([-variables.id(n),-variables.id(f)])
                        elif self.type(n) in ['output','d','r','buf','clk']:
                                f = self.fanin(n).pop()
                                clauses.append([variables.id(n),-variables.id(f)])
                                clauses.append([-variables.id(n),variables.id(f)])
                        elif self.type(n) in ['xor','xnor']:
                                # break into heirarchical xors
                                nets = list(self.fanin(n))

                                # xor gen
                                def xorClauses(a,b,c):
                                        clauses.append([-variables.id(c),-variables.id(b),-variables.id(a)])
                                        clauses.append([-variables.id(c),variables.id(b),variables.id(a)])
                                        clauses.append([variables.id(c),-variables.id(b),variables.id(a)])
                                        clauses.append([variables.id(c),variables.id(b),-variables.id(a)])

                                while len(nets)>2:
                                        #create new net
                                        new_net = 'xor_'+nets[-2]+'_'+nets[-1]
                                        variables.id(new_net)

                                        # add sub xors
                                        xorClauses(nets[-2],nets[-1],new_net)

                                        # remove last 2 nets
                                        nets = nets[:-2]

                                        # insert before out
                                        nets.insert(0,new_net)

                                # add final xor
                                if self.type(n) == 'xor':
                                        xorClauses(nets[-2],nets[-1],n)
                                else:
                                        # invert xor
                                        variables.id(f'xor_inv_{n}')
                                        xorClauses(nets[-2],nets[-1],f'xor_inv_{n}')
                                        clauses.append([variables.id(n),variables.id(f'xor_inv_{n}')])
                                        clauses.append([-variables.id(n),-variables.id(f'xor_inv_{n}')])
                        elif self.type(n) == '0':
                                clauses.append([-variables.id(n)])
                        elif self.type(n) == '1':
                                clauses.append([variables.id(n)])
                        elif self.type(n) in ['ff','lat','input']:
                                pass
                        else:
                                print(f"unknown gate type: {self.type(n)}")
                                code.interact(local=dict(globals(), **locals()))

                return clauses,variables

        def verilog(self):
                inputs = []
                outputs = []
                insts = []
                wires = []

                for n in self.nodes():
                        if c.type(n) in ['xor','xnor','buf','not','nor','or','and','nand']:
                                fanin = ','.join(p for p in c.fanin(n))
                                insts.append(f"{c.type(n)} g_{n} ({n},{fanin})")
                                wires.append(n)
                        elif c.type(n) in ['0','1']:
                                insts.append(f"assign {n} = 1'b{c.type()}")
                        elif c.type(n) in ['input']:
                                inputs.append(n)
                                wires.append(n)
                        elif c.type(n) in ['output']:
                                outputs.append(n.replace('output[','')[:-1])
                        elif c.type(n) in ['ff']:
                                d = c.fanin(f'd[{n}]').pop()
                                clk = c.fanin(f'clk[{n}]').pop()
                                insts.append(f"fflopd g_{n} (.CK({clk}),.D({d}),.Q({n}))")
                        elif c.type(n) in ['lat']:
                                d = c.fanin(f'd[{n}]').pop()
                                clk = c.fanin(f'clk[{n}]').pop()
                                r = c.fanin(f'r[{n}]').pop()
                                insts.append(f"latchdrs g_{n} (.ENA({clk}),.D({d}),.R({r}),.S(1'b1),.Q({n}))")
                        elif c.type(n) in ['clk','d','r']:
                                pass
                        else:
                                print(f"unknown gate type: {c.type(n)}")
                                return

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

                return verilog

        def syn(self,printOutput=False):
                verilog = self.verilog()

                with tempfile.NamedTemporaryFile() as tmp:
                        cmd = ['genus','-execute',f"""set_db / .library $env(GENUS_DIR)/share/synth/tutorials/tech/tutorial.lib;
                                        read_hdl -sv {tmp.name};
                                        elaborate;
                                        set_db syn_generic_effort high
                                        syn_generic;
                                        syn_map;
                                        syn_opt;
                                        write_hdl -generic;
                                        exit;"""]
                        tmp.write(bytes(verilog,'ascii'))
                        tmp.flush()
                        #code.interact(local=dict(globals(), **locals()))

                        process = Popen(cmd,stdout=PIPE,stderr=PIPE,universal_newlines=True)
                        output = ''
                        while True:
                                line = process.stdout.readline()
                                if line == '' and process.poll() is not None:
                                        break
                                if line:
                                        if printOutput:
                                                print(line.strip())
                                        output += line

                regex = "(module.*endmodule)"
                m = re.search(regex,output,re.DOTALL)
                syn_verilog = m.group(1)

                c = Circuit(verilog=syn_verilog,name=self.name)
                c.name = f'{self.name}_syn'
                return c

        def two_input(self):
                two_inp_c = nx.DiGraph()

                # create nodes
                for n in self.nodes():
                        two_inp_c.add_node(n)
                        for a,v in c.nodes[n].items():
                                two_inp_c.nodes[n][a] = v

                # connect nodes
                for n in self.nodes():
                        # handle fanin
                        pred = list(c.predecessors(n))

                        # select new gate type
                        if c.nodes[n]['type'] in ['and','nand']:
                                t = 'and'
                        elif c.nodes[n]['type'] in ['or','nor']:
                                t = 'or'
                        elif c.nodes[n]['type'] in ['xor','xnor']:
                                t = 'xor'

                        while len(pred)>2:
                                # create new, connect
                                two_inp_c.add_node(f'{n}_add_{len(pred)}',output=False,gate=t)
                                two_inp_c.add_edges_from((p,f'{n}_add_{len(pred)}') for p in pred[0:2])

                                # update list
                                pred.append(f'{n}_add_{len(pred)}')
                                pred = pred[2:]

                        # add final input connections
                        two_inp_c.add_edges_from((p,n) for p in pred)

                        # ensure all are two inputs
                        for n in two_inp_c.nodes():
                                if two_inp_c.in_degree(n)>2:
                                        print(f"gate with more than 2 inputs")
                                        code.interact(local=dict(globals(), **locals()))

                return two_inp_c

        def dual_rail_ternary_encoding(self):
                d = deepcopy(c)

                # add dual nodes
                for n in c:
                        if c.nodes[n]['type'] in ['and','nand']:
                                d.add_node(f'{n}_x',gate='and',output=c.nodes[n]['output'])
                                d.add_node(f'{n}_x_in_fi',gate='or',output=False)
                                d.add_node(f'{n}_0_not_in_fi',gate='nor',output=False)
                                d.add_edges_from([(f'{n}_x_in_fi',f'{n}_x'),(f'{n}_0_not_in_fi',f'{n}_x')])
                                d.add_edges_from((f'{p}_x',f'{n}_x_in_fi') for p in c.predecessors(n))
                                for p in c.predecessors(n):
                                        d.add_node(f'{p}_is_0',gate='nor',output=False)
                                        d.add_edge(f'{p}_is_0',f'{n}_0_not_in_fi')
                                        d.add_edge(f'{p}_x',f'{p}_is_0')
                                        d.add_edge(p,f'{p}_is_0')

                        elif c.nodes[n]['type'] in ['or','nor']:
                                d.add_node(f'{n}_x',gate='and',output=c.nodes[n]['output'])
                                d.add_node(f'{n}_x_in_fi',gate='or',output=False)
                                d.add_node(f'{n}_1_not_in_fi',gate='nor',output=False)
                                d.add_edges_from([(f'{n}_x_in_fi',f'{n}_x'),(f'{n}_1_not_in_fi',f'{n}_x')])
                                d.add_edges_from((f'{p}_x',f'{n}_x_in_fi') for p in c.predecessors(n))
                                for p in c.predecessors(n):
                                        d.add_node(f'{p}_is_1',gate='and',output=False)
                                        d.add_edge(f'{p}_is_1',f'{n}_1_not_in_fi')
                                        d.add_node(f'{p}_not_x',gate='not',output=False)
                                        d.add_edge(f'{p}_x',f'{p}_not_x')
                                        d.add_edge(f'{p}_not_x',f'{p}_is_1')
                                        d.add_edge(p,f'{p}_is_1')

                        elif c.nodes[n]['type'] in ['buf','not']:
                                d.add_node(f'{n}_x',gate='buf',output=c.nodes[n]['output'])
                                p = list(c.predecessors(n))[0]
                                d.add_edge(f'{p}_x',f'{n}_x')

                        elif c.nodes[n]['type'] in ['xor','xnor']:
                                d.add_node(f'{n}_x',gate='or',output=c.nodes[n]['output'])
                                d.add_edges_from((f'{p}_x',f'{n}_x') for p in c.predecessors(n))

                        elif c.nodes[n]['type'] in ['0','1']:
                                d.add_node(f'{n}_x',gate='0',output=c.nodes[n]['output'])

                        elif c.nodes[n]['type'] in ['input']:
                                d.add_node(f'{n}_x',gate='input',output=c.nodes[n]['output'])

                        elif c.nodes[n]['type'] in ['dff']:
                                d.add_node(f'{n}_x',gate='dff',output=c.nodes[n]['output'],clk=c.nodes[n]['clk'])
                                p = list(c.predecessors(n))[0]
                                d.add_edge(f'{p}_x',f'{n}_x')

                        elif c.nodes[n]['type'] in ['lat']:
                                d.add_node(f'{n}_x',gate='lat',output=c.nodes[n]['output'],clk=c.nodes[n]['clk'],rst=c.nodes[n]['rst'])
                                p = list(c.predecessors(n))[0]
                                d.add_edge(f'{p}_x',f'{n}_x')

                        else:
                                print(f"unknown gate type: {c.nodes[n]['type']}")
                                code.interact(local=locals())

                for n in d:
                        if 'type' not in d.nodes[n]:
                                print(f"empty gate type: {n}")
                                code.interact(local=locals())

                return d

        def parseModule(self,verilog,path):
                # read verilog
                if path:
                        with open(path, 'r') as f:
                                verilog = f.read()

                # find module
                regex = f"module\s+{self.name}\s*\(.*?\);(.*?)endmodule"
                m = re.search(regex,verilog,re.DOTALL)
                module =  m.group(1)

                # create graph
                G = nx.DiGraph()

                # handle gates
                regex = "(or|nor|and|nand|not|xor|xnor)\s+\S+\s*\((.+?)\);"
                for gate, net_str in re.findall(regex,module,re.DOTALL):

                        # parse all nets
                        nets = net_str.replace(" ","").replace("\n","").replace("\t","").split(",")
                        output = nets[0]
                        inputs = nets[1:]

                        # add to graph
                        G.add_edges_from((net,output) for net in inputs)
                        G.nodes[output]['type'] = gate

                # handle ffs
                regex = "fflopd\s+\S+\s*\(\.CK\s*\((.+?)\),\s*.D\s*\((.+?)\),\s*.Q\s*\((.+?)\)\);"
                for clk,d,q in re.findall(regex,module,re.DOTALL):

                        # add to graph
                        G.add_node(q,type='ff')

                        G.add_edge(d,f'd[{q}]')
                        G.nodes[f'd[{q}]']['type'] = 'd'
                        G.add_edge(f'd[{q}]',q)

                        G.add_edge(clk,f'clk[{q}]')
                        G.nodes[f'clk[{q}]']['type'] = 'clk'
                        G.add_edge(f'clk[{q}]',q)

                # handle lats
                regex = "latchdrs\s+\S+\s*\(\s*\.R\s*\((.+?)\),\s*\.S\s*\((.+?)\),\s*\.ENA\s*\((.+?)\),\s*.D\s*\((.+?)\),\s*.Q\s*\((.+?)\)\s*\);"
                for r,s,c,d,q in re.findall(regex,module,re.DOTALL):

                        # add to graph
                        G.add_node(q,type='lat')

                        G.add_edge(d,f'd[{q}]')
                        G.nodes[f'd[{q}]']['type'] = 'd'
                        G.add_edge(f'd[{q}]',q)

                        G.add_edge(clk,f'clk[{q}]')
                        G.nodes[f'clk[{q}]']['type'] = 'clk'
                        G.add_edge(f'clk[{q}]',q)

                        G.add_edge(d,f'r[{q}]')
                        G.nodes[f'r[{q}]']['type'] = 'r'
                        G.add_edge(f'r[{q}]',q)

                # handle assigns
                assign_regex = "assign\s+(.+?)\s*=\s*(.+?);"
                for n0, n1 in re.findall(assign_regex,module):
                        output = n0.replace(' ','')
                        inpt = n1.replace(' ','')
                        G.add_edge(inpt,output)
                        G.nodes[output]['type'] = 'buf'

                for n in G.nodes():
                        if 'type' not in G.nodes[n]:
                                if n == "1'b0":
                                        G.nodes[n]['type'] = '0'
                                elif n == "1'b1":
                                        G.nodes[n]['type'] = '1'
                                else:
                                        G.nodes[n]['type'] = 'input'

                # get outputs
                out_regex = "output\s(.+?);"
                for net_str in re.findall(out_regex,module,re.DOTALL):
                        nets = net_str.replace(" ","").replace("\n","").replace("\t","").split(",")
                        for net in nets:
                                G.add_edge(net,f'output[{net}]')
                                G.nodes[f'output[{net}]']['type'] = 'output'

                return G

        def miter(self,c=None,inputs=None,outputs=None):
                if not c:
                        c = self
                if not inputs:
                        inputs = self.startpoints()
                if not outputs:
                        outputs = self.endpoints()

                # get inputs to be used in miter
                common_inputs = self.startpoints()&inputs
                common_outputs = self.endpoints()&outputs

                # create miter
                m = c.relabel({n:f'c0_{n}' for n in c.nodes()-common_inputs})
                m.extend(self.relabel({n:f'c1_{n}' for n in c.nodes()-common_inputs}))

                # compare outputs
                m.add('sat','or')
                for o in common_outputs:
                        m.add(f'miter_{o}','xor',fanin=[f'c0_{o}',f'c1_{o}'],fanout=['sat'])

                return m

        def unroll(self,cycles):
                pass

Methods

def add(self, n, type, fanin=None, fanout=None)
Expand source code
def add(self,n,type,fanin=None,fanout=None):
        if fanin is None: fanin=[]
        if fanout is None: fanout=[]
        self.graph.add_node(n,type=type)
        self.graph.add_edges_from((f,n) for f in fanin)
        self.graph.add_edges_from((n,f) for f in fanout)
def cnf(self)
Expand source code
def cnf(self):
        variables = IDPool()
        clauses = CNF()

        for n in self.nodes():
                variables.id(n)
                if self.type(n) == 'and':
                        for f in self.fanin(n):
                                clauses.append([-variables.id(n),variables.id(f)])
                        clauses.append([variables.id(n)] + [-variables.id(f) for f in self.fanin(n)])
                elif self.type(n) == 'nand':
                        for f in self.fanin(n):
                                clauses.append([variables.id(n),variables.id(f)])
                        clauses.append([-variables.id(n)] + [-variables.id(f) for f in self.fanin(n)])
                elif self.type(n) == 'or':
                        for f in self.fanin(n):
                                clauses.append([variables.id(n),-variables.id(f)])
                        clauses.append([-variables.id(n)] + [variables.id(f) for f in self.fanin(n)])
                elif self.type(n) == 'nor':
                        for f in self.fanin(n):
                                clauses.append([-variables.id(n),-variables.id(f)])
                        clauses.append([variables.id(n)] + [variables.id(f) for f in self.fanin(n)])
                elif self.type(n) == 'not':
                        f = self.fanin(n).pop()
                        clauses.append([variables.id(n),variables.id(f)])
                        clauses.append([-variables.id(n),-variables.id(f)])
                elif self.type(n) in ['output','d','r','buf','clk']:
                        f = self.fanin(n).pop()
                        clauses.append([variables.id(n),-variables.id(f)])
                        clauses.append([-variables.id(n),variables.id(f)])
                elif self.type(n) in ['xor','xnor']:
                        # break into heirarchical xors
                        nets = list(self.fanin(n))

                        # xor gen
                        def xorClauses(a,b,c):
                                clauses.append([-variables.id(c),-variables.id(b),-variables.id(a)])
                                clauses.append([-variables.id(c),variables.id(b),variables.id(a)])
                                clauses.append([variables.id(c),-variables.id(b),variables.id(a)])
                                clauses.append([variables.id(c),variables.id(b),-variables.id(a)])

                        while len(nets)>2:
                                #create new net
                                new_net = 'xor_'+nets[-2]+'_'+nets[-1]
                                variables.id(new_net)

                                # add sub xors
                                xorClauses(nets[-2],nets[-1],new_net)

                                # remove last 2 nets
                                nets = nets[:-2]

                                # insert before out
                                nets.insert(0,new_net)

                        # add final xor
                        if self.type(n) == 'xor':
                                xorClauses(nets[-2],nets[-1],n)
                        else:
                                # invert xor
                                variables.id(f'xor_inv_{n}')
                                xorClauses(nets[-2],nets[-1],f'xor_inv_{n}')
                                clauses.append([variables.id(n),variables.id(f'xor_inv_{n}')])
                                clauses.append([-variables.id(n),-variables.id(f'xor_inv_{n}')])
                elif self.type(n) == '0':
                        clauses.append([-variables.id(n)])
                elif self.type(n) == '1':
                        clauses.append([variables.id(n)])
                elif self.type(n) in ['ff','lat','input']:
                        pass
                else:
                        print(f"unknown gate type: {self.type(n)}")
                        code.interact(local=dict(globals(), **locals()))

        return clauses,variables
def dual_rail_ternary_encoding(self)
Expand source code
def dual_rail_ternary_encoding(self):
        d = deepcopy(c)

        # add dual nodes
        for n in c:
                if c.nodes[n]['type'] in ['and','nand']:
                        d.add_node(f'{n}_x',gate='and',output=c.nodes[n]['output'])
                        d.add_node(f'{n}_x_in_fi',gate='or',output=False)
                        d.add_node(f'{n}_0_not_in_fi',gate='nor',output=False)
                        d.add_edges_from([(f'{n}_x_in_fi',f'{n}_x'),(f'{n}_0_not_in_fi',f'{n}_x')])
                        d.add_edges_from((f'{p}_x',f'{n}_x_in_fi') for p in c.predecessors(n))
                        for p in c.predecessors(n):
                                d.add_node(f'{p}_is_0',gate='nor',output=False)
                                d.add_edge(f'{p}_is_0',f'{n}_0_not_in_fi')
                                d.add_edge(f'{p}_x',f'{p}_is_0')
                                d.add_edge(p,f'{p}_is_0')

                elif c.nodes[n]['type'] in ['or','nor']:
                        d.add_node(f'{n}_x',gate='and',output=c.nodes[n]['output'])
                        d.add_node(f'{n}_x_in_fi',gate='or',output=False)
                        d.add_node(f'{n}_1_not_in_fi',gate='nor',output=False)
                        d.add_edges_from([(f'{n}_x_in_fi',f'{n}_x'),(f'{n}_1_not_in_fi',f'{n}_x')])
                        d.add_edges_from((f'{p}_x',f'{n}_x_in_fi') for p in c.predecessors(n))
                        for p in c.predecessors(n):
                                d.add_node(f'{p}_is_1',gate='and',output=False)
                                d.add_edge(f'{p}_is_1',f'{n}_1_not_in_fi')
                                d.add_node(f'{p}_not_x',gate='not',output=False)
                                d.add_edge(f'{p}_x',f'{p}_not_x')
                                d.add_edge(f'{p}_not_x',f'{p}_is_1')
                                d.add_edge(p,f'{p}_is_1')

                elif c.nodes[n]['type'] in ['buf','not']:
                        d.add_node(f'{n}_x',gate='buf',output=c.nodes[n]['output'])
                        p = list(c.predecessors(n))[0]
                        d.add_edge(f'{p}_x',f'{n}_x')

                elif c.nodes[n]['type'] in ['xor','xnor']:
                        d.add_node(f'{n}_x',gate='or',output=c.nodes[n]['output'])
                        d.add_edges_from((f'{p}_x',f'{n}_x') for p in c.predecessors(n))

                elif c.nodes[n]['type'] in ['0','1']:
                        d.add_node(f'{n}_x',gate='0',output=c.nodes[n]['output'])

                elif c.nodes[n]['type'] in ['input']:
                        d.add_node(f'{n}_x',gate='input',output=c.nodes[n]['output'])

                elif c.nodes[n]['type'] in ['dff']:
                        d.add_node(f'{n}_x',gate='dff',output=c.nodes[n]['output'],clk=c.nodes[n]['clk'])
                        p = list(c.predecessors(n))[0]
                        d.add_edge(f'{p}_x',f'{n}_x')

                elif c.nodes[n]['type'] in ['lat']:
                        d.add_node(f'{n}_x',gate='lat',output=c.nodes[n]['output'],clk=c.nodes[n]['clk'],rst=c.nodes[n]['rst'])
                        p = list(c.predecessors(n))[0]
                        d.add_edge(f'{p}_x',f'{n}_x')

                else:
                        print(f"unknown gate type: {c.nodes[n]['type']}")
                        code.interact(local=locals())

        for n in d:
                if 'type' not in d.nodes[n]:
                        print(f"empty gate type: {n}")
                        code.interact(local=locals())

        return d
def edges(self)
Expand source code
def edges(self):
        return self.graph.edges
def endpoints(self, ns=None)
Expand source code
def endpoints(self,ns=None):
        if ns:
                return set(n for n in self.transitiveFanout(ns) if self.type(n) in ['d','output'])
        else:
                return set(n for n in self.graph if self.type(n) in ['d','output'])
def extend(self, c)
Expand source code
def extend(self,c):
        self.graph.update(c.graph)
def fanin(self, ns)
Expand source code
def fanin(self,ns):
        gates = set()
        if isinstance(ns,str): ns = [ns]
        for n in ns:
                gates |= set(self.graph.predecessors(n))
        return gates
def fanout(self, ns)
Expand source code
def fanout(self,ns):
        gates = set()
        if isinstance(ns,str): ns = [ns]
        for n in ns:
                gates |= set(self.graph.successors(n))
        return gates
def ffs(self)
Expand source code
def ffs(self): return self.nodes('ff')
def inputs(self)
Expand source code
def inputs(self): return self.nodes('input')
def io(self)
Expand source code
def io(self): return self.nodes(['output','input'])
def lats(self)
Expand source code
def lats(self): return self.nodes('lat')
def miter(self, c=None, inputs=None, outputs=None)
Expand source code
def miter(self,c=None,inputs=None,outputs=None):
        if not c:
                c = self
        if not inputs:
                inputs = self.startpoints()
        if not outputs:
                outputs = self.endpoints()

        # get inputs to be used in miter
        common_inputs = self.startpoints()&inputs
        common_outputs = self.endpoints()&outputs

        # create miter
        m = c.relabel({n:f'c0_{n}' for n in c.nodes()-common_inputs})
        m.extend(self.relabel({n:f'c1_{n}' for n in c.nodes()-common_inputs}))

        # compare outputs
        m.add('sat','or')
        for o in common_outputs:
                m.add(f'miter_{o}','xor',fanin=[f'c0_{o}',f'c1_{o}'],fanout=['sat'])

        return m
def nodes(self, types=None)
Expand source code
def nodes(self,types=None):
        if types is None:
                return self.graph.nodes
        else:
                if isinstance(types,str): types = [types]
                return set(n for n in self.nodes() if self.type(n) in types)
def outputs(self)
Expand source code
def outputs(self): return self.nodes('input')
def parseModule(self, verilog, path)
Expand source code
def parseModule(self,verilog,path):
        # read verilog
        if path:
                with open(path, 'r') as f:
                        verilog = f.read()

        # find module
        regex = f"module\s+{self.name}\s*\(.*?\);(.*?)endmodule"
        m = re.search(regex,verilog,re.DOTALL)
        module =  m.group(1)

        # create graph
        G = nx.DiGraph()

        # handle gates
        regex = "(or|nor|and|nand|not|xor|xnor)\s+\S+\s*\((.+?)\);"
        for gate, net_str in re.findall(regex,module,re.DOTALL):

                # parse all nets
                nets = net_str.replace(" ","").replace("\n","").replace("\t","").split(",")
                output = nets[0]
                inputs = nets[1:]

                # add to graph
                G.add_edges_from((net,output) for net in inputs)
                G.nodes[output]['type'] = gate

        # handle ffs
        regex = "fflopd\s+\S+\s*\(\.CK\s*\((.+?)\),\s*.D\s*\((.+?)\),\s*.Q\s*\((.+?)\)\);"
        for clk,d,q in re.findall(regex,module,re.DOTALL):

                # add to graph
                G.add_node(q,type='ff')

                G.add_edge(d,f'd[{q}]')
                G.nodes[f'd[{q}]']['type'] = 'd'
                G.add_edge(f'd[{q}]',q)

                G.add_edge(clk,f'clk[{q}]')
                G.nodes[f'clk[{q}]']['type'] = 'clk'
                G.add_edge(f'clk[{q}]',q)

        # handle lats
        regex = "latchdrs\s+\S+\s*\(\s*\.R\s*\((.+?)\),\s*\.S\s*\((.+?)\),\s*\.ENA\s*\((.+?)\),\s*.D\s*\((.+?)\),\s*.Q\s*\((.+?)\)\s*\);"
        for r,s,c,d,q in re.findall(regex,module,re.DOTALL):

                # add to graph
                G.add_node(q,type='lat')

                G.add_edge(d,f'd[{q}]')
                G.nodes[f'd[{q}]']['type'] = 'd'
                G.add_edge(f'd[{q}]',q)

                G.add_edge(clk,f'clk[{q}]')
                G.nodes[f'clk[{q}]']['type'] = 'clk'
                G.add_edge(f'clk[{q}]',q)

                G.add_edge(d,f'r[{q}]')
                G.nodes[f'r[{q}]']['type'] = 'r'
                G.add_edge(f'r[{q}]',q)

        # handle assigns
        assign_regex = "assign\s+(.+?)\s*=\s*(.+?);"
        for n0, n1 in re.findall(assign_regex,module):
                output = n0.replace(' ','')
                inpt = n1.replace(' ','')
                G.add_edge(inpt,output)
                G.nodes[output]['type'] = 'buf'

        for n in G.nodes():
                if 'type' not in G.nodes[n]:
                        if n == "1'b0":
                                G.nodes[n]['type'] = '0'
                        elif n == "1'b1":
                                G.nodes[n]['type'] = '1'
                        else:
                                G.nodes[n]['type'] = 'input'

        # get outputs
        out_regex = "output\s(.+?);"
        for net_str in re.findall(out_regex,module,re.DOTALL):
                nets = net_str.replace(" ","").replace("\n","").replace("\t","").split(",")
                for net in nets:
                        G.add_edge(net,f'output[{net}]')
                        G.nodes[f'output[{net}]']['type'] = 'output'

        return G
def relabel(self, mapping, name=None)
Expand source code
def relabel(self,mapping,name=None):
        return Circuit(graph=nx.relabel_nodes(self.graph,mapping),name=name)
def sat(self, true=None, false=None)
Expand source code
def sat(self,true=None,false=None):
        solver,clauses,variables = self.solver(true,false)
        if solver.solve():
                model = solver.get_model()
                return {n:model[variables.id(n)-1]>0 for n in self.nodes()}
        else:
                return False
def seq(self)
Expand source code
def seq(self): return self.nodes(['ff','lat'])
def seqGraph(self)
Expand source code
def seqGraph(self):
        graph = nx.DiGraph()

        # add nodes
        for n in self.io()|self.seq():
                graph.add_node(n,gate=self.type(n))

        # add edges
        for n in graph.nodes:
                graph.add_edges_from((s,n) for s in self.startpoints(n))

        return graph
def solver(self, true=None, false=None)
Expand source code
def solver(self,true=None,false=None):
        if true is None: true = set()
        if false is None: false = set()
        clauses,variables = self.cnf()
        for n in true: clauses.append([variables.id(n)])
        for n in false: clauses.append([-variables.id(n)])
        solver = Cadical(bootstrap_with=clauses)
        return solver,clauses,variables
def startpoints(self, ns=None)
Expand source code
def startpoints(self,ns=None):
        if ns:
                return set(n for n in self.transitiveFanin(ns) if self.type(n) in ['ff','lat','input'])
        else:
                return set(n for n in self.graph if self.type(n) in ['ff','lat','input'])
def syn(self, printOutput=False)
Expand source code
def syn(self,printOutput=False):
        verilog = self.verilog()

        with tempfile.NamedTemporaryFile() as tmp:
                cmd = ['genus','-execute',f"""set_db / .library $env(GENUS_DIR)/share/synth/tutorials/tech/tutorial.lib;
                                read_hdl -sv {tmp.name};
                                elaborate;
                                set_db syn_generic_effort high
                                syn_generic;
                                syn_map;
                                syn_opt;
                                write_hdl -generic;
                                exit;"""]
                tmp.write(bytes(verilog,'ascii'))
                tmp.flush()
                #code.interact(local=dict(globals(), **locals()))

                process = Popen(cmd,stdout=PIPE,stderr=PIPE,universal_newlines=True)
                output = ''
                while True:
                        line = process.stdout.readline()
                        if line == '' and process.poll() is not None:
                                break
                        if line:
                                if printOutput:
                                        print(line.strip())
                                output += line

        regex = "(module.*endmodule)"
        m = re.search(regex,output,re.DOTALL)
        syn_verilog = m.group(1)

        c = Circuit(verilog=syn_verilog,name=self.name)
        c.name = f'{self.name}_syn'
        return c
def transitiveFanin(self, ns, stopat=['ff', 'lat'], gates=None)
Expand source code
def transitiveFanin(self,ns,stopat=['ff','lat'],gates=None):
        if gates is None: gates=set()
        if isinstance(ns,str): ns = [ns]
        for n in ns:
                for p in self.graph.predecessors(n):
                        if p not in gates:
                                gates.add(p)
                                if self.type(p) not in stopat:
                                        self.transitiveFanin(p,stopat,gates)
        return gates
def transitiveFanout(self, ns, stopat=['d'], gates=None)
Expand source code
def transitiveFanout(self,ns,stopat=['d'],gates=None):
        if gates is None: gates=set()
        if isinstance(ns,str): ns = [ns]
        for n in ns:
                for s in self.graph.successors(n):
                        if s not in gates:
                                gates.add(s)
                                if self.type(s) not in stopat:
                                        self.transitiveFanout(s,stopat,gates)
        return gates
def two_input(self)
Expand source code
def two_input(self):
        two_inp_c = nx.DiGraph()

        # create nodes
        for n in self.nodes():
                two_inp_c.add_node(n)
                for a,v in c.nodes[n].items():
                        two_inp_c.nodes[n][a] = v

        # connect nodes
        for n in self.nodes():
                # handle fanin
                pred = list(c.predecessors(n))

                # select new gate type
                if c.nodes[n]['type'] in ['and','nand']:
                        t = 'and'
                elif c.nodes[n]['type'] in ['or','nor']:
                        t = 'or'
                elif c.nodes[n]['type'] in ['xor','xnor']:
                        t = 'xor'

                while len(pred)>2:
                        # create new, connect
                        two_inp_c.add_node(f'{n}_add_{len(pred)}',output=False,gate=t)
                        two_inp_c.add_edges_from((p,f'{n}_add_{len(pred)}') for p in pred[0:2])

                        # update list
                        pred.append(f'{n}_add_{len(pred)}')
                        pred = pred[2:]

                # add final input connections
                two_inp_c.add_edges_from((p,n) for p in pred)

                # ensure all are two inputs
                for n in two_inp_c.nodes():
                        if two_inp_c.in_degree(n)>2:
                                print(f"gate with more than 2 inputs")
                                code.interact(local=dict(globals(), **locals()))

        return two_inp_c
def type(self, n)
Expand source code
def type(self,n):
        return self.graph.nodes[n]['type']
def unroll(self, cycles)
Expand source code
def unroll(self,cycles):
        pass
def verilog(self)
Expand source code
def verilog(self):
        inputs = []
        outputs = []
        insts = []
        wires = []

        for n in self.nodes():
                if c.type(n) in ['xor','xnor','buf','not','nor','or','and','nand']:
                        fanin = ','.join(p for p in c.fanin(n))
                        insts.append(f"{c.type(n)} g_{n} ({n},{fanin})")
                        wires.append(n)
                elif c.type(n) in ['0','1']:
                        insts.append(f"assign {n} = 1'b{c.type()}")
                elif c.type(n) in ['input']:
                        inputs.append(n)
                        wires.append(n)
                elif c.type(n) in ['output']:
                        outputs.append(n.replace('output[','')[:-1])
                elif c.type(n) in ['ff']:
                        d = c.fanin(f'd[{n}]').pop()
                        clk = c.fanin(f'clk[{n}]').pop()
                        insts.append(f"fflopd g_{n} (.CK({clk}),.D({d}),.Q({n}))")
                elif c.type(n) in ['lat']:
                        d = c.fanin(f'd[{n}]').pop()
                        clk = c.fanin(f'clk[{n}]').pop()
                        r = c.fanin(f'r[{n}]').pop()
                        insts.append(f"latchdrs g_{n} (.ENA({clk}),.D({d}),.R({r}),.S(1'b1),.Q({n}))")
                elif c.type(n) in ['clk','d','r']:
                        pass
                else:
                        print(f"unknown gate type: {c.type(n)}")
                        return

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

        return verilog