add internal-to-external bond number conversion
[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 outerpos = {'N': {}, 'S': {}, 'E': {}, 'W': {} }
97 for i, pin in enumerate(pins['pads.west']):
98 ht = hoffs + height - (i * scale) + scale*0.5
99 endline = (woffs-scale*4.5, ht-scale*0.5)
100 dwg.add(dwg.line((woffs-scale*2, ht-scale*0.5),
101 endline,
102 stroke=svgwrite.rgb(16, 255, 16, '%'),
103 stroke_width=scale/10.0))
104 dwg.add(dwg.text(pin.upper(), insert=(woffs-scale*14, ht),
105 fill='black'))
106 dwg.add(dwg.text("W%d" % (i+1), insert=(woffs-scale*1.5, ht),
107 fill='white'))
108
109 for i, pin in enumerate(pins['pads.east']):
110 ht = hoffs + height - (i * scale) + scale*0.5
111 wd = width + woffs + scale*2
112 endline = (wd+scale*4.5, ht-scale*0.5)
113 dwg.add(dwg.line((wd+scale*2, ht-scale*0.5),
114 endline,
115 stroke=svgwrite.rgb(16, 255, 16, '%'),
116 stroke_width=scale/10.0))
117 dwg.add(dwg.text(pin.upper(), insert=(wd+scale*5, ht-scale*0.25),
118 fill='black'))
119 dwg.add(dwg.text("E%d" % (i+1), insert=(wd, ht-scale*0.25),
120 fill='white'))
121
122 for i, pin in enumerate(pins['pads.north']):
123 wd = woffs + i * scale + scale*1.5
124 endline = (wd, hoffs-scale*4.5)
125 dwg.add(dwg.line((wd, hoffs-scale*2),
126 endline,
127 stroke=svgwrite.rgb(16, 255, 16, '%'),
128 stroke_width=scale/10.0))
129 pos=(wd, hoffs-scale*5.0)
130 txt = dwg.text(pin.upper(), insert=pos, fill='black')
131 txt.rotate(-90, pos)
132 dwg.add(txt)
133 pos=(wd+scale*0.25, hoffs-scale*0.25)
134 txt = dwg.text("N%d" % (i+1), insert=pos, fill='white')
135 txt.rotate(-90, pos)
136 dwg.add(txt)
137
138 for i, pin in enumerate(pins['pads.south']):
139 wd = woffs + i * scale + scale*1.5
140 ht = hoffs + height + scale*2
141 endline = (wd, ht+scale*4.5)
142 dwg.add(dwg.line((wd, ht+scale*2),
143 endline,
144 stroke=svgwrite.rgb(16, 255, 16, '%'),
145 stroke_width=scale/10.0))
146 pos=(wd-scale*0.25, ht+scale*5.0)
147 txt = dwg.text(pin.upper(), insert=pos, fill='black')
148 txt.rotate(90, pos)
149 dwg.add(txt)
150 pos=(wd-scale*0.25, ht+scale*0.25)
151 txt = dwg.text("S%d" % (i+1), insert=pos, fill='white')
152 txt.rotate(90, pos)
153 dwg.add(txt)
154
155 dwg.save()
156
157
158 def display(of, pins, banksel=None, muxwidth=4):
159 of.write("""\
160 | Pin | Mux0 | Mux1 | Mux2 | Mux3 |
161 | --- | ----------- | ----------- | ----------- | ----------- |
162 """)
163 pinidx = sorted(pins.keys())
164 for pin in pinidx:
165 pdata = pins.get(pin)
166 if banksel:
167 skip = False
168 for mux in range(muxwidth):
169 if mux not in pdata:
170 continue
171 name, bank = pdata[mux]
172 if banksel != bank:
173 skip = True
174 if skip:
175 continue
176 res = '| %3d |' % pin
177 for mux in range(muxwidth):
178 if mux not in pdata:
179 res += " |"
180 continue
181 name, bank = pdata[mux]
182 res += " %s %-9s |" % (bank, name)
183 of.write("%s\n" % res)
184
185
186 def fnsplit(f):
187 a = ''
188 n = 0
189 if not f.startswith('FB_'):
190 f2 = f.split('_')
191 if len(f2) == 2:
192 if f2[1].isdigit():
193 return f2[0], int(f2[1])
194 return f2[0], f2[1]
195 #print f
196 while f and not f[0].isdigit():
197 a += f[0]
198 f = f[1:]
199 return a, int(f) if f else None
200
201
202 def fnsort(f1, f2):
203 a1, n1 = fnsplit(f1)
204 a2, n2 = fnsplit(f2)
205 x = cmp(a1, a2)
206 if x != 0:
207 return x
208 return cmp(n1, n2)
209
210
211 def find_fn(fname, names):
212 for n in names:
213 if fname.startswith(n):
214 return n
215
216 def map_name(pinmap, fn, fblower, pin, rename):
217 if not rename:
218 if pin[:-1].isdigit():
219 print "map name digit", pin, fn, fblower
220 if fn in ['PWM', 'EINT', 'VDD', 'VSS']:
221 return fn.lower() + pin.lower()
222 if fn == 'GPIO':
223 return 'gpio' + pin[1:].lower()
224 return pin.lower()
225 pin = pin.lower()
226 if fn == 'GPIO':
227 pk = '%s%s_%s' % (fblower, pin[0], pin[:-1])
228 elif pin[:-1].isdigit() and fn != 'EINT':
229 pk = '%s%s_out' % (fblower, pin[:-1])
230 else:
231 pk = '%s_%s' % (fblower, pin[:-1])
232 print "map name", pk, fblower, pinmap.has_key(pk)
233 if not pinmap.has_key(pk):
234 return pin.lower()
235 remapped = pinmap[pk]
236 uscore = remapped.find('_')
237 if uscore == -1:
238 return pin.lower()
239 fn, pin = remapped[:uscore], remapped[uscore+1:] + pin[-1]
240 return pin.lower()
241
242 def python_pindict(of, pinmap, pins, function_names, dname, remap):
243
244 res = OrderedDict()
245 of.write("\n%s = OrderedDict()\n" % dname)
246
247 for k, pingroup in pins.byspec.items():
248 (a, n) = k.split(":")
249 if n.isdigit():
250 a = "%s%s" % (a, n)
251 fblower = a.lower()
252 of.write("%s['%s'] = [ " % (dname, fblower))
253 res[fblower] = []
254 count = 0
255 for i, p in enumerate(pingroup):
256 name = map_name(pinmap, k[0], fblower, p, remap)
257 res[fblower].append(name)
258 of.write("'%s', " % name)
259 count += 1
260 if count == 4 and i != len(pingroup)-1:
261 of.write("\n ")
262 count = 0
263 of.write("]\n")
264 print " dict %s" % dname, a, n, pingroup
265 of.write("\n\n")
266 return res
267
268 def python_dict_fns(of, pinmap, pins, function_names):
269 of.write("# auto-generated by Libre-SOC pinmux program: do not edit\n")
270 of.write("# python src/pinmux_generator.py -v -s {spec} -o {output}\n")
271 of.write("# use OrderedDict to fix stable order for JTAG Boundary Scan\n")
272 of.write("from collections import OrderedDict\n")
273
274 fn_names = function_names.keys()
275 fns = {}
276
277 fnidx = list(fns.keys())
278 fnidx.sort(key=fnsplit)
279
280 print "python fnames", function_names
281 print "python speckeys", pins.byspec.keys()
282 print "python dict fns", dir(pins.gpio)
283 print pins.gpio.pinfn('', '')
284 print pins.pwm.pinfn('', '')
285 print pins.sdmmc.pinfn('', '')
286 print "by spec", pins.byspec
287 print pinmap
288
289 pd = python_pindict(of, {}, pins, function_names, 'pindict', False)
290 ld = python_pindict(of, pinmap, pins, function_names, 'litexdict', True)
291
292 print "pd", pd
293 print "ld", ld
294 # process results and create name map
295 litexmap = OrderedDict()
296 for k in pd.keys():
297 pl = pd[k]
298 ll = ld[k]
299 for pname, lname in zip(pl, ll):
300 pname = "%s_%s" % (k, pname[:-1]) # strip direction +/-/*
301 lname = lname[:-1] # strip direction +/-/*
302 if k in ['eint', 'pwm', 'gpio', 'vdd', 'vss']: # sigh
303 lname = "%s_%s" % (k, lname)
304 litexmap[pname] = lname
305 print "litexmap", litexmap
306 of.write("litexmap = {\n")
307 for k, v in litexmap.items():
308 of.write("\t'%s': '%s',\n" % (k, v))
309 of.write("}\n")
310 return litexmap
311
312
313 def display_fns(of, bankspec, pins, function_names):
314 fn_names = function_names.keys()
315 fns = {}
316 for (pin, pdata) in pins.items():
317 for mux in range(0, 4): # skip GPIO for now
318 if mux not in pdata:
319 continue
320 name, bank = pdata[mux]
321 assert name is not None, str(bank)
322 if name not in fns:
323 fns[name] = []
324 fns[name].append((pin - bankspec[bank], mux, bank))
325
326 fnidx = list(fns.keys())
327 fnidx.sort(key=fnsplit)
328 current_fn = None
329 for fname in fnidx:
330 fnbase = find_fn(fname, fn_names)
331 #fblower = fnbase.lower()
332 assert fnbase in function_names, "fn %s not in descriptions %s" % \
333 (fname, str(function_names.keys()))
334 #print "name", fname, fnbase
335 if fnbase != current_fn:
336 if current_fn is not None:
337 of.write('\n')
338 of.write("## %s\n\n%s\n\n" % (fnbase, function_names[fnbase]))
339 current_fn = fnbase
340 of.write("* %-9s :" % fname)
341 for (pin, mux, bank) in fns[fname]:
342 of.write(" %s%d/%d" % (bank, pin, mux))
343 of.write('\n')
344
345 return fns
346
347
348 def check_functions(of, title, bankspec, fns, pins, required, eint, pwm,
349 descriptions=None):
350 fns = deepcopy(fns)
351 pins = deepcopy(pins)
352 if descriptions is None:
353 descriptions = {}
354 fnidx = fns.keys()
355
356 #print dir(fns)
357 #print dir(pins)
358
359 of.write("# Pinmap for %s\n\n" % title)
360
361 print "fn_idx", fnidx
362 print "fns", fns
363 print "fnspec", pins.fnspec.keys()
364 print "required", required
365 for name in required:
366 of.write("## %s\n\n" % name)
367 if descriptions and name in descriptions:
368 of.write("%s\n\n" % descriptions[name])
369
370 name = name.split(':')
371 if len(name) == 2:
372 findbank = name[0][0]
373 findmux = int(name[0][1:])
374 name = name[1]
375 else:
376 name = name[0]
377 findbank = None
378 findmux = None
379 name = name.split('/')
380 if len(name) == 2:
381 count = int(name[1])
382 else:
383 count = 100000
384 name = name[0]
385 #print name
386 found = set()
387 pinfound = {}
388 located = set()
389 for fname in fnidx:
390 if not fname.startswith(name):
391 continue
392 for k in pins.fnspec.keys():
393 if fname.startswith(k):
394 fk = list(pins.fnspec[k].keys())
395 fn = pins.fnspec[k]
396 fn = fn[fk[0]]
397 #print fname, fn, dir(fn)
398 if count == 100000:
399 count = len(fn.pingroup)
400 for pin, mux, bank in fns[fname]:
401 if findbank is not None:
402 if findbank != bank:
403 continue
404 if findmux != mux:
405 continue
406 pin_ = pin + bankspec[bank]
407 if pin_ in pins:
408 pinfound[pin_] = (fname, pin_, bank, pin, mux)
409
410 pinidx = sorted(pinfound.keys())
411
412 fname = None
413 removedcount = 0
414 print ("pinidx", pinidx)
415 for pin_ in pinidx:
416 fname, pin_, bank, pin, mux = pinfound[pin_]
417 if fname in found:
418 continue
419 found.add(fname)
420 if len(found) > count:
421 continue
422 del pins[pin_]
423 removedcount += 1
424 of.write("* %s %d %s%d/%d\n" % (fname, pin_, bank, pin, mux))
425
426 print fns
427 if removedcount != count:
428 if fname is None:
429 print "no match between required and available pins"
430 else:
431 print ("not all found", name, removedcount, count, title, found,
432 fns[fname])
433 print ("pins found", pinfound)
434
435 # fnidx.sort(fnsort)
436 of.write('\n')
437
438 # gpios
439 gpios = []
440 for name in descriptions.keys():
441 if not name.startswith('GPIO'):
442 continue
443 if name == 'GPIO':
444 continue
445 gpios.append(name)
446 gpios.sort()
447
448 if gpios:
449 of.write("## GPIO\n\n")
450
451 for fname in gpios:
452 if fname in found:
453 continue
454 desc = ''
455 if descriptions and fname in descriptions:
456 desc = ': %s' % descriptions[fname]
457 bank = fname[4]
458 pin = int(fname[7:])
459 pin_ = pin + bankspec[bank]
460 if pin_ not in pins:
461 continue
462 del pins[pin_]
463 found.add(fname)
464 of.write("* %-8s %d %s%-2d %s\n" % (fname, pin_, bank, pin, desc))
465 of.write('\n')
466
467 if eint:
468 display_group(of, bankspec, "EINT", eint, fns, pins, descriptions)
469 if pwm:
470 display_group(of, bankspec, "PWM", pwm, fns, pins, descriptions)
471
472 of.write("## Unused Pinouts (spare as GPIO) for '%s'\n\n" % title)
473 if descriptions and 'GPIO' in descriptions:
474 of.write("%s\n\n" % descriptions['GPIO'])
475 display(of, pins)
476 of.write('\n')
477
478 return pins # unused
479
480
481 def display_group(of, bankspec, title, todisplay, fns, pins, descriptions):
482 of.write("## %s\n\n" % title)
483
484 found = set()
485 for fname in todisplay:
486 desc = ''
487 if descriptions and fname in descriptions:
488 desc = ': %s' % descriptions[fname]
489 fname = fname.split(':')
490 if len(fname) == 2:
491 findbank = fname[0][0]
492 findmux = int(fname[0][1:])
493 fname = fname[1]
494 else:
495 fname = fname[0]
496 findbank = None
497 findmux = None
498 for (pin, mux, bank) in fns[fname]:
499 if findbank is not None:
500 if findbank != bank:
501 continue
502 if findmux != mux:
503 continue
504 if fname in found:
505 continue
506 pin_ = pin + bankspec[bank]
507 if pin_ not in pins:
508 continue
509 del pins[pin_]
510 found.add(fname)
511 of.write("* %s %d %s%d/%d %s\n" %
512 (fname, pin_, bank, pin, mux, desc))
513 of.write('\n')
514
515
516 def display_fixed(of, fixed, offs):
517
518 fkeys = sorted(fixed.keys())
519 pin_ = offs
520 res = []
521 for pin, k in enumerate(fkeys):
522 of.write("## %s\n\n" % k)
523 prevname = ''
524 linecount = 0
525 for name in fixed[k]:
526 if linecount == 4:
527 linecount = 0
528 of.write('\n')
529 if prevname[:2] == name[:2] and linecount != 0:
530 of.write(" %s" % name)
531 linecount += 1
532 else:
533 if linecount != 0:
534 of.write('\n')
535 of.write("* %d: %d %s" % (pin_, pin, name))
536 linecount = 1
537 res.append((pin_, name))
538
539 prevname = name
540 pin_ += 1
541 if linecount != 0:
542 of.write('\n')
543 of.write('\n')
544
545 return res