• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Nic30 / hwtLib / b469f1f6-6a00-4958-bfb0-f9fbf427a589

06 Jun 2024 06:38PM UTC coverage: 93.399% (-0.03%) from 93.431%
b469f1f6-6a00-4958-bfb0-f9fbf427a589

push

circleci

Nic30
docs

8040 of 9100 branches covered (88.35%)

39136 of 41902 relevant lines covered (93.4%)

0.93 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.1
/hwtLib/amba/axi_comp/cache/lru_array.py
1
#!/usr/bin/env python3
2
# -*- coding: utf-8 -*-
3

4
from hwt.code import If, Or, Concat
1✔
5
from hwt.code_utils import rename_signal
1✔
6
from hwt.constants import WRITE, READ
1✔
7
from hwt.hObjList import HObjList
1✔
8
from hwt.hdl.types.defs import BIT
1✔
9
from hwt.hdl.types.struct import HStruct
1✔
10
from hwt.hwIOs.std import HwIOVectSignal, HwIODataRdVld, HwIORdVldSync
1✔
11
from hwt.hwIOs.utils import addClkRstn, propagateClkRstn
1✔
12
from hwt.hwParam import HwParam
1✔
13
from hwt.math import log2ceil
1✔
14
from hwt.pyUtils.arrayQuery import flatten, grouper
1✔
15
from hwt.pyUtils.typingFuture import override
1✔
16
from hwtLib.amba.axi_comp.cache.addrTypeConfig import CacheAddrTypeConfig
1✔
17
from hwtLib.amba.axi_comp.cache.pseudo_lru import PseudoLru
1✔
18
from hwtLib.commonHwIO.addr import HwIOAddrRdVld
1✔
19
from hwtLib.commonHwIO.addr_data import HwIOAddrDataRdVld
1✔
20
from hwtLib.logic.binToOneHot import binToOneHot
1✔
21
from hwtLib.mem.ramXor import RamXorSingleClock
1✔
22

23

24
# extract variant with id
25
class HwIOIndexWayRdVld(HwIORdVldSync):
1✔
26

27
    @override
1✔
28
    def hwConfig(self):
1✔
29
        self.ID_WIDTH = HwParam(0)
1✔
30
        self.INDEX_WIDTH = HwParam(10)
1✔
31
        self.WAY_CNT = HwParam(4)
1✔
32

33
    @override
1✔
34
    def hwDeclr(self):
1✔
35
        if self.ID_WIDTH:
1!
36
            self.id = HwIOVectSignal(self.ID_WIDTH)
×
37
        self.index = HwIOVectSignal(self.INDEX_WIDTH)
1✔
38
        if self.WAY_CNT > 1:
1!
39
            self.way = HwIOVectSignal(log2ceil(self.WAY_CNT - 1))
1✔
40
        HwIORdVldSync.hwDeclr(self)
1✔
41

42

43
class AxiCacheLruArray(CacheAddrTypeConfig):
1✔
44
    """
45
    A memory storing Tree-PLRU records with multiple ports.
46
    The access using various ports is merged together.
47
    The victim_req port also marks the way as lastly used.
48
    The set port disables all discards all pending updates
49
    and it is meant to be used for an initialization of the array/cache.
50

51
    .. figure:: ./_static/AxiCacheLruArray.png
52

53
    .. hwt-autodoc::
54
    """
55

56
    @override
1✔
57
    def hwConfig(self):
1✔
58
        CacheAddrTypeConfig.hwConfig(self)
1✔
59
        self.INCR_PORT_CNT = HwParam(2)
1✔
60
        self.WAY_CNT = HwParam(4)
1✔
61

62
    def _compute_constants(self):
1✔
63
        assert self.WAY_CNT >= 1, self.WAY_CNT
1✔
64
        self._compupte_tag_index_offset_widths()
1✔
65
        self.LRU_WIDTH = PseudoLru.lru_reg_width(self.WAY_CNT)
1✔
66

67
    @override
1✔
68
    def hwDeclr(self):
