Settings.pm 29.8 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
use Log::Log4perl qw(get_logger);
use Scalar::Util "looks_like_number";
use Config::Simple;
36
37
38
use Data::Dumper;
use POSIX qw(ceil);
use List::Util qw(max);
39

40
41
use FIJI qw(:all);

Christian Fibich's avatar
Christian Fibich committed
42
43
my @base_resources;

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

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

76
  # If there is a file given, try to read it. Else just create a default instance.
77
  if (defined($fiji_ini_file)) {
Stefan Tauner's avatar
Stefan Tauner committed
78
    $fiji_settings_ref = read_settingsfile($phase, $fiji_ini_file, $fiji_settings_ref);
79
80
    if (!ref($fiji_settings_ref)) {
      return $fiji_settings_ref; # actually an error message
81
82
    }
  } else {
83
84
    $fiji_settings_ref->{'design'} = {};
    _set_defaults(DESIGNMAP, $fiji_settings_ref->{'design'}, $phase);
85
    $fiji_settings_ref->{'fius'} = [];
86
  }
87

Christian Fibich's avatar
Christian Fibich committed
88
89
90
91
92
93
  @base_resources = _est_resources(DESIGNMAP->{'FREQUENCY'}->{'default'},
                                    DESIGNMAP->{'BAUDRATE'}->{'default'},
                                    DESIGNMAP->{'TIMER_WIDTH'}->{'default'},
                                    DESIGNMAP->{'RESET_DUT_IN_DURATION'}->{'default'},
                                    DESIGNMAP->{'LFSR_WIDTH'}->{'default'},0,"logarithmic");

Stefan Tauner's avatar
Stefan Tauner committed
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
  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'})) {
111
112
113
114
115
116
117
118
119
  }
}

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

190
191
192
193
194
195
196
197
198
  if (!defined($fiji_ini->write($fiji_ini_file))) {
    my $err = Config::Simple->error();
    $logger->error($err);
    return $err;
  }
  return undef;
}


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

227
228
  my $design_ref = $fiji_ini->get_block("CONSTS");
  if (!(%$design_ref)) {
229
230
231
    my $msg = "Could not fetch CONSTS block from config file \"$fiji_ini_file\"";
    $logger->error($msg);
    return $msg;
232
  }
233
  _set_defaults(DESIGNMAP, $design_ref, $phase);
Stefan Tauner's avatar
Stefan Tauner committed
234

235
236
  $design_ref = _rename_import(DESIGNMAP, $design_ref);
  if (!defined($design_ref)) {
237
238
239
    my $msg = "Design constants do not match the FIJI Settings naming scheme.";
    $logger->error($msg);
    return $msg;
240
  }
Stefan Tauner's avatar
Stefan Tauner committed
241

242
  # Create a new instance or reuse the shallow hull of the existing one
243
  my $fiji_settings_ref;
244
245
246
247
248
249
250
251
  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
252
    }
253
254
255
256
257
   }
  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
258
    } else {
259
      $msg = "Given settings are not of type FIJI::Settings.";
Stefan Tauner's avatar
Stefan Tauner committed
260
    }
261
262
    $logger->error($msg);
    return $msg;
263
  }
264
  $fiji_settings_ref->{'design'} = $design_ref;
265
  $fiji_settings_ref->{'fius'} = [];
Stefan Tauner's avatar
Stefan Tauner committed
266
267

  # sanitize and validate read design constants
268
269
270
271
  $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
272
  }
273
274

  my $fiu_num = 0;
275
  # loop over all read fius
276
277
  while (1) {
    my $fiu_name = "FIU" . $fiu_num;
278
279
    my $fiu_ref = $fiji_ini->get_block($fiu_name);
    if (!(%$fiu_ref)) {
280
281
      last;
    }
282
283
    $fiu_ref = _rename_import(FIUMAP, $fiu_ref);
    if (!defined($design_ref)) {
284
285
286
      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
287
    }
288

Stefan Tauner's avatar
Stefan Tauner committed
289
290
    my $tmp_fiu = {};
    _set_defaults(FIUMAP, $tmp_fiu, $phase);
291
292
293
    # overwrite default entries
    foreach my $k (keys(%{$fiu_ref})) {
      $tmp_fiu->{$k} = $fiu_ref->{$k};
Stefan Tauner's avatar
Stefan Tauner committed
294
    }
295
296
297
298
299
    $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.";
300
301
      $logger->error($msg);
      return $msg;
302
    }
