Add module circuits: models traces, components, and PCB of a digital circuit
authorTJ <git@iam.tj>
Sat, 16 Nov 2013 13:50:36 +0000 (13:50 +0000)
committerTJ <git@iam.tj>
Sat, 16 Nov 2013 13:50:36 +0000 (13:50 +0000)
circuits.py [new file with mode: 0644]

diff --git a/circuits.py b/circuits.py
new file mode 100644 (file)
index 0000000..f7bae7c
--- /dev/null
@@ -0,0 +1,282 @@
+#!/usr/bin/python3
+# (c) Copyright 2013 TJ <hacker@iam.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  """
+