Settings.pm 19 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
use Log::Log4perl qw(get_logger);
use Scalar::Util "looks_like_number";
14
use Clone qw(clone);
15
16
use Config::Simple;

17
18
use FIJI qw(:all);

Stefan Tauner's avatar
Stefan Tauner committed
19
20
21
22
23
24
25
26
27
28
## @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.
# \param $existing_settings (optional) An instance to reuse and return
#        after overwriting existing values with defaults and any values
#        read from file. Existing values will not be validated.
#        Superfluous elements not present in the existing hash will be
#        removed, e.g. FIUs.
29
30
sub new ($;$$) {
  my $logger = get_logger();
Stefan Tauner's avatar
Stefan Tauner committed
31
32
33
34
35
36
37
38
39
40
41
42
43
44
  my ($class, $phase, $fiji_ini_file, $existing_settings) = @_;

  my $fiji_settings_ref;
  # if there are no existing settings yet, create some with default values
  if (!defined($existing_settings)) {
    $fiji_settings_ref = {};
    $fiji_settings_ref->{'design'} = {};
    _set_defaults(DESIGNMAP, $fiji_settings_ref->{'design'}, $phase);
    $fiji_settings_ref = bless($fiji_settings_ref, $class);
    if (!ref($fiji_settings_ref) || !UNIVERSAL::can($fiji_settings_ref,'can')) {
      $logger->error("Could not bless FIJI::Settings class from \"$fiji_ini_file\".");
      return undef;
    }
  } else {
45
    $fiji_settings_ref = _clone($existing_settings);
Stefan Tauner's avatar
Stefan Tauner committed
46
  }
47
48

  if (defined($fiji_ini_file)) {
Stefan Tauner's avatar
Stefan Tauner committed
49
50
    $fiji_settings_ref = read_settingsfile($phase, $fiji_ini_file, $fiji_settings_ref);
    if (!defined($fiji_settings_ref)) {
51
52
53
54
55
56
      return undef;
    }
    if (defined($existing_settings)) {
      return $existing_settings;
    }
  } else {
Stefan Tauner's avatar
Stefan Tauner committed
57
    $fiji_settings_ref->{'FIUs'} = ();
58
  }
Stefan Tauner's avatar
Stefan Tauner committed
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
  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'})) {
76
77
78
79
80
81
82
83
84
  }
}

## @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
85
sub save ($) {
86
87
88
89
90
  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');
91
92
  my $design_ref;
  my $fiu_cnt = 0;
93
94
95
96
97
98
  foreach my $key (keys %{$self}) {
    my $val = $self->{$key};
    $logger->debug(sprintf("Key: %s, type: %s, value: %s", $key, ref(\$val), $val));
    if (ref(\$val) eq "REF") {
      if (ref($val) eq "HASH") {
        if ($key eq "design") {
99
          $design_ref = $val;
100
101
102
103
104
105
          next;
        }
      } elsif (ref($val) eq "ARRAY") {
        if ($key eq "FIUs") {
          foreach my $fiu (@{$val}) {
            my $ini_fiu;
Stefan Tauner's avatar
Stefan Tauner committed
106
            
107
            foreach my $k (keys $fiu) {
Stefan Tauner's avatar
Stefan Tauner committed
108
              my $ini_name = FIUMAP->{$k}->{'ini_name'};
109
110
111
112
113
              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
114
              $ini_fiu->{$ini_name} = $fiu->{$k};
115
116
              # Convert value to external representation
              _export_value(FIUMAP, $k, \$$fiu{$k});
Stefan Tauner's avatar
Stefan Tauner committed
117
              $logger->trace(sprintf("Renaming setting %s -> %s (=%s).", $k, $ini_name, defined($ini_fiu->{$ini_name}) ? $ini_fiu->{$ini_name} : "undef"));
118
119
120
121
122
123
124
125
126
127
128
            }
            $fiji_ini->set_block("FIU" . $fiu_cnt++, $ini_fiu);
          }
          next;
        }
      }
    }
    my $err = "Unknown element found in FIJI Settings: \"$val\"";
    $logger->error($err);
    return $err;
  }
129
  $design_ref->{'FIU_NUM'} = $fiu_cnt;
130
131
132
133
134
135
136
137
138
139
140
  my $ini_design;
  foreach my $k (keys $design_ref) {
    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
    _export_value(DESIGNMAP, $k, \$$design_ref{$k});
Stefan Tauner's avatar
Stefan Tauner committed
141
  }
142
  $fiji_ini->set_block("CONSTS", $ini_design);
143

144
145
146
147
148
149
150
151
152
  if (!defined($fiji_ini->write($fiji_ini_file))) {
    my $err = Config::Simple->error();
    $logger->error($err);
    return $err;
  }
  return undef;
}


