3 poin oleh GN⁺ 2024-04-10 | 1 komentar | Bagikan ke WhatsApp
  • Ini adalah catatan pembuatan perangkat keras yang menambahkan kemampuan kirim-terima Ethernet 10BASE-T ke komputer logika diskret yang dibuat tanpa CPU komersial atau chip jaringan khusus
  • Di atas adapter lapisan fisik 10BASE-T↔SPI yang dibuat sebelumnya, ditambahkan modul lapisan MAC untuk dihubungkan ke homebrew computer, dan pemancar serta penerima disusun dalam arsitektur full-duplex yang independen
  • Penerima mengubah data SPI menjadi byte dan menyimpannya ke SRAM 2 kB, lalu memeriksa 6 byte pertama di perangkat keras agar hanya menerima FE:FA:F6:F2:EE:EA atau MAC broadcast
  • Untuk menyederhanakan rangkaian, pemancar menyerahkan pembuatan FCS dan penyiapan preamble ke perangkat lunak, dan hanya mendukung frame dengan panjang tetap 1024 byte
  • Bahkan dibuat compiler C yang mampu mengompilasi uIP 1.0 untuk menjalankan aplikasi jaringan, dengan hasil rata-rata ping 85 ms dan unduhan file statis HTTP sekitar 2.6 kB/s

Menambahkan Ethernet ke komputer logika diskret

  • Sebagai kelanjutan dari upaya membangun sistem komputer lengkap dengan komponen logika diskret, dibuat adapter Ethernet yang mampu menjalankan aplikasi jaringan
  • Sebelumnya telah dibuat adapter lapisan fisik yang mengubah sinyal Ethernet 10BASE-T menjadi SPI dan sebaliknya, dan saat itu pengujian operasi menggunakan mikrokontroler STM32
  • Inti dari pekerjaan kali ini adalah modul lapisan MAC untuk menghubungkan adapter tersebut ke homebrew computer
  • Adapter ini berarsitektur full-duplex, dan bagian pemancar serta penerima bekerja independen satu sama lain

Penerima: menyimpan data SPI ke buffer frame

  • Penerima mengubah data serial SPI menjadi data paralel per byte dan mengekstrak clock byte
  • Alamat MAC tujuan diperiksa pada 6 byte pertama, dan frame yang tidak memenuhi kriteria akan ditolak
  • Byte yang diterima ditulis ke buffer SRAM 6116 2 kB
  • Saat frame selesai, penerima dinonaktifkan dan tidak akan menerima frame tambahan sampai diaktifkan kembali
  • Pencacah byte tetap mempertahankan nilainya setelah berhenti agar CPU dapat membaca panjang data yang diterima
  • FCS tidak diperiksa di perangkat keras
  • Pengumpulan data dan akses buffer

    • Data serial SPI masuk ke shift register U32, dan U30 serta U31 masing-masing menghitung bit dan byte
    • D flip-flop U29B membuat sinyal tulis SRAM recv_buf_we, dan sinyal ini sesaat menjadi rendah setiap 8 bit data masuk
    • Byte yang diterima ditulis ke SRAM 6116 U20
    • U13, U16, U18 membentuk multiplexer alamat untuk memilih pencacah byte atau bus alamat sistem sebagai input alamat SRAM
    • U21 berperan sebagai buffer tri-state yang mengirim byte yang diterima ke RAM
    • Agar CPU bisa mengakses data dan panjang data terima, RAM dan pencacah byte dihubungkan ke bus data sistem
    • U25 menghubungkan RAM penerima ke bus data sistem
    • Setelah frame selesai, nilai pencacah byte dipertahankan pada bus recv_byte_cnt
    • U26, U27 mengirim nilai ini ke bus data sistem saat ada permintaan baca alamat tertentu
    • Separuh sisa dari U27 membentuk register status baca-saja 2-bit untuk mengecek status penerima dan pemancar

