Netlist.pm 54.6 KB
Newer Older
1
2
#** @file Netlist.pm
# @verbatim
Christian Fibich's avatar
Christian Fibich committed
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#-------------------------------------------------------------------------------
#  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:              Netlist.pm
#  Created on:        29.04.2015
#  $LastChangedBy$
#  $LastChangedDate$
#
#  Description:
#
20
#  FIJI Netlist class: Functions to instrument & export a Verilog::Netlist
Christian Fibich's avatar
Christian Fibich committed
21
#-------------------------------------------------------------------------------
22
23
# @endverbatim
#*
Christian Fibich's avatar
Christian Fibich committed
24

Christian Fibich's avatar
Christian Fibich committed
25
26
27
28
29
## @file Netlist.pm
# @brief Contains class \ref FIJI::Netlist

## @class FIJI::Netlist
# @brief Functions to instrument & export a Verilog::Netlist
30
31
32
33
package FIJI::Netlist;

use strict;
use warnings;
34
use diagnostics;
35

36
use Scalar::Util 'blessed';
37
use List::Util qw[min max];
38
use Log::Log4perl qw(get_logger :easy);
39
use File::Basename qw(basename);
40

41
42
use Verilog::Netlist 99.415;
use Verilog::Language 99.415;
43
44
45
46
use Data::Dumper;

use FIJI::VHDL;

47
use constant HIERSEP               => "/";
48
use constant FIJI_NAMESPACE_PREFIX      => "fiji_";
Christian Fibich's avatar
Christian Fibich committed
49
50
use constant FIJI_PORT_IN_POSTFIX  => "_inj_i";
use constant FIJI_PORT_OUT_POSTFIX => "_ori_o";
51
use constant MAX_UNIQUE_TRIES => 10;
52

Christian Fibich's avatar
Christian Fibich committed
53
use constant FIJI_LOGO => <<logo_end;
54
55
56
57
58
59
//  FIJIFIJIFIJIFIJIFIJIFIJIFIJIFIJIFIJIFIJI
//  FIJIFIJIFIJIFIJIF   FIJIFIJIFIJIFIJIFIJI
//  FIJIFIJIFIJIFIJ    IFIJIFIJIFIJIFIJIFIJI
//  FIJIF       FI           IJIFIJIFIJIFIJI
//  FIJIFIJIFI            JIFIJIFIJIFIJIFIJI
//  FIJIFIJI            FIJIFIJIFIJIFIJIFIJI
60
61
//  FIJIFI    JI   IFI   IJIFIJIFIJIFIJIFIJI
//  FIJIF   FIJ   JIFIJ   JIFIJIFIJIFIJIFIJI
62
63
64
65
66
67
68
69
70
71
//  FIJIFIJIFI   IJIFIJI   IFIJIFIJIFIJIFIJI
//  FIJIFIJIF    IJIFIJIFIJIFIJIFIJIFIJIFIJI
//  FIJIFIJI    FIJIFIJIFIJIFIJIFIJIFIJIFIJI
//  FIJIFIJ     FIJIF           FIJIFIJIFIJI
//  FIJIFI     IFI      Fault      IFIJIFIJI
//  FIJIF             InJection     FIJIFIJI
//  FIJ              Instrumenter    IJIFIJI
//  F                                    IJI
//  FIJIFIJIFIJIFIJIFIJIFIJIFIJIFIJIFIJIFIJI
logo_end
72

73
74
## @function public new ()
# @brief creates a new FIJI::Netlist object
Christian Fibich's avatar
Christian Fibich committed
75
#
76
77
# @returns the newly created FIJI::Netlist object
sub new {
Christian Fibich's avatar
Christian Fibich committed
78

79
    my ($class) = @_;
Christian Fibich's avatar
Christian Fibich committed
80

81
82
    my $self = {};
    bless $self, $class;
Christian Fibich's avatar
Christian Fibich committed
83

84
    $self->{'nl'} = new Verilog::Netlist(
Christian Fibich's avatar
Christian Fibich committed
85

86
87
88
89
90
91
        # options => $opt,
        # keep_comments => 1, # include comments in netlist
        link_read_nonfatal => 1,    # do not fail if module description not found
        use_vars           => 1,
    );
    return $self;
92
93
}

94
95
96
97
98
99
100
101
## @method public read_file ($filename)
# @brief Tries to read a Verilog netlist from the given file
#
# @param filename    The Verilog file to read
#
# @returns 1   if an error occurred
# @returns 0   if successful
sub read_file {
102
    my $logger = get_logger("");
Christian Fibich's avatar
Christian Fibich committed
103
    my ($self, $filename) = @_;
104

105
106
    ## Netlist synthesized from VHDL could contain SV keywords at this point.
    Verilog::Language::language_standard("1364-2001");
107

108
    $logger->info("Reading in netlist from file \"$filename\".");
109
    eval {
Christian Fibich's avatar
Christian Fibich committed
110
111
        $self->{'nl'}->read_file(filename => $filename);    # read Verilog file
        $self->{'nl'}->link();                              # Read in any sub-modules
112
    };
113

Christian Fibich's avatar
Christian Fibich committed
114
115
    if ($self->{'nl'}->errors() != 0 || $@) {
        $logger->error("Could not parse $filename!", $@ ? "\n" . $@ : "");
116
117
        return 1;
    }
118

119
    $self->{'filename'} = $filename;
120

121
122
    $logger->info("Successfully read in netlist from file \"$filename\".");
    return 0;
123
124
}

