# Microtronix Avalon MPFE mtx_avalon_mpfe_hw.tcl
# For more information and updates please visit http://www.microtronix.com

# Build: 103/2021.01.20

# +-----------------------------------
# |
# |
set_module_property NAME mtx_avalon_mpfe
set_module_property VERSION 1.0
set_module_property INTERNAL false
set_module_property GROUP "Microtronix"
set_module_property AUTHOR Microtronix
set_module_property DISPLAY_NAME "Avalon Multi-port Front End"
set_module_property DESCRIPTION "Microtronix Avalon Multi-port Front End <br>Build : 103/2021.01.20"
set_module_property INSTANTIATE_IN_SYSTEM_MODULE true
set_module_property EDITABLE false
set_module_property VALIDATION_CALLBACK mtx_avalon_mpfe_validation
set_module_property ELABORATION_CALLBACK mtx_avalon_mpfe_elaboration
set_module_property GENERATION_CALLBACK mtx_avalon_mpfe_generation
# | 
# +-----------------------------------

# +-----------------------------------
# | files
# | 
add_file mtx_avalon_mpfe_scheduler.sv {SYNTHESIS}
add_file mtx_avalon_mpfe_controller.sv {SYNTHESIS}
add_file mtx_avalon_mpfe_burst.sv {SYNTHESIS}
add_file mtx_avalon_mpfe_random.sv {SYNTHESIS}
add_file mtx_avalon_mpfe_controller.ocp {SYNTHESIS}
add_file mtx_avalon_mpfe.sdc {SDC}
# | 
# +-----------------------------------

# +-----------------------------------
# | parameters
# | 
set PORT_0 0
set PORT_1 1
set PORT_2 2
set PORT_3 3
set PORT_4 4
set PORT_5 5
set PORT_6 6
set PORT_7 7
set PORT_8 8
set PORT_9 9
set PORT_10 10
set PORT_11 11
set PORT_12 12
set PORT_13 13
set PORT_14 14
set PORT_15 15
set PORT_16 16
set PORT_17 17
set PORT_18 18
set PORT_19 19
set PORT_20 20
set PORT_21 21
set PORT_22 22
set PORT_23 23
set PORT_24 24
set PORT_25 25
set PORT_LAST $PORT_25
set PORT_FIRST $PORT_0

# Port modes
set MODE_DISABLED 0
set MODE_BURST 1
set MODE_RANDOM 2
		
set ports($PORT_0) port_0
set ports($PORT_1) port_1
set ports($PORT_2) port_2
set ports($PORT_3) port_3
set ports($PORT_4) port_4
set ports($PORT_5) port_5
set ports($PORT_6) port_6
set ports($PORT_7) port_7
set ports($PORT_8) port_8
set ports($PORT_9) port_9
set ports($PORT_10) port_10
set ports($PORT_11) port_11
set ports($PORT_12) port_12
set ports($PORT_13) port_13
set ports($PORT_14) port_14
set ports($PORT_15) port_15
set ports($PORT_16) port_16
set ports($PORT_17) port_17
set ports($PORT_18) port_18
set ports($PORT_19) port_19
set ports($PORT_20) port_20
set ports($PORT_21) port_21
set ports($PORT_22) port_22
set ports($PORT_23) port_23
set ports($PORT_24) port_24
set ports($PORT_25) port_25