153
## @function read_settingsfile ($phase, $fiji_ini_file)
154
# @brief Load the FIJI Settings file containing design and FIU constants.
155
#
156
# \param phase  Tool flow phase the settings stored in the given file
Stefan Tauner's avatar
Stefan Tauner committed
157
#               need to be compatible with.
158
159
# \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
160
#           \ref _sanitize_design.
161
162
163
#         - 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
164
165
#
# \returns a reference to the hash containing the read constants.
166
sub read_settingsfile {
167
  my $logger = get_logger();
Stefan Tauner's avatar
Stefan Tauner committed
168
  my ($phase, $fiji_ini_file, $existing_settings) = @_;
169
170
171
  my $fiji_ini;
  eval { $fiji_ini = new Config::Simple($fiji_ini_file) }; # pesky library tries to die on syntax errors
  if (!defined($fiji_ini)) {
172
    $logger->error("Could not read config file \"$fiji_ini_file\": " . (defined($@) ? $@ : Config::Simple->error()));
173
174
175
    return undef;
  }

Stefan Tauner's avatar
Stefan Tauner committed
176
177
  my $fiji_design_cfg = $fiji_ini->get_block("CONSTS");
  if (!(%$fiji_design_cfg)) {
178
    $logger->error("Could not fetch CONSTS block from config file \"$fiji_ini_file\"");
179
180
    return undef;
  }
Stefan Tauner's avatar
Stefan Tauner committed
181
182
183
184

  my $fiji_design = _rename_import(DESIGNMAP, $fiji_design_cfg);
  if (!defined($fiji_design)) {
    $logger->error("Design constants do not match the FIJI Settings naming scheme.");
185
186
    return undef;
  }
Stefan Tauner's avatar
Stefan Tauner committed
187

188
  my $fiji_settings_ref;
189
  if (defined($existing_settings)) {
Stefan Tauner's avatar
Stefan Tauner committed
190
    if (!blessed($existing_settings) || !$existing_settings->isa("FIJI::Settings")) {
191
192
193
      $logger->error("Given settings are not of type FIJI::Settings.");
      return undef;
    }
Stefan Tauner's avatar
Stefan Tauner committed
194
195
196
197
198
199
200
201
202
203
204
    # overwrite existing Design Constants
    foreach my $k (keys($fiji_design)) {
      $existing_settings->{'design'}->{$k} = $fiji_design->{$k};
    }
    $fiji_design = $existing_settings->{'design'};
    if (defined($existing_settings->{'FIUs'})) {
      $fiji_settings_ref->{'FIUs'} = $existing_settings->{'FIUs'};
    } else {
      $fiji_settings_ref->{'FIUs'} = ();
    }
  } else {
205
206
    $fiji_settings_ref->{'FIUs'} = ();
  }
Stefan Tauner's avatar
Stefan Tauner committed
207
208
209
210
211
212
213
214

  # sanitize and validate read design constants
  $fiji_design = _sanitize_design($fiji_design, $phase);
  if (!defined($fiji_design)) {
    $logger->error("Design constants in FIJI Settings invalid");
    return undef;
  }
  $fiji_settings_ref->{'design'} = $fiji_design;
215
216
217
218
219
220
221
222

  my $fiu_num = 0;
  while (1) {
    my $fiu_name = "FIU" . $fiu_num;
    my $fiji_fiu_cfg = $fiji_ini->get_block($fiu_name);
    if (!(%$fiji_fiu_cfg)) {
      last;
    }
Stefan Tauner's avatar
Stefan Tauner committed
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
    my $fiji_fiu = _rename_import(FIUMAP, $fiji_fiu_cfg);
    if (!defined($fiji_design)) {
      $logger->error("FIU constants of $fiu_name do not match the FIJI Settings naming scheme.");
      return undef;
    }
    my $tmp_fiu = {};
    _set_defaults(FIUMAP, $tmp_fiu, $phase);

    if (defined($existing_settings)) {
      my $ex_fiu = @{$existing_settings->{'FIUs'}}[$fiu_num];
      if (defined($ex_fiu)) {
        # overwrite default entries
        foreach my $k (keys($ex_fiu)) {
          $tmp_fiu->{$k} = $ex_fiu->{$k};
        }
      }
      # overwrite existing FIU entries
      # $logger->trace("Overwriting values of $fiu_name.") if (defined($ex_fiu) && scalar(keys($ex_fiu)) > 0);
      foreach my $k (keys($fiji_fiu)) {
        $tmp_fiu->{$k} = $fiji_fiu->{$k};
      }
      $fiji_fiu = $tmp_fiu;
    }
    $fiji_fiu = _sanitize_fiu($fiji_fiu, $phase);
247
    if (!defined($fiji_fiu)) {
248
      $logger->error("Constants for $fiu_name in FIJI Settings are invalid");
249
250
      return undef;
    }
Stefan Tauner's avatar
Stefan Tauner committed
251
    @{$fiji_settings_ref->{'FIUs'}}[$fiu_num] = $fiji_fiu;
252
253
254
255
256
    $fiu_num++;
    $logger->trace("Read in $fiu_name from FIJI Settings file successfully.");
  }

  if ($fiu_num == 0) {
257
258
    $logger->debug("Could not fetch any FIU block from config file \"$fiji_ini_file\"");
    $fiji_settings_ref->{'FIUs'} = [];
259
  }
260
261
262

  # FIU_NUM is optional in the Settings file... if it was set check that
  # it corresponds to the number of FIU<number> blocks.
Stefan Tauner's avatar
Stefan Tauner committed
263
  if (defined($fiji_design->{'FIU_NUM'}) && $fiji_design->{'FIU_NUM'} != $fiu_num) {
264
      $logger->error(FIU_NUM->{'ini_name'} . " does not match the numbers of FIU blocks found.");
265
266
      return undef;
  } else {
Stefan Tauner's avatar
Stefan Tauner committed
267
    $fiji_design->{'FIU_NUM'} = $fiu_num; # assume the best if FIU_NUM constant is not given
268
269
  }

Stefan Tauner's avatar
Stefan Tauner committed
270
  splice(@{$fiji_settings_ref->{'FIUs'}}, $fiu_num);
271
  $logger->info("Successfully read in design constants and $fiu_num FIU definitions from FIJI Settings file.");
272
273
274
  return $fiji_settings_ref;
}

