5 use Data::Dump qw(dump);
12 my $opt_horizontal = 0;
20 'invert!' => \$opt_invert,
21 'vertical-flip!' => \$opt_vertical,
22 'horizontal-flip!' => \$opt_horizontal,
23 'edge-pins!' => \$opt_edge,
24 'middle-pins!' => \$opt_middle,
25 'zebra!' => \$opt_zebra,
26 'lines!' => \$opt_lines,
30 my $font_w = 1.67; # < 2.54, font is not perfect square
31 my $font_b = 2.10; # font baseline position
34 open(my $fh, '<', shift);
41 my $model = slurp('/proc/device-tree/model');
42 $model =~ s/\x00$//; # strip kernel NULL
43 warn "# model [$model]";
52 warn "MODEL [$1] == [$model] ?\n";
53 if ( $model =~ m/$1/ ) {
58 } elsif ( $include ) {
59 push @{ $pins->{$1} }, $line_i while ( m/\t(\w+\d+)/g );
65 warn "IGNORE: [$_]\n";
69 die "add pin definition for # $model" unless $pins;
71 shift(@lines) while ( ! $lines[0] ); # remove empty at beginning
72 pop(@lines) while ( ! $lines[-1] ); # remove empty at end
74 warn "# pins ",dump($pins);
79 open(my $fh, '<', '/sys/kernel/debug/pinctrl/pinctrl-handles');
82 if ( m/device: [0-9a-f]+\.(\w+)/ ) {
84 } elsif ( m/group: (\w+\d+)\s.+function: (\S+)/ ) {
85 my ($pin, $function) = ($1,$2);
86 $pin_function->{$pin} = "$device $function";
88 if ( $pins->{$pin} ) {
89 foreach my $line ( @{$pins->{$pin}} ) {
90 my $t = $lines[$line];
92 $t =~ s/$pin/[$device $function]/;
94 $t =~ s/$pin/$pin [$device $function]/ || die "can't find $pin in [$t]";
97 warn "# $line: $lines[$line]\n";
100 warn "IGNORED: pin $pin function $function\n";
105 warn "# pin_function = ",dump($pin_function);
107 my @max_len = ( 0,0,0,0 );
109 foreach my $line (@lines) {
110 if ( $line =~ m/^#/ ) {
111 push @line_parts, [ $line ] unless $opt_svg;
114 $line =~ s/(\[(?:uart|serial))([^\t]*\]\s[^\t]*(rx|tx)d?)/$1 $3$2/gi;
115 $line =~ s/(\[i2c)([^\t]*\]\s[^\t]*(scl?k?|sda))/$1 $3$2/gi;
116 $line =~ s/(\[spi)([^\t]*\]\s[^\t]*(miso|mosi|s?clk|c[se]\d*))/$1 $3$2/gi;
117 $line =~ s/\s*\([^\)]+\)//g if ! $opt_alt;
119 # shorten duplicate kernel device/function
120 $line =~ s/\[serial (\w+) (uart\d+)\]/[$2 $1]/g;
121 $line =~ s/\[(\w+) (\w+) \1(\d+)\]/[$1$3 $2]/g;
123 my @v = split(/\s*\t+\s*/,$line,4);
124 @v = ( $v[2], $v[3], $v[0], $v[1] ) if $opt_horizontal;
126 push @line_parts, [ @v ];
127 foreach my $i ( 0 .. 3 ) {
128 next unless exists $v[$i];
129 next if $v[$i] =~ m/^#/; # don't calculate comments into max length
130 my $l = length($v[$i]);
131 $max_len[$i] = $l if $l > $max_len[$i];
135 warn "# max_len = ",dump( \@max_len );
136 warn "# line_parts = ",dump( \@line_parts );
138 #print "$_\n" foreach @lines;
144 print qq{<?xml version="1.0" encoding="UTF-8" standalone="no"?>
146 xmlns:dc="http://purl.org/dc/elements/1.1/"
147 xmlns:cc="http://creativecommons.org/ns#"
148 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
149 xmlns:svg="http://www.w3.org/2000/svg"
150 xmlns="http://www.w3.org/2000/svg"
151 xmlns:xlink="http://www.w3.org/1999/xlink"
154 viewBox="0 0 210 297"
161 }; # svg, insert rest of rect
163 print qq{<rect x="0" y="0" width="210" height="297" style="fill:#000000" id="high-contrast"/>} if $opt_invert;
168 my $cols = { # foreground background
169 txt => [ '#000000', '#ffffff' ],
170 pins => [ '#ffffff', '#ff00ff' ],
171 vcc => [ '#ff0000', '#ffff00' ],
172 gnd => [ '#000000', '#00ffff' ],
173 i2c => [ '#008800', '#ffcccc' ],
174 serial=>[ '#000088', '#ccffcc' ],
175 spi => [ '#880000', '#ccccff' ],
180 die "$swap not found in ",dump($cols) unless $cols->{$swap};
181 my ( $c1, $c2 ) = @{ $cols->{$swap} };
182 $cols->{$swap} = [ $c2, $c1 ];
185 swap_cols 'txt' if $opt_invert;
189 my ($name,$x,$y,$col) = @_;
190 $y -= $font_b; # shift box overlay to right vertical position based on font baseline
193 my ($x,$y,$col,$fill) = @_;
194 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};
198 if ( $name =~ m/^(\d+)$/ ) { # pins
200 my ( $fg, $bg ) = @{ $cols->{pins} };
202 my $w = $max_len[$col]*$font_w - 0.1;
205 #print qq{<polygon points="$x,$y $cx,$y $x,$cy $x,$y" stroke="$fg" stroke-width="0.25" fill="$bg" />};
206 #print qq{<polygon points="$x,$cy $cx,$cy $cx,$y $x,$cy" stroke="$bg" stroke-width="0.25" fill="$fg" />};
207 print qq{<rect x="$x" y="$y" width="$w" height="2.54" stroke="$fg" stroke-width="0.3" fill="$bg" />};
208 my ( $fg, $bg ) = @{ $cols->{txt} };
209 print qq{<rect x="$x" y="$y" width="$w" height="2.54" rx="1" ry="1" stroke="$fg" stroke-width="0.3" fill="$bg" />};
213 return qq{ style="fill:$bg"};
216 if ( $name =~ m/(VCC|3V3|3.3V|5v)/i ) {
217 my ($fg,$bg) = @{ $cols->{vcc} };
219 return qq{ style="fill:$fg"};
220 } elsif ( $name =~ m/(G(ND|Round)|VSS|0v)/i ) {
221 my ($fg,$bg) = @{ $cols->{gnd} };
223 return qq{ style="fill:$fg"};
224 } elsif ( $name =~ m/\[(\w+)/ ) { # kernel
226 my ($fg,$bg) = @{ $cols->{txt} };
227 ($fg,$bg) = @{ $cols->{$dev} } if exists $cols->{$dev};
229 return qq{ style="fill:$fg"};
231 my ( $fg, $bg ) = @{ $cols->{txt} };
233 #return qq{ style="fill:$fg"};
240 my @cols_order = ( 0,1,2,3 );
241 my @cols_align = ( '','-','','-' ); # sprintf prefix
243 my @cols_shuffle = @cols_order;
246 # pins outside on the right
247 @cols_shuffle = ( 0,1,3,2 ) if $opt_edge;
248 @cols_align = ( '-','-','','' );
249 } elsif ( $opt_middle ) {
251 @cols_shuffle = ( 1,0,2,3 );
252 @cols_align = ( '','','-','-' );
256 my ( $what, $order ) = @_;
258 foreach my $i ( 0 .. $#$what ) {
259 $new->[$i] = $what->[ $order->[$i] ];
261 warn "# cols_shuffle what=",dump($what)," order=",dump($order)," new=",dump($new);
265 @cols_order = cols_shuffle( \@cols_order, \@cols_shuffle );
266 @max_len = cols_shuffle( \@max_len, \@cols_shuffle );
268 warn "# cols_order = ",dump( \@cols_order );
269 warn "# cols_align = ",dump( \@cols_align );
271 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";
275 my ($fg,$bg) = @{ $cols->{txt} };
276 my $line_fmt = qq{<line x1="%s" y1="%s" x2="%s" y2="%s" style="stroke:$fg;stroke-width:0.10;fill:$bg" />\n};
281 return unless $opt_svg;
282 push @cut_marks, sprintf($line_fmt, $x-5, $y-$font_b, $x+5, $y-$font_b);
283 push @cut_marks, sprintf($line_fmt, $x, $y-$font_b-5, $x, $y-$font_b+5);
287 $max_x += $max_len[$_] * $font_w foreach ( 0 .. 3 );
291 my ($x,$y,$max_x) = @_;
292 push @cut_marks, sprintf($line_fmt, $x, $y-$font_b, $max_x, $y-$font_b);
296 my $last_cut_mark = 0;
298 foreach my $i ( 0 .. $#line_parts ) {
299 $i = $#line_parts - $i if $opt_vertical;
300 my $line = $line_parts[$i];
304 if ( ! exists $line->[0] ) {
305 # before first empty line
306 if ( $last_cut_mark == 0 ) {
310 line $x, $y, $max_x if $opt_lines;
311 $y += 15; # make spacing between pinouts
313 } elsif ( $last_cut_mark ) {
319 #warn "CUTMARK no magic";
322 line $x, $y, $max_x if $opt_lines && exists $line->[1];
324 my ($fg,$bg) = @{ $cols->{txt} };
325 my $tspan = qq{<tspan x="$x" y="$y" style="line-height:2.54;fill:$fg;stroke:none;">\n};
328 foreach my $i ( 0 .. $#cols_order ) {
329 my $order = $cols_order[$i];
330 next unless $line->[$order];
331 my $text_anchor = 'middle';
332 my $x2 = $x_pos + ( $max_len[$i] * $font_w ) / 2;
333 $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]);
334 $x_pos += $max_len[$i] * $font_w;
337 $tspan .= qq{</tspan>\n};
338 push @later,sprintf $tspan, @$line;
341 # swap pin colors for line stripe
343 swap_cols $_ foreach qw( pins txt );
348 if ( $#$line == 0 ) {
349 print $line->[0], "\n";
351 push @$line, '' while ($#$line < 3); # fill-in single row header
352 printf $fmt, map { $line->[$_] } @cols_order;
361 line $x, $y, $max_x if $opt_lines;
368 style="font-size:2.34px;line-height:2.54px;font-family:'Andale Mono';stroke:none"
369 xml:space="preserve">
373 print @later, qq{</text>\n}, @cut_marks, qq{</g>\n</svg>};
378 # Cubietech Cubieboard
379 ## U14 (Next to SATA connector)
381 48 PI13 (SPI0-MISO/UART6-RX/EINT25) 47 PI11 (SPI0-CLK/UART5-RX/EINT23)
382 46 PI12 (SPI0-MOSI/UART6-TX/EINT24) 45 PI10 (SPI0-CS/UART5-TX/EINT22)
384 44 3.3V (nc in 2012-08-08) 43 VCC-5V
386 40 PB10 (LCD0-SCK/LCD-PIO1) 39 PB11 (LCD0-SDA/LCD-PIO2)
387 38 Ground 37 PH7 (LCD0-BL-EN/LCD-PIO0/UART5-RX/EINT7)
388 36 XN_TP (TP-X2) 35 YN_TP (TP-Y2)
389 34 XP_TP (TP-X1) 33 YP_TP (TP-Y1)
390 32 PD25 (LCDDE) 31 PB2 (PWM0)
391 30 PD26 (LCDHSYNC/VGA-HSYNC) 29 PD24 (LCDCLK)
392 28 PD23 (LCDD23) 27 PD27 (LCDVSYNC/VGA-VSYNC)
393 26 PD21 (LCDD21) 25 PD22 (LCDD22)
394 24 PD19 (LCDD19/LVDS1N3) 23 PD20 (LCDD20)
395 22 PD17 (LCDD17/LVDS1NC) 21 PD18 (LCDD18/LVDS1P3)
396 20 Ground 19 PD16 (LCDD16/LVDS1PC)
397 18 PD14 (LCDD14/LVDS1P2) 17 PD15 (LCDD15/LVDS1N2)
398 16 PD12 (LCDD12/LVDS1P1) 15 PD13 (LCDD13/LVDS1N1)
399 14 PD10 (LCDD10/LVDS1P0) 13 PD11 (LCDD11/LVDS1N0)
400 12 PD8 (LCDD8/LVDS0P3) 11 PD9 (LCDD9/LVDS0N3)
401 10 PD7 (LCDD7/LVDS0NC) 9 Ground
402 8 PD5 (LCDD5/LVDS0N2) 7 PD6 (LCDD6/LVDS0PC)
403 6 PD3 (LCDD3/LVDS0N1) 5 PD4 (LCDD4/LNVS0P2)
404 4 PD1 (LCDD1/LVDS0N0) 3 PD2 (LCDD2/LVDS0P1)
405 2 Ground 1 PD0 (LCDD0/LVDSP0)
407 ## U15 (Between Ethernet port and USB ports)
409 1 VCC-5V 2 PH15 (CSI1-PWR/EINT15)
410 3 CSI1-IO-2V8 4 PH14 (CSI1-RST#/EINT14)
411 5 PG0 (CSI1-PCLK/SDC1-CMD) 6 PB18 (TWI1-SCK)
412 7 PB19 (TWI1-SDA) 8 PG3 (CSI1-VSYNC/SDC1-D1)
413 9 PG2 (CSI1-HSYNC/SDC1-D0) 10 PG1 (CSI1-MCLK/SDC1-CLK)
414 11 PG4 (CSI1-D0/SDC1-D2) 12 PG5 (CSI1-D1/SDC1-D3)
415 13 PG6 (CSI1-D2/UART3-TX) 14 PG7 (CSI1-D3/UART3-RX)
416 15 PG8 (CSI1-D4/UART3-RTS) 16 PG9 (CSI1-D5/UART3-CTS)
417 17 PG10 (CSI1-D6/UART4-TX) 18 PG11 (CSI1-D7/UART4-RX)
420 21 FMINL 22 PI4 (SDC3-CMD)
421 23 FMINR 24 PI5 (SDC3-CLK)
422 25 Ground 26 PI6 (SDC3-D0)
423 27 VGA-R 28 PI7 (SDC3-D1)
424 29 VGA-G 30 PI8 (SDC3-D2)
425 31 VGA-B 32 PI9 (SDC3-D3)
427 33 LCD1-VSYNC 34 PE4 (CSI0-D0)
428 35 LCD1-HSYNC 36 PE5 (CSI0-D1)
429 37 Ground 38 PE6 (CSI0-D2)
430 39 AVCC 40 PE7 (CSI0-D3)
431 41 LRADC0 42 PE8 (CSI0-D4)
432 43 CVBS 44 PE9 (CSI0-D5)
433 45 HPL 46 PE10 (CSI0-D6)
434 47 HPR 48 PE11 (CSI0-D7)
436 ## DEBUG serial (middle of board)
444 ## CON3 rpi DIP26-254
448 7 PI3 PWM1 8 PH0 UART3_TX
450 11 PI19 UART2_RX 12 PH2
451 13 PI18 UART2_TX 14 0v
452 15 PI17 UART2_CTS 16 PH21 CAN_RX
453 17 3.3v 18 PH20 CAN_TX
454 19 PI12 SPI0_MOSI 20 0v
455 21 PI13 SPI0_MISO 22 PI16 UART2_RTS
456 23 PI11 SPI0_SCLK 24 PI10 SPI0_CS0
457 25 0v 26 PI14 SPI0_CS1
465 6 PI20 UART7_TX 5 PH3
466 4 PI21 UART7_RX 3 PH5
473 7 gpio4 (WPi 7) 8 gpio14 (TxD)
475 11 gpio17 (WPi 0) 12 gpio18 (WPi 1)
476 13 gpio27 (WPi 2) 14 0v
477 15 gpio22 (WPi 3) 16 gpio23 (WPi 4)
478 17 3.3v 18 gpio24 (WPi 5)
479 19 gpio10 (MOSI) 20 0v
480 21 gpio9 (MISO) 22 gpio25 (WPi 6)
481 23 gpio11 (SCLK) 24 gpio8 (CE0)
483 # Raspberry Pi 3 Model B Rev 1.2
484 27 gpio0 (SDA.0) 28 gpio1 (SCL.0)
485 29 gpio5 (WPi 21) 30 0v
486 31 gpio6 (WPi 22) 32 gpio12 (WPi 26)
487 33 gpio13 (WPi 23) 34 0v
488 35 gpio19 (WPi 24) 36 gpio16 (WPi 27)
489 37 gpio26 (WPi 25) 38 gpio20 (WPi 28)
490 39 0v 40 gpio21 (WPi 29)