#include "debug_tools.h" #if !defined(GLOBAL_Q) #define GLOBAL_Q 16 #endif DebugLowLevel_t debug_ll = DEBUG_LOWLEVEL_INIT; ///< Структура отладки нижнего уровня (инициализация) static int getDebugVar(DebugVar_t *var, int32_t *int_var, float *float_var); static int convertDebugVarToIQx(DebugVar_t *var, int32_t *ret_var); static int iqTypeToQ(DebugVarIQType_t t); ///////////////////////////----EXAPLE-----////////////////////////////// int var_numb = 1; ///< Пример переменной для отладки DebugVarName_t var_name; ///< Имя переменной int32_t return_var; ///< Переменная для возврата результата int32_t return_ll_var; ///< Возвращаемое значение с нижнего уровня int result; ///< Переменная результата DateTime_t ext_date = {2025, 11, 07, 16, 50}; ///< Пример внешней даты сборки /** * @brief Пример использования функций отладки. * @details Демонстрационная функция для работы с переменными отладки. */ void Debug_Test_Example(void) { return; result = Debug_ReadVar(var_numb, &return_var); result = Debug_ReadVarName(var_numb, var_name); if(Debug_LowLevel_Initialize(&ext_date) == 0) result = Debug_LowLevel_ReadVar(&return_ll_var); } ///////////////////////////----PUBLIC-----////////////////////////////// /** * @brief Читает переменную по индексу. * @param var_ind – индекс переменной. * @param return_32b – указатель для возврата результата. * @return int – 0: успех, 1: ошибка. * @details Используется для чтения значений переменных по их индексу. */ int Debug_ReadVar(int var_ind, int32_t *return_32b) { int32_t tmp_var; if(return_32b == NULL) return 1; if (var_ind >= DebugVar_Qnt) return 1; if((dbg_vars[var_ind].ptr_type == pt_struct) || (dbg_vars[var_ind].ptr_type == pt_union) || (dbg_vars[var_ind].ptr_type == pt_unknown)) return 1; return convertDebugVarToIQx(&dbg_vars[var_ind], return_32b); } /** * @brief Читает возвращаемый тип переменной по индексу. * @param var_ind – индекс переменной. * @param vartype – указатель для возврата типа. * @return int – 0: успех, 1: ошибка. * @details Используется для чтения значений переменных по их индексу. */ int Debug_ReadVarReturnType(int var_ind, int *vartype) { int rettype; if(vartype == NULL) return 1; if (var_ind >= DebugVar_Qnt) return 1; if((dbg_vars[var_ind].ptr_type == pt_struct) || (dbg_vars[var_ind].ptr_type == pt_union) || (dbg_vars[var_ind].ptr_type == pt_unknown)) return 1; *vartype = iqTypeToQ(dbg_vars[var_ind].return_type); return 0; } /** * @brief Читает имя переменной по индексу. * @param var_ind – индекс переменной. * @param name_ptr – указатель на буфер имени (DebugVarName_t). * @return int – 0: успех, 1: ошибка. * @details Копирует имя переменной в предоставленный буфер. */ int Debug_ReadVarName(int var_ind, DebugVarName_t name_ptr) { int i; if(name_ptr == NULL) return 1; if (var_ind >= DebugVar_Qnt) return 1; // Копирование с защитой от переполнения и явной остановкой по '\0' for (i = 0; i < sizeof(dbg_vars[var_ind].name); i++) { name_ptr[i] = dbg_vars[var_ind].name[i]; if (dbg_vars[var_ind].name[i] == '\0') break; } // Гарантированное завершение строки (на случай, если в var->name не было '\0') name_ptr[sizeof(dbg_vars[var_ind].name) - 1] = '\0'; return 0; } /** * @brief Читает значение переменной отладки с нижнего уровня. * @param return_32b – указатель на переменную, куда записывается результат. * @return int – 0: успех, 1: ошибка, 2: недопустимый адрес. * @details Использует адресс, передаваемый с терминалки для получения значения. */ int Debug_LowLevel_ReadVar(int32_t *return_32b) { uint8_t *addr = debug_ll.dbg_var.Ptr; uint32_t addr_val = (uint32_t)addr; if (return_32b == NULL) return 1; if (debug_ll.isVerified == 0) return 1; // Разрешённые диапазоны памяти (из .cmd файла) if (!( (addr_val <= 0x0007FF) || // RAMM0 + RAMM1 (addr_val >= 0x008120 && addr_val <= 0x009FFC) || // L0 + L1 SARAM (addr_val >= 0x3F8000 && addr_val <= 0x3F9FFF) || // PRAMH0 + DRAMH0 (addr_val >= 0x3FF000 && addr_val <= 0x3FFFFF) || // BOOTROM + RESET (addr_val >= 0x080002 && addr_val <= 0x09FFFF) || // RAMEX1 (addr_val >= 0x0F0000 && addr_val <= 0x0FFEFF) || // RAMEX4 (addr_val >= 0x100002 && addr_val <= 0x103FFF) || // RAMEX0 + RAMEX2 + RAMEX01 (addr_val >= 0x102000 && addr_val <= 0x103FFF) // RAMEX2 )) { return 2; // Запрещённый адрес — нельзя читать } return convertDebugVarToIQx(&debug_ll.dbg_var, return_32b); } /** * @brief Инициализация отладки на нижнем уровне по дате сборки. * @param external_date – структура с датой DateTime_t * @return int – 0: совпадает, 1: не совпадает, -1: ошибка. * @details Сравнивает дату компиляции с запрашиваемой и инициализирует отладочную переменную. */ int Debug_LowLevel_Initialize(DateTime_t* external_date) { if (external_date == NULL) { return -1; } // Сравнение всех полей if (external_date->year == debug_ll.build_date.year && external_date->month == debug_ll.build_date.month && external_date->day == debug_ll.build_date.day && external_date->hour == debug_ll.build_date.hour && external_date->minute == debug_ll.build_date.minute) { debug_ll.isVerified = 1; return 0; // Совпало } debug_ll.isVerified = 0; return 1; // Не совпало } /////////////////////----INTERNAL FUNCTIONS-----//////////////////////// /** * @brief Преобразует тип IQ переменной в число битов для сдвига(Q-фактор). * @param t – тип IQ (перечисление DebugVarIQType_t). * @return int – Q-фактор (например, 24), 0: если t_iq_none, -1: ошибка. * @details Сопоставляет тип IQ переменной с соответствующим Q-значением. */ static int iqTypeToQ(DebugVarIQType_t t) { if (t == t_iq_none) return 0; // без IQ, float, int else if (t == t_iq) return GLOBAL_Q; // общий IQ, например 24 else if (t >= t_iq1 && t <= t_iq30) return (int)t - (int)t_iq1 + 1; // например t_iq1 -> 1, t_iq2 -> 2 и т.д. else return -1; // ошибка } /** * @brief Преобразует переменную отладки в IQ формат. * @param var – указатель на переменную отладки. * @param ret_var – указатель для возврата значения в формате 32 бита. * @return int – 0: успех, 1: ошибка чтения, 2: неправильный формат, 3: переполнение. * @details Определяет формат IQ переменной, конвертирует её в 32b с учётом масштаба. */ static int convertDebugVarToIQx(DebugVar_t *var, int32_t *ret_var) { int32_t iq_numb, iq_united, iq_final; int64_t iq_united64 = 0; int64_t iq_final64 = 0; float float_numb; if(getDebugVar(var, &iq_numb, &float_numb) != 0) return 1; int src_q = iqTypeToQ(var->iq_type); int dst_q = iqTypeToQ(var->return_type); if (src_q < 0 || dst_q < 0) return 2; // неправильный формат // Конвертация к GLOBAL_Q (64-бит) if (var->iq_type == t_iq_none) { if (var->ptr_type == pt_float) { // float_numb умножаем на 2^GLOBAL_Q // Результат приводим к 64 бита iq_united64 = (int64_t)(float_numb * ((uint32_t)1 << GLOBAL_Q)); } else { iq_united64 = ((int64_t)iq_numb) << GLOBAL_Q; } } else { int shift = GLOBAL_Q - src_q; if (shift >= 0) iq_united64 = ((int64_t)iq_numb) << shift; else iq_united64 = ((int64_t)iq_numb) >> (-shift); } // Конвертация из GLOBAL_Q в целевой IQ (64-бит) if (var->return_type == t_iq_none) { // Возвращаем целое, отбросив дробную часть *ret_var = (uint32_t)(iq_united64 >> GLOBAL_Q); } else { int shift = dst_q - GLOBAL_Q; if (shift >= 0) iq_final64 = iq_united64 << shift; else iq_final64 = iq_united64 >> (-shift); // Проверяем переполнение int32_t if (iq_final64 > 2147483647 || iq_final64 < -2147483648) return 3; // переполнение *ret_var = (int32_t)iq_final64; } return 0; } /** * @brief Прочитать значение переменной отладки. * @param var – указатель на структуру DebugVar. * @param int_var – указатель на переменную типа 32 бита для возврата целочисленного значения. * @param float_var – указатель на переменную типа float для возврата значения с плавающей точкой. * @return int – 0: успех, 1: ошибка указателей или неподдерживаемый тип, 3/4: ошибка выравнивания. * @details В зависимости от типа переменной считывает её значение и сохраняет в соответствующем указателе. */ static int getDebugVar(DebugVar_t *var, int32_t *int_var, float *float_var) { uint8_t *addr = var->Ptr; uint32_t addr_val = (uint32_t)addr; if (!var || !int_var || !float_var || !var->Ptr) return 1; // ошибка: null указатель switch (var->ptr_type) { case pt_int8: // 8 бит if ((addr_val & ALIGN_8BIT) != 0) // проверяем выравнивание return 1; // ошибка выравнивания *int_var = *((volatile int8_t *)addr); break; case pt_uint8: if ((addr_val & ALIGN_8BIT) != 0) // проверяем выравнивание return 1; // ошибка выравнивания *int_var = *((volatile uint8_t *)addr); break; case pt_int16: // 16 бит (int) if ((addr_val & ALIGN_16BIT) != 0) // проверяем выравнивание return 2; // ошибка выравнивания *int_var = *((volatile int16_t *)addr); break; case pt_uint16: if ((addr_val & ALIGN_16BIT) != 0) // проверяем выравнивание return 2; // ошибка выравнивания *int_var = *((volatile uint16_t *)addr); break; case pt_int32: // 32 бит if ((addr_val & ALIGN_32BIT) != 0) // проверяем выравнивание return 3; // ошибка выравнивания *int_var = *((volatile int32_t *)addr); break; case pt_uint32: if ((addr_val & ALIGN_32BIT) != 0) // проверяем выравнивание return 3; // ошибка выравнивания *int_var = *((volatile uint32_t *)addr); break; case pt_float: // float (4 байта) if ((addr_val & ALIGN_FLOAT) != 0) // проверка выравнивания return 4; // ошибка выравнивания *float_var = *((volatile float *)addr); break; default: return 1; // неподдерживаемый тип // для указателей и массивов не поддерживается чтение // case pt_ptr_int8: // case pt_ptr_int16: // case pt_ptr_int32: // case pt_ptr_uint8: // case pt_ptr_uint16: // case pt_ptr_uint32: // case pt_arr_int8: // case pt_arr_int16: // case pt_arr_int32: // case pt_arr_uint8: // case pt_arr_uint16: // case pt_arr_uint32: } return 0; // успех }