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))
|