์นดํ…Œ๊ณ ๋ฆฌ ์—†์Œ

STM32H7 LwIP TCP ๋Œ€์šฉ๋Ÿ‰ ํ†ต์‹  ์‹œ pbuf ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๋ถ„์„๊ณผ Zero-Copy ๋น„๋™๊ธฐ ์ตœ์ ํ™” ๊ฐ€์ด๋“œ

๊ฒŒ์ž„๊ต์ˆ˜ 2026. 9. 17. 13:01
๋ฐ˜์‘ํ˜•

๐ŸŒ English Abstract

This article addresses critical Ethernet communication failures on STM32H7 microcontrollers using the LwIP protocol stack during continuous high-throughput TCP data streaming. It explores the root causes of pbuf memory leaks triggered by D-Cache coherency mismatch, improper MPU (Memory Protection Unit) configurations, and DMA descriptor starvation. Furthermore, it details step-by-step troubleshooting procedures and demonstrates a Zero-Copy asynchronous pipeline implementation to achieve maximum throughput with minimal CPU overhead.

STM32H7 ์‹œ๋ฆฌ์ฆˆ๋Š” ์ตœ๋Œ€ 480MHz ์ด์ƒ์˜ ๋†’์€ ํด๋Ÿญ ์Šคํ”ผ๋“œ์™€ ๊ฐ•๋ ฅํ•œ ํผํฌ๋จผ์Šค๋ฅผ ์ œ๊ณตํ•˜๋Š” ARM Cortex-M7 ๊ธฐ๋ฐ˜์˜ 32๋น„ํŠธ ๋งˆ์ดํฌ๋กœ์ปจํŠธ๋กค๋Ÿฌ(MCU)์ž…๋‹ˆ๋‹ค. ํŠนํžˆ ๊ณ ์† ์ดˆ์ €์ง€์—ฐ ์ด๋”๋„ท(Ethernet) MAC ๋งคํฌ๋กœ๋ฅผ ํƒ‘์žฌํ•˜๊ณ  ์žˆ์–ด ์‚ฐ์—…์šฉ ๊ฒŒ์ดํŠธ์›จ์ด, ์˜๋ฃŒ๊ธฐ๊ธฐ, ๊ณ ์„ฑ๋Šฅ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘(DAQ) ์žฅ๋น„ ๋“ฑ์— ๋„๋ฆฌ ์“ฐ์ž…๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ LwIP(Lightweight IP) ๊ฒฝ๋Ÿ‰ ๋„คํŠธ์›Œํฌ ํ”„๋กœํ† ์ฝœ ์Šคํƒ์„ STM32H7์— ์ด์‹ํ•˜์—ฌ ๋Œ€์šฉ๋Ÿ‰ TCP(Transmission Control Protocol) ์ŠคํŠธ๋ฆฌ๋ฐ ํ†ต์‹ ์„ ๊ตฌํ˜„ํ•˜๋‹ค ๋ณด๋ฉด, ๋™์ž‘ ํ›„ ์ˆ˜ ๋ถ„์—์„œ ์ˆ˜ ์‹œ๊ฐ„ ๋‚ด์— ํ†ต์‹ ์ด ๊ฐ‘์ž๊ธฐ ์ค‘๋‹จ๋˜๋Š” ์น˜๋ช…์ ์ธ ๋ฌธ์ œ์— ์ง๋ฉดํ•˜๊ณค ํ•ฉ๋‹ˆ๋‹ค. ๋””๋ฒ„๊ฑฐ๋ฅผ ์—ฐ๊ฒฐํ•ด ๋ณด๋ฉด ๋Œ€๋ถ€๋ถ„ LwIP์˜ ๋ฉ”๋ชจ๋ฆฌ ๋ฒ„ํผ์ธ pbuf ํ• ๋‹น ์‹คํŒจ(ERR_MEM) ๋˜๋Š” DMA ๋””์Šคํฌ๋ฆฝํ„ฐ ๊ต์ฐฉ ์ƒํƒœ(Deadlock)์— ๋น ์ ธ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ณธ ํฌ์ŠคํŒ…์—์„œ๋Š” STM32H7 MCU ํ™˜๊ฒฝ์—์„œ ๋Œ€์šฉ๋Ÿ‰ TCP ๋ฐ์ดํ„ฐ ์†ก์ˆ˜์‹  ์ค‘ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” pbuf ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜(Memory Leak)์˜ ๊ทผ๋ณธ์ ์ธ ์›์ธ์„ ํ•˜๋“œ์›จ์–ด ๋ฐ ์†Œํ”„ํŠธ์›จ์–ด ์•„ํ‚คํ…์ฒ˜ ๊ด€์ ์—์„œ ํŒŒ์•…ํ•˜๊ณ , ์ด๋ฅผ ์™„์ „ํžˆ ํ•ด๊ฒฐํ•˜๋Š” Zero-Copy ๋น„๋™๊ธฐ ํŒŒ์ดํ”„๋ผ์ธ(Asynchronous Pipeline) ์ตœ์ ํ™” ๊ธฐ๋ฒ•์„ ์‹ค๋ฌด ์ฝ”๋“œ์™€ ํ•จ๊ป˜ ๊นŠ์ด ์žˆ๊ฒŒ ๋‹ค๋ฃน๋‹ˆ๋‹ค.



