Settings.pm 19.8 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
# \returns The new settings instance or a string describing the reason
#          why it could not be created.
31
32
sub new ($;$$) {
  my $logger = get_logger();
Stefan Tauner's avatar
Stefan Tauner committed
33
34
35
36
37
38
39
40
41
42
  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')) {
43
44
45
      my $msg = "Could not bless FIJI::Settings class from \"$fiji_ini_file\".";
      $logger->error($msg);
      return $msg;
Stefan Tauner's avatar
Stefan Tauner committed
46
47
    }
  } else {
48
    $fiji_settings_ref = _clone($existing_settings);
Stefan Tauner's avatar
Stefan Tauner committed
49
  }
50
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
57
58
59
    }
    if (defined($existing_settings)) {
      return $existing_settings;
    }
  } else {
Stefan Tauner's avatar
Stefan Tauner committed
60
    $fiji_settings_ref->{'FIUs'} = ();
61
  }
Stefan Tauner's avatar
Stefan Tauner committed
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
  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'})) {
79
80
81
82
83
84
85
86
87
  }
}

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

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


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

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

  my $fiji_design = _rename_import(DESIGNMAP, $fiji_design_cfg);
  if (!defined($fiji_design)) {
189
190
191
    my $msg = "Design constants do not match the FIJI Settings naming scheme.";
    $logger->error($msg);
    return $msg;
192
  }
Stefan Tauner's avatar
Stefan Tauner committed
193

194
  my $fiji_settings_ref;
195
  if (defined($existing_settings)) {
Stefan Tauner's avatar
Stefan Tauner committed
196
    if (!blessed($existing_settings) || !$existing_settings->isa("FIJI::Settings")) {
197
198
199
      my $msg ="Given settings are not of type FIJI::Settings.";
      $logger->error($msg);
      return $msg;
200
    }
Stefan Tauner's avatar
Stefan Tauner committed
201
202
203
204
205
206
207
208
209
210
211
    # 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 {
212
213
    $fiji_settings_ref->{'FIUs'} = ();
  }
Stefan Tauner's avatar
Stefan Tauner committed
214
215
216

  # sanitize and validate read design constants
  $fiji_design = _sanitize_design($fiji_design, $phase);
217
218
219
  if (!ref($fiji_design)) {
    $logger->error($fiji_design);
    return $fiji_design;
Stefan Tauner's avatar
Stefan Tauner committed
220
221
  }
  $fiji_settings_ref->{'design'} = $fiji_design;
222
223
224
225
226
227
228
229

  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
230
231
    my $fiji_fiu = _rename_import(FIUMAP, $fiji_fiu_cfg);
    if (!defined($fiji_design)) {
232
233
234
      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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
    }
    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);
255
    if (!defined($fiji_fiu)) {
256
257
258
      my $msg = "Constants for $fiu_name in FIJI Settings are invalid";
      $logger->error($msg);
      return $msg;
259
    }
Stefan Tauner's avatar
Stefan Tauner committed
260
    @{$fiji_settings_ref->{'FIUs'}}[$fiu_num] = $fiji_fiu;
261
262
263
264
265
    $fiu_num++;
    $logger->trace("Read in $fiu_name from FIJI Settings file successfully.");
  }

  if ($fiu_num == 0) {
266
267
    $logger->debug("Could not fetch any FIU block from config file \"$fiji_ini_file\"");
    $fiji_settings_ref->{'FIUs'} = [];
268
  }
269
270
271

  # 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
272
  if (defined($fiji_design->{'FIU_NUM'}) && $fiji_design->{'FIU_NUM'} != $fiu_num) {
273
274
275
      my $msg = FIU_NUM->{'ini_name'} . " does not match the numbers of FIU blocks found.";
      $logger->error($msg);
      return $msg;
276
  } else {
Stefan Tauner's avatar
Stefan Tauner committed
277
    $fiji_design->{'FIU_NUM'} = $fiu_num; # assume the best if FIU_NUM constant is not given
278
279
  }

