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);
83 open(my $fh, '<', $opt_read . '/sys/kernel/debug/pinctrl/pinctrl-handles');
86 if ( m/device: [0-9a-f]+\.(\w+)/ ) {
88 } elsif ( m/group: (\w+\d+)\s.+function: (\S+)/ ) {
89 my ($pin, $function) = ($1,$2);
90 $pin_function->{$pin} = "$device $function";
92 if ( $pins->{$pin} ) {
93 foreach my $line ( @{$pins->{$pin}} ) {
94 my $t = $lines[$line];
96 $t =~ s/$pin/[$device $function]/;
98 $t =~ s/$pin/$pin [$device $function]/ || die "can't find $pin in [$t]";
101 warn "# $line: $lines[$line]\n";
104 warn "IGNORED: pin $pin function $function\n";
109 warn "# pin_function = ",dump($pin_function);
111 my @max_len = ( 0,0,0,0 );
113 foreach my $line (@lines) {
114 if ( $line =~ m/^#/ ) {
115 push @line_parts, [ $line ] unless $opt_svg;
118 $line =~ s/(\[(?:uart|serial))([^\t]*\]\s[^\t]*(rx|tx)d?)/$1 $3$2/gi;
119 $line =~ s/(\[i2c)([^\t]*\]\s[^\t]*(scl?k?|sda))/$1 $3$2/gi;
120 $line =~ s/(\[spi)([^\t]*\]\s[^\t]*(miso|mosi|s?clk|c[se]\d*))/$1 $3$2/gi;
121 $line =~ s/\s*\([^\)]+\)//g if ! $opt_alt;
123 # shorten duplicate kernel device/function
124 $line =~ s/\[serial (\w+) (uart\d+)\]/[$2 $1]/g;
125 $line =~ s/\[(\w+) (\w+) \1(\d+)\]/[$1$3 $2]/g;
127 my @v = split(/\s*\t+\s*/,$line,4);
128 @v = ( $v[2], $v[3], $v[0], $v[1] ) if $opt_horizontal;
130 push @line_parts, [ @v ];
131 foreach my $i ( 0 .. 3 ) {
132 next unless exists $v[$i];
133 next if $v[$i] =~ m/^#/; # don't calculate comments into max length
134 my $l = length($v[$i]);
135 $max_len[$i] = $l if $l > $max_len[$i];
139 warn "# max_len = ",dump( \@max_len );
140 warn "# line_parts = ",dump( \@line_parts );
142 #print "$_\n" foreach @lines;
148 print qq{<?xml version="1.0" encoding="UTF-8" standalone="no"?>
150 xmlns:dc="http://purl.org/dc/elements/1.1/"
151 xmlns:cc="http://creativecommons.org/ns#"
152 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
153 xmlns:svg="http://www.w3.org/2000/svg"
154 xmlns="http://www.w3.org/2000/svg"
155 xmlns:xlink="http://www.w3.org/1999/xlink"
158 viewBox="0 0 210 297"
165 }; # svg, insert rest of rect
167 print qq{<rect x="0" y="0" width="210" height="297" style="fill:#000000" id="high-contrast"/>} if $opt_invert;
172 my $cols = { # foreground background
173 txt => [ '#000000', '#ffffff' ],
174 pins => [ '#ffffff', '#ff00ff' ],
175 vcc => [ '#ff0000', '#ffff00' ],
176 gnd => [ '#000000', '#00ffff' ],
177 i2c => [ '#008800', '#ffcccc' ],
178 serial=>[ '#000088', '#ccffcc' ],
179 spi => [ '#880000', '#ccccff' ],
184 die "$swap not found in ",dump($cols) unless $cols->{$swap};
185 my ( $c1, $c2 ) = @{ $cols->{$swap} };
186 $cols->{$swap} = [ $c2, $c1 ];
189 swap_cols 'txt' if $opt_invert;
193 my ($name,$x,$y,$col) = @_;
194 $y -= $font_b; # shift box overlay to right vertical position based on font baseline
197 my ($x,$y,$col,$fill) = @_;
198 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};
202 if ( $name =~ m/^(\d+)$/ ) { # pins
204 my ( $fg, $bg ) = @{ $cols->{pins} };
206 my $w = $max_len[$col]*$font_w - 0.1;
209 #print qq{<polygon points="$x,$y $cx,$y $x,$cy $x,$y" stroke="$fg" stroke-width="0.25" fill="$bg" />};
210 #print qq{<polygon points="$x,$cy $cx,$cy $cx,$y $x,$cy" stroke="$bg" stroke-width="0.25" fill="$fg" />};
211 print qq{<rect x="$x" y="$y" width="$w" height="2.54" stroke="$fg" stroke-width="0.3" fill="$bg" />};
212 my ( $fg, $bg ) = @{ $cols->{txt} };
213 print qq{<rect x="$x" y="$y" width="$w" height="2.54" rx="1" ry="1" stroke="$fg" stroke-width="0.3" fill="$bg" />};
217 return qq{ style="fill:$bg"};
220 if ( $name =~ m/(VCC|3V3|3.3V|5v)/i ) {
221 my ($fg,$bg) = @{ $cols->{vcc} };
223 return qq{ style="fill:$fg"};
224 } elsif ( $name =~ m/(G(ND|Round)|VSS|0v)/i ) {
225 my ($fg,$bg) = @{ $cols->{gnd} };
227 return qq{ style="fill:$fg"};
228 } elsif ( $name =~ m/\[(\w+)/ ) { # kernel
230 my ($fg,$bg) = @{ $cols->{txt} };
231 ($fg,$bg) = @{ $cols->{$dev} } if exists $cols->{$dev};
233 return qq{ style="fill:$fg"};
235 my ( $fg, $bg ) = @{ $cols->{txt} };
237 #return qq{ style="fill:$fg"};
244 my @cols_order = ( 0,1,2,3 );
245 my @cols_align = ( '','-','','-' ); # sprintf prefix
247 my @cols_shuffle = @cols_order;
250 # pins outside on the right
251 @cols_shuffle = ( 0,1,3,2 ) if $opt_edge;
252 @cols_align = ( '-','-','','' );
253 } elsif ( $opt_middle ) {
255 @cols_shuffle = ( 1,0,2,3 );
256 @cols_align = ( '','','-','-' );
260 my ( $what, $order ) = @_;
262 foreach my $i ( 0 .. $#$what ) {
263 $new->[$i] = $what->[ $order->[$i] ];
265 warn "# cols_shuffle what=",dump($what)," order=",dump($order)," new=",dump($new);
269 @cols_order = cols_shuffle( \@cols_order, \@cols_shuffle );
270 @max_len = cols_shuffle( \@max_len, \@cols_shuffle );
272 warn "# cols_order = ",dump( \@cols_order );
273 warn "# cols_align = ",dump( \@cols_align );
275 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";
279 my ($fg,$bg) = @{ $cols->{txt} };
280 my $line_fmt = qq{<line x1="%s" y1="%s" x2="%s" y2="%s" style="stroke:$fg;stroke-width:0.10;fill:$bg" />\n};
285 return unless $opt_svg;
286 push @cut_marks, sprintf($line_fmt, $x-5, $y-$font_b, $x+5, $y-$font_b);
287 push @cut_marks, sprintf($line_fmt, $x, $y-$font_b-5, $x, $y-$font_b+5);
291 $max_x += $max_len[$_] * $font_w foreach ( 0 .. 3 );
295 my ($x,$y,$max_x) = @_;
296 push @cut_marks, sprintf($line_fmt, $x, $y-$font_b, $max_x, $y-$font_b);
300 my $last_cut_mark = 0;
302 foreach my $i ( 0 .. $#line_parts ) {
303 $i = $#line_parts - $i if $opt_vertical;
304 my $line = $line_parts[$i];
308 if ( ! exists $line->[0] ) {
309 # before first empty line
310 if ( $last_cut_mark == 0 ) {
314 line $x, $y, $max_x if $opt_lines;
315 $y += 15; # make spacing between pinouts
317 } elsif ( $last_cut_mark ) {
323 #warn "CUTMARK no magic";
326 line $x, $y, $max_x if $opt_lines && exists $line->[1];
328 my ($fg,$bg) = @{ $cols->{txt} };
329 my $tspan = qq{<tspan x="$x" y="$y" style="line-height:2.54;fill:$fg;stroke:none;">\n};
332 foreach my $i ( 0 .. $#cols_order ) {
333 my $order = $cols_order[$i];
334 next unless $line->[$order];
335 my $text_anchor = 'middle';
336 my $x2 = $x_pos + ( $max_len[$i] * $font_w ) / 2;
337 $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]);
338 $x_pos += $max_len[$i] * $font_w;
341 $tspan .= qq{</tspan>\n};
342 push @later,sprintf $tspan, @$line;
345 # swap pin colors for line stripe
347 swap_cols $_ foreach qw( pins txt );
352 if ( $#$line == 0 ) {
353 print $line->[0], "\n";
355 push @$line, '' while ($#$line < 3); # fill-in single row header
356 printf $fmt, map { $line->[$_] } @cols_order;
365 line $x, $y, $max_x if $opt_lines;
372 style="font-size:2.34px;line-height:2.54px;font-family:'Andale Mono';stroke:none"
373 xml:space="preserve">
377 print @later, qq{</text>\n}, @cut_marks, qq{</g>\n</svg>};
382 # Cubietech Cubieboard
383 ## U14 (Next to SATA connector)
385 48 PI13 (SPI0-MISO/UART6-RX/EINT25) 47 PI11 (SPI0-CLK/UART5-RX/EINT23)
386 46 PI12 (SPI0-MOSI/UART6-TX/EINT24) 45 PI10 (SPI0-CS/UART5-TX/EINT22)
388 44 3.3V (nc in 2012-08-08) 43 VCC-5V
390 40 PB10 (LCD0-SCK/LCD-PIO1) 39 PB11 (LCD0-SDA/LCD-PIO2)
391 38 Ground 37 PH7 (LCD0-BL-EN/LCD-PIO0/UART5-RX/EINT7)
392 36 XN_TP (TP-X2) 35 YN_TP (TP-Y2)
393 34 XP_TP (TP-X1) 33 YP_TP (TP-Y1)
394 32 PD25 (LCDDE) 31 PB2 (PWM0)
395 30 PD26 (LCDHSYNC/VGA-HSYNC) 29 PD24 (LCDCLK)
396 28 PD23 (LCDD23) 27 PD27 (LCDVSYNC/VGA-VSYNC)
397 26 PD21 (LCDD21) 25 PD22 (LCDD22)
398 24 PD19 (LCDD19/LVDS1N3) 23 PD20 (LCDD20)
399 22 PD17 (LCDD17/LVDS1NC) 21 PD18 (LCDD18/LVDS1P3)
400 20 Ground 19 PD16 (LCDD16/LVDS1PC)
401 18 PD14 (LCDD14/LVDS1P2) 17 PD15 (LCDD15/LVDS1N2)
402 16 PD12 (LCDD12/LVDS1P1) 15 PD13 (LCDD13/LVDS1N1)
403 14 PD10 (LCDD10/LVDS1P0) 13 PD11 (LCDD11/LVDS1N0)
404 12 PD8 (LCDD8/LVDS0P3) 11 PD9 (LCDD9/LVDS0N3)
405 10 PD7 (LCDD7/LVDS0NC) 9 Ground
406 8 PD5 (LCDD5/LVDS0N2) 7 PD6 (LCDD6/LVDS0PC)
407 6 PD3 (LCDD3/LVDS0N1) 5 PD4 (LCDD4/LNVS0P2)
408 4 PD1 (LCDD1/LVDS0N0) 3 PD2 (LCDD2/LVDS0P1)
409 2 Ground 1 PD0 (LCDD0/LVDSP0)
411 ## U15 (Between Ethernet port and USB ports)
413 1 VCC-5V 2 PH15 (CSI1-PWR/EINT15)
414 3 CSI1-IO-2V8 4 PH14 (CSI1-RST#/EINT14)
415 5 PG0 (CSI1-PCLK/SDC1-CMD) 6 PB18 (TWI1-SCK)
416 7 PB19 (TWI1-SDA) 8 PG3 (CSI1-VSYNC/SDC1-D1)
417 9 PG2 (CSI1-HSYNC/SDC1-D0) 10 PG1 (CSI1-MCLK/SDC1-CLK)
418 11 PG4 (CSI1-D0/SDC1-D2) 12 PG5 (CSI1-D1/SDC1-D3)
419 13 PG6 (CSI1-D2/UART3-TX) 14 PG7 (CSI1-D3/UART3-RX)
420 15 PG8 (CSI1-D4/UART3-RTS) 16 PG9 (CSI1-D5/UART3-CTS)
421 17 PG10 (CSI1-D6/UART4-TX) 18 PG11 (CSI1-D7/UART4-RX)
424 21 FMINL 22 PI4 (SDC3-CMD)
425 23 FMINR 24 PI5 (SDC3-CLK)
426 25 Ground 26 PI6 (SDC3-D0)
427 27 VGA-R 28 PI7 (SDC3-D1)
428 29 VGA-G 30 PI8 (SDC3-D2)
429 31 VGA-B 32 PI9 (SDC3-D3)
431 33 LCD1-VSYNC 34 PE4 (CSI0-D0)
432 35 LCD1-HSYNC 36 PE5 (CSI0-D1)
433 37 Ground 38 PE6 (CSI0-D2)
434 39 AVCC 40 PE7 (CSI0-D3)
435 41 LRADC0 42 PE8 (CSI0-D4)
436 43 CVBS 44 PE9 (CSI0-D5)
437 45 HPL 46 PE10 (CSI0-D6)
438 47 HPR 48 PE11 (CSI0-D7)
440 ## DEBUG serial (middle of board)
448 ## CON3 rpi DIP26-254
452 7 PI3 PWM1 8 PH0 UART3_TX
454 11 PI19 UART2_RX 12 PH2
455 13 PI18 UART2_TX 14 0v
456 15 PI17 UART2_CTS 16 PH21 CAN_RX
457 17 3.3v 18 PH20 CAN_TX
458 19 PI12 SPI0_MOSI 20 0v
459 21 PI13 SPI0_MISO 22 PI16 UART2_RTS
460 23 PI11 SPI0_SCLK 24 PI10 SPI0_CS0
461 25 0v 26 PI14 SPI0_CS1
469 6 PI20 UART7_TX 5 PH3
470 4 PI21 UART7_RX 3 PH5
477 7 gpio4 (WPi 7) 8 gpio14 (TxD)
479 11 gpio17 (WPi 0) 12 gpio18 (WPi 1)
480 13 gpio27 (WPi 2) 14 0v
481 15 gpio22 (WPi 3) 16 gpio23 (WPi 4)
482 17 3.3v 18 gpio24 (WPi 5)
483 19 gpio10 (MOSI) 20 0v
484 21 gpio9 (MISO) 22 gpio25 (WPi 6)
485 23 gpio11 (SCLK) 24 gpio8 (CE0)
487 # Raspberry Pi 3 Model B Rev 1.2
488 27 gpio0 (SDA.0) 28 gpio1 (SCL.0)
489 29 gpio5 (WPi 21) 30 0v
490 31 gpio6 (WPi 22) 32 gpio12 (WPi 26)
491 33 gpio13 (WPi 23) 34 0v
492 35 gpio19 (WPi 24) 36 gpio16 (WPi 27)
493 37 gpio26 (WPi 25) 38 gpio20 (WPi 28)
494 39 0v 40 gpio21 (WPi 29)