f7bae7c4b133487adadf4dddf5998c98ae40cb2c
[base2-runner.git] / circuits.py
1 #!/usr/bin/python3
2 # (c) Copyright 2013 TJ <hacker@iam.tj>
3 # Licensed on the terms of the GNU General Public License version 3 (see COPYING)
4 #
5 # Base2 Runner: Circuit design and management 
6 #
7 # TODO: module in development, not ready for any use yet
8 #
9
10 import os.path, pygame, engine
11 from library import enum
12
13 class TraceSegment:
14  """ Models a straight line segment of a complete Trace """
15  # C-style pseudo-enum representing the directions a segment might run
16  directions = enum('N', 'NE', 'E', 'SE', 'S' , 'SW', 'W' , 'NW')
17  angles     = list( 0 ,  45 ,  90,  135,  180,  225,  270,  315)
18
19  # references to an attached Via and its position
20  via = None
21  via_pos = enum('START', 'MID', 'END')
22  via_position = None
23
24  def __init__(self, length=1, direction=TraceSegment.directions.E, via=None, position=TraceSegment.via_pos.START):
25   """ default segment runs for one game tile length left-to-right
26    length:    number of game 'tiles' this segment covers
27    direction:     direction
28   """
29   # ensure all values are legal before doing any assignment
30   if not direction in TraceSegment.directions.reverse_mapping:
31    # 'direction' is an index enum, so ensure it exists in the enum before using it to select the angle
32    raise IndexError('direction invalid')
33   elif not position in TraceSegment.via_pos:
34    # 'position' is an enum so ensure it is valid
35    raise IndexError('position invalid')
36   elif via and not isinstance(via, Via):
37    raise TypeError('expecting a Via')
38
39   self.length = length
40   self.angle = angles[direction]
41   self.via = via
42   self.via_position = position
43  
44  def via_set(self, via, position):
45   """ link to the related Via
46    via:       Via object
47    position:  enum via_pos
48   """
49   if via and isinstance(via, Via):
50    if position in via_pos:
51     # FIXME: ensure the line above works for testing position's membership of via_pos
52     self.via = via
53     self.via_position = position
54
55
56 class Trace:
57  """ Models a single circuit trace made up of multiple segments """
58  segments = list() # ordered list of one or more TraceSegments
59  colours = dict()
60  colours['metal'] = (192, 192, 192) # colours of the trace
61  colours['shadow'] = (64, 64, 64)
62  colours['reflection'] = (255, 255, 255)
63
64  def check_range(self, colour):
65   """ Ensure a colour value is legal """
66   return colour >= 0 and colour <= 255
67
68  def __init__(self, metal=None, shadow=None, reflection=None, segments=TraceSegment()):
69   # set the colours of the trace to give a 3D raised appearance
70   if metal and check_range(metal): self.colours['metal'] = metal
71   if shadow and check_range(shadow): self.colours['shadow'] = shadow
72   if reflection and check_range(reflection): self.colours['reflection'] = reflection
73
74   # initialise the via dictionary
75   with self.segments[0]:
76    via = None
77    via_position = via_pos.START
78
79  def draw(self, surface):
80   """ Draw the Trace onto pygame.Surface """
81
82 class Via:
83  """ Models a through-hole otherwise known as a Via """
84  trace = None # the Trace this via is attached to
85  linked = None # the Via this via links to
86
87
88 class Bus:
89  """ Group of traces all going in the same direction """
90  width = 8 # default is an 8-bit bus
91  group = None
92
93  def __init__(self, buswidth=8):
94   """ Create a new bus consisting of 'buswidth' traces """
95
96
97 class IntegratedCircuit:
98  """ semiconductor component """
99  signals = None # key = signal name, value = pin number
100
101  def __init__(self):
102   """ populate pseudo-enum with common signal names """
103   if not IntegratedCircuit.signals:
104    signals = ('NC', 'GND', 'Vcc', 'Vdd')
105    # logic gate signal names (covers multi-gate components up to quad-gate)
106    for l in ('A', 'B', 'Y'):
107     for n in range(1, 5):
108      signals.append("%s%s" % (n, l))
109
110   IntegratedCircuit.signals = enum(signals)
111
112
113 class DualInLine(IntegratedCircuit):
114  """ Dual In-Line Integrated Circuit """
115  sides = 2
116  qty = 0
117  pins = list()
118
119  def __init__(self, pins):
120   if pins > 0 and pins % 2 == 0:
121    for p in range(1, pins+1):
122     pins[p] = IntegratedCircuit.signals.NC
123
124
125 class Gate:
126  """ Models a single logic gate """
127  types = None
128  inputs = list()
129  output = False
130  type = None
131
132  def __init__(self, type, inputs=None):
133   # populate dictionary with common logic gate types
134   if not Gate.types:
135    Gate.types = enum('AND', 'OR', 'XOR',  'NOT', 'NAND', 'NOR')
136
137   if type in Gate.types:
138    self.type = type
139
140   if inputs and type(inputs) is list and isinstance(inputs[0], bool):
141    self.inputs = inputs
142   else:
143    # default to 2 inputs at logic 0
144    self.inputs = (False, False)
145
146   self.input_max = len(self.inputs)
147   self.update()
148
149  def update(self):
150   """ Needs to be over-ridden in derived classes """
151   return
152
153  def set_input(self, input, value):
154   if input >= 0 and input < self.input_max:
155    if isinstance(value, bool):
156     self.inputs[input] = value
157     self.update()
158
159  def get_output(self):
160   return self.output
161
162  def min_inputs(self, inputs, minimum):
163   """ enforce a minimum number of logic inputs """
164   return inputs if len(inputs) >= minimum else (inputs, (False for i in range(0, minimum - len(inputs))))
165    
166
167 # Derive each of the common logic gate types
168 class GateAND(Gate):
169  def __init__(self, inputs=None):
170   Gate.__init__(Gate.types.AND, self.min_inputs(inputs, 2))
171
172  def update(self):
173   self.output = self.inputs[0]
174   for i in range(1, self.input_max):
175    self.output &= self.inputs[i] # AND
176
177
178 class GateOR(Gate):
179  def __init__(self, inputs=None):
180   Gate.__init__(Gate.types.OR, self.min_inputs(inputs, 2))
181
182  def update(self):
183   self.output = self.inputs[0]
184   for i in range (1, self.input_max):
185    self.output |= self.inputs[i] # OR
186
187
188 class GateXOR(Gate):
189  def __init__(self, inputs=None):
190   Gate.__init__(Gate.types.XOR, self.min_inputs(inputs, 2))
191
192  def update(self):
193   self.output = self.inputs[0]
194   for i in range (1, self.input_max):
195    self.output ^= self.inputs[i] # XOR
196
197
198 class GateNOT(Gate):
199  def __init__(self, inputs=(False, )):
200   if len(inputs) > 1:
201    # inverters can only have 1 input
202    inputs = (inputs[0], )
203   Gate.__init__(Gate.types.NOT, inputs)
204
205  def update(self):
206   self.output = ~self.inputs[0] # invert
207
208
209 class GateNAND(Gate):
210   def __init__(self, inputs=None):
211    Gate.__init__(Gate.types.NAND, self.min_inputs(inputs, 2))
212
213  def update(self):
214   self.output = self.inputs[0]
215   for i in range(1, self.input_max):
216    self.output &= self.inputs[i] # AND
217   self.output = ~self.output # invert
218
219
220 class GateNOR(Gate):
221  def __init__(self, inputs=None):
222   Gate.__init__(Gate.types.NOR, self.min_inputs(inputs, 2))
223
224  def update(self):
225   self.output = self.inputs[0]
226   for i in range (1, self.input_max):
227    self.output |= self.inputs[i] # OR
228   self.output = ~self.output # invert
229
230
231 class ICLogic:
232  """ Models an IC that contains one or more digital logic gates """
233  gates = list() # list of gates
234  package = None
235
236  def __init__(self, gates=(), package=DualInLine(14)):
237   if package: self.package = package
238   if type(gates) is list and type gates[0] is Gate: self.gates = gates
239
240  def add(self, gate):
241   if gate and type(gate) is Gate: self.gates.append(gate)
242
243
244 class SN7400(ICLogic, DualInLine):
245  """ Model of a Texas Instruments SN7400 quad 2-input NAND gate DIP14 package """
246
247  def __init__(self):
248   # call the super classes constructors
249   gates = list()
250   for i in range(0, 4):
251    gates.append(GateNAND())
252   # TODO: may need to move this functionality into an intermediate super class that combines package and Logic chip
253   ICLogic.__init__(gates)
254   DualInLine.__init(14)
255
256
257 class SN741G00(ICLogic, ...):
258  """ Model of a Texas Instruments SN741G00 single 2-input NAND gate """
259
260
261 class TSSOP(IntegratedCircuit):
262  """ Thin Shrink Small Outline Package """
263  sides = 2
264  pins = list()
265
266  def __init__(self, pins):
267   
268
269
270 class PCB:
271  """ Models a digital electronics logic circuit printed on a circuit board  """
272  layout = None
273
274  def __init__(self):
275   """ """
276
277  def trace_add(self, trace_list):
278   """ Add a """
279  
280  def component_insert(self, component):
281   """ Adds a component to  """
282