μΉ΄ν…Œκ³ λ¦¬ μ—†μŒ

STM32 LwIP Modbus TCP κ³ μœ λŸ‰ νŒ¨ν‚· μˆ˜μ‹  μ‹œ μ†ŒμΌ“ 동결 원인 뢄석과 DMA Zero-Copy μˆ˜μ‹  버퍼링 μ΅œμ ν™”

κ²Œμž„κ΅μˆ˜ 2026. 9. 7. 01:01
λ°˜μ‘ν˜•

🌐 English Abstract

This technical article investigates the root cause of socket freeze issues occurring in STM32 MCUs running the LwIP TCP/IP stack under high-throughput Modbus TCP traffic. It analyzes how dynamic memory (pbuf) exhaustion triggers Ethernet MAC DMA Receive Buffer Unavailable (RBUS) deadlocks. Furthermore, it delivers a practical troubleshooting guide including lwipopts.h memory tuning, DMA Zero-Copy RX ring buffer refactoring, and STM32 D-Cache consistency management for robust industrial embedded networking.

μ‚°μ—…μš© μž„λ² λ””λ“œ μ‹œμŠ€ν…œμ—μ„œ STM32 마이크둜컨트둀러(MCU)와 LwIP(Lightweight IP) κ²½λŸ‰ λ„€νŠΈμ›Œν¬ μŠ€νƒμ˜ 쑰합은 ν‘œμ€€μ²˜λŸΌ μ‚¬μš©λ˜κ³  μžˆμŠ΅λ‹ˆλ‹€. 특히 곡μž₯ μžλ™ν™” 및 μ „λ ₯ μ œμ–΄ λΆ„μ•Όμ—μ„œ Modbus TCP ν”„λ‘œν† μ½œμ„ ν†΅ν•œ λͺ¨λ‹ˆν„°λ§ 및 μ œμ–΄ μš”κ΅¬μ‚¬ν•­μ΄ 급증함에 따라, MCUκ°€ μ²˜λ¦¬ν•΄μ•Ό ν•˜λŠ” μ΄ˆλ‹Ή νŒ¨ν‚· 수(PPS, Packets Per Second)의 λ‹¨μœ„λ„ 크게 μ¦κ°€ν–ˆμŠ΅λ‹ˆλ‹€.

ν•˜μ§€λ§Œ ν•„λ“œ ν˜„μž₯μ—μ„œ μ‹œμŠ€ν…œμ„ μš΄μš©ν•˜λ‹€ 보면, 초기 개발 λ‹¨κ³„λ‚˜ λ‹¨λ°œμ„± ν…ŒμŠ€νŠΈμ—μ„œλŠ” λ‚˜νƒ€λ‚˜μ§€ μ•Šλ˜ "μ†ŒμΌ“ 동결(Socket Freeze)" λ˜λŠ” "톡신 λ§ˆλΉ„" ν˜„μƒμ΄ κ³ μœ λŸ‰(High-Throughput) νŠΈλž˜ν”½ ν™˜κ²½μ—μ„œ λ‚œλ°μ—†μ΄ λ°œμƒν•˜λŠ” κ²½μš°κ°€ μžˆμŠ΅λ‹ˆλ‹€. ν•‘(Ping, ICMP) 응닡쑰차 λ˜μ§€ μ•Šκ±°λ‚˜ TCP 접속 μžμ²΄κ°€ 끊겨 리셋 μ „κΉŒμ§€ λ³΅κ΅¬λ˜μ§€ μ•ŠλŠ” 이 λ¬Έμ œλŠ” pbuf λ©”λͺ¨λ¦¬ 고갈과 Ethernet MAC DMA의 RBUS ꡐ착 μƒνƒœκ°€ κ²°ν•©ν•˜μ—¬ λ°œμƒν•˜λŠ” μ „ν˜•μ μΈ ν•˜λ“œμ›¨μ–΄-μ†Œν”„νŠΈμ›¨μ–΄ 경계 νŠΈλŸ¬λΈ”μŠˆνŒ… λŒ€μƒμž…λ‹ˆλ‹€.

