Files
hdl-projects/roadmap.md
Jose Luis Montañes Ojados b3d11de769 add ram
2026-03-01 03:29:11 +01:00

371 lines
16 KiB
Markdown

# 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
- [x] Fase 2.4 — Register File
- [x] 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