GN⁺: Show HN: 이산 논리 네트워크 카드 제작
(qdiv.dev)이산 논리 네트워크 카드 제작기
이 글은 이산 논리 회로를 사용하여 완전한 컴퓨터 시스템을 구축하는 과정을 다룬 시리즈물의 일부임. 현재까지 HTTP 서버나 LAN 게임 등의 네트워크 애플리케이션을 실행할 수 있는 컴퓨터를 만듦.
개요
- 지난해 10BASE-T 이더넷 신호를 SPI로 변환하고 그 반대로도 변환하는 물리 계층 어댑터를 제작함. 당시에는 STM32 마이크로컨트롤러를 사용하여 동작을 테스트했고, 이제는 직접 만든 컴퓨터에 연결하기 위해 MAC 계층 모듈을 구현하고 있음.
- 두 어댑터는 모두 전이중이며 독립적인 송신기와 수신기 부분을 가지고 있음.
수신기
수신기 동작 요약:
- SPI 직렬 데이터가 바이트 단위의 병렬 데이터로 변환되고, 바이트 클럭이 추출됨.
- 처음 6바이트가 목적지 MAC 주소 기준과 비교되고, 일치하지 않는 프레임은 거부됨.
- 바이트가 정적 RAM 버퍼에 기록됨.
- 프레임이 끝나면 수신기가 비활성화되고, 사용자가 수신기를 다시 시작할 때까지 추가 프레임이 거부됨. 바이트 카운터가 중지되고 그 값이 사용자에게 제공됨.
데이터 수집
- 직렬 SPI 데이터를 바이트 스트림으로 변환해야 함.
- 직렬 데이터는 시프트 레지스터(
U32
)로 이동함.U30
과U31
은 각각 비트와 바이트를 카운트함. - 정적 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와 작동하려면 브로드캐스트 MACFF: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)
- 이 컴퓨터 셋업의 모듈화가 훌륭함.
- 단순하고 효과적으로 설명한 것이 인상적이고 큰 격려를 받을 만함.