EMAC/MDIO 모듈 아키텍처 및 인터럽트 시스템의 심층 분석

1. EMAC/MDIO 모듈 구조 및 핵심 메커니즘

임베디드 네트워크 통신 시스템에서 EMAC(이더넷 매체 접근 제어)와 MDIO(관리 데이터 입력/출력) 모듈은 물리 계층과 데이터 링크 계층을 연결하는 핵심 구성 요소입니다. 이 구조는 하드웨어 가속 기반으로 네트워크 패킷 처리 효율성을 극대화하며, 다음 세 가지 주요 메커니즘을 통해 설계되었습니다:

첫째, 우선순위 기반 DMA 전송 방식입니다. EMAC 내부에는 수신 및 송신용 독립적인 DMA 엔진이 있으며, 각 엔진은 8개 채널을 지원합니다. MSTPRI 레지스터를 통해 전송 노드 우선순위를 설정할 수 있으며, 기본값은 100b(중간 우선순위)로 설정됩니다. 실무 경험에서 다수의 DMA 마스터 장치가 존재할 경우, 적절한 우선순위 조정은 네트워크 지연을 크게 줄일 수 있음을 확인했습니다. 예를 들어 영상 전송 시스템에서는 EMAC 우선순위를 010b로 설정하여 비디오 스트림이 다른 외부 장치의 DMA 작업에 영향을 받지 않도록 보장했습니다.

둘째, 이중 버퍼 기반 메모리 관리 전략입니다. EMAC 컨트롤 모듈은 8KB 용량의 전용 RAM을 갖추고 있으며, 이는 전달 디스크립터 접근 지연을 최소화하는 데 기여합니다. DM6467 프로세서 실험 결과에 따르면, 디스크립터 읽기 지연 시간은 일반 메모리 접근 대비 약 3분의 1 수준입니다. 또한 EMAC 내부는 64바이트 단위의 Cell 유닛을 사용해 데이터 전송을 수행하며, 1Gbps 모드에서 각 Cell의 전송 시간은 0.512μs입니다. 이에 따라 메모리 컨트롤러는 다음과 같은 요구 사항을 충족시켜야 합니다:

  • 단기 평균 64바이트 요청 서비스 시간 ≤ 5.12μs (100Mbps 모드)
  • 단일 지연 이벤트 시간 ≤ (5.12×TXCELLTHRESH)μs

셋째, 모듈화된 전력 관리 설계입니다. EMAC, MDIO 및 EMAC 컨트롤 모듈은 별도로 저전력 모드에 진입할 수 있으며, PSC(Power and Sleep Controller) 레지스터를 통해 제어합니다. 이 설계는 자동차 시스템에서 특히 유용하며, 네트워크 휴식 상태 감지 시 MDIO 모듈만을 종료함으로써 전력 소비를 절감할 수 있습니다. 반면 EMAC 커널은 계속해서 리스닝 상태를 유지합니다.

주의사항: EMAC 및 EMAC 컨트롤 모듈은 동기화된 리셋이 필요합니다. 올바른 리셋 순서는 먼저 EMAC의 SOFTRESET 비트를 설정하고, 리셋 효과가 나타난 후에 EMAC 컨트롤 모듈의 CMSOFTRESET 비트를 설정해야 합니다. 순서 오류는 DMA 채널 데드락을 유발할 수 있습니다.

2. 인터럽트 시스템 심층 분석

2.1 인터럽트 유형 및 트리거 메커니즘

EMAC/MDIO 인터럽트 시스템은 4단계 구조로 설계되어 28개 인터럽트 소스를 4개 CPU 인터럽트 라인으로 집약합니다. 이 구조는 유연성과 시스템 자원 낭비를 동시에 방지합니다.

수신 채널 인터럽트는 네트워크 데이터 처리의 핵심 경로로, 두 가지 트리거 모드가 있습니다:

  • 閾値 인터럽트(RXTHRESHOLDPEND) : RXnFREEBUFFER ≤ RXnFLOWTHRESH 시 트리거되어 버퍼 부족을 경고합니다. 영상 모니터링 시스템 개발 시 버퍼 양의 1/3을 임계치로 설정하여 드라이버가 충분한 시간을 갖고 버퍼를 보충할 수 있도록 했습니다.
  • 완료 인터럽트(RXPEND) : 하드웨어가 프레임 수신 완료 후, 마지막 디스크립터 주소를 RXnCP 레지스터에 기록하여 트리거합니다. 드라이버는 소프트웨어 처리 포인터와 하드웨어 포인터 비교를 통해 새 데이터 존재 여부를 판단해야 합니다.