add_parameter am_data_width int 32
set_parameter_property am_data_width ALLOWED_RANGES {16 32 64 128 256 512 1024}
set_parameter_property am_data_width DISPLAY_NAME "Data Width (bits)"
set_parameter_property am_data_width DESCRIPTION "Width of the Avalon Master port data bus in bits"
set_parameter_property am_data_width DERIVED false
set_parameter_property am_data_width VISIBLE true
set_parameter_property am_data_width AFFECTS_ELABORATION true
add_parameter am_addr_width int 18
set_parameter_property am_addr_width ALLOWED_RANGES {8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40}
set_parameter_property am_addr_width DISPLAY_NAME "Address Width"
set_parameter_property am_addr_width DESCRIPTION "Number of address bits for the Avalon Master port."
set_parameter_property am_addr_width DERIVED false
set_parameter_property am_addr_width VISIBLE true
set_parameter_property am_addr_width AFFECTS_ELABORATION true
add_parameter am_reads int 4
set_parameter_property am_reads ALLOWED_RANGES {1 2 3 4 5 6 7 8}
set_parameter_property am_reads DISPLAY_NAME "Maximum Pending Reads"
set_parameter_property am_reads DESCRIPTION "Maximum number of pending reads the MPFE will generate."
set_parameter_property am_reads DERIVED false
set_parameter_property am_reads VISIBLE true
set_parameter_property am_reads AFFECTS_ELABORATION true
add_parameter lam_addr_width int 16
set_parameter_property lam_addr_width DERIVED true
set_parameter_property lam_addr_width VISIBLE false
set_parameter_property lam_addr_width AFFECTS_ELABORATION true
add_parameter burst_width_master_bits int 0
set_parameter_property burst_width_master_bits DERIVED true
set_parameter_property burst_width_master_bits VISIBLE false
set_parameter_property burst_width_master_bits AFFECTS_ELABORATION true
add_parameter use_control_interface boolean false
set_parameter_property use_control_interface DISPLAY_NAME "Add Control Port"
set_parameter_property use_control_interface DESCRIPTION "Add a control port to the MPFE to allow random ports caches to be flushed."
set_parameter_property use_control_interface DERIVED false
set_parameter_property use_control_interface VISIBLE true
set_parameter_property use_control_interface AFFECTS_ELABORATION true
set_parameter_property use_control_interface DISPLAY_HINT boolean
add_parameter DEVICE_FAMILY string "unknown"
set_parameter_property DEVICE_FAMILY SYSTEM_INFO device_family
set_parameter_property DEVICE_FAMILY DESCRIPTION "The selected device family."
add_display_item {} {Avalon Master} GROUP
add_display_item {Avalon Master} am_data_width PARAMETER
add_display_item {Avalon Master} am_addr_width PARAMETER
add_display_item {Avalon Master} am_reads PARAMETER
add_display_item {Avalon Master} use_control_interface PARAMETER
add_display_item {Avalon Master} mem_scheduler_parameter PARAMETER
add_display_item {Avalon Master} DEVICE_FAMILY PARAMETER
set PC_DISABLED "Disabled"
set PC_INTERVAL_PLUS1 "Increase Priority"
set sc_modes [list "$PC_DISABLED" "$PC_INTERVAL_PLUS1"]
add_parameter starvation_control_high string $PC_DISABLED
set_parameter_property starvation_control_high DISPLAY_NAME "High Priority Starvation Control"
set_parameter_property starvation_control_high DESCRIPTION "Starvation control mode for high priority ports."
set_parameter_property starvation_control_high ALLOWED_RANGES  $sc_modes
set_parameter_property starvation_control_high AFFECTS_ELABORATION false
add_parameter starvation_parm1_high positive 128
set_parameter_property starvation_parm1_high DISPLAY_NAME "High Priority Starvation Limit"
set_parameter_property starvation_parm1_high DESCRIPTION "The maximum number of grants on higher priority ports before starvation control takes effect for high priority ports."
set_parameter_property starvation_parm1_high ALLOWED_RANGES {2:65535}
set_parameter_property starvation_parm1_high AFFECTS_ELABORATION false
add_parameter starvation_control_medium string $PC_DISABLED
set_parameter_property starvation_control_medium DISPLAY_NAME "Medium Priority Starvation Control"
set_parameter_property starvation_control_medium DESCRIPTION "Starvation control mode for medium priority ports."
set_parameter_property starvation_control_medium ALLOWED_RANGES  $sc_modes
set_parameter_property starvation_control_medium AFFECTS_ELABORATION false
add_parameter starvation_parm1_medium positive 128
set_parameter_property starvation_parm1_medium DISPLAY_NAME "Medium Priority Starvation Limit"
set_parameter_property starvation_parm1_medium DESCRIPTION "The maximum number of grants on higher priority ports before starvation control takes effect for medium priority ports."
set_parameter_property starvation_parm1_medium ALLOWED_RANGES {2:65535}
set_parameter_property starvation_parm1_medium AFFECTS_ELABORATION false
add_parameter starvation_control_low string $PC_DISABLED
set_parameter_property starvation_control_low DISPLAY_NAME "Low Priority Starvation Control"
set_parameter_property starvation_control_low DESCRIPTION "Starvation control mode for low priority ports."
set_parameter_property starvation_control_low ALLOWED_RANGES  $sc_modes
set_parameter_property starvation_control_low AFFECTS_ELABORATION false
add_parameter starvation_parm1_low positive 128
set_parameter_property starvation_parm1_low DISPLAY_NAME "Low Priority Starvation Limit"
set_parameter_property starvation_parm1_low DESCRIPTION "The maximum number of grants on higher priority ports before starvation control takes effect for low priority ports."
set_parameter_property starvation_parm1_low ALLOWED_RANGES {2:65535}
set_parameter_property starvation_parm1_low AFFECTS_ELABORATION false
add_parameter starvation_control_lowest string $PC_DISABLED
set_parameter_property starvation_control_lowest DISPLAY_NAME "Lowest Priority Starvation Control"
set_parameter_property starvation_control_lowest DESCRIPTION "Starvation control mode for lowest priority ports."
set_parameter_property starvation_control_lowest ALLOWED_RANGES  $sc_modes
set_parameter_property starvation_control_lowest AFFECTS_ELABORATION false
add_parameter starvation_parm1_lowest positive 128
set_parameter_property starvation_parm1_lowest DISPLAY_NAME "Lowest Priority Starvation Limit"
set_parameter_property starvation_parm1_lowest DESCRIPTION "The maximum number of grants on higher priority ports before starvation control takes effect for lowest priority ports."
set_parameter_property starvation_parm1_lowest ALLOWED_RANGES {2:65535}
set_parameter_property starvation_parm1_lowest AFFECTS_ELABORATION false
add_display_item {} {Scheduler} GROUP 
add_display_item {Scheduler} starvation_control_high PARAMETER
add_display_item {Scheduler} starvation_parm1_high PARAMETER
add_display_item {Scheduler} starvation_control_medium PARAMETER
add_display_item {Scheduler} starvation_parm1_medium PARAMETER
add_display_item {Scheduler} starvation_control_low PARAMETER
add_display_item {Scheduler} starvation_parm1_low PARAMETER
add_display_item {Scheduler} starvation_control_lowest PARAMETER
add_display_item {Scheduler} starvation_parm1_lowest PARAMETER
add_display_item {} {Avalon Slave Ports} GROUP 