λ³Έ ν¬μŠ€νŒ…μ—μ„œλŠ” STM32 MCU ν™˜κ²½μ—μ„œ κ³ μœ λŸ‰ Modbus TCP νŒ¨ν‚· μˆ˜μ‹  μ‹œ λ°œμƒν•˜λŠ” μ†ŒμΌ“ λ™κ²°μ˜ λ©”μ»€λ‹ˆμ¦˜μ„ 심격 λΆ„μ„ν•˜κ³ , 이λ₯Ό μ›μ²œμ μœΌλ‘œ ν•΄κ²°ν•˜κΈ° μœ„ν•œ LwIP λ©”λͺ¨λ¦¬ μŠ€νƒ μ΅œμ ν™” 및 DMA Zero-Copy(제둜 μΉ΄ν”Ό) μˆ˜μ‹  버퍼링 κ΅¬ν˜„ 기법을 μƒμ„Ένžˆ μ„€λͺ…ν•©λ‹ˆλ‹€.



1. Modbus TCP κ³ μœ λŸ‰ ν™˜κ²½μ—μ„œ λ°œμƒν•˜λŠ” μ†ŒμΌ“ λ™κ²°μ˜ 원인 뢄석

STM32의 이더넷 ν•˜λ“œμ›¨μ–΄ κ΅¬μ‘°λŠ” Ethernet MAC Controller와 λ‚΄λΆ€ DMA(Direct Memory Access) Engine을 톡해 PHY μΉ©μœΌλ‘œλΆ€ν„° 전달받은 데이터 νŒ¨ν‚·μ„ RAM μƒμ˜ μˆ˜μ‹  λ””μŠ€ν¬λ¦½ν„°(RX Descriptor) Ring Buffer에 κΈ°λ‘ν•©λ‹ˆλ‹€. μ΄λ•Œ LwIP μŠ€νƒκ³Όμ˜ 연동 κ³Όμ •μ—μ„œ μ‹¬κ°ν•œ 병λͺ© ν˜„μƒμ΄ λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€.

κ°€. Interrupt Flood와 pbuf Allocation μ‹€νŒ¨ λ©”μ»€λ‹ˆμ¦˜

κ³ μœ λŸ‰ Modbus TCP 폴링(Polling) νŒ¨ν‚·μ΄ λ°€λ €λ“€λ©΄ Ethernet RX μΈν„°λŸ½νŠΈκ°€ λŒ€λŸ‰μœΌλ‘œ λ°œμƒν•©λ‹ˆλ‹€. LwIP의 ν¬νŒ… μΈν„°νŽ˜μ΄μŠ€μΈ ethernetif_input() ν•¨μˆ˜λŠ” μˆ˜μ‹  μΈν„°λŸ½νŠΈ μ„œλΉ„μŠ€ 루틴(ISR) λ˜λŠ” λ…λ¦½λœ Ethernet Taskμ—μ„œ μ‹€ν–‰λ˜μ–΄ λ‹€μŒκ³Ό 같은 μž‘μ—…μ„ μˆ˜ν–‰ν•©λ‹ˆλ‹€:

  1. DMA RX λ””μŠ€ν¬λ¦½ν„°λ‘œλΆ€ν„° νŒ¨ν‚· μˆ˜μ‹  μ—¬λΆ€ 확인
  2. pbuf_alloc(PBUF_RAW, len, PBUF_POOL)을 ν˜ΈμΆœν•˜μ—¬ νŒ¨ν‚· 동적 λ©”λͺ¨λ¦¬ ν• λ‹Ή
  3. DMA μˆ˜μ‹  λ²„νΌμ˜ 데이터λ₯Ό μƒˆλ‘œ ν• λ‹Ήλœ pbuf둜 λ©”λͺ¨λ¦¬ 볡사(memcpy)
  4. LwIP μŠ€νƒμ˜ μƒμœ„ 계측(tcp_input())으둜 pbuf 전달

λ¬Έμ œλŠ” Modbus TCP νŒ¨ν‚· 처리 속도보닀 νŒ¨ν‚· μœ μž… 속도가 λΉ λ₯Ό λ•Œ λ°œμƒν•©λ‹ˆλ‹€. LwIP νž™ λ©”λͺ¨λ¦¬λ‚˜ PBUF_POOL이 남지 μ•Šκ²Œ 되면 pbuf_alloc()은 NULL을 λ°˜ν™˜ν•©λ‹ˆλ‹€.