송신 인터럽트(TXPEND) 처리는 더 복잡합니다. 일반적으로 개발자는 각 데이터 패킷 송신 후 인터럽트를 처리하는 실수를 자주 하는데, 고밀도 트래픽 시 CPU 부하가 과도해질 수 있습니다. 보다 효과적인 방법은 다음과 같습니다:

// 최적화된 송신 인터럽트 처리 예제
void TxISR()
{
    while(read_reg(TXnCP) != last_processed_desc) {
        process_packet(last_processed_desc);
        last_processed_desc = get_next_desc(last_processed_desc);
    }
    write_reg(TXnCP, last_processed_desc); // 일괄적으로 모든 처리 완료 패킷 확인
}

2.2 오류 처리 및 복구 메커니즘

HOSTPEND 인터럽트는 시스템 안정성의 마지막 방어선으로, 다음과 같은 심각한 오류를 포착합니다:

  • 송신측: SOP 마킹 오류, 소유권 비트 미설정, 영 길이 버퍼 등
  • 수신측: 버퍼 소유권 문제 또는 NULL 포인터

이러한 오류는 대부분 드라이버의 메모리 관리 결함에서 발생합니다. Linux 드라이버 개발 중 우리는 특정 사례를 겪었습니다. 애플리케이션 측이 소켓을 갑작스럽게 닫을 경우, 드라이버가 DMA 버퍼를 즉시 회수하지 않으면 HOSTPEND가 발생합니다. 해결책은 종료 시점에서의 자원 회수 검사를 추가하는 것입니다:

void netdev_close()
{
    disable_irq();
    if (dma_busy) {
        reclaim_dma_buffers();
        udelay(100);
    }
    ...
}

통계 인터럽트(STATPEND) 처리에도 팁이 있습니다. 어떤 통계 레지스터 값이 0x80000000 이상일 때 트리거되며, 초기화 방법은 단순히 0으로 쓰는 것이 아니라 계수 값을 감소시켜야 합니다. 스위치 개발 시 이를 활용해 자동 트래픽 샘플링을 구현했습니다:

void stat_isr()
{
    for (int i=0; i<STAT_REG_NUM; i++) {
        if (stat_reg[i] & 0x80000000) {
            stat_reg[i] -= sampling_interval;
            update_traffic_graph(i);
        }
    }
}

3. 모듈 초기화 전체 과정

3.1 전원 및 클럭 구성

EMAC/MDIO 모듈은 케이스 상전압 후 기본적으로 비활성화 상태이며, PSC 모듈을 통해 활성화해야 합니다. 이 과정에서 주의해야 할 세 가지 사항은 다음과 같습니다:

  1. VDD3P3V_PWDN 레지스터는 PHY 인터페이스 공급을 위해 정확히 구성해야 합니다
  2. 레지스터 접근 전 모듈 클럭이 안정되어 있어야 합니다(최소 10ms 지연 권장)
  3. MDIO 클럭 분할 계산: MDC = PLL1/(6×(CLKDIV+1))

표준 클럭 구성 과정은 다음과 같습니다:

#define PLL1_RATE 594000000
#define MDC_RATE  1000000  // 목표 MDC 클럭 1MHz

void mdio_clock_init()
{
    uint32_t clkdiv = (PLL1_RATE/6/MDC_RATE) - 1;
    MDIO_REGS->CONTROL = (1 << 31) | (clkdiv << 0);
    
    // 일부 PHY는 전도 파ulses 필요
    if (phy_type == MARVELL_88E1111)
        MDIO_REGS->CONTROL |= (1 << 30);
}

3.2 EMAC 컨트롤 모듈 초기화

컨트롤 모듈 초기화는 전체 시스템의 기초로, 다음 순서를 엄격히 준수해야 합니다:

  1. 인터럽트 맵핑 구성 :
