|
| 1 | +- Start Date: 2025-06-02 |
| 2 | +- RFC PR: [amaranth-lang/rfcs#79](https://github.com/amaranth-lang/rfcs/pull/79) |
| 3 | +- Amaranth Issue: [amaranth-lang/amaranth#0000](https://github.com/amaranth-lang/amaranth/issues/0000) |
| 4 | + |
| 5 | +# Add `PortLike.with_direction` |
| 6 | + |
| 7 | +## Summary |
| 8 | +[summary]: #summary |
| 9 | + |
| 10 | +Add a `with_direction` method to `lib.io.PortLike`, allowing for "downcasting" a bidirectional port to an input port or an output port. |
| 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 generic and reusable I/O components. Many such components can be parametrized by the "signature" of I/O ports used, including their directionality. One such motivating example is Glasgow's `IOStreamer`. |
| 16 | + |
| 17 | +It is often convenient to treat a bidirectional port as an input-only port, or an output-only port. `lib.io` already allows such usage by instantiating a `*Buffer` with a requested direction. However, for generic components, it's oftern convenient to determine requested buffer direction by introspecting the provided port — in such case, we need some means to explicitly "downcast" the port before providing it to the component. |
| 18 | + |
| 19 | +## Guide-level explanation |
| 20 | +[guide-level-explanation]: #guide-level-explanation |
| 21 | + |
| 22 | +Given a `PortLike` with a direction of `Direction.Bidir`, you can use the `with_direction` method to obtain an input-only or output-only version of it: |
| 23 | + |
| 24 | +``` |
| 25 | +port_io = platform.request("io0", dir="-") |
| 26 | +assert port_io.direction == Direction.Bidir |
| 27 | +port_i = port_io.with_direction("i") |
| 28 | +assert port_i.direction == Direction.Input |
| 29 | +``` |
| 30 | + |
| 31 | +## Reference-level explanation |
| 32 | +[reference-level-explanation]: #reference-level-explanation |
| 33 | + |
| 34 | +A new method is added to `PortLike` and its implementations: |
| 35 | + |
| 36 | +- `with_direction(self, direction: Direction) -> PortLike`: returns a new `PortLike` equivalent to this one, but with a limitted direction |
| 37 | + - if the `direction` provided is the same as the port's direction, the returned `PortLike` is fully equivalent to `self` |
| 38 | + - if the `direction` is `Direction.Input` or `Direction.Output` and `self.direction is Direction.Bidir`, the returned `PortLike` has the given direction |
| 39 | + - any other combination of `direction` and `self.direction` is an error |
| 40 | + |
| 41 | +## Drawbacks |
| 42 | +[drawbacks]: #drawbacks |
| 43 | + |
| 44 | +This adds a new required method to implement on a `PortLike`, and is thus a breaking change for users implementing their own `PortLike`s. |
| 45 | + |
| 46 | +## Rationale and alternatives |
| 47 | +[rationale-and-alternatives]: #rationale-and-alternatives |
| 48 | + |
| 49 | +The only obvious alternative to this RFC is providing the information to the generic component out-of-band. However, this is inconvenient, particularly when groups of ports are involved. |
| 50 | + |
| 51 | +We can have the usual bikeshedding about the name. |
| 52 | + |
| 53 | +## Prior art |
| 54 | +[prior-art]: #prior-art |
| 55 | + |
| 56 | +Most languages allow using an `inout` as an `input` or `output`. |
| 57 | + |
| 58 | +## Unresolved questions |
| 59 | +[unresolved-questions]: #unresolved-questions |
| 60 | + |
| 61 | +None. |
| 62 | + |
| 63 | +## Future possibilities |
| 64 | +[future-possibilities]: #future-possibilities |
| 65 | + |
| 66 | +None. |
0 commit comments