1. STM32H7 LwIP TCP ํ†ต์‹  ์ค‘๋‹จ ํ˜„์ƒ์˜ ๊ทผ๋ณธ ์›์ธ ๋ถ„์„

STM32H7์—์„œ LwIP๋ฅผ ํ†ตํ•ด ๋Œ€์šฉ๋Ÿ‰ TCP ํŠธ๋ž˜ํ”ฝ์„ ์ฒ˜๋ฆฌํ•  ๋•Œ ์‹œ์Šคํ…œ์ด ๋ฉˆ์ถ”๊ฑฐ๋‚˜ ํŒจํ‚ท ๋“œ๋ž(Packet Drop)์ด ๋„๋ฏธ๋…ธ ํ˜„์ƒ์ฒ˜๋Ÿผ ๋ฐœ์ƒํ•˜๋Š” ์›์ธ์€ ํฌ๊ฒŒ ์„ธ ๊ฐ€์ง€๋กœ ์š”์•ฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

1.1 D-Cache Coherency(๋ฐ์ดํ„ฐ ์บ์‹œ ์ผ๊ด€์„ฑ) ๋ถˆ์ผ์น˜

Cortex-M7 ์ฝ”์–ด๋Š” L1 ๋ฐ์ดํ„ฐ ์บ์‹œ(D-Cache)๋ฅผ ํฌํ•จํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋”๋„ท MAC์˜ DMA(Direct Memory Access) ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ SRAM์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ง์ ‘ ์ฝ๊ฑฐ๋‚˜ ์“ธ ๋•Œ, CPU์˜ D-Cache์— ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ์™€ ์‹ค์ œ SRAM ๋ฐ์ดํ„ฐ ๊ฐ„์˜ ์ผ๊ด€์„ฑ ๋ถˆ์ผ์น˜(Coherency Mismatch)๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

  • Rx(์ˆ˜์‹ ) ๋ฐฉํ–ฅ: DMA๊ฐ€ ์ด๋”๋„ท ํŒจํ‚ท์„ SRAM์— ์จ ๋„ฃ์—ˆ์ง€๋งŒ, CPU๋Š” D-Cache์˜ ์˜ค๋ž˜๋œ(Stale) ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด ํŒจํ‚ท ์†์ƒ ๋ฐœ์ƒ.
  • Tx(์†ก์‹ ) ๋ฐฉํ–ฅ: CPU๊ฐ€ ํŒจํ‚ท์„ ์ค€๋น„ํ•˜๊ณ  D-Cache์—๋งŒ ๋ฐ˜์˜๋œ ์ƒํƒœ์—์„œ, DMA๊ฐ€ SRAM์˜ ๋ณ€๊ฒฝ ์ „ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด ๋„คํŠธ์›Œํฌ๋กœ ์†ก์‹ .

์ด ๊ณผ์ •์—์„œ ํŒจํ‚ท์˜ ๊ธธ์ด(Length) ์ •๋ณด๋‚˜ ํฌ์ธํ„ฐ ๊ตฌ์กฐ์ฒด๊ฐ€ ์˜ค์—ผ๋˜๋ฉด LwIP๋Š” ํŒจํ‚ท ์ˆ˜์‹ ์„ ์™„๋ฃŒํ•˜์ง€ ๋ชปํ•˜๊ณ  ๋ฉ”๋ชจ๋ฆฌ ๋ฒ„ํผ์ธ pbuf๋ฅผ ์ฒด์ธ(Chain) ํ˜•ํƒœ๋กœ ๋ฌถ์–ด๋‘” ์ฑ„ ํ•ด์ œํ•˜์ง€ ๋ชปํ•˜๋Š” ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜(Memory Leak) ์ƒํƒœ์— ๋น ์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

