|
3 | 3 |
|
4 | 4 | from transactron.utils.transactron_helpers import from_method_layout, make_layout |
5 | 5 | from ..core import * |
6 | | -from ..utils import SrcLoc, get_src_loc |
| 6 | +from ..utils import SrcLoc, get_src_loc, MultiPriorityEncoder |
7 | 7 | from typing import Optional |
8 | | -from transactron.utils import assign, AssignType, LayoutList |
| 8 | +from transactron.utils import assign, AssignType, LayoutList, MethodLayout |
9 | 9 | from .reqres import ArgumentsToResultsZipper |
10 | 10 |
|
11 | | -__all__ = ["MemoryBank", "AsyncMemoryBank"] |
| 11 | +__all__ = ["MemoryBank", "ContentAddressableMemory", "AsyncMemoryBank"] |
12 | 12 |
|
13 | 13 |
|
14 | 14 | class MemoryBank(Elaboratable): |
@@ -37,7 +37,7 @@ def __init__( |
37 | 37 | elem_count: int, |
38 | 38 | granularity: Optional[int] = None, |
39 | 39 | safe_writes: bool = True, |
40 | | - src_loc: int | SrcLoc = 0 |
| 40 | + src_loc: int | SrcLoc = 0, |
41 | 41 | ): |
42 | 42 | """ |
43 | 43 | Parameters |
@@ -138,6 +138,103 @@ def _(arg): |
138 | 138 | return m |
139 | 139 |
|
140 | 140 |
|
| 141 | +class ContentAddressableMemory(Elaboratable): |
| 142 | + """Content addresable memory |
| 143 | +
|
| 144 | + This module implements a content-addressable memory (in short CAM) with Transactron interface. |
| 145 | + CAM is a type of memory where instead of predefined indexes there are used values fed in runtime |
| 146 | + as keys (similar as in python dictionary). To insert new entry a pair `(key, value)` has to be |
| 147 | + provided. Such pair takes an free slot which depends on internal implementation. To read value |
| 148 | + a `key` has to be provided. It is compared with every valid key stored in CAM. If there is a hit, |
| 149 | + a value is read. There can be many instances of the same key in CAM. In such case it is undefined |
| 150 | + which value will be read. |
| 151 | +
|
| 152 | +
|
| 153 | + .. warning:: |
| 154 | + Pushing the value with index already present in CAM is an undefined behaviour. |
| 155 | +
|
| 156 | + Attributes |
| 157 | + ---------- |
| 158 | + read : Method |
| 159 | + Nondestructive read |
| 160 | + write : Method |
| 161 | + If index present - do update |
| 162 | + remove : Method |
| 163 | + Remove |
| 164 | + push : Method |
| 165 | + Inserts new data. |
| 166 | + """ |
| 167 | + |
| 168 | + def __init__(self, address_layout: MethodLayout, data_layout: MethodLayout, entries_number: int): |
| 169 | + """ |
| 170 | + Parameters |
| 171 | + ---------- |
| 172 | + address_layout : LayoutLike |
| 173 | + The layout of the address records. |
| 174 | + data_layout : LayoutLike |
| 175 | + The layout of the data. |
| 176 | + entries_number : int |
| 177 | + The number of slots to create in memory. |
| 178 | + """ |
| 179 | + self.address_layout = from_method_layout(address_layout) |
| 180 | + self.data_layout = from_method_layout(data_layout) |
| 181 | + self.entries_number = entries_number |
| 182 | + |
| 183 | + self.read = Method(i=[("addr", self.address_layout)], o=[("data", self.data_layout), ("not_found", 1)]) |
| 184 | + self.remove = Method(i=[("addr", self.address_layout)]) |
| 185 | + self.push = Method(i=[("addr", self.address_layout), ("data", self.data_layout)]) |
| 186 | + self.write = Method(i=[("addr", self.address_layout), ("data", self.data_layout)], o=[("not_found", 1)]) |
| 187 | + |
| 188 | + def elaborate(self, platform) -> TModule: |
| 189 | + m = TModule() |
| 190 | + |
| 191 | + address_array = Array( |
| 192 | + [Signal(self.address_layout, name=f"address_array_{i}") for i in range(self.entries_number)] |
| 193 | + ) |
| 194 | + data_array = Array([Signal(self.data_layout, name=f"data_array_{i}") for i in range(self.entries_number)]) |
| 195 | + valids = Signal(self.entries_number, name="valids") |
| 196 | + |
| 197 | + m.submodules.encoder_read = encoder_read = MultiPriorityEncoder(self.entries_number, 1) |
| 198 | + m.submodules.encoder_write = encoder_write = MultiPriorityEncoder(self.entries_number, 1) |
| 199 | + m.submodules.encoder_push = encoder_push = MultiPriorityEncoder(self.entries_number, 1) |
| 200 | + m.submodules.encoder_remove = encoder_remove = MultiPriorityEncoder(self.entries_number, 1) |
| 201 | + m.d.top_comb += encoder_push.input.eq(~valids) |
| 202 | + |
| 203 | + @def_method(m, self.push, ready=~valids.all()) |
| 204 | + def _(addr, data): |
| 205 | + id = Signal(range(self.entries_number), name="id_push") |
| 206 | + m.d.top_comb += id.eq(encoder_push.outputs[0]) |
| 207 | + m.d.sync += address_array[id].eq(addr) |
| 208 | + m.d.sync += data_array[id].eq(data) |
| 209 | + m.d.sync += valids.bit_select(id, 1).eq(1) |
| 210 | + |
| 211 | + @def_method(m, self.write) |
| 212 | + def _(addr, data): |
| 213 | + write_mask = Signal(self.entries_number, name="write_mask") |
| 214 | + m.d.top_comb += write_mask.eq(Cat([addr == stored_addr for stored_addr in address_array]) & valids) |
| 215 | + m.d.top_comb += encoder_write.input.eq(write_mask) |
| 216 | + with m.If(write_mask.any()): |
| 217 | + m.d.sync += data_array[encoder_write.outputs[0]].eq(data) |
| 218 | + return {"not_found": ~write_mask.any()} |
| 219 | + |
| 220 | + @def_method(m, self.read) |
| 221 | + def _(addr): |
| 222 | + read_mask = Signal(self.entries_number, name="read_mask") |
| 223 | + m.d.top_comb += read_mask.eq(Cat([addr == stored_addr for stored_addr in address_array]) & valids) |
| 224 | + m.d.top_comb += encoder_read.input.eq(read_mask) |
| 225 | + return {"data": data_array[encoder_read.outputs[0]], "not_found": ~read_mask.any()} |
| 226 | + |
| 227 | + @def_method(m, self.remove) |
| 228 | + def _(addr): |
| 229 | + rm_mask = Signal(self.entries_number, name="rm_mask") |
| 230 | + m.d.top_comb += rm_mask.eq(Cat([addr == stored_addr for stored_addr in address_array]) & valids) |
| 231 | + m.d.top_comb += encoder_remove.input.eq(rm_mask) |
| 232 | + with m.If(rm_mask.any()): |
| 233 | + m.d.sync += valids.bit_select(encoder_remove.outputs[0], 1).eq(0) |
| 234 | + |
| 235 | + return m |
| 236 | + |
| 237 | + |
141 | 238 | class AsyncMemoryBank(Elaboratable): |
142 | 239 | """AsyncMemoryBank module. |
143 | 240 |
|
|
0 commit comments