Settings.pm 20.7 KB
Newer Older
1
## @file
2

3
## @class FIJI::Settings
4
#
5
# Contains helper functions to deal with FIJI Settings files.
6
7
package FIJI::Settings;

8
9
10
use strict;
use warnings;

11
use Scalar::Util 'blessed';
12
13
14
15
use Log::Log4perl qw(get_logger);
use Scalar::Util "looks_like_number";
use Config::Simple;

16
17
use FIJI qw(:all);

Stefan Tauner's avatar
Stefan Tauner committed
18
19
20
21
22
## @function new ($phase, $fiji_ini_file, $existing_settings)
# Create a new settings instance.
#
# \param $phase Tool flow phase the settings need to be compatible with.
# \param $fiji_ini_file (optional) Path to the configuration file to read.
23
24
# \param $existing_settings (optional) A reference to reuse and return
#        with values read from file. Any contained data will be cleared.
25
26
# \returns The new settings instance or a string describing the reason
#          why it could not be created.
27
28
sub new ($;$$) {
  my $logger = get_logger();
Stefan Tauner's avatar
Stefan Tauner committed
29
30
31
  my ($class, $phase, $fiji_ini_file, $existing_settings) = @_;

  my $fiji_settings_ref;
32
  # if there is no existing settings instance yet, create one
Stefan Tauner's avatar
Stefan Tauner committed
33
34
35
36
  if (!defined($existing_settings)) {
    $fiji_settings_ref = {};
    $fiji_settings_ref = bless($fiji_settings_ref, $class);
  } else {
37
38
39
40
41
42
43
44
45
46
47
    $fiji_settings_ref = $existing_settings;
  }
  if (!blessed($fiji_settings_ref) || !$fiji_settings_ref->isa("FIJI::Settings")) {
    my $msg;
    if (!defined($existing_settings)) {
      $msg = "Could not create FIJI::Settings instance.";
    } else {
      $msg = "Given settings are not of type FIJI::Settings.";
    }
    $logger->error($msg);
    return $msg;
Stefan Tauner's avatar
Stefan Tauner committed
48
  }
49

50
  # If there is a file given, try to read it. Else just create a default instance.
51
  if (defined($fiji_ini_file)) {
Stefan Tauner's avatar
Stefan Tauner committed
52
    $fiji_settings_ref = read_settingsfile($phase, $fiji_ini_file, $fiji_settings_ref);
53
54
    if (!ref($fiji_settings_ref)) {
      return $fiji_settings_ref; # actually an error message
55
56
    }
  } else {
57
58
    $fiji_settings_ref->{'design'} = {};
    _set_defaults(DESIGNMAP, $fiji_settings_ref->{'design'}, $phase);
59
    $fiji_settings_ref->{'fius'} = [];
60
  }
Stefan Tauner's avatar
Stefan Tauner committed
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
  return $fiji_settings_ref;
}


sub _export_value {
  my $logger = get_logger();
  my ($map_ref, $k, $v_ref) = @_;

  if (defined($map_ref->{$k}->{'type'})) {
    my $orig = ${$v_ref};
    if ($map_ref->{$k}->{'type'} eq 'hexadecimal') {
      ${$v_ref} = sprintf("0x%x", $orig);
    # } elsif ($map_ref->{$k}->{'type'} eq 'natural') {
    # } elsif ($map_ref->{$k}->{'type'} eq 'boolean') {
      $logger->trace("Converted value of $k (\"$orig\") to \"${$v_ref}\".") if ($orig ne ${$v_ref});
    }
  # } elsif (defined($map_ref->{$k}->{'values'})) {
78
79
80
81
82
83
84
85
86
  }
}