λ‚˜. DMA RBUS(Receive Buffer Unavailable) ꡐ착 μƒνƒœ(Deadlock)

pbuf_alloc() μ‹€νŒ¨ μ‹œ μ μ ˆν•œ μ˜ˆμ™Έ μ²˜λ¦¬κ°€ 이루어지지 μ•ŠμœΌλ©΄, DMA μˆ˜μ‹  λ””μŠ€ν¬λ¦½ν„°λŠ” 'OWN bit = DMA' μƒνƒœλ‘œ λ˜λŒμ•„κ°€μ§€ λͺ»ν•˜κ³  'OWN bit = CPU' μƒνƒœλ‘œ λ‚¨μ•„μžˆκ²Œ λ©λ‹ˆλ‹€.

μ—°μ†ν•΄μ„œ λͺ¨λ“  RX λ””μŠ€ν¬λ¦½ν„°κ°€ λΉ„μ›Œμ§€μ§€ λͺ»ν•˜κ³  차버리면 STM32 이더넷 DMA μƒνƒœ λ ˆμ§€μŠ€ν„°(ETH_DMASR)에 RBUS(Receive Buffer Unavailable Status) λΉ„νŠΈκ°€ μ„ΈνŒ…λ©λ‹ˆλ‹€. DMA 엔진은 μˆ˜μ‹  버퍼가 μ—†λ‹€κ³  νŒλ‹¨ν•˜μ—¬ RX Suspend(μˆ˜μ‹  μΌμ‹œμ •μ§€) μƒνƒœλ‘œ μ§„μž…ν•˜κ²Œ 되며, 이후 μƒˆλ‘œμš΄ νŒ¨ν‚·μ΄ 물리 칩에 듀어와도 ν•˜λ“œμ›¨μ–΄ μˆ˜μ€€μ—μ„œ νŒ¨ν‚·μ„ λ“œλ‘­(Drop)ν•΄ λ²„λ¦½λ‹ˆλ‹€.

λ”μš± 치λͺ…적인 점은, LwIP λ©”λͺ¨λ¦¬κ°€ λ‚˜μ€‘μ— ν•΄μ œλ˜μ–΄ μ—¬μœ κ°€ 생기더라도 DMA RX 엔진이 Resume(재개) λͺ…령을 λ°›μ§€ λͺ»ν•˜λ©΄ μ˜μ›νžˆ νŒ¨ν‚·μ„ μˆ˜μ‹ ν•˜μ§€ λͺ»ν•˜λŠ” μ†ŒμΌ“ 동결 ν˜„μƒμœΌλ‘œ μ΄μ–΄μ§„λ‹€λŠ” κ²ƒμž…λ‹ˆλ‹€.



2. LwIP λ©”λͺ¨λ¦¬ μ˜΅μ…˜(lwipopts.h) 및 TCP μŠ€νƒ μ΅œμ ν™”

κ³ μœ λŸ‰ νŒ¨ν‚· 처리λ₯Ό μœ„ν•΄ κ°€μž₯ λ¨Όμ € μˆ˜ν–‰ν•΄μ•Ό ν•˜λŠ” μž‘μ—…μ€ LwIP ν™˜κ²½ μ„€μ • 파일인 lwipopts.hλ₯Ό 싀무 νŠΈλž˜ν”½μ— μ ν•©ν•˜κ²Œ νŠœλ‹ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€. κΈ°λ³Έ CubeMX μžλ™ 생성 섀정값은 λ©”λͺ¨λ¦¬ μ‚¬μš©λŸ‰μ„ κ·Ήλ‹¨μ μœΌλ‘œ 쀄이도둝 μ„€κ³„λ˜μ–΄ μžˆμ–΄ 고속 톡신 μ‹œ μ¦‰μ‹œ pbuf κ³ κ°ˆμ„ μœ λ°œν•©λ‹ˆλ‹€.

κ°€. 핡심 λ©”λͺ¨λ¦¬ νŒŒλΌλ―Έν„° μž¬μ„€μ •

