3P by neo 2달전 | favorite | 댓글 1개

이산 논리 네트워크 카드 제작기

이 글은 이산 논리 회로를 사용하여 완전한 컴퓨터 시스템을 구축하는 과정을 다룬 시리즈물의 일부임. 현재까지 HTTP 서버나 LAN 게임 등의 네트워크 애플리케이션을 실행할 수 있는 컴퓨터를 만듦.

개요

  • 지난해 10BASE-T 이더넷 신호를 SPI로 변환하고 그 반대로도 변환하는 물리 계층 어댑터를 제작함. 당시에는 STM32 마이크로컨트롤러를 사용하여 동작을 테스트했고, 이제는 직접 만든 컴퓨터에 연결하기 위해 MAC 계층 모듈을 구현하고 있음.
  • 두 어댑터는 모두 전이중이며 독립적인 송신기와 수신기 부분을 가지고 있음.

수신기

수신기 동작 요약:

  • SPI 직렬 데이터가 바이트 단위의 병렬 데이터로 변환되고, 바이트 클럭이 추출됨.
  • 처음 6바이트가 목적지 MAC 주소 기준과 비교되고, 일치하지 않는 프레임은 거부됨.
  • 바이트가 정적 RAM 버퍼에 기록됨.
  • 프레임이 끝나면 수신기가 비활성화되고, 사용자가 수신기를 다시 시작할 때까지 추가 프레임이 거부됨. 바이트 카운터가 중지되고 그 값이 사용자에게 제공됨.

데이터 수집

  • 직렬 SPI 데이터를 바이트 스트림으로 변환해야 함.
  • 직렬 데이터는 시프트 레지스터(U32)로 이동함. U30U31은 각각 비트와 바이트를 카운트함.
  • 정적 RAM 쓰기 신호 recv_buf_we는 D 플립플롭 U29B를 사용하여 형성됨. 이 신호는 입력 데이터의 8비트 각각 이후에 잠시 낮아짐.
  • 수신된 바이트는 6116(U20) 2kB 정적 RAM 버퍼에 기록됨.
  • U13, U16, U18은 주소 멀티플렉서를 형성하여 바이트 카운터 또는 시스템 주소 버스 중 하나를 SRAM(U20)의 주소 입력으로 선택함.
  • 삼상태 버퍼 U21은 수신된 바이트를 RAM으로 전달함.

MAC 주소 필터링

  • 이더넷 트래픽을 분석할 때 프레임이 보통 작은 그룹(짧은 지연으로 분리된 3-4개의 프레임)으로 오는 것을 발견함. 한 그룹의 프레임은 일반적으로 서로 다른 목적지 MAC 주소를 가짐.
  • 이는 내 컴퓨터가 MAC별로 수신된 프레임을 필터링하고 수신기를 충분히 빠르게 재시작하여 자신을 위한 프레임을 잡을 수 없을 것이라는 생각이 들게 만듦. 하드웨어 MAC 주소 필터링이 필요했음.
  • 사용자 지정 MAC 주소를 어딘가에 저장한 다음 처음 6개의 수신 바이트와 비교하는 것은 너무 복잡함. 단일 바이트의 반복(예: FE:FE:FE:FE:FE:FE)으로 만들 수도 있지만 지루함.
  • 내 MAC에 약간의 변화를 주기 위해 바이트 인덱스의 함수로 만듦:
    • 비트 0은 0으로 고정
    • 비트 1은 1로 고정
    • 비트 2-4는 바이트 인덱스의 반전
    • 비트 5-7은 1로 고정
  • 이 규칙을 사용하면 MAC 주소는 FE:FA:F6:F2:EE:EA가 됨. 또한 ARP와 작동하려면 브로드캐스트 MAC FF:FF:FF:FF:FF:FF를 수락해야 함.

송신기

  • 수신기와 유사하게 송신기는 FCS 생성을 구현하지 않고 소프트웨어에서 수행됨.
  • 송신기를 더 단순화하기 위해 고정 길이 프레임만 지원하기로 결정함. 이렇게 하면 복잡한 디지털 비교기가 필요하지 않고, 프레임 전송 로직은 바이트 카운터의 단일 비트에만 의존함.
  • 프레임 길이를 1024바이트로 선택했는데, 이는 일반적인 MTU인 1500바이트에 가까움.
  • 10BASE-T에 필요한 프레임 프리앰블(0x55의 시퀀스 뒤에 0xD5가 오는 것)도 1024바이트에 포함되며 소프트웨어에 로드해야 함.

카운터

  • 수신기와 마찬가지로 비트(U12)와 바이트(U14)를 세는 데 두 개의 카운터가 사용됨.
  • 첫 번째 카운터는 통합 발진기의 20MHz 클럭으로 공급됨.
  • 20MHz는 직접 사용되지 않고 최소한 2로 나누어짐. 이렇게 하면 발진기의 듀티 사이클이 출력 신호에 영향을 미치지 않음.

