Skip to content

Commit

Permalink
target/esp32c6: Add LP core debugging support
Browse files Browse the repository at this point in the history
  • Loading branch information
gerekon committed Jul 19, 2024
1 parent 4dae77e commit c9d3b07
Show file tree
Hide file tree
Showing 12 changed files with 267 additions and 24 deletions.
15 changes: 15 additions & 0 deletions src/jtag/drivers/esp_usb_jtag.c
Original file line number Diff line number Diff line change
Expand Up @@ -915,6 +915,14 @@ COMMAND_HANDLER(esp_usb_jtag_chip_id)
return ERROR_OK;
}

extern void libusb_list_devices(void);

COMMAND_HANDLER(esp_usb_jtag_list)
{
libusb_list_devices();
return ERROR_OK;
}

static const struct command_registration esp_usb_jtag_subcommands[] = {
{
.name = "tdo",
Expand Down Expand Up @@ -958,6 +966,13 @@ static const struct command_registration esp_usb_jtag_subcommands[] = {
.help = "set chip_id to transfer to the bridge",
.usage = "chip_id",
},
{
.name = "list",
.handler = &esp_usb_jtag_list,
.mode = COMMAND_ANY,
.help = "list",
.usage = "list",
},
COMMAND_REGISTRATION_DONE
};

Expand Down
20 changes: 20 additions & 0 deletions src/jtag/drivers/libusb_helper.c
Original file line number Diff line number Diff line change
Expand Up @@ -501,3 +501,23 @@ int oocd_libusb_dev_mem_free(libusb_device_handle *devh,
}
return ERROR_FAIL;
}

void libusb_list_devices(void)
{
struct libusb_device **list;

int cnt = libusb_get_device_list(jtag_libusb_context, &list);

for (int idx = 0; idx < cnt; idx++) {
struct libusb_device *device = list[idx];
struct libusb_device_descriptor dev_desc;

int err = libusb_get_device_descriptor(device, &dev_desc);
if (err != LIBUSB_SUCCESS) {
LOG_ERROR("libusb_get_device_descriptor() failed with %s", libusb_error_name(err));
continue;
}
LOG_INFO("USB dev VID/PID %x/%x", dev_desc.idVendor, dev_desc.idProduct);
}
libusb_free_device_list(list, 1);
}
54 changes: 52 additions & 2 deletions src/target/espressif/esp32c6.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <helper/command.h>
#include <helper/bits.h>
#include <target/target.h>
#include <target/smp.h>
#include <target/target_type.h>
#include <target/register.h>
#include <target/semihosting_common.h>
Expand Down Expand Up @@ -117,6 +118,17 @@ static bool esp32c6_is_idram_address(target_addr_t addr)
return addr >= ESP32C6_DRAM_LOW && addr < ESP32C6_DRAM_HIGH;
}

static void esp32c6_trigger_match_result_fixup(struct target *target, riscv_reg_t *tdata1_ignore_mask, bool t6)
{
if (target->coreid == 1 && !t6) {
// For LP core DM reports that triggers support BP in user mode ("u" bit is set in mcontrol).
// After writing 0x28001044 to mcontrol 0x2800104c is read back. But LP core MISA register
// has no "U" extension enabled. So ignore this field, otherwise RISCV code will fail to find
// suitable HW trigger for breakpoint.
*tdata1_ignore_mask |= CSR_MCONTROL_U;
}
}

