Verificación de Hardware con Python: UVM y cocotb
Published:
¿Qué es la Verificación de Hardware?
La verificación de hardware es un proceso crítico en el diseño de circuitos digitales que asegura que un diseño funcione correctamente antes de su fabricación. Este proceso tradicionalmente se realizaba con lenguajes como SystemVerilog y metodologías como UVM (Universal Verification Methodology). Sin embargo, con la llegada de herramientas como cocotb, ahora es posible realizar verificación de hardware utilizando Python, lo que hace el proceso más accesible y productivo.
¿Qué es cocotb?
cocotb (Coroutine-based Cosimulation Testbench) es un framework de verificación de hardware que permite escribir testbenches en Python. Funciona como una interfaz entre Python y simuladores de Verilog/SystemVerilog como Icarus Verilog, Verilator, o ModelSim.
Ventajas de cocotb:
- Python: Aprovecha todo el ecosistema de Python (pandas, numpy, matplotlib, etc.)
- Productividad: Escribir tests en Python es más rápido que en SystemVerilog
- Accesibilidad: Python es más fácil de aprender que SystemVerilog
- Reutilización: Puedes usar bibliotecas existentes de Python para análisis y visualización
- Coroutines: Soporte nativo para operaciones asíncronas
Proyecto: verification_uvm_cocotb
El repositorio verification_uvm_cocotb es una colección educativa de ejercicios y proyectos para aprender verificación de hardware utilizando Python con cocotb y UVM (pyuvm).
Dependencias del Proyecto
El proyecto requiere las siguientes bibliotecas de Python:
- cocotb (>=2.0.1): Framework principal para simulación y verificación
- pandas (>=2.3.3): Manejo y análisis de datos
- pytest (>=9.0.2): Framework para pruebas unitarias
- pyuvm (>=4.0.1): Implementación de UVM en Python
Puedes instalarlas usando:
uv sync
# o
pip install -e .
Herramientas del Sistema
Además de las bibliotecas de Python, necesitas:
- Icarus Verilog (iverilog): Simulador de Verilog open-source
- GTKWave: Visor de waveforms para analizar señales
En Ubuntu/Debian:
sudo apt-get install iverilog gtkwave
Estructura del Proyecto
main.py
Script principal que verifica la instalación de todas las dependencias necesarias:
def check_essential_libraries():
libraries = ["cocotb", "pandas", "pytest", "pyuvm"]
all_present = True
for lib in libraries:
try:
__import__(lib)
print(f"{lib} is installed.")
except ImportError:
print(f"{lib} is not installed.")
all_present = False
return all_present
Course_1: Fundamentos Python
Contiene módulos básicos para operaciones fundamentales:
v_binaries.py: Operaciones binarias básicas (AND, OR, XOR, NOT)v_binary_types.py: Utilidades para tipos de datos binariosv_decorator.py: Decoradores para funcionesv_loggin.py: Utilidades personalizadas de loggingv_random.py: Generación de números aleatoriosv_random_choice.py: Selección aleatoria de elementos
Course_2: Proyectos Prácticos con cocotb
Esta es la sección más importante, con proyectos completos de verificación:
1. Sumador de 4 bits (4bit_adder)
Verificación de un sumador básico:
@cocotb.test()
async def test(dut):
logging.getLogger().setLevel(logging.INFO)
err = 0
for i in range(10):
a = random.randint(0,15)
b = random.randint(0,15)
dut.a.value = a
dut.b.value = b
await Timer(10, unit='ns')
y = dut.y.value.to_unsigned()
if(y == (a + b)):
logging.info('Test Passed: a:%0d, b:%0d and y:%0d @ %0s',
a, b, y, str(get_sim_time(unit='ns')))
else:
logging.error('Test Failed: a:%0d, b:%0d and y:%0d @ %0s',
a, b, y, str(get_sim_time(unit='ns')))
err += 1
2. Multiplexor 8:1 (8_1_mux)
Verificación de un multiplexor con selección de 3 bits:
@cocotb.test()
async def test(dut):
error_count = 0
logging.getLogger().setLevel(logging.INFO)
din_bin = LogicArray(0, 8)
sel_bin = LogicArray(0, 3)
dout_bin = LogicArray(0, 1)
for _ in range(30):
din = random.randint(0,255)
sel = random.randint(0, 7)
dut.din.value = din
dut.sel.value = sel
await Timer(10, 'ns')
dout = dut.dout.value
if str(din_bin)[7 - sel] != str(dout_bin):
error_count += 1
3. Flip-Flop D (d_flip_flop)
Verificación de un flip-flop tipo D con reset:
@cocotb.test()
async def test(dut):
logging.getLogger().setLevel(logging.INFO)
cocotb.start_soon(rst_stimuli(dut))
cocotb.start_soon(Clock(dut.clk, 20, "ns").start())
await Timer(100, "ns")
err = 0
for i in range(10):
din = random.randint(0, 1)
dut.din.value = din
await RisingEdge(dut.clk)
await RisingEdge(dut.clk)
if dut.dout.value != din:
err += 1
4. Memoria (memory)
Verificación de un módulo de memoria con operaciones de lectura y escritura:
mem_arr = {}
async def write_data(dut):
await RisingEdge(dut.rst)
await FallingEdge(dut.rst)
logging.info('Writing Data to Memory')
for i in range(15):
addr = random.randint(0, 15)
data = random.randint(0, 255)
mem_arr[addr] = data
dut.addr.value = addr
dut.din.value = data
dut.wr.value = 1
await ClockCycles(dut.clk, 1)
async def read_data(dut):
await FallingEdge(dut.wr)
logging.info('Reading Data from Memory')
for i in range(15):
addr = random.randint(0, 15)
dut.addr.value = addr
dut.wr.value = 0
await ClockCycles(dut.clk, 2)
dout = dut.dout.value
if mem_arr.get(addr) != dout:
err += 1
5. Operaciones Binarias (binary)
Demostración de operaciones con tipos binarios en cocotb:
@cocotb.test()
async def test(dut):
logging.getLogger().setLevel(logging.INFO)
# Operaciones bitwise
a = LogicArray("1011")
b = LogicArray("1100")
logging.info(f"a & b = {a & b}")
logging.info(f"a | b = {a | b}")
logging.info(f"a ^ b = {a ^ b}")
logging.info(f"~a = {~a}")
# Operaciones de shift
a = LogicArray("10110011")
n = len(a)
val = a.to_unsigned() << 2
val &= (1 << n) - 1
a = LogicArray.from_unsigned(val, n)
logging.info(f"a << 2 = {a}")
6. Codificador de Prioridad (pri_encoder)
Verificación con modelo de referencia:
def pri_model(input_bin):
# Modelo de referencia para verificar el codificador
for i in range(len(input_bin)-1, -1, -1):
if input_bin[i] == 1:
return LogicArray.from_unsigned(7-i, 3)
return LogicArray(0, 3)
@cocotb.test()
async def test(dut):
error_count = 0
for _ in range(30):
input_rand = random.randint(0, 255)
input_bin[:] = input_rand
dut.en.value = 1
dut.i.value = input_rand
await Timer(10, 'ns')
output = dut.y.value.to_unsigned()
if pri_model(input_bin) != output_bin:
error_count += 1
7. Generación de Reloj (clock)
Diferentes métodos para generar señales de reloj:
# Método manual
async def clk1(dut):
ton = 10
toff = 10
while True:
dut.clk1.value = 1
await Timer(ton, 'ns')
dut.clk1.value = 0
await Timer(toff, 'ns')
# Usando clase Clock de cocotb
@cocotb.test()
async def top_tb(dut):
cocotb.start_soon(Clock(dut.clk, 10, unit='ns').start())
await Timer(100, unit='ns')
Conceptos Clave de cocotb
1. Decoradores y Coroutines
@cocotb.test()
async def test(dut):
# dut = Device Under Test
await Timer(10, 'ns')
2. Triggers (Disparadores)
Timer: Espera un tiempo específicoRisingEdge: Espera flanco de subidaFallingEdge: Espera flanco de bajadaEdge: Cualquier cambioClockCycles: Espera N ciclos de reloj
3. Acceso a señales DUT
# Escribir valores
dut.a.value = 5
dut.b.value = 10
# Leer valores
result = dut.y.value.to_unsigned()
4. Tareas concurrentes
cocotb.start_soon(write_data(dut))
cocotb.start_soon(read_data(dut))
cocotb.start_soon(Clock(dut.clk, 10, 'ns').start())
5. Tipos de datos LogicArray
from cocotb.types import LogicArray
# Crear arrays binarios
a = LogicArray(0, 8) # 8 bits en 0
b = LogicArray("1011") # Desde string binario
c = LogicArray.from_signed(-5, 8) # Número con signo
# Conversiones
unsigned_val = a.to_unsigned()
signed_val = a.to_signed()
Estructura de un Proyecto cocotb
Cada proyecto en Course_2 sigue esta estructura:
project_name/
├── module.sv # Diseño en Verilog
├── module_tb.py # Testbench en Python
├── makefile # Configuración de simulación
├── results.xml # Resultados de las pruebas
├── dump.vcd # Waveforms generados
└── sim_build/ # Archivos temporales
Ejemplo de Makefile
TOPLEVEL_LANG ?= verilog
SIM ?= icarus
VERILOG_SOURCES = $(shell pwd)/adder.sv
TOPLEVEL := adder
COCOTB_TEST_MODULES := adder_tb
include $(shell cocotb-config --makefiles)/Makefile.sim
Cómo Usar el Proyecto
Verificar Python: Asegúrate de tener Python >= 3.12
- Instalar dependencias:
uv sync # o pip install -e . - Verificar instalación:
uv run main.py - Ejecutar simulaciones: Navega a cualquier proyecto en
Course_2/:cd Course_2/4bit_adder make - Ver resultados: Abre el archivo
.vcdcon GTKWave:gtkwave adder.vcd
Ventajas de Python para Verificación
Ecosistema rico: Puedes usar pandas para análisis, matplotlib para gráficas, numpy para operaciones matemáticas
Debugging más fácil: Puedes usar cualquier IDE de Python con breakpoints
Tests más legibles: El código Python es generalmente más claro que SystemVerilog
Automatización: Fácil integración con CI/CD y frameworks de testing
Comunidad: Gran comunidad de Python para soporte
Casos de Uso Reales
cocotb se usa en:
- Chips RISC-V: Verificación de procesadores open-source
- Interfaces de comunicación: I2C, SPI, UART, PCIe
- Aceleradores ML: Verificación de hardware para machine learning
- FPGAs: Desarrollo y testing de diseños para FPGA
- ASICs: Verificación de diseños para fabricación
Recursos Adicionales
- Documentación oficial de cocotb: https://docs.cocotb.org
- PyUVM: https://pyuvm.github.io/pyuvm/
- Ejemplos de cocotb: https://github.com/cocotb/cocotb/tree/master/examples
- Repositorio del proyecto: https://github.com/rigo93acosta/verification_uvm_cocotb
Conclusión
La verificación de hardware con Python y cocotb representa un cambio paradigmático en la metodología tradicional de verificación. Combina la potencia de Python con la necesidad crítica de verificar diseños digitales, haciéndolo más accesible tanto para ingenieros de hardware que quieren aprovechar Python, como para desarrolladores de software que quieren incursionar en hardware.
El proyecto verification_uvm_cocotb proporciona una ruta de aprendizaje estructurada, desde conceptos básicos de Python aplicados a verificación (Course_1) hasta implementaciones completas de testbenches para diseños reales (Course_2). Es un recurso invaluable para cualquiera interesado en aprender verificación de hardware de manera moderna y práctica.
Ya sea que estés diseñando un simple sumador o un complejo sistema on chip (SoC), cocotb y Python te proporcionan las herramientas necesarias para verificarlo de manera eficiente y profesional.