mirror of https://github.com/efabless/caravel.git
167 lines
7.0 KiB
Python
167 lines
7.0 KiB
Python
|
|
||
|
import cocotb
|
||
|
from cocotb.triggers import Timer, RisingEdge, ReadOnly
|
||
|
from cocotb_bus.monitors import Monitor
|
||
|
from cocotb.log import SimLogFormatter, SimTimeContextFilter
|
||
|
from cocotb.binary import BinaryValue
|
||
|
from cocotb.result import TestFailure
|
||
|
from math import ceil
|
||
|
import copy
|
||
|
import logging
|
||
|
from fnmatch import fnmatch
|
||
|
|
||
|
|
||
|
class HKmonitor(Monitor):
|
||
|
"""Observes single input """
|
||
|
def __init__(self, name, block_path,interfaces, clock,reset,is_logger = False, callback=None, event=None):
|
||
|
self.name = name
|
||
|
self.interfaces = interfaces
|
||
|
self.clock = clock
|
||
|
self.reset = reset
|
||
|
self.block_path = block_path
|
||
|
self.is_logger = is_logger
|
||
|
self.setup_logger()
|
||
|
Monitor.__init__(self, callback, event)
|
||
|
|
||
|
|
||
|
async def _monitor_recv(self):
|
||
|
old_trans_hold = None
|
||
|
old_trans_no_valid = None
|
||
|
while True:
|
||
|
# Capture signal at rising edge of clock
|
||
|
if "_clk" in self.interfaces: # for interfaces with own clock
|
||
|
signal = self.block_path._id(self.interfaces['_clk']['signal'],False)
|
||
|
await RisingEdge(signal)
|
||
|
else:
|
||
|
await RisingEdge(self.clock)
|
||
|
|
||
|
|
||
|
# if self.reset.value.binstr == '0':
|
||
|
# continue
|
||
|
|
||
|
if "_valid_cycle" in self.interfaces: # for interfaces with valid signal
|
||
|
signal = self.block_path._id(self.interfaces['_valid_cycle']['signal'],False).value.binstr
|
||
|
if signal is not '1':
|
||
|
continue
|
||
|
|
||
|
if "_valid_cycle_n" in self.interfaces: # for interfaces with valid signal
|
||
|
signal = self.block_path._id(self.interfaces['_valid_cycle']['signal'],False).value.binstr
|
||
|
if signal is not '0':
|
||
|
continue
|
||
|
|
||
|
# update signal
|
||
|
for key2,signal in self.interfaces.items():
|
||
|
# if fnmatch(key2,"_*"):
|
||
|
# continue
|
||
|
signal['val'] = self.block_path._id(signal['signal'],False).value
|
||
|
|
||
|
# if no_valid signal exist trans didn't change so monitor will not monitor anything
|
||
|
# no_valid means if the signal didn't change no addition action would needed
|
||
|
if "_no_valid" in self.interfaces:
|
||
|
if old_trans_no_valid is None:
|
||
|
old_trans_no_valid = copy.deepcopy(self.interfaces)
|
||
|
elif (old_trans_no_valid == self.interfaces):
|
||
|
return
|
||
|
else:
|
||
|
old_trans_no_valid = copy.deepcopy(self.interfaces)
|
||
|
|
||
|
# logger
|
||
|
self.logger.debug(f' ')
|
||
|
self.handler.terminator = ""
|
||
|
self.handler.setFormatter(SimLogFormatter())
|
||
|
self.logger.debug(f'')
|
||
|
self.handler.setFormatter(logging.Formatter('%(message)s'))
|
||
|
for key2,signal in self.interfaces.items():
|
||
|
if fnmatch(key2,"_*"):
|
||
|
continue
|
||
|
if signal['val'].is_resolvable:
|
||
|
length = self.lengths[key2] - (len(hex(signal['val'].integer)))
|
||
|
self.logger.debug(f" {hex(signal['val'].integer)}{' '*length}|")
|
||
|
# signal['val'] = self.block_path._id(signal['signal'],False).value.integer
|
||
|
else:
|
||
|
length = self.lengths[key2] - (len('x'))
|
||
|
self.logger.debug(f" x{' '*length}|")
|
||
|
self.handler.terminator = "\n"
|
||
|
|
||
|
# special case in HKoutputsMonitorwishbone when writing to reg_mprj_datal(because it uses _buf) the data out is x's
|
||
|
# and in this case scoreboard raise obejection that the value is unresolved
|
||
|
if self.name == "HKoutputsMonitorwishbone":
|
||
|
if not self.interfaces['data']['val'].is_resolvable:
|
||
|
self.interfaces['data']['val'] = BinaryValue(value=0,n_bits=self.interfaces['data']['val'].n_bits)
|
||
|
cocotb.log.debug(f'[HKmonitor][_monitor_recv] interface at monitor {self.name} self.interfaces {self.interfaces}')
|
||
|
self._recv(self.interfaces)
|
||
|
|
||
|
## assertion that the values can't change until hold is released
|
||
|
if "_hold" in self.interfaces:
|
||
|
if old_trans_hold is None:
|
||
|
skip = False
|
||
|
old_trans_hold = copy.deepcopy(self.interfaces)
|
||
|
elif self.interfaces['_hold']['val'] == BinaryValue(value=1):
|
||
|
skip = True
|
||
|
elif skip:
|
||
|
old_trans_hold = copy.deepcopy(self.interfaces)
|
||
|
skip = False
|
||
|
else:
|
||
|
if old_trans_hold != self.interfaces:
|
||
|
cocotb.log.error(f'[HKmonitor][_monitor_recv] interface at monitor {self.name} change value before hold value is asserted \nold value {old_trans_hold} \nnew value {self.interfaces}')
|
||
|
raise TestFailure
|
||
|
|
||
|
|
||
|
"""method for setting up logger for WB model"""
|
||
|
def setup_logger(self):
|
||
|
self.logger = logging.getLogger(f'HouseKeeping{self.name}')
|
||
|
self.logger.setLevel(logging.DEBUG)
|
||
|
if not self.is_logger:
|
||
|
self.logger.setLevel(logging.INFO)
|
||
|
self.handler = logging.StreamHandler()
|
||
|
# return
|
||
|
else :
|
||
|
self.handler = logging.FileHandler(f"{self.name}.log",mode='w')
|
||
|
self.handler.addFilter(SimTimeContextFilter())
|
||
|
self.logger.addHandler(self.handler)
|
||
|
# get the sizes of signals
|
||
|
#for key,interface in self.interfaces.items():
|
||
|
for key,signal in self.interfaces.items():
|
||
|
if fnmatch(key,"_*"):
|
||
|
continue
|
||
|
signal['val'] = self.block_path._id(signal['signal'],False).value
|
||
|
size = signal['val'].n_bits
|
||
|
signal['val'] = BinaryValue(value = int(size) * '1',n_bits=size)
|
||
|
# set the logger file header
|
||
|
# set first line
|
||
|
self.handler.terminator = ""
|
||
|
self.logger.debug(f' timestamp level ')
|
||
|
length =0
|
||
|
for key2,signal in self.interfaces.items():
|
||
|
if fnmatch(key2,"_*"):
|
||
|
continue
|
||
|
length += max(ceil(signal['val'].n_bits/4)+2 , len(key2)) +3
|
||
|
length -= len(key)+1
|
||
|
self.logger.debug(f'| signals {" "*int(length)}')
|
||
|
self.handler.terminator = "\n"
|
||
|
self.logger.debug(f' ')
|
||
|
# set second line
|
||
|
self.handler.terminator = ""
|
||
|
self.logger.debug(f'{" "*20}|')
|
||
|
length =0
|
||
|
self.lengths = dict()
|
||
|
for key2,signal in self.interfaces.items():
|
||
|
if fnmatch(key2,"_*"):
|
||
|
continue
|
||
|
self.lengths[key2] = max((len(hex(signal['val'].integer))),len(key2)) +1
|
||
|
length = self.lengths[key2] - len(key2)
|
||
|
self.logger.debug(f'{key2}{" "*length} ')
|
||
|
self.handler.terminator = "\n"
|
||
|
|
||
|
|
||
|
class color:
|
||
|
PURPLE = '\033[95m'
|
||
|
CYAN = '\033[96m'
|
||
|
DARKCYAN = '\033[36m'
|
||
|
BLUE = '\033[94m'
|
||
|
GREEN = '\033[92m'
|
||
|
YELLOW = '\033[93m'
|
||
|
RED = '\033[91m'
|
||
|
BOLD = '\033[1m'
|
||
|
UNDERLINE = '\033[4m'
|
||
|
END = '\033[0m'
|