## @method save ($fiji_ini_file)
# @brief Store contained FIJI Settings to file.
#
# @ATTENTION Will happily overwrite existing files!
#
# \param fiji_ini_file The file name to write the FIJI Settings to.
Stefan Tauner's avatar
Stefan Tauner committed
87
sub save ($) {
88
89
90
91
92
  my $logger = get_logger();
  my ($self, $fiji_ini_file) = @_;
  return "No file name given" if !defined($fiji_ini_file);

  my $fiji_ini = new Config::Simple(syntax=>'ini');
93
94
  my $design_ref;
  my $fiu_cnt = 0;
95
96
97
98
99
  foreach my $key (keys %{$self}) {
    my $val = $self->{$key};
    if (ref(\$val) eq "REF") {
      if (ref($val) eq "HASH") {
        if ($key eq "design") {
100
          $design_ref = $val;
101
102
103
          next;
        }
      } elsif (ref($val) eq "ARRAY") {
104
        if ($key eq "fius") {
105
106
          foreach my $fiu (@{$val}) {
            my $ini_fiu;
Stefan Tauner's avatar
Stefan Tauner committed
107
            
108
            foreach my $k (keys(%{$fiu})) {
Stefan Tauner's avatar
Stefan Tauner committed
109
              my $ini_name = FIUMAP->{$k}->{'ini_name'};
110
111
112
113
114
              if (!defined($fiu->{$k})) {
                $logger->debug("Skip saving undefined value of FIU constant with key $ini_name.");
                next;
              }
              # Copy value to new hash with external naming.
Stefan Tauner's avatar
Stefan Tauner committed
115
              $ini_fiu->{$ini_name} = $fiu->{$k};
116
              # Convert value to external representation
117
118
119
120
121
122
123
124
              _export_value(FIUMAP, $k, \$ini_fiu->{$ini_name});
              $logger->trace(sprintf("Exporting FIU%d setting %s -> %s (%s -> %s).",
                                     $fiu_cnt,
                                     $k,
                                     $ini_name,
                                     defined($fiu->{$k}) ? $fiu->{$k} : "undef",
                                     defined($ini_fiu->{$ini_name}) ? $ini_fiu->{$ini_name} : "undef"),
                            );
125
126
127
128
129
130
131
132
133
134
135
            }
            $fiji_ini->set_block("FIU" . $fiu_cnt++, $ini_fiu);
          }
          next;
        }
      }
    }
    my $err = "Unknown element found in FIJI Settings: \"$val\"";
    $logger->error($err);
    return $err;
  }
136
  $design_ref->{'FIU_NUM'} = $fiu_cnt;
137
  my $ini_design;
138
  foreach my $k (keys(%{$design_ref})) {
139
140
141
142
143
144
145
146
    my $ini_name = DESIGNMAP->{$k}->{'ini_name'};
    if (!defined($design_ref->{$k})) {
      $logger->debug("Skip saving undefined value of design constant with key $ini_name.");
      next;
    }
    # Copy value to new hash with external naming.
    $ini_design->{$ini_name} = $design_ref->{$k};
    # Convert value to external representation
147
148
149
150
151
152
153
    _export_value(DESIGNMAP, $k, \$ini_design->{$ini_name});
    $logger->trace(sprintf("Exporting Design setting %s -> %s (%s -> %s).",
                           $k,
                           $ini_name,
                           defined($design_ref->{$k}) ? $design_ref->{$k} : "undef",
                           defined($ini_design->{$ini_name}) ? $ini_design->{$ini_name} : "undef"),
                  );
Stefan Tauner's avatar
Stefan Tauner committed
154
  }
155
  $fiji_ini->set_block("CONSTS", $ini_design);
156

157
158
159
160
161
162
163
164
165
  if (!defined($fiji_ini->write($fiji_ini_file))) {
    my $err = Config::Simple->error();
    $logger->error($err);
    return $err;
  }
  return undef;
}


