|
Target
Board |
STM32F765(W5500)
|
|
Host
|
Raspberry Pi CM5
|
|
목표
|
W5500 TCP/IP 이더넷 통신 속도 개선
|
1. 배경
: Raspberry Pi CM5 ↔ STM32F765(W5500) TCP/IP 이더넷 통신 구성
CM5는 기본 이더넷 포트가 1개(eth0) 있는데 외부 통신용으로 할당하여, STM32F765(W5500)과 통신을 위해 USB to Ethernet(AX88772C, eth1)을 추가로 구성했다.
2. 문제점
: CM5 eth1 ↔ STM32F765(W5500) 경로에서만 통신이 느려짐
CM5 eth1 ↔ STM32F765(W5500) 통신할 때 파일 전송이 느리고, 중간중간 뚝뚝 멈추는 현상이 발생. 반면 CM5 eth0(네이티브)로는 문제가 없었다.
3. 원인 규명
- 현상1 : 케이블/W5500 자체 문제 아님. 유실(loss)은 항상 0, TCP가 복구.
- eth1(USB)은 재전송 272건·~1.0Mbps, eth0(네이티브)는 재전송 0건·~1.7Mbps로 확연히 차이.
- 이 시점 가설: USB 배칭(batching)으로 패킷이 버스트로 나가서 W5500 8KB 버퍼를 순간적으로 초과 → 재전송.
- 현상 2 : 체감상 느려 보였지만, 실제로는 멈춤이 반복된 것
- 로그를 보니 실제로는 200ms 이상 뚝 끊기는 간헐적 정지였다(정상 구간은 1~11ms, 그 중간값은 전혀 없었음). 이 멈춤 길이는 CM5의 재전송 대기시간(RTO)과 정확히 일치 — 즉 "멈춤 = 재전송 대기".
- 원인은 eth1(USB 랜칩 AX88772C)이 USB 특성상 데이터를 뭉쳐서 내보내며 패킷 순서가 실제로 뒤바뀌는 것(reord_seen 6~7회 로그 확인). 보통 TCP는 SACK으로 "몇 번만 빠졌다"고 알아채 재정렬하지만, W5500엔 SACK이 없어(데이터시트 확인) 순서만 바뀐 것도 유실로 간주해 뒤를 통째로 재전송한다.
- 게다가 수신 버퍼가 2KB(조각 2개분)라 중복 ACK이 3번 쌓이지 못해 "빠른 재전송(~0.1ms)" 대신 "느린 재전송(RTO 200ms)"으로 빠진다.
- eth0는 SoC 전용 하드웨어가 DMA로 처리해 순서 뒤바뀜 자체가 없어 문제가 안 드러났을 뿐, 버퍼는 eth0도 똑같이 2KB였다(같은 약점이 있지만 회선이 깨끗해 발현 안 됨).
4. 조치 (STM32F765 펌웨어)
① SPI 클럭 상향 (1.7MHz → 27MHz)
: SPI_BAUDRATEPRESCALER_64 → SPI_BAUDRATEPRESCALER_4 (64분주→4분주, 16배).
static void MX_SPI1_Init(void)
{
/* USER CODE BEGIN SPI1_Init 0 */
/* USER CODE END SPI1_Init 0 */
/* USER CODE BEGIN SPI1_Init 1 */
/* USER CODE END SPI1_Init 1 */
/* SPI1 parameter configuration*/
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH;
hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 7;
hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
if (HAL_SPI_Init(&hspi1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN SPI1_Init 2 */
/* USER CODE END SPI1_Init 2 */
}
- 동작 원리
: W5500 RX 버퍼의 데이터를 꺼내오는 유일한 통로가 SPI. SPI가 느리면 읽기 자체가 오래 걸려 다음 폴링까지 지연이 길어짐.
- 효과 범위
: 읽는 시간만 줄임 ; 2KB 한 번 읽기가 계산상 9.7ms→0.6ms, 로그 실측도 11ms→1~2ms로 일치. 재전송 대기(멈춤)에는 영향 없음.
② 수신 버퍼 2KB → 8KB
#ifndef W5500_MAX_BUF_K
#define W5500_MAX_BUF_K 8 // default 2k, MAX 8,
#endif
- W5500은 총 16KB RX 버퍼를 8개 소켓에 나눠 씀. 기존엔 8소켓×2KB 균등분배({2,2,...,2}). 파일소켓 하나에 8KB를 몰아주려면 나머지 소켓을 줄여야 함.
- 핵심 메커니즘
: TCP는 "빠른 재전송"(중복ACK 3회→즉시재전송, ~0.1ms)과 '느린 재전송'(RTO 타임아웃, ~200ms) 두 경로가 있다. 버퍼 2KB(조각 2개분)면 뒤에 최대 1개만 쌓여 중복ACK 1번뿐 → 3번을 못 채워 200ms RTO를 그대로 기다림. 버퍼 8KB(조각 8개분)로 늘리면 뒤에 최대 7개가 쌓여 중복ACK 3회 조건을 즉시 만족 → 0.1ms 만에 회복.
- 정리
: eth1의 패킷 순서 뒤바뀜 자체는 못 없애지만, W5500에 없는 SACK 기능을 '버퍼 확대로 빠른 재전송 유도' 방식으로 대체 보완한 것.
중요: ①만 단독 적용하면 오히려 느려짐(1.649초→2.207초). ①은 "읽는 속도"를, ②는 "오류 시 멈춤 회복"을 고치는 것이라 서로 다른 문제이기 때문에 ①+②를 함께 적용해야 효과가 남.
5. 결과
|
SPI속도, 버퍼
|
총 소요시간
|
재전송 비율
|
|
개선 전 (1.7MHz, 2KB)
|
1.649초
|
0~8.5%
|
|
①만 (27MHz, 2KB)
|
2.207초
|
6.1~15.2% (오히려 악화)
|
|
①+② (27MHz, 8KB)
|
0.175초
|
18~39% (더 늘었지만 총시간은 최소)
|
- 개선 전 대비 약 9.4배 빨라졌다. 체크섬(c=cc)도 3개 파일 모두 일치해 데이터가 깨지지 않았음을 확인했다. 다시 말해 이번 조치는 "오류(순서 뒤바뀜)를 없앤 것"이 아니라, 오류가 나도 빠르게 회복하도록 만든 것이다. W5500에는 없는 SACK 기능을, 버퍼를 키워서 대신 채워준 셈이다.
- 이번 개선(SPI 27MHz + 버퍼 8KB)으로 멈춤 문제는 해결했고 실측 처리량도 최대 약 6.9Mbps까지 나왔다. 다만 W5500은 RX/TX 각 16KB뿐인 소형 버퍼로 동작하는 임베디드용 이더넷 칩이라, 이 정도가 실질적인 상한에 가깝다. 소용량 데이터를 주고받는 임베디드 통신에는 충분하지만, 대용량 파일 전송이나 고속 스트리밍용으로는 여전히 부족한 대역폭이다. 더 큰 대역폭이 필요하면 SPI 클럭을 더 올리는 정도로는 한계가 있고, 아키텍처 자체(예: 네이티브 이더넷 MAC 탑재 MCU, 또는 별도 고속 인터페이스)를 바꿔야 한다.
'ST > MCU' 카테고리의 다른 글
| 리눅스 기반 임베디드 플랫폼 선택을 위한 SoC/MPU 비교표 (0) | 2025.05.14 |
|---|---|
| ST Stellar(스텔라) 32-bit automotive MCU (1) | 2025.04.24 |
| MCU(4) - NXP (0) | 2025.03.17 |
| MCU(3) - Infineon (0) | 2025.03.17 |
| MCU(2) - TI (0) | 2025.03.17 |



































































