add jtag to ESP32
This commit is contained in:
parent
853d12f17c
commit
a15c797e93
Binary file not shown.
|
@ -0,0 +1,63 @@
|
||||||
|
# USE jtag for openocd in ESP32
|
||||||
|
|
||||||
|
## board
|
||||||
|
|
||||||
|
ESP-C3-32S-Kit https://docs.ai-thinker.com/_media/esp32/docs/esp-c3-32s-kit-v1.0_规格书.pdf
|
||||||
|
|
||||||
|
## install JLink deb software and driver
|
||||||
|
|
||||||
|
## build esp32-c3 binary
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# At esp-idf
|
||||||
|
source export.sh
|
||||||
|
cd examples/get-started/blink
|
||||||
|
idf.py set-target esp32-c3
|
||||||
|
idf.py menuconfig # config the io and LED mode
|
||||||
|
idf.py build
|
||||||
|
idf.py -p (PORT) flash # to flash to module to verify the effect of binary by USART port
|
||||||
|
```
|
||||||
|
|
||||||
|
## 配置ESP32
|
||||||
|
|
||||||
|
由于 ESP32-C3 默认选用 内置的 USB_SERIAL_JTAG 外设。此时需要烧录 efuse 来选择外接 JTAG 适配器,有以下两种方式:
|
||||||
|
|
||||||
|
* 烧毁 DIS_USB_JTAG eFuse: 将永久禁用 USB_SERIAL_JTAG 和 CPU 的 JTAG 端口之间的连接。 然后可以将 JTAG 接口连接到 GPIO4 - GPIO7。 请注意,USB_SERIAL_JTAG 的 USB CDC 功能仍然可用,即仍然可以通过 USB CDC 进行烧录和 log 查看。
|
||||||
|
* 烧毁 JTAG_SEL_ENABLE eFuse: 将启用由 Strapping 引脚 GPIO10 选择的 JTAG 接口。 如果 ESP32-C3 复位时 Strapping 引脚为低电平,则 JTAG 接口将使用 GPIO4 - GPIO7。 如果 Strapping 引脚为高电平,则 USB_SERIAL_JTAG 将用作 JTAG 接口。
|
||||||
|
|
||||||
|
`espefuse.py burn_efuse JTAG_SEL_ENABLE -p /dev/ttyUSB0`
|
||||||
|
然后你会得到一个需要输入BURN 的确认信息,如下:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
Connecting....
|
||||||
|
Detecting chip type... ESP32-C3
|
||||||
|
espefuse.py v3.3-dev
|
||||||
|
The efuses to burn:
|
||||||
|
from BLOCK0
|
||||||
|
- JTAG_SEL_ENABLE
|
||||||
|
|
||||||
|
Burning efuses:
|
||||||
|
|
||||||
|
- 'JTAG_SEL_ENABLE' (Disables USB JTAG. JTAG access via pads is controlled separately) 0b0 -> 0b1
|
||||||
|
|
||||||
|
Check all blocks for burn...
|
||||||
|
idx, BLOCK_NAME, Conclusion
|
||||||
|
[00] BLOCK0 is not empty
|
||||||
|
(written ): 0x000000000000008000000000000000000000800000000000
|
||||||
|
(to write): 0x000000000000000000000000000000000000020000000000
|
||||||
|
(coding scheme = NONE)
|
||||||
|
.
|
||||||
|
This is an irreversible operation!
|
||||||
|
Type 'BURN' (all capitals) to continue.
|
||||||
|
```
|
||||||
|
|
||||||
|
## usage
|
||||||
|
|
||||||
|
* 链接JLink的 jtag 四个IO,`TMS` `TDI` `TDO` `TCK`
|
||||||
|
* 默认固件开发板的JTAG IO不能用于其他功能,必须是闲置状态
|
||||||
|
* Must use esp's `openocd` by source export.sh in esp-idy, after esp-idf installed.
|
||||||
|
|
||||||
|
## cmd
|
||||||
|
|
||||||
|
* `./openocd.sh` start openocd
|
||||||
|
* `./gdb.sh` start gdb
|
|
@ -0,0 +1,59 @@
|
||||||
|
#----------------------------------------
|
||||||
|
# Purpose - Create some $BIT variables
|
||||||
|
# Create $K and $M variables
|
||||||
|
# and some bit field extraction variables.
|
||||||
|
# Creat helper variables ...
|
||||||
|
# BIT0.. BIT31
|
||||||
|
|
||||||
|
for { set x 0 } { $x < 32 } { set x [expr $x + 1]} {
|
||||||
|
set vn [format "BIT%d" $x]
|
||||||
|
global $vn
|
||||||
|
set $vn [expr (1 << $x)]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create K bytes values
|
||||||
|
# __1K ... to __2048K
|
||||||
|
for { set x 1 } { $x < 2048 } { set x [expr $x * 2]} {
|
||||||
|
set vn [format "__%dK" $x]
|
||||||
|
global $vn
|
||||||
|
set $vn [expr (1024 * $x)]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create M bytes values
|
||||||
|
# __1M ... to __2048K
|
||||||
|
for { set x 1 } { $x < 2048 } { set x [expr $x * 2]} {
|
||||||
|
set vn [format "__%dM" $x]
|
||||||
|
global $vn
|
||||||
|
set $vn [expr (1024 * 1024 * $x)]
|
||||||
|
}
|
||||||
|
|
||||||
|
proc create_mask { MSB LSB } {
|
||||||
|
return [expr (((1 << ($MSB - $LSB + 1))-1) << $LSB)]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Cut Bits $MSB to $LSB out of this value.
|
||||||
|
# Example: % format "0x%08x" [extract_bitfield 0x12345678 27 16]
|
||||||
|
# Result: 0x02340000
|
||||||
|
|
||||||
|
proc extract_bitfield { VALUE MSB LSB } {
|
||||||
|
return [expr [create_mask $MSB $LSB] & $VALUE]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Cut bits $MSB to $LSB out of this value
|
||||||
|
# and shift (normalize) them down to bit 0.
|
||||||
|
#
|
||||||
|
# Example: % format "0x%08x" [normalize_bitfield 0x12345678 27 16]
|
||||||
|
# Result: 0x00000234
|
||||||
|
#
|
||||||
|
proc normalize_bitfield { VALUE MSB LSB } {
|
||||||
|
return [expr [extract_bitfield $VALUE $MSB $LSB ] >> $LSB]
|
||||||
|
}
|
||||||
|
|
||||||
|
proc show_normalize_bitfield { VALUE MSB LSB } {
|
||||||
|
set m [create_mask $MSB $LSB]
|
||||||
|
set mr [expr $VALUE & $m]
|
||||||
|
set sr [expr $mr >> $LSB]
|
||||||
|
echo [format "((0x%08x & 0x%08x) -> 0x%08x) >> %2d => (0x%x) %5d " $VALUE $m $mr $LSB $sr $sr]
|
||||||
|
return $sr
|
||||||
|
}
|
Binary file not shown.
|
@ -0,0 +1,134 @@
|
||||||
|
# The ESP32-C3 only supports JTAG.
|
||||||
|
transport select jtag
|
||||||
|
|
||||||
|
set CPU_MAX_ADDRESS 0xFFFFFFFF
|
||||||
|
source [find bitsbytes.tcl]
|
||||||
|
source [find memory.tcl]
|
||||||
|
source [find mmr_helpers.tcl]
|
||||||
|
# Source the ESP common configuration file
|
||||||
|
source [find target/esp_common.cfg]
|
||||||
|
|
||||||
|
# Target specific registers
|
||||||
|
set EFUSE_MAC_ADDR_REG 0x60008844
|
||||||
|
|
||||||
|
if { [info exists CHIPNAME] } {
|
||||||
|
set _CHIPNAME $CHIPNAME
|
||||||
|
} else {
|
||||||
|
set _CHIPNAME esp32c3
|
||||||
|
}
|
||||||
|
|
||||||
|
if { [info exists CPUTAPID] } {
|
||||||
|
set _CPUTAPID $CPUTAPID
|
||||||
|
} else {
|
||||||
|
set _CPUTAPID 0x00005c25
|
||||||
|
}
|
||||||
|
|
||||||
|
set _TARGETNAME $_CHIPNAME
|
||||||
|
set _CPUNAME cpu
|
||||||
|
set _TAPNAME $_CHIPNAME.$_CPUNAME
|
||||||
|
|
||||||
|
jtag newtap $_CHIPNAME $_CPUNAME -irlen 5 -expected-id $_CPUTAPID
|
||||||
|
|
||||||
|
proc esp32c3_wdt_disable { } {
|
||||||
|
# Halt event can occur during config phase (before "init" is done).
|
||||||
|
# Ignore it since mww commands don't work at that time.
|
||||||
|
if { [string compare [command mode] config] == 0 } {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
# Timer Group 0 & 1 WDTs
|
||||||
|
mww 0x6001f064 0x50D83AA1
|
||||||
|
mww 0x6001F048 0
|
||||||
|
mww 0x60020064 0x50D83AA1
|
||||||
|
mww 0x60020048 0
|
||||||
|
# RTC WDT
|
||||||
|
mww 0x600080a8 0x50D83AA1
|
||||||
|
mww 0x60008090 0
|
||||||
|
# SWD
|
||||||
|
mww 0x600080b0 0x8F1D312A
|
||||||
|
mww 0x600080ac 0x84B00000
|
||||||
|
}
|
||||||
|
|
||||||
|
proc esp32c3_soc_reset { } {
|
||||||
|
# This procedure does "digital system reset", i.e. resets
|
||||||
|
# all the peripherals except for the RTC block.
|
||||||
|
# It is called from reset-assert-post target event callback,
|
||||||
|
# after assert_reset procedure was called.
|
||||||
|
# Since we need the hart to to execute a write to RTC_CNTL_SW_SYS_RST,
|
||||||
|
# temporarily take it out of reset. Save the dmcontrol state before
|
||||||
|
# doing so.
|
||||||
|
riscv dmi_write 0x10 0x80000001
|
||||||
|
# If SBA is used to trigger the reset, the debug module gets stuck
|
||||||
|
# in a busy state waiting for the bus write to complete (HW quirk?),
|
||||||
|
# so trigger the reset by CPU instead.
|
||||||
|
riscv set_prefer_sba off
|
||||||
|
mww 0x60008000 0x9c00a000
|
||||||
|
# Workaround for stuck in cpu start during calibration.
|
||||||
|
# By writing zero to TIMG_RTCCALICFG_REG, we are disabling calibration
|
||||||
|
mww 0x6001F068 0
|
||||||
|
# Wait for the reset to happen
|
||||||
|
sleep 10
|
||||||
|
poll
|
||||||
|
# Restore SBA setting
|
||||||
|
riscv set_prefer_sba on
|
||||||
|
# Disable the watchdogs again
|
||||||
|
esp32c3_wdt_disable
|
||||||
|
# Put the hart back into reset state. Note that we need to keep haltreq set.
|
||||||
|
riscv dmi_write 0x10 0x80000003
|
||||||
|
}
|
||||||
|
|
||||||
|
proc esp32c3_memprot_is_enabled { } {
|
||||||
|
# IRAM0 PMS lock, SENSITIVE_CORE_X_IRAM0_PMS_CONSTRAIN_0_REG
|
||||||
|
if { [mmr_get_bit 0x600C10A8 0] != 0 } {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
# DRAM0 PMS lock, SENSITIVE_CORE_X_DRAM0_PMS_CONSTRAIN_0_REG
|
||||||
|
if { [mmr_get_bit 0x600C10C0 0] != 0 } {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if { $_RTOS == "none" } {
|
||||||
|
target create $_TARGETNAME esp32c3 -chain-position $_TAPNAME
|
||||||
|
} else {
|
||||||
|
target create $_TARGETNAME esp32c3 -chain-position $_TAPNAME -rtos $_RTOS
|
||||||
|
}
|
||||||
|
|
||||||
|
$_TARGETNAME configure -event reset-assert-post { esp32c3_soc_reset }
|
||||||
|
$_TARGETNAME configure -event halted {
|
||||||
|
esp32c3_wdt_disable
|
||||||
|
}
|
||||||
|
$_TARGETNAME configure -event examine-end {
|
||||||
|
# Need this to handle 'apptrace init' syscall correctly because semihosting is not enabled by default
|
||||||
|
arm semihosting enable
|
||||||
|
arm semihosting_resexit enable
|
||||||
|
}
|
||||||
|
$_TARGETNAME configure -event gdb-attach {
|
||||||
|
# 'halt' is necessary to auto-probe flash bank when GDB is connected and generate proper memory map
|
||||||
|
halt
|
||||||
|
if { [esp32c3_memprot_is_enabled] } {
|
||||||
|
# 'reset halt' to disable memory protection and allow flasher to work correctly
|
||||||
|
echo "Memory protection is enabled. Reset target to disable it..."
|
||||||
|
reset halt
|
||||||
|
}
|
||||||
|
# by default mask interrupts while stepping
|
||||||
|
riscv maskisr steponly
|
||||||
|
}
|
||||||
|
|
||||||
|
# stub flasher may need a lot of memory in case of compressed writes to flash (~107KB):
|
||||||
|
# - for apptrace: 2x16KB up buffers + 32KB down buffer
|
||||||
|
# - for uncompression: 32KB for unzip buffer size + 11KB for inflator data structs
|
||||||
|
# TODO: In general when up buffers are swapped apptrace copies `host->target` data from new up buffer to down buffer to free space for `target->host` data.
|
||||||
|
# In case of flash writes we use apptrace transfers in one direction only. So we can avoid copying and re-use up buffer instead of down one.
|
||||||
|
configure_esp_workarea $_TARGETNAME 0x40380000 0x4000 0x3FC84000 0x20000
|
||||||
|
configure_esp_flash_bank $_TARGETNAME $_TARGETNAME $_FLASH_SIZE
|
||||||
|
|
||||||
|
if { $_FLASH_SIZE == 0 } {
|
||||||
|
gdb_breakpoint_override hard
|
||||||
|
}
|
||||||
|
|
||||||
|
riscv set_reset_timeout_sec 2
|
||||||
|
riscv set_command_timeout_sec 5
|
||||||
|
riscv set_prefer_sba on
|
|
@ -0,0 +1,5 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
source ~/develop/esp-idf/export.sh
|
||||||
|
riscv32-esp-elf-gdb -x gdbinit blink.elf
|
|
@ -0,0 +1,7 @@
|
||||||
|
target remote :3333
|
||||||
|
set remote hardware-watchpoint-limit 2
|
||||||
|
mon reset halt
|
||||||
|
flushregs
|
||||||
|
thb app_main
|
||||||
|
directory /home/colin/develop/esp-idf
|
||||||
|
c
|
|
@ -0,0 +1,16 @@
|
||||||
|
#
|
||||||
|
# SEGGER J-Link
|
||||||
|
#
|
||||||
|
# http://www.segger.com/jlink.html
|
||||||
|
#
|
||||||
|
|
||||||
|
adapter driver jlink
|
||||||
|
adapter speed 1000
|
||||||
|
|
||||||
|
|
||||||
|
# The serial number can be used to select a specific device in case more than
|
||||||
|
# one is connected to the host.
|
||||||
|
#
|
||||||
|
# Example: Select J-Link with serial number 123456789
|
||||||
|
#
|
||||||
|
# adapter serial 123456789
|
|
@ -0,0 +1,187 @@
|
||||||
|
# MEMORY
|
||||||
|
#
|
||||||
|
# All Memory regions have two components.
|
||||||
|
# (1) A count of regions, in the form N_NAME
|
||||||
|
# (2) An array within info about each region.
|
||||||
|
#
|
||||||
|
# The ARRAY
|
||||||
|
#
|
||||||
|
# <NAME>( RegionNumber , ATTRIBUTE )
|
||||||
|
#
|
||||||
|
# Where <NAME> is one of:
|
||||||
|
#
|
||||||
|
# N_FLASH & FLASH (internal memory)
|
||||||
|
# N_RAM & RAM (internal memory)
|
||||||
|
# N_MMREGS & MMREGS (for memory mapped registers)
|
||||||
|
# N_XMEM & XMEM (off chip memory, ie: flash on cs0, sdram on cs2)
|
||||||
|
# or N_UNKNOWN & UNKNOWN for things that do not exist.
|
||||||
|
#
|
||||||
|
# We have 1 unknown region.
|
||||||
|
set N_UNKNOWN 1
|
||||||
|
# All MEMORY regions must have these attributes
|
||||||
|
# CS - chip select (if internal, use -1)
|
||||||
|
set UNKNOWN(0,CHIPSELECT) -1
|
||||||
|
# BASE - base address in memory
|
||||||
|
set UNKNOWN(0,BASE) 0
|
||||||
|
# LEN - length in bytes
|
||||||
|
set UNKNOWN(0,LEN) $CPU_MAX_ADDRESS
|
||||||
|
# HUMAN - human name of the region
|
||||||
|
set UNKNOWN(0,HUMAN) "unknown"
|
||||||
|
# TYPE - one of:
|
||||||
|
# flash, ram, mmr, unknown
|
||||||
|
# For harvard arch:
|
||||||
|
# iflash, dflash, iram, dram
|
||||||
|
set UNKNOWN(0,TYPE) "unknown"
|
||||||
|
# RWX - access ablity
|
||||||
|
# unix style chmod bits
|
||||||
|
# 0 - no access
|
||||||
|
# 1 - execute
|
||||||
|
# 2 - write
|
||||||
|
# 4 - read
|
||||||
|
# hence: 7 - readwrite execute
|
||||||
|
set RWX_NO_ACCESS 0
|
||||||
|
set RWX_X_ONLY $BIT0
|
||||||
|
set RWX_W_ONLY $BIT1
|
||||||
|
set RWX_R_ONLY $BIT2
|
||||||
|
set RWX_RW [expr $RWX_R_ONLY + $RWX_W_ONLY]
|
||||||
|
set RWX_R_X [expr $RWX_R_ONLY + $RWX_X_ONLY]
|
||||||
|
set RWX_RWX [expr $RWX_R_ONLY + $RWX_W_ONLY + $RWX_X_ONLY]
|
||||||
|
set UNKNOWN(0,RWX) $RWX_NO_ACCESS
|
||||||
|
|
||||||
|
# WIDTH - access width
|
||||||
|
# 8,16,32 [0 means ANY]
|
||||||
|
set ACCESS_WIDTH_NONE 0
|
||||||
|
set ACCESS_WIDTH_8 $BIT0
|
||||||
|
set ACCESS_WIDTH_16 $BIT1
|
||||||
|
set ACCESS_WIDTH_32 $BIT2
|
||||||
|
set ACCESS_WIDTH_ANY [expr $ACCESS_WIDTH_8 + $ACCESS_WIDTH_16 + $ACCESS_WIDTH_32]
|
||||||
|
set UNKNOWN(0,ACCESS_WIDTH) $ACCESS_WIDTH_NONE
|
||||||
|
|
||||||
|
proc iswithin { ADDRESS BASE LEN } {
|
||||||
|
return [expr ((($ADDRESS - $BASE) >= 0) && (($BASE + $LEN - $ADDRESS) > 0))]
|
||||||
|
}
|
||||||
|
|
||||||
|
proc address_info { ADDRESS } {
|
||||||
|
|
||||||
|
foreach WHERE { FLASH RAM MMREGS XMEM UNKNOWN } {
|
||||||
|
if { info exists $WHERE } {
|
||||||
|
set lmt [set N_[set WHERE]]
|
||||||
|
for { set region 0 } { $region < $lmt } { incr region } {
|
||||||
|
if { iswithin $ADDRESS $WHERE($region,BASE) $WHERE($region,LEN) } {
|
||||||
|
return "$WHERE $region";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Return the 'unknown'
|
||||||
|
return "UNKNOWN 0"
|
||||||
|
}
|
||||||
|
|
||||||
|
proc memread32 {ADDR} {
|
||||||
|
set foo(0) 0
|
||||||
|
if ![ catch { mem2array foo 32 $ADDR 1 } msg ] {
|
||||||
|
return $foo(0)
|
||||||
|
} else {
|
||||||
|
error "memread32: $msg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
proc memread16 {ADDR} {
|
||||||
|
set foo(0) 0
|
||||||
|
if ![ catch { mem2array foo 16 $ADDR 1 } msg ] {
|
||||||
|
return $foo(0)
|
||||||
|
} else {
|
||||||
|
error "memread16: $msg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
proc memread8 {ADDR} {
|
||||||
|
set foo(0) 0
|
||||||
|
if ![ catch { mem2array foo 8 $ADDR 1 } msg ] {
|
||||||
|
return $foo(0)
|
||||||
|
} else {
|
||||||
|
error "memread8: $msg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
proc memwrite32 {ADDR DATA} {
|
||||||
|
set foo(0) $DATA
|
||||||
|
if ![ catch { array2mem foo 32 $ADDR 1 } msg ] {
|
||||||
|
return $foo(0)
|
||||||
|
} else {
|
||||||
|
error "memwrite32: $msg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
proc memwrite16 {ADDR DATA} {
|
||||||
|
set foo(0) $DATA
|
||||||
|
if ![ catch { array2mem foo 16 $ADDR 1 } msg ] {
|
||||||
|
return $foo(0)
|
||||||
|
} else {
|
||||||
|
error "memwrite16: $msg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
proc memwrite8 {ADDR DATA} {
|
||||||
|
set foo(0) $DATA
|
||||||
|
if ![ catch { array2mem foo 8 $ADDR 1 } msg ] {
|
||||||
|
return $foo(0)
|
||||||
|
} else {
|
||||||
|
error "memwrite8: $msg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
proc memread32_phys {ADDR} {
|
||||||
|
set foo(0) 0
|
||||||
|
if ![ catch { mem2array foo 32 $ADDR 1 phys } msg ] {
|
||||||
|
return $foo(0)
|
||||||
|
} else {
|
||||||
|
error "memread32: $msg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
proc memread16_phys {ADDR} {
|
||||||
|
set foo(0) 0
|
||||||
|
if ![ catch { mem2array foo 16 $ADDR 1 phys } msg ] {
|
||||||
|
return $foo(0)
|
||||||
|
} else {
|
||||||
|
error "memread16: $msg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
proc memread8_phys {ADDR} {
|
||||||
|
set foo(0) 0
|
||||||
|
if ![ catch { mem2array foo 8 $ADDR 1 phys } msg ] {
|
||||||
|
return $foo(0)
|
||||||
|
} else {
|
||||||
|
error "memread8: $msg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
proc memwrite32_phys {ADDR DATA} {
|
||||||
|
set foo(0) $DATA
|
||||||
|
if ![ catch { array2mem foo 32 $ADDR 1 phys } msg ] {
|
||||||
|
return $foo(0)
|
||||||
|
} else {
|
||||||
|
error "memwrite32: $msg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
proc memwrite16_phys {ADDR DATA} {
|
||||||
|
set foo(0) $DATA
|
||||||
|
if ![ catch { array2mem foo 16 $ADDR 1 phys } msg ] {
|
||||||
|
return $foo(0)
|
||||||
|
} else {
|
||||||
|
error "memwrite16: $msg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
proc memwrite8_phys {ADDR DATA} {
|
||||||
|
set foo(0) $DATA
|
||||||
|
if ![ catch { array2mem foo 8 $ADDR 1 phys } msg ] {
|
||||||
|
return $foo(0)
|
||||||
|
} else {
|
||||||
|
error "memwrite8: $msg"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
|
||||||
|
proc proc_exists { NAME } {
|
||||||
|
set n [info commands $NAME]
|
||||||
|
set l [string length $n]
|
||||||
|
return [expr $l != 0]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Give: REGISTER name - must be a global variable.
|
||||||
|
proc show_mmr32_reg { NAME } {
|
||||||
|
|
||||||
|
global $NAME
|
||||||
|
# we want $($NAME)
|
||||||
|
set a [set [set NAME]]
|
||||||
|
|
||||||
|
if ![catch { set v [memread32 $a] } msg ] {
|
||||||
|
echo [format "%15s: (0x%08x): 0x%08x" $NAME $a $v]
|
||||||
|
|
||||||
|
# Was a helper defined?
|
||||||
|
set fn show_${NAME}_helper
|
||||||
|
if [ proc_exists $fn ] {
|
||||||
|
# Then call it
|
||||||
|
$fn $NAME $a $v
|
||||||
|
}
|
||||||
|
return $v;
|
||||||
|
} else {
|
||||||
|
error [format "%s (%s)" $msg $NAME ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Give: NAMES - an array of names accessible
|
||||||
|
# in the callers symbol-scope.
|
||||||
|
# VAL - the bits to display.
|
||||||
|
|
||||||
|
proc show_mmr32_bits { NAMES VAL } {
|
||||||
|
|
||||||
|
upvar $NAMES MYNAMES
|
||||||
|
|
||||||
|
set w 5
|
||||||
|
foreach {IDX N} $MYNAMES {
|
||||||
|
set l [string length $N]
|
||||||
|
if { $l > $w } { set w $l }
|
||||||
|
}
|
||||||
|
|
||||||
|
for { set x 24 } { $x >= 0 } { incr x -8 } {
|
||||||
|
echo -n " "
|
||||||
|
for { set y 7 } { $y >= 0 } { incr y -1 } {
|
||||||
|
set s $MYNAMES([expr $x + $y])
|
||||||
|
echo -n [format "%2d: %-*s | " [expr $x + $y] $w $s ]
|
||||||
|
}
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo -n " "
|
||||||
|
for { set y 7 } { $y >= 0 } { incr y -1 } {
|
||||||
|
echo -n [format " %d%*s | " [expr !!($VAL & (1 << ($x + $y)))] [expr $w -1] ""]
|
||||||
|
}
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
proc show_mmr_bitfield { MSB LSB VAL FIELDNAME FIELDVALUES } {
|
||||||
|
set width [expr (($MSB - $LSB + 1) + 7) / 4]
|
||||||
|
set nval [show_normalize_bitfield $VAL $MSB $LSB ]
|
||||||
|
set name0 [lindex $FIELDVALUES 0 ]
|
||||||
|
if [ string compare $name0 _NUMBER_ ] {
|
||||||
|
set sval [lindex $FIELDVALUES $nval]
|
||||||
|
} else {
|
||||||
|
set sval ""
|
||||||
|
}
|
||||||
|
echo [format "%-15s: %d (0x%0*x) %s" $FIELDNAME $nval $width $nval $sval ]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Give: ADDR - address of the register.
|
||||||
|
# BIT - bit's number.
|
||||||
|
|
||||||
|
proc mmr_get_bit { ADDR BIT } {
|
||||||
|
set val [memread32 $ADDR]
|
||||||
|
set bit_val [expr $val & [expr 1 << $BIT]]
|
||||||
|
return $bit_val
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Give: ADDR - address of the register.
|
||||||
|
# MSB - MSB bit's number.
|
||||||
|
# LSB - LSB bit's number.
|
||||||
|
|
||||||
|
proc mmr_get_bitfield { ADDR MSB LSB } {
|
||||||
|
set rval [memread32 $ADDR]
|
||||||
|
return normalize_bitfield $rval $MSB $LSB
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
source ~/develop/esp-idf/export.sh
|
||||||
|
openocd -f jlink.cfg -f esp32c3.cfg
|
|
@ -0,0 +1,269 @@
|
||||||
|
# Common ESP chips definitions
|
||||||
|
|
||||||
|
if { [info exists ESP_RTOS] } {
|
||||||
|
set _RTOS "$ESP_RTOS"
|
||||||
|
} else {
|
||||||
|
set _RTOS "FreeRTOS"
|
||||||
|
}
|
||||||
|
|
||||||
|
if { [info exists ESP_SEMIHOST_BASEDIR] } {
|
||||||
|
set _SEMIHOST_BASEDIR "$ESP_SEMIHOST_BASEDIR"
|
||||||
|
} else {
|
||||||
|
# by default current dir (when OOCD has been started)
|
||||||
|
set _SEMIHOST_BASEDIR "."
|
||||||
|
}
|
||||||
|
|
||||||
|
if { [info exists ESP_FLASH_SIZE] } {
|
||||||
|
set _FLASH_SIZE $ESP_FLASH_SIZE
|
||||||
|
} else {
|
||||||
|
set _FLASH_SIZE "auto"
|
||||||
|
}
|
||||||
|
|
||||||
|
proc configure_esp_workarea { TGT CODE_ADDR CODE_SZ DATA_ADDR DATA_SZ } {
|
||||||
|
#WARNING: be careful when selecting working ares for code and data, they should not overlap due to ESP32 physical memory mappings
|
||||||
|
$TGT configure -work-area-phys $CODE_ADDR -work-area-virt $CODE_ADDR -work-area-size $CODE_SZ -work-area-backup 1
|
||||||
|
# since ESP32 cannot use single address space for code and data we need additional working area to keep data
|
||||||
|
$TGT configure -alt-work-area-phys $DATA_ADDR -alt-work-area-virt $DATA_ADDR -alt-work-area-size $DATA_SZ -alt-work-area-backup 1
|
||||||
|
}
|
||||||
|
|
||||||
|
proc configure_esp_workarea_backups { wab_list awab_list } {
|
||||||
|
set index 0
|
||||||
|
foreach tgt [target names] {
|
||||||
|
$tgt configure -work-area-backup [lindex $wab_list $index]
|
||||||
|
$tgt configure -alt-work-area-backup [lindex $awab_list $index]
|
||||||
|
incr $index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
proc configure_esp_flash_bank { TGT DRV SIZE } {
|
||||||
|
set _SIZE SIZE
|
||||||
|
if { $SIZE == 0 } {
|
||||||
|
echo "WARNING: ESP flash support is disabled!"
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
if { $SIZE == "auto" } {
|
||||||
|
# special value for flash driver
|
||||||
|
set _SIZE 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# whole flash for programming purposes
|
||||||
|
# TODO: remove it when support for GDB's 'load' comand is implemented
|
||||||
|
flash bank $TGT.flash $DRV 0x0 $_SIZE 0 0 $TGT
|
||||||
|
# So define mapped flash regions as separate flashes
|
||||||
|
# OOCD creates memory map using registered flash banks
|
||||||
|
flash bank $TGT.irom $DRV 0x0 0 0 0 $TGT
|
||||||
|
flash bank $TGT.drom $DRV 0x0 0 0 0 $TGT
|
||||||
|
}
|
||||||
|
|
||||||
|
# special function to program ESP chip, it differs from the original 'program' that
|
||||||
|
# it verifies written image by reading flash directly, instead of reading memory mapped flash regions
|
||||||
|
proc program_esp {filename args} {
|
||||||
|
set exit 0
|
||||||
|
set compress 0
|
||||||
|
set restore_clock 0
|
||||||
|
|
||||||
|
set flash_list_size [llength [flash list]]
|
||||||
|
if { $flash_list_size == 0} {
|
||||||
|
program_error "** ESP flash programming is not supported yet! **" $exit
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach arg $args {
|
||||||
|
if {[string equal $arg "verify"]} {
|
||||||
|
set verify 1
|
||||||
|
} elseif {[string equal $arg "reset"]} {
|
||||||
|
set reset 1
|
||||||
|
} elseif {[string equal $arg "exit"]} {
|
||||||
|
set exit 1
|
||||||
|
} elseif {[string equal $arg "compress"]} {
|
||||||
|
set compress 1
|
||||||
|
} elseif {[string equal $arg "restore_clock"]} {
|
||||||
|
set restore_clock 1
|
||||||
|
} else {
|
||||||
|
set address $arg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# make sure init is called
|
||||||
|
if {[catch {init}] != 0} {
|
||||||
|
program_error "** OpenOCD init failed **" 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# reset target and call any init scripts
|
||||||
|
if {[catch {reset init}] != 0} {
|
||||||
|
program_error "** Unable to reset target **" $exit
|
||||||
|
}
|
||||||
|
|
||||||
|
set wab_list {}
|
||||||
|
set awab_list {}
|
||||||
|
foreach tgt [target names] {
|
||||||
|
lappend wab_list [$tgt cget -work-area-backup]
|
||||||
|
$tgt configure -work-area-backup 0
|
||||||
|
lappend awab_list [$tgt cget -alt-work-area-backup]
|
||||||
|
$tgt configure -alt-work-area-backup 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if {$compress == 1} {
|
||||||
|
eval esp compression "on"
|
||||||
|
} else {
|
||||||
|
eval esp compression "off"
|
||||||
|
}
|
||||||
|
|
||||||
|
# start programming phase
|
||||||
|
echo "** Programming Started **"
|
||||||
|
if {[info exists address]} {
|
||||||
|
set flash_args "$filename $address"
|
||||||
|
} else {
|
||||||
|
set flash_args "$filename"
|
||||||
|
}
|
||||||
|
|
||||||
|
if {[catch {eval esp flash_stub_clock_boost "on"}] != 0} {
|
||||||
|
program_error "** Clock configuration set failed **" $exit
|
||||||
|
}
|
||||||
|
|
||||||
|
if {[catch {eval flash write_image erase $flash_args}] == 0} {
|
||||||
|
echo "** Programming Finished **"
|
||||||
|
if {[info exists verify]} {
|
||||||
|
# verify phase
|
||||||
|
echo "** Verify Started **"
|
||||||
|
if {[catch {eval esp verify_bank_hash 0 $flash_args}] == 0} {
|
||||||
|
echo "** Verified OK **"
|
||||||
|
} else {
|
||||||
|
configure_esp_workarea_backups $wab_list $awab_list
|
||||||
|
if {$restore_clock == 1} {
|
||||||
|
eval esp flash_stub_clock_boost "off"
|
||||||
|
}
|
||||||
|
program_error "** Verify Failed **" $exit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
configure_esp_workarea_backups $wab_list $awab_list
|
||||||
|
|
||||||
|
if {$restore_clock == 1} {
|
||||||
|
if {[catch {eval esp flash_stub_clock_boost "off"}] != 0} {
|
||||||
|
program_error "** Clock configuration restore failed **" $exit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if {[info exists reset]} {
|
||||||
|
# reset target if requested
|
||||||
|
echo "** Resetting Target **"
|
||||||
|
reset run
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
configure_esp_workarea_backups $wab_list $awab_list
|
||||||
|
if {$restore_clock == 1} {
|
||||||
|
eval esp flash_stub_clock_boost "off"
|
||||||
|
}
|
||||||
|
program_error "** Programming Failed **" $exit
|
||||||
|
}
|
||||||
|
|
||||||
|
if {$exit == 1} {
|
||||||
|
shutdown
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
add_help_text program_esp "write an image to flash, address is only required for binary images. verify, reset, exit, compress, restore_clock are optional"
|
||||||
|
add_usage_text program_esp "<filename> \[address\] \[verify\] \[reset\] \[exit\] \[compress\] \[restore_clock\]"
|
||||||
|
|
||||||
|
proc program_esp_bins {build_dir filename args} {
|
||||||
|
set exit 0
|
||||||
|
set compress 0
|
||||||
|
set restore_clock 0
|
||||||
|
|
||||||
|
foreach arg $args {
|
||||||
|
if {[string equal $arg "reset"]} {
|
||||||
|
set reset 1
|
||||||
|
} elseif {[string equal $arg "verify"]} {
|
||||||
|
set verify 1
|
||||||
|
} elseif {[string equal $arg "exit"]} {
|
||||||
|
set exit 1
|
||||||
|
} elseif {[string equal $arg "compress"]} {
|
||||||
|
set compress 1
|
||||||
|
} elseif {[string equal $arg "restore_clock"]} {
|
||||||
|
set restore_clock 1
|
||||||
|
} else {
|
||||||
|
echo "** Unsupported arg $arg, skipping **"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Open and Read file
|
||||||
|
set fp [open [file join $build_dir $filename] r]
|
||||||
|
set file_data [read $fp]
|
||||||
|
close $fp
|
||||||
|
|
||||||
|
# Decode JSON to dict
|
||||||
|
set flasher_args [json::decode $file_data]
|
||||||
|
|
||||||
|
set flash_files [dict get $flasher_args flash_files]
|
||||||
|
|
||||||
|
foreach addr [dict keys $flash_files] {
|
||||||
|
set bin_file [dict get $flash_files $addr]
|
||||||
|
set bin_file_path [file join $build_dir $bin_file]
|
||||||
|
|
||||||
|
echo "Flashing $bin_file_path at $addr"
|
||||||
|
|
||||||
|
if {[info exists verify]} {
|
||||||
|
set flash_args "$bin_file_path $addr verify"
|
||||||
|
} else {
|
||||||
|
set flash_args "$bin_file_path $addr"
|
||||||
|
}
|
||||||
|
|
||||||
|
if {$compress == 1} {
|
||||||
|
append flash_args " compress"
|
||||||
|
}
|
||||||
|
|
||||||
|
if {$restore_clock == 1} {
|
||||||
|
append flash_args " restore_clock"
|
||||||
|
}
|
||||||
|
|
||||||
|
if {[ catch { eval program_esp $flash_args} ] == 0} {
|
||||||
|
echo "** Flashing done for $bin_file **"
|
||||||
|
} else {
|
||||||
|
echo "** Flashing Failed **"
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Reset
|
||||||
|
if {[info exists reset]} {
|
||||||
|
echo "** Resetting Target **"
|
||||||
|
reset run
|
||||||
|
}
|
||||||
|
|
||||||
|
# Exit
|
||||||
|
if {$exit == 1} {
|
||||||
|
shutdown
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
add_help_text program_esp_bins "write all the images at address specified in flasher_args.json generated while building idf project"
|
||||||
|
add_usage_text program_esp_bins "<build_dir> flasher_args.json \[verify\] \[reset\] \[exit\] \[compress\] \[restore_clock\]"
|
||||||
|
|
||||||
|
proc esp_get_mac {args} {
|
||||||
|
global EFUSE_MAC_ADDR_REG
|
||||||
|
foreach arg $args {
|
||||||
|
if {[string equal $arg "format"]} {
|
||||||
|
set format 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if { [string equal [target current] esp32c3] } {
|
||||||
|
mem2array mac 8 $EFUSE_MAC_ADDR_REG 6
|
||||||
|
} else {
|
||||||
|
xtensa set_permissive 1
|
||||||
|
mem2array mac 8 $EFUSE_MAC_ADDR_REG 6
|
||||||
|
xtensa set_permissive 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if {[info exists format]} {
|
||||||
|
format %02x:%02x:%02x:%02x:%02x:%02x $mac(5) $mac(4) $mac(3) $mac(2) $mac(1) $mac(0)
|
||||||
|
} else {
|
||||||
|
format 0x0000%02x%02x%02x%02x%02x%02x $mac(5) $mac(4) $mac(3) $mac(2) $mac(1) $mac(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
add_help_text esp_get_mac "Print MAC address of the chip. Use a `format` argument to return formatted MAC value"
|
||||||
|
add_usage_text esp_get_mac "\[format\]"
|
|
@ -0,0 +1,16 @@
|
||||||
|
# USE USB for openocd in ESP32
|
||||||
|
|
||||||
|
## board
|
||||||
|
|
||||||
|
ESP-C3-32S-Kit https://docs.ai-thinker.com/_media/esp32/docs/esp-c3-32s-kit-v1.0_规格书.pdf
|
||||||
|
|
||||||
|
|
||||||
|
## usage
|
||||||
|
|
||||||
|
* 连接usb的io18和io19,并确保这两个IO没有被连接到其他地方
|
||||||
|
* 配置esp32-c3打开usb及jtag
|
||||||
|
|
||||||
|
## cmd
|
||||||
|
|
||||||
|
* `source openocd.sh` 启动openocd
|
||||||
|
* `source gdb.sh` 启动gdb
|
Binary file not shown.
|
@ -0,0 +1,2 @@
|
||||||
|
source ~/develop/esp-idf/export.sh
|
||||||
|
riscv32-esp-elf-gdb -x gdbinit blink.elf
|
|
@ -0,0 +1,6 @@
|
||||||
|
target remote :3333
|
||||||
|
set remote hardware-watchpoint-limit 2
|
||||||
|
mon reset halt
|
||||||
|
flushregs
|
||||||
|
thb app_main
|
||||||
|
c
|
|
@ -0,0 +1,2 @@
|
||||||
|
source ~/develop/esp-idf/export.sh
|
||||||
|
openocd -f board/esp32c3-builtin.cfg
|
Loading…
Reference in New Issue