Commit fe32c671 authored by Christian Fibich's avatar Christian Fibich Committed by Stefan Tauner
Browse files

Extended export-to-verilog functionality

parent ca0dd298
...@@ -11,6 +11,7 @@ use warnings; ...@@ -11,6 +11,7 @@ use warnings;
use Log::Log4perl qw(get_logger :easy); use Log::Log4perl qw(get_logger :easy);
use Verilog::Netlist; use Verilog::Netlist;
use Verilog::Language;
use Data::Dumper; use Data::Dumper;
use FIJI::VHDL; use FIJI::VHDL;
...@@ -20,6 +21,25 @@ my $FIJI_PORT_PREFIX = "fiji_"; ...@@ -20,6 +21,25 @@ my $FIJI_PORT_PREFIX = "fiji_";
my $FIJI_PORT_IN_POSTFIX = "_inj_i"; my $FIJI_PORT_IN_POSTFIX = "_inj_i";
my $FIJI_PORT_OUT_POSTFIX = "_ori_o"; my $FIJI_PORT_OUT_POSTFIX = "_ori_o";
my $FIJI_LOGO =<<logo_end;
// FIJIFIJIFIJIFIJIFIJIFIJIFIJIFIJIFIJIFIJI
// FIJIFIJIFIJIFIJIF FIJIFIJIFIJIFIJIFIJI
// FIJIFIJIFIJIFIJ IFIJIFIJIFIJIFIJIFIJI
// FIJIF FI IJIFIJIFIJIFIJI
// FIJIFIJIFI JIFIJIFIJIFIJIFIJI
// FIJIFIJI FIJIFIJIFIJIFIJIFIJI
// FIJIFI I IFI IJIFIJIFIJIFIJIFIJI
// FIJIF JIFIJ JIFIJIFIJIFIJIFIJI
// 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
sub new ($) { sub new ($) {
my ($class) = @_; my ($class) = @_;
...@@ -40,6 +60,10 @@ sub new ($) { ...@@ -40,6 +60,10 @@ sub new ($) {
sub read_file ($) { sub read_file ($) {
my $logger = get_logger(); my $logger = get_logger();
my ($self, $filename) = @_; my ($self, $filename) = @_;
## Netlist synthesized from VHDL could contain SV keywords at this point.
Verilog::Language::language_standard("1364-2001");
eval { eval {
$self->{'nl'}->read_file(filename => $filename); # read Verilog file $self->{'nl'}->read_file(filename => $filename); # read Verilog file
$self->{'nl'}->link(); # Read in any sub-modules $self->{'nl'}->link(); # Read in any sub-modules
...@@ -238,6 +262,12 @@ sub net_add_function ($$$;$) { ...@@ -238,6 +262,12 @@ sub net_add_function ($$$;$) {
$op->net($net); $op->net($net);
$net->module->new_contassign (keyword => "assign",
lhs => $op->name,
rhs => $net->name,
module => $op->module,
netlist => $op->module->netlist);
$self->{'nl'}->link; $self->{'nl'}->link;
return undef; return undef;
...@@ -247,7 +277,7 @@ sub net_add_function ($$$;$) { ...@@ -247,7 +277,7 @@ sub net_add_function ($$$;$) {
# params # params
# net the net to instrument # net the net to instrument
# driver the driver of this net (can be pin, port or contassign) # driver the driver of this net (can be pin, port or contassign)
# fiu_idx the FIU # this external access shall be connected to # fiu_idx the FIU number this external access shall be connected to
sub instrument_net ($$;$) { sub instrument_net ($$;$) {
#FIXME only works with single-bit pins/ports/nets #FIXME only works with single-bit pins/ports/nets
my $logger = get_logger(); my $logger = get_logger();
...@@ -276,7 +306,7 @@ sub instrument_net ($$;$) { ...@@ -276,7 +306,7 @@ sub instrument_net ($$;$) {
} }
} }
} elsif(@{$connections{'drivers'}} == 1) { } elsif(@{$connections{'drivers'}} == 1) {
if(@{$connections{'drivers'}}[0] != $driver) { if(defined $driver && @{$connections{'drivers'}}[0] != $driver) {
$msg = "Driver mismatch on net ".$net->name; $msg = "Driver mismatch on net ".$net->name;
return $msg; return $msg;
} }
...@@ -290,9 +320,9 @@ sub instrument_net ($$;$) { ...@@ -290,9 +320,9 @@ sub instrument_net ($$;$) {
return $msg; return $msg;
} }
my $output_name = (($FIJI_PORT_PREFIX.$net->name.$FIJI_PORT_OUT_POSTFIX) =~ s/__/_/gr); # VHDL signals must not contain multiple subsequent underscores
my $output_name = (($FIJI_PORT_PREFIX.$net->name.$FIJI_PORT_OUT_POSTFIX) =~ s/_+/_/gr);
my $input_name = (($FIJI_PORT_PREFIX.$net->name.$FIJI_PORT_IN_POSTFIX) =~ s/__/_/gr); my $input_name = (($FIJI_PORT_PREFIX.$net->name.$FIJI_PORT_IN_POSTFIX) =~ s/_+/_/gr);
$msg = _check_name_in_hierarchy($net->module,$output_name); $msg = _check_name_in_hierarchy($net->module,$output_name);
return $msg if defined $msg; return $msg if defined $msg;
...@@ -306,6 +336,7 @@ sub instrument_net ($$;$) { ...@@ -306,6 +336,7 @@ sub instrument_net ($$;$) {
$logger->debug($input_name." can be used as fiji connector"); $logger->debug($input_name." can be used as fiji connector");
my $ip = _add_port_to_hierarchy($net->module,$input_name,FIJI::VHDL->FIJI_PORTTYPE_MODIFIED,$fiu_idx); my $ip = _add_port_to_hierarchy($net->module,$input_name,FIJI::VHDL->FIJI_PORTTYPE_MODIFIED,$fiu_idx);
# connecting newly created output to driver
foreach my $connection (@{$connections{'drivers'}}) { foreach my $connection (@{$connections{'drivers'}}) {
my $log = "Original: Connecting "; my $log = "Original: Connecting ";
if(ref($connection) eq "Verilog::Netlist::Pin") { if(ref($connection) eq "Verilog::Netlist::Pin") {
...@@ -324,6 +355,7 @@ sub instrument_net ($$;$) { ...@@ -324,6 +355,7 @@ sub instrument_net ($$;$) {
$self->{'nl'}->link; $self->{'nl'}->link;
} }
# connecting newly created input to driven cells
foreach my $connection (@{$connections{'driven'}}) { foreach my $connection (@{$connections{'driven'}}) {
my $log = "Modified: Connecting "; my $log = "Modified: Connecting ";
if(ref($connection) eq "Verilog::Netlist::Pin") { if(ref($connection) eq "Verilog::Netlist::Pin") {
...@@ -361,11 +393,13 @@ sub _select_driver($$) { ...@@ -361,11 +393,13 @@ sub _select_driver($$) {
my $sel; my $sel;
while(1) { while(1) {
$sel = <STDIN>; $sel = <STDIN>;
if ($sel =~ m/[0-9]+/) { if ($sel =~ m/[0-9]+/ && defined @{$connected}[$sel]) {
last; last;
} elsif ($sel =~ m/[xX]/) { } elsif ($sel =~ m/[xX]/) {
my $msg = "No driver selected for net ".$net->name; my $msg = "No driver selected for net ".$net->name;
return $msg; return $msg;
} else {
print "Invalid driver.\n";
} }
} }
return @{$connected}[$sel]; return @{$connected}[$sel];
...@@ -415,6 +449,10 @@ sub _get_net_connections ($$) { ...@@ -415,6 +449,10 @@ sub _get_net_connections ($$) {
# ,byte_controller_c_state[2],byte_controller_c_state[1] # ,byte_controller_c_state[2],byte_controller_c_state[1]
# ,byte_controller_c_state[4],byte_controller_c_state[0] # ,byte_controller_c_state[4],byte_controller_c_state[0]
# }) # })
#
# TODO: can Verilog::Language::split_bus help us here?
my @netnames_list = (); my @netnames_list = ();
if ((defined $net->msb && defined $net->lsb)) { if ((defined $net->msb && defined $net->lsb)) {
...@@ -472,4 +510,154 @@ sub _get_net_connections ($$) { ...@@ -472,4 +510,154 @@ sub _get_net_connections ($$) {
$connections->{'connected'} = \@connected; $connections->{'connected'} = \@connected;
} }
# Writes the current netlist to a specified file
#
# params
# filename the filename for the resulting Verilog file
# id the FIJI id (included in a comment if defined)
#
sub export ($;$) {
my $logger = get_logger();
my ($self, $filename, $id) = @_;
open(my $fh_nl,">",$filename);
if(!defined $fh_nl) {
my $msg = "Could not open $filename for writing: $!";
return $msg;
}
print $fh_nl "//------------------------------------------------------------------------------\n";
print $fh_nl "// FIJI instrumented netlist\n";
print $fh_nl "//\n";
print $fh_nl $FIJI_LOGO;
print $fh_nl "//\n";
print $fh_nl "//------------------------------------------------------------------------------\n";
print $fh_nl "// Generated ".localtime()." by $0\n";
print $fh_nl "// Netlist ID: 0x".sprintf("%04x",$id)."\n" if defined $id;
print $fh_nl "//------------------------------------------------------------------------------\n\n";
for my $mod ($self->{'nl'}->modules_sorted) {
my $verilog_text = _export_module($mod);
print $fh_nl $verilog_text;
}
close($fh_nl);
return undef;
}
# exports a Verilog::Netlist module to vqm-compatible Verilog.
# returns the Verilog text as a large string.
#
# params
# mod the module to be exported
#
sub _export_module($) {
# FIXME do we need any additional Verilog Syntax (Interface, Modport)?
my $logger = get_logger();
my ($mod) = @_;
$logger->info("Generating verilog text for module ".$mod->name);
my $module_header = "module ".$mod->name." (\n";
my $net_declarations = "";
my $assigns = "";
my $instantiations = "";
my $module_footer = "endmodule /* ".$mod->name." */\n\n";
my @ports = $mod->ports_sorted;
my %defparams;
my @contassigns;
$logger->info("Generating ports for module ".$mod->name);
for (my $i = 0; $i < @ports; $i++) {
my $port = $ports[$i];
$module_header .= " ".$port->name;
my $direction = $port->direction.(($port->direction =~ m/^(in|out)$/) ? "put" : "");
my $comment = "";
if (defined ($port->userdata(FIJI::VHDL->FIJI_USERDATA_PORTTYPE))) {
$comment = " /* FIJI */";
}
$net_declarations .= sprintf(" %-6s %+7s %s;%s\n",$direction,(defined $port->data_type) ? $port->data_type : "",$port->name,$comment);
if($i < @ports - 1) {
$module_header .= ",";
}
$module_header .= $comment."\n";
}
$module_header .= ");\n";
$logger->info("Generating nets for module ".$mod->name);
foreach my $net ($mod->nets) {
if($net->net_type ne "" && $net->decl_type eq "net") {
# net type is empty when this net belongs to a bussed port.
$net_declarations .= sprintf(" %-6s %+7s %s;\n",$net->net_type,(defined $net->data_type) ? $net->data_type : "",$net->name);
}
}
my $assign_indent = 0;
my $defparam_indent = 0;
$logger->info("Generating assigns for module ".$mod->name);
# need to separate assigns from defparams - see later...
foreach my $statement ($mod->statements_sorted) {
if(ref($statement) eq "Verilog::Netlist::ContAssign") {
push @contassigns, $statement;
$assign_indent = length $statement->lhs if (length $statement->lhs > $assign_indent);
} elsif (ref($statement) eq "Verilog::Netlist::Defparam") {
# Build a hash of defparams, with the key being the cell identifier.
# FIXME this regex might break with other synthesis tool because of other delimiters (?)
my $k = (split(/\./,$statement->lhs))[0];
push @{$defparams{$k}}, $statement;
#$defparam_indent = length $statement->lhs if (length $statement->lhs > $defparam_indent);
}
}
foreach my $assign (@contassigns) {
$assigns .= sprintf(" assign %-${assign_indent}s = %s;\n",$assign->lhs,$assign->rhs);
}
$logger->info("Generating instantiations for module ".$mod->name);
# FIXME this is somewhat slow for large modules. Other solutions?
foreach my $cell ($mod->cells_sorted) {
my $cellname = $cell->name;
$instantiations .= " ".$cell->submodname." ".$cell->name." (\n";
my @pins = $cell->pins_sorted;
for (my $i = 0; $i < @pins; $i++) {
my $pin = $pins[$i];
$instantiations .= " .".$pin->name." (".$pin->netname.")";
if ($i < @pins - 1) {
$instantiations .= ",\n";
}
}
$instantiations .= "\n );\n";
# NOTE this is needed because Quartus requires the defparams setting LUT mask etc.
# to be right after the cell instantiation.
foreach my $defparam (@{$defparams{$cellname}}) {
$instantiations .= sprintf(" defparam %-${defparam_indent}s = %s;\n",$defparam->lhs,$defparam->rhs);
}
$instantiations .= "\n";
# not needed anymore
delete $defparams{$cellname};
}
foreach my $k (keys (%defparams)) {
$logger->warn("Defparam ".$defparams{$k}->lhs." could not be matched with a cell");
$instantiations .= sprintf(" defparam %-${defparam_indent}s = %s;\n",$defparams{$k}->lhs,$defparams{$k}->rhs);
}
$instantiations .= "\n";
return $module_header."\n".$net_declarations."\n".$assigns."\n".$instantiations.$module_footer;
}
1; 1;
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