125
126
127
128
129
## @method public get_toplevel_port_names ()
# @brief retrieves the port names of all toplevel modules
#
# @returns an array of Verilog::Port references
sub get_toplevel_port_names {
Christian Fibich's avatar
Christian Fibich committed
130
    my ($self, $dir) = @_;
131
    my $ports_ref = [];
Christian Fibich's avatar
Christian Fibich committed
132
    foreach my $mod ($self->{'nl'}->top_modules_sorted) {
133
        foreach my $port ($mod->ports) {
Christian Fibich's avatar
Christian Fibich committed
134
135
136
137
            if (   !defined($dir)
                || ($dir eq "o" && $port->direction eq "out")
                || ($dir eq "i" && $port->direction eq "in"))
            {
Christian Fibich's avatar
Christian Fibich committed
138
139
                push @{$ports_ref}, $port->name;
            }
140
141
142
        }
    }
    return $ports_ref;
143
144
}

Christian Fibich's avatar
Christian Fibich committed
145
146
147
148
149
150
## @method public get_toplevel_module ()
# @brief retrieves the port names of all toplevel modules
#
# @returns a Verilog::Module reference
sub get_toplevel_module {
    my ($self) = @_;
Christian Fibich's avatar
Christian Fibich committed
151
152
153
154
155
    my @m      = $self->{'nl'}->top_modules_sorted;
    my $n      = @m;
    return $m[0] if ($n == 1);
    return "More than one toplevel module present in netlist." if ($n > 1);
    return "No toplevel module found.";
Christian Fibich's avatar
Christian Fibich committed
156
157
}

158
159
160
161
## @method public get_nets ()
# @brief retrieves all nets in the netlist
#
# @returns an array of hashes for all nets containing:
162
163
164
# 'name' the name of the net
# 'path' the hierarchical path of the net
# 'net'  the Verilog::Netlist::Net reference to the net
165
sub get_nets {
166
167
168
169
170
    my ($self) = @_;

    # my $nets_ref = {'metadata' => [], 'names' => [], 'nets' => []};
    my $nets_ref = [];
    my $hier     = "";
171
172
173
    my $top      = $self->get_toplevel_module();
    if ($top->isa("Verilog::Netlist::Module")) {
        $self->_get_subnets($nets_ref, $top, $hier);
174
175
    }
    return $nets_ref;
176
177
}

178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
sub _extract_low_high {
    my ($in_low, $in_high) = @_;
    my ($out_low, $out_high);

    # msb might be lower than lsb if index range is "upto" -> swap
    if ($in_high < $in_low) {
        $out_low = $in_high;
        $out_high = $in_low;
    } else {
        $out_low = $in_low;
        $out_high = $in_high;
    }
    return ($out_low, $out_high);
}

193
194
195
196
197
198
199
200
201
#** @method private _get_subnets ($nets_ref,$mod,$hier)
# @brief retrieves all nets in the given module
#
# @param nets_ref    the central reference to push found nets (name,path,netref) to
# @param mod         the module to search
# @param hier        a string representing the current hierarchy level, separated
#                    be HIERSEP
sub _get_subnets {

202
    my $logger = get_logger("");
203
    my ($self, $nets_ref, $mod, $hier, $instname) = @_;
204

205
    my $thishier = $hier;
Christian Fibich's avatar
Christian Fibich committed
206
    $thishier .= HIERSEP if $thishier ne "";
207
208
209
210
211
    if (defined $instname) {
        $thishier .= $instname;
    } else {
        $thishier .= $mod->name;
    }
212

Christian Fibich's avatar
Christian Fibich committed
213
    foreach my $n ($mod->nets) {
214
        if (defined($n->msb) && defined($n->lsb)) {
215
            my ($low, $high) = _extract_low_high($n->lsb, $n->msb);
216
            for (my $sub = $low ; $sub <= $high ; $sub++) {
Christian Fibich's avatar
Christian Fibich committed
217
                my $thisnet_ref = {name => $n->name . "[$sub]", path => $thishier, net => $n, index => $sub};
218
219
220
221
222
223
224
                push(@{$nets_ref}, $thisnet_ref);
            }
        } else {
            my $thisnet_ref = {name => $n->name, path => $thishier, net => $n};
            push(@{$nets_ref}, $thisnet_ref);
        }

225
    }
226

227
    foreach my $cell ($mod->cells) {
Christian Fibich's avatar
Christian Fibich committed
228
        if (defined($cell->submod)) {
229
            $self->_get_subnets($nets_ref, $cell->submod, $thishier, $cell->name);
230
        }
231
232
233
    }
}

