Nand to Tetris en español parte 1
Bienvenido querido lector a esta nueva publicación. Antes de empezar quiero comentarte que esto no es una traducción del contenido Nand to Tetris sino más bien mi recorrido a travez de este curso.
Ahora bien. en este curso tiene como finalidad construir un ordenador a partir de compuertas lógicas y posteriormente programaremos el mítico videojuego Tetris.
El curso propone hacerlo todo en un simulador, lo cual plantea un escenario práctico. Sin embargo como reto personal y pasión por el hardware he adquirido una FPGA básica para poder hacer la construcción en hardware real. Esto me plantea un reto adicional, aprender Verilog. Mas adelante te contaré su rol ese en el proceso.
En electrónica digital usamos compuertas lógicas como And, Or o Not. Para construir un circuito que cumpla con determinada función. Ahora bien para construir un ordenador necesitaríamos varias compuertas lógicas para este fin.

La imagen anterior nos deja ver un procesador de 8 bits, como se puede observar requiere bastante espacio.
El curso Nand to Tetris propone construir un procesador de 16 bits por lo que nuestro computador podría ser demasiado grande para hacerlo en protoboard. Además del hecho que gestionar todas estas conexiones se puede volver muy complicado y propenso a errores por interferencia y falsos contactos.
Aquí es donde la FPGA cobra sentido, la FPGA nos va permitir crear todas estas conexiones entre compuertas lógicas a nivel de código, hay que tener claro que esto no es una simulación, internamente la FPGA crea dichas conexiones reales entre compuertas lógicas. De hecho grandes compañías que diseñan chips como Apple o Nvidia hacen sus desarrollos en una FPGA para pode validar su diseño y hacer correcciones necesarias antes de su producción en masa.
Ahora bien, ¿cómo se programa una FPGA?. Muy sencillo con un lenguaje descriptivo tipo HDL "Hardware Description Language". Existen muchos lenguajes tipo HDL pero el más utilizado resulta ser Verilog y esta es la razón por lo que en paralelo a este curso iré aprendiendo Verilog
// implementacion compuerta NOT a partir de NAND en Verilog
module not_ntt (
input wire a,
output wire out
);
nand_ntt nand_0(.a(a), .b(a), .out(out));
endmodule
Este curso inicia explicando la Aritmetica booleana. que dicta como se realizan operaciones matemáticas entre números binarios. Además describe como un numero decimal se puede representar en binario y viceversa.
Uno de las demostraciones más importantes de esta primera parte explica como toda operacion boolena se puede realizar únicamente con compuertas NAND. Lo que sabemos hasta el momento es que toda operación booleana se puede representar con operaciones AND OR o NOT. Pero hay una forma de reducirlo únicamente a AND y NOT ya que según el teorema de Morgan
Not(A And B) = Not(A) Or Not(B)
pero si negamos A y B en la primera parte tendremos lo siguiente
Not(Not(A) And Not(B)) = Not(Not(A)) Or Not(Not(B))
y es que una doble negacion sobre un booleano es lo mismo que no tener ninguna negación. dejanodonos la sigueiente expresion
Not(Not(A) And Not(B))= A Or B

Ya hemos reducido en numero de compuertas a dos AND y NOT. pero todo lo podemos simplificar a una única compuerta la NAND
En este post no he ahondado mucho sobre la aritmetica booleana ni sus propiedades, pero si deseas saber más sobre este tema me lo pudes hacer saber en la sección de los comentarios.
A continuación te mostraré como a partir de la compuerta NAND podemos crear las demás compuertas. NOT, OR y AND.