1✔
69
        self._compute_constants()
1✔
70
        addClkRstn(self)
1✔
71
        # used to initialize the LRU data (in the case of cache reset)
72
        # while set port is active all other ports are blocked
73
        s = self.set = HwIOAddrDataRdVld()
1✔
74
        s.ADDR_WIDTH = self.INDEX_W
1✔
75
        s.DATA_WIDTH = self.LRU_WIDTH
1✔
76

77
        # used to increment the LRU data in the case of hit
78
        self.incr = HObjList(HwIOIndexWayRdVld() for _ in range(self.INCR_PORT_CNT))
1✔
79
        for i in self.incr:
1✔
80
            i.INDEX_WIDTH = self.INDEX_W
1✔
81
            i.WAY_CNT = self.WAY_CNT
1✔
82

83
        # get a victim for a selected cacheline index
84
        # The cacheline returned as a victim is also marked as used just now
85
        vr = self.victim_req = HwIOAddrRdVld()
1✔
86
        vr.ADDR_WIDTH = self.INDEX_W
1✔
87
        vr.ID_WIDTH = 0
1✔
88
        vd = self.victim_data = HwIODataRdVld()._m()
1✔
89
        vd.DATA_WIDTH = log2ceil(self.WAY_CNT - 1)
1✔
90

91
        m = self.lru_mem = RamXorSingleClock()
1✔
92
        m.ADDR_WIDTH = self.INDEX_W
1✔
93
        m.DATA_WIDTH = self.LRU_WIDTH
1✔
94
        m.PORT_CNT = (
1✔
95
            # victim_req preload, victim_req write back or set,
96
            READ, WRITE,
97
            #  incr preload, incr write back...
98
            *flatten((READ, WRITE) for _ in range(self.INCR_PORT_CNT))
99
        )
100

101
    def merge_successor_writes_into_incr_one_hot(self, succ_writes, incr_val_oh):
1✔
102
        if succ_writes:
1✔
103
            for succ_write_vld, succ_write_oh in succ_writes:
1✔
104
                en_mask = Concat(*[succ_write_vld for _ in range(succ_write_oh._dtype.bit_length())])
1✔
105
                incr_val_oh = incr_val_oh | (succ_write_oh & en_mask)
1✔
106
        return incr_val_oh
1✔
107

108
    @override
1✔
109
    def hwImpl(self):
1✔
110
        m = self.lru_mem
1✔
111
        victim_req_r, victim_req_w = m.port[:2]
1✔
112

113
        # victim selection ports
114
        victim_req = self.victim_req
1✔
115
        victim_req_r.en(victim_req.vld)
1✔
116
        victim_req_r.addr(victim_req.addr)
1✔
117
        victim_req_tmp = self._reg(
1✔
118
            "victim_req_tmp",
119
            HStruct(
120
              (victim_req.addr._dtype, "index"),
121
              (BIT, "vld")
122
            ),
123
            def_val={"vld": 0}
124
        )
125
        set_ = self.set
1✔
126
        victim_data = self.victim_data
1✔
127
        victim_req.rd(~set_.vld & (~victim_req_tmp.vld | victim_data.rd))
1✔
128
        If((~victim_req_tmp.vld | victim_data.rd) & ~set_.vld,
1✔
129
           victim_req_tmp.index(victim_req.addr),
130
           victim_req_tmp.vld(victim_req.vld),
131
        )
132

133
        incr_rw = list(grouper(2, m.port[2:]))
1✔
134

135
        # in the first stp we have to collect all pending addresses
136
        # because we need it in order to resolve any potential access merging
137
        incr_tmp_mask_oh = []
1✔
138
        for i, (incr_in, (incr_r, _)) in enumerate(zip(self.incr, incr_rw)):
1✔
139
            incr_tmp = self._reg(
1✔
140
                f"incr_tmp{i:d}",
141
                HStruct(
142
                    (incr_in.index._dtype, "index"),
143
                    (incr_in.way._dtype, "way"),
144
                    (BIT, "vld")
145
                ),
146
                def_val={"vld": 0}
147
            )
