5 Copyright (C) 2012-2015 Molnar Karoly <molnarkaroly@users.sf.net>
7 This library is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 2, or (at your option) any
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this library; see the file COPYING. If not, write to the
19 Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston,
22 ================================================================================
24 This program optimizes or unoptimizes the pic16devices.txt file.
25 For more explanation: optimize_pic16devices.pl -h
32 no if $] >= 5.018, warnings
=> "experimental::smartmatch"; # perl 5.16
33 use 5.12.0; # when (regex)
34 use POSIX
'ULONG_MAX';
36 use constant FALSE
=> 0;
37 use constant TRUE
=> 1;
39 use constant FNV1A32_INIT
=> 0x811C9DC5;
40 use constant FNV1A32_PRIME
=> 0x01000193;
46 use constant OP_NULL
=> 0;
47 use constant OP_OPTIMIZE
=> 1;
48 use constant OP_UNOPTIMIZE
=> 2;
50 my $operation = OP_NULL
;
52 my @devices_header = ();
53 my @device_names = ();
55 #-----------------------------------------------
58 The structure of one element of the %devices_by_name:
90 use constant RELEVANCE_RAM
=> 2;
91 use constant RELEVANCE_CONFWORD
=> 4;
92 use constant RELEVANCE_IDWORD
=> 2;
93 use constant RELEVANCE_FATAL
=> 1000;
95 my %devices_by_name = ();
97 # @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
98 # @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
99 #@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@
100 #@@@@@@@@@@@@@@@@@@@@@@@@ Some auxiliary function. @@@@@@@@@@@@@@@@@@@@@@@@@@@
101 #@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@
102 # @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
103 # @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
107 return ($_[0] =~ /([^\/]+)$/) ?
$1 : '';
110 #---------------------------------------------------------------------------------------------------
114 die "This option \"$_[0]\" requires a parameter.\n" if ($_[1] > $#ARGV);
117 #---------------------------------------------------------------------------------------------------
123 return hex($1) if ($Str =~ /^0x([[:xdigit:]]+)$/io);
124 return oct($1) if ($Str =~ /^(0[0-7]+)$/o);
125 return int($Str) if ($Str =~ /^-?\d+$/o);
127 die "This string not integer: \"$Str\"";
130 #---------------------------------------------------------------------------------------------------
134 return if (pop(@_) > $verbose);
135 foreach (@_) { print STDERR
$_; }
139 #---------------------------------------------------------------------------------------------------
143 my ($File, $Function) = @_;
146 open($handle, '<', $File) || die "${Function}(): Could not open the \"$File\" file.\n";
150 #---------------------------------------------------------------------------------------------------
154 my ($String, $Hash) = @_;
156 foreach (unpack('C*', $String))
159 $Hash *= FNV1A32_PRIME
;
166 #---------------------------------------------------------------------------------------------------
168 sub fnv1a32_int32
($$)
170 my ($Int, $Hash) = @_;
173 for ($i = 4; $i; --$i)
175 $Hash ^= $Int & 0xFF;
176 $Hash *= FNV1A32_PRIME
;
184 #---------------------------------------------------------------------------------------------------
186 sub versionCompare
($$)
188 my ($Str1, $Str2) = @_;
190 if ((${$Str1} =~ /^\d/o) && (${$Str2} =~ /^\d/o))
192 # $Str1 number and $Str2 number
193 return (int(${$Str1}) <=> int(${$Str2}));
196 return (${$Str1} cmp ${$Str2});
199 #---------------------------------------------------------------------------------------------------
203 my @a_s = ($_[0] =~ /(\d+|\D+)/go);
204 my @b_s = ($_[1] =~ /(\d+|\D+)/go);
205 my ($i, $k, $end, $ret);
226 for ($i = 0; $i < $end; ++$i)
228 $k = versionCompare
(\
$a_s[$i], \
$b_s[$i]);
230 return $k if ($k != 0);
236 # @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
237 # @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
238 #@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
239 #@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ The important procedures. @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
240 #@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
241 # @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
242 # @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
245 # Reads the entire pic16devices.txt file.
248 sub read_pic16devices_txt
($)
251 my ($parent, $first, $last, $txt, $ref, $is_using);
252 my $in = Open
($File, 'read_pic16devices_txt');
256 Log
("Reads the $File file.", 4);
264 $header = FALSE
if (/^\s*name\b/io);
268 push(@devices_header, $_);
274 when (/^\s*name\s+(\w+)$/io)
306 $devices_by_name{$1} = $device;
310 when (/^\s*using\s+(\w+)$/io)
312 die "Device not exists." if (! defined($device));
314 $parent = $devices_by_name{$1};
316 die "In device - \"$device->{NAME}\" - not exists the parent: \"$1\"\n" if (! defined($parent));
318 # Unlock the "using" keyword.
320 Log
("using : $1", 7);
321 %{$device->{RAM
}} = %{$parent->{RAM
}};
322 $device->{CONFIG
}->{FIRST
} = $parent->{CONFIG
}->{FIRST
};
323 $device->{CONFIG
}->{LAST
} = $parent->{CONFIG
}->{LAST
};
324 %{$device->{CONFIG
}->{WORDS
}} = %{$parent->{CONFIG
}->{WORDS
}};
325 $device->{ID
}->{FIRST
} = $parent->{ID
}->{FIRST
};
326 $device->{ID
}->{LAST
} = $parent->{ID
}->{LAST
};
327 %{$device->{ID
}->{WORDS
}} = %{$parent->{ID
}->{WORDS
}};
328 $device->{XINST
} = $parent->{XINST
};
332 when (/^\s*ramsize\s+(\w+)$/io)
334 die "Device not exists." if (! defined($device));
336 Log
("ramsize : $1", 7);
337 $device->{RAM
}->{SIZE
} = str2int
($1);
340 when (/^\s*split\s+(\w+)$/io)
342 die "Device not exists." if (! defined($device));
344 Log
("split : $1", 7);
345 $device->{RAM
}->{SPLIT
} = str2int
($1);
348 when (/^\s*configrange\s+(\w+)\s+(\w+)$/io)
350 die "Device not exists." if (! defined($device));
352 ($first, $last) = (str2int
($1), str2int
($2));
353 Log
("configrange: $first, $last", 7);
355 if (($device->{CONFIG
}->{FIRST
} >= 0) || ($device->{CONFIG
}->{LAST
} >= 0))
357 Log
("The configrange already exists in the \"$device->{NAME}\".", 0);
359 if (($device->{CONFIG
}->{FIRST
} != $first) || ($device->{CONFIG
}->{LAST
} != $last))
361 Log
(" In addition the previous values different from the new values.", 0);
362 Log
(sprintf(" previous: 0x%06X - 0x%06X, new: 0x%06X - 0x%06X",
363 $device->{CONFIG
}->{FIRST
}, $device->{CONFIG
}->{LAST
},
367 # The previous values invalid.
369 $device->{CONFIG
}->{WORDS
} = {};
372 $device->{CONFIG
}->{FIRST
} = $first;
373 $device->{CONFIG
}->{LAST
} = $last;
376 when (/^\s*configword\s+(\w+)\s+(\w+)\s+(\w+)(?:\s+(\w+))?$/io)
378 my ($addr, $mask, $val, $amask, $hash);
380 die "Device not exists." if (! defined($device));
382 ($addr, $mask, $val) = (str2int
($1), str2int
($2), str2int
($3));
386 $amask = str2int
($4);
387 Log
("configword : $addr, $mask, $val, $amask", 7);
392 Log
("configword : $addr, $mask, $val", 7);
395 $hash = fnv1a32_int32
($addr, FNV1A32_INIT
);
396 $hash = fnv1a32_int32
($mask, $hash);
397 $hash = fnv1a32_int32
($val, $hash);
398 $hash = fnv1a32_int32
($amask, $hash);
407 if ($is_using && ! defined($device->{CONFIG
}->{WORDS
}->{$addr}))
409 printf STDERR
"Database error: The 0x%06X config word not exist in the ancestor MCU!\n", $addr;
413 $device->{CONFIG
}->{WORDS
}->{$addr} = $ref;
416 when (/^\s*XINST\s+(\w+)\s*$/io)
418 die "Device not exists." if (! defined($device));
420 Log
("XINST : $1", 7);
421 $device->{XINST
} = str2int
($1);
424 when (/^\s*idlocrange\s+(\w+)\s+(\w+)$/io)
426 die "Device not exists." if (! defined($device));
428 Log
("idlocrange : $1 $2", 7);
429 $device->{ID
}->{FIRST
} = str2int
($1);
430 $device->{ID
}->{LAST
} = str2int
($2);
433 when (/^\s*idword\s+(\w+)\s+(\w+)$/io)
435 my ($addr, $val, $hash);
437 die "Device not exists." if (! defined($device));
439 ($addr, $val) = (str2int
($1), str2int
($2));
440 Log
("idword : $1 $2", 7);
441 $hash = fnv1a32_int32
($addr, FNV1A32_INIT
);
442 $hash = fnv1a32_int32
($val, $hash);
449 if ($is_using && ! defined($device->{ID
}->{WORDS
}->{$addr}))
451 printf STDERR
"Database error: The 0x%06X id word not exist in the ancestor MCU!\n", $addr;
455 $device->{ID
}->{WORDS
}->{$addr} = $ref;
460 die "Device not exists." if (! defined($device));
462 Log
("comment : \"$_\"", 7);
463 push(@
{$device->{COMMENTS
}}, $_);
468 Log
("unrecognized line: \"$_\"", 7);
476 #---------------------------------------------------------------------------------------------------
481 my ($ref1, $ref2, $hash);
483 $ref1 = $DevRef->{RAM
};
484 $hash = fnv1a32_int32
($ref1->{SIZE
}, FNV1A32_INIT
);
485 $ref1->{HASH
} = fnv1a32_int32
($ref1->{SPLIT
}, $hash);
489 $ref1 = $DevRef->{CONFIG
};
490 $hash = fnv1a32_int32
($ref1->{FIRST
}, FNV1A32_INIT
);
491 $hash = fnv1a32_int32
($ref1->{LAST
}, $hash);
493 @
{$ref1->{ORD_WORDS
}} = sort {$a->{ADDRESS
} <=> $b->{ADDRESS
}} values %{$ref1->{WORDS
}};
495 foreach $ref2 (@
{$ref1->{ORD_WORDS
}})
497 $hash = fnv1a32_int32
($ref2->{ADDRESS
}, $hash);
498 $hash = fnv1a32_int32
($ref2->{MASK
}, $hash);
499 $hash = fnv1a32_int32
($ref2->{VALUE
}, $hash);
500 $hash = fnv1a32_int32
($ref2->{AND_MASK
}, $hash);
503 $ref1->{HASH
} = $hash;
507 $ref1 = $DevRef->{ID
};
511 $hash = fnv1a32_int32
($ref1->{FIRST
}, FNV1A32_INIT
);
512 $hash = fnv1a32_int32
($ref1->{LAST
}, $hash);
514 if (defined($ref1->{WORDS
}))
516 @
{$ref1->{ORD_WORDS
}} = sort {$a->{ADDRESS
} <=> $b->{ADDRESS
}} values %{$ref1->{WORDS
}};
518 foreach $ref2 (@
{$ref1->{ORD_WORDS
}})
520 $hash = fnv1a32_int32
($ref2->{ADDRESS
}, $hash);
521 $hash = fnv1a32_int32
($ref2->{VALUE
}, $hash);
525 $ref1->{HASH
} = $hash;
529 #---------------------------------------------------------------------------------------------------
531 sub difference_of_arrays
($$)
533 my ($ArrayRef1, $ArrayRef2) = @_;
534 my ($diff, $len, $i);
536 $len = @
{$ArrayRef1};
537 # The lenght of two arrays must be of equal.
538 return RELEVANCE_FATAL
if ($len != scalar(@
{$ArrayRef2}));
541 for ($i = 0; $i < $len; ++$i)
543 if ($ArrayRef1->[$i]->{ADDRESS
} != $ArrayRef2->[$i]->{ADDRESS
})
545 $diff += RELEVANCE_FATAL
;
546 $ArrayRef1->[$i]->{DIFF
} = TRUE
;
548 elsif ($ArrayRef1->[$i]->{HASH
} != $ArrayRef2->[$i]->{HASH
})
550 $diff += RELEVANCE_CONFWORD
;
551 $ArrayRef1->[$i]->{DIFF
} = TRUE
;
555 $ArrayRef1->[$i]->{DIFF
} = FALSE
;
562 #---------------------------------------------------------------------------------------------------
565 # Compares the $Dev1 and the $Dev2.
568 sub difference_of_devices
($$)
570 my ($DevRef1, $DevRef2) = @_;
571 my ($diff, $r1, $r2, $aref1, $aref2, $len1, $len2, $min, $i);
573 $i = defined($DevRef1) + defined($DevRef2);
575 return RELEVANCE_FATAL
if ($i != 2);
578 $r1 = $DevRef1->{RAM
};
579 $r2 = $DevRef2->{RAM
};
581 if ($r1->{HASH
} != $r2->{HASH
})
583 $diff += RELEVANCE_RAM
;
591 $r1 = $DevRef1->{CONFIG
};
592 $r2 = $DevRef2->{CONFIG
};
594 if ($r1->{HASH
} != $r2->{HASH
})
596 $diff += RELEVANCE_FATAL
if ($r1->{FIRST
} != $r2->{FIRST
});
597 $diff += RELEVANCE_FATAL
if ($r1->{LAST
} != $r2->{LAST
});
598 $diff += difference_of_arrays
($r1->{ORD_WORDS
}, $r2->{ORD_WORDS
});
606 $r1 = $DevRef1->{ID
};
607 $r2 = $DevRef2->{ID
};
609 if ($r1->{HASH
} != $r2->{HASH
})
611 $diff += RELEVANCE_FATAL
if ($r1->{FIRST
} != $r2->{FIRST
});
612 $diff += RELEVANCE_FATAL
if ($r1->{LAST
} != $r2->{LAST
});
613 $diff += difference_of_arrays
($r1->{ORD_WORDS
}, $r2->{ORD_WORDS
});
621 # The value of two XINST elements must be of equal.
622 $diff += RELEVANCE_FATAL
if ($DevRef1->{XINST
} != $DevRef1->{XINST
});
626 #---------------------------------------------------------------------------------------------------
628 sub print_config_words
($)
632 return if (! defined($Words));
636 printf "configword 0x%06X 0x%02X 0x%02X", $_->{ADDRESS
}, $_->{MASK
}, $_->{VALUE
};
637 printf " 0x%02X", $_->{AND_MASK
} if ($_->{AND_MASK
} > 0);
642 #---------------------------------------------------------------------------------------------------
644 sub print_id_words
($)
648 return if (! defined($Words));
652 printf "idword 0x%06X 0x%02X\n", $_->{ADDRESS
}, $_->{VALUE
};
656 #---------------------------------------------------------------------------------------------------
658 sub print_diff_config_words
($)
660 my $ArrayRef = $_[0];
662 foreach (@
{$ArrayRef})
664 next if (! $_->{DIFF
});
666 printf "configword 0x%06X 0x%02X 0x%02X", $_->{ADDRESS
}, $_->{MASK
}, $_->{VALUE
};
667 printf " 0x%02X", $_->{AND_MASK
} if ($_->{AND_MASK
} > 0);
672 #---------------------------------------------------------------------------------------------------
674 sub print_diff_id_words
($)
676 my $ArrayRef = $_[0];
678 foreach (@
{$ArrayRef})
680 next if (! $_->{DIFF
});
682 printf "idword 0x%06X 0x%02X\n", $_->{ADDRESS
}, $_->{VALUE
};
686 #---------------------------------------------------------------------------------------------------
691 my $mcu = $device_names[$Index];
692 my $dev = $devices_by_name{$mcu};
693 my ($min_diff, $diff);
694 my ($ac, $ancestor, $i, $ref1, $ref2);
696 return if (! defined($dev));
698 Log
("Prints the $mcu MCU.", 4);
702 if ($operation == OP_OPTIMIZE
)
704 # Optimized writing is required.
706 $min_diff = ULONG_MAX
;
707 for ($i = 0; $i < scalar(@device_names); ++$i)
709 $ac = $devices_by_name{$device_names[$i]};
711 last if ($Index == $i);
712 next if ($ac->{CHILD
});
714 $diff = difference_of_devices
($dev, $ac);
716 if ($min_diff > $diff)
723 $ancestor = undef if ($min_diff > 15);
726 print "name $dev->{NAME}\n";
728 if ($dev->{COMMENTS
})
730 foreach (@
{$dev->{COMMENTS
}})
738 if (defined($ancestor))
740 $dev->{CHILD
} = TRUE
;
742 print "using $ancestor->{NAME}\n";
743 difference_of_devices
($dev, $ancestor);
747 $ref2 = $ancestor->{RAM
};
749 if ($ref1->{SIZE
} != $ref2->{SIZE
})
751 print "ramsize $ref1->{SIZE}\n";
754 if ($ref1->{SPLIT
} != $ref2->{SPLIT
})
756 printf "split 0x%02X\n", $ref1->{SPLIT
};
760 $ref1 = $dev->{CONFIG
};
764 print_diff_config_words
($ref1->{ORD_WORDS
});
767 printf "XINST $dev->{XINST}\n" if (($ancestor->{XINST
} < 0) && ($dev->{XINST
} > 0));
773 print_diff_id_words
($ref1->{ORD_WORDS
});
778 print "ramsize $ref1->{SIZE}\n";
779 printf "split 0x%02X\n", $ref1->{SPLIT
};
781 $ref1 = $dev->{CONFIG
};
782 printf "configrange 0x%06X 0x%06X\n", $ref1->{FIRST
}, $ref1->{LAST
};
783 print_config_words
($ref1->{ORD_WORDS
});
785 printf "XINST $dev->{XINST}\n" if ($dev->{XINST
} > 0);
789 if (($ref1->{FIRST
} > 0) && ($ref1->{LAST
} > 0))
791 printf "idlocrange 0x%06X 0x%06X\n", $ref1->{FIRST
}, $ref1->{LAST
};
792 print_id_words
($ref1->{ORD_WORDS
});
797 #---------------------------------------------------------------------------------------------------
802 Usage: $PROGRAM <option> path/to/pic16devices.txt > output.txt
808 If a MCU features matches with an earlier listed MCU features,
809 then use the "using" keyword and with this method significantly
810 reduces the file size.
814 Unlocks the "using" keywords and displays the full original
817 -v <level> or --verbose <level>
819 It provides information on from the own operation.
820 Possible value of the level between 0 and 10. (default: 0)
829 # @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
830 # @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
831 #@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
832 #@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ The main program. @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
833 #@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
834 # @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
835 # @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
837 $PROGRAM = basename
($0);
839 for (my $i = 0; $i < @ARGV; )
841 my $opt = $ARGV[$i++];
845 when (/^-(o|-optimize)$/o) { $operation = OP_OPTIMIZE
; }
847 when (/^-(u|-unoptimize)$/o) { $operation = OP_UNOPTIMIZE
; }
849 when (/^-(v|-verbose)$/o)
851 param_exist
($opt, $i);
852 $verbose = int($ARGV[$i++]);
853 $verbose = 0 if (! defined($verbose) || $verbose < 0);
854 $verbose = 10 if ($verbose > 10);
857 when (/^-(h|-help)$/o)
868 } # for (my $i = 0; $i < @ARGV; )
870 if (($file eq '') || ($operation == OP_NULL
))
876 (-f
$file) || die "This file - \"$file\" - not exist!";
878 read_pic16devices_txt
($file);
880 @device_names = sort {versionSort
($a, $b)} keys(%devices_by_name);
882 foreach (@device_names)
884 make_hashes
($devices_by_name{$_});
887 print join("\n", @devices_header) . "\n";
890 my $v = @device_names;