static const struct esp_semihost_ops esp32c6_semihost_ops = {
.prepare = NULL,
.post_reset = esp_semihosting_post_reset
Expand Down Expand Up @@ -187,9 +199,47 @@ static int esp32c6_init_target(struct command_context *cmd_ctx,
if (ret != ERROR_OK)
return ret;

esp_riscv->riscv.trigger_match_result_fixup = esp32c6_trigger_match_result_fixup;

return ERROR_OK;
}

static int esp32c6_read_memory(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, uint8_t *buffer)
{
struct target *hp_core_target = target;
// Only HP core can access whole memory
if (target->smp && target->coreid != 0) {
struct target_list *entry;
foreach_smp_target(entry, target->smp_targets) {
struct target *t = entry->target;
if (t->coreid == 0) {
hp_core_target = t;
break;
}
}
}
return esp_riscv_read_memory(hp_core_target, address, size, count, buffer);
}

static int esp32c6_write_memory(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, const uint8_t *buffer)
{
struct target *hp_core_target = target;
// Only HP core can access whole memory
if (target->smp && target->coreid != 0) {
struct target_list *entry;
foreach_smp_target(entry, target->smp_targets) {
struct target *t = entry->target;
if (t->coreid == 0) {
hp_core_target = t;
break;
}
}
}
return esp_riscv_write_memory(hp_core_target, address, size, count, buffer);
}

static const struct command_registration esp32c6_command_handlers[] = {
{
.usage = "",
Expand Down Expand Up @@ -226,8 +276,8 @@ struct target_type esp32c6_target = {
.assert_reset = riscv_assert_reset,
.deassert_reset = riscv_deassert_reset,

.read_memory = esp_riscv_read_memory,
.write_memory = esp_riscv_write_memory,
.read_memory = esp32c6_read_memory,
.write_memory = esp32c6_write_memory,

.checksum_memory = riscv_checksum_memory,

Expand Down
20 changes: 16 additions & 4 deletions src/target/riscv/riscv.c
Original file line number Diff line number Diff line change
Expand Up @@ -965,6 +965,8 @@ static struct match_triggers_tdata1_fields fill_match_triggers_tdata1_fields_t2(
},
.tdata1_ignore_mask = CSR_MCONTROL_MASKMAX(riscv_xlen(target))
};
if (r->trigger_match_result_fixup)
r->trigger_match_result_fixup(target, &result.tdata1_ignore_mask, false);
return result;
}

Expand All @@ -974,6 +976,7 @@ static struct match_triggers_tdata1_fields fill_match_triggers_tdata1_fields_t6(
bool misa_s = riscv_supports_extension(target, 'S');
bool misa_u = riscv_supports_extension(target, 'U');
bool misa_h = riscv_supports_extension(target, 'H');
RISCV_INFO(r);

struct match_triggers_tdata1_fields result = {
.common =
Expand Down Expand Up @@ -1003,6 +1006,8 @@ static struct match_triggers_tdata1_fields fill_match_triggers_tdata1_fields_t6(
},
.tdata1_ignore_mask = 0
};
if (r->trigger_match_result_fixup)
r->trigger_match_result_fixup(target, &result.tdata1_ignore_mask, true);
return result;
}

Expand Down Expand Up @@ -1823,11 +1828,18 @@ static int set_debug_reason(struct target *target, enum riscv_halt_reason halt_r
case RISCV_HALT_TRIGGER:
if (riscv_hit_trigger_hit_bit(target, &r->trigger_hit) != ERROR_OK)
return ERROR_FAIL;
target->debug_reason = DBG_REASON_WATCHPOINT;
/* Check if we hit a hardware breakpoint. */
for (struct breakpoint *bp = target->breakpoints; bp; bp = bp->next) {
// riscv_hit_trigger_hit_bit() looks for fired trigger basing on mcontrol 'hit' bit.
// That bit is optional. GDB handles(steps over) BPs and watchpoints differently.
// ESPRESSIF: ESP32-C6 LP core does not implement this bit, we changed logic here
// and by default we assume that debug reason is DBG_REASON_BREAKPOINT
// instead of DBG_REASON_WATCHPOINT (as done in upstream).
// This should not change behaviour for the chips implementing mcontrol 'hit' bit,
// and fixes the problem with GDB behaviour for the chips which lacks support for it.
target->debug_reason = DBG_REASON_BREAKPOINT;
/* Check if we hit a hardware watchpoint. */
for (struct watchpoint *bp = target->watchpoints; bp; bp = bp->next) {
if (bp->unique_id == r->trigger_hit)
target->debug_reason = DBG_REASON_BREAKPOINT;
target->debug_reason = DBG_REASON_WATCHPOINT;
}
break;
case RISCV_HALT_INTERRUPT:
Expand Down
3 changes: 3 additions & 0 deletions src/target/riscv/riscv.h
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,9 @@ struct riscv_info {
bool enable_equality_match_trigger;
bool enable_napot_trigger;
bool enable_ge_lt_trigger;

/* ESPRESSIF */
void (*trigger_match_result_fixup)(struct target *target, riscv_reg_t *tdata1_ignore_mask, bool t6);
};

COMMAND_HELPER(riscv_print_info_line, const char *section, const char *key,
Expand Down
15 changes: 15 additions & 0 deletions tcl/board/esp32c6-lpcore-bridge.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# Example OpenOCD configuration file for ESP32-C6 connected via ESP USB Bridge board
#
# For example, OpenOCD can be started for ESP32-C6 debugging on
#
# openocd -f board/esp32c6-lpcore-bridge.cfg
#

# Source the JTAG interface configuration file
source [find interface/esp_usb_bridge.cfg]
# ESP32C6 chip id defined in the idf esp_chip_model_t
espusbjtag chip_id 13
# Source the ESP32-C6 configuration file
source [find target/esp32c6-lpcore.cfg]
13 changes: 13 additions & 0 deletions tcl/board/esp32c6-lpcore-builtin.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# Example OpenOCD configuration file for ESP32-C6 + LP core connected via builtin USB-JTAG adapter.
#
# For example, OpenOCD can be started for ESP32-C6 + LP core debugging on
#
# openocd -f board/esp32c6-lpcore-builtin.cfg
#

# Source the JTAG interface configuration file
source [find interface/esp_usb_jtag.cfg]
# Source the ESP32-C6 configuration file
source [find target/esp32c6-lpcore.cfg]
13 changes: 13 additions & 0 deletions tcl/board/esp32c6-lpcore-ftdi.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# Example OpenOCD configuration file for ESP32-C6 + LP core connected via ESP-Prog.
#
# For example, OpenOCD can be started for ESP32-C6 + LP core debugging on
#
# openocd -f board/esp32c6-lpcore-ftdi.cfg
#

# Source the JTAG interface configuration file
source [find interface/ftdi/esp32_devkitj_v1.cfg]
# Source the ESP32-C6 configuration file
source [find target/esp32c6-lpcore.cfg]
24 changes: 24 additions & 0 deletions tcl/esp-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,30 @@
"board/esp32c6-bridge.cfg"
]
},
{
"name": "ESP32-C6 chip with LP core (via builtin USB-JTAG)",
"description": "ESP32-C6 with LP core debugging via builtin USB-JTAG",
"target": "esp32c6",
"config_files": [
"board/esp32c6-lpcore-builtin.cfg"
]
},
{
"name": "ESP32-C6 chip with LP core (via ESP-PROG)",
"description": "ESP32-C6 with LP core debugging via ESP-PROG board",
"target": "esp32c6",
"config_files": [
"board/esp32c6-lpcore-ftdi.cfg"
]
},
{
"name": "ESP32-C6 chip with LP core (via ESP-PROG-2)",
"description": "ESP32-C6 with LP core debugging via ESP-PROG-2 board",
"target": "esp32c6",
"config_files": [
"board/esp32c6-lpcore-bridge.cfg"
]
},
{
"name": "ESP32-H2 chip (via builtin USB-JTAG)",
"description": "ESP32-H2 debugging via builtin USB-JTAG",
Expand Down
58 changes: 58 additions & 0 deletions tcl/target/esp32c6-lpcore.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#

# Target specific global variables
# config for 2 cores (HP+LP) on 1 TAP in SMP mode
set _ONLYCPU 0x11
set _ESP_SMP_TARGET 1
# allow to use SW breakpoints when flash support is disabled
set _NO_FLASH_FORCES_HW_BPS 0
set ESP_RTOS "hwthread"
set ESP_FLASH_SIZE 0

# Source the ESP32-C6 configuration file.
source [find target/esp32c6.cfg]

# Source some helpers.
source [find mem_helper.tcl]

set PMU_LP_CPU_PWR0_REG 0x600B017c
set PMU_HP_LP_CPU_COMM_REG 0x600B0184
set PMU_LP_CPU_PWR1_REG 0x600B0180
set LPPERI_CPU_REG 0x600B280c

proc reg_write { ADDR VAL } {
mmw $ADDR $VAL 0
}

proc lpcore_reset { } {
global PMU_LP_CPU_PWR0_REG PMU_HP_LP_CPU_COMM_REG PMU_LP_CPU_PWR1_REG LPPERI_CPU_REG

# PMU.lp_ext.pwr0.slp_stall_en = 1;
# PMU.lp_ext.pwr0.slp_reset_en = 1;
reg_write $PMU_LP_CPU_PWR0_REG 0x60000000

# PMU.lp_ext.pwr1.wakeup_en = LP_CORE_LL_WAKEUP_SOURCE_HP_CPU;
# PMU.lp_ext.pwr1.sleep_req = 1;
reg_write $PMU_LP_CPU_PWR1_REG 0x80000001

# set up infinite loop code 'j 0x0' at 0x50000080
mww 0x50000080 0x0000006F

# PMU.hp_lp_cpu_comm.hp_trigger_lp = 1;
reg_write $PMU_HP_LP_CPU_COMM_REG 0x80000000

# Wait for the reset to happen
sleep 10
poll

# PMU.lp_ext.pwr0.slp_stall_en = 1;
# PMU.lp_ext.pwr0.slp_reset_en = 0;
reg_write $PMU_LP_CPU_PWR0_REG 0x20000000
# LPPERI.cpu.lpcore_dbgm_unavaliable = 0;
reg_write $LPPERI_CPU_REG 0x00000000
}

$_TARGETNAME_0 configure -event reset-start {
lpcore_reset
}
29 changes: 18 additions & 11 deletions tcl/target/esp32c6.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ set _CHIP_LP_NAME $_CHIPNAME.lp
set _CHIP_HP_NAME $_CHIPNAME.hp
set _CPUTAPID 0x0000dc25
set _ESP_ARCH "riscv"
set _ONLYCPU 0x01
# _ONLYCPU can be set in LP core config file which includes this one
if { ![info exists _ONLYCPU] } {
set _ONLYCPU 0x01
}
set _LP_TAPNUM 1
set _HP_TAPNUM 0
set _ESP_EFUSE_MAC_ADDR_REG 0x600B0844
Expand Down Expand Up @@ -48,16 +51,20 @@ proc esp32c6_wdt_disable { } {
proc esp32c6_soc_reset { } {
global _RISCV_DMCONTROL _RISCV_SB_CS _RISCV_SB_ADDR0 _RISCV_SB_DATA0

riscv dmi_write $_RISCV_DMCONTROL 0x80000001
riscv dmi_write $_RISCV_SB_CS 0x48000
riscv dmi_write $_RISCV_SB_ADDR0 0x600b1034
riscv dmi_write $_RISCV_SB_DATA0 0x80000000
# clear dmactive to clear sbbusy otherwise debug module gets stuck
riscv dmi_write $_RISCV_DMCONTROL 0
# request halt
riscv dmi_write $_RISCV_DMCONTROL 0x80000001

# LP_AON_SYS_CFG_REG = LP_AON_HPSYS_SW_RESET
riscv dmi_write $_RISCV_SB_CS 0x48000
riscv dmi_write $_RISCV_SB_ADDR0 0x600b1034
riscv dmi_write $_RISCV_SB_DATA0 0x80000000
# clear dmactive to clear sbbusy otherwise debug module gets stuck
riscv dmi_write $_RISCV_DMCONTROL 0

riscv dmi_write $_RISCV_SB_CS 0x48000
riscv dmi_write $_RISCV_SB_ADDR0 0x600b1038
riscv dmi_write $_RISCV_SB_DATA0 0x10000000
# LP_AON_CPUCORE0_CFG_REG = LP_AON_CPU_CORE0_SW_RESET
riscv dmi_write $_RISCV_SB_CS 0x48000
riscv dmi_write $_RISCV_SB_ADDR0 0x600b1038
riscv dmi_write $_RISCV_SB_DATA0 0x10000000

# clear dmactive to clear sbbusy otherwise debug module gets stuck
riscv dmi_write $_RISCV_DMCONTROL 0
Expand All @@ -77,7 +84,7 @@ proc esp32c6_soc_reset { } {
riscv dmi_write $_RISCV_DMCONTROL 0x40000001

# Put the hart back into reset state. Note that we need to keep haltreq set.
riscv dmi_write $_RISCV_DMCONTROL 0x80000003
riscv dmi_write $_RISCV_DMCONTROL 0x80000003
}

proc esp32c6_memprot_is_enabled { } {
Expand Down
Loading

0 comments on commit c9d3b07

Please sign in to comment.