303
    $fiji_settings_ref->{'fius'}->[$fiu_num] = $fiu_ref;
304
305
306
307
308
    $fiu_num++;
    $logger->trace("Read in $fiu_name from FIJI Settings file successfully.");
  }

  if ($fiu_num == 0) {
309
    $logger->debug("Could not fetch any FIU blocks from config file \"$fiji_ini_file\"");
310
  }
311
312
313

  # FIU_NUM is optional in the Settings file... if it was set check that
  # it corresponds to the number of FIU<number> blocks.
314
  if (defined($design_ref->{'FIU_NUM'}) && $design_ref->{'FIU_NUM'} != $fiu_num) {
315
316
317
      my $msg = FIU_NUM->{'ini_name'} . " does not match the numbers of FIU blocks found.";
      $logger->error($msg);
      return $msg;
318
  } else {
319
    $design_ref->{'FIU_NUM'} = $fiu_num; # assume the best if FIU_NUM constant is not given
320
321
  }

322
  splice(@{$fiji_settings_ref->{'fius'}}, $fiu_num);
323
  $logger->info("Successfully read in design constants and $fiu_num FIU definitions from FIJI Settings file \"$fiji_ini_file\".");
324
325
326
  return $fiji_settings_ref;
}

Stefan Tauner's avatar
Stefan Tauner committed
327
## @method set_fiu_defaults (%$fiu_ref)
328
329
330
331
# @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
332
333
334
335
336
337
338
339
  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) = @_;
340
  foreach my $k (keys(%{$map_ref})) {
341
    if (exists($map_ref->{$k}->{'default'})) {
Stefan Tauner's avatar
Stefan Tauner committed
342
      # Set default only if it is not mandatory in the given phase.
343
344
      if (defined($phase) && scalar(grep {$_ eq $phase} @{$map_ref->{$k}->{'phases_opt'}}) == 0) {
        next;
345
      }
346

347
348
349
350
      # Also, do not overwrite existing values
      if (defined($consts_ref->{$k})) {
        next;
      }
351
352
      $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}));
353
354
    }
  }
355
356
}

357

Stefan Tauner's avatar
Stefan Tauner committed
358
359
360
361
362
363
364
365
366
367
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);
}

368
369
370
371
372
373
374
375
376
377
## @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
378
  my $logger = get_logger();
379
  my ($map_ref, $k, $v_ref, $old, $log_func) = @_;
Stefan Tauner's avatar
Stefan Tauner committed
380
381
382
  $log_func = \&Log::Log4perl::Logger::trace if !defined($log_func);
  if (defined($map_ref->{$k}->{'type'})) {
    my $orig = ${$v_ref};
383
384
385
386
387
    # 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'}}
    # }

388
389
390
391
    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
392
393
394
      if ($orig !~ /^(0|(0[xX][[:xdigit:]]+))$/) {
        $log_func->($logger, "$k: $orig does not look like a natural hexadecimal number.");
        return 0;
395
      }
Stefan Tauner's avatar
Stefan Tauner committed
396
397
398
399
400
401
402
      ${$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') {
403
      # Match hexadecimal, binary, octal and decimal numbers (the latter also in scientific notation)
Stefan Tauner's avatar
Stefan Tauner committed
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
      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;
429
      }
Stefan Tauner's avatar
Stefan Tauner committed
430
431
432
433
434
435
      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;
436
    }
437
    $logger->trace("Converted value of $k (\"$orig\") to \"${$v_ref}\".") if (defined($orig) && $orig ne ${$v_ref});
Stefan Tauner's avatar
Stefan Tauner committed
438
  }
439

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

497
  return $consts_ref;
498
499
}

Stefan Tauner's avatar
Stefan Tauner committed
500