λ‹€μŒ 섀정을 μ μš©ν•˜μ—¬ pbuf λ©”λͺ¨λ¦¬ ν’€κ³Ό μˆ˜μ‹  큐의 크기λ₯Ό λ„‰λ„‰ν•˜κ²Œ ν™•μž₯ν•΄μ•Ό ν•©λ‹ˆλ‹€:

  • MEM_ALIGNMENT = 4: STM32 32-bit μ•„ν‚€ν…μ²˜ μ •λ ¬ κΈ°μ€€ μ„€μ •.
  • MEM_SIZE: LwIP νž™ λ©”λͺ¨λ¦¬ 크기λ₯Ό μ΅œμ†Œ 16KB ~ 32KB μ΄μƒμœΌλ‘œ ν™•μž₯ (예: 16 * 1024).
  • PBUF_POOL_SIZE: νŒ¨ν‚· μˆ˜μ‹  μ „μš© ν’€ 개수λ₯Ό κΈ°λ³Έκ°’(4~8)μ—μ„œ 24~32 μ΄μƒμœΌλ‘œ λŒ€ν­ 증섀.
  • PBUF_POOL_BUFSIZE: Ethernet Frame 전체 크기(1518 Byte) + LwIP 헀더 μ˜€ν”„μ…‹ 정렬을 κ³ λ €ν•˜μ—¬ 1536 Byte λ˜λŠ” 1552 Byte둜 μ§€μ •.
  • MEMP_NUM_PBUF: μ‹œμŠ€ν…œ μ „μ²΄μ—μ„œ λ™μ‹œ 보유 κ°€λŠ₯ν•œ pbuf ꡬ쑰체 개수λ₯Ό 32 μ΄μƒμœΌλ‘œ μ„€μ •.

λ‚˜. TCP Flow Control 및 Window Size Tuning

솑신 μΈ‘(Modbus Client/Master)이 μˆ˜μ‹  μΈ‘(STM32 Server/Slave)의 처리 λŠ₯λ ₯을 μ΄ˆκ³Όν•˜μ—¬ νŒ¨ν‚·μ„ νΌλΆ“λŠ” 것을 막기 μœ„ν•΄ TCP μˆ˜μ‹  μœˆλ„μš° 크기λ₯Ό μ œμ–΄ν•΄μ•Ό ν•©λ‹ˆλ‹€.

  • TCP_WND: μˆ˜μ‹  μœˆλ„μš° 크기λ₯Ό (4 * TCP_MSS) μˆ˜μ€€μΈ 5840 Byte 정도 μ„€μ •. μˆ˜μ‹  버퍼가 λΆ€μ‘±ν•΄μ§€λ©΄ TCP Ack νŒ¨ν‚· λ‚΄μ˜ Window Sizeλ₯Ό 쀄여 솑신 츑의 솑신 속도λ₯Ό μžμ—°μŠ€λŸ½κ²Œ λŠ¦μΆ”λŠ” 흐름 μ œμ–΄(Flow Control)λ₯Ό μœ λ„ν•©λ‹ˆλ‹€.
  • TCP_QUEUE_OOSEQ = 0: μˆœμ„œκ°€ 뒀바뀐 νŒ¨ν‚·(Out-of-Order Packet)을 μž¬μ‘°ν•©ν•˜κΈ° μœ„ν•΄ 큐에 λ³΄κ΄€ν•˜λŠ” κΈ°λŠ₯을 λΉ„ν™œμ„±ν™”ν•©λ‹ˆλ‹€. 고속 둜컬 LAN 톡신 ν™˜κ²½μΈ Modbus TCPμ—μ„œλŠ” μˆœμ„œ 어긋남이 μ μœΌλ―€λ‘œ 이 κΈ°λŠ₯을 κΊΌμ„œ λ©”λͺ¨λ¦¬ λ‚­λΉ„λ₯Ό λ§‰μŠ΅λ‹ˆλ‹€.

3. STM32 Ethernet DMA Zero-Copy μˆ˜μ‹  버퍼링 κ΅¬ν˜„

