fca9ccc2406cc73ac49f4e4d2bdf7e897b4d1abe
[fw/sdcc] / support / scripts / inc2h-pic16.pl
1 #!/usr/bin/perl -w
2
3 use strict;
4
5 # Parse MPASM include files to extract SDCC header/device library files
6 # This script is (c) 2007 by Raphael Neider <rneider AT web.de>,
7 # it is licensed under the terms of the GPL v2.
8 #
9 # Usage: perl inc2h-pic16.pl /path/to/pDEVICE.inc
10 # where pDEVICE.inc might be contained in a gputils(.sf.net) package.
11 #
12 # Steps to add a new target device to SDCC/PIC16:
13 #
14 # 1. Create picDEVICE.c and picDEVICE.h from pDEVICE.inc using
15 #    perl inc2h-pic16.pl /path/to/pDEVICE.inc
16 # 2. mv picDEVICE.h $SDCC/device/include/pic16
17 # 3. mv picDEVICE.c $SDCC/device/lib/pic16/libdev
18 # 4. add DEVICE to $SDCC/device/lib/pic16/pics.all (and .build)
19 # 5. adjust $SDCC/device/lib/pic16/libio/*.ignore if the device
20 #    does not support ADC, I2C, or USART
21 # 6. edit $SDCC/device/include/pic16/pic18fregs.h
22 # 7. edit $SDCC/src/pic16/devices.inc
23 #
24 # The file format of steps 6 and 7 is self explanatory, in most
25 # if not all cases you can copy and paste another device's records
26 # and adjust them to the newly added device.
27 #
28
29 my $SCRIPT = $0;
30 $SCRIPT =~ s/.*\///g; # remove path prefix
31
32 sub max
33 {
34     my ($a,$b) = @_;
35     if ($a < $b) { return $b; }
36     else { return $a; }
37 }
38
39 sub LOG
40 {
41     foreach my $i (@_) {
42       print $i;
43     }
44 }
45
46 sub setup
47 {
48     my ($proc) = @_;
49     $proc = lc ($proc);
50     $proc =~ s,^pic,,;
51     $proc =~ s,^p,,;
52     my $header = "pic${proc}.h";
53     my $library = "pic${proc}.c";
54     open (HEADER, '>', "$header") or die "Could not open header file $header: $!";
55     open (LIBRARY, '>', "$library") or die "Could not open library file $library: $!.";
56
57
58     $proc = uc($proc);
59
60     print HEADER <<"HEREDOC"
61 /* 
62  * $header - device specific declarations
63  *
64  * This file is part of the GNU PIC library for SDCC,
65  * originally devised by Vangelis Rokas <vrokas AT otenet.gr>
66  *
67  * It has been automatically generated by $SCRIPT,
68  * (c) 2007 by Raphael Neider <rneider AT web.de>
69  */
70
71 #ifndef __PIC${proc}_H__
72 #define __PIC${proc}_H__ 1
73
74 HEREDOC
75 ;
76
77     print LIBRARY <<"HEREDOC"
78 /*
79  * $library - device specific definitions
80  *
81  * This file is part of the GNU PIC library for SDCC,
82  * originally devised by Vangelis Rokas <vrokas AT otenet.gr>
83  *
84  * It has been automatically generated by $SCRIPT,
85  * (c) 2007 by Raphael Neider <rneider AT web.de>
86  */
87
88 #include <$header>
89
90 HEREDOC
91 ;
92 }
93
94 sub release
95 {
96     print HEADER <<HEREDOC
97
98 #endif
99
100 HEREDOC
101 ;
102
103     print LIBRARY <<HEREDOC
104
105 HEREDOC
106 ;    
107     close HEADER;
108     close LIBRARY;
109 }
110
111 sub header
112 {
113     my $i;
114     foreach $i (@_) {
115         print HEADER $i;
116         #print $i;
117     }
118 }
119
120 sub library
121 {
122     my $i;
123     foreach $i (@_) {
124         print LIBRARY $i;
125         #print $i;
126     }
127 }
128
129 sub DEFINE
130 {
131     my ($name, $val, $comment) = @_;
132     header (sprintf("#define\t%-20s\t%s", $name, $val));
133     if (defined $comment) { header ("\t // $comment"); }
134     header "\n";
135 }
136
137 #######################
138 # main
139 #######################
140
141 my $state = 0;
142 my $sfrs = {};
143 my ($processor, $name);
144
145 while (<>) {
146     # extract processor type
147     chomp;
148     s/\s+/ /g;
149     next if (/^\s*$/);
150     
151     if (/IFNDEF _*(18.*[0-9]+)/i) {
152         $processor = lc($1);
153         #LOG "Found processor: $processor.\n";
154         setup($processor);
155         next;
156     }
157
158     # extract SFR declarations
159     if (/;--.*Register Files.*--/i) {
160         $state = 1;
161     }
162     if ($state == 1 and /(\w+) EQU H'([0-9a-f]+)/i) {
163         my $addr = oct("0x" . $2);
164         $name = uc ($1);
165         $sfrs->{"$name"} = { "addr" => $addr,
166             "maxnames" => 0,
167             "bit0" => [],
168             "bit1" => [],
169             "bit2" => [],
170             "bit3" => [],
171             "bit4" => [],
172             "bit5" => [],
173             "bit6" => [],
174             "bit7" => [],
175         };
176
177         #LOG sprintf("Found register definition: $name @ 0x%X.\n", $addr);
178         next;
179     } elsif ($state == 1 and /(\w+) EQU ([0-9]+)/i) {
180         my $addr = 0+$2;
181         $name = uc ($1);
182         $sfrs->{"$name"} = { "addr" => $addr,
183             "maxnames" => 0,
184             "bit0" => [],
185             "bit1" => [],
186             "bit2" => [],
187             "bit3" => [],
188             "bit4" => [],
189             "bit5" => [],
190             "bit6" => [],
191             "bit7" => [],
192         };
193
194         #LOG sprintf("Found register definition: $name @ 0x%X.\n", $addr);
195         next;
196     }
197
198     # extract device id positions
199     if (/(_DEVID[0-9a-f]*) EQU H'([0-9a-f]+)/i) {
200         my $addr = oct("0x" . $2);
201         #LOG sprintf("Found device ID $1 at 0x%X.\n", $addr);
202         if ($state != 6) {
203             #print "\n// device IDs\n";
204             $state = 6;
205         }
206         DEFINE ($1, sprintf ("0x%X", $addr));
207         next;
208     }
209
210     if (/(_IDLOC[0-9a-f]*) EQU H'([90-9a-f]+)/i) {
211         my $addr = oct("0x" . $2);
212         #LOG sprintf("Found ID location: $1 at 0x%X.\n", $addr);
213         if ($state != 5) {
214             #print "\n// ID locations\n";
215             $state = 5;
216         }
217         DEFINE ($1, sprintf ("0x%X", $addr));
218         next;
219     }
220
221     # extract configuration bits
222     if (/Configuration Bits/i) {
223         $state = 3;
224         #print "\n\n// Configuration Bits\n";
225         header "\n\n// Configuration Bits\n";
226         next;
227     }
228
229     if ($state == 3 and /(_\w+) EQU H'([0-9a-f]+)/i) {
230         $name = $1;
231         my $addr = oct("0x" . $2);
232         # convert to double underscore form for SDCC internal consistency
233         $name =~ s/^_//g;
234         $name = "__".$name;
235         #LOG sprintf("Found config word $1 at 0x%X.\n", $addr);
236         DEFINE ($name, sprintf ("0x%X", $addr));
237         next;
238     }
239
240     if (($state == 3 or $state == 4) and /;--+ ((\w+) Options) --/i) {
241         $name = uc($2);
242         $state = 4;
243         #print "\n// $1\n";
244         header "\n// $1\n";
245         next;
246     }
247     if ($state == 4 and /(\w+) EQU H'([0-9a-f]+)(.*)/i) {
248         my $option = $1;
249         my $mask = oct ("0x" . $2);
250         my $comment = $3;
251         if ($comment =~ /[^;]*;\s*(.*)$/) {
252             $comment = $1;
253         }
254         #LOG sprintf ("Found config option $option, mask 0x%X in $name; comment: $comment.\n", $mask);
255         DEFINE ($option, sprintf("0x%X", $mask), $comment);
256         next;
257     }
258
259     # extract bit definitions
260     if (/;\s*-+\s*(\w+)\s*(Bits)?\s*-+/i) {
261         $state = 2;
262         $name = uc ($1);
263         next;
264     }
265     if ($state == 2 and /(\w+) EQU H'([0-9a-f]+)/i) {
266         my $bit = oct("0x" . $2);
267         #LOG "Found bit declaration: $1 as bit $bit in reg $name.\n";
268         push @{$sfrs->{"$name"}->{"bit$bit"}}, $1;
269         $sfrs->{"$name"}->{"maxnames"} = max(
270             scalar @{$sfrs->{"$name"}->{"bit$bit"}},
271             $sfrs->{"$name"}->{"maxnames"}
272         );
273         next;
274     } elsif ($state == 2 and /(\w+) EQU ([0-9]+)/i) {
275         #print "@@@@ FOUND $1 $2 for $name\n";
276         my $bit = 0+$2;
277         #LOG "Found bit declaration: $1 as bit $bit in reg $name.\n";
278         push @{$sfrs->{"$name"}->{"bit$bit"}}, $1;
279         $sfrs->{"$name"}->{"maxnames"} = max(
280             scalar @{$sfrs->{"$name"}->{"bit$bit"}},
281             $sfrs->{"$name"}->{"maxnames"}
282         );
283         next;
284     }
285
286     # unknown/unhandled line
287     #print "// $_\n";
288 }
289
290 header "\n";
291 library "\n";
292 my $namelut = {};
293 foreach my $reg (keys %$sfrs) {
294     if (!defined $namelut->{$sfrs->{"$reg"}->{"addr"}}) {
295         $namelut->{$sfrs->{"$reg"}->{"addr"}} = ();
296     }
297     push @{$namelut->{$sfrs->{"$reg"}->{"addr"}}}, $reg;
298 }
299
300 foreach my $idx (sort keys %$namelut) {
301     foreach my $reg (sort @{$namelut->{$idx}}) {
302         my $names = $sfrs->{"$reg"}->{"maxnames"};
303
304         header sprintf ("extern __sfr __at (0x%03X) %s;\n", $idx, $reg);
305         library sprintf (      "__sfr __at (0x%03X) %s;\n", $idx, $reg);
306
307         #print sprintf ("$reg @ %X (<= %d bit names)\n", $sfrs->{"$reg"}->{"addr"}, $names);
308         if ($names > 0) {
309             header sprintf ("typedef union {\n");
310
311             for (my $j=0; $j < $names; $j++) {
312                 header sprintf ("\tstruct {\n");
313                 for (my $bit=0; $bit < 8; $bit++) {
314                     my $bitname = $sfrs->{"$reg"}->{"bit$bit"}->[$j];
315                     if (defined $bitname) {
316                         header sprintf ("\t\tunsigned %-10s\t: 1;\n", $bitname);
317                     } else {
318                         header sprintf ("\t\tunsigned %-10s\t: 1;\n", "");
319                     }
320                 }
321                 header sprintf ("\t};\n");
322             }
323
324             header sprintf ("} __${reg}bits_t;\n");
325             header sprintf ("extern volatile __${reg}bits_t __at (0x%03X) ${reg}bits;\n", $idx);
326             library sprintf (      "volatile __${reg}bits_t __at (0x%03X) ${reg}bits;\n", $idx);
327         }
328
329         header "\n";
330         library "\n";
331     }
332 }
333
334 release;