5c596635f8641f52b1a1be1d14fda49280c883c9
[pinmux.git] / src / spec / ifaceprint.py
1 #!/usr/bin/env python
2
3 from copy import deepcopy
4 from collections import OrderedDict
5 from math import pi
6
7 def bond_int_to_ext(pin, bank):
8 """ note that internal numbering is 0-31 whereas the DISPLAY internal
9 numbering is 1-32. this uses the INTERNAL numbering.
10
11 side: N S E W
12
13 outer ring numbers: the python list order of the outer pads
14 middle numbers: the package number wires (plus side they are on)
15 inner numbers: the IO pad *python internal (0-31)* numbers plus side
16
17 0 1 2 3 N 34 35 36 37
18 N102 N101 N100 99 N 68 N67 N66 N65
19 W29 W30 W31 N0 N N31 E29 E30 E31
20
21 25 103 W28 E28 64 25
22 W W W E E E
23 0 128 W3 E3 39 0
24
25 W2 W1 W0 S0 S S31 E0 E1 E2
26 S1 S2 S3 4 S 35 S36 S37 S38
27 0 1 2 3 S 34 35 36 37
28
29 """
30 # returns side, order-on-the-side, pin number
31 if bank == 'N':
32 return 'N', pin+3, 99-pin
33 if bank == 'S':
34 return 'S', pin+3, 4+pin
35 if bank == 'W':
36 if pin >= 29: # 29, 30, 31
37 return 'N', pin-29, 100+(31-pin)
38 if pin <= 2:
39 return 'S', 2-pin, 3-pin
40 return 'W', pin-3, 103+(28-pin)
41 if bank == 'E':
42 if pin >= 29:
43 return 'N', pin+6, 67-(pin-29)
44 if pin <= 2:
45 return 'S', pin+35, 36+pin
46 return 'E', pin-3, 39+(pin-3)
47
48
49 def create_sv(fname, pins):
50 """unsophisticated drawer of an SVG
51 """
52
53 try:
54 import svgwrite
55 except ImportError:
56 print ("WARNING, no SVG image, not producing image %s" % fname)
57 return
58
59 # create internal to external map
60 bondmap = {'N': {}, 'S': {}, 'E': {}, 'W': {} }
61 padside = {'pads.north': 'N', 'pads.east': 'E', 'pads.south': 'S',
62 'pads.west': 'W'}
63 for pinpad, bank in padside.items():
64 for ipin in range(len(pins[pinpad])):
65 eside, enum, epinnum = bond_int_to_ext(ipin, bank)
66 bondmap[eside][enum] = (epinnum, ipin, bank)
67 with open("/tmp/bondmap.txt", "w") as f:
68 for k,v in bondmap.items():
69 f.write("%s\n" % k)
70 for enum, (epinnum, ipin, bank) in v.items():
71 f.write(" %d %d -> %s %d\n" % (enum, epinnum, bank, ipin))
72
73 scale = 15
74 width = len(pins['pads.north']) * scale
75 height = len(pins['pads.east']) * scale
76 woffs = scale*40#-width/2
77 hoffs = scale*40#-height/2
78
79 dwg = svgwrite.Drawing(fname, profile='full',
80 size=(width+scale*80, height+scale*80))
81 dwg.add(dwg.rect((woffs-scale*2, hoffs-scale*2),
82 (woffs+width-scale*34, hoffs+height-scale*34),
83 stroke=svgwrite.rgb(16, 255, 16, '%'),
84 stroke_width=scale/10.0))
85
86 dwg.add(dwg.text("Libre-SOC ls180 QFP-128",
87 insert=(woffs+width/2-scale*5, woffs+height/2),
88 fill='white'))
89 dwg.add(dwg.text("In collaboration with LIP6.fr",
90 insert=(woffs+width/2-scale*5, woffs+height/2+scale),
91 fill='white'))
92 dwg.add(dwg.text("Cell Libraries by Chips4Makers",
93 insert=(woffs+width/2-scale*5, woffs+height/2+scale*2),
94 fill='white'))
95
96 for i, pin in enumerate(pins['pads.west']):
97 ht = hoffs + height - (i * scale) + scale*0.5
98 dwg.add(dwg.line((woffs-scale*2, ht-scale*0.5),
99 (woffs-scale*4.5, ht-scale*0.5),
100 stroke=svgwrite.rgb(16, 255, 16, '%'),
101 stroke_width=scale/10.0))
102 dwg.add(dwg.text(pin.upper(), insert=(woffs-scale*14, ht),
103 fill='black'))
104 dwg.add(dwg.text("W%d" % (i+1), insert=(woffs-scale*1.5, ht),
105 fill='white'))
106
107 for i, pin in enumerate(pins['pads.east']):
108 ht = hoffs + height - (i * scale) + scale*0.5
109 wd = width + woffs + scale*2
110 dwg.add(dwg.line((wd+scale*2, ht-scale*0.5),
111 (wd+scale*4.5, ht-scale*0.5),
112 stroke=svgwrite.rgb(16, 255, 16, '%'),
113 stroke_width=scale/10.0))
114 dwg.add(dwg.text(pin.upper(), insert=(wd+scale*5, ht-scale*0.25),
115 fill='black'))
116 dwg.add(dwg.text("E%d" % (i+1), insert=(wd, ht-scale*0.25),
117 fill='white'))
118
119 for i, pin in enumerate(pins['pads.north']):
120 wd = woffs + i * scale + scale*1.5
121 dwg.add(dwg.line((wd, hoffs-scale*2),
122 (wd, hoffs-scale*4.5),
123 stroke=svgwrite.rgb(16, 255, 16, '%'),
124 stroke_width=scale/10.0))
125 pos=(wd, hoffs-scale*5.0)
126 txt = dwg.text(pin.upper(), insert=pos, fill='black')
127 txt.rotate(-90, pos)
128 dwg.add(txt)
129 pos=(wd+scale*0.25, hoffs-scale*0.25)
130 txt = dwg.text("N%d" % (i+1), insert=pos, fill='white')
131 txt.rotate(-90, pos)
132 dwg.add(txt)
133
134 for i, pin in enumerate(pins['pads.south']):
135 wd = woffs + i * scale + scale*1.5
136 ht = hoffs + height + scale*2
137 dwg.add(dwg.line((wd, ht+scale*2),
138 (wd, ht+scale*4.5),
139 stroke=svgwrite.rgb(16, 255, 16, '%'),
140 stroke_width=scale/10.0))
141 pos=(wd-scale*0.25, ht+scale*5.0)
142 txt = dwg.text(pin.upper(), insert=pos, fill='black')
143 txt.rotate(90, pos)
144 dwg.add(txt)
145 pos=(wd-scale*0.25, ht+scale*0.25)
146 txt = dwg.text("S%d" % (i+1), insert=pos, fill='white')
147 txt.rotate(90, pos)
148 dwg.add(txt)
149
150 dwg.save()
151
152
153 def display(of, pins, banksel=None, muxwidth=4):
154 of.write("""\
155 | Pin | Mux0 | Mux1 | Mux2 | Mux3 |
156 | --- | ----------- | ----------- | ----------- | ----------- |
157 """)
158 pinidx = sorted(pins.keys())
159 for pin in pinidx:
160 pdata = pins.get(pin)
161 if banksel:
162 skip = False
163 for mux in range(muxwidth):
164 if mux not in pdata:
165 continue
166 name, bank = pdata[mux]
167 if banksel != bank:
168 skip = True
169 if skip:
170 continue
171 res = '| %3d |' % pin
172 for mux in range(muxwidth):
173 if mux not in pdata:
174 res += " |"
175 continue
176 name, bank = pdata[mux]
177 res += " %s %-9s |" % (bank, name)
178 of.write("%s\n" % res)
179
180
181 def fnsplit(f):
182 a = ''
183 n = 0
184 if not f.startswith('FB_'):
185 f2 = f.split('_')
186 if len(f2) == 2:
187 if f2[1].isdigit():
188 return f2[0], int(f2[1])
189 return f2[0], f2[1]
190 #print f
191 while f and not f[0].isdigit():
192 a += f[0]
193 f = f[1:]
194 return a, int(f) if f else None
195
196
197 def fnsort(f1, f2):
198 a1, n1 = fnsplit(f1)
199 a2, n2 = fnsplit(f2)
200 x = cmp(a1, a2)
201 if x != 0:
202 return x
203 return cmp(n1, n2)
204
205
206 def find_fn(fname, names):
207 for n in names:
208 if fname.startswith(n):
209 return n
210
211 def map_name(pinmap, fn, fblower, pin, rename):
212 if not rename:
213 if pin[:-1].isdigit():
214 print "map name digit", pin, fn, fblower
215 if fn in ['PWM', 'EINT', 'VDD', 'VSS']:
216 return fn.lower() + pin.lower()
217 if fn == 'GPIO':
218 return 'gpio' + pin[1:].lower()
219 return pin.lower()
220 pin = pin.lower()
221 if fn == 'GPIO':
222 pk = '%s%s_%s' % (fblower, pin[0], pin[:-1])
223 elif pin[:-1].isdigit() and fn != 'EINT':
224 pk = '%s%s_out' % (fblower, pin[:-1])
225 else:
226 pk = '%s_%s' % (fblower, pin[:-1])
227 print "map name", pk, fblower, pinmap.has_key(pk)
228 if not pinmap.has_key(pk):
229 return pin.lower()
230 remapped = pinmap[pk]
231 uscore = remapped.find('_')
232 if uscore == -1:
233 return pin.lower()
234 fn, pin = remapped[:uscore], remapped[uscore+1:] + pin[-1]
235 return pin.lower()
236
237 def python_pindict(of, pinmap, pins, function_names, dname, remap):
238
239 res = OrderedDict()
240 of.write("\n%s = OrderedDict()\n" % dname)
241
242 for k, pingroup in pins.byspec.items():
243 (a, n) = k.split(":")
244 if n.isdigit():
245 a = "%s%s" % (a, n)
246 fblower = a.lower()
247 of.write("%s['%s'] = [ " % (dname, fblower))
248 res[fblower] = []
249 count = 0
250 for i, p in enumerate(pingroup):
251 name = map_name(pinmap, k[0], fblower, p, remap)
252 res[fblower].append(name)
253 of.write("'%s', " % name)
254 count += 1
255 if count == 4 and i != len(pingroup)-1:
256 of.write("\n ")
257 count = 0
258 of.write("]\n")
259 print " dict %s" % dname, a, n, pingroup
260 of.write("\n\n")
261 return res
262
263 def python_dict_fns(of, pinmap, pins, function_names):
264 of.write("# auto-generated by Libre-SOC pinmux program: do not edit\n")
265 of.write("# python src/pinmux_generator.py -v -s {spec} -o {output}\n")
266 of.write("# use OrderedDict to fix stable order for JTAG Boundary Scan\n")
267 of.write("from collections import OrderedDict\n")
268
269 fn_names = function_names.keys()
270 fns = {}
271
272 fnidx = list(fns.keys())
273 fnidx.sort(key=fnsplit)
274
275 print "python fnames", function_names
276 print "python speckeys", pins.byspec.keys()
277 print "python dict fns", dir(pins.gpio)
278 print pins.gpio.pinfn('', '')
279 print pins.pwm.pinfn('', '')
280 print pins.sdmmc.pinfn('', '')
281 print "by spec", pins.byspec
282 print pinmap
283
284 pd = python_pindict(of, {}, pins, function_names, 'pindict', False)
285 ld = python_pindict(of, pinmap, pins, function_names, 'litexdict', True)
286
287 print "pd", pd
288 print "ld", ld
289 # process results and create name map
290 litexmap = OrderedDict()
291 for k in pd.keys():
292 pl = pd[k]
293 ll = ld[k]
294 for pname, lname in zip(pl, ll):
295 pname = "%s_%s" % (k, pname[:-1]) # strip direction +/-/*
296 lname = lname[:-1] # strip direction +/-/*
297 if k in ['eint', 'pwm', 'gpio', 'vdd', 'vss']: # sigh
298 lname = "%s_%s" % (k, lname)
299 litexmap[pname] = lname
300 print "litexmap", litexmap
301 of.write("litexmap = {\n")
302 for k, v in litexmap.items():
303 of.write("\t'%s': '%s',\n" % (k, v))
304 of.write("}\n")
305 return litexmap
306
307
308 def display_fns(of, bankspec, pins, function_names):
309 fn_names = function_names.keys()
310 fns = {}
311 for (pin, pdata) in pins.items():
312 for mux in range(0, 4): # skip GPIO for now
313 if mux not in pdata:
314 continue
315 name, bank = pdata[mux]
316 assert name is not None, str(bank)
317 if name not in fns:
318 fns[name] = []
319 fns[name].append((pin - bankspec[bank], mux, bank))
320
321 fnidx = list(fns.keys())
322 fnidx.sort(key=fnsplit)
323 current_fn = None
324 for fname in fnidx:
325 fnbase = find_fn(fname, fn_names)
326 #fblower = fnbase.lower()
327 assert fnbase in function_names, "fn %s not in descriptions %s" % \
328 (fname, str(function_names.keys()))
329 #print "name", fname, fnbase
330 if fnbase != current_fn:
331 if current_fn is not None:
332 of.write('\n')
333 of.write("## %s\n\n%s\n\n" % (fnbase, function_names[fnbase]))
334 current_fn = fnbase
335 of.write("* %-9s :" % fname)
336 for (pin, mux, bank) in fns[fname]:
337 of.write(" %s%d/%d" % (bank, pin, mux))
338 of.write('\n')
339
340 return fns
341
342
343 def check_functions(of, title, bankspec, fns, pins, required, eint, pwm,
344 descriptions=None):
345 fns = deepcopy(fns)
346 pins = deepcopy(pins)
347 if descriptions is None:
348 descriptions = {}
349 fnidx = fns.keys()
350
351 #print dir(fns)
352 #print dir(pins)
353
354 of.write("# Pinmap for %s\n\n" % title)
355
356 print "fn_idx", fnidx
357 print "fns", fns
358 print "fnspec", pins.fnspec.keys()
359 print "required", required
360 for name in required:
361 of.write("## %s\n\n" % name)
362 if descriptions and name in descriptions:
363 of.write("%s\n\n" % descriptions[name])
364
365 name = name.split(':')
366 if len(name) == 2:
367 findbank = name[0][0]
368 findmux = int(name[0][1:])
369 name = name[1]
370 else:
371 name = name[0]
372 findbank = None
373 findmux = None
374 name = name.split('/')
375 if len(name) == 2:
376 count = int(name[1])
377 else:
378 count = 100000
379 name = name[0]
380 #print name
381 found = set()
382 pinfound = {}
383 located = set()
384 for fname in fnidx:
385 if not fname.startswith(name):
386 continue
387 for k in pins.fnspec.keys():
388 if fname.startswith(k):
389 fk = list(pins.fnspec[k].keys())
390 fn = pins.fnspec[k]
391 fn = fn[fk[0]]
392 #print fname, fn, dir(fn)
393 if count == 100000:
394 count = len(fn.pingroup)
395 for pin, mux, bank in fns[fname]:
396 if findbank is not None:
397 if findbank != bank:
398 continue
399 if findmux != mux:
400 continue
401 pin_ = pin + bankspec[bank]
402 if pin_ in pins:
403 pinfound[pin_] = (fname, pin_, bank, pin, mux)
404
405 pinidx = sorted(pinfound.keys())
406
407 fname = None
408 removedcount = 0
409 print ("pinidx", pinidx)
410 for pin_ in pinidx:
411 fname, pin_, bank, pin, mux = pinfound[pin_]
412 if fname in found:
413 continue
414 found.add(fname)
415 if len(found) > count:
416 continue
417 del pins[pin_]
418 removedcount += 1
419 of.write("* %s %d %s%d/%d\n" % (fname, pin_, bank, pin, mux))
420
421 print fns
422 if removedcount != count:
423 if fname is None:
424 print "no match between required and available pins"
425 else:
426 print ("not all found", name, removedcount, count, title, found,
427 fns[fname])
428 print ("pins found", pinfound)
429
430 # fnidx.sort(fnsort)
431 of.write('\n')
432
433 # gpios
434 gpios = []
435 for name in descriptions.keys():
436 if not name.startswith('GPIO'):
437 continue
438 if name == 'GPIO':
439 continue
440 gpios.append(name)
441 gpios.sort()
442
443 if gpios:
444 of.write("## GPIO\n\n")
445
446 for fname in gpios:
447 if fname in found:
448 continue
449 desc = ''
450 if descriptions and fname in descriptions:
451 desc = ': %s' % descriptions[fname]
452 bank = fname[4]
453 pin = int(fname[7:])
454 pin_ = pin + bankspec[bank]
455 if pin_ not in pins:
456 continue
457 del pins[pin_]
458 found.add(fname)
459 of.write("* %-8s %d %s%-2d %s\n" % (fname, pin_, bank, pin, desc))
460 of.write('\n')
461
462 if eint:
463 display_group(of, bankspec, "EINT", eint, fns, pins, descriptions)
464 if pwm:
465 display_group(of, bankspec, "PWM", pwm, fns, pins, descriptions)
466
467 of.write("## Unused Pinouts (spare as GPIO) for '%s'\n\n" % title)
468 if descriptions and 'GPIO' in descriptions:
469 of.write("%s\n\n" % descriptions['GPIO'])
470 display(of, pins)
471 of.write('\n')
472
473 return pins # unused
474
475
476 def display_group(of, bankspec, title, todisplay, fns, pins, descriptions):
477 of.write("## %s\n\n" % title)
478
479 found = set()
480 for fname in todisplay:
481 desc = ''
482 if descriptions and fname in descriptions:
483 desc = ': %s' % descriptions[fname]
484 fname = fname.split(':')
485 if len(fname) == 2:
486 findbank = fname[0][0]
487 findmux = int(fname[0][1:])
488 fname = fname[1]
489 else:
490 fname = fname[0]
491 findbank = None
492 findmux = None
493 for (pin, mux, bank) in fns[fname]:
494 if findbank is not None:
495 if findbank != bank:
496 continue
497 if findmux != mux:
498 continue
499 if fname in found:
500 continue
501 pin_ = pin + bankspec[bank]
502 if pin_ not in pins:
503 continue
504 del pins[pin_]
505 found.add(fname)
506 of.write("* %s %d %s%d/%d %s\n" %
507 (fname, pin_, bank, pin, mux, desc))
508 of.write('\n')
509
510
511 def display_fixed(of, fixed, offs):
512
513 fkeys = sorted(fixed.keys())
514 pin_ = offs
515 res = []
516 for pin, k in enumerate(fkeys):
517 of.write("## %s\n\n" % k)
518 prevname = ''
519 linecount = 0
520 for name in fixed[k]:
521 if linecount == 4:
522 linecount = 0
523 of.write('\n')
524 if prevname[:2] == name[:2] and linecount != 0:
525 of.write(" %s" % name)
526 linecount += 1
527 else:
528 if linecount != 0:
529 of.write('\n')
530 of.write("* %d: %d %s" % (pin_, pin, name))
531 linecount = 1
532 res.append((pin_, name))
533
534 prevname = name
535 pin_ += 1
536 if linecount != 0:
537 of.write('\n')
538 of.write('\n')
539
540 return res