Usar una tarjeta SD como almacenamiento de código para RISC-V YRV en FPGA Tang Nano 9K
En placas FPGA económicas como la Tang Nano 9K, los recursos limitados de síntesis restringen la RAM integrada, lo que hace inviable almacenar todo el código del programa en memoria de bloques. Solo se dispone de 16–20 KB para pila y datos: suficiente para pruebas básicas, pero no para firmware completo. Este artículo propone usar una tarjeta SD como memoria de solo lectura (ROM) externa, con lecturas sectoriales de 512 bytes —funcionalmente similar a la ejecución directa desde flash QSPI (XIP), pero adaptada a las limitaciones del protocolo SPI.
El núcleo YRV se conecta a la memoria mediante un bus de 16 bits. La lógica de lectura/escritura se implementa en Verilog:
always @ (posedge clk) begin
if (mem_trans[0]) begin
mem_rdata[31:24] <= mcu_mem_bank3 [mem_addr[13:2]];
mem_rdata[23:16] <= mcu_mem_bank2 [mem_addr[13:2]];
mem_rdata[15:8] <= mcu_mem_bank1 [mem_addr[13:2]];
mem_rdata[7:0] <= mcu_mem_bank0 [mem_addr[13:2]];
end
if (mem_wr_byte[3]) mcu_mem_bank3 [mem_addr_reg[13:2]] <= mem_wdata[31:24];
if (mem_wr_byte[2]) mcu_mem_bank2 [mem_addr_reg[13:2]] <= mem_wdata[23:16];
if (mem_wr_byte[1]) mcu_mem_bank1 [mem_addr_reg[13:2]] <= mem_wdata[15:8];
if (mem_wr_byte[0]) mcu_mem_bank0 [mem_addr_reg[13:2]] <= mem_wdata[7:0];
end
Máquina de estados para caché de sectores SD
La RAM se reduce a solo 512 bytes —un único sector—. Cuando la CPU accede a una dirección dentro del sector actualmente en caché, la ejecución continúa inmediatamente con mem_ready activado (análogo a HREADY en AHB-Lite). En caso de fallo de caché —es decir, al acceder fuera del sector actual—, la máquina de estados finita (FSM) desencadena una nueva lectura de sector desde la tarjeta SD. Un contador byte_cnt rastrea el progreso de la lectura.
La FSM incluye tres estados:
- IDLE: Espera una solicitud de acceso a memoria.
- READ: Lee el sector solicitado de 512 bytes desde la tarjeta SD.
- START: Inicia el comando de lectura (envía
rd).
El controlador SD se deriva de proyectos de código abierto como FPGA-SDcard-Reader y MIT 6.111, y luego se adapta a la interfaz SPI de la Tang Nano 9K (las líneas DAT1/DAT2 se dejan sin conectar). La inicialización se ejecuta a 400 kHz para garantizar robustez —no se requiere reinicio por ciclo de alimentación.
Integración en el proyecto YRV Plus
Implementado en labs/99_experimental/99_03_yrv_sd, construido sobre la base basics-graphics-music. La configuración tang_nano_9k_tm1638_sd asigna los pines SD a 3,3 V. La salida usa el controlador TM1638: port0 maneja los segmentos BCD; port1 selecciona la posición del dígito.
Ejemplo de código ensamblador para mostrar «HELO» (cada carácter almacenado en su propio sector):
li s0, 0xFFFF0000 # port0 — datos de segmentos
li s1, 0xFFFF0002 # port1 — posición del dígito
main_loop:
# 'H' (dígito 3)
li t1, 0x8
sh t1, 0(s1)
.rept 100
nop
.endr
li t2, 0b00110111
sh t2, 0(s0)
.rept 1000
nop
.endr
# Repetir de forma análoga para E, L, O
El firmware se escribe sector a sector usando HxD.
Resultados de las pruebas:
- Se ejecutan ~1.000 instrucciones NOP entre caracteres (representando carga real).
- «HELO» se muestra con claridad en cuatro dígitos de siete segmentos.
- Reinicio e inicialización fiables —sin necesidad de reiniciar la alimentación.
- Al retirar la tarjeta SD, la ejecución se detiene de forma limpia.
Conclusiones clave
- Las tarjetas SD en modo SPI permiten una ejecución tipo XIP cuando se combinan con una caché de 512 bytes por sector.
- La FSM minimiza la latencia;
mem_readymantiene sincronizada la CPU durante las lecturas. - Ideal para aplicaciones de bajo ancho de banda donde el tiempo de señal está en el rango de milisegundos.
- Para un rendimiento más fluido, considere aumentar la caché a 1–2 KB.
- Totalmente compatible con núcleos YRV en FPGAs Gowin —incluso sin SRAM externa.
Oportunidades de optimización
La implementación actual requiere 6+ ciclos de reloj por lectura de sector (frente a 6 en QSPI). Ampliar la caché a 2–4 sectores reduciría drásticamente los fallos de caché. También es factible implementar búferes multi-banco, replicando la lógica original de memoria de YRV. Para desarrolladores avanzados: integre esta solución con adaptadores Wishbone o AXI para escalarla en diseños SoC más grandes.
— Editorial Team
Aún no hay comentarios.