234
235
236
## @function private _check_name_in_hierarchy ($startmod,$name)
# @brief checks if a given name exists
# checks if the $name exists as port, net, or cell name in the instantiation tree.
237
#
238
239
# @param startmod    the module to start with
# @param name        the name to check against
240
sub _check_name_in_hierarchy {
241

242
    my $logger = get_logger("");
Christian Fibich's avatar
Christian Fibich committed
243
    my ($startmod, $name) = @_;
244
245
    my $nl = $startmod->netlist;

246
    $logger->debug("Checking \"" . $startmod->name . "\" for name \"$name\"");
247
248

    # check if a net is named the same
249
    for my $net ($startmod->nets) {
Christian Fibich's avatar
Christian Fibich committed
250
        if ($net->name eq $name) {
251
            my $msg = "Name \"$name\" does already exist as net in " . $startmod->name;
252
            return $msg;
253
254
255
256
        }
    }

    # check if a port is named the same
257
    for my $port ($startmod->ports) {
Christian Fibich's avatar
Christian Fibich committed
258
        if ($port->name eq $name) {
259
            my $msg = "Name \"$name\" does already exist as port in " . $startmod->name;
260
            return $msg;
261
262
263
        }
    }

264
    for my $cell ($startmod->cells) {
Christian Fibich's avatar
Christian Fibich committed
265
        if ($cell->name eq $name) {
266
            my $msg = "Name \"$name\" does already exist as cell in " . $startmod->name;
267
            return $msg;
268
269
270
271
        }
    }

    # find any module instantiating the current start module
272
273
    foreach my $mod ($nl->modules) {
        foreach my $cell ($mod->cells) {
Christian Fibich's avatar
Christian Fibich committed
274
275
            if (defined $cell->submod && $cell->submod == $startmod) {
                my $msg = _check_name_in_hierarchy($mod, $name);
276
                return $msg if defined $msg;
277
278
279
            }
        }
    }
280

281
    return undef;
282
283
}

284
285
## @function private _add_port_to_hierarchy ($startmod,$name,$function,$index,$indent)
# @brief adds a port to all modules starting from a leaf node
286
#
287
288
289
290
291
292
293
294
# @param startmod    the module to start with
# @param name        the port name to be generated
# @param function    the function of this port in FIJI (FIJI::VHDL->FIJI_PORTTYPE_xxx)
# @param index       for ORIGINAL,MODIFIED and FAULT_DETECT: the index of this net
# @param indent      optional, needed just for formatting logging output
#
# @returns undef if the given port $name is already found
# @returns Verilog::Port reference to the new port if successful
Christian Fibich's avatar
Christian Fibich committed
295
sub _add_port_to_hierarchy {
296

297
    my $logger = get_logger("");
Christian Fibich's avatar
Christian Fibich committed
298
    my ($startmod, $name, $function, $index, $indent) = @_;
299
    my $nl        = $startmod->netlist;
300
    my $direction = "undef";
Christian Fibich's avatar
Christian Fibich committed
301
    if (!defined $indent) {
302
303
304
305
306
        $indent = "";
    } else {
        $indent .= "  ";
    }

Christian Fibich's avatar
Christian Fibich committed
307
    return undef if ($startmod->find_port($name));
308

309
    $logger->debug($indent . "Adding port \"$name\" to module \"" . $startmod->name . "\"");
310
311

    # decide direction
312
    if (   $function == FIJI::VHDL->FIJI_PORTTYPE_MODIFIED
Christian Fibich's avatar
Christian Fibich committed
313
        || $function == FIJI::VHDL->FIJI_PORTTYPE_RESET_TO_DUT)
314
    {
315
        $direction = "in";
316
    } else {
317
318
319
320
        $direction = "out";
    }

    # generate port
Christian Fibich's avatar
Christian Fibich committed
321
    my $np = $startmod->new_port(name => $name, direction => $direction);
322
323

    # set port type for wrapper generation
Christian Fibich's avatar
Christian Fibich committed
324
    $np->userdata(FIJI::VHDL->FIJI_USERDATA_PORTTYPE, $function);
325
326

    # set indices
327
    if (   $function == FIJI::VHDL->FIJI_PORTTYPE_MODIFIED
Christian Fibich's avatar
Christian Fibich committed
328
        || $function == FIJI::VHDL->FIJI_PORTTYPE_ORIGINAL)
329
    {
Christian Fibich's avatar
Christian Fibich committed
330
331
332
        $np->userdata(FIJI::VHDL->FIJI_USERDATA_FIU_INDEX, $index);
    } elsif ($function == FIJI::VHDL->FIJI_PORTTYPE_FAULT_DETECTION) {
        $np->userdata(FIJI::VHDL->FIJI_USERDATA_FD_INDEX, $index);
333
334
    }

Christian Fibich's avatar
Christian Fibich committed
335
336
    # let Verilog-Perl create a new net for the new port.
    $startmod->link;
337
338

    # find all modules instantiating the current module
Christian Fibich's avatar
Christian Fibich committed
339
    foreach my $mod ($nl->modules_sorted) {
340
        foreach my $cell ($mod->cells) {
Christian Fibich's avatar
Christian Fibich committed
341
            if (defined $cell->submod && $cell->submod == $startmod) {
342
343
                $logger->debug($indent . "Adding pin \"$name\" to cell \"" . $cell->name . "\"");
                $logger->debug($indent . "Connecting pin \"" . $cell->name . HIERSEP . $name . "\" to port \"" . $np->module->name . HIERSEP . $np->name . "\"");
344
345
346
                $cell->new_pin(
                    name     => $name,
                    portname => $np->name,
347
                    netnames => [{'netname' => $np->net->name}],
348
349
                );

Christian Fibich's avatar
Christian Fibich committed
350
                # let verilog-perl find the net and port.
351
                $startmod->link;
Christian Fibich's avatar
Christian Fibich committed
352
                _add_port_to_hierarchy($mod, $name, $function, $index, $indent);
353
354
355
            }
        }
    }
356

357
358
359
    return $np;
}

