added lines which help to align pins without zebra
[linux-gpio-pinout] / gpio.pl
1 #!/usr/bin/perl
2 use warnings;
3 use strict;
4 use autodie;
5 use Data::Dump qw(dump);
6 use Getopt::Long;
7
8 my $opt_svg = $ENV{SVG} || 0;
9 my $opt_alt = $ENV{ALT} || 0;
10 my $opt_invert = $ENV{INVERT} || 0;
11 my $opt_vertical = $ENV{VERTICAL} || 0;
12 my $opt_zebra = $ENV{ZEBRA} || 0;
13 my $opt_lines = $ENV{LINES} || 0;
14 GetOptions(
15         'svg!' => \$opt_svg,
16         'alt!' => \$opt_alt,
17         'invert!' => \$opt_invert,
18         'vertical!' => \$opt_vertical,
19         'zebra!' => \$opt_zebra,
20         'lines!' => \$opt_lines,
21 );
22
23 # svg font hints
24 my $font_w = 1.67; # < 2.54, font is not perfect square
25 my $font_b = 2.10; # font baseline position
26
27 sub slurp {
28         open(my $fh, '<', shift);
29         local $/ = undef;
30         <$fh>;
31 }
32
33 my $pins;
34
35 my $model = slurp('/proc/device-tree/model');
36 $model =~ s/\x00$//; # strip kernel NULL
37 warn "# model [$model]";
38
39 my @lines;
40 my $line_i = 0;
41
42 my $include = 0;
43 while(<DATA>) {
44         chomp;
45         if ( m/^#\s(.+)/ ) {
46                 warn "MODEL [$1] == [$model] ?\n";
47                 if ( $model =~ m/$1/ ) {
48                         $include = 1;
49                 } else {
50                         $include = 0;
51                 }
52         } elsif ( m/^#\s+/ ) {
53                 $include = 1;
54         } elsif ( $include ) {
55                 push @{ $pins->{$1} }, $line_i while ( m/\t(\w+\d+)/g );
56
57                 push @lines, $_;
58
59                 $line_i++;
60         } else {
61                 warn "IGNORE: [$_]\n";
62         }
63 }
64
65 die "add pin definition for # $model" unless $pins;
66
67 warn "# pins ",dump($pins);
68
69 my $pin_function;
70 my $device;
71
72 open(my $fh, '<', '/sys/kernel/debug/pinctrl/pinctrl-handles');
73 while(<$fh>) {
74         chomp;
75         if ( m/device: [0-9a-f]+\.(\w+)/ ) {
76                 $device = $1;
77         } elsif ( m/group: (\w+\d+)\s.+function: (\S+)/ ) {
78                 my ($pin, $function) = ($1,$2);
79                 $pin_function->{$pin} = "$device $function";
80
81                 if ( $pins->{$pin} ) {
82                         foreach my $line ( @{$pins->{$pin}} ) {
83 warn "XXX $pin $line";
84                                 my $t = $lines[$line];
85                                 if ( $opt_svg ) {
86                                         $t =~ s/$pin/[$device $function]/;
87                                 } else {
88                                         $t =~ s/$pin/$pin [$device $function]/ || die "can't find $pin in [$t]";
89                                 }
90                                 $lines[$line] = $t;
91                                 warn "# $line: $lines[$line]\n";
92                         }
93                 } else {
94                         warn "IGNORED: pin $pin function $function\n";
95                 }
96         }
97 }
98
99 warn "# pin_function = ",dump($pin_function);
100
101 my @max_len = ( 0,0,0,0 );
102 my @line_parts;
103 foreach my $line (@lines) {
104         if ( $line =~ m/^#/ ) {
105                 push @line_parts, [ $line ] unless $opt_svg;
106                 next;
107         }
108         $line =~ s/(\[(?:uart|serial))([^\t]*\]\s[^\t]*(rx|tx)d?)/$1 $3$2/gi;
109         $line =~ s/(\[i2c)([^\t]*\]\s[^\t]*(scl?k?|sda))/$1 $3$2/gi;
110         $line =~ s/(\[spi)([^\t]*\]\s[^\t]*(miso|mosi|s?clk|c[se]\d*))/$1 $3$2/gi;
111         $line =~ s/\s*\([^\)]+\)//g if ! $opt_alt;
112
113         # shorten duplicate kernel device/function
114         $line =~ s/\[serial (\w+) (uart\d+)\]/[$2 $1]/g;
115         $line =~ s/\[(\w+) (\w+) \1(\d+)\]/[$1$3 $2]/g;
116
117         my @v = split(/\s*\t+\s*/,$line,4);
118         push @line_parts, [ @v ];
119         foreach my $i ( 0 .. 3 ) {
120                 next unless exists $v[$i];
121                 next if $v[$i] =~ m/^#/; # don't calculate comments into max length
122                 my $l = length($v[$i]);
123                 $max_len[$i] = $l if $l > $max_len[$i];
124         }
125 }
126
127 warn "# max_len = ",dump( \@max_len );
128 warn "# line_parts = ",dump( \@line_parts );
129
130 #print "$_\n" foreach @lines;
131
132 my $fmt = "%$max_len[0]s %-$max_len[1]s %$max_len[2]s %-$max_len[3]s\n";
133
134 my $x = 30.00; # mm
135 my $y = 30.00; # mm
136
137 if ( $opt_svg ) {
138         print qq{<?xml version="1.0" encoding="UTF-8" standalone="no"?>
139 <svg
140    xmlns:dc="http://purl.org/dc/elements/1.1/"
141    xmlns:cc="http://creativecommons.org/ns#"
142    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
143    xmlns:svg="http://www.w3.org/2000/svg"
144    xmlns="http://www.w3.org/2000/svg"
145    xmlns:xlink="http://www.w3.org/1999/xlink"
146    id="svg8"
147    version="1.1"
148    viewBox="0 0 210 297"
149    height="297mm"
150    width="210mm">
151
152
153 <g id="layer1">
154
155         }; # svg, insert rest of rect
156
157         print qq{<rect x="0" y="0" width="210" height="297" style="fill:#000000" id="high-contrast"/>} if $opt_invert;
158 }
159
160 my @later;
161
162 my $cols = {    # foreground background
163         txt  => [ '#000000', '#ffffff' ],
164         pins => [ '#ffffff', '#ff00ff' ],
165         vcc  => [ '#ff0000', '#ffff00' ],
166         gnd  => [ '#000000', '#00ffff' ],
167         i2c  => [ '#008800', '#ffcccc' ],
168         serial=>[ '#000088', '#ccffcc' ],
169         spi  => [ '#880000', '#ccccff' ],
170 };
171
172 sub swap_cols {
173         my $swap = shift;
174         die "$swap not found in ",dump($cols) unless $cols->{$swap};
175         my ( $c1, $c2 ) = @{ $cols->{$swap} };
176         $cols->{$swap} = [ $c2, $c1 ];
177 }
178
179 swap_cols 'txt' if $opt_invert;
180         
181
182 sub svg_style {
183         my ($name,$x,$y,$col) = @_;
184         $y -= $font_b; # shift box overlay to right vertical position based on font baseline
185
186         sub rect {
187                 my ($x,$y,$col,$fill) = @_;
188                 print qq{<rect x="$x" y="$y" height="2.54" width="}, $max_len[$col] * $font_w, qq{" style="fill:$fill;stroke:#ffffff;stroke-width:0.10" />\n};
189
190         }
191
192         if ( $name =~ m/^(\d+)$/ ) { # pins
193                 my $pin = $1;
194                 my ( $fg, $bg ) = @{ $cols->{pins} };
195                 if ( $pin == 1 ) {
196                         my $w  = $max_len[$col]*$font_w - 0.1;
197                         my $cx = $x + $w;
198                         my $cy = $y + 2.54;
199                         #print qq{<polygon points="$x,$y $cx,$y $x,$cy $x,$y" stroke="$fg" stroke-width="0.25" fill="$bg" />};
200                         #print qq{<polygon points="$x,$cy $cx,$cy $cx,$y $x,$cy" stroke="$bg" stroke-width="0.25" fill="$fg" />};
201                         print qq{<rect x="$x" y="$y" width="$w" height="2.54" stroke="$fg" stroke-width="0.3" fill="$bg" />};
202                         my ( $fg, $bg ) = @{ $cols->{txt} };
203                         print qq{<rect x="$x" y="$y" width="$w" height="2.54" rx="1" ry="1" stroke="$fg" stroke-width="0.3" fill="$bg" />};
204                 } else {
205                         rect $x,$y,$col,$fg;
206                 }
207                 return qq{ style="fill:$bg"};
208         }
209
210         if ( $name =~ m/(VCC|3V3|3.3V|5v)/i ) {
211                 my ($fg,$bg) = @{ $cols->{vcc} };
212                 rect $x,$y,$col,$bg;
213                 return qq{ style="fill:$fg"};
214         } elsif ( $name =~ m/(G(ND|Round)|VSS|0v)/i ) {
215                 my ($fg,$bg) = @{ $cols->{gnd} };
216                 rect $x,$y,$col,$bg;
217                 return qq{ style="fill:$fg"};
218         } elsif ( $name =~ m/\[(\w+)/ ) { # kernel
219                 my $dev = $1;
220                 my ($fg,$bg) = @{ $cols->{txt} };
221                 ($fg,$bg) = @{ $cols->{$dev} } if exists $cols->{$dev};
222                 rect $x,$y,$col,$bg;
223                 return qq{ style="fill:$fg"};
224         } else {
225                 my ( $fg, $bg ) = @{ $cols->{txt} };
226                 rect $x,$y,$col,$bg;
227                 #return qq{ style="fill:$fg"};
228                 return '';
229         }
230 }
231
232 my $alt_col = 0;
233
234 my @cols_order = ( 0,1,2,3 );
235 my @cols_align = ( '','-','','-' ); # sprintf prefix
236
237 @cols_order = ( 0,1,3,2 ); # pins outside on the right
238 @cols_align = ( '','-','-','' );
239
240
241 # cut marks
242 my ($fg,$bg) = @{ $cols->{txt} };
243 my $line_fmt = qq{<line x1="%s" y1="%s" x2="%s" y2="%s" style="stroke:$fg;stroke-width:0.10;fill:$bg" />\n};
244
245 my @cut_marks;
246 sub cut_mark {
247         my ($x,$y) = @_;
248         return unless $opt_svg;
249         push @cut_marks, sprintf($line_fmt, $x-5, $y-$font_b,   $x+5, $y-$font_b);
250         push @cut_marks, sprintf($line_fmt, $x,   $y-$font_b-5, $x,   $y-$font_b+5);
251 }
252 cut_mark $x, $y;
253 my $max_x = $x;
254 $max_x += $max_len[$_] * $font_w foreach ( 0 .. 3 );
255 cut_mark $max_x, $y;
256
257 sub line {
258         my ($x,$y,$max_x) = @_;
259         push @cut_marks, sprintf($line_fmt, $x, $y-$font_b, $max_x, $y-$font_b);
260 }
261
262
263 my $last_cut_mark = 0;
264
265 foreach my $i ( 0 .. $#line_parts ) {
266         $i = $#line_parts - $i if $opt_vertical;
267         my $line = $line_parts[$i];
268
269         if ( $opt_svg ) {
270
271                 if ( ! exists $line->[0] ) {
272                         # before first empty line
273                         if ( $last_cut_mark == 0 ) {
274                                 cut_mark $x, $y;
275                                 cut_mark $max_x, $y;
276                                 $last_cut_mark = 1;
277                                 line $x, $y, $max_x if $opt_lines;
278                                 $y += 15; # make spacing between pinouts
279                         }
280                 } elsif ( $last_cut_mark ) {
281                         # first full line
282                         cut_mark $x, $y;
283                         cut_mark $max_x, $y;
284                         $last_cut_mark = 0;
285                 } else {
286                         #warn "CUTMARK no magic";
287                 }
288
289                 line $x, $y, $max_x if $opt_lines && exists $line->[1];
290
291                 my ($fg,$bg) = @{ $cols->{txt} };
292                 my $tspan = qq{<tspan x="$x" y="$y" style="line-height:2.54;fill:$fg;stroke:none;">\n};
293
294                 my $x_pos = $x;
295                 foreach my $i ( @cols_order ) {
296                         next unless $line->[$i];
297                         my $text_anchor = 'middle';
298                         my $x2 = $x_pos + ( $max_len[$i] * $font_w ) / 2;
299                         $tspan .= qq{<tspan x="$x2" text-anchor="$text_anchor"}.svg_style($line->[$i],$x_pos,$y,$i).sprintf( '>%' . $cols_align[$i] . $max_len[$i] . 's</tspan>', $line->[$i]);
300                         $x_pos += $max_len[$i] * $font_w;
301                 }
302
303                 $tspan .= qq{</tspan>\n};
304                 push @later,sprintf $tspan, @$line;
305                 $y += 2.54;
306
307                 # swap pin colors for line stripe
308                 if ( $opt_zebra ) {
309                         swap_cols $_ foreach qw( pins txt );
310                 }
311
312         } else {
313
314                 if ( $#$line == 0 ) {
315                         print $line->[0], "\n";
316                 } else {
317                         push @$line, '' while ($#$line < 3); # fill-in single row header
318                         printf $fmt, @$line;
319                 }
320
321         }
322 }
323
324 if ( $opt_svg ) {
325         print qq{
326     <text
327        id="text4506"
328        y="$x"
329        x="$y"
330        style="font-size:2.34px;line-height:2.54px;font-family:'Andale Mono';stroke:none"
331        xml:space="preserve">
332
333         }; #svg
334
335         print @later, qq{</text>\n}, @cut_marks, qq{</g>\n</svg>};
336
337 }
338
339 __DATA__
340 # Cubietech Cubieboard2
341 ## U14 (Next to SATA connector)
342 ###     SPI0
343 48      PI13 (SPI0-MISO/UART6-RX/EINT25)        47      PI11 (SPI0-CLK/UART5-RX/EINT23)
344 46      PI12 (SPI0-MOSI/UART6-TX/EINT24)        45      PI10 (SPI0-CS/UART5-TX/EINT22)
345 ###     LCD
346 44      3.3V (nc in 2012-08-08)                 43      VCC-5V
347 42      Ground                                  41      SPDIF
348 40      PB10 (LCD0-SCK/LCD-PIO1)                39      PB11 (LCD0-SDA/LCD-PIO2)
349 38      Ground                                  37      PH7 (LCD0-BL-EN/LCD-PIO0/UART5-RX/EINT7)
350 36      XN_TP (TP-X2)                           35      YN_TP (TP-Y2)
351 34      XP_TP (TP-X1)                           33      YP_TP (TP-Y1)
352 32      PD25 (LCDDE)                            31      PB2 (PWM0)
353 30      PD26 (LCDHSYNC/VGA-HSYNC)               29      PD24 (LCDCLK)
354 28      PD23 (LCDD23)                           27      PD27 (LCDVSYNC/VGA-VSYNC)
355 26      PD21 (LCDD21)                           25      PD22 (LCDD22)
356 24      PD19 (LCDD19/LVDS1N3)                   23      PD20 (LCDD20)
357 22      PD17 (LCDD17/LVDS1NC)                   21      PD18 (LCDD18/LVDS1P3)
358 20      Ground                                  19      PD16 (LCDD16/LVDS1PC)
359 18      PD14 (LCDD14/LVDS1P2)                   17      PD15 (LCDD15/LVDS1N2)
360 16      PD12 (LCDD12/LVDS1P1)                   15      PD13 (LCDD13/LVDS1N1)
361 14      PD10 (LCDD10/LVDS1P0)                   13      PD11 (LCDD11/LVDS1N0)
362 12      PD8 (LCDD8/LVDS0P3)                     11      PD9 (LCDD9/LVDS0N3)
363 10      PD7 (LCDD7/LVDS0NC)                     9       Ground
364 8       PD5 (LCDD5/LVDS0N2)                     7       PD6 (LCDD6/LVDS0PC)
365 6       PD3 (LCDD3/LVDS0N1)                     5       PD4 (LCDD4/LNVS0P2)
366 4       PD1 (LCDD1/LVDS0N0)                     3       PD2 (LCDD2/LVDS0P1)
367 2       Ground                                  1       PD0 (LCDD0/LVDSP0)
368
369 # Cubietech Cubieboard2
370 ## U15 (Between Ethernet port and USB ports)
371 ### CSI1/TS
372 1       VCC-5V                                  2       PH15 (CSI1-PWR/EINT15)
373 3       CSI1-IO-2V8                             4       PH14 (CSI1-RST#/EINT14)
374 5       PG0 (CSI1-PCLK/SDC1-CMD)                6       PB18 (TWI1-SCK)
375 7       PB19 (TWI1-SDA)                         8       PG3 (CSI1-VSYNC/SDC1-D1)
376 9       PG2 (CSI1-HSYNC/SDC1-D0)                10      PG1 (CSI1-MCLK/SDC1-CLK)
377 11      PG4 (CSI1-D0/SDC1-D2)                   12      PG5 (CSI1-D1/SDC1-D3)
378 13      PG6 (CSI1-D2/UART3-TX)                  14      PG7 (CSI1-D3/UART3-RX)
379 15      PG8 (CSI1-D4/UART3-RTS)                 16      PG9 (CSI1-D5/UART3-CTS)
380 17      PG10 (CSI1-D6/UART4-TX)                 18      PG11 (CSI1-D7/UART4-RX)
381 19      Ground                                  20      Ground
382 ###     Analog SDIO3
383 21      FMINL                                   22      PI4 (SDC3-CMD)
384 23      FMINR                                   24      PI5 (SDC3-CLK)
385 25      Ground                                  26      PI6 (SDC3-D0)
386 27      VGA-R                                   28      PI7 (SDC3-D1)
387 29      VGA-G                                   30      PI8 (SDC3-D2)
388 31      VGA-B                                   32      PI9 (SDC3-D3)
389 ###     CSI0/TS
390 33      LCD1-VSYNC                              34      PE4 (CSI0-D0)
391 35      LCD1-HSYNC                              36      PE5 (CSI0-D1)
392 37      Ground                                  38      PE6 (CSI0-D2)
393 39      AVCC                                    40      PE7 (CSI0-D3)
394 41      LRADC0                                  42      PE8 (CSI0-D4)
395 43      CVBS                                    44      PE9 (CSI0-D5)
396 45      HPL                                     46      PE10 (CSI0-D6)
397 47      HPR                                     48      PE11 (CSI0-D7)
398
399 ## DEBUG serial (middle of board)
400 4       PB22 (UART0-TX)
401 3       PB23 (UART0-RX)
402 2       VCC-3V3
403 1       GND
404
405
406 # Lamobo R1
407 ## CON3 rpi DIP26-254
408 1       3.3v                    2       5v     
409 3       PB20 SDA.1              4       5V     
410 5       PB21 SCL.1              6       0v     
411 7       PI3 PWM1                8       PH0 UART3_TX
412 9       0v                      10      PH1 UART3_RX
413 11      PI19 UART2_RX           12      PH2
414 13      PI18 UART2_TX           14      0v     
415 15      PI17 UART2_CTS          16      PH21 CAN_RX 
416 17      3.3v                    18      PH20 CAN_TX 
417 19      PI12 SPI0_MOSI          20      0v     
418 21      PI13 SPI0_MISO          22      PI16 UART2_RTS   
419 23      PI11 SPI0_SCLK          24      PI10 SPI0_CS0    
420 25      0v                      26      PI14 SPI0_CS1
421
422 ## J13 DIP2-254
423 2       PB22 UART0_TX
424 1       PB23 UART0_RX
425
426 ## J12 DIP8-254
427 8       GND                     7       GND
428 6       PI20 UART7_TX           5       PH3
429 4       PI21 UART7_RX           3       PH5
430 2       3V3                     1       SATA-5V
431
432 # Raspberry Pi 3 Model B Rev 1.2
433 1       3.3v                    2       5v
434 3       gpio2 (SDA.1)           4       5v
435 5       gpio3 (SCL.1)           6       0v
436 7       gpio4 (GPIO. 7)         8       gpio14  (TxD)
437 9       0v                      10      gpio15  (RxD)
438 11      gpio17 (GPIO. 0)        12      gpio18  (GPIO. 1)
439 13      gpio27 (GPIO. 2)        14      0v
440 15      gpio22 (GPIO. 3)        16      gpio23  (GPIO. 4)
441 17      3.3v                    18      gpio24  (GPIO. 5)
442 19      gpio10 (MOSI)           20      0v
443 21      gpio9 (MISO)            22      gpio25  (GPIO. 6) 
444 23      gpio11 (SCLK)           24      gpio8   (CE0)
445 25      0v                      26      gpio7   (CE1)
446 27      gpio0 (SDA.0)           28      gpio1   (SCL.0)
447 29      gpio5 (GPIO.21)         30      0v
448 31      gpio6 (GPIO.22)         32      gpio12  (GPIO.26)
449 33      gpio13 (GPIO.23)        34      0v
450 35      gpio19 (GPIO.24)        36      gpio16  (GPIO.27)
451 37      gpio26 (GPIO.25)        38      gpio20  (GPIO.28)
452 39      0v                      40      gpio21  (GPIO.29)