From a039749b7dddbbb81f65eb529bc72869ffaa2cfa Mon Sep 17 00:00:00 2001 From: TJ Date: Sat, 16 Nov 2013 13:50:36 +0000 Subject: [PATCH] Add module circuits: models traces, components, and PCB of a digital circuit --- circuits.py | 282 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 282 insertions(+) create mode 100644 circuits.py diff --git a/circuits.py b/circuits.py new file mode 100644 index 0000000..f7bae7c --- /dev/null +++ b/circuits.py @@ -0,0 +1,282 @@ +#!/usr/bin/python3 +# (c) Copyright 2013 TJ +# Licensed on the terms of the GNU General Public License version 3 (see COPYING) +# +# Base2 Runner: Circuit design and management +# +# TODO: module in development, not ready for any use yet +# + +import os.path, pygame, engine +from library import enum + +class TraceSegment: + """ Models a straight line segment of a complete Trace """ + # C-style pseudo-enum representing the directions a segment might run + directions = enum('N', 'NE', 'E', 'SE', 'S' , 'SW', 'W' , 'NW') + angles = list( 0 , 45 , 90, 135, 180, 225, 270, 315) + + # references to an attached Via and its position + via = None + via_pos = enum('START', 'MID', 'END') + via_position = None + + def __init__(self, length=1, direction=TraceSegment.directions.E, via=None, position=TraceSegment.via_pos.START): + """ default segment runs for one game tile length left-to-right + length: number of game 'tiles' this segment covers + direction: direction + """ + # ensure all values are legal before doing any assignment + if not direction in TraceSegment.directions.reverse_mapping: + # 'direction' is an index enum, so ensure it exists in the enum before using it to select the angle + raise IndexError('direction invalid') + elif not position in TraceSegment.via_pos: + # 'position' is an enum so ensure it is valid + raise IndexError('position invalid') + elif via and not isinstance(via, Via): + raise TypeError('expecting a Via') + + self.length = length + self.angle = angles[direction] + self.via = via + self.via_position = position + + def via_set(self, via, position): + """ link to the related Via + via: Via object + position: enum via_pos + """ + if via and isinstance(via, Via): + if position in via_pos: + # FIXME: ensure the line above works for testing position's membership of via_pos + self.via = via + self.via_position = position + + +class Trace: + """ Models a single circuit trace made up of multiple segments """ + segments = list() # ordered list of one or more TraceSegments + colours = dict() + colours['metal'] = (192, 192, 192) # colours of the trace + colours['shadow'] = (64, 64, 64) + colours['reflection'] = (255, 255, 255) + + def check_range(self, colour): + """ Ensure a colour value is legal """ + return colour >= 0 and colour <= 255 + + def __init__(self, metal=None, shadow=None, reflection=None, segments=TraceSegment()): + # set the colours of the trace to give a 3D raised appearance + if metal and check_range(metal): self.colours['metal'] = metal + if shadow and check_range(shadow): self.colours['shadow'] = shadow + if reflection and check_range(reflection): self.colours['reflection'] = reflection + + # initialise the via dictionary + with self.segments[0]: + via = None + via_position = via_pos.START + + def draw(self, surface): + """ Draw the Trace onto pygame.Surface """ + +class Via: + """ Models a through-hole otherwise known as a Via """ + trace = None # the Trace this via is attached to + linked = None # the Via this via links to + + +class Bus: + """ Group of traces all going in the same direction """ + width = 8 # default is an 8-bit bus + group = None + + def __init__(self, buswidth=8): + """ Create a new bus consisting of 'buswidth' traces """ + + +class IntegratedCircuit: + """ semiconductor component """ + signals = None # key = signal name, value = pin number + + def __init__(self): + """ populate pseudo-enum with common signal names """ + if not IntegratedCircuit.signals: + signals = ('NC', 'GND', 'Vcc', 'Vdd') + # logic gate signal names (covers multi-gate components up to quad-gate) + for l in ('A', 'B', 'Y'): + for n in range(1, 5): + signals.append("%s%s" % (n, l)) + + IntegratedCircuit.signals = enum(signals) + + +class DualInLine(IntegratedCircuit): + """ Dual In-Line Integrated Circuit """ + sides = 2 + qty = 0 + pins = list() + + def __init__(self, pins): + if pins > 0 and pins % 2 == 0: + for p in range(1, pins+1): + pins[p] = IntegratedCircuit.signals.NC + + +class Gate: + """ Models a single logic gate """ + types = None + inputs = list() + output = False + type = None + + def __init__(self, type, inputs=None): + # populate dictionary with common logic gate types + if not Gate.types: + Gate.types = enum('AND', 'OR', 'XOR', 'NOT', 'NAND', 'NOR') + + if type in Gate.types: + self.type = type + + if inputs and type(inputs) is list and isinstance(inputs[0], bool): + self.inputs = inputs + else: + # default to 2 inputs at logic 0 + self.inputs = (False, False) + + self.input_max = len(self.inputs) + self.update() + + def update(self): + """ Needs to be over-ridden in derived classes """ + return + + def set_input(self, input, value): + if input >= 0 and input < self.input_max: + if isinstance(value, bool): + self.inputs[input] = value + self.update() + + def get_output(self): + return self.output + + def min_inputs(self, inputs, minimum): + """ enforce a minimum number of logic inputs """ + return inputs if len(inputs) >= minimum else (inputs, (False for i in range(0, minimum - len(inputs)))) + + +# Derive each of the common logic gate types +class GateAND(Gate): + def __init__(self, inputs=None): + Gate.__init__(Gate.types.AND, self.min_inputs(inputs, 2)) + + def update(self): + self.output = self.inputs[0] + for i in range(1, self.input_max): + self.output &= self.inputs[i] # AND + + +class GateOR(Gate): + def __init__(self, inputs=None): + Gate.__init__(Gate.types.OR, self.min_inputs(inputs, 2)) + + def update(self): + self.output = self.inputs[0] + for i in range (1, self.input_max): + self.output |= self.inputs[i] # OR + + +class GateXOR(Gate): + def __init__(self, inputs=None): + Gate.__init__(Gate.types.XOR, self.min_inputs(inputs, 2)) + + def update(self): + self.output = self.inputs[0] + for i in range (1, self.input_max): + self.output ^= self.inputs[i] # XOR + + +class GateNOT(Gate): + def __init__(self, inputs=(False, )): + if len(inputs) > 1: + # inverters can only have 1 input + inputs = (inputs[0], ) + Gate.__init__(Gate.types.NOT, inputs) + + def update(self): + self.output = ~self.inputs[0] # invert + + +class GateNAND(Gate): + def __init__(self, inputs=None): + Gate.__init__(Gate.types.NAND, self.min_inputs(inputs, 2)) + + def update(self): + self.output = self.inputs[0] + for i in range(1, self.input_max): + self.output &= self.inputs[i] # AND + self.output = ~self.output # invert + + +class GateNOR(Gate): + def __init__(self, inputs=None): + Gate.__init__(Gate.types.NOR, self.min_inputs(inputs, 2)) + + def update(self): + self.output = self.inputs[0] + for i in range (1, self.input_max): + self.output |= self.inputs[i] # OR + self.output = ~self.output # invert + + +class ICLogic: + """ Models an IC that contains one or more digital logic gates """ + gates = list() # list of gates + package = None + + def __init__(self, gates=(), package=DualInLine(14)): + if package: self.package = package + if type(gates) is list and type gates[0] is Gate: self.gates = gates + + def add(self, gate): + if gate and type(gate) is Gate: self.gates.append(gate) + + +class SN7400(ICLogic, DualInLine): + """ Model of a Texas Instruments SN7400 quad 2-input NAND gate DIP14 package """ + + def __init__(self): + # call the super classes constructors + gates = list() + for i in range(0, 4): + gates.append(GateNAND()) + # TODO: may need to move this functionality into an intermediate super class that combines package and Logic chip + ICLogic.__init__(gates) + DualInLine.__init(14) + + +class SN741G00(ICLogic, ...): + """ Model of a Texas Instruments SN741G00 single 2-input NAND gate """ + + +class TSSOP(IntegratedCircuit): + """ Thin Shrink Small Outline Package """ + sides = 2 + pins = list() + + def __init__(self, pins): + + + +class PCB: + """ Models a digital electronics logic circuit printed on a circuit board """ + layout = None + + def __init__(self): + """ """ + + def trace_add(self, trace_list): + """ Add a """ + + def component_insert(self, component): + """ Adds a component to """ + -- 2.17.1