###
# Microtronix MPFE Qsys Builder Perl Script
# Build : 103/2021.01.20
###

#open FILE_DEBUG, ">DEBUG.txt" ;
#print FILE_DEBUG "Start of log:\n";


my $MODE_DISABLED = 0;
my $MODE_BURST = 1;
my $MODE_RANDOM = 2;

my $PRIORITY_LOWEST  = 1;
my $PRIORITY_LOW     = 2;
my $PRIORITY_MEDIUM  = 3;
my $PRIORITY_HIGH    = 4;
my $PRIORITY_HIGHEST = 5;

my @port_mode;
my @port_buffer_size;
my @port_max_burst;
my @port_data_width_shift;
my @port_disable_cache;
my @port_include_be;
my @port_priority;
my @port_data_width;
my @port_addr_width;
my @port_burst_width;
my @port_tag_mode;

# Read Command Line Arguments
my $filename = shift(@ARGV);
my $directory = shift(@ARGV);
my $top_level_module_name = shift(@ARGV);
my $device = shift(@ARGV);

my $am_data_width = shift(@ARGV);
my $am_addr_width = shift(@ARGV);
my $burst_width_master_bits = shift(@ARGV);
my $lam_addr_width = shift(@ARGV);
my $use_control_interface = 0 ;
my $temp = shift(@ARGV);
if( $temp eq "true" ) {
	$use_control_interface = 1 ;
	}
my $port_first = shift(@ARGV);
my $port_last = shift(@ARGV);
my $am_pending_reads = shift(@ARGV);

my $scm_lowest = shift(@ARGV);
my $scp_lowest = shift(@ARGV);
my $scm_low = shift(@ARGV);
my $scp_low = shift(@ARGV);
my $scm_medium = shift(@ARGV);
my $scp_medium = shift(@ARGV);
my $scm_high = shift(@ARGV);
my $scp_high = shift(@ARGV);

for( my $i=$port_first; $i <= $port_last; $i++ ) {
	$port_mode{$i} = shift(@ARGV);
	$port_buffer_size{$i} = shift(@ARGV);
	$port_max_burst{$i} = shift(@ARGV);
	$port_disable_cache{$i} = 0 ;	
	$temp = shift(@ARGV);
	if( $temp eq "true" ) {
		$port_disable_cache{$i} = 1 ;
		}
	$temp = shift(@ARGV);
	if( $temp eq "Low" ) {
		$port_priority{$i} = $PRIORITY_LOW ;
	} elsif ( $temp eq "High" ) {
		$port_priority{$i} = $PRIORITY_HIGH ;		
	} elsif ( $temp eq "Lowest" ) {
		$port_priority{$i} = $PRIORITY_LOWEST ;		
	} elsif ( $temp eq "Highest" ) {
		$port_priority{$i} = $PRIORITY_HIGHEST ;		
	} else {
		$port_priority{$i} = $PRIORITY_MEDIUM ;
	}	
	$port_data_width_shift{$i} = shift(@ARGV);
   $port_data_width{$i} = shift(@ARGV);
	$port_addr_width{$i} = shift(@ARGV);
	$port_burst_width{$i} = shift(@ARGV);
	$temp = shift(@ARGV);
	if( $temp eq "true" ) {
		$port_include_be{$i} = 1 ;
	} else {
		$port_include_be{$i} = 0 ;
	}	
	$port_tag_mode{$i} = shift(@ARGV);	
}

# Subroutine 
# Check what ports we have
# uses $port_mode, $MODE_RANDOM, $MODE_BURST
sub PortInfo {
	my( $first, $last, $control_port ) = @_;
	my $i, $port_last_active, $random;
	my $flush_interface = 0 ;

	$port_last_active = $port_first;
	$random = 0;
	for( $i=$first; $i<=$last; $i++) {
		if( $port_mode{$i} == $MODE_RANDOM || $port_mode{$i} == $MODE_BURST ) {
		$port_last_active = $i;
		}
	if( $port_mode{$i} == $MODE_RANDOM ) {
		$random = 1;
		}
	}
	if( $control_port && $random ) {
		$flush_interface = 1 ;
		}
	$num_ports = $port_last_active - $first + 1 ;	
	return ($port_last_active, $flush_interface, $num_ports);
}