λ©”λͺ¨λ¦¬ μ¦μ„€λ§ŒμœΌλ‘œλŠ” CPU μ˜€λ²„ν—€λ“œ 자체λ₯Ό ν•΄κ²°ν•  수 μ—†μŠ΅λ‹ˆλ‹€. μˆ˜μ‹  μΈν„°λŸ½νŠΈκ°€ λ°œμƒν•  λ•Œλ§ˆλ‹€ DMA λ²„νΌμ—μ„œ pbuf둜 데이터λ₯Ό memcpy() ν•˜λŠ” 방식은 CPU 사이클을 κ³Όλ„ν•˜κ²Œ μ†Œλͺ¨ν•˜μ—¬ κ³ μœ λŸ‰ λͺ¨λ“œλ²„μŠ€ νŒ¨ν‚· 처리의 병λͺ© 원인이 λ©λ‹ˆλ‹€. 이λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ Zero-Copy(제둜 μΉ΄ν”Ό) μˆ˜μ‹  μ•„ν‚€ν…μ²˜λ₯Ό ꡬ좕해야 ν•©λ‹ˆλ‹€.

κ°€. Zero-Copy μˆ˜μ‹  μ•„ν‚€ν…μ²˜μ˜ 원리

Zero-Copy μˆ˜μ‹  방식은 DMA RX λ””μŠ€ν¬λ¦½ν„°μ˜ 버퍼 포인터에 LwIP의 pbuf νŽ˜μ΄λ‘œλ“œ(Payload) μ£Όμ†Œλ₯Ό 직접 λ§€ν•‘ν•˜λŠ” κΈ°λ²•μž…λ‹ˆλ‹€.

  1. μ΄ˆκΈ°ν™” μ‹œ PBUF_POOLμ—μ„œ pbufλ₯Ό 미리 ν• λ‹Ήλ°›μ•„ DMA RX λ””μŠ€ν¬λ¦½ν„°μ˜ Buffer Address에 μ„€μ •ν•©λ‹ˆλ‹€.
  2. νŒ¨ν‚· μˆ˜μ‹  μ‹œ DMAκ°€ pbuf->payload λ©”λͺ¨λ¦¬μ— 직접 데이터λ₯Ό μ”λ‹ˆλ‹€.
  3. μΈν„°λŸ½νŠΈ λ°œμƒ μ‹œ CPUλŠ” 데이터 볡사 없이 ν•΄λ‹Ή pbuf ν¬μΈν„°λ§Œ μƒμœ„ LwIP μŠ€νƒμœΌλ‘œ 전달(Pass-through)ν•©λ‹ˆλ‹€.
  4. μƒˆλ‘œμš΄ pbufλ₯Ό ν• λ‹Ήλ°›μ•„ DMA λ””μŠ€ν¬λ¦½ν„°μ— λ‹€μ‹œ μ—°κ²°ν•˜κ³  OWN bitλ₯Ό DMA둜 λ„˜κ²¨μ€λ‹ˆλ‹€.

λ‚˜. DMA RBUS ꡐ착 μƒνƒœ νƒˆμΆœ μ˜ˆμ™Έ 처리 μ½”λ“œ

아무리 μ΅œμ ν™”ν•˜λ”λΌλ„ λ©”λͺ¨λ¦¬κ°€ μˆœκ°„ 고갈될 수 μžˆμŠ΅λ‹ˆλ‹€. μ΄λ•Œ pbuf_alloc()이 NULL을 λ°˜ν™˜ν•  경우, μˆ˜μ‹  νŒ¨ν‚·μ„ μ¦‰μ‹œ λ“œλ‘­ν•˜κ³  DMA μˆ˜μ‹  엔진을 λ³΅κ΅¬ν•˜λŠ” μ½”λ“œκ°€ ν•„μˆ˜μ μž…λ‹ˆλ‹€.

μ•„λž˜ μ½”λ“œλŠ” STM32 HAL / LL 라이브러리 ν™˜κ²½μ—μ„œ ethernetif_input()을 λ¦¬νŒ©ν† λ§ν•œ μ˜ˆμ™Έ 처리 패치 μ˜ˆμ‹œμž…λ‹ˆλ‹€:

struct pbuf *low_level_input(struct netif *netif)
{
    struct pbuf *p = NULL;
    ETH_BufferTypeDef RxBuff;
    uint32_t framelength = 0;

