5 use Data::Dump qw(dump);
12 my $opt_horizontal = 0;
23 'invert!' => \$opt_invert,
24 'vertical-flip!' => \$opt_vertical,
25 'horizontal-flip!' => \$opt_horizontal,
26 'edge-pins!' => \$opt_edge,
27 'middle-pins!' => \$opt_middle,
28 'zebra!' => \$opt_zebra,
29 'lines!' => \$opt_lines,
30 'read=s' => \$opt_read,
31 'pins=s' => \$opt_pins,
32 'color' => \$opt_color,
36 my $font_w = 1.67; # < 2.54, font is not perfect square
37 my $font_b = 2.10; # font baseline position
39 $opt_read .= '/' unless $opt_read =~ m/\/$/;
42 open(my $fh, '<', $opt_read . shift);
49 my $model = slurp('/proc/device-tree/model');
50 $model =~ s/\x00$//; # strip kernel NULL
51 warn "# model [$model]";
53 open(DATA, '<', $opt_pins) if $opt_pins;
62 warn "MODEL [$1] == [$model] ?\n";
63 if ( $model =~ m/$1/ ) {
68 } elsif ( $include || $opt_pins ) {
69 push @{ $pins->{$1} }, $line_i while ( m/\t(\w+\d+)/g );
75 warn "IGNORE: [$_]\n";
79 die "add pin definition for # $model" unless $pins;
81 shift(@lines) while ( ! $lines[0] ); # remove empty at beginning
82 pop(@lines) while ( ! $lines[-1] ); # remove empty at end
84 warn "# pins ",dump($pins);
87 foreach ( glob $opt_read . '/sys//devices/platform/soc*/*.serial/tty/tty*' ) {
88 my @v = split(/\//, $_);
89 $serial_tty->{ $v[-3] } = $v[-1];
91 warn "# serial_tty = ",dump($serial_tty);
97 open(my $fh, '<', $opt_read . '/sys/kernel/debug/pinctrl/pinctrl-handles');
100 if ( m/device: (\S+)/ ) {
102 if ( my $replace = $serial_tty->{$device} ) {
103 $device = $replace; # replace serial hex with kernel name
105 $device =~ s/^[0-9a-f]*\.//; # remove hex address
107 } elsif ( m/group: (\w+\d+)\s.+function: (\S+)/ ) {
108 my ($pin, $function) = ($1,$2);
109 $pin_function->{$pin} = "$device $function";
111 if ( $pins->{$pin} ) {
112 foreach my $line ( @{$pins->{$pin}} ) {
113 my $t = $lines[$line];
115 $t =~ s/$pin/[$device $function]/;
117 $t =~ s/$pin/$pin [$device $function]/ || warn "can't find $pin in [$t]";
120 warn "# $line: $lines[$line]\n";
123 warn "IGNORED: pin $pin function $function\n";
128 warn "# pin_function = ",dump($pin_function);
130 my @max_len = ( 0,0,0,0 );
132 foreach my $line (@lines) {
133 if ( $line =~ m/^#/ ) {
134 push @line_parts, [ $line ] unless $opt_svg && $line =~ m/^###+/; # SVG doesn't display 3rd level comments
137 $line =~ s/(\[(?:uart|serial|tty\w+))([^\t]*\]\s[^\t]*(rx|tx)d?)/$1 $3$2/gi;
138 $line =~ s/(\[i2c)([^\t]*\]\s[^\t]*(scl?k?|sda))/$1 $3$2/gi;
139 $line =~ s/(\[spi)([^\t]*\]\s[^\t]*(miso|mosi|s?clk|c[se]\d*))/$1 $3$2/gi;
140 $line =~ s/\s*\([^\)]+\)//g if ! $opt_alt;
142 # shorten duplicate kernel device/function
143 $line =~ s/\[serial (\w+) (uart\d+)\]/[$2 $1]/g;
144 $line =~ s/\[(\w+) (\w+) \1(\d+)\]/[$1$3 $2]/g;
146 my @v = split(/\s*\t+\s*/,$line,4);
147 @v = ( $v[2], $v[3], $v[0], $v[1] ) if $opt_horizontal && $v[2];
149 push @line_parts, [ @v ];
150 foreach my $i ( 0 .. 3 ) {
151 next unless exists $v[$i];
152 next if $v[$i] =~ m/^#/; # don't calculate comments into max length
153 my $l = length($v[$i]);
154 $max_len[$i] = $l if $l > $max_len[$i];
158 warn "# max_len = ",dump( \@max_len );
159 warn "# line_parts = ",dump( \@line_parts );
161 #print "$_\n" foreach @lines;
167 print qq{<?xml version="1.0" encoding="UTF-8" standalone="no"?>
169 xmlns:dc="http://purl.org/dc/elements/1.1/"
170 xmlns:cc="http://creativecommons.org/ns#"
171 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
172 xmlns:svg="http://www.w3.org/2000/svg"
173 xmlns="http://www.w3.org/2000/svg"
174 xmlns:xlink="http://www.w3.org/1999/xlink"
177 viewBox="0 0 210 297"
184 }; # svg, insert rest of rect
186 print qq{<rect x="0" y="0" width="210" height="297" style="fill:#000000" id="high-contrast"/>} if $opt_invert;
191 my $cols = { # foreground background
192 txt => [ '#000000', '#ffffff' ],
193 pins => [ '#ffffff', '#ff00ff' ],
194 vcc => [ '#ff0000', '#ffff00' ],
195 gnd => [ '#000000', '#00ffff' ],
196 i2c => [ '#008800', '#ffcccc' ],
197 serial=>[ '#000088', '#ccffcc' ],
198 spi => [ '#880000', '#ccccff' ],
203 die "$swap not found in ",dump($cols) unless $cols->{$swap};
204 my ( $c1, $c2 ) = @{ $cols->{$swap} };
205 $cols->{$swap} = [ $c2, $c1 ];
208 swap_cols 'txt' if $opt_invert;
212 my ($name,$x,$y,$col) = @_;
214 return '' unless $opt_color;
216 $y -= $font_b; # shift box overlay to right vertical position based on font baseline
219 my ($x,$y,$col,$fill) = @_;
220 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};
224 if ( $name =~ m/^(\d+)$/ ) { # pins
226 my ( $fg, $bg ) = @{ $cols->{pins} };
228 my $w = $max_len[$col]*$font_w - 0.1;
231 #print qq{<polygon points="$x,$y $cx,$y $x,$cy $x,$y" stroke="$fg" stroke-width="0.25" fill="$bg" />};
232 #print qq{<polygon points="$x,$cy $cx,$cy $cx,$y $x,$cy" stroke="$bg" stroke-width="0.25" fill="$fg" />};
233 print qq{<rect x="$x" y="$y" width="$w" height="2.54" stroke="$fg" stroke-width="0.3" fill="$bg" />};
234 my ( $fg, $bg ) = @{ $cols->{txt} };
235 print qq{<rect x="$x" y="$y" width="$w" height="2.54" rx="1" ry="1" stroke="$fg" stroke-width="0.3" fill="$bg" />};
239 return qq{ style="fill:$bg"};
242 if ( $name =~ m/(VCC|3V3|3.3V|5v)/i ) {
243 my ($fg,$bg) = @{ $cols->{vcc} };
245 return qq{ style="fill:$fg"};
246 } elsif ( $name =~ m/(G(ND|Round)|VSS|0v)/i ) {
247 my ($fg,$bg) = @{ $cols->{gnd} };
249 return qq{ style="fill:$fg"};
250 } elsif ( $name =~ m/\[(\w+)/ ) { # kernel
252 my ($fg,$bg) = @{ $cols->{txt} };
253 $dev = 'serial' if $dev =~ m/^tty/;
254 ($fg,$bg) = @{ $cols->{$dev} } if exists $cols->{$dev};
256 return qq{ style="fill:$fg"};
258 my ( $fg, $bg ) = @{ $cols->{txt} };
260 #return qq{ style="fill:$fg"};
267 my @cols_order = ( 0,1,2,3 );
268 my @cols_align = ( '','-','','-' ); # sprintf prefix
270 my @cols_shuffle = @cols_order;
273 # pins outside on the right
274 @cols_shuffle = ( 0,1,3,2 ) if $opt_edge;
275 @cols_align = ( '-','-','','' );
276 } elsif ( $opt_middle ) {
278 @cols_shuffle = ( 1,0,2,3 );
279 @cols_align = ( '','','-','-' );
283 my ( $what, $order ) = @_;
285 foreach my $i ( 0 .. $#$what ) {
286 $new->[$i] = $what->[ $order->[$i] ];
288 warn "# cols_shuffle what=",dump($what)," order=",dump($order)," new=",dump($new);
292 @cols_order = cols_shuffle( \@cols_order, \@cols_shuffle );
293 @max_len = cols_shuffle( \@max_len, \@cols_shuffle );
295 warn "# cols_order = ",dump( \@cols_order );
296 warn "# cols_align = ",dump( \@cols_align );
298 my $fmt = "%$cols_align[0]$max_len[0]s %$cols_align[1]$max_len[1]s %$cols_align[2]$max_len[2]s %$cols_align[3]$max_len[3]s\n";
302 my ($fg,$bg) = @{ $cols->{txt} };
303 my $line_fmt = qq{<line x1="%s" y1="%s" x2="%s" y2="%s" style="stroke:$fg;stroke-width:0.10;fill:$bg" />\n};
308 return unless $opt_svg;
309 push @cut_marks, sprintf($line_fmt, $x-5, $y-$font_b, $x+5, $y-$font_b);
310 push @cut_marks, sprintf($line_fmt, $x, $y-$font_b-5, $x, $y-$font_b+5);
314 $max_x += $max_len[$_] * $font_w foreach ( 0 .. 3 );
315 #cut_mark $max_x, $y;
318 my ($x,$y,$max_x) = @_;
319 push @cut_marks, sprintf($line_fmt, $x, $y-$font_b, $max_x, $y-$font_b);
323 my $last_cut_mark = 0;
325 foreach my $i ( 0 .. $#line_parts ) {
326 $i = $#line_parts - $i if $opt_vertical;
327 my $line = $line_parts[$i];
331 # not a minimal two column pin description
332 if ( ! exists $line->[1] ) {
333 $last_cut_mark = 1 if $line->[0] =~ m/^##/; # skip comments
335 # before first empty line
336 if ( $last_cut_mark == 0 ) {
340 line $x, $y, $max_x if $opt_lines;
341 $y += 15; # make spacing between pinouts
343 } elsif ( $last_cut_mark ) {
349 #warn "CUTMARK no magic";
352 line $x, $y, $max_x if $opt_lines && exists $line->[1];
354 my ($fg,$bg) = @{ $cols->{txt} };
355 my $tspan = qq{<tspan x="$x" y="$y" style="line-height:2.54;fill:$fg;stroke:none;">\n};
358 foreach my $i ( 0 .. $#cols_order ) {
359 my $order = $cols_order[$i];
360 next unless $line->[$order];
362 my $text_anchor = 'middle';
363 my $len = $max_len[$i];
364 my $x2 = $x_pos + ( $len * $font_w ) / 2;
366 if ( $#$line == 0 && $line->[$order] =~ s/^#+s*// ) {
367 # comment, center over whole width
368 $len = length($line->[$order]);
369 $x2 = $x + (($max_x-$x)/2); # middle
370 $tspan .= qq{\t<tspan x="$x2" text-anchor="$text_anchor"}.sprintf( '>%' . $cols_align[$i] . $len . 's</tspan>', $line->[0]);
372 $tspan .= qq{\t<tspan x="$x2" text-anchor="$text_anchor"}.svg_style($line->[$order],$x_pos,$y,$i).sprintf( '>%' . $cols_align[$i] . $len . 's</tspan>', $line->[$order]);
374 $x_pos += $len * $font_w;
377 $tspan .= qq{\n</tspan>\n};
378 push @later,sprintf $tspan, @$line;
381 # swap pin colors for line stripe
383 swap_cols $_ foreach qw( pins txt );
390 if ( $#$line == 0 ) {
391 print $line->[0], "\n";
393 push @$line, '' while ($#$line < 3); # fill-in single row header
394 printf $fmt, map { $line->[$_] } @cols_order;
403 line $x, $y, $max_x if $opt_lines;
410 style="font-size:2.34px;line-height:2.54px;font-family:'Andale Mono';stroke:none"
411 xml:space="preserve">
415 print @later, qq{</text>\n}, @cut_marks, qq{</g>\n</svg>};
420 # Cubietech Cubieboard
421 ## U14 (Next to SATA connector)
423 48 PI13 (SPI0-MISO/UART6-RX/EINT25) 47 PI11 (SPI0-CLK/UART5-RX/EINT23)
424 46 PI12 (SPI0-MOSI/UART6-TX/EINT24) 45 PI10 (SPI0-CS/UART5-TX/EINT22)
426 44 3.3V (nc in 2012-08-08) 43 VCC-5V
428 40 PB10 (LCD0-SCK/LCD-PIO1) 39 PB11 (LCD0-SDA/LCD-PIO2)
429 38 Ground 37 PH7 (LCD0-BL-EN/LCD-PIO0/UART5-RX/EINT7)
430 36 XN_TP (TP-X2) 35 YN_TP (TP-Y2)
431 34 XP_TP (TP-X1) 33 YP_TP (TP-Y1)
432 32 PD25 (LCDDE) 31 PB2 (PWM0)
433 30 PD26 (LCDHSYNC/VGA-HSYNC) 29 PD24 (LCDCLK)
434 28 PD23 (LCDD23) 27 PD27 (LCDVSYNC/VGA-VSYNC)
435 26 PD21 (LCDD21) 25 PD22 (LCDD22)
436 24 PD19 (LCDD19/LVDS1N3) 23 PD20 (LCDD20)
437 22 PD17 (LCDD17/LVDS1NC) 21 PD18 (LCDD18/LVDS1P3)
438 20 Ground 19 PD16 (LCDD16/LVDS1PC)
439 18 PD14 (LCDD14/LVDS1P2) 17 PD15 (LCDD15/LVDS1N2)
440 16 PD12 (LCDD12/LVDS1P1) 15 PD13 (LCDD13/LVDS1N1)
441 14 PD10 (LCDD10/LVDS1P0) 13 PD11 (LCDD11/LVDS1N0)
442 12 PD8 (LCDD8/LVDS0P3) 11 PD9 (LCDD9/LVDS0N3)
443 10 PD7 (LCDD7/LVDS0NC) 9 Ground
444 8 PD5 (LCDD5/LVDS0N2) 7 PD6 (LCDD6/LVDS0PC)
445 6 PD3 (LCDD3/LVDS0N1) 5 PD4 (LCDD4/LNVS0P2)
446 4 PD1 (LCDD1/LVDS0N0) 3 PD2 (LCDD2/LVDS0P1)
447 2 Ground 1 PD0 (LCDD0/LVDSP0)
449 ## U15 (Between Ethernet port and USB ports)
451 1 VCC-5V 2 PH15 (CSI1-PWR/EINT15)
452 3 CSI1-IO-2V8 4 PH14 (CSI1-RST#/EINT14)
453 5 PG0 (CSI1-PCLK/SDC1-CMD) 6 PB18 (TWI1-SCK)
454 7 PB19 (TWI1-SDA) 8 PG3 (CSI1-VSYNC/SDC1-D1)
455 9 PG2 (CSI1-HSYNC/SDC1-D0) 10 PG1 (CSI1-MCLK/SDC1-CLK)
456 11 PG4 (CSI1-D0/SDC1-D2) 12 PG5 (CSI1-D1/SDC1-D3)
457 13 PG6 (CSI1-D2/UART3-TX) 14 PG7 (CSI1-D3/UART3-RX)
458 15 PG8 (CSI1-D4/UART3-RTS) 16 PG9 (CSI1-D5/UART3-CTS)
459 17 PG10 (CSI1-D6/UART4-TX) 18 PG11 (CSI1-D7/UART4-RX)
462 21 FMINL 22 PI4 (SDC3-CMD)
463 23 FMINR 24 PI5 (SDC3-CLK)
464 25 Ground 26 PI6 (SDC3-D0)
465 27 VGA-R 28 PI7 (SDC3-D1)
466 29 VGA-G 30 PI8 (SDC3-D2)
467 31 VGA-B 32 PI9 (SDC3-D3)
469 33 LCD1-VSYNC 34 PE4 (CSI0-D0)
470 35 LCD1-HSYNC 36 PE5 (CSI0-D1)
471 37 Ground 38 PE6 (CSI0-D2)
472 39 AVCC 40 PE7 (CSI0-D3)
473 41 LRADC0 42 PE8 (CSI0-D4)
474 43 CVBS 44 PE9 (CSI0-D5)
475 45 HPL 46 PE10 (CSI0-D6)
476 47 HPR 48 PE11 (CSI0-D7)
478 ## DEBUG serial (middle of board)
486 ## CON3 rpi DIP26-254
490 7 PI3 PWM1 8 PH0 UART3_TX
492 11 PI19 UART2_RX 12 PH2
493 13 PI18 UART2_TX 14 0v
494 15 PI17 UART2_CTS 16 PH21 CAN_RX
495 17 3.3v 18 PH20 CAN_TX
496 19 PI12 SPI0_MOSI 20 0v
497 21 PI13 SPI0_MISO 22 PI16 UART2_RTS
498 23 PI11 SPI0_SCLK 24 PI10 SPI0_CS0
499 25 0v 26 PI14 SPI0_CS1
507 6 PI20 UART7_TX 5 PH3
508 4 PI21 UART7_RX 3 PH5
515 7 gpio4 (WPi 7) 8 gpio14 (TxD)
517 11 gpio17 (WPi 0) 12 gpio18 (WPi 1)
518 13 gpio27 (WPi 2) 14 0v
519 15 gpio22 (WPi 3) 16 gpio23 (WPi 4)
520 17 3.3v 18 gpio24 (WPi 5)
521 19 gpio10 (MOSI) 20 0v
522 21 gpio9 (MISO) 22 gpio25 (WPi 6)
523 23 gpio11 (SCLK) 24 gpio8 (CE0)
525 # Raspberry Pi 3 Model B Rev 1.2
526 27 gpio0 (SDA.0) 28 gpio1 (SCL.0)
527 29 gpio5 (WPi 21) 30 0v
528 31 gpio6 (WPi 22) 32 gpio12 (WPi 26)
529 33 gpio13 (WPi 23) 34 0v
530 35 gpio19 (WPi 24) 36 gpio16 (WPi 27)
531 37 gpio26 (WPi 25) 38 gpio20 (WPi 28)
532 39 0v 40 gpio21 (WPi 29)