Settings.pm 35.3 KB
Newer Older
Christian Fibich's avatar
Christian Fibich committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#-------------------------------------------------------------------------------
#  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
#
#-------------------------------------------------------------------------------
#  Description:
#
# Contains helper functions to deal with FIJI Settings files.
#-------------------------------------------------------------------------------

Christian Fibich's avatar
Christian Fibich committed
16
17
## @file Settings.pm
# @brief Contains class \ref FIJI::Settings
18

19
## @class FIJI::Settings
20
#
Christian Fibich's avatar
Christian Fibich committed
21
# @brief Contains helper functions to deal with FIJI Settings files.
22
23
package FIJI::Settings;

24
25
26
use strict;
use warnings;

27
use Scalar::Util 'blessed';
28
29
use Log::Log4perl qw(get_logger);
use Scalar::Util "looks_like_number";
30
31
use POSIX qw(ceil);
use List::Util qw(max);
32
use File::Spec;
33

Christian Fibich's avatar
Christian Fibich committed
34
use FIJI::ConfigSorted;
35
36
use FIJI qw(:all);

Christian Fibich's avatar
Christian Fibich committed
37
## @var @base_resources stores the resource count for default config
Christian Fibich's avatar
Christian Fibich committed
38
39
my @base_resources;

40
## @function public new ($phase, $fiji_ini_file, $existing_settings, $nl_ref)
Stefan Tauner's avatar
Stefan Tauner committed
41
42
# Create a new settings instance.
#
43
44
45
46
47
48
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 with values read from file.
#                          Any contained data will be cleared.
# @param nl_ref            A reference to a FIJI::netlist (for validation of nets and drivers only)
# @returns                 A list comprising a) The new settings instance (or undef in the case of an error),
#                          b) a diagnostic string describing the reason why it could not be created (undef if successful),
#                          c) a diagnostic string describing any anomalies of \c $fiji_ini_file that did not hinder creation.
sub new {
52
    my $logger = get_logger("");
53
    my ($class, $phase, $fiji_ini_file, $existing_settings, $nl_ref) = @_;
54
55
56
57

    my $fiji_settings_ref;

    # if there is no existing settings instance yet, create one
Christian Fibich's avatar
Christian Fibich committed
58
    if (!defined($existing_settings)) {
59
        $fiji_settings_ref = {};
Christian Fibich's avatar
Christian Fibich committed
60
        $fiji_settings_ref = bless($fiji_settings_ref, $class);
61
    } else {
62
63
        $fiji_settings_ref = $existing_settings;
    }
Christian Fibich's avatar
Christian Fibich committed
64
    if (!blessed($fiji_settings_ref) || !$fiji_settings_ref->isa("FIJI::Settings")) {
65
        my $msg;
Christian Fibich's avatar
Christian Fibich committed
66
        if (!defined($existing_settings)) {
67
68
69
70
71
            $msg = "Could not create FIJI::Settings instance.";
        } else {
            $msg = "Given settings are not of type FIJI::Settings.";
        }
        $logger->error($msg);
72
        return (undef, $msg);
73
74
    }

75
    my ($errors, $warnings);
76
    # If there is a file given, try to read it. Else just create a default instance.
Christian Fibich's avatar
Christian Fibich committed
77
    if (defined($fiji_ini_file)) {
78
        ($fiji_settings_ref, $errors, $warnings) = read_settingsfile($phase, $fiji_ini_file, $fiji_settings_ref, $nl_ref);
79
        if (!defined($fiji_settings_ref)) {
80
            return (undef, $errors, $warnings);
81
        }
82
        $fiji_settings_ref->{'filename'} = $fiji_ini_file;
83
84
    } else {
        $fiji_settings_ref->{'design'} = {};
Christian Fibich's avatar
Christian Fibich committed
85
        _set_defaults(DESIGNMAP, $fiji_settings_ref->{'design'}, $phase);
86
        $fiji_settings_ref->{'fius'}     = [];
87
        $fiji_settings_ref->{'filename'} = File::Spec->curdir();
88
    }
Stefan Tauner's avatar
Stefan Tauner committed
89

90
    @base_resources = _est_resources(DESIGNMAP->{'FREQUENCY'}->{'default'}, DESIGNMAP->{'BAUDRATE'}->{'default'}, DESIGNMAP->{'TIMER_WIDTH'}->{'default'}, DESIGNMAP->{'RST_DUT_IN_DUR'}->{'default'}, DESIGNMAP->{'LFSR_WIDTH'}->{'default'}, 1, "logarithmic");
91

92
    return ($fiji_settings_ref, $errors, $warnings);
93
}
Stefan Tauner's avatar
Stefan Tauner committed
94
95

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

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

            # } elsif ($map_ref->{$k}->{'type'} eq 'natural') {
            # } elsif ($map_ref->{$k}->{'type'} eq 'boolean') {