    /* 1. DMA RX λ””μŠ€ν¬λ¦½ν„°λ‘œλΆ€ν„° νŒ¨ν‚· 정보 νšλ“ */
    if (HAL_ETH_GetRxDataBuffer(&heth, &RxBuff) == HAL_OK)
    {
        framelength = HAL_ETH_GetRxDataLength(&heth);

        /* 2. Zero-Copyλ₯Ό μœ„ν•œ Custom pbuf 생성 λ˜λŠ” μƒˆ pbuf ν• λ‹Ή */
        p = pbuf_alloc(PBUF_RAW, framelength, PBUF_POOL);

        if (p != NULL)
        {
            /* μˆ˜μ‹ λœ DMA 버퍼 데이터λ₯Ό pbuf둜 직접 λ§€ν•‘ λ˜λŠ” Zero-Copy 처리 */
            /* STM32H7/F7 λ“± Cache 적용 λͺ¨λΈμ˜ 경우 Invalidate D-Cache ν•„μˆ˜ */
            SCB_InvalidateDCache_by_Addr((uint32_t *)RxBuff.buffer, framelength);
            
            pbuf_take(p, RxBuff.buffer, framelength);
        }
        else
        {
            /* [μ€‘μš”] pbuf ν• λ‹Ή μ‹€νŒ¨ μ‹œ (λ©”λͺ¨λ¦¬ 고갈) ν•΄λ‹Ή νŒ¨ν‚· λ“œλ‘­ 및 DMA λ””μŠ€ν¬λ¦½ν„° μ¦‰μ‹œ λ°˜ν™˜ */
            // LINK_STATS_INC(link.memerr);
            // LINK_STATS_INC(link.drop);
        }

        /* 3. DMA μˆ˜μ‹  λ””μŠ€ν¬λ¦½ν„° 리셋 및 CPUμ—μ„œ DMA둜 μ†Œμœ κΆŒ λ°˜ν™˜ */
        HAL_ETH_BuildRxDescriptors(&heth);
    }

    /* 4. RBUS (Receive Buffer Unavailable) μΌμ‹œμ •μ§€ μƒνƒœ 점검 및 Resume */
    if ((heth.Instance->DMASR & ETH_DMASR_RBUS) != 0U)
    {
        /* RBUS ν”Œλž˜κ·Έ 클리어 */
        heth.Instance->DMASR = ETH_DMASR_RBUS;
        
        /* DMA RX Poll Demand λ ˆμ§€μŠ€ν„°μ— 값을 μ¨μ„œ DMA μˆ˜μ‹  재개 */
        heth.Instance->DMARXPOLLD = 0U;
    }

    return p;
}

λ‹€. Cortex-M7 (STM32F7 / STM32H7) D-Cache 일관성 μœ μ§€

High-Performance STM32 MCUλ₯Ό μ‚¬μš©ν•˜λŠ” 경우 Data Cache(D-Cache) ν™œμ„±ν™”μ— λ”°λ₯Έ 일관성(Coherency) 문제λ₯Ό λ°˜λ“œμ‹œ κ³ λ €ν•΄μ•Ό ν•©λ‹ˆλ‹€. DMAκ°€ SRAM에 데이터λ₯Ό 써도 CPU의 D-Cache에 이전 데이터가 λ‚¨μ•„μžˆμœΌλ©΄ Modbus νŒ¨ν‚· 데이터가 μ˜€μ—Ό(Corruption)λ©λ‹ˆλ‹€.

  • MPU(Memory Protection Unit) μ„€μ •: 이더넷 DMA λ””μŠ€ν¬λ¦½ν„° 및 μˆ˜μ‹  버퍼 μ˜μ—­μ„ Bufferable, Non-Cacheable μ˜μ—­μœΌλ‘œ μ§€μ •ν•˜λŠ” 것을 ꢌμž₯ν•©λ‹ˆλ‹€.
  • Cache Invalidation: MPU λ―Έμ„€μ • μ‹œ νŒ¨ν‚· 읽기 μ „ SCB_InvalidateDCache_by_Addr()λ₯Ό λ°˜λ“œμ‹œ ν˜ΈμΆœν•˜μ—¬ μΊμ‹œλ₯Ό λ¬΄νš¨ν™”ν•΄μ•Ό ν•©λ‹ˆλ‹€.

4. 싀무 검증 및 μ„±λŠ₯ μΈ‘μ • κ²°κ³Ό

μœ„μ˜ μ΅œμ ν™” μž‘μ—…μ„ STM32F407/STM32H743 기반 Modbus TCP Slave μž₯비에 μ μš©ν•˜μ—¬ κ³ μœ λŸ‰ λΆ€ν•˜ ν…ŒμŠ€νŠΈ(Stress Test)λ₯Ό μ§„ν–‰ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

