Files
axipg/scripts/gen_axi_intf.pl

384 lines
20 KiB
Perl
Executable File

#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
use Getopt::Long;
my $app = {};
$app->{'cfg'}->{'gen_intf'} = 0;
$app->{'cfg'}->{'gen_trace_wa'} = 0;
GetOptions("gen_intf" => \$app->{'cfg'}->{'gen_intf'},
"gen_trace_wa" => \$app->{'cfg'}->{'gen_trace_wa'});
get_axi_intf_data($app);
augment_intf_data($app);
#print Dumper($app->{'intf_data'}); # Debugging output
if ($app->{'cfg'}->{'gen_intf'}) {
gen_intf($app);
}
if ($app->{'cfg'}->{'gen_trace_wa'}) {
show_trace_interface_driver($app);
}
# -----------------
# Subroutines
# -----------------
sub gen_intf {
my ($app) = @_;
print "// Generated AXI Interface\n";
print "// This file is automatically generated by $0\n";
print "// Do not edit this file directly.\n";
print "//\n";
print "// Interface Name: $app->{'intf_data'}->{'name'}\n";
print "interface axi_intf #(\n";
my @plines = get_param_lines($app);
print join(",\n", @plines);
print "\n)\n";
my @eplines = get_ext_ports_lines($app);
print "\n(\n";
print join(",\n", @eplines);
print "\n);\n";
show_interface($app);
show_modport($app, 'Manager');
show_modport($app, 'Subordinate');
print "endinterface // axi_intf\n";
}
# -----------------
sub get_param_lines {
my ($app) = @_;
my @plines;
foreach my $p (sort { $a cmp $b } keys %{$app->{'intf_data'}->{'params'}}) {
my $param_value = $app->{'intf_data'}->{'params'}->{$p}->{'d'};
push(@plines, sprintf(" parameter %$app->{'intf_data'}->{'info'}->{'param_width'}s = %d",
$p, $param_value));
}
return @plines;
}
# -----------------
sub get_ext_ports_lines {
my ($app) = @_;
my @eplines;
foreach my $p (sort { $a cmp $b } @{$app->{'intf_data'}->{'ext_sigs'}}) {
push(@eplines, sprintf(" input %s",
$p->{'n'}));
}
return @eplines;
}
# -----------------
sub show_interface {
my ($app) = @_;
foreach my $section (@{$app->{'intf_data'}->{'sections'}}) {
print "\n";
print " // $section->{'name'}\n";
# Display signals
foreach my $signal (@{$section->{'signals'}}) {
if ($signal->{'s'} eq 'External') {
printf " // External signal: %-$app->{'intf_data'}->{'info'}->{'sig_width'}s // $signal->{'d'} [$signal->{'s'}]\n",
"$signal->{'n'};";
} else {
printf " logic %$app->{'intf_data'}->{'info'}->{'dim_width'}s %-$app->{'intf_data'}->{'info'}->{'sig_width'}s // $signal->{'d'} [$signal->{'s'}]\n",
get_dimension($app, $signal->{'w'}), "$signal->{'n'};";
}
}
}
}
# -----------------
sub show_trace_interface_driver {
my ($app) = @_;
print qq{`ifdef VERI_TRACE_EN
// --------------------------------------------------
// Work around Verilator's lack of support for virutal interface tracing:
// Issue #5044: Wires driven through virtual interface traced improperly
// https://github.com/verilator/verilator/issues/5044
`AXI_INTF t_if (.ACLK(clk), .ARESETn(rst_n));
};
foreach my $section (@{$app->{'intf_data'}->{'sections'}}) {
print " // $section->{'name'}\n";
# Display signals
foreach my $signal (@{$section->{'signals'}}) {
my $line = "";
if ($signal->{'s'} eq 'External') {
$line = sprintf " // External signal: %-$app->{'intf_data'}->{'info'}->{'sig_width'}s // $signal->{'d'} [$signal->{'s'}]",
"$signal->{'n'};";
} else {
$line = sprintf " always @(a_if.%-$app->{'intf_data'}->{'info'}->{'sig_width'}s t_if.%-$app->{'intf_data'}->{'info'}->{'sig_width'}s <= a_if.%-$app->{'intf_data'}->{'info'}->{'sig_width'}s",
"$signal->{'n'})", $signal->{'n'}, "$signal->{'n'};";
}
$line =~ s/\s+$//;
print "$line\n";
}
}
print qq{
`endif
};
}
# -----------------
sub show_modport{
my ($app, $role) = @_;
my $last = 0;
print "\n";
print " // Modport for $role role\n";
print " modport " . uc($role) . " (\n";
my $num_sections = scalar @{$app->{'intf_data'}->{'sections'}};
my $section_count = 0;
foreach my $section (@{$app->{'intf_data'}->{'sections'}}) {
print "\n";
print " // $section->{'name'}\n";
$section_count++;
# Display signals
my $num_signals = scalar @{$section->{'signals'}};
my $signal_count = 0;
foreach my $signal (@{$section->{'signals'}}) {
# When role is Manager, signals with 'Manager' source are output, others are input
# When role is Subordinate, signals with 'Subordinate' source are output, others are input
my $direction = ($role eq 'Global') ? 'input' : ($role eq $signal->{'s'}) ? 'output' : 'input';
$signal_count++;
$last = 1 if ($num_signals == $signal_count && $num_sections == $section_count);
printf " %-8s $signal->{'n'}" . (($last == 1) ? "" : ","). "\n", $direction;
}
}
print " );\n";
}
# -----------------
sub augment_intf_data {
my ($app) = @_;
# Pretty print widths
$app->{'intf_data'}->{'info'}->{'param_width'} = 0;
$app->{'intf_data'}->{'info'}->{'dim_width'} = 0;
$app->{'intf_data'}->{'info'}->{'sig_width'} = 0;
# Capture parameters from signals
$app->{'intf_data'}->{'params'} = {};
# Capture external signals
$app->{'intf_data'}->{'ext_sigs'} = [];
foreach my $section (@{$app->{'intf_data'}->{'sections'}}) {
foreach my $signal (@{$section->{'signals'}}) {
my $width = $signal->{'w'};
if ($width !~ /^\d+$/) {
unless (exists $app->{'intf_data'}->{'params'}->{$width}) {
$app->{'intf_data'}->{'params'}->{$width} = {
'd' => 1, # Default value for the parameter
};
$app->{'intf_data'}->{'info'}->{'param_width'} = length($width)
if (length($width) > $app->{'intf_data'}->{'info'}->{'param_width'});
}
}
if ($signal->{'s'} eq 'External') {
push(@{$app->{'intf_data'}->{'ext_sigs'}}, $signal);
}
}
}
# Find the maximum width of the parameters
foreach my $section (@{$app->{'intf_data'}->{'sections'}}) {
foreach my $signal (@{$section->{'signals'}}) {
$app->{'intf_data'}->{'info'}->{'dim_width'} = length(get_dimension($app, $signal->{'w'}))
if (length(get_dimension($app, $signal->{'w'})) > $app->{'intf_data'}->{'info'}->{'dim_width'});
$app->{'intf_data'}->{'info'}->{'sig_width'} = length($signal->{'n'})
if (length($signal->{'n'}) > $app->{'intf_data'}->{'info'}->{'sig_width'});
}
$app->{'intf_data'}->{'info'}->{'sig_width'} += 1; # Account for the semi-colon
}
}
# -----------------
sub get_dimension {
my ($app, $width) = @_;
if ($width =~ /^\d+$/) {
if ($width == 1) {
return ""; # Single bit width
} else {
return "[" . ($width-1) . ":0]"; # Specific width
}
} else {
return "[$width-1:0]"; # Width parameter
}
}
# -----------------
sub get_axi_intf_data {
my ($app) = @_;
$app->{'intf_data'} = {
'name' => "AXI Signal List",
'sections' =>
[
{
'name' => 'A2.4.1 Clock and reset signals',
'signals' => [
{ 'n' => 'ACLK', 'w' => '1', 's' => 'External', 'd' => 'External Global clock signal'},
{ 'n' => 'ARESETn', 'w' => '1', 's' => 'External', 'd' => 'External Global reset signal'}
]
},
{
'name' => 'A2.1.1 Write request channel',
'signals' => [
# n: name, w: width, s: source, d: description
{'n' =>'AWVALID','w' =>'1','s' =>'Manager','d' =>'Valid indicator' },
{'n' =>'AWREADY','w' =>'1','s' =>'Subordinate','d' =>'Ready indicator' },
{'n' =>'AWID','w' =>'ID_W_WIDTH','s' =>'Manager','d' =>'Transaction identifier for the write channels' },
{'n' =>'AWADDR','w' =>'ADDR_WIDTH','s' =>'Manager','d' =>'Transaction address' },
{'n' =>'AWREGION','w' =>'4','s' =>'Manager','d' =>'Region identifier' },
{'n' =>'AWLEN','w' =>'8','s' =>'Manager','d' =>'Transaction length' },
{'n' =>'AWSIZE','w' =>'3','s' =>'Manager','d' =>'Transaction size' },
{'n' =>'AWBURST','w' =>'2','s' =>'Manager','d' =>'Burst attribute' },
{'n' =>'AWLOCK','w' =>'1','s' =>'Manager','d' =>'Exclusive access indicator' },
{'n' =>'AWCACHE','w' =>'4','s' =>'Manager','d' =>'Memory attributes' },
{'n' =>'AWPROT','w' =>'3','s' =>'Manager','d' =>'Access attributes' },
{'n' =>'AWNSE','w' =>'1','s' =>'Manager','d' =>'Non-secure extension bit for RME' },
{'n' =>'AWQOS','w' =>'4','s' =>'Manager','d' =>'QoS identifier' },
{'n' =>'AWUSER','w' =>'USER_REQ_WIDTH','s' =>'Manager','d' =>'User-defined extension to a request' },
{'n' =>'AWDOMAIN','w' =>'2','s' =>'Manager','d' =>'Shareability domain of a request' },
{'n' =>'AWSNOOP','w' =>'AWSNOOP_WIDTH','s' =>'Manager','d' =>'Write request opcode' },
{'n' =>'AWSTASHNID','w' =>'11','s' =>'Manager','d' =>'Stash Node ID' },
{'n' =>'AWSTASHNIDEN','w' =>'1','s' =>'Manager','d' =>'Stash Node ID enable' },
{'n' =>'AWSTASHLPID','w' =>'5','s' =>'Manager','d' =>'Stash Logical Processor ID' },
{'n' =>'AWSTASHLPIDEN','w' =>'1','s' =>'Manager','d' =>'Stash Logical Processor ID enable' },
{'n' =>'AWTRACE','w' =>'1','s' =>'Manager','d' =>'Trace signal' },
{'n' =>'AWLOOP','w' =>'LOOP_W_WIDTH','s' =>'Manager','d' =>'Loopback signals on the write channels' },
{'n' =>'AWMMUVALID','w' =>'1','s' =>'Manager','d' =>'MMU signal qualifier' },
{'n' =>'AWMMUSECSID','w' =>'SECSID_WIDTH','s' =>'Manager','d' =>'Secure Stream ID' },
{'n' =>'AWMMUSID','w' =>'SID_WIDTH','s' =>'Manager','d' =>'StreamID' },
{'n' =>'AWMMUSSIDV','w' =>'1','s' =>'Manager','d' =>'SubstreamID valid' },
{'n' =>'AWMMUSSID','w' =>'SSID_WIDTH','s' =>'Manager','d' =>'SubstreamID' },
{'n' =>'AWMMUATST','w' =>'1','s' =>'Manager','d' =>'Address translated indicator' },
{'n' =>'AWMMUFLOW','w' =>'2','s' =>'Manager','d' =>'SMMU flow type' },
{'n' =>'AWPBHA','w' =>'4','s' =>'Manager','d' =>'Page-based Hardware Attributes' },
{'n' =>'AWNSAID','w' =>'4','s' =>'Manager','d' =>'Non-secure Access ID' },
{'n' =>'AWSUBSYSID','w' =>'SUBSYSID_WIDTH','s' =>'Manager','d' =>'Subsystem ID' },
{'n' =>'AWATOP','w' =>'6','s' =>'Manager','d' =>'Atomic transaction opcode' },
{'n' =>'AWMPAM','w' =>'MPAM_WIDTH','s' =>'Manager','d' =>'MPAM information with a request' },
{'n' =>'AWIDUNQ','w' =>'1','s' =>'Manager','d' =>'Unique ID indicator' },
{'n' =>'AWCMO','w' =>'AWCMO_WIDTH','s' =>'Manager','d' =>'CMO type' },
{'n' =>'AWTAGOP','w' =>'2','s' =>'Manager','d' =>'Memory Tag operation for write requests' },
{'n' =>'AWMECID','w' =>'MECID_WIDTH','s' =>'Manager','d' =>'Memory Encryption Context identifier' },
]
},
{
'name' => 'A2.1.1 Write request channel',
'signals' => [
{ 'n' => 'WVALID', 'w' => '1', 's' => 'Manager', 'd' => 'Valid indicator' },
{ 'n' => 'WREADY', 'w' => '1', 's' => 'Subordinate', 'd' => 'Ready indicator' },
{ 'n' => 'WDATA', 'w' => 'DATA_WIDTH', 's' => 'Manager', 'd' => 'Write data' },
{ 'n' => 'WSTRB', 'w' => 'DATA_WIDTH_DIV_8', 's' => 'Manager', 'd' => 'Write data strobes' },
{ 'n' => 'WTAG', 'w' => 'CEIL_DATA_WIDTH_DIV_128_TMS_4', 's' => 'Manager', 'd' => 'Memory Tag' },
{ 'n' => 'WTAGUPDATE', 'w' => 'CEIL_DATA_WIDTH_DIV_128', 's' => 'Manager', 'd' => 'Memory Tag update' },
{ 'n' => 'WLAST', 'w' => '1', 's' => 'Manager', 'd' => 'Last write data' },
{ 'n' => 'WUSER', 'w' => 'USER_DATA_WIDTH', 's' => 'Manager', 'd' => 'User-defined extension to write data' },
{ 'n' => 'WPOISON', 'w' => 'CEIL_DATA_WIDTH_DIV_64', 's' => 'Manager', 'd' => 'Poison indicator' },
{ 'n' => 'WTRACE', 'w' => '1', 's' => 'Manager', 'd' => 'Trace signal' },
]
},
{
'name' => 'A2.1.3 Write response channel',
'signals' => [
{ 'n' => 'BVALID', 'w' => '1', 's' => 'Subordinate', 'd' => 'Valid indicator' },
{ 'n' => 'BREADY', 'w' => '1', 's' => 'Manager', 'd' => 'Ready indicator' },
{ 'n' => 'BID', 'w' => 'ID_W_WIDTH', 's' => 'Subordinate', 'd' => 'Transaction identifier for the write channels' },
{ 'n' => 'BIDUNQ', 'w' => '1', 's' => 'Subordinate', 'd' => 'Unique ID indicator' },
{ 'n' => 'BRESP', 'w' => 'BRESP_WIDTH', 's' => 'Subordinate', 'd' => 'Write response code' },
{ 'n' => 'BCOMP', 'w' => '1', 's' => 'Subordinate', 'd' => 'Completion response indicator' },
{ 'n' => 'BPERSIST', 'w' => '1', 's' => 'Subordinate', 'd' => "Persist response" },
{ 'n' => 'BTAGMATCH', 'w' => '2', 's' => 'Subordinate', 'd' => 'Memory Tag Match response' },
{ 'n' => 'BUSER', 'w' => 'USER_RESP_WIDTH', 's' => 'Subordinate', 'd' => 'User-defined extension to a write response' },
{ 'n' => 'BTRACE', 'w' => '1', 's' => 'Subordinate', 'd' => 'Trace signal' },
{ 'n' => 'BLOOP', 'w' => 'LOOP_W_WIDTH', 's' => 'Subordinate', 'd' => 'Loopback signals on the write channels' },
{ 'n' => 'BBUSY', 'w' => '2', 's' => 'Subordinate', 'd' => "Busy indicator" },
]
},
{
'name' => 'A2.2.1 Read request channel',
'signals' => [
{ 'n' => 'ARVALID', 'w' => '1', 's' => 'Manager', 'd' => 'Valid indicator' },
{ 'n' => 'ARREADY', 'w' => '1', 's' => 'Subordinate', 'd' => 'Ready indicator' },
{ 'n' => 'ARID', 'w' => 'ID_R_WIDTH', 's' => 'Manager', 'd' => 'Transaction identifier for the read channels' },
{ 'n' => 'ARADDR', 'w' => 'ADDR_WIDTH', 's' => 'Manager', 'd' => 'Transaction address' },
{ 'n' => 'ARREGION', 'w' => '4', 's' => 'Manager', 'd' => 'Region identifier' },
{ 'n' => 'ARLEN', 'w' => '8', 's' => 'Manager', 'd'=> "Transaction length" },
{ 'n' => 'ARSIZE', 'w' => '3', 's' => 'Manager', 'd' => 'Transaction size' },
{ 'n' => 'ARBURST', 'w' => '2', 's' => 'Manager', 'd' => 'Burst attribute' },
{ 'n' => 'ARLOCK', 'w' => '1', 's' => 'Manager', 'd' => 'Exclusive access indicator' },
{ 'n' => 'ARCACHE', 'w' => '4', 's' => 'Manager', 'd' => 'Memory attributes' },
{ 'n' => 'ARPROT', 'w' => '3', 's' => 'Manager', 'd' => 'Access attributes' },
{ 'n' => 'ARNSE', 'w' => '1', 's' => 'Manager', 'd' => 'Non-secure extension bit for RME' },
{ 'n' => 'ARQOS', 'w' => '4', 's' => 'Manager', 'd' => 'QoS identifier' },
{ 'n' => 'ARUSER', 'w' => 'USER_REQ_WIDTH', 's' => 'Manager', 'd' => 'User-defined extension to a request' },
{ 'n' => 'ARDOMAIN', 'w' => '2', 's' => 'Manager', 'd' => 'Shareability domain of a request' },
{ 'n' => 'ARSNOOP', 'w' => 'ARSNOOP_WIDTH', 's' => 'Manager', 'd' => 'Read request opcode' },
{ 'n' => 'ARTRACE', 'w' => '1', 's' => 'Manager', 'd' => 'Trace signal' },
{ 'n' => 'ARLOOP', 'w' => 'LOOP_R_WIDTH', 's' => 'Manager', 'd' => 'Loopback signals on the read channels' },
{ 'n' => 'ARMMUVALID', 'w' => '1', 's' => 'Manager', 'd' => 'MMU signal qualifier' },
{ 'n' => 'ARMMUSECSID', 'w' => 'SECSID_WIDTH', 's' => 'Manager', 'd' => 'Secure Stream ID' },
{ 'n' => 'ARMMUSID', 'w' => 'SID_WIDTH', 's' => 'Manager', 'd' => 'StreamID' },
{ 'n' => 'ARMMUSSIDV', 'w' => '1', 's' => 'Manager', 'd' => 'SubstreamID valid' },
{ 'n' => 'ARMMUSSID', 'w' => 'SSID_WIDTH', 's' => 'Manager', 'd' => 'SubstreamID' },
{ 'n' => 'ARMMUATST', 'w' => '1', 's' => 'Manager', 'd' => 'Address translated indicator' },
{ 'n' => 'ARMMUFLOW', 'w' => '2', 's' => 'Manager', 'd' => 'SMMU flow type' },
{ 'n' => 'ARPBHA', 'w' => '4', 's' => 'Manager', 'd' => 'Page-based Hardware Attributes' },
{ 'n' => 'ARNSAID', 'w' => '4', 's' => 'Manager', 'd' => 'Non-secure Access ID' },
{ 'n' => 'ARSUBSYSID', 'w' => 'SUBSYSID_WIDTH', 's' => 'Manager', 'd' => 'Subsystem ID' },
{ 'n' => 'ARCHUNKEN','w' => '1','s' => 'Manager','d' => 'Read data chunking enable'},
{ 'n' => 'ARIDUNQ','w' => '1','s' => 'Manager','d' => 'Unique ID indicator'},
{ 'n' => 'ARTAGOP','w' => '2','s' => 'Manager','d' => 'Memory Tag operation for read requests'},
{ 'n' => 'ARMECID','w' => 'MECID_WIDTH','s' => 'Manager','d' => 'Memory Encryption Context identifier'},
]
},
{
'name' => 'A2.2.2 Read data channel',
'signals' => [
{ 'n' => 'RVALID', 'w' => '1', 's' => 'Subordinate', 'd' => 'Valid indicator' },
{ 'n' => 'RREADY', 'w' => '1', 's' => 'Manager', 'd' => 'Ready indicator' },
{ 'n' => 'RID', 'w' => 'ID_R_WIDTH', 's' => 'Subordinate', 'd' => 'Transaction identifier for the read channels' },
{ 'n' => 'RIDUNQ', 'w' => '1', 's' => 'Subordinate', 'd' => 'Unique ID indicator' },
{ 'n' => 'RDATA', 'w' => 'DATA_WIDTH', 's' => 'Subordinate', 'd' => 'Read data' },
{ 'n' => 'RTAG', 'w' => 'CEIL_DATA_WIDTH_DIV_128_TMS_4', 's' => 'Subordinate', 'd' => 'Memory Tag' },
{ 'n' => 'RRESP', 'w' => 'RRESP_WIDTH', 's' => 'Subordinate', 'd' => 'Read response' },
{ 'n' => 'RLAST', 'w' => '1', 's' => 'Subordinate', 'd' => 'Last read data' },
{ 'n' => 'RUSER', 'w' => 'SUM_USER_DATA_WIDTH_USER_RESP_WIDTH', 's' => 'Subordinate', 'd' => 'User-defined extension to read data and response' },
{ 'n' => 'RPOISON', 'w' => 'CEIL_DATA_WIDTH_DIV_64', 's' => 'Subordinate', 'd' => 'Poison indicator' },
{ 'n' => 'RTRACE', 'w' => '1', 's' => 'Subordinate', 'd' => 'Trace signal' },
{ 'n' => 'RLOOP', 'w' => 'LOOP_R_WIDTH', 's' => 'Subordinate', 'd' => 'Loopback signals on the read channels' },
{ 'n' => 'RCHUNKV','w' => '1', 's' => 'Subordinate', 'd' => 'Read data chunking valid' },
{ 'n' => 'RCHUNKNUM', 'w' => 'RCHUNKNUM_WIDTH', 's' => 'Subordinate', 'd' => 'Read data chunk number' },
{ 'n' => 'RCHUNKSTRB', 'w' => 'RCHUNKSTRB_WIDTH', 's' => 'Subordinate', 'd' => 'Read data chunk strobe' },
{ 'n' => 'RBUSY', 'w' => '2', 's' => 'Subordinate', 'd' => 'Busy indicator' },
]
},
]
}
}