# Subroutine 
# Generate the port priority signal text
sub GetPortPriorityMasks {
	my( $first, $last ) = @_;
	my $i, $priority_lowest_text, $priority_low_text, $priority_medium_text, $priority_high_text, $priority_highest_text;

	$ports = $last-$first+1;
	$priority_lowest_text = $ports."'b";	
	$priority_low_text = $ports."'b";
	$priority_medium_text = $ports."'b";
	$priority_high_text = $ports."'b";
	$priority_highest_text = $ports."'b";	
	for( $i=$last; $i>=$first; $i--) {
		if( $port_priority{$i} == $PRIORITY_LOWEST ) {
			$priority_lowest_text .= "1";
		} else {
			$priority_lowest_text .= "0";
		}			
		if( $port_priority{$i} == $PRIORITY_LOW ) {
			$priority_low_text .= "1";
		} else {
			$priority_low_text .= "0";
		}			
		if( $port_priority{$i} == $PRIORITY_HIGH ) {
			$priority_high_text .= "1";
		} else {
			$priority_high_text .= "0";
		}	
		if( $port_priority{$i} == $PRIORITY_HIGHEST ) {
			$priority_highest_text .= "1";
		} else {
			$priority_highest_text .= "0";
		}			
		if( $port_priority{$i} == $PRIORITY_MEDIUM ) {
			$priority_medium_text .= "1";
		} else {
			$priority_medium_text .= "0";
		}	
	}		
	return ($priority_lowest_text, $priority_low_text, $priority_medium_text, $priority_high_text, $priority_highest_text);
}


# Subroutine
# replace & with spaces such that the location where & was found is at least position from start of line
sub ReplaceTabTags {
	my( $InputText, $position) = @_;
	my $string;
	my @lines = split /\n/, $InputText;
	my $pos, $pad ;
	$string = "";
	foreach my $line (@lines) {
		$pos = index( $line, "&" );
		$pad = "" ;
		if( $pos < $position ) {
			$pad = " " x ($position - $pos);
			}
		$line =~ s/&/$pad/g;
		$string .= $line . "\n";
		}
		return $string;		
}		

# Subroutine
# Return a string with the signals required for the avalon master in the top level entity. 
sub TopSignals_AvalonMaster {
	my ($addr_width, $burst_width, $data_width) = @_;
	my $string;
	$string .=  qq
{   
   // Avalon Master Interface
   input      &am_rst,
   input	     &am_clk,
   output     &am_rd,	
   output     &am_wr,
   output [AA:0] &am_addr,
   output [BB:0] &am_burstcount,
   output [CC:0] &am_be,
   input	 [DDD:0] &am_data_in,
   output [DDD:0] &am_data_out,
   input	     &am_waitreq,
   input	     &am_datavalid,
   output     &am_burststart		
);
};
	my $addr_max = $addr_width - 1 ;
	my $burst_max = $burst_width - 1 ;
	my $be_max = ($data_width / 8) - 1 ;
	my $data_max = $data_width - 1;
	$string =~ s/DDD/$data_max/g;
	$string =~ s/AA/$addr_max/g;	
	$string =~ s/BB/$burst_max/g;	
	$string =~ s/CC/$be_max/g;	
	$string = ReplaceTabTags( $string, 22);
	return $string;
}


# Subroutine
# Return a string with the signals required for the control port in the top level entity. 
sub TopSignals_ControlPort {
	my $string;
	$string = qq 
{   // Control Port
   input      &as_ctrl_clk,
   input      &as_ctrl_rst,
   output     &as_ctrl_waitreq,
   input      &as_ctrl_rd,
   input      &as_ctrl_wr,
   input	[31:0] &as_ctrl_data_in,
   output[31:0] &as_ctrl_data_out,
   input  [5:0] &as_ctrl_addr,
};
	$string = ReplaceTabTags( $string, 22);
	return $string;
}



