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