# script for stm32h5x family

#
# stm32h5 devices support both JTAG and SWD transports.
#
source [find target/swj-dp.tcl]
source [find mem_helper.tcl]
source [find gdb_helper.tcl]

if { [info exists CHIPNAME] } {
	set _CHIPNAME $CHIPNAME
} else {
	set _CHIPNAME stm32h5
}

if { [info exists CONNECT_UNDER_RESET] } {
   set _CONNECT_UNDER_RESET $CONNECT_UNDER_RESET
} else {
   set _CONNECT_UNDER_RESET 0
}

if { [info exists ENABLE_LOW_POWER] } {
   set _ENABLE_LOW_POWER $ENABLE_LOW_POWER
} else {
   set _ENABLE_LOW_POWER 0
}

if { [info exists STOP_WATCHDOG] } {
   set _STOP_WATCHDOG $STOP_WATCHDOG
} else {
   set _STOP_WATCHDOG 0
}

set _ENDIAN little

# Work-area is a space in RAM used for flash programming
# By default use 16kB at address 0x20000000
if { [info exists WORKAREASIZE] } {
	set _WORKAREASIZE $WORKAREASIZE
} else {
	set _WORKAREASIZE 0x4000
}

if { [info exists WORKAREAADDR] } {
	set _WORKAREAADDR $WORKAREAADDR
} else {
	set _WORKAREAADDR 0x20000000
}

# By default do not use the flash loader in TZ-CLOSED product state
if { [info exists USE_LOADER_IN_TZ_CLOSED] } {
	set _USE_LOADER_IN_TZ_CLOSED $USE_LOADER_IN_TZ_CLOSED
} else {
	set _USE_LOADER_IN_TZ_CLOSED 0
}

#jtag scan chain
if { [info exists CPUTAPID] } {
	set _CPUTAPID $CPUTAPID
} else {
	if { [using_jtag] } {
		# See STM Document RM0438
		# RM0481 Rev0.3c, Section 58.3.1 JTAG debug port - Table 672. JTAG-DP data registers
		# Corresponds to Cortex®-M33 JTAG debug port ID code
		set _CPUTAPID 0x0ba04477
	} {
		# SWD IDCODE (single drop, arm)
		set _CPUTAPID 0x0be12477
	}
}

swj_newdap $_CHIPNAME cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_CPUTAPID
set _TARGETNAME $_CHIPNAME.cpu

dap create $_CHIPNAME.dap -chain-position $_TARGETNAME

target create $_CHIPNAME.ap0 mem_ap -endian $_ENDIAN -dap $_CHIPNAME.dap -ap-num 0

target create $_TARGETNAME cortex_m -endian $_ENDIAN -dap $_CHIPNAME.dap -ap-num 1 -gdb-max-connections 2

# use non-secure RAM by default
$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 1

tpiu create $_CHIPNAME.tpiu -dap $_CHIPNAME.dap -ap-num 1 -baseaddr 0xE0040000

# create sec/ns flash and otp memories (sizes will be probed)
flash bank $_CHIPNAME.flash_ns      stm32h5x 0x08000000 0 0 0 $_TARGETNAME
flash bank $_CHIPNAME.flash_alias_s stm32h5x 0x0C000000 0 0 0 $_TARGETNAME

if { [info exists FLASH_LOADERS] } {
	foreach FLASH_LOADER $FLASH_LOADERS {
		flash bank $FLASH_LOADER stldr 0 0 0 0 [target current] $FLASH_LOADER
	}
}

if { [info exists CLOCK_FREQ] } {
	set _CLOCK_FREQ $CLOCK_FREQ
} else {
	set _CLOCK_FREQ 4000
}
adapter speed $_CLOCK_FREQ

adapter srst delay 100
if {[using_jtag]} {
	jtag_ntrst_delay 100
}

if { ![using_hla] } {
	# if srst is not fitted use SYSRESETREQ to
	# perform a soft reset
	cortex_m reset_config sysresetreq
}

proc stm32h5x_is_secure {} {
	# read Debug Security Control and Status Regsiter (DSCSR) and check CDS (bit 16)
	set DSCSR [mrw 0xE000EE08]
	set secure [expr {($DSCSR & (1 << 16)) != 0}]
	return $secure
}

proc stm32h5x_ahb_ap_non_secure_access {} {
	# SPROT=1=Non Secure access, Priv=1
	[[target current] cget -dap] apcsw 0x4B000000 0x4F000000
}

proc stm32h5x_ahb_ap_secure_access {} {
	# SPROT=0=Secure access, Priv=1
	[[target current] cget -dap] apcsw 0x0B000000 0x4F000000
}

# get _CHIPNAME from current target
proc stm32h5x_get_chipname {} {
	set t [target current]
	set sep [string last "." $t]
	if {$sep == -1} {
		return $t
	}
	return [string range $t 0 [expr {$sep - 1}]]
}

