Settings.pm 21.3 KB
Newer Older
Christian Fibich's avatar
Christian Fibich committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#-------------------------------------------------------------------------------
#  University of Applied Sciences Technikum Wien
#
#  Department of Embedded Systems
#  http://embsys.technikum-wien.at
#
#  Josef Ressel Center for Verification of Embedded Computing Systems
#  http://vecs.technikum-wien.at
#
#-------------------------------------------------------------------------------
#  File:              Settings.pm
#  Created on:        16.02.2015
#  $LastChangedBy$
#  $LastChangedDate$
#
#  Description:
#
# Contains helper functions to deal with FIJI Settings files.
#-------------------------------------------------------------------------------


22
## @file
23

24
## @class FIJI::Settings
25
#
26
# Contains helper functions to deal with FIJI Settings files.
27
28
package FIJI::Settings;

29
30
31
use strict;
use warnings;

32
use Scalar::Util 'blessed';
33
34
35
36
use Log::Log4perl qw(get_logger);
use Scalar::Util "looks_like_number";
use Config::Simple;

37
38
use FIJI qw(:all);

Stefan Tauner's avatar
Stefan Tauner committed
39
40
41
42
43
## @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.
44
45
# \param $existing_settings (optional) A reference to reuse and return
#        with values read from file. Any contained data will be cleared.
46
47
# \returns The new settings instance or a string describing the reason
#          why it could not be created.
48
49
sub new ($;$$) {
  my $logger = get_logger();
Stefan Tauner's avatar
Stefan Tauner committed
50
51
52
  my ($class, $phase, $fiji_ini_file, $existing_settings) = @_;

  my $fiji_settings_ref;
53
  # if there is no existing settings instance yet, create one
Stefan Tauner's avatar
Stefan Tauner committed
54
55
56
57
  if (!defined($existing_settings)) {
    $fiji_settings_ref = {};
    $fiji_settings_ref = bless($fiji_settings_ref, $class);
  } else {
58
59
60
61
62
63
64
65
66
67
68
    $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
69
  }
70

71
  # If there is a file given, try to read it. Else just create a default instance.
72
  if (defined($fiji_ini_file)) {
Stefan Tauner's avatar
Stefan Tauner committed
73
    $fiji_settings_ref = read_settingsfile($phase, $fiji_ini_file, $fiji_settings_ref);
74
75
    if (!ref($fiji_settings_ref)) {
      return $fiji_settings_ref; # actually an error message
76
77
    }
  } else {
78
79
    $fiji_settings_ref->{'design'} = {};
    _set_defaults(DESIGNMAP, $fiji_settings_ref->{'design'}, $phase);
80
    $fiji_settings_ref->{'fius'} = [];
81
  }
Stefan Tauner's avatar
Stefan Tauner committed
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
  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'})) {
99
100
101
102
103
104
105
106
107
  }
}

## @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
108
sub save ($) {
109
110
111
112
113
  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');
114
115
  my $design_ref;
  my $fiu_cnt = 0;
116
117
118
119
120
  foreach my $key (keys %{$self}) {
    my $val = $self->{$key};
    if (ref(\$val) eq "REF") {
      if (ref($val) eq "HASH") {
        if ($key eq "design") {
121
          $design_ref = $val;
122
123
124
          next;
        }
      } elsif (ref($val) eq "ARRAY") {
125
        if ($key eq "fius") {
126
127
          foreach my $fiu (@{$val}) {
            my $ini_fiu;
Stefan Tauner's avatar
Stefan Tauner committed
128
            
129
            foreach my $k (keys(%{$fiu})) {
Stefan Tauner's avatar
Stefan Tauner committed
130
              my $ini_name = FIUMAP->{$k}->{'ini_name'};
131
132
133
134
135
              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
136
              $ini_fiu->{$ini_name} = $fiu->{$k};
137
              # Convert value to external representation
138
139
140
141
142
143
144
145
              _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"),
                            );
146
147
148
149
150
151
152
153
154
155
156
            }
            $fiji_ini->set_block("FIU" . $fiu_cnt++, $ini_fiu);
          }
          next;
        }
      }
    }
    my $err = "Unknown element found in FIJI Settings: \"$val\"";
    $logger->error($err);
    return $err;
  }
157
  $design_ref->{'FIU_NUM'} = $fiu_cnt;
158
  my $ini_design;
159
  foreach my $k (keys(%{$design_ref})) {
160
161
162
163
164
165
166
167
    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
168
169
170
171
172
173
174
    _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
175
  }
176
  $fiji_ini->set_block("CONSTS", $ini_design);
177