// EMAC 인터럽트를 ARM 인터럽트 컨트롤러에 맵핑
void map_interrupts()
{
    ARM_INTC->EINTMUX |= (EMAC_RX_INT << 16) | 
                         (EMAC_TX_INT << 8) |
                         (EMAC_MISC_INT << 0);
}
  1. 인터럽트 토크 설정 :
// 초당 인터럽트 횟수 제한 설정
void set_int_throttle()
{
    EmacControlRegs->CMRXINTMAX = 4000;  // 4000 RX 인터럽트/초
    EmacControlRegs->CMTXINTMAX = 2000;  // 2000 TX 인터럽트/초
    EmacControlRegs->CMINTCTRL = 0x30000; // 토크 활성화
}
  1. 디스크립터 메모리 초기화 :
// 8KB 메모리 영역을 송신/수신 디스크립터 루프로 분할
void init_desc_ram()
{
    uint32_t base = EMAC_CONTROL_RAM_BASE;
    for (int i=0; i<8; i++) {
        tx_ring[i].base = base + i*512;
        rx_ring[i].base = base + 4096 + i*512;
    }
}

3.3 MDIO 모듈 PHY 관리

MDIO 모듈의 PHY 자동 탐지는 특별한 주의가 필요합니다:

  • 32개 PHY 주소 탐지(약 50μs/레지스터)
  • PHY 링크 설정은 최대 3초 소요
  • 인터럽트 대신 루프백 방식 권장

안정적인 PHY 초기화 프로세스는 다음과 같이 구성해야 합니다:

void phy_init()
{
    // 1. PHY 기본 매개변수 설정
    mdio_write(PHY_ADDR, REG_CTRL, 
              (1<<12) | // 자동 협상
              (1<<9)  | // full duplex
              (1<<6));  // 100Mbps
    
    // 2. 링크 변화 인터럽트 설정
    mdio_write(PHY_ADDR, REG_INT_MASK, 
               LINK_UP_MASK | LINK_DOWN_MASK);
    
    // 3. MDIO 인터럽트 활성화
    MDIO_REGS->USERINTMASKSET = 0x1;
    MDIO_REGS->USERPHYSEL0 = PHY_ADDR | (1<<15);
}

4. 레지스터 레벨 프로그래밍 실전

4.1 핵심 레지스터 설명

SOFTRESET 레지스터 :

  • 비트0: 1을 쓰면 EMAC 소프트 리셋 트리거
  • 해당 비트가 자동으로 0이 되어야 리셋 완료
  • 리셋 중 DMA는 비활성 상태여야 함

CMSOFTRESET 레지스터 :

  • EMAC 컨트롤 모듈의 8KB RAM 리셋
  • SOFTRESET과 함께 사용 시 엄격한 시퀀스 요구

MACCONTROL 레지스터 설정 예시:

void emac_enable()
{
    EMAC_REGS->MACCONTROL = 
        (1<<5) | // GMII 모드
        (1<<3) | // full duplex
        (1<<2) | // CRC 검증
        (1<<1);  // 송신 활성화
    
    // PHY 준비 지연
    udelay(1000);
}

4.2 디스크립터 큐 관리

디스크립터 체인은 DMA 전송의 핵심으로, 일반적인 문제는 메모리 정렬과 캐시 일관성입니다:

struct descriptor {
    uint32_t next;     // 반드시 32바이트 정렬
    uint32_t buffer;   // 데이터 버퍼 주소
    uint16_t buflen;   // 실제 데이터 길이
    uint16_t flags;    // SOP/EOP 등 제어 비트
} __attribute__((aligned(32)));

// 송신 디스크립터 초기화
void init_tx_desc_ring()
{
    for (int i=0; i<TX_RING_SIZE-1; i++) {
        tx_ring[i].next = virt_to_phys(&tx_ring[i+1]);
        tx_ring[i].flags = OWNERSHIP_FLAG;
    }
    tx_ring[TX_RING_SIZE-1].next = virt_to_phys(&tx_ring[0]); // 루프 형성
}

주의사항: 모든 디스크립터가 EMAC에 제출되기 전에 캐시가 플러시되어야 합니다. ARM Cortex-A8 플랫폼에서는 다음과 같은 연산이 필요합니다:

