From 35cb7e2c391fc44250aa86b787cf5236db4cebb8 Mon Sep 17 00:00:00 2001 From: Nick Downing Date: Sun, 20 Jul 2025 20:12:33 +1000 Subject: [PATCH] Make fets.txt / gates.txt object oriented, in preparation for circuit analysis --- scripts/blocks.py | 268 ++++++++++++++++++++++++---------------------- scripts/fets.py | 32 +++++- scripts/gates.py | 178 ++++++++++++++++-------------- 3 files changed, 267 insertions(+), 211 deletions(-) diff --git a/scripts/blocks.py b/scripts/blocks.py index 39845d1..49ee272 100755 --- a/scripts/blocks.py +++ b/scripts/blocks.py @@ -3,85 +3,90 @@ import re import sys +SYMBOL_TYPE_FET = 0 +SYMBOL_TYPE_GATE = 1 +N_SYMBOL_TYPES = 2 + if len(sys.argv) < 3: - print(f'usage: {sys.argv[0]:s} fets.txt dot_dir') + print(f'usage: {sys.argv[0]:s} symbols.txt dot_dir') sys.exit(1) -fets_txt = sys.argv[1] +symbols_txt = sys.argv[1] dot_dir = sys.argv[2] nets = {} blocks = {} -fets = [] -with open(fets_txt) as fin: +symbols = [] +with open(symbols_txt) as fin: line = fin.readline() - assert len(line) - fields = [int(i) for i in line.split()] - assert len(fields) >= 1 - n_fets = fields[0] - n_gates = fields[1] if len(fields) >= 2 else 0 - - for i in range(n_fets): - line = fin.readline() - assert len(line) + while len(line): fields = line.split() - assert len(fields) == 7 - block = fields[0] - channel = fields[1] - poly_net = fields[2] - n_diff_nets = int(fields[3]) - x = float(fields[4]) - y = float(fields[5]) - mass = int(fields[6]) - diff_nets = [] - r_matrix = [] - for j in range(n_diff_nets): - line = fin.readline() - assert len(line) - fields = line.split() - assert len(fields) == 1 + j - diff_nets.append(fields[0]) - r_matrix.append([float(k) for k in fields[1:]]) - - for net in [poly_net] + diff_nets: - if net not in nets: - nets[net] = set() # blocks (of fets/gates) that the net visits - nets[net].add(block) - + assert len(fields) >= 2 + symbol_type = int(fields[0]) + block = fields[1] if block not in blocks: - blocks[block] = ([], []) # fets, gates - blocks[block][0].append(len(fets)) - fets.append( - (block, channel, poly_net, diff_nets, x, y, mass, r_matrix) - ) - - gates = [] - for i in range(n_gates): - line = fin.readline() - assert len(line) - fields = line.split() - assert len(fields) == 3 - block = fields[0] - output_net = fields[1] - n_products = int(fields[2]) - - expr = [] - for j in range(n_products): - line = fin.readline() - assert len(line) - expr.append(line.split()) - - for product in expr: - for net in product: + blocks[block] = [] # symbols + blocks[block].append(len(symbols)) + + if symbol_type == SYMBOL_TYPE_FET: + assert len(fields) == 8 + poly_net = fields[2] + n_diff_nets = int(fields[3]) + channel = fields[4] + x = float(fields[5]) + y = float(fields[6]) + mass = int(fields[7]) + + diff_nets = [] + r_matrix = [] + for i in range(n_diff_nets): + line = fin.readline() + assert len(line) + fields = line.split() + assert len(fields) == 1 + i + diff_nets.append(fields[0]) + r_matrix.append([float(j) for j in fields[1:]]) + + for net in [poly_net] + diff_nets: if net not in nets: - nets[net] = set() # blocks (of fets/gates) that the net visits + nets[net] = set() # blocks (of symbols) that the net visits nets[net].add(block) - if block not in blocks: - blocks[block] = ([], []) # fets, gates - blocks[block][1].append(len(gates)) - gates.append((block, output_net, expr)) + symbols.append( + ( + symbol_type, + block, + (poly_net, diff_nets), + channel, + x, + y, + mass, + r_matrix + ) + ) + elif symbol_type == SYMBOL_TYPE_GATE: + assert len(fields) == 4 + output_net = fields[2] + n_products = int(fields[3]) + + expr = [] + for j in range(n_products): + line = fin.readline() + assert len(line) + expr.append(line.split()) + + for product in expr: + for net in product: + if net not in nets: + nets[net] = set() # blocks (of symbols) that the net visits + nets[net].add(block) + + symbols.append((symbol_type, block, (output_net, expr))) + else: + assert False + + line = fin.readline() -for block, (block_fets, block_gates) in sorted(blocks.items()): +for block, block_symbols in sorted(blocks.items()): with open(f'{dot_dir:s}/{block:s}.dot', 'w') as fout: fout.write(f'digraph "{block:s}" {{\n') @@ -104,65 +109,76 @@ for block, (block_fets, block_gates) in sorted(blocks.items()): fout.write(f' "{net_node:s}" [shape={shape:s}, label="{net:s}"]\n') return net_node - for i in block_fets: - _, channel, poly_net, diff_nets, x, y, mass, r_matrix = fets[i] - - node = f'fet:{channel:s}' - label = channel - if len(r_matrix) == 2: - label += f'\nR={r_matrix[1][0]:.3f}' - else: - label += f'\nC={mass / 2e4:.3f}' - fout.write(f' "{node:s}" [label="{label:s}"]\n') - - net_node = make_net_node(poly_net) - fout.write(f' "{net_node:s}" -> "{node:s}"\n') - - for diff_net in diff_nets: - net_node = make_net_node(diff_net) - fout.write(f' "{node:s}" -> "{net_node:s}" [arrowhead=none]\n') - - for i in block_gates: - _, output_net, expr = gates[i] - - node = f'gate:{output_net:s}' - if len(expr) == 1: - label = ( - 'FALSE' - if len(expr[0]) == 0 else - 'NOT' - if len(expr[0]) == 1 else - 'NAND' - ) - fout.write( - f' "{node:s}" [shape="invhouse", label="{label:s}"]\n' - ) - - for input_net in expr[0]: - net_node = make_net_node(input_net) - fout.write(f' "{net_node:s}" -> "{node:s}"\n') - else: - label = 'TRUE' if len(expr) == 0 else 'NOR' - fout.write( - f' "{node:s}" [shape="invhouse", label="{label:s}"]\n' - ) - - for j in range(len(expr)): - inner_node = f'{node:s}:{j:d}' - if len(expr[j]) == 1: - net_node = make_net_node(expr[j][0]) + for i in block_symbols: + symbol_type = symbols[i][0] + if symbol_type == SYMBOL_TYPE_FET: + ( + _, + _, + (poly_net, + diff_nets), + channel, + x, + y, + mass, + r_matrix + ) = symbols[i] + + node = f'fet:{channel:s}' + label = channel + if len(r_matrix) == 2: + label += f'\nR={r_matrix[1][0]:.3f}' + else: + label += f'\nC={mass / 2e4:.3f}' + fout.write(f' "{node:s}" [label="{label:s}"]\n') + + net_node = make_net_node(poly_net) + fout.write(f' "{net_node:s}" -> "{node:s}"\n') + + for diff_net in diff_nets: + net_node = make_net_node(diff_net) + fout.write(f' "{node:s}" -> "{net_node:s}" [arrowhead=none]\n') + elif symbol_type == SYMBOL_TYPE_GATE: + _, _, (output_net, expr) = symbols[i] + + node = f'gate:{output_net:s}' + if len(expr) == 1: + label = ( + 'FALSE' + if len(expr[0]) == 0 else + 'NOT' + if len(expr[0]) == 1 else + 'NAND' + ) + fout.write( + f' "{node:s}" [shape="invhouse", label="{label:s}"]\n' + ) + + for input_net in expr[0]: + net_node = make_net_node(input_net) fout.write(f' "{net_node:s}" -> "{node:s}"\n') - else: - label = 'TRUE' if len(expr[j]) == 0 else 'AND' - fout.write( - f' "{inner_node:s}" [shape="invhouse", label="{label:s}"]\n' - ) - - for input_net in expr[j]: - net_node = make_net_node(input_net) - fout.write(f' "{net_node:s}" -> "{inner_node:s}"\n') - fout.write(f' "{inner_node:s}" -> "{node:s}"\n') - net_node = make_net_node(output_net) - fout.write(f' "{node:s}" -> "{net_node:s}"\n') - + else: + label = 'TRUE' if len(expr) == 0 else 'NOR' + fout.write( + f' "{node:s}" [shape="invhouse", label="{label:s}"]\n' + ) + + for j in range(len(expr)): + inner_node = f'{node:s}:{j:d}' + if len(expr[j]) == 1: + net_node = make_net_node(expr[j][0]) + fout.write(f' "{net_node:s}" -> "{node:s}"\n') + else: + label = 'TRUE' if len(expr[j]) == 0 else 'AND' + fout.write( + f' "{inner_node:s}" [shape="invhouse", label="{label:s}"]\n' + ) + + for input_net in expr[j]: + net_node = make_net_node(input_net) + fout.write(f' "{net_node:s}" -> "{inner_node:s}"\n') + fout.write(f' "{inner_node:s}" -> "{node:s}"\n') + net_node = make_net_node(output_net) + fout.write(f' "{node:s}" -> "{net_node:s}"\n') + fout.write('}\n') diff --git a/scripts/fets.py b/scripts/fets.py index 1031bec..fc0521e 100755 --- a/scripts/fets.py +++ b/scripts/fets.py @@ -6,6 +6,9 @@ import scipy.sparse import scipy.sparse.linalg import sys +SYMBOL_TYPE_FET = 0 +N_SYMBOL_TYPES = 1 + # palette for resistor solver diagnostics (usually commented) #PALETTE = numpy.array( # [ @@ -158,7 +161,7 @@ def resistor_solver(image): #print('R', 1. / sum_I2, 'squares') return 1. / sum_I2 -fets = [] +symbols = [] image = numpy.zeros((ys, xs), numpy.uint8) for (channel, (channel_items, channel_reports)) in sorted(channels.items()): #adjacent_nodes = set([items[item1][0] for _, item1 in channel_reports]) @@ -268,10 +271,29 @@ for (channel, (channel_items, channel_reports)) in sorted(channels.items()): r_matrix[-1].append(resistor_solver(image[min_y:max_y, min_x:max_x])) image[min_y:max_y, min_x:max_x] = 0 - fets.append((block, channel, poly_net, diff_nets, x, y, mass, r_matrix)) + symbols.append( + ( + SYMBOL_TYPE_FET, + block, + (poly_net, diff_nets), + channel, + x, + y, + mass, + r_matrix + ) + ) -print(len(fets)) -for block, channel, poly_net, diff_nets, x, y, mass, r_matrix in fets: - print(block, channel, poly_net, len(diff_nets), x, y, mass) +for ( + symbol_type, + block, + (poly_net, diff_nets), + channel, + x, + y, + mass, + r_matrix +) in symbols: + print(symbol_type, block, poly_net, len(diff_nets), channel, x, y, mass) for i in range(len(diff_nets)): print(' ' + diff_nets[i], ' '.join([str(j) for j in r_matrix[i]])) diff --git a/scripts/gates.py b/scripts/gates.py index bbfe6f6..1b34460 100755 --- a/scripts/gates.py +++ b/scripts/gates.py @@ -3,54 +3,71 @@ import re import sys +SYMBOL_TYPE_FET = 0 +SYMBOL_TYPE_GATE = 1 +N_SYMBOL_TYPES = 2 + if len(sys.argv) < 3: - print(f'usage: {sys.argv[0]:s} fets.txt net_gnd,net_vcc') + print(f'usage: {sys.argv[0]:s} symbols.txt net_gnd,net_vcc') sys.exit(1) -fets_txt = sys.argv[1] +symbols_txt = sys.argv[1] [net_gnd, net_vcc] = sys.argv[2].split(',') nets = {} -fets = [] -with open(fets_txt) as fin: +symbols = [] +with open(symbols_txt) as fin: line = fin.readline() - assert len(line) - [n_fets] = [int(i) for i in line.split()] + while len(line): + fields = line.split() + assert len(fields) >= 2 + symbol_type = int(fields[0]) + block = fields[1] + + if symbol_type == SYMBOL_TYPE_FET: + assert len(fields) == 8 + poly_net = fields[2] + n_diff_nets = int(fields[3]) + channel = fields[4] + x = float(fields[5]) + y = float(fields[6]) + mass = int(fields[7]) + + diff_nets = [] + r_matrix = [] + for i in range(n_diff_nets): + line = fin.readline() + assert len(line) + fields = line.split() + assert len(fields) == 1 + i + diff_nets.append(fields[0]) + r_matrix.append([float(j) for j in fields[1:]]) + + for net in [poly_net] + diff_nets: + if net not in nets: + nets[net] = [False, 0, []] # visited, n_connections, switches + nets[net][1] += 1 + + # make an index so we can quickly follow symbols wired as switches + if len(diff_nets) == 2: + [diff_net0, diff_net1] = diff_nets + nets[diff_net0][2].append((len(symbols), diff_net1)) + nets[diff_net1][2].append((len(symbols), diff_net0)) + symbols.append( + [ + symbol_type, # can be overwritten with -1 to delete the symbol + block, + (poly_net, diff_nets), + channel, + x, + y, + mass, + r_matrix + ] + ) + else: + assert False - for i in range(n_fets): line = fin.readline() - assert len(line) - fields = line.split() - assert len(fields) == 7 - block = fields[0] - channel = fields[1] - poly_net = fields[2] - n_diff_nets = int(fields[3]) - x = float(fields[4]) - y = float(fields[5]) - mass = int(fields[6]) - diff_nets = [] - r_matrix = [] - for i in range(n_diff_nets): - line = fin.readline() - assert len(line) - fields = line.split() - assert len(fields) == 1 + i - diff_nets.append(fields[0]) - r_matrix.append([float(j) for j in fields[1:]]) - - for net in [poly_net] + diff_nets: - if net not in nets: - nets[net] = [False, 0, []] # visited, n_connections, switches - nets[net][1] += 1 - - # make an index so we can quickly follow fets wired as switches - if len(diff_nets) == 2: - [diff_net0, diff_net1] = diff_nets - nets[diff_net0][2].append((len(fets), diff_net1)) - nets[diff_net1][2].append((len(fets), diff_net0)) - fets.append( - [True, block, channel, poly_net, diff_nets, x, y, mass, r_matrix] - ) # first element is valid flag, set to False to delete the fet # raised by follow() if pulled-up net doesn't appear to be a logic gate class InvalidGate(Exception): @@ -61,7 +78,7 @@ class InvalidGate(Exception): # inner list has nets to be ANDed together, outer list has products to # be ORed together -- the OR of products, if true, pulls the output down # note: expr of [] does not pull the output down, expr of [[]] does -def follow(del_fets, net, parent_net): +def follow(del_symbols, net, parent_net): if net == net_gnd: return [[]] if net == net_vcc: @@ -76,22 +93,23 @@ def follow(del_fets, net, parent_net): try: expr = [] - for fet1, net1 in nets[net][2]: + for symbol1, net1 in nets[net][2]: if net1 != parent_net: - del_fets.append(fet1) - valid1, _, _, poly_net1, _, _, _, _, _ = fets[fet1] - if not valid1: + del_symbols.append(symbol1) + symbol_type1, _, (poly_net1, _), _, _, _, _, _ = symbols[symbol1] + if symbol_type1 == -1: raise InvalidGate() - expr1 = follow(del_fets, net1, net) + expr1 = follow(del_symbols, net1, net) expr.extend([i + [poly_net1] for i in expr1]) return expr finally: nets[net][0] = False -gates = [] -for i in range(len(fets)): - valid, block, _, poly_net, diff_nets, _, _, _, _ = fets[i] - if valid: +for i in range(len(symbols)): + symbol_type = symbols[i][0] + if symbol_type == SYMBOL_TYPE_FET: + [_, block, (poly_net, diff_nets), _, _, _, _, _] = symbols[i] + # look for a fet wired as a pull-up to VCC (implied to be depletion) if ( poly_net != net_gnd and @@ -100,16 +118,16 @@ for i in range(len(fets)): ): # the top level analysis ignores anything connected to the output that # doesn't look like a pull-down (could be driving a transmission gate) - del_fets = [i] + del_symbols = [i] expr = [] nets[poly_net][0] = True # set visited flag to avoid infinite recursion - for fet1, net1 in nets[poly_net][2]: - valid1, _, _, poly_net1, _, _, _, _, _ = fets[fet1] - if valid1: - del_fets1 = [fet1] + for symbol1, net1 in nets[poly_net][2]: + symbol_type1, _, (poly_net1, _), _, _, _, _, _ = symbols[symbol1] + if symbol_type1 != -1: + del_symbols1 = [symbol1] try: - expr1 = follow(del_fets1, net1, poly_net) - del_fets.extend(del_fets1) + expr1 = follow(del_symbols1, net1, poly_net) + del_symbols.extend(del_symbols1) expr.extend([i + [poly_net1] for i in expr1]) except InvalidGate: pass @@ -117,28 +135,28 @@ for i in range(len(fets)): # don't allow constant expr (probably an error or analog circuit) if expr != [] and expr != [[]]: - for fet1 in del_fets: - fets[fet1][0] = False - gates.append((block, poly_net, expr)) - -n_fets = sum([int(valid) for valid, _, _, _, _, _, _, _, _ in fets]) -print(n_fets, len(gates)) -for [ - valid, - block, - channel, - poly_net, - diff_nets, - x, - y, - mass, - r_matrix -] in fets: - if valid: - print(block, channel, poly_net, len(diff_nets), x, y, mass) + for symbol1 in del_symbols: + symbols[symbol1][0] = -1 + symbols.append((SYMBOL_TYPE_GATE, block, (poly_net, expr))) + +for i in range(len(symbols)): + symbol_type = symbols[i][0] + if symbol_type == SYMBOL_TYPE_FET: + [ + _, + block, + (poly_net, diff_nets), + channel, + x, + y, + mass, + r_matrix + ] = symbols[i] + print(symbol_type, block, poly_net, len(diff_nets), channel, x, y, mass) for i in range(len(diff_nets)): print(' ' + diff_nets[i], ' '.join([str(j) for j in r_matrix[i]])) -for block, output_net, expr in gates: - print(block, output_net, len(expr)) - for i in expr: - print(' ' + ' '.join(i)) + elif symbol_type == SYMBOL_TYPE_GATE: + [_, block, (output_net, expr)] = symbols[i] + print(symbol_type, block, output_net, len(expr)) + for i in expr: + print(' ' + ' '.join(i)) -- 2.34.1