1.2 pbuf ํ• ๋‹น ํ•ด์ œ ํƒ€์ด๋ฐ ๋ถˆ์ผ์น˜ ๋ฐ ๋””์Šคํฌ๋ฆฝํ„ฐ ๊ณ ๊ฐˆ

LwIP์—์„œ PBUF_POOL ํƒ€์ž… ๋ฒ„ํผ๋Š” ๋„คํŠธ์›Œํฌ ์ธํ„ฐํŽ˜์ด์Šค์˜ Rx DMA ๋””์Šคํฌ๋ฆฝํ„ฐ(Descriptor)์— ์‚ฌ์ „์— ํ• ๋‹น(Pre-allocation)๋ฉ๋‹ˆ๋‹ค. ์ˆ˜์‹  ์ธํ„ฐ๋ŸฝํŠธ(ISR)๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์ˆ˜์‹ ๋œ pbuf๋ฅผ ์ƒ์œ„ ๋ ˆ์ด์–ด(TCP/IP Stack)๋กœ ์ „๋‹ฌํ•˜๊ณ  ์ƒˆ๋กœ์šด pbuf๋ฅผ ํ• ๋‹นํ•˜์—ฌ ๋””์Šคํฌ๋ฆฝํ„ฐ์— ์ฑ„์›Œ๋„ฃ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ๋†’์€ ํŠธ๋ž˜ํ”ฝ ํ™˜๊ฒฝ์—์„œ ์ฒ˜๋ฆฌ ์†๋„๊ฐ€ ์ˆ˜์‹  ์†๋„๋ฅผ ๋”ฐ๋ผ๊ฐ€์ง€ ๋ชปํ•˜๋ฉด ์ˆœ๊ฐ„์ ์œผ๋กœ pbuf ํ’€(Pool)์ด ๊ณ ๊ฐˆ๋˜๋ฉฐ, ๋””์Šคํฌ๋ฆฝํ„ฐ์— NULL ํฌ์ธํ„ฐ๊ฐ€ ๋“ค์–ด๊ฐ€๋Š” Rx Descriptor Starvation ํ˜„์ƒ์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ํ•œ๋ฒˆ ๋””์Šคํฌ๋ฆฝํ„ฐ ๊ณ ๊ฐˆ ์ƒํƒœ์— ๋น ์ง€๋ฉด STM32 ์ด๋”๋„ท DMA๋Š” ์ˆ˜์‹  ์ •์ง€(Suspended) ์ƒํƒœ๊ฐ€ ๋˜๋ฉฐ, ์ด๋ฅผ ๋ณต๊ตฌํ•˜๋Š” ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๊ฐ€ ์—†์œผ๋ฉด ํ†ต์‹ ์ด ์˜๊ตฌ์ ์œผ๋กœ ๋‘์ ˆ๋ฉ๋‹ˆ๋‹ค.

1.3 MPU(Memory Protection Unit) ์„ค์ • ์˜ค๋ฅ˜

STM32H7์˜ ๋ง˜๋ชจ์Šค๊ธ‰ RAM ๊ตฌ์กฐ(AXI SRAM, SRAM1/2/3, D3 Domain SRAM4 ๋“ฑ)์—์„œ ์ด๋”๋„ท DMA ๋””์Šคํฌ๋ฆฝํ„ฐ์™€ LwIP pbuf๊ฐ€ ์œ„์น˜ํ•œ ๋ฉ”๋ชจ๋ฆฌ ์˜์—ญ์˜ MPU ์†์„ฑ์„ ์ •ํ™•ํžˆ ์ œ์–ดํ•˜์ง€ ์•Š์œผ๋ฉด Cache Invalidate/Clean ์กฐ์ž‘๋งŒ์œผ๋กœ ์™„๋ฒฝํ•œ ์ผ๊ด€์„ฑ์„ ๋ณด์žฅํ•˜๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค.

๊ตฌ๋ถ„ (Memory Type) ์ถ”์ฒœ SRAM ์˜์—ญ MPU ์†์„ฑ (Cache Policy) ์ฃผ์š” ์šฉ๋„
Ethernet Descriptors SRAM3 (0x30040000) Non-cacheable / Bufferable DMA Rx/Tx Ring Descriptor ํ…Œ์ด๋ธ”
LwIP pbuf Pool AXI SRAM / SRAM1,2 Write-Through ๋˜๋Š” Write-Back ์‹ค์ œ ํŒจํ‚ท ํŽ˜์ด๋กœ๋“œ(Payload) ๋ฒ„ํผ

2. ๋””๋ฒ„๊น… ๋ฐ MPU/Cache ๋ฌธ์ œ ํ•ด๊ฒฐ ๋ฐฉ์•ˆ