ꡬ뢄 μ΅œμ ν™” μ „ (Default Config) μ΅œμ ν™” ν›„ (Zero-Copy + RBUS Resume)
μ΅œλŒ€ μˆ˜μ‹  νŒ¨ν‚· μ²˜λ¦¬λŸ‰ μ•½ 350 PPS (μ΄ˆλ‹Ή νŒ¨ν‚· 수) 2,500+ PPS 이상 연속 처리
CPU 점유율 (1,000 PPS κΈ°μ€€) 85% 이상 (병λͺ© 심화) 22% λ‚΄μ™Έ μ•ˆμ •ν™”
μ†ŒμΌ“ 동결 λ°œμƒ μ—¬λΆ€ λΆ€ν•˜ μ£Όμž… 10λΆ„ 이내 RBUS 락업 λ°œμƒ 72μ‹œκ°„ 둱런 ν…ŒμŠ€νŠΈ 패슀 (무결점)


5. κ²°λ‘  및 μš”μ•½

STM32 MCU 기반 LwIP λ„€νŠΈμ›Œν¬ μ‘μš© ν”„λ‘œκ·Έλž¨μ—μ„œ λ°œμƒν•˜λŠ” μ†ŒμΌ“ 동결 ν˜„μƒμ€ λ‹¨μˆœν•œ μ†Œν”„νŠΈμ›¨μ–΄ 버그가 μ•„λ‹Œ, LwIP λ©”λͺ¨λ¦¬ 고갈과 Ethernet DMA ν•˜λ“œμ›¨μ–΄μ˜ RBUS μˆ˜μ‹  쀑단 μƒνƒœκ°€ 맞물렀 λ°œμƒν•˜λŠ” λŒ€ν‘œμ μΈ νŠΈλŸ¬λΈ”μŠˆνŒ… μ‚¬λ‘€μž…λ‹ˆλ‹€.

이 문제λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄μ„œλŠ” λ‹€μŒ μ„Έ κ°€μ§€ 핡심 λŒ€μ‘ μ‘°μΉ˜κ°€ λ°˜λ“œμ‹œ λ³‘ν–‰λ˜μ–΄μ•Ό ν•©λ‹ˆλ‹€:

  1. lwipopts.h νŠœλ‹: PBUF_POOL_SIZE 증섀 및 TCP_WND μ‘°μ ˆμ„ ν†΅ν•œ μ†Œν”„νŠΈμ›¨μ–΄ μˆ˜μ‹  μ—¬μœ  확보.
  2. Zero-Copy RX κ΅¬ν˜„: memcpy() μ΅œμ†Œν™” 및 DMA Ring Buffer μ΅œμ ν™”λ₯Ό ν†΅ν•œ CPU 점유율 κ°μ†Œ.
  3. RBUS μ˜ˆμ™Έ 처리 μ½”λ”©: pbuf ν• λ‹Ή μ‹€νŒ¨ μ‹œ νŒ¨ν‚· λ“œλ‘­ 및 DMA RX Resume(DMARXPOLLD) κ°•μ œ 싀행을 ν†΅ν•œ ν•˜λ“œμ›¨μ–΄ 락업 λ°©μ§€.

ν˜„μž₯ μ„€μΉ˜ ν›„ λ°œμƒν•˜λŠ” 이더넷 톡신 λ§ˆλΉ„λŠ” μ œν’ˆ 신뒰성에 치λͺ…적인 타격을 μ€λ‹ˆλ‹€. Modbus TCP λ“± μ΄ˆλ‹Ή 데이터 전솑 λΉˆλ„κ°€ 높은 μ‚°μ—…μš© μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μ„ 개발 쀑이라면, 초기 섀계 단계뢀터 λ³Έ ν¬μŠ€νŒ…μ˜ Zero-Copy μˆ˜μ‹  버퍼링 ꡬ쑰와 RBUS μ˜ˆμ™Έ 처리 기법을 λ°˜λ“œμ‹œ λ°˜μ˜ν•˜μ‹œκΈ° λ°”λžλ‹ˆλ‹€.

λ°˜μ‘ν˜•