-
Notifications
You must be signed in to change notification settings - Fork 12
Interface definition library #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
How does this fit into Python's type system? Ideally it could be used for verification. |
I agree. I'm still exploring options here, so I have no definite answer for you. I am definitely going to make sure that whatever solution we end up with can fit into Python's type system. |
5f6fd38
to
a5a5030
Compare
This is an interesting approach! Trying to come-up with an example: we could end-up with something like this? input_port_template = Signature({
"data": In[unsigned(4)],
"vld": In[Signal(1)],
"rdy": Out[Signal(1)]
})
output_port_template = Signature({
"data": Out[unsigned(4)],
"vld": Out[Signal(1)],
"rdy": In[Signal(1)]
})
class DebugTap(Elaboratable):
def __init__(self):
self.i = Interface(input_port_template)
self.o = Interface(output_port_template)
self.debug = Interface(output_port_template)
def elaborate(self, platform):
m = Module()
# pass data from input port to output port
m.d.comb += self.o.eq(self.i)
# also pass it to an extra debug port
m.d.comb += self.debug.eq(self.i) |
The
With the Was it voluntary to avoid small wrapper classes? class In(Flow):
def __init__(self, signature):
super().__init__(self, Flow.In, signature):
class Out(Flow):
def __init__(self, signature):
super().__init__(self, Flow.Out, signature): Then they look like some wrapper around a shape, adding a direction to them. input_port_template = Signature({
"data": In(unsigned(4)),
"vld": In(Signal(1)),
"rdy": Out(Signal(1))
}) |
Why is there both Interface and Signature? What is Interface adding? |
I understand I think the use-case is to be able to define a Signature for commonplace internal ports like Wishbone, FIFOs, valid/ready, etc. and then plug multiple instances of these. Multiple |
Yep! |
A Maybe it is possible to have signatures defined as classes (inheriting from a Something a bit like how class WishbonePort(data.Interface):
dat_r: In(Signal(32))
addr: In(Signal(32))
stb: In(Signal(1))
ack: Out(Signal(1))
... That looks good on paper, but: |
It's simpler. Any class that has a compatible interface matches a Signature. It doesn't have to be an |
Ah, I think I'm starting to see. Signature (or its derivatives) are objects that hold the prototype of the composite port. Interface(signature) is an object instance that essentially replaces the pile-of-signals and give you pluggability. It could be an instanciation method on the signature object, that's just a matter of taste. |
Indeed, there is a possible confusion for users who would try (A):
While it would need to be (B):
I suppose (B) which adds more types was chosen instead of (A) for some reason? Just trying to spot what I might have missed. For anyone who would prefer (A), it would likely be possible to implement (B) as part of Interface's |
Answers to my own question: "Why is Signature in addition to Interface?": The flow! I imagine it would be possible to have a same |
Yeah |
I am trying to summarize a typical usage for that RFC: # defining the controller side in the signature by convention?
spi_signature = Signature({
"cs": Out[Signal(1)],
"copi": Out[Signal(1)],
"cipo": In[Signal(1)],
"clk": Out[Signal(1)],
})
class SpiControllerInterface(Interface):
def __init__(self):
super().__init__(spi_signature)
class SpiPeripheralInterface(Interface):
def __init__(self):
super().__init__(~spi_signature) This looks rather minimal. And if we add extra helpers: # defining the controller side in the signature by convention?
spi_signature = Signature({
"cs": Out[Signal(1)],
"copi": Out[Signal(1)],
"cipo": In[Signal(1)],
"clk": Out[Signal(1)],
})
class SpiControllerInterface(Interface):
def __init__(self):
super().__init__(spi_signature)
# for simulation:
def read_byte(self, data):
pass
def write_byte(self, data):
pass
class SpiPeripheralInterface(Interface):
def __init__(self):
super().__init__(~spi_signature)
# for simulation:
def transfer_callback(self, fn):
self.simulation_transfer_callback = fn Maybe it would be convenient to turn a Resource into a Signature (or rather, the other way around?) to share data formats between those. |
Resources will be more or less replaced by Signatures at a later point. |
Here is my attempt at using Records in a way that looks like Interfaces/Signatures, class SPISignature(Record):
def __init__(self, *, ctrl, peri):
return super().__init__([
("clk", 1, ctrl), # ctrl and peri here are to
("copi", 1, ctrl), # tell who drives the signal
("cipo", 1, peri),
("cs", 1, ctrl),
])
class SPICtrlInterface(SPISignature):
def __init__(self):
super().__init__(ctrl=DIR_FANOUT, peri=DIR_FANIN)
def spi_ctrl_methods(self):
pass
class SPIPeriInterface(SPISignature):
def __init__(self):
super().__init__(peri=DIR_FANOUT, ctrl=DIR_FANIN)
def spi_peri_methods(self):
pass |
82ff052
to
409b1b9
Compare
EDIT: answered or made obsolete by 53e5dc3.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have some (very) minor comments from a read through of the changes since last week.
text/0002-interfaces.md
Outdated
* the `.shape` property raise `TypeError`; | ||
* the `.reset` property raise `TypeError`; | ||
* the `.signature` property return `signature`. | ||
* `member.flip()` returns a `flipped_member` where `flipped_member.flow` == `~member.flow`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* `member.flip()` returns a `flipped_member` where `flipped_member.flow` == `~member.flow`. | |
* `member.flip()` returns a `flipped_member` where `flipped_member.flow == member.flow.flip()`. |
text/0002-interfaces.md
Outdated
* For every port member with a given `path` and `first_shape` in `first`, there is a port member with the same `path` in each of `rest` with shape `rest_shape`, where `Shape.cast(first_shape) == Shape.cast(rest_shape)`, and there are no other members in any of `rest`. In other words, the width and signedness of all of port members must be equal, but the shape-castable objects specifying the width and signedness do not have to be. | ||
* It is expected that any mismatch in signatures will be resolved (through wrappers, mutating signature members, or otherwise) before interfaces are being connected. | ||
* Either: | ||
1. There is exactly one interface in `rest`, and for every pair of members `first_member` and `rest_member`, `first_member.flow == ~rest_member.flow`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1. There is exactly one interface in `rest`, and for every pair of members `first_member` and `rest_member`, `first_member.flow == ~rest_member.flow`. | |
1. There is exactly one interface in `rest`, and for every pair of members `first_member` and `rest_member`, `first_member.flow == rest_member.flow.flip()`. |
text/0002-interfaces.md
Outdated
* It is expected that any mismatch in signatures will be resolved (through wrappers, mutating signature members, or otherwise) before interfaces are being connected. | ||
* Either: | ||
1. There is exactly one interface in `rest`, and for every pair of members `first_member` and `rest_member`, `first_member.flow == ~rest_member.flow`. | ||
2. There is any amount of interfaces in `rest`, and for every pair of members `first_member` and `rest_member`, `first_member.flow == Out` and `rest_member.flow == In`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could also be stated non-pairwise.
text/0002-interfaces.md
Outdated
1. There is exactly one interface in `rest`, and for every pair of members `first_member` and `rest_member`, `first_member.flow == ~rest_member.flow`. | ||
2. There is any amount of interfaces in `rest`, and for every pair of members `first_member` and `rest_member`, `first_member.flow == Out` and `rest_member.flow == In`. | ||
|
||
In both of the cases (1) and (2), the function, for each pair of an input port and an output of port with the same path, the function connects these as follows: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is worth nothing that, depending on the CSS in use, (1)
and (2)
here may not resemble the list item numbers above. Here they're rendered i.
and ii.
since they're second-level list items. It may be clearer to say "In both of these cases", and/or to indent this text and the code block at the same level as the sublist as follows:
...
-
Either:
- There is exactly one interface in
rest
, and for every pair of membersfirst_member
andrest_member
,first_member.flow == rest_member.flow.flip()
. - There is any amount of interfaces in
rest
, and for every pair of membersfirst_member
andrest_member
,first_member.flow == Out
andrest_member.flow == In
.
In both of these cases, the function, for each pair of an input port and an output of port with the same path, the function connects these as follows:
m.d.comb += output_port.eq(input_port)
- There is exactly one interface in
Drawbacks
...
text/0002-interfaces.md
Outdated
|
||
In both of the cases (1) and (2), the function, for each pair of an input port and an output of port with the same path, the function connects these as follows: | ||
```python | ||
m.d.comb += output_port.eq(input_port) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the overloading of "out"/"output" and "in"/"input" in this illustrative code block has great potential to confuse — e.g. it's the output stream of SequenceSource
(which is described in the source with the member Out[16]
) from the above example which would be the input_port
here, and it's the input stream of the NumberSink
(described with In[16]
in its signature) which is the output_port
.
In the context of knowing only fan-out is supported (as noted later in § Future work), this makes sense, but in the context of case (2) saying "if you have multiple rest
interfaces, all their members must be Flow.In
", it may read as if there would be multiple input_port
s and thus some kind of fan-in instead.
text/0002-interfaces.md
Outdated
|
||
- Do nothing. `Record` will continue to be used alongside the continued proliferation of ad-hoc implementations of similar functionality, and continue to impair the use of Amaranth components together. | ||
|
||
- Replace the `interface.lib.module.connect` free function with a function `Interface.connect` or a function `amaranth.hdl.dsl.Module.connect`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Replace the `interface.lib.module.connect` free function with a function `Interface.connect` or a function `amaranth.hdl.dsl.Module.connect`. | |
- Replace the `amaranth.lib.module.connect` free function with a function `Interface.connect` or a function `amaranth.hdl.dsl.Module.connect`. |
Also, this RFC no longer proposes an Interface
class, and Signature.connect
wouldn't work.
78f8c38
to
503ab88
Compare
Co-authored-by: Charlotte <[email protected]>
Co-authored-by: Charlotte <[email protected]>
We have discussed this RFC on the 2023-08-21 weekly meeting. The disposition was to merge. |
Co-authored-by: Charlotte <[email protected]>
Co-authored-by: Charlotte <[email protected]>
Co-authored-by: Charlotte <[email protected]>
Co-authored-by: Charlotte <[email protected]>
Co-authored-by: Charlotte <[email protected]>
Co-authored-by: Charlotte <[email protected]>
Co-authored-by: Charlotte <[email protected]>
Co-authored-by: Charlotte <[email protected]>
Co-authored-by: Charlotte <[email protected]>
Co-authored-by: Charlotte <[email protected]>
Rendered