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]";
54 open(DATA, '<', $opt_pins) if $opt_pins;
63 warn "MODEL [$1] == [$model] ?\n";
64 if ( $model =~ m/$1/ ) {
69 } elsif ( $include || $opt_pins ) {
70 push @{ $pins->{$1} }, $line_i while ( m/\t(\w+\d+)/g );
76 warn "IGNORE: [$_]\n";
80 if ( ! $opt_pins && ! $pins ) {
82 my $glob =~ s/^(\w+).*$/$1/;
83 my @pins = glob "pins/${glob}*";
84 warn "# possible pins: ",dump( \@pins );
89 die "add pin definition for # $model" unless $pins;
91 #warn "# lines ",dump( \@lines );
92 warn "# pins ",dump($pins);
96 glob($opt_read . '/sys/devices/platform/soc*/*.serial/tty/tty*'), # 4.x
97 glob( '/sys/devices/soc.*/*.uart/tty/tty*') # 3.10
99 my @v = split(/\//, $_);
100 $serial_tty->{ $v[-3] } = $v[-1];
102 warn "# serial_tty = ",dump($serial_tty);
110 open(my $fh, '<', $opt_read . '/sys/kernel/debug/pinctrl/pinctrl-maps');
113 if ( m/^device (\S+)/ ) {
115 if ( my $replace = $serial_tty->{$device} ) {
116 $device = $replace; # replace serial hex with kernel name
118 $device =~ s/^[0-9a-f]*\.//; # remove hex address
120 } elsif ( m/^group (\w+\d+)/ ) {
123 } elsif ( m/^function (\S+)/ ) {
126 if ( $device && $pin && $function ) {
127 push @{ $pin_function->{$pin} }, "$device $function";
129 if ( $pins->{$pin} ) {
130 foreach my $line ( @{$pins->{$pin}} ) {
131 my $t = $lines[$line];
133 $t =~ s/$pin/[$device $function]/;
135 $t =~ s/$pin/$pin [$device $function]/ || warn "can't find $pin in [$t]";
138 warn "# $line: $lines[$line]\n";
141 warn "IGNORED: pin $pin function $function\n";
145 warn "missing one of ",dump( $device, $pin, $function );
155 warn "# pin_function = ",dump($pin_function);
157 my @max_len = ( 0,0,0,0 );
160 shift(@lines) while ( ! $lines[0] ); # remove empty at beginning
161 pop(@lines) while ( ! $lines[-1] ); # remove empty at end
163 foreach my $line (@lines) {
164 if ( $line =~ m/^#/ ) {
165 push @line_parts, [ $line ] unless $opt_svg && $line =~ m/^###+/; # SVG doesn't display 3rd level comments
168 $line =~ s/\[(\w+)\s+(\w+)\] \[\1\s+(\w+)\]/[$1 $2 $3]/g; # compress kernel annotation with same prefix
169 $line =~ s/(\[(?:uart\d*|serial|tty\w+))([^\t]*\]\s[^\t]*(rx|tx)d?)/$1 $3$2/gi;
170 $line =~ s/(\[i2c)([^\t]*\]\s[^\t]*(scl?k?|sda))/$1 $3$2/gi;
171 $line =~ s/(\[spi)([^\t]*\]\s[^\t]*(miso|mosi|s?clk|c[se]\d*))/$1 $3$2/gi;
172 $line =~ s/\s*\([^\)]+\)//g if ! $opt_alt;
174 # shorten duplicate kernel device/function
175 $line =~ s/\[serial (\w+) (uart\d+)\]/[$2 $1]/g;
176 $line =~ s/\[(\w+) (\w+) \1(\d+)\]/[$1$3 $2]/g;
178 $line =~ s/\[(\w+)\s+([^\]]+)\s+\1\]/[$1 $2]/g; # duplicate
180 my @v = split(/\s*\t+\s*/,$line,4);
181 @v = ( $v[2], $v[3], $v[0], $v[1] ) if $opt_horizontal && $v[2];
183 push @line_parts, [ @v ];
184 foreach my $i ( 0 .. 3 ) {
185 next unless exists $v[$i];
186 next if $v[$i] =~ m/^#/; # don't calculate comments into max length
187 my $l = length($v[$i]);
188 $max_len[$i] = $l if $l > $max_len[$i];
192 warn "# max_len = ",dump( \@max_len );
193 warn "# line_parts = ",dump( \@line_parts );
195 #print "$_\n" foreach @lines;
201 print qq{<?xml version="1.0" encoding="UTF-8" standalone="no"?>
203 xmlns:dc="http://purl.org/dc/elements/1.1/"
204 xmlns:cc="http://creativecommons.org/ns#"
205 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
206 xmlns:svg="http://www.w3.org/2000/svg"
207 xmlns="http://www.w3.org/2000/svg"
208 xmlns:xlink="http://www.w3.org/1999/xlink"
211 viewBox="0 0 210 297"
218 }; # svg, insert rest of rect
220 print qq{<rect x="0" y="0" width="210" height="297" style="fill:#000000" id="high-contrast"/>} if $opt_invert;
225 my $cols = { # foreground background
226 txt => [ '#000000', '#ffffff' ],
227 pins => [ '#ffffff', '#ff00ff' ],
228 vcc => [ '#ff0000', '#ffff00' ],
229 gnd => [ '#000000', '#00ffff' ],
230 i2c => [ '#008800', '#ffcccc' ],
231 serial=>[ '#000088', '#ccffcc' ],
232 spi => [ '#880000', '#ccccff' ],
237 die "$swap not found in ",dump($cols) unless $cols->{$swap};
238 my ( $c1, $c2 ) = @{ $cols->{$swap} };
239 $cols->{$swap} = [ $c2, $c1 ];
242 swap_cols 'txt' if $opt_invert;
246 my ($name,$x,$y,$col) = @_;
248 return '' unless $opt_color;
250 $y -= $font_b; # shift box overlay to right vertical position based on font baseline
253 my ($x,$y,$col,$fill) = @_;
254 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};
258 if ( $name =~ m/^(\d+)$/ ) { # pins
260 my ( $fg, $bg ) = @{ $cols->{pins} };
262 my $w = $max_len[$col]*$font_w - 0.1;
265 #print qq{<polygon points="$x,$y $cx,$y $x,$cy $x,$y" stroke="$fg" stroke-width="0.25" fill="$bg" />};
266 #print qq{<polygon points="$x,$cy $cx,$cy $cx,$y $x,$cy" stroke="$bg" stroke-width="0.25" fill="$fg" />};
267 print qq{<rect x="$x" y="$y" width="$w" height="2.54" stroke="$fg" stroke-width="0.3" fill="$bg" />};
268 my ( $fg, $bg ) = @{ $cols->{txt} };
269 print qq{<rect x="$x" y="$y" width="$w" height="2.54" rx="1" ry="1" stroke="$fg" stroke-width="0.3" fill="$bg" />};
273 return qq{ style="fill:$bg"};
276 if ( $name =~ m/(VCC|3V3|3.3V|5v)/i ) {
277 my ($fg,$bg) = @{ $cols->{vcc} };
279 return qq{ style="fill:$fg"};
280 } elsif ( $name =~ m/(G(ND|Round)|VSS|0v)/i ) {
281 my ($fg,$bg) = @{ $cols->{gnd} };
283 return qq{ style="fill:$fg"};
284 } elsif ( $name =~ m/\[(\w+)/ ) { # kernel
286 my ($fg,$bg) = @{ $cols->{txt} };
287 $dev = 'serial' if $dev =~ m/^tty/;
288 ($fg,$bg) = @{ $cols->{$dev} } if exists $cols->{$dev};
290 return qq{ style="fill:$fg"};
292 my ( $fg, $bg ) = @{ $cols->{txt} };
294 #return qq{ style="fill:$fg"};
301 my @cols_order = ( 0,1,2,3 );
302 my @cols_align = ( '','-','','-' ); # sprintf prefix
304 my @cols_shuffle = @cols_order;
307 # pins outside on the right
308 @cols_shuffle = ( 0,1,3,2 ) if $opt_edge;
309 @cols_align = ( '-','-','','' );
310 } elsif ( $opt_middle ) {
312 @cols_shuffle = ( 1,0,2,3 );
313 @cols_align = ( '','','-','-' );
317 my ( $what, $order ) = @_;
319 foreach my $i ( 0 .. $#$what ) {
320 $new->[$i] = $what->[ $order->[$i] ];
322 warn "# cols_shuffle what=",dump($what)," order=",dump($order)," new=",dump($new);
326 @cols_order = cols_shuffle( \@cols_order, \@cols_shuffle );
327 @max_len = cols_shuffle( \@max_len, \@cols_shuffle );
329 warn "# cols_order = ",dump( \@cols_order );
330 warn "# cols_align = ",dump( \@cols_align );
332 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";
336 my ($fg,$bg) = @{ $cols->{txt} };
337 my $line_fmt = qq{<line x1="%s" y1="%s" x2="%s" y2="%s" style="stroke:$fg;stroke-width:0.10;fill:$bg" />\n};
342 return unless $opt_svg;
343 push @cut_marks, sprintf($line_fmt, $x-5, $y-$font_b, $x+5, $y-$font_b);
344 push @cut_marks, sprintf($line_fmt, $x, $y-$font_b-5, $x, $y-$font_b+5);
348 $max_x += $max_len[$_] * $font_w foreach ( 0 .. 3 );
349 #cut_mark $max_x, $y;
352 my ($x,$y,$max_x) = @_;
353 push @cut_marks, sprintf($line_fmt, $x, $y-$font_b, $max_x, $y-$font_b);
357 my $last_cut_mark = 0;
359 foreach my $i ( 0 .. $#line_parts ) {
360 $i = $#line_parts - $i if $opt_vertical;
361 my $line = $line_parts[$i];
365 # not a minimal two column pin description
366 if ( ! exists $line->[1] ) {
367 $last_cut_mark = 1 if $line->[0] =~ m/^##/; # skip comments
369 # before first empty line
370 if ( $last_cut_mark == 0 ) {
374 line $x, $y, $max_x if $opt_lines;
375 $y += 15; # make spacing between pinouts
377 } elsif ( $last_cut_mark ) {
383 #warn "CUTMARK no magic";
386 line $x, $y, $max_x if $opt_lines && exists $line->[1];
388 my ($fg,$bg) = @{ $cols->{txt} };
389 my $tspan = qq{<tspan x="$x" y="$y" style="line-height:2.54;fill:$fg;stroke:none;">\n};
392 foreach my $i ( 0 .. $#cols_order ) {
393 my $order = $cols_order[$i];
394 next unless $line->[$order];
396 my $text_anchor = 'middle';
397 my $len = $max_len[$i];
398 my $x2 = $x_pos + ( $len * $font_w ) / 2;
400 if ( $#$line == 0 && $line->[$order] =~ s/^#+s*// ) {
401 # comment, center over whole width
402 $len = length($line->[$order]);
403 $x2 = $x + (($max_x-$x)/2); # middle
404 $tspan .= qq{\t<tspan x="$x2" text-anchor="$text_anchor"}.sprintf( '>%' . $cols_align[$i] . $len . 's</tspan>', $line->[0]);
406 $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]);
408 $x_pos += $len * $font_w;
411 $tspan .= qq{\n</tspan>\n};
412 push @later,sprintf $tspan, @$line;
415 # swap pin colors for line stripe
417 swap_cols $_ foreach qw( pins txt );
424 if ( $#$line == 0 ) {
425 print $line->[0], "\n";
427 push @$line, '' while ($#$line < 3); # fill-in single row header
428 printf $fmt, map { $line->[$_] } @cols_order;
437 line $x, $y, $max_x if $opt_lines;
444 style="font-size:2.34px;line-height:2.54px;font-family:'Andale Mono';stroke:none"
445 xml:space="preserve">
449 print @later, qq{</text>\n}, @cut_marks, qq{</g>\n</svg>};
454 # Cubietech Cubieboard
455 ## U14 (Next to SATA connector)
457 48 PI13 (SPI0-MISO/UART6-RX/EINT25) 47 PI11 (SPI0-CLK/UART5-RX/EINT23)
458 46 PI12 (SPI0-MOSI/UART6-TX/EINT24) 45 PI10 (SPI0-CS/UART5-TX/EINT22)
460 44 3.3V (nc in 2012-08-08) 43 VCC-5V
462 40 PB10 (LCD0-SCK/LCD-PIO1) 39 PB11 (LCD0-SDA/LCD-PIO2)
463 38 Ground 37 PH7 (LCD0-BL-EN/LCD-PIO0/UART5-RX/EINT7)
464 36 XN_TP (TP-X2) 35 YN_TP (TP-Y2)
465 34 XP_TP (TP-X1) 33 YP_TP (TP-Y1)
466 32 PD25 (LCDDE) 31 PB2 (PWM0)
467 30 PD26 (LCDHSYNC/VGA-HSYNC) 29 PD24 (LCDCLK)
468 28 PD23 (LCDD23) 27 PD27 (LCDVSYNC/VGA-VSYNC)
469 26 PD21 (LCDD21) 25 PD22 (LCDD22)
470 24 PD19 (LCDD19/LVDS1N3) 23 PD20 (LCDD20)
471 22 PD17 (LCDD17/LVDS1NC) 21 PD18 (LCDD18/LVDS1P3)
472 20 Ground 19 PD16 (LCDD16/LVDS1PC)
473 18 PD14 (LCDD14/LVDS1P2) 17 PD15 (LCDD15/LVDS1N2)
474 16 PD12 (LCDD12/LVDS1P1) 15 PD13 (LCDD13/LVDS1N1)
475 14 PD10 (LCDD10/LVDS1P0) 13 PD11 (LCDD11/LVDS1N0)
476 12 PD8 (LCDD8/LVDS0P3) 11 PD9 (LCDD9/LVDS0N3)
477 10 PD7 (LCDD7/LVDS0NC) 9 Ground
478 8 PD5 (LCDD5/LVDS0N2) 7 PD6 (LCDD6/LVDS0PC)
479 6 PD3 (LCDD3/LVDS0N1) 5 PD4 (LCDD4/LNVS0P2)
480 4 PD1 (LCDD1/LVDS0N0) 3 PD2 (LCDD2/LVDS0P1)
481 2 Ground 1 PD0 (LCDD0/LVDSP0)
483 ## U15 (Between Ethernet port and USB ports)
485 1 VCC-5V 2 PH15 (CSI1-PWR/EINT15)
486 3 CSI1-IO-2V8 4 PH14 (CSI1-RST#/EINT14)
487 5 PG0 (CSI1-PCLK/SDC1-CMD) 6 PB18 (TWI1-SCK)
488 7 PB19 (TWI1-SDA) 8 PG3 (CSI1-VSYNC/SDC1-D1)
489 9 PG2 (CSI1-HSYNC/SDC1-D0) 10 PG1 (CSI1-MCLK/SDC1-CLK)
490 11 PG4 (CSI1-D0/SDC1-D2) 12 PG5 (CSI1-D1/SDC1-D3)
491 13 PG6 (CSI1-D2/UART3-TX) 14 PG7 (CSI1-D3/UART3-RX)
492 15 PG8 (CSI1-D4/UART3-RTS) 16 PG9 (CSI1-D5/UART3-CTS)
493 17 PG10 (CSI1-D6/UART4-TX) 18 PG11 (CSI1-D7/UART4-RX)
496 21 FMINL 22 PI4 (SDC3-CMD)
497 23 FMINR 24 PI5 (SDC3-CLK)
498 25 Ground 26 PI6 (SDC3-D0)
499 27 VGA-R 28 PI7 (SDC3-D1)
500 29 VGA-G 30 PI8 (SDC3-D2)
501 31 VGA-B 32 PI9 (SDC3-D3)
503 33 LCD1-VSYNC 34 PE4 (CSI0-D0)
504 35 LCD1-HSYNC 36 PE5 (CSI0-D1)
505 37 Ground 38 PE6 (CSI0-D2)
506 39 AVCC 40 PE7 (CSI0-D3)
507 41 LRADC0 42 PE8 (CSI0-D4)
508 43 CVBS 44 PE9 (CSI0-D5)
509 45 HPL 46 PE10 (CSI0-D6)
510 47 HPR 48 PE11 (CSI0-D7)
512 ## DEBUG serial (middle of board)
520 ## CON3 rpi DIP26-254
524 7 PI3 PWM1 8 PH0 UART3_TX
526 11 PI19 UART2_RX 12 PH2
527 13 PI18 UART2_TX 14 0v
528 15 PI17 UART2_CTS 16 PH21 CAN_RX
529 17 3.3v 18 PH20 CAN_TX
530 19 PI12 SPI0_MOSI 20 0v
531 21 PI13 SPI0_MISO 22 PI16 UART2_RTS
532 23 PI11 SPI0_SCLK 24 PI10 SPI0_CS0
533 25 0v 26 PI14 SPI0_CS1
541 6 PI20 UART7_TX 5 PH3
542 4 PI21 UART7_RX 3 PH5
549 7 gpio4 (WPi 7) 8 gpio14 (TxD)
551 11 gpio17 (WPi 0) 12 gpio18 (WPi 1)
552 13 gpio27 (WPi 2) 14 0v
553 15 gpio22 (WPi 3) 16 gpio23 (WPi 4)
554 17 3.3v 18 gpio24 (WPi 5)
555 19 gpio10 (MOSI) 20 0v
556 21 gpio9 (MISO) 22 gpio25 (WPi 6)
557 23 gpio11 (SCLK) 24 gpio8 (CE0)
559 # Raspberry Pi 3 Model B Rev 1.2
560 27 gpio0 (SDA.0) 28 gpio1 (SCL.0)
561 29 gpio5 (WPi 21) 30 0v
562 31 gpio6 (WPi 22) 32 gpio12 (WPi 26)
563 33 gpio13 (WPi 23) 34 0v
564 35 gpio19 (WPi 24) 36 gpio16 (WPi 27)
565 37 gpio26 (WPi 25) 38 gpio20 (WPi 28)
566 39 0v 40 gpio21 (WPi 29)