void flush_desc(struct descriptor *desc)
{
    __asm__ __volatile__ (
        "MCR p15, 0, %0, c7, c10, 4" :: "r" (desc)
    );
}

5. 성능 최적화 및 디버깅 기술

5.1 인터럽트 토크 기술

고밀도 트래픽 시 인터럽트 폭풍이 일반적인 문제입니다. 이를 해결하기 위해 세 가지 기술을 조합했습니다:

  1. 인터럽트 병합 :
// EMAC 컨트롤 모듈에서 초당 최대 인터럽트 수 설정
EmacControlRegs->CMRXINTMAX = 1000; // 1000개 RX 인터럽트/ms
EmacControlRegs->CMTXINTMAX = 500;  // 500개 TX 인터럽트/ms
  1. NAPI 메커니즘 :
// 트래픽이 임계치를 넘을 때 펄로우 모드로 전환
if (packet_count > THRESHOLD) {
disable_irq();
while (poll_packets() > 0) {
process_packets();
}
enable_irq();
}
  1. 인터럽트 친화성 설정 :
// 특정 CPU 코어에 인터럽트 바인딩
void set_irq_affinity(int irq, int cpu)
{
cpu_set_t set;
CPU_ZERO(&set);
CPU_SET(cpu, &set);
sched_setaffinity(0, sizeof(set), &set);
}

5.2 메모리 접근 최적화

DMA 접근 패턴 분석을 통해 다음과 같은 최적화 원칙을 도출했습니다:

  1. 디스크립터 루프 크기 권장:
  • 100Mbps 네트워크: 64-128개 디스크립터
  • 1Gbps 네트워크: 256-512개 디스크립터
  1. 버퍼 정렬 원칙:
  • 데이터 버퍼는 64바이트 정렬(CELL 크기와 일치)
  • 비캐시 메모리 사용 또는 DMA 일관성 보장
  1. 프리페치 전략:
void prefetch_desc(struct descriptor *desc)
{
__builtin_prefetch(desc, 0, 3); // 강제 프리페치
if (desc->flags & EOP_FLAG) {
__builtin_prefetch(phys_to_virt(desc->buffer), 1, 3);
}
}

5.3 디버깅 도구 및 기술

  1. 레지스터 진단 도구 :
# devmem2 도구를 사용해 핵심 레지스터 확인
devmem2 0x01c80000 w  # MACCONTROL 읽기
devmem2 0x01c80044 w  # RXSTATUS 읽기
  1. 오류 주입 테스트 :
// 의도적으로 디스크립터 오류 생성하여 HOSTPEND 처리 테스트
void inject_desc_error()
{
struct descriptor *desc = get_tx_desc();
desc->buffer = 0;  // 무효 주소
desc->buflen = 1518;
flush_cache(desc);
trigger_dma();
}
  1. 실시간 통계 모니터링 :
void monitor_stats()
{
printf("RX 패킷: %u 오류: %u\n",
EMAC_REGS->RXGOODFRAMES,
EMAC_REGS->RXALIGNMENTERRORS);

if (EMAC_REGS->MACSTATUS & (1<<8)) {
printf("경고: 송신 FIFO 언더플루! \n");
}
}

실제 프로젝트에서 EMAC/MDIO 모듈의 안정성은 세부 사항에 대한 정교한 관리에 달려 있습니다. 저는 한 산업 게이트웨이 프로젝트에서 환경 온도가 70℃를 넘어갈 경우 특정 PHY 칩의 MDIO 접근이 불안정해지는 문제를 발견했습니다. 최종 해결책은 MDIO 클럭 분할 레지스터에 온도 보상 논리를 추가하는 것이었습니다:

void set_mdio_clock(int temp)
{
int clkdiv = BASE_CLKDIV;
if (temp > 70) {
clkdiv += (temp - 70) * 2; // 온도 1℃ 상승 시 분할 계수 2 증가
}
MDIO_REGS->CONTROL = (1<<31) | (clkdiv << 0);
}

태그: EMAC MDIO DMA 인터럽트 처리 Linux 드라이버 개발

6월 26일 18:44에 게시됨