Skip to content

Commit

Permalink
esp32: skip window over/underflow exceptions when stepping
Browse files Browse the repository at this point in the history
If isrmask option is enabled, stepping disables interrupts from
disrupting the debugging flow. Window exceptions are not masked by
PS.INTLEVEL, and can’t be skipped the same way as high level
interrupts, via ICOUNTLEVEL. This change adds code to detect whether
execution is happening inside window exception and to step out of it.

This also happens to work around GDB assert when reading EPC1, since
that happens in the code path for window exceptions.

Closes #37
  • Loading branch information
igrr committed Jun 4, 2018
1 parent 5c9de29 commit b9127b4
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 0 deletions.
13 changes: 13 additions & 0 deletions src/target/esp108_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,11 @@ struct xtensa_algorithm {
| ((S & 0x0F) << 8 ) \
| ((T & 0x0F) << 4 ))

#define _XT_INS_FORMAT_RRI4(OPCODE,IMM4,R,S,T) (OPCODE \
| ((IMM4 & 0x0F) << 20) \
| ((R & 0x0F) << 12) \
| ((S & 0x0F) << 8) \
| ((T & 0x0F) << 4))


/* Xtensa processor instruction opcodes
Expand Down Expand Up @@ -253,6 +258,14 @@ struct xtensa_algorithm {
#define XT_PS_CALLINC_MSK (0x3 << 16)
#define XT_PS_OWB_MSK (0xF << 8)

#define XT_INS_L32E(R,S,T) _XT_INS_FORMAT_RRI4(0x90000,0,R,S,T)
#define XT_INS_S32E(R,S,T) _XT_INS_FORMAT_RRI4(0x490000,0,R,S,T)
#define XT_INS_L32E_S32E_MASK 0xFF000F

#define XT_INS_RFWO 0x3400
#define XT_INS_RFWU 0x3500
#define XT_INS_RFWO_RFWU_MASK 0xFFFFFF


/* ESP32 memory map */
#define ESP32_DROM_LOW 0x3F400000
Expand Down
32 changes: 32 additions & 0 deletions src/target/esp32.c
Original file line number Diff line number Diff line change
Expand Up @@ -965,6 +965,27 @@ static int xtensa_remove_watchpoint(struct target *target, struct watchpoint *wa
}
#define XCHAL_EXCM_LEVEL 3 /* level masked by PS.EXCM */

static bool pc_in_window_exception(struct target *target, uint32_t pc)
{
uint32_t insn;
int err = xtensa_read_memory(target, pc, 4, 1, (uint8_t*) &insn);
if (err != ERROR_OK) {
return false;
}

uint32_t masked = insn & XT_INS_L32E_S32E_MASK;
if (masked == XT_INS_L32E(0, 0, 0) || masked == XT_INS_S32E(0, 0, 0)) {
return true;
}

masked = insn & XT_INS_RFWO_RFWU_MASK;
if (masked == XT_INS_RFWO || masked == XT_INS_RFWU) {
return true;
}

return false;
}

static int xtensa_step(struct target *target,
int current,
uint32_t address,
Expand Down Expand Up @@ -1065,6 +1086,17 @@ static int xtensa_step(struct target *target,
esp32_fetch_all_regs(target, 1 << esp32->active_cpu);

cur_pc = esp108_reg_get(&reg_list[XT_REG_IDX_PC]);

if (esp32->isrmasking_mode == ESP32_ISRMASK_ON &&
pc_in_window_exception(target, cur_pc))
{
/* isrmask = on, need to step out of the window exception handler */
LOG_DEBUG("Stepping out of window exception, PC=%X", cur_pc);
oldpc = cur_pc;
address = oldpc + 3;
continue;
}

if (oldpc == cur_pc) {
LOG_WARNING("%s: %s: Stepping doesn't seem to change PC! dsr=0x%08x", target->cmd_name, __func__, intfromchars(dsr));
}
Expand Down
26 changes: 26 additions & 0 deletions testing/esp/test_apps/gen_ut_app/main/gen_ut_app.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,39 @@ void blink_task(void *pvParameter)
}
}

/* This test calls functions recursively many times, exhausing the
* register space and triggering window overflow exceptions.
* Upon returning, it triggers window underflow exceptions.
* If the test passes, then OpenOCD and GDB can both handle
* window exceptions correctly.
*/
int sum; // not static so the whole loop is not optimized away

static void recursive(int levels)
{
if (levels - 1 == 0) {
return;
}
sum += levels;
recursive(levels - 1);
}

void window_exception_test(void* arg)
{
recursive(20);
printf("sum=%d\n",sum);
}

void app_main()
{
ESP_LOGI(TAG, "Run test %d\n", run_test);
switch(run_test){
case 100:
xTaskCreate(&blink_task, "blink_task", 2048, NULL, 5, NULL);
break;
case 200:
xTaskCreate(&window_exception_test, "win_exc_task", 8192, NULL, 5, NULL);
break;
default:
ESP_LOGE(TAG, "Invalid test id (%d)!", run_test);
while(1){
Expand Down
25 changes: 25 additions & 0 deletions testing/esp/test_step.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,31 @@ def test_step_over_breakpoint(self):
def test_step_after_active_thread_swicth(self):
pass

def test_step_window_exception(self):
# start the test, stopping at the window_exception_test function
self.select_sub_test(200)
bp = self.gdb.add_bp('window_exception_test')
self.resume_exec()
rsn = self.gdb.wait_target_state(dbg.Gdb.TARGET_STATE_STOPPED, 5)
self.assertEqual(rsn, dbg.Gdb.TARGET_STOP_REASON_BP)
self.gdb.delete_bp(bp)

# do "step in", 3 steps per recursion level
for i in range(0, 59):
get_logger().info('Step in {}'.format(i))
self.step_in()

# check that we have reached the end of recursion
self.assertEqual(int(self.gdb.data_eval_expr('levels')), 1)

# do "step out" once per recursion level
for i in range(0, 20):
get_logger().info('Step out {}'.format(i))
self.step_out()

cur_frame = self.gdb.get_current_frame()
self.assertEqual(cur_frame['func'], 'window_exception_test')


########################################################################
# TESTS DEFINITION WITH SPECIAL TESTS #
Expand Down

0 comments on commit b9127b4

Please sign in to comment.