pbuf ๋ˆ„์ˆ˜๋ฅผ ์ฐจ๋‹จํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๊ฐ€์žฅ ๋จผ์ € ํ•˜๋“œ์›จ์–ด DMA ์˜์—ญ๊ณผ CPU ์บ์‹œ ๊ฐ„์˜ ์‹ ๋ขฐ์„ฑ ๋†’์€ ๋™๊ธฐํ™” ์„ค์ •์„ ์™„๋ฃŒํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

2.1 MPU ์˜ฌ๋ฐ”๋ฅธ ์˜์—ญ ๋ถ„๋ฆฌ ๋ฐ ๊ตฌ์„ฑ

์ด๋”๋„ท ๋””์Šคํฌ๋ฆฝํ„ฐ ์˜์—ญ(DMA Rx/Tx Descriptor)์€ ํ•˜๋“œ์›จ์–ด๊ฐ€ ๋นˆ๋ฒˆํ•˜๊ฒŒ ์ƒํƒœ ํ•„๋“œ(Status Field)๋ฅผ ๊ฐฑ์‹ ํ•˜๋ฏ€๋กœ ๋ฐ˜๋“œ์‹œ ์บ์‹œ๊ฐ€ ์ ์šฉ๋˜์ง€ ์•Š๋Š” Non-cacheable ์˜์—ญ์œผ๋กœ ์ง€์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

/* MPU Configuration for STM32H7 Ethernet */
void MPU_Config_Ethernet(void)
{
    MPU_Region_InitTypeDef MPU_InitStruct = {0};

    HAL_MPU_Disable();

    /* 1. Descriptors: SRAM3 (Non-cacheable) */
    MPU_InitStruct.Enable = MPU_REGION_ENABLE;
    MPU_InitStruct.Number = MPU_REGION_NUMBER0;
    MPU_InitStruct.BaseAddress = 0x30040000;
    MPU_InitStruct.Size = MPU_REGION_SIZE_256B; // ๋””์Šคํฌ๋ฆฝํ„ฐ ํฌ๊ธฐ์— ๋งž๊ฒŒ ์„ค์ •
    MPU_InitStruct.SubRegionDisable = 0x0;
    MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
    MPU_InitStruct.IsShareable = MPU_SHAREABLE;
    MPU_InitStruct.IsCacheable = MPU_NOT_CACHEABLE;
    MPU_InitStruct.IsBufferable = MPU_NOT_BUFFERABLE;
    HAL_MPU_ConfigRegion(&MPU_InitStruct);

    /* 2. LwIP Buffers: AXI SRAM (Write-Through) */
    MPU_InitStruct.Enable = MPU_REGION_ENABLE;
    MPU_InitStruct.Number = MPU_REGION_NUMBER1;
    MPU_InitStruct.BaseAddress = 0x24000000; // AXI SRAM
    MPU_InitStruct.Size = MPU_REGION_SIZE_64KB;
    MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
    MPU_InitStruct.IsShareable = MPU_NOT_SHAREABLE;
    MPU_InitStruct.IsCacheable = MPU_CACHEABLE;
    MPU_InitStruct.IsBufferable = MPU_NOT_BUFFERABLE; // Write-Through
    HAL_MPU_ConfigRegion(&MPU_InitStruct);

    HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}

2.2 D-Cache Maintenance(์บ์‹œ ์œ ์ง€๋ณด์ˆ˜)์˜ ์ •ํ™•ํ•œ ํ˜ธ์ถœ ์œ„์น˜

LwIP pbuf ๋ฉ”๋ชจ๋ฆฌ ๊ณต๊ฐ„์ด ์บ์‹œ ๊ฐ€๋Šฅ(Cacheable) ์˜์—ญ์— ์œ„์น˜ํ•œ ๊ฒฝ์šฐ, DMA ๋™์ž‘ ์ „ํ›„๋กœ ์บ์‹œ๋ฅผ ์กฐ์ž‘ํ•ด ์ฃผ์–ด์•ผ ๋ฐ์ดํ„ฐ ์˜ค์—ผ๊ณผ ์ด์— ๋”ฐ๋ฅธ pbuf ์ฐธ์กฐ ์นด์šดํŠธ(Ref count) ๊ผฌ์ž„์„ ๋ง‰์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • Rx ํŒจํ‚ท ์ˆ˜์‹  ์‹œ: DMA ์ˆ˜์‹  ์™„๋ฃŒ ํ›„ CPU๊ฐ€ pbuf ์ฝ๊ธฐ ์ „์— SCB_InvalidateDCache_by_Addr() ํ˜ธ์ถœ.
  • Tx ํŒจํ‚ท ์†ก์‹  ์‹œ: CPU๊ฐ€ pbuf ์“ฐ๊ธฐ ์™„๋ฃŒ ํ›„ DMA ์†ก์‹  ์‹œ์ž‘ ์ „์— SCB_CleanDCache_by_Addr() ํ˜ธ์ถœ.