360
361
362
## @method public net_add_function($net, $function, $port_name, $index)
# @brief Generate external access to a single net
#
Christian Fibich's avatar
Christian Fibich committed
363
364
365
366
367
368
# Performs the following steps:
#    1.  check if the default port name does not yet exist
#    1a. if it exists, generate a new net name
#    2.  add a port through the entire hierarchy
#    3.  assign the net to the port using a contassign statement
#
369
370
371
372
373
374
375
# @param net         the Verilog::Net object to be used
# @param function    the function out of FIJI::VHDL->FIJI_PORTTYPE_xxx
# @param port_name   how the port shall be named (will be prefixed with "fiji_")
# @param index       for some FIJI_PORTTYPEs, an index is needed (FIU and Fault Detect)
#
# @returns undef
sub net_add_function {
376
    my $logger = get_logger("");
Christian Fibich's avatar
Christian Fibich committed
377
    my ($self, $net, $function, $port_name, $index) = @_;
378

379
    $logger->debug("Adding function to \"" . $net->module->name . "\", net \"" . $net->name . "\"");
380
381

    my $prefix = "fiji_";
382
383
384
385
386

    my $unique_name = _unique_name($net->module,$prefix.$port_name);

    if (!defined $unique_name) {
        $logger->error("Could not find a unique name for prefix ".$prefix.$port_name)
387
388
    }

389
390
    $logger->debug("\"" . $unique_name . "\" can be used as fiji connector");
    my $op = _add_port_to_hierarchy($net->module, $unique_name, $function, $index);
391

392
    $logger->debug("Connecting Port \"" . $op->name . "\" to net \"" . $net->name . "\"");
393

Christian Fibich's avatar
Christian Fibich committed
394
    # connect the net to the newly created port
395
396
397
398
399
400
401
    $net->module->new_contassign(
        keyword => "assign",
        lhs     => $op->name,
        rhs     => $net->name,
        module  => $op->module,
        netlist => $op->module->netlist
    );
402

403
404
405
    return undef;
}

406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
# tries to generate a unique but recognizable net name by appending a
# random hex number if necessary
sub _unique_name {
    my $logger = get_logger("");
    my ($mod,$requested_name) = @_;

    # check if requested name is OK
    my $msg = _check_name_in_hierarchy($mod, $requested_name);
    
    if (!defined $msg) {
        return $requested_name;
    }

    # else try up to 10 times to generate a unique name by appending
    # a 4-digit random hex number
    for (my $tries = 0; $tries < MAX_UNIQUE_TRIES; $tries++) {
        my $name = sprintf("%s_%04x", $requested_name, rand(0xffff));
        $msg = _check_name_in_hierarchy($mod, $name);
        return $name if (!defined $msg);
    }
    
    return undef
}

430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
# take a possible escaped identifier and make it a legal _and_ sane verilog identifier
sub _sanitize_identifier {
    my $insane = shift;

    # Verilog allows "non-alphanumeric" characters in identifiers if they start with a backslash and end with whitespace (escaped identifiers) but we don't want to.
    my $sane = ($insane =~ s/[^a-zA-Z0-9_]+/_/gr);
    # VHDL signals must not contain multiple subsequent underscores
    $sane =~ s/__+/_/g;
    return $sane
}

sub generate_contassign {
    my $logger = get_logger("");
    my ($module, $lhs, $rhs) = @_;
    $logger->trace("Setting \"$lhs = $rhs\"");
    $module->new_contassign(
        keyword => "assign",
        lhs     => $lhs,
        rhs     => $rhs,
        module  => $module,
        netlist => $module->netlist
    );
}