Christian Fibich's avatar
Christian Fibich committed
106
            $logger->trace("Converted value of $k (\"$orig\") to \"${$v_ref}\".") if ($orig ne ${$v_ref});
107
108
109
        } elsif ($map_ref->{$k}->{'type'} eq 'net') {
            # Due to an annoying behavior of Config::Simple we have to enclose
            # escaped identifiers with quotes. These are necessary to preserve spaces.
110
111
            # This also helps with concatenations... so simply quote all nets unconditionally.
            ${$v_ref} = "\"$orig\"";
112
113
        }
    }
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 FIJI::ConfigSorted(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))) {
Christian Fibich's avatar
Christian Fibich committed
189
        my $err = FIJI::ConfigSorted->error();
190
191
192
        $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, $existing_settings, $nl_ref)
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
210
# @param existing_settings (optional) A reference to reuse and return with values read from file.
#                          Any contained data will be cleared.
# @param nl_ref A reference to a FIJI::netlist (for validation of nets and drivers only)
211
#
212
213
214
# @returns A list comprising a) a reference to the hash containing the read constants (or undef in the case of an error),
#          b) a diagnostic string describing the reason why it could not be created (undef if successful),
#          c) a diagnostic string describing any anomalies of \c $fiji_ini_file that did not hinder creation.
215
sub read_settingsfile ($$$) {
216
    my $logger = get_logger("");
217
    my ($phase, $fiji_ini_file, $existing_settings, $nl_ref) = @_;
218
    my $fiji_ini;
Christian Fibich's avatar
Christian Fibich committed
219
    eval { $fiji_ini = new FIJI::ConfigSorted($fiji_ini_file) };    # pesky library tries to die on syntax errors
Christian Fibich's avatar
Christian Fibich committed
220
    if (!defined($fiji_ini)) {
Christian Fibich's avatar
Christian Fibich committed
221
        my $submsg = defined($@) ? $@ : FIJI::ConfigSorted->error();
Christian Fibich's avatar
Christian Fibich committed
222
        if (length($submsg) == 0) {
223
224
225
226
            $submsg = "Empty file?";
        }
        my $msg = "Could not read config file \"$fiji_ini_file\": $submsg";
        $logger->error($msg);
227
        return (undef, $msg);
228
229
230
    }

    my $design_ref = $fiji_ini->get_block("CONSTS");
Christian Fibich's avatar
Christian Fibich committed
231
    if (!(%$design_ref)) {
232
233
        my $msg = "Could not fetch CONSTS block from config file \"$fiji_ini_file\"";
        $logger->error($msg);
234
        return (undef, $msg);
235
    }
Christian Fibich's avatar
Christian Fibich committed
236
    _set_defaults(DESIGNMAP, $design_ref, $phase);
237

Christian Fibich's avatar
Christian Fibich committed
238
239
    $design_ref = _rename_import(DESIGNMAP, $design_ref);
    if (!defined($design_ref)) {
240
241
        my $msg = "Design constants do not match the FIJI Settings naming scheme.";
        $logger->error($msg);
242
        return (undef, $msg);
243
244
245
246
    }

    # Create a new instance or reuse the shallow hull of the existing one
    my $fiji_settings_ref;
Christian Fibich's avatar
Christian Fibich committed
247
    if (!defined($existing_settings)) {
248
        $fiji_settings_ref = {};
Christian Fibich's avatar
Christian Fibich committed
249
        bless($fiji_settings_ref, "FIJI::Settings");
Stefan Tauner's avatar
Stefan Tauner committed
250
    } else {
251
252
253
        $fiji_settings_ref = $existing_settings;

        # Clear the hash
Christian Fibich's avatar
Christian Fibich committed
254
        for (keys %$fiji_settings_ref) {
255
256
257
            delete $fiji_settings_ref->{$_};
        }
    }
Christian Fibich's avatar
Christian Fibich committed
258
    if (!blessed($fiji_settings_ref) || !$fiji_settings_ref->isa("FIJI::Settings")) {
259
        my $msg;
Christian Fibich's avatar
Christian Fibich committed
260
        if (!defined($existing_settings)) {
261
262
263
264
265
            $msg = "Could not create FIJI::Settings instance.";
        } else {
            $msg = "Given settings are not of type FIJI::Settings.";
        }
        $logger->error($msg);
266
        return (undef, $msg);
267
268
269
270
271
    }
    $fiji_settings_ref->{'design'} = $design_ref;
    $fiji_settings_ref->{'fius'}   = [];

    # sanitize and validate read design constants
272
273
274

    my ($errors, $warnings);
    ($design_ref, $errors, $warnings) = _sanitize_design($design_ref, $phase, $nl_ref);
Christian Fibich's avatar
Christian Fibich committed
275
    if (!ref($design_ref)) {
276
277
        $logger->error($errors);
        return (undef, $errors, $warnings);
278
279
280
    }

    my $fiu_num = 0;
281
282
    my $error;
    # Loop over all read FIUs
283
    while (1) {
284

285
286
        my $fiu_id = "FIU" . $fiu_num;
        my $fiu_ref  = $fiji_ini->get_block($fiu_id);
Christian Fibich's avatar
Christian Fibich committed
287
        if (!(%$fiu_ref)) {
288
289
            last;
        }
Christian Fibich's avatar
Christian Fibich committed
290
        $fiu_ref = _rename_import(FIUMAP, $fiu_ref);
291
292
        if (!defined($fiu_ref)) {
            my $msg = "FIU constants of $fiu_id do not match the FIJI Settings naming scheme.";
293
            $logger->error($msg);
294
            return (undef, $msg);
295
296
297
        }

        my $tmp_fiu = {};
298
        _set_defaults(FIUMAP, $tmp_fiu, $phase, $design_ref, $fiu_num);
299
300

        # overwrite default entries
Christian Fibich's avatar
Christian Fibich committed
301
        foreach my $k (keys(%{$fiu_ref})) {
302
303
304
305
            $tmp_fiu->{$k} = $fiu_ref->{$k};
        }
        $fiu_ref = $tmp_fiu;

306
307
        my ($fiu_errors, $fiu_warnings);
        ($fiu_ref, $fiu_errors, $fiu_warnings) = _sanitize_fiu($fiu_ref, $phase, $nl_ref);
Christian Fibich's avatar
Christian Fibich committed
308
        if (!ref($fiu_ref)) {
309
310
311
            my $msg = "";
            $msg .= "$errors\n" if defined($errors);
            $msg .= "Fatal problems sanitizing $fiu_id:\n$fiu_errors";
312
            $logger->error($msg);
313
314
315
316
317
318
319
            return (undef, $msg, $warnings);
        }
        my $name_str = $fiu_id;
        $name_str .= " (".$fiu_ref->{'FIU_NAME'} . ")" if (defined($fiu_ref->{'FIU_NAME'}) && $fiu_ref->{'FIU_NAME'} ne "");
        if (defined($fiu_errors)) {
            $errors = "" if !defined($errors);
            $errors .= "$name_str:\n$fiu_errors";
320
        }
321

322
323
324
        if (defined($fiu_warnings)) {
            $warnings = "" if !defined($warnings);
            $warnings .= "$name_str:\n$fiu_warnings";
325
326
        }

327
328
        $fiji_settings_ref->{'fius'}->[$fiu_num] = $fiu_ref;
        $fiu_num++;
329
        $logger->trace("Read in $fiu_id from FIJI Settings file successfully.");
330
331
    }

Christian Fibich's avatar
Christian Fibich committed
332
    if ($fiu_num == 0) {
333
        my $msg = "No FIU blocks in config file. Synthesis would fail.";
334
        $logger->warn($msg);
335
336
337
338
339
340
341
        if ($phase eq 'instrument') {
            $errors = "" if !defined($errors);
            $errors .= "$msg\n";
        } else {
            $warnings = "" if !defined($warnings);
            $warnings .= "$msg\n";
        }
342
343
344
345
    }

    # 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
346
    if (defined($design_ref->{'FIU_NUM'}) && $design_ref->{'FIU_NUM'} != $fiu_num) {
347
348
349
350
        my $msg = FIU_NUM->{'ini_name'} . " does not match the numbers of FIU blocks found (and loaded).";
        $logger->warn($msg);
        $warnings = defined($warnings) ? "$warnings\n" : "";
        $warnings .= "$msg\n";
351
352
353
354
    } else {
        $design_ref->{'FIU_NUM'} = $fiu_num;    # assume the best if FIU_NUM constant is not given
    }

355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
    my $dups = $fiji_settings_ref->determine_duplicate_fiu_nets();
    my @dup_nets = keys(%{$dups});
    if (scalar(@dup_nets) > 0) {
        my $msg = "More than one FIU is attached to the following net(s):\n";
        for my $dup (@dup_nets) {
            $msg .= "$dup is attached to FIUs " . join(", ", @{$dups->{$dup}}) . "\n";
        }
        chomp($msg);
        if ($phase eq 'instrument') {
            $logger->error($msg);
            if (defined($errors)) {
                $errors .= "\n$msg\n";
            } else {
                $errors = "$msg\n";
            }
        } else {
            $logger->warn($msg);
            if (defined($warnings)) {
                $warnings .= "\n$msg\n";
            } else {
                $warnings = "$msg\n";
            }
        }
    }

    $logger->info("Successfully read in design constants and $fiu_num FIU definitions from FIJI Settings file \"$fiji_ini_file\".") if !defined($errors);
    return (!defined($errors) ? $fiji_settings_ref : undef, $errors, $warnings);
}

