Commit 218337b0 authored by Stefan Tauner's avatar Stefan Tauner
Browse files

Netlist: add preliminary concatenation support

parent b6869ae8
......@@ -34,6 +34,7 @@ use warnings;
use diagnostics;
use Scalar::Util 'blessed';
use List::Util qw[min max];
use Log::Log4perl qw(get_logger :easy);
use File::Basename qw(basename);
......@@ -545,6 +546,7 @@ sub instrument_net {
# Switch the driver from the original signal to the intermediate net.
my $driver_is_vector = 0;
my $driver_is_port = 0;
my $driver_bit = $msb;
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
......@@ -591,48 +593,28 @@ sub instrument_net {
$logger->debug("Connecting (input) port \"" . $connection->name . "\" to intermediate net \"$net_name_tmp\"");
$connection->name($net_name_tmp); # NB: this will automatically change the cell's configuration on the next link() call.
} 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";
}
my $rhs_elems = $self->_extract_netstring_elements($connection->rhs);
my $rhs_net_name = $rhs_elems->{'net_name'};
my $rhs_net = $mod->find_net($rhs_net_name);
# If driver is a vector we need a vectored intermediate bus.
# This is the case if on the RHS
# - the underlying net is a vector and there are no indices
# - the underlying net is a vector and there are two different indices
if (defined($rhs_net->msb) && (!defined($rhs_elems->{'msb'}) || ($rhs_elems->{'msb'} != $rhs_elems->{'msb'}))) {
$driver_is_vector = 1;
}
$logger->debug("Connecting to intermediate net \"" . $net_name_tmp . "\" the continuous assignment of \"" . $connection->rhs . "\"");
$connection->lhs($net_name_tmp);
if(1) {} 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."]");
# 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($connection->userdata->{'fiji_driver_bit'})) {
$driver_is_vector = 1;
$driver_bit = $connection->userdata->{'fiji_driver_bit'};
}
$logger->debug("Connecting to intermediate net \"" . $net_name_tmp . "\" the continuous assignment of \"" . $connection->rhs . "\"");
$connection->lhs($net_name_tmp);
} else {
$logger->debug("Driver instance is neither pin, port nor contassign?");
$logger->error("Driver instance is neither pin, port nor contassign?");
}
}
}
......@@ -654,14 +636,14 @@ sub instrument_net {
}
# 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)\])?$/) {
if ($statement->rhs =~ /^[ \t]*~?[ \t]*\Q$net_name_tmp\E(\[(\Q$driver_bit\E)\])?$/) {
$logger->debug(" unassigning \"" . $statement->lhs . " = " . $statement->rhs . "\"");
$statement->delete();
}
}
$logger->debug(" reassigning \"" . $net_name."[".$msb."] = " . $ip->net->name . "\"");
generate_contassign($mod, $net_name."[".$msb."]", $ip->net->name);
$logger->debug(" reassigning \"" . $net_name."[".$driver_bit."] = " . $ip->net->name . "\"");
generate_contassign($mod, $net_name."[".$driver_bit."]", $ip->net->name);
$driver_is_vector = 1;
} else {
# 2.) Generate intermediate (tmp) net for easier input and output routing
......@@ -697,13 +679,14 @@ sub instrument_net {
} elsif (!$driver_is_vector) {
# If the net is a vector but the driver was driving only one bit
# then we need to drive the originally driven bit only
generate_contassign($mod, $net_name."[".$msb."]", $ip->net->name);
generate_contassign($mod, $net_name."[".$driver_bit."]", $ip->net->name);
} else {
# For drivers of complete busses we need to connect all non-instrumented bits of the tmp net
# additionally to preserve their unmodified values.
# In case of concatenations the index might be different to the one given.
my ($low, $high) = _extract_low_high($net->lsb, $net->msb);
for (my $i = $low ; $i <= $high ; $i++) {
if ($i == $msb) {
if ($i == $driver_bit) {
generate_contassign($mod, $net_name."[".$i."]", $ip->net->name);
} else {
generate_contassign($mod, $net_name."[".$i."]", $net_name_tmp."[".$i."]");
......@@ -718,8 +701,8 @@ sub instrument_net {
$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."]");
$logger->debug(" assigning \"" . $op->net->name . " = " . $net_name_tmp ."[".$driver_bit."]\"");
generate_contassign($mod, $op->net->name, $net_name_tmp."[".$driver_bit."]");
}
$mod->link;
return undef;
......@@ -920,12 +903,67 @@ sub _untie_concatenations {
$concat = $1;
}
my @net_strings;
foreach my $net_string ($concat =~ /(.+)/g) {
foreach my $net_string (split(/,/, $concat)) {
push(@net_strings, $net_string);
}
return \@net_strings;
}
sub _handle_connection_statement {
my $logger = get_logger("");
my ($self, $net, $bit, $connections, $statement, $side) = @_;
my $mod = $net->module;
my $net_name = $net->name;
my $net_strings = $self->_untie_concatenations($side);
$logger->trace("Concatenation comprises " . scalar(@$net_strings) . " concatenated nets: " . join(', ', @$net_strings));
my $off = -1;
my $is_vector = 0;
foreach my $net_string (@$net_strings) {
# Fetch a number of infos about the investigated net(string) to allow for calculation of the correct offsets
my $netstring_elements = $self->_extract_netstring_elements($net_string);
# If $off is still -1 then we have not found a netstring matching our net (and the optional bit) yet
if ($off == -1) {
next if ($netstring_elements->{'net_name'} ne $net_name);
}
# If a single bit was requested we need to verify that it is contained in the respective net
# But even for normal nets the calculations below are necessary for concatenations.
my ($msb, $lsb);
# In case the string allows us to derive a range we have to use that (it might be narrower than that of the net),
# else we need to fall back to the underlying net.
if (defined($netstring_elements->{'msb'})) {
$msb = $netstring_elements->{'msb'};
$lsb = $netstring_elements->{'lsb'};
} else {
$msb = $net->msb;
$lsb = $net->lsb;
}
# If this iteration is the first matching net...
if ($off == -1) {
if (defined($bit)) {
$off = _offset_of_bit_in_range($bit, $msb, $lsb);
next if ($off == -1);
$is_vector = 1 if ($msb ne $lsb);
} else {
$off = 0;
}
$logger->debug(" assign (due to $net_string): \"" . $statement->lhs . "\" = \"" . $statement->rhs . "\"");
push($connections, $statement);
} else {
# If we got a valid offset already we still need to count the ranges of followup nets...
$off += max($msb, $lsb) - min($msb, $lsb);
$is_vector = 1;
}
}
if ($is_vector) {
$connections->[-1]->userdata('fiji_driver_bit' => $off);
}
}
## @method private _get_net_connections ($net,$connection_hashref)
# @brief retrieves connections of a given net
#
......@@ -957,9 +995,6 @@ sub _get_net_connections {
$port_driver_supplied = $driver->isa("Verilog::Netlist::Port");
$assign_driver_supplied = (!$pin_driver_supplied && !$port_driver_supplied);
}
if (!defined($bit)) {
$bit = "";
}
# We need this in regexes below, that do not execute function calls (FFS) thus create an intermediate variable.
my $net_name = $net->name();
......@@ -972,18 +1007,6 @@ sub _get_net_connections {
$connections->{'driven'} = \@driven;
$connections->{'connected'} = \@connected;
# @FIXME what to do with bussed nets
# @FIXME what to do with instantiations like that (concatenated nets):
# input [5:0] p_nbus_byte_controller_c_state ;
# ...
# .p_nbus_byte_controller_c_state (
# {byte_controller_c_state[5],byte_controller_c_state[3]
# ,byte_controller_c_state[2],byte_controller_c_state[1]
# ,byte_controller_c_state[4],byte_controller_c_state[0]
# })
#
# @TODO: can Verilog::Language::split_bus help us here?
if (!$net->isa("Verilog::Netlist::Net")) {
my $msg = "$net is no Verilog::Netlist::Net";
$logger->error($msg);
......@@ -996,38 +1019,19 @@ sub _get_net_connections {
# find drivers and driven nets in continuous assignments
foreach my $statement ($mod->statements) {
# FIXME: handle concatenations
# my $rhs_nets_names = $self->_untie_concatenations($statement->rhs);
# if (@{$rhs_nets_names} gt 1) {
# $logger->debug("RHS comprises " . $rhs_nets_names . " concatenated nets:");
# foreach my $rhs_net_name ($rhs_nets_names) {
# $logger->debug("RHS net: \"" . $rhs_net_name . "\"");
# }
# } else {
# $logger->debug("RHS is " . $rhs_nets_names->[0]);
# }
# Possibly inverted possibly vectored net on the RHS
if ($statement->rhs =~ /^[ \t]*~?[ \t]*\Q$net_name\E(\[(\Q$bit\E)\])?$/) {
$logger->debug(" assign (as driver): \"" . $statement->lhs . "\" = \"" . $statement->rhs . "\"");
push @driven, $statement;
} else {
if ($statement->lhs =~ /^\Q$net_name\E(\[(\Q$bit\E)\])?$/) {
if (defined($bit)) {
if (defined($2) && $bit != $2) {
next;
}
# fine, we match the net and the bit!
}
# continuous assign statement to this net, there can't be another driver
# FIXME: use that knowledge to fail early (if another iteration or loop matches too)?
$logger->debug(" assign (as driven): \"" . $net_name . "\" = \"" . $statement->rhs . "\"");
if (defined($driver) && $assign_driver_supplied && $statement != $driver) {
# return "Driver mismatch: actual \"" . ($statement->rhs) . "\" is not given \"$driver_path\"";
$logger->debug("Driver mismatch: actual \"" . ($statement->rhs) . "\" is not given \"$driver_path\". This can happen for vectored nets if they are instrumented multiple times.");
}
push @drivers, $statement;
}
# Possibly inverted possibly vectored net possibly within a concatenation on RHS and LHS
$logger->trace("looking at \"" . $statement->lhs . "\" = \"" . $statement->rhs . "\"");
# RHS. If we find our net here the statement is driven by it.
$self->_handle_connection_statement($net, $bit, \@driven, $statement, $statement->rhs);
# LHS. If we find out net here the statement is driving it
$self->_handle_connection_statement($net, $bit, \@drivers, $statement, $statement->lhs);
# continuous assign statement to this net, there can't be another driver
# FIXME: use that knowledge to fail early (if another iteration or loop matches too)?
if (exists($drivers[0]) && $assign_driver_supplied && $statement != $drivers[0]) {
# return "Driver mismatch: actual \"" . ($statement->rhs) . "\" is not given \"$driver_path\"";
$logger->debug("Driver mismatch: actual \"" . ($statement->rhs) . "\" is not given \"$driver_path\". This can happen for vectored nets if they are instrumented multiple times.");
}
}
......@@ -1129,49 +1133,32 @@ sub export {
return undef;
}
sub _is_net_range_valid {
my ($net) = @_;
if (defined($net->lsb) && !defined($net->msb)) {
return 0;
}
if (!defined($net->lsb) && defined($net->msb)) {
return 0;
}
return 1;
}
## @method private _offset_of_bit_in_netdescr($bit, $net_descr)
# sub _offset_of_bit_in_netdescr {
# my ($self, $bit, $net_descr) = @_;
# return _offset_of_bit_in_range($bit, $net_descr->{'msb'}, $net_descr->{'lsb'});
# }
## @method private _is_pin_in_range($pin_msb, $pin_lsb, $net)
# @brief Tests if pin range is contained in the range of a net
sub _is_pinrange_in_net {
my ($pin_msb, $pin_lsb, $net) = @_;
return 0 if !_is_net_range_valid($net);
## @method private _offset_of_bit_in_netrange($bit, $net)
# @brief Tests if a bit is contained in the range of a net
sub _offset_of_bit_in_netrange {
my ($bit, $net) = @_;
return _offset_of_bit_in_range($bit, $net->msb, $net->lsb);
}
if (defined($pin_msb)) {
return 0 if (!defined($net->msb));
# MSB out of net range
if ((($pin_msb < $net->lsb) && ($pin_msb < $net->msb)) || # completely below range
(($pin_msb > $net->lsb) && ($pin_msb > $net->msb))) { # completely above range
return 0;
}
} else {
if (defined($net->msb)) {
return 0;
}
}
if (defined($pin_lsb)) {
return 0 if (!defined($net->lsb));
# LSB out of net range
if ((($pin_lsb < $net->lsb) && ($pin_msb < $net->msb)) || # completely below range
(($pin_lsb > $net->lsb) && ($pin_msb > $net->msb))) { # completely above range
return 0;
}
} else {
if (defined($net->lsb)) {
return 0;
}
}
return 1;
## @method private _offset_of_bit_in_range($bit, $range_msb, $range_lsb)
# @brief Tests if bit is contained in the given range
#
# @returnes 0 if any parameter is undefined
# @returns -1 if the bit is not contained, or its offset within the range
sub _offset_of_bit_in_range {
my ($bit, $range_msb, $range_lsb) = @_;
return 0 if (!defined($bit) || !defined($range_msb) || !defined($range_lsb));
return -1 if ((($bit < $range_lsb) && ($bit < $range_msb)) || # completely below range
(($bit > $range_lsb) && ($bit > $range_msb))); # completely above range
return $bit - min($range_msb, $range_lsb);
}
## @method private _extract_netpath_elements($netpath)
......@@ -1352,8 +1339,8 @@ sub get_netdescriptor_from_path {
if (!defined($lsb) || !defined($msb)) {
return sprintf("Net '%s%s%s' in netlist %s has a defined pin range but requested net does not.", $mod->name, HIERSEP, $net_name, $self->{'filename'});
}
if (!_is_pinrange_in_net($msb, $lsb, $net)) {
return sprintf("Pins [%s%s] are not in net '%s%s%s' in netlist %s", $msb, (defined($lsb) ? ":" . $lsb : ""), $mod->name, HIERSEP, $net_name, $self->{'filename'});
if (-1 == _offset_of_bit_in_netrange($msb, $net)) {
return sprintf("Pin [%s] is not in net '%s%s' in netlist %s", $msb, $mod->name, HIERSEP, $net_name, $self->{'filename'});
}
}
......
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