454
455
456
457
## @method public instrument_net($net,$fiu_idx, $driver, $driver_type)
# @brief instruments a single net for fault injection
#
# This method performs the following steps
458
459
460
#   1. determine the connected objects (driver as well as driven nets, pins etc)
#   2. generate external access output and input ports and intermediate nets
#   3. interconnect these ports to the matching driver and driven cells via the intermediate nets
Christian Fibich's avatar
Christian Fibich committed
461
#
462
# @param net_path        the Verilog::Net to instrument
463
# @param fiu_idx         the FIU number this external access shall be connected to
464
465
# @param driver_path     the path to the driver of this net (optional but depends on driver_type)
# @param driver_type     the type of the driver (can be PIN, PORT, ASSIGN) (optional but depends on driver_path)
466
467
468
469
#
# @returns STRING          if an error occurred
# @returns undef           if successful
sub instrument_net {
470

471
    my $logger = get_logger("");
472
    my ($self, $net_path, $fiu_idx, $driver_path, $driver_type) = @_;
473

474
    # split hierarchical net path
475
    my $net_descriptor = $self->get_netdescriptor_from_path($net_path);
476
477

    if (ref($net_descriptor) ne "HASH") {
478
        return $net_descriptor;
479
480
481
    }

    my $net = $net_descriptor->{'net'};
482
    my $net_name = $net_descriptor->{'net_name'};
483
484
    my $msb = $net_descriptor->{'msb'};
    my $lsb = $net_descriptor->{'lsb'};
485
    my $mod = $net_descriptor->{'mod'};
486
487
488
    my $idx = '';
    my $idx_postfix = '';

489
490
491
492
493
494
    # We need to produce names for the i/o ports that route the signals
    # to the FIC. To allow to instrument multiple indices of a single
    # signal we need to make these names unique thus we even include the
    # indices if used.
    if (defined $msb && defined $lsb) {
        if ($msb ne $lsb) {
495
            # We dont support the instrumentation of vectors (only sinlge indices of busses).
496
            return "The given net to instrument is a vector with multiple bits.\nThis is not supported.\nMaybe you want to instrument a single bit of said vector instead?";
497
498
            # $idx         = "[".$msb.":".$lsb."]";
            # $idx_postfix = "_".$msb."_".$lsb."_";
499
        } else {
500
501
            $idx         = "[".$msb."]";
            $idx_postfix = "_".$msb."_";
502
503
504
        }
    }

505
    $logger->info("Instrumenting \"" . $net->module->name . "\", net \"" . $net->name.$idx . "\"");
506

507
508
509
510
511
    # we want to perceive the connection state before any changes (at least w/o changes by this invocation)
    my %connections;
    my $rv = $self->_get_net_connections($net, \%connections, $msb, $driver_path, $driver_type);
    return $rv if (defined $rv);

512
513
514
515
    my $output_name = (FIJI_NAMESPACE_PREFIX . $net->name . $idx_postfix . FIJI_PORT_OUT_POSTFIX);
    $output_name = _sanitize_identifier($output_name);
    my $input_name  = (FIJI_NAMESPACE_PREFIX . $net->name . $idx_postfix . FIJI_PORT_IN_POSTFIX);
    $input_name = _sanitize_identifier($input_name);
516

517
    # generate unique name for output port, return with error message if that fails
518
    my $unique_output_name = _unique_name($mod, $output_name);
519
    return "Could not generate unique name for prefix $output_name" if (!defined $unique_output_name);
520

521
    $logger->debug("\"" . $unique_output_name . "\" will be used as fiji connector (output)");
522
    my $op = _add_port_to_hierarchy($mod, $unique_output_name, FIJI::VHDL->FIJI_PORTTYPE_ORIGINAL, $fiu_idx);
523

524
525
    # generate unique name for input port, return with error message if that fails
    my $unique_input_name = _unique_name($mod, $input_name);
526
    return "Could not generate unique name for prefix $input_name" if (!defined $unique_input_name);
527

528
    $logger->debug("\"" . $unique_input_name . "\" will be used as fiji connector (input)");
529
    my $ip = _add_port_to_hierarchy($mod, $unique_input_name, FIJI::VHDL->FIJI_PORTTYPE_MODIFIED, $fiu_idx);
530

Christian Fibich's avatar
Christian Fibich committed
531

532
533
534
    # Add an intermediate net to allow patching without rewriting connections everywhere
    #
    # We use the requested signal as sink/destination.
535
    # That way we only need to change the driver to drive our intermediate net.
536
    #
537
538
539
    # We first generate a suitable name for the intermediate net,
    # then change the driver accordingly and handle other aspects of
    # the intermediate net at the very bottom of this function.
540

541
    # Choose intermediate net name
542
    my $net_name_tmp = _sanitize_identifier(FIJI_NAMESPACE_PREFIX . $net_name . "_in_tmp");
543
    my $name_check = _check_name_in_hierarchy($mod, $net_name_tmp);
544

545
546
547
    # Switch the driver from the original signal to the intermediate net.
    my $driver_is_vector = 0;
    my $driver_is_port = 0;
548
    my $driver_bit = $msb;
549
    foreach my $connection (@{$connections{'drivers'}}) {
Christian Fibich's avatar
Christian Fibich committed
550
        if (ref($connection) eq "Verilog::Netlist::Pin") {
551
552
553
            # If the driver is a pin of a (sub)cell, connect this pin to the intermediate net
            $logger->debug("Connecting (output) pin \"" . $connection->cell->name . HIERSEP . $connection->name . "\" to intermediate net \"$net_name_tmp\"");
            # FIXME: do concatenations really work? They are apparently split already by Verilog::perl but...
554
            for my $netname (@{$connection->netnames}) {
555
                if ($netname->{'netname'} eq $net->name) {
556
557
                    $netname->{'netname'} = $net_name_tmp; # FIXME: do we need to force a re-link (by deleting $connection->nets)?
                    # This net is a vector if the underlying net is a bus and we do not just select a single bit
558
559
560
561
                    # If driver is a vector we need a vectored intermediate bus.
                    # This is the case if
                    #   - the underlying net is a vector and there are no indices
                    #   - the underlying net is a vector and there are two different indices
562
                    if (defined($net->msb) && (!defined($netname->{'msb'}) || $netname->{'msb'} != $netname->{'lsb'})) {
563
                        $driver_is_vector = 1;
564
565
566
567
                    } else {
                        # Make sure we do not output the index unnecessarily (if the driver is a single bit of a vector)
                        undef($netname->{'msb'});
                        undef($netname->{'lsb'});
568
                    }
569
                }
570
            }
571
572
573
            # $connection->port(undef);    # resolved by link
        } else {
            if (ref($connection) eq "Verilog::Netlist::Port") {
574
575
576
577
578
                $driver_is_port = 1;
                # If instrumented net is a vector we will need an intermediate bus
                if (defined($net->msb)) {
                    $driver_is_vector = 1;
                }
579
580
581
582
                # If we are changing the name of a port of the top module we need to inform the VHDL generator
                if ($mod->is_top) {
                        $connection->userdata(FIJI::VHDL->FIJI_USERDATA_PREV_PORTNAME, $connection->name);
                }
583
584
585
586
587
588
589
590
                # Change type of existing non-instrumented input to wire -
                # practically transforming it to an ordinary wire that can easily be intrumented.
                $logger->debug("Transforming previous port named \"". $connection->name . "\" into an ordinary wire");
                $connection->net->decl_type(undef);
                $connection->net->net_type("wire");
                # Unsetting the port<->net inter-references forces their automatic re-setting at link time
                $connection->net->port(undef);
                $connection->net(undef);
591
                # Eventually connect this port to the intermediate net by changing its name
592
                $logger->debug("Connecting (input) port \"" . $connection->name . "\" to intermediate net \"$net_name_tmp\"");
593
                $connection->name($net_name_tmp); # NB: this will automatically change the cell's configuration on the next link() call.
594
595
            } elsif (ref($connection) eq "Verilog::Netlist::ContAssign") {
                # If the driver is an assignment, replace the LHS with the intermediate net
596
597
598
599
600
601
602
603
                # Retrieve net object of LHS of the assignment to determine if it is a bus and if the assignment is to the complete net or a single bit.
                my $lhs_elems = $self->_extract_netstring_elements($connection->lhs);
                my $lhs_net_name = $lhs_elems->{'net_name'};
                my $lhs_net = $mod->find_net($lhs_net_name);
                if (!defined($lhs_net)) {
                    my $lhs_port = $mod->find_port($lhs_net_name);
                    if (!defined($lhs_port)) {
                        return "Could not find net or port in module \"" . $mod->name . "\" matching LHS of assignment \"" . $connection->lhs . "\" = \"" . $connection->rhs . "\"";
604
                    }
605
606
607
608
609
610
611
                    # FIXME: so this is actually a port...
                    $logger->fatal("Found port name in continuous assignment. This is not supported yet.");
                    return "BORKED";
                }
                if (defined($connection->userdata->{'fiji_driver_bit'})) {
                    $driver_is_vector = 1;
                    $driver_bit = $connection->userdata->{'fiji_driver_bit'};
612
                }
613
614
                $logger->debug("Connecting to intermediate net \"" . $net_name_tmp . "\" the continuous assignment of \"" . $connection->rhs . "\"");
                $connection->lhs($net_name_tmp);
615
            } else {
616
                $logger->error("Driver instance is neither pin, port nor contassign?");
617
            }
618
619
        }
    }
620

621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
    # Below we create the intermediate net as needed.
    # A suitable name for the tmp net was already determined.
    # 1.) Generate the tmp net.
    # 2.) Assign injected signal from input pin to original signal.
    # 3.) If original signal is a bus forward orignal data from tmp net to untouched bits of original signal
    # 4.) Forward (injected bit of) intermediate net to FIC by assigning it to the output pin
    #
    # If the name is already taken then assume we need to instrument another bit of a bus
    # FIXME: maybe we should try harder to find out the reason why _check_name_in_hierarchy failed
    if (defined($name_check)) {
        if (!defined($net->msb)) {
            my $err = "Tried to instrument non-vector signal (\"$net_name\") twice or we really have a naming conflict.";
            $logger->error($err);
            return $err;
        }
        # We need to undo the previous assignment of the respective bit
        foreach my $statement ($mod->statements) {
638
            if ($statement->rhs =~ /^[ \t]*~?[ \t]*\Q$net_name_tmp\E(\[(\Q$driver_bit\E)\])?$/) {
639
640
641
642
643
                $logger->debug("    unassigning \"" . $statement->lhs . " = " . $statement->rhs . "\"");
                $statement->delete();
            }
        }

644
645
        $logger->debug("    reassigning \"" . $net_name."[".$driver_bit."] = " . $ip->net->name . "\"");
        generate_contassign($mod, $net_name."[".$driver_bit."]", $ip->net->name);
646
        $driver_is_vector = 1;
647
648
    } else {
        # 2.) Generate intermediate (tmp) net for easier input and output routing
649
650
651
        # For ports we don't need to do that ourselves because Verilog::Perl will
        # generate a new apropriate net when linking the port.
        if ($driver_is_port) {
652
            $logger->debug("Intermediate port named \"" . $net_name_tmp . (defined($net->msb) ? "[" . $net->msb .":". $net->lsb . "]" : "") . "\" to patch \"$net_name\" will be generated automatically later");
653
        } else {
654
655
656
657
658
659
660
661
662
663
664
665
666
667
            # If the driver is a vector then we will generate a vectored intermediate net as well.
            # However, if the driver is a single-bit net or a single bit of a vector we generate
            # a simple intermediate net (and could get away with none at all actually).
            my @net_cfg = (name => $net_name_tmp);
            if ($driver_is_vector) {
                $logger->debug("Generating intermediate wire named \"" . $net_name_tmp . "[" . $net->msb .":". $net->lsb . "]\" to patch \"$net_name\"");
                push(@net_cfg,
                    msb => $net->msb,
                    lsb => $net->lsb,
                );
            } else {
                $logger->debug("Generating intermediate wire named \"" . $net_name_tmp . "\" to patch \"$net_name\"");
            }
            $mod->new_net(@net_cfg);
668
669
670
671
672
673
674
675
676
677
        }

        # 3+4.) Assign injected (and uninjected bits of vectors) to previous signal.
        # Below we assign the altered signal from the FIC to the original signal.
        # For busses we need to connect all non-instrumented bits of the tmp net
        # additionally to preserve their unmodified values.

        if (!defined($net->msb)) {
            # If the instrumented net is not a vector we simply assign the injected signal
            generate_contassign($mod, $net_name, $ip->net->name);
678
        } elsif (!$driver_is_vector) {
679
680
            # If the net is a vector but the driver was driving only one bit
            # then we need to drive the originally driven bit only
681
            generate_contassign($mod, $net_name."[".$driver_bit."]", $ip->net->name);
682
683
684
        } else {
            # For drivers of complete busses we need to connect all non-instrumented bits of the tmp net
            # additionally to preserve their unmodified values.
685
            # In case of concatenations the index might be different to the one given.
686
            my ($low, $high) = _extract_low_high($net->lsb, $net->msb);
687
            for (my $i = $low ; $i <= $high ; $i++) {
688
                if ($i == $driver_bit) {
689
690
691
692
693
694
695
696
697
                    generate_contassign($mod, $net_name."[".$i."]", $ip->net->name);
                } else {
                    generate_contassign($mod, $net_name."[".$i."]", $net_name_tmp."[".$i."]");
                }
            }
        }
    }

    # 5.) Connect the tmp net to the output pin that forwards the signal to the FIC
698
699
    #     If the driver is not a vector a simple assignment is fine, else
    if (!$driver_is_vector) {
700
701
702
        $logger->debug("    assigning \"" . $op->net->name . " = " . $net_name_tmp . "\"");
        generate_contassign($mod, $op->net->name, $net_name_tmp);
    } else {
703
704
        $logger->debug("    assigning \"" . $op->net->name . " = " . $net_name_tmp ."[".$driver_bit."]\"");
        generate_contassign($mod, $op->net->name, $net_name_tmp."[".$driver_bit."]");
705
    }
706
    $mod->link;
707
708
709
    return undef;
}

