Зачем Программисту Микроконтроллеров Диофантовы Уравнения

5a02fdd786d37b153f93188187d8238d.png

Пролог

В математике есть такая тема как Диофантовы уравнения. В самом простом виде выглядят они так.

ax+by=d \qquad \qquad\qquad (1)

Тут одно уравнение и две неизвестные: x, y. При этом a, b, d — это известные константы.

На первый взгляд может показаться как бесполезная вишь для программирования MCU, но не тут то было.

Когда программируешь микроконтроллер, то первое что приходится делать это конфигурировать тактирование процессорного ядра. Тактированием заправляет phase-locked loop (PLL) подсистема SoC (а).

И там надо подобрать три константы: M, N и F. В микроконтроллере Artery эта электрическая цепь выглядит вот так.

80400ebd92969cd91d708da9dc04aef1.png

По формуле (2)

\frac{(N\frac{F_{quartz}}{M})   }  {F} = F_{sys}    \qquad  \qquad  (2)

Вычисляется системная частота F_sys. Это уравнение преобразуется в (3)

F_{quartz}   \frac{N}{M} -F_{sys}F     = 0   \qquad  \qquad  (3)

А уравнение (3) это самое настоящее диофантово уравнение. Тут F_quartz — это частота кварцевого резонатора, который задается схемотехникой электронной платы.
F_sys задается программистом для достижения желаемой производительности прошивки. Остаются целочисленные неизвестные N, M и F.

Далее накладываются еще ограничения уже производителем микроконтроллера. Для Artery это

Переменная

Минимальное

Максимальное

M

1

15

N

31

500

FR (степени двойки)

1

32

Это даже прописано в исходных кодах на Cи, которые предоставляет производитель.


/**
  *    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)
  

Постановка задачи.

Есть электронная плата с MCU Artery. К ней подключен кварцевый резонатор c частотой 8 MHz. Необходимо сконфигурировать системную частоту на 100 MHz.

Какими должны быть коэффициенты PLL: N, M и RF?

Решение

По сути, задача свелась к тому чтобы решить вот это диофантово уравнение (4).

8000000 \frac{N}{M} -100000000F     = 0   \qquad  \qquad  (4)

Можно попробовать прибегнуть к помощи Artificial intelligence (AI) на сайте Wolfram Alfa

14725b6fa0c0bded19ae424afe162700.png

Однако это не решение в частном виде. На практике нужно численное решение, чтобы проинициализировать им функцию 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 решений на выбор.

205f1fec2d6b513764a06ecf8ca592a5.png

Далее подставив четвёртое решение в утилиту проверки я увидел, что конфиг валидный.

2445bbec2b0a0bd1b64d4cd85fb76f98.png

Успех!

Итоги

Как видите, программирование микроконтроллеров требует математической подготовки.

Благодаря способности решать диофантовы уравнения, вы можете переконфигурировать частоту процессора далеко в 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/Диофантово_уравнение

© Habrahabr.ru