fiji_instrument.pl 17 KB
Newer Older
1
2
#!/usr/bin/env perl

Christian Fibich's avatar
Christian Fibich committed
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#-------------------------------------------------------------------------------
#  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:
#
#  FIJI instrument script
#
#-------------------------------------------------------------------------------

19
## @file
Christian Fibich's avatar
Christian Fibich committed
20
21
22
# @brief FIJI instrument script
## @file
# Instruments a given netlist with the given settings
23
24
25
26

use strict;
use warnings;

27
28
use Scalar::Util 'blessed';

29
30
31
use FindBin;
use lib "$FindBin::Bin";

32
33
use File::Basename qw(basename);

34
use Log::Log4perl qw(get_logger);
35
use Clone qw(clone);
36
use Digest::CRC qw(crcccitt);
37

38
39
use FIJI qw(:all);

40
41
use FIJI::Settings;
use FIJI::Netlist;
42

43
use FIJI::VHDL;
44
45
use FIJI::Constraints;

46
47
use FIJI::Utils;

Christian Fibich's avatar
Christian Fibich committed
48
use File::Spec;
49
use File::Path qw(make_path);
50

51
use Getopt::Long qw(:config bundling);
52

Christian Fibich's avatar
Christian Fibich committed
53
54
55
56
57
58
use constant NETLIST_SUFFIX => {
    ALTERA_QUARTUS => 'vqm',
    XILINX_VIVADO  => 'vm',
    OTHER          => 'v',
};

59
my @argv;
Stefan Tauner's avatar
Stefan Tauner committed
60

61
62
63
64
65
66
67
68
sub log_start {
    my $name = basename($0);
    $name =~ s/\.p[lm]//;
    my $logger = get_logger("");
    $logger->debug("=== Starting new execution of $name ===");
    $logger->debug(sprintf("%d argument(s)%s", scalar(@argv), scalar(@argv) > 0 ? ": @argv" : ""));
}

69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# Filter file line by line and stuff result into $ctx->addfile.
# The filtering happens in a child attached to a pipe to avoid buffering it completely.
sub _crc_filtered {
    my ($file, $ctx, $matcher) = @_;
    my $logger = get_logger("");
    my $fh_filtered;
    # This forks another perl and basically connects its output to $fh_filtered.
    my $pid = open($fh_filtered, "-|");
    if (!defined($pid)) {
        $logger->fatal("Could not fork to filter netlist for CRC calculation: $!");
        return 1;
    }

    if ($pid != 0) {
        # Parent
        $ctx->addfile($fh_filtered); # Pass fh of filtering pipe to the CRC algorithm
        waitpid($pid, 0); # Wait till kid is done
        close($fh_filtered);
    } else {
        # Child
        open(my $in_fh, "<", $file);
        if (!defined $in_fh) {
            $logger->error($!);
            close($in_fh);
            exit(1);
        }
        # read from in_fh, filter, and print it to stdout
        while (my $line = <$in_fh>) {
            chomp($line);
            # print STDERR "line: >$line<, len:".length($line).", match? ".$matcher->($line)."\n";
            print "$line\n" or die "filter output failed: $!" if $matcher->($line);
        }
        close($in_fh);
        exit(1);
    }
}