데이터 흐름

  • RAM(U22)의 주소 입력을 선택하기 위해 수신기와 동일한 세 개의 74HC157 멀티플렉서(여기에는 표시되지 않음)가 사용됨.
  • U23은 데이터를 RAM에 로드하는 데 사용됨.
  • U24는 현재 전송 중인 바이트에 대한 중간 저장소 역할을 함. 여기서의 아이디어는 내 VGA 파이프라인과 유사함.
  • 바이트 카운터 74HC4040은 리플 카운터이며 안정화되는 데 시간이 걸리는데, U24는 RAM 출력이 여전히 유효하지 않은 동안 안정적인 출력을 제공함.
  • 이 데이터는 시프트 레지스터 U28로 공급되고 비트 단위로 이동됨.

CPU 인터페이스

프로그래머의 관점에서 내 이더넷 어댑터의 인터페이스는 다음과 같음:

  • 두 프레임 버퍼 모두 0xF000에 매핑됨.
  • 두 개의 읽기 전용 레지스터가 있음:
    • 0xFB00의 8비트 상태 레지스터에는 두 개의 플래그가 있음:
      • RX_FULL - 프레임이 수신됨
      • TX_BUSY - 프레임이 전송 중임
    • 0xFB02의 16비트 수신 데이터 길이 레지스터
  • 0xFB00에 任意의 값을 쓰면 수신기가 재시작됨.
  • 0xFB01에 任意의 값을 쓰면 전송이 시작됨.
  • 내 CPU가 인터럽트를 지원하지 않기 때문에 인터럽트가 없음.

프로그래밍

네트워크 지원을 원했지만 직접 TCP/IP 스택을 구현하고 싶지는 않았음. 또한 첫 번째 컴파일러가 형편없었고 어셈블리로 프로그래밍하는 것이 귀찮기 때문에 괜찮은 C 컴파일러를 원했음. 그래서 C 컴파일러를 만듦. 이는 uIP 1.0(작은 TCP/IP 라이브러리)을 컴파일할 수 있을 만큼 성숙함. 내 CPU의 코드 밀도가 매우 낮음에도 불구하고 uIP는 RAM에 맞출 수 있을 만큼 작고 실제 애플리케이션을 위한 공간이 남아있음.

네트워크 성능은 매우 낮지만 상용 CPU나 특수 칩이 사용되지 않은 것을 고려하면 여전히 매우 만족스러움:

  • 평균 핑 왕복 시간 85ms
  • HTTP 서버 다운로드 속도 2.6kB/s(SD 카드에서 정적 파일 제공)

프로젝트 저장소

모델, 회로도 파일 및 PCB 도면은 GitHub에 있음.

GN⁺의 의견

  • 이 프로젝트는 개발자의 하드웨어에 대한 깊은 이해와 열정을 보여줌. 모든 것을 직접 구현하려는 노력은 대단히 인상적이지만, 실용성 측면에서는 의문이 듦.
  • 현대의 컴퓨팅 시스템은 매우 복잡하고 전문화되어 있어서 모든 것을 처음부터 구현하는 것은 매우 비효율적임. 특히 네트워크 프로토콜 스택과 같이 잘 정립되고 최적화된 영역은 기존 구현체를 활용하는 것이 현명함.
  • 그럼에도 불구하고 이런 프로젝트는 교육적 가치가 매우 높음. 저수준 하드웨어와 소프트웨어가 어떻게 상호작용하는지, 프로토콜이 어떻게 구현되는지 직접 경험해 볼 수 있기 때문.
  • 또한 요즘 개발자들 사이에서 하드웨어에 대한 이해도가 낮아지고 있는 상황에서, 이런 프로젝트는 컴퓨터 시스템의 근간을 일깨워주는 귀중한 사례가 될 수 있음.
  • 아쉬운 점은 성능이 매우 낮다는 것. 실제 활용 가능성을 높이려면 더 최적화된 구현이 필요해 보임. 하지만 그것이 이 프로젝트의 주된 목적은 아닌 듯함.
Hacker News 의견
  • 이 프로젝트는 하드웨어 MAC 주소 필터링을 구현한 사용자 정의 컴퓨터를 위한 이더넷 카드를 만든 것으로, 작업 과정의 추론 스택 트레이스가 교육적이고 훌륭함.
  • 일반 PC를 위한 이더넷 카드의 최소 구현은 유사할 것이나, PC의 CPU에서 체크섬을 계산하고 USB 등으로 연결해야 할 것임.
  • 이 프로젝트를 위해 C 컴파일러, 링커, libc 등을 직접 만든 것이 인상적임.
  • 이런 프로젝트에 투입된 열정과 노력에 감탄하며, 은퇴 후에 이런 하드웨어/소프트웨어 프로젝트를 해보고 싶음.
  • 과거 Etherlink 3c501 이더넷 카드는 성능이 좋지 않았음.
  • 이산 논리 회로로 네트워크 카드를 만든 것으로 보임. (이산 논리 네트워크 카드가 아니라)
  • 모든 네트워크 카드가 이산 논리 소자로 만들어지는 것은 아님. (naive question)
  • 이 컴퓨터 셋업의 모듈화가 훌륭함.
  • 단순하고 효과적으로 설명한 것이 인상적이고 큰 격려를 받을 만함.