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);
85 foreach ( glob $opt_read . '/sys//devices/platform/soc*/*.serial/tty/tty*' ) {
86 my @v = split(/\//, $_);
87 $serial_tty->{ $v[-3] } = $v[-1];
89 warn "# serial_tty = ",dump($serial_tty);
95 open(my $fh, '<', $opt_read . '/sys/kernel/debug/pinctrl/pinctrl-handles');
98 if ( m/device: (\S+)/ ) {
100 if ( my $replace = $serial_tty->{$device} ) {
101 $device = $replace; # replace serial hex with kernel name
103 $device =~ s/^[0-9a-f]*\.//; # remove hex address
105 } elsif ( m/group: (\w+\d+)\s.+function: (\S+)/ ) {
106 my ($pin, $function) = ($1,$2);
107 $pin_function->{$pin} = "$device $function";
109 if ( $pins->{$pin} ) {
110 foreach my $line ( @{$pins->{$pin}} ) {
111 my $t = $lines[$line];
113 $t =~ s/$pin/[$device $function]/;
115 $t =~ s/$pin/$pin [$device $function]/ || warn "can't find $pin in [$t]";
118 warn "# $line: $lines[$line]\n";
121 warn "IGNORED: pin $pin function $function\n";
126 warn "# pin_function = ",dump($pin_function);
128 my @max_len = ( 0,0,0,0 );
131 shift(@lines) while ( ! $lines[0] ); # remove empty at beginning
132 pop(@lines) while ( ! $lines[-1] ); # remove empty at end
134 foreach my $line (@lines) {
135 if ( $line =~ m/^#/ ) {
136 push @line_parts, [ $line ] unless $opt_svg && $line =~ m/^###+/; # SVG doesn't display 3rd level comments
139 $line =~ s/(\[(?:uart|serial|tty\w+))([^\t]*\]\s[^\t]*(rx|tx)d?)/$1 $3$2/gi;
140 $line =~ s/(\[i2c)([^\t]*\]\s[^\t]*(scl?k?|sda))/$1 $3$2/gi;
141 $line =~ s/(\[spi)([^\t]*\]\s[^\t]*(miso|mosi|s?clk|c[se]\d*))/$1 $3$2/gi;
142 $line =~ s/\s*\([^\)]+\)//g if ! $opt_alt;
144 # shorten duplicate kernel device/function
145 $line =~ s/\[serial (\w+) (uart\d+)\]/[$2 $1]/g;
146 $line =~ s/\[(\w+) (\w+) \1(\d+)\]/[$1$3 $2]/g;
148 my @v = split(/\s*\t+\s*/,$line,4);
149 @v = ( $v[2], $v[3], $v[0], $v[1] ) if $opt_horizontal && $v[2];
151 push @line_parts, [ @v ];
152 foreach my $i ( 0 .. 3 ) {
153 next unless exists $v[$i];
154 next if $v[$i] =~ m/^#/; # don't calculate comments into max length
155 my $l = length($v[$i]);
156 $max_len[$i] = $l if $l > $max_len[$i];
160 warn "# max_len = ",dump( \@max_len );
161 warn "# line_parts = ",dump( \@line_parts );
163 #print "$_\n" foreach @lines;
169 print qq{<?xml version="1.0" encoding="UTF-8" standalone="no"?>
171 xmlns:dc="http://purl.org/dc/elements/1.1/"
172 xmlns:cc="http://creativecommons.org/ns#"
173 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
174 xmlns:svg="http://www.w3.org/2000/svg"
175 xmlns="http://www.w3.org/2000/svg"
176 xmlns:xlink="http://www.w3.org/1999/xlink"
179 viewBox="0 0 210 297"
186 }; # svg, insert rest of rect
188 print qq{<rect x="0" y="0" width="210" height="297" style="fill:#000000" id="high-contrast"/>} if $opt_invert;
193 my $cols = { # foreground background
194 txt => [ '#000000', '#ffffff' ],
195 pins => [ '#ffffff', '#ff00ff' ],
196 vcc => [ '#ff0000', '#ffff00' ],
197 gnd => [ '#000000', '#00ffff' ],
198 i2c => [ '#008800', '#ffcccc' ],
199 serial=>[ '#000088', '#ccffcc' ],
200 spi => [ '#880000', '#ccccff' ],
205 die "$swap not found in ",dump($cols) unless $cols->{$swap};
206 my ( $c1, $c2 ) = @{ $cols->{$swap} };
207 $cols->{$swap} = [ $c2, $c1 ];
210 swap_cols 'txt' if $opt_invert;
214 my ($name,$x,$y,$col) = @_;
216 return '' unless $opt_color;
218 $y -= $font_b; # shift box overlay to right vertical position based on font baseline
221 my ($x,$y,$col,$fill) = @_;
222 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};
226 if ( $name =~ m/^(\d+)$/ ) { # pins
228 my ( $fg, $bg ) = @{ $cols->{pins} };
230 my $w = $max_len[$col]*$font_w - 0.1;
233 #print qq{<polygon points="$x,$y $cx,$y $x,$cy $x,$y" stroke="$fg" stroke-width="0.25" fill="$bg" />};
234 #print qq{<polygon points="$x,$cy $cx,$cy $cx,$y $x,$cy" stroke="$bg" stroke-width="0.25" fill="$fg" />};
235 print qq{<rect x="$x" y="$y" width="$w" height="2.54" stroke="$fg" stroke-width="0.3" fill="$bg" />};
236 my ( $fg, $bg ) = @{ $cols->{txt} };
237 print qq{<rect x="$x" y="$y" width="$w" height="2.54" rx="1" ry="1" stroke="$fg" stroke-width="0.3" fill="$bg" />};
241 return qq{ style="fill:$bg"};
244 if ( $name =~ m/(VCC|3V3|3.3V|5v)/i ) {
245 my ($fg,$bg) = @{ $cols->{vcc} };
247 return qq{ style="fill:$fg"};
248 } elsif ( $name =~ m/(G(ND|Round)|VSS|0v)/i ) {
249 my ($fg,$bg) = @{ $cols->{gnd} };
251 return qq{ style="fill:$fg"};
252 } elsif ( $name =~ m/\[(\w+)/ ) { # kernel
254 my ($fg,$bg) = @{ $cols->{txt} };
255 $dev = 'serial' if $dev =~ m/^tty/;
256 ($fg,$bg) = @{ $cols->{$dev} } if exists $cols->{$dev};
258 return qq{ style="fill:$fg"};
260 my ( $fg, $bg ) = @{ $cols->{txt} };
262 #return qq{ style="fill:$fg"};
269 my @cols_order = ( 0,1,2,3 );
270 my @cols_align = ( '','-','','-' ); # sprintf prefix
272 my @cols_shuffle = @cols_order;
275 # pins outside on the right
276 @cols_shuffle = ( 0,1,3,2 ) if $opt_edge;
277 @cols_align = ( '-','-','','' );
278 } elsif ( $opt_middle ) {
280 @cols_shuffle = ( 1,0,2,3 );
281 @cols_align = ( '','','-','-' );
285 my ( $what, $order ) = @_;
287 foreach my $i ( 0 .. $#$what ) {
288 $new->[$i] = $what->[ $order->[$i] ];
290 warn "# cols_shuffle what=",dump($what)," order=",dump($order)," new=",dump($new);
294 @cols_order = cols_shuffle( \@cols_order, \@cols_shuffle );
295 @max_len = cols_shuffle( \@max_len, \@cols_shuffle );
297 warn "# cols_order = ",dump( \@cols_order );
298 warn "# cols_align = ",dump( \@cols_align );
300 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";
304 my ($fg,$bg) = @{ $cols->{txt} };
305 my $line_fmt = qq{<line x1="%s" y1="%s" x2="%s" y2="%s" style="stroke:$fg;stroke-width:0.10;fill:$bg" />\n};
310 return unless $opt_svg;
311 push @cut_marks, sprintf($line_fmt, $x-5, $y-$font_b, $x+5, $y-$font_b);
312 push @cut_marks, sprintf($line_fmt, $x, $y-$font_b-5, $x, $y-$font_b+5);
316 $max_x += $max_len[$_] * $font_w foreach ( 0 .. 3 );
317 #cut_mark $max_x, $y;
320 my ($x,$y,$max_x) = @_;
321 push @cut_marks, sprintf($line_fmt, $x, $y-$font_b, $max_x, $y-$font_b);
325 my $last_cut_mark = 0;
327 foreach my $i ( 0 .. $#line_parts ) {
328 $i = $#line_parts - $i if $opt_vertical;
329 my $line = $line_parts[$i];
333 # not a minimal two column pin description
334 if ( ! exists $line->[1] ) {
335 $last_cut_mark = 1 if $line->[0] =~ m/^##/; # skip comments
337 # before first empty line
338 if ( $last_cut_mark == 0 ) {
342 line $x, $y, $max_x if $opt_lines;
343 $y += 15; # make spacing between pinouts
345 } elsif ( $last_cut_mark ) {
351 #warn "CUTMARK no magic";
354 line $x, $y, $max_x if $opt_lines && exists $line->[1];
356 my ($fg,$bg) = @{ $cols->{txt} };
357 my $tspan = qq{<tspan x="$x" y="$y" style="line-height:2.54;fill:$fg;stroke:none;">\n};
360 foreach my $i ( 0 .. $#cols_order ) {
361 my $order = $cols_order[$i];
362 next unless $line->[$order];
364 my $text_anchor = 'middle';
365 my $len = $max_len[$i];
366 my $x2 = $x_pos + ( $len * $font_w ) / 2;
368 if ( $#$line == 0 && $line->[$order] =~ s/^#+s*// ) {
369 # comment, center over whole width
370 $len = length($line->[$order]);
371 $x2 = $x + (($max_x-$x)/2); # middle
372 $tspan .= qq{\t<tspan x="$x2" text-anchor="$text_anchor"}.sprintf( '>%' . $cols_align[$i] . $len . 's</tspan>', $line->[0]);
374 $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]);
376 $x_pos += $len * $font_w;
379 $tspan .= qq{\n</tspan>\n};
380 push @later,sprintf $tspan, @$line;
383 # swap pin colors for line stripe
385 swap_cols $_ foreach qw( pins txt );
392 if ( $#$line == 0 ) {
393 print $line->[0], "\n";
395 push @$line, '' while ($#$line < 3); # fill-in single row header
396 printf $fmt, map { $line->[$_] } @cols_order;
405 line $x, $y, $max_x if $opt_lines;
412 style="font-size:2.34px;line-height:2.54px;font-family:'Andale Mono';stroke:none"
413 xml:space="preserve">
417 print @later, qq{</text>\n}, @cut_marks, qq{</g>\n</svg>};
422 # Cubietech Cubieboard
423 ## U14 (Next to SATA connector)
425 48 PI13 (SPI0-MISO/UART6-RX/EINT25) 47 PI11 (SPI0-CLK/UART5-RX/EINT23)
426 46 PI12 (SPI0-MOSI/UART6-TX/EINT24) 45 PI10 (SPI0-CS/UART5-TX/EINT22)
428 44 3.3V (nc in 2012-08-08) 43 VCC-5V
430 40 PB10 (LCD0-SCK/LCD-PIO1) 39 PB11 (LCD0-SDA/LCD-PIO2)
431 38 Ground 37 PH7 (LCD0-BL-EN/LCD-PIO0/UART5-RX/EINT7)
432 36 XN_TP (TP-X2) 35 YN_TP (TP-Y2)
433 34 XP_TP (TP-X1) 33 YP_TP (TP-Y1)
434 32 PD25 (LCDDE) 31 PB2 (PWM0)
435 30 PD26 (LCDHSYNC/VGA-HSYNC) 29 PD24 (LCDCLK)
436 28 PD23 (LCDD23) 27 PD27 (LCDVSYNC/VGA-VSYNC)
437 26 PD21 (LCDD21) 25 PD22 (LCDD22)
438 24 PD19 (LCDD19/LVDS1N3) 23 PD20 (LCDD20)
439 22 PD17 (LCDD17/LVDS1NC) 21 PD18 (LCDD18/LVDS1P3)
440 20 Ground 19 PD16 (LCDD16/LVDS1PC)
441 18 PD14 (LCDD14/LVDS1P2) 17 PD15 (LCDD15/LVDS1N2)
442 16 PD12 (LCDD12/LVDS1P1) 15 PD13 (LCDD13/LVDS1N1)
443 14 PD10 (LCDD10/LVDS1P0) 13 PD11 (LCDD11/LVDS1N0)
444 12 PD8 (LCDD8/LVDS0P3) 11 PD9 (LCDD9/LVDS0N3)
445 10 PD7 (LCDD7/LVDS0NC) 9 Ground
446 8 PD5 (LCDD5/LVDS0N2) 7 PD6 (LCDD6/LVDS0PC)
447 6 PD3 (LCDD3/LVDS0N1) 5 PD4 (LCDD4/LNVS0P2)
448 4 PD1 (LCDD1/LVDS0N0) 3 PD2 (LCDD2/LVDS0P1)
449 2 Ground 1 PD0 (LCDD0/LVDSP0)
451 ## U15 (Between Ethernet port and USB ports)
453 1 VCC-5V 2 PH15 (CSI1-PWR/EINT15)
454 3 CSI1-IO-2V8 4 PH14 (CSI1-RST#/EINT14)
455 5 PG0 (CSI1-PCLK/SDC1-CMD) 6 PB18 (TWI1-SCK)
456 7 PB19 (TWI1-SDA) 8 PG3 (CSI1-VSYNC/SDC1-D1)
457 9 PG2 (CSI1-HSYNC/SDC1-D0) 10 PG1 (CSI1-MCLK/SDC1-CLK)
458 11 PG4 (CSI1-D0/SDC1-D2) 12 PG5 (CSI1-D1/SDC1-D3)
459 13 PG6 (CSI1-D2/UART3-TX) 14 PG7 (CSI1-D3/UART3-RX)
460 15 PG8 (CSI1-D4/UART3-RTS) 16 PG9 (CSI1-D5/UART3-CTS)
461 17 PG10 (CSI1-D6/UART4-TX) 18 PG11 (CSI1-D7/UART4-RX)
464 21 FMINL 22 PI4 (SDC3-CMD)
465 23 FMINR 24 PI5 (SDC3-CLK)
466 25 Ground 26 PI6 (SDC3-D0)
467 27 VGA-R 28 PI7 (SDC3-D1)
468 29 VGA-G 30 PI8 (SDC3-D2)
469 31 VGA-B 32 PI9 (SDC3-D3)
471 33 LCD1-VSYNC 34 PE4 (CSI0-D0)
472 35 LCD1-HSYNC 36 PE5 (CSI0-D1)
473 37 Ground 38 PE6 (CSI0-D2)
474 39 AVCC 40 PE7 (CSI0-D3)
475 41 LRADC0 42 PE8 (CSI0-D4)
476 43 CVBS 44 PE9 (CSI0-D5)
477 45 HPL 46 PE10 (CSI0-D6)
478 47 HPR 48 PE11 (CSI0-D7)
480 ## DEBUG serial (middle of board)
488 ## CON3 rpi DIP26-254
492 7 PI3 PWM1 8 PH0 UART3_TX
494 11 PI19 UART2_RX 12 PH2
495 13 PI18 UART2_TX 14 0v
496 15 PI17 UART2_CTS 16 PH21 CAN_RX
497 17 3.3v 18 PH20 CAN_TX
498 19 PI12 SPI0_MOSI 20 0v
499 21 PI13 SPI0_MISO 22 PI16 UART2_RTS
500 23 PI11 SPI0_SCLK 24 PI10 SPI0_CS0
501 25 0v 26 PI14 SPI0_CS1
509 6 PI20 UART7_TX 5 PH3
510 4 PI21 UART7_RX 3 PH5
517 7 gpio4 (WPi 7) 8 gpio14 (TxD)
519 11 gpio17 (WPi 0) 12 gpio18 (WPi 1)
520 13 gpio27 (WPi 2) 14 0v
521 15 gpio22 (WPi 3) 16 gpio23 (WPi 4)
522 17 3.3v 18 gpio24 (WPi 5)
523 19 gpio10 (MOSI) 20 0v
524 21 gpio9 (MISO) 22 gpio25 (WPi 6)
525 23 gpio11 (SCLK) 24 gpio8 (CE0)
527 # Raspberry Pi 3 Model B Rev 1.2
528 27 gpio0 (SDA.0) 28 gpio1 (SCL.0)
529 29 gpio5 (WPi 21) 30 0v
530 31 gpio6 (WPi 22) 32 gpio12 (WPi 26)
531 33 gpio13 (WPi 23) 34 0v
532 35 gpio19 (WPi 24) 36 gpio16 (WPi 27)
533 37 gpio26 (WPi 25) 38 gpio20 (WPi 28)
534 39 0v 40 gpio21 (WPi 29)