106
sub main {
107
    my $logger = get_logger("");
108
109
110
111
    my ($options) = @_;

    my $rv;
    my %hash;
Stefan Tauner's avatar
Stefan Tauner committed
112
    my $self = bless(\%hash);
113
114
    my $output_dir;

115
    my ($settings_ref, $msg) = FIJI::Settings->new("setup", $options->{'fiji_settings_file'});
116
117
    my $netlist_filename = $options->{'netlist_file'};

Christian Fibich's avatar
Christian Fibich committed
118
    if (!ref($settings_ref)) {
119
        $logger->error($msg);
120
121
122
123
        return 1;
    }
    $self->{'settings'} = $settings_ref;

124
    # Retrieve directory meta data of the settings file
125
    my ($basevolume, $basedirs, $file) = File::Spec->splitpath($self->{'settings'}->{'filename'});
Christian Fibich's avatar
Christian Fibich committed
126

127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
    # Set/Create output directory
    if (defined $options->{'output_dir'}) {
        # Command line parameter has highest precedence
        $output_dir = $options->{'output_dir'};
    } else {
        # If parameter is not given default is to use FIJI::Settings value
        # (which is either given in the settings file or by the default in FIJI.pm)
        $output_dir = File::Spec->rel2abs($self->{'settings'}->{'design'}->{'OUTPUT_DIR'}, File::Spec->catpath($basevolume, $basedirs, ""));
        $logger->info("Using output directory from FIJI settings: \"$output_dir\".");
    }

    if (!(-d $output_dir)) {
        make_path($output_dir, {error => \my $err});

        if (@$err) {
            my $msg = "Could not create output directory: $!";
            $logger->fatal($msg);
            return 1;
        }
    }

148
    # Configure logging
149
    if (defined $self->{'settings'}->{'design'}->{'INST_LOG'}) {
150
151
152
153
154
155
156
157
        my $logfile_path;
        # If an output dir is given put log file relatively to that
        if (defined($options->{'output_dir'})) {
            $logfile_path = $options->{'output_dir'};
        } else {
            # ... else put the log file relatively to the directory used for the settings file
            $logfile_path = File::Spec->catpath($basevolume, $basedirs, "");
        }
158
        $logfile_path = File::Spec->rel2abs($self->{'settings'}->{'design'}->{'INST_LOG'}, $logfile_path);
159

Christian Fibich's avatar
Christian Fibich committed
160
        my $file_appender        = $Log::Log4perl::Logger::APPENDER_BY_NAME{'logfile'};
161
        my $custom_file_appender = Log::Log4perl::Appender->new(
Christian Fibich's avatar
Christian Fibich committed
162
163
164
            "Log::Log4perl::Appender::File",
            name      => "custom_file_log",
            Threshold => "TRACE",
165
            filename  => $logfile_path,
Christian Fibich's avatar
Christian Fibich committed
166
        );
167
168
        my $layout = $file_appender->{'layout'};
        $custom_file_appender->layout(clone($layout));
169
170
171
172
173
174
175
176
177
178
179

        my $ign = new Log::Log4perl::Filter("ignore_all_filter", sub { 0 });
        foreach my $a (values %{Log::Log4perl->appenders()}) {
            if ($a->name eq "logfile") {
                $logger->info("Switching from initial log file to custom log file " . $custom_file_appender->filename);
                unlink($a->filename);
                Log::Log4perl->eradicate_appender($a->name);
                next;
            }
            $a->filter($ign);
        }
180
        $logger->add_appender($custom_file_appender);
181
182
183
184
        log_start();
        foreach my $a (values %{Log::Log4perl->appenders()}) {
            $a->{filter} = undef;
        }
185
186
    }

Christian Fibich's avatar
Christian Fibich committed
187
188
189
190
191
192
193
194
195
196
197
198
199
    my $nl = new FIJI::Netlist();

    if ($nl->read_file($netlist_filename) != 0) {
        my $msg = "Netlist could not be loaded correctly from \"$netlist_filename\".";
        $logger->fatal($msg);
        return 1;
    }

    #
    # Retrieve toplevel module
    #

    my $toplevel_module = $nl->get_toplevel_module();
Christian Fibich's avatar
Christian Fibich committed
200
201

    if (ref($toplevel_module) ne "Verilog::Netlist::Module") {
Christian Fibich's avatar
Christian Fibich committed
202
203
204
205
206
207
208
        $logger->fatal($toplevel_module);
        return 1;
    }

    if (!defined $options->{'file_prefix'}) {
        $options->{'file_prefix'} = $toplevel_module->name;
    }
Christian Fibich's avatar
Christian Fibich committed
209
    my $nsh = NETLIST_SUFFIX;
210
211
    my $netlist_suffix = (defined $nsh->{$settings_ref->{'design'}->{'IMPL_TOOL'}}) ?
                          $nsh->{$settings_ref->{'design'}->{'IMPL_TOOL'}} :
Christian Fibich's avatar
Christian Fibich committed
212
                          $nsh->{'OTHERS'};
Christian Fibich's avatar
Christian Fibich committed
213

Christian Fibich's avatar
Christian Fibich committed
214
    my $export_nl_filename                    = File::Spec->catfile($output_dir,  $options->{'file_prefix'} . "_instrumented.".$netlist_suffix);
Christian Fibich's avatar
Christian Fibich committed
215
216
    my $export_cfg_filename                   = File::Spec->catfile($output_dir,  $options->{'file_prefix'} . "_download.cfg");
    my $export_wrapper_filename               = File::Spec->catfile($output_dir,  $options->{'file_prefix'} . "_wrapper.vhd");
Christian Fibich's avatar
Christian Fibich committed
217
218
    my $export_pkg_filename                   = File::Spec->catfile($output_dir,  $options->{'file_prefix'} . "_config_pkg.vhd");
    my $export_constraints_filename           = File::Spec->catfile($output_dir,  $options->{'file_prefix'} . "_constraints");
Christian Fibich's avatar
Christian Fibich committed
219

220
221
222
    #
    # start FIU instrumentation
    #
223

224
    # FIU part
Christian Fibich's avatar
Christian Fibich committed
225
    for (my $fiu_idx = 0 ; $fiu_idx < @{$settings_ref->{'fius'}} ; $fiu_idx++) {
226

Christian Fibich's avatar
Christian Fibich committed
227
        my $fiu = @{$settings_ref->{'fius'}}[$fiu_idx];
228

229
        my $ret = $nl->instrument_net($fiu->{'FIU_NET_NAME'}, $fiu_idx, $fiu->{'FIU_DRIVER_PATH'}, $fiu->{'FIU_DRIVER_TYPE'});
230
231
232
233
234

        if ($ret) {
            $logger->error($ret);
            return 1;
        }
235
    }
236

237
238
    # Clock net
    $logger->info("Adding CLOCK net");
239
240
241
    my $clk_net_descriptor = $nl->get_netdescriptor_from_path($settings_ref->{'design'}->{'CLOCK_NET'});
    if (ref($clk_net_descriptor) ne 'HASH') {
        $logger->error($clk_net_descriptor);
242
243
244
        return 1;
    }

245
    my $ret = $nl->net_add_function($clk_net_descriptor, FIJI::VHDL->FIJI_PORTTYPE_CLOCK, "clock_from_dut_o");
246
    if ($ret) {
247
        $logger->error($ret);
248
        return 1;
249
    }
250
251

    # Instrument reset from DUT if enabled
252
253
    if ($settings_ref->{'design'}->{'RST_DUT_OUT_EN'} == 1) {
        $logger->info("Adding RST_DUT_OUT net");
254

255
        my $rst_in_net_descriptor = $nl->get_netdescriptor_from_path($settings_ref->{'design'}->{'RST_DUT_OUT_NAME'});
256
257
        if (ref($rst_in_net_descriptor) ne 'HASH') {
            $logger->error($rst_in_net_descriptor);
258
259
260
            return 1;
        }

261
        my $ret = $nl->net_add_function($rst_in_net_descriptor, FIJI::VHDL->FIJI_PORTTYPE_RST_FROM_DUT, "reset_from_dut_o");
262
263
264
265
        if ($ret) {
            $logger->error($ret);
            return 1;
        }
266
267
    }

268
    # Instrument reset to DUT if enabled
269
270
    if ($settings_ref->{'design'}->{'RST_DUT_IN_EN'} == 1) {
        $logger->info("Adding RST_DUT_IN net");
271

272
        my $port = $toplevel_module->find_port($settings_ref->{'design'}->{'RST_DUT_IN_NAME'});
273

Christian Fibich's avatar
Christian Fibich committed
274
        if (!defined $port) {
275
            $msg = "Could not find port '" . $toplevel_module->name . "|" . $settings_ref->{'design'}->{'RST_DUT_IN_NAME'} . "'\n";
276
277
278
279
            $logger->error($msg);
            return 1;
        }

280
        $port->userdata(FIJI::VHDL->FIJI_USERDATA_PORTTYPE, FIJI::VHDL->FIJI_PORTTYPE_RST_TO_DUT);
Christian Fibich's avatar
Christian Fibich committed
281
        $toplevel_module->link;
282
    }
283

284
    # Trigger from DUT if enabled
285

286
287
288
    if ($settings_ref->{'design'}->{'TRIG_DUT_EN'} == 1) {
        $logger->info("Adding TRIG_DUT net");
        my $rst_out_net_descriptor = $nl->get_netdescriptor_from_path($settings_ref->{'design'}->{'TRIG_DUT_NAME'});
289
290
        if (ref($rst_out_net_descriptor) ne 'HASH') {
            $logger->error($rst_out_net_descriptor);
291
292
293
            return 1;
        }

294
        my $ret = $nl->net_add_function($rst_out_net_descriptor, FIJI::VHDL->FIJI_PORTTYPE_TRIG_FROM_DUT, "trigger_from_dut_o");
295
296
297
298
        if ($ret) {
            $logger->error($ret);
            return 1;
        }
299
300
    }

301
    # instrument fault detect signal 1 if enabled
302
303
    if ($settings_ref->{'design'}->{'FD_1_EN'} == 1) {
        $logger->info("Adding FD_1 net");
304

305
        my $fd1_net_descriptor = $nl->get_netdescriptor_from_path($settings_ref->{'design'}->{'FD_1_NAME'});
306
307
        if (ref($fd1_net_descriptor) ne 'HASH') {
            $logger->error($fd1_net_descriptor);
308
309
310
            return 1;
        }

311
        my $ret = $nl->net_add_function($fd1_net_descriptor, FIJI::VHDL->FIJI_PORTTYPE_FAULT_DETECTION, "fault_detect_1_o", 0);
312
313
314
315
        if ($ret) {
            $logger->error($ret);
            return 1;
        }
316
317
    }

318
    # instrument fault detect signal 2 if enabled
319
320
    if ($settings_ref->{'design'}->{'FD_2_EN'} == 1) {
        $logger->info("Adding FD_2 net");
321

322
        my $fd2_net_descriptor = $nl->get_netdescriptor_from_path($settings_ref->{'design'}->{'FD_2_NAME'});
323
324
        if (ref($fd2_net_descriptor) ne 'HASH') {
            $logger->error($fd2_net_descriptor);
325
326
            return 1;
        }
327

328
        my $ret = $nl->net_add_function($fd2_net_descriptor, FIJI::VHDL->FIJI_PORTTYPE_FAULT_DETECTION, "fault_detect_2_o", 1);
329
330
331
332
333
        if ($ret) {
            $logger->error($ret);
            return 1;
        }
    }
334

335
336
    # calculate design ID
    # Design ID = CRC-CCITT of all input data:
337
338
    # - input netlist (file content excluding full-line comments)
    # - input settings file (file content excluding full-line comments)
339
340
341
    # - toplevel module name
    # - file prefix

342
    my $ctx = Digest::CRC->new(type => "crcccitt");
343

344
345
    # First process the netlist file:
    return 1 if _crc_filtered($netlist_filename, $ctx, sub {$_[0] !~ m{^\s*//}}) != 0;
346

347
348
    # Then the cfg file:
    return 1 if _crc_filtered($options->{'fiji_settings_file'}, $ctx, sub {$_[0] !~ /^\s*;/}) != 0;
349

350
    # Finally, the easy parts:
Christian Fibich's avatar
Christian Fibich committed
351
    $ctx->add($toplevel_module->name);
Christian Fibich's avatar
Christian Fibich committed
352
    $ctx->add($options->{'file_prefix'});
353

354
355
    my $id = hex $ctx->hexdigest;

Christian Fibich's avatar
Christian Fibich committed
356
    $logger->info(sprintf("Design ID chosen to be 0x%04X.", $id));
357
358
    #
    # Generate download cfg file
Christian Fibich's avatar
Christian Fibich committed
359
    # @FIXME should we overwrite the original file instead?
360
361
362
363
364
365
366
367
    #
    $settings_ref->{'design'}->{'ID'} = $id;
    $settings_ref->save($export_cfg_filename);

    #
    # output netlist
    #

Christian Fibich's avatar
Christian Fibich committed
368
    $rv = $nl->export($export_nl_filename, $id);
369

Christian Fibich's avatar
Christian Fibich committed
370
    if (defined $rv) {
371
372
        $logger->error($rv);
        return 1;
373
374
    }

375
376
377
378
379
    #
    # Make wrapper
    #
    my $wrapper_config = {};
    $wrapper_config->{'netlist'}                  = $nl->{'nl'};
Christian Fibich's avatar
Christian Fibich committed
380
    $wrapper_config->{'dut_toplevel_module_name'} = $toplevel_module->name;
381
382
383
384
385
    $wrapper_config->{'fiji_settings_filename'}   = $export_cfg_filename;
    $wrapper_config->{'vhdl_filename'}            = $export_wrapper_filename;

    $rv = FIJI::VHDL->generate_wrapper_module($wrapper_config);

Christian Fibich's avatar
Christian Fibich committed
386
    if (defined $rv) {
387
388
        $logger->error($rv);
        return 1;
389
    }
390

391
392
393
    #
    # Generate cfg vhd
    #
Christian Fibich's avatar
Christian Fibich committed
394
    $rv = FIJI::VHDL->generate_config_package($export_cfg_filename, $export_pkg_filename);
395

Christian Fibich's avatar
Christian Fibich committed
396
    if (defined $rv) {
397
398
        $logger->error($rv);
        return 1;
399
400
    }

Christian Fibich's avatar
Christian Fibich committed
401
402
403
404
405
406
    my $dut_inst_name  = (defined $settings_ref->{'design'}->{'DUT_INST_NAME'})     ? $settings_ref->{'design'}->{'DUT_INST_NAME'}     : FIJI::VHDL->FIJI_DEFAULTS->{'DUT_INST_NAME'};
    my $fiji_inst_name = (defined $settings_ref->{'design'}->{'FIJI_INST_NAME'})    ? $settings_ref->{'design'}->{'FIJI_INST_NAME'}    : FIJI::VHDL->FIJI_DEFAULTS->{'FIJI_INST_NAME'};
    my $wrapper_name   = (defined $settings_ref->{'design'}->{'FIJI_WRAPPER_NAME'}) ? $settings_ref->{'design'}->{'FIJI_WRAPPER_NAME'} : FIJI::VHDL->FIJI_DEFAULTS->{'FIJI_WRAPPER_NAME'};
    my $gen_fault_injection_label   = FIJI::VHDL->FIJI_DEFAULTS->{'FIJI_FAULT_INJECTION_GEN_LABEL'};


407
408
409
    #
    # Generate placement constraints
    #
Christian Fibich's avatar
Christian Fibich committed
410
    $rv = FIJI::Constraints->generate_constraints(
411
        $settings_ref->{'design'}->{'SYNTHESIS_TOOL'},
412
        $settings_ref->{'design'}->{'IMPL_TOOL'}, {
413
            dut_module  => $wrapper_config->{'dut_toplevel_module_name'},
Christian Fibich's avatar
Christian Fibich committed
414
415
416
417
            fiji_module => $wrapper_name,
            dut_inst    => $dut_inst_name,
            fiji_inst   => $fiji_inst_name,
            fiji_gen    => $gen_fault_injection_label,
418
            mode        => $settings_ref->{'design'}->{'OPTIMIZATIONS'},
419
        },
Christian Fibich's avatar
Christian Fibich committed
420
        $export_constraints_filename
421
422
    );

Christian Fibich's avatar
Christian Fibich committed
423
424
    # FIXME add constraint export to other tools

Christian Fibich's avatar
Christian Fibich committed
425
    if (defined $rv) {
426
427
        $logger->error($rv);
        return 1;
428
429
    }

430
    $logger->debug("=== Stopping execution ===");
431
432
    return 0;
}
433

Christian Fibich's avatar
Christian Fibich committed
434
sub usage {
Christian Fibich's avatar
Christian Fibich committed
435
    my ($err) = @_;
Stefan Tauner's avatar
Stefan Tauner committed
436
    my $scr = basename($0);
437
    my $msg = <<USAGE;
Stefan Tauner's avatar
Stefan Tauner committed
438
439
440

$scr is the FIJI Instrumentation tool

Christian Fibich's avatar
Christian Fibich committed
441
442
443
444
Usage: $0 [PARAMETERS]

Required parameters:

Stefan Tauner's avatar
Stefan Tauner committed
445
    -s, --settings=<filename>   FIJI Settings file
Christian Fibich's avatar
Christian Fibich committed
446

Stefan Tauner's avatar
Stefan Tauner committed
447
    -n, --netlist=<filename>    Netlist file to instrument
Christian Fibich's avatar
Christian Fibich committed
448
449
450

Optional parameters:

Stefan Tauner's avatar
Stefan Tauner committed
451
452
453
454
455
456
457
458
459
460
461
462
463
464
    -p, --file-prefix=<prefix>  Prefix for all generated files
                                Otherwise: toplevel module name
                                
    -o, --output-dir=<path>     The directory where the generated files are put.
                                The directory will be created if necessary.
                                Overrules the directory specified in
                                the FIJI settings file.
                                Otherwise: current working directory
                                
    -v, --verbose               Increase verbosity of output
                                (can be given multiple times to increase
                                 the verbosity level once per occurrence).

    --help                      Display this help and exit
Christian Fibich's avatar
Christian Fibich committed
465
466
USAGE

Christian Fibich's avatar
Christian Fibich committed
467
    if ($err == 0) {
Christian Fibich's avatar
Christian Fibich committed
468
469
470
471
        print STDOUT $msg;
    } else {
        print STDERR $msg;
    }
Stefan Tauner's avatar
Stefan Tauner committed
472
    return $err;
Christian Fibich's avatar
Christian Fibich committed
473
474
}

475
476
my $log_conf = File::Spec->catfile($FindBin::Bin, 'logger.conf');
Log::Log4perl::init_and_watch($log_conf, 'HUP');
477
Log::Log4perl->eradicate_appender("string"); # No need for buffered log output
478

479
my $logger = get_logger("");
480
my $break  = undef;
481
482
483
484

# ARGV gets modified by GetOptions below but we need it later in log_start (again)
@argv=@ARGV;
log_start();
485

Christian Fibich's avatar
Christian Fibich committed
486
# Defaults
487
488
489
490
my $options = {
    fiji_settings_file => undef,
    netlist_file       => undef,
    file_prefix        => undef,
Stefan Tauner's avatar
Stefan Tauner committed
491
    verbosity_delta    => 0,
492
493
    output_dir         => undef
};
494

495
GetOptions(
Stefan Tauner's avatar
Stefan Tauner committed
496
497
498
499
    "s|settings=s"     => \$options->{'fiji_settings_file'},
    "n|netlist=s"      => \$options->{'netlist_file'},
    "p|file-prefix=s"  => \$options->{'file_prefix'},
    "o|output-dir=s"   => \$options->{'output_dir'},
Stefan Tauner's avatar
Stefan Tauner committed
500
    "v|verbose+"       => \$options->{'verbosity_delta'},
Stefan Tauner's avatar
Stefan Tauner committed
501
    "help|h"           => sub { exit(usage(0)); },
502
);
503

Christian Fibich's avatar
Christian Fibich committed
504
# Check if all required options present
Christian Fibich's avatar
Christian Fibich committed
505
506
for my $k (keys(%{$options})) {
    if (!defined $options->{$k}) {
Christian Fibich's avatar
Christian Fibich committed
507
        if ($k eq 'output_dir' || $k eq 'file_prefix') {
508
509
510
511
512
513
514
        } else {
            $break = 1;
            $logger->error("Option $k is required.");
        }
    }
}

Stefan Tauner's avatar
Stefan Tauner committed
515
exit usage(1) if (defined $break);
Christian Fibich's avatar
Christian Fibich committed
516
517

# clean up the passed paths
Christian Fibich's avatar
Christian Fibich committed
518
519
520
521
# @FIXME needed ?
$options->{'fiji_settings_file'} = File::Spec->canonpath($options->{'fiji_settings_file'});
$options->{'netlist_file'}       = File::Spec->canonpath($options->{'netlist_file'});
$options->{'output_dir'}         = File::Spec->canonpath($options->{'output_dir'}) if defined $options->{'output_dir'};
522

Stefan Tauner's avatar
Stefan Tauner committed
523
FIJI::Utils::increase_verbosity($options->{'verbosity_delta'});
524

Stefan Tauner's avatar
Stefan Tauner committed
525
exit main($options);