5 use Data::Dump qw(dump);
8 my $opt_svg = $ENV{SVG} || 0;
9 my $opt_alt = $ENV{ALT} || 0;
10 my $opt_invert = $ENV{INVERT} || 0;
11 my $opt_vertical = $ENV{VERTICAL} || 0;
12 my $opt_zebra = $ENV{ZEBRA} || 0;
13 my $opt_lines = $ENV{LINES} || 0;
17 'invert!' => \$opt_invert,
18 'vertical!' => \$opt_vertical,
19 'zebra!' => \$opt_zebra,
20 'lines!' => \$opt_lines,
24 my $font_w = 1.67; # < 2.54, font is not perfect square
25 my $font_b = 2.10; # font baseline position
28 open(my $fh, '<', shift);
35 my $model = slurp('/proc/device-tree/model');
36 $model =~ s/\x00$//; # strip kernel NULL
37 warn "# model [$model]";
46 warn "MODEL [$1] == [$model] ?\n";
47 if ( $model =~ m/$1/ ) {
52 } elsif ( $include ) {
53 push @{ $pins->{$1} }, $line_i while ( m/\t(\w+\d+)/g );
59 warn "IGNORE: [$_]\n";
63 die "add pin definition for # $model" unless $pins;
65 shift(@lines) while ( ! $lines[0] ); # remove empty at beginning
66 pop(@lines) while ( ! $lines[-1] ); # remove empty at end
68 warn "# pins ",dump($pins);
73 open(my $fh, '<', '/sys/kernel/debug/pinctrl/pinctrl-handles');
76 if ( m/device: [0-9a-f]+\.(\w+)/ ) {
78 } elsif ( m/group: (\w+\d+)\s.+function: (\S+)/ ) {
79 my ($pin, $function) = ($1,$2);
80 $pin_function->{$pin} = "$device $function";
82 if ( $pins->{$pin} ) {
83 foreach my $line ( @{$pins->{$pin}} ) {
84 warn "XXX $pin $line";
85 my $t = $lines[$line];
87 $t =~ s/$pin/[$device $function]/;
89 $t =~ s/$pin/$pin [$device $function]/ || die "can't find $pin in [$t]";
92 warn "# $line: $lines[$line]\n";
95 warn "IGNORED: pin $pin function $function\n";
100 warn "# pin_function = ",dump($pin_function);
102 my @max_len = ( 0,0,0,0 );
104 foreach my $line (@lines) {
105 if ( $line =~ m/^#/ ) {
106 push @line_parts, [ $line ] unless $opt_svg;
109 $line =~ s/(\[(?:uart|serial))([^\t]*\]\s[^\t]*(rx|tx)d?)/$1 $3$2/gi;
110 $line =~ s/(\[i2c)([^\t]*\]\s[^\t]*(scl?k?|sda))/$1 $3$2/gi;
111 $line =~ s/(\[spi)([^\t]*\]\s[^\t]*(miso|mosi|s?clk|c[se]\d*))/$1 $3$2/gi;
112 $line =~ s/\s*\([^\)]+\)//g if ! $opt_alt;
114 # shorten duplicate kernel device/function
115 $line =~ s/\[serial (\w+) (uart\d+)\]/[$2 $1]/g;
116 $line =~ s/\[(\w+) (\w+) \1(\d+)\]/[$1$3 $2]/g;
118 my @v = split(/\s*\t+\s*/,$line,4);
119 push @line_parts, [ @v ];
120 foreach my $i ( 0 .. 3 ) {
121 next unless exists $v[$i];
122 next if $v[$i] =~ m/^#/; # don't calculate comments into max length
123 my $l = length($v[$i]);
124 $max_len[$i] = $l if $l > $max_len[$i];
128 warn "# max_len = ",dump( \@max_len );
129 warn "# line_parts = ",dump( \@line_parts );
131 #print "$_\n" foreach @lines;
133 my $fmt = "%$max_len[0]s %-$max_len[1]s %$max_len[2]s %-$max_len[3]s\n";
139 print qq{<?xml version="1.0" encoding="UTF-8" standalone="no"?>
141 xmlns:dc="http://purl.org/dc/elements/1.1/"
142 xmlns:cc="http://creativecommons.org/ns#"
143 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
144 xmlns:svg="http://www.w3.org/2000/svg"
145 xmlns="http://www.w3.org/2000/svg"
146 xmlns:xlink="http://www.w3.org/1999/xlink"
149 viewBox="0 0 210 297"
156 }; # svg, insert rest of rect
158 print qq{<rect x="0" y="0" width="210" height="297" style="fill:#000000" id="high-contrast"/>} if $opt_invert;
163 my $cols = { # foreground background
164 txt => [ '#000000', '#ffffff' ],
165 pins => [ '#ffffff', '#ff00ff' ],
166 vcc => [ '#ff0000', '#ffff00' ],
167 gnd => [ '#000000', '#00ffff' ],
168 i2c => [ '#008800', '#ffcccc' ],
169 serial=>[ '#000088', '#ccffcc' ],
170 spi => [ '#880000', '#ccccff' ],
175 die "$swap not found in ",dump($cols) unless $cols->{$swap};
176 my ( $c1, $c2 ) = @{ $cols->{$swap} };
177 $cols->{$swap} = [ $c2, $c1 ];
180 swap_cols 'txt' if $opt_invert;
184 my ($name,$x,$y,$col) = @_;
185 $y -= $font_b; # shift box overlay to right vertical position based on font baseline
188 my ($x,$y,$col,$fill) = @_;
189 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};
193 if ( $name =~ m/^(\d+)$/ ) { # pins
195 my ( $fg, $bg ) = @{ $cols->{pins} };
197 my $w = $max_len[$col]*$font_w - 0.1;
200 #print qq{<polygon points="$x,$y $cx,$y $x,$cy $x,$y" stroke="$fg" stroke-width="0.25" fill="$bg" />};
201 #print qq{<polygon points="$x,$cy $cx,$cy $cx,$y $x,$cy" stroke="$bg" stroke-width="0.25" fill="$fg" />};
202 print qq{<rect x="$x" y="$y" width="$w" height="2.54" stroke="$fg" stroke-width="0.3" fill="$bg" />};
203 my ( $fg, $bg ) = @{ $cols->{txt} };
204 print qq{<rect x="$x" y="$y" width="$w" height="2.54" rx="1" ry="1" stroke="$fg" stroke-width="0.3" fill="$bg" />};
208 return qq{ style="fill:$bg"};
211 if ( $name =~ m/(VCC|3V3|3.3V|5v)/i ) {
212 my ($fg,$bg) = @{ $cols->{vcc} };
214 return qq{ style="fill:$fg"};
215 } elsif ( $name =~ m/(G(ND|Round)|VSS|0v)/i ) {
216 my ($fg,$bg) = @{ $cols->{gnd} };
218 return qq{ style="fill:$fg"};
219 } elsif ( $name =~ m/\[(\w+)/ ) { # kernel
221 my ($fg,$bg) = @{ $cols->{txt} };
222 ($fg,$bg) = @{ $cols->{$dev} } if exists $cols->{$dev};
224 return qq{ style="fill:$fg"};
226 my ( $fg, $bg ) = @{ $cols->{txt} };
228 #return qq{ style="fill:$fg"};
235 my @cols_order = ( 0,1,2,3 );
236 my @cols_align = ( '','-','','-' ); # sprintf prefix
238 @cols_order = ( 0,1,3,2 ); # pins outside on the right
239 @cols_align = ( '','-','-','' );
243 my ($fg,$bg) = @{ $cols->{txt} };
244 my $line_fmt = qq{<line x1="%s" y1="%s" x2="%s" y2="%s" style="stroke:$fg;stroke-width:0.10;fill:$bg" />\n};
249 return unless $opt_svg;
250 push @cut_marks, sprintf($line_fmt, $x-5, $y-$font_b, $x+5, $y-$font_b);
251 push @cut_marks, sprintf($line_fmt, $x, $y-$font_b-5, $x, $y-$font_b+5);
255 $max_x += $max_len[$_] * $font_w foreach ( 0 .. 3 );
259 my ($x,$y,$max_x) = @_;
260 push @cut_marks, sprintf($line_fmt, $x, $y-$font_b, $max_x, $y-$font_b);
264 my $last_cut_mark = 0;
266 foreach my $i ( 0 .. $#line_parts ) {
267 $i = $#line_parts - $i if $opt_vertical;
268 my $line = $line_parts[$i];
272 if ( ! exists $line->[0] ) {
273 # before first empty line
274 if ( $last_cut_mark == 0 ) {
278 line $x, $y, $max_x if $opt_lines;
279 $y += 15; # make spacing between pinouts
281 } elsif ( $last_cut_mark ) {
287 #warn "CUTMARK no magic";
290 line $x, $y, $max_x if $opt_lines && exists $line->[1];
292 my ($fg,$bg) = @{ $cols->{txt} };
293 my $tspan = qq{<tspan x="$x" y="$y" style="line-height:2.54;fill:$fg;stroke:none;">\n};
296 foreach my $i ( @cols_order ) {
297 next unless $line->[$i];
298 my $text_anchor = 'middle';
299 my $x2 = $x_pos + ( $max_len[$i] * $font_w ) / 2;
300 $tspan .= qq{<tspan x="$x2" text-anchor="$text_anchor"}.svg_style($line->[$i],$x_pos,$y,$i).sprintf( '>%' . $cols_align[$i] . $max_len[$i] . 's</tspan>', $line->[$i]);
301 $x_pos += $max_len[$i] * $font_w;
304 $tspan .= qq{</tspan>\n};
305 push @later,sprintf $tspan, @$line;
308 # swap pin colors for line stripe
310 swap_cols $_ foreach qw( pins txt );
315 if ( $#$line == 0 ) {
316 print $line->[0], "\n";
318 push @$line, '' while ($#$line < 3); # fill-in single row header
328 line $x, $y, $max_x if $opt_lines;
335 style="font-size:2.34px;line-height:2.54px;font-family:'Andale Mono';stroke:none"
336 xml:space="preserve">
340 print @later, qq{</text>\n}, @cut_marks, qq{</g>\n</svg>};
345 # Cubietech Cubieboard
346 ## U14 (Next to SATA connector)
348 48 PI13 (SPI0-MISO/UART6-RX/EINT25) 47 PI11 (SPI0-CLK/UART5-RX/EINT23)
349 46 PI12 (SPI0-MOSI/UART6-TX/EINT24) 45 PI10 (SPI0-CS/UART5-TX/EINT22)
351 44 3.3V (nc in 2012-08-08) 43 VCC-5V
353 40 PB10 (LCD0-SCK/LCD-PIO1) 39 PB11 (LCD0-SDA/LCD-PIO2)
354 38 Ground 37 PH7 (LCD0-BL-EN/LCD-PIO0/UART5-RX/EINT7)
355 36 XN_TP (TP-X2) 35 YN_TP (TP-Y2)
356 34 XP_TP (TP-X1) 33 YP_TP (TP-Y1)
357 32 PD25 (LCDDE) 31 PB2 (PWM0)
358 30 PD26 (LCDHSYNC/VGA-HSYNC) 29 PD24 (LCDCLK)
359 28 PD23 (LCDD23) 27 PD27 (LCDVSYNC/VGA-VSYNC)
360 26 PD21 (LCDD21) 25 PD22 (LCDD22)
361 24 PD19 (LCDD19/LVDS1N3) 23 PD20 (LCDD20)
362 22 PD17 (LCDD17/LVDS1NC) 21 PD18 (LCDD18/LVDS1P3)
363 20 Ground 19 PD16 (LCDD16/LVDS1PC)
364 18 PD14 (LCDD14/LVDS1P2) 17 PD15 (LCDD15/LVDS1N2)
365 16 PD12 (LCDD12/LVDS1P1) 15 PD13 (LCDD13/LVDS1N1)
366 14 PD10 (LCDD10/LVDS1P0) 13 PD11 (LCDD11/LVDS1N0)
367 12 PD8 (LCDD8/LVDS0P3) 11 PD9 (LCDD9/LVDS0N3)
368 10 PD7 (LCDD7/LVDS0NC) 9 Ground
369 8 PD5 (LCDD5/LVDS0N2) 7 PD6 (LCDD6/LVDS0PC)
370 6 PD3 (LCDD3/LVDS0N1) 5 PD4 (LCDD4/LNVS0P2)
371 4 PD1 (LCDD1/LVDS0N0) 3 PD2 (LCDD2/LVDS0P1)
372 2 Ground 1 PD0 (LCDD0/LVDSP0)
374 ## U15 (Between Ethernet port and USB ports)
376 1 VCC-5V 2 PH15 (CSI1-PWR/EINT15)
377 3 CSI1-IO-2V8 4 PH14 (CSI1-RST#/EINT14)
378 5 PG0 (CSI1-PCLK/SDC1-CMD) 6 PB18 (TWI1-SCK)
379 7 PB19 (TWI1-SDA) 8 PG3 (CSI1-VSYNC/SDC1-D1)
380 9 PG2 (CSI1-HSYNC/SDC1-D0) 10 PG1 (CSI1-MCLK/SDC1-CLK)
381 11 PG4 (CSI1-D0/SDC1-D2) 12 PG5 (CSI1-D1/SDC1-D3)
382 13 PG6 (CSI1-D2/UART3-TX) 14 PG7 (CSI1-D3/UART3-RX)
383 15 PG8 (CSI1-D4/UART3-RTS) 16 PG9 (CSI1-D5/UART3-CTS)
384 17 PG10 (CSI1-D6/UART4-TX) 18 PG11 (CSI1-D7/UART4-RX)
387 21 FMINL 22 PI4 (SDC3-CMD)
388 23 FMINR 24 PI5 (SDC3-CLK)
389 25 Ground 26 PI6 (SDC3-D0)
390 27 VGA-R 28 PI7 (SDC3-D1)
391 29 VGA-G 30 PI8 (SDC3-D2)
392 31 VGA-B 32 PI9 (SDC3-D3)
394 33 LCD1-VSYNC 34 PE4 (CSI0-D0)
395 35 LCD1-HSYNC 36 PE5 (CSI0-D1)
396 37 Ground 38 PE6 (CSI0-D2)
397 39 AVCC 40 PE7 (CSI0-D3)
398 41 LRADC0 42 PE8 (CSI0-D4)
399 43 CVBS 44 PE9 (CSI0-D5)
400 45 HPL 46 PE10 (CSI0-D6)
401 47 HPR 48 PE11 (CSI0-D7)
403 ## DEBUG serial (middle of board)
411 ## CON3 rpi DIP26-254
415 7 PI3 PWM1 8 PH0 UART3_TX
417 11 PI19 UART2_RX 12 PH2
418 13 PI18 UART2_TX 14 0v
419 15 PI17 UART2_CTS 16 PH21 CAN_RX
420 17 3.3v 18 PH20 CAN_TX
421 19 PI12 SPI0_MOSI 20 0v
422 21 PI13 SPI0_MISO 22 PI16 UART2_RTS
423 23 PI11 SPI0_SCLK 24 PI10 SPI0_CS0
424 25 0v 26 PI14 SPI0_CS1
432 6 PI20 UART7_TX 5 PH3
433 4 PI21 UART7_RX 3 PH5
440 7 gpio4 (WPi 7) 8 gpio14 (TxD)
442 11 gpio17 (WPi 0) 12 gpio18 (WPi 1)
443 13 gpio27 (WPi 2) 14 0v
444 15 gpio22 (WPi 3) 16 gpio23 (WPi 4)
445 17 3.3v 18 gpio24 (WPi 5)
446 19 gpio10 (MOSI) 20 0v
447 21 gpio9 (MISO) 22 gpio25 (WPi 6)
448 23 gpio11 (SCLK) 24 gpio8 (CE0)
450 # Raspberry Pi 3 Model B Rev 1.2
451 27 gpio0 (SDA.0) 28 gpio1 (SCL.0)
452 29 gpio5 (WPi 21) 30 0v
453 31 gpio6 (WPi 22) 32 gpio12 (WPi 26)
454 33 gpio13 (WPi 23) 34 0v
455 35 gpio19 (WPi 24) 36 gpio16 (WPi 27)
456 37 gpio26 (WPi 25) 38 gpio20 (WPi 28)
457 39 0v 40 gpio21 (WPi 29)