5 use Data::Dump qw(dump);
12 my $opt_horizontal = 0;
22 'invert!' => \$opt_invert,
23 'vertical-flip!' => \$opt_vertical,
24 'horizontal-flip!' => \$opt_horizontal,
25 'edge-pins!' => \$opt_edge,
26 'middle-pins!' => \$opt_middle,
27 'zebra!' => \$opt_zebra,
28 'lines!' => \$opt_lines,
29 'read=s' => \$opt_read,
30 'pins=s' => \$opt_pins,
34 my $font_w = 1.67; # < 2.54, font is not perfect square
35 my $font_b = 2.10; # font baseline position
37 $opt_read .= '/' unless $opt_read =~ m/\/$/;
40 open(my $fh, '<', $opt_read . shift);
47 my $model = slurp('/proc/device-tree/model');
48 $model =~ s/\x00$//; # strip kernel NULL
49 warn "# model [$model]";
51 open(DATA, '<', $opt_pins) if $opt_pins;
60 warn "MODEL [$1] == [$model] ?\n";
61 if ( $model =~ m/$1/ ) {
66 } elsif ( $include || $opt_pins ) {
67 push @{ $pins->{$1} }, $line_i while ( m/\t(\w+\d+)/g );
73 warn "IGNORE: [$_]\n";
77 die "add pin definition for # $model" unless $pins;
79 shift(@lines) while ( ! $lines[0] ); # remove empty at beginning
80 pop(@lines) while ( ! $lines[-1] ); # remove empty at end
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 );
130 foreach my $line (@lines) {
131 if ( $line =~ m/^#/ ) {
132 push @line_parts, [ $line ] unless $opt_svg;
135 $line =~ s/(\[(?:uart|serial|tty\w+))([^\t]*\]\s[^\t]*(rx|tx)d?)/$1 $3$2/gi;
136 $line =~ s/(\[i2c)([^\t]*\]\s[^\t]*(scl?k?|sda))/$1 $3$2/gi;
137 $line =~ s/(\[spi)([^\t]*\]\s[^\t]*(miso|mosi|s?clk|c[se]\d*))/$1 $3$2/gi;
138 $line =~ s/\s*\([^\)]+\)//g if ! $opt_alt;
140 # shorten duplicate kernel device/function
141 $line =~ s/\[serial (\w+) (uart\d+)\]/[$2 $1]/g;
142 $line =~ s/\[(\w+) (\w+) \1(\d+)\]/[$1$3 $2]/g;
144 my @v = split(/\s*\t+\s*/,$line,4);
145 @v = ( $v[2], $v[3], $v[0], $v[1] ) if $opt_horizontal && $v[2];
147 push @line_parts, [ @v ];
148 foreach my $i ( 0 .. 3 ) {
149 next unless exists $v[$i];
150 next if $v[$i] =~ m/^#/; # don't calculate comments into max length
151 my $l = length($v[$i]);
152 $max_len[$i] = $l if $l > $max_len[$i];
156 warn "# max_len = ",dump( \@max_len );
157 warn "# line_parts = ",dump( \@line_parts );
159 #print "$_\n" foreach @lines;
165 print qq{<?xml version="1.0" encoding="UTF-8" standalone="no"?>
167 xmlns:dc="http://purl.org/dc/elements/1.1/"
168 xmlns:cc="http://creativecommons.org/ns#"
169 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
170 xmlns:svg="http://www.w3.org/2000/svg"
171 xmlns="http://www.w3.org/2000/svg"
172 xmlns:xlink="http://www.w3.org/1999/xlink"
175 viewBox="0 0 210 297"
182 }; # svg, insert rest of rect
184 print qq{<rect x="0" y="0" width="210" height="297" style="fill:#000000" id="high-contrast"/>} if $opt_invert;
189 my $cols = { # foreground background
190 txt => [ '#000000', '#ffffff' ],
191 pins => [ '#ffffff', '#ff00ff' ],
192 vcc => [ '#ff0000', '#ffff00' ],
193 gnd => [ '#000000', '#00ffff' ],
194 i2c => [ '#008800', '#ffcccc' ],
195 serial=>[ '#000088', '#ccffcc' ],
196 spi => [ '#880000', '#ccccff' ],
201 die "$swap not found in ",dump($cols) unless $cols->{$swap};
202 my ( $c1, $c2 ) = @{ $cols->{$swap} };
203 $cols->{$swap} = [ $c2, $c1 ];
206 swap_cols 'txt' if $opt_invert;
210 my ($name,$x,$y,$col) = @_;
211 $y -= $font_b; # shift box overlay to right vertical position based on font baseline
214 my ($x,$y,$col,$fill) = @_;
215 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};
219 if ( $name =~ m/^(\d+)$/ ) { # pins
221 my ( $fg, $bg ) = @{ $cols->{pins} };
223 my $w = $max_len[$col]*$font_w - 0.1;
226 #print qq{<polygon points="$x,$y $cx,$y $x,$cy $x,$y" stroke="$fg" stroke-width="0.25" fill="$bg" />};
227 #print qq{<polygon points="$x,$cy $cx,$cy $cx,$y $x,$cy" stroke="$bg" stroke-width="0.25" fill="$fg" />};
228 print qq{<rect x="$x" y="$y" width="$w" height="2.54" stroke="$fg" stroke-width="0.3" fill="$bg" />};
229 my ( $fg, $bg ) = @{ $cols->{txt} };
230 print qq{<rect x="$x" y="$y" width="$w" height="2.54" rx="1" ry="1" stroke="$fg" stroke-width="0.3" fill="$bg" />};
234 return qq{ style="fill:$bg"};
237 if ( $name =~ m/(VCC|3V3|3.3V|5v)/i ) {
238 my ($fg,$bg) = @{ $cols->{vcc} };
240 return qq{ style="fill:$fg"};
241 } elsif ( $name =~ m/(G(ND|Round)|VSS|0v)/i ) {
242 my ($fg,$bg) = @{ $cols->{gnd} };
244 return qq{ style="fill:$fg"};
245 } elsif ( $name =~ m/\[(\w+)/ ) { # kernel
247 my ($fg,$bg) = @{ $cols->{txt} };
248 $dev = 'serial' if $dev =~ m/^tty/;
249 ($fg,$bg) = @{ $cols->{$dev} } if exists $cols->{$dev};
251 return qq{ style="fill:$fg"};
253 my ( $fg, $bg ) = @{ $cols->{txt} };
255 #return qq{ style="fill:$fg"};
262 my @cols_order = ( 0,1,2,3 );
263 my @cols_align = ( '','-','','-' ); # sprintf prefix
265 my @cols_shuffle = @cols_order;
268 # pins outside on the right
269 @cols_shuffle = ( 0,1,3,2 ) if $opt_edge;
270 @cols_align = ( '-','-','','' );
271 } elsif ( $opt_middle ) {
273 @cols_shuffle = ( 1,0,2,3 );
274 @cols_align = ( '','','-','-' );
278 my ( $what, $order ) = @_;
280 foreach my $i ( 0 .. $#$what ) {
281 $new->[$i] = $what->[ $order->[$i] ];
283 warn "# cols_shuffle what=",dump($what)," order=",dump($order)," new=",dump($new);
287 @cols_order = cols_shuffle( \@cols_order, \@cols_shuffle );
288 @max_len = cols_shuffle( \@max_len, \@cols_shuffle );
290 warn "# cols_order = ",dump( \@cols_order );
291 warn "# cols_align = ",dump( \@cols_align );
293 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";
297 my ($fg,$bg) = @{ $cols->{txt} };
298 my $line_fmt = qq{<line x1="%s" y1="%s" x2="%s" y2="%s" style="stroke:$fg;stroke-width:0.10;fill:$bg" />\n};
303 return unless $opt_svg;
304 push @cut_marks, sprintf($line_fmt, $x-5, $y-$font_b, $x+5, $y-$font_b);
305 push @cut_marks, sprintf($line_fmt, $x, $y-$font_b-5, $x, $y-$font_b+5);
309 $max_x += $max_len[$_] * $font_w foreach ( 0 .. 3 );
313 my ($x,$y,$max_x) = @_;
314 push @cut_marks, sprintf($line_fmt, $x, $y-$font_b, $max_x, $y-$font_b);
318 my $last_cut_mark = 0;
320 foreach my $i ( 0 .. $#line_parts ) {
321 $i = $#line_parts - $i if $opt_vertical;
322 my $line = $line_parts[$i];
326 if ( ! exists $line->[0] ) {
327 # before first empty line
328 if ( $last_cut_mark == 0 ) {
332 line $x, $y, $max_x if $opt_lines;
333 $y += 15; # make spacing between pinouts
335 } elsif ( $last_cut_mark ) {
341 #warn "CUTMARK no magic";
344 line $x, $y, $max_x if $opt_lines && exists $line->[1];
346 my ($fg,$bg) = @{ $cols->{txt} };
347 my $tspan = qq{<tspan x="$x" y="$y" style="line-height:2.54;fill:$fg;stroke:none;">\n};
350 foreach my $i ( 0 .. $#cols_order ) {
351 my $order = $cols_order[$i];
352 next unless $line->[$order];
353 my $text_anchor = 'middle';
354 my $x2 = $x_pos + ( $max_len[$i] * $font_w ) / 2;
355 $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]);
356 $x_pos += $max_len[$i] * $font_w;
359 $tspan .= qq{</tspan>\n};
360 push @later,sprintf $tspan, @$line;
363 # swap pin colors for line stripe
365 swap_cols $_ foreach qw( pins txt );
372 if ( $#$line == 0 ) {
373 print $line->[0], "\n";
375 push @$line, '' while ($#$line < 3); # fill-in single row header
376 printf $fmt, map { $line->[$_] } @cols_order;
385 line $x, $y, $max_x if $opt_lines;
392 style="font-size:2.34px;line-height:2.54px;font-family:'Andale Mono';stroke:none"
393 xml:space="preserve">
397 print @later, qq{</text>\n}, @cut_marks, qq{</g>\n</svg>};
402 # Cubietech Cubieboard
403 ## U14 (Next to SATA connector)
405 48 PI13 (SPI0-MISO/UART6-RX/EINT25) 47 PI11 (SPI0-CLK/UART5-RX/EINT23)
406 46 PI12 (SPI0-MOSI/UART6-TX/EINT24) 45 PI10 (SPI0-CS/UART5-TX/EINT22)
408 44 3.3V (nc in 2012-08-08) 43 VCC-5V
410 40 PB10 (LCD0-SCK/LCD-PIO1) 39 PB11 (LCD0-SDA/LCD-PIO2)
411 38 Ground 37 PH7 (LCD0-BL-EN/LCD-PIO0/UART5-RX/EINT7)
412 36 XN_TP (TP-X2) 35 YN_TP (TP-Y2)
413 34 XP_TP (TP-X1) 33 YP_TP (TP-Y1)
414 32 PD25 (LCDDE) 31 PB2 (PWM0)
415 30 PD26 (LCDHSYNC/VGA-HSYNC) 29 PD24 (LCDCLK)
416 28 PD23 (LCDD23) 27 PD27 (LCDVSYNC/VGA-VSYNC)
417 26 PD21 (LCDD21) 25 PD22 (LCDD22)
418 24 PD19 (LCDD19/LVDS1N3) 23 PD20 (LCDD20)
419 22 PD17 (LCDD17/LVDS1NC) 21 PD18 (LCDD18/LVDS1P3)
420 20 Ground 19 PD16 (LCDD16/LVDS1PC)
421 18 PD14 (LCDD14/LVDS1P2) 17 PD15 (LCDD15/LVDS1N2)
422 16 PD12 (LCDD12/LVDS1P1) 15 PD13 (LCDD13/LVDS1N1)
423 14 PD10 (LCDD10/LVDS1P0) 13 PD11 (LCDD11/LVDS1N0)
424 12 PD8 (LCDD8/LVDS0P3) 11 PD9 (LCDD9/LVDS0N3)
425 10 PD7 (LCDD7/LVDS0NC) 9 Ground
426 8 PD5 (LCDD5/LVDS0N2) 7 PD6 (LCDD6/LVDS0PC)
427 6 PD3 (LCDD3/LVDS0N1) 5 PD4 (LCDD4/LNVS0P2)
428 4 PD1 (LCDD1/LVDS0N0) 3 PD2 (LCDD2/LVDS0P1)
429 2 Ground 1 PD0 (LCDD0/LVDSP0)
431 ## U15 (Between Ethernet port and USB ports)
433 1 VCC-5V 2 PH15 (CSI1-PWR/EINT15)
434 3 CSI1-IO-2V8 4 PH14 (CSI1-RST#/EINT14)
435 5 PG0 (CSI1-PCLK/SDC1-CMD) 6 PB18 (TWI1-SCK)
436 7 PB19 (TWI1-SDA) 8 PG3 (CSI1-VSYNC/SDC1-D1)
437 9 PG2 (CSI1-HSYNC/SDC1-D0) 10 PG1 (CSI1-MCLK/SDC1-CLK)
438 11 PG4 (CSI1-D0/SDC1-D2) 12 PG5 (CSI1-D1/SDC1-D3)
439 13 PG6 (CSI1-D2/UART3-TX) 14 PG7 (CSI1-D3/UART3-RX)
440 15 PG8 (CSI1-D4/UART3-RTS) 16 PG9 (CSI1-D5/UART3-CTS)
441 17 PG10 (CSI1-D6/UART4-TX) 18 PG11 (CSI1-D7/UART4-RX)
444 21 FMINL 22 PI4 (SDC3-CMD)
445 23 FMINR 24 PI5 (SDC3-CLK)
446 25 Ground 26 PI6 (SDC3-D0)
447 27 VGA-R 28 PI7 (SDC3-D1)
448 29 VGA-G 30 PI8 (SDC3-D2)
449 31 VGA-B 32 PI9 (SDC3-D3)
451 33 LCD1-VSYNC 34 PE4 (CSI0-D0)
452 35 LCD1-HSYNC 36 PE5 (CSI0-D1)
453 37 Ground 38 PE6 (CSI0-D2)
454 39 AVCC 40 PE7 (CSI0-D3)
455 41 LRADC0 42 PE8 (CSI0-D4)
456 43 CVBS 44 PE9 (CSI0-D5)
457 45 HPL 46 PE10 (CSI0-D6)
458 47 HPR 48 PE11 (CSI0-D7)
460 ## DEBUG serial (middle of board)
468 ## CON3 rpi DIP26-254
472 7 PI3 PWM1 8 PH0 UART3_TX
474 11 PI19 UART2_RX 12 PH2
475 13 PI18 UART2_TX 14 0v
476 15 PI17 UART2_CTS 16 PH21 CAN_RX
477 17 3.3v 18 PH20 CAN_TX
478 19 PI12 SPI0_MOSI 20 0v
479 21 PI13 SPI0_MISO 22 PI16 UART2_RTS
480 23 PI11 SPI0_SCLK 24 PI10 SPI0_CS0
481 25 0v 26 PI14 SPI0_CS1
489 6 PI20 UART7_TX 5 PH3
490 4 PI21 UART7_RX 3 PH5
497 7 gpio4 (WPi 7) 8 gpio14 (TxD)
499 11 gpio17 (WPi 0) 12 gpio18 (WPi 1)
500 13 gpio27 (WPi 2) 14 0v
501 15 gpio22 (WPi 3) 16 gpio23 (WPi 4)
502 17 3.3v 18 gpio24 (WPi 5)
503 19 gpio10 (MOSI) 20 0v
504 21 gpio9 (MISO) 22 gpio25 (WPi 6)
505 23 gpio11 (SCLK) 24 gpio8 (CE0)
507 # Raspberry Pi 3 Model B Rev 1.2
508 27 gpio0 (SDA.0) 28 gpio1 (SCL.0)
509 29 gpio5 (WPi 21) 30 0v
510 31 gpio6 (WPi 22) 32 gpio12 (WPi 26)
511 33 gpio13 (WPi 23) 34 0v
512 35 gpio19 (WPi 24) 36 gpio16 (WPi 27)
513 37 gpio26 (WPi 25) 38 gpio20 (WPi 28)
514 39 0v 40 gpio21 (WPi 29)