Stefan Tauner's avatar
Stefan Tauner committed
275
## @method set_fiu_defaults (%$fiu_ref)
276
277
278
279
# @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
280
281
282
283
284
285
286
287
  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) = @_;
288
289
  foreach my $k (keys($map_ref)) {
    if (exists($map_ref->{$k}->{'default'})) {
Stefan Tauner's avatar
Stefan Tauner committed
290
      # Set default only if it is not mandatory in the given phase.
291
292
      if (defined($phase) && scalar(grep {$_ eq $phase} @{$map_ref->{$k}->{'phases_opt'}}) == 0) {
        next;
293
      }
294
295
296

      $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}));
297
298
    }
  }
299
300
}

301

302
303
304
305
306
307
308
sub _clone ($) {
  my ($existing_settings_ref) = @_;
  my $new_settings_ref = \%{ Clone::clone ($existing_settings_ref) };
 return $new_settings_ref;
}


Stefan Tauner's avatar
Stefan Tauner committed
309
310
311
312
313
314
315
316
317
318
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);
}

319
320
321
322
323
324
325
326
327
328
## @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
329
  my $logger = get_logger();
330
  my ($map_ref, $k, $v_ref, $old, $log_func) = @_;
Stefan Tauner's avatar
Stefan Tauner committed
331
332
333
  $log_func = \&Log::Log4perl::Logger::trace if !defined($log_func);
  if (defined($map_ref->{$k}->{'type'})) {
    my $orig = ${$v_ref};
334
335
336
337
338
    # 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'}}
    # }

Stefan Tauner's avatar
Stefan Tauner committed
339
340
341
342
    if ($map_ref->{$k}->{'type'} eq 'hexadecimal') {
      if ($orig !~ /^(0|(0[xX][[:xdigit:]]+))$/) {
        $log_func->($logger, "$k: $orig does not look like a natural hexadecimal number.");
        return 0;
343
      }
Stefan Tauner's avatar
Stefan Tauner committed
344
345
346
347
348
349
350
      ${$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') {
351
      # Match hexadecimal, binary, octal and decimal numbers (the latter also in scientific notation)
Stefan Tauner's avatar
Stefan Tauner committed
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
      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;
377
      }
Stefan Tauner's avatar
Stefan Tauner committed
378
379
380
381
382
383
      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;
384
    }
Stefan Tauner's avatar
Stefan Tauner committed
385
386
    $logger->trace("Converted value of $k (\"$orig\") to \"${$v_ref}\".") if ($orig ne ${$v_ref});
  }
