106а. Ethernet на F407: RMII, PHY по MDIO и дескрипторы DMA
Разделы 105 и 106 разобрали сеть по уровням: кадр, IP, ARP, TCP и UDP. Раздел 37 сравнил способы подключить прибор к сети и назвал два пути для провода: микросхема со своим стеком (W5500 по SPI) или MAC внутри микроконтроллера и стек TCP/IP в прошивке. Здесь — второй путь на F407: как MAC говорит с микросхемой PHY, как F407 узнаёт, есть ли кабель, и как кадры попадают в память без участия процессора.
Опоры: справочное руководство RM0090 (глава Ethernet), драйвер
EMAC_STM32F4xx.c из пака Keil STM32F4xx_DFP, драйверы PHY LAN8742A
и DP83848C из пака CMSIS-Driver. Код собран armclang для F407; расчёты —
скриптом на ПК. QEMU Ethernet F407 не моделирует.
Как это называется в документации
Оригинал Перевод MAC (media access control) контроллер кадров: адреса, FCS, очереди — внутри F407 PHY (physical layer) приёмопередатчик: провод, трансформатор, сигналы — отдельная микросхема MII, RMII шина между MAC и PHY: 4 бита на 25 МГц или 2 бита на 50 МГц SMI: MDC, MDIO служебная шина к регистрам PHY: такт и данные REF_CLK опорные 50 МГц RMII descriptor, OWN дескриптор DMA и бит «чей он сейчас» auto-negotiation автосогласование скорости и дуплекса checksum offload (IPCO) подсчёт контрольных сумм IP, TCP, UDP аппаратно
106а.1 Что внутри, что снаружи
У F407 внутри — MAC с собственным DMA и FIFO по 2 КБ на приём и передачу. Снаружи — PHY: микросхема вроде LAN8742A или DP83848C, которая превращает биты в сигналы в проводе и обратно. Между ними две шины:
- данные: RMII — TXD0, TXD1, TX_EN, RXD0, RXD1, CRS_DV и REF_CLK; 50 МГц × 2 бита = 100 Мбит/с. MII — по 4 бита на 25 МГц, выводов больше. На платах почти всегда RMII;
- управление: MDC и MDIO — двухпроводная служебная шина к регистрам PHY, до 32 PHY на одной шине.
Опорные 50 МГц RMII подаёт на F407 сама PHY со встроенным умножителем или внешний генератор (RM0090, «RMII clock sources»).
106а.2 Порядок включения: RMII — до тактов
Выбор MII или RMII — бит 23 MII_RMII_SEL регистра SYSCFG_PMC. RM0090 (33.4.4) требует выставить его, пока контроллер Ethernet в сбросе или до включения его тактов. Потом — такты MAC и программный сброс DMA:
void eth_rmii_start(void)
{
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;
SYSCFG->PMC |= SYSCFG_PMC_MII_RMII_SEL; /* RMII — до включения тактов MAC */
RCC->AHB1ENR |= RCC_AHB1ENR_ETHMACEN | RCC_AHB1ENR_ETHMACTXEN | RCC_AHB1ENR_ETHMACRXEN;
ETH->DMABMR |= ETH_DMABMR_SR;
while (ETH->DMABMR & ETH_DMABMR_SR) { /* без 50 МГц от PHY на REF_CLK здесь зависнет */
}
}
Бит SR сбрасывается сам, когда сброс закончен во всех тактовых доменах
ядра. Часть логики MAC тактируется от REF_CLK (RM0090, рисунок 358): если
PHY не дала 50 МГц — не запитана, в сбросе, не тот кварц, — цикл не кончится.
Драйвер из пака делает то же самое в том же порядке: SYSCFG->PMC —
раньше тактов.
106а.3 PHY по MDIO: кто там и есть ли кабель
Регистр PHY читают через MACMIIAR и MACMIIDR: адрес PHY (5 бит), номер регистра (5 бит), делитель MDC и бит MB «занято»:
uint16_t mdio_read(uint32_t phy, uint32_t reg)
{
ETH->MACMIIAR = ETH_MACMIIAR_CR_Div102 | (phy << ETH_MACMIIAR_PA_Pos)
| (reg << ETH_MACMIIAR_MR_Pos) | ETH_MACMIIAR_MB;
while (ETH->MACMIIAR & ETH_MACMIIAR_MB) { /* 64 такта MDC — около 39 мкс */
}
return (uint16_t)ETH->MACMIIDR;
}
MDC не должен быть выше 2,5 МГц, и делитель берут по частоте HCLK (RM0090, таблица 187):
| HCLK | Делитель | MDC |
|---|---|---|
| 168 МГц | /102 | 1,647 МГц |
| 120 МГц | /62 | 1,935 МГц |
| 84 МГц | /42 | 2,000 МГц |
| 48 МГц | /26 | 1,846 МГц |
Кадр MDIO — 64 бита: 32 единицы преамбулы, начало, код операции, адрес PHY, номер регистра, 2 такта поворота и 16 бит данных. При 168 МГц одно чтение регистра — 38,9 мкс. Опрашивать PHY в прерывании каждую миллисекунду не стоит; раз в 100–500 мс — достаточно.
Первые регистры у всех PHY одинаковы (стандарт IEEE 802.3), и драйверы из пака называют их так:
| Регистр | Что в нём |
|---|---|
| 0 BCR | сброс (бит 15), автосогласование (бит 12), скорость и дуплекс вручную |
| 1 BSR | связь есть (бит 2), автосогласование закончено (бит 5), что умеет PHY |
| 2, 3 PHYIDR | идентификатор изготовителя и модели |
LAN8742A отвечает 0x0007 и 0xC130, DP83848C — 0x2000 и 0x5C90. Драйвер
LAN8742A сравнивает второй регистр по маске 0xFFF0: в младших четырёх
битах — ревизия кристалла, у разных партий она разная. Сравнение «как
есть» сломается на следующей партии.
Результат автосогласования — скорость и дуплекс — у каждой PHY в своём регистре (у LAN8742A — 31, биты «10 Мбит/с» и «полный дуплекс»). Его переносят в MACCR (биты FES и DM): MAC сам этого не узнаёт.
106а.4 Дескрипторы: как кадры попадают в память
DMA Ethernet не работает с регистрами «адрес и длина», как DMA в разделе 9: он идёт по списку дескрипторов в ОЗУ. Обычный дескриптор — четыре слова, 16 байт (RM0090, 33.6.7 и 33.6.8):
- слово 0 — состояние; бит 31 OWN: 1 — дескриптор у DMA, 0 — у программы; у принятого кадра в битах 29:16 длина (FL);
- слово 1 — размер буфера (RBS1, кратен 4) и флаги, в том числе RCH: четвёртое слово — адрес следующего дескриптора;
- слово 2 — адрес буфера;
- слово 3 — следующий дескриптор.
Программа готовит кольцо, отдаёт каждый дескриптор DMA (OWN = 1) и пишет адрес первого в DMARDLAR. Дальше DMA сам кладёт каждый кадр в очередной буфер, пишет длину и снимает OWN — кадр теперь у программы. Разобрала — снова ставит OWN.
void eth_rx_ring_init(void)
{
for (uint32_t i = 0; i < NRX; i++) {
rx_ring[i].buf = (uint8_t *)rx_buf[i];
rx_ring[i].des1 = RCH | BUF_SIZE;
rx_ring[i].next = &rx_ring[(i + 1) % NRX];
rx_ring[i].des0 = OWN; /* отдать DMA — последним */
}
ETH->DMARDLAR = (uint32_t)&rx_ring[0];
}
OWN пишется последним: пока он 0, DMA не тронет дескриптор, даже если остальные поля ещё не готовы. Длину кадра armclang достаёт одной командой извлечения поля, а проверку OWN — сравнением слова со знаком (бит 31 — знаковый):
eth_rx_len: LDR r1,[r0,#0]
UBFX r0,r1,#16,#14
CMP r1,#0
IT MI
MOVMI r0,#0
Если включить аппаратный подсчёт контрольных сумм (бит IPCO в MACCR) или отметки времени, дескрипторы нужны расширенные — 32 байта, восемь слов (бит EDFE в DMABMR). Драйвер из пака включает подсчёт сумм по умолчанию и поэтому берёт расширенные.
106а.5 Сколько памяти и сколько времени
Драйвер EMAC_STM32F4xx.c: 4 буфера приёма и 2 передачи по 1536 байт —
6144 + 3072 = 9216 байт; дескрипторов 6 — 96 байт обычных или 192
расширенных.
Сколько программа может не разбирать приём, пока кольцо из 4 буферов не переполнится (100 Мбит/с, 80 нс на байт; числа кадров — раздел 105):
| Поток | Один кадр | 4 буфера заполнятся за |
|---|---|---|
| самые короткие кадры (84 байтовых интервала) | 6,72 мкс | 26,88 мкс |
| самые длинные (1538) | 123,04 мкс | 492,16 мкс |
Поток коротких кадров — широковещательный шторм в сети — переполнит кольцо быстрее, чем пройдёт одно прерывание со стеком. DMA упирается в дескриптор, который ещё у программы, ставит флаг RBUS («буфер недоступен») и приостанавливает приём: кадры теряются, но память цела. Больше буферов — больше запас; меньше широковещательных кадров — фильтр MAC по адресу.
106а.6 Стек поверх: что брать
Драйвер MAC и PHY — только кадры. IP, ARP, TCP и DHCP (разделы 105–107) делает стек в прошивке:
- lwIP — его подключает CubeMX вместе с драйвером HAL
stm32f4xx_hal_eth; - MDK Network в паке Keil — поверх драйвера CMSIS
EMAC_STM32F4xx.cи драйвера PHY; у платы MCBSTM32F400 в паке есть готовые примеры HTTP-сервера, Telnet, FTP, SMTP и SNMP; - если нужен только TCP и нет памяти под стек — W5500 по SPI (раздел 37): стек в микросхеме, а у F103 MAC нет вовсе.
106а.7 Грабли
- RMII выбран после тактов. Бит MII_RMII_SEL выставлен после включения тактов MAC — контроллер остаётся в MII. Сначала SYSCFG_PMC, потом RCC.
- Сброс DMA висит. Нет 50 МГц на REF_CLK: PHY в сбросе, не запитана, у неё не тот кварц или режим. Бит SR не снимется никогда — ставьте тайм-аут и проверяйте такт осциллографом.
- Буферы в CCM. CCM (64 КБ с 0x1000 0000) доступна только процессору: DMA Ethernet её не видит (RM0090, 2.1 и 2.3.1). Дескрипторы и буферы — в SRAM1 или SRAM2.
- Обычные дескрипторы при включённом IPCO. Подсчёт контрольных сумм требует расширенных дескрипторов: с обычными раскладка слов не та, которую ждёт DMA.
- OWN раньше полей. Дескриптор отдан DMA раньше, чем записан адрес буфера — кадр уйдёт в случайную память.
- Идентификатор PHY сравнивают целиком. Младшие 4 бита второго
регистра — ревизия; сравнение без маски
0xFFF0ломается на новой партии. - Скорость не перенесена в MAC. PHY договорилась о 10 Мбит/с или полудуплексе, а MACCR осталась на 100 Мбит/с и полный дуплекс — кадры идут, но с ошибками и повторами.
106а.8 Итоги
- MAC с DMA — внутри F407, PHY — снаружи; между ними RMII (50 МГц × 2 бита) и служебная шина MDC/MDIO.
- MII_RMII_SEL — до тактов MAC; сброс DMA не кончится без 50 МГц от PHY.
- MDC не выше 2,5 МГц: при HCLK 168 МГц делитель /102 — 1,647 МГц, 38,9 мкс на регистр PHY.
- Кадры идут по кольцу дескрипторов по 16 байт (32 — с IPCO); бит OWN решает, чей дескриптор.
- Драйвер из пака: 9216 байт буферов; 4 буфера коротких кадров заполняются за 26,88 мкс.
- Буферы и дескрипторы — не в CCM: DMA её не видит.
Полные исходники
eth106a.c — Ethernet F407 по регистрам: RMII, чтение PHY по MDIO, кольцо дескрипторов приёма.
/* 106а: Ethernet F407 по регистрам — RMII, чтение PHY по MDIO, кольцо дескрипторов приёма.
Собирается armclang: -DSTM32F407xx. */
#include <stddef.h>
#include <stdint.h>
/* обычный дескриптор DMA — четыре слова (RM0090, 33.6.7 и 33.6.8) */
typedef struct eth_desc {
volatile uint32_t des0; /* бит 31 OWN: 1 — дескриптор у DMA */
uint32_t des1; /* размер буфера и флаги */
uint8_t *buf; /* адрес буфера */
struct eth_desc *next; /* следующий дескриптор (режим цепочки) */
} eth_desc_t;
_Static_assert(sizeof(eth_desc_t) == 16, "обычный дескриптор — 16 байт");
_Static_assert(SYSCFG_PMC_MII_RMII_SEL == (1u << 23), "MII_RMII_SEL — бит 23 SYSCFG_PMC");
_Static_assert(ETH_MACMIIAR_CR_Div102 == (4u << ETH_MACMIIAR_CR_Pos), "CR = 100: HCLK/102");
#define OWN (1u << 31)
#define RCH (1u << 14) /* RDES1: next — адрес следующего дескриптора */
#define NRX 4u
#define BUF_SIZE 1536u /* кратно 4: RBS1 обязан быть кратен ширине шины */
static eth_desc_t rx_ring[NRX];
static uint32_t rx_buf[NRX][BUF_SIZE / 4]; /* в SRAM1/SRAM2, не в CCM: DMA её не видит */
void eth_rmii_start(void)
{
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;
SYSCFG->PMC |= SYSCFG_PMC_MII_RMII_SEL; /* RMII — до включения тактов MAC */
RCC->AHB1ENR |= RCC_AHB1ENR_ETHMACEN | RCC_AHB1ENR_ETHMACTXEN | RCC_AHB1ENR_ETHMACRXEN;
ETH->DMABMR |= ETH_DMABMR_SR;
while (ETH->DMABMR & ETH_DMABMR_SR) { /* без 50 МГц от PHY на REF_CLK здесь зависнет */
}
}
uint16_t mdio_read(uint32_t phy, uint32_t reg)
{
ETH->MACMIIAR = ETH_MACMIIAR_CR_Div102 | (phy << ETH_MACMIIAR_PA_Pos)
| (reg << ETH_MACMIIAR_MR_Pos) | ETH_MACMIIAR_MB;
while (ETH->MACMIIAR & ETH_MACMIIAR_MB) { /* 64 такта MDC — около 39 мкс */
}
return (uint16_t)ETH->MACMIIDR;
}
int phy_is_lan8742a(uint32_t phy)
{
return mdio_read(phy, 2) == 0x0007 && (mdio_read(phy, 3) & 0xFFF0u) == 0xC130;
}
void eth_rx_ring_init(void)
{
for (uint32_t i = 0; i < NRX; i++) {
rx_ring[i].buf = (uint8_t *)rx_buf[i];
rx_ring[i].des1 = RCH | BUF_SIZE;
rx_ring[i].next = &rx_ring[(i + 1) % NRX];
rx_ring[i].des0 = OWN; /* отдать DMA — последним */
}
ETH->DMARDLAR = (uint32_t)&rx_ring[0];
}
/* длина принятого кадра — поле FL (RDES0[29:16]); 0, если дескриптор ещё у DMA */
uint32_t eth_rx_len(const eth_desc_t *d)
{
uint32_t s = d->des0;
return (s & OWN) ? 0u : (s >> 16) & 0x3FFFu;
}
Дальше: раздел 106б «Пакет по байтам: Ethernet, ARP, IPv4, ICMP, UDP и TCP глазами микроконтроллера» — те же кадры, разобранные до байта: ARP, ping, UDP и сброс TCP своим кодом.