3. Zero-Copy ๋น„๋™๊ธฐ ํŒŒ์ดํ”„๋ผ์ธ ์ตœ์ ํ™” ๊ธฐ๋ฒ•

์ผ๋ฐ˜์ ์ธ LwIP ํฌํŒ… ๊ตฌ์กฐ์—์„œ๋Š” DMA ์ˆ˜์‹  ๋ฒ„ํผ์—์„œ pbuf๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณต์‚ฌ(memcpy)ํ•˜๊ณ , ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”๋“œ๋กœ ์ „๋‹ฌํ•  ๋•Œ ๋˜ ๋‹ค์‹œ ๋ณต์‚ฌ๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ๋Œ€์šฉ๋Ÿ‰(์ˆ˜์‹ญ Mbps ์ด์ƒ) TCP ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆฌ๋ฐ ์‹œ ๋ณต์‚ฌ ์—ฐ์‚ฐ์— ์˜ํ•œ CPU ๋ถ€ํ•˜์™€ ํŒจํ‚ท ์ฒ˜๋ฆฌ ์ง€์—ฐ์ด pbuf ๊ณ ๊ฐˆ์„ ์œ ๋ฐœํ•˜๋Š” ํ•ต์‹ฌ ์š”์†Œ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

์ด๋ฅผ ๊ทน๋ณตํ•˜๊ธฐ ์œ„ํ•ด ๋ฉ”๋ชจ๋ฆฌ ๋ณต์‚ฌ๋ฅผ ์›์ฒœ์ ์œผ๋กœ ๋ฐฐ์ œํ•˜๋Š” Zero-Copy ๋น„๋™๊ธฐ ํŒŒ์ดํ”„๋ผ์ธ Architecture๋ฅผ ๊ตฌ์ถ•ํ•ฉ๋‹ˆ๋‹ค.

3.1 Custom pbuf(pbuf_custom) ๊ธฐ๋ฐ˜์˜ Zero-Copy Rx ๊ตฌํ˜„

LwIP์—์„œ ์ œ๊ณตํ•˜๋Š” PBUF_REF ๋˜๋Š” PBUF_POOL ๋Œ€์‹  pbuf_custom ๊ตฌ์กฐ์ฒด๋ฅผ ํ™œ์šฉํ•˜๋ฉด, DMA ๋””์Šคํฌ๋ฆฝํ„ฐ๊ฐ€ ๊ฐ€๋ฆฌํ‚ค๋Š” ์‹ค์ œ ๋ฌผ๋ฆฌ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๋ณต์‚ฌ ์—†์ด ์ƒ์œ„ ๋ ˆ์ด์–ด๋กœ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ˆ˜์‹ ์ด ๋๋‚œ ๋ฒ„ํผ๋Š” ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ์‹๋ณ„ํ•˜์—ฌ DMA ๋””์Šคํฌ๋ฆฝํ„ฐ์— ๋‹ค์‹œ ์žฌํ• ๋‹น๋ฉ๋‹ˆ๋‹ค.

/* Custom pbuf Structure for Zero-Copy */
typedef struct {
    struct pbuf_custom p;
    void *dma_buffer_address;
} my_custom_pbuf_t;

/* Custom Free Callback Function */
void custom_pbuf_free_cb(struct pbuf *p)
{
    my_custom_pbuf_t *my_p = (my_custom_pbuf_t *)p;
    
    /* 1. D-Cache Invalidate for recycled memory */
    SCB_InvalidateDCache_by_Addr((uint32_t *)my_p->dma_buffer_address, p->tot_len);

    /* 2. Rx DMA Descriptor์— ๋ฉ”๋ชจ๋ฆฌ ํฌ์ธํ„ฐ ์žฌ์—ฐ๊ฒฐ */
    Reassign_Rx_Descriptor(my_p->dma_buffer_address);

    /* 3. Custom Pool ๊ฐ์ฒด ๋ฐ˜ํ™˜ */
    Free_Custom_Pbuf_Pool(my_p);
}

3.2 ๋น„๋™๊ธฐ TCP Tx ํŒŒ์ดํ”„๋ผ์ธ ๊ตฌ์ถ• (Zero-Copy Send)

