import logging
|
|
import cocotb
|
|
from cocotb.utils import get_sim_time
|
|
from cocotb.triggers import FallingEdge, RisingEdge, Timer
|
|
|
|
|
|
class Vai:
|
|
"""VAI base class"""
|
|
|
|
def __init__(self, clock, data, valid, accept, *args, **kwargs):
|
|
self._version = "0.0.1"
|
|
|
|
self.log = logging.getLogger(f"cocotb.{valid._path}")
|
|
|
|
self._data = data
|
|
self._valid = valid
|
|
self._accept = accept
|
|
self._clock = clock
|
|
|
|
self._clkedge = RisingEdge(self._clock)
|
|
|
|
|
|
class VaiDriver(Vai):
|
|
"""Valid-Accept Driver"""
|
|
|
|
def __init__(self, clock, data, valid, accept, *args, **kwargs):
|
|
super().__init__(clock, data, valid, accept, *args, **kwargs)
|
|
|
|
self.log.info("Valid-accept driver")
|
|
self.log.info(" cocotbext-vai version %s", self._version)
|
|
self.log.info(" Copyright (c) 2022 Torsten Meissner")
|
|
|
|
# Hack to drive lists of signals
|
|
if isinstance(self._data, list):
|
|
for entry in self._data:
|
|
entry.setimmediatevalue(0)
|
|
else:
|
|
self._data.setimmediatevalue(0)
|
|
self._valid.setimmediatevalue(0)
|
|
|
|
async def send(self, data, sync=True):
|
|
if sync:
|
|
await self._clkedge
|
|
|
|
self._valid.value = 1
|
|
|
|
if isinstance(self._data, list):
|
|
_info = ', '.join(map(lambda x: str(hex(x)), data))
|
|
for i in range(len(self._data)):
|
|
self._data[i].value = data[i]
|
|
|
|
else:
|
|
self._data.value = data
|
|
_info = hex(data)
|
|
|
|
self.log.info(f"Send data: {_info}")
|
|
|
|
while True:
|
|
if self._accept.value:
|
|
break
|
|
await self._clkedge
|
|
await self._clkedge
|
|
|
|
self._valid.value = 0
|
|
|
|
|
|
|
|
class VaiReceiver(Vai):
|
|
"""Valid-Accept Receiver"""
|
|
|
|
def __init__(self, clock, data, valid, accept, *args, **kwargs):
|
|
super().__init__(clock, data, valid, accept, *args, **kwargs)
|
|
|
|
self.log.info("Valid-accept receiver")
|
|
self.log.info(" cocotbext-vai version %s", self._version)
|
|
self.log.info(" Copyright (c) 2022 Torsten Meissner")
|
|
|
|
# Drive input defaults (setimmediatevalue to avoid x asserts)
|
|
self._accept.setimmediatevalue(0)
|
|
|
|
async def receive(self, sync=True):
|
|
if sync:
|
|
await self._clkedge
|
|
|
|
while True:
|
|
if self._valid.value:
|
|
break
|
|
await self._clkedge
|
|
|
|
await self._clkedge
|
|
self._accept.value = 1
|
|
_rec = self._data.value
|
|
self.log.info(f"Receive data: {hex(_rec)}")
|
|
|
|
await self._clkedge
|
|
self._accept.value = 0
|
|
|
|
return _rec
|
|
|
|
|
|
class VaiMonitor(Vai):
|
|
"""Valid-Accept Receiver"""
|
|
|
|
def __init__(self, clock, data, valid, accept, queue=None, *args, **kwargs):
|
|
super().__init__(clock, data, valid, accept, *args, **kwargs)
|
|
|
|
self.log.info("Valid-accept monitor")
|
|
self.log.info(" cocotbext-vai version %s", self._version)
|
|
self.log.info(" Copyright (c) 2022 Torsten Meissner")
|
|
|
|
self._active = None
|
|
self._queue = queue
|
|
self._transactions = {}
|
|
self._restart()
|
|
|
|
def _restart(self):
|
|
self.log.debug("SramMonitor._restart()")
|
|
if self._active is not None:
|
|
self._active.kill()
|
|
# Schedule VAI read to run concurrently
|
|
self._active = cocotb.start_soon(self._read())
|
|
|
|
async def _read(self, cb=None):
|
|
while True:
|
|
await self._clkedge
|
|
if self._valid.value and self._accept.value:
|
|
if self._queue:
|
|
await self._queue.put(self._data)
|
|
#self._transactions[str(get_sim_time('ns'))] = {
|
|
# "data" : self._data.value}
|
|
|
|
|
|
@property
|
|
def transactions(self, index=None):
|
|
if index:
|
|
key = list(self._transactions.keys())[index]
|
|
return {key: self._transactions[key]}
|
|
else:
|
|
return self._transactions
|