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