Stefan Tauner's avatar
Stefan Tauner committed
280
  splice(@{$fiji_settings_ref->{'FIUs'}}, $fiu_num);
281
  $logger->info("Successfully read in design constants and $fiu_num FIU definitions from FIJI Settings file.");
282
283
284
  return $fiji_settings_ref;
}

Stefan Tauner's avatar
Stefan Tauner committed
285
## @method set_fiu_defaults (%$fiu_ref)
286
287
288
289
# @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
290
291
292
293
294
295
296
297
  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) = @_;
298
299
  foreach my $k (keys($map_ref)) {
    if (exists($map_ref->{$k}->{'default'})) {
Stefan Tauner's avatar
Stefan Tauner committed
300
      # Set default only if it is not mandatory in the given phase.
301
302
      if (defined($phase) && scalar(grep {$_ eq $phase} @{$map_ref->{$k}->{'phases_opt'}}) == 0) {
        next;
303
      }
304
305
306

      $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}));
307
308
    }
  }
309
310
}

311

312
313
314
315
316
317
318
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
319
320
321
322
323
324
325
326
327
328
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);
}

329
330
331
332
333
334
335
336
337
338
## @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
339
  my $logger = get_logger();
340
  my ($map_ref, $k, $v_ref, $old, $log_func) = @_;
Stefan Tauner's avatar
Stefan Tauner committed
341
342
343
  $log_func = \&Log::Log4perl::Logger::trace if !defined($log_func);
  if (defined($map_ref->{$k}->{'type'})) {
    my $orig = ${$v_ref};
344
345
346
347
348
    # 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'}}
    # }

349
350
351
352
    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
353
354
355
      if ($orig !~ /^(0|(0[xX][[:xdigit:]]+))$/) {
        $log_func->($logger, "$k: $orig does not look like a natural hexadecimal number.");
        return 0;
356
      }
Stefan Tauner's avatar
Stefan Tauner committed
357
358
359
360
361
362
363
      ${$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') {
364
      # Match hexadecimal, binary, octal and decimal numbers (the latter also in scientific notation)
Stefan Tauner's avatar
Stefan Tauner committed
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
      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;
390
      }
Stefan Tauner's avatar
Stefan Tauner committed
391
392
393
394
395
396
      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;
397
    }
398
    $logger->trace("Converted value of $k (\"$orig\") to \"${$v_ref}\".") if (defined($orig) && $orig ne ${$v_ref});
Stefan Tauner's avatar
Stefan Tauner committed
399
  }
400

Stefan Tauner's avatar
Stefan Tauner committed
401
402
403
404
405
406
407
  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;
408
      }
Stefan Tauner's avatar
Stefan Tauner committed
409
    } elsif (ref($values_ref) eq 'CODE') {
410
      if (!$values_ref->(${$v_ref}, $old)) {
Stefan Tauner's avatar
Stefan Tauner committed
411
412
        $log_func->($logger, "$k: ${$v_ref} is not allowed.");
        return 0;
413
      }
414
415
    }
  }
Stefan Tauner's avatar
Stefan Tauner committed
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
441
442
443
444
445
446
447
448
449
450
451
452
453
  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};
    }
  }

454
  return $consts_ref;
455
456
}

Stefan Tauner's avatar
Stefan Tauner committed
457

458
## @function _sanitize_fiu (%$fiu_ref, $phase)
459
460
# @brief Convert and sanity check FIJI Settings.
#
461
462
463
464
465
# \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
466
sub _sanitize_fiu ($;$) {
467
  my $logger = get_logger();
Stefan Tauner's avatar
Stefan Tauner committed
468
  my ($fiu_ref, $phase) = @_;
469
  if (ref($fiu_ref) ne 'HASH') {
470
471
472
    my $msg = "Parameter is not a reference to a hash (containing FIU constants).";
    $logger->error($msg);
    return $msg;
473
474
  }

Stefan Tauner's avatar
Stefan Tauner committed
475
  $fiu_ref = _validate_hashmap(FIUMAP, $fiu_ref, $phase);
476
  if (!ref($fiu_ref)) {
Stefan Tauner's avatar
Stefan Tauner committed
477
      $logger->error("Could not validate Design Constants.");
478
      return $fiu_ref;
479
480
481
482
483
  }

  return $fiu_ref;
}

