1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
import random
from cocotb.binary import BinaryValue
from cocotb_coverage.coverage import CoverCross, CoverPoint
from .data import BAD_PC
from .common import log
class PcTable:
def __init__(self):
self._pcs = {}
def __getitem__(self, group):
if isinstance(group, BinaryValue):
group = group.integer
return self._pcs.get(group, BAD_PC)
def __setitem__(self, group, pc):
assert (pc & 3) == 0
if isinstance(group, BinaryValue):
group = group.integer
self._pcs[group] = pc >> 2
class Memory:
def __init__(self, name, *, word_size, words, start=0):
word_mask = word_size - 1
assert word_size > 0 and (word_mask & word_size) == 0, \
f'{word_size} is not a power of two'
self._data = {}
self._dirty = set()
self._observed = set()
self._start = start
self._words = words
self._word_size = word_size
self._subword_mask = word_mask
self._all_ones = (1 << (8 * word_size)) - 1
@CoverPoint(
f'{name}.read_dirty',
bins = [True, False],
bins_labels = ['dirty', 'clean'],
rel = lambda word_num, dirty: self._test_bin(word_num, self._dirty, dirty),
)
@CoverPoint(
f'{name}.read_observed',
bins = [True, False],
bins_labels = ['multiple_reads', 'first_read'],
rel = lambda word_num, observed: self._test_bin(word_num, self._observed, observed),
)
@CoverCross(
f'{name}.read_dirty_observed',
items = [
f'{name}.read_dirty',
f'{name}.read_observed',
],
ign_bins = [
('dirty', 'first_read'),
],
)
def _read_word(word_num):
if self._expect_in_range(word_num):
self._observed.add(word_num)
word = self._data.get(word_num)
if word is None:
log.warning(f'Uninitialized memory read: {self._addr_repr(word_num)}')
word = self._all_ones
return word
@CoverPoint(
f'{name}.write_dirty',
bins = [True, False],
bins_labels = ['dirty_write', 'clean_write'],
xf = lambda word_num, data: word_num,
rel = lambda word_num, dirty: self._test_bin(word_num, self._observed, dirty),
)
def _write_word(word_num, data):
if self._expect_in_range(word_num):
self._data[word_num] = data
if word_num in self._observed:
self._dirty.add(word_num)
self._read_word = _read_word
self._write_word = _write_word
def read(self, addr):
return self._data[addr >> 2]
def randomize_line(self, addr):
first_word = (addr >> 2) & ~15
for word_num in range(first_word, first_word + 16):
self._write_word(word_num, random.randint(0, self._all_ones))
def __getitem__(self, index):
if not isinstance(index, slice):
return super()[index]
assert index.stop >= index.start
assert (index.stop & self._subword_mask) == 0
assert (index.start & self._subword_mask) == 0
return MemoryRead(self, index.start // self._word_size, index.stop // self._word_size)
def _expect_in_range(self, word_num):
delta = word_num - self._start
in_range = delta >= 0 and delta < self._words
if not in_range:
log.error(f'Bad memory address: {self._addr_repr(word_num)}')
return in_range
def _test_bin(self, word_num, flag_set, flag_bin):
if not self._expect_in_range(word_num):
return False
return (word_num in flag_set) == flag_bin
def _addr_repr(self, word_num):
addr = word_num * self._word_size
return f'0x{addr:08x}'
class MemoryRead:
def __init__(self, mem, start, stop):
self._mem, self._start, self._stop = mem, start, stop
def tobytes(self):
array = bytearray()
for word_num in range(self._start, self._stop):
word = self._mem._read_word(word_num)
array.extend(word.to_bytes(self._mem._word_size, 'little'))
return array
|