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 ( m/^#\s+/ ) {
54 } elsif ( $include ) {
55 push @{ $pins->{$1} }, $line_i while ( m/\t(\w+\d+)/g );
61 warn "IGNORE: [$_]\n";
65 die "add pin definition for # $model" unless $pins;
67 warn "# pins ",dump($pins);
72 open(my $fh, '<', '/sys/kernel/debug/pinctrl/pinctrl-handles');
75 if ( m/device: [0-9a-f]+\.(\w+)/ ) {
77 } elsif ( m/group: (\w+\d+)\s.+function: (\S+)/ ) {
78 my ($pin, $function) = ($1,$2);
79 $pin_function->{$pin} = "$device $function";
81 if ( $pins->{$pin} ) {
82 foreach my $line ( @{$pins->{$pin}} ) {
83 warn "XXX $pin $line";
84 my $t = $lines[$line];
86 $t =~ s/$pin/[$device $function]/;
88 $t =~ s/$pin/$pin [$device $function]/ || die "can't find $pin in [$t]";
91 warn "# $line: $lines[$line]\n";
94 warn "IGNORED: pin $pin function $function\n";
99 warn "# pin_function = ",dump($pin_function);
101 my @max_len = ( 0,0,0,0 );
103 foreach my $line (@lines) {
104 if ( $line =~ m/^#/ ) {
105 push @line_parts, [ $line ] unless $opt_svg;
108 $line =~ s/(\[(?:uart|serial))([^\t]*\]\s[^\t]*(rx|tx)d?)/$1 $3$2/gi;
109 $line =~ s/(\[i2c)([^\t]*\]\s[^\t]*(scl?k?|sda))/$1 $3$2/gi;
110 $line =~ s/(\[spi)([^\t]*\]\s[^\t]*(miso|mosi|s?clk|c[se]\d*))/$1 $3$2/gi;
111 $line =~ s/\s*\([^\)]+\)//g if ! $opt_alt;
113 # shorten duplicate kernel device/function
114 $line =~ s/\[serial (\w+) (uart\d+)\]/[$2 $1]/g;
115 $line =~ s/\[(\w+) (\w+) \1(\d+)\]/[$1$3 $2]/g;
117 my @v = split(/\s*\t+\s*/,$line,4);
118 push @line_parts, [ @v ];
119 foreach my $i ( 0 .. 3 ) {
120 next unless exists $v[$i];
121 next if $v[$i] =~ m/^#/; # don't calculate comments into max length
122 my $l = length($v[$i]);
123 $max_len[$i] = $l if $l > $max_len[$i];
127 warn "# max_len = ",dump( \@max_len );
128 warn "# line_parts = ",dump( \@line_parts );
130 #print "$_\n" foreach @lines;
132 my $fmt = "%$max_len[0]s %-$max_len[1]s %$max_len[2]s %-$max_len[3]s\n";
138 print qq{<?xml version="1.0" encoding="UTF-8" standalone="no"?>
140 xmlns:dc="http://purl.org/dc/elements/1.1/"
141 xmlns:cc="http://creativecommons.org/ns#"
142 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
143 xmlns:svg="http://www.w3.org/2000/svg"
144 xmlns="http://www.w3.org/2000/svg"
145 xmlns:xlink="http://www.w3.org/1999/xlink"
148 viewBox="0 0 210 297"
155 }; # svg, insert rest of rect
157 print qq{<rect x="0" y="0" width="210" height="297" style="fill:#000000" id="high-contrast"/>} if $opt_invert;
162 my $cols = { # foreground background
163 txt => [ '#000000', '#ffffff' ],
164 pins => [ '#ffffff', '#ff00ff' ],
165 vcc => [ '#ff0000', '#ffff00' ],
166 gnd => [ '#000000', '#00ffff' ],
167 i2c => [ '#008800', '#ffcccc' ],
168 serial=>[ '#000088', '#ccffcc' ],
169 spi => [ '#880000', '#ccccff' ],
174 die "$swap not found in ",dump($cols) unless $cols->{$swap};
175 my ( $c1, $c2 ) = @{ $cols->{$swap} };
176 $cols->{$swap} = [ $c2, $c1 ];
179 swap_cols 'txt' if $opt_invert;
183 my ($name,$x,$y,$col) = @_;
184 $y -= $font_b; # shift box overlay to right vertical position based on font baseline
187 my ($x,$y,$col,$fill) = @_;
188 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};
192 if ( $name =~ m/^(\d+)$/ ) { # pins
194 my ( $fg, $bg ) = @{ $cols->{pins} };
196 my $w = $max_len[$col]*$font_w - 0.1;
199 #print qq{<polygon points="$x,$y $cx,$y $x,$cy $x,$y" stroke="$fg" stroke-width="0.25" fill="$bg" />};
200 #print qq{<polygon points="$x,$cy $cx,$cy $cx,$y $x,$cy" stroke="$bg" stroke-width="0.25" fill="$fg" />};
201 print qq{<rect x="$x" y="$y" width="$w" height="2.54" stroke="$fg" stroke-width="0.3" fill="$bg" />};
202 my ( $fg, $bg ) = @{ $cols->{txt} };
203 print qq{<rect x="$x" y="$y" width="$w" height="2.54" rx="1" ry="1" stroke="$fg" stroke-width="0.3" fill="$bg" />};
207 return qq{ style="fill:$bg"};
210 if ( $name =~ m/(VCC|3V3|3.3V|5v)/i ) {
211 my ($fg,$bg) = @{ $cols->{vcc} };
213 return qq{ style="fill:$fg"};
214 } elsif ( $name =~ m/(G(ND|Round)|VSS|0v)/i ) {
215 my ($fg,$bg) = @{ $cols->{gnd} };
217 return qq{ style="fill:$fg"};
218 } elsif ( $name =~ m/\[(\w+)/ ) { # kernel
220 my ($fg,$bg) = @{ $cols->{txt} };
221 ($fg,$bg) = @{ $cols->{$dev} } if exists $cols->{$dev};
223 return qq{ style="fill:$fg"};
225 my ( $fg, $bg ) = @{ $cols->{txt} };
227 #return qq{ style="fill:$fg"};
234 my @cols_order = ( 0,1,2,3 );
235 my @cols_align = ( '','-','','-' ); # sprintf prefix
237 @cols_order = ( 0,1,3,2 ); # pins outside on the right
238 @cols_align = ( '','-','-','' );
242 my ($fg,$bg) = @{ $cols->{txt} };
243 my $line_fmt = qq{<line x1="%s" y1="%s" x2="%s" y2="%s" style="stroke:$fg;stroke-width:0.10;fill:$bg" />\n};
248 return unless $opt_svg;
249 push @cut_marks, sprintf($line_fmt, $x-5, $y-$font_b, $x+5, $y-$font_b);
250 push @cut_marks, sprintf($line_fmt, $x, $y-$font_b-5, $x, $y-$font_b+5);
254 $max_x += $max_len[$_] * $font_w foreach ( 0 .. 3 );
258 my ($x,$y,$max_x) = @_;
259 push @cut_marks, sprintf($line_fmt, $x, $y-$font_b, $max_x, $y-$font_b);
263 my $last_cut_mark = 0;
265 foreach my $i ( 0 .. $#line_parts ) {
266 $i = $#line_parts - $i if $opt_vertical;
267 my $line = $line_parts[$i];
271 if ( ! exists $line->[0] ) {
272 # before first empty line
273 if ( $last_cut_mark == 0 ) {
277 line $x, $y, $max_x if $opt_lines;
278 $y += 15; # make spacing between pinouts
280 } elsif ( $last_cut_mark ) {
286 #warn "CUTMARK no magic";
289 line $x, $y, $max_x if $opt_lines && exists $line->[1];
291 my ($fg,$bg) = @{ $cols->{txt} };
292 my $tspan = qq{<tspan x="$x" y="$y" style="line-height:2.54;fill:$fg;stroke:none;">\n};
295 foreach my $i ( @cols_order ) {
296 next unless $line->[$i];
297 my $text_anchor = 'middle';
298 my $x2 = $x_pos + ( $max_len[$i] * $font_w ) / 2;
299 $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]);
300 $x_pos += $max_len[$i] * $font_w;
303 $tspan .= qq{</tspan>\n};
304 push @later,sprintf $tspan, @$line;
307 # swap pin colors for line stripe
309 swap_cols $_ foreach qw( pins txt );
314 if ( $#$line == 0 ) {
315 print $line->[0], "\n";
317 push @$line, '' while ($#$line < 3); # fill-in single row header
330 style="font-size:2.34px;line-height:2.54px;font-family:'Andale Mono';stroke:none"
331 xml:space="preserve">
335 print @later, qq{</text>\n}, @cut_marks, qq{</g>\n</svg>};
340 # Cubietech Cubieboard2
341 ## U14 (Next to SATA connector)
343 48 PI13 (SPI0-MISO/UART6-RX/EINT25) 47 PI11 (SPI0-CLK/UART5-RX/EINT23)
344 46 PI12 (SPI0-MOSI/UART6-TX/EINT24) 45 PI10 (SPI0-CS/UART5-TX/EINT22)
346 44 3.3V (nc in 2012-08-08) 43 VCC-5V
348 40 PB10 (LCD0-SCK/LCD-PIO1) 39 PB11 (LCD0-SDA/LCD-PIO2)
349 38 Ground 37 PH7 (LCD0-BL-EN/LCD-PIO0/UART5-RX/EINT7)
350 36 XN_TP (TP-X2) 35 YN_TP (TP-Y2)
351 34 XP_TP (TP-X1) 33 YP_TP (TP-Y1)
352 32 PD25 (LCDDE) 31 PB2 (PWM0)
353 30 PD26 (LCDHSYNC/VGA-HSYNC) 29 PD24 (LCDCLK)
354 28 PD23 (LCDD23) 27 PD27 (LCDVSYNC/VGA-VSYNC)
355 26 PD21 (LCDD21) 25 PD22 (LCDD22)
356 24 PD19 (LCDD19/LVDS1N3) 23 PD20 (LCDD20)
357 22 PD17 (LCDD17/LVDS1NC) 21 PD18 (LCDD18/LVDS1P3)
358 20 Ground 19 PD16 (LCDD16/LVDS1PC)
359 18 PD14 (LCDD14/LVDS1P2) 17 PD15 (LCDD15/LVDS1N2)
360 16 PD12 (LCDD12/LVDS1P1) 15 PD13 (LCDD13/LVDS1N1)
361 14 PD10 (LCDD10/LVDS1P0) 13 PD11 (LCDD11/LVDS1N0)
362 12 PD8 (LCDD8/LVDS0P3) 11 PD9 (LCDD9/LVDS0N3)
363 10 PD7 (LCDD7/LVDS0NC) 9 Ground
364 8 PD5 (LCDD5/LVDS0N2) 7 PD6 (LCDD6/LVDS0PC)
365 6 PD3 (LCDD3/LVDS0N1) 5 PD4 (LCDD4/LNVS0P2)
366 4 PD1 (LCDD1/LVDS0N0) 3 PD2 (LCDD2/LVDS0P1)
367 2 Ground 1 PD0 (LCDD0/LVDSP0)
369 # Cubietech Cubieboard2
370 ## U15 (Between Ethernet port and USB ports)
372 1 VCC-5V 2 PH15 (CSI1-PWR/EINT15)
373 3 CSI1-IO-2V8 4 PH14 (CSI1-RST#/EINT14)
374 5 PG0 (CSI1-PCLK/SDC1-CMD) 6 PB18 (TWI1-SCK)
375 7 PB19 (TWI1-SDA) 8 PG3 (CSI1-VSYNC/SDC1-D1)
376 9 PG2 (CSI1-HSYNC/SDC1-D0) 10 PG1 (CSI1-MCLK/SDC1-CLK)
377 11 PG4 (CSI1-D0/SDC1-D2) 12 PG5 (CSI1-D1/SDC1-D3)
378 13 PG6 (CSI1-D2/UART3-TX) 14 PG7 (CSI1-D3/UART3-RX)
379 15 PG8 (CSI1-D4/UART3-RTS) 16 PG9 (CSI1-D5/UART3-CTS)
380 17 PG10 (CSI1-D6/UART4-TX) 18 PG11 (CSI1-D7/UART4-RX)
383 21 FMINL 22 PI4 (SDC3-CMD)
384 23 FMINR 24 PI5 (SDC3-CLK)
385 25 Ground 26 PI6 (SDC3-D0)
386 27 VGA-R 28 PI7 (SDC3-D1)
387 29 VGA-G 30 PI8 (SDC3-D2)
388 31 VGA-B 32 PI9 (SDC3-D3)
390 33 LCD1-VSYNC 34 PE4 (CSI0-D0)
391 35 LCD1-HSYNC 36 PE5 (CSI0-D1)
392 37 Ground 38 PE6 (CSI0-D2)
393 39 AVCC 40 PE7 (CSI0-D3)
394 41 LRADC0 42 PE8 (CSI0-D4)
395 43 CVBS 44 PE9 (CSI0-D5)
396 45 HPL 46 PE10 (CSI0-D6)
397 47 HPR 48 PE11 (CSI0-D7)
399 ## DEBUG serial (middle of board)
407 ## CON3 rpi DIP26-254
411 7 PI3 PWM1 8 PH0 UART3_TX
413 11 PI19 UART2_RX 12 PH2
414 13 PI18 UART2_TX 14 0v
415 15 PI17 UART2_CTS 16 PH21 CAN_RX
416 17 3.3v 18 PH20 CAN_TX
417 19 PI12 SPI0_MOSI 20 0v
418 21 PI13 SPI0_MISO 22 PI16 UART2_RTS
419 23 PI11 SPI0_SCLK 24 PI10 SPI0_CS0
420 25 0v 26 PI14 SPI0_CS1
428 6 PI20 UART7_TX 5 PH3
429 4 PI21 UART7_RX 3 PH5
432 # Raspberry Pi 3 Model B Rev 1.2
436 7 gpio4 (GPIO. 7) 8 gpio14 (TxD)
438 11 gpio17 (GPIO. 0) 12 gpio18 (GPIO. 1)
439 13 gpio27 (GPIO. 2) 14 0v
440 15 gpio22 (GPIO. 3) 16 gpio23 (GPIO. 4)
441 17 3.3v 18 gpio24 (GPIO. 5)
442 19 gpio10 (MOSI) 20 0v
443 21 gpio9 (MISO) 22 gpio25 (GPIO. 6)
444 23 gpio11 (SCLK) 24 gpio8 (CE0)
446 27 gpio0 (SDA.0) 28 gpio1 (SCL.0)
447 29 gpio5 (GPIO.21) 30 0v
448 31 gpio6 (GPIO.22) 32 gpio12 (GPIO.26)
449 33 gpio13 (GPIO.23) 34 0v
450 35 gpio19 (GPIO.24) 36 gpio16 (GPIO.27)
451 37 gpio26 (GPIO.25) 38 gpio20 (GPIO.28)
452 39 0v 40 gpio21 (GPIO.29)