Examples of using cocotb for functional verification of VHDL designs with GHDL.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

121 lines
3.8 KiB

import cocotb
from cocotb.triggers import RisingEdge, Timer
from cocotb.queue import QueueEmpty, Queue
from cocotb.clock import Clock
import logging
import enum
import pyuvm
# Logger setup
logging.basicConfig(level=logging.NOTSET)
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
# AES mode enum
@enum.unique
class Mode(enum.IntEnum):
Encrypt = 0
Decrypt = 1
# VAI BFM with queues for
class VaiBfm(metaclass=pyuvm.Singleton):
"""Valid-Accept Bfm"""
def __init__(self):
self.log = logging.getLogger()
self.log.info("Valid-accept BFM")
self.log.info(" Copyright (c) 2024 Torsten Meissner")
self.dut = cocotb.top
self.driver_queue = Queue(maxsize=1)
self.in_monitor_queue = Queue(maxsize=0)
self.out_monitor_queue = Queue(maxsize=0)
self.clock = Clock(
self.dut.clk_i, 10, units="ns"
) # Create a 10 ns period clock
cocotb.start_soon(self.clock.start())
# Reset coroutine
async def reset(self):
self.dut.reset_i.value = 0
self.dut.valid_i.value = 0
self.dut.mode_i.value = 0
self.dut.key_i.value = 0
self.dut.data_i.value = 0
self.dut.accept_i.value = 0
await Timer(100, units="ns")
self.dut.reset_i.value = 1
# VAI input driver
async def __driver(self):
self.dut.valid_i.value = 0
self.dut.key_i.value = 0
self.dut.data_i.value = 0
while True:
await RisingEdge(self.dut.clk_i)
if not self.dut.valid_i.value:
try:
(mode, key, data) = self.driver_queue.get_nowait()
self.dut.mode_i.value = mode
self.dut.key_i.value = key
self.dut.data_i.value = data
self.dut.valid_i.value = 1
except QueueEmpty:
continue
else:
if self.dut.accept_o.value:
self.dut.valid_i.value = 0
# VAI output receiver
# We ignore data out, we use the output monitor instead
async def __receiver(self):
self.dut.accept_i.value = 0
while True:
await RisingEdge(self.dut.clk_i)
if self.dut.valid_o.value and not self.dut.accept_i.value:
self.dut.accept_i.value = 1
else:
self.dut.accept_i.value = 0
# VAI input monitor
async def __in_monitor(self):
while True:
await RisingEdge(self.dut.clk_i)
if self.dut.valid_i.value and self.dut.accept_o.value:
in_tuple = (
self.dut.mode_i.value,
self.dut.key_i.value,
self.dut.data_i.value,
)
self.in_monitor_queue.put_nowait(in_tuple)
# VAI output monitor
async def __out_monitor(self):
while True:
await RisingEdge(self.dut.clk_i)
if self.dut.valid_o.value and self.dut.accept_i.value:
out_data = self.dut.data_o.value
self.out_monitor_queue.put_nowait(out_data)
# Launching the coroutines using start_soon
def start_tasks(self):
cocotb.start_soon(self.__driver())
cocotb.start_soon(self.__receiver())
cocotb.start_soon(self.__in_monitor())
cocotb.start_soon(self.__out_monitor())
# The get_input() coroutine returns the next VAI input
async def get_input(self):
data = await self.in_monitor_queue.get()
return data
# The get_output() coroutine returns the next VAI output
async def get_output(self):
data = await self.out_monitor_queue.get()
return data
# send_op puts the VAI input operation into the driver queue
async def send_op(self, mode, key, data):
await self.driver_queue.put((mode, key, data))