# Subroutine
# Return a string with the signals required for a port in the top level entity. 
# This subroutine supports both random and burst ports.
sub TopSignals_Port {
	my ($port_id, $data_width, $addr_width, $port_mode, $burst_width, $include_be) = @_;
	if( ($port_mode != $MODE_BURST) && ($port_mode != $MODE_RANDOM) ) {
		return ;
		}
	my $string	;
	if( $port_mode == $MODE_BURST ) {
		$string = qq 
{   // port $port_id (Burst)}
	} else {
		$string = qq 
{   // port $port_id (Random)}
	}
	$string .= qq
{
   input   &as_port_XX_rst,
   input   &as_port_XX_clk,
   input   &as_port_XX_cs,
   input   &as_port_XX_rd,
   input   &as_port_XX_wr,
   input  [AA:0] &as_port_XX_addr,};
   if( $port_mode == $MODE_RANDOM or $include_be ) {
	$string .= qq
{  
   input  [BB:0] &as_port_XX_be,};
	}
	$string .= qq
{	
   input  [DDD:0] &as_port_XX_data_in,
   output [DDD:0] &as_port_XX_data_out,
   output         &as_port_XX_waitreq,
};
	my $data_max = $data_width - 1 ;
	my $addr_max = $addr_width - 1 ;
	my $be_max = ($data_width / 8) - 1 ;
	$string =~ s/DDD/$data_max/g;
	$string =~ s/AA/$addr_max/g;	
	$string =~ s/BB/$be_max/g;	
	if( $port_mode == $MODE_BURST ) {
		$string .= qq
{   input  [BB:0] &as_port_XX_burstcount,
   output      &as_port_XX_datavalid,
};
}
	$string =~ s/XX/$port_id/g;
	my $burst_max = $burst_width -1;
	$string =~ s/BB/$burst_max/g;	
	$string = ReplaceTabTags( $string, 22 );
	$string .= "\n";
	return $string;
}



# Subroutine
# Return a string with the port interconnect signals for a port. 
# This subroutine supports both random and burst ports.
sub TopSignals_PortInterconnect {
	my ($port_id, $data_width, $addr_width, $port_mode, $burst_width, $use_control_port, $support_flush) = @_;
	if( ($port_mode != $MODE_BURST) && ($port_mode != $MODE_RANDOM) ) {
		return ;
		}
	my $string	;
	if( $port_mode == $MODE_BURST ) {
		$string = qq 
{// port $port_id interconnect signals (Burst)}
	} else {
		$string = qq 
{// port $port_id interconnect signals (Random)}
	}
	$string .= qq
{
logic [AA:0] &lam_XX_addr;	
logic      &lam_XX_dir;
logic [BB:0] &lam_XX_length;
logic      &lam_XX_read;
logic      &lam_XX_write;
logic      &lam_XX_request;
logic      &lam_XX_granted;
logic      &lam_XX_end;
logic [DDD:0] &lam_XX_data_out;
logic [CC:0]  &lam_XX_data_mask;
};
	if( $support_flush && $port_mode == $MODE_RANDOM )	{	
		$string .= "logic &cache_XX_flush;\n";
		$string .= "logic &cache_XX_flush_clr;\n"; 
		}
	$string =~ s/XX/$port_id/g;
	my $data_max = $data_width - 1 ;
	my $addr_max = $addr_width - 1 ;
	my $be_max = ($data_width / 8) - 1 ;
	$string =~ s/DDD/$data_max/g;
	$string =~ s/AA/$addr_max/g;	
	$string =~ s/CC/$be_max/g;	
	my $burst_max = $burst_width - 1;
	$string =~ s/BB/$burst_max/g;	
	$string = ReplaceTabTags( $string, 18);	
	$string .= "\n";	
	return $string;
}



# subroutine
# Add misc signals and logic after top level entity
sub LogicMisc {
	my ($data_width) = @_;
	my $string ;
	
	$string = qq
{
logic             clk;	
logic             lam_ready;

assign clk = am_clk; 

};
	return $string;
}


# Subroutine
sub ShiftRight {
   my ($value, $shift) = @_;
	my $output;
	if( $shift < 0 ) {
		$output = $value << (-1*$shift);
		}
	elsif ( $shift	> 0 ) {
		$output = $value >> $shift ;
	} else {
		$output = $value ;
		}
	return $output;
}	

 
 
 # Subroutine
 # Return the text for a Random Port instance with parameter place holders
 sub  PortInstanceText_Random {
   my ($support_flush ) = @_;
	my $string;
	$string .= qq
{
// port XX (Random)
mtx_avalon_mpfe_random
#(
// Avalon
   .avalon_addr_width      (#AV_ADDR#),
   .avalon_data_width      (#AV_DATA#),
   .avalon_data_mask_width (#AV_MASK#),

   // Local Port
   .lsp_addr_width         (#LAM_ADDR#),
   .lsp_data_width         (#LAM_DATA#),
   .lsp_data_mask_width    (#LAM_MASK#),
   .lsp_burst_width        (#LAM_BURST#),
   .lsp_write_clocks       (1),
   .lsp_write_data_width   (#LAM_DATA#),

   // Cache
   .device_family          ("#DEVICE#"),
   .cache_av_total_words   (#AV_WORDS#),
   .cache_av_addr_width    (#AV_WORDS_ADDR#),	
   .cache_lsp_total_words  (#LAM_WORDS#),
   .cache_lsp_addr_width   (#LAM_WORDS_ADDR#),
   .cache_disable          (#DISABLE#),
   .data_width_shift       (#SHIFT#),
	.cache_tagging          (#TAGGING#)
)
port_XX
(
   // Avalon Slave Interface
   .as_rst      (as_port_XX_rst),
   .as_clk      (as_port_XX_clk),
   .as_cs       (as_port_XX_cs),
   .as_rd       (as_port_XX_rd),
   .as_wr       (as_port_XX_wr),
   .as_addr     (as_port_XX_addr),
   .as_be       (as_port_XX_be),
   .as_data_in  (as_port_XX_data_in),
   .as_data_out (as_port_XX_data_out),
   .as_waitreq  (as_port_XX_waitreq),

   // Controller Interface Port
   .lsp_clk         (clk),
   .lsp_ready       (lam_ready),
   .lsp_addr        (lam_XX_addr),
   .lsp_dir         (lam_XX_dir),
   .lsp_length      (lam_XX_length),
   .lsp_read        (lam_XX_read),
   .lsp_write       (lam_XX_write),
   .lsp_request     (lam_XX_request),
   .lsp_granted     (lam_XX_granted),
   .lsp_end         (lam_XX_end),
   .lsp_data_in     (controller_data_out),
   .lsp_data_out    (lam_XX_data_out),
   .lsp_data_mask   (lam_XX_data_mask),
   .lsp_data_in_clk (clk),
    // Control Interface
};
	if( $support_flush )	{
		$string .= "   .cache_flush     (cache_XX_flush),\n";
		$string .= "   .cache_flush_clr (cache_XX_flush_clr)\n";
	} else {
		$string .= "   .cache_flush     (1'b0)\n";
	}
	$string .= ");\n";
	return $string;
}
	

 # Subroutine
 # Return the text for a Burst Port instance with parameter place holders
 sub  PortInstanceText_Burst {
	my ($include_be, $be_width) = @_;
	my $string;
	$string .= qq
{
// port XX (Burst)
mtx_avalon_mpfe_burst
#(
	// Avalon
	.avalon_addr_width      (#AV_ADDR#),
	.avalon_data_width      (#AV_DATA#),
	.avalon_data_mask_width (#AV_MASK#),
	.avalon_burst_width     (#AV_BURST#),

   // Local Port
   .lsp_addr_width         (#LAM_ADDR#),
   .lsp_data_width         (#LAM_DATA#),
   .lsp_data_mask_width    (#LAM_MASK#),
   .lsp_burst_width        (#LAM_BURST#),
   .lsp_write_clocks       (1),
   .lsp_write_data_width   (#LAM_DATA#),

	// Cache
	.device_family          ("#DEVICE#"),
	.av_fifo_total_words    (#AV_WORDS#),
	.av_fifo_addr_width     (#AV_WORDS_ADDR#),
	.lsp_fifo_total_words   (#LAM_WORDS#),
	.lsp_fifo_addr_width    (#LAM_WORDS_ADDR#),	
	.data_width_shift       (#SHIFT#)
)
port_XX
(
	// Avalon Slave Interface
	.as_rst          (as_port_XX_rst),
	.as_clk          (as_port_XX_clk),
	.as_cs           (as_port_XX_cs),
	.as_rd           (as_port_XX_rd),
	.as_wr           (as_port_XX_wr),
	.as_addr         (as_port_XX_addr),
	.as_be           (as_port_XX_be),
	.as_data_in      (as_port_XX_data_in),
	.as_data_out     (as_port_XX_data_out),
	.as_waitreq      (as_port_XX_waitreq),
   .as_burstcount   (as_port_XX_burstcount),
	.as_datavalid    (as_port_XX_datavalid),
	
	// Local Port
	.lsp_clk         (clk),
	.lsp_ready       (lam_ready),
	.lsp_addr        (lam_XX_addr),
	.lsp_dir         (lam_XX_dir),
	.lsp_length      (lam_XX_length),
	.lsp_read        (lam_XX_read),
	.lsp_write       (lam_XX_write),
	.lsp_request     (lam_XX_request),
	.lsp_granted     (lam_XX_granted),
	.lsp_end         (lam_XX_end),
	.lsp_data_in     (controller_data_out),
	.lsp_data_out    (lam_XX_data_out),
	.lsp_data_mask   (lam_XX_data_mask),
	.lsp_data_in_clk (clk)
);
};
	if( not $include_be)	{
		my $s = " {#BE#{1'b1}} ";
		$s =~ s/#BE#/$be_width/g;	
		$string =~ s/as_port_XX_be/$s/g;	
	} 
	return $string;
}
		
 
 
# Subroutine
# Return a string containing an instance of a slave port. 
# This subroutine supports BURST and RANDOM ports. 
sub PortInstance {
	my ($port_id, $mode, $device, $support_flush, $port_addr_width, $port_data_width, $addr_width, $data_width, 
	     $master_burst_width, $port_buffer_size, $port_disable_cache, $data_width_shift, $port_burst_max, 
		  $port_include_be, $port_tagging
	     ) = @_;			 
	my $string ;		 
	my $be_width = ($port_data_width / 8) ;	
	if( $mode == $MODE_RANDOM ) { 
		$string = PortInstanceText_Random($support_flush);
	} elsif( $mode == $MODE_BURST ) {
		$string = PortInstanceText_Burst($port_include_be, $be_width);
	} else {
		return;
		}	 
	$string =~ s/XX/$port_id/g;
	$string =~ s/#DEVICE#/$device/g;
	$string =~ s/#AV_ADDR#/$port_addr_width/g;
	$string =~ s/#AV_DATA#/$port_data_width/g;	
	$string =~ s/#AV_MASK#/$be_width/g;
	my $av_burst = 1 + log($port_burst_max) / log(2);	
	$string =~ s/#AV_BURST#/$av_burst/g;		
	$string =~ s/#LAM_ADDR#/$addr_width/g;
	$string =~ s/#LAM_DATA#/$data_width/g;
	$be_width = $am_data_width / 8 ;
	$string =~ s/#LAM_MASK#/$be_width/g;	
	$string =~ s/#LAM_BURST#/$master_burst_width/g;	
	
	$string =~ s/#AV_WORDS#/$port_buffer_size/g;	
	my $bits = log($port_buffer_size) / log(2);
	$string =~ s/#AV_WORDS_ADDR#/$bits/g;		
	
	my $adjusted_buffer_size = ShiftRight( $port_buffer_size, $data_width_shift );
	$string =~ s/#LAM_WORDS#/$adjusted_buffer_size/g;	
	$bits = log($adjusted_buffer_size) / log(2);
	$string =~ s/#LAM_WORDS_ADDR#/$bits/g;		
	my $disable = 0 ;
	if( $port_cache_disable ) {
		$disable = 1 ;
		}
	$string =~ s/#DISABLE#/$disable/g;
	$string =~ s/#SHIFT#/$data_width_shift/g;			
	$string =~ s/#TAGGING#/$port_tagging/g;					
	$string .= "\n";
	return $string;
}

# Subroutine
sub Controller_Interconnect_GenI {
	my($first, $last, $SourceString, $width, $random_only) = @_;	
	my $string = "";
	for( $i=$last; $i>=$first; $i--) {	
		if( $string ne "" ) {
			$string .= ", ";
			}				
		if( ((length $string) - rindex($string, "\n")) > 60 ) {
			$string .= "\n& ";
		}
		if($port_mode{$i} == $MODE_RANDOM || ($port_mode{$i} == $MODE_BURST && not $random_only )) {
			# add the signal
			$string .= $SourceString;
			$string =~ s/XX/$i/g;
		} else {
				$string .= "#WIDTH#'b0";
				$string =~ s/#WIDTH#/$width/g;
				}
		}
	return $string ;
}

# Subroutine
sub Controller_Interconnect_GenO {
	my($first, $last, $TargetString, $SourceString, $random_only) = @_;	
	my $string = "";
	for( $i=$first; $i<=$last; $i++) {			
		if($port_mode{$i} == $MODE_RANDOM || ($port_mode{$i} == $MODE_BURST && not $random_only )) {
		if( $string ne "" ) {
			$string .= "\n";
			}				
			# add the signal
			$string .= $TargetString;
			$string =~ s/XX/$i/g;
			$string .= $SourceString;
			$string =~ s/XX/$i/g;
			} 
		}
	return $string ;
}


# Subroutine
# generate the controller interconnect text
# uses $port_mode variable, $MODE_RANDOM, $MODE_BURST
sub ControllerInterconnect {
    my( $first, $last_active, $support_flush, $addr_width, $data_width, $burst_width, $control ) = @_;
	 
	my $string;
	my $lam_addr, $lam_data_in, $lam_data_mask, $lam_dir, $lam_read, $lam_write, $lam_length, $lam_request, $lam_granted, $lam_end, $lam_flush, $lam_flush_clr;
	$string .= qq
(
// Controller Interconnect
logic [#ADDR_MAX#:0] &controller_lam_addr;
logic [#DATA_OUT_MAX#:0] &controller_data_out;
logic [#DATA_MAX#:0] &controller_lam_data_in;
logic [#MASK_MAX#:0] &controller_lam_data_mask;
logic [#LAST_PORT#:0] &controller_lam_dir;
logic [#LAST_PORT#:0] &controller_lam_read;
logic [#LAST_PORT#:0] &controller_lam_write;
logic [#LENGTH_MAX#:0] &controller_lam_length;
logic [#LAST_PORT#:0] &controller_lam_request;
logic [#LAST_PORT#:0] &controller_lam_granted;
logic [#LAST_PORT#:0] &controller_lam_end;
);
	if( $support_flush ) {
		$string .= qq
(	
logic [#LAST_PORT#:0] &controller_lam_flush;
logic [#LAST_PORT#:0] &controller_lam_flush_clr;
);
		}		
	# replace tags
	my $ports_max = $port_last_active - $first;
	my $nports = $ports_max + 1 ;
	$string =~ s/#LAST_PORT#/$ports_max/g;	
	my $addr_max = $nports * $addr_width - 1 ;
	$string =~ s/#ADDR_MAX#/$addr_max/g;
	my $data_max = $nports * $data_width - 1 ;
	$string =~ s/#DATA_MAX#/$data_max/g;
	my $mask_max = $nports * $data_width/8 - 1 ;
	$string =~ s/#MASK_MAX#/$mask_max/g;
	my $burst_max = $nports * $burst_width - 1 ;
	$string =~ s/#LENGTH_MAX#/$burst_max/g;
	my $data_out_max = $data_width - 1 ;
	$string =~ s/#DATA_OUT_MAX#/$data_out_max/g;
	$string = ReplaceTabTags( $string, 18 );			
	$string .= qq
(	
assign controller_lam_addr      &= {#LAM_ADDR#};
assign controller_lam_data_in   &= {#LAM_DATA_IN#};
assign controller_lam_data_mask &= {#LAM_MASK#};
assign controller_lam_dir       &= {#LAM_DIR#};
assign controller_lam_length    &= {#LAM_LENGTH#};
assign controller_lam_request   &= {#LAM_REQUEST#};
#LAM_READ#
#LAM_WRITE#
#LAM_GRANTED#
#LAM_END#
);
	if( $support_flush ) {
		$string .= qq
(	
#LAM_CACHE#
assign controller_lam_flush_clr &= {#LAM_CLR#};
);
		}
	# replace tags
	my $temp;
	$temp = Controller_Interconnect_GenI($first, $port_last_active, "lam_XX_addr", $addr_width, 0);
	$string =~ s/#LAM_ADDR#/$temp/g;
	$temp = Controller_Interconnect_GenI($first, $port_last_active, "lam_XX_data_out", $data_width, 0);
	$string =~ s/#LAM_DATA_IN#/$temp/g;	
	$temp = Controller_Interconnect_GenI($first, $port_last_active, "lam_XX_data_mask", $data_width/8, 0);
	$string =~ s/#LAM_MASK#/$temp/g;
	$temp = Controller_Interconnect_GenI($first, $port_last_active, "lam_XX_dir", 1, 0);
	$string =~ s/#LAM_DIR#/$temp/g;
	$temp = Controller_Interconnect_GenI($first, $port_last_active, "lam_XX_request", 1, 0);
	$string =~ s/#LAM_REQUEST#/$temp/g;
	$temp = Controller_Interconnect_GenI($first, $port_last_active, "lam_XX_length", $burst_width, 0);
	$string =~ s/#LAM_LENGTH#/$temp/g;
	$temp = Controller_Interconnect_GenI($first, $port_last_active, "cache_XX_flush_clr", 1, 1);
	$string =~ s/#LAM_CLR#/$temp/g;	
	$temp = Controller_Interconnect_GenO($first, $port_last_active, "assign lam_XX_read &= ", "controller_lam_read[XX];", 0);
	$string =~ s/#LAM_READ#/$temp/g;
	$temp = Controller_Interconnect_GenO($first, $port_last_active, "assign lam_XX_write &= ", "controller_lam_write[XX];", 0);
	$string =~ s/#LAM_WRITE#/$temp/g;
	$temp = Controller_Interconnect_GenO($first, $port_last_active, "assign lam_XX_granted &= ", "controller_lam_granted[XX];", 0);
	$string =~ s/#LAM_GRANTED#/$temp/g;
	$temp = Controller_Interconnect_GenO($first, $port_last_active, "assign lam_XX_end &= ", "controller_lam_end[XX];", 0);
	$string =~ s/#LAM_END#/$temp/g;
	$temp = Controller_Interconnect_GenO($first, $port_last_active, "assign cache_XX_flush &= ", "controller_lam_flush[XX];",  1);
	$string =~ s/#LAM_CACHE#/$temp/g;
	$string = ReplaceTabTags( $string, 36 );
	return $string;
}


# Subroutine
# Add MPFE controller
sub Controller {
   my ($support_flush, $control_port, $device, $addr_width, $data_width, $burst_width, $lam_address_width, $nports, $scheduler_parameter ) = @_;
	my $string;

	$string = qq 
{
mtx_avalon_mpfe_controller
#(
	.device_family         ("#DEVICE#"),
	.control_port          (#CONTROL#),	
	.am_addr_width         (#AV_ADDR#),
	.am_data_width         (#AV_DATA#),
	.am_data_mask_width    (#AV_MASK#),
	.am_burst_width        (#AV_BURST#),		
	.lam_total_ports       (#NPORTS#),
	.lam_addr_width        (#LAM_ADDR_WIDTH#),
	.am_pending_reads      (#LAM_PENDING_READS#),
	.lowest_priority_ports (#LOWEST_PRIORITY#),	
	.low_priority_ports    (#LOW_PRIORITY#),
	.medium_priority_ports (#MED_PRIORITY#),	
	.high_priority_ports   (#HIGH_PRIORITY#),
   .highest_priority_ports(#HIGHEST_PRIORITY#),
	.sc_mode_lowest        (#SCM_LOWEST#),
	.sc_parm1_lowest       (#SCP_LOWEST#),
	.sc_mode_low           (#SCM_LOW#),
	.sc_parm1_low          (#SCP_LOW#),
	.sc_mode_medium        (#SCM_MED#),
	.sc_parm1_medium       (#SCP_MED#),
	.sc_mode_high          (#SCM_HIGH#),
	.sc_parm1_high         (#SCP_HIGH#)	
)
mtx_mpfe
(
	// Local Port
	.lam_addr      (controller_lam_addr),
	.lam_data_in   (controller_lam_data_in),
	.lam_data_out  (controller_data_out),
	.lam_data_mask (controller_lam_data_mask),
	.lam_dir       (controller_lam_dir),
	.lam_read      (controller_lam_read),
	.lam_write     (controller_lam_write),
	.lam_length    (controller_lam_length),
	.lam_request   (controller_lam_request),
	.lam_granted   (controller_lam_granted),
	.lam_end       (controller_lam_end),
	.lam_ready     (lam_ready),};

	if( $support_flush ) {
	$string .= qq 
{
   .lam_flush		(controller_lam_flush),
   .lam_flush_clr	(controller_lam_flush_clr),};
	}
	else {
	   $string .= "\n";
		$string .= "   .lam_flush_clr ('0),";
	}

	if( $control_port ) {
		$string .= qq 
{	
	// Control Port
	.as_ctrl_clk	  (as_ctrl_clk),
	.as_ctrl_rst	  (as_ctrl_rst),
	.as_ctrl_waitreq (as_ctrl_waitreq),
	.as_ctrl_rd		  (as_ctrl_rd),
	.as_ctrl_wr		  (as_ctrl_wr),
	.as_ctrl_data_in (as_ctrl_data_in),
	.as_ctrl_data_out(as_ctrl_data_out),
	.as_ctrl_addr	  (as_ctrl_addr),};
	}

	$string .= qq 	
{
	// Avalon Master Interface
	.am_rst			(am_rst),
	.am_clk			(am_clk),
	.am_rd			(am_rd),	
	.am_wr			(am_wr),
	.am_addr			(am_addr),
	.am_burstcount	(am_burstcount),
	.am_be			(am_be),
	.am_data_in		(am_data_in),
	.am_data_out	(am_data_out),
	.am_waitreq		(am_waitreq),
	.am_burststart (am_burststart),
	.am_datavalid	(am_datavalid)
);
	};	
	# Replace Tags
	$string =~ s/#DEVICE#/$device/g;
	$string =~ s/#AV_ADDR#/$addr_width/g;
	$string =~ s/#AV_DATA#/$data_width/g;
	my $be_width = ($data_width / 8) ;
	$string =~ s/#AV_MASK#/$be_width/g;
	$string =~ s/#AV_BURST#/$burst_width/g;			
	$string =~ s/#LAM_ADDR_WIDTH#/$lam_address_width/g;
	$string =~ s/#LAM_PENDING_READS#/$am_pending_reads/g;	
	$string =~ s/#NPORTS#/$nports/g;
	$string =~ s/#CONTROL#/$control_port/g;
	($lowest_priority_ports, $low_priority_ports, $medium_priority_ports, $high_priority_ports, $highest_priority_ports) = GetPortPriorityMasks($port_first, $port_last_active);
	$string =~ s/#LOWEST_PRIORITY#/$lowest_priority_ports/g;	
	$string =~ s/#LOW_PRIORITY#/$low_priority_ports/g;
	$string =~ s/#MED_PRIORITY#/$medium_priority_ports/g;	
	$string =~ s/#HIGH_PRIORITY#/$high_priority_ports/g;
	$string =~ s/#HIGHEST_PRIORITY#/$highest_priority_ports/g;	
	$string =~ s/#SCM_LOWEST#/$scm_lowest/g;	
	$string =~ s/#SCP_LOWEST#/$scp_lowest/g;		
	$string =~ s/#SCM_LOW#/$scm_low/g;	
	$string =~ s/#SCP_LOW#/$scp_low/g;		
	$string =~ s/#SCM_MED#/$scm_medium/g;	
	$string =~ s/#SCP_MED#/$scp_medium/g;		
	$string =~ s/#SCM_HIGH#/$scm_high/g;	
	$string =~ s/#SCP_HIGH#/$scp_high/g;		
	return $string;
}



# *******************************************************************************************************************************
#          File   Generation
# *******************************************************************************************************************************
# Evaluate what ports and interfaces we have	 
my ($port_last_active, $support_flush_interface, $number_of_ports) = PortInfo($port_first, $port_last, $use_control_interface) ;

my $TopLevel = qq
{
///////////////////////////////////////
//   Microtronix MPFE               
//            Top Level             
//     Generated by Qsys script     
//      Build : 100/2016.08.12     
//////////////////////////////////////
module $top_level_module_name
(
};  

# Add top level signals for ports
for( my $port=$port_first;  $port <= $port_last; $port++ ) {
	$TopLevel .=  TopSignals_Port( $port, $port_data_width{$port}, $port_addr_width{$port}, $port_mode{$port}, $port_burst_width{$port}, $port_include_be{$port} );
}
# Add top level signals for control port
if( $use_control_interface ) {
	$TopLevel .= TopSignals_ControlPort(     );
}
# Add top level signals for avalon master
$TopLevel .= TopSignals_AvalonMaster ($am_addr_width, $burst_width_master_bits, $am_data_width );

#Add misc signals and logic
$TopLevel .= LogicMisc ( $am_data_width );

# Add interconnect signals for ports
for( my $port=$port_first;  $port <= $port_last; $port++ ) {
	$TopLevel .=  TopSignals_PortInterconnect( $port, $am_data_width, $lam_addr_width, $port_mode{$port}, $burst_width_master_bits, $use_control_interface, $support_flush_interface );
}

# Add controller interconnect
$TopLevel .= ControllerInterconnect( $port_first, $port_last_active, $support_flush_interface, $lam_addr_width, $am_data_width, $burst_width_master_bits, $use_control_interface );

# Add port instances
for( my $port=$port_first;  $port <= $port_last; $port++ ) {
	$TopLevel .=  PortInstance( $port, $port_mode{$port}, $device, $support_flush_interface, $port_addr_width{$port}, 
	                            $port_data_width{$port}, $lam_addr_width, $am_data_width, $burst_width_master_bits,
										  $port_buffer_size{$port}, $port_disable_cache{$port}, $port_data_width_shift{$port}, 
										  $port_max_burst{$port}, $port_include_be{$port}, $port_tag_mode{$port} );
}

# Add MPFE controller
$TopLevel .= Controller($support_flush_interface, $use_control_interface, $device, $am_addr_width, $am_data_width, $burst_width_master_bits, $lam_addr_width, $number_of_ports, $mem_scheduler_parameter );

# Add End Module
$TopLevel .= "\n" ."endmodule\n";

open  FILE_OUT, ">$filename" or die "cannot create: $!";
print FILE_OUT $TopLevel;

close FILE_OUT;
close FILE_DEBUG;

