fix(iomux): Fix port signal length (given mux size non-power of 2)
[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 rev=False):
69 bank = offs[0]
70 pf = self.pinfn(suffix, bank)
71 print ("pf", suffix, bank, pf)
72 pingroup, gangedgroup, clock = pf
73 if clock:
74 self.pinouts.clocks[self.fname] = clock
75 if isinstance(pingroup, tuple):
76 prefix, pingroup = pingroup
77 else:
78 prefix = self.fname
79 if start and limit: # limit turns into an offset from start
80 limit = start + limit
81 sk = "%s:%s" % (self.fname, str(suffix))
82 print ("pingroup pre", sk, pingroup)
83 pingroup = pingroup[start:limit] # see comment in spec.pinfunctions
84 if rev:
85 # reverse order of pingroup
86 pingroup.reverse()
87 print ("pingroup post", sk, pingroup)
88 if sk in self.pinouts.byspec:
89 self.pinouts.byspec[sk] += pingroup
90 else:
91 self.pinouts.byspec[sk] = deepcopy(pingroup)
92 pins = Pins(prefix, pingroup, self.bankspec,
93 suffix, offs, bank, mux,
94 spec, origsuffix=suffix, gangedgrp=gangedgroup)
95 fname = self.pinouts.pinmerge(pins)
96 self.pinouts.setganged(fname, gangedgroup)
97
98 # pinouts class
99
100
101 class Pinouts(object):
102 def __init__(self, bankspec):
103 self.bankspec = bankspec
104 self.pins = {}
105 self.fnspec = {}
106 self.ganged = {}
107 self.clocks = {}
108 self.byspec = {}
109 for fname, pinfn in pinspec:
110 if isinstance(pinfn, tuple):
111 name, pinfn = pinfn
112 else:
113 name = pinfn.__name__
114 pin = PinGen(self, fname, pinfn, self.bankspec)
115 setattr(self, name, pin)
116
117 def setganged(self, fname, grp):
118 grp = map(lambda x: x[:-1], grp)
119 if fname not in self.ganged:
120 self.ganged[fname] = []
121 self.ganged[fname] += grp
122
123 def __contains__(self, k):
124 return k in self.pins
125
126 def has_key(self, k):
127 return k in self.pins
128
129 def add_spec(self, k, v):
130 self.fnspec[k] = v
131
132 def update(self, pinidx, v):
133 if pinidx not in self.pins:
134 self.pins[pinidx] = v
135 else:
136 for k in v:
137 assert k not in self.pins[pinidx], \
138 "pin %d position %d already taken\n%s\n%s" % \
139 (pinidx, k, str(v), self.pins[pinidx])
140 self.pins[pinidx].update(v)
141
142 def keys(self):
143 return self.pins.keys()
144
145 def items(self):
146 return self.pins.items()
147
148 def get(self, k):
149 return self.pins[k]
150
151 def __len__(self):
152 return len(self.pins)
153
154 def __delitem__(self, k):
155 del self.pins[k]
156
157 def __getitem__(self, k):
158 return self.pins[k]
159
160 def pinmerge(self, fn):
161 # hack, store the function specs in the pins dict
162 fname = fn.fname
163 suffix = fn.origsuffix
164 bank = fn.bank
165
166 if not hasattr(self, 'fnspec'):
167 self.fnspec = pins
168 if fname == 'GPIO':
169 fname = fname + bank
170 assert 'EINT' not in self
171 if fname not in self.fnspec:
172 self.add_spec(fname, {})
173 if suffix or fname == 'EINT' or fname == 'PWM':
174 specname = fname + suffix
175 else:
176 specname = fname
177 # print "fname bank specname suffix ", fname, bank, specname, repr(
178 # suffix)
179 if specname in self.fnspec[fname]:
180 # ok so some declarations may bring in different
181 # names at different stages (EINT, PWM, flexbus1/2)
182 # so we have to merge the names in. main thing is
183 # the pingroup
184 tomerge = self.fnspec[fname][specname]
185 for p in fn.pingroup:
186 if p not in tomerge.pingroup:
187 tomerge.pingroup.append(p)
188 tomerge.pins.update(fn.pins)
189 tomerge.fntype.update(fn.fntype)
190 else:
191 self.fnspec[fname][specname] = deepcopy(fn)
192
193 # merge actual pins
194 for (pinidx, v) in fn.pins.items():
195 self.update(pinidx, v)
196
197 return fname
198
199
200 class Pins(object):
201
202 def __init__(self, fname, pingroup, bankspec, suffix, offs, bank, mux,
203 spec=None, limit=None, origsuffix=None, gangedgrp=None):
204
205 # function type can be in, out or inout, represented by - + *
206 # strip function type out of each pin name
207 self.fntype = {}
208 for i in range(len(pingroup)):
209 pname = pingroup[i]
210 if not pname:
211 continue
212 fntype = pname[-1]
213 if fntype not in '+-*':
214 continue
215 pname = pname[:-1]
216 fntype = {'-': 'in', '+': 'out', '*': 'inout'}[fntype]
217 self.fntype[pname] = fntype
218 pingroup[i] = pname
219
220 self.fname = fname
221 self.pingroup = pingroup
222 self.gangedgroup = gangedgrp
223 self.bankspec = bankspec
224 self.suffix = suffix
225 self.origsuffix = origsuffix or suffix
226 self.bank = bank
227 self.mux = mux
228
229 # create consistent name suffixes
230 pingroup = namesuffix(fname, suffix, pingroup)
231 suffix = '' # hack
232
233 res = {}
234 names = {}
235 idx = 0
236 for name in pingroup[:limit]:
237 if suffix and name:
238 name_ = "%s_%s" % (name, suffix)
239 else:
240 name_ = name
241 if spec and name in spec:
242 continue
243 pin = {mux: (name_, bank)}
244 offs_bank, offs_ = offs
245 idx_ = offs_ + idx
246 idx += 1
247 idx_ += bankspec[bank]
248 res[idx_] = pin
249 names[name] = idx_
250 for name in pingroup:
251 if suffix and name:
252 name_ = "%s_%s" % (name, suffix)
253 else:
254 name_ = name
255 if not spec:
256 continue
257 if name not in spec:
258 continue
259 idx_, mux_ = spec[name]
260 idx_ = names[idx_]
261 pin = {mux_: (name_, bank)}
262 if idx_ in res:
263 res[idx_].update(pin)
264 else:
265 res[idx_] = pin
266
267 self.pins = res