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

Christian Fibich's avatar
Christian Fibich committed
21
22
## @file Settings.pm
# @brief Contains class \ref FIJI::Settings
23

24
## @class FIJI::Settings
25
#
Christian Fibich's avatar
Christian Fibich committed
26
# @brief 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
use File::Spec;
40

41
42
use FIJI qw(:all);

Christian Fibich's avatar
Christian Fibich committed
43
## @var @base_resources stores the resource count for default config
Christian Fibich's avatar
Christian Fibich committed
44
45
my @base_resources;

46
## @function public new ($phase, $fiji_ini_file, $existing_settings)
Stefan Tauner's avatar
Stefan Tauner committed
47
48
# Create a new settings instance.
#
Christian Fibich's avatar
Christian Fibich committed
49
50
51
# \param phase Tool flow phase the settings need to be compatible with.
# \param fiji_ini_file (optional) Path to the configuration file to read.
# \param existing_settings (optional) A reference to reuse and return
52
#        with values read from file. Any contained data will be cleared.
53
54
# \returns The new settings instance or a string describing the reason
#          why it could not be created.
55
sub new ($;$$) {
56
    my $logger = get_logger("");
Christian Fibich's avatar
Christian Fibich committed
57
    my ($class, $phase, $fiji_ini_file, $existing_settings) = @_;
58
59
60
61

    my $fiji_settings_ref;

    # if there is no existing settings instance yet, create one
Christian Fibich's avatar
Christian Fibich committed
62
    if (!defined($existing_settings)) {
63
        $fiji_settings_ref = {};
Christian Fibich's avatar
Christian Fibich committed
64
        $fiji_settings_ref = bless($fiji_settings_ref, $class);
65
    } else {
66
67
        $fiji_settings_ref = $existing_settings;
    }
Christian Fibich's avatar
Christian Fibich committed
68
    if (!blessed($fiji_settings_ref) || !$fiji_settings_ref->isa("FIJI::Settings")) {
69
        my $msg;
Christian Fibich's avatar
Christian Fibich committed
70
        if (!defined($existing_settings)) {
71
72
73
74
75
76
77
78
79
            $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.
Christian Fibich's avatar
Christian Fibich committed
80
81
82
    if (defined($fiji_ini_file)) {
        $fiji_settings_ref = read_settingsfile($phase, $fiji_ini_file, $fiji_settings_ref);
        if (!ref($fiji_settings_ref)) {
83
84
            return $fiji_settings_ref;    # actually an error message
        }
85
        $fiji_settings_ref->{'filename'} = $fiji_ini_file;
86
87
    } else {
        $fiji_settings_ref->{'design'} = {};
Christian Fibich's avatar
Christian Fibich committed
88
        _set_defaults(DESIGNMAP, $fiji_settings_ref->{'design'}, $phase);
89
        $fiji_settings_ref->{'fius'}     = [];
90
        $fiji_settings_ref->{'filename'} = File::Spec->curdir();
91
    }
Stefan Tauner's avatar
Stefan Tauner committed
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'}, 1, "logarithmic");
94
95
96

    return $fiji_settings_ref;
}
Stefan Tauner's avatar
Stefan Tauner committed
97
98

sub _export_value {
99
    my $logger = get_logger("");
Christian Fibich's avatar
Christian Fibich committed
100
    my ($map_ref, $k, $v_ref) = @_;
101

Christian Fibich's avatar
Christian Fibich committed
102
    if (defined($map_ref->{$k}->{'type'})) {
103
        my $orig = ${$v_ref};
104
        if ($map_ref->{$k}->{'type'} eq 'hexadecimal' || $map_ref->{$k}->{'type'} eq 'lfsrpoly') {
Christian Fibich's avatar
Christian Fibich committed
105
            ${$v_ref} = sprintf("0x%x", $orig);
106
107
108

            # } elsif ($map_ref->{$k}->{'type'} eq 'natural') {
            # } elsif ($map_ref->{$k}->{'type'} eq 'boolean') {
Christian Fibich's avatar
Christian Fibich committed
109
            $logger->trace("Converted value of $k (\"$orig\") to \"${$v_ref}\".") if ($orig ne ${$v_ref});
110
111
112
113
        }

        # } elsif (defined($map_ref->{$k}->{'values'})) {
    }
114
115
}

116
## @method public save ($fiji_ini_file)
117
118
# @brief Store contained FIJI Settings to file.
#
Christian Fibich's avatar
Christian Fibich committed
119
# @attention Will happily overwrite existing files!
120
#
121
# @param fiji_ini_file The file name to write the FIJI Settings to.
Stefan Tauner's avatar
Stefan Tauner committed
122
sub save ($) {
123
    my $logger = get_logger("");
Christian Fibich's avatar
Christian Fibich committed
124
    my ($self, $fiji_ini_file) = @_;
125
126
    return "No file name given" if !defined($fiji_ini_file);

Christian Fibich's avatar
Christian Fibich committed
127
    my $fiji_ini = new Config::Simple(syntax => 'ini');
128
129
    my $design_ref;
    my $fiu_cnt = 0;
Christian Fibich's avatar
Christian Fibich committed
130
    foreach my $key (keys %{$self}) {
131
        my $val = $self->{$key};
Christian Fibich's avatar
Christian Fibich committed
132
133
134
        if (ref(\$val) eq "REF") {
            if (ref($val) eq "HASH") {
                if ($key eq "design") {
135
136
137
                    $design_ref = $val;
                    next;
                }
Christian Fibich's avatar
Christian Fibich committed
138
139
140
            } elsif (ref($val) eq "ARRAY") {
                if ($key eq "fius") {
                    foreach my $fiu (@{$val}) {
141
142
                        my $ini_fiu;

Christian Fibich's avatar
Christian Fibich committed
143
                        foreach my $k (keys(%{$fiu})) {
144
                            my $ini_name = FIUMAP->{$k}->{'ini_name'};
Christian Fibich's avatar
Christian Fibich committed
145
                            if (!defined($fiu->{$k})) {
146
147
148
149
150
151
152
153
                                $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
Christian Fibich's avatar
Christian Fibich committed
154
155
                            _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"),);
156
                        }
Christian Fibich's avatar
Christian Fibich committed
157
                        $fiji_ini->set_block("FIU" . $fiu_cnt++, $ini_fiu);
158
159
160
                    }
                    next;
                }
161
            }
162
163
        } elsif ($key eq "filename") {
            next;
164
        }
165
166

        my $err = "Unknown element found in FIJI Settings: key: \"$key\" val: \"$val\"";
167
168
169
170
171
        $logger->error($err);
        return $err;
    }
    $design_ref->{'FIU_NUM'} = $fiu_cnt;
    my $ini_design;
Christian Fibich's avatar
Christian Fibich committed
172
    foreach my $k (keys(%{$design_ref})) {
173
        my $ini_name = DESIGNMAP->{$k}->{'ini_name'};
Christian Fibich's avatar
Christian Fibich committed
174
        if (!defined($design_ref->{$k})) {
175
176
177
178
179
180
            $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};
181

182
        # Convert value to external representation
Christian Fibich's avatar
Christian Fibich committed
183
184
        _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"),);
185
    }
Christian Fibich's avatar
Christian Fibich committed
186
    $fiji_ini->set_block("CONSTS", $ini_design);
187

Christian Fibich's avatar
Christian Fibich committed
188
    if (!defined($fiji_ini->write($fiji_ini_file))) {
189
190
191
192
        my $err = Config::Simple->error();
        $logger->error($err);
        return $err;
    }
193
    $self->{'filename'} = $fiji_ini_file;
194
195
    return undef;
}
196

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

Christian Fibich's avatar
Christian Fibich committed
233
234
    $design_ref = _rename_import(DESIGNMAP, $design_ref);
    if (!defined($design_ref)) {
235
236
237
238
239
240
241
        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;
Christian Fibich's avatar
Christian Fibich committed
242
    if (!defined($existing_settings)) {
243
        $fiji_settings_ref = {};
Christian Fibich's avatar
Christian Fibich committed
244
        bless($fiji_settings_ref, "FIJI::Settings");
Stefan Tauner's avatar
Stefan Tauner committed
245
    } else {
246
247
248
        $fiji_settings_ref = $existing_settings;

        # Clear the hash
Christian Fibich's avatar
Christian Fibich committed
249
        for (keys %$fiji_settings_ref) {
250
251
252
            delete $fiji_settings_ref->{$_};
        }
    }
Christian Fibich's avatar
Christian Fibich committed
253
    if (!blessed($fiji_settings_ref) || !$fiji_settings_ref->isa("FIJI::Settings")) {
254
        my $msg;
Christian Fibich's avatar
Christian Fibich committed
255
        if (!defined($existing_settings)) {
256
257
258
259
260
261
262
263
264
265
266
            $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
Christian Fibich's avatar
Christian Fibich committed
267
268
    $design_ref = _sanitize_design($design_ref, $phase);
    if (!ref($design_ref)) {
269
270
271
272
273
274
275
276
277
278
        $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);
Christian Fibich's avatar
Christian Fibich committed
279
        if (!(%$fiu_ref)) {
280
281
            last;
        }
Christian Fibich's avatar
Christian Fibich committed
282
283
        $fiu_ref = _rename_import(FIUMAP, $fiu_ref);
        if (!defined($design_ref)) {
284
285
286
287
288
289
            my $msg = "FIU constants of $fiu_name do not match the FIJI Settings naming scheme.";
            $logger->error($msg);
            return $msg;
        }

        my $tmp_fiu = {};
Christian Fibich's avatar
Christian Fibich committed
290
        _set_defaults(FIUMAP, $tmp_fiu, $phase);
291
292

        # overwrite default entries
Christian Fibich's avatar
Christian Fibich committed
293
        foreach my $k (keys(%{$fiu_ref})) {
294
295
296
297
            $tmp_fiu->{$k} = $fiu_ref->{$k};
        }
        $fiu_ref = $tmp_fiu;

Christian Fibich's avatar
Christian Fibich committed
298
299
        $fiu_ref = _sanitize_fiu($fiu_ref, $phase);
        if (!ref($fiu_ref)) {
300
301
302
303
304
305
306
307
308
            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.");
    }

Christian Fibich's avatar
Christian Fibich committed
309
    if ($fiu_num == 0) {
310
311
312
313
314
        $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.
Christian Fibich's avatar
Christian Fibich committed
315
    if (defined($design_ref->{'FIU_NUM'}) && $design_ref->{'FIU_NUM'} != $fiu_num) {
316
317
318
319
320
321
322
        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
    }

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

328
## @method public set_fiu_defaults (%$fiu_ref)
329
330
331
# @brief Overwrite existing fields (if defined) with defaults defined in FIJI.pm.
#
sub set_fiu_defaults ($) {
Christian Fibich's avatar
Christian Fibich committed
332
333
    my ($self, $consts_ref) = @_;
    return _set_defaults(FIUMAP, $consts_ref);
Stefan Tauner's avatar
Stefan Tauner committed
334
335
336
337
338
}

## @function _set_defaults (%$map_ref, %$consts_ref)
# @brief Set defaults according to FIJI.pm.
sub _set_defaults {
339
    my $logger = get_logger("");
Christian Fibich's avatar
Christian Fibich committed
340
341
342
    my ($map_ref, $consts_ref, $phase) = @_;
    foreach my $k (keys(%{$map_ref})) {
        if (exists($map_ref->{$k}->{'default'})) {
343

344
            # Set default only if it is not mandatory in the given phase.
Christian Fibich's avatar
Christian Fibich committed
345
            if (defined($phase) && scalar(grep { $_ eq $phase } @{$map_ref->{$k}->{'phases_opt'}}) == 0) {
346
347
348
349
                next;
            }

            # Also, do not overwrite existing values
Christian Fibich's avatar
Christian Fibich committed
350
            if (defined($consts_ref->{$k})) {
351
352
353
                next;
            }
            $consts_ref->{$k} = $map_ref->{$k}->{default};
Christian Fibich's avatar
Christian Fibich committed
354
            $logger->trace(sprintf("Adding default constant: %s (%s) = %s.", $k, $map_ref->{$k}->{'ini_name'}, $map_ref->{$k}->{default}));
355
356
357
        }
    }
}
358

Stefan Tauner's avatar
Stefan Tauner committed
359
sub validate_design_value {
Christian Fibich's avatar
Christian Fibich committed
360
361
    my ($k, $v_ref) = @_;
    return validate_value(DESIGNMAP, $k, $v_ref);
Stefan Tauner's avatar
Stefan Tauner committed
362
363
364
}

sub validate_fiu_value {
Christian Fibich's avatar
Christian Fibich committed
365
366
    my ($k, $v_ref) = @_;
    return validate_value(FIUMAP, $k, $v_ref);
Stefan Tauner's avatar
Stefan Tauner committed
367
368
}

369
370
371
372
373
374
## @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)
375
#   \param dep_ref    scalar reference to a value the proposed value depends on
376
377
378
#   \param old      (optional) previously valid value
#   \param log_func (optional) the (log4perl) log function to use
#                   (defaul is \&Log::Log4perl::Logger::trace)
379
sub validate_value {
380
    my $logger = get_logger("");
381
    my ($map_ref, $k, $v_ref, $dep_ref, $old, $log_func) = @_;
382
    $log_func = \&Log::Log4perl::Logger::trace if !defined($log_func);
Christian Fibich's avatar
Christian Fibich committed
383
    if (defined($map_ref->{$k}->{'type'})) {
384
385
        my $orig = ${$v_ref};

Christian Fibich's avatar
Christian Fibich committed
386
        # @FIXME: check if the constant is depending on another constant, that needs to be enabled.
387
388
389
390
        # my $dependency = @{$map_ref->{$k}->{'depends_on'}};
        # if (defined($dependency) && defined(@{$map_ref->{$dependency}->{'value'}}
        # }

Christian Fibich's avatar
Christian Fibich committed
391
        if ($map_ref->{$k}->{'type'} eq 'net') {
392

Christian Fibich's avatar
Christian Fibich committed
393
            # @TODO: check syntax? there is nothing more to do in here unless we supply allowed nets...
394
            # which is actually possible because the input netlist is stored in the settings as well
395
        } elsif ($map_ref->{$k}->{'type'} eq 'hexadecimal' || $map_ref->{$k}->{'type'} eq 'lfsrpoly' ) {
Christian Fibich's avatar
Christian Fibich committed
396
397
            if ($orig !~ /^(0|(0[xX][[:xdigit:]]+))$/) {
                $log_func->($logger, "$k: $orig does not look like a natural hexadecimal number.");
398
399
400
401
402
                return 0;
            }
            ${$v_ref} = hex($orig);

            # Check for natural value range. Should never trigger due to the regex above.
Christian Fibich's avatar
Christian Fibich committed
403
404
            if (${$v_ref} < 0) {
                $log_func->($logger, "$k: $orig is negative.");
405
406
                return 0;
            }
Christian Fibich's avatar
Christian Fibich committed
407
        } elsif ($map_ref->{$k}->{'type'} eq 'natural') {
408
409

            # Match hexadecimal, binary, octal and decimal numbers (the latter also in scientific notation)
Christian Fibich's avatar
Christian Fibich committed
410
411
            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.");
412
413
414
415
416
                return 0;
            }

            # convert non-decimal (hexadecimal, binary, octal) values to decimal
            ${$v_ref} = oct($orig) if $orig =~ /^0/;
Christian Fibich's avatar
Christian Fibich committed
417
418
            if (!looks_like_number(${$v_ref})) {
                $log_func->($logger, "$k: $orig does not look like a number.");
419
420
                return 0;
            }
Stefan Tauner's avatar
Stefan Tauner committed
421

422
            # Check for natural value range. Should never trigger due to the regex above.
Christian Fibich's avatar
Christian Fibich committed
423
424
            if (${$v_ref} < 0) {
                $log_func->($logger, "$k: $orig is negative.");
425
426
                return 0;
            }
Christian Fibich's avatar
Christian Fibich committed
427
        } elsif ($map_ref->{$k}->{'type'} eq 'boolean') {
428
429

            # convert strings to binary if need be
Christian Fibich's avatar
Christian Fibich committed
430
431
            if (!defined($orig)) {
                $log_func->($logger, "$k: \"undef\" is not a boolean value.");
432
                return 0;
Christian Fibich's avatar
Christian Fibich committed
433
            } elsif (lc($orig) eq 'true') {
434
                $orig = 1;
Christian Fibich's avatar
Christian Fibich committed
435
            } elsif (lc($orig) eq 'false') {
436
437
                $orig = 0;
            }
Christian Fibich's avatar
Christian Fibich committed
438
439
            if (($orig ne '0') && ($orig ne '1')) {
                $log_func->($logger, "$k: \"$orig\" does not look like a boolean value.");
440
441
442
443
                return 0;
            }

            # ensure proper boolean value, i.e. 0 or 1
Christian Fibich's avatar
Christian Fibich committed
444
            ${$v_ref} = (!!$orig) ? 1 : 0;
445
        }
Christian Fibich's avatar
Christian Fibich committed
446
        $logger->trace("Converted value of $k (\"$orig\") to \"${$v_ref}\".") if (defined($orig) && $orig ne ${$v_ref});
447
448
    }

Christian Fibich's avatar
Christian Fibich committed
449
    if (defined($map_ref->{$k}->{'values'})) {
450
        my $values_ref = $map_ref->{$k}->{'values'};
451
452
453
454
455
        my $dep_val;
        if (ref $dep_ref eq 'SCALAR') {
            $dep_val = $dep_ref;
        }

Christian Fibich's avatar
Christian Fibich committed
456
        if (ref($values_ref) eq 'ARRAY') {
457
458

            # Look for given value in allowed values
Christian Fibich's avatar
Christian Fibich committed
459
460
            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'}}));
461
462
                return 0;
            }
Christian Fibich's avatar
Christian Fibich committed
463
        } elsif (ref($values_ref) eq 'CODE') {
464
            if (!$values_ref->(${$v_ref}, $old, $$dep_val)) {
Christian Fibich's avatar
Christian Fibich committed
465
                $log_func->($logger, "$k: ${$v_ref} is not allowed.");
466
467
468
469
470
471
                return 0;
            }
        }
    }
    return 1;
}
Stefan Tauner's avatar
Stefan Tauner committed
472
473
474
475
476
477
478
479
480
481
482

# @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 {
483
    my $logger = get_logger("");
Christian Fibich's avatar
Christian Fibich committed
484
485
    my ($map_ref, $consts_ref) = @_;
    if (ref($consts_ref) ne 'HASH') {
486
487
488
        $logger->error("Parameter is not a reference to a hash (containing design constants).");
        return undef;
    }
489

490
491
    # Iterating over respective hash from FIJI.pm and rename the entries
    # to match our internal naming scheme.
Christian Fibich's avatar
Christian Fibich committed
492
    foreach my $k (keys(%{$map_ref})) {
493
        my $ini_name = $map_ref->{$k}->{'ini_name'};
Christian Fibich's avatar
Christian Fibich committed
494
        if (exists($consts_ref->{$ini_name}) && $ini_name ne $k) {
495
            $consts_ref->{$k} = $consts_ref->{$ini_name};
Christian Fibich's avatar
Christian Fibich committed
496
            $logger->trace(sprintf("Renaming Design setting %s -> %s (=%s).", $ini_name, $k, defined($consts_ref->{$ini_name}) ? $consts_ref->{$ini_name} : "undef"),);
497
498
499
            delete $consts_ref->{$ini_name};
        }
    }
Christian Fibich's avatar
Christian Fibich committed
500
501
502
    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});
503
504
505
506
507
508
            delete $consts_ref->{$entry_key};
        }
    }

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

510
## @function _sanitize_fiu (%$fiu_ref, $phase)
511
512
# @brief Convert and sanity check FIJI Settings.
#
513
514
# \param fiu_ref a reference to a hash containing FIJI Settings for a
#                single FIU.
Christian Fibich's avatar
Christian Fibich committed
515
# \param phase   the current design phase
516
517
518
#
# \returns A new hash with all constants required in the FIU settings
#          in sanitized form, or undef on errors.
519
sub _sanitize_fiu {
520
    my $logger = get_logger("");
Christian Fibich's avatar
Christian Fibich committed
521
522
    my ($fiu_ref, $phase) = @_;
    if (ref($fiu_ref) ne 'HASH') {
523
524
525
526
        my $msg = "Parameter is not a reference to a hash (containing FIU constants).";
        $logger->error($msg);
        return $msg;
    }
527

Christian Fibich's avatar
Christian Fibich committed
528
529
    $fiu_ref = _validate_hashmap(FIUMAP, $fiu_ref, $phase);
    if (!ref($fiu_ref)) {
530
531
532
        $logger->error("Could not validate Design Constants.");
        return $fiu_ref;
    }
Stefan Tauner's avatar
Stefan Tauner committed
533

534
    return $fiu_ref;
535
536
}

537
sub _disabled_via_dependency {
Christian Fibich's avatar
Christian Fibich committed
538
    my ($map_ref, $consts_ref, $k) = @_;
539
540
541
    my $dependency = $map_ref->{$k}->{'depends_on'};
    return defined($dependency) && !$consts_ref->{$dependency};
}
542

Christian Fibich's avatar
Christian Fibich committed
543
sub _validate_hashmap {
544
    my $logger = get_logger("");
Christian Fibich's avatar
Christian Fibich committed
545
546
547
    my ($map_ref, $consts_ref, $phase) = @_;
    my @map_keys = keys(%{$map_ref});
    foreach my $entry_key (keys(%{$consts_ref})) {
548
549
550
        my $forbidden_by = $map_ref->{$entry_key}->{'forbidden_by'};
        my $ini_name     = $map_ref->{$entry_key}->{'ini_name'};

Christian Fibich's avatar
Christian Fibich committed
551
552
        if (!exists($map_ref->{$entry_key})) {
            $logger->debug("Deleting unknown setting %s = %s.", $entry_key, $consts_ref->{$entry_key});
553
554
            next;
        }
Stefan Tauner's avatar
Stefan Tauner committed
555

556
        @map_keys = grep { $_ ne $entry_key } @map_keys;    # mark constant key as done
Christian Fibich's avatar
Christian Fibich committed
557
558
559
560
        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});
561
562
563
564
            $logger->error($msg);
            return $msg;
        }

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

