This is an automated email from Gerrit. "Jan Matyas <[email protected]>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/9757
-- gerrit commit 733641b208cd2f5a9e07e4b5b0d62efd9e4ae93e Author: Jan Matyas <[email protected]> Date: Thu Jun 25 15:14:04 2026 +0200 rtos/freertos: Experiments with FreeRTOS on RISC-V Not to be merged. This is a draft that documents my quick-and-dirty experiments with adding FreeRTOS support on RISC-V into mainline OpenOCD. The findings, hints and instructions from this draft may be useful for other developers that will try to add proper support, hence I am publishing them in a draft form. Change-Id: I3fb6214d1b891cdede101011c97f60dedba631f7 Signed-off-by: Jan Matyas <[email protected]> diff --git a/src/rtos/freertos.c b/src/rtos/freertos.c index 5a9224ec08..eb245f8f33 100644 --- a/src/rtos/freertos.c +++ b/src/rtos/freertos.c @@ -18,9 +18,58 @@ #include "rtos_standard_stackings.h" #include "target/armv7m.h" #include "target/cortex_m.h" +#include "target/riscv/gdb_regs.h" #define FREERTOS_MAX_PRIORITIES 63 +#define RISCV_NUM_CORE_REGS 33 // TODO: Needs to be double-checked + +// TODO: Needs to be double-checked +static const struct stack_register_offset freertos_riscv_rv32i_offsets[RISCV_NUM_CORE_REGS] = { + { GDB_REGNO_ZERO, -1, 32 }, // not on the stack - denoted by the special value -1 + { GDB_REGNO_RA, 0x04, 32 }, + { GDB_REGNO_SP, -2, 32 }, // SP: Not on the stack, taken from the TCB. + { GDB_REGNO_GP, -1, 32 }, // GP: Not on the stack. TODO: What to do about this register? + { GDB_REGNO_TP, -1, 32 }, // TP: Not on the stack. TODO: What to do about this register? + { GDB_REGNO_T0, 0x08, 32 }, + { GDB_REGNO_T1, 0x0c, 32 }, + { GDB_REGNO_T2, 0x10, 32 }, + { GDB_REGNO_FP, 0x14, 32 }, + { GDB_REGNO_S1, 0x18, 32 }, + { GDB_REGNO_A0, 0x1c, 32 }, + { GDB_REGNO_A1, 0x20, 32 }, + { GDB_REGNO_A2, 0x24, 32 }, + { GDB_REGNO_A3, 0x28, 32 }, + { GDB_REGNO_A4, 0x2c, 32 }, + { GDB_REGNO_A5, 0x30, 32 }, + { GDB_REGNO_A6, 0x34, 32 }, + { GDB_REGNO_A7, 0x38, 32 }, + { GDB_REGNO_S2, 0x3c, 32 }, + { GDB_REGNO_S3, 0x40, 32 }, + { GDB_REGNO_S4, 0x44, 32 }, + { GDB_REGNO_S5, 0x48, 32 }, + { GDB_REGNO_S6, 0x4c, 32 }, + { GDB_REGNO_S7, 0x50, 32 }, + { GDB_REGNO_S8, 0x54, 32 }, + { GDB_REGNO_S9, 0x58, 32 }, + { GDB_REGNO_S10, 0x5c, 32 }, + { GDB_REGNO_S11, 0x60, 32 }, + { GDB_REGNO_T3, 0x64, 32 }, + { GDB_REGNO_T4, 0x68, 32 }, + { GDB_REGNO_T5, 0x6c, 32 }, + { GDB_REGNO_T6, 0x70, 32 }, + { GDB_REGNO_PC, 0x00, 32 }, +}; + +// TODO: Needs to be double-checked +const struct rtos_register_stacking freertos_riscv_rv32i_stacking = { + .stack_registers_size = 31 * 4, // see portCONTEXT_SIZE in RISC-V/portContext.h + .stack_growth_direction = -1, + .num_output_registers = RISCV_NUM_CORE_REGS, + .calculate_process_stack = NULL, /*rtos_standard_cortex_m3_stack_align,*/ // TODO: needed? + .register_offsets = freertos_riscv_rv32i_offsets +}; + /* FIXME: none of the _width parameters are actually observed properly! * you WILL need to edit more if you actually attempt to target a 8/16/64 * bit target! @@ -36,9 +85,13 @@ struct freertos_params { const unsigned char list_elem_content_offset; /* offsetof(ListItem_t, pvOwner) */ const unsigned char thread_stack_offset; /* offsetof(TCB_t, pxTopOfStack) */ const unsigned char thread_name_offset; /* offsetof(TCB_t, pcTaskName) */ + + // TODO: This part needs better solution - how to work with different + // stackings across different targets. const struct rtos_register_stacking *stacking_info_cm3; const struct rtos_register_stacking *stacking_info_cm4f; const struct rtos_register_stacking *stacking_info_cm4f_fpu; + const struct rtos_register_stacking *stacking_info_rv32i; }; static const struct freertos_params freertos_params_list[] = { @@ -55,6 +108,7 @@ static const struct freertos_params freertos_params_list[] = { &rtos_standard_cortex_m3_stacking, /* stacking_info */ &rtos_standard_cortex_m4f_stacking, &rtos_standard_cortex_m4f_fpu_stacking, + NULL, }, { "hla_target", /* target_name */ @@ -69,6 +123,23 @@ static const struct freertos_params freertos_params_list[] = { &rtos_standard_cortex_m3_stacking, /* stacking_info */ &rtos_standard_cortex_m4f_stacking, &rtos_standard_cortex_m4f_fpu_stacking, + NULL, + }, + { + // TODO: This part needs to be double-checked + "riscv", /* target_name */ + 4, /* thread_count_width; */ + 4, /* pointer_width; */ + 12, /* list_next_offset; */ + 20, /* list_width; */ + 4, /* list_elem_next_offset; */ + 12, /* list_elem_content_offset */ + 0, /* thread_stack_offset; */ + 52, /* thread_name_offset; */ + NULL, /* stacking_info */ + NULL, + NULL, + &freertos_riscv_rv32i_stacking, }, }; @@ -409,7 +480,7 @@ static int freertos_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, thread_id + param->thread_stack_offset, &pointer_casts_are_bad); if (retval != ERROR_OK) { - LOG_ERROR("Error reading stack frame from FreeRTOS thread"); + LOG_ERROR("Error reading stack frame address from FreeRTOS thread control block"); return retval; } stack_ptr = pointer_casts_are_bad; @@ -417,45 +488,59 @@ static int freertos_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, thread_id + param->thread_stack_offset, stack_ptr); - /* Check for armv7m with *enabled* FPU, i.e. a Cortex-M4F */ - int cm4_fpu_enabled = 0; - struct armv7m_common *armv7m_target = target_to_armv7m(rtos->target); - if (is_armv7m(armv7m_target)) { - if ((armv7m_target->fp_feature == FPV4_SP) || (armv7m_target->fp_feature == FPV5_SP) || - (armv7m_target->fp_feature == FPV5_DP)) { - /* Found ARM v7m target which includes a FPU */ - uint32_t cpacr; - - retval = target_read_u32(rtos->target, FPU_CPACR, &cpacr); - if (retval != ERROR_OK) { - LOG_ERROR("Could not read CPACR register to check FPU state"); - return -1; + // TODO: This part (selecting the register stacking) needs better solution + // than the hardcoded version below. + if (strcmp(param->target_name, "hla_target") == 0 || strcmp(param->target_name, "cortex_m") == 0) { + /* Check for armv7m with *enabled* FPU, i.e. a Cortex-M4F */ + int cm4_fpu_enabled = 0; + struct armv7m_common *armv7m_target = target_to_armv7m(rtos->target); + if (is_armv7m(armv7m_target)) { + if ((armv7m_target->fp_feature == FPV4_SP) || (armv7m_target->fp_feature == FPV5_SP) || + (armv7m_target->fp_feature == FPV5_DP)) { + /* Found ARM v7m target which includes a FPU */ + uint32_t cpacr; + + retval = target_read_u32(rtos->target, FPU_CPACR, &cpacr); + if (retval != ERROR_OK) { + LOG_ERROR("Could not read CPACR register to check FPU state"); + return -1; + } + + /* Check if CP10 and CP11 are set to full access. */ + if (cpacr & 0x00F00000) { + /* Found target with enabled FPU */ + cm4_fpu_enabled = 1; + } } + } - /* Check if CP10 and CP11 are set to full access. */ - if (cpacr & 0x00F00000) { - /* Found target with enabled FPU */ - cm4_fpu_enabled = 1; + if (cm4_fpu_enabled == 1) { + /* Read the LR to decide between stacking with or without FPU */ + uint32_t lr_svc = 0; + retval = target_read_u32(rtos->target, + stack_ptr + 0x20, + &lr_svc); + if (retval != ERROR_OK) { + LOG_ERROR("Error reading stack frame from FreeRTOS thread"); + return retval; } + if ((lr_svc & 0x10) == 0) + return rtos_generic_stack_read(rtos->target, param->stacking_info_cm4f_fpu, stack_ptr, reg_list, num_regs); + else + return rtos_generic_stack_read(rtos->target, param->stacking_info_cm4f, stack_ptr, reg_list, num_regs); + } else { + return rtos_generic_stack_read(rtos->target, param->stacking_info_cm3, stack_ptr, reg_list, num_regs); } + } else if (strcmp(param->target_name, "riscv") == 0) { + // TODO: This part blindly assumes a RV32I target (without V and F/D extensions). + // + // Proper RISC-V FreeRTOS support should be aware of multiple possible RISC-V stackings, + // depending on the RISC-V ISA width (32/64) and the implemented extensions. + // See: FreeRTOS/Source/portable/GCC/RISC-V/portContext.h + return rtos_generic_stack_read(rtos->target, param->stacking_info_rv32i, stack_ptr, reg_list, num_regs); + } else { + assert(false && "Unexpected target_name in freertos_get_thread_reg_list"); } - - if (cm4_fpu_enabled == 1) { - /* Read the LR to decide between stacking with or without FPU */ - uint32_t lr_svc = 0; - retval = target_read_u32(rtos->target, - stack_ptr + 0x20, - &lr_svc); - if (retval != ERROR_OK) { - LOG_OUTPUT("Error reading stack frame from FreeRTOS thread"); - return retval; - } - if ((lr_svc & 0x10) == 0) - return rtos_generic_stack_read(rtos->target, param->stacking_info_cm4f_fpu, stack_ptr, reg_list, num_regs); - else - return rtos_generic_stack_read(rtos->target, param->stacking_info_cm4f, stack_ptr, reg_list, num_regs); - } else - return rtos_generic_stack_read(rtos->target, param->stacking_info_cm3, stack_ptr, reg_list, num_regs); } static int freertos_get_symbol_list_to_lookup(struct symbol_table_elem *symbol_list[]) diff --git a/src/rtos/rtos.c b/src/rtos/rtos.c index cc0e92edff..9654f880af 100644 --- a/src/rtos/rtos.c +++ b/src/rtos/rtos.c @@ -490,6 +490,14 @@ int rtos_thread_packet(struct connection *connection, char const *packet, int pa if ((packet[1] == 'g') && (target->rtos)) { threadid_t threadid; sscanf(packet, "Hg%16" SCNx64, &threadid); + + // TODO: There is a GDB bug that occurs for thread IDs + // >= 0x8000_0000: GDB truncates it to negative values. + // https://sourceware.org/bugzilla/show_bug.cgi?id=34316 + // + // Quick and dirty workaround could be: + // threadid = (uint32_t)threadid; + LOG_DEBUG("RTOS: GDB requested to set current thread to 0x%" PRIx64, threadid); /* threadid of 0 indicates target should choose */ if (threadid == 0) @@ -617,7 +625,7 @@ int rtos_get_gdb_reg_list(struct connection *connection) int num_regs; LOG_DEBUG("RTOS: getting register list for thread 0x%" PRIx64 - ", target->rtos->current_thread=0x%" PRIx64 "\r\n", + ", target->rtos->current_thread=0x%" PRIx64, current_threadid, target->rtos->current_thread); @@ -664,21 +672,25 @@ int rtos_generic_stack_read(struct target *target, LOG_ERROR("null stack pointer in thread"); return -5; } + /* Read the stack */ uint8_t *stack_data = malloc(stacking->stack_registers_size); uint32_t address = stack_ptr; if (stacking->stack_growth_direction == 1) address -= stacking->stack_registers_size; + if (stacking->read_stack) retval = stacking->read_stack(target, address, stacking, stack_data); else retval = target_read_buffer(target, address, stacking->stack_registers_size, stack_data); + if (retval != ERROR_OK) { free(stack_data); LOG_ERROR("Error reading stack frame from thread"); return retval; } + LOG_DEBUG("RTOS: Read stack frame at 0x%" PRIx32, address); #if 0 diff --git a/src/server/gdb_server.c b/src/server/gdb_server.c index 6bbf01cc98..73f000cb90 100644 --- a/src/server/gdb_server.c +++ b/src/server/gdb_server.c @@ -3105,6 +3105,13 @@ static bool gdb_handle_vcont_packet(struct connection *connection, const char *p thread_id = 0; } + // TODO: There is a GDB bug that occurs for thread IDs + // >= 0x8000_0000: GDB truncates it to negative values. + // https://sourceware.org/bugzilla/show_bug.cgi?id=34316 + // + // Quick and dirty workaround could be: + // thread_id = (uint32_t)thread_id; + if (target->rtos) { /* Sometimes this results in picking a different thread than * gdb just requested to step. Then we fake it, and now there's --
