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 #warn "# lines ",dump( \@lines );
82 warn "# pins ",dump($pins);
86 glob($opt_read . '/sys/devices/platform/soc*/*.serial/tty/tty*'), # 4.x
87 glob( '/sys/devices/soc.*/*.uart/tty/tty*') # 3.10
89 my @v = split(/\//, $_);
90 $serial_tty->{ $v[-3] } = $v[-1];
92 warn "# serial_tty = ",dump($serial_tty);
100 open(my $fh, '<', $opt_read . '/sys/kernel/debug/pinctrl/pinctrl-maps');
103 if ( m/^device (\S+)/ ) {
105 if ( my $replace = $serial_tty->{$device} ) {
106 $device = $replace; # replace serial hex with kernel name
108 $device =~ s/^[0-9a-f]*\.//; # remove hex address
110 } elsif ( m/^group (\w+\d+)/ ) {
113 } elsif ( m/^function (\S+)/ ) {
116 if ( $device && $pin && $function ) {
117 push @{ $pin_function->{$pin} }, "$device $function";
119 if ( $pins->{$pin} ) {
120 foreach my $line ( @{$pins->{$pin}} ) {
121 my $t = $lines[$line];
123 $t =~ s/$pin/[$device $function]/;
125 $t =~ s/$pin/$pin [$device $function]/ || warn "can't find $pin in [$t]";
128 warn "# $line: $lines[$line]\n";
131 warn "IGNORED: pin $pin function $function\n";
135 warn "missing one of ",dump( $device, $pin, $function );
145 warn "# pin_function = ",dump($pin_function);
147 my @max_len = ( 0,0,0,0 );
150 shift(@lines) while ( ! $lines[0] ); # remove empty at beginning
151 pop(@lines) while ( ! $lines[-1] ); # remove empty at end
153 foreach my $line (@lines) {
154 if ( $line =~ m/^#/ ) {
155 push @line_parts, [ $line ] unless $opt_svg && $line =~ m/^###+/; # SVG doesn't display 3rd level comments
158 $line =~ s/\[(\w+)\s+(\w+)\] \[\1\s+(\w+)\]/[$1 $2 $3]/g; # compress kernel annotation with same prefix
159 $line =~ s/(\[(?:uart\d*|serial|tty\w+))([^\t]*\]\s[^\t]*(rx|tx)d?)/$1 $3$2/gi;
160 $line =~ s/(\[i2c)([^\t]*\]\s[^\t]*(scl?k?|sda))/$1 $3$2/gi;
161 $line =~ s/(\[spi)([^\t]*\]\s[^\t]*(miso|mosi|s?clk|c[se]\d*))/$1 $3$2/gi;
162 $line =~ s/\s*\([^\)]+\)//g if ! $opt_alt;
164 # shorten duplicate kernel device/function
165 $line =~ s/\[serial (\w+) (uart\d+)\]/[$2 $1]/g;
166 $line =~ s/\[(\w+) (\w+) \1(\d+)\]/[$1$3 $2]/g;
168 $line =~ s/\[(\w+)\s+([^\]]+)\s+\1\]/[$1 $2]/g; # duplicate
170 my @v = split(/\s*\t+\s*/,$line,4);
171 @v = ( $v[2], $v[3], $v[0], $v[1] ) if $opt_horizontal && $v[2];
173 push @line_parts, [ @v ];
174 foreach my $i ( 0 .. 3 ) {
175 next unless exists $v[$i];
176 next if $v[$i] =~ m/^#/; # don't calculate comments into max length
177 my $l = length($v[$i]);
178 $max_len[$i] = $l if $l > $max_len[$i];
182 warn "# max_len = ",dump( \@max_len );
183 warn "# line_parts = ",dump( \@line_parts );
185 #print "$_\n" foreach @lines;
191 print qq{<?xml version="1.0" encoding="UTF-8" standalone="no"?>
193 xmlns:dc="http://purl.org/dc/elements/1.1/"
194 xmlns:cc="http://creativecommons.org/ns#"
195 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
196 xmlns:svg="http://www.w3.org/2000/svg"
197 xmlns="http://www.w3.org/2000/svg"
198 xmlns:xlink="http://www.w3.org/1999/xlink"
201 viewBox="0 0 210 297"
208 }; # svg, insert rest of rect
210 print qq{<rect x="0" y="0" width="210" height="297" style="fill:#000000" id="high-contrast"/>} if $opt_invert;
215 my $cols = { # foreground background
216 txt => [ '#000000', '#ffffff' ],
217 pins => [ '#ffffff', '#ff00ff' ],
218 vcc => [ '#ff0000', '#ffff00' ],
219 gnd => [ '#000000', '#00ffff' ],
220 i2c => [ '#008800', '#ffcccc' ],
221 serial=>[ '#000088', '#ccffcc' ],
222 spi => [ '#880000', '#ccccff' ],
227 die "$swap not found in ",dump($cols) unless $cols->{$swap};
228 my ( $c1, $c2 ) = @{ $cols->{$swap} };
229 $cols->{$swap} = [ $c2, $c1 ];
232 swap_cols 'txt' if $opt_invert;
236 my ($name,$x,$y,$col) = @_;
238 return '' unless $opt_color;
240 $y -= $font_b; # shift box overlay to right vertical position based on font baseline
243 my ($x,$y,$col,$fill) = @_;
244 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};
248 if ( $name =~ m/^(\d+)$/ ) { # pins
250 my ( $fg, $bg ) = @{ $cols->{pins} };
252 my $w = $max_len[$col]*$font_w - 0.1;
255 #print qq{<polygon points="$x,$y $cx,$y $x,$cy $x,$y" stroke="$fg" stroke-width="0.25" fill="$bg" />};
256 #print qq{<polygon points="$x,$cy $cx,$cy $cx,$y $x,$cy" stroke="$bg" stroke-width="0.25" fill="$fg" />};
257 print qq{<rect x="$x" y="$y" width="$w" height="2.54" stroke="$fg" stroke-width="0.3" fill="$bg" />};
258 my ( $fg, $bg ) = @{ $cols->{txt} };
259 print qq{<rect x="$x" y="$y" width="$w" height="2.54" rx="1" ry="1" stroke="$fg" stroke-width="0.3" fill="$bg" />};
263 return qq{ style="fill:$bg"};
266 if ( $name =~ m/(VCC|3V3|3.3V|5v)/i ) {
267 my ($fg,$bg) = @{ $cols->{vcc} };
269 return qq{ style="fill:$fg"};
270 } elsif ( $name =~ m/(G(ND|Round)|VSS|0v)/i ) {
271 my ($fg,$bg) = @{ $cols->{gnd} };
273 return qq{ style="fill:$fg"};
274 } elsif ( $name =~ m/\[(\w+)/ ) { # kernel
276 my ($fg,$bg) = @{ $cols->{txt} };
277 $dev = 'serial' if $dev =~ m/^tty/;
278 ($fg,$bg) = @{ $cols->{$dev} } if exists $cols->{$dev};
280 return qq{ style="fill:$fg"};
282 my ( $fg, $bg ) = @{ $cols->{txt} };
284 #return qq{ style="fill:$fg"};
291 my @cols_order = ( 0,1,2,3 );
292 my @cols_align = ( '','-','','-' ); # sprintf prefix
294 my @cols_shuffle = @cols_order;
297 # pins outside on the right
298 @cols_shuffle = ( 0,1,3,2 ) if $opt_edge;
299 @cols_align = ( '-','-','','' );
300 } elsif ( $opt_middle ) {
302 @cols_shuffle = ( 1,0,2,3 );
303 @cols_align = ( '','','-','-' );
307 my ( $what, $order ) = @_;
309 foreach my $i ( 0 .. $#$what ) {
310 $new->[$i] = $what->[ $order->[$i] ];
312 warn "# cols_shuffle what=",dump($what)," order=",dump($order)," new=",dump($new);
316 @cols_order = cols_shuffle( \@cols_order, \@cols_shuffle );
317 @max_len = cols_shuffle( \@max_len, \@cols_shuffle );
319 warn "# cols_order = ",dump( \@cols_order );
320 warn "# cols_align = ",dump( \@cols_align );
322 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";
326 my ($fg,$bg) = @{ $cols->{txt} };
327 my $line_fmt = qq{<line x1="%s" y1="%s" x2="%s" y2="%s" style="stroke:$fg;stroke-width:0.10;fill:$bg" />\n};
332 return unless $opt_svg;
333 push @cut_marks, sprintf($line_fmt, $x-5, $y-$font_b, $x+5, $y-$font_b);
334 push @cut_marks, sprintf($line_fmt, $x, $y-$font_b-5, $x, $y-$font_b+5);
338 $max_x += $max_len[$_] * $font_w foreach ( 0 .. 3 );
339 #cut_mark $max_x, $y;
342 my ($x,$y,$max_x) = @_;
343 push @cut_marks, sprintf($line_fmt, $x, $y-$font_b, $max_x, $y-$font_b);
347 my $last_cut_mark = 0;
349 foreach my $i ( 0 .. $#line_parts ) {
350 $i = $#line_parts - $i if $opt_vertical;
351 my $line = $line_parts[$i];
355 # not a minimal two column pin description
356 if ( ! exists $line->[1] ) {
357 $last_cut_mark = 1 if $line->[0] =~ m/^##/; # skip comments
359 # before first empty line
360 if ( $last_cut_mark == 0 ) {
364 line $x, $y, $max_x if $opt_lines;
365 $y += 15; # make spacing between pinouts
367 } elsif ( $last_cut_mark ) {
373 #warn "CUTMARK no magic";
376 line $x, $y, $max_x if $opt_lines && exists $line->[1];
378 my ($fg,$bg) = @{ $cols->{txt} };
379 my $tspan = qq{<tspan x="$x" y="$y" style="line-height:2.54;fill:$fg;stroke:none;">\n};
382 foreach my $i ( 0 .. $#cols_order ) {
383 my $order = $cols_order[$i];
384 next unless $line->[$order];
386 my $text_anchor = 'middle';
387 my $len = $max_len[$i];
388 my $x2 = $x_pos + ( $len * $font_w ) / 2;
390 if ( $#$line == 0 && $line->[$order] =~ s/^#+s*// ) {
391 # comment, center over whole width
392 $len = length($line->[$order]);
393 $x2 = $x + (($max_x-$x)/2); # middle
394 $tspan .= qq{\t<tspan x="$x2" text-anchor="$text_anchor"}.sprintf( '>%' . $cols_align[$i] . $len . 's</tspan>', $line->[0]);
396 $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]);
398 $x_pos += $len * $font_w;
401 $tspan .= qq{\n</tspan>\n};
402 push @later,sprintf $tspan, @$line;
405 # swap pin colors for line stripe
407 swap_cols $_ foreach qw( pins txt );
414 if ( $#$line == 0 ) {
415 print $line->[0], "\n";
417 push @$line, '' while ($#$line < 3); # fill-in single row header
418 printf $fmt, map { $line->[$_] } @cols_order;
427 line $x, $y, $max_x if $opt_lines;
434 style="font-size:2.34px;line-height:2.54px;font-family:'Andale Mono';stroke:none"
435 xml:space="preserve">
439 print @later, qq{</text>\n}, @cut_marks, qq{</g>\n</svg>};
444 # Cubietech Cubieboard
445 ## U14 (Next to SATA connector)
447 48 PI13 (SPI0-MISO/UART6-RX/EINT25) 47 PI11 (SPI0-CLK/UART5-RX/EINT23)
448 46 PI12 (SPI0-MOSI/UART6-TX/EINT24) 45 PI10 (SPI0-CS/UART5-TX/EINT22)
450 44 3.3V (nc in 2012-08-08) 43 VCC-5V
452 40 PB10 (LCD0-SCK/LCD-PIO1) 39 PB11 (LCD0-SDA/LCD-PIO2)
453 38 Ground 37 PH7 (LCD0-BL-EN/LCD-PIO0/UART5-RX/EINT7)
454 36 XN_TP (TP-X2) 35 YN_TP (TP-Y2)
455 34 XP_TP (TP-X1) 33 YP_TP (TP-Y1)
456 32 PD25 (LCDDE) 31 PB2 (PWM0)
457 30 PD26 (LCDHSYNC/VGA-HSYNC) 29 PD24 (LCDCLK)
458 28 PD23 (LCDD23) 27 PD27 (LCDVSYNC/VGA-VSYNC)
459 26 PD21 (LCDD21) 25 PD22 (LCDD22)
460 24 PD19 (LCDD19/LVDS1N3) 23 PD20 (LCDD20)
461 22 PD17 (LCDD17/LVDS1NC) 21 PD18 (LCDD18/LVDS1P3)
462 20 Ground 19 PD16 (LCDD16/LVDS1PC)
463 18 PD14 (LCDD14/LVDS1P2) 17 PD15 (LCDD15/LVDS1N2)
464 16 PD12 (LCDD12/LVDS1P1) 15 PD13 (LCDD13/LVDS1N1)
465 14 PD10 (LCDD10/LVDS1P0) 13 PD11 (LCDD11/LVDS1N0)
466 12 PD8 (LCDD8/LVDS0P3) 11 PD9 (LCDD9/LVDS0N3)
467 10 PD7 (LCDD7/LVDS0NC) 9 Ground
468 8 PD5 (LCDD5/LVDS0N2) 7 PD6 (LCDD6/LVDS0PC)
469 6 PD3 (LCDD3/LVDS0N1) 5 PD4 (LCDD4/LNVS0P2)
470 4 PD1 (LCDD1/LVDS0N0) 3 PD2 (LCDD2/LVDS0P1)
471 2 Ground 1 PD0 (LCDD0/LVDSP0)
473 ## U15 (Between Ethernet port and USB ports)
475 1 VCC-5V 2 PH15 (CSI1-PWR/EINT15)
476 3 CSI1-IO-2V8 4 PH14 (CSI1-RST#/EINT14)
477 5 PG0 (CSI1-PCLK/SDC1-CMD) 6 PB18 (TWI1-SCK)
478 7 PB19 (TWI1-SDA) 8 PG3 (CSI1-VSYNC/SDC1-D1)
479 9 PG2 (CSI1-HSYNC/SDC1-D0) 10 PG1 (CSI1-MCLK/SDC1-CLK)
480 11 PG4 (CSI1-D0/SDC1-D2) 12 PG5 (CSI1-D1/SDC1-D3)
481 13 PG6 (CSI1-D2/UART3-TX) 14 PG7 (CSI1-D3/UART3-RX)
482 15 PG8 (CSI1-D4/UART3-RTS) 16 PG9 (CSI1-D5/UART3-CTS)
483 17 PG10 (CSI1-D6/UART4-TX) 18 PG11 (CSI1-D7/UART4-RX)
486 21 FMINL 22 PI4 (SDC3-CMD)
487 23 FMINR 24 PI5 (SDC3-CLK)
488 25 Ground 26 PI6 (SDC3-D0)
489 27 VGA-R 28 PI7 (SDC3-D1)
490 29 VGA-G 30 PI8 (SDC3-D2)
491 31 VGA-B 32 PI9 (SDC3-D3)
493 33 LCD1-VSYNC 34 PE4 (CSI0-D0)
494 35 LCD1-HSYNC 36 PE5 (CSI0-D1)
495 37 Ground 38 PE6 (CSI0-D2)
496 39 AVCC 40 PE7 (CSI0-D3)
497 41 LRADC0 42 PE8 (CSI0-D4)
498 43 CVBS 44 PE9 (CSI0-D5)
499 45 HPL 46 PE10 (CSI0-D6)
500 47 HPR 48 PE11 (CSI0-D7)
502 ## DEBUG serial (middle of board)
510 ## CON3 rpi DIP26-254
514 7 PI3 PWM1 8 PH0 UART3_TX
516 11 PI19 UART2_RX 12 PH2
517 13 PI18 UART2_TX 14 0v
518 15 PI17 UART2_CTS 16 PH21 CAN_RX
519 17 3.3v 18 PH20 CAN_TX
520 19 PI12 SPI0_MOSI 20 0v
521 21 PI13 SPI0_MISO 22 PI16 UART2_RTS
522 23 PI11 SPI0_SCLK 24 PI10 SPI0_CS0
523 25 0v 26 PI14 SPI0_CS1
531 6 PI20 UART7_TX 5 PH3
532 4 PI21 UART7_RX 3 PH5
539 7 gpio4 (WPi 7) 8 gpio14 (TxD)
541 11 gpio17 (WPi 0) 12 gpio18 (WPi 1)
542 13 gpio27 (WPi 2) 14 0v
543 15 gpio22 (WPi 3) 16 gpio23 (WPi 4)
544 17 3.3v 18 gpio24 (WPi 5)
545 19 gpio10 (MOSI) 20 0v
546 21 gpio9 (MISO) 22 gpio25 (WPi 6)
547 23 gpio11 (SCLK) 24 gpio8 (CE0)
549 # Raspberry Pi 3 Model B Rev 1.2
550 27 gpio0 (SDA.0) 28 gpio1 (SCL.0)
551 29 gpio5 (WPi 21) 30 0v
552 31 gpio6 (WPi 22) 32 gpio12 (WPi 26)
553 33 gpio13 (WPi 23) 34 0v
554 35 gpio19 (WPi 24) 36 gpio16 (WPi 27)
555 37 gpio26 (WPi 25) 38 gpio20 (WPi 28)
556 39 0v 40 gpio21 (WPi 29)