Christian Fibich's avatar
Christian Fibich committed
572
    if (!defined($phase)) {
573

574
575
576
577
578
        # 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;
    }
579

580
581
582
583
584
    # 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 (
Christian Fibich's avatar
Christian Fibich committed
585
586
587
            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
           )
588
589
590
591
592
593
594
        {
            my $msg = "$k is mandatory in phase $phase.";
            $logger->error($msg);
            return $msg;
        }

    }
Stefan Tauner's avatar
Stefan Tauner committed
595

Christian Fibich's avatar
Christian Fibich committed
596
    # @TODO: implement the same mechanism with forbidden_by
597
598
599

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

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

    # check for sane values
Christian Fibich's avatar
Christian Fibich committed
628
629
    $consts_ref = _validate_hashmap(DESIGNMAP, $consts_ref, $phase);
    if (!ref($consts_ref)) {
630
631
632
633
634
        my $msg = "Could not validate Design Constants.";
        $logger->error($msg);
        return "$msg $consts_ref";
    }

Christian Fibich's avatar
Christian Fibich committed
635
    if (($consts_ref->{'FIU_CFG_BITS'} <= 0)) {
636
637
638
639
        my $msg = "FIU_CFG_BITS is <= 0.";
        $logger->error($msg);
        return $msg;
    }
