aboutsummaryrefslogtreecommitdiff
path: root/tcl/target/gd32vf103.cfg
blob: 77fdff7a235d2e4156ee547b389d73275ea143f4 (plain)
1
2
3
4
5
6
7
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# SPDX-License-Identifier: GPL-2.0-or-later

#
# GigaDevice GD32VF103 target
#
# https://www.gigadevice.com/products/microcontrollers/gd32/risc-v/
#

adapter speed 1000
source [find mem_helper.tcl]

transport select jtag

reset_config srst_nogate

# The smallest RAM size 6kB (GD32VF103C4/T4/R4)
if { [info exists WORKAREASIZE] } {
   set _WORKAREASIZE $WORKAREASIZE
} else {
   set _WORKAREASIZE 0x1800
}

set _CHIPNAME gd32vf103
# The vendor's configuration expects an ID of 0x1e200a6d, but this one is what
# I have on my board (Sipeed Longan Nano, GD32VF103CBT6).
jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x1000563d
jtag newtap $_CHIPNAME bs -irlen 5 -expected-id 0x790007a3

set _TARGETNAME $_CHIPNAME.cpu
target create $_TARGETNAME riscv -chain-position $_TARGETNAME
$_TARGETNAME riscv set_enable_virt2phys off

proc default_mem_access {} {
	riscv set_mem_access progbuf
}

default_mem_access

$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0

set _FLASHNAME $_CHIPNAME.flash
flash bank $_FLASHNAME stm32f1x 0x08000000 0 0 0 $_TARGETNAME

# Address 0 is only aliased to main flash when the chip is not running its
# built-in bootloader. When it is, it's instead aliased to a read only section
# of flash at 0x1fffb000. However, we can't detect or dynamically switch this,
# so just pretend it's always aliased to main flash. We need to tell OpenOCD
# about this alias because otherwise we'll try to use software breakpoints on
# code in flash, which don't work because flash mappings are read-only.
flash bank $_CHIPNAME.flashalias virtual 0x0 0 0 0 $_TARGETNAME $_FLASHNAME

# On this chip, ndmreset (the debug module bit that triggers a software reset)
# doesn't work. So for JTAG connections without an SRST, we need to trigger a
# reset manually. This is an undocumented reset sequence that's used by the
# JTAG flashing script in the vendor-supplied GD32VF103 PlatformIO plugin:
#
#   https://github.com/sipeed/platform-gd32v/commit/f9cbb44819bc05dd2010cc815c32be0486800cc2
#
$_TARGETNAME configure -event reset-assert {
	set dmcontrol 		0x10
	set dmcontrol_dmactive	[expr 1 << 0]
	set dmcontrol_haltreq	[expr 1 << 31]

	global _RESETMODE
	global _TARGETNAME

	# Halt the core so that we can write to memory. We do this first so
	# that it doesn't clobber our dmcontrol configuration.
	halt

	# Set haltreq appropriately for the type of reset we're doing. This
	# replicates what the generic RISC-V reset_assert() function would
	# do if we weren't overriding it. The $_RESETMODE hack sucks, but
	# it's the least invasive way to determine whether we need to halt,
	# and psoc6.cfg already uses the same trick. (reset_deassert(), which
	# does run, also does this, but at that point it may be too late: the
	# reset has already been triggered, so there's a race between it and
	# the haltreq write.)
	#
	# If we didn't override the generic handler, we'd actually still have
	# to do this: the default handler sets ndmreset, which prevents memory
	# access even though it doesn't actually trigger a reset on this chip.
	# So we'd need to unset it here, which involves a write to dmcontrol,
	# Since haltreq is write-only and there's no way to leave it unchanged,
	# we'd have to figure out its proper value anyway.
	set val $dmcontrol_dmactive
	if {$_RESETMODE ne "run"} {
		set val [expr $val | $dmcontrol_haltreq]
	}
	$_TARGETNAME riscv dmi_write $dmcontrol $val

	# Unlock 0xe0042008 so that the next write triggers a reset
	$_TARGETNAME mww 0xe004200c 0x4b5a6978

	# We need to trigger the reset using abstract memory access, since
	# progbuf access tries to read a status code out of a core register
	# after the write happens, which fails when the core is in reset.
	riscv set_mem_access abstract

	# Go!
	$_TARGETNAME mww 0xe0042008 0x1

	# Put the memory access mode back to what it was.
	default_mem_access
}

# Capture the mode of a given reset so that we can use it later in the
# reset-assert handler.
proc init_reset { mode } {
	global _RESETMODE
	set _RESETMODE $mode

	if {[using_jtag]} {
		jtag arp_init-reset
	}
}

# On this chip, ndmreset (the debug module bit that triggers a software reset)
# doesn't work. So for JTAG connections without an SRST, we need to trigger a
# reset manually. This is an undocumented reset sequence that's used by the
# JTAG flashing script in the vendor-supplied GD32VF103 PlatformIO plugin:
#
#   https://github.com/sipeed/platform-gd32v/commit/f9cbb44819bc05dd2010cc815c32be0486800cc2
#
$_TARGETNAME configure -event reset-assert {
	set dmcontrol 		0x10
	set dmcontrol_dmactive	[expr {1 << 0}]
	set dmcontrol_ackhavereset [expr {1 << 28}]
	set dmcontrol_haltreq	[expr {1 << 31}]

	global _RESETMODE

	# If hardware NRST signal is connected and configured (reset_config srst_only)
	# the device has been recently reset in 'jtag arp_init-reset', therefore
	# DM_DMSTATUS_ANYHAVERESET reads 1.
	# The following 'halt' command checks this status bit
	# and shows 'Hart 0 unexpectedly reset!' if set.
	# Prevent this message by sending an acknowledge first.
	set val [expr {$dmcontrol_dmactive | $dmcontrol_ackhavereset}]
	riscv dmi_write $dmcontrol $val

	# Halt the core so that we can write to memory. We do this first so
	# that it doesn't clobber our dmcontrol configuration.
	halt

	# Set haltreq appropriately for the type of reset we're doing. This
	# replicates what the generic RISC-V reset_assert() function would
	# do if we weren't overriding it. The $_RESETMODE hack sucks, but
	# it's the least invasive way to determine whether we need to halt.
	#
	# If we didn't override the generic handler, we'd actually still have
	# to do this: the default handler sets ndmreset, which prevents memory
	# access even though it doesn't actually trigger a reset on this chip.
	# So we'd need to unset it here, which involves a write to dmcontrol,
	# Since haltreq is write-only and there's no way to leave it unchanged,
	# we'd have to figure out its proper value anyway.
	set val $dmcontrol_dmactive
	if {$_RESETMODE ne "run"} {
		set val [expr {$val | $dmcontrol_haltreq}]
	}
	riscv dmi_write $dmcontrol $val

	# Unlock 0xe0042008 so that the next write triggers a reset
	mww 0xe004200c 0x4b5a6978

	# We need to trigger the reset using abstract memory access, since
	# progbuf access tries to read a status code out of a core register
	# after the write happens, which fails when the core is in reset.
	riscv set_mem_access abstract

	# Go!
	mww 0xe0042008 0x1

	# Put the memory access mode back to what it was.
	default_mem_access
}

# Capture the mode of a given reset so that we can use it later in the
# reset-assert handler.
proc init_reset { mode } {
	global _RESETMODE
	set _RESETMODE $mode

	if {[using_jtag]} {
		jtag arp_init-reset
	}
}