|
1 | 1 | import operator
|
2 |
| -from collections import OrderedDict |
3 | 2 | from collections.abc import MutableSequence
|
4 | 3 |
|
5 | 4 | from .. import tracer
|
6 | 5 | from ._ast import *
|
7 |
| -from ._ir import Elaboratable, Fragment, AlreadyElaborated |
| 6 | +from ._ir import Fragment, AlreadyElaborated |
8 | 7 | from ..utils import ceil_log2
|
9 |
| -from .._utils import deprecated, final |
| 8 | +from .._utils import final |
10 | 9 |
|
11 | 10 |
|
12 |
| -__all__ = ["MemoryData", "Memory", "ReadPort", "WritePort", "DummyPort"] |
| 11 | +__all__ = ["MemoryData", "MemoryInstance"] |
13 | 12 |
|
14 | 13 |
|
15 | 14 | @final
|
@@ -270,260 +269,3 @@ def write_port(self, *, domain, addr, data, en):
|
270 | 269 | assert len(port._addr) == ceil_log2(self._data.depth)
|
271 | 270 | self._write_ports.append(port)
|
272 | 271 | return len(self._write_ports) - 1
|
273 |
| - |
274 |
| - |
275 |
| -class Memory(Elaboratable): |
276 |
| - """A word addressable storage. |
277 |
| -
|
278 |
| - Parameters |
279 |
| - ---------- |
280 |
| - width : int |
281 |
| - Access granularity. Each storage element of this memory is ``width`` bits in size. |
282 |
| - depth : int |
283 |
| - Word count. This memory contains ``depth`` storage elements. |
284 |
| - init : list of int |
285 |
| - Initial values. At power on, each storage element in this memory is initialized to |
286 |
| - the corresponding element of ``init``, if any, or to zero otherwise. |
287 |
| - Uninitialized memories are not currently supported. |
288 |
| - name : str |
289 |
| - Name hint for this memory. If ``None`` (default) the name is inferred from the variable |
290 |
| - name this ``Signal`` is assigned to. |
291 |
| - attrs : dict |
292 |
| - Dictionary of synthesis attributes. |
293 |
| -
|
294 |
| - Attributes |
295 |
| - ---------- |
296 |
| - width : int |
297 |
| - depth : int |
298 |
| - init : list of int |
299 |
| - attrs : dict |
300 |
| - """ |
301 |
| - # TODO(amaranth-0.6): remove |
302 |
| - @deprecated("`amaranth.hdl.Memory` is deprecated, use `amaranth.lib.memory.Memory` instead") |
303 |
| - def __init__(self, *, width, depth, init=None, name=None, attrs=None, simulate=True): |
304 |
| - if not isinstance(width, int) or width < 0: |
305 |
| - raise TypeError("Memory width must be a non-negative integer, not {!r}" |
306 |
| - .format(width)) |
307 |
| - if not isinstance(depth, int) or depth < 0: |
308 |
| - raise TypeError("Memory depth must be a non-negative integer, not {!r}" |
309 |
| - .format(depth)) |
310 |
| - |
311 |
| - self.name = name or tracer.get_var_name(depth=3, default="$memory") |
312 |
| - self.src_loc = tracer.get_src_loc(src_loc_at=1) |
313 |
| - |
314 |
| - self.width = width |
315 |
| - self.depth = depth |
316 |
| - self.attrs = OrderedDict(() if attrs is None else attrs) |
317 |
| - |
318 |
| - self._read_ports = [] |
319 |
| - self._write_ports = [] |
320 |
| - self._data = MemoryData(shape=width, depth=depth, init=init or []) |
321 |
| - |
322 |
| - @property |
323 |
| - def init(self): |
324 |
| - return self._data.init |
325 |
| - |
326 |
| - @init.setter |
327 |
| - def init(self, new_init): |
328 |
| - self._data.init = new_init |
329 |
| - |
330 |
| - def read_port(self, *, src_loc_at=0, **kwargs): |
331 |
| - """Get a read port. |
332 |
| -
|
333 |
| - See :class:`ReadPort` for details. |
334 |
| -
|
335 |
| - Arguments |
336 |
| - --------- |
337 |
| - domain : str |
338 |
| - transparent : bool |
339 |
| -
|
340 |
| - Returns |
341 |
| - ------- |
342 |
| - An instance of :class:`ReadPort` associated with this memory. |
343 |
| - """ |
344 |
| - return ReadPort(self, src_loc_at=1 + src_loc_at, **kwargs) |
345 |
| - |
346 |
| - def write_port(self, *, src_loc_at=0, **kwargs): |
347 |
| - """Get a write port. |
348 |
| -
|
349 |
| - See :class:`WritePort` for details. |
350 |
| -
|
351 |
| - Arguments |
352 |
| - --------- |
353 |
| - domain : str |
354 |
| - granularity : int |
355 |
| -
|
356 |
| - Returns |
357 |
| - ------- |
358 |
| - An instance of :class:`WritePort` associated with this memory. |
359 |
| - """ |
360 |
| - return WritePort(self, src_loc_at=1 + src_loc_at, **kwargs) |
361 |
| - |
362 |
| - def __getitem__(self, index): |
363 |
| - return self._data[index] |
364 |
| - |
365 |
| - def elaborate(self, platform): |
366 |
| - f = MemoryInstance(data=self._data, attrs=self.attrs, src_loc=self.src_loc) |
367 |
| - write_ports = {} |
368 |
| - for port in self._write_ports: |
369 |
| - port._MustUse__used = True |
370 |
| - iport = f.write_port(domain=port.domain, addr=port.addr, data=port.data, en=port.en) |
371 |
| - write_ports.setdefault(port.domain, []).append(iport) |
372 |
| - for port in self._read_ports: |
373 |
| - port._MustUse__used = True |
374 |
| - if port.domain == "comb": |
375 |
| - f.read_port(domain="comb", addr=port.addr, data=port.data, en=Const(1), transparent_for=()) |
376 |
| - else: |
377 |
| - transparent_for = [] |
378 |
| - if port.transparent: |
379 |
| - transparent_for = write_ports.get(port.domain, []) |
380 |
| - f.read_port(domain=port.domain, addr=port.addr, data=port.data, en=port.en, transparent_for=transparent_for) |
381 |
| - return f |
382 |
| - |
383 |
| - |
384 |
| -class ReadPort(Elaboratable): |
385 |
| - """A memory read port. |
386 |
| -
|
387 |
| - Parameters |
388 |
| - ---------- |
389 |
| - memory : :class:`Memory` |
390 |
| - Memory associated with the port. |
391 |
| - domain : str |
392 |
| - Clock domain. Defaults to ``"sync"``. If set to ``"comb"``, the port is asynchronous. |
393 |
| - Otherwise, the read data becomes available on the next clock cycle. |
394 |
| - transparent : bool |
395 |
| - Port transparency. If set (default), a read at an address that is also being written to in |
396 |
| - the same clock cycle will output the new value. Otherwise, the old value will be output |
397 |
| - first. This behavior only applies to ports in the same domain. |
398 |
| -
|
399 |
| - Attributes |
400 |
| - ---------- |
401 |
| - memory : :class:`Memory` |
402 |
| - domain : str |
403 |
| - transparent : bool |
404 |
| - addr : Signal(range(memory.depth)), in |
405 |
| - Read address. |
406 |
| - data : Signal(memory.width), out |
407 |
| - Read data. |
408 |
| - en : Signal or Const, in |
409 |
| - Read enable. If asserted, ``data`` is updated with the word stored at ``addr``. |
410 |
| -
|
411 |
| - Exceptions |
412 |
| - ---------- |
413 |
| - Raises :exn:`ValueError` if the read port is simultaneously asynchronous and non-transparent. |
414 |
| - """ |
415 |
| - def __init__(self, memory, *, domain="sync", transparent=True, src_loc_at=0): |
416 |
| - if domain == "comb" and not transparent: |
417 |
| - raise ValueError("Read port cannot be simultaneously asynchronous and non-transparent") |
418 |
| - |
419 |
| - self.memory = memory |
420 |
| - self.domain = domain |
421 |
| - self.transparent = transparent |
422 |
| - |
423 |
| - self.addr = Signal(range(memory.depth), |
424 |
| - name=f"{memory.name}_r_addr", src_loc_at=1 + src_loc_at) |
425 |
| - self.data = Signal(memory.width, |
426 |
| - name=f"{memory.name}_r_data", src_loc_at=1 + src_loc_at) |
427 |
| - if self.domain != "comb": |
428 |
| - self.en = Signal(name=f"{memory.name}_r_en", init=1, |
429 |
| - src_loc_at=1 + src_loc_at) |
430 |
| - else: |
431 |
| - self.en = Const(1) |
432 |
| - |
433 |
| - memory._read_ports.append(self) |
434 |
| - |
435 |
| - def elaborate(self, platform): |
436 |
| - if self is self.memory._read_ports[0]: |
437 |
| - return self.memory |
438 |
| - else: |
439 |
| - return Fragment() |
440 |
| - |
441 |
| - |
442 |
| -class WritePort(Elaboratable): |
443 |
| - """A memory write port. |
444 |
| -
|
445 |
| - Parameters |
446 |
| - ---------- |
447 |
| - memory : :class:`Memory` |
448 |
| - Memory associated with the port. |
449 |
| - domain : str |
450 |
| - Clock domain. Defaults to ``"sync"``. Writes have a latency of 1 clock cycle. |
451 |
| - granularity : int |
452 |
| - Port granularity. Defaults to ``memory.width``. Write data is split evenly in |
453 |
| - ``memory.width // granularity`` chunks, which can be updated independently. |
454 |
| -
|
455 |
| - Attributes |
456 |
| - ---------- |
457 |
| - memory : :class:`Memory` |
458 |
| - domain : str |
459 |
| - granularity : int |
460 |
| - addr : Signal(range(memory.depth)), in |
461 |
| - Write address. |
462 |
| - data : Signal(memory.width), in |
463 |
| - Write data. |
464 |
| - en : Signal(memory.width // granularity), in |
465 |
| - Write enable. Each bit selects a non-overlapping chunk of ``granularity`` bits on the |
466 |
| - ``data`` signal, which is written to memory at ``addr``. Unselected chunks are ignored. |
467 |
| -
|
468 |
| - Exceptions |
469 |
| - ---------- |
470 |
| - Raises :exn:`ValueError` if the write port granularity is greater than memory width, or does not |
471 |
| - divide memory width evenly. |
472 |
| - """ |
473 |
| - def __init__(self, memory, *, domain="sync", granularity=None, src_loc_at=0): |
474 |
| - if granularity is None: |
475 |
| - granularity = memory.width |
476 |
| - if not isinstance(granularity, int) or granularity < 0: |
477 |
| - raise TypeError("Write port granularity must be a non-negative integer, not {!r}" |
478 |
| - .format(granularity)) |
479 |
| - if granularity > memory.width: |
480 |
| - raise ValueError("Write port granularity must not be greater than memory width " |
481 |
| - "({} > {})" |
482 |
| - .format(granularity, memory.width)) |
483 |
| - if memory.width // granularity * granularity != memory.width: |
484 |
| - raise ValueError("Write port granularity must divide memory width evenly") |
485 |
| - |
486 |
| - self.memory = memory |
487 |
| - self.domain = domain |
488 |
| - self.granularity = granularity |
489 |
| - |
490 |
| - self.addr = Signal(range(memory.depth), |
491 |
| - name=f"{memory.name}_w_addr", src_loc_at=1 + src_loc_at) |
492 |
| - self.data = Signal(memory.width, |
493 |
| - name=f"{memory.name}_w_data", src_loc_at=1 + src_loc_at) |
494 |
| - self.en = Signal(memory.width // granularity, |
495 |
| - name=f"{memory.name}_w_en", src_loc_at=1 + src_loc_at) |
496 |
| - |
497 |
| - memory._write_ports.append(self) |
498 |
| - |
499 |
| - def elaborate(self, platform): |
500 |
| - if not self.memory._read_ports and self is self.memory._write_ports[0]: |
501 |
| - return self.memory |
502 |
| - else: |
503 |
| - return Fragment() |
504 |
| - |
505 |
| - |
506 |
| -class DummyPort: |
507 |
| - """Dummy memory port. |
508 |
| -
|
509 |
| - This port can be used in place of either a read or a write port for testing and verification. |
510 |
| - It does not include any read/write port specific attributes, i.e. none besides ``"domain"``; |
511 |
| - any such attributes may be set manually. |
512 |
| - """ |
513 |
| - # TODO(amaranth-0.6): remove |
514 |
| - @deprecated("`DummyPort` is deprecated, use `amaranth.lib.memory.ReadPort` or " |
515 |
| - "`amaranth.lib.memory.WritePort` instead") |
516 |
| - def __init__(self, *, data_width, addr_width, domain="sync", name=None, granularity=None): |
517 |
| - self.domain = domain |
518 |
| - |
519 |
| - if granularity is None: |
520 |
| - granularity = data_width |
521 |
| - if name is None: |
522 |
| - name = tracer.get_var_name(depth=3, default="dummy") |
523 |
| - |
524 |
| - self.addr = Signal(addr_width, |
525 |
| - name=f"{name}_addr", src_loc_at=1) |
526 |
| - self.data = Signal(data_width, |
527 |
| - name=f"{name}_data", src_loc_at=1) |
528 |
| - self.en = Signal(data_width // granularity, |
529 |
| - name=f"{name}_en", src_loc_at=1) |
0 commit comments