for { set i $::PORT_FIRST } { $i < ($PORT_LAST+1) } { incr i 1 } {
	if { $i > $::PORT_FIRST } {
		add_parameter $ports($i)_mode_string string "Disabled"
		add_parameter $ports($i)_mode int 0
	} else {
		add_parameter $ports($i)_mode_string string "Burst"
		add_parameter $ports($i)_mode int 2
	}
	set_parameter_property $ports($i)_mode DERIVED true
	set_parameter_property $ports($i)_mode VISIBLE false
	set_parameter_property $ports($i)_mode_string ALLOWED_RANGES {Disabled Burst Random}		
	set port_name $ports($i)
	set_parameter_property $ports($i)_mode_string DISPLAY_NAME "Port Type"
   set_parameter_property $ports($i)_mode_string DESCRIPTION "The type of slave port, either Random or Burst"	
	add_parameter $ports($i)_buffer_size int 16
	set_parameter_property $ports($i)_buffer_size ALLOWED_RANGES {4 8 16 32 64 128 256 512 1024 2048 4096}
	set_parameter_property $ports($i)_buffer_size DISPLAY_NAME "Buffer Size (words)"
   set_parameter_property $ports($i)_buffer_size DESCRIPTION "Size of the ports buffer in words. Burst ports have a read buffer and a write buffer each with this size. Random ports have one buffer that operates as a cache."
	set_parameter_property $ports($i)_buffer_size AFFECTS_ELABORATION true
	add_parameter $ports($i)_max_burst positive 16
	set_parameter_property $ports($i)_max_burst DISPLAY_NAME "Maximum Burst Length (words)"
	set_parameter_property $ports($i)_max_burst ALLOWED_RANGES {1 2 4 8 16 32 64 128 256 512 1024}
	set_parameter_property $ports($i)_max_burst DESCRIPTION "The maximum burst length supported by the port. This parameter determines the width of the burstcount signal."
	add_parameter $ports($i)_data_width_adjust string "1"
	set_parameter_property $ports($i)_data_width_adjust ALLOWED_RANGES {"2" "1" "1/2" "1/4" "1/8" "1/16"}
	set_parameter_property $ports($i)_data_width_adjust DISPLAY_NAME "Slave Data Width Ratio"
	set_parameter_property $ports($i)_data_width_adjust DESCRIPTION "The slave port may have a different data with than the master. For burst ports that are narrower than the master, restrictions on use of the port must be observed to prevent generation of byte enable patterns that do not conform to the Avlaon Interface Specification."
	set_parameter_property $ports($i)_data_width_adjust AFFECTS_ELABORATION true
	add_parameter $ports($i)_data_width integer 128
	set_parameter_property $ports($i)_data_width DISPLAY_NAME "Calculated Data Width (bits)"	
	set_parameter_property $ports($i)_data_width DISPLAY_NAME "The width in bits of the data_in and data_out signals of the slave port."
	set_parameter_property $ports($i)_data_width AFFECTS_ELABORATION false
	set_parameter_property $ports($i)_data_width DERIVED true
	add_parameter $ports($i)_priority string "Medium"
	set_parameter_property $ports($i)_priority DISPLAY_NAME "Service Request Priority"
   set_parameter_property $ports($i)_priority DESCRIPTION "Assigns the port to one of the five service priority levels that are available."
	set_parameter_property $ports($i)_priority ALLOWED_RANGES {"Highest" "High" "Medium" "Low" "Lowest"}
	set_parameter_property $ports($i)_priority AFFECTS_ELABORATION false	
	add_parameter $ports($i)_include_be boolean true
	set_parameter_property $ports($i)_include_be DISPLAY_NAME "Include Byte Enable Signal"
	set_parameter_property $ports($i)_include_be DESCRIPTION "This parameter determines of the slave port supports the byteenable signal."
	set_parameter_property $ports($i)_include_be DISPLAY_HINT boolean
	set_parameter_property $ports($i)_include_be AFFECTS_ELABORATION true	
	add_parameter $ports($i)_disable_cache boolean false
	set_parameter_property $ports($i)_disable_cache DISPLAY_NAME "Disable Port Cache"
	set_parameter_property $ports($i)_disable_cache DESCRIPTION "This parameter is available for random ports only. When set the cache is flushed after each write operation."
	set_parameter_property $ports($i)_disable_cache DISPLAY_HINT boolean
	set_parameter_property $ports($i)_disable_cache AFFECTS_ELABORATION false
	add_parameter $ports($i)_cache_tagging_mode STRING "Word"
	set_parameter_property $ports($i)_cache_tagging_mode DISPLAY_NAME "Cache Tagging Mode"	
	set_parameter_property $ports($i)_cache_tagging_mode ALLOWED_RANGES {"Word" "Cache"}
	set_parameter_property $ports($i)_cache_tagging_mode AFFECTS_ELABORATION false
	set_parameter_property $ports($i)_cache_tagging_mode VISIBLE false		
	add_parameter $ports($i)_addr_width int 0 
	set_parameter_property $ports($i)_addr_width DERIVED true
	set_parameter_property $ports($i)_addr_width AFFECTS_ELABORATION true
	set_parameter_property $ports($i)_addr_width VISIBLE false
	add_parameter $ports($i)_burst_width int 0 
	set_parameter_property $ports($i)_burst_width DERIVED true
	set_parameter_property $ports($i)_burst_width AFFECTS_ELABORATION true
	set_parameter_property $ports($i)_burst_width VISIBLE false
	set group_name "Port [format %d $i]"
	add_display_item {Avalon Slave Ports} $group_name GROUP tab
	add_display_item $group_name $ports($i)_mode_string PARAMETER
	add_display_item $group_name $ports($i)_buffer_size PARAMETER
	add_display_item $group_name $ports($i)_max_burst PARAMETER
	add_display_item $group_name $ports($i)_data_width_adjust PARAMETER
	add_display_item $group_name $ports($i)_priority PARAMETER
	add_display_item $group_name $ports($i)_disable_cache PARAMETER
	add_display_item $group_name $ports($i)_include_be PARAMETER	
	add_display_item $group_name $ports($i)_data_width PARAMETER	
	add_display_item $group_name $ports($i)_cache_tagging_mode PARAMETER		
}