## @method determine_duplicate_fiu_nets (@$fius_ref)
# @brief Test for multiple instrumentations of a single net
#
# @param fius_ref   arrayref to a list of FIU hashes
# @param netname    (optional) Look only for a specific net.
#
# @returns          a (possible empty) hash keys by nets that are referred to multiple times.
#                   The values are arrays of FIU indices that refer to the key's net.
sub determine_duplicate_fiu_nets {
    my ($self, $target_net) = @_;

    my $fius_ref = $self->{'fius'};
    my $seen = {};
    my $dup_nets = {};
    my $i = 0;
    foreach my $fiu_ref (@{$fius_ref}) {
        my $netname = $fiu_ref->{'FIU_NET_NAME'};
        if (defined($netname) && length($netname) > 0) {
            if (!defined($target_net) || ($target_net eq $netname)) {
                if (defined $seen->{$netname}) {
                    if (!defined($dup_nets->{$netname})) {
                        $dup_nets->{$netname} = [$seen->{$netname}];
                    }
                    push(@{$dup_nets->{$netname}}, $i);
                } else {
                    $seen->{$netname} = $i;
                }
            }
        }
        $i += 1;
    }
    return $dup_nets;
416
417
}

418
## @method public set_fiu_defaults (%$fiu_ref)
419
420
421
# @brief Overwrite existing fields (if defined) with defaults defined in FIJI.pm.
#
sub set_fiu_defaults ($) {
422
423
    my ($self, $consts_ref, $phase, $fiu_num) = @_;
    return _set_defaults(FIUMAP, $consts_ref, $phase, $self->{'design'}, $fiu_num);
Stefan Tauner's avatar
Stefan Tauner committed
424
425
}

