1#!/usr/bin/perl -s
2
3##############################################################################
4#
5# Project:  TwlSDK - header generator -
6# File:     header_generator_ioreg.TWL.pl
7#
8# Copyright 2007-2008 Nintendo. All rights reserved.
9#
10# These coded instructions, statements, and computer programs contain
11# proprietary information of Nintendo of America Inc. and/or Nintendo
12# Company Ltd., and are protected by Federal copyright law. They may
13# not be disclosed to third parties or copied or duplicated in any form,
14# in whole or in part, without the prior written consent of Nintendo.
15#
16# $Date:: 2008-10-20#$
17# $Rev: 9005 $
18# $Author: okubata_ryoma $
19##############################################################################
20
21#
22# Header file macro generation script
23#
24# How to use:
25# conv.pl [-v] [-dup] filename1.csv [filename2.csv ...]
26#
27# This outputs the header file(s) filename1.h (filename2.h) from the CSV file for which macro definitions are listed.
28#
29#
30# See (SDKRoot)/docs/private/how-to-make-headers.txt for CSV file format.
31#
32#
33
34use Text::ParseWords;
35
36#
37# Global variables
38#
39$line_no = 0;
40%name_hash = ();
41@hash_array = ();
42
43
44
45
46#
47# verbose output
48#
49sub verbose {
50  if ($verbose_mode == 1) {
51    print STDERR @_;
52  }
53}
54
55
56
57
58#
59# Remove spaces at beginning and end of field.
60#
61sub trim {
62  my @out = @_;
63  for (@out) {
64    s/^\s+//;
65    s/\s+$//;
66  }
67  return @out;
68}
69
70
71
72
73#
74# CSV parsing routine
75#
76sub parse_csv {
77  my @fields = quotewords(",", 0, shift @_);
78  @fields = trim(@fields);
79
80  my $tmp;
81  # Remove the "...." at the end of the line
82  while(defined($tmp = pop @fields) && $tmp =~ /^$/ ) {
83       ;
84  }
85  push @fields, $tmp;
86
87  return @fields;
88}
89
90
91
92
93#
94# Preprocessor
95#
96sub preprocess {
97  return parse_csv(shift @_);
98}
99
100
101
102
103#
104# Check for duplicate macro names
105#
106
107sub check_macro_duplicate {
108  my $name = shift @_;
109  my $condition = shift @_;
110  my $no   = shift @_;
111
112  if ($condition ne "") {
113    return 0;
114  }
115
116  if (exists $name_hash{$name}) {
117    if ($duplicate_ok == 1) {
118      print STDERR  "WARNING: generating duplicate macro \'$name\' in line $no\n";
119    } else {
120      die "ERROR: duplicate macro \'$name\' in line $no";
121    }
122    return 1;
123  } else  {
124    $name_hash{$name} = 1;
125    return 0;
126  }
127}
128
129
130
131#
132# Analyze
133#
134sub analyze {
135  my @fields = @_;
136
137  my $fields;
138  my $tmphash;
139  my $address, $condition, $name, $bitwidth, $rw, $category, $volatile;
140  $tmphash = {};
141
142  $address = shift @fields;
143  die "ERROR: Illegal address \'$address\' in line $line_no\n" unless ($address =~ /^0x[0-9A-Fa-f]+$/);
144
145  $condition = shift @fields;
146  die "ERROR: Illegal condition name \'$condition\' in line $line_no\n" unless ($address =~ /^[A-Za-z0-9_()&|!]+$/);
147
148  $name = shift @fields;
149  die "ERROR: Illegal macro name \'$name\' in line $line_no\n" unless ($name =~ /^[A-Za-z0-9_]+$/);
150  check_macro_duplicate($name, $condition, $line_no);
151
152
153  $bitwidth = shift @fields;
154  if ($bitwidth eq "8") {
155    $mask_format = "0x%02x";
156  } elsif ($bitwidth eq "16") {
157    $mask_format = "0x%04x";
158  } elsif ($bitwidth eq "32") {
159    $mask_format = "0x%08x";
160  } elsif ($bitwidth eq "64") {
161    $mask_format = "0x%016x";
162  } else {
163    die "ERROR: Illegal bitwidth \'$bitwidth\' in line $line_no\n";
164  }
165
166
167  $rw = shift @fields;
168  $category = shift @fields;
169  $volatile = shift @fields;
170
171  $tmphash->{"condition"} = $condition;
172  $tmphash->{"name"} = $name;
173  $tmphash->{"offset"} = $address;
174  $tmphash->{"bitwidth"} = $bitwidth;
175  $tmphash->{"rw"} = $rw;
176  if ($rw eq "r") {
177    $tmphash->{"const"} = "const";
178  } else {
179    $tmphash->{"const"} = "";
180  }
181
182  $tmphash->{"category"} = $category;
183
184  if ($volatile eq "volatile") {
185    $tmphash->{"volatile"} = "v";
186  } elsif ($volatile eq "permanent") {
187    $tmphash->{"volatile"} = "";
188  } else {
189    die "ERROR: specify volatile/permanent in line $line_no\n";
190  }
191
192  @{$tmphash->{"option"}} = splice @fields, 0;
193
194  $tmphash->{"mask_format"} = $mask_format;
195  $tmphash->{"line_no"} = $line_no;
196  return $tmphash;
197}
198
199
200#
201# Add elements to array
202# If there are duplicates, link them as linear list
203#
204sub push_fields {
205    my $hash = shift @_;
206    my $array_num;
207    my $tmp_hash;
208
209    # If the same name is already registered, add it to hash table as linear list
210    if ( exists($index_name_hash{ $hash->{"name"} }) ) {
211        $array_num = $index_name_hash{ $hash->{"name"} };  # Get index of corresponding array
212        # Add so that (condition eq "") comes to the end of linear list
213        $tmp_hash = $hash_array[ $array_num ];
214        if ( $tmp_hash->{"condition"} eq "") {
215            $hash->{"next"} = $tmp_hash;
216            $hash_array[ $array_num] = $hash;
217            return;
218        }
219
220        while ( exists($tmp_hash->{"next"}) ) {
221            if ($tmp_hash->{"next"}->{"condition"} eq "") {
222                $hash->{"next"} = $tmp_hash->{"next"};
223                $tmp_hash->{"next"} = $hash;
224                return;
225            }
226            $tmp_hash = $tmp_hash->{"next"};
227        }
228        $tmp_hash->{"next"} = $hash;
229    } else {
230        $array_num = @hash_array;
231        $index_name_hash{ $hash->{"name"} } = $array_num;
232        push @hash_array, $hash;
233    }
234}
235
236
237#
238# Category information collection
239#
240sub collect_category {
241  my %cat_hash;
242  my $hash;
243  foreach $hash (@hash_array) {
244    $cat_hash{$hash->{"category"}} = 1;
245  }
246  return (keys %cat_hash);
247}
248
249#
250# Merge condition
251#
252sub merge_condition {
253    my $cond1 = shift @_;
254    my $cond2 = shift @_;
255    my $name = shift @_;
256
257    my $merged_cond;
258
259    if ( $cond1 eq "" || $cond2 eq "") {
260        $merged_cond = "";
261        return $merged_cond;
262    }
263
264    if ( "$cond1" eq "!$cond2" || "!$cond1" eq "$cond2" ) {
265        $merge_cond = "";
266    } else {
267        $merged_cond = "$cond1 || $cond2";
268    }
269
270    return $merged_cond;
271}
272
273
274#
275# Get offset definition of register
276#
277sub cull_register_offset_def {
278    my $hash = shift @_;
279
280    my $offset_name;
281    my @tmp_hash;
282    my @def_array = ();
283    my $i, $k;
284
285    $i = 0;
286    while ( 1 ) {
287
288        {
289            $offset_name = "REG_$hash->{'name'}_OFFSET";
290            $hash->{"offset_def"} = $offset_name;
291            $hash->{"address_def"} = "REG_$hash->{'name'}_ADDR";
292            # Keep this variable definition because it is used later
293            $hash->{"valname_def"} = "REG_$hash->{'category'}_$hash->{'name'}";
294            $hash->{"valname_def_new"} = "reg_$hash->{'category'}_$hash->{'name'}";
295        }
296
297        $tmp_hash[$i]->{"condition"}        = $hash->{"condition"};
298        $tmp_hash[$i]->{"name"}             = $hash->{"name"};
299        $tmp_hash[$i]->{"offset_def"}       = $hash->{"offset_def"};
300        $tmp_hash[$i]->{"offset"}           = $hash->{"offset"};
301        $tmp_hash[$i]->{"address_def"}      = $hash->{"address_def"};
302        $tmp_hash[$i]->{"valname_def"}      = $hash->{"valname_def"};
303        $tmp_hash[$i]->{"valname_def_new"}  = $hash->{"valname_def_new"};
304
305        my $val = "(*($hash->{'const'} REGType$hash->{'bitwidth'}$hash->{'volatile'} *) $hash->{'address_def'})";
306
307        $tmp_hash[$i]->{"reg_type"} = $val;
308
309        my $find_flg = 0;
310
311        #If reg_type and offset are identical, merge condition
312        foreach $k (@def_array) {
313            if ( $k->{'condition'} eq "" ) {
314                # Field without conditions should come last, so if it exists ahead already, condition setting is wrong
315                print STDERR "WARNING: illegal condition in \'$k->{'name'}\'\n";
316            }
317
318            if ( ($k->{"reg_type"} eq $tmp_hash[$i]->{"reg_type"}) && ($k->{"offset"} eq $tmp_hash[$i]->{"offset"}) )
319            {
320                $k->{'condition'} = merge_condition( $k->{'condition'}, $tmp_hash[$i]->{'condition'}, $k->{'name'} );
321                $find_flg = 1;
322                last;
323            }
324        }
325        if ($find_flg == 0) {
326            push @def_array, $tmp_hash[$i];
327            $i++;
328        }
329
330        # If still in the list, process the next
331        if (exists($hash->{"next"})) {
332            $hash = $hash->{"next"};
333        } else {
334            last;
335        }
336    }
337
338    return @def_array;
339}
340
341
342#
343# Output
344#
345sub output {
346  my $output_filename = shift @_;
347  my $category = shift @_;
348
349  # Open output file
350
351  open OUT, ">$output_filename" or die "ERROR: Cannot create file \'$output_filename\'\n";
352  my $handle = OUT;
353#  my $handle = STDOUT;
354
355  my $include_guard = $output_filename;
356  $include_guard =~ s/[.\/]/_/g;
357  $include_guard = uc($include_guard). "_";
358
359  #
360  # Output
361  #
362  print $handle <<ENDDOC;
363/*---------------------------------------------------------------------------*
364  Project:  TwlSDK - IO Register List -
365  File:     $output_filename
366
367  Copyright 2007-2008 Nintendo.  All rights reserved.
368
369  These coded instructions, statements, and computer programs contain
370  proprietary information of Nintendo of America Inc. and/or Nintendo
371  Company Ltd., and are protected by Federal copyright law.  They may
372  not be disclosed to third parties or copied or duplicated in any form,
373  in whole or in part, without the prior written consent of Nintendo.
374
375 *---------------------------------------------------------------------------*/
376//
377//  I was generated automatically, don't edit me directly!!!
378//
379#ifndef $include_guard
380#define $include_guard
381
382#ifndef SDK_ASM
383#include <nitro/types.h>
384#include <twl/hw/ARM9/mmap_global.h>
385#endif
386
387#ifdef __cplusplus
388extern "C" {
389#endif
390
391/*
392 * Definition of Register offsets, addresses and variables.
393 */
394
395
396ENDDOC
397    my $hash, $tmp_hash;
398
399    foreach $hash (@hash_array) {
400        next if ($hash->{"category"} ne $category);
401
402        print $handle "/* $hash->{'name'} */\n\n";
403
404        my @reg_array = cull_register_offset_def($hash);  # Optimize duplicate items
405        my $nest_state = 0;
406
407        foreach $tmp_hash (@reg_array)
408        {
409            if ($tmp_hash->{"condition"} ne "") {
410                if ($nest_state == 0) {
411                    $nest_state = 1;
412                    printf $handle "#if %s\n\n", $tmp_hash->{'condition'};
413                } elsif ($nest_state == 1) {
414                    printf $handle "\n#elif %s\n\n", $tmp_hash->{'condition'};
415                }
416            } elsif ($nest_state == 1) {
417                printf $handle "\n#else\n\n";
418            }
419
420            printf $handle "#define %-50s %s\n", $tmp_hash->{'offset_def'}, $tmp_hash->{'offset'};
421            printf $handle "#define %-50s %s\n", $tmp_hash->{"address_def"}, "(HW_REG_BASE + $tmp_hash->{'offset_def'})";
422            printf $handle "#define %-50s %s\n", $tmp_hash->{"valname_def_new"}, $tmp_hash->{"reg_type"};
423
424            if ($nest_state == 1) {
425                printf $handle "// endif %s\n", $tmp_hash->{"condition"}
426            }
427        }
428
429        printf $handle "\n";
430        if ($nest_state == 1) {
431            printf $handle "#endif\n\n";
432        }
433    }
434
435    print $handle <<ENDDOC;
436
437/*
438 * Definitions of Register fields
439 */
440
441ENDDOC
442    my $hash;
443    my $nest_state;
444
445    foreach $hash (@hash_array) {
446      next if ($hash->{"category"} ne $category);
447
448      $nest_state = 0;
449
450      printf $handle "\n";
451      print $handle "/* $hash->{'name'} */\n";
452
453      while ( 1 ) {
454        if ($hash->{"condition"} ne "") {
455            if ($nest_state == 0) {
456                $nest_state = 1;
457                printf $handle "#if %s\n", $hash->{'condition'};
458            } elsif ($nest_state == 1) {
459                printf $handle "#elif %s\n", $hash->{'condition'};
460            }
461        } elsif ($nest_state == 1) {
462            printf $handle "#else\n";
463        }
464
465        my $no = $hash->{"line_no"};
466
467        my @array = @{$hash->{"option"}};
468
469        my $field_macro_func = "#define $hash->{'valname_def'}_FIELD(";
470        my $field_macro_flag = 0;
471        my $field_macro_body = "    (u$hash->{'bitwidth'})( \\\n";
472
473        my $fieldname, $fieldshift, $fieldsize;
474        while($fieldname = shift @array) {
475            my $shift_macro;
476
477            die "ERROR: Field option not good in line $no\n" if (!defined($fieldshift = shift @array));
478            die "ERROR: Field option not good in line $no\n" if (!defined($fieldsize = shift @array));
479
480            print $handle "\n";
481
482            # Output REG_XXX_XXXXX_SHIFT
483            {
484                my $name = "$hash->{'valname_def'}_" . $fieldname . "_SHIFT";
485                check_macro_duplicate($name, $hash->{'condition'}, $no);
486                $shift_macro = $name;
487                printf $handle "#define %-50s %s\n", $name, $fieldshift;
488            }
489
490            # Output REG_XXX_XXXXX_SIZE
491            {
492                my $name = "$hash->{'valname_def'}_" . $fieldname . "_SIZE";
493                check_macro_duplicate($name, $hash->{'condition'}, $no);
494                printf $handle "#define %-50s %s\n", $name, $fieldsize;
495            }
496
497            # Output REG_XXX_XXXXX_MASK
498            {
499                my $width = $hash->{"bitwidth"};
500                my $mask_val = 1 << $fieldshift;
501                my $tmp = $mask_val;
502                my $i;
503                for ($i = 0; $i < $fieldsize - 1; $i++) {
504                    $mask_val = $mask_val << 1;
505                    $mask_val = $mask_val + $tmp;
506                }
507                my $name = "$hash->{'valname_def'}_" . $fieldname . "_MASK";
508                check_macro_duplicate($name, $hash->{'condition'}, $no);
509                printf $handle "#define %-50s $hash->{'mask_format'}\n", $name, $mask_val;
510            }
511
512            # Create one line worth of definition of REG_XXX_XXXXX_FIELD
513            my $fn = lc($fieldname);
514            if ($field_macro_flag == 1) {
515                $field_macro_func = $field_macro_func . ", " . $fn;
516                $field_macro_body = $field_macro_body . " | \\\n" . "    ((u32)($fn) << $shift_macro)";
517            } else {
518                $field_macro_func = $field_macro_func . " " . $fn;
519                $field_macro_body = $field_macro_body . "    ((u32)($fn) << $shift_macro)";
520                $field_macro_flag = 1;
521            }
522        }
523
524        if ($field_macro_flag == 1) {
525            # Output REG_XXX_XXXXX_FIELD
526            $field_macro_func .= " ) \\\n";
527            $field_macro_func .= $field_macro_body;
528            print $handle "\n#ifndef SDK_ASM\n";
529            print $handle $field_macro_func, ")\n";
530            print $handle "#endif\n\n"
531
532        }
533        if ($nest_state == 1) {
534            printf $handle "// endif %s\n", $hash->{"condition"};
535            if ( !exists($hash->{"next"})) {
536                printf $handle "#endif  \n\n";
537            }
538        }
539
540        # If still in the list, display the next
541        if (exists($hash->{'next'})) {
542            $hash = $hash->{'next'};
543        } else {
544            last;
545        }
546      }
547    }
548
549    print $handle <<ENDDOC;
550
551#ifdef __cplusplus
552} /* extern "C" */
553#endif
554
555/* $include_guard */
556#endif
557ENDDOC
558
559  verbose("done.\n");
560}
561
562#
563# Set command line option
564#
565
566
567# If -v or -verbose is specified, verbose mode
568if ($v == 1 || $verbose == 1) {
569  $verbose_mode = 1;
570  verbose("verbose mode on\n");
571} else {
572  $verbose_mode = 0;
573}
574
575# If -nodup is specified, duplicate macro names are not allowed
576if ($nodup == 1) {
577  $duplicate_ok = 0;
578} else {
579  $duplicate_ok = 1;
580}
581
582
583
584
585#
586# Main routine
587#
588foreach $filename (@ARGV) {
589  $line_no = 0;
590  @hash_array = ();
591
592  open INPUTFILE, "$filename" or die  "ERROR: Cannot open file \'$filename\'\n";
593
594  my $line;
595  while($line = <INPUTFILE>) {
596    $line_no++;
597    $line =~ s/\"//g;
598    $line =~ s/\#.*//;
599    next if ($line =~ /^[\s,]*$/);
600
601    @fields = preprocess($line);
602    push @hash_array, analyze(@fields);
603  }
604  verbose("$line_no lines read\n");
605
606  my @categories = collect_category();
607  my $category;
608  my @headerfile_array = ();
609
610
611  foreach $category (@categories) {
612    my $output_filename = $filename;
613    if (($output_filename =~ s/\.csv/\.h/) == 0) {
614      $output_filename .= ".h";
615    }
616    $output_filename =~ s/\.h/_$category.h/;
617
618    push @headerfile_array, $output_filename;
619
620    verbose("Output filename is $output_filename\n");
621    output($output_filename, $category);
622  }
623
624  my $master_filename = $filename;
625  if (($master_filename =~ s/\.csv/\.h/) == 0) {
626    $master_filename .= ".h";
627  }
628
629  open MASTER, ">$master_filename" or die "ERROR: Cannot create file \'$master_filename$'\n";
630
631  my $include_guard = $master_filename;
632  $include_guard =~ s/[.\/]/_/g;
633  $include_guard = uc($include_guard). "_";
634
635  print MASTER <<ENDDOC;
636/*---------------------------------------------------------------------------*
637  Project:  TwlSDK - IO Register List -
638  File:     $master_filename
639
640  Copyright 2007-2008 Nintendo.  All rights reserved.
641
642  These coded instructions, statements, and computer programs contain
643  proprietary information of Nintendo of America Inc. and/or Nintendo
644  Company Ltd., and are protected by Federal copyright law.  They may
645  not be disclosed to third parties or copied or duplicated in any form,
646  in whole or in part, without the prior written consent of Nintendo.
647
648 *---------------------------------------------------------------------------*/
649
650#ifndef $include_guard
651#define $include_guard
652
653#ifdef __cplusplus
654extern "C" {
655#endif
656
657ENDDOC
658
659  foreach $header_file (@headerfile_array) {
660    print MASTER "#include <$header_file>\n"
661  }
662
663  print MASTER <<ENDDOC;
664
665#ifdef __cplusplus
666} /* extern "C" */
667#endif
668
669/* $include_guard */
670#endif
671ENDDOC
672
673
674}
675
676
677
678
679
680