# +-----------------------------------
# | ports
# | 

# Avalon Memory Master Clock
add_interface am_clk clock sink
set_interface_property am_clk ENABLED true
# Avalon Memory Master Reset
add_interface am_rst reset sink
set_interface_property am_rst associatedClock am_clk
set_interface_property am_rst ASSOCIATED_CLOCK am_clk
set_interface_property am_rst synchronousEdges DEASSERT
set_interface_property am_rst ENABLED true
# Avalon MM master - clock and reset only. Elaboration adds the other signals
add_interface am_master avalon master 
set_interface_property am_master ENABLED true
set_interface_property am_master ASSOCIATED_CLOCK am_clk
set_interface_property am_master associatedClock  am_clk
set_interface_property am_master associatedReset  am_rst
# Control port
add_interface clock_control clock sink
add_interface reset_control reset sink clock_control
add_interface control avalon end clock_control
set_interface_property control associatedReset reset_control
set_interface_property control addressAlignment NATIVE
# Disable control port by default
set_interface_property clock_control ENABLED false
set_interface_property reset_control ENABLED false
set_interface_property control ENABLED false
for { set i [expr $::PORT_FIRST] } { $i < ($::PORT_LAST+1) } { incr i 1 } {
	add_interface clock_$ports($i) clock end
	add_interface reset_$ports($i) reset end clock_$ports($i)
	add_interface $ports($i) avalon end clock_$ports($i)
	set_interface_property $ports($i) associatedReset reset_$ports($i)
	set_interface_property clock_$ports($i) ENABLED false
	set_interface_property $ports($i) bridgesToMaster true
	set_interface_property reset_$ports($i) ENABLED false
	set_interface_property $ports($i) ENABLED false
	set_interface_property $ports($i) addressGroup 1
}

# Shift right with negative shifts meaning shift left
proc ShiftRight {value shift} {
	set out [expr $value >>$shift]	
	if { $shift < 0 } {
		set out [expr $value << -$shift]
	}
	return $out
}