710
711
712
## @method public validate_driver($net_path, $driver_path, $driver_type)
# @brief Check if the given driver is valid for the given net
#
713
714
# Check if the driver specified by $driver_type and $driver_path
# is actually connected to the net specified by $net_path
715
716
717
718
719
720
721
#
# @param net_path        The hierarchical path of the net, separeted by HIERSEP
# @param driver_path     The hierarchical path of the driver object, separated by HIERSEP
# @param driver_type     The type of the driver object, one of {PIN, PORT, ASSIGN}
#
# @returns STRING          if an error occurred
# @returns undef           if successfull
722
sub _validate_driver {
723
    my $logger = get_logger("");
Christian Fibich's avatar
Christian Fibich committed
724
    my ($self, $net_path, $driver_path, $driver_type) = @_;
725

Christian Fibich's avatar
Christian Fibich committed
726
    my $connection_object = $self->get_connection_object($driver_path, $driver_type);
727
    my $connections       = {};
728
    $self->_get_net_connections_from_path($net_path, $connections);
729

730
731
    goto FAIL if (!defined $connection_object);

Christian Fibich's avatar
Christian Fibich committed
732
733
    my @in_drivers     = grep { $_ == $connection_object } @{$connections->{'drivers'}};
    my @in_connections = grep { $_ == $connection_object } @{$connections->{'connected'}};
734

735
    goto FAIL if (@in_drivers == 0 && @in_connections == 0);
736
737

    return undef;
738
739
740
741
742

FAIL:
    my $msg = "No possible driver found";
    $logger->error($msg);
    return $msg;
743
744
}

