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_kernel = $ENV{kernel} = 1;
16 'invert!' => \$opt_invert,
17 'vertical!' => \$opt_vertical,
18 'kernel!' => \$opt_kernel,
22 my $font_w = 1.67; # < 2.54, font is not perfect square
23 my $font_b = 2.10; # font baseline position
26 open(my $fh, '<', shift);
33 my $model = slurp('/proc/device-tree/model');
34 $model =~ s/\x00$//; # strip kernel NULL
35 warn "# model [$model]";
44 warn "MODEL [$1] == [$model] ?\n";
45 if ( $model =~ m/$1/ ) {
50 } elsif ( m/^#\s+/ ) {
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 warn "# pins ",dump($pins);
70 open(my $fh, '<', '/sys/kernel/debug/pinctrl/pinctrl-handles');
73 if ( m/device: [0-9a-f]+\.(\w+)/ ) {
75 } elsif ( m/group: (\w+\d+)\s.+function: (\S+)/ ) {
76 my ($pin, $function) = ($1,$2);
77 $pin_function->{$pin} = "$device $function";
79 next unless $opt_kernel;
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]*(sclk?|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 my @v = split(/\s*\t+\s*/,$line,4);
114 push @line_parts, [ @v ];
115 foreach my $i ( 0 .. 3 ) {
116 next unless exists $v[$i];
117 next if $v[$i] =~ m/^#/; # don't calculate comments into max length
118 my $l = length($v[$i]);
119 $max_len[$i] = $l if $l > $max_len[$i];
123 warn "# max_len = ",dump( \@max_len );
124 warn "# line_parts = ",dump( \@line_parts );
126 #print "$_\n" foreach @lines;
128 my $fmt = "%$max_len[0]s %-$max_len[1]s %$max_len[2]s %-$max_len[3]s\n";
134 print qq{<?xml version="1.0" encoding="UTF-8" standalone="no"?>
136 xmlns:dc="http://purl.org/dc/elements/1.1/"
137 xmlns:cc="http://creativecommons.org/ns#"
138 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
139 xmlns:svg="http://www.w3.org/2000/svg"
140 xmlns="http://www.w3.org/2000/svg"
141 xmlns:xlink="http://www.w3.org/1999/xlink"
144 viewBox="0 0 210 297"
151 }; # svg, insert rest of rect
153 print qq{<rect x="0" y="0" width="210" height="297" style="fill:#000000" id="high-contrast"/>} if $opt_invert;
158 my $cols = { # foreground background
159 txt => [ '#000000', '#ffffff' ],
160 pins => [ '#ffffff', '#ff00ff' ],
161 vcc => [ '#ff0000', '#ffff00' ],
162 gnd => [ '#000000', '#00ffff' ],
163 i2c => [ '#008800', '#ffcccc' ],
164 serial=>[ '#000088', '#ccffcc' ],
165 spi => [ '#880000', '#ccccff' ],
170 die "$swap not found in ",dump($cols) unless $cols->{$swap};
171 my ( $c1, $c2 ) = @{ $cols->{$swap} };
172 $cols->{$swap} = [ $c2, $c1 ];
175 swap_cols 'txt' if $opt_invert;
179 my ($name,$x,$y,$col) = @_;
180 $y -= $font_b; # shift box overlay to right vertical position based on font baseline
183 my ($x,$y,$col,$fill) = @_;
184 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};
188 if ( $name =~ m/^(\d+)$/ ) { # pins
190 my ( $fg, $bg ) = @{ $cols->{pins} };
192 my $w = $max_len[$col]*$font_w - 0.1;
195 #print qq{<polygon points="$x,$y $cx,$y $x,$cy $x,$y" stroke="$fg" stroke-width="0.25" fill="$bg" />};
196 #print qq{<polygon points="$x,$cy $cx,$cy $cx,$y $x,$cy" stroke="$bg" stroke-width="0.25" fill="$fg" />};
197 print qq{<rect x="$x" y="$y" width="$w" height="2.54" stroke="$fg" stroke-width="0.3" fill="$bg" />};
198 my ( $fg, $bg ) = @{ $cols->{txt} };
199 print qq{<rect x="$x" y="$y" width="$w" height="2.54" rx="1" ry="1" stroke="$fg" stroke-width="0.3" fill="$bg" />};
203 return qq{ style="fill:$bg"};
206 if ( $name =~ m/(VCC|3V3|3.3V|5v)/i ) {
207 my ($fg,$bg) = @{ $cols->{vcc} };
209 return qq{ style="fill:$fg"};
210 } elsif ( $name =~ m/(G(ND|Round)|VSS|0v)/i ) {
211 my ($fg,$bg) = @{ $cols->{gnd} };
213 return qq{ style="fill:$fg"};
214 } elsif ( $name =~ m/\[(\w+)/ ) { # kernel
216 my ($fg,$bg) = @{ $cols->{txt} };
217 ($fg,$bg) = @{ $cols->{$dev} } if exists $cols->{$dev};
219 return qq{ style="fill:$fg"};
221 my ( $fg, $bg ) = @{ $cols->{txt} };
223 #return qq{ style="fill:$fg"};
230 my @cols_order = ( 0,1,2,3 );
231 my @cols_align = ( '','-','','-' ); # sprintf prefix
233 @cols_order = ( 0,1,3,2 ); # pins outside on the right
234 @cols_align = ( '','-','-','' );
238 my ($fg,$bg) = @{ $cols->{txt} };
239 my $line_fmt = qq{<line x1="%s" y1="%s" x2="%s" y2="%s" style="stroke:$fg;stroke-width:0.10;fill:$bg" />\n};
244 return unless $opt_svg;
245 push @cut_marks, sprintf($line_fmt, $x-5, $y-$font_b, $x+5, $y-$font_b);
246 push @cut_marks, sprintf($line_fmt, $x, $y-$font_b-5, $x, $y-$font_b+5);
250 $max_x += $max_len[$_] * $font_w foreach ( 0 .. 3 );
253 my $last_cut_mark = 0;
255 foreach my $i ( 0 .. $#line_parts ) {
256 $i = $#line_parts - $i if $opt_vertical;
257 my $line = $line_parts[$i];
261 if ( ! exists $line->[0] ) {
262 # before first empty line
263 if ( $last_cut_mark == 0 ) {
267 $y += 15; # make spacing between pinouts
269 } elsif ( $last_cut_mark ) {
275 #warn "CUTMARK no magic";
278 my ($fg,$bg) = @{ $cols->{txt} };
279 my $tspan = qq{<tspan x="$x" y="$y" style="line-height:2.54;fill:$fg;stroke:none;">\n};
282 foreach my $i ( @cols_order ) {
283 next unless $line->[$i];
284 my $text_anchor = 'middle';
285 my $x2 = $x_pos + ( $max_len[$i] * $font_w ) / 2;
286 $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]);
287 $x_pos += $max_len[$i] * $font_w;
290 $tspan .= qq{</tspan>\n};
291 push @later,sprintf $tspan, @$line;
294 # swap pin colors for line stripes
295 swap_cols $_ foreach qw( pins txt );
299 if ( $#$line == 0 ) {
300 print $line->[0], "\n";
302 push @$line, '' while ($#$line < 3); # fill-in single row header
315 style="font-size:2.34px;line-height:2.54px;font-family:'Andale Mono';stroke:none"
316 xml:space="preserve">
320 print @later, qq{</text>\n}, @cut_marks, qq{</g>\n</svg>};
325 # Cubietech Cubieboard2
326 ## U14 (Next to SATA connector)
328 48 PI13 (SPI0-MISO/UART6-RX/EINT25) 47 PI11 (SPI0-CLK/UART5-RX/EINT23)
329 46 PI12 (SPI0-MOSI/UART6-TX/EINT24) 45 PI10 (SPI0-CS/UART5-TX/EINT22)
331 44 3.3V (nc in 2012-08-08) 43 VCC-5V
333 40 PB10 (LCD0-SCK/LCD-PIO1) 39 PB11 (LCD0-SDA/LCD-PIO2)
334 38 Ground 37 PH7 (LCD0-BL-EN/LCD-PIO0/UART5-RX/EINT7)
335 36 XN_TP (TP-X2) 35 YN_TP (TP-Y2)
336 34 XP_TP (TP-X1) 33 YP_TP (TP-Y1)
337 32 PD25 (LCDDE) 31 PB2 (PWM0)
338 30 PD26 (LCDHSYNC/VGA-HSYNC) 29 PD24 (LCDCLK)
339 28 PD23 (LCDD23) 27 PD27 (LCDVSYNC/VGA-VSYNC)
340 26 PD21 (LCDD21) 25 PD22 (LCDD22)
341 24 PD19 (LCDD19/LVDS1N3) 23 PD20 (LCDD20)
342 22 PD17 (LCDD17/LVDS1NC) 21 PD18 (LCDD18/LVDS1P3)
343 20 Ground 19 PD16 (LCDD16/LVDS1PC)
344 18 PD14 (LCDD14/LVDS1P2) 17 PD15 (LCDD15/LVDS1N2)
345 16 PD12 (LCDD12/LVDS1P1) 15 PD13 (LCDD13/LVDS1N1)
346 14 PD10 (LCDD10/LVDS1P0) 13 PD11 (LCDD11/LVDS1N0)
347 12 PD8 (LCDD8/LVDS0P3) 11 PD9 (LCDD9/LVDS0N3)
348 10 PD7 (LCDD7/LVDS0NC) 9 Ground
349 8 PD5 (LCDD5/LVDS0N2) 7 PD6 (LCDD6/LVDS0PC)
350 6 PD3 (LCDD3/LVDS0N1) 5 PD4 (LCDD4/LNVS0P2)
351 4 PD1 (LCDD1/LVDS0N0) 3 PD2 (LCDD2/LVDS0P1)
352 2 Ground 1 PD0 (LCDD0/LVDSP0)
354 # Cubietech Cubieboard2
355 ## U15 (Between Ethernet port and USB ports)
357 1 VCC-5V 2 PH15 (CSI1-PWR/EINT15)
358 3 CSI1-IO-2V8 4 PH14 (CSI1-RST#/EINT14)
359 5 PG0 (CSI1-PCLK/SDC1-CMD) 6 PB18 (TWI1-SCK)
360 7 PB19 (TWI1-SDA) 8 PG3 (CSI1-VSYNC/SDC1-D1)
361 9 PG2 (CSI1-HSYNC/SDC1-D0) 10 PG1 (CSI1-MCLK/SDC1-CLK)
362 11 PG4 (CSI1-D0/SDC1-D2) 12 PG5 (CSI1-D1/SDC1-D3)
363 13 PG6 (CSI1-D2/UART3-TX) 14 PG7 (CSI1-D3/UART3-RX)
364 15 PG8 (CSI1-D4/UART3-RTS) 16 PG9 (CSI1-D5/UART3-CTS)
365 17 PG10 (CSI1-D6/UART4-TX) 18 PG11 (CSI1-D7/UART4-RX)
368 21 FMINL 22 PI4 (SDC3-CMD)
369 23 FMINR 24 PI5 (SDC3-CLK)
370 25 Ground 26 PI6 (SDC3-D0)
371 27 VGA-R 28 PI7 (SDC3-D1)
372 29 VGA-G 30 PI8 (SDC3-D2)
373 31 VGA-B 32 PI9 (SDC3-D3)
375 33 LCD1-VSYNC 34 PE4 (CSI0-D0)
376 35 LCD1-HSYNC 36 PE5 (CSI0-D1)
377 37 Ground 38 PE6 (CSI0-D2)
378 39 AVCC 40 PE7 (CSI0-D3)
379 41 LRADC0 42 PE8 (CSI0-D4)
380 43 CVBS 44 PE9 (CSI0-D5)
381 45 HPL 46 PE10 (CSI0-D6)
382 47 HPR 48 PE11 (CSI0-D7)
384 ## DEBUG serial (middle of board)
392 ## CON3 rpi DIP26-254
396 7 PI3 PWM1 8 PH0 UART3_TX
398 11 PI19 UART2_RX 12 PH2
399 13 PI18 UART2_TX 14 0v
400 15 PI17 UART2_CTS 16 PH21 CAN_RX
401 17 3.3v 18 PH20 CAN_TX
402 19 PI12 SPI0_MOSI 20 0v
403 21 PI13 SPI0_MISO 22 PI16 UART2_RTS
404 23 PI11 SPI0_SCLK 24 PI10 SPI0_CS0
405 25 0v 26 PI14 SPI0_CS1
413 6 PI20 UART7_TX 5 PH3
414 4 PI21 UART7_RX 3 PH5
417 # Raspberry Pi 3 Model B Rev 1.2
421 7 gpio4 (GPIO. 7) 8 gpio14 (TxD)
423 11 gpio17 (GPIO. 0) 12 gpio18 (GPIO. 1)
424 13 gpio27 (GPIO. 2) 14 0v
425 15 gpio22 (GPIO. 3) 16 gpio23 (GPIO. 4)
426 17 3.3v 18 gpio24 (GPIO. 5)
427 19 gpio10 (MOSI) 20 0v
428 21 gpio9 (MISO) 22 gpio25 (GPIO. 6)
429 23 gpio11 (SCLK) 24 gpio8 (CE0)
431 27 gpio0 (SDA.0) 28 gpio1 (SCL.0)
432 29 gpio5 (GPIO.21) 30 0v
433 31 gpio6 (GPIO.22) 32 gpio12 (GPIO.26)
434 33 gpio13 (GPIO.23) 34 0v
435 35 gpio19 (GPIO.24) 36 gpio16 (GPIO.27)
436 37 gpio26 (GPIO.25) 38 gpio20 (GPIO.28)
437 39 0v 40 gpio21 (GPIO.29)