- Es un relato de construcción de hardware que agrega funciones de transmisión y recepción Ethernet 10BASE-T a una computadora de lógica discreta, hecha sin CPU comercial ni chip de red dedicado.
- Sobre un adaptador de capa física 10BASE-T↔SPI creado anteriormente, se añadió un módulo de capa MAC para conectarlo con una homebrew computer, y el transmisor y el receptor se configuraron como una estructura full-duplex independiente.
- El receptor convierte datos SPI en bytes, los almacena en 2 kB de SRAM y revisa en hardware los primeros 6 bytes para aceptar solo
FE:FA:F6:F2:EE:EAo la MAC de broadcast. - Para simplificar el circuito, el transmisor deja la generación de FCS y la preparación del preámbulo al software, y solo admite tramas de longitud fija de 1024 bytes.
- También se creó un compilador de C capaz de compilar uIP 1.0 para ejecutar apps de red; el resultado fue un ping promedio de 85 ms y descargas de archivos estáticos por HTTP de 2.6 kB/s.
Agregar Ethernet a una computadora de lógica discreta
- Como extensión del trabajo de crear un sistema de computadora completo con componentes de lógica discreta, se implementó un adaptador Ethernet capaz de ejecutar aplicaciones de red.
- Anteriormente se había creado un adaptador de capa física que convierte señales Ethernet 10BASE-T a SPI y viceversa, y en ese momento se usó un microcontrolador STM32 para las pruebas de funcionamiento.
- El núcleo de este trabajo es el módulo de capa MAC para conectar ese adaptador a la homebrew computer.
- El adaptador tiene una estructura full-duplex, y las partes de transmisión y recepción funcionan de manera independiente.
Receptor: guardar datos SPI en un búfer de tramas
- El receptor convierte los datos seriales SPI en datos paralelos por byte y extrae el reloj de bytes.
- La dirección MAC de destino se revisa en los primeros 6 bytes, y las tramas que no cumplen el criterio se rechazan.
- Los bytes recibidos se escriben en un búfer SRAM 6116 de 2 kB.
- Cuando termina la trama, el receptor se desactiva y no recibe tramas adicionales hasta que se vuelva a activar.
- El contador de bytes mantiene su valor incluso después de detenerse, para que la CPU pueda leer la longitud recibida.
- El FCS no se verifica en hardware.
-
Captura de datos y acceso al búfer
- Los datos seriales SPI entran al registro de desplazamiento
U32, yU30yU31cuentan bits y bytes, respectivamente. - El flip-flop D
U29Bgenera la señal de escritura de SRAMrecv_buf_we, y esta señal baja brevemente cada 8 bits de datos de entrada. - Los bytes recibidos se escriben en la SRAM 6116
U20. U13,U16yU18forman un multiplexor de direcciones que elige como entrada de dirección de la SRAM el contador de bytes o el bus de direcciones del sistema.U21actúa como búfer de tres estados que entrega los bytes recibidos a la RAM.- La RAM y el contador de bytes se conectan al bus de datos del sistema para que la CPU pueda acceder a los datos recibidos y a su longitud.
U25conecta la RAM de recepción al bus de datos del sistema.- Después de completarse la trama, el valor del contador de bytes se mantiene en el bus
recv_byte_cnt. U26yU27entregan este valor al bus de datos del sistema cuando llega una solicitud de lectura a una dirección específica.- La otra mitad de
U27crea un registro de estado de solo lectura de 2 bits para consultar el estado del receptor y del transmisor.
- Los datos seriales SPI entran al registro de desplazamiento
Filtrado de direcciones MAC en hardware
- Al analizar el tráfico Ethernet, se observó que las tramas generalmente llegaban en pequeños grupos de 3 a 4 unidades, separados por retardos breves, y que incluso dentro de un mismo grupo a menudo tenían direcciones MAC de destino distintas.
- Como la computadora podría no ser lo bastante rápida para hacer filtrado MAC por software y reactivar el receptor, se necesitaba filtrado MAC en hardware.
- Se descartó el método de guardar una dirección MAC personalizada y compararla con los primeros 6 bytes porque era demasiado complejo.
- También era posible usar una dirección MAC formada por un único byte repetido, pero finalmente se creó la dirección MAC como función del índice de byte:
- el bit 0 queda fijo en 0
- el bit 1 queda fijo en 1
- los bits 2~4 son el valor invertido del índice de byte
- los bits 5~7 quedan fijos en 1
- La dirección MAC generada con esta regla es
FE:FA:F6:F2:EE:EA. - Para que funcione ARP, también se acepta la MAC de broadcast
FF:FF:FF:FF:FF:FF. U33compara si el bit 0 y los bits 2~4 de los datos coinciden con los valores deseados, y la salida deU34Ase pone en alto cuando esos bits coinciden.U35Aimplementa la comprobación de la MAC de broadcast, y su salida se pone en alto cuando el bit 0 y los bits 2~4 son todos 1.- Las dos señales se combinan con un OR de diodos usando
D7yR6, yU35Bverifica que todos los bits restantes sean 1. - El resultado de validez de un byte individual se acumula en
U10A:- cuando no se está recibiendo una trama, la señal incoming SPI slave select
ssestá baja yU10Ase fija en 1 - durante la recepción de una trama, el valor se actualiza con cada byte recibido
- si la dirección MAC de destino cumple el criterio, el valor de
U10Ase mantiene en alto - cuando la dirección de byte llega a 5, el valor final se enclava en
U36B, y si la dirección de destino no coincide se bloquea la recepción de la trama
- cuando no se está recibiendo una trama, la señal incoming SPI slave select
Transmisor: simplificar el circuito con tramas de longitud fija
- Al igual que el receptor, el transmisor no implementa la generación de FCS en hardware, sino que la maneja por software.
- Para reducir el circuito, el transmisor solo admite tramas de longitud fija.
- La longitud de trama elegida fue de 1024 bytes, un valor cercano al MTU típico de 1500 bytes.
- El preámbulo necesario para 10BASE-T está compuesto por varios
0x55y un0xD5final, y el software debe cargarlo también dentro de esos 1024 bytes. - La longitud fija de trama no afecta a los protocolos superiores:
- los protocolos superiores codifican el tamaño del paquete en la cabecera
- no dependen de la longitud real de la trama Ethernet
-
Flujo de datos de transmisión
- Los datos de transmisión se almacenan en SRAM.
- Un reloj de 20 MHz entra a un contador de 4 bits, y la salida de overflow se usa como reloj de bytes.
- Al escribir un valor en una ubicación de memoria específica de solo escritura, el contador se activa y comienza la transmisión de la trama.
- Los datos paralelos por byte se serializan mediante un registro de desplazamiento.
- Igual que en el receptor,
U12cuenta los bits yU14cuenta los bytes. - El reloj de 20 MHz proviene de un oscilador integrado, y no se usa directamente, sino como mínimo dividido por 2.
- Este método evita que el ciclo de trabajo del oscilador afecte a la señal de salida.
-
RAM, registro de desplazamiento y temporización
- Para seleccionar la entrada de dirección de la RAM
U22, se usan tres multiplexores 74HC157 como en el receptor. U23se usa para cargar datos en la RAM.U24actúa como almacenamiento intermedio del byte que se está transmitiendo actualmente.- El contador de bytes 74HC4040 es un contador ripple, por lo que tarda en estabilizarse.
- Mientras la salida de la RAM aún no es válida,
U24entrega una salida estable. - Los datos entran al registro de desplazamiento
U28y se desplazan bit a bit. - Hubo un bug de hardware por conectar mal el orden de bits que va de la RAM al registro de desplazamiento, por lo que hubo que sortearlo mezclando bits en software.
MOSIySCKdeben estar sincronizados con precisión para generar una buena señal 10BASE-T.U11AyU8Bse encargan de esta sincronización.tx_cnt0es el bit 0 del contador de bits, y se usa como reloj con una señal de 20 MHz dividida por 2.U11Acambia su salida de acuerdo con esta señal.U8Bretrasa el reloj para ajustarlo al retardo generado porU11A.- Un latch D es más complejo que una compuerta AND simple y tiene unos 5 ns más de retardo, por lo que se usa el 74LV74A, más rápido.
- El 74LV74A es el único chip de una familia rápida en esta placa.
- Para seleccionar la entrada de dirección de la RAM
Interfaz de CPU y mapeo de memoria
- Desde el punto de vista del programador, el adaptador Ethernet se ve como una interfaz mapeada en memoria.
- Los dos búferes de tramas se mapean en
0xF000. - Hay dos registros de solo lectura:
- el registro de estado de 8 bits en
0xFB00tiene las banderasRX_FULLyTX_BUSY RX_FULLindica que la recepción de una trama se completóTX_BUSYindica que se está transmitiendo una trama- el registro de 16 bits en
0xFB02contiene la longitud de los datos recibidos
- el registro de estado de 8 bits en
- Las operaciones de escritura se usan como comandos de control:
- escribir cualquier valor en
0xFB00vuelve a activar el receptor - escribir cualquier valor en
0xFB01inicia la transmisión
- escribir cualquier valor en
- Como la CPU no soporta interrupciones, no hay interrupciones.
- Todas las direcciones relacionadas comienzan con
F, donde los 4 bits superiores son 1; esta condición la verificaU2A. - La dirección del búfer requiere que el bit 11 sea 0, y
U1D,D2,R2yU1Elo verifican. - La dirección de los registros requiere que el segundo dígito hexadecimal sea
B, es decir1011, yU1ByU2Blo comprueban. - Los decodificadores
U4AyU4Bse usan para seleccionar funciones individuales. - Dos LED indican accesos al búfer o a los registros.
Programación y rendimiento
- Aunque se quería soporte de red, no se quería implementar directamente un stack TCP/IP, y programar en ensamblador era incómodo, así que se creó un compilador de C.
- Este compilador es lo bastante maduro como para compilar uIP 1.0, una pequeña biblioteca TCP/IP.
- La densidad de código de la CPU es muy baja, pero uIP cabe en la RAM y todavía queda espacio para aplicaciones reales.
- El rendimiento de red es bajo, pero es un resultado logrado sin CPU comercial ni chips especiales:
- promedio de ida y vuelta de ping: 85 ms
- velocidad de descarga del servidor HTTP: 2.6 kB/s
- el servidor HTTP ofrece archivos estáticos desde una tarjeta SD
- Los modelos, archivos de esquemáticos y diseños de PCB están en el repositorio de GitHub.
1 comentarios
Opiniones en Hacker News
Excelente trabajo, gracias por compartirlo. Me gustó especialmente el stack trace del proceso de razonamiento, y es muy educativo que haya resuelto muchas cosas desde primeros principios o haya intentado explicarlas desde la perspectiva de alguien principiante.
Aunque no sea práctico para redes reales, no creo que sea solo un juguete. En una época en la que se descubren backdoors en chips de red excesivamente complejos, en el futuro podría atraer a un público más serio o dar lugar a motivaciones de proyecto más serias.
Esto es para una computadora totalmente personalizada, así que ya de por sí es mucho más impresionante, y ni hablar de la parte de “entonces hice un compilador de C”. Aun así, me da curiosidad cuál sería la implementación mínima de una tarjeta Ethernet para una PC “normal”.
Supongo que buena parte sería parecida, y el checksum podría dejarse en manos del CPU de la PC. La conexión tendría que ser serial cruda o, de forma más práctica, USB, y al final habría que usar un driver “real” o pasarlo al espacio de usuario para procesarlo.
Al ver cosas similares pensé que, si el dispositivo implementara https://en.wikipedia.org/wiki/USB_communications_device_clas..., tal vez podría “simplemente funcionar” sin un driver propio, pero eso no parece encajar muy bien con hacer todos los checksums del lado del host.
Buscando también encontré https://en.wikipedia.org/wiki/Ethernet_over_USB, aunque no sé si eso significa que se podría hacer un adaptador que solo convierta la conexión física a USB y deje que la computadora se encargue del resto.
Tal vez se pueda convencer a un dispositivo USB estilo FTDI de hacer bit-banging de Ethernet 10base2. Sería implementar solo la parte “PHY” que convierte el tráfico de la línea en un flujo de bits limpio y alinea el inicio de la trama, y que la PC haga todo lo demás por software.
En el caso de USB, CDC-NCM en sí no es difícil de implementar en cualquier MCU, pero implementar un PHY USB HS en la práctica requiere hardware ASIC.
Usando un PHY USB HS ULPI de 0.30 dólares, debería ser bastante fácil implementar USB CDC-NCM en una FPGA.
Al final hay un enlace al compilador de C creado para este proyecto: https://github.com/imihajlow/ccpu-cc
Parece que también tiene linker y libc. No sé bien qué tan complejo es el diseño de hardware, pero es impresionante que haya hecho y conectado “ligeramente” un compilador de C.
Realmente impresionante. Me dan ganas de intentar hacer un proyecto así, y admiro la pasión y las incontables horas invertidas en entender el sistema y luego construirlo.
No es que esté esperando jubilarme, pero tal vez para entonces termine dedicando tiempo a este tipo de proyectos de hardware y software.
Entonces, ¿es mejor o peor que una Etherlink 3c501? :-D
https://mirror.math.princeton.edu/pub/oldlinux/Linux.old/net...
Si mal no recuerdo, un paquete nuevo que entraba por la red sobrescribía el buffer que el CPU estaba intentando leer. La usé durante un tiempo en Linux y el rendimiento era realmente malo.
Es sorprendente cuánto nos ocultan de la vista los drivers y las actualizaciones de firmware.
La mía es mejor porque tiene dos buffers :) Aunque aun así solo conserva una trama recibida.
Me pareció interesante la parte que dice: “Fijar la longitud de la trama no afecta a los protocolos superiores, porque estos codifican el tamaño del paquete en sus headers y no dependen de la longitud real de la trama Ethernet”.
Hace poco hice un decodificador de paquetes, y en cada capa validé explícitamente que la longitud de la capa inferior coincidiera. En el caso de IP, en mi decodificador la longitud del datagrama IP tiene que coincidir exactamente con la longitud de la trama Ethernet y la longitud del header de la capa de enlace.
No fue por ser meticuloso, sino para detectar tramas cortas, y después decidí tratar también las tramas largas como error. El autor usa uIP, pero me da curiosidad cómo lo manejan Linux u otros sistemas operativos modernos. También me pregunto si hizo pruebas de interoperabilidad.
Si la aplicación no ve los datos L2, el stack IP de Linux simplemente los ignora.
Es físicamente mucho más pequeña que el primer conjunto de tarjetas Ethernet SSI de DEC: https://i.ebayimg.com/images/g/NEYAAOSw-mZlg0lZ/s-l1600.jpg
Las placas DEC DEUNA miden más de un pie de largo, pero también tienen muchas más funciones. DEUNA es una NIC “real”: tiene colas de transmisión y recepción, las maneja de forma autónoma y también hace DMA. Claro que además tiene su propio PDP-11 en la tarjeta para ejecutar eso.
Muy bueno. Me pregunto cuánto tiempo le tomó.
Muestra lo fácil que es meter un backdoor dentro de un chip conectado al puerto de red.
En el curso de Communication Systems Engineering implementé procesamiento de señales Ethernet y luego implementé un stack TCP/IP, incluyendo ARP y switching, en ensamblador Motorola 68k QUIC.
Fueron los 18 meses más largos de mi vida.