Stefan Tauner's avatar
Stefan Tauner committed
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499

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})) {
500
501
502
      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
503
504
505
506
507
508
509
510
511
512
513
514
515
516
    }
  }

  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) {
517
518
519
      my $msg = "$k is mandatory in phase $phase.";
      $logger->error($msg);
      return $msg;
Stefan Tauner's avatar
Stefan Tauner committed
520
521
522
523
524
525
    }
  }
  return $consts_ref
}


526
## @function _sanitize_design (%$consts_ref, $phase)
Stefan Tauner's avatar
Stefan Tauner committed
527
# @brief Sanity check FIJI Design Settings.
528
#
Stefan Tauner's avatar
Stefan Tauner committed
529
# The function deals with sanity checks for the values
530
# themselves. It checks for the following conditions:
531
532
533
#
#   - FIU_NUM: > 0
#   - FIU_CFG_BITS: > 0
Stefan Tauner's avatar
Stefan Tauner committed
534
#   - TIMER_WIDTH: > 0, multiple of 8
535
536
537
#   - ID: > 0, < 2^15-1
#   - BAUDRATE: > 0
#
538
# \param consts_ref a reference to a hash containing some design settings
Stefan Tauner's avatar
Stefan Tauner committed
539
# \param phase the tool flow phase that defines the rules to check against
540
#
Stefan Tauner's avatar
Stefan Tauner committed
541
# \returns The given hash with all constants required in the design
542
#          settings in sanitized form, or an error message.
Stefan Tauner's avatar
Stefan Tauner committed
543
sub _sanitize_design {
544
  my $logger = get_logger();
Stefan Tauner's avatar
Stefan Tauner committed
545
  my ($consts_ref, $phase) = @_;
546
  if (ref($consts_ref) ne 'HASH') {
547
548
549
    my $msg = "Parameter is not a reference to a hash (containing design constants).";
    $logger->error($msg);
    return $msg;
550
  }
Stefan Tauner's avatar
Stefan Tauner committed
551
  
552
  # check for sane values
Stefan Tauner's avatar
Stefan Tauner committed
553
  $consts_ref = _validate_hashmap(DESIGNMAP, $consts_ref, $phase);
554
555
556
557
  if (!ref($consts_ref)) {
      my $msg = "Could not validate Design Constants.";
      $logger->error($msg);
      return "$msg $consts_ref";
558
  }
Stefan Tauner's avatar
Stefan Tauner committed
559
560

  if (($consts_ref->{'FIU_CFG_BITS'} <= 0)) {
561
562
563
      my $msg = "FIU_CFG_BITS is <= 0.";
      $logger->error($msg);
      return $msg;
564
  }
Stefan Tauner's avatar
Stefan Tauner committed
565
  if (($consts_ref->{'TIMER_WIDTH'} <= 0) || ($consts_ref->{'TIMER_WIDTH'} % 8 != 0)) {
566
567
568
      my $msg = "TIMER_WIDTH is invalid ($consts_ref->{'TIMER_WIDTH'}).";
      $logger->error($msg);
      return $msg;
569
  }
Stefan Tauner's avatar
Stefan Tauner committed
570
  if (defined($consts_ref->{'ID'}) && ($consts_ref->{ID} < 0 || $consts_ref->{ID} > (2**15 - 1))) {
571
572
573
      my $msg = "ID is invalid ($consts_ref->{ID}).";
      $logger->error($msg);
      return $msg;
574
  }
Stefan Tauner's avatar
Stefan Tauner committed
575
  if (($consts_ref->{'BAUDRATE'} <= 0)) {
576
577
578
      my $msg = "BAUDRATE missing is <= 0.";
      $logger->error($msg);
      return $msg;
579
  }
Stefan Tauner's avatar
Stefan Tauner committed
580

581
  return $consts_ref;
582
583
584
}

1;