Commit c5d3f293 authored by Stefan Tauner's avatar Stefan Tauner
Browse files

instrumentation: use new scheme where we only change the drivers

Still has quite some issues, namely:

 - instrumenting individual assignments of vectors does not work correctly
   because the rest of the net will not be assigned to the intermediate net.
 - instrumenting ports of the respective module changes the name of the port
   which obviously breaks the instantiation without further work.
parent 8778480c
......@@ -449,9 +449,9 @@ sub generate_contassign {
# @brief instruments a single net for fault injection
#
# This method performs the following steps
# 1. tries to determine the driver, or otherwise prompts the user to select it
# 2. generate external access output and input ports
# 3. interconnects these ports to the matching driver and driven cells
# 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
#
# @param net_path the Verilog::Net to instrument
# @param fiu_idx the FIU number this external access shall be connected to
......@@ -464,7 +464,6 @@ sub instrument_net {
my $logger = get_logger("");
my ($self, $net_path, $fiu_idx, $driver_path, $driver_type) = @_;
my $msg;
# split hierarchical net path
my $net_descriptor = $self->get_netdescriptor_from_path($net_path);
......@@ -474,6 +473,7 @@ sub instrument_net {
}
my $net = $net_descriptor->{'net'};
my $net_name = $net_descriptor->{'net_name'};
my $msb = $net_descriptor->{'msb'};
my $lsb = $net_descriptor->{'lsb'};
my $mod = $net_descriptor->{'mod'};
......@@ -498,6 +498,11 @@ sub instrument_net {
$logger->info("Instrumenting \"" . $net->module->name . "\", net \"" . $net->name.$idx . "\"");
# 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);
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);
......@@ -517,222 +522,138 @@ sub instrument_net {
$logger->debug("\"" . $unique_input_name . "\" will be used as fiji connector (input)");
my $ip = _add_port_to_hierarchy($mod, $unique_input_name, FIJI::VHDL->FIJI_PORTTYPE_MODIFIED, $fiu_idx);
my %connections;
my $rv = $self->_get_net_connections($net, \%connections, $msb, $driver_path, $driver_type);
return $rv if (defined $rv);
# connecting newly created output to driver
foreach my $connection (@{$connections{'drivers'}}) {
if (ref($connection) eq "Verilog::Netlist::Pin") {
# FIXME: handle concatenations
# For buses we introduce a dedicated temporary wire to re-route/inject single bits of it.
# Therefore we can simply leave them alone here.
if (defined($lsb) && defined($msb)) {
$logger->warn("Ignoring Pin \"" . $connection->cell->name . HIERSEP . $connection->name . "\" because instrumented net \"" . $net->name . "\" is a bus.");
next;
}
# If the driver is a pin of a cell, connect this pin to the newly created net
$logger->debug("Original: Connecting (output) pin \"" . $connection->cell->name . HIERSEP . $connection->name . "\" to generated output \"" . $op->name . "\"");
for my $netname (@{$connection->netnames}) {
# @FIXME work to be done for Buses
if (($netname->{'netname'} eq $net->name) &&
(!defined $netname->{lsb} || $netname->{lsb} eq $lsb) &&
(!defined $netname->{msb} || $netname->{msb} eq $msb)) {
$netname->{'netname'} = $op->net->name;
# there will never be an indexed newly created port (only single-bit FIUs)
$netname->{lsb} = undef;
$netname->{msb} = undef;
}
# Add an intermediate net to allow patching without rewriting connections everywhere
#
# We use the requested signal as sink/destination.
# That way we only need to change the driver to drive our intermediate net later
#
# 1.) Choose a suitable name for the tmp net.
# 2.) Generate the tmp net.
# 3.) Assign injected signal from input pin to original signal.
# 4.) If original signal is a bus forward orignal data from tmp net to untouched bits of original signal
# 5.) Forward (injected bit of) intermediate net to FIC by assigning it to the output pin
#
# The only thing that remains to do after that is to switch the driver from the
# original signal to the intermediate net.
# 1.) Choose intermediate net name
my $net_name_tmp = _sanitize_identifier(FIJI_NAMESPACE_PREFIX . $net_name . "_in_tmp");
my $msg = _check_name_in_hierarchy($mod, $net_name_tmp);
my $net_tmp;
# 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($msg)) {
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;
}
$net_tmp = $mod->find_net($net_name_tmp);
# We need to undo the previous assignment of the respective bit
foreach my $statement ($mod->statements) {
if ($statement->rhs =~ /^[ \t]*~?[ \t]*\Q$net_name_tmp\E(\[(\Q$msb\E)\])?$/) {
$logger->debug(" unassigning \"" . $statement->lhs . " = " . $statement->rhs . "\"");
$statement->delete();
}
}
#$connection->net(undef); # resolved by link
$connection->port(undef); # resolved by link
} elsif (ref($connection) eq "Verilog::Netlist::Port") {
# @FIXME work to be done for Buses
# if it is a port of a module, connect this port to the newly created net
$logger->debug("Original: Connecting (input) port \"" . $connection->name . "\" to generated output \"" . $op->name . "\"");
$connection->net($op->net);
$mod->new_contassign(
keyword => "assign",
lhs => $op->name,
rhs => $connection->name.$idx,
module => $mod,
netlist => $mod->netlist
);
} elsif (ref($connection) eq "Verilog::Netlist::ContAssign") {
# 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 . "\"";
}
# FIXME: so this is actually a port...
$logger->fatal("Found port name in continuous assignment. This is not supported yet.");
return "BORKED";
}
if (!defined($lhs_net->msb)) {
# If the signal in LHS is not a vector at all we simply drive the new port instead, easy.
$connection->lhs($op->name);
$logger->debug("Original: Connecting to generated output \"" . $op->name . "\" continuous assignment of \"" . $connection->rhs . "\"");
} else {
# If LHS is a bus however we need more effort.
# We need to extract the bit we want and drive the new port, but
# also continue to drive the remaining bits of the vector just like before
# e.g. when we want to tap [1] of \dumb.non.hierarchical_bus then we need to transform this:
# wire [3:0] wirebus;
# wire [3:0] \dumb.non.hierarchical_bus ;
# assign \dumb.non.hierarchical_bus = ~wirebus;
# into this:
# wire [3:0] wirebus;
# wire [3:0] \dumb.non.hierarchical_bus ;
# wire [3:0] tmp_hierarchical_bus;
# assign tmp_hierarchical_bus = ~wirebus;
# assign tmp_signal_o = tmp_hierarchical_bus[1];
# assign \dumb.non.hierarchical_bus[0] = tmp_hierarchical_bus[0];
# assign \dumb.non.hierarchical_bus[1] = tmp_signal_i;
# assign \dumb.non.hierarchical_bus[2] = tmp_hierarchical_bus[2];
# assign \dumb.non.hierarchical_bus[3] = tmp_hierarchical_bus[3];
# However, we just need to create the temporary bus once even if more than one bit of it get instrumented...
my ($op_rhs, $ip_lhs);
# If the LHS is a bit range of a vector we have either
# - generated the net earlier (in the else branch below) or,
# - it was a bit-wise assignment in the original netlist as well.
# In any case we only need to generate a temporary net if the assignment is not to a bit of a vector.
if (defined($lhs_elems->{'lsb'})) {
$op_rhs = $connection->rhs;
$ip_lhs = $connection->lhs;
$logger->debug(" reassigning \"" . $net_name."[".$msb."] = " . $ip->net->name . "\"");
generate_contassign($mod, $net_name."[".$msb."]", $ip->net->name);
} else {
# 2.) Generate intermediate (tmp) nets for easier input and output routing
$logger->debug("Generating intermediate wire named \"" . $net_name_tmp . "\" to patch \"$net_name\"");
$net_tmp = $mod->new_net(
name => $net_name_tmp,
msb => $net->msb,
lsb => $net->lsb,
);
# 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)) {
generate_contassign($mod, $net_name, $ip->net->name);
} else {
my ($low, $high) = _extract_low_high($net->lsb, $net->msb);
for (my $i = $low ; $i <= $high ; $i++) {
if ($i == $msb) {
generate_contassign($mod, $net_name."[".$i."]", $ip->net->name);
} else {
my $tmp_name = FIJI_NAMESPACE_PREFIX . $lhs_net->name . "_tmp";
my $unique_tmp_name = _unique_name($mod, $tmp_name);
return "Could not generate unique name for prefix ".$tmp_name if (!defined $unique_tmp_name);
# Generate intermediate (tmp) bus and assign old RHS to it
$logger->debug("Generating intermediate bus to connect split up result of \"" . $connection->rhs . "\"");
my $tmp_bus = $mod->new_net(
name => _sanitize_identifier($unique_tmp_name),
msb => $lhs_net->msb,
lsb => $lhs_net->lsb,
);
generate_contassign($mod, $tmp_bus->name, $connection->rhs);
# Connect the individual bits of the tmp bus with the (previous) LHS that we don't want to instrument
my ($low, $high) = _extract_low_high($lhs_net->lsb, $lhs_net->msb);
for (my $i = $low ; $i <= $high ; $i++) {
if ($i != $msb) {
generate_contassign($mod, $connection->lhs."[".$i."]", $tmp_bus->name."[".$i."]");
}
}
$op_rhs = $tmp_bus->name . "[".$msb."]";
$ip_lhs = $connection->lhs."[".$msb."]";
generate_contassign($mod, $net_name."[".$i."]", $net_name_tmp."[".$i."]");
}
# Connect requested bit of the tmp bus with the output port
$logger->debug("Original: Adding assignment \"" . $op->name . " = " . $op_rhs . "\"");
generate_contassign($mod, $op->name, $op_rhs);
$logger->debug("Modified: Adding assignment \"" . $ip_lhs . " = " . $ip->name . "\"");
$connection->rhs($ip->name);
$connection->lhs($ip_lhs);
}
} else {
$logger->debug("Driver instance is neither pin, port nor contassign?");
}
}
# create interconnections for newly created port/pin
# @TODO needed here or OK linking once after loop?
# $mod->link;
# 5.) Connect the tmp net to the output pin that forwards the signal to the FIC
if (!defined($net->msb)) {
$logger->debug(" assigning \"" . $op->net->name . " = " . $net_name_tmp . "\"");
generate_contassign($mod, $op->net->name, $net_name_tmp);
} else {
$logger->debug(" assigning \"" . $op->net->name . " = " . $net_name_tmp ."[".$msb."]\"");
generate_contassign($mod, $op->net->name, $net_name_tmp."[".$msb."]");
}
# create interconnections for newly created port/pin
# @TODO needed here or OK linking once after all loops?
# $mod->link;
# exactly the same for the input
# connecting newly created input to driven cells
foreach my $connection (@{$connections{'driven'}}) {
# connecting newly created output to driver
foreach my $connection (@{$connections{'drivers'}}) {
if (ref($connection) eq "Verilog::Netlist::Pin") {
# 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...
for my $netname (@{$connection->netnames}) {
if ($netname->{'netname'} eq $net->name) {
# FIXME: we'll ignore all indexed nets for now but that certainly is not right... or is it?
if (!defined($lsb) && !defined($msb)) {
$logger->warn("Modified: Connecting (input) pin \"" . $connection->cell->name . HIERSEP . $connection->name . "\" to generated input \"" . $ip->name . "\"");
$netname->{'netname'} = $ip->net->name;
#$connection->net(undef); # resolved by link
$connection->port(undef); # resolved by link
# } else {
# $netname->{lsb} eq $lsb) &&
# $netname->{msb} eq $msb)) {
# there will never be an indexed newly created port (only single-bit FIUs)
# $netname->{lsb} = undef;
# $netname->{msb} = undef;
}
$netname->{'netname'} = $net_name_tmp;
}
}
} elsif (ref($connection) eq "Verilog::Netlist::Port") {
# @FIXME work to be done for Buses
$logger->debug("Modified: Connecting (output) port \"" . $connection->module->name . HIERSEP . $connection->name . "\" to generated input \"" . $ip->name . "\"");
$mod->new_contassign(
keyword => "assign",
lhs => $connection->name.$idx,
rhs => $ip->name,
module => $mod,
netlist => $mod->netlist
);
} elsif (ref($connection) eq "Verilog::Netlist::ContAssign") {
# 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 . "\"";
# $connection->port(undef); # resolved by link
} else {
if (ref($connection) eq "Verilog::Netlist::Port") {
# If the driver is a port of a module, connect this port to the intermediate net
$logger->debug("Connecting (input) port \"" . $connection->name . "\" to intermediate net \"$net_name_tmp\"");
$connection->name($net_name_tmp);
} elsif (ref($connection) eq "Verilog::Netlist::ContAssign") {
# FIXME: concatenations
# If the driver is an assignment, replace the LHS with the intermediate net
if(0) {
$logger->debug("Connecting to intermediate net \"" . $net_name_tmp . "\" the continuous assignment of \"" . $connection->rhs . "\"");
$connection->lhs($net_name_tmp);
} else {
# 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 . "\"";
}
# FIXME: so this is actually a port...
$logger->fatal("Found port name in continuous assignment. This is not supported yet.");
return "BORKED";
}
if (!defined($lhs_net->msb)) {
# If the signal in LHS is not a vector at all we simply drive the new port instead, easy.
$logger->debug("Connecting to intermediate net \"" . $net_name_tmp . "\" the continuous assignment of \"" . $connection->rhs . "\"");
$connection->lhs($net_name_tmp);
} else {
# If LHS is a bus however we need to drive the bit we want to instrument only
$logger->debug("Connecting to intermediate net \"" . $net_name_tmp."[".$msb."]" . "\" the continuous assignment of \"" . $connection->rhs . "\"");
$connection->lhs($net_name_tmp."[".$msb."]");
# $logger->debug("Connecting to non-instrumented bits of the intermediate net \"" . $connection->lhs."[".$msb."]" . "\" the continuous assignment of \"" . $net_name_tmp."[".$msb."]" . "\"");
# generate_contassign($mod, $connection->lhs."[".$msb."]", $net_name_tmp."[".$msb."]");
}
}
# FIXME: so this is actually a port...
$logger->fatal("Found port name in continuous assignment. This is not supported yet.");
return "BORKED";
}
if (!defined($lhs_net->msb)) {
# If the signal in LHS is not a vector at all we simply assign the injected net from the new port, easy.
# However, we need to check for inversions on the RHS and conserve them
$connection->rhs =~ /^[ \t]*(~)/;
my $new_rhs = $1 . $ip->name;
$connection->rhs($new_rhs);
$logger->debug("Modified: Connecting to generated input \"$new_rhs\" in continuous assignment to \"" . $connection->lhs . "\"");
} else {
# FIXME: if LHS is a bus possibly containing one or more wires we need to instrument, we may need to use a temp wire to preserve the old values like for drivers above - I think.
# $logger->warn("*Would* set RHS of \"" . $connection->lhs . " = " . $connection->rhs . "\" to \"$input_name\"");
# $connection->rhs($input_name);
$logger->debug("Driver instance is neither pin, port nor contassign?");
}
} else {
$logger->debug("Driven instance is neither pin, port nor contassign?");
}
}
# create interconnections for newly created ports/pins
# @TODO OK linking once after loop?
$mod->link;
# Clean up "loose" wires:
# If we instrument nets that are only wired through this module from and into others the need for the intermediate wire is removed by the introduction of our instrumentation pins
my %instrumented_connections;
$rv = $self->_get_net_connections($net, \%instrumented_connections, $msb, $driver_path, $driver_type);
return $rv if (defined $rv);
if (scalar(@{$instrumented_connections{'driven'}}) == 0 && scalar(@{$instrumented_connections{'drivers'}}) == 0 && scalar(@{$instrumented_connections{'connected'}}) == 0) {
$logger->warn("Instrumenting \"" . $mod->name . "\", net \"" . $net->name.$idx . "\" has lead to a loose wire - removing.");
$net->delete();
$mod->link;
}
return undef;
}
......@@ -857,7 +778,7 @@ sub get_connection_object {
if ($connection_type eq "PIN") {
if ($connection_path =~ /^(.+)\Q$SEP\E(.+)\Q$SEP\E(.+)$/) {
$logger->debug("Looking for pin named \"$3\" in cell \"$2\" of module \"$1\"...");
$logger->trace("Looking for pin named \"$3\" in cell \"$2\" of module \"$1\"...");
my $mod = $self->{'nl'}->find_module($1);
my $cell = $mod->find_cell($2) if (defined $mod);
......@@ -867,7 +788,7 @@ sub get_connection_object {
} elsif ($connection_type eq "PORT") {
if ($connection_path =~ /^(.+)\Q$SEP\E(.+)$/) {
$logger->debug("Looking for port named \"$2\" in module \"$1\"...");
$logger->trace("Looking for port named \"$2\" in module \"$1\"...");
my $mod = $self->{'nl'}->find_module($1);
my $port = $mod->find_port($2) if (defined $mod);
......@@ -877,7 +798,7 @@ sub get_connection_object {
if ($connection_path =~ /^(.+)\Q$SEP\E(.+)$/) {
my $lhs = $2;
$logger->debug("Looking for assignment to/from \"$2\" in module \"$1\"...");
$logger->trace("Looking for assignment to/from \"$2\" in module \"$1\"...");
my $mod = $self->{'nl'}->find_module($1);
if (defined $mod) {
......@@ -885,7 +806,7 @@ sub get_connection_object {
for my $a (grep { $_->isa("Verilog::Netlist::ContAssign") } $mod->statements) {
if ($a->lhs eq $lhs || $a->rhs =~ /\Q$lhs\E/) {
$assign = $a;
$logger->debug(sprintf("Constant assignment: \"%s\" = \"%s\"", $a->lhs, $a->rhs));
$logger->trace(sprintf("Constant assignment: \"%s\" = \"%s\"", $a->lhs, $a->rhs));
last;
}
}
......@@ -1297,7 +1218,7 @@ sub get_netdescriptor_from_path {
my $net = $mod->find_net($net_name);
if (!blessed($net) || !$net->isa("Verilog::Netlist::Net")) {
return "Could not find net '" . $mod->name . HIERSEP . $net_name . " in netlist $self->{'filename'}'";
return "Could not find net \"" . $mod->name . HIERSEP . $net_name . "\" in netlist $self->{'filename'}'";
}
$net_descr->{'net'} = $net;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment