3 puan yazan GN⁺ 2024-04-10 | 1 yorum | WhatsApp'ta paylaş
  • Ticari CPU veya özel ağ çipi kullanmadan yapılmış ayrık mantık bilgisayarına 10BASE-T Ethernet gönderme/alma işlevi ekleyen bir donanım yapım yazısı
  • Daha önce yapılan 10BASE-T↔SPI fiziksel katman adaptörünün üzerine MAC katmanı modülü eklenerek homebrew computer'a bağlanıyor; verici ve alıcı bağımsız, tam çift yönlü bir yapıda
  • Alıcı, SPI verisini baytlara dönüştürüp 2 kB SRAM'e kaydediyor; ilk 6 baytı donanımda kontrol ederek yalnızca FE:FA:F6:F2:EE:EA veya yayın MAC adresini kabul ediyor
  • Verici, devreyi basitleştirmek için FCS üretimini ve preamble hazırlığını yazılıma bırakıyor ve yalnızca 1024 bayt sabit uzunlukta çerçeveleri destekliyor
  • uIP 1.0'ı derleyebilen bir C derleyicisi de geliştirilerek ağ uygulamaları çalıştırılmış; sonuçlar ortalama 85 ms ping ve 2,6 kB/s düzeyinde HTTP statik dosya indirme hızı

Ayrık mantık bilgisayarına Ethernet eklemek

  • Tam bir bilgisayar sistemini ayrık mantık bileşenleriyle yapma çalışmasının devamı olarak, ağ uygulamalarını çalıştırabilen bir Ethernet adaptörü gerçekleştiriliyor
  • Daha önce 10BASE-T Ethernet sinyalini SPI'ye dönüştürüp geri çeviren bir fiziksel katman adaptörü yapılmıştı; o dönemde çalışma testleri için STM32 mikrodenetleyici kullanılmıştı
  • Bu çalışmanın özü, o adaptörü homebrew computer'a bağlamak için geliştirilen MAC katmanı modülü
  • Adaptör tam çift yönlü yapıda; gönderme ve alma birimleri birbirinden bağımsız çalışıyor

Alıcı: SPI verisini çerçeve arabelleğine kaydetme

  • Alıcı, SPI seri verisini bayt düzeyinde paralel veriye dönüştürüyor ve bayt saatini çıkarıyor
  • Hedef MAC adresi ilk 6 baytta kontrol ediliyor; ölçütlere uymayan çerçeveler reddediliyor
  • Alınan baytlar 6116 2 kB SRAM arabelleğine yazılıyor
  • Çerçeve bittiğinde alıcı devre dışı kalıyor ve yeniden etkinleştirilene kadar ek çerçeve almıyor
  • Bayt sayacı durduktan sonra da değerini koruyor; böylece CPU alınan uzunluğu okuyabiliyor
  • FCS donanımda kontrol edilmiyor
  • Veri toplama ve arabellek erişimi

    • SPI seri verisi U32 kaydırma yazmacına giriyor; U30 ve U31 sırasıyla bitleri ve baytları sayıyor
    • D flip-flop U29B, SRAM yazma sinyali recv_buf_we'yi üretiyor; bu sinyal her 8 bit giriş verisinde kısa süreliğine düşük seviyeye iniyor
    • Alınan baytlar U20 olan 6116 SRAM'e yazılıyor
    • U13, U16, U18, SRAM adres girişine bayt sayacını veya sistem adres veriyolunu seçen adres çoklayıcısını oluşturuyor
    • U21, alınan baytları RAM'e aktaran üç durumlu arabellek görevi görüyor
    • CPU'nun alınan verilere ve uzunluğa erişebilmesi için RAM ve bayt sayacı sistem veri veriyoluna bağlanıyor
    • U25, alıcı RAM'ini sistem veri veriyoluna bağlıyor
    • Çerçeve tamamlandıktan sonra bayt sayacı değeri recv_byte_cnt veriyolunda tutuluyor
    • U26, U27, belirli bir adres okuma isteği geldiğinde bu değeri sistem veri veriyoluna aktarıyor
    • U27'nin kalan yarısı, alıcı ve verici durumunu sorgulayan 2 bitlik salt okunur durum yazmacını oluşturuyor

Donanımsal MAC adresi filtreleme

  • Ethernet trafiği analiz edildiğinde çerçevelerin genelde kısa gecikmelerle ayrılmış 3-4'lü küçük gruplar halinde geldiği, aynı grup içinde bile hedef MAC adreslerinin çoğu zaman farklı olduğu görüldü
  • Bilgisayarın MAC filtrelemeyi yazılımla yapıp alıcıyı yeniden etkinleştirmesi yeterince hızlı olmayabileceğinden donanımsal MAC filtreleme gerekiyordu
  • Özel bir MAC adresini saklayıp ilk 6 baytla karşılaştırma yöntemi fazla karmaşık olduğu için elendi
  • Tek bayt tekrarlı MAC adresi de mümkündü; ancak sonunda MAC adresi, bayt indeksinin bir fonksiyonu olarak oluşturuldu
    • bit 0 sabit 0
    • bit 1 sabit 1
    • bit 2~4 bayt indeksinin ters değeri
    • bit 5~7 sabit 1
  • Bu kuralla oluşturulan MAC adresi FE:FA:F6:F2:EE:EA
  • ARP'nin çalışması için yayın MAC adresi FF:FF:FF:FF:FF:FF de kabul ediliyor
  • U33, veri bit 0 ile bit 2~4'ün istenen değerlerle eşleşip eşleşmediğini karşılaştırıyor; U34A çıkışı bu bitler eşleştiğinde yüksek oluyor
  • U35A, yayın MAC kontrolünü gerçekleştiriyor; bit 0 ve bit 2~4'ün tamamı 1 ise çıkış yüksek oluyor
  • İki sinyal, D7 ve R6 kullanan diyot OR ile birleştiriliyor; U35B ise kalan bitlerin tamamının 1 olup olmadığını kontrol ediyor
  • Tek bir baytın geçerlilik sonucu U10A'da biriktiriliyor
    • Çerçeve alınmazken gelen SPI slave select sinyali ss düşük seviyede ve U10A 1'e ayarlanıyor
    • Çerçeve alımı sırasında değer her alınan baytta güncelleniyor
    • Hedef MAC adresi ölçütlere uyuyorsa U10A değeri yüksek kalıyor
    • Bayt adresi 5'e ulaştığında son değer U36B'ye latch'leniyor; hedef adres uymuyorsa çerçeve alımı engelleniyor

Verici: devreyi basitleştirmek için sabit uzunlukta çerçeveler

  • Verici de alıcı gibi FCS üretimini donanımda gerçekleştirmiyor; yazılımla işliyor
  • Devreyi azaltmak için verici yalnızca sabit uzunlukta çerçeveleri destekliyor
  • Çerçeve uzunluğu 1024 bayt olarak seçildi; bu, yaygın MTU değeri olan 1500 bayta yakın
  • 10BASE-T için gereken preamble, birden çok 0x55 ve sondaki 0xD5'ten oluşuyor; yazılım bunu da 1024 baytın içine yüklemeli
  • Sabit çerçeve uzunluğu üst protokolleri etkilemiyor
    • Üst protokoller paket boyutunu başlıklarda kodluyor
    • Gerçek Ethernet çerçevesi uzunluğuna bağlı değiller
  • Gönderim veri akışı

    • Gönderilecek veri SRAM'de saklanıyor
    • 20 MHz saat 4 bitlik sayaca giriyor; taşma çıkışı bayt saati olarak kullanılıyor
    • Yalnızca yazılabilir belirli bir bellek konumuna değer yazıldığında sayaç etkinleşiyor ve çerçeve gönderimi başlıyor
    • Paralel bayt verisi, kaydırma yazmacı üzerinden serileştiriliyor
    • Alıcıda olduğu gibi U12 bitleri, U14 baytları sayıyor
    • 20 MHz saat entegre osilatörden geliyor; doğrudan kullanılmayıp en az 2'ye bölünerek kullanılıyor
    • Bu yöntem, osilatörün görev döngüsünün çıkış sinyalini etkilememesini sağlıyor
  • RAM, kaydırma yazmacı ve zamanlama

    • RAM U22'nin adres girişi seçimi için, alıcıdaki gibi üç adet 74HC157 çoklayıcı kullanılıyor
    • U23, RAM'e veri yüklemek için kullanılıyor
    • U24, o anda gönderilmekte olan bayt için ara depolama görevi görüyor
    • Bayt sayacı 74HC4040 bir ripple counter olduğundan kararlı hale gelmesi yavaş
    • RAM çıkışı henüz geçerli değilken U24 kararlı bir çıkış sağlıyor
    • Veri, U28 kaydırma yazmacına girerek bit bit ilerliyor
    • RAM'den kaydırma yazmacına giden bit sırasının yanlış bağlandığı bir donanım hatası olduğundan, yazılımda bitleri karıştırarak bunun etrafından dolaşmak gerekti
    • MOSI ve SCK, iyi bir 10BASE-T sinyali üretmek için tam olarak senkronize olmalı
    • U11A ve U8B bu senkronizasyonu gerçekleştiriyor
    • tx_cnt0, bit sayacının bit 0'ı; 20 MHz'i 2'ye bölmüş sinyal olarak saat için kullanılıyor
    • U11A bu sinyale göre çıkışını değiştiriyor
    • U8B, U11A'nın oluşturduğu gecikmeye uyum sağlamak için saati geciktiriyor
    • D latch, basit AND kapısına göre daha karmaşık ve yaklaşık 5 ns daha fazla gecikmeye sahip olduğundan daha hızlı 74LV74A kullanılıyor
    • 74LV74A bu karttaki hızlı aileden tek çip

CPU arayüzü ve bellek eşleme

  • Programcı açısından Ethernet adaptörü bellek eşlemeli bir arayüz gibi görünüyor
  • İki çerçeve arabelleği 0xF000 adresine eşleniyor
  • İki salt okunur yazmaç var
    • 0xFB00 adresindeki 8 bitlik durum yazmacında RX_FULL ve TX_BUSY bayrakları bulunuyor
    • RX_FULL, çerçeve alımının tamamlandığı durumu gösteriyor
    • TX_BUSY, çerçeve gönderiminin sürdüğü durumu gösteriyor
    • 0xFB02 adresindeki 16 bitlik yazmaç alınan veri uzunluğunu içeriyor
  • Yazma işlemleri kontrol komutu olarak kullanılıyor
    • 0xFB00 adresine herhangi bir değer yazmak alıcıyı yeniden etkinleştiriyor
    • 0xFB01 adresine herhangi bir değer yazmak gönderimi başlatıyor
  • CPU kesmeleri desteklemediği için kesme yok
  • İlgili adreslerin tamamı üst 4 biti 1 olan F ile başlıyor; bu koşulu U2A kontrol ediyor
  • Arabellek adreslerinde bit 11'in 0 olması gerekiyor; U1D, D2, R2, U1E bunu kontrol ediyor
  • Yazmaç adreslerinde ikinci onaltılık basamağın B, yani 1011 olması gerekiyor; U1B ve U2B bunu doğruluyor
  • U4A, U4B kod çözücüleri ayrı işlevlerin seçimi için kullanılıyor
  • İki LED, arabellek veya yazmaç erişimini gösteriyor

Programlama ve performans

  • Ağ desteği istense de TCP/IP yığınını baştan yazmak istenmediğinden ve assembly programlama da zahmetli olduğundan bir C derleyicisi geliştirildi
  • Bu derleyici, küçük bir TCP/IP kitaplığı olan uIP 1.0'ı derleyebilecek kadar olgun
  • CPU'nun kod yoğunluğu çok düşük olsa da uIP RAM'e sığıyor ve gerçek uygulamalar için de yer kalıyor
  • Ağ performansı düşük, ancak bu ticari CPU veya özel çip olmadan elde edilmiş bir sonuç
    • Ping gidiş-dönüş ortalaması: 85 ms
    • HTTP sunucusu indirme hızı: 2,6 kB/s
    • HTTP sunucusu SD karttaki statik dosyaları sunuyor
  • Model, devre şeması dosyaları ve PCB çizimleri GitHub deposunda bulunuyor