# like mrw, but with target selection
proc stm32h5x_mrw {used_target reg} {
	return [$used_target read_memory $reg 32 1]
}

# like mmw, but with target selection
proc stm32h5x_mmw {used_target reg setbits clearbits} {
	set old [stm32h5x_mrw $used_target $reg]
	set new [expr {($old & ~$clearbits) | $setbits}]
	$used_target mww $reg $new
}

proc stm32h5x_enter_debug {} {
global _USE_LOADER_IN_TZ_CLOSED

	set _CHIPNAME [stm32h5x_get_chipname]

	# check security status
	set secure [stm32h5x_is_secure]

	# check flash options, from OPTSR_CUR and OPTSR2_CUR registers
	set optsr [mrw 0x40022050]
	set optsr2 [mrw 0x40022070]

	# read TrustZone state
	set tzen [expr {($optsr2 >> 24) & 0xFF}]
	set tz [expr {$tzen == 0xB4}]

	if {$secure || $tz} {
		stm32h5x_ahb_ap_secure_access
	} else {
		stm32h5x_ahb_ap_non_secure_access
	}

	# print the secure state only when it changes
	global $_CHIPNAME.secure
	set initialized [info exists $_CHIPNAME.secure]

	if {!$initialized || $secure != [set $_CHIPNAME.secure]} {
		# update saved security state
		set $_CHIPNAME.secure $secure

		set secure_str [expr {$secure ? "Secure" : "Non-Secure"}]
		echo "$_CHIPNAME.cpu in $secure_str state"
	}

	# avoid some noise, when reset is asserted OPTSR registers are read as zero
	if {$optsr == 0 || $optsr2 == 0} { return }

	# read product state
	set pstate [expr {($optsr >> 8) & 0xFF}]

	set PSTATE_OPEN             0xED
	set PSTATE_PROVISIONING		0x17
	set PSTATE_IROT_PROVISIONED	0x2E
	set PSTATE_TZ_CLOSED		0xC6
	set PSTATE_CLOSED			0x72

	# ensure that workarea is correctly configured if there is a change in tz or pstate
	global $_CHIPNAME.tz $_CHIPNAME.pstate
	set initialized [expr {[info exists $_CHIPNAME.tz] && [info exists $_CHIPNAME.pstate]}]

	# return here if there is no config change
	if { $initialized && $tz == [set $_CHIPNAME.tz] && $pstate == [set $_CHIPNAME.pstate] } {
		return
	}

	# update saved tz and pstate
	set $_CHIPNAME.tz $tz
	set $_CHIPNAME.pstate $pstate

	set tz_state_str [expr {$tz ? "enabled" : "disabled"}]
	echo "$_CHIPNAME TrustZone $tz_state_str"

	# use secure workarea only when TrustZone is enabled and pstate is PSTATE_OPEN
	set use_secure_workarea 0
	if {$tz && ($pstate == $PSTATE_OPEN)} {
		set use_secure_workarea 1
	}

	set workarea_addr [$_CHIPNAME.cpu cget -work-area-phys]

	if {$use_secure_workarea} {
		set workarea_addr [expr {$workarea_addr | 0x10000000}]
	} else {
		set workarea_addr [expr {$workarea_addr & ~0x10000000}]
	}

	set workarea_addr_str [format "0x%08X" $workarea_addr]
	echo "$_CHIPNAME.cpu work-area address is set to $workarea_addr_str"
	$_CHIPNAME.cpu configure -work-area-phys $workarea_addr

	# when PSTATE is not OPEN, only non-secure flash could be programmed
	# but workarea is not accessible since the RAM is secure.
	# to fall back to programming without loader set the workarea size to zero

	global $_CHIPNAME.workarea_size
	if {![info exists $_CHIPNAME.workarea_size]} {
		set $_CHIPNAME.workarea_size [$_CHIPNAME.cpu cget -work-area-size]
	}

	if {($pstate != $PSTATE_OPEN) && !$_USE_LOADER_IN_TZ_CLOSED} {
		set workarea_size 0
		echo "$_CHIPNAME.cpu work-area is disabled"
	} else {
		set workarea_size [set $_CHIPNAME.workarea_size]
		echo "$_CHIPNAME.cpu work-area is enabled"
	}

	$_CHIPNAME.cpu configure -work-area-size $workarea_size
}

$_TARGETNAME configure -event reset-init {
global _CLOCK_FREQ

	adapter speed $_CLOCK_FREQ
}

$_CHIPNAME.ap0 configure -event examine-end {
	set _CHIPNAME [stm32h5x_get_chipname]

	# Enable Trace Port and Clock (uses more power)
	# DBGMCU_CR |= TRACE_EN
	stm32h5x_mmw $_CHIPNAME.ap0 0xE0044004 0x00000020 0
}

