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