166
## @function read_settingsfile ($phase, $fiji_ini_file)
167
# @brief Load the FIJI Settings file containing design and FIU constants.
168
#
169
# \param phase  Tool flow phase the settings stored in the given file
Stefan Tauner's avatar
Stefan Tauner committed
170
#               need to be compatible with.
171
172
# \param fiji_ini_file The name of an .ini file with FIJI Settings:
#         - a 'consts' block containing the constants specified by
Stefan Tauner's avatar
Stefan Tauner committed
173
#           \ref _sanitize_design.
174
175
176
#         - at least one FIU block named "FIU<number>" where "<number>"
#           is a strictly increasing integer starting with 0 containing
#           the constants for the respective FIU, see \ref _sanitize_fiu
177
178
#
# \returns a reference to the hash containing the read constants.
179
sub read_settingsfile ($$$) {
180
  my $logger = get_logger();
Stefan Tauner's avatar
Stefan Tauner committed
181
  my ($phase, $fiji_ini_file, $existing_settings) = @_;
182
183
184
  my $fiji_ini;
  eval { $fiji_ini = new Config::Simple($fiji_ini_file) }; # pesky library tries to die on syntax errors
  if (!defined($fiji_ini)) {
185
186
187
188
189
    my $submsg = defined($@) ? $@ : Config::Simple->error();
    if (length($submsg) == 0) {
      $submsg = "Empty file?";
    }
    my $msg = "Could not read config file \"$fiji_ini_file\": $submsg";
190
191
    $logger->error($msg);
    return $msg;
192
193
  }

194
195
  my $design_ref = $fiji_ini->get_block("CONSTS");
  if (!(%$design_ref)) {
196
197
198
    my $msg = "Could not fetch CONSTS block from config file \"$fiji_ini_file\"";
    $logger->error($msg);
    return $msg;
199
  }
200
  _set_defaults(DESIGNMAP, $design_ref, $phase);
Stefan Tauner's avatar
Stefan Tauner committed
201

202
203
  $design_ref = _rename_import(DESIGNMAP, $design_ref);
  if (!defined($design_ref)) {
204
205
206
    my $msg = "Design constants do not match the FIJI Settings naming scheme.";
    $logger->error($msg);
    return $msg;
207
  }
Stefan Tauner's avatar
Stefan Tauner committed
208

209
  # Create a new instance or reuse the shallow hull of the existing one
210
  my $fiji_settings_ref;
211
212
213
214
215
216
217
218
  if (!defined($existing_settings)) {
    $fiji_settings_ref = {};
    bless($fiji_settings_ref, "FIJI::Settings");
  } else {
    $fiji_settings_ref = $existing_settings;
    # Clear the hash
    for (keys %$fiji_settings_ref) {
        delete $fiji_settings_ref->{$_};
Stefan Tauner's avatar
Stefan Tauner committed
219
    }
220
221
222
223
224
   }
  if (!blessed($fiji_settings_ref) || !$fiji_settings_ref->isa("FIJI::Settings")) {
    my $msg;
    if (!defined($existing_settings)) {
      $msg = "Could not create FIJI::Settings instance.";
Stefan Tauner's avatar
Stefan Tauner committed
225
    } else {
226
      $msg = "Given settings are not of type FIJI::Settings.";
Stefan Tauner's avatar
Stefan Tauner committed
227
    }
228
229
    $logger->error($msg);
    return $msg;
230
  }
231
  $fiji_settings_ref->{'design'} = $design_ref;
232
  $fiji_settings_ref->{'fius'} = [];
Stefan Tauner's avatar
Stefan Tauner committed
233
234

  # sanitize and validate read design constants
235
236
237
238
  $design_ref = _sanitize_design($design_ref, $phase);
  if (!ref($design_ref)) {
    $logger->error($design_ref);
    return $design_ref;
Stefan Tauner's avatar
Stefan Tauner committed
239
  }
240
241

  my $fiu_num = 0;
242
  # loop over all read fius
243
244
  while (1) {
    my $fiu_name = "FIU" . $fiu_num;
245
246
    my $fiu_ref = $fiji_ini->get_block($fiu_name);
    if (!(%$fiu_ref)) {
247
248
      last;
    }
249
250
    $fiu_ref = _rename_import(FIUMAP, $fiu_ref);
    if (!defined($design_ref)) {
251
252
253
      my $msg = "FIU constants of $fiu_name do not match the FIJI Settings naming scheme.";
      $logger->error($msg);
      return $msg;
Stefan Tauner's avatar
Stefan Tauner committed
254
    }
255

Stefan Tauner's avatar
Stefan Tauner committed
256
257
    my $tmp_fiu = {};
    _set_defaults(FIUMAP, $tmp_fiu, $phase);
258
259
260
    # overwrite default entries
    foreach my $k (keys(%{$fiu_ref})) {
      $tmp_fiu->{$k} = $fiu_ref->{$k};
Stefan Tauner's avatar
Stefan Tauner committed
261
    }
262
263
264
265
266
    $fiu_ref = $tmp_fiu;

    $fiu_ref = _sanitize_fiu($fiu_ref, $phase);
    if (!ref($fiu_ref)) {
      my $msg = "(Some) constants for $fiu_name in FIJI Settings are invalid.";
267
268
      $logger->error($msg);
      return $msg;
269
    }
270
    $fiji_settings_ref->{'fius'}->[$fiu_num] = $fiu_ref;
271
272
273
274
275
    $fiu_num++;
    $logger->trace("Read in $fiu_name from FIJI Settings file successfully.");
  }

  if ($fiu_num == 0) {
276
    $logger->debug("Could not fetch any FIU blocks from config file \"$fiji_ini_file\"");
277
  }
278
279
280

  # FIU_NUM is optional in the Settings file... if it was set check that
  # it corresponds to the number of FIU<number> blocks.
281
  if (defined($design_ref->{'FIU_NUM'}) && $design_ref->{'FIU_NUM'} != $fiu_num) {
282
283
284
      my $msg = FIU_NUM->{'ini_name'} . " does not match the numbers of FIU blocks found.";
      $logger->error($msg);
      return $msg;
285
  } else {
286
    $design_ref->{'FIU_NUM'} = $fiu_num; # assume the best if FIU_NUM constant is not given
287
288
  }

289
  splice(@{$fiji_settings_ref->{'fius'}}, $fiu_num);
290
  $logger->info("Successfully read in design constants and $fiu_num FIU definitions from FIJI Settings file \"$fiji_ini_file\".");
291
292
293
  return $fiji_settings_ref;
}