1 yorum

 
GN⁺ 2024-04-10
Hacker News yorumları
  • Paylaştığın için teşekkürler, harika bir iş. Özellikle akıl yürütme sürecinin stack trace’i çok hoşuma gitti; birçok şeyi ilk ilkelerden çözmesi ya da yeni başlayan bakış açısıyla açıklamaya çalışması eğitim açısından çok değerli.
    Gerçek ağ kullanımı için pratik olmasa da bunun basit bir oyuncaktan ibaret olmadığını düşünüyorum. Aşırı karmaşık ağ çiplerinde backdoor’ların bulunduğu bir çağda, ileride daha ciddi bir okur kitlesi ya da proje motivasyonu da oluşabilir.

    • Modern silikonun içinde gizli kaç güvenlik açığı olduğunu merak ediyorum. Birkaç bin satırlık kodda bile neredeyse her gün açık çıkıyor; oysa sabit biçimde silikona gömülmüş mikroçipler, fiilen milyarlarca satırlık koda denk bir şey barındırıyor sayılır.
  • Bu tamamen özel yapım bir bilgisayar için olduğundan, başlı başına çok daha etkileyici; “bu yüzden bir C derleyicisi yaptım” kısmını söylemeye bile gerek yok. Yine de “sıradan” bir PC için Ethernet kartının asgari uygulaması ne kadar olurdu diye merak ediyorum.
    Büyük kısmı benzer olur gibi; checksum işlemi PC CPU’suna yaptırılabilir. Bağlantı ham seri hat olabilir ya da daha pratik olarak USB gerekir; sonuçta ya “gerçek” bir sürücü kullanmak ya da işi kullanıcı alanına devredip orada işlemek gerekecek gibi.
    Benzer şeylere bakarken, cihaz https://en.wikipedia.org/wiki/USB_communications_device_clas... uygularsa kendi sürücüsü olmadan “öylece çalıştırmak” mümkün olmaz mı diye düşünmüştüm; ama tüm checksum’ları host tarafında işlemekle pek uyuşmayacak gibi.
    Ararken https://en.wikipedia.org/wiki/Ethernet_over_USB sayfasını da buldum; bu, sadece fiziksel bağlantıyı USB’ye çeviren bir adaptör yapıp gerisini bilgisayarın halletmesini sağlamak mümkün olabilir anlamına mı geliyor, emin değilim.

    • USB’nin, çok daha eski 10base2 Ethernet’ten çok daha karmaşık olduğu söylenebilir. Bir 10base2 Ethernet ağına PCIe ya da USB üzerinden bağlanmaya çalışırsanız, ikisi de Ethernet tarafına göre çok daha fazla iş çıkarır.
      FTDI tarzı bir USB cihazını 10base2 Ethernet’i bit-bang etmeye ikna etmek belki mümkün olabilir. Hat trafiğini temiz bir bit akışına dönüştüren ve frame başlangıcını hizalayan “PHY” tarafını uygulayıp, geri kalan her şeyi PC’nin yazılımla işlemesini sağlamak gibi.
    • Sıradan PC’lerde 30 yıl önceki gibi ISA bus olsaydı, ağ kartımı küçük değişikliklerle ona bağlayabilirdim.
    • FPGA ile NIC uygulamak, genelde PCIe bağlantısıyla birlikte oldukça yaygın bir şey.
      USB tarafında CDC-NCM’nin kendisini herhangi bir MCU’da uygulamak zor değil; ama USB HS PHY uygulaması pratikte ASIC donanım gerektiriyor.
      0,30 dolarlık bir USB HS ULPI PHY kullanılırsa, FPGA’da USB CDC-NCM’yi epey kolay uygulamak mümkün olurdu.
  • Son kısımda bu proje için yapılan C derleyicisinin bağlantısı var: https://github.com/imihajlow/ccpu-cc
    Görünüşe göre linker ve libc de var. Donanım tasarımının ne kadar karmaşık olduğunu tam bilmiyorum ama C derleyicisini hafifçe yapıp eklemiş olması müthiş.

    • Rust ile yazılmış bir C derleyicisi ve dil ayrıştırma için lang_c crate’ini kullanıyor.
  • Gerçekten etkileyici. Böyle bir projeyi ben de denemek isterdim; sistemi anlayıp ardından onu yapmaya harcanan tutkuyu ve sayısız saati takdir etmemek elde değil.
    Emekliliği özellikle istemiyorum ama belki o zamanlar böyle donanım-yazılım projelerine zaman ayırırım.

  • Peki bu Etherlink 3c501’den daha mı iyi, yoksa daha mı kötü? :-D
    https://mirror.math.princeton.edu/pub/oldlinux/Linux.old/net...
    Yanlış hatırlamıyorsam, CPU’nun okumaya çalıştığı buffer’ın ağdan gelen yeni paket tarafından üzerine yazılması gibi bir durum vardı. Bir süre Linux’ta kullandım; performansı gerçekten berbattı.

  • “Frame uzunluğunu sabitlemek üst protokolleri etkilemez. Çünkü üst protokoller paket boyutunu header’da kodlar ve gerçek Ethernet frame uzunluğuna dayanmaz” kısmı ilginç.
    Yakın zamanda bir paket decoder’ı yaptım; her katmanda alt katman uzunluğunun uyup uymadığını açıkça doğruladım. IP için benim decoder’ımda IP datagram uzunluğunun Ethernet frame uzunluğu ve link katmanı header uzunluğuyla tam olarak eşleşmesi gerekiyor.
    Bunu titiz olmak için değil, kısa frame’leri tespit etmek için yapmıştım; daha sonra uzun frame’leri de hata saymaya karar verdim. Yazar uIP kullanıyor ama Linux’un ya da başka modern OS’lerin bunu nasıl ele aldığını merak ediyorum. Birlikte çalışabilirlik testi yapıp yapmadığını da merak ettim.

    • Ağa uzun frame’ler gönderiyorum; elimdeki hiçbir OS sorun göstermedi. Bazı router’ların paketin arkasına metadata saklamak için gerçekten uzun frame kullandığını bir yerde okumuştum.
    • Timestamp’ler ve başka tür in-band ağ telemetrisi de frame’lere trailer biçiminde eklenebiliyor. Bu durumda yeni bir FCS ekleniyor.
      Uygulama L2 verisini görmüyorsa Linux IP stack’i bunu sadece yok sayıyor.
  • DEC’in ilk SSI Ethernet kart setinden fiziksel olarak çok daha küçük: https://i.ebayimg.com/images/g/NEYAAOSw-mZlg0lZ/s-l1600.jpg
    DEC DEUNA kartları 1 feet’ten daha uzun ama işlevleri de çok daha fazla. DEUNA “gerçek” bir NIC; gönderme/alma kuyrukları var, bunları otonom biçimde işliyor ve DMA da yapıyor. Tabii kartın üzerinde bunları çalıştıran kendi PDP-11’i de var.

  • Gerçekten harika. Ne kadar sürdüğünü merak ediyorum.

    • Ağ modülünü yapmak yaklaşık bir ay sürdü, ama derleyici yazmak çok daha fazla zaman aldı.
  • Ağ portuna bağlı bir çipin içine backdoor yerleştirmenin ne kadar kolay olduğunu gösteriyor.

  • Communication Systems Engineering dersinde Ethernet sinyal işlemeyi uygulamıştık; ardından ARP ve switching dahil TCP/IP stack’ini Motorola 68k QUIC assembly ile uyguladık.
    Hayatımın en uzun 18 ayıydı.