Skip to content

Commit b9127b4

Browse files
committed
esp32: skip window over/underflow exceptions when stepping
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
1 parent 5c9de29 commit b9127b4

File tree

4 files changed

+96
-0
lines changed

4 files changed

+96
-0
lines changed

src/target/esp108_common.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,11 @@ struct xtensa_algorithm {
199199
| ((S & 0x0F) << 8 ) \
200200
| ((T & 0x0F) << 4 ))
201201

202+
#define _XT_INS_FORMAT_RRI4(OPCODE,IMM4,R,S,T) (OPCODE \
203+
| ((IMM4 & 0x0F) << 20) \
204+
| ((R & 0x0F) << 12) \
205+
| ((S & 0x0F) << 8) \
206+
| ((T & 0x0F) << 4))
202207

203208

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

261+
#define XT_INS_L32E(R,S,T) _XT_INS_FORMAT_RRI4(0x90000,0,R,S,T)
262+
#define XT_INS_S32E(R,S,T) _XT_INS_FORMAT_RRI4(0x490000,0,R,S,T)
263+
#define XT_INS_L32E_S32E_MASK 0xFF000F
264+
265+
#define XT_INS_RFWO 0x3400
266+
#define XT_INS_RFWU 0x3500
267+
#define XT_INS_RFWO_RFWU_MASK 0xFFFFFF
268+
256269

257270
/* ESP32 memory map */
258271
#define ESP32_DROM_LOW 0x3F400000

src/target/esp32.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -965,6 +965,27 @@ static int xtensa_remove_watchpoint(struct target *target, struct watchpoint *wa
965965
}
966966
#define XCHAL_EXCM_LEVEL 3 /* level masked by PS.EXCM */
967967

968+
static bool pc_in_window_exception(struct target *target, uint32_t pc)
969+
{
970+
uint32_t insn;
971+
int err = xtensa_read_memory(target, pc, 4, 1, (uint8_t*) &insn);
972+
if (err != ERROR_OK) {
973+
return false;
974+
}
975+
976+
uint32_t masked = insn & XT_INS_L32E_S32E_MASK;
977+
if (masked == XT_INS_L32E(0, 0, 0) || masked == XT_INS_S32E(0, 0, 0)) {
978+
return true;
979+
}
980+
981+
masked = insn & XT_INS_RFWO_RFWU_MASK;
982+
if (masked == XT_INS_RFWO || masked == XT_INS_RFWU) {
983+
return true;
984+
}
985+
986+
return false;
987+
}
988+
968989
static int xtensa_step(struct target *target,
969990
int current,
970991
uint32_t address,
@@ -1065,6 +1086,17 @@ static int xtensa_step(struct target *target,
10651086
esp32_fetch_all_regs(target, 1 << esp32->active_cpu);
10661087

10671088
cur_pc = esp108_reg_get(&reg_list[XT_REG_IDX_PC]);
1089+
1090+
if (esp32->isrmasking_mode == ESP32_ISRMASK_ON &&
1091+
pc_in_window_exception(target, cur_pc))
1092+
{
1093+
/* isrmask = on, need to step out of the window exception handler */
1094+
LOG_DEBUG("Stepping out of window exception, PC=%X", cur_pc);
1095+
oldpc = cur_pc;
1096+
address = oldpc + 3;
1097+
continue;
1098+
}
1099+
10681100
if (oldpc == cur_pc) {
10691101
LOG_WARNING("%s: %s: Stepping doesn't seem to change PC! dsr=0x%08x", target->cmd_name, __func__, intfromchars(dsr));
10701102
}

testing/esp/test_apps/gen_ut_app/main/gen_ut_app.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,39 @@ void blink_task(void *pvParameter)
4545
}
4646
}
4747

48+
/* This test calls functions recursively many times, exhausing the
49+
* register space and triggering window overflow exceptions.
50+
* Upon returning, it triggers window underflow exceptions.
51+
* If the test passes, then OpenOCD and GDB can both handle
52+
* window exceptions correctly.
53+
*/
54+
int sum; // not static so the whole loop is not optimized away
55+
56+
static void recursive(int levels)
57+
{
58+
if (levels - 1 == 0) {
59+
return;
60+
}
61+
sum += levels;
62+
recursive(levels - 1);
63+
}
64+
65+
void window_exception_test(void* arg)
66+
{
67+
recursive(20);
68+
printf("sum=%d\n",sum);
69+
}
70+
4871
void app_main()
4972
{
5073
ESP_LOGI(TAG, "Run test %d\n", run_test);
5174
switch(run_test){
5275
case 100:
5376
xTaskCreate(&blink_task, "blink_task", 2048, NULL, 5, NULL);
5477
break;
78+
case 200:
79+
xTaskCreate(&window_exception_test, "win_exc_task", 8192, NULL, 5, NULL);
80+
break;
5581
default:
5682
ESP_LOGE(TAG, "Invalid test id (%d)!", run_test);
5783
while(1){

testing/esp/test_step.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,31 @@ def test_step_over_breakpoint(self):
3030
def test_step_after_active_thread_swicth(self):
3131
pass
3232

33+
def test_step_window_exception(self):
34+
# start the test, stopping at the window_exception_test function
35+
self.select_sub_test(200)
36+
bp = self.gdb.add_bp('window_exception_test')
37+
self.resume_exec()
38+
rsn = self.gdb.wait_target_state(dbg.Gdb.TARGET_STATE_STOPPED, 5)
39+
self.assertEqual(rsn, dbg.Gdb.TARGET_STOP_REASON_BP)
40+
self.gdb.delete_bp(bp)
41+
42+
# do "step in", 3 steps per recursion level
43+
for i in range(0, 59):
44+
get_logger().info('Step in {}'.format(i))
45+
self.step_in()
46+
47+
# check that we have reached the end of recursion
48+
self.assertEqual(int(self.gdb.data_eval_expr('levels')), 1)
49+
50+
# do "step out" once per recursion level
51+
for i in range(0, 20):
52+
get_logger().info('Step out {}'.format(i))
53+
self.step_out()
54+
55+
cur_frame = self.gdb.get_current_frame()
56+
self.assertEqual(cur_frame['func'], 'window_exception_test')
57+
3358

3459
########################################################################
3560
# TESTS DEFINITION WITH SPECIAL TESTS #

0 commit comments

Comments
 (0)