5 use Data::Dump qw(dump);
12 my $opt_horizontal = 0;
21 'invert!' => \$opt_invert,
22 'vertical-flip!' => \$opt_vertical,
23 'horizontal-flip!' => \$opt_horizontal,
24 'edge-pins!' => \$opt_edge,
25 'middle-pins!' => \$opt_middle,
26 'zebra!' => \$opt_zebra,
27 'lines!' => \$opt_lines,
28 'read=s' => \$opt_read,
32 my $font_w = 1.67; # < 2.54, font is not perfect square
33 my $font_b = 2.10; # font baseline position
35 $opt_read .= '/' unless $opt_read =~ m/\/$/;
38 open(my $fh, '<', $opt_read . shift);
45 my $model = slurp('/proc/device-tree/model');
46 $model =~ s/\x00$//; # strip kernel NULL
47 warn "# model [$model]";
56 warn "MODEL [$1] == [$model] ?\n";
57 if ( $model =~ m/$1/ ) {
62 } elsif ( $include ) {
63 push @{ $pins->{$1} }, $line_i while ( m/\t(\w+\d+)/g );
69 warn "IGNORE: [$_]\n";
73 die "add pin definition for # $model" unless $pins;
75 shift(@lines) while ( ! $lines[0] ); # remove empty at beginning
76 pop(@lines) while ( ! $lines[-1] ); # remove empty at end
78 warn "# pins ",dump($pins);
81 foreach ( glob $opt_read . '/sys//devices/platform/soc*/*.serial/tty/tty*' ) {
82 my @v = split(/\//, $_);
83 $serial_tty->{ $v[-3] } = $v[-1];
85 warn "# serial_tty = ",dump($serial_tty);
91 open(my $fh, '<', $opt_read . '/sys/kernel/debug/pinctrl/pinctrl-handles');
94 if ( m/device: (\S+)/ ) {
96 if ( my $replace = $serial_tty->{$device} ) {
97 $device = $replace; # replace serial hex with kernel name
99 $device =~ s/^[0-9a-f]*\.//; # remove hex address
101 } elsif ( m/group: (\w+\d+)\s.+function: (\S+)/ ) {
102 my ($pin, $function) = ($1,$2);
103 $pin_function->{$pin} = "$device $function";
105 if ( $pins->{$pin} ) {
106 foreach my $line ( @{$pins->{$pin}} ) {
107 my $t = $lines[$line];
109 $t =~ s/$pin/[$device $function]/;
111 $t =~ s/$pin/$pin [$device $function]/ || die "can't find $pin in [$t]";
114 warn "# $line: $lines[$line]\n";
117 warn "IGNORED: pin $pin function $function\n";
122 warn "# pin_function = ",dump($pin_function);
124 my @max_len = ( 0,0,0,0 );
126 foreach my $line (@lines) {
127 if ( $line =~ m/^#/ ) {
128 push @line_parts, [ $line ] unless $opt_svg;
131 $line =~ s/(\[(?:uart|serial|tty\w+))([^\t]*\]\s[^\t]*(rx|tx)d?)/$1 $3$2/gi;
132 $line =~ s/(\[i2c)([^\t]*\]\s[^\t]*(scl?k?|sda))/$1 $3$2/gi;
133 $line =~ s/(\[spi)([^\t]*\]\s[^\t]*(miso|mosi|s?clk|c[se]\d*))/$1 $3$2/gi;
134 $line =~ s/\s*\([^\)]+\)//g if ! $opt_alt;
136 # shorten duplicate kernel device/function
137 $line =~ s/\[serial (\w+) (uart\d+)\]/[$2 $1]/g;
138 $line =~ s/\[(\w+) (\w+) \1(\d+)\]/[$1$3 $2]/g;
140 my @v = split(/\s*\t+\s*/,$line,4);
141 @v = ( $v[2], $v[3], $v[0], $v[1] ) if $opt_horizontal && $v[2];
143 push @line_parts, [ @v ];
144 foreach my $i ( 0 .. 3 ) {
145 next unless exists $v[$i];
146 next if $v[$i] =~ m/^#/; # don't calculate comments into max length
147 my $l = length($v[$i]);
148 $max_len[$i] = $l if $l > $max_len[$i];
152 warn "# max_len = ",dump( \@max_len );
153 warn "# line_parts = ",dump( \@line_parts );
155 #print "$_\n" foreach @lines;
161 print qq{<?xml version="1.0" encoding="UTF-8" standalone="no"?>
163 xmlns:dc="http://purl.org/dc/elements/1.1/"
164 xmlns:cc="http://creativecommons.org/ns#"
165 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
166 xmlns:svg="http://www.w3.org/2000/svg"
167 xmlns="http://www.w3.org/2000/svg"
168 xmlns:xlink="http://www.w3.org/1999/xlink"
171 viewBox="0 0 210 297"
178 }; # svg, insert rest of rect
180 print qq{<rect x="0" y="0" width="210" height="297" style="fill:#000000" id="high-contrast"/>} if $opt_invert;
185 my $cols = { # foreground background
186 txt => [ '#000000', '#ffffff' ],
187 pins => [ '#ffffff', '#ff00ff' ],
188 vcc => [ '#ff0000', '#ffff00' ],
189 gnd => [ '#000000', '#00ffff' ],
190 i2c => [ '#008800', '#ffcccc' ],
191 serial=>[ '#000088', '#ccffcc' ],
192 spi => [ '#880000', '#ccccff' ],
197 die "$swap not found in ",dump($cols) unless $cols->{$swap};
198 my ( $c1, $c2 ) = @{ $cols->{$swap} };
199 $cols->{$swap} = [ $c2, $c1 ];
202 swap_cols 'txt' if $opt_invert;
206 my ($name,$x,$y,$col) = @_;
207 $y -= $font_b; # shift box overlay to right vertical position based on font baseline
210 my ($x,$y,$col,$fill) = @_;
211 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};
215 if ( $name =~ m/^(\d+)$/ ) { # pins
217 my ( $fg, $bg ) = @{ $cols->{pins} };
219 my $w = $max_len[$col]*$font_w - 0.1;
222 #print qq{<polygon points="$x,$y $cx,$y $x,$cy $x,$y" stroke="$fg" stroke-width="0.25" fill="$bg" />};
223 #print qq{<polygon points="$x,$cy $cx,$cy $cx,$y $x,$cy" stroke="$bg" stroke-width="0.25" fill="$fg" />};
224 print qq{<rect x="$x" y="$y" width="$w" height="2.54" stroke="$fg" stroke-width="0.3" fill="$bg" />};
225 my ( $fg, $bg ) = @{ $cols->{txt} };
226 print qq{<rect x="$x" y="$y" width="$w" height="2.54" rx="1" ry="1" stroke="$fg" stroke-width="0.3" fill="$bg" />};
230 return qq{ style="fill:$bg"};
233 if ( $name =~ m/(VCC|3V3|3.3V|5v)/i ) {
234 my ($fg,$bg) = @{ $cols->{vcc} };
236 return qq{ style="fill:$fg"};
237 } elsif ( $name =~ m/(G(ND|Round)|VSS|0v)/i ) {
238 my ($fg,$bg) = @{ $cols->{gnd} };
240 return qq{ style="fill:$fg"};
241 } elsif ( $name =~ m/\[(\w+)/ ) { # kernel
243 my ($fg,$bg) = @{ $cols->{txt} };
244 $dev = 'serial' if $dev =~ m/^tty/;
245 ($fg,$bg) = @{ $cols->{$dev} } if exists $cols->{$dev};
247 return qq{ style="fill:$fg"};
249 my ( $fg, $bg ) = @{ $cols->{txt} };
251 #return qq{ style="fill:$fg"};
258 my @cols_order = ( 0,1,2,3 );
259 my @cols_align = ( '','-','','-' ); # sprintf prefix
261 my @cols_shuffle = @cols_order;
264 # pins outside on the right
265 @cols_shuffle = ( 0,1,3,2 ) if $opt_edge;
266 @cols_align = ( '-','-','','' );
267 } elsif ( $opt_middle ) {
269 @cols_shuffle = ( 1,0,2,3 );
270 @cols_align = ( '','','-','-' );
274 my ( $what, $order ) = @_;
276 foreach my $i ( 0 .. $#$what ) {
277 $new->[$i] = $what->[ $order->[$i] ];
279 warn "# cols_shuffle what=",dump($what)," order=",dump($order)," new=",dump($new);
283 @cols_order = cols_shuffle( \@cols_order, \@cols_shuffle );
284 @max_len = cols_shuffle( \@max_len, \@cols_shuffle );
286 warn "# cols_order = ",dump( \@cols_order );
287 warn "# cols_align = ",dump( \@cols_align );
289 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";
293 my ($fg,$bg) = @{ $cols->{txt} };
294 my $line_fmt = qq{<line x1="%s" y1="%s" x2="%s" y2="%s" style="stroke:$fg;stroke-width:0.10;fill:$bg" />\n};
299 return unless $opt_svg;
300 push @cut_marks, sprintf($line_fmt, $x-5, $y-$font_b, $x+5, $y-$font_b);
301 push @cut_marks, sprintf($line_fmt, $x, $y-$font_b-5, $x, $y-$font_b+5);
305 $max_x += $max_len[$_] * $font_w foreach ( 0 .. 3 );
309 my ($x,$y,$max_x) = @_;
310 push @cut_marks, sprintf($line_fmt, $x, $y-$font_b, $max_x, $y-$font_b);
314 my $last_cut_mark = 0;
316 foreach my $i ( 0 .. $#line_parts ) {
317 $i = $#line_parts - $i if $opt_vertical;
318 my $line = $line_parts[$i];
322 if ( ! exists $line->[0] ) {
323 # before first empty line
324 if ( $last_cut_mark == 0 ) {
328 line $x, $y, $max_x if $opt_lines;
329 $y += 15; # make spacing between pinouts
331 } elsif ( $last_cut_mark ) {
337 #warn "CUTMARK no magic";
340 line $x, $y, $max_x if $opt_lines && exists $line->[1];
342 my ($fg,$bg) = @{ $cols->{txt} };
343 my $tspan = qq{<tspan x="$x" y="$y" style="line-height:2.54;fill:$fg;stroke:none;">\n};
346 foreach my $i ( 0 .. $#cols_order ) {
347 my $order = $cols_order[$i];
348 next unless $line->[$order];
349 my $text_anchor = 'middle';
350 my $x2 = $x_pos + ( $max_len[$i] * $font_w ) / 2;
351 $tspan .= qq{<tspan x="$x2" text-anchor="$text_anchor"}.svg_style($line->[$order],$x_pos,$y,$i).sprintf( '>%' . $cols_align[$i] . $max_len[$i] . 's</tspan>', $line->[$order]);
352 $x_pos += $max_len[$i] * $font_w;
355 $tspan .= qq{</tspan>\n};
356 push @later,sprintf $tspan, @$line;
359 # swap pin colors for line stripe
361 swap_cols $_ foreach qw( pins txt );
366 if ( $#$line == 0 ) {
367 print $line->[0], "\n";
369 push @$line, '' while ($#$line < 3); # fill-in single row header
370 printf $fmt, map { $line->[$_] } @cols_order;
379 line $x, $y, $max_x if $opt_lines;
386 style="font-size:2.34px;line-height:2.54px;font-family:'Andale Mono';stroke:none"
387 xml:space="preserve">
391 print @later, qq{</text>\n}, @cut_marks, qq{</g>\n</svg>};
396 # Cubietech Cubieboard
397 ## U14 (Next to SATA connector)
399 48 PI13 (SPI0-MISO/UART6-RX/EINT25) 47 PI11 (SPI0-CLK/UART5-RX/EINT23)
400 46 PI12 (SPI0-MOSI/UART6-TX/EINT24) 45 PI10 (SPI0-CS/UART5-TX/EINT22)
402 44 3.3V (nc in 2012-08-08) 43 VCC-5V
404 40 PB10 (LCD0-SCK/LCD-PIO1) 39 PB11 (LCD0-SDA/LCD-PIO2)
405 38 Ground 37 PH7 (LCD0-BL-EN/LCD-PIO0/UART5-RX/EINT7)
406 36 XN_TP (TP-X2) 35 YN_TP (TP-Y2)
407 34 XP_TP (TP-X1) 33 YP_TP (TP-Y1)
408 32 PD25 (LCDDE) 31 PB2 (PWM0)
409 30 PD26 (LCDHSYNC/VGA-HSYNC) 29 PD24 (LCDCLK)
410 28 PD23 (LCDD23) 27 PD27 (LCDVSYNC/VGA-VSYNC)
411 26 PD21 (LCDD21) 25 PD22 (LCDD22)
412 24 PD19 (LCDD19/LVDS1N3) 23 PD20 (LCDD20)
413 22 PD17 (LCDD17/LVDS1NC) 21 PD18 (LCDD18/LVDS1P3)
414 20 Ground 19 PD16 (LCDD16/LVDS1PC)
415 18 PD14 (LCDD14/LVDS1P2) 17 PD15 (LCDD15/LVDS1N2)
416 16 PD12 (LCDD12/LVDS1P1) 15 PD13 (LCDD13/LVDS1N1)
417 14 PD10 (LCDD10/LVDS1P0) 13 PD11 (LCDD11/LVDS1N0)
418 12 PD8 (LCDD8/LVDS0P3) 11 PD9 (LCDD9/LVDS0N3)
419 10 PD7 (LCDD7/LVDS0NC) 9 Ground
420 8 PD5 (LCDD5/LVDS0N2) 7 PD6 (LCDD6/LVDS0PC)
421 6 PD3 (LCDD3/LVDS0N1) 5 PD4 (LCDD4/LNVS0P2)
422 4 PD1 (LCDD1/LVDS0N0) 3 PD2 (LCDD2/LVDS0P1)
423 2 Ground 1 PD0 (LCDD0/LVDSP0)
425 ## U15 (Between Ethernet port and USB ports)
427 1 VCC-5V 2 PH15 (CSI1-PWR/EINT15)
428 3 CSI1-IO-2V8 4 PH14 (CSI1-RST#/EINT14)
429 5 PG0 (CSI1-PCLK/SDC1-CMD) 6 PB18 (TWI1-SCK)
430 7 PB19 (TWI1-SDA) 8 PG3 (CSI1-VSYNC/SDC1-D1)
431 9 PG2 (CSI1-HSYNC/SDC1-D0) 10 PG1 (CSI1-MCLK/SDC1-CLK)
432 11 PG4 (CSI1-D0/SDC1-D2) 12 PG5 (CSI1-D1/SDC1-D3)
433 13 PG6 (CSI1-D2/UART3-TX) 14 PG7 (CSI1-D3/UART3-RX)
434 15 PG8 (CSI1-D4/UART3-RTS) 16 PG9 (CSI1-D5/UART3-CTS)
435 17 PG10 (CSI1-D6/UART4-TX) 18 PG11 (CSI1-D7/UART4-RX)
438 21 FMINL 22 PI4 (SDC3-CMD)
439 23 FMINR 24 PI5 (SDC3-CLK)
440 25 Ground 26 PI6 (SDC3-D0)
441 27 VGA-R 28 PI7 (SDC3-D1)
442 29 VGA-G 30 PI8 (SDC3-D2)
443 31 VGA-B 32 PI9 (SDC3-D3)
445 33 LCD1-VSYNC 34 PE4 (CSI0-D0)
446 35 LCD1-HSYNC 36 PE5 (CSI0-D1)
447 37 Ground 38 PE6 (CSI0-D2)
448 39 AVCC 40 PE7 (CSI0-D3)
449 41 LRADC0 42 PE8 (CSI0-D4)
450 43 CVBS 44 PE9 (CSI0-D5)
451 45 HPL 46 PE10 (CSI0-D6)
452 47 HPR 48 PE11 (CSI0-D7)
454 ## DEBUG serial (middle of board)
462 ## CON3 rpi DIP26-254
466 7 PI3 PWM1 8 PH0 UART3_TX
468 11 PI19 UART2_RX 12 PH2
469 13 PI18 UART2_TX 14 0v
470 15 PI17 UART2_CTS 16 PH21 CAN_RX
471 17 3.3v 18 PH20 CAN_TX
472 19 PI12 SPI0_MOSI 20 0v
473 21 PI13 SPI0_MISO 22 PI16 UART2_RTS
474 23 PI11 SPI0_SCLK 24 PI10 SPI0_CS0
475 25 0v 26 PI14 SPI0_CS1
483 6 PI20 UART7_TX 5 PH3
484 4 PI21 UART7_RX 3 PH5
491 7 gpio4 (WPi 7) 8 gpio14 (TxD)
493 11 gpio17 (WPi 0) 12 gpio18 (WPi 1)
494 13 gpio27 (WPi 2) 14 0v
495 15 gpio22 (WPi 3) 16 gpio23 (WPi 4)
496 17 3.3v 18 gpio24 (WPi 5)
497 19 gpio10 (MOSI) 20 0v
498 21 gpio9 (MISO) 22 gpio25 (WPi 6)
499 23 gpio11 (SCLK) 24 gpio8 (CE0)
501 # Raspberry Pi 3 Model B Rev 1.2
502 27 gpio0 (SDA.0) 28 gpio1 (SCL.0)
503 29 gpio5 (WPi 21) 30 0v
504 31 gpio6 (WPi 22) 32 gpio12 (WPi 26)
505 33 gpio13 (WPi 23) 34 0v
506 35 gpio19 (WPi 24) 36 gpio16 (WPi 27)
507 37 gpio26 (WPi 25) 38 gpio20 (WPi 28)
508 39 0v 40 gpio21 (WPi 29)