Embedded HandbookПриложения
Все разделы › XII. Сети и DevOps для инженера
STM32F407, STM32F429 · уровень средний · 10 мин чтения
Открыть в интерактивном справочнике

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, которая превращает биты в сигналы в проводе и обратно. Между ними две шины:

Опорные 50 МГц RMII подаёт на F407 сама PHY со встроенным умножителем или внешний генератор (RM0090, «RMII clock sources»).

Ethernet на F407: MAC с DMA внутри кристалла, PHY снаружи; между ними RMII — семь линий данных и тактов с опорными 50 МГц — и служебная шина MDC/MDIO; DMA сам кладёт принятые кадры в буферы ОЗУ по кольцу дескрипторов и отдаёт их программе битом OWN


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):

Программа готовит кольцо, отдаёт каждый дескриптор 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) делает стек в прошивке:


106а.7 Грабли


106а.8 Итоги

Полные исходники

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 своим кодом.

Открыть в интерактивном справочнике