Stefan Tauner's avatar
Stefan Tauner committed
294
## @method set_fiu_defaults (%$fiu_ref)
295
296
297
298
# @brief Overwrite existing fields (if defined) with defaults defined in FIJI.pm.
#
sub set_fiu_defaults ($) {
  my ($self, $consts_ref) = @_;
Stefan Tauner's avatar
Stefan Tauner committed
299
300
301
302
303
304
305
306
  return _set_defaults(FIUMAP, $consts_ref);
}

## @function _set_defaults (%$map_ref, %$consts_ref)
# @brief Set defaults according to FIJI.pm.
sub _set_defaults {
  my $logger = get_logger();
  my ($map_ref, $consts_ref, $phase) = @_;
307
  foreach my $k (keys(%{$map_ref})) {
308
    if (exists($map_ref->{$k}->{'default'})) {
Stefan Tauner's avatar
Stefan Tauner committed
309
      # Set default only if it is not mandatory in the given phase.
310
311
      if (defined($phase) && scalar(grep {$_ eq $phase} @{$map_ref->{$k}->{'phases_opt'}}) == 0) {
        next;
312
      }
313

314
315
316
317
      # Also, do not overwrite existing values
      if (defined($consts_ref->{$k})) {
        next;
      }
318
319
      $consts_ref->{$k} = $map_ref->{$k}->{default};
      $logger->trace(sprintf("Adding default constant: %s (%s) = %s.", $k, $map_ref->{$k}->{'ini_name'}, $map_ref->{$k}->{default}));
320
321
    }
  }
322
323
}

324

Stefan Tauner's avatar
Stefan Tauner committed
325
326
327
328
329
330
331
332
333
334
sub validate_design_value {
  my ($k, $v_ref) = @_;
  return validate_value(DESIGNMAP, $k, $v_ref);
}

sub validate_fiu_value {
  my ($k, $v_ref) = @_;
  return validate_value(FIUMAP, $k, $v_ref);
}

