Settings.pm 31.6 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
#-------------------------------------------------------------------------------
#  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.
#-------------------------------------------------------------------------------

21
## @file
22

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

28
29
30
use strict;
use warnings;

31
use Scalar::Util 'blessed';
32
33
34
use Log::Log4perl qw(get_logger);
use Scalar::Util "looks_like_number";
use Config::Simple;
35
36
37
use Data::Dumper;
use POSIX qw(ceil);
use List::Util qw(max);
38

39
40
use FIJI qw(:all);

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

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

    my $fiji_settings_ref;

    # if there is no existing settings instance yet, create one
    if ( !defined($existing_settings) ) {
        $fiji_settings_ref = {};
        $fiji_settings_ref = bless( $fiji_settings_ref, $class );
62
    } else {
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
        $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;
    }

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

88
89
90
91
    @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" );

    return $fiji_settings_ref;
}
Stefan Tauner's avatar
Stefan Tauner committed
92
93

sub _export_value {
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
    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'})) {
    }
109
110
111
112
113
114
115
116
}

## @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
117
sub save ($) {
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
    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' );
    my $design_ref;
    my $fiu_cnt = 0;
    foreach my $key ( keys %{$self} ) {
        my $val = $self->{$key};
        if ( ref( \$val ) eq "REF" ) {
            if ( ref($val) eq "HASH" ) {
                if ( $key eq "design" ) {
                    $design_ref = $val;
                    next;
                }
            } elsif ( ref($val) eq "ARRAY" ) {
                if ( $key eq "fius" ) {
                    foreach my $fiu ( @{$val} ) {
                        my $ini_fiu;

                        foreach my $k ( keys( %{$fiu} ) ) {
                            my $ini_name = FIUMAP->{$k}->{'ini_name'};
                            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.
                            $ini_fiu->{$ini_name} = $fiu->{$k};

                            # Convert value to external representation
                            _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" ), );
                        }
                        $fiji_ini->set_block( "FIU" . $fiu_cnt++, $ini_fiu );
                    }
                    next;
                }
156
157
            }
        }
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
        my $err = "Unknown element found in FIJI Settings: \"$val\"";
        $logger->error($err);
        return $err;
    }
    $design_ref->{'FIU_NUM'} = $fiu_cnt;
    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};
173

174
175
176
177
178
179
180
181
182
183
184
185
186
        # Convert value to external representation
        _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" ), );
    }
    $fiji_ini->set_block( "CONSTS", $ini_design );

    if ( !defined( $fiji_ini->write($fiji_ini_file) ) ) {
        my $err = Config::Simple->error();
        $logger->error($err);
        return $err;
    }
    return undef;
}
187

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

    my $design_ref = $fiji_ini->get_block("CONSTS");
    if ( !(%$design_ref) ) {
        my $msg = "Could not fetch CONSTS block from config file \"$fiji_ini_file\"";
        $logger->error($msg);
        return $msg;
    }
    _set_defaults( DESIGNMAP, $design_ref, $phase );

    $design_ref = _rename_import( DESIGNMAP, $design_ref );
    if ( !defined($design_ref) ) {
        my $msg = "Design constants do not match the FIJI Settings naming scheme.";
        $logger->error($msg);
        return $msg;
    }

    # Create a new instance or reuse the shallow hull of the existing one
    my $fiji_settings_ref;
    if ( !defined($existing_settings) ) {
        $fiji_settings_ref = {};
        bless( $fiji_settings_ref, "FIJI::Settings" );
Stefan Tauner's avatar
Stefan Tauner committed
236
    } else {
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
        $fiji_settings_ref = $existing_settings;

        # Clear the hash
        for ( keys %$fiji_settings_ref ) {
            delete $fiji_settings_ref->{$_};
        }
    }
    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;
    }
    $fiji_settings_ref->{'design'} = $design_ref;
    $fiji_settings_ref->{'fius'}   = [];

    # sanitize and validate read design constants
    $design_ref = _sanitize_design( $design_ref, $phase );
    if ( !ref($design_ref) ) {
        $logger->error($design_ref);
        return $design_ref;
    }

    my $fiu_num = 0;

    # loop over all read fius
    while (1) {
        my $fiu_name = "FIU" . $fiu_num;
        my $fiu_ref  = $fiji_ini->get_block($fiu_name);
        if ( !(%$fiu_ref) ) {
            last;
        }
        $fiu_ref = _rename_import( FIUMAP, $fiu_ref );
        if ( !defined($design_ref) ) {
            my $msg = "FIU constants of $fiu_name do not match the FIJI Settings naming scheme.";
            $logger->error($msg);
            return $msg;
        }

        my $tmp_fiu = {};
        _set_defaults( FIUMAP, $tmp_fiu, $phase );

        # overwrite default entries
        foreach my $k ( keys( %{$fiu_ref} ) ) {
            $tmp_fiu->{$k} = $fiu_ref->{$k};
        }
        $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.";
            $logger->error($msg);
            return $msg;
        }
        $fiji_settings_ref->{'fius'}->[$fiu_num] = $fiu_ref;
        $fiu_num++;
        $logger->trace("Read in $fiu_name from FIJI Settings file successfully.");
    }

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

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

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

