Пролог
В математике есть такая тема как Диофантовы уравнения. В самом простом виде выглядят они так.
Тут одно уравнение и две неизвестные: x,y. При этом a, b, d – это известные константы.
На первый взгляд может показаться как бесполезная вещь для программирования Micro Controller Unit (MCU), но не тут-то было.
Когда программируешь микроконтроллер (MCU), то первое, что приходится делать – это конфигурировать тактирование процессорного ядра. Тактированием заправляет устройство с названием phase-locked loop (PLL/ФАПЧ). Это подсистема SoC(а).
И там надо подобрать три константы (натуральных числа): M, N и F. В микроконтроллере Artery, абстрактно изображая, эта электрическая цепь выглядит вот так. Умножители и делители частот.
По формуле (2)
Вычисляется системная частота F_sys (sclk). Это уравнение (2) преобразуется в уравнение (3)
А уравнение (3), в свою очередь, это самое настоящее Диофантово уравнение! Да.. Тут F_quartz – это частота кварцевого резонатора, который задается схемотехникой электронной платы.
F_sys задается программистом для достижения желаемой производительности прошивки. Остаются целочисленные неизвестные N, M и F.
Далее накладываются ещё ограничения уже производителем микроконтроллера. Для Artery это
Переменная |
Минимальное |
Максимальное |
M |
1 |
15 |
N |
31 |
500 |
FR (степени двойки) |
1 |
32 |
Это ограничение даже прописано комментариями в исходных кодах на Cи, которые бесплатно предоставляет производитель Artery Tech.
/** * config crm pll * pll_rcs_freq * pll_ns * pll clock = -------------------------------- * pll_ms * pll_fr_n * attemtion: * 31 <= pll_ns <= 500 * 1 <= pll_ms <= 15 * * pll_rcs_freq * 2mhz <= ---------------------- <= 16mhz * pll_ms * * pll_rcs_freq * pll_ns * 500mhz <= -------------------------------- <= 1200mhz * pll_ms * @param clock_source * this parameter can be one of the following values: * - CRM_PLL_SOURCE_HICK * - CRM_PLL_SOURCE_HEXT * @param pll_ns (31~500) * @param pll_ms (1~15) * @param pll_fr * this parameter can be one of the following values: * - CRM_PLL_FR_1 * - CRM_PLL_FR_2 * - CRM_PLL_FR_4 * - CRM_PLL_FR_8 * - CRM_PLL_FR_16 * - CRM_PLL_FR_32 * @retval none */ void crm_pll_config(crm_pll_clock_source_type clock_source, uint16_t pll_ns, uint16_t pll_ms, crm_pll_fr_type pll_fr)
Итак, постановка задачи:
Есть электронная плата с Artery MCU . На PCB припаян кварцевый резонатор c частотой 8 MHz и подключен к MCU. Необходимо программно сконфигурировать системную частоту ядра ровно на 100 MHz.
Какими при этом должны быть коэффициенты PLL: N, M и RF?
Решение
По сути, задача свелась к тому чтобы решить Диофантово уравнение (4).
Можно попробовать прибегнуть к помощи Artificial intelligence (AI) на сайте Wolfram Alfa
Однако это не решение в частном виде. На практике же нужно численное решение, чтобы проинициализировать им функцию crm_pll_config().
Однако нам повезло. В частности, тут область значений функции (2) достаточно маленькая, поэтому можно найти решение уравнения (3) обыкновенным перебором.
Для этого я написал свой простой численный решатель Диофантова уравнения для PLL прямо на Си.
uint64_t ipow(uint32_t base, uint32_t exponenta) { uint64_t ret = 1, i = 0; if(0 != exponenta) { for(i = 1; i <= exponenta; i++) { ret *= base; } } return ret; } typedef struct{ uint32_t ms; uint32_t ns; uint32_t fr; }PllArtety_t; bool pll_calc_artery(uint32_t freq_xtal_hz, uint32_t freq_sys_hz, PllArtety_t* const PllArtety) { bool res = false; LOG_INFO(PLL_CALC, "FreqXtal:%u Hz,FreqSys:%u Hz", freq_xtal_hz, freq_sys_hz); log_printf("{[(Xtal:%uHz/M)*N]/FR }= Sys:%u Hz" CRLF, freq_xtal_hz, freq_sys_hz); if(PllArtety) { uint32_t m = 0; uint32_t temp_hz = 0; uint32_t cur_freq_sys_hz = 0; for(m = 1; m <= 15; m++) { uint32_t n = 0; for(n = 31; n <= 500; n++) { uint32_t f = 0; for(f = 0; f <= 5; f++) { uint32_t fr = ipow(2, f); cur_freq_sys_hz = ((n * freq_xtal_hz) / (m * fr)); if(freq_sys_hz == cur_freq_sys_hz) { temp_hz = freq_xtal_hz * n / m; /*condition from Artery New Clock Config*/ if(500000000 <= temp_hz) { if(temp_hz <= 1200000000) { log_printf("MS:%u,NS:%u,FR:%u" CRLF, m, n, fr); PllArtety->ms = m; PllArtety->ns = n; PllArtety->fr = fr; res = true; } } } } } } } if(res) { LOG_INFO(PLL_CALC, "SpotPllVals!"); } else { LOG_ERROR(PLL_CALC, "NoPllVals!"); } return res; }
Отладка
Вот так, в симуляторе прошивки на PC я исполнил команду обсчета PLL и получил аж 5 решений на выбор.
Далее подставив четвёртое решение в утилиту проверки PLL я увидел, что конфиг валидный!
Успех!
Итоги
Как видите, программирование микроконтроллеров требует математической подготовки. А как Вы хотели?
Благодаря способности решать Диофантовы уравнения, Вы можете переконфигурировать частоту процессора далеко в run-time.
Можно, например, перейти в режим энергосбережения специально понизив частоту ядра. Или увеличить для ускорения расчета какой-то сложной математической процедуры.
Вот так..
Links/URLs
https://www.wolframalpha.com/input?i=8000000x-100000000y%3D0
http://latex.codecogs.com/eqneditor/editor.php
https://ru.wikipedia.org/wiki/Диофантово_уравнение