335
336
337
338
339
340
341
342
343
344
## @function validate_value (%$map_ref, $k, $$v_ref, $old, $log_func)
#   Do validation (and conversation from external->internal representation)
#
#   \param map_ref  reference to FIJI Settings mappings
#   \param k        key identifying the respective setting
#   \param v_ref    scalar reference to the proposed value (that may be modified)
#   \param old      (optional) previously valid value
#   \param log_func (optional) the (log4perl) log function to use
#                   (defaul is \&Log::Log4perl::Logger::trace)
sub validate_value ($$$;$$) {
Stefan Tauner's avatar
Stefan Tauner committed
345
  my $logger = get_logger();
346
  my ($map_ref, $k, $v_ref, $old, $log_func) = @_;
Stefan Tauner's avatar
Stefan Tauner committed
347
348
349
  $log_func = \&Log::Log4perl::Logger::trace if !defined($log_func);
  if (defined($map_ref->{$k}->{'type'})) {
    my $orig = ${$v_ref};
350
351
352
353
354
    # FIXME: check if the constant is depending on another constant, that needs to be enabled.
    # my $dependency = @{$map_ref->{$k}->{'depends_on'}};
    # if (defined($dependency) && defined(@{$map_ref->{$dependency}->{'value'}}
    # }

355
356
357
358
    if ($map_ref->{$k}->{'type'} eq 'net') {
      # TODO: check syntax? there is nothing more to do in here unless we supply allowed nets...
      # which is actually possible because the input netlist is stored in the settings as well
    } elsif ($map_ref->{$k}->{'type'} eq 'hexadecimal') {
Stefan Tauner's avatar
Stefan Tauner committed
359
360
361
      if ($orig !~ /^(0|(0[xX][[:xdigit:]]+))$/) {
        $log_func->($logger, "$k: $orig does not look like a natural hexadecimal number.");
        return 0;
362
      }
Stefan Tauner's avatar
Stefan Tauner committed
363
364
365
366
367
368
369
      ${$v_ref} = hex($orig);
      # Check for natural value range. Should never trigger due to the regex above.
      if (${$v_ref} < 0) {
        $log_func->($logger, "$k: $orig is negative.");
        return 0;
      }
    } elsif ($map_ref->{$k}->{'type'} eq 'natural') {
370
      # Match hexadecimal, binary, octal and decimal numbers (the latter also in scientific notation)
Stefan Tauner's avatar
Stefan Tauner committed
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
      if ($orig !~ /^(0|(0(x[0-9a-fA-F]+|[0-7]+))|(0b[01]+)|([1-9][0-9]*(e(-[0-9])?[0-9]+)?))$/) {
        $log_func->($logger, "$k: $orig does not look like a number.");
        return 0;
      }
      
      # convert non-decimal (hexadecimal, binary, octal) values to decimal
      ${$v_ref} = oct($orig) if $orig =~ /^0/;
      if (!looks_like_number(${$v_ref})) {
        $log_func->($logger, "$k: $orig does not look like a number.");
        return 0;
      }
      # Check for natural value range. Should never trigger due to the regex above.
      if (${$v_ref} < 0) {
        $log_func->($logger, "$k: $orig is negative.");
        return 0;
      }
    } elsif ($map_ref->{$k}->{'type'} eq 'boolean') {
      # convert strings to binary if need be
      if (!defined($orig)) {
        $log_func->($logger, "$k: \"undef\" is not a boolean value.");
        return 0;
      } elsif (lc($orig) eq 'true') {
        $orig = 1;
      } elsif (lc($orig) eq 'false') {
        $orig = 0;
396
      }
Stefan Tauner's avatar
Stefan Tauner committed
397
398
399
400
401
402
      if (($orig ne '0') && ($orig ne '1')) {
        $log_func->($logger, "$k: \"$orig\" does not look like a boolean value.");
        return 0;
      }
      # ensure proper boolean value, i.e. 0 or 1
      ${$v_ref} = (!!$orig) ? 1 : 0;
403
    }
404
    $logger->trace("Converted value of $k (\"$orig\") to \"${$v_ref}\".") if (defined($orig) && $orig ne ${$v_ref});
Stefan Tauner's avatar
Stefan Tauner committed
405
  }
406

Stefan Tauner's avatar
Stefan Tauner committed
407
408
409
410
411
412
413
  if (defined($map_ref->{$k}->{'values'})) {
    my $values_ref = $map_ref->{$k}->{'values'};
    if (ref($values_ref) eq 'ARRAY') {
      # Look for given value in allowed values
      if (scalar(grep {$_ eq ${$v_ref}} @{$values_ref}) == 0) {
        $log_func->($logger, "$k: ${$v_ref} is not allowed. Allowed values are: " . join(", ", @{$map_ref->{$k}->{'values'}}));
        return 0;
414
      }
Stefan Tauner's avatar
Stefan Tauner committed
415
    } elsif (ref($values_ref) eq 'CODE') {
416
      if (!$values_ref->(${$v_ref}, $old)) {
Stefan Tauner's avatar
Stefan Tauner committed
417
418
        $log_func->($logger, "$k: ${$v_ref} is not allowed.");
        return 0;
419
      }
420
421
    }
  }
Stefan Tauner's avatar
Stefan Tauner committed
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
  return 1;
}