Stefan Tauner's avatar
Stefan Tauner committed
319
## @method set_fiu_defaults (%$fiu_ref)
320
321
322
# @brief Overwrite existing fields (if defined) with defaults defined in FIJI.pm.
#
sub set_fiu_defaults ($) {
323
324
    my ( $self, $consts_ref ) = @_;
    return _set_defaults( FIUMAP, $consts_ref );
Stefan Tauner's avatar
Stefan Tauner committed
325
326
327
328
329
}

## @function _set_defaults (%$map_ref, %$consts_ref)
# @brief Set defaults according to FIJI.pm.
sub _set_defaults {
330
331
332
333
    my $logger = get_logger();
    my ( $map_ref, $consts_ref, $phase ) = @_;
    foreach my $k ( keys( %{$map_ref} ) ) {
        if ( exists( $map_ref->{$k}->{'default'} ) ) {
334

335
336
337
338
339
340
341
342
343
344
345
346
347
348
            # Set default only if it is not mandatory in the given phase.
            if ( defined($phase) && scalar( grep { $_ eq $phase } @{ $map_ref->{$k}->{'phases_opt'} } ) == 0 ) {
                next;
            }

            # Also, do not overwrite existing values
            if ( defined( $consts_ref->{$k} ) ) {
                next;
            }
            $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} ) );
        }
    }
}
349

Stefan Tauner's avatar
Stefan Tauner committed
350
sub validate_design_value {
351
352
    my ( $k, $v_ref ) = @_;
    return validate_value( DESIGNMAP, $k, $v_ref );
Stefan Tauner's avatar
Stefan Tauner committed
353
354
355
}

sub validate_fiu_value {
356
357
    my ( $k, $v_ref ) = @_;
    return validate_value( FIUMAP, $k, $v_ref );
Stefan Tauner's avatar
Stefan Tauner committed
358
359
}

360
361
362
363
364
365
366
367
368
369
## @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 ($$$;$$) {
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
    my $logger = get_logger();
    my ( $map_ref, $k, $v_ref, $old, $log_func ) = @_;
    $log_func = \&Log::Log4perl::Logger::trace if !defined($log_func);
    if ( defined( $map_ref->{$k}->{'type'} ) ) {
        my $orig = ${$v_ref};

        # 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'}}
        # }

        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' ) {
            if ( $orig !~ /^(0|(0[xX][[:xdigit:]]+))$/ ) {
                $log_func->( $logger, "$k: $orig does not look like a natural hexadecimal number." );
                return 0;
            }
            ${$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' ) {

            # Match hexadecimal, binary, octal and decimal numbers (the latter also in scientific notation)
            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;
            }
Stefan Tauner's avatar
Stefan Tauner committed
411

412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
            # 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;
            }
            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;
        }
        $logger->trace("Converted value of $k (\"$orig\") to \"${$v_ref}\".") if ( defined($orig) && $orig ne ${$v_ref} );
    }

    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;
            }
        } elsif ( ref($values_ref) eq 'CODE' ) {
            if ( !$values_ref->( ${$v_ref}, $old ) ) {
                $log_func->( $logger, "$k: ${$v_ref} is not allowed." );
                return 0;
            }
        }
    }
    return 1;
}
Stefan Tauner's avatar
Stefan Tauner committed
457
458
459
460
461
462
463
464
465
466
467

# @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 {
468
469
470
471
472
473
    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;
    }
474

475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
    # 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 Design 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};
        }
    }

    return $consts_ref;
}
Stefan Tauner's avatar
Stefan Tauner committed
494

495
## @function _sanitize_fiu (%$fiu_ref, $phase)
496
497
# @brief Convert and sanity check FIJI Settings.
#
498
499
500
501
502
# \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
503
sub _sanitize_fiu ($;$) {
504
505
506
507
508
509
510
    my $logger = get_logger();
    my ( $fiu_ref, $phase ) = @_;
    if ( ref($fiu_ref) ne 'HASH' ) {
        my $msg = "Parameter is not a reference to a hash (containing FIU constants).";
        $logger->error($msg);
        return $msg;
    }
511

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

518
    return $fiu_ref;
519
520
}

