#!/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' }, ] }, ] } }