426
427
428
429
430
431
432
433
## @method public default_timer_value()
# @brief Return sensible default timer values for these settings
sub default_timer_value {
    my ($self) = @_;
    # default to 10 ms at default frequency
    return (10e-3 * $self->{'design'}->{'FREQUENCY'});
}

Stefan Tauner's avatar
Stefan Tauner committed
434
435
436
## @function _set_defaults (%$map_ref, %$consts_ref)
# @brief Set defaults according to FIJI.pm.
sub _set_defaults {
437
    my $logger = get_logger("");
438
    my ($map_ref, $consts_ref, $phase, $design_ref, $fiu_num) = @_;
Christian Fibich's avatar
Christian Fibich committed
439
440
    foreach my $k (keys(%{$map_ref})) {
        if (exists($map_ref->{$k}->{'default'})) {
441

442
            # Set default only if it is not mandatory in the given phase.
Christian Fibich's avatar
Christian Fibich committed
443
            if (defined($phase) && scalar(grep { $_ eq $phase } @{$map_ref->{$k}->{'phases_opt'}}) == 0) {
444
445
446
447
                next;
            }

            # Also, do not overwrite existing values
Christian Fibich's avatar
Christian Fibich committed
448
            if (defined($consts_ref->{$k})) {
449
450
                next;
            }
451
452
453
454
455
456
            my $default = $map_ref->{$k}->{default};
            if (ref($default) eq 'CODE') {
                $consts_ref->{$k} = $default->($consts_ref, $phase, $design_ref, $fiu_num);
            } else {
                $consts_ref->{$k} = $default;
            }
Christian Fibich's avatar
Christian Fibich committed
457
            $logger->trace(sprintf("Adding default constant: %s (%s) = %s.", $k, $map_ref->{$k}->{'ini_name'}, $map_ref->{$k}->{default}));
458
459
460
        }
    }
}
461

