f66e474d4a9d8a52a7768e920da50e753bf3e99e
[pinmux.git] / src / spec / interfaces.py
1 #!/usr/bin/env python
2
3 from spec.pinfunctions import pinspec
4 from copy import deepcopy
5
6
7 def namesuffix(name, suffix, namelist):
8 names = []
9 for n in namelist:
10 if n:
11 names.append("%s%s_%s" % (name, suffix, n))
12 else:
13 names.append("%s_%s" % (name, suffix))
14 return names
15
16
17 class PinGen(object):
18 """ a meta-helper which creates pins from the pinspec
19 and adds them to the pinouts.
20
21 __call__ is used to effectively create a lambda function, which
22 in combination with setattr (below) gives the function a name
23 in the Pinouts class, according to the pinspec.
24
25 arguments to __call__ (which ends up as Pinouts.i2s, Pinouts.sdmmc
26 and so on, according to spec.pinfunctions.pinspec) are:
27
28 suffix: e.g. GPIO or SD or SPI
29 offs : a tuple of (Bank, Bank offset) as a string, integer
30 mux : which column in the multiplexer
31 start : the start of a subset of pins to be inserted
32 limit : the end of the subset (or the number if start also given)
33 spec : *EXTRA* pins to be inserted (at different implicit positions)
34
35 the pins are inserted with the suffix, starting from the
36 offset position using offs as the row and mux as the column,
37 and working in a constant increment down the rows.
38
39 spec is slightly complicated, basically there's extra
40 functions that we want to be on the same pin (but a different mux)
41 because their use is mutually-exclusive. without this spec
42 argument the extra pins would need to be MANUALLY moved about
43 during the development of the pinmux, if the "main" pins
44 were also moved about. this would be a pain.
45
46 so instead, extra pins are given of the form:
47 { 'EXTRA1' : ('PREEXISTING_NAME', MUX_COLUMN),
48 ...
49 }
50
51 where the function EXTRA1 will always be placed on the SAME ROW
52 as PREEXISTING_NAME, just in MUX_COLUMN. this may be done
53 several times i.e. multiple new EXTRA functions can be added
54 on the same row as PRE_EXISTING_NAME, just with different
55 MUX_COLUMN values.
56
57 Note: spec must implicitly be in the same Bank.
58 """
59
60 def __init__(self, pinouts, fname, pinfn, bankspec):
61 self.pinouts = pinouts
62 self.bankspec = bankspec
63 self.pinfn = pinfn
64 self.fname = fname
65
66 def __call__(self, suffix, offs, mux,
67 start=None, limit=None, spec=None, origsuffix=None):
68 bank = offs[0]
69 pf = self.pinfn(suffix, bank)
70 print "pf", suffix, bank, pf
71 pingroup, gangedgroup, clock = pf
72 if clock:
73 self.pinouts.clocks[self.fname] = clock
74 if isinstance(pingroup, tuple):
75 prefix, pingroup = pingroup
76 else:
77 prefix = self.fname
78 if start and limit: # limit turns into an offset from start
79 limit = start + limit
80 sk = "%s:%s" % (self.fname, str(suffix))
81 print "pingroup pre", sk, pingroup
82 pingroup = pingroup[start:limit] # see comment in spec.pinfunctions
83 print "pingroup post", sk, pingroup
84 if self.pinouts.byspec.has_key(sk):
85 self.pinouts.byspec[sk] += pingroup
86 else:
87 self.pinouts.byspec[sk] = deepcopy(pingroup)
88 pins = Pins(prefix, pingroup, self.bankspec,
89 suffix, offs, bank, mux,
90 spec, origsuffix=suffix, gangedgrp=gangedgroup)
91 fname = self.pinouts.pinmerge(pins)
92 self.pinouts.setganged(fname, gangedgroup)
93
94 # pinouts class
95
96
97 class Pinouts(object):
98 def __init__(self, bankspec):
99 self.bankspec = bankspec
100 self.pins = {}
101 self.fnspec = {}
102 self.ganged = {}
103 self.clocks = {}
104 self.byspec = {}
105 for fname, pinfn in pinspec:
106 if isinstance(pinfn, tuple):
107 name, pinfn = pinfn
108 else:
109 name = pinfn.__name__
110 pin = PinGen(self, fname, pinfn, self.bankspec)
111 setattr(self, name, pin)
112
113 def setganged(self, fname, grp):
114 grp = map(lambda x: x[:-1], grp)
115 if fname not in self.ganged:
116 self.ganged[fname] = []
117 self.ganged[fname] += grp
118
119 def __contains__(self, k):
120 return k in self.pins
121
122 def has_key(self, k):
123 return k in self.pins
124
125 def add_spec(self, k, v):
126 self.fnspec[k] = v
127
128 def update(self, pinidx, v):
129 if pinidx not in self.pins:
130 self.pins[pinidx] = v
131 else:
132 for k in v:
133 assert k not in self.pins[pinidx], \
134 "pin %d position %d already taken\n%s\n%s" % \
135 (pinidx, k, str(v), self.pins[pinidx])
136 self.pins[pinidx].update(v)
137
138 def keys(self):
139 return self.pins.keys()
140
141 def items(self):
142 return self.pins.items()
143
144 def get(self, k):
145 return self.pins[k]
146
147 def __len__(self):
148 return len(self.pins)
149
150 def __delitem__(self, k):
151 del self.pins[k]
152
153 def __getitem__(self, k):
154 return self.pins[k]
155
156 def pinmerge(self, fn):
157 # hack, store the function specs in the pins dict
158 fname = fn.fname
159 suffix = fn.origsuffix
160 bank = fn.bank
161
162 if not hasattr(self, 'fnspec'):
163 self.fnspec = pins
164 if fname == 'GPIO':
165 fname = fname + bank
166 assert 'EINT' not in self
167 if fname not in self.fnspec:
168 self.add_spec(fname, {})
169 if suffix or fname == 'EINT' or fname == 'PWM':
170 specname = fname + suffix
171 else:
172 specname = fname
173 # print "fname bank specname suffix ", fname, bank, specname, repr(
174 # suffix)
175 if specname in self.fnspec[fname]:
176 # ok so some declarations may bring in different
177 # names at different stages (EINT, PWM, flexbus1/2)
178 # so we have to merge the names in. main thing is
179 # the pingroup
180 tomerge = self.fnspec[fname][specname]
181 for p in fn.pingroup:
182 if p not in tomerge.pingroup:
183 tomerge.pingroup.append(p)
184 tomerge.pins.update(fn.pins)
185 tomerge.fntype.update(fn.fntype)
186 else:
187 self.fnspec[fname][specname] = deepcopy(fn)
188
189 # merge actual pins
190 for (pinidx, v) in fn.pins.items():
191 self.update(pinidx, v)
192
193 return fname
194
195
196 class Pins(object):
197
198 def __init__(self, fname, pingroup, bankspec, suffix, offs, bank, mux,
199 spec=None, limit=None, origsuffix=None, gangedgrp=None):
200
201 # function type can be in, out or inout, represented by - + *
202 # strip function type out of each pin name
203 self.fntype = {}
204 for i in range(len(pingroup)):
205 pname = pingroup[i]
206 if not pname:
207 continue
208 fntype = pname[-1]
209 if fntype not in '+-*':
210 continue
211 pname = pname[:-1]
212 fntype = {'-': 'in', '+': 'out', '*': 'inout'}[fntype]
213 self.fntype[pname] = fntype
214 pingroup[i] = pname
215
216 self.fname = fname
217 self.pingroup = pingroup
218 self.gangedgroup = gangedgrp
219 self.bankspec = bankspec
220 self.suffix = suffix
221 self.origsuffix = origsuffix or suffix
222 self.bank = bank
223 self.mux = mux
224
225 # create consistent name suffixes
226 pingroup = namesuffix(fname, suffix, pingroup)
227 suffix = '' # hack
228
229 res = {}
230 names = {}
231 idx = 0
232 for name in pingroup[:limit]:
233 if suffix and name:
234 name_ = "%s_%s" % (name, suffix)
235 else:
236 name_ = name
237 if spec and name in spec:
238 continue
239 pin = {mux: (name_, bank)}
240 offs_bank, offs_ = offs
241 idx_ = offs_ + idx
242 idx += 1
243 idx_ += bankspec[bank]
244 res[idx_] = pin
245 names[name] = idx_
246 for name in pingroup:
247 if suffix and name:
248 name_ = "%s_%s" % (name, suffix)
249 else:
250 name_ = name
251 if not spec:
252 continue
253 if name not in spec:
254 continue
255 idx_, mux_ = spec[name]
256 idx_ = names[idx_]
257 pin = {mux_: (name_, bank)}
258 if idx_ in res:
259 res[idx_].update(pin)
260 else:
261 res[idx_] = pin
262
263 self.pins = res