Settings.pm 20.4 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 there is a file given, try to read it. Else just add an empty FIUs array.
52
  if (defined($fiji_ini_file)) {
Stefan Tauner's avatar
Stefan Tauner committed
53
    $fiji_settings_ref = read_settingsfile($phase, $fiji_ini_file, $fiji_settings_ref);
54
55
    if (!ref($fiji_settings_ref)) {
      return $fiji_settings_ref; # actually an error message
56
57
    }
  } else {
Stefan Tauner's avatar
Stefan Tauner committed
58
    $fiji_settings_ref->{'FIUs'} = ();
59
  }
Stefan Tauner's avatar
Stefan Tauner committed
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
  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'})) {
77
78
79
80
81
82
83
84
85
  }
}

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

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


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

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

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

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

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

  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
228
229
    my $fiji_fiu = _rename_import(FIUMAP, $fiji_fiu_cfg);
    if (!defined($fiji_design)) {
230
231
232
      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
233
234
235
236
237
238
239
240
    }
    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
241
        foreach my $k (keys(%{$ex_fiu})) {
Stefan Tauner's avatar
Stefan Tauner committed
242
243
244
245
          $tmp_fiu->{$k} = $ex_fiu->{$k};
        }
      }
      # overwrite existing FIU entries
246
247
      # $logger->trace("Overwriting values of $fiu_name.") if (defined($ex_fiu) && scalar(keys(%{$ex_fiu})) > 0);
      foreach my $k (keys(%{$fiji_fiu})) {
Stefan Tauner's avatar
Stefan Tauner committed
248
249
250
251
252
        $tmp_fiu->{$k} = $fiji_fiu->{$k};
      }
      $fiji_fiu = $tmp_fiu;
    }
    $fiji_fiu = _sanitize_fiu($fiji_fiu, $phase);
253
    if (!defined($fiji_fiu)) {
254
255
256
      my $msg = "Constants for $fiu_name in FIJI Settings are invalid";
      $logger->error($msg);
      return $msg;
257
    }
Stefan Tauner's avatar
Stefan Tauner committed
258
    @{$fiji_settings_ref->{'FIUs'}}[$fiu_num] = $fiji_fiu;
259
260
261
262
263
    $fiu_num++;
    $logger->trace("Read in $fiu_name from FIJI Settings file successfully.");
  }

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

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

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

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

      $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}));
305
306
    }
  }
307
308
}

309

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

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

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

Stefan Tauner's avatar
Stefan Tauner committed
399
400
401
402
403
404
405
  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;
406
      }
Stefan Tauner's avatar
Stefan Tauner committed
407
    } elsif (ref($values_ref) eq 'CODE') {
408
      if (!$values_ref->(${$v_ref}, $old)) {
Stefan Tauner's avatar
Stefan Tauner committed
409
410
        $log_func->($logger, "$k: ${$v_ref} is not allowed.");
        return 0;
411
      }
412
413
    }
  }
Stefan Tauner's avatar
Stefan Tauner committed
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
  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.
437
  foreach my $k (keys(%{$map_ref})) {
Stefan Tauner's avatar
Stefan Tauner committed
438
439
440
441
442
443
444
    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};
    }
  }
445
  foreach my $entry_key (keys(%{$consts_ref})) {
Stefan Tauner's avatar
Stefan Tauner committed
446
447
448
449
450
451
    if (!exists($map_ref->{$entry_key})) {
      $logger->debug("Deleting unknown setting %s = %s.", $entry_key, $consts_ref->{$entry_key});
      delete $consts_ref->{$entry_key};
    }
  }

452
  return $consts_ref;
453
454
}

Stefan Tauner's avatar
Stefan Tauner committed
455

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

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

  return $fiu_ref;
}

Stefan Tauner's avatar
Stefan Tauner committed
482

483
484
485
486
487
488
489
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
490
491
492
sub _validate_hashmap ($$;$) {
  my $logger = get_logger();
  my ($map_ref, $consts_ref, $phase) = @_;
493
494
  my @map_keys = keys(%{$map_ref});
  foreach my $entry_key (keys(%{$consts_ref})) {
Stefan Tauner's avatar
Stefan Tauner committed
495
496
497
498
499
500
501
    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;
    }

502
503
504
505
    @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})) {
506
507
508
      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
509
510
511
512
513
514
515
516
517
518
519
520
521
    }
  }

  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) {
522
523
524
525
526
    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
       ) {
527
528
529
      my $msg = "$k is mandatory in phase $phase.";
      $logger->error($msg);
      return $msg;
Stefan Tauner's avatar
Stefan Tauner committed
530
531
532
533
534
535
    }
  }
  return $consts_ref
}


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

  if (($consts_ref->{'FIU_CFG_BITS'} <= 0)) {
571
572
573
      my $msg = "FIU_CFG_BITS is <= 0.";
      $logger->error($msg);
      return $msg;
574
  }
Stefan Tauner's avatar
Stefan Tauner committed
575
  if (($consts_ref->{'TIMER_WIDTH'} <= 0) || ($consts_ref->{'TIMER_WIDTH'} % 8 != 0)) {
576
577
578
      my $msg = "TIMER_WIDTH is invalid ($consts_ref->{'TIMER_WIDTH'}).";
      $logger->error($msg);
      return $msg;
579
  }
Stefan Tauner's avatar
Stefan Tauner committed
580
  if (defined($consts_ref->{'ID'}) && ($consts_ref->{ID} < 0 || $consts_ref->{ID} > (2**15 - 1))) {
581
582
583
      my $msg = "ID is invalid ($consts_ref->{ID}).";
      $logger->error($msg);
      return $msg;
584
  }
Stefan Tauner's avatar
Stefan Tauner committed
585
  if (($consts_ref->{'BAUDRATE'} <= 0)) {
586
587
588
      my $msg = "BAUDRATE missing is <= 0.";
      $logger->error($msg);
      return $msg;
589
  }
Stefan Tauner's avatar
Stefan Tauner committed
590

591
  return $consts_ref;
592
593
594
}

1;