# Convert string to slave port data width shift
proc SlaveWidthShift  { i } {
	set port_data_width_adjust_string [get_parameter_value $::ports($i)_data_width_adjust]
	set shift 0
	if { $port_data_width_adjust_string == "1/16" }	{
		set shift 4			
		}	elseif { $port_data_width_adjust_string == "1/8" } {
		set shift 3				
		}	elseif { $port_data_width_adjust_string == "1/4" } {
		set shift 2		
		} elseif { $port_data_width_adjust_string == "1/2" } {
		set shift 1
		} elseif { $port_data_width_adjust_string == "2" } {
		set shift -1
		}
	return $shift
}

proc mtx_avalon_mpfe_validation {} {
	# Set value of derived parameters
	set family [get_parameter_value DEVICE_FAMILY]
	set am_data_width [get_parameter_value am_data_width]
	set am_addr_width [get_parameter_value am_addr_width]
	set num_bits [expr {pow(2, $am_addr_width) * 8 }]
	set num_kilobytes [expr round($num_bits / 1024 / 8)]
	set num_megabytes [expr round($num_bits / 1024 / 1024 / 8)]
	if {  $num_kilobytes < 1025  } { 
		send_message Info "Microtronix Avalon MPFE: Avalon Master Byte Address Space: $num_kilobytes kbytes"
	} else {
		send_message Info "Microtronix Avalon MPFE: Avalon Master Byte Address Space: $num_megabytes Mbytes"
	}	
	if { [get_parameter_value starvation_control_high]  != $::PC_DISABLED } {
		set_parameter_property starvation_parm1_high ENABLED true
	} else {
		set_parameter_property starvation_parm1_high ENABLED false
	}
	if { [get_parameter_value starvation_control_medium] != $::PC_DISABLED } {
		set_parameter_property starvation_parm1_medium ENABLED true
	} else {
		set_parameter_property starvation_parm1_medium ENABLED false
	}
	if { [get_parameter_value starvation_control_low] != $::PC_DISABLED } {
		set_parameter_property starvation_parm1_low ENABLED true
	} else {
		set_parameter_property starvation_parm1_low ENABLED false
	}
	if { [get_parameter_value starvation_control_lowest] != $::PC_DISABLED } {
		set_parameter_property starvation_parm1_lowest ENABLED true
	} else {
		set_parameter_property starvation_parm1_lowest ENABLED false
	}
		
   set port_count 0
	for { set i [expr $::PORT_FIRST] } { $i < ($::PORT_LAST+1) } { incr i 1 } {
		set port_mode_string [get_parameter_value $::ports($i)_mode_string]
		if { $port_mode_string == "Burst" } {
			set_parameter_value $::ports($i)_mode $::MODE_BURST
			set port_count [expr $port_count + 1] 
		} elseif { $port_mode_string == "Random" } {
			set_parameter_value $::ports($i)_mode $::MODE_RANDOM
			set port_count [expr $port_count + 1] 
		} else {
			set_parameter_value $::ports($i)_mode $::MODE_DISABLED
		}
		set_parameter_property $::ports($i)_mode_string ENABLED true
	}
	if { $port_count == 0 } {
		send_message Error "At least 1 slave port must be enabled"
		}	
	# enable and disable port parameters as needed, check values
	for { set i $::PORT_FIRST } { $i < ($::PORT_LAST+1) } { incr i 1 } {
		set port_mode [get_parameter_value $::ports($i)_mode]
		set mem_width [get_parameter_value am_data_width]
		set width_divisor [get_parameter_value $::ports($i)_data_width_adjust]

		if {   $port_mode == $::MODE_DISABLED   } {
			set_parameter_property $::ports($i)_buffer_size ENABLED false
			set_parameter_property $::ports($i)_max_burst ENABLED false
			set_parameter_property $::ports($i)_data_width_adjust ENABLED false
			set_parameter_property $::ports($i)_data_width VISIBLE false
			set_parameter_property $::ports($i)_disable_cache ENABLED false
			set_parameter_property $::ports($i)_priority ENABLED false
			set_parameter_property $::ports($i)_buffer_size ALLOWED_RANGES {2:4096}	
			set_parameter_property $::ports($i)_data_width_adjust ALLOWED_RANGES {"2" "1" "1/2" "1/4" "1/8" "1/16"}
			set_parameter_property $::ports($i)_include_be ENABLED false
			set_parameter_property $::ports($i)_cache_tagging_mode ENABLED false
		} else {
			set_parameter_property $::ports($i)_buffer_size ENABLED true
			set_parameter_property $::ports($i)_data_width_adjust ENABLED TRUE
			set_parameter_property $::ports($i)_data_width VISIBLE true
			set_parameter_property $::ports($i)_priority ENABLED true
		   # (must read property value before setting ranges else it can't be read correctly)
			set_parameter_value $::ports($i)_data_width [ShiftRight $mem_width [SlaveWidthShift $i]]
		}
		if {   $port_mode == $::MODE_BURST   } {		
			set_parameter_property $::ports($i)_max_burst ENABLED true			
			set_parameter_property $::ports($i)_disable_cache ENABLED false
			set_parameter_property $::ports($i)_cache_tagging_mode ENABLED false
			set_parameter_property $::ports($i)_include_be ENABLED true
			set_parameter_property $::ports($i)_data_width_adjust ALLOWED_RANGES {"2" "1" "1/2" "1/4" "1/8" "1/16"}
			# Enforce minimum buffer sizes for data width reduction modes to prevent filling the buffer when 
			# servicing a mis-aligned transaction that needs to store extra data words to the buffer
			if {   $width_divisor == "1/16"   } {
				set_parameter_property $::ports($i)_buffer_size ALLOWED_RANGES {64 128 256 512 1024 2048 4096}
			} elseif {   $width_divisor == "1/8"   } {
				set_parameter_property $::ports($i)_buffer_size ALLOWED_RANGES {32 64 128 256 512 1024 2048 4096}	
			} elseif {   $width_divisor == "1/4"   } {
				set_parameter_property $::ports($i)_buffer_size ALLOWED_RANGES {16 32 64 128 256 512 1024 2048 4096}
			} else {
				set_parameter_property $::ports($i)_buffer_size ALLOWED_RANGES {8 16 32 64 128 256 512 1024 2048 4096}
			}
			set max_burst [get_parameter_value $::ports($i)_max_burst]
			set buffer [get_parameter_value $::ports($i)_buffer_size]
			if {  $max_burst  > $buffer / 2  } {
				send_message Info "Port $i: Write bursts larger than half the buffer size will be split into 2 or more operations on the Avalon Master port"
				}
			if {  $max_burst  > $buffer  } {
				send_message Info "Port $i: Read bursts larger than the buffer size will always be split into 2 or more operations on the Avalon Master port"
				}				
			if { [get_parameter_value $::ports($i)_include_be] } {
				if { $width_divisor == "1/2" } {
					send_message Warning "Port $i: Including the Byte Enable signal on this port may result in bit patterns on the Avalon Master Byte Enable signal that don't conform to the Avalon Bus Specification"
				}
			}
			if { $width_divisor == "1/4" || $width_divisor == "1/8" || $width_divisor == "1/16" } {
				send_message Warning "Port $i: Including the Byte Enable signal or performing misaligned operations on this reduced width port may result in bit patterns on the Avalon Master Byte Enable signal that don't conform to the Avalon Bus Specification"
			}	
		} elseif {   $port_mode == $::MODE_RANDOM   } {
			set_parameter_property $::ports($i)_max_burst ENABLED false
			set_parameter_property $::ports($i)_disable_cache ENABLED true
			set_parameter_property $::ports($i)_include_be ENABLED false
			set_parameter_value $::ports($i)_include_be true
			set_parameter_property $::ports($i)_buffer_size ALLOWED_RANGES {2 4 8 16 32 64 128}
			set_parameter_property $::ports($i)_data_width_adjust ALLOWED_RANGES {"2" "1"}			
			set_parameter_property $::ports($i)_cache_tagging_mode ENABLED true									
			set tag_mode [get_parameter_value $::ports($i)_cache_tagging_mode]
			if {  $tag_mode  == "Byte" } {
				send_message Info "Port $i: Byte tagging may generate byteenable patterns that don't conform to the avalon bus specification. Ensure the Avalon master will support the patterns generated by data accesses to this slave port."
				}			
		}
	}
}