178
179
180
181
182
183
184
185
186
  if (!defined($fiji_ini->write($fiji_ini_file))) {
    my $err = Config::Simple->error();
    $logger->error($err);
    return $err;
  }
  return undef;
}


187
## @function read_settingsfile ($phase, $fiji_ini_file)
188
# @brief Load the FIJI Settings file containing design and FIU constants.
189
#
190
# \param phase  Tool flow phase the settings stored in the given file
Stefan Tauner's avatar
Stefan Tauner committed
191
#               need to be compatible with.
192
193
# \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
194
#           \ref _sanitize_design.
195
196
197
#         - 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
198
199
#
# \returns a reference to the hash containing the read constants.
200
sub read_settingsfile ($$$) {
201
  my $logger = get_logger();
Stefan Tauner's avatar
Stefan Tauner committed
202
  my ($phase, $fiji_ini_file, $existing_settings) = @_;
203
204
205
  my $fiji_ini;
  eval { $fiji_ini = new Config::Simple($fiji_ini_file) }; # pesky library tries to die on syntax errors
  if (!defined($fiji_ini)) {
206
207
208
209
210
    my $submsg = defined($@) ? $@ : Config::Simple->error();
    if (length($submsg) == 0) {
      $submsg = "Empty file?";
    }
    my $msg = "Could not read config file \"$fiji_ini_file\": $submsg";
211
212
    $logger->error($msg);
    return $msg;
213
214
  }

215
216
  my $design_ref = $fiji_ini->get_block("CONSTS");
  if (!(%$design_ref)) {
217
218
219
    my $msg = "Could not fetch CONSTS block from config file \"$fiji_ini_file\"";
    $logger->error($msg);
    return $msg;
220
  }
221
  _set_defaults(DESIGNMAP, $design_ref, $phase);
Stefan Tauner's avatar
Stefan Tauner committed
222

223
224
  $design_ref = _rename_import(DESIGNMAP, $design_ref);
  if (!defined($design_ref)) {
225
226
227
    my $msg = "Design constants do not match the FIJI Settings naming scheme.";
    $logger->error($msg);
    return $msg;
228
  }
Stefan Tauner's avatar
Stefan Tauner committed
229

230
  # Create a new instance or reuse the shallow hull of the existing one
231
  my $fiji_settings_ref;
232
233
234
235
236
237
238
239
  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
240
    }
241
242
243
244
245
   }
  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
246
    } else {
247
      $msg = "Given settings are not of type FIJI::Settings.";
Stefan Tauner's avatar
Stefan Tauner committed
248
    }
249
250
    $logger->error($msg);
    return $msg;
251
  }
252
  $fiji_settings_ref->{'design'} = $design_ref;
253
  $fiji_settings_ref->{'fius'} = [];
Stefan Tauner's avatar
Stefan Tauner committed
254
255

  # sanitize and validate read design constants
256
257
258
259
  $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
260
  }
261
262

  my $fiu_num = 0;
263
  # loop over all read fius
264
265
  while (1) {
    my $fiu_name = "FIU" . $fiu_num;
266
267
    my $fiu_ref = $fiji_ini->get_block($fiu_name);
    if (!(%$fiu_ref)) {
268
269
      last;
    }
270
271
    $fiu_ref = _rename_import(FIUMAP, $fiu_ref);
    if (!defined($design_ref)) {
272
273
274
      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
275
    }
276

Stefan Tauner's avatar
Stefan Tauner committed
277
278
    my $tmp_fiu = {};
    _set_defaults(FIUMAP, $tmp_fiu, $phase);
279
280
281
    # overwrite default entries
    foreach my $k (keys(%{$fiu_ref})) {
      $tmp_fiu->{$k} = $fiu_ref->{$k};
Stefan Tauner's avatar
Stefan Tauner committed
282
    }
283
284
285
286
287
    $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.";
288
289
      $logger->error($msg);
      return $msg;
290
    }
291
    $fiji_settings_ref->{'fius'}->[$fiu_num] = $fiu_ref;
292
293
294
295
296
    $fiu_num++;
    $logger->trace("Read in $fiu_name from FIJI Settings file successfully.");
  }

  if ($fiu_num == 0) {
297
    $logger->debug("Could not fetch any FIU blocks from config file \"$fiji_ini_file\"");
298
  }
299
300
301

  # FIU_NUM is optional in the Settings file... if it was set check that
  # it corresponds to the number of FIU<number> blocks.
302
  if (defined($design_ref->{'FIU_NUM'}) && $design_ref->{'FIU_NUM'} != $fiu_num) {
303
304
305
      my $msg = FIU_NUM->{'ini_name'} . " does not match the numbers of FIU blocks found.";
      $logger->error($msg);
      return $msg;
306
  } else {
307
    $design_ref->{'FIU_NUM'} = $fiu_num; # assume the best if FIU_NUM constant is not given
308
309
  }

