|
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