- Đây là bản ghi quá trình chế tạo phần cứng bổ sung chức năng thu/phát 10BASE-T Ethernet cho một máy tính logic rời rạc được làm không dùng CPU thương mại hay chip mạng chuyên dụng
- Trên nền bộ chuyển đổi tầng vật lý 10BASE-T↔SPI đã làm trước đó, tác giả thêm mô-đun tầng MAC để kết nối với homebrew computer; bộ phát và bộ thu được cấu thành theo kiến trúc full-duplex độc lập
- Bộ thu chuyển dữ liệu SPI thành byte, lưu vào SRAM 2 kB, và kiểm tra 6 byte đầu bằng phần cứng để chỉ nhận
FE:FA:F6:F2:EE:EAhoặc MAC broadcast - Để đơn giản hóa mạch, bộ phát giao việc tạo FCS và chuẩn bị preamble cho phần mềm, và chỉ hỗ trợ frame có độ dài cố định 1024 byte
- Tác giả còn tạo cả trình biên dịch C có thể biên dịch uIP 1.0 để chạy ứng dụng mạng; kết quả đạt ping trung bình 85 ms và tải file tĩnh qua HTTP ở mức 2,6 kB/s
Gắn Ethernet vào máy tính logic rời rạc
- Như phần mở rộng của dự án tạo một hệ thống máy tính hoàn chỉnh bằng linh kiện logic rời rạc, tác giả đã triển khai bộ chuyển đổi Ethernet có thể chạy các ứng dụng mạng
- Trước đó, tác giả đã làm bộ chuyển đổi tầng vật lý chuyển tín hiệu 10BASE-T Ethernet sang SPI và ngược lại, khi đó dùng vi điều khiển STM32 để kiểm thử hoạt động
- Trọng tâm của lần này là mô-đun tầng MAC để kết nối bộ chuyển đổi đó với homebrew computer
- Bộ chuyển đổi có kiến trúc full-duplex, với phần phát và phần thu hoạt động độc lập với nhau
Bộ thu: lưu dữ liệu SPI vào frame buffer
- Bộ thu chuyển dữ liệu nối tiếp SPI thành dữ liệu song song theo byte và trích xuất byte clock
- Địa chỉ MAC đích được kiểm tra trong 6 byte đầu; các frame không khớp tiêu chí sẽ bị từ chối
- Các byte nhận được được ghi vào buffer SRAM 6116 2 kB
- Khi frame kết thúc, bộ thu bị vô hiệu hóa và không nhận thêm frame cho đến khi được kích hoạt lại
- Bộ đếm byte vẫn giữ giá trị sau khi dừng, để CPU có thể đọc độ dài nhận được
- FCS không được kiểm tra bằng phần cứng
-
Thu thập dữ liệu và truy cập buffer
- Dữ liệu nối tiếp SPI đi vào shift register
U32, cònU30vàU31lần lượt đếm bit và byte - D flip-flop
U29Btạo tín hiệu ghi SRAMrecv_buf_we, tín hiệu này hạ xuống trong chốc lát sau mỗi 8 bit dữ liệu đầu vào - Byte nhận được được ghi vào SRAM 6116 là
U20 U13,U16,U18tạo thành bộ multiplex địa chỉ, chọn bộ đếm byte hoặc bus địa chỉ hệ thống làm đầu vào địa chỉ SRAMU21đóng vai trò buffer ba trạng thái chuyển byte nhận được sang RAM- RAM và bộ đếm byte được nối với bus dữ liệu hệ thống để CPU có thể truy cập dữ liệu nhận và độ dài
U25nối RAM nhận với bus dữ liệu hệ thống- Sau khi frame hoàn tất, giá trị bộ đếm byte được giữ trên bus
recv_byte_cnt U26,U27đưa giá trị này lên bus dữ liệu hệ thống khi có yêu cầu đọc một địa chỉ cụ thể- Nửa còn lại của
U27tạo một thanh ghi trạng thái chỉ đọc 2 bit để truy vấn trạng thái bộ thu và bộ phát
- Dữ liệu nối tiếp SPI đi vào shift register
Lọc địa chỉ MAC bằng phần cứng
- Khi phân tích lưu lượng Ethernet, tác giả thấy frame thường đi vào theo các cụm nhỏ gồm 3~4 frame, được phân tách bởi độ trễ ngắn; ngay cả trong cùng một cụm, địa chỉ MAC đích cũng thường khác nhau
- Máy tính có thể không đủ nhanh để thực hiện lọc MAC bằng phần mềm rồi kích hoạt lại bộ thu, nên cần lọc MAC bằng phần cứng
- Cách lưu địa chỉ MAC tùy chỉnh rồi so sánh với 6 byte đầu bị loại vì quá phức tạp
- Địa chỉ MAC lặp lại một byte duy nhất cũng khả thi, nhưng cuối cùng tác giả tạo địa chỉ MAC dưới dạng hàm của chỉ số byte
- bit 0 cố định là 0
- bit 1 cố định là 1
- bit 2~4 là giá trị đảo của chỉ số byte
- bit 5~7 cố định là 1
- Địa chỉ MAC được tạo theo quy tắc này là
FE:FA:F6:F2:EE:EA - Để ARP hoạt động, MAC broadcast
FF:FF:FF:FF:FF:FFcũng được chấp nhận U33so sánh data bit 0 và bit 2~4 với giá trị mong muốn, và đầu raU34Alên mức cao khi các bit đó khớpU35Atriển khai kiểm tra MAC broadcast; nếu bit 0 và bit 2~4 đều là 1 thì đầu ra lên mức cao- Hai tín hiệu được kết hợp bằng diode OR dùng
D7vàR6, rồiU35Bkiểm tra xem các bit còn lại có đều là 1 hay không - Kết quả hợp lệ của từng byte được tích lũy trong
U10A- Khi không nhận frame, tín hiệu incoming SPI slave select
ssở mức thấp vàU10Ađược đặt thành 1 - Trong lúc nhận frame, giá trị được cập nhật ở mỗi byte nhận được
- Nếu địa chỉ MAC đích khớp tiêu chí, giá trị
U10Avẫn được giữ ở mức cao - Khi địa chỉ byte đạt 5, giá trị cuối cùng được chốt vào
U36B; nếu địa chỉ đích không khớp thì việc nhận frame bị chặn
- Khi không nhận frame, tín hiệu incoming SPI slave select
Bộ phát: đơn giản hóa mạch bằng frame độ dài cố định
- Tương tự bộ thu, bộ phát cũng không triển khai tạo FCS bằng phần cứng mà xử lý bằng phần mềm
- Để giảm mạch, bộ phát chỉ hỗ trợ frame có độ dài cố định
- Độ dài frame được chọn là 1024 byte, một giá trị gần với MTU phổ biến 1500 byte
- Preamble cần cho 10BASE-T gồm nhiều byte
0x55và byte kết thúc0xD5; phần mềm phải nạp chúng cùng trong 1024 byte này - Độ dài frame cố định không ảnh hưởng đến các giao thức tầng trên
- Các giao thức tầng trên mã hóa kích thước gói trong header
- Chúng không phụ thuộc vào độ dài frame Ethernet thực tế
-
Luồng dữ liệu phát
- Dữ liệu phát được lưu trong SRAM
- Clock 20 MHz được đưa vào bộ đếm 4 bit, và đầu ra overflow được dùng làm byte clock
- Khi ghi một giá trị vào một vị trí bộ nhớ chỉ ghi cụ thể, bộ đếm được kích hoạt và bắt đầu phát frame
- Dữ liệu byte song song được tuần tự hóa qua shift register
- Giống bộ thu,
U12đếm bit vàU14đếm byte - Clock 20 MHz đến từ oscillator tích hợp và không được dùng trực tiếp, mà tối thiểu được chia 2 trước khi sử dụng
- Cách này giúp duty cycle của oscillator không ảnh hưởng đến tín hiệu đầu ra
-
RAM, shift register và timing
- Phần chọn đầu vào địa chỉ của RAM
U22dùng ba bộ multiplex 74HC157, giống như bộ thu U23được dùng để nạp dữ liệu vào RAMU24đóng vai trò bộ lưu tạm trung gian cho byte đang được phát hiện tại- Bộ đếm byte 74HC4040 là ripple counter nên ổn định chậm
- Trong khi đầu ra RAM chưa hợp lệ,
U24cung cấp đầu ra ổn định - Dữ liệu đi vào shift register
U28và dịch theo từng bit - Có một lỗi phần cứng nối sai thứ tự bit từ RAM sang shift register, nên phần mềm phải trộn bit để обход qua
MOSIvàSCKphải được đồng bộ chính xác để tạo tín hiệu 10BASE-T tốtU11AvàU8Bxử lý việc đồng bộ nàytx_cnt0là bit 0 của bộ đếm bit, được dùng làm clock như tín hiệu 20 MHz chia 2U11Athay đổi đầu ra theo tín hiệu nàyU8Btrì hoãn clock để khớp với độ trễ doU11Atạo ra- D latch phức tạp hơn cổng AND đơn giản và có độ trễ lớn hơn khoảng 5 ns, nên tác giả dùng 74LV74A nhanh hơn
- 74LV74A là chip duy nhất thuộc dòng tốc độ cao trên bo mạch này
- Phần chọn đầu vào địa chỉ của RAM
Giao diện CPU và ánh xạ bộ nhớ
- Từ góc nhìn lập trình viên, bộ chuyển đổi Ethernet hiện ra như một giao diện memory-mapped
- Hai frame buffer được ánh xạ tại
0xF000 - Có hai thanh ghi chỉ đọc
- Thanh ghi trạng thái 8 bit tại
0xFB00có các cờRX_FULLvàTX_BUSY RX_FULLbiểu thị trạng thái nhận frame hoàn tấtTX_BUSYbiểu thị trạng thái đang phát frame- Thanh ghi 16 bit tại
0xFB02chứa độ dài dữ liệu nhận được
- Thanh ghi trạng thái 8 bit tại
- Thao tác ghi được dùng như lệnh điều khiển
- Ghi bất kỳ giá trị nào vào
0xFB00sẽ kích hoạt lại bộ thu - Ghi bất kỳ giá trị nào vào
0xFB01sẽ bắt đầu phát
- Ghi bất kỳ giá trị nào vào
- Do CPU không hỗ trợ interrupt nên không có interrupt
- Tất cả địa chỉ liên quan đều bắt đầu bằng
F, tức 4 bit cao đều là 1; điều kiện này đượcU2Akiểm tra - Địa chỉ buffer phải có bit 11 là 0;
U1D,D2,R2,U1Ekiểm tra điều này - Địa chỉ thanh ghi phải có chữ số thập lục phân thứ hai là
B, tức1011;U1BvàU2Bxác nhận điều này - Các decoder
U4A,U4Bđược dùng để chọn từng chức năng riêng lẻ - Hai đèn LED hiển thị truy cập buffer hoặc thanh ghi
Lập trình và hiệu năng
- Tác giả muốn có hỗ trợ mạng nhưng không muốn tự triển khai TCP/IP stack, và việc lập trình assembly cũng bất tiện, nên đã tạo trình biên dịch C
- Trình biên dịch này đã đủ trưởng thành để biên dịch uIP 1.0, một thư viện TCP/IP nhỏ
- Mật độ mã của CPU rất thấp, nhưng uIP vẫn nằm vừa trong RAM và còn chừa chỗ cho ứng dụng thực tế
- Hiệu năng mạng thấp, nhưng đây là kết quả đạt được mà không dùng CPU thương mại hay chip chuyên dụng
- Thời gian khứ hồi ping trung bình: 85 ms
- Tốc độ tải xuống từ HTTP server: 2,6 kB/s
- HTTP server cung cấp các file tĩnh trên thẻ SD
- Model, file sơ đồ mạch và bản vẽ PCB có trong kho GitHub
1 bình luận
Ý kiến từ Hacker News
Thật tuyệt khi bạn chia sẻ công trình này. Mình đặc biệt thích stack trace của quá trình suy luận, và việc bạn giải quyết nhiều thứ từ các nguyên lý đầu tiên hoặc cố gắng giải thích từ góc nhìn của người mới bắt đầu là cực kỳ có giá trị về mặt giáo dục.
Dù không thực dụng cho mạng máy tính thực tế, mình không nghĩ đây chỉ là một trò đùa đơn thuần. Trong thời đại mà các backdoor bị phát hiện trong những chip mạng phức tạp quá mức, sau này có thể sẽ xuất hiện một lượng độc giả nghiêm túc hơn hoặc động lực dự án rõ ràng hơn
Đây là thứ dành cho một máy tính hoàn toàn tự chế, nên bản thân nó đã ấn tượng hơn nhiều, chưa kể đến đoạn “thế là tôi làm luôn một trình biên dịch C”. Dù vậy, mình vẫn tò mò mức triển khai tối thiểu của một card Ethernet cho PC “thông thường” sẽ đến đâu.
Có lẽ phần lớn sẽ tương tự, và checksum cũng có thể để CPU của PC xử lý. Kết nối thì sẽ cần serial thô, hoặc thực tế hơn là USB, và rốt cuộc có lẽ phải dùng driver “thật” hoặc đẩy sang user space để xử lý.
Khi xem những thứ tương tự, mình từng nghĩ nếu thiết bị triển khai https://en.wikipedia.org/wiki/USB_communications_device_clas... thì có thể làm cho nó “cứ thế mà chạy” mà không cần driver riêng, nhưng có vẻ điều đó không hợp lắm với việc để mọi checksum do phía host xử lý.
Trong lúc tìm kiếm mình còn thấy https://en.wikipedia.org/wiki/Ethernet_over_USB, nên có lẽ điều đó có nghĩa là có thể tạo một adapter chỉ chuyển đổi kết nối vật lý sang USB rồi để máy tính tự lo phần còn lại
Có lẽ có thể thuyết phục một thiết bị USB kiểu FTDI bit-bang 10base2 Ethernet. Cách làm là chỉ triển khai phần “PHY” để biến lưu lượng trên đường truyền thành một bitstream sạch và đồng bộ điểm bắt đầu khung, rồi để PC xử lý toàn bộ phần còn lại bằng phần mềm
Phía USB thì bản thân CDC-NCM không khó triển khai trên bất kỳ MCU nào, nhưng việc triển khai USB HS PHY trên thực tế gần như cần phần cứng ASIC.
Dùng một USB HS ULPI PHY giá 0,30 USD thì có thể triển khai USB CDC-NCM trên FPGA khá dễ dàng
Ở cuối bài có liên kết đến trình biên dịch C được tạo cho dự án này: https://github.com/imihajlow/ccpu-cc
Có vẻ cũng có cả linker và libc. Mình không thật sự biết thiết kế phần cứng phức tạp đến mức nào, nhưng việc tiện tay làm luôn một trình biên dịch C để gắn vào là điều rất đáng nể
Thật sự rất ấn tượng. Mình muốn tự làm một dự án như thế này, và rất kính nể niềm đam mê cùng vô số thời gian cần bỏ ra để hiểu hệ thống rồi đi đến mức tự chế tạo được nó.
Mình không hẳn mong đến lúc nghỉ hưu, nhưng có lẽ khi đó mình sẽ dành thời gian cho những dự án phần cứng/phần mềm kiểu này
Vậy nó tốt hơn hay tệ hơn Etherlink 3c501? :-D
https://mirror.math.princeton.edu/pub/oldlinux/Linux.old/net...
Nếu mình nhớ không nhầm, kiểu như gói tin mới đi vào từ mạng sẽ ghi đè lên buffer mà CPU đang định đọc. Mình đã dùng nó với Linux một thời gian, và hiệu năng thật sự rất tệ
Thật ngạc nhiên khi driver và firmware update đã che giấu biết bao nhiêu thứ khỏi tầm mắt
Cái của mình tốt hơn vì có hai buffer :) Dù vậy, vẫn chỉ lưu được một frame nhận
Đoạn “ngay cả khi cố định độ dài frame thì cũng không ảnh hưởng đến các giao thức tầng trên, vì các giao thức tầng trên mã hóa kích thước gói trong header và không phụ thuộc vào độ dài frame Ethernet thực tế” rất thú vị.
Gần đây mình có làm một bộ giải mã gói tin, và đã xác minh tường minh ở từng tầng xem độ dài của tầng dưới có khớp không. Với IP, trong bộ giải mã của mình thì độ dài IP datagram phải khớp chính xác với độ dài frame Ethernet và độ dài header tầng liên kết.
Không phải vì mình muốn quá khắt khe, mà là để phát hiện frame bị ngắn; sau đó mình cũng quyết định coi frame quá dài là lỗi. Tác giả dùng uIP, nhưng mình tò mò Linux hoặc các OS hiện đại khác xử lý chuyện này thế nào. Cũng tò mò không biết tác giả có kiểm thử khả năng tương tác hay không
Nếu ứng dụng không nhìn vào dữ liệu L2 thì Linux IP stack sẽ đơn giản là bỏ qua
Xét về mặt vật lý thì nó nhỏ hơn rất nhiều so với bộ card SSI Ethernet đầu tiên của DEC: https://i.ebayimg.com/images/g/NEYAAOSw-mZlg0lZ/s-l1600.jpg
Các bo mạch DEC DEUNA dài hơn 1 foot, nhưng cũng có nhiều chức năng hơn hẳn. DEUNA là một NIC “thật sự”, có hàng đợi gửi/nhận và tự xử lý chúng, đồng thời còn làm DMA. Tất nhiên nó cũng chạy một PDP-11 riêng trên card
Quá tuyệt. Không biết mất bao lâu để làm xong
Điều này cho thấy việc cài backdoor vào con chip nối với cổng mạng có thể dễ đến mức nào
Trong khóa học Communication Systems Engineering, mình đã triển khai xử lý tín hiệu Ethernet, rồi tiếp tục triển khai TCP/IP stack có cả ARP và switching bằng Motorola 68k QUIC assembly
Đó là 18 tháng dài nhất trong cuộc đời mình