310
  splice(@{$fiji_settings_ref->{'fius'}}, $fiu_num);
311
  $logger->info("Successfully read in design constants and $fiu_num FIU definitions from FIJI Settings file \"$fiji_ini_file\".");
312
313
314
  return $fiji_settings_ref;
}

Stefan Tauner's avatar
Stefan Tauner committed
315
## @method set_fiu_defaults (%$fiu_ref)
316
317
318
319
# @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
320
321
322
323
324
325
326
327
  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) = @_;
328
  foreach my $k (keys(%{$map_ref})) {
329
    if (exists($map_ref->{$k}->{'default'})) {
Stefan Tauner's avatar
Stefan Tauner committed
330
      # Set default only if it is not mandatory in the given phase.
331
332
      if (defined($phase) && scalar(grep {$_ eq $phase} @{$map_ref->{$k}->{'phases_opt'}}) == 0) {
        next;
333
      }
334

335
336
337
338
      # Also, do not overwrite existing values
      if (defined($consts_ref->{$k})) {
        next;
      }
339
340
      $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}));
341
342
    }
  }
343
344
}

345

Stefan Tauner's avatar
Stefan Tauner committed
346
347
348
349
350
351
352
353
354
355
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);
}

356
357
358
359
360
361
362
363
364
365
## @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
366
  my $logger = get_logger();
367
  my ($map_ref, $k, $v_ref, $old, $log_func) = @_;
Stefan Tauner's avatar
Stefan Tauner committed
368
369
370
  $log_func = \&Log::Log4perl::Logger::trace if !defined($log_func);
  if (defined($map_ref->{$k}->{'type'})) {
    my $orig = ${$v_ref};
371
372
373
374
375
    # 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'}}
    # }

376
377
378
379
    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
380
381
382
      if ($orig !~ /^(0|(0[xX][[:xdigit:]]+))$/) {
        $log_func->($logger, "$k: $orig does not look like a natural hexadecimal number.");
        return 0;
383
      }
Stefan Tauner's avatar
Stefan Tauner committed
384
385
386
387
388
389
390
      ${$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') {
391
      # Match hexadecimal, binary, octal and decimal numbers (the latter also in scientific notation)
Stefan Tauner's avatar
Stefan Tauner committed
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
      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;
417
      }
Stefan Tauner's avatar
Stefan Tauner committed
418
419
420
421
422
423
      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;
424
    }
425
    $logger->trace("Converted value of $k (\"$orig\") to \"${$v_ref}\".") if (defined($orig) && $orig ne ${$v_ref});
Stefan Tauner's avatar
Stefan Tauner committed
426
  }
427

Stefan Tauner's avatar
Stefan Tauner committed
428
429
430
431
432
433
434
  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;
435
      }
Stefan Tauner's avatar
Stefan Tauner committed
436
    } elsif (ref($values_ref) eq 'CODE') {
437
      if (!$values_ref->(${$v_ref}, $old)) {
Stefan Tauner's avatar
Stefan Tauner committed
438
439
        $log_func->($logger, "$k: ${$v_ref} is not allowed.");
        return 0;
440
      }
441
442
    }
  }
Stefan Tauner's avatar
Stefan Tauner committed
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
  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.
466
  foreach my $k (keys(%{$map_ref})) {
Stefan Tauner's avatar
Stefan Tauner committed
467
468
469
    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};
470
471
472
473
474
      $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
475
476
477
      delete $consts_ref->{$ini_name};
    }
  }
478
  foreach my $entry_key (keys(%{$consts_ref})) {
Stefan Tauner's avatar
Stefan Tauner committed
479
480
481
482
483
484
    if (!exists($map_ref->{$entry_key})) {
      $logger->debug("Deleting unknown setting %s = %s.", $entry_key, $consts_ref->{$entry_key});
      delete $consts_ref->{$entry_key};
    }
  }

485
  return $consts_ref;
486
487
}

Stefan Tauner's avatar
Stefan Tauner committed
488