Penyaringan alamat MAC di perangkat keras

  • Setelah menganalisis lalu lintas Ethernet, terlihat bahwa frame biasanya masuk dalam kelompok kecil berisi 3–4 buah yang dipisahkan jeda singkat, dan bahkan dalam kelompok yang sama pun alamat MAC tujuan sering berbeda
  • Komputer mungkin tidak cukup cepat untuk melakukan penyaringan MAC di perangkat lunak lalu mengaktifkan kembali penerima, sehingga dibutuhkan penyaringan MAC di perangkat keras
  • Metode menyimpan alamat MAC kustom lalu membandingkannya dengan 6 byte pertama dianggap terlalu rumit dan dikesampingkan
  • Alamat MAC dengan pengulangan byte tunggal juga memungkinkan, tetapi akhirnya dipilih pembuatan alamat MAC sebagai fungsi dari indeks byte
    • bit 0 tetap 0
    • bit 1 tetap 1
    • bit 2~4 adalah nilai terbalik dari indeks byte
    • bit 5~7 tetap 1
  • Alamat MAC yang dihasilkan dengan aturan ini adalah FE:FA:F6:F2:EE:EA
  • Agar ARP berfungsi, MAC broadcast FF:FF:FF:FF:FF:FF juga diterima
  • U33 membandingkan apakah bit data 0 dan bit 2~4 sesuai dengan nilai yang diinginkan, dan keluaran U34A menjadi tinggi saat bit-bit tersebut cocok
  • U35A mengimplementasikan pemeriksaan MAC broadcast, dan keluarannya menjadi tinggi jika bit 0 dan bit 2~4 semuanya 1
  • Kedua sinyal digabung dengan diode OR menggunakan D7 dan R6, dan U35B memeriksa apakah semua bit sisanya bernilai 1
  • Hasil validitas satu byte diakumulasikan di U10A
    • Saat tidak menerima frame, sinyal incoming SPI slave select ss rendah dan U10A diatur ke 1
    • Selama penerimaan frame, nilainya diperbarui untuk setiap byte yang diterima
    • Jika alamat MAC tujuan sesuai kriteria, nilai U10A tetap tinggi
    • Saat alamat byte mencapai 5, nilai akhirnya dilatch ke U36B, dan jika alamat tujuan tidak cocok maka penerimaan frame diblokir

Pemancar: menyederhanakan rangkaian dengan frame panjang tetap

  • Seperti pada penerima, pemancar juga tidak mengimplementasikan pembuatan FCS di perangkat keras dan menanganinya di perangkat lunak
  • Untuk mengurangi rangkaian, pemancar hanya mendukung frame dengan panjang tetap
  • Panjang frame dipilih 1024 byte, nilai yang mendekati MTU umum 1500 byte
  • Preamble yang dibutuhkan 10BASE-T terdiri dari beberapa 0x55 dan diakhiri 0xD5, dan perangkat lunak harus memuatnya bersama data ke dalam 1024 byte ini
  • Panjang frame tetap tidak memengaruhi protokol lapisan atas
    • Protokol lapisan atas mengenkode ukuran paket di header
    • Tidak bergantung pada panjang frame Ethernet sebenarnya
  • Alur data transmisi

    • Data kirim disimpan di SRAM
    • Clock 20 MHz masuk ke pencacah 4-bit, dan keluaran overflow digunakan sebagai clock byte
    • Menulis nilai ke lokasi memori write-only tertentu akan mengaktifkan pencacah dan memulai transmisi frame
    • Data byte paralel diserialkan melalui shift register
    • Sama seperti penerima, U12 menghitung bit dan U14 menghitung byte
    • Clock 20 MHz berasal dari osilator terintegrasi, dan tidak digunakan langsung melainkan minimal dibagi 2 terlebih dahulu
    • Cara ini memastikan duty cycle osilator tidak memengaruhi sinyal keluaran
  • RAM, shift register, dan timing

    • Seperti pada penerima, tiga multiplexer 74HC157 digunakan untuk memilih input alamat RAM U22
    • U23 digunakan untuk memuat data ke RAM
    • U24 berfungsi sebagai penyimpanan sementara untuk byte yang sedang dikirim
    • Pencacah byte 74HC4040 adalah ripple counter sehingga stabilisasinya lambat
    • U24 menyediakan keluaran yang stabil saat keluaran RAM belum valid
    • Data masuk ke shift register U28 dan digeser per bit
    • Ada bug perangkat keras yang salah menghubungkan urutan bit dari RAM ke shift register, sehingga harus diakali di perangkat lunak dengan menukar bit
    • MOSI dan SCK harus disinkronkan secara tepat untuk menghasilkan sinyal 10BASE-T yang baik
    • U11A dan U8B menangani sinkronisasi ini
    • tx_cnt0 adalah bit 0 dari pencacah bit, yaitu sinyal hasil pembagian dua dari 20 MHz yang dipakai sebagai clock
    • U11A mengubah keluaran sesuai sinyal ini
    • U8B menunda clock agar sesuai dengan delay yang dibuat U11A
    • D latch lebih kompleks daripada gerbang AND sederhana dan memiliki delay sekitar 5 ns lebih besar, sehingga digunakan 74LV74A yang lebih cepat
    • 74LV74A adalah satu-satunya chip keluarga cepat di papan ini