$_TARGETNAME configure -event gdb-flash-erase-start {
	set _CHIPNAME [stm32h5x_get_chipname]
	global $_CHIPNAME.tz $_CHIPNAME.pstate

	# halt if cpu not in OPEN product state
	if {1 == [set $_CHIPNAME.tz] && 0xED != [set $_CHIPNAME.pstate]} {
		halt
	} else {
		reset init
	}
}

$_TARGETNAME configure -event reset-start {
	set _CHIPNAME [stm32h5x_get_chipname]
	global $_CHIPNAME.tz $_CHIPNAME.pstate
	global NS_Reset_Handler_address
	set initialized [expr {[info exists $_CHIPNAME.tz] && [info exists $_CHIPNAME.pstate]}]

	if {$initialized && 1 == [set $_CHIPNAME.tz] && 0xED != [set $_CHIPNAME.pstate]} {
		if {$halt} {
			halt
			# Don't get Non Secure Reset Handler address if it is entered in debug configuration
			if { ![info exists NS_Reset_Handler_address] } {
				set VTOR_S [mrw 0xe000ed08]
				set Reset_add [expr {($VTOR_S + 4)}]
				set NS_Reset_Handler_address [read_memory $Reset_add 32 1]
			}
			bp $NS_Reset_Handler_address 2 hw
		}
	}
}

$_TARGETNAME configure -event reset-end {
	set _CHIPNAME [stm32h5x_get_chipname]
	global $_CHIPNAME.tz $_CHIPNAME.pstate
	global NS_Reset_Handler_address
	set initialized [expr {[info exists $_CHIPNAME.tz] && [info exists $_CHIPNAME.pstate]}]

	if {$initialized && 1 == [set $_CHIPNAME.tz] && 0xED != [set $_CHIPNAME.pstate]} {
		if {$halt} {
			if { ![info exists NS_Reset_Handler_address] } {
				set VTOR_S [mrw 0xe000ed08]
				set Reset_add [expr {($VTOR_S + 4)}]
				set NS_Reset_Handler_address [read_memory $Reset_add 32 1]
			}
			rbp $NS_Reset_Handler_address
		}
	}

	global FLASH_LOADERS
	global NAME_INITIALIZED_LOADER
	global INDEX_INITIALIZED_LOADER

	if { [info exists INDEX_INITIALIZED_LOADER] } {
		set NAME_INITIALIZED_LOADER [lindex $FLASH_LOADERS $INDEX_INITIALIZED_LOADER]
		set flash_list [flash list]
		for {set i 0} {$i < [llength $flash_list]} {incr i} {
			if { [lindex [lindex $flash_list  $i] 1] == $NAME_INITIALIZED_LOADER } {
				set INIT_BANK_ID $i
				break
			}
		}
		if { ![info exists INIT_BANK_ID] } {
			echo "ERROR: can't find bank_id for stldr init"
		} else {
			stldr init $INIT_BANK_ID
		}
	}
}

$_TARGETNAME configure -event examine-end {
global _ENABLE_LOW_POWER
global _STOP_WATCHDOG

	stm32h5x_enter_debug

	set _CHIPNAME [stm32h5x_get_chipname]

	if { $_ENABLE_LOW_POWER == 1 } {
		# Enable debug during low power modes (uses more power)
		# DBGMCU_CR |= DBG_STANDBY | DBG_STOP
		stm32h5x_mmw $_CHIPNAME.ap0 0xE0044004 0x00000006 0
	} else {
		# Disable debug during low power modes
		# DBGMCU_CR |= ~(DBG_STANDBY | DBG_STOP)
		stm32h5x_mmw $_CHIPNAME.ap0 0xE0044004 0 0x00000006
	}
	if { $_STOP_WATCHDOG == 1 } {
		# Stop watchdog counters during halt
		# DBGMCU_APB1_FZ |= DBG_IWDG_STOP | DBG_WWDG_STOP
		stm32h5x_mmw $_CHIPNAME.ap0 0xE0044008 0x00001800 0
	} else {
		# Don't stop watchdog counters during halt
		# DBGMCU_APB1_FZ |= ~(DBG_IWDG_STOP | DBG_WWDG_STOP)
		stm32h5x_mmw $_CHIPNAME.ap0 0xE0044008 0 0x00001800
	}
}

$_TARGETNAME configure -event halted {
	stm32h5x_enter_debug
}

proc first_gdb_attach_hook {} {
global _CONNECT_UNDER_RESET

	# Needed to be able to use the connect_assert_srst in reset_config
	# otherwise, wrong value when reading device flash size register
	if { $_CONNECT_UNDER_RESET == 1 } {
		reset init
	}
}

$_TARGETNAME configure -event gdb-attach { gdb_attach_hook }
$_TARGETNAME configure -event gdb-detach { gdb_detach_hook }
