title | subtitle | author | date |
---|---|---|---|
Using Python for Verification of Digital Systems | QZ 2021 | [Torsten Meißner torsten.meissner@secunet.com] | February 2022 |
Introduction
Functional Verification
Co-Simulation
Cocotb
Python Packages
Live Demo
Summary
HDL testbench controls program flow
Python testbench controls program flow
# Reference to toplevel clock input
clk = dut.clk_i
# Reference to signal in sub-unit
cpu_pc = dut.cpu.regfile.pc
# Via value property
valid = dut.valid_i.value
if valid.value == 0:
valid.value = 1
# Direct access through hierarchy
if dut.reset_i.value == 1:
dut.cpu.regfile.pc.value = 0
async def reset(signal, time,):
signal.value = 0
# Block execution, wait for simulator time advances by 100 ns
await Timer(time, units='ns') # cocotb built-in class
signal.value = 1
print("Hold reset")
await reset(dut.reset_i)
print("Released reset")
await cocotb.start(reset(dut.reset_i, 100)
await Timer(90, units='ns')
print(f"Reset is still active: {dut.reset_i.value}")
await Timer(15, units='ns')
print(f"Reset has gone inactive: {dut.reset_i.value}")
clock = Clock(dut.clk_i, 10, units="ns") # Create a clock, cocotb built-in class
cocotb.start_soon(clock.start()) # Start the clock concurrently
@cocotb.test()
async def test_aes_init(dut):
""" Test AES initialization """
...
@cocotb.test()
async def test_aes_enc(dut):
""" Test AES encryption """
...
# This test is skipped from execution
@cocotb.test(skip=True)
async def test_aes_enc(dut):
""" Test AES encryption """
# Wait for 100 ns
await Timer(100, units='ns')
# Wait for rising clock edge
await RisingEdge(dut.clk_i)
# Wait for 10 clock cycles
await ClockCycles(dut.clk_i, 10)
# Fires when first trigger in fires & returns its result
t1 = Timer(10, units='ns')
t2 = Timer(15, units='ns')
t_ret = await First(t1, t2) # returns after 10 ns simulation time
async def send(self, data, sync=True):
if sync:
await self._clkedge
self._valid.value = 1
if isinstance(self._data, list):
for i in range(len(self._data)):
self._data[i].value = data[i]
else:
self._data.value = data
while True:
await ReadOnly()
if self._accept.value:
break
await self._clkedge
await self._clkedge
self._valid.value = 0
async def receive(self):
# Wait for frame start
await FallingEdge(self._txrx)
# Consume start bit
await self._get_start_bit()
# Receive data bits
self._rec = 0
for x in range(self._bits):
await self._wait_cycle()
await ReadOnly()
self._rec |= bool(self._txrx.value.integer) << x
if self._par:
# Consume parity bit
await self._get_parity_bit()
# Consume stop bit
await self._get_stop_bit()
return self._rec
@cocotb.test()
async def test_uarttx(dut):
# Instantiate VAI driver & UART receiver
vai_driver = VaiDriver(dut.clk_i, dut.data_i, dut.valid_i, dut.accept_o)
uart_receiver = UartReceiver(dut.tx_o, dut.clk_i, 10, 8, True);
# Drive input defaults (setimmediatevalue to avoid x asserts)
dut.data_i.setimmediatevalue(0)
dut.valid_i.setimmediatevalue(0)
cocotb.start_soon(Clock(dut.clk_i, 10, units="ns").start()) # Start the clock
await reset(dut.reset_n_i, 100) # Block until reset() has completed
# Test 10 UART transmissions
for i in range(256):
await RisingEdge(dut.clk_i)
await vai_driver.send(i)
rec = await uart_receiver.receive();
assert rec == i, "UART sent data was incorrect on the {}th cycle".format(i)
loading VPI module '/usr/local/lib/python3.9/dist-packages/cocotb/libs/libcocotbvpi_ghdl.so'
-.--ns INFO cocotb.gpi ../gpi/GpiCommon.cpp:99 in gpi_print_registered_impl VPI registered
VPI module loaded!
0.00ns INFO Running on GHDL version 2.0.0-dev (v1.0.0-974-g0e46300c) [Dunoon edition]
0.00ns INFO Running tests with cocotb v1.7.0.dev0 from /usr/local/lib/python3.9/...
0.00ns INFO Seeding Python random module with 1644512771
0.00ns INFO Found test tb_uarttx.test_uarttx
0.00ns INFO running test_uarttx (1/1)
First simple test
0.00ns INFO Valid-accept driver
0.00ns INFO cocotbext-vai version 0.0.1
0.00ns INFO Copyright (c) 2022 Torsten Meissner
0.00ns INFO UART receiver
0.00ns INFO cocotbext-uart version 0.0.1
0.00ns INFO Copyright (c) 2022 Torsten Meissner
100.00ns INFO Released reset
110.00ns INFO Send data: 0xb6
...
11160.00ns INFO Received data: 0xd8
11160.00ns INFO test_uarttx passed
11160.00ns INFO **********************************************************************
** TEST STATUS SIM TIME (ns) REAL TIME (s)
**********************************************************************
** tb_uarttx.test_uarttx PASS 11160.00 0.21
**********************************************************************
** TESTS=1 PASS=1 FAIL=0 SKIP=0 11160.00 0.22
**********************************************************************
@vsc.randobj
class my_cr():
def __init__(self):
self.a = vsc.rand_bit_t(8)
self.b = vsc.rand_bit_t(8)
@vsc.constraint
def ab_c(self):
self.a != 0
self.a <= self.b
self.b in vsc.rangelist(1,2,4,8)
dut.some_array[0].value = 1