462
## @function validate_value (%$map_ref, $nl_ref, $k, $$v_ref, $dep_ref, $consts_ref, $log_func)
463
464
#   Do validation (and conversation from external->internal representation)
#
465
466
467
468
469
470
471
472
473
474
#   @param map_ref  reference to FIJI Settings mappings
#   @param nl_ref   reference to a FIJI::netlist (for validation of nets, drivers etc. only)
#   @param k        key identifying the respective setting
#   @param v_ref    scalar reference to the proposed value (that may be modified)
#   @param dep_ref  scalar reference to a value the proposed value depends on
#   @param consts_ref (optional) reference to related settings hash
#   @param log_func (optional) the (log4perl) log function to use
#                   (default is \&Log::Log4perl::Logger::trace)
#
#   @returns undef if value is valid, or a string to explain why it is invalid.
475
sub validate_value {
476
    my $logger = get_logger("");
477
    my ($map_ref, $nl_ref, $k, $v_ref, $dep_ref, $consts_ref, $log_func) = @_;
478
    $log_func = \&Log::Log4perl::Logger::trace if !defined($log_func);
479
480
481
482
483

    if (!defined (${$v_ref})) {
        # if a key does not contain a value, e.g.
        # DRIVER_TYPE=
        # set the corresponding value to an empty string instead of undef
484
        $logger->trace("$k is undef, setting to empty string");
485
486
487
        ${$v_ref} = ""
    }

Christian Fibich's avatar
Christian Fibich committed
488
    if (defined($map_ref->{$k}->{'type'})) {
489
490
        my $orig = ${$v_ref};

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

Christian Fibich's avatar
Christian Fibich committed
496
        if ($map_ref->{$k}->{'type'} eq 'net') {
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
            return "Netlist not available" if !defined($nl_ref);

            my $msg = $nl_ref->validate_net($orig);
            if (defined($msg)) {
                return $msg;
            }
        } elsif ($map_ref->{$k}->{'type'} eq 'driver') {
            return "Netlist not available" if !defined($nl_ref);

            my $net_path = $consts_ref->{'FIU_NET_NAME'};
            my $driver_path = $orig;
            my $driver_type = $consts_ref->{'FIU_DRIVER_TYPE'};
            
            my $msg = $nl_ref->validate_driver($net_path, $driver_path, $driver_type);
            if (defined($msg)) {
                return "$msg";
            }
Christian Fibich's avatar
Christian Fibich committed
514
        } elsif ($map_ref->{$k}->{'type'} eq 'hexadecimal' || $map_ref->{$k}->{'type'} eq 'lfsrpoly') {
Christian Fibich's avatar
Christian Fibich committed
515
            if ($orig !~ /^(0|(0[xX][[:xdigit:]]+))$/) {
516
                return "$orig does not look like a natural hexadecimal number";
517
518
519
520
            }
            ${$v_ref} = hex($orig);

            # Check for natural value range. Should never trigger due to the regex above.
Christian Fibich's avatar
Christian Fibich committed
521
            if (${$v_ref} < 0) {
522
                return "$orig is negative";
523
            }
Christian Fibich's avatar
Christian Fibich committed
524
        } elsif ($map_ref->{$k}->{'type'} eq 'natural') {
525
526

            # Match hexadecimal, binary, octal and decimal numbers (the latter also in scientific notation)
Christian Fibich's avatar
Christian Fibich committed
527
            if ($orig !~ /^(0|(0(x[0-9a-fA-F]+|[0-7]+))|(0b[01]+)|([1-9][0-9]*(e(-[0-9])?[0-9]+)?))$/) {
528
                return "$orig does not look like a number";
529
530
531
532
            }

            # convert non-decimal (hexadecimal, binary, octal) values to decimal
            ${$v_ref} = oct($orig) if $orig =~ /^0/;
Christian Fibich's avatar
Christian Fibich committed
533
            if (!looks_like_number(${$v_ref})) {
534
                return "$orig does not look like a number";
535
            }
Stefan Tauner's avatar
Stefan Tauner committed
536

537
            # Check for natural value range. Should never trigger due to the regex above.
Christian Fibich's avatar
Christian Fibich committed
538
            if (${$v_ref} < 0) {
539
                return "$orig is negative";
540
            }
Christian Fibich's avatar
Christian Fibich committed
541
        } elsif ($map_ref->{$k}->{'type'} eq 'boolean') {
542
543

            # convert strings to binary if need be
Christian Fibich's avatar
Christian Fibich committed
544
            if (!defined($orig)) {
545
                return "\"undef\" is not a boolean value";
Christian Fibich's avatar
Christian Fibich committed
546
            } elsif (lc($orig) eq 'true') {
547
                $orig = 1;
Christian Fibich's avatar
Christian Fibich committed
548
            } elsif (lc($orig) eq 'false') {
549
550
                $orig = 0;
            }
Christian Fibich's avatar
Christian Fibich committed
551
            if (($orig ne '0') && ($orig ne '1')) {
552
                return "\"$orig\" does not look like a boolean value";
553
554
555
            }

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

Christian Fibich's avatar
Christian Fibich committed
561
    if (defined($map_ref->{$k}->{'values'})) {
562
        my $values_ref = $map_ref->{$k}->{'values'};
563
        my $dep_val;
564
565
        if (defined($map_ref->{$k}->{'depends_on'})) {
            $dep_val = $consts_ref->{$map_ref->{$k}->{'depends_on'}};
566
567
        }

Christian Fibich's avatar
Christian Fibich committed
568
        if (ref($values_ref) eq 'ARRAY') {
569
570

            # Look for given value in allowed values
Christian Fibich's avatar
Christian Fibich committed
571
            if (scalar(grep { $_ eq ${$v_ref} } @{$values_ref}) == 0) {
572
                return "${$v_ref} is not allowed. Allowed values are: " . join(", ", @{$map_ref->{$k}->{'values'}});
573
            }
Christian Fibich's avatar
Christian Fibich committed
574
        } elsif (ref($values_ref) eq 'CODE') {
575
576
            if (!$values_ref->(${$v_ref}, $consts_ref->{$k}, $dep_val)) {
                return "${$v_ref} is not allowed";
577
578
579
            }
        }
    }
580
    return undef;
581
}
Stefan Tauner's avatar
Stefan Tauner committed
582
583
584
585

# @function _rename_import (%$map_ref, %$consts_ref)
# Rename and convert entries in consts_ref according to map_ref.
#
586
587
588
589
# This function takes a hash of FIJI Settings and converts its keys
# to the respective internal representation. This allows us to use different
# name representations in the external file than within the implementation.
#
Stefan Tauner's avatar
Stefan Tauner committed
590
591
592
#
# \returns $consts_ref, or undef on errors
sub _rename_import {
593
    my $logger = get_logger("");
Christian Fibich's avatar
Christian Fibich committed
594
595
    my ($map_ref, $consts_ref) = @_;
    if (ref($consts_ref) ne 'HASH') {
596
597
598
        $logger->error("Parameter is not a reference to a hash (containing design constants).");
        return undef;
    }
599

600
601
    # Iterating over respective hash from FIJI.pm and rename the entries
    # to match our internal naming scheme.
Christian Fibich's avatar
Christian Fibich committed
602
    foreach my $k (keys(%{$map_ref})) {
603
        my $ini_name = $map_ref->{$k}->{'ini_name'};
Christian Fibich's avatar
Christian Fibich committed
604
        if (exists($consts_ref->{$ini_name}) && $ini_name ne $k) {
605
            $consts_ref->{$k} = $consts_ref->{$ini_name};
606
            $logger->trace(sprintf("Renaming Design setting %s -> %s (=%s).", $ini_name, $k, defined($consts_ref->{$ini_name}) ? $consts_ref->{$ini_name} : "<undef>"),);
607
608
609
            delete $consts_ref->{$ini_name};
        }
    }
Christian Fibich's avatar
Christian Fibich committed
610
611
    foreach my $entry_key (keys(%{$consts_ref})) {
        if (!exists($map_ref->{$entry_key})) {
612
            $logger->warn(sprintf("Deleting unknown setting %s = %s.", $entry_key, defined($consts_ref->{$entry_key}) ? $consts_ref->{$entry_key} : "<undef>"));
613
614
615
616
617
618
            delete $consts_ref->{$entry_key};
        }
    }

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

620
## @function _sanitize_fiu (%$fiu_ref, $phase)
621
622
# @brief Convert and sanity check FIJI Settings.
#
623
624
# \param fiu_ref a reference to a hash containing FIJI Settings for a
#                single FIU.
Christian Fibich's avatar
Christian Fibich committed
625
# \param phase   the current design phase
626
627
628
#
# \returns A new hash with all constants required in the FIU settings
#          in sanitized form, or undef on errors.
629
630
631
# @returns A list comprising a) the given hash with all constants required in the FIU settings in sanitized form (or undef in the case of an error),
#          b) a diagnostic string describing the reason why sanitation failed (undef if successful),
#          c) a diagnostic string describing any anomalies that did not hinder sanitation.
632
sub _sanitize_fiu {
633
    my $logger = get_logger("");
634
    my ($fiu_ref, $phase, $nl_ref) = @_;
Christian Fibich's avatar
Christian Fibich committed
635
    if (ref($fiu_ref) ne 'HASH') {
636
637
        my $msg = "Parameter is not a reference to a hash (containing FIU constants).";
        $logger->error($msg);
638
        return (undef, $msg);
639
    }
640

641
642
    my ($warnings, $errors);
    ($fiu_ref, $errors, $warnings) = _validate_hashmap(FIUMAP, $nl_ref, $fiu_ref, $phase);
Christian Fibich's avatar
Christian Fibich committed
643
    if (!ref($fiu_ref)) {
644
645
        my $msg = "Could not validate FIU Constants. $errors";
        return chomp $msg;
646
    }
Stefan Tauner's avatar
Stefan Tauner committed
647

648
    return ($fiu_ref, $errors, $warnings);
649
650
}

651
sub _disabled_via_dependency {
Christian Fibich's avatar
Christian Fibich committed
652
    my ($map_ref, $consts_ref, $k) = @_;
653
654
655
    my $dependency = $map_ref->{$k}->{'depends_on'};
    return defined($dependency) && !$consts_ref->{$dependency};
}
656

Christian Fibich's avatar
Christian Fibich committed
657
sub _validate_hashmap {
658
    my $logger = get_logger("");
659
    my ($map_ref, $nl_ref, $consts_ref, $phase) = @_;
Christian Fibich's avatar
Christian Fibich committed
660
    my @map_keys = keys(%{$map_ref});
661
662
    my $errors;
    my $warnings;
Christian Fibich's avatar
Christian Fibich committed
663
    foreach my $entry_key (keys(%{$consts_ref})) {
664
665
666
667
        my $forbidden_by = $map_ref->{$entry_key}->{'forbidden_by'};
        my $ini_name     = $map_ref->{$entry_key}->{'ini_name'};

        @map_keys = grep { $_ ne $entry_key } @map_keys;    # mark constant key as done
Christian Fibich's avatar
Christian Fibich committed
668
669
        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'}));
670
671
672
673
674
675
676
677
678
679
680
681
682
683
        } else {
            my $rv = validate_value($map_ref, $nl_ref, $entry_key, \$consts_ref->{$entry_key}, undef, $consts_ref);
            if (defined($rv)) {
                my $msg = sprintf("%s = \"%s\" is invalid (%s).", $entry_key, !defined($consts_ref->{$entry_key}) ? "<undef>" : $consts_ref->{$entry_key}, $rv);
                if (scalar(grep { $_ eq $phase } @{$map_ref->{$entry_key}->{'phases_opt'}}) == 0) {
                    $logger->error($msg);
                    $errors = "" if !defined($errors);
                    $errors .= "$msg\n";
                } else {
                    $logger->warn($msg);
                    $warnings = "" if !defined($warnings);
                    $warnings .= "$msg\n";
                }
            }
684
685
        }

Christian Fibich's avatar
Christian Fibich committed
686
        if (defined($forbidden_by) && $consts_ref->{$entry_key} && $consts_ref->{$forbidden_by}) {
687
688
            my $msg = "$entry_key is forbidden when $forbidden_by is enabled";
            $logger->error($msg);
689
690
            $errors = "" if !defined($errors);
            $errors .= "$msg\n";
691
        }
Stefan Tauner's avatar
Stefan Tauner committed
692
    }
693

Christian Fibich's avatar
Christian Fibich committed
694
    if (!defined($phase)) {
695

696
697
698
        # 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.
699
        return ($consts_ref, $errors, $warnings);
700
    }
701

702
703
704
705
706
    # 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
707
708
709
            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
           )
710
711
712
        {
            my $msg = "$k is mandatory in phase $phase.";
            $logger->error($msg);
713
714
            $errors = "" if !defined($errors);
            $errors .= "$msg\n";
715
716
717
        }

    }
Stefan Tauner's avatar
Stefan Tauner committed
718

Christian Fibich's avatar
Christian Fibich committed
719
    # @TODO: implement the same mechanism with forbidden_by
720

721
    return ($consts_ref, $errors, $warnings);
722
}
Stefan Tauner's avatar
Stefan Tauner committed
723