Christian Fibich's avatar
Christian Fibich committed
640
    if (($consts_ref->{'TIMER_WIDTH'} <= 0) || ($consts_ref->{'TIMER_WIDTH'} > 16)) {
641
642
643
644
        my $msg = "TIMER_WIDTH is invalid ($consts_ref->{'TIMER_WIDTH'}).";
        $logger->error($msg);
        return $msg;
    }
Christian Fibich's avatar
Christian Fibich committed
645
    if (defined($consts_ref->{'ID'}) && ($consts_ref->{ID} < 0 || $consts_ref->{ID} > (2**16 - 1))) {
646
647
648
649
        my $msg = "ID is invalid ($consts_ref->{ID}).";
        $logger->error($msg);
        return $msg;
    }
Christian Fibich's avatar
Christian Fibich committed
650
    if (($consts_ref->{'BAUDRATE'} <= 0)) {
651
652
653
654
655
656
        my $msg = "BAUDRATE missing is <= 0.";
        $logger->error($msg);
        return $msg;
    }

    return $consts_ref;
657
658
}

659
660
sub _log2 {
    my $val = shift;
Christian Fibich's avatar
Christian Fibich committed
661
    return ($val > 0) ? (log($val) / log(2)) : 0;
662
663
}

664
665
## @function private _est_resources ($FREQUENCY, $BAUD, $TIMER_WIDTH, $RESET_CYCLES, $LFSR_WIDTH, $FIU_NUM, $algo)
# @brief estimates the number of Registers and LUTs to implement FIJI logic with the given settings
Christian Fibich's avatar
Christian Fibich committed
666
#
667
668
669
670
671
# The function ant its parameters were determined by experiment & fitted by scipy.optimize.curve_fit
#
# @param FREQUENCY      the clock frequency the FIJI logic will run at in Hz
# @param BAUD           the baud rate for DUT <-> FIJI communication
# @param TIMER_WIDTH    the width of the injection timer (durations) in Bytes
Christian Fibich's avatar
Christian Fibich committed
672
# @param RESET_CYCLES   the number of cycles to apply a FIJI-to-DUT reset
673
# @param LFSR_WIDTH     width of the LFSR used for stuck-open emulation
Christian Fibich's avatar
Christian Fibich committed
674
# @param FIU_NUM        the number of FIUs in the configuration
675
676
677
#
# @returns ($registers, $lutsum)