# @function _rename_import (%$map_ref, %$consts_ref)
# Rename and convert entries in consts_ref according to map_ref.
#
# This function takes a hash of FIJI Settings and converts its contents
# to the respective internal representation. This allows to use different
# names and value representations in the external file than within the
# implementation.
#
# \returns $consts_ref, or undef on errors
sub _rename_import {
  my $logger = get_logger();
  my ($map_ref, $consts_ref) = @_;
  if (ref($consts_ref) ne 'HASH') {
    $logger->error("Parameter is not a reference to a hash (containing design constants).");
    return undef;
  }

  # Iterating over respective hash from FIJI.pm and rename the entries
  # to match our internal naming scheme.
445
  foreach my $k (keys(%{$map_ref})) {
Stefan Tauner's avatar
Stefan Tauner committed
446
447
448
    my $ini_name = $map_ref->{$k}->{'ini_name'};
    if (exists($consts_ref->{$ini_name}) && $ini_name ne $k) {
      $consts_ref->{$k} = $consts_ref->{$ini_name};
449
450
451
452
453
      $logger->trace(sprintf("Renaming Design setting %s -> %s (=%s).",
                             $ini_name,
                             $k,
                             defined($consts_ref->{$ini_name}) ? $consts_ref->{$ini_name} : "undef"),
                    );
Stefan Tauner's avatar
Stefan Tauner committed
454
455
456
      delete $consts_ref->{$ini_name};
    }
  }
457
  foreach my $entry_key (keys(%{$consts_ref})) {
Stefan Tauner's avatar
Stefan Tauner committed
458
459
460
461
462
463
    if (!exists($map_ref->{$entry_key})) {
      $logger->debug("Deleting unknown setting %s = %s.", $entry_key, $consts_ref->{$entry_key});
      delete $consts_ref->{$entry_key};
    }
  }

464
  return $consts_ref;
465
466
}

Stefan Tauner's avatar
Stefan Tauner committed
467

468
## @function _sanitize_fiu (%$fiu_ref, $phase)
469
470
# @brief Convert and sanity check FIJI Settings.
#
471
472
473
474
475
# \param fiu_ref a reference to a hash containing FIJI Settings for a
#                single FIU.
#
# \returns A new hash with all constants required in the FIU settings
#          in sanitized form, or undef on errors.
Stefan Tauner's avatar
Stefan Tauner committed
476
sub _sanitize_fiu ($;$) {
477
  my $logger = get_logger();
Stefan Tauner's avatar
Stefan Tauner committed
478
  my ($fiu_ref, $phase) = @_;
479
  if (ref($fiu_ref) ne 'HASH') {
480
481
482
    my $msg = "Parameter is not a reference to a hash (containing FIU constants).";
    $logger->error($msg);
    return $msg;
483
484
  }

Stefan Tauner's avatar
Stefan Tauner committed
485
  $fiu_ref = _validate_hashmap(FIUMAP, $fiu_ref, $phase);
486
  if (!ref($fiu_ref)) {
Stefan Tauner's avatar
Stefan Tauner committed
487
      $logger->error("Could not validate Design Constants.");
488
      return $fiu_ref;
489
490
491
492
493
  }

  return $fiu_ref;
}

Stefan Tauner's avatar
Stefan Tauner committed
494

495
496
497
498
499
500
501
sub _disabled_via_dependency ($$$) {
  my ($map_ref, $consts_ref, $k) = @_;
  my $dependency = $map_ref->{$k}->{'depends_on'};
  return defined($dependency) && !$consts_ref->{$dependency};
}


Stefan Tauner's avatar
Stefan Tauner committed
502
503
504
sub _validate_hashmap ($$;$) {
  my $logger = get_logger();
  my ($map_ref, $consts_ref, $phase) = @_;
505
506
  my @map_keys = keys(%{$map_ref});
  foreach my $entry_key (keys(%{$consts_ref})) {
Stefan Tauner's avatar
Stefan Tauner committed
507
508
509
510
511
512
513
    my $ini_name = $map_ref->{$entry_key}->{'ini_name'};
    
    if (!exists($map_ref->{$entry_key})) {
      $logger->debug("Deleting unknown setting %s = %s.", $entry_key, $consts_ref->{$entry_key});
      next;
    }

514
515
516
517
    @map_keys = grep {$_ ne $entry_key} @map_keys; # mark constant key as done
    if (_disabled_via_dependency($map_ref, $consts_ref, $entry_key)) {
      $logger->debug(sprintf("Key %s is disabled via %s. Skipping validation.", $entry_key, $map_ref->{$entry_key}->{'depends_on'}));
    } elsif (!validate_value($map_ref, $entry_key, \$consts_ref->{$entry_key})) {
518
519
520
      my $msg = sprintf("%s = %s is invalid.", $entry_key, !defined($consts_ref->{$entry_key}) ? "<undef>" : $consts_ref->{$entry_key});
      $logger->error($msg);
      return $msg;
Stefan Tauner's avatar
Stefan Tauner committed
521
522
523
524
525
526
527
528
529
530
531
532
533
    }
  }

  if (!defined($phase)) {
    # If there is no phase we are creating an "empty" hash that has
    # all possible defaults set earlier already and the there is
    # nothing we can or should do here.
    return $consts_ref;
  }

  # Iterate over the constants defined in FIJI.pm that apparently are
  # not contained in $consts_ref.
  foreach my $k (@map_keys) {
534
535
536
537
538
    my $dependency = $map_ref->{$k}->{'depends_on'};
    if (
        scalar(grep {$_ eq $phase} @{$map_ref->{$k}->{'phases_opt'}}) == 0 && # mandatory in current phase
        !_disabled_via_dependency($map_ref, $consts_ref, $k) # no dependency or dependency is enabled
       ) {
539
540
541
      my $msg = "$k is mandatory in phase $phase.";
      $logger->error($msg);
      return $msg;
Stefan Tauner's avatar
Stefan Tauner committed
542
543
544
545
546
547
    }
  }
  return $consts_ref
}