148
            incr_tmp.index(incr_in.index)
1✔
149
            incr_tmp.way(incr_in.way)
1✔
150
            incr_tmp.vld(incr_in.vld & ~set_.vld)
1✔
151

152
            incr_val_oh = rename_signal(self, binToOneHot(incr_in.way), f"incr_val{i:d}_oh")
1✔
153
            incr_tmp_mask_oh.append((incr_tmp, incr_val_oh))
1✔
154

155
        lru = PseudoLru(victim_req_r.dout)
1✔
156
        victim = rename_signal(self, lru.get_lru(), "victim")
1✔
157
        victim_oh = rename_signal(self, binToOneHot(victim), "victim_oh")
1✔
158

159
        victim_data.data(victim)
1✔
160
        victim_data.vld(victim_req_tmp.vld & ~set_.vld)
1✔
161

162
        succ_writes = [
1✔
163
            (incr2_tmp.vld & incr2_tmp.index._eq(victim_req_tmp.index), incr2_val_oh)
164
            for incr2_tmp, incr2_val_oh in incr_tmp_mask_oh
165
        ]
166
        _victim_oh = self.merge_successor_writes_into_incr_one_hot(succ_writes, victim_oh)
1✔
167
        _victim_oh = rename_signal(self, _victim_oh, f"victim_val{i:d}_oh_final")
1✔
168

169
        set_.rd(1)
1✔
170
        If(set_.vld,
1✔
171
            # repurpose victim req victim_req_w port for an intialization of LRU array using "set" port
172
            victim_req_w.en(1),
173
            victim_req_w.addr(set_.addr),
174
            victim_req_w.din(set_.data),
175
        ).Else(
176
            # use victim_req_w port for a victim req write back as usuall
177
            victim_req_w.en(victim_req_tmp.vld),
178
            victim_req_w.addr(victim_req_tmp.index),
179
            victim_req_w.din(lru.mark_use_many(_victim_oh)),
180
        )
181

182
        for i, (incr_in, (incr_r, incr_w), (incr_tmp, incr_val_oh)) in enumerate(zip(self.incr, incr_rw, incr_tmp_mask_oh)):
1✔
183
            # drive incr_r port
184
            incr_r.addr(incr_in.index)
1✔
185
            incr_r.en(incr_in.vld)
1✔
186
            incr_in.rd(~set_.vld)
1✔
187

188
            # resolve the final mask of LRU incrementation for this port and drive incr_w
189
            prev_writes = [
1✔
190
                    victim_req_tmp.vld & victim_req_tmp.index._eq(incr_tmp.index)
191
                ] + [
192
                    incr2_tmp.vld & incr2_tmp.index._eq(incr_tmp.index)
193
                    for incr2_tmp, _ in incr_tmp_mask_oh[:i]
194
                ]
195
            # if any of previous port writes to same index we need to ommit this write as it is writen by some previous port
196
            incr_w.addr(incr_tmp.index)
1✔
197
            incr_w.en(incr_tmp.vld & ~Or(*prev_writes) & ~set_.vld)
1✔
198

199
            succ_writes = [
1✔
200
                (incr2_tmp.vld & incr2_tmp.index._eq(incr_tmp.index), incr2_val_oh)
201
                for incr2_tmp, incr2_val_oh in incr_tmp_mask_oh[i + 1:]
202
            ]
203

204
            # if collides with others merge the incr_val_oh
205
            incr_val_oh = self.merge_successor_writes_into_incr_one_hot(succ_writes, incr_val_oh)
1✔
206
            incr_val_oh = rename_signal(self, incr_val_oh, f"incr_val{i:d}_oh_final")
1✔
207
            incr_w.din(PseudoLru(incr_r.dout).mark_use_many(incr_val_oh))
1✔
208

209
        propagateClkRstn(self)
1✔
210

211

212
if __name__ == "__main__":
213
    from hwt.synth import to_rtl_str
214
    u = AxiCacheLruArray()
215
    print(to_rtl_str(u))
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc