# SPDX-License-Identifier: GPL-2.0-or-later

# Defines basic Tcl procs for OpenOCD target module

proc new_target_name { } {
	return [target number [expr {[target count] - 1}]]
}

global in_process_reset
set in_process_reset 0

# Catch reset recursion
proc ocd_process_reset { MODE } {
	global in_process_reset
	if {$in_process_reset} {
		set in_process_reset 0
		return -code error "'reset' can not be invoked recursively"
	}

	set in_process_reset 1
	set success [expr {[catch {ocd_process_reset_inner $MODE} result] == 0}]
	set in_process_reset 0

	if {$success} {
		return $result
	} else {
		return -code error $result
	}
}

proc ocd_process_reset_inner { MODE } {
	set targets [target names]

	# If this target must be halted...
	switch $MODE {
		halt -
		init {
			set halt 1
		}
		run {
			set halt 0
		}
		default {
			return -code error "Invalid mode: $MODE, must be one of: halt, init, or run";
		}
	}

	# Target event handlers *might* change which TAPs are enabled
	# or disabled, so we fire all of them.  But don't issue any
	# target "arp_*" commands, which may issue JTAG transactions,
	# unless we know the underlying TAP is active.
	#
	# NOTE:  ARP == "Advanced Reset Process" ... "advanced" is
	# relative to a previous restrictive scheme

	foreach t $targets {
		# New event script.
		$t invoke-event reset-start
	}

	# Use TRST or TMS/TCK operations to reset all the tap controllers.
	# TAP reset events get reported; they might enable some taps.
	init_reset $MODE

	# Examine all targets on enabled taps.
	foreach t $targets {
		if {![using_jtag] || [jtag tapisenabled [$t cget -chain-position]]} {
			$t invoke-event examine-start
			set err [catch "$t arp_examine allow-defer"]
			if { $err } {
				$t invoke-event examine-fail
			} else {
				$t invoke-event examine-end
			}
		}
	}

	# Assert SRST, and report the pre/post events.
	# Note:  no target sees SRST before "pre" or after "post".
	foreach t $targets {
		$t invoke-event reset-assert-pre
	}
	foreach t $targets {
		# C code needs to know if we expect to 'halt'
		if {![using_jtag] || [jtag tapisenabled [$t cget -chain-position]]} {
			$t arp_reset assert $halt
		}
	}
	foreach t $targets {
		$t invoke-event reset-assert-post
	}

	# Now de-assert SRST, and report the pre/post events.
	# Note:  no target sees !SRST before "pre" or after "post".
	foreach t $targets {
		$t invoke-event reset-deassert-pre
	}
	foreach t $targets {
		# Again, de-assert code needs to know if we 'halt'
		if {![using_jtag] || [jtag tapisenabled [$t cget -chain-position]]} {
			$t arp_reset deassert $halt
		}
	}
	foreach t $targets {
		$t invoke-event reset-deassert-post
	}

	# Pass 1 - Now wait for any halt (requested as part of reset
	# assert/deassert) to happen.  Ideally it takes effect without
	# first executing any instructions.
	if { $halt } {
		foreach t $targets {
			if {[using_jtag] && ![jtag tapisenabled [$t cget -chain-position]]} {
				continue
			}

			if { ![$t was_examined] } {
				# don't wait for targets where examination is deferred
				# they can not be halted anyway at this point
				if { [$t examine_deferred] } {
					continue
				}
				# try to re-examine or target state will be unknown
				$t invoke-event examine-start
				set err [catch "$t arp_examine allow-defer"]
				if { $err } {
					$t invoke-event examine-fail
					return -code error [format "TARGET: %s - Not examined" $t]
				} else {
					$t invoke-event examine-end
				}
			}

			# Wait up to 1 second for target to halt. Why 1sec? Cause
			# the JTAG tap reset signal might be hooked to a slow
			# resistor/capacitor circuit - and it might take a while
			# to charge

			# Catch, but ignore any errors.
			catch { $t arp_waitstate halted 1000 }

			# Did we succeed?
			set s [$t curstate]

			if { $s != "halted" } {
				return -code error [format "TARGET: %s - Not halted" $t]
			}
		}
	}

	#Pass 2 - if needed "init"
	if { $MODE == "init" } {
		foreach t $targets {
			if {[using_jtag] && ![jtag tapisenabled [$t cget -chain-position]]} {
				continue
			}

			# don't wait for targets where examination is deferred
			# they can not be halted anyway at this point
			if { ![$t was_examined] && [$t examine_deferred] } {
				continue
			}

			set err [catch "$t arp_waitstate halted 5000"]
			# Did it halt?
			if { $err == 0 } {
				$t invoke-event reset-init
			}
		}
	}

	foreach t $targets {
		$t invoke-event reset-end
	}
}

proc using_jtag {} {
	set _TRANSPORT [ transport select ]
	expr { [ string first "jtag" $_TRANSPORT ] != -1 }
}

proc using_swd {} {
	set _TRANSPORT [ transport select ]
	expr { [ string first "swd" $_TRANSPORT ] != -1 }
}

proc using_hla {} {
	set _TRANSPORT [ transport select ]
	expr { [ string first "hla" $_TRANSPORT ] != -1 }
}

#########

# Target/chain configuration scripts can either execute commands directly
# or define a procedure which is executed once all configuration
# scripts have completed.
#
# By default(classic) the config scripts will set up the target configuration
proc init_targets {} {
}

proc set_default_target_event {t e s} {
	if {[$t cget -event $e] == ""} {
		$t configure -event $e $s
	}
}

proc init_target_events {} {
	set targets [target names]

	foreach t $targets {
		set_default_target_event $t gdb-flash-erase-start "reset init"
		set_default_target_event $t gdb-flash-write-end "reset halt"
		set_default_target_event $t gdb-attach "halt 1000"
	}
}

# Additionally board config scripts can define a procedure init_board that will be executed after init and init_targets
proc init_board {} {
}

lappend _telnet_autocomplete_skip _post_init_target_array_mem
proc _post_init_target_array_mem {} {
	set targets [target names]
	lappend targets ""

	foreach t $targets {
		if {$t != ""} {
			set t "$t "
		}
		eval [format	{lappend ::_telnet_autocomplete_skip "%smem2array"} $t]
		eval [format	{proc {%smem2array} {arrayname bitwidth address count {phys ""}} {
							echo "DEPRECATED! use 'read_memory' not 'mem2array'"

							upvar $arrayname $arrayname
							set $arrayname ""
							set i 0

							foreach elem [%sread_memory $address $bitwidth $count {*}$phys] {
								set ${arrayname}($i) $elem
								incr i
							}
						}} $t $t]
		eval [format    {lappend ::_telnet_autocomplete_skip "%sarray2mem"} $t]
		eval [format    {proc {%sarray2mem} {arrayname bitwidth address count {phys ""}} {
							echo "DEPRECATED! use 'write_memory' not 'array2mem'"

							upvar $arrayname $arrayname
							set data ""

							for {set i 0} {$i < $count} {incr i} {
								lappend data [expr $${arrayname}($i)]
							}

							%swrite_memory $address $bitwidth $data {*}$phys
						}} $t $t]
	}
}
lappend post_init_commands _post_init_target_array_mem

# smp_on/smp_off were already DEPRECATED in v0.11.0 through http://openocd.zylin.com/4615
lappend _telnet_autocomplete_skip "aarch64 smp_on"
proc "aarch64 smp_on" {args} {
	echo "DEPRECATED! use 'aarch64 smp on' not 'aarch64 smp_on'"
	eval aarch64 smp on $args
}

lappend _telnet_autocomplete_skip "aarch64 smp_off"
proc "aarch64 smp_off" {args} {
	echo "DEPRECATED! use 'aarch64 smp off' not 'aarch64 smp_off'"
	eval aarch64 smp off $args
}

lappend _telnet_autocomplete_skip "cortex_a smp_on"
proc "cortex_a smp_on" {args} {
	echo "DEPRECATED! use 'cortex_a smp on' not 'cortex_a smp_on'"
	eval cortex_a smp on $args
}

lappend _telnet_autocomplete_skip "cortex_a smp_off"
proc "cortex_a smp_off" {args} {
	echo "DEPRECATED! use 'cortex_a smp off' not 'cortex_a smp_off'"
	eval cortex_a smp off $args
}

lappend _telnet_autocomplete_skip "mips_m4k smp_on"
proc "mips_m4k smp_on" {args} {
	echo "DEPRECATED! use 'mips_m4k smp on' not 'mips_m4k smp_on'"
	eval mips_m4k smp on $args
}

lappend _telnet_autocomplete_skip "mips_m4k smp_off"
proc "mips_m4k smp_off" {args} {
	echo "DEPRECATED! use 'mips_m4k smp off' not 'mips_m4k smp_off'"
	eval mips_m4k smp off $args
}
