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);
111 my ($pin, $note) = @_;
112 if ( $pins->{$pin} ) {
113 foreach my $line ( @{$pins->{$pin}} ) {
114 my $t = $lines[$line];
118 $t =~ s/$pin/$pin $note/ || warn "can't find $pin in [$t]";
121 warn "# $line: $lines[$line]\n";
124 warn "IGNORED: pin $pin function $function\n";
128 open(my $fh, '<', $opt_read . '/sys/kernel/debug/pinctrl/pinctrl-maps');
131 if ( m/^device (\S+)/ ) {
133 if ( my $replace = $serial_tty->{$device} ) {
134 $device = $replace; # replace serial hex with kernel name
136 $device =~ s/^[0-9a-f]*\.//; # remove hex address
138 } elsif ( m/^group (\w+\d+)/ ) {
141 } elsif ( m/^function (\S+)/ ) {
144 if ( $device && $pin && $function ) {
145 push @{ $pin_function->{$pin} }, "$device $function";
147 annotate_pin $pin, "[$device $function]";
149 warn "missing one of ",dump( $device, $pin, $function );
159 warn "# pin_function = ",dump($pin_function);
161 open(my $pio, '-|', 'sunxi-pio -m print');
165 my @p = split(/\s+/,$_);
166 # annotate input 0 and output 1 pins
167 annotate_pin $p[0], ( $p[1] ? 'O' : 'I' ) . ':' . $p[4] if $p[1] == 0 || $p[1] == 1;
171 my @max_len = ( 0,0,0,0 );
174 shift(@lines) while ( ! $lines[0] ); # remove empty at beginning
175 pop(@lines) while ( ! $lines[-1] ); # remove empty at end
177 foreach my $line (@lines) {
178 if ( $line =~ m/^#/ ) {
179 push @line_parts, [ $line ] unless $opt_svg && $line =~ m/^###+/; # SVG doesn't display 3rd level comments
182 $line =~ s/\[(\w+)\s+(\w+)\] \[\1\s+(\w+)\]/[$1 $2 $3]/g; # compress kernel annotation with same prefix
183 $line =~ s/(\[(?:uart\d*|serial|tty\w+))([^\t]*\]\s[^\t]*(rx|tx)d?)/$1 $3$2/gi;
184 $line =~ s/(\[i2c)([^\t]*\]\s[^\t]*(scl?k?|sda))/$1 $3$2/gi;
185 $line =~ s/(\[spi)([^\t]*\]\s[^\t]*(miso|mosi|s?clk|c[se]\d*))/$1 $3$2/gi;
186 $line =~ s/\s*\([^\)]+\)//g if ! $opt_alt;
188 # shorten duplicate kernel device/function
189 $line =~ s/\[serial (\w+) (uart\d+)\]/[$2 $1]/g;
190 $line =~ s/\[(\w+) (\w+) \1(\d+)\]/[$1$3 $2]/g;
192 $line =~ s/\[(\w+)\s+([^\]]+)\s+\1\]/[$1 $2]/g; # duplicate
194 my @v = split(/\s*\t+\s*/,$line,4);
195 @v = ( $v[2], $v[3], $v[0], $v[1] ) if $opt_horizontal && $v[2];
197 push @line_parts, [ @v ];
198 foreach my $i ( 0 .. 3 ) {
199 next unless exists $v[$i];
200 next if $v[$i] =~ m/^#/; # don't calculate comments into max length
201 my $l = length($v[$i]);
202 $max_len[$i] = $l if $l > $max_len[$i];
206 warn "# max_len = ",dump( \@max_len );
207 warn "# line_parts = ",dump( \@line_parts );
209 #print "$_\n" foreach @lines;
215 print qq{<?xml version="1.0" encoding="UTF-8" standalone="no"?>
217 xmlns:dc="http://purl.org/dc/elements/1.1/"
218 xmlns:cc="http://creativecommons.org/ns#"
219 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
220 xmlns:svg="http://www.w3.org/2000/svg"
221 xmlns="http://www.w3.org/2000/svg"
222 xmlns:xlink="http://www.w3.org/1999/xlink"
225 viewBox="0 0 210 297"
232 }; # svg, insert rest of rect
234 print qq{<rect x="0" y="0" width="210" height="297" style="fill:#000000" id="high-contrast"/>} if $opt_invert;
239 my $cols = { # foreground background
240 txt => [ '#000000', '#ffffff' ],
241 pins => [ '#ffffff', '#ff00ff' ],
242 vcc => [ '#ff0000', '#ffff00' ],
243 gnd => [ '#000000', '#00ffff' ],
244 i2c => [ '#008800', '#ffcccc' ],
245 serial=>[ '#000088', '#ccffcc' ],
246 spi => [ '#880000', '#ccccff' ],
251 die "$swap not found in ",dump($cols) unless $cols->{$swap};
252 my ( $c1, $c2 ) = @{ $cols->{$swap} };
253 $cols->{$swap} = [ $c2, $c1 ];
256 swap_cols 'txt' if $opt_invert;
260 my ($name,$x,$y,$col) = @_;
262 return '' unless $opt_color;
264 $y -= $font_b; # shift box overlay to right vertical position based on font baseline
267 my ($x,$y,$col,$fill) = @_;
268 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};
272 if ( $name =~ m/^(\d+)$/ ) { # pins
274 my ( $fg, $bg ) = @{ $cols->{pins} };
276 my $w = $max_len[$col]*$font_w - 0.1;
279 #print qq{<polygon points="$x,$y $cx,$y $x,$cy $x,$y" stroke="$fg" stroke-width="0.25" fill="$bg" />};
280 #print qq{<polygon points="$x,$cy $cx,$cy $cx,$y $x,$cy" stroke="$bg" stroke-width="0.25" fill="$fg" />};
281 print qq{<rect x="$x" y="$y" width="$w" height="2.54" stroke="$fg" stroke-width="0.3" fill="$bg" />};
282 my ( $fg, $bg ) = @{ $cols->{txt} };
283 print qq{<rect x="$x" y="$y" width="$w" height="2.54" rx="1" ry="1" stroke="$fg" stroke-width="0.3" fill="$bg" />};
287 return qq{ style="fill:$bg"};
290 if ( $name =~ m/(VCC|3V3|3.3V|5v)/i ) {
291 my ($fg,$bg) = @{ $cols->{vcc} };
293 return qq{ style="fill:$fg"};
294 } elsif ( $name =~ m/(G(ND|Round)|VSS|0v)/i ) {
295 my ($fg,$bg) = @{ $cols->{gnd} };
297 return qq{ style="fill:$fg"};
298 } elsif ( $name =~ m/\[(\w+)/ ) { # kernel
300 my ($fg,$bg) = @{ $cols->{txt} };
301 $dev = 'serial' if $dev =~ m/^tty/;
302 ($fg,$bg) = @{ $cols->{$dev} } if exists $cols->{$dev};
304 return qq{ style="fill:$fg"};
306 my ( $fg, $bg ) = @{ $cols->{txt} };
308 #return qq{ style="fill:$fg"};
315 my @cols_order = ( 0,1,2,3 );
316 my @cols_align = ( '','-','','-' ); # sprintf prefix
318 my @cols_shuffle = @cols_order;
321 # pins outside on the right
322 @cols_shuffle = ( 0,1,3,2 ) if $opt_edge;
323 @cols_align = ( '-','-','','' );
324 } elsif ( $opt_middle ) {
326 @cols_shuffle = ( 1,0,2,3 );
327 @cols_align = ( '','','-','-' );
331 my ( $what, $order ) = @_;
333 foreach my $i ( 0 .. $#$what ) {
334 $new->[$i] = $what->[ $order->[$i] ];
336 warn "# cols_shuffle what=",dump($what)," order=",dump($order)," new=",dump($new);
340 @cols_order = cols_shuffle( \@cols_order, \@cols_shuffle );
341 @max_len = cols_shuffle( \@max_len, \@cols_shuffle );
343 warn "# cols_order = ",dump( \@cols_order );
344 warn "# cols_align = ",dump( \@cols_align );
346 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";
350 my ($fg,$bg) = @{ $cols->{txt} };
351 my $line_fmt = qq{<line x1="%s" y1="%s" x2="%s" y2="%s" style="stroke:$fg;stroke-width:0.10;fill:$bg" />\n};
356 return unless $opt_svg;
357 push @cut_marks, sprintf($line_fmt, $x-5, $y-$font_b, $x+5, $y-$font_b);
358 push @cut_marks, sprintf($line_fmt, $x, $y-$font_b-5, $x, $y-$font_b+5);
362 $max_x += $max_len[$_] * $font_w foreach ( 0 .. 3 );
363 #cut_mark $max_x, $y;
366 my ($x,$y,$max_x) = @_;
367 push @cut_marks, sprintf($line_fmt, $x, $y-$font_b, $max_x, $y-$font_b);
371 my $last_cut_mark = 0;
373 foreach my $i ( 0 .. $#line_parts ) {
374 $i = $#line_parts - $i if $opt_vertical;
375 my $line = $line_parts[$i];
379 # not a minimal two column pin description
380 if ( ! exists $line->[1] ) {
381 $last_cut_mark = 1 if $line->[0] =~ m/^##/; # skip comments
383 # before first empty line
384 if ( $last_cut_mark == 0 ) {
388 line $x, $y, $max_x if $opt_lines;
389 $y += 15; # make spacing between pinouts
391 } elsif ( $last_cut_mark ) {
397 #warn "CUTMARK no magic";
400 line $x, $y, $max_x if $opt_lines && exists $line->[1];
402 my ($fg,$bg) = @{ $cols->{txt} };
403 my $tspan = qq{<tspan x="$x" y="$y" style="line-height:2.54;fill:$fg;stroke:none;">\n};
406 foreach my $i ( 0 .. $#cols_order ) {
407 my $order = $cols_order[$i];
408 next unless $line->[$order];
410 my $text_anchor = 'middle';
411 my $len = $max_len[$i];
412 my $x2 = $x_pos + ( $len * $font_w ) / 2;
414 if ( $#$line == 0 && $line->[$order] =~ s/^#+s*// ) {
415 # comment, center over whole width
416 $len = length($line->[$order]);
417 $x2 = $x + (($max_x-$x)/2); # middle
418 $tspan .= qq{\t<tspan x="$x2" text-anchor="$text_anchor"}.sprintf( '>%' . $cols_align[$i] . $len . 's</tspan>', $line->[0]);
420 $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]);
422 $x_pos += $len * $font_w;
425 $tspan .= qq{\n</tspan>\n};
426 push @later,sprintf $tspan, @$line;
429 # swap pin colors for line stripe
431 swap_cols $_ foreach qw( pins txt );
438 if ( $#$line == 0 ) {
439 print $line->[0], "\n";
441 push @$line, '' while ($#$line < 3); # fill-in single row header
442 printf $fmt, map { $line->[$_] } @cols_order;
451 line $x, $y, $max_x if $opt_lines;
458 style="font-size:2.34px;line-height:2.54px;font-family:'Andale Mono';stroke:none"
459 xml:space="preserve">
463 print @later, qq{</text>\n}, @cut_marks, qq{</g>\n</svg>};
468 # Cubietech Cubieboard
469 ## U14 (Next to SATA connector)
471 48 PI13 (SPI0-MISO/UART6-RX/EINT25) 47 PI11 (SPI0-CLK/UART5-RX/EINT23)
472 46 PI12 (SPI0-MOSI/UART6-TX/EINT24) 45 PI10 (SPI0-CS/UART5-TX/EINT22)
474 44 3.3V (nc in 2012-08-08) 43 VCC-5V
476 40 PB10 (LCD0-SCK/LCD-PIO1) 39 PB11 (LCD0-SDA/LCD-PIO2)
477 38 Ground 37 PH7 (LCD0-BL-EN/LCD-PIO0/UART5-RX/EINT7)
478 36 XN_TP (TP-X2) 35 YN_TP (TP-Y2)
479 34 XP_TP (TP-X1) 33 YP_TP (TP-Y1)
480 32 PD25 (LCDDE) 31 PB2 (PWM0)
481 30 PD26 (LCDHSYNC/VGA-HSYNC) 29 PD24 (LCDCLK)
482 28 PD23 (LCDD23) 27 PD27 (LCDVSYNC/VGA-VSYNC)
483 26 PD21 (LCDD21) 25 PD22 (LCDD22)
484 24 PD19 (LCDD19/LVDS1N3) 23 PD20 (LCDD20)
485 22 PD17 (LCDD17/LVDS1NC) 21 PD18 (LCDD18/LVDS1P3)
486 20 Ground 19 PD16 (LCDD16/LVDS1PC)
487 18 PD14 (LCDD14/LVDS1P2) 17 PD15 (LCDD15/LVDS1N2)
488 16 PD12 (LCDD12/LVDS1P1) 15 PD13 (LCDD13/LVDS1N1)
489 14 PD10 (LCDD10/LVDS1P0) 13 PD11 (LCDD11/LVDS1N0)
490 12 PD8 (LCDD8/LVDS0P3) 11 PD9 (LCDD9/LVDS0N3)
491 10 PD7 (LCDD7/LVDS0NC) 9 Ground
492 8 PD5 (LCDD5/LVDS0N2) 7 PD6 (LCDD6/LVDS0PC)
493 6 PD3 (LCDD3/LVDS0N1) 5 PD4 (LCDD4/LNVS0P2)
494 4 PD1 (LCDD1/LVDS0N0) 3 PD2 (LCDD2/LVDS0P1)
495 2 Ground 1 PD0 (LCDD0/LVDSP0)
497 ## U15 (Between Ethernet port and USB ports)
499 1 VCC-5V 2 PH15 (CSI1-PWR/EINT15)
500 3 CSI1-IO-2V8 4 PH14 (CSI1-RST#/EINT14)
501 5 PG0 (CSI1-PCLK/SDC1-CMD) 6 PB18 (TWI1-SCK)
502 7 PB19 (TWI1-SDA) 8 PG3 (CSI1-VSYNC/SDC1-D1)
503 9 PG2 (CSI1-HSYNC/SDC1-D0) 10 PG1 (CSI1-MCLK/SDC1-CLK)
504 11 PG4 (CSI1-D0/SDC1-D2) 12 PG5 (CSI1-D1/SDC1-D3)
505 13 PG6 (CSI1-D2/UART3-TX) 14 PG7 (CSI1-D3/UART3-RX)
506 15 PG8 (CSI1-D4/UART3-RTS) 16 PG9 (CSI1-D5/UART3-CTS)
507 17 PG10 (CSI1-D6/UART4-TX) 18 PG11 (CSI1-D7/UART4-RX)
510 21 FMINL 22 PI4 (SDC3-CMD)
511 23 FMINR 24 PI5 (SDC3-CLK)
512 25 Ground 26 PI6 (SDC3-D0)
513 27 VGA-R 28 PI7 (SDC3-D1)
514 29 VGA-G 30 PI8 (SDC3-D2)
515 31 VGA-B 32 PI9 (SDC3-D3)
517 33 LCD1-VSYNC 34 PE4 (CSI0-D0)
518 35 LCD1-HSYNC 36 PE5 (CSI0-D1)
519 37 Ground 38 PE6 (CSI0-D2)
520 39 AVCC 40 PE7 (CSI0-D3)
521 41 LRADC0 42 PE8 (CSI0-D4)
522 43 CVBS 44 PE9 (CSI0-D5)
523 45 HPL 46 PE10 (CSI0-D6)
524 47 HPR 48 PE11 (CSI0-D7)
526 ## DEBUG serial (middle of board)
534 ## CON3 rpi DIP26-254
538 7 PI3 PWM1 8 PH0 UART3_TX
540 11 PI19 UART2_RX 12 PH2
541 13 PI18 UART2_TX 14 0v
542 15 PI17 UART2_CTS 16 PH21 CAN_RX
543 17 3.3v 18 PH20 CAN_TX
544 19 PI12 SPI0_MOSI 20 0v
545 21 PI13 SPI0_MISO 22 PI16 UART2_RTS
546 23 PI11 SPI0_SCLK 24 PI10 SPI0_CS0
547 25 0v 26 PI14 SPI0_CS1
555 6 PI20 UART7_TX 5 PH3
556 4 PI21 UART7_RX 3 PH5
563 7 gpio4 (WPi 7) 8 gpio14 (TxD)
565 11 gpio17 (WPi 0) 12 gpio18 (WPi 1)
566 13 gpio27 (WPi 2) 14 0v
567 15 gpio22 (WPi 3) 16 gpio23 (WPi 4)
568 17 3.3v 18 gpio24 (WPi 5)
569 19 gpio10 (MOSI) 20 0v
570 21 gpio9 (MISO) 22 gpio25 (WPi 6)
571 23 gpio11 (SCLK) 24 gpio8 (CE0)
573 # Raspberry Pi 3 Model B Rev 1.2
574 27 gpio0 (SDA.0) 28 gpio1 (SCL.0)
575 29 gpio5 (WPi 21) 30 0v
576 31 gpio6 (WPi 22) 32 gpio12 (WPi 26)
577 33 gpio13 (WPi 23) 34 0v
578 35 gpio19 (WPi 24) 36 gpio16 (WPi 27)
579 37 gpio26 (WPi 25) 38 gpio20 (WPi 28)
580 39 0v 40 gpio21 (WPi 29)