521
522
523
524
525
sub _disabled_via_dependency ($$$) {
    my ( $map_ref, $consts_ref, $k ) = @_;
    my $dependency = $map_ref->{$k}->{'depends_on'};
    return defined($dependency) && !$consts_ref->{$dependency};
}
526

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

540
541
542
543
544
545
546
547
548
549
550
551
552
553
        @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} ) ) {
            my $msg = sprintf( "%s = %s is invalid.", $entry_key, !defined( $consts_ref->{$entry_key} ) ? "<undef>" : $consts_ref->{$entry_key} );
            $logger->error($msg);
            return $msg;
        }

        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
554
    }
555

556
    if ( !defined($phase) ) {
557

558
559
560
561
562
        # 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;
    }
563

564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
    # Iterate over the constants defined in FIJI.pm that apparently are
    # not contained in $consts_ref.
    foreach my $k (@map_keys) {
        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
          )
        {
            my $msg = "$k is mandatory in phase $phase.";
            $logger->error($msg);
            return $msg;
        }

    }
Stefan Tauner's avatar
Stefan Tauner committed
579

580
581
582
583
    # TODO: implement the same mechanism with forbidden_by

    return $consts_ref;
}
Stefan Tauner's avatar
Stefan Tauner committed
584

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

    # check for sane values
    $consts_ref = _validate_hashmap( DESIGNMAP, $consts_ref, $phase );
    if ( !ref($consts_ref) ) {
        my $msg = "Could not validate Design Constants.";
        $logger->error($msg);
        return "$msg $consts_ref";
    }

    if ( ( $consts_ref->{'FIU_CFG_BITS'} <= 0 ) ) {
        my $msg = "FIU_CFG_BITS is <= 0.";
        $logger->error($msg);
        return $msg;
    }
    if ( ( $consts_ref->{'TIMER_WIDTH'} <= 0 ) || ( $consts_ref->{'TIMER_WIDTH'} > 16 ) ) {
        my $msg = "TIMER_WIDTH is invalid ($consts_ref->{'TIMER_WIDTH'}).";
        $logger->error($msg);
        return $msg;
    }
    if ( defined( $consts_ref->{'ID'} ) && ( $consts_ref->{ID} < 0 || $consts_ref->{ID} > ( 2**16 - 1 ) ) ) {
        my $msg = "ID is invalid ($consts_ref->{ID}).";
        $logger->error($msg);
        return $msg;
    }
    if ( ( $consts_ref->{'BAUDRATE'} <= 0 ) ) {
        my $msg = "BAUDRATE missing is <= 0.";
        $logger->error($msg);
        return $msg;
    }

    return $consts_ref;
641
642
}

643
644
sub _log2 {
    my $val = shift;
645
    return ( $val > 0 ) ? ( log($val) / log(2) ) : 0;
646
647
}

Christian Fibich's avatar
Christian Fibich committed
648
649
650
651
## Determined by experiment & fitted by scipy.optimize.curve_fit
#
sub _est_resources {
    my $logger = get_logger();
652
    my ( $FREQUENCY, $BAUD, $TIMER_WIDTH, $RESET_CYCLES, $LFSR_WIDTH, $FIU_NUM, $algo ) = @_;
Christian Fibich's avatar
Christian Fibich committed
653

654
655
656
    # FIXME where do we put these values? they are likely to change if the VHDL
    # source is changed...

657
    $algo = "logarithmic" if ( !defined $algo );
Christian Fibich's avatar
Christian Fibich committed
658
659
660
661
662
663

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

664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
    $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;
        $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) );

        $lutsum = 141.184641269;
        $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) );

        #    $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" ) {
Christian Fibich's avatar
Christian Fibich committed
704
705

        $registers = -2010875.59555;
706
707
708
709
710
711
        $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 );
Christian Fibich's avatar
Christian Fibich committed
712
713

        $lutsum = 94081.448429;
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
        $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 );
Christian Fibich's avatar
Christian Fibich committed
740
741
}

742
sub estimate_resources {
743
    my $logger         = get_logger();
744
    my ($settings_ref) = @_;
745
746
747
    my $consts_ref     = $settings_ref->{'design'};
    my $fiu_ref        = $settings_ref->{'fius'};
    my $fiu_num        = @{$fiu_ref};
Christian Fibich's avatar
Christian Fibich committed
748

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

751
    my $rv = {
752
753
754
        regs     => sprintf( "%.2f", $calcrv[0] / $base_resources[0] ),
        lut_calc => sprintf( "%.2f", $calcrv[1] / $base_resources[1] ),
    };
Christian Fibich's avatar
Christian Fibich committed
755

756
    $logger->debug("ESTIMATE: current config will need Base*$rv->{'regs'} registers and Base*$rv->{'lut_calc'} combinational resources");
757
758
759
    return $rv;
}

760
1;