|
| 1 | +- Start Date: 2024-01-22 |
| 2 | +- RFC PR: [amaranth-lang/rfcs#40](https://github.com/amaranth-lang/rfcs/pull/40) |
| 3 | +- Amaranth Issue: [amaranth-lang/amaranth#1048](https://github.com/amaranth-lang/amaranth/issues/1048) |
| 4 | + |
| 5 | +# Arbitrary `Memory` shapes |
| 6 | + |
| 7 | +## Summary |
| 8 | +[summary]: #summary |
| 9 | + |
| 10 | +Extend `Memory` to support arbitrary element shapes. |
| 11 | + |
| 12 | +## Motivation |
| 13 | +[motivation]: #motivation |
| 14 | + |
| 15 | +`Memory` currently only supports plain unsigned elements, with the width set by the `width` argument. |
| 16 | +Extending this to allow arbitrary shapes eliminates the need for manual conversion when used to store signed data and value-castables. |
| 17 | + |
| 18 | +## Guide-level explanation |
| 19 | +[guide-level-explanation]: #guide-level-explanation |
| 20 | + |
| 21 | +The `width` argument to `Memory()` is replaced with `shape`, accepting anything that is `ShapeLike`. |
| 22 | +Since a plain bit width is `ShapeLike`, this is a direct superset of existing functionality. |
| 23 | + |
| 24 | +If `shape` is shape-castable, each element passed to the `init` argument is passed through `shape.const()`. |
| 25 | + |
| 26 | +Example: |
| 27 | +```python |
| 28 | +RGB = StructLayout({"r": 8, "g": 8, "b": 8}) |
| 29 | + |
| 30 | +palette = Memory(shape = RGB, depth = 16, init = [ |
| 31 | + {"r": 0, "g": 0, "b": 0}, |
| 32 | + {"r": 255, "g": 0, "b": 0}, |
| 33 | + # ... |
| 34 | +]) |
| 35 | +``` |
| 36 | + |
| 37 | +## Reference-level explanation |
| 38 | +[reference-level-explanation]: #reference-level-explanation |
| 39 | + |
| 40 | +`Memory.__init__()` gets a new `shape` argument, accepting any `ShapeLike`. |
| 41 | + |
| 42 | +The `width` argument to `Memory.__init__()` deprecated and removed in a later Amaranth version. Passing both `width` and `shape` is an error. |
| 43 | + |
| 44 | +The `Memory.shape` attribute is added. |
| 45 | + |
| 46 | +The `Memory.width` attribute is made a read-only wrapper for `Shape.cast(self.shape).width`. |
| 47 | + |
| 48 | +The `Memory.depth` attribute is made read-only. |
| 49 | + |
| 50 | +`ReadPort.data` and `WritePort.data` are updated to be `Signal(memory.shape)`. |
| 51 | + |
| 52 | +`WritePort.__init__()` raises an exception if `granularity` is specified and `shape` is not an unsigned `Shape`. |
| 53 | + |
| 54 | +`DummyPort.__init__()` gets a new `data_shape` argument. `data_width` is deprecated and removed in a later Amaranth version. |
| 55 | + |
| 56 | +## Drawbacks |
| 57 | +[drawbacks]: #drawbacks |
| 58 | + |
| 59 | +Churn. |
| 60 | + |
| 61 | +## Rationale and alternatives |
| 62 | +[rationale-and-alternatives]: #rationale-and-alternatives |
| 63 | + |
| 64 | +- This could also be accomplished by adding a wrapper around `Memory`. |
| 65 | + - A wrapper would result in more code to maintain than simply updating `Memory`, since both the memory object itself and the port objects would have to be wrapped. |
| 66 | + |
| 67 | +## Prior art |
| 68 | +[prior-art]: #prior-art |
| 69 | + |
| 70 | +Being able to make a `Memory` with an arbitrary element shape is analogous to being able to make an array with an arbitrary element type in any high level programming language. |
| 71 | + |
| 72 | +## Unresolved questions |
| 73 | +[unresolved-questions]: #unresolved-questions |
| 74 | + |
| 75 | +None. |
| 76 | + |
| 77 | +## Future possibilities |
| 78 | +[future-possibilities]: #future-possibilities |
| 79 | + |
| 80 | +- Once `Memory` is extended to support arbitrary shapes, it is natural that higher level constructs building on `Memory` like FIFOs gets the same treatment. |
| 81 | + |
| 82 | +- `granularity` could later be allowed to be used with other kinds of shapes. |
| 83 | + - This is desirable for e.g. `lib.data.ArrayLayout`, but is not currently possible since `Memory` lives in `hdl.mem`, and `hdl` can't depend on `lib`. |
0 commit comments