Christian Fibich's avatar
Christian Fibich committed
678
sub _est_resources {
679
    my $logger = get_logger("");
680
    my ($FREQUENCY, $BAUD, $TIMER_WIDTH, $RESET_CYCLES, $LFSR_WIDTH, $FIU_NUM) = @_;
Christian Fibich's avatar
Christian Fibich committed
681

Christian Fibich's avatar
Christian Fibich committed
682
    # @FIXME where do we put these values? they are likely to change if the VHDL
683
684
    # source is changed...

Christian Fibich's avatar
Christian Fibich committed
685
686
    my $registers;
    my $lut6;
687
    my $out_of_range = [];
Christian Fibich's avatar
Christian Fibich committed
688

689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
    if ($FREQUENCY < 1000000 || $FREQUENCY > 500000000) {
        $logger->debug("FREQUENCY $FREQUENCY out of range for correct resource estimation (1000000 - 500000000)");
        push @{$out_of_range}, "FREQUENCY";
    }
    if ($BAUD < 9600         || $BAUD > 3000000) {
        $logger->debug("BAUD $BAUD out of range for correct resource estimation (9600 - 3000000)");
        push @{$out_of_range}, "BAUD";
    }
    if ($TIMER_WIDTH < 1     || $TIMER_WIDTH > 8) {
        $logger->debug("TIMER_WIDTH $TIMER_WIDTH out of range for correct resource estimation (1 - 8)");
        push @{$out_of_range}, "TIMER_WIDTH";
    }
    if ($RESET_CYCLES < 1    || $RESET_CYCLES > 16) {
        $logger->debug("RESET_CYCLES $RESET_CYCLES out of range for correct resource estimation (1 - 16)");
        push @{$out_of_range}, "RESET_CYCLES";
    }
    if ($LFSR_WIDTH < 16     || $LFSR_WIDTH > 64) {
        $logger->debug("LFSR_WIDTH $LFSR_WIDTH out of range for correct resource estimation (16 - 64)");
        push @{$out_of_range}, "RESET_CYCLES";
    }
    if ($FIU_NUM < 1         || $FIU_NUM > 64) {
        $logger->debug("FIU_NUM $FIU_NUM out of range for correct resource estimation (1 - 64)");
        push @{$out_of_range}, "FIU_NUM";
    }
713

714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
    $registers = 8769408455.04;
    $registers += (3.16525787254e-08)*($FREQUENCY) + (-8063276676.79)*(2**(-1.60957863771e-17 * $FREQUENCY));
    $registers += (0.000329011902135)*($BAUD) + (-705457194.107)*(2**(7.38032560255e-13 * $BAUD));
    $registers += (86.8258719199)*($TIMER_WIDTH) + (-424084.540754)*(2**(0.000218517717305 * $TIMER_WIDTH));
    $registers += (5.02154556118)*($RESET_CYCLES) + (51167.4447102)*(2**(-0.000135580209031 * $RESET_CYCLES));
    $registers += (-5.30893252274)*($LFSR_WIDTH) + (-301970.077109)*(2**(-2.99010866996e-05 * $LFSR_WIDTH));
    $registers += (13.746880792)*($FIU_NUM) + (437.305954808)*(2**(-0.00583539664462 * $FIU_NUM));

    $lut6 = -1591399138.51;
    $lut6 += (5.65349933334e-06)*($FREQUENCY) + (-245548261.879)*(2**(3.26830064347e-14 * $FREQUENCY));
    $lut6 += (-0.0602788840594)*($BAUD) + (1868596655.23)*(2**(4.65257746085e-11 * $BAUD));
    $lut6 += (9.04974779295)*($TIMER_WIDTH) + (-123738.805411)*(2**(7.77721022021e-07 * $TIMER_WIDTH));
    $lut6 += (0.156780025973)*($RESET_CYCLES) + (-176988.018256)*(2**(1e-08 * $RESET_CYCLES));
    $lut6 += (-1.88257966999)*($LFSR_WIDTH) + (-31351130.663)*(2**(-8.63995764468e-08 * $LFSR_WIDTH));
    $lut6 += (3.95693187928)*($FIU_NUM) + (2718.76465806)*(2**(-0.000895867386597 * $FIU_NUM));

730
    return ($registers, $lut6, $out_of_range);
Christian Fibich's avatar
Christian Fibich committed
731
732
}

733
734
## @function public estimate_resources ($settings_ref)
# @brief estimates the resources needed to implement the given settings
Christian Fibich's avatar
Christian Fibich committed
735
#
736
737
# @returns a hash with the members 'regs' and 'lut_calc'
#
738
sub estimate_resources {
739
    my $logger         = get_logger("");
740
    my ($settings_ref) = @_;
741
742
743
    my $consts_ref     = $settings_ref->{'design'};
    my $fiu_ref        = $settings_ref->{'fius'};
    my $fiu_num        = @{$fiu_ref};
Christian Fibich's avatar
Christian Fibich committed
744

Christian Fibich's avatar
Christian Fibich committed
745
    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
746

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

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

757
1;