489
## @function _sanitize_fiu (%$fiu_ref, $phase)
490
491
# @brief Convert and sanity check FIJI Settings.
#
492
493
494
495
496
# \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
497
sub _sanitize_fiu ($;$) {
498
  my $logger = get_logger();
Stefan Tauner's avatar
Stefan Tauner committed
499
  my ($fiu_ref, $phase) = @_;
500
  if (ref($fiu_ref) ne 'HASH') {
501
502
503
    my $msg = "Parameter is not a reference to a hash (containing FIU constants).";
    $logger->error($msg);
    return $msg;
504
505
  }

Stefan Tauner's avatar
Stefan Tauner committed
506
  $fiu_ref = _validate_hashmap(FIUMAP, $fiu_ref, $phase);
507
  if (!ref($fiu_ref)) {
Stefan Tauner's avatar
Stefan Tauner committed
508
      $logger->error("Could not validate Design Constants.");
509
      return $fiu_ref;
510
511
512
513
514
  }

  return $fiu_ref;
}

Stefan Tauner's avatar
Stefan Tauner committed
515

516
517
518
519
520
521
522
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
523
524
525
sub _validate_hashmap ($$;$) {
  my $logger = get_logger();
  my ($map_ref, $consts_ref, $phase) = @_;
526
527
  my @map_keys = keys(%{$map_ref});
  foreach my $entry_key (keys(%{$consts_ref})) {
Stefan Tauner's avatar
Stefan Tauner committed
528
529
530
531
532
533
534
    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;
    }

535
536
537
538
    @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})) {
539
540
541
      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
542
543
544
545
546
547
548
549
550
551
552
553
554
    }
  }

  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) {
555
556
557
558
559
    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
       ) {
560
561
562
      my $msg = "$k is mandatory in phase $phase.";
      $logger->error($msg);
      return $msg;
Stefan Tauner's avatar
Stefan Tauner committed
563
564
565
566
567
568
    }
  }
  return $consts_ref
}


569
## @function _sanitize_design (%$consts_ref, $phase)
Stefan Tauner's avatar
Stefan Tauner committed
570
# @brief Sanity check FIJI Design Settings.
571
#
Stefan Tauner's avatar
Stefan Tauner committed
572
# The function deals with sanity checks for the values
573
# themselves. It checks for the following conditions:
574
575
576
#
#   - FIU_NUM: > 0
#   - FIU_CFG_BITS: > 0
577
#   - TIMER_WIDTH: > 0, <= 16
578
579
580
#   - ID: > 0, < 2^15-1
#   - BAUDRATE: > 0
#
581
# \param consts_ref a reference to a hash containing some design settings
Stefan Tauner's avatar
Stefan Tauner committed
582
# \param phase the tool flow phase that defines the rules to check against
583
#
Stefan Tauner's avatar
Stefan Tauner committed
584
# \returns The given hash with all constants required in the design
585
#          settings in sanitized form, or an error message.
Stefan Tauner's avatar
Stefan Tauner committed
586
sub _sanitize_design {
587
  my $logger = get_logger();
Stefan Tauner's avatar
Stefan Tauner committed
588
  my ($consts_ref, $phase) = @_;
589
  if (ref($consts_ref) ne 'HASH') {
590
591
592
    my $msg = "Parameter is not a reference to a hash (containing design constants).";
    $logger->error($msg);
    return $msg;
593
  }
Stefan Tauner's avatar
Stefan Tauner committed
594
  
595
  # check for sane values
Stefan Tauner's avatar
Stefan Tauner committed
596
  $consts_ref = _validate_hashmap(DESIGNMAP, $consts_ref, $phase);
597
598
599
600
  if (!ref($consts_ref)) {
      my $msg = "Could not validate Design Constants.";
      $logger->error($msg);
      return "$msg $consts_ref";
601
  }
Stefan Tauner's avatar
Stefan Tauner committed
602
603

  if (($consts_ref->{'FIU_CFG_BITS'} <= 0)) {
604
605
606
      my $msg = "FIU_CFG_BITS is <= 0.";
      $logger->error($msg);
      return $msg;
607
  }
608
  if (($consts_ref->{'TIMER_WIDTH'} <= 0) || ($consts_ref->{'TIMER_WIDTH'} > 16)) {
609
610
611
      my $msg = "TIMER_WIDTH is invalid ($consts_ref->{'TIMER_WIDTH'}).";
      $logger->error($msg);
      return $msg;
612
  }
613
  if (defined($consts_ref->{'ID'}) && ($consts_ref->{ID} < 0 || $consts_ref->{ID} > (2**16 - 1))) {
614
615
616
      my $msg = "ID is invalid ($consts_ref->{ID}).";
      $logger->error($msg);
      return $msg;
617
  }
Stefan Tauner's avatar
Stefan Tauner committed
618
  if (($consts_ref->{'BAUDRATE'} <= 0)) {
619
620
621
      my $msg = "BAUDRATE missing is <= 0.";
      $logger->error($msg);
      return $msg;
622
  }
Stefan Tauner's avatar
Stefan Tauner committed
623

624
  return $consts_ref;
625
626
627
}

1;