proc mtx_avalon_mpfe_elaboration {} {
	set am_data_width [get_parameter_value am_data_width]
	set avalon_data_width 0

	# Add contol interface if needed
	set use_control_interface [get_parameter_value use_control_interface]
	if { $use_control_interface } {
		set_interface_property clock_control ENABLED true
		set_interface_property reset_control ENABLED true
		set_interface_property control ENABLED true
		add_interface_port clock_control as_ctrl_clk clk Input 1
		add_interface_port reset_control as_ctrl_rst reset Input 1
		add_interface_port control as_ctrl_waitreq waitrequest Output 1
		add_interface_port control as_ctrl_rd read Input 1
		add_interface_port control as_ctrl_wr write Input 1
		add_interface_port control as_ctrl_data_in writedata Input 32
		add_interface_port control as_ctrl_data_out readdata Output 32
		add_interface_port control as_ctrl_addr address Input 6		
	} else {
		set_interface_property clock_control ENABLED false
		set_interface_property reset_control ENABLED false
		set_interface_property control ENABLED false
	}	
	# calculation of some values from the parameters
	set am_addr_width [get_parameter_value am_addr_width]
	set am_data_width [get_parameter_value am_data_width]
	set am_data_byte_width  [expr {$am_data_width/8}]
	set master_addr_width  $am_addr_width
	set slave_addr_adj_bits [expr {round(ceil(log($am_data_byte_width)/log(2)))}]
	set slave_addr_width   [expr {$am_addr_width-$slave_addr_adj_bits}]	

	# minimum burst width for the avalon master
	set burst_count_master_max 1	
	for { set i $::PORT_FIRST} { $i < ($::PORT_LAST+1) } { incr i 1 } {
		set port_mode [get_parameter_value $::ports($i)_mode]			
		if { $port_mode == $::MODE_DISABLED } {
			set_interface_property clock_$::ports($i) ENABLED false
			set_interface_property reset_$::ports($i) ENABLED false
			set_interface_property $::ports($i) ENABLED false
		} else {
		   set slave_width_shift [SlaveWidthShift $i]
			set slave_data_width [ ShiftRight $am_data_width $slave_width_shift ]
			if {$slave_data_width <8 || $slave_data_width > 1024 } {
				set port_name $::ports($i)
				send_message Error "Slave Port [format %s $port_name]: Data Width=$slave_data_width, but must be 8 to 1024 bits."
				# limit the range to prevent extraneous error messages
				if { $slave_data_width < 8 } {
				   set slave_data_width 8 
				}
				if { $slave_data_width > 1024 } {
				   set slave_data_width 1024 
				}						
			}		 
			set_interface_property clock_$::ports($i) ENABLED true
			set_interface_property reset_$::ports($i) ENABLED true
			set_interface_property $::ports($i) ENABLED true
			set_interface_property $::ports($i) addressAlignment DYNAMIC
			set_interface_property $::ports($i) bridgesToMaster am_master
			set_interface_property $::ports($i) explicitAddressSpan 0			
			set_interface_property $::ports($i) burstOnBurstBoundariesOnly false
			set_interface_property $::ports($i) holdTime 0
			set_interface_property $::ports($i) isMemoryDevice 0
			set_interface_property $::ports($i) isNonVolatileStorage 0
			set_interface_property $::ports($i) linewrapBursts 0
			set_interface_property $::ports($i) printableDevice 0
			set_interface_property $::ports($i) readWaitStates 0
			set_interface_property $::ports($i) readWaitTime 0
			set_interface_property $::ports($i) setupTime 0
			set_interface_property $::ports($i) timingUnits Cycles
			set_interface_property $::ports($i) writeWaitTime 0
			add_interface_port clock_$::ports($i) as_$::ports($i)_clk clk Input 1
			add_interface_port reset_$::ports($i) as_$::ports($i)_rst reset Input 1
			add_interface_port $::ports($i) as_$::ports($i)_waitreq waitrequest Output 1
			add_interface_port $::ports($i) as_$::ports($i)_cs chipselect Input 1
			add_interface_port $::ports($i) as_$::ports($i)_rd read Input 1
			add_interface_port $::ports($i) as_$::ports($i)_wr write Input 1
			add_interface_port $::ports($i) as_$::ports($i)_data_in writedata Input $slave_data_width
			add_interface_port $::ports($i) as_$::ports($i)_data_out readdata Output $slave_data_width
			if { [get_parameter_value $::ports($i)_include_be]   }  {
				add_interface_port $::ports($i) as_$::ports($i)_be byteenable Input [expr $slave_data_width/8]
			}
			set word_addressing_bits [expr {round(ceil(log($slave_data_width/8)/log(2)))}]			
			set address_width [expr $master_addr_width - $word_addressing_bits]
			set_parameter_value $::ports($i)_addr_width $address_width			
			add_interface_port $::ports($i) as_$::ports($i)_addr address Input $address_width
			if { $port_mode == $::MODE_BURST } {
				set max_burst [get_parameter_value $::ports($i)_max_burst]
				set burst_width [expr {round(ceil(log($max_burst+1)/log(2)))}]
				set_interface_property $::ports($i) readLatency 0
				set_interface_property $::ports($i) maximumPendingReadTransactions 20
				add_interface_port $::ports($i) as_$::ports($i)_burstcount burstcount Input $burst_width
				add_interface_port $::ports($i) as_$::ports($i)_datavalid readdatavalid Output 1
				set_parameter_value $::ports($i)_burst_width $burst_width
			} else {
				set max_burst [get_parameter_value $::ports($i)_buffer_size]
				set_interface_property $::ports($i) readLatency 1
				set_interface_property $::ports($i) maximumPendingReadTransactions 0
			}			
			set max_burst_adj [ShiftRight $max_burst $slave_width_shift]
			if { $slave_width_shift > 0 } {
				set max_burst_adj [expr $max_burst_adj + 1 ]
			}
			if { $max_burst_adj>$burst_count_master_max} {
				set burst_count_master_max $max_burst_adj
				}				
		}
	}	
	# Set a parameter for the local avalon master address width in words
	set lam_addr_width  [expr {$am_addr_width-round(ceil(log($am_data_width/8)/log(2)))}]
	set_parameter_value lam_addr_width $lam_addr_width	
	# Elaboration for Memory Master port
	set burst_width_master [expr {round(ceil(log($burst_count_master_max+1)/log(2)))}]
	set_parameter_value burst_width_master_bits $burst_width_master	
	set am_pending_reads [get_parameter_value am_reads]	
	set_interface_property am_master maximumPendingReadTransactions $am_pending_reads
	set_interface_property am_master constantBurstBehavior false
	add_interface_port am_clk am_clk clk Input 1
	add_interface_port am_rst am_rst reset Input 1
	add_interface_port am_master am_addr 			address 					Output 	$master_addr_width
	add_interface_port am_master am_data_in 		readdata 				Input 	$am_data_width
	add_interface_port am_master am_data_out 		writedata 				Output 	$am_data_width
	add_interface_port am_master am_wr				write 					Output 	1
	add_interface_port am_master am_rd				read 						Output 	1
	add_interface_port am_master am_waitreq		waitrequest				Input 	1
	add_interface_port am_master am_be				byteenable     		Output   $am_data_byte_width   
	add_interface_port am_master am_burstcount	burstcount     		Output   $burst_width_master
	add_interface_port am_master am_burststart	beginbursttransfer   Output   1
	add_interface_port am_master am_datavalid  	readdatavalid        Input    1
}