TCP ์†ก์‹  ์‹œ tcp_write() ํ•จ์ˆ˜์— TCP_WRITE_FLAG_COPY ๋Œ€์‹  0 (Non-copy flag)์„ ์ „๋‹ฌํ•˜์—ฌ LwIP ๋‚ด๋ถ€์˜ ์†ก์‹  ๋ฉ”๋ชจ๋ฆฌ ๋ณต์‚ฌ๋ฅผ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค. ์ด๋•Œ ์ „์†ก ์™„๋ฃŒ ์ด๋ฒคํŠธ์ธ tcp_sent() ์ฝœ๋ฐฑ์ด ํ˜ธ์ถœ๋  ๋•Œ๊นŒ์ง€ ์ „๋‹ฌํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฒ„ํผ์˜ ๋‚ด์šฉ์ด ๋ณ€ํ˜•๋˜๊ฑฐ๋‚˜ ํ•ด์ œ(Free)๋˜๋ฉด ์•ˆ ๋ฉ๋‹ˆ๋‹ค.

/* Asynchronous Zero-Copy TCP Send Implementation */
err_t send_large_data_zero_copy(struct tcp_pcb *tpcb, uint8_t *payload_ptr, uint16_t len)
{
    err_t err;

    /* 1. Cache Clean: SRAM์˜ ์ตœ์‹  ๋ฐ์ดํ„ฐ๋ฅผ ๋ฌผ๋ฆฌ RAM์œผ๋กœ ๋™๊ธฐํ™” */
    SCB_CleanDCache_by_Addr((uint32_t *)payload_ptr, len);

    /* 2. Zero-Copy Enqueue (TCP_WRITE_FLAG_COPY๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ) */
    err = tcp_write(tpcb, payload_ptr, len, 0);
    
    if (err == ERR_OK) {
        /* 3. ์ฆ‰์‹œ ์ „์†ก ์‹œ๋„ */
        tcp_output(tpcb);
    } else if (err == ERR_MEM) {
        /* LwIP Send Queue๊ฐ€ ๊ฐ€๋“ ์ฐฌ ๊ฒฝ์šฐ ๋น„๋™๊ธฐ ๋ง ๋ฒ„ํผ์—์„œ ๋Œ€๊ธฐ */
        Enqueue_Pending_Tx_Buffer(payload_ptr, len);
    }

    return err;
}

/* TCP Tx Sent Callback */
err_t tcp_sent_callback(void *arg, struct tcp_pcb *tpcb, u16_t len)
{
    /* ์ „์†ก์ด ์™„๋ฃŒ๋œ ๋ฒ„ํผ ํ•ด์ œ ๋ฐ ๋น„๋™๊ธฐ ํŒŒ์ดํ”„๋ผ์ธ ๋‹ค์Œ ํ”„๋ ˆ์ž„ ์ „์†ก ํŠธ๋ฆฌ๊ฐ€๋ง */
    Release_Sent_Buffer_Memory(len);
    Process_Next_Async_Tx_Queue(tpcb);

    return ERR_OK;
}

3.3 LwIP ํ•ต์‹ฌ ์˜ต์…˜(lwipopts.h) ํŠœ๋‹ ํŒŒ๋ผ๋ฏธํ„ฐ

STM32H7 ํ™˜๊ฒฝ์—์„œ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜์™€ ๋ณ‘๋ชฉ ํ˜„์ƒ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด lwipopts.h ํ—ค๋” ํŒŒ์ผ์—์„œ ์•„๋ž˜ ์„ค์ •๊ฐ’์„ ์กฐ์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • MEM_ALIGNMENT = 32: Cortex-M7 D-Cache Line Size(32 Bytes)์™€์˜ ์ •๋ ฌ์„ ์œ„ํ•ด ํ•„์ˆ˜ ์„ค์ •.
  • MEMP_NUM_PBUF = 32: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ ๊นŠ์ด์— ๋งž์ถ”์–ด ์ถฉ๋ถ„ํ•œ ์ˆ˜๋Ÿ‰ ํ™•๋ณด.
  • PBUF_POOL_SIZE = 64: Rx DMA Ring ํฌ๊ธฐ(๋ณดํ†ต 16~32๊ฐœ)์˜ 2๋ฐฐ ์ด์ƒ์œผ๋กœ ์„ค์ •ํ•˜์—ฌ ํŠธ๋ž˜ํ”ฝ ํญ์ฃผ ์‹œ ๋ฐ๋“œ๋ฝ ๋ฐฉ์ง€.
  • TCP_WND = (8 * TCP_MSS): STM32H7์˜ ๋Œ€์šฉ๋Ÿ‰ ์ฒ˜๋ฆฌ ๋Šฅ๋ ฅ์— ๋ถ€ํ•ฉํ•˜๋„๋ก ์ˆ˜์‹  ์œˆ๋„์šฐ ํฌ๊ธฐ ํ™•์žฅ.
  • LWIP_NETIF_TX_SINGLE_PBUF = 1: Tx ์‹œ pbuf ์ฒด์ด๋‹ ๋‹จํŽธํ™”๋ฅผ ๋ฐฉ์ง€ํ•˜๊ณ  ์—ฐ์†๋œ ๋‹จ์ผ ๋ฉ”๋ชจ๋ฆฌ ๋ธ”๋ก ์ฒ˜๋ฆฌ ๊ฐ•์ œ.