724
## @function _sanitize_design (%$consts_ref, $phase, $nl_ref)
Stefan Tauner's avatar
Stefan Tauner committed
725
# @brief Sanity check FIJI Design Settings.
726
#
727
728
# The function deals with sanity checks for the values themselves as
# specified by FIJI.pm. Additionally, it checks for the following conditions:
729
730
#
#   - FIU_CFG_BITS: > 0
731
#   - TIMER_WIDTH: > 0, <= 16
732
733
734
#   - ID: > 0, < 2^15-1
#   - BAUDRATE: > 0
#
735
736
737
# @param consts_ref A reference to a hash containing some design settings
# @param phase      The tool flow phase that defines the rules to check against
# @param nl_ref     A reference to a FIJI::netlist (for validation of nets and drivers only)
738
#
739
740
741
# @returns A list comprising a) the given hash ref with all constants required in the design settings in sanitized form (or undef in the case of an error),
#          b) a diagnostic string describing the reason why it could not be created (undef if successful),
#          c) a diagnostic string describing any anomalies that did not hinder sanitation.
Stefan Tauner's avatar
Stefan Tauner committed
742
sub _sanitize_design {
743
    my $logger = get_logger("");
744
    my ($consts_ref, $phase, $nl_ref) = @_;
Christian Fibich's avatar
Christian Fibich committed
745
    if (ref($consts_ref) ne 'HASH') {
746
        return (undef, "Parameter is not a reference to a hash (containing design constants).");
747
748
749
    }

    # check for sane values
750
751
    my ($warnings, $errors);
    ($consts_ref, $errors, $warnings) = _validate_hashmap(DESIGNMAP, $nl_ref, $consts_ref, $phase);
Christian Fibich's avatar
Christian Fibich committed
752
    if (!ref($consts_ref)) {
753
754
        my $msg = "Could not validate Design Constants:\n$errors";
        return (undef, chomp $msg, $warnings);
755
756
    }

Christian Fibich's avatar
Christian Fibich committed
757
    if (($consts_ref->{'FIU_CFG_BITS'} <= 0)) {
758
759
        $errors = "" if !defined($errors);
        $errors .= "FIU_CFG_BITS is <= 0.\n";
760
    }
Christian Fibich's avatar
Christian Fibich committed
761
    if (($consts_ref->{'TIMER_WIDTH'} <= 0) || ($consts_ref->{'TIMER_WIDTH'} > 16)) {
762
763
        $errors = "" if !defined($errors);
        $errors .= "TIMER_WIDTH is invalid ($consts_ref->{'TIMER_WIDTH'}).\n";
764
    }
Christian Fibich's avatar
Christian Fibich committed
765
    if (defined($consts_ref->{'ID'}) && ($consts_ref->{ID} < 0 || $consts_ref->{ID} > (2**16 - 1))) {
766
767
        $errors = "" if !defined($errors);
        $errors .= sprintf("ID is invalid (0x%04X).\n", $consts_ref->{ID});
768
    }
Christian Fibich's avatar
Christian Fibich committed
769
    if (($consts_ref->{'BAUDRATE'} <= 0)) {
770
771
        $errors = "" if !defined($errors);
        $errors .= "BAUDRATE is <= 0.\n";
772
773
    }

774
    return ($consts_ref, $errors, $warnings);
775
776
}