548
## @function _sanitize_design (%$consts_ref, $phase)
Stefan Tauner's avatar
Stefan Tauner committed
549
# @brief Sanity check FIJI Design Settings.
550
#
Stefan Tauner's avatar
Stefan Tauner committed
551
# The function deals with sanity checks for the values
552
# themselves. It checks for the following conditions:
553
554
555
#
#   - FIU_NUM: > 0
#   - FIU_CFG_BITS: > 0
556
#   - TIMER_WIDTH: > 0, <= 16
557
558
559
#   - ID: > 0, < 2^15-1
#   - BAUDRATE: > 0
#
560
# \param consts_ref a reference to a hash containing some design settings
Stefan Tauner's avatar
Stefan Tauner committed
561
# \param phase the tool flow phase that defines the rules to check against
562
#
Stefan Tauner's avatar
Stefan Tauner committed
563
# \returns The given hash with all constants required in the design
564
#          settings in sanitized form, or an error message.
Stefan Tauner's avatar
Stefan Tauner committed
565
sub _sanitize_design {
566
  my $logger = get_logger();
Stefan Tauner's avatar
Stefan Tauner committed
567
  my ($consts_ref, $phase) = @_;
568
  if (ref($consts_ref) ne 'HASH') {
569
570
571
    my $msg = "Parameter is not a reference to a hash (containing design constants).";
    $logger->error($msg);
    return $msg;
572
  }
Stefan Tauner's avatar
Stefan Tauner committed
573
  
574
  # check for sane values
Stefan Tauner's avatar
Stefan Tauner committed
575
  $consts_ref = _validate_hashmap(DESIGNMAP, $consts_ref, $phase);
576
577
578
579
  if (!ref($consts_ref)) {
      my $msg = "Could not validate Design Constants.";
      $logger->error($msg);
      return "$msg $consts_ref";
580
  }
Stefan Tauner's avatar
Stefan Tauner committed
581
582

  if (($consts_ref->{'FIU_CFG_BITS'} <= 0)) {
583
584
585
      my $msg = "FIU_CFG_BITS is <= 0.";
      $logger->error($msg);
      return $msg;
586
  }
587
  if (($consts_ref->{'TIMER_WIDTH'} <= 0) || ($consts_ref->{'TIMER_WIDTH'} > 16)) {
588
589
590
      my $msg = "TIMER_WIDTH is invalid ($consts_ref->{'TIMER_WIDTH'}).";
      $logger->error($msg);
      return $msg;
591
  }
592
  if (defined($consts_ref->{'ID'}) && ($consts_ref->{ID} < 0 || $consts_ref->{ID} > (2**16 - 1))) {
593
594
595
      my $msg = "ID is invalid ($consts_ref->{ID}).";
      $logger->error($msg);
      return $msg;
596
  }
Stefan Tauner's avatar
Stefan Tauner committed
597
  if (($consts_ref->{'BAUDRATE'} <= 0)) {
598
599
600
      my $msg = "BAUDRATE missing is <= 0.";
      $logger->error($msg);
      return $msg;
601
  }
Stefan Tauner's avatar
Stefan Tauner committed
602

603
  return $consts_ref;
604
605
606
}

1;