745
746
747
748
749
750
751
752
753
## @function private _select_driver($connected,$net)
# @brief Prompt the user to select a driver for a net from a set of given cells/outputs
#
# @param connected       list reference containing connected Verilog::Perl instances
# @param net             the Verilog::Perl::Net instance to select the driver for
#
# @returns STRING          if an error occurred
# @returns Driver object   if successful
sub _select_driver {
Christian Fibich's avatar
Christian Fibich committed
754
    my ($connected, $net) = @_;
755

756
    print "Select driver for net " . $net->name . ": \n";
757
    my $di;
Christian Fibich's avatar
Christian Fibich committed
758
759
    for ($di = 0 ; $di < @{$connected} ; $di++) {
        printf("[%d] %s\n", $di, FIJI::Netlist->_connection_tostr(@{$connected}[$di]));
760
    }
Christian Fibich's avatar
Christian Fibich committed
761
    printf("[x] none of the above.\n", $di);
762
    my $sel;
763
    while (1) {
764
        $sel = <STDIN>;
Christian Fibich's avatar
Christian Fibich committed
765
        if ($sel =~ m/[0-9]+/ && defined @{$connected}[$sel]) {
766
            last;
Christian Fibich's avatar
Christian Fibich committed
767
        } elsif ($sel =~ m/[xX]/) {
768
            my $msg = "No driver selected for net " . $net->name;
769
            return $msg;
770
771
        } else {
            print "Invalid driver.\n";
772
773
774
        }
    }
    return @{$connected}[$sel];
775
776
}

777
778
779
## @method private _connection_tostr ($connection,$conn_str_list_ref)
# @brief Stringifies a connection information
#
Christian Fibich's avatar
Christian Fibich committed
780
# The string is in the format \<TYPE\>: \<PATH|TO|Netname\>
781
# Where '|' can be any HIERSEP and TYPE is one of {PIN, PORT, ASSIGN}
782
# and optionally pushes a hash {path=>...,type=>...,} onto the list @$conn_str_list_ref
783
#
784
785
786
# @param connection          the connection to print
# @param conn_str_list_ref   optional list where a hash describing the connection is pushed
#
Christian Fibich's avatar
Christian Fibich committed
787
# @returns STRING  in the format \<TYPE\>: \<PATH|TO|Netname\>
788
sub _connection_tostr {
Christian Fibich's avatar
Christian Fibich committed
789
    my ($self, $connection, $conn_str_list_ref) = @_;
790
791
    my $path;
    my $type;
792
    my $str;
793

Christian Fibich's avatar
Christian Fibich committed
794
    if (ref($connection) eq "Verilog::Netlist::Pin") {
795
        $path = $connection->cell->module->name . HIERSEP . $connection->cell->name . HIERSEP . $connection->name;
796
        $type = "PIN";
797
        $str = $type . ": " . $path
Christian Fibich's avatar
Christian Fibich committed
798
    } elsif (ref($connection) eq "Verilog::Netlist::Port") {
799
        $path = $connection->module->name . HIERSEP . $connection->name;
800
        $type = "PORT";
801
        $str = $type . ": " . $path
802
    } elsif (ref($connection) eq "Verilog::Netlist::ContAssign") {
803
        $path = $connection->module->name . HIERSEP . $connection->rhs;
804
        $type = "ASSIGN";
805
        $str = $type . ": " . $connection->rhs;
806
    }
807
808
    push @{$conn_str_list_ref}, {path => $path, type => $type} if defined $conn_str_list_ref;
    return $str
809
810
}