777
778
sub _log2 {
    my $val = shift;
Christian Fibich's avatar
Christian Fibich committed
779
    return ($val > 0) ? (log($val) / log(2)) : 0;
780
781
}

782
## @function private _est_resources ($FREQUENCY, $BAUD, $TIMER_WIDTH, $RST_CYCLES, $LFSR_WIDTH, $FIU_NUM, $algo)
783
# @brief estimates the number of registers and LUTs to implement FIJI logic with the given settings
Christian Fibich's avatar
Christian Fibich committed
784
#
785
786
787
788
789
# 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
790
# @param RST_CYCLES   the number of cycles to apply a FIJI-to-DUT reset
791
# @param LFSR_WIDTH     width of the LFSR used for stuck-open emulation
Christian Fibich's avatar
Christian Fibich committed
792
# @param FIU_NUM        the number of FIUs in the configuration
793
794
795
#
# @returns ($registers, $lutsum)

Christian Fibich's avatar
Christian Fibich committed
796
sub _est_resources {
797
    my $logger = get_logger("");
798
    my ($FREQUENCY, $BAUD, $TIMER_WIDTH, $RST_CYCLES, $LFSR_WIDTH, $FIU_NUM) = @_;
Christian Fibich's avatar
Christian Fibich committed
799

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

Christian Fibich's avatar
Christian Fibich committed
803
804
    my $registers;
    my $lut6;
805
    my $out_of_range = [];
Christian Fibich's avatar
Christian Fibich committed
806

807
808
809
810
    if ($FREQUENCY < 1000000 || $FREQUENCY > 500000000) {
        $logger->debug("FREQUENCY $FREQUENCY out of range for correct resource estimation (1000000 - 500000000)");
        push @{$out_of_range}, "FREQUENCY";
    }
Christian Fibich's avatar
Christian Fibich committed
811
    if ($BAUD < 9600 || $BAUD > 3000000) {
812
813
814
        $logger->debug("BAUD $BAUD out of range for correct resource estimation (9600 - 3000000)");
        push @{$out_of_range}, "BAUD";
    }
Christian Fibich's avatar
Christian Fibich committed
815
    if ($TIMER_WIDTH < 1 || $TIMER_WIDTH > 8) {
816
817
818
        $logger->debug("TIMER_WIDTH $TIMER_WIDTH out of range for correct resource estimation (1 - 8)");
        push @{$out_of_range}, "TIMER_WIDTH";
    }
819
820
821
    if ($RST_CYCLES < 1 || $RST_CYCLES > 16) {
        $logger->debug("RST_CYCLES $RST_CYCLES out of range for correct resource estimation (1 - 16)");
        push @{$out_of_range}, "RST_CYCLES";
822
    }
Christian Fibich's avatar
Christian Fibich committed
823
    if ($LFSR_WIDTH < 16 || $LFSR_WIDTH > 64) {
824
        $logger->debug("LFSR_WIDTH $LFSR_WIDTH out of range for correct resource estimation (16 - 64)");
825
        push @{$out_of_range}, "RST_CYCLES";
826
    }
Christian Fibich's avatar
Christian Fibich committed
827
    if ($FIU_NUM < 1 || $FIU_NUM > 64) {
828
829
830
        $logger->debug("FIU_NUM $FIU_NUM out of range for correct resource estimation (1 - 64)");
        push @{$out_of_range}, "FIU_NUM";
    }
831

832
    $registers = 8769408455.04;
Christian Fibich's avatar
Christian Fibich committed
833
834
835
    $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));
836
    $registers += (5.02154556118) *     ($RST_CYCLES) + (51167.4447102) *  (2**(-0.000135580209031 * $RST_CYCLES));
Christian Fibich's avatar
Christian Fibich committed
837
838
    $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));
839
840

    $lut6 = -1591399138.51;
Christian Fibich's avatar
Christian Fibich committed
841
842
843
    $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));
844
    $lut6 += (0.156780025973) *    ($RST_CYCLES) + (-176988.018256) * (2**(1e-08 * $RST_CYCLES));
Christian Fibich's avatar
Christian Fibich committed
845
846
    $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));
847

848
    return ($registers, $lut6, $out_of_range);
Christian Fibich's avatar
Christian Fibich committed
849
850