En una FPGA no es necesario construir estas compuertas lógicas (OR, AND, NOT) pues son operaciones básicas, sin embargo para mantener el espíritu del programa Nand to Tetris crearé dichas compuertas junto con los bloques que se pueden construir a partir de ellas (Mux, DMux, etc...). toda esta información estará disponible en el repositorio de GitHub a continuación.
Nand to tetris propone usar un HDL simplificado que ellos mismos han elaborado con fines educativos, pero como te he mencionado anteriormente mi intención es construír este computador en hardware real y este lenguaje no esta soportado en la placa que he seleccionado.
Entremos entonces en el terreno de verilog para construir nuestras primeras compuertas.
Verilog tiene dos modos de operar, modo estructural y modo comportamental. De momento me centraré en el modo estructural, que es el que me permite definir cómo se construye un bloque.
Un modulo sirve para definir un bloque que implemente alguna funcionalidad en concreta.
module module_name (puertos)
endmodule
A su vez el módulo puede tener puertos, estos son como las conexiones físicas que tiene un circuito integrado. por ejemplo una compuerta AND tiene dos entradas y una salida.
Los puertos pueden tener distintos modos de operacion: entrada, salida o entrada y salida, además este puerto puede ser de dos tipos diferentes wire o reg. de momento únicamente me enfocaré en wire que es el tipo que representa una conexión
otra característica de los módulos es que internamente yo puedo implementar otros módulos. En un circuito real sería como crear un flip-flop a partir de compuertas lógicas, veamos esta característica construyendo una compuerta XOR en verilog.
module xor_ntt (
input wire a,
input wire b,
output wire out
);
or_ntt or_a_b(.a(a), .b(b));
nand_ntt nand_a_b(.a(a), .b(b));
and_ntt xor_a_b(.a(or_a_b.out), .b(nand_a_b.out), .out(out));
endmodule
Veamos paso a paso como esta estructurado este módulo
- definimos a y b como entradas y out como salida del módulo
- creamos una instancia del módulo or_ntt y les "conectamos" la entrada del módulo principal a la entrada de esta instancia. aquí or_ntt es el módulo que vamos a instanciar y or_a_b es el nombre que le he dado a esa instancia
- creamos una instancia nand_ntt y asignamos sus entradas y salidas
- creamos la última instancia una and_ntt y aquí tomamos la salida de los módulos anteriores como las entradas de esta última instancia y "conectamos" la salida de la instancia a la salida del módulo
Verilog tiene una herramienta llamada "Synthesis" que se encarga de traducir nuestro módulo en compuertas lógicas, de modo que al correr esta herramienta con nuestro módulo xor_ntt deberíamos ver algo así
Hasta el momento hemos visto como se define un módulo, y como se puede instanciar otros módulos dentro de el mismo. Ahora veremos otra característica que me ha resultado bastante útil al momento de diseñar nuevos módulos, esté es el bloque generativo, su funcionamiento es similar a un ciclo for pero a diferencia de un ciclo for condicional que ejecuta una tarea tras otra por una duración fija, el ciclo for de un bloque generativo crea múltiples instancias de un elemento.
Veamos este comportamiento creando otra de las compuertas lógicas requeridas en el programa Nand to tetris, esté es el código para una Not 16 (16 entradas y 16 salidas).
module not16_ntt (
input wire [15:0] in,
output wire [15:0] out
);
genvar i;
generate
for ( i =0 ; i<16 ; i = i + 1 ) begin: not16
not_ntt u_not(.a(in[i]), .out(out[i]));
end
endgenerate
endmodule
Lo primero que salta a la vista es la "palabra reservada" genvar que es un tipo de dato de verilog para ser usado únicamente con los bloques generativos for, al igual que un ciclo for tradicional tenemos la inicialización de la variable, la condición para terminar el ciclo y el incremento de la variable.
algo inusual es este fragmento begin: not16 aquí begin funciona como un corchete de apertura, que es lo que marca los límites del bloque generativo y lo que precede a : es el prefijo que lleva cada nuevo bloque instanciado.
Como hemos visto anteriormente Verilog lo que hace es describir como ordena y conectan diversos componentes de un circuito lógico, y algo que reuslta imprecindible para poder analizar si algo falla es poder identificar cada elemento presente en este circuito, es por ello que debemos nombrar cada instancia para dar un contexto de que es cada módulo

como podemos ver cada bloque tiene el prefijo "not16" seguido de su indice creado por el incremento de la variable "genvar i", todo esto antecede al nombre que le hemos definido a cada instancia "u_not".
Como siempre muchas gracias querido lector por acompañarme hasta aquí. Cualquier duda que te surja la puedes dejar en la sección de comentarios y haré lo posible para ayudarte, nos vemos en la siguiente entrega donde veremos otros módulos más "complejos" y te mostraré como podemos testear nuestro diseño con ayuda de herramientas como "iverilog"
Member discussion