811
812
813
## @method public get_connection_object ($connection_path,$connection_type)
# @brief Retrieves the connection object specified by path and type
#
814
# Retrieves a reference to the Verilog::Pin, Verilog::Port or Verilog::ContAssign
815
816
817
818
819
# Object specified by the parameters
#
# @param connection_path     the hierarchichal PATH|To|the|object
# @param connection_type     the type of the object {PIN,PORT,ASSIGN}
#
820
# @returns the Verilog::Pin, Verilog::Port or Verilog::ContAssign Object specified by the parameters
821
sub get_connection_object {
822
    my $logger = get_logger("");
Christian Fibich's avatar
Christian Fibich committed
823
    my ($self, $connection_path, $connection_type) = @_;
824
825

    my $rv;
826
    my $SEP = HIERSEP;
827

Christian Fibich's avatar
Christian Fibich committed
828
    if ($connection_type eq "PIN") {
829
        if ($connection_path =~ /^(.+)\Q$SEP\E(.+)\Q$SEP\E(.+)$/) {
830

831
            $logger->trace("Looking for pin named \"$3\" in cell \"$2\" of module \"$1\"...");
832
833

            my $mod  = $self->{'nl'}->find_module($1);
Christian Fibich's avatar
Christian Fibich committed
834
835
            my $cell = $mod->find_cell($2) if (defined $mod);
            my $pin  = $cell->find_pin($3) if (defined $cell);
836
837
            $rv = $pin;
        }
Christian Fibich's avatar
Christian Fibich committed
838
    } elsif ($connection_type eq "PORT") {
839
        if ($connection_path =~ /^(.+)\Q$SEP\E(.+)$/) {
840

841
            $logger->trace("Looking for port named \"$2\" in module \"$1\"...");
842

843
            my $mod = $self->{'nl'}->find_module($1);
Christian Fibich's avatar
Christian Fibich committed
844
            my $port = $mod->find_port($2) if (defined $mod);
845
846
            $rv = $port;
        }
Christian Fibich's avatar
Christian Fibich committed
847
    } elsif ($connection_type eq "ASSIGN") {
848
        if ($connection_path =~ /^(.+)\Q$SEP\E(.+)$/) {
849
            my $net = $2;
850

851
            $logger->trace("Looking for assignment to/from \"$net\" in module \"$1\"...");
852
            my $mod = $self->{'nl'}->find_module($1);
853

Christian Fibich's avatar
Christian Fibich committed
854
            if (defined $mod) {
855
                my $assign;
856
                for my $a (grep { $_->isa("Verilog::Netlist::ContAssign") } $mod->statements) {
857
                    if ($a->lhs =~ /\Q$net\E/ || $a->rhs =~ /\Q$net\E/) {
858
                        $assign = $a;
859
                        $logger->trace(sprintf("Constant assignment: \"%s\" = \"%s\"", $a->lhs, $a->rhs));
860
861
862
                        last;
                    }
                }
863
                $rv = $assign;
864
865
866
            }
        }
    }
867
    $logger->warn("Could not find $connection_type \"$connection_path\"!") if !defined($rv);
868
    return $rv;
869
870
}

871

872
873
874
## @method private _get_net_connections ($net,$connection_hashref)
# @brief retrieves connections of a given net
#
875
# gets all pins, ports and assignments a net is connected to
876
#
877
# @param net_path               hierarchical path string of the net to be examined
878
879
880
881
882
# @param connection_hashref     a hashref where the results can be placed
#                               connection_hashref->{'drivers'} contains a list of driver cells
#                               connection_hashref->{'driven'} contains a list of driven cells
#                               connection_hashref->{'connected'} contains a list cells connected to the
#                               net but driver/driven cannot be decided
883
sub _get_net_connections_from_path {
884
    my $logger = get_logger("");
885
    my ($self, $net_path, $connection_hashref) = @_;
Stefan Tauner's avatar
Stefan Tauner committed
886
    my $net_descriptor = $self->get_netdescriptor_from_path($net_path);
887
888
    return $net_descriptor if (ref($net_descriptor) ne "HASH");
    my $net = $net_descriptor->{net};
889
    return $self->_get_net_connections($net, $connection_hashref, $net_descriptor->{'msb'});
890
891
892
893
894
895
}

## @method private _untie_concatenations ($concat, $arrayref)
# @brief breaks up a verilog concatenation into single net elements
#
# @param concat     string (including the {}) forming a verilog concatenation
896
# @return arrayref  array reference containing the individual concatenated verilog expressions in the given string
897
898
899
900
901
902
903
904
sub _untie_concatenations {
    my $logger = get_logger("");
    my (