proc mtx_avalon_mpfe_generation {} {
	set device_family [get_parameter_value DEVICE_FAMILY]
	set outdir [get_generation_property OUTPUT_DIRECTORY]
	set outputname [get_generation_property OUTPUT_NAME]
	set qdr $::env(QUARTUS_ROOTDIR)
	# Windows: use the version of perl which shipped with Quartus
	set perl $qdr/bin/perl/bin/perl.exe
	if { ! [ file executable $perl ] } {
		# Maybe Quartus 14+:
		set perl $qdr/bin64/perl/bin/perl.exe
		   if { ! [ file executable $perl ] } {
		# If that didn't work, maybe perl can be found in the path:
		set perl "perl"
		}
	}
	set command_line [list $perl "mtx_avalon_mpfe.pl" "${outdir}${outputname}.sv" $outdir $outputname $device_family]	
	# Add other command line parameters
	lappend command_line [get_parameter_value am_data_width]
	lappend command_line [get_parameter_value am_addr_width]
	lappend command_line [get_parameter_value burst_width_master_bits]
	lappend command_line [get_parameter_value lam_addr_width]	
	lappend command_line [get_parameter_value use_control_interface]	
	lappend command_line $::PORT_FIRST 
	lappend command_line $::PORT_LAST 	
	lappend command_line [get_parameter_value am_reads]
	lappend command_line [lsearch $::sc_modes [get_parameter_value starvation_control_lowest]]
	lappend command_line [get_parameter_value starvation_parm1_lowest]
	lappend command_line [lsearch $::sc_modes [get_parameter_value starvation_control_low]]
	lappend command_line [get_parameter_value starvation_parm1_low]
	lappend command_line [lsearch $::sc_modes [get_parameter_value starvation_control_medium]]
	lappend command_line [get_parameter_value starvation_parm1_medium]
	lappend command_line [lsearch $::sc_modes [get_parameter_value starvation_control_high]]
	lappend command_line [get_parameter_value starvation_parm1_high]
	# Generator command line parameters for each port
	for { set i $::PORT_FIRST } { $i < ($::PORT_LAST+1) } { incr i 1 } {
		set port_mode [get_parameter_value $::ports($i)_mode]
		set port_buffer_size [get_parameter_value $::ports($i)_buffer_size]
		set port_max_burst [get_parameter_value $::ports($i)_max_burst]
		set port_disable_cache [get_parameter_value $::ports($i)_disable_cache]		
		set priority [get_parameter_value $::ports($i)_priority] 
		set am_data_width [get_parameter_value am_data_width]
		set slave_width_shift [SlaveWidthShift $i]
		set slave_data_width [ ShiftRight $am_data_width $slave_width_shift ]
		set slave_addr_width [get_parameter_value $::ports($i)_addr_width]
		set slave_burst_width [get_parameter_value $::ports($i)_burst_width]
		set include_be [get_parameter_value $::ports($i)_include_be]
		set tag_mode [get_parameter_value $::ports($i)_cache_tagging_mode]
		set tag_mode_num 2 
		if {  $tag_mode == "Byte" } {
			set tag_mode_num  0
		} elseif { $tag_mode == "Word" }  {
			set tag_mode_num  1
		}		
		lappend command_line $port_mode $port_buffer_size $port_max_burst $port_disable_cache $priority $slave_width_shift $slave_data_width $slave_addr_width $slave_burst_width $include_be $tag_mode_num
	}
	# need to use eval so that each part of the command line is separate (not quoted together)
	eval exec $command_line
	add_file ${outdir}${outputname}.sv {SYNTHESIS SIMULATION}

}

