# Verilog Roadmap: De Turing Complete a FPGA > Objetivo: traducir tu CPU custom de 16 bits (Turing Complete) a Verilog sintetizable > y montarla en la Tang Primer 20K (Gowin GW2A). > > Prerequisitos que ya tienes: > - Logica digital (puertas, muxes, flip-flops, FSMs) — via Turing Complete > - Arquitectura de CPU de 16 bits — tu CPU custom > - Lenguaje j-lang + compilador para tu CPU > - Toolchain: iverilog + vvp + gtkwave funcionando > - Primer ejercicio (half_adder) completado en 3 estilos > > Tu CPU (mycpu_v2): > - 16 registros de 16 bits (REG0-REG11 generales, REG12=RAM_VAL, REG13=RAM_ADDR, > REG14=PC, REG15=IN/OUT) > - Instrucciones de 64 bits: [OPCODE:16][PARAM1:16][PARAM2:16][TARGET:16] > - Modo de direccionamiento en bits 7-6 del opcode (reg/reg, imm/reg, reg/imm, imm/imm) > - 12 ops ALU (ADD,SUB,MUL,DIV,AND,OR,NOT,NAND,NOR,XOR,XNOR,NEG) > - 6 condicionales (EQ,NEQ,LS,LSE,GR,GRE) — branch si condicion true > - Stack: PUSH, POP, RSTR > - Funciones: CALL, RET, HALT > - ISA completo en: D:\Proyectos\c-labs\j-lang\mycpu_v2.md > > FPGA target: Tang Primer 20K (Gowin GW2A-LV18PG256C8/I7) > - 20736 LUT4 | 15552 FF | 828K BSRAM | 48 multiplicadores 18x18 > - 128MB DDR3 | 32Mbit Flash | JTAG+UART debug | 117 IO --- ## Fase 1: Bloques combinacionales fundamentales Cosas que ya conoces del juego, ahora las escribes en Verilog. Cada ejercicio: modulo + testbench + simulacion con gtkwave. ### 1.1 Full Adder - **Carpeta:** `full_adder/` - **Objetivo:** Combinar dos half adders para hacer un full adder (con carry-in) - **Practica:** Hacerlo structural (instanciando tu half_adder) y dataflow - **Concepto Verilog:** instanciacion de modulos, jerarquia ### 1.2 Adder de N bits (ripple carry) - **Carpeta:** `adder_nbit/` - **Objetivo:** Sumador parametrizable de N bits usando `generate` y `parameter` - **Practica:** Parametrizarlo a 4, 8 y 16 bits desde el testbench - **Concepto Verilog:** `parameter`, `generate for`, buses `[N-1:0]` ### 1.3 ALU basica - **Carpeta:** `alu_basic/` - **Objetivo:** ALU de 16 bits con las 12 operaciones de tu CPU: ADD, SUB, MUL, DIV, AND, OR, NOT, NAND, NOR, XOR, XNOR, NEG - **Practica:** Selector de operacion de 4 bits (opcode[3:0]), flags (zero, carry, negative) - **Concepto Verilog:** `case`, operadores aritmeticos, concatenacion `{}` - **Conexion directa:** Este modulo es tu `alu.v` final casi tal cual ### 1.4 Multiplexor y Demultiplexor - **Carpeta:** `mux_demux/` - **Objetivo:** Mux 2:1, 4:1 y 8:1 parametrizables. Demux 1:4 - **Practica:** Mux con `assign` ternario vs `case` vs `generate` - **Concepto Verilog:** operador ternario `? :`, seleccion por bits ### 1.5 Decoder / Encoder - **Carpeta:** `decoder_encoder/` - **Objetivo:** Decoder 3:8, encoder con prioridad - **Practica:** Usar `casez` para don't cares - **Concepto Verilog:** `casez`, `casex`, wildcards --- ## Fase 2: Bloques secuenciales Aqui entran los flip-flops y el clock. Es el salto mas importante desde la logica combinacional. ### 2.1 Flip-Flop D y registro - **Carpeta:** `flip_flop/` - **Objetivo:** FF-D con reset sincrono y asincrono, enable - **Practica:** Registro de N bits parametrizable - **Concepto Verilog:** `always @(posedge clk)`, `posedge`/`negedge`, blocking vs non-blocking (`=` vs `<=`) > **CRITICO:** En logica secuencial SIEMPRE usa `<=` (non-blocking). > En combinacional usa `=` (blocking). Mezclarlos es el error #1 de Verilog. ### 2.2 Contador - **Carpeta:** `counter/` - **Objetivo:** Contador up/down de N bits con enable, load y reset - **Practica:** Hacerlo parametrizable, probarlo a distintas frecuencias - **Concepto Verilog:** Aritmetica en always secuencial, overflow ### 2.3 Shift Register - **Carpeta:** `shift_register/` - **Objetivo:** Registro de desplazamiento con carga paralela, shift left/right - **Practica:** SISO, SIPO, PISO, PIPO - **Concepto Verilog:** Operadores de shift en contexto secuencial ### 2.4 Register File - **Carpeta:** `register_file/` - **Objetivo:** Banco de 16 registros de 16 bits (como tu CPU: REG0-REG15), 2 puertos de lectura simultanea, 1 de escritura - **Practica:** Lectura combinacional, escritura sincrona. Registros especiales (REG12=RAM_VAL, REG13=RAM_ADDR, REG14=PC, REG15=IO) pueden tener logica extra - **Concepto Verilog:** Arreglos `reg [15:0] regs [0:15]`, indexado por variable de 4 bits --- ## Fase 3: Memorias ### 3.1 RAM sincrona - **Carpeta:** `ram/` - **Objetivo:** RAM de 256x16 (256 posiciones de 16 bits), lectura/escritura sincrona - **Practica:** Single port y dual port - **Concepto Verilog:** Inferencia de block RAM, `$readmemh` para cargar datos iniciales ### 3.2 ROM - **Carpeta:** `rom/` - **Objetivo:** ROM de 256x16 con contenido inicializado desde archivo .hex - **Practica:** Cargar un programa simple en hex y leerlo secuencialmente - **Concepto Verilog:** `$readmemh`, `initial begin` - **Conexion con tu proyecto:** Aqui eventualmente cargaras el binario que genera tu compilador j-lang ### 3.3 Stack (LIFO) - **Carpeta:** `stack/` - **Objetivo:** Stack de 256 posiciones de 16 bits con PUSH, POP y RSTR (reset) - **Practica:** Puntero de stack, deteccion de overflow/underflow - **Conexion directa:** Tu CPU usa PUSH (0x19/0x59), POP (0x1A), RSTR (0x18) y CALL/RET que tambien usan el stack para guardar/restaurar el PC --- ## Fase 4: Maquinas de estados finitos (FSM) Ya las conoces de Turing Complete. Ahora las codificas formalmente. ### 4.1 FSM basica — Semaforo - **Carpeta:** `fsm_traffic/` - **Objetivo:** Semaforo con estados RED, GREEN, YELLOW y temporizador - **Practica:** FSM con patron de 2 always (estado_actual + logica_siguiente) - **Concepto Verilog:** `localparam` para estados, el patron clasico de FSM ### 4.2 FSM — Controlador UART TX (simplificado) - **Carpeta:** `uart_tx/` - **Objetivo:** Transmitir un byte por UART (start bit, 8 data, stop bit) - **Practica:** Baud rate configurable con counter, FSM de transmision - **Concepto:** FSM real que usaras para debug en FPGA --- ## Fase 5: Pipeline y CPU simples Aqui empiezas a armar las piezas en subsistemas tipo CPU. ### 5.1 Fetch unit - **Carpeta:** `fetch_unit/` - **Objetivo:** Program Counter + ROM = fetch de instrucciones - **Practica:** PC con increment, load (para jumps), reset - **Conexion:** Este es el primer pedazo real de tu CPU ### 5.2 Decoder de instrucciones - **Carpeta:** `instruction_decoder/` - **Objetivo:** Tomar una instruccion de 64 bits y extraer: - `opcode[7:0]` — operacion + tipo de grupo - `opcode[7:6]` — modo de direccionamiento (00=R/R, 01=I/R, 10=R/I, 11=I/I) - `opcode[5:4]` — grupo (00=ALU, 01=COND, 01=CTRL, 10=FUNC) - `opcode[3:0]` — operacion dentro del grupo - `param1[15:0]` — registro o inmediato segun modo - `param2[15:0]` — registro o inmediato segun modo - `target[15:0]` — registro destino o direccion de salto - **Practica:** Generar senales de control: alu_op, reg_write, mem_read, mem_write, branch, stack_op, is_immediate1, is_immediate2 - **Conexion:** Mapeo directo del ISA de mycpu_v2 ### 5.3 CPU single-cycle minima - **Carpeta:** `cpu_minimal/` - **Objetivo:** CPU simplificada que ejecuta un subconjunto de tu ISA: ADD (reg,imm), SUB, AND, OR — EQ, NEQ — PUSH, POP — CALL, RET, HALT - **Practica:** Conectar fetch + decode + ALU + register file + RAM + stack - **Concepto:** Datapath + Control Unit, todo single-cycle - **Nota:** Tu instruccion es de 64 bits (4 words), asi que el fetch necesita 4 ciclos de lectura de ROM o una ROM con palabra de 64 bits ### 5.4 Programa de prueba - **Objetivo:** Escribir a mano un programa en hex que sume numeros del 1 al 10 - **Practica:** Cargarlo en la ROM con `$readmemh`, simular, verificar resultado - **Conexion:** Eventualmente esto lo hara tu compilador j-lang automaticamente --- ## Fase 6: TU CPU de Turing Complete en Verilog Aqui es donde todo converge. ### 6.1 Documentar tu ISA (ya tienes mycpu_v2.md — completar) - **Carpeta:** `tc_cpu/docs/` - **Base:** Ya tienes mycpu_v2.md con registros, opcodes y modos - **Que falta agregar para Verilog:** - Diagrama de bloques del datapath (que se conecta con que) - Ciclo de ejecucion: fetch (4 words) -> decode -> execute -> writeback - Como funciona el acceso a RAM (escribir REG13=addr, leer/escribir REG12=data) - Comportamiento exacto de CALL (push PC al stack, jump) y RET (pop PC del stack) - Comportamiento de RSTR (reset del stack pointer?) - Que pasa con flags/condiciones: se compara en el mismo ciclo? - Senales de I/O via REG15: protocolo? ### 6.2 Implementar modulos individuales - **Carpeta:** `tc_cpu/rtl/` - **Objetivo:** Cada bloque testeado independientemente - **Modulos concretos de tu CPU:** | Modulo | Archivo | Descripcion | | -------------------- | -------------------- | --------------------------------------------------- | | ALU | `alu.v` | 12 ops (ADD-NEG), entradas 16bit, flags Z/C/N | | Register File | `register_file.v` | 16 regs x 16bit, 2 read + 1 write, regs especiales | | Program Counter | `pc.v` | Increment por 4 words, load para branch/CALL | | Instruction Memory | `imem.v` | ROM de 64 bits de ancho (o 4 lecturas de 16 bits) | | Data Memory (RAM) | `dmem.v` | RAM 16 bit, interfaz via REG12/REG13 | | Stack | `stack.v` | PUSH/POP/RSTR, para datos y CALL/RET | | Control Unit | `control_unit.v` | Decode opcode[7:0] -> senales de control | | Mux de operandos | `operand_mux.v` | Selecciona reg o inmediato segun opcode[7:6] | | Comparador | `comparator.v` | EQ/NEQ/LS/LSE/GR/GRE para branches | | I/O Controller | `io_controller.v` | Interfaz REG15 con el mundo exterior | ### 6.3 Integrar el top-level - **Archivo:** `tc_cpu/rtl/cpu_top.v` - **Objetivo:** Conectar todos los modulos - **Ciclo de ejecucion probable (multi-ciclo o FSM):** 1. **FETCH:** Leer 4 words consecutivos de IMEM -> registro de instruccion de 64 bits 2. **DECODE:** Extraer opcode, modo, param1, param2, target. Generar senales de control 3. **EXECUTE:** ALU o comparacion. Resolver operandos (reg/imm mux) 4. **MEMORY:** Si opcode accede a RAM (via REG12/REG13), hacer read/write 5. **WRITEBACK:** Escribir resultado en registro target - **Nota:** Como tus instrucciones son de 64 bits, probablemente sea multi-ciclo (4 ciclos de fetch + 1-2 de execute). Alternativa: ROM de 64 bits de ancho ### 6.4 Compilar j-lang -> hex -> simular en tu CPU Verilog - **Objetivo:** Cerrar el loop completo - **Flujo:** `codigo.j` -> compilador j-lang -> `.hex` -> `$readmemh` -> simulacion iverilog - **Test sugerido:** Un programa j-lang simple (fibonacci, factorial, algo con loops) - **Verificacion:** Comparar traza de ejecucion en gtkwave vs ejecucion en Turing Complete - **Esto es el momento "wow":** el mismo programa que corria en Turing Complete ahora corre en tu CPU Verilog --- ## Fase 7: FPGA — Tang Primer 20K Tu placa: **Tang Primer 20K** con Gowin GW2A-LV18PG256C8/I7. ### Recursos de tu FPGA vs lo que necesita tu CPU ``` Recurso | Disponible | Tu CPU necesita (estimado) -----------------|---------------|--------------------------- LUT4 | 20,736 | ~2,000-4,000 (sobra mucho) Flip-Flops | 15,552 | ~500-1,000 BSRAM (828Kb) | 46 bloques | ~2-4 bloques (IMEM + DMEM) Multiplicador | 48 (18x18) | 1 (para MUL de 16 bits) PLL | 4 | 1 (para generar tu clock) IO | 117 | ~20-30 (UART, LEDs, botones) ``` Te sobra FPGA para tu CPU. Podrias meter varias instancias si quisieras. ### 7.1 Instalar Gowin EDA y primer proyecto - **Toolchain:** Gowin EDA (Education Edition es gratuita) - Descarga: https://www.gowinsemi.com (necesitas registro) - Incluye: Gowin Synthesizer, Place & Route, Programmer - **Primer proyecto:** Blink LED - Aprender: crear proyecto, seleccionar chip GW2A-LV18PG256C8/I7 - Constraints (.cst): asignar pines fisicos a senales - Clock: la placa tiene un oscilador de 27 MHz, usar PLL para tu frecuencia - Flujo: Synthesis -> Place & Route -> Generate Bitstream -> Program - **Dock vs Lite:** Dependiendo de que placa de expansion uses, los pines cambian. El Dock trae mas LEDs, botones y conectores ### 7.2 Adaptar tu CPU para sintesis - **Cambios necesarios:** - Quitar todo `$display`, `$dumpfile`, `$dumpvars`, `#delays` (solo simulacion) - IMEM: Usar inicializacion de BSRAM de Gowin (soporta `$readmemh` en sintesis para block RAM, pero verificar con Gowin Synthesizer) - DMEM: Inferir BSRAM (el synthesizer lo detecta si sigues el patron correcto) - MUL: Gowin puede mapear `*` a los multiplicadores DSP hardware automaticamente - Clock: Usar la PLL Gowin (PLLVR primitive) para generar un clock limpio - Reset: Usar el boton de la placa como reset global (con debouncing) ### 7.3 Agregar I/O real - **Basico (debug):** - LEDs de la placa -> mostrar estado/registro/flags - Botones -> reset, step (ejecutar instruccion a instruccion), run - 7-segmentos (si hay en el dock) -> mostrar PC o valor de registro - **UART (esencial para debug):** - Ya la habras hecho en Fase 4.2, conectarla al REG15 (IN/OUT) - Permite enviar/recibir datos entre PC y tu CPU via terminal serial - Debug: imprimir valor de registros, estado del PC, etc. - **Avanzado (futuro):** - LCD via el conector de 8 pines - Tarjeta SD para cargar programas sin resintesis - DDR3 para expandir RAM (requiere controlador DDR3, mas complejo) ### 7.4 Meta final - Tu CPU ejecutando un programa compilado con j-lang en hardware real - Poder cargar programas y ver resultados via UART desde tu PC - El loop completo: `codigo.j` -> compilador -> `.hex` -> FPGA -> resultados reales --- ## Consejos generales ### Estructura de cada ejercicio ``` nombre_ejercicio/ nombre.v # modulo principal nombre_tb.v # testbench readme.md # notas, comandos, que aprendiste ``` ### Comandos (ya los conoces) ```bash iverilog -o modulo_tb.vvp modulo_tb.v vvp modulo_tb.vvp gtkwave modulo.vcd ``` ### Errores comunes a evitar 1. **Blocking vs non-blocking:** `=` en combinacional, `<=` en secuencial. Siempre. 2. **Latch inferido:** Si usas `always @(*)` con `if` sin `else`, Verilog infiere un latch. Siempre pon `else` o valores por defecto. 3. **Sensitivity list incompleta:** Usa `always @(*)` para combinacional, nunca listes senales manualmente (es error-prone). 4. **Senales no inicializadas en testbench:** Todo `reg` empieza en `x` si no lo inicializas. 5. **Confusion wire vs reg:** `wire` para assign y conexiones, `reg` para always blocks. ### Recursos recomendados - **HDLBits** (hdlbits.01xz.net) — Ejercicios interactivos de Verilog online. Excelente complemento para practicar sintaxis. - **"Digital Design and Computer Architecture"** (Harris & Harris) — El libro de referencia para ir de compuertas a CPU. Tiene ejemplos en Verilog. - **Nand2Tetris** — Ya hiciste algo similar con Turing Complete, pero el libro tiene buena formalizacion del ISA y el compilador. --- ## Progreso - [x] Fase 1.0 — Half Adder (3 estilos + testbench) - [x] Fase 1.1 — Full Adder - [x] Fase 1.2 — Adder N bits - [x] Fase 1.3 — ALU basica - [x] Fase 1.4 — Mux / Demux - [x] Fase 1.5 — Decoder / Encoder - [x] Fase 2.1 — Flip-Flop D y registro - [x] Fase 2.2 — Contador - [x] Fase 2.3 — Shift Register - [ ] Fase 2.4 — Register File - [ ] Fase 3.1 — RAM sincrona - [ ] Fase 3.2 — ROM - [ ] Fase 3.3 — Stack - [ ] Fase 4.1 — FSM Semaforo - [ ] Fase 4.2 — UART TX - [ ] Fase 5.1 — Fetch Unit - [ ] Fase 5.2 — Decoder de instrucciones - [ ] Fase 5.3 — CPU single-cycle minima - [ ] Fase 5.4 — Programa de prueba - [ ] Fase 6.1 — Documentar ISA de tu CPU - [ ] Fase 6.2 — Modulos individuales de tu CPU - [ ] Fase 6.3 — Top-level integrado - [ ] Fase 6.4 — j-lang -> hex -> simulacion - [ ] Fase 7.1 — Gowin EDA + blink LED en Tang Primer 20K - [ ] Fase 7.2 — CPU sintetizable (quitar sim-only, inferir BSRAM, PLL) - [ ] Fase 7.3 — I/O real (LEDs, botones, UART) - [ ] Fase 7.4 — j-lang corriendo en hardware real