387

Stefan Tauner's avatar
Stefan Tauner committed
388
389
390
391
392
393
394
  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;
395
      }
Stefan Tauner's avatar
Stefan Tauner committed
396
    } elsif (ref($values_ref) eq 'CODE') {
397
      if (!$values_ref->(${$v_ref}, $old)) {
Stefan Tauner's avatar
Stefan Tauner committed
398
399
        $log_func->($logger, "$k: ${$v_ref} is not allowed.");
        return 0;
400
      }
401
402
    }
  }
Stefan Tauner's avatar
Stefan Tauner committed
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
  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.
  foreach my $k (keys($map_ref)) {
    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};
      $logger->trace(sprintf("Renaming setting %s -> %s (=%s).", $ini_name, $k, defined($consts_ref->{$ini_name}) ? $consts_ref->{$ini_name} : "undef"));
      delete $consts_ref->{$ini_name};
    }
  }
  foreach my $entry_key (keys($consts_ref)) {
    if (!exists($map_ref->{$entry_key})) {
      $logger->debug("Deleting unknown setting %s = %s.", $entry_key, $consts_ref->{$entry_key});
      delete $consts_ref->{$entry_key};
    }
  }

441
  return $consts_ref;
442
443
}

Stefan Tauner's avatar
Stefan Tauner committed
444

445
## @function _sanitize_fiu (%$fiu_ref, $phase)
446
447
# @brief Convert and sanity check FIJI Settings.
#
448
449
450
451
452
# \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
453
sub _sanitize_fiu ($;$) {
454
  my $logger = get_logger();
Stefan Tauner's avatar
Stefan Tauner committed
455
  my ($fiu_ref, $phase) = @_;
456
457
458
459
460
  if (ref($fiu_ref) ne 'HASH') {
    $logger->error("Parameter is not a reference to a hash (containing FIU constants).");
    return undef;
  }

Stefan Tauner's avatar
Stefan Tauner committed
461
462
463
464
  $fiu_ref = _validate_hashmap(FIUMAP, $fiu_ref, $phase);
  if (!defined($fiu_ref)) {
      $logger->error("Could not validate Design Constants.");
      return undef;
465
466
467
468
469
  }

  return $fiu_ref;
}

Stefan Tauner's avatar
Stefan Tauner committed
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485