Antarmuka CPU dan pemetaan memori

  • Dari sudut pandang pemrogram, adapter Ethernet terlihat sebagai antarmuka yang dipetakan ke memori
  • Dua buffer frame dipetakan ke 0xF000
  • Ada dua register baca-saja
    • Register status 8-bit di 0xFB00 memiliki flag RX_FULL dan TX_BUSY
    • RX_FULL menunjukkan status penerimaan frame selesai
    • TX_BUSY menunjukkan status sedang mengirim frame
    • Register 16-bit di 0xFB02 menyimpan panjang data terima
  • Operasi tulis digunakan sebagai perintah kontrol
    • Menulis nilai apa pun ke 0xFB00 akan mengaktifkan kembali penerima
    • Menulis nilai apa pun ke 0xFB01 akan memulai transmisi
  • Karena CPU tidak mendukung interupsi, tidak ada interupsi
  • Semua alamat terkait dimulai dengan F yang berarti 4 bit teratas bernilai 1, dan kondisi ini diperiksa oleh U2A
  • Alamat buffer mengharuskan bit 11 bernilai 0, dan ini diperiksa oleh U1D, D2, R2, U1E
  • Alamat register mengharuskan digit heksadesimal kedua bernilai B yaitu 1011, dan U1B serta U2B memverifikasinya
  • Decoder U4A, U4B digunakan untuk memilih fungsi individual
  • Dua LED menunjukkan akses ke buffer atau register

Pemrograman dan performa

  • Ingin ada dukungan jaringan, tetapi tidak ingin mengimplementasikan sendiri stack TCP/IP dan juga kurang nyaman menulis assembly, sehingga dibuat compiler C
  • Compiler ini sudah cukup matang untuk mengompilasi uIP 1.0, pustaka TCP/IP kecil
  • Kepadatan kode CPU sangat rendah, tetapi uIP muat di RAM dan masih menyisakan ruang untuk aplikasi nyata
  • Performa jaringan memang rendah, tetapi ini adalah hasil yang dicapai tanpa CPU komersial atau chip khusus
    • Rata-rata RTT ping: 85 ms
    • Kecepatan unduh server HTTP: 2.6 kB/s
    • Server HTTP menyajikan file statis dari kartu SD
  • Model, berkas skematik, dan gambar PCB tersedia di repositori GitHub