501
## @function _sanitize_fiu (%$fiu_ref, $phase)
502
503
# @brief Convert and sanity check FIJI Settings.
#
504
505
506
507
508
# \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
509
sub _sanitize_fiu ($;$) {
510
  my $logger = get_logger();
Stefan Tauner's avatar
Stefan Tauner committed
511
  my ($fiu_ref, $phase) = @_;
512
  if (ref($fiu_ref) ne 'HASH') {
513
514
515
    my $msg = "Parameter is not a reference to a hash (containing FIU constants).";
    $logger->error($msg);
    return $msg;
516
517
  }

Stefan Tauner's avatar
Stefan Tauner committed
518
  $fiu_ref = _validate_hashmap(FIUMAP, $fiu_ref, $phase);
519
  if (!ref($fiu_ref)) {
Stefan Tauner's avatar
Stefan Tauner committed
520
      $logger->error("Could not validate Design Constants.");
521
      return $fiu_ref;
522
523
524
525
526
  }

  return $fiu_ref;
}

Stefan Tauner's avatar
Stefan Tauner committed
527

528
529
530
531
532
533
534
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
535
536
537
sub _validate_hashmap ($$;$) {
  my $logger = get_logger();
  my ($map_ref, $consts_ref, $phase) = @_;
538
539
  my @map_keys = keys(%{$map_ref});
  foreach my $entry_key (keys(%{$consts_ref})) {
540
    my $forbidden_by = $map_ref->{$entry_key}->{'forbidden_by'};
Stefan Tauner's avatar
Stefan Tauner committed
541
542
543
544
545
546
547
    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;
    }

548
549
550
551
    @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})) {
552
553
554
      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
555
    }
556
557
558
559
560
561

    if ( defined ($forbidden_by) && $consts_ref->{$entry_key} &&  $consts_ref->{$forbidden_by} ) {
      my $msg = "$entry_key is forbidden when $forbidden_by is enabled";
      $logger->error($msg);
      return $msg;
    }
Stefan Tauner's avatar
Stefan Tauner committed
562
563
564
565
566
567
568
569
570
571
572
573
  }

  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) {
574
    my $dependency   = $map_ref->{$k}->{'depends_on'};
575
576
577
578
    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
       ) {
579
580
581
      my $msg = "$k is mandatory in phase $phase.";
      $logger->error($msg);
      return $msg;
Stefan Tauner's avatar
Stefan Tauner committed
582
    }
583

Stefan Tauner's avatar
Stefan Tauner committed
584
  }
585
586
587

  # TODO: implement the same mechanism with forbidden_by

Stefan Tauner's avatar
Stefan Tauner committed
588
589
590
591
  return $consts_ref
}


592
## @function _sanitize_design (%$consts_ref, $phase)
Stefan Tauner's avatar
Stefan Tauner committed
593
# @brief Sanity check FIJI Design Settings.
594
#
Stefan Tauner's avatar
Stefan Tauner committed
595
# The function deals with sanity checks for the values
596
# themselves. It checks for the following conditions:
597
598
599
#
#   - FIU_NUM: > 0
#   - FIU_CFG_BITS: > 0
600
#   - TIMER_WIDTH: > 0, <= 16
601
602
603
#   - ID: > 0, < 2^15-1
#   - BAUDRATE: > 0
#
604
# \param consts_ref a reference to a hash containing some design settings
Stefan Tauner's avatar
Stefan Tauner committed
605
# \param phase the tool flow phase that defines the rules to check against
606
#
Stefan Tauner's avatar
Stefan Tauner committed
607
# \returns The given hash with all constants required in the design
608
#          settings in sanitized form, or an error message.
Stefan Tauner's avatar
Stefan Tauner committed
609
sub _sanitize_design {
610
  my $logger = get_logger();
Stefan Tauner's avatar
Stefan Tauner committed
611
  my ($consts_ref, $phase) = @_;
612
  if (ref($consts_ref) ne 'HASH') {
613
614
615
    my $msg = "Parameter is not a reference to a hash (containing design constants).";
    $logger->error($msg);
    return $msg;
616
  }
Stefan Tauner's avatar
Stefan Tauner committed
617
  
618
  # check for sane values
Stefan Tauner's avatar
Stefan Tauner committed
619
  $consts_ref = _validate_hashmap(DESIGNMAP, $consts_ref, $phase);
620
621
622
623
  if (!ref($consts_ref)) {
      my $msg = "Could not validate Design Constants.";
      $logger->error($msg);
      return "$msg $consts_ref";
624
  }
Stefan Tauner's avatar
Stefan Tauner committed
625
626

  if (($consts_ref->{'FIU_CFG_BITS'} <= 0)) {
627
628
629
      my $msg = "FIU_CFG_BITS is <= 0.";
      $logger->error($msg);
      return $msg;
630
  }
631
  if (($consts_ref->{'TIMER_WIDTH'} <= 0) || ($consts_ref->{'TIMER_WIDTH'} > 16)) {
632
633
634
      my $msg = "TIMER_WIDTH is invalid ($consts_ref->{'TIMER_WIDTH'}).";
      $logger->error($msg);
      return $msg;
635
  }
636
  if (defined($consts_ref->{'ID'}) && ($consts_ref->{ID} < 0 || $consts_ref->{ID} > (2**16 - 1))) {
637
638
639
      my $msg = "ID is invalid ($consts_ref->{ID}).";
      $logger->error($msg);
      return $msg;
640
  }
Stefan Tauner's avatar
Stefan Tauner committed
641
  if (($consts_ref->{'BAUDRATE'} <= 0)) {
642
643
644
      my $msg = "BAUDRATE missing is <= 0.";
      $logger->error($msg);
      return $msg;
645
  }
Stefan Tauner's avatar
Stefan Tauner committed
646

647
  return $consts_ref;
648
649
}