4. ์‹ค๋ฌด ๋””๋ฒ„๊น… ๊ฒ€์ฆ ๋ฐ ์ตœ์ ํ™” ์„ฑ๊ณผ ๋ถ„์„

์œ„ ์ตœ์ ํ™” ๊ธฐ๋ฒ•์„ ์ ์šฉํ•˜๊ธฐ ์ „๊ณผ ํ›„์˜ ์‹œ์Šคํ…œ ์„ฑ๋Šฅ์„ 100Mbps Full-Duplex ์ด๋”๋„ท ํ™˜๊ฒฝ์—์„œ ์žฅ์‹œ๊ฐ„ ๋Œ€์šฉ๋Ÿ‰ TCP ๋ฐ์ดํ„ฐ ์†ก์ˆ˜์‹  ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•˜์—ฌ ์ธก์ •ํ•œ ๋น„๊ต ๋ฐ์ดํ„ฐ์ž…๋‹ˆ๋‹ค.

ํ‰๊ฐ€ ์ง€ํ‘œ (Metrics) ๊ธฐ์กด (Standard LwIP Copy) ์ตœ์ ํ™” ํ›„ (Zero-Copy Async)
TCP Throughput ~35 Mbps (๋ถˆ์•ˆ์ •) 94.5 Mbps (๋ผ์ธ ์Šคํ”ผ๋“œ ๋„๋‹ฌ)
CPU ์ ์œ ์œจ (400MHz ๊ธฐ์ค€) 68% ~ 82% (Memcpy ๋ถ€ํ•˜) 12% ~ 18% (ํš๊ธฐ์  ๊ฐ์†Œ)
pbuf ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๋ฐœ์ƒ ์—ฌ๋ถ€ ์ˆ˜ ์‹œ๊ฐ„ ๋‚ด ERR_MEM ๋ฐœ์ƒ ํ›„ ๋‹ค์šด 72์‹œ๊ฐ„ ์ด์ƒ ์—ฐ์† ๋™์ž‘ ์‹œ ๋ˆ„์ˆ˜ 0
์ง€ํ„ฐ(Jitter) / ์ง€์—ฐ์‹œ๊ฐ„ ๋†’์Œ (๊ฐ€๋น„์ง€ ๋ฒ„ํผ ๋ˆ„์ ) ์ดˆ์ €์ง€์—ฐ (Constant Latency)

์‹ค๋ฌด ์ ์šฉ ์‹œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ์—ฃ์ง€ ์ผ€์ด์Šค(Edge Cases)

  1. Link Down / Link Up ์žฌ์„ค์ •: ๋žœ์„  ํƒˆ์ฐฉ ์‹œ ์ด๋”๋„ท DMA ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ๋ฆฌ์…‹๋ฉ๋‹ˆ๋‹ค. ์ด๋•Œ ๊ธฐ์กด์— DMA ๋””์Šคํฌ๋ฆฝํ„ฐ๊ฐ€ ์ ์œ ํ•˜๊ณ  ์žˆ๋˜ ๋ชจ๋“  pbuf ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ pbuf_free() ํ•ด์ฃผ๋Š” Cleanup ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ด์•ผ ๋ฉ”๋ชจ๋ฆฌ ์œ ์‹ค์„ ์˜ˆ๋ฐฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  2. TCP Zero-Window ์ƒํƒœ ํ•ธ๋“ค๋ง: ์ˆ˜์‹  ์ธก ์œˆ๋„์šฐ ํฌ๊ธฐ๊ฐ€ 0์ด ๋  ๊ฒฝ์šฐ tcp_write()๊ฐ€ ์ฐจ๋‹จ๋ฉ๋‹ˆ๋‹ค. ํƒ€์ž„์•„์›ƒ ํƒ€์ด๋จธ๋ฅผ ๋‘๊ณ  ์ผ์ • ์‹œ๊ฐ„ ์ด์ƒ ๋น„๋™๊ธฐ ํ์—์„œ ๋Œ€๊ธฐํ•  ๊ฒฝ์šฐ ํ๋ฅผ ๋น„์šฐ๊ณ  ์†Œ์ผ“ ์—ฐ๊ฒฐ์„ ์•ˆ์ „ํ•˜๊ฒŒ ๋Š๋„๋ก ๋ฆฌ์…‹ ๋กœ์ง์„ ๊ตฌ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

