|
| 1 | +- Start Date: 2025-06-02 |
| 2 | +- RFC PR: [amaranth-lang/rfcs#78](https://github.com/amaranth-lang/rfcs/pull/78) |
| 3 | +- Amaranth Issue: [amaranth-lang/amaranth#0000](https://github.com/amaranth-lang/amaranth/issues/0000) |
| 4 | + |
| 5 | +# Internal I/O ports |
| 6 | + |
| 7 | +## Summary |
| 8 | +[summary]: #summary |
| 9 | + |
| 10 | +`SimulationPort` is renamed to `InternalPort`, and using it outside of simulation is allowed. It allows for connecting I/O port-expecting components to internal signals instead of external I/O ports. |
| 11 | + |
| 12 | +## Motivation |
| 13 | +[motivation]: #motivation |
| 14 | + |
| 15 | +[RFC 55](https://amaranth-lang.org/rfcs/0055-lib-io.html) has made way for a variety of reusable I/O components, capable of attaching to arbitrary `PortLike`s. However, their reusability is limitted by requiring external ports for all of their interface. This limitation comes up in two scenerios: |
| 16 | + |
| 17 | +1. The component is used internally, to interface with other gateware within the same design. |
| 18 | +2. One of the optional pins of the I/O interface is unused and should be discarded (for outputs), or tied to a constant value (for inputs). |
| 19 | + |
| 20 | +Having a way to connect such gateware to internal signals resolves both issues. Since we already have such capability in simulation, we only need to extend it to allow synthesis usage. |
| 21 | + |
| 22 | +## Guide-level and reference-level explanation |
| 23 | +[guide-level-explanation]: #guide-level-explanation |
| 24 | + |
| 25 | +`lib.io.SimulationPort` is renamed to `lib.io.InternalPort`. The old name is deprecated and will be removed in Amaranth 0.8. |
| 26 | + |
| 27 | +Use of `InternalPort` is allowed with `lib.io.Buffer` and `lib.io.FFBuffer` regardless of the platform (and bypassing the platform hooks). |
| 28 | + |
| 29 | +To simplify tying off unused input ports to constant values, `init=` keyword-only argument is added to the `InternalPort` constructor. Is is passed directly to the `init=` argument of the port's `i` signal. It is an error to use this argument with an output-only port. |
| 30 | + |
| 31 | +## Drawbacks |
| 32 | +[drawbacks]: #drawbacks |
| 33 | + |
| 34 | +- more complex I/O subsystem |
| 35 | +- the tie-off case could be better served by other means |
| 36 | + |
| 37 | +## Rationale and alternatives |
| 38 | +[rationale-and-alternatives]: #rationale-and-alternatives |
| 39 | + |
| 40 | +This is a fairly simple solution leveraging existing code. For the internal connection case, no other design is apparent. For tie-off, there are two other possibilities: |
| 41 | + |
| 42 | +1. Have a separate `TieoffPort` class. |
| 43 | + |
| 44 | + Such class could serve as a marker that the port output will always be discarded, or that input will always have the tieoff value, allowing for simplification in controller logic. |
| 45 | + |
| 46 | + Further, fancy platform-specific buffer types (such as high data rate SERDES) could be trivially instantiated for tie-off ports (by simply doing nothing for discarded output, or replicating the tieoff value for input), which is not in general possible for arbitrary `InternalPort`. |
| 47 | + |
| 48 | +2. Have an out-of-band mechanism that removes the unused port from the component's signature. |
| 49 | + |
| 50 | +## Prior art |
| 51 | +[prior-art]: #prior-art |
| 52 | + |
| 53 | +This RFC reuses existing `SimulationPort` machinery. |
| 54 | + |
| 55 | +Passing around a triple of `(I, O, OE)` is a fairly standard way of connecting an I/O controller to other logic. |
| 56 | + |
| 57 | +## Unresolved questions |
| 58 | +[unresolved-questions]: #unresolved-questions |
| 59 | + |
| 60 | +- how should `DDRBuffer` interact with `InternalPort`, both in simulation and synthesis? |
| 61 | + |
| 62 | +## Future possibilities |
| 63 | +[future-possibilities]: #future-possibilities |
| 64 | + |
| 65 | +None. |
0 commit comments