#include "debug_tools.h" #include "IQmathLib.h" DebugLowLevel_t debug_ll = DEBUG_LOWLEVEL_INIT; ///< Структура отладки нижнего уровня (инициализация) static int getDebugVar(DebugVar_t *var, long *int_var, float *float_var); static int convertDebugVarToIQx(DebugVar_t *var, long *ret_var); ///////////////////////////----EXAPLE-----////////////////////////////// long var_numb = 1; ///< Пример переменной для отладки DebugVarName_t var_name; ///< Имя переменной long return_var; ///< Переменная для возврата результата long return_ll_var; ///< Возвращаемое значение с нижнего уровня int result; ///< Переменная результата char ext_date[] = {7, 233, 11, 07, 16, 50}; ///< Пример внешней даты сборки /** * @brief Пример использования функций отладки. * @details Демонстрационная функция для работы с переменными отладки. */ void Debug_Test_Example(void) { 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_long – указатель для возврата результата. * @return int – 0: успех, 1: ошибка. * @details Используется для чтения значений переменных по их индексу. */ int Debug_ReadVar(int var_ind, long *return_long) { if(return_long == NULL) return 1; long tmp_var; if (var_ind >= DebugVar_Qnt) return 1; if((dbg_vars[var_numb].ptr_type == pt_struct) || (dbg_vars[var_numb].ptr_type == pt_union) || (dbg_vars[var_numb].ptr_type == pt_unknown)) return 1; return convertDebugVarToIQx(&dbg_vars[var_numb], return_long); } /** * @brief Читает имя переменной по индексу. * @param var_ind – индекс переменной. * @param name_ptr – указатель на буфер имени (DebugVarName_t). * @return int – 0: успех, 1: ошибка. * @details Копирует имя переменной в предоставленный буфер. */ int Debug_ReadVarName(int var_ind, DebugVarName_t name_ptr) { if(name_ptr == NULL) return 1; if (var_ind >= DebugVar_Qnt) return 1; int i; // Копирование с защитой от переполнения и явной остановкой по '\0' for (i = 0; i < sizeof(dbg_vars[var_numb].name); i++) { name_ptr[i] = dbg_vars[var_numb].name[i]; if (dbg_vars[var_numb].name[i] == '\0') break; } // Гарантированное завершение строки (на случай, если в var->name не было '\0') name_ptr[sizeof(dbg_vars[var_numb].name) - 1] = '\0'; return 0; } /** * @brief Читает значение переменной отладки с нижнего уровня. * @param return_long – указатель на переменную, куда записывается результат. * @return int – 0: успех, 1: ошибка, 2: недопустимый адрес. * @details Использует адресс, передаваемый с терминалки для получения значения. */ int Debug_LowLevel_ReadVar(long *return_long) { if (return_long == NULL) return 1; if (debug_ll.isVerified == 0) return 1; char *addr = debug_ll.dbg_var.Ptr; unsigned long addr_val = (unsigned long)addr; // Разрешённые диапазоны памяти (из .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_long); ; } /** * @brief Инициализация отладки на нижнем уровне по дате сборки. * @param external_date – указатель на массив из 6 байт: {year_hi, year_lo, day, month, hour, minute}. * @return int – 0: совпадает, 1: не совпадает, -1: ошибка. * @details Сравнивает дату компиляции с запрашиваемой и инициализирует отладочную переменную. */ int Debug_LowLevel_Initialize(const char* external_date) { if (external_date == NULL) { return -1; } // Преобразуем external_date в структуру DateTime_t ext; ext.year = (external_date[0] << 8) | external_date[1]; ext.day = external_date[2]; ext.month = external_date[3]; ext.hour = external_date[4]; ext.minute = external_date[5]; // Сравнение всех полей if (ext.year == debug_ll.build_date.year && ext.month == debug_ll.build_date.month && ext.day == debug_ll.build_date.day && ext.hour == debug_ll.build_date.hour && ext.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 – указатель для возврата значения в формате long. * @return int – 0: успех, 1: ошибка чтения, 2: неправильный формат, 3: переполнение. * @details Определяет формат IQ переменной, конвертирует её в long с учётом масштаба. */ static int convertDebugVarToIQx(DebugVar_t *var, long *ret_var) { long iq_numb, iq_united, iq_final; 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; // неправильный формат long long iq_united64 = 0; long long iq_final64 = 0; // Конвертация к GLOBAL_Q (64-бит) if (var->iq_type == t_iq_none) { if (var->ptr_type == pt_float) { // float_numb умножаем на 2^GLOBAL_Q (2^24=16777216) // Результат приводим к long long iq_united64 = (long long)(float_numb * 16777216.0f); } else { iq_united64 = ((long long)iq_numb) << GLOBAL_Q; } } else { int shift = GLOBAL_Q - src_q; if (shift >= 0) iq_united64 = ((long long)iq_numb) << shift; else iq_united64 = ((long long)iq_numb) >> (-shift); } // Конвертация из GLOBAL_Q в целевой IQ (64-бит) if (var->return_type == t_iq_none) { // Возвращаем целое, отбросив дробную часть *ret_var = (long)(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 > LONG_MAX || iq_final64 < LONG_MIN) return 3; // переполнение *ret_var = (long)iq_final64; } return 0; } /** * @brief Прочитать значение переменной отладки. * @param var – указатель на структуру DebugVar. * @param int_var – указатель на переменную типа long для возврата целочисленного значения. * @param float_var – указатель на переменную типа float для возврата значения с плавающей точкой. * @return int – 0: успех, 1: ошибка указателей или неподдерживаемый тип, 3/4: ошибка выравнивания. * @details В зависимости от типа переменной считывает её значение и сохраняет в соответствующем указателе. */ static int getDebugVar(DebugVar_t *var, long *int_var, float *float_var) { if (!var || !int_var || !float_var || !var->Ptr) return 1; // ошибка: null указатель char *addr = var->Ptr; unsigned long addr_val = (unsigned long)addr; switch (var->ptr_type) { case pt_int8: // 8 бит case pt_uint8: // выравнивание не нужно для 8 бит *int_var = *((volatile char *)addr); break; case pt_int16: // 16 бит (int) case pt_uint16: *int_var = *((volatile int *)addr); break; case pt_int32: // 32 бит (long) case pt_uint32: if (addr_val & 0x1) // проверяем выравнивание по 2 словам (4 байта) return 3; // ошибка выравнивания *int_var = *((volatile long *)addr); break; // case pt_int64: // 64 бит (long long) // case pt_uint64: // if (addr_val & 0x3) // проверка выравнивания по 4 словам (8 байтам) // return 2; // ошибка выравнивания // // Тут просто читаем, но long long может не поместиться в *int_var // // Можно заменить логику под 64-битное чтение при необходимости // *int_var = *((volatile long long *)addr); // break; case pt_float: // float (4 байта) if (addr_val & 0x1) // проверка выравнивания по 2 словам 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; // успех } ///////////// OUTDATE //////////////// // // // приведение к одному IQ // switch(var->iq_type) // { // case t_iq_none: // if(var->ptr_type == pt_float) // { // iq_united = _IQ(float_numb); // } // else // { // iq_united = _IQ(iq_numb); // } // break; // case t_iq1: // iq_united = _IQ1toIQ(iq_numb); // break; // case t_iq2: // iq_united = _IQ2toIQ(iq_numb); // break; // case t_iq3: // iq_united = _IQ3toIQ(iq_numb); // break; // case t_iq4: // iq_united = _IQ4toIQ(iq_numb); // break; // case t_iq5: // iq_united = _IQ5toIQ(iq_numb); // break; // case t_iq6: // iq_united = _IQ6toIQ(iq_numb); // break; // case t_iq7: // iq_united = _IQ7toIQ(iq_numb); // break; // case t_iq8: // iq_united = _IQ8toIQ(iq_numb); // break; // case t_iq9: // iq_united = _IQ9toIQ(iq_numb); // break; // case t_iq10: // iq_united = _IQ10toIQ(iq_numb); // break; // case t_iq11: // iq_united = _IQ11toIQ(iq_numb); // break; // case t_iq12: // iq_united = _IQ12toIQ(iq_numb); // break; // case t_iq13: // iq_united = _IQ13toIQ(iq_numb); // break; // case t_iq14: // iq_united = _IQ14toIQ(iq_numb); // break; // case t_iq15: // iq_united = _IQ15toIQ(iq_numb); // break; // case t_iq16: // iq_united = _IQ16toIQ(iq_numb); // break; // case t_iq17: // iq_united = _IQ17toIQ(iq_numb); // break; // case t_iq18: // iq_united = _IQ18toIQ(iq_numb); // break; // case t_iq19: // iq_united = _IQ19toIQ(iq_numb); // break; // case t_iq20: // iq_united = _IQ20toIQ(iq_numb); // break; // case t_iq21: // iq_united = _IQ21toIQ(iq_numb); // break; // case t_iq22: // iq_united = _IQ22toIQ(iq_numb); // break; // case t_iq23: // iq_united = _IQ23toIQ(iq_numb); // break; // case t_iq24: // iq_united = _IQ24toIQ(iq_numb); // break; // case t_iq25: // iq_united = _IQ25toIQ(iq_numb); // break; // case t_iq26: // iq_united = _IQ26toIQ(iq_numb); // break; // case t_iq27: // iq_united = _IQ27toIQ(iq_numb); // break; // case t_iq28: // iq_united = _IQ28toIQ(iq_numb); // break; // case t_iq29: // iq_united = _IQ29toIQ(iq_numb); // break; // case t_iq30: // iq_united = _IQ30toIQ(iq_numb); // break; // } // // // приведение общего IQ к запрашиваемому // switch(var->return_type) // { // case t_iq_none: // iq_final = (long)_IQtoF(iq_united); // break; // case t_iq1: // iq_final = _IQtoIQ1(iq_united); // break; // case t_iq2: // iq_final = _IQtoIQ2(iq_united); // break; // case t_iq3: // iq_final = _IQtoIQ3(iq_united); // break; // case t_iq4: // iq_final = _IQtoIQ4(iq_united); // break; // case t_iq5: // iq_final = _IQtoIQ5(iq_united); // break; // case t_iq6: // iq_final = _IQtoIQ6(iq_united); // break; // case t_iq7: // iq_final = _IQtoIQ7(iq_united); // break; // case t_iq8: // iq_final = _IQtoIQ8(iq_united); // break; // case t_iq9: // iq_final = _IQtoIQ9(iq_united); // break; // case t_iq10: // iq_final = _IQtoIQ10(iq_united); // break; // case t_iq11: // iq_final = _IQtoIQ11(iq_united); // break; // case t_iq12: // iq_final = _IQtoIQ12(iq_united); // break; // case t_iq13: // iq_final = _IQtoIQ13(iq_united); // break; // case t_iq14: // iq_final = _IQtoIQ14(iq_united); // break; // case t_iq15: // iq_final = _IQtoIQ15(iq_united); // break; // case t_iq16: // iq_final = _IQtoIQ16(iq_united); // break; // case t_iq17: // iq_final = _IQtoIQ17(iq_united); // break; // case t_iq18: // iq_final = _IQtoIQ18(iq_united); // break; // case t_iq19: // iq_final = _IQtoIQ19(iq_united); // break; // case t_iq20: // iq_final = _IQtoIQ20(iq_united); // break; // case t_iq21: // iq_final = _IQtoIQ21(iq_united); // break; // case t_iq22: // iq_final = _IQtoIQ22(iq_united); // break; // case t_iq23: // iq_final = _IQtoIQ23(iq_united); // break; // case t_iq24: // iq_final = _IQtoIQ24(iq_united); // break; // case t_iq25: // iq_final = _IQtoIQ25(iq_united); // break; // case t_iq26: // iq_final = _IQtoIQ26(iq_united); // break; // case t_iq27: // iq_final = _IQtoIQ27(iq_united); // break; // case t_iq28: // iq_final = _IQtoIQ28(iq_united); // break; // case t_iq29: // iq_final = _IQtoIQ29(iq_united); // break; // case t_iq30: // iq_final = _IQtoIQ30(iq_united); // break; // } // *ret_var = iq_final;