|  |  |  | @ -9,6 +9,9 @@ from .circuit import Circuit@@ -9,6 +9,9 @@ from .circuit import Circuit | 
			
		
	
		
			
				
					|  |  |  |  | BUF1 = np.uint16(0b1010_1010_1010_1010) | 
			
		
	
		
			
				
					|  |  |  |  | INV1 = ~BUF1 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | __const0__ = BUF1 | 
			
		
	
		
			
				
					|  |  |  |  | __const1__ = INV1 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | AND2 = np.uint16(0b1000_1000_1000_1000) | 
			
		
	
		
			
				
					|  |  |  |  | AND3 = np.uint16(0b1000_0000_1000_0000) | 
			
		
	
		
			
				
					|  |  |  |  | AND4 = np.uint16(0b1000_0000_0000_0000) | 
			
		
	
	
		
			
				
					|  |  |  | @ -41,7 +44,10 @@ AOI211, OAI211 = ~AO211, ~OA211@@ -41,7 +44,10 @@ AOI211, OAI211 = ~AO211, ~OA211 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | MUX21 = np.uint16(0b1100_1010_1100_1010)  # z = i1 if i2 else i0 (i2 is select) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | names = dict([(v, k) for k, v in globals().items() if isinstance(v, np.uint16)]) | 
			
		
	
		
			
				
					|  |  |  |  | names = dict([(v, k) for k, v in globals().items() if isinstance(v, np.uint16) and '__' not in k]) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | prim2name = dict([(v, k) for k, v in globals().items() if isinstance(v, np.uint16) and '__' not in k]) | 
			
		
	
		
			
				
					|  |  |  |  | name2prim = dict([(k, v) for k, v in globals().items() if isinstance(v, np.uint16)]) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | kind_prefixes = { | 
			
		
	
		
			
				
					|  |  |  |  |     'nand': (NAND4, NAND3, NAND2), | 
			
		
	
	
		
			
				
					|  |  |  | @ -177,84 +183,75 @@ class SimOps:@@ -177,84 +183,75 @@ class SimOps: | 
			
		
	
		
			
				
					|  |  |  |  |         self.ppo_offset = self.ppi_offset + self.s_len | 
			
		
	
		
			
				
					|  |  |  |  |         self.c_locs_len = self.ppo_offset + self.s_len | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         # translate circuit structure into self.ops | 
			
		
	
		
			
				
					|  |  |  |  |         ops = [] | 
			
		
	
		
			
				
					|  |  |  |  |         interface_dict = dict((n, i) for i, n in enumerate(circuit.s_nodes)) | 
			
		
	
		
			
				
					|  |  |  |  |         for n in circuit.topological_order(): | 
			
		
	
		
			
				
					|  |  |  |  |             if n in interface_dict: | 
			
		
	
		
			
				
					|  |  |  |  |                 inp_idx = self.ppi_offset + interface_dict[n] | 
			
		
	
		
			
				
					|  |  |  |  |                 if len(n.outs) > 0 and n.outs[0] is not None:  # first output of a PI/PPI | 
			
		
	
		
			
				
					|  |  |  |  |                     ops.append((BUF1, n.outs[0].index, inp_idx, self.zero_idx, self.zero_idx, self.zero_idx, *a_ctrl[n.outs[0]])) | 
			
		
	
		
			
				
					|  |  |  |  |                 if 'dff' in n.kind.lower():  # second output of DFF is inverted | 
			
		
	
		
			
				
					|  |  |  |  |                     if len(n.outs) > 1 and n.outs[1] is not None: | 
			
		
	
		
			
				
					|  |  |  |  |                         ops.append((INV1, n.outs[1].index, inp_idx, self.zero_idx, self.zero_idx, self.zero_idx, *a_ctrl[n.outs[1]])) | 
			
		
	
		
			
				
					|  |  |  |  |                 else:  # if not DFF, no output is inverted. | 
			
		
	
		
			
				
					|  |  |  |  |                     for o_line in n.outs[1:]: | 
			
		
	
		
			
				
					|  |  |  |  |                         if o_line is not None: | 
			
		
	
		
			
				
					|  |  |  |  |                             ops.append((BUF1, o_line.index, inp_idx, self.zero_idx, self.zero_idx, self.zero_idx, *a_ctrl[o_line])) | 
			
		
	
		
			
				
					|  |  |  |  |                 continue | 
			
		
	
		
			
				
					|  |  |  |  |             # regular node, not PI/PPI or PO/PPO | 
			
		
	
		
			
				
					|  |  |  |  |             o0_idx = n.outs[0].index if len(n.outs) > 0 and n.outs[0] is not None else self.tmp_idx | 
			
		
	
		
			
				
					|  |  |  |  |             i0_idx = n.ins[0].index if len(n.ins) > 0 and n.ins[0] is not None else self.zero_idx | 
			
		
	
		
			
				
					|  |  |  |  |             i1_idx = n.ins[1].index if len(n.ins) > 1 and n.ins[1] is not None else self.zero_idx | 
			
		
	
		
			
				
					|  |  |  |  |             i2_idx = n.ins[2].index if len(n.ins) > 2 and n.ins[2] is not None else self.zero_idx | 
			
		
	
		
			
				
					|  |  |  |  |             i3_idx = n.ins[3].index if len(n.ins) > 3 and n.ins[3] is not None else self.zero_idx | 
			
		
	
		
			
				
					|  |  |  |  |             kind = n.kind.lower() | 
			
		
	
		
			
				
					|  |  |  |  |             if kind == '__fork__': | 
			
		
	
		
			
				
					|  |  |  |  |                 if not strip_forks: | 
			
		
	
		
			
				
					|  |  |  |  |                     for o_line in n.outs: | 
			
		
	
		
			
				
					|  |  |  |  |                         if o_line is not None: | 
			
		
	
		
			
				
					|  |  |  |  |                             ops.append((BUF1, o_line.index, i0_idx, i1_idx, i2_idx, i3_idx, *a_ctrl[o_line])) | 
			
		
	
		
			
				
					|  |  |  |  |                 continue | 
			
		
	
		
			
				
					|  |  |  |  |             sp = None | 
			
		
	
		
			
				
					|  |  |  |  |             for prefix, prims in kind_prefixes.items(): | 
			
		
	
		
			
				
					|  |  |  |  |                 if kind.startswith(prefix): | 
			
		
	
		
			
				
					|  |  |  |  |                     sp = prims[0] | 
			
		
	
		
			
				
					|  |  |  |  |                     if i3_idx == self.zero_idx: | 
			
		
	
		
			
				
					|  |  |  |  |                         sp = prims[1] | 
			
		
	
		
			
				
					|  |  |  |  |                         if i2_idx == self.zero_idx: | 
			
		
	
		
			
				
					|  |  |  |  |                             sp = prims[2] | 
			
		
	
		
			
				
					|  |  |  |  |                     break | 
			
		
	
		
			
				
					|  |  |  |  |             if sp is None: | 
			
		
	
		
			
				
					|  |  |  |  |                 print('unknown cell type', kind) | 
			
		
	
		
			
				
					|  |  |  |  |             else: | 
			
		
	
		
			
				
					|  |  |  |  |                 ops.append((sp, o0_idx, i0_idx, i1_idx, i2_idx, i3_idx, *a_ctrl[o0_idx])) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         self.ops = np.asarray(ops, dtype='int32') | 
			
		
	
		
			
				
					|  |  |  |  |         # ALAP-toposort the circuit into self.ops | 
			
		
	
		
			
				
					|  |  |  |  |         levels = [] | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         ppio2idx = dict((n, i) for i, n in enumerate(circuit.s_nodes)) | 
			
		
	
		
			
				
					|  |  |  |  |         pis = set([n for n in circuit.s_nodes if len(n.ins) == 0]) | 
			
		
	
		
			
				
					|  |  |  |  |         ppos = set([n for n in circuit.s_nodes if len(n.ins) > 0]) | 
			
		
	
		
			
				
					|  |  |  |  |         readers = np.array([1 if l.reader in ppos else len(l.reader.outs) for l in circuit.lines], dtype=np.int32)  # for ref-counting forks | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         level_lines = [n.ins[0] for n in ppos]  # start from PPOs | 
			
		
	
		
			
				
					|  |  |  |  |         # FIXME: Should probably instanciate buffers for PPOs and attach DFF clocks | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         while len(level_lines) > 0:  # traverse the circuit level-wise back towards (P)PIs | 
			
		
	
		
			
				
					|  |  |  |  |             level_ops = [] | 
			
		
	
		
			
				
					|  |  |  |  |             prev_level_lines = [] | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |             for l in level_lines: | 
			
		
	
		
			
				
					|  |  |  |  |                 n = l.driver | 
			
		
	
		
			
				
					|  |  |  |  |                 in_idxs = [n.ins[x].index if len(n.ins) > x and n.ins[x] is not None else self.zero_idx for x in [0,1,2,3]] | 
			
		
	
		
			
				
					|  |  |  |  |                 if n in ppio2idx: | 
			
		
	
		
			
				
					|  |  |  |  |                     in_idxs[0] = self.ppi_offset + ppio2idx[n] | 
			
		
	
		
			
				
					|  |  |  |  |                     if l.driver_pin == 1 and 'dff' in n.kind.lower():  # second output of DFF is inverted | 
			
		
	
		
			
				
					|  |  |  |  |                         level_ops.append((INV1, l.index, *in_idxs, *a_ctrl[l])) | 
			
		
	
		
			
				
					|  |  |  |  |                     else: | 
			
		
	
		
			
				
					|  |  |  |  |                         level_ops.append((BUF1, l.index, *in_idxs, *a_ctrl[l])) | 
			
		
	
		
			
				
					|  |  |  |  |                 elif n.kind == '__fork__': | 
			
		
	
		
			
				
					|  |  |  |  |                     readers[n.ins[0]] -= 1 | 
			
		
	
		
			
				
					|  |  |  |  |                     if readers[n.ins[0]] == 0: prev_level_lines.append(n.ins[0]) | 
			
		
	
		
			
				
					|  |  |  |  |                     if not strip_forks: level_ops.append((BUF1, l.index, *in_idxs, *a_ctrl[l])) | 
			
		
	
		
			
				
					|  |  |  |  |                 else: | 
			
		
	
		
			
				
					|  |  |  |  |                     prev_level_lines += n.ins | 
			
		
	
		
			
				
					|  |  |  |  |                     sp = None | 
			
		
	
		
			
				
					|  |  |  |  |                     kind = n.kind.lower() | 
			
		
	
		
			
				
					|  |  |  |  |                     for prefix, prims in kind_prefixes.items(): | 
			
		
	
		
			
				
					|  |  |  |  |                         if kind.startswith(prefix): | 
			
		
	
		
			
				
					|  |  |  |  |                             sp = prims[0] | 
			
		
	
		
			
				
					|  |  |  |  |                             if in_idxs[3] == self.zero_idx: | 
			
		
	
		
			
				
					|  |  |  |  |                                 sp = prims[1] | 
			
		
	
		
			
				
					|  |  |  |  |                                 if in_idxs[2] == self.zero_idx: | 
			
		
	
		
			
				
					|  |  |  |  |                                     sp = prims[2] | 
			
		
	
		
			
				
					|  |  |  |  |                             break | 
			
		
	
		
			
				
					|  |  |  |  |                     if sp is None: | 
			
		
	
		
			
				
					|  |  |  |  |                         print('unknown cell type', kind) | 
			
		
	
		
			
				
					|  |  |  |  |                     else: | 
			
		
	
		
			
				
					|  |  |  |  |                         level_ops.append((sp, l.index, *in_idxs, *a_ctrl[l])) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |             if len(level_ops) > 0: levels.append(level_ops) | 
			
		
	
		
			
				
					|  |  |  |  |             level_lines = prev_level_lines | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         self.levels = [np.asarray(lv, dtype=np.int32) for lv in levels[::-1]] | 
			
		
	
		
			
				
					|  |  |  |  |         level_sums = np.cumsum([0]+[len(lv) for lv in self.levels], dtype=np.int32) | 
			
		
	
		
			
				
					|  |  |  |  |         self.level_starts, self.level_stops = level_sums[:-1], level_sums[1:] | 
			
		
	
		
			
				
					|  |  |  |  |         self.ops = np.vstack(self.levels) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         # create a map from fanout lines to stem lines for fork stripping | 
			
		
	
		
			
				
					|  |  |  |  |         stems = np.zeros(self.c_locs_len, dtype='int32') - 1  # default to -1: 'no fanout line' | 
			
		
	
		
			
				
					|  |  |  |  |         stems = np.full(self.c_locs_len, -1, dtype=np.int32)  # default to -1: 'no fanout line' | 
			
		
	
		
			
				
					|  |  |  |  |         if strip_forks: | 
			
		
	
		
			
				
					|  |  |  |  |             for f in circuit.forks.values(): | 
			
		
	
		
			
				
					|  |  |  |  |                 prev_line = f.ins[0] | 
			
		
	
		
			
				
					|  |  |  |  |                 while prev_line.driver.kind == '__fork__': | 
			
		
	
		
			
				
					|  |  |  |  |                     prev_line = prev_line.driver.ins[0] | 
			
		
	
		
			
				
					|  |  |  |  |                 stem_idx = prev_line.index | 
			
		
	
		
			
				
					|  |  |  |  |                 for ol in f.outs: | 
			
		
	
		
			
				
					|  |  |  |  |                     if ol is not None: | 
			
		
	
		
			
				
					|  |  |  |  |                         stems[ol] = stem_idx | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         # calculate level (distance from PI/PPI) and reference count for each line | 
			
		
	
		
			
				
					|  |  |  |  |         levels = np.zeros(self.c_locs_len, dtype='int32') | 
			
		
	
		
			
				
					|  |  |  |  |         ref_count = np.zeros(self.c_locs_len, dtype='int32') | 
			
		
	
		
			
				
					|  |  |  |  |         level_starts = [0] | 
			
		
	
		
			
				
					|  |  |  |  |         current_level = 1 | 
			
		
	
		
			
				
					|  |  |  |  |         for i, op in enumerate(self.ops): | 
			
		
	
		
			
				
					|  |  |  |  |             # if we fork-strip, always take the stems for determining fan-in level | 
			
		
	
		
			
				
					|  |  |  |  |             i0_idx = stems[op[2]] if stems[op[2]] >= 0 else op[2] | 
			
		
	
		
			
				
					|  |  |  |  |             i1_idx = stems[op[3]] if stems[op[3]] >= 0 else op[3] | 
			
		
	
		
			
				
					|  |  |  |  |             i2_idx = stems[op[4]] if stems[op[4]] >= 0 else op[4] | 
			
		
	
		
			
				
					|  |  |  |  |             i3_idx = stems[op[5]] if stems[op[5]] >= 0 else op[5] | 
			
		
	
		
			
				
					|  |  |  |  |             if levels[i0_idx] >= current_level or levels[i1_idx] >= current_level or levels[i2_idx] >= current_level or levels[i3_idx] >= current_level: | 
			
		
	
		
			
				
					|  |  |  |  |                 current_level += 1 | 
			
		
	
		
			
				
					|  |  |  |  |                 level_starts.append(i) | 
			
		
	
		
			
				
					|  |  |  |  |             levels[op[1]] = current_level  # set level of the output line | 
			
		
	
		
			
				
					|  |  |  |  |             ref_count[i0_idx] += 1 | 
			
		
	
		
			
				
					|  |  |  |  |             ref_count[i1_idx] += 1 | 
			
		
	
		
			
				
					|  |  |  |  |             ref_count[i2_idx] += 1 | 
			
		
	
		
			
				
					|  |  |  |  |             ref_count[i3_idx] += 1 | 
			
		
	
		
			
				
					|  |  |  |  |         self.level_starts = np.asarray(level_starts, dtype='int32') | 
			
		
	
		
			
				
					|  |  |  |  |         self.level_stops = np.asarray(level_starts[1:] + [len(self.ops)], dtype='int32') | 
			
		
	
		
			
				
					|  |  |  |  |                         stems[ol] = prev_line.index | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         ref_count = np.zeros(self.c_locs_len, dtype=np.int32) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         for op in self.ops: | 
			
		
	
		
			
				
					|  |  |  |  |             for x in [2, 3, 4, 5]: | 
			
		
	
		
			
				
					|  |  |  |  |                 ref_count[stems[op[x]] if stems[op[x]] >= 0 else op[x]] += 1 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         # combinational signal allocation table. maps line and interface indices to self.c memory locations | 
			
		
	
		
			
				
					|  |  |  |  |         self.c_locs = np.full((self.c_locs_len,), -1, dtype=np.int32) | 
			
		
	
	
		
			
				
					|  |  |  | @ -280,9 +277,9 @@ class SimOps:@@ -280,9 +277,9 @@ class SimOps: | 
			
		
	
		
			
				
					|  |  |  |  |                 ref_count[i0_idx] += 1 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         # allocate memory for the rest of the circuit | 
			
		
	
		
			
				
					|  |  |  |  |         for op_start, op_stop in zip(self.level_starts, self.level_stops): | 
			
		
	
		
			
				
					|  |  |  |  |         for ops in self.levels: | 
			
		
	
		
			
				
					|  |  |  |  |             free_set = set() | 
			
		
	
		
			
				
					|  |  |  |  |             for op in self.ops[op_start:op_stop]: | 
			
		
	
		
			
				
					|  |  |  |  |             for op in ops: | 
			
		
	
		
			
				
					|  |  |  |  |                 # if we fork-strip, always take the stems | 
			
		
	
		
			
				
					|  |  |  |  |                 i0_idx = stems[op[2]] if stems[op[2]] >= 0 else op[2] | 
			
		
	
		
			
				
					|  |  |  |  |                 i1_idx = stems[op[3]] if stems[op[3]] >= 0 else op[3] | 
			
		
	
	
		
			
				
					|  |  |  | @ -301,7 +298,8 @@ class SimOps:@@ -301,7 +298,8 @@ class SimOps: | 
			
		
	
		
			
				
					|  |  |  |  |                 self.c_locs[o_idx], self.c_caps[o_idx] = h.alloc(cap), cap | 
			
		
	
		
			
				
					|  |  |  |  |             if c_reuse: | 
			
		
	
		
			
				
					|  |  |  |  |                 for loc in free_set: | 
			
		
	
		
			
				
					|  |  |  |  |                     h.free(loc) | 
			
		
	
		
			
				
					|  |  |  |  |                     if loc >= 0:  # DFF clocks are not allocated. Ignore for now. | 
			
		
	
		
			
				
					|  |  |  |  |                         h.free(loc) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         # copy memory location and capacity from stems to fanout lines | 
			
		
	
		
			
				
					|  |  |  |  |         for lidx, stem in enumerate(stems): | 
			
		
	
	
		
			
				
					|  |  |  | 
 |