1 komentar

 
GN⁺ 2024-04-10
Komentar Hacker News
  • Karya yang keren, terima kasih sudah berbagi. Stack trace proses penalaran-nya terutama bagus, dan secara edukatif sangat baik karena banyak hal diuraikan dari prinsip pertama atau dicoba dijelaskan dari sudut pandang pemula.
    Walau tidak praktis untuk jaringan nyata, menurut saya ini bukan sekadar main-main sederhana. Di era ketika backdoor ditemukan di chip jaringan yang terlalu kompleks, ke depan mungkin akan muncul pembaca yang lebih serius atau motivasi proyek seperti ini.

    • Saya jadi penasaran ada berapa banyak kerentanan tersembunyi di dalam silikon modern. Bahkan pada kode beberapa ribu baris saja hampir setiap hari ditemukan kerentanan, sementara di dalam silikon yang di-hardcode pada dasarnya ada mikrochip yang setara dengan miliaran baris kode.
  • Ini dibuat untuk komputer yang sepenuhnya kustom, jadi dengan sendirinya jauh lebih mengesankan; belum lagi bagian “jadi saya membuat compiler C”. Meski begitu, saya jadi penasaran seperti apa implementasi minimal kartu Ethernet untuk PC “umum”.
    Sepertinya banyak bagiannya akan mirip, dan checksum mungkin bisa diproses oleh CPU PC. Koneksinya perlu serial mentah atau, lebih praktis, USB; pada akhirnya sepertinya harus memakai driver “sungguhan” atau menyerahkannya ke user space untuk diproses.
    Saat melihat hal-hal serupa, saya sempat berpikir kalau perangkat mengimplementasikan https://en.wikipedia.org/wiki/USB_communications_device_clas..., mungkin bisa “langsung jalan” tanpa driver sendiri, tetapi itu tampaknya kurang cocok dengan memproses semua checksum di sisi host.
    Saat mencari, saya juga menemukan https://en.wikipedia.org/wiki/Ethernet_over_USB, dan mungkin maksudnya kita bisa membuat adaptor yang hanya mengubah koneksi fisik menjadi USB lalu membiarkan komputer menangani sisanya.

    • USB bisa dibilang jauh lebih kompleks daripada 10base2 Ethernet yang sudah lama ada. Kalau ingin tersambung ke jaringan 10base2 Ethernet lewat PCIe atau USB, keduanya akan menjadi pekerjaan yang jauh lebih besar daripada sisi Ethernet-nya.
      Mungkin perangkat USB gaya FTDI bisa dibujuk untuk melakukan bit-banging 10base2 Ethernet. Caranya hanya mengimplementasikan sisi “PHY” yang mengubah lalu lintas kabel menjadi bitstream bersih dan menyelaraskan awal frame, lalu PC menangani semua sisanya di software.
    • Kalau PC umum masih punya bus ISA seperti 30 tahun lalu, kartu jaringan saya bisa disambungkan ke sana dengan sedikit modifikasi saja.
    • Mengimplementasikan NIC dengan FPGA, biasanya bersama koneksi PCIe, sangat umum.
      Untuk sisi USB, CDC-NCM sendiri tidak sulit diimplementasikan pada MCU mana pun, tetapi implementasi USB HS PHY pada praktiknya membutuhkan hardware ASIC.
      Dengan USB HS ULPI PHY seharga 0,30 dolar, USB CDC-NCM kemungkinan bisa diimplementasikan di FPGA dengan cukup mudah.
  • Di bagian akhir ada tautan ke compiler C yang dibuat untuk proyek ini: https://github.com/imihajlow/ccpu-cc
    Sepertinya ada linker dan libc juga. Saya tidak benar-benar tahu seberapa kompleks desain hardwarenya, tetapi hebat sekali bisa membuat dan menempelkan compiler C ringan seperti itu.

    • Itu compiler C yang ditulis dengan Rust, dan untuk parsing bahasa memakai crate lang_c.
  • Sangat mengesankan. Saya ingin mencoba proyek seperti ini sendiri, dan saya jadi menghormati semangat serta jam kerja tak terhitung yang dibutuhkan untuk memahami sistem lalu sampai membuatnya.
    Saya tidak terlalu berharap pensiun, tetapi mungkin sekitar saat itulah saya akan menghabiskan waktu untuk proyek hardware dan software seperti ini.

  • Jadi ini lebih baik daripada Etherlink 3c501, atau lebih buruk? :-D
    https://mirror.math.princeton.edu/pub/oldlinux/Linux.old/net...
    Kalau ingatan saya benar, paket baru yang masuk dari jaringan bisa menimpa buffer yang hendak dibaca CPU. Saya memakainya di Linux selama beberapa waktu, dan performanya benar-benar buruk.

  • Bagian “meski panjang frame dibuat tetap, protokol di atasnya tidak terpengaruh, karena protokol tingkat atas mengodekan ukuran paket di header dan tidak bergantung pada panjang frame Ethernet sebenarnya” menarik.
    Baru-baru ini saya membuat packet decoder, dan di setiap layer saya secara eksplisit memverifikasi apakah panjang dari layer bawah sesuai. Untuk IP, di decoder saya panjang datagram IP harus persis cocok dengan panjang frame Ethernet dan panjang header link-layer.
    Itu bukan karena ingin terlalu teliti, melainkan untuk mendeteksi frame pendek; belakangan saya juga memutuskan untuk menganggap frame panjang sebagai error. Penulis memakai uIP, tetapi saya penasaran bagaimana Linux atau OS modern lain menanganinya. Saya juga jadi penasaran apakah sudah dilakukan pengujian interoperabilitas.

    • Frame panjang memang dikirim ke jaringan, dan tidak ada OS yang saya punya menunjukkan masalah. Saya pernah membaca di suatu tempat bahwa sebagian router benar-benar memakai frame panjang untuk menyimpan metadata di belakang paket.
    • Timestamp dan bentuk lain dari telemetri jaringan in-band kadang disisipkan ke frame dalam bentuk trailer. Saat itu FCS baru ditambahkan.
      Jika aplikasi tidak melihat data L2, stack IP Linux akan mengabaikannya begitu saja.
  • Secara fisik jauh lebih kecil daripada set kartu SSI Ethernet pertama DEC: https://i.ebayimg.com/images/g/NEYAAOSw-mZlg0lZ/s-l1600.jpg
    Papan DEC DEUNA panjangnya lebih dari 1 kaki, tetapi fungsinya juga jauh lebih banyak. DEUNA adalah NIC “sungguhan”, punya antrean kirim/terima, menanganinya secara mandiri, dan juga melakukan DMA. Tentu saja di atas kartunya juga ada PDP-11 sendiri untuk menjalankannya.

  • Keren sekali. Saya penasaran butuh berapa lama.

    • Untuk membuat modul jaringan butuh sekitar satu bulan, tetapi menulis compiler memakan waktu jauh lebih banyak.
  • Ini menunjukkan betapa mudahnya menanam backdoor di dalam chip yang terhubung ke port jaringan.

  • Di mata kuliah Communication Systems Engineering, saya mengimplementasikan pemrosesan sinyal Ethernet, lalu mengimplementasikan stack TCP/IP termasuk ARP dan switching dalam assembler Motorola 68k QUIC.
    Itu 18 bulan terpanjang dalam hidup saya.