650
651
652
653
654
sub _log2 {
    my $val = shift;
    return ($val > 0) ? (log($val)/log(2)) : 0;
}

Christian Fibich's avatar
Christian Fibich committed
655
656
657
658
659
660
## Determined by experiment & fitted by scipy.optimize.curve_fit
#
sub _est_resources {
    my $logger = get_logger();
    my ($FREQUENCY, $BAUD, $TIMER_WIDTH, $RESET_CYCLES, $LFSR_WIDTH, $FIU_NUM, $algo) = @_;

661
662
663
    # FIXME where do we put these values? they are likely to change if the VHDL
    # source is changed...

Christian Fibich's avatar
Christian Fibich committed
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
    $algo = "logarithmic" if (!defined $algo);

    my $registers;
    my $lutsum;
    my $lut6;
    my $comb;

    $logger->debug("FREQUENCY $FREQUENCY out of range (1000000 - 500000000)")  if ($FREQUENCY    < 1000000 || $FREQUENCY    > 500000000);
    $logger->debug("BAUD $BAUD out of range (9600 - 3000000)")                 if ($BAUD         < 9600    || $BAUD         > 3000000);
    $logger->debug("TIMER_WIDTH $TIMER_WIDTH out of range (1 - 8)")            if ($TIMER_WIDTH  < 1       || $TIMER_WIDTH  > 8);
    $logger->debug("RESET_CYCLES $RESET_CYCLES out of range (1 - 16)")         if ($RESET_CYCLES < 1       || $RESET_CYCLES > 16);
    $logger->debug("LFSR_WIDTH $LFSR_WIDTH out of range (16 - 64)")            if ($LFSR_WIDTH   < 16      || $LFSR_WIDTH   > 64);
    $logger->debug("FIU_NUM $FIU_NUM out of range (0 - 64)")                   if ($FIU_NUM      < 1       || $FIU_NUM      > 64);

    if ($algo eq "logarithmic") {
        $registers  = 105.90215234;
680
681
682
683
684
685
        $registers += (-8.78501935753e-10) * ($FREQUENCY)    + (2.02400278472)   * (_log2($FREQUENCY));
        $registers += (-1.92362445319e-08) * ($BAUD)         + (-1.99903895447)  * (_log2($BAUD));
        $registers += (25.294652022)       * ($TIMER_WIDTH)  + (-3.6942823707)   * (_log2($TIMER_WIDTH));
        $registers += (-0.0388887762348)   * ($RESET_CYCLES) + (1.12647034117)   * (_log2($RESET_CYCLES));
        $registers += (1.0012871074)       * ($LFSR_WIDTH)   + (-0.035302419887) * (_log2($LFSR_WIDTH));
        $registers += (13.0019033469)      * ($FIU_NUM)      + (-1.06808285072)  * (_log2($FIU_NUM));
Christian Fibich's avatar
Christian Fibich committed
686
687

        $lutsum  = 141.184641269;
688
689
690
691
692
693
        $lutsum += (5.14383788335e-09) * ($FREQUENCY)    + (4.44319121639)   * (_log2($FREQUENCY));
        $lutsum += (8.15160754702e-07) * ($BAUD)         + (-4.96956691254)  * (_log2($BAUD));
        $lutsum += (21.280632646)      * ($TIMER_WIDTH)  + (-7.40399315196)  * (_log2($TIMER_WIDTH));
        $lutsum += (0.0181923149046)   * ($RESET_CYCLES) + (1.33545547133)   * (_log2($RESET_CYCLES));
        $lutsum += (0.0264092868862)   * ($LFSR_WIDTH)   + (-0.817645982805) * (_log2($LFSR_WIDTH));
        $lutsum += (3.88874581602)     * ($FIU_NUM)      + (0.262418360097)  * (_log2($FIU_NUM));
Christian Fibich's avatar
Christian Fibich committed
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748

    #    $lut6  = 108.374524207;
    #    $lut6 += (2.01487096337e-09)*($FREQUENCY) + (1.83543879998)*(_log2($FREQUENCY));
    #    $lut6 += (-3.30317499662e-07)*($BAUD) + (-2.11380910663)*(_log2($BAUD));
    #    $lut6 += (11.2173091132)*($TIMER_WIDTH) + (-4.43369929787)*(_log2($TIMER_WIDTH));
    #    $lut6 += (-0.154956213792)*($RESET_CYCLES) + (1.35245013922)*(_log2($RESET_CYCLES));
    #    $lut6 += (0.0215685459865)*($LFSR_WIDTH) + (-0.668625639476)*(_log2($LFSR_WIDTH));
    #    $lut6 += (2.42277105024)*($FIU_NUM) + (0.194734794118)*(_log2($FIU_NUM));

    #    $comb  = 137.316369361;
    #    $comb += (1.6814589881e-08)*($FREQUENCY) + (8.4678903776)*(_log2($FREQUENCY));
    #    $comb += (3.49377938517e-06)*($BAUD) + (-9.93771915344)*(_log2($BAUD));
    #    $comb += (41.3625159451)*($TIMER_WIDTH) + (-12.9768582431)*(_log2($TIMER_WIDTH));
    #    $comb += (0.287036354808)*($RESET_CYCLES) + (0.866505995634)*(_log2($RESET_CYCLES));
    #    $comb += (0.0360914264988)*($LFSR_WIDTH) + (-1.11962532017)*(_log2($LFSR_WIDTH));
    #    $comb += (3.88031724863)*($FIU_NUM) + (0.443465557591)*(_log2($FIU_NUM));
    } elsif ($algo eq "polynomial") {

        $registers = -2010875.59555;
        $registers += (2.76229402251e-07)*($FREQUENCY) + (-1.99515157117e-15)*($FREQUENCY**2) + (3.00590523596e-24)*($FREQUENCY**3);
        $registers += (-4.12206137854)*($BAUD) + (3.43014697591e-05)*($BAUD**2) + (-1.09771659281e-11)*($BAUD**3);
        $registers += (19.1505478618)*($TIMER_WIDTH) + (0.968580877998)*($TIMER_WIDTH**2) + (-0.0572727376031)*($TIMER_WIDTH**3);
        $registers += (2639374.42117)*($RESET_CYCLES) + (-659843.417883)*($RESET_CYCLES**2) + (31421.1133492)*($RESET_CYCLES**3);
        $registers += (3990.13449895)*($LFSR_WIDTH) + (-124.660493304)*($LFSR_WIDTH**2) + (1.11304032394)*($LFSR_WIDTH**3);
        $registers += (11.9791223372)*($FIU_NUM) + (0.0532451990358)*($FIU_NUM**2) + (-0.000610665213464)*($FIU_NUM**3);

        $lutsum = 94081.448429;
        $lutsum += (6.84901011695e-07)*($FREQUENCY) + (-5.31630997207e-15)*($FREQUENCY**2) + (8.18999405393e-24)*($FREQUENCY**3);
        $lutsum += (2.76529833194)*($BAUD) + (-2.30130317053e-05)*($BAUD**2) + (7.36465871249e-12)*($BAUD**3);
        $lutsum += (-1.31145039083)*($TIMER_WIDTH) + (4.78626536988)*($TIMER_WIDTH**2) + (-0.330321432416)*($TIMER_WIDTH**3);
        $lutsum += (-122105.518664)*($RESET_CYCLES) + (30526.6179134)*($RESET_CYCLES**2) + (-1453.65059187)*($RESET_CYCLES**3);
        $lutsum += (-2766.64987134)*($LFSR_WIDTH) + (86.4567706424)*($LFSR_WIDTH**2) + (-0.771930699286)*($LFSR_WIDTH**3);
        $lutsum += (3.54441195947)*($FIU_NUM) + (0.031558519242)*($FIU_NUM**2) + (-0.000405853019244)*($FIU_NUM**3);

    #    $lut6 = 3682107.46593
    #    $lut6 += (3.39444096763e-07)*($FREQUENCY) + (-2.85399782298e-15)*($FREQUENCY**2) + (4.47796044682e-24)*($FREQUENCY**3);
    #    $lut6 += (-5.44327330276)*($BAUD) + (4.52960038538e-05)*($BAUD**2) + (-1.44956417381e-11)*($BAUD**3);
    #    $lut6 += (-13.5605250998)*($TIMER_WIDTH) + (5.73916956842)*($TIMER_WIDTH**2) + (-0.403826909123)*($TIMER_WIDTH**3);
    #    $lut6 += (2875771582.21)*($RESET_CYCLES) + (-718942895.354)*($RESET_CYCLES**2) + (34235375.9671)*($RESET_CYCLES**3);
    #    $lut6 += (-240045086.949)*($LFSR_WIDTH) + (7501408.9663)*($LFSR_WIDTH**2) + (-66976.8657666)*($LFSR_WIDTH**3);
    #    $lut6 += (2.41751820708)*($FIU_NUM) + (0.00465166689604)*($FIU_NUM**2) + (-6.74436958278e-05)*($FIU_NUM**3);

    #    $comb = 96899684.7121
    #    $comb += (1.3719568736e-06)*($FREQUENCY) + (-1.08779793899e-14)*($FREQUENCY**2) + (1.68660125067e-23)*($FREQUENCY**3);
    #    $comb += (-130.621939943)*($BAUD) + (0.00108697725211)*($BAUD**2) + (-3.47854948947e-10)*($BAUD**3);
    #    $comb += (14.5954045564)*($TIMER_WIDTH) + (4.76821272642)*($TIMER_WIDTH**2) + (-0.30108920038)*($TIMER_WIDTH**3);
    #    $comb += (-163775236.728)*($RESET_CYCLES) + (40943809.4054)*($RESET_CYCLES**2) + (-1949705.21116)*($RESET_CYCLES**3);
    #    $comb += (3175785.67136)*($LFSR_WIDTH) + (-99243.3036534)*($LFSR_WIDTH**2) + (886.100931985)*($LFSR_WIDTH**3);
    #    $comb += (3.45401115376)*($FIU_NUM) + (0.04166391798)*($FIU_NUM**2) + (-0.000540550065177)*($FIU_NUM**3);

    }

    return ($registers, $lutsum, $lut6, $comb);
}