5. ๊ฒฐ๋ก  ๋ฐ ์š”์•ฝ

STM32H7 MCU์—์„œ LwIP ๊ธฐ๋ฐ˜ ๋Œ€์šฉ๋Ÿ‰ TCP ํ†ต์‹  ์‹œ ๋ฐœ์ƒํ•˜๋Š” ์‹œ์Šคํ…œ ๋‹ค์šด ํ˜„์ƒ์€ ์ฝ”๋“œ ์ž์ฒด์˜ ๋‹จ์ˆœ ์˜ค๋ฅ˜๋ผ๊ธฐ๋ณด๋‹ค๋Š” Cortex-M7์˜ D-Cache ๊ตฌ์กฐ, MPU ์„ธํŒ…, Ethernet DMA ๋””์Šคํฌ๋ฆฝํ„ฐ ๋ฉ”์ปค๋‹ˆ์ฆ˜, LwIP pbuf ์ƒ๋ช…์ฃผ๊ธฐ(Lifecycle) ๊ฐ„์˜ ์ข…ํ•ฉ์ ์ธ ์ธํ„ฐ๋ž™์…˜ ๋ฏธ์ˆ™์—์„œ ๋น„๋กฏ๋˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Šต๋‹ˆ๋‹ค.

๋ณธ ๊ธฐ์‚ฌ์—์„œ ๋‹ค๋ฃฌ ํ•ด๋ฒ•์„ ๋ฐ”ํƒ•์œผ๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์•„ํ‚คํ…์ฒ˜๋ฅผ ์žฌ๊ตฌ์„ฑํ•˜๋ฉด ์™„๋ฒฝํ•˜๊ณ  ์•ˆ์ •์ ์ธ ๊ณ ์† ์ด๋”๋„ท ํ†ต์‹ ์„ ๋‹ฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์ด๋”๋„ท ๋””์Šคํฌ๋ฆฝํ„ฐ ์˜์—ญ์„ Non-cacheable MPU Region์œผ๋กœ ๋ถ„๋ฆฌ ์„ค์ •.
  • pbuf payload ์กฐ์ž‘ ์‹œ Cache Invalidate / Clean API ์ •ํ™•ํ•œ ์œ„์น˜์— ์ ์šฉ.
  • pbuf_custom ๋ฐ ๋น„๋™๊ธฐ ์ฝœ๋ฐฑ์„ ๋„์ž…ํ•˜์—ฌ CPU ๋ณต์‚ฌ ์—ฐ์‚ฐ์„ ์†Œ๊ฑฐํ•˜๋Š” Zero-Copy ํŒŒ์ดํ”„๋ผ์ธ ๊ตฌํ˜„.
  • ๋„คํŠธ์›Œํฌ ์žฌ์—ฐ๊ฒฐ ๋ฐ Exception ์ƒํ™ฉ ๋ฐœ์ƒ ์‹œ Rx/Tx ๋””์Šคํฌ๋ฆฝํ„ฐ ๋ง ๋ฒ„ํผ ์™„์ „ ์ดˆ๊ธฐํ™” ๋ฐ pbuf ๊ฐ•์ œ ํ•ด์ œ ์ˆ˜์ˆœ ๋งˆ๋ จ.

์ดˆ๊ณ ์† ๋ฐ ๋†’์€ ์‹ ๋ขฐ์„ฑ์„ ์š”๊ตฌํ•˜๋Š” ์ž„๋ฒ ๋””๋“œ ๋„คํŠธ์›Œํฌ ์‹œ์Šคํ…œ์„ ์„ค๊ณ„ ์ค‘์ด๋ผ๋ฉด ์œ„ ์ตœ์ ํ™” ํฌ์ธํŠธ๋ฅผ ๋‹จ๊ณ„๋ณ„๋กœ ๊ฒ€์ฆํ•ด ๋ณด์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.

๋ฐ˜์‘ํ˜•