Pull to refresh

Расчёт параметров предделителя для 8250-совместимых USART

Reading time2 min
Views5.9K
Сегодняшним вечером я расскажу сказку о том, как можно эффективно вычислить параметры предделителя, который обычно используется для задания тактовой частоты USART портов, в частности 8250-совместимых, применяемых в Intel SoC.


На текущий момент существует как минимум две линейки Intel SoC, в которых применяется USART с дробным предделителем
  1. Medfield, CloverTrail, Tangier
  2. BayTrail, Braswell

В чём между ними разница я расскажу чуть позже.

Ниже представлены формулы, по которым вычисляется Fusart — тактовая частота USART, и baud — скорость передачи.




Остальные
Fref — частота до предделителя, m — числитель, n — знаменатель рациональной дроби предделителя, prescaler — целое число, на которое делится Fusart, DLAB — 16-разрядный делитель, представленный в 8250-совместимых чипах.

Из всего набора нам известны только две величины, а именно: Fref и baud. Величина prescaler как правило равна 16 и в некоторых чипах отсутствует возможность её изменять (именно этим отличаются линейки Intel SoC друг от друга)

Расчёт величины prescaler



Поскольку мы работаем с дробным предделителем, а не с полноценной PLL, то из (1) следует




И следовательно мы можем уже вычислить значение prescaler
prescaler = 16

fusart = baud * prescaler
if fref < fusart:
        if fref >= baud:
            prescaler = fref / baud
        else:
            prescaler = 1
        fusart = baud * prescaler


Вычисление Fusart



Предыдущая часть работает только в том случае, если не выполняется (3). В противном же случае Fusart может оказаться сильно низкой относительно Fref при запрашиваемой довольно низкой скорости передачи, например 110 бод.

А поскольку числитель и знаменатель делителя как правило равной ширины (в битах), то неплохо бы ограничить их расхождение. Для этого приблизим Fusart к Fref насколько возможно.
fusart <<= int(math.log(fref / fusart, 2))

Почему выбрано именно такое приближение? Дело вот в чём, если у нас возникнет переполнение DLAB, а он всего 16-разрядный, то в случае использования степени двойки мы простейшими битовыми операциями можем это отследить.

Очередь за знаменателем



Очевидно, что теперь из (1,2), следует простая пропорция



Таким образом мы легко вычисляем числитель и знаменатель рациональной дроби предделителя.
divisor = gcd(fref, fusart)
n = fref / divisor
while n > (2 ** width - 1):
    divisor <<= 1
    n >>= 1
m = fusart / divisor


Здесь width — ширина в битах числителя и знаменателя.

Вот собственно и всё. Для желающих исходник на Python можно найти на GitHub Gist (он уже несколько раз обновился и в будущем может обновится ещё).

Пример рассчёта для Fref=50MHz, width=24 bits
Вывод в формате 1) m, 2) n, 3) prescaler, 4) Fusart, 5) загадочное число, 6) список скоростей передачи, в скобках значение DLAB.

24 25 12 48000000 64000000 4000000(1)
49 50 14 49000000 56000000 3500000(1)
4 5 16 40000000 40000000 2500000(1)
16 25 16 32000000 32000000 500000(4),1000000(2),2000000(1)
24 25 16 48000000 48000000 1500000(2),3000000(1)
2304 3125 16 36864000 36864000 576000(4),1152000(2)
8192 15625 16 26214400 26214400 50(32768),200(8192)
9216 15625 16 29491200 29491200 1800(1024),57600(32),115200(16),230400(8),460800(4),921600(2),1843200(1)
12288 15625 16 39321600 39321600 75(32768),150(16384),300(8192),600(4096),1200(2048),2400(1024),4800(512),9600(256),19200(128),38400(64)
45056 78125 16 28835840 28835840 110(16384)
274432 390625 16 35127296 35127296 134(16384)


Прошу заметить, что приведенный алгоритм прост и быстр, хотя не настолько точен, но нашей цели удовлетворяет.
Tags:
Hubs:
+11
Comments0

Articles

Change theme settings