749
sub estimate_resources {
750
751
752
    my $logger = get_logger();
    my ($settings_ref) = @_;
    my $consts_ref = $settings_ref->{'design'};
753
    my $fiu_ref    = $settings_ref->{'fius'};
Christian Fibich's avatar
Christian Fibich committed
754
755
756
757
758
759
760
    my $fiu_num = @{$fiu_ref};

    my @calcrv = _est_resources($consts_ref->{'FREQUENCY'},
                                $consts_ref->{'BAUDRATE'},
                                $consts_ref->{'TIMER_WIDTH'},
                                $consts_ref->{'RESET_DUT_IN_DURATION'},
                                $consts_ref->{'LFSR_WIDTH'},
761
                                $fiu_num,    "logarithmic");
Christian Fibich's avatar
Christian Fibich committed
762

763
764
765
766
    my $rv = {
                regs     => sprintf("%.2f", $calcrv[0]/$base_resources[0]),
                lut_calc => sprintf("%.2f", $calcrv[1]/$base_resources[1]),
             };
Christian Fibich's avatar
Christian Fibich committed
767

768
    $logger->debug("ESTIMATE: current config will need Base*$rv->{'regs'} registers and Base*$rv->{'lut_calc'} combinational resources");
769
770
771
    return $rv;
}

772
1;