sub _validate_hashmap ($$;$) {
  my $logger = get_logger();
  my ($map_ref, $consts_ref, $phase) = @_;
  my @map_keys = keys($map_ref);
  foreach my $entry_key (keys($consts_ref)) {
    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;
    }

    # mark constant key as done
    @map_keys = grep {$_ ne $entry_key} @map_keys;
    if (!validate_value($map_ref, $entry_key, \$consts_ref->{$entry_key})) {
486
      $logger->error(sprintf("%s = %s is invalid.", $entry_key, !defined($consts_ref->{$entry_key}) ? "<undef>" : $consts_ref->{$entry_key}));
Stefan Tauner's avatar
Stefan Tauner committed
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
      return undef;
    }
  }

  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) {
    if (scalar(grep {$_ eq $phase} @{$map_ref->{$k}->{'phases_opt'}}) == 0) {
      $logger->error("$k is mandatory in phase $phase.");
      return undef;
    }
  }
  return $consts_ref
}


510
## @function _sanitize_design (%$consts_ref, $phase)
Stefan Tauner's avatar
Stefan Tauner committed
511
# @brief Sanity check FIJI Design Settings.
512
#
Stefan Tauner's avatar
Stefan Tauner committed
513
# The function deals with sanity checks for the values
514
# themselves. It checks for the following conditions:
515
516
517
#
#   - FIU_NUM: > 0
#   - FIU_CFG_BITS: > 0
Stefan Tauner's avatar
Stefan Tauner committed
518
#   - TIMER_WIDTH: > 0, multiple of 8
519
520
521
#   - ID: > 0, < 2^15-1
#   - BAUDRATE: > 0
#
522
# \param consts_ref a reference to a hash containing some design settings
Stefan Tauner's avatar
Stefan Tauner committed
523
# \param phase the tool flow phase that defines the rules to check against
524
#
Stefan Tauner's avatar
Stefan Tauner committed
525
526
527
# \returns The given hash with all constants required in the design
#          settings in sanitized form, or undef on errors.
sub _sanitize_design {
528
  my $logger = get_logger();
Stefan Tauner's avatar
Stefan Tauner committed
529
  my ($consts_ref, $phase) = @_;
530
  if (ref($consts_ref) ne 'HASH') {
531
    $logger->error("Parameter is not a reference to a hash (containing design constants).");
532
533
    return undef;
  }
Stefan Tauner's avatar
Stefan Tauner committed
534
  
535
  # check for sane values
Stefan Tauner's avatar
Stefan Tauner committed
536
537
538
  $consts_ref = _validate_hashmap(DESIGNMAP, $consts_ref, $phase);
  if (!defined($consts_ref)) {
      $logger->error("Could not validate Design Constants.");
539
      return undef;
540
  }
Stefan Tauner's avatar
Stefan Tauner committed
541
542
543

  if (($consts_ref->{'FIU_CFG_BITS'} <= 0)) {
      $logger->error("FIU_CFG_BITS is <= 0.");
544
      return undef;
545
  }
Stefan Tauner's avatar
Stefan Tauner committed
546
547
  if (($consts_ref->{'TIMER_WIDTH'} <= 0) || ($consts_ref->{'TIMER_WIDTH'} % 8 != 0)) {
      $logger->error("TIMER_WIDTH is invalid ($consts_ref->{'TIMER_WIDTH'}).");
548
      return undef;
549
  }
Stefan Tauner's avatar
Stefan Tauner committed
550
  if (defined($consts_ref->{'ID'}) && ($consts_ref->{ID} < 0 || $consts_ref->{ID} > (2**15 - 1))) {
551
      $logger->error("ID is invalid ($consts_ref->{ID}).");
552
      return undef;
553
  }
Stefan Tauner's avatar
Stefan Tauner committed
554
  if (($consts_ref->{'BAUDRATE'} <= 0)) {
555
      $logger->error("BAUDRATE missing is <= 0.");
556
      return undef;
557
  }
Stefan Tauner's avatar
Stefan Tauner committed
558

559
  return $consts_ref;
560
561
562
}

1;