whitequark changed the topic of #amaranth-lang to: Amaranth hardware definition language · weekly meetings on Mondays at 1700 UTC · code https://github.com/amaranth-lang · logs https://libera.irclog.whitequark.org/amaranth-lang
peepsalot has quit [Server closed connection]
peepsalot has joined #amaranth-lang
Bluefoxicy has quit [Server closed connection]
Bluefoxicy has joined #amaranth-lang
gruetzkopf has quit [Server closed connection]
gruetzkopf has joined #amaranth-lang
jjsuperpower has joined #amaranth-lang
Degi_ has joined #amaranth-lang
Degi has quit [Ping timeout: 240 seconds]
Degi_ is now known as Degi
mithro has quit [Server closed connection]
mithro has joined #amaranth-lang
benreynwar has quit [Server closed connection]
benreynwar has joined #amaranth-lang
Guest67 has joined #amaranth-lang
Guest67 has quit [Quit: Client closed]
jjsuperpower has quit [Ping timeout: 240 seconds]
mindw0rk has quit [Ping timeout: 258 seconds]
mindw0rk has joined #amaranth-lang
josuah has quit [Server closed connection]
nak has joined #amaranth-lang
crzwdjk has joined #amaranth-lang
<whitequark> good evening everyone
<whitequark> it is time for our scheduled meeting
<whitequark> there are two items on the agenda, all of them relatively informal as no RFCs are due to have a disposition chosen for them
<whitequark> the items are: discussing the interfaces RFC, and discussing the CSR bit level register definition interface that @jfng is working on
<whitequark> let's start with the first item, interfaces. to remind, the text of the RFC is: https://github.com/amaranth-lang/rfcs/blob/interfaces/text/0002-interfaces.md
<jfng[m]> i think it's still 1600 utc ?
<whitequark> oh, oops.
<whitequark> okay, it is not time for our scheduled meeting yet then :)
<whitequark> see yall in about a hour
<tpw_rules> is it actually possible to use keyword arguments inside []?
<tpw_rules> all i see is a rejected PEP
<whitequark> hm
<whitequark> it is not :(
<tpw_rules> then can enumeration instances implement __call__?
<whitequark> yes
<whitequark> that was the backup plan if people didn't like the [] syntax
<whitequark> I guess we have to do that direction anyway now
<tpw_rules> could you "index" with a Signal instead if you wanted something more complex than number of bits?
<tpw_rules> it seemed mildly funky, i expect that kind of thing with type annotations and got a little confused when i didn't see those In/Out objects being used as such
<tpw_rules> if you like we can pontificate more during the scheduled meeting time
<tpw_rules> or maybe an Out.like(some signal)
<whitequark> Signals are runtime only though
<whitequark> they never have values at elaboration time
<whitequark> oh, I see, you want type variables?
<tpw_rules> but they don't need values, it would just pull the reset value, signedness, etc. from the passed Signal
<whitequark> Out(some_signal.shape())
<whitequark> I guess we can add .like so that it pulls reset value yeah
<whitequark> don't see why not
<tpw_rules> i'm saying you can do that and keep the [] syntax. if you want a reset, you would have to do Out.like(Signal(16, reset=0x1234))
<tpw_rules> or maybe Out[Signal(16, reset=0x1234)]
<tpw_rules> (although this sort of assumes Member objects are secretly Signals underneath?)
<whitequark> no, they are absolutely not
<whitequark> they are Signal templates
<whitequark> I think creating a Signal just for the sake of getting its shape is pretty cursed
<tpw_rules> is that codified as its own thing at all?
<whitequark> sort of? like, in effect, yes, by this RFC
<whitequark> but I was referring to a concept and not an established term
<tpw_rules> wrt the cursedness, you would still be able to do Out[16] if you didn't want anything special
<tpw_rules> is reset value and signedness part of the "shape"?
<whitequark> I think resets aren't particularly special, you'll see them in many Streams probably
<whitequark> signedness is, reset value no
<whitequark> "shape" is what's necessary and sufficient to perform arithmetic and bit container operations on a value, as well as allocate storage for it
<whitequark> there are many other properties a value has that aren't captured by it
<tpw_rules> what does a signal have at elaboration time aside from that and a reset value?
<whitequark> domain from which it is driven; decoder (though we're going to phase that out in favor of having that be a part of the shape-castable); attributes
<whitequark> expressions in which it is used (if we're really getting in the weeds; but this still matters, since CDC analysis is based on it)
<tpw_rules> but those don't become relevant to a Member until it gets used to make a Signal with .apply
<whitequark> yea
<tpw_rules> ok. then maybe reset is a special case. it does seem very useful to define on the Member, along with the obvious shape
<whitequark> yes
<tpw_rules> ok, makes more sense why you think my idea is cursed
<tpw_rules> then Out.like seems less useful than i thought. doesn't sound like we would want to gate having a reset value behind it
<whitequark> yep
<tpw_rules> but in any case it's clear that __getitem__(arg, **kwargs) is not a thing and i guess __call__ will have to do
<tpw_rules> "no other members in any of rest" so each interface connected with .connect has to only have members which are part of the interface? seems very limiting? what about members which start with _? and here "interface" is an Elaboratable with a signature member, right?
<tpw_rules> (how are "members" even detected, groveling __dict__? what is a "port member"? does connect even access the signature member?
<tpw_rules> )
<whitequark> .signature property
<tpw_rules> maybe i'm missing something but i'm not sure "interface" is defined anywhere and how it is different from an Elaboratable.
<tpw_rules> so an interface is an object with a .signature property of type Signature?
<whitequark> "members" are attributes containing a Signal or a sub-interface. an interface is any object that has a .signature property which contains an amaranth.lib.component.Signature instance that is compatible with that object
<whitequark> yes
<whitequark> the object also has to match the signature
<whitequark> (otherwise it is not an "interface" it is an "error")
<galibert[m]> tpw: ultimately all modules will have a signature, and you can think about it as the prototype of the module
<tpw_rules> is that what the "no other members in any of rest" is about, or does that only look at stuff defined in signature?
<whitequark> .connect only cares about things defined in .signature
<tpw_rules> and the object matches the signature if signature.apply is called with it as the parameter?
<tpw_rules> (or you manually construct everything i guess)
<whitequark> this is actually described in the RFC in the section for "signature.compatible"
<whitequark> but yes
<galibert[m]> the idea would be to have apply(*self) automagically called anyway
<whitequark> I mean it's not magic, it's a normal Python constructor
<galibert[m]> so you don't create the signals/other of the signature, it's done for you
<tpw_rules> ok that makes sense. maybe i'm getting member confused in my head with a .whatever, which i guess is called an attribute here
<galibert[m]> Catherine: There may be an ordering issue, if you want to module constructor to change the signature depending on constructor parameters
<tpw_rules> (and by python, getattr etc)
<galibert[m]> s/to/the/
<whitequark> yes, python calls it an attribute
<whitequark> I was careful to use names that do not overlap with anything
<whitequark> unfortunately instead of being outright ambiguous, this makes them merely confusing
<tpw_rules> ok. that's my problem. i got member confused with attribute. it doesn't overlap in python but does in other languages
<whitequark> a past edition used "amaranth.lib.module" instead of "amaranth.lib.component"
<whitequark> yes
<whitequark> we need to have a glossary section in the docs, for sure
<whitequark> I'll work on that
<tpw_rules> can you clarify that member is something that's a child of .signature and that's how members are detected?
<whitequark> been very hard hit by PTSD flashbacks this weekend :(
<galibert[m]> Catherine: ouch, sorry about that
<tpw_rules> i'm sorry. don't want you overstressed.
<whitequark> tpw_rules: a member is specifically something that is a part of the .signature property
<galibert[m]> btw, it's 19:02 here, so I guess the meeting is starting
<whitequark> * of the value of the .signature property
<whitequark> yes
<whitequark> essentially started
<galibert[m]> indeed
<whitequark> the agenda is the interfaces RFC draft
<galibert[m]> merge :-)
<galibert[m]> you know my POV on that one
<whitequark> I mean it's a draft
<whitequark> we do not merge drafts
<galibert[m]> Pfffffff
<whitequark> I want to hear what folks think about the approach and pitfalls and so on
<whitequark> tpw_rules found a minor hole already
<tpw_rules> ok. then maybe reiterate or use capital Member? nowhere does it actually say under "reference interface description" what an interface is
<whitequark> before the RFC is merged I want to have it implemented at least
<whitequark> tpw_rules: yes, you're right, I define the Python structures describing an interface but I never say what the term means
<whitequark> that's a glaring omission on my part
<galibert[m]> it's much more than a replacement of the connectivity characteristics of Records though
<whitequark> yes, as intended
<galibert[m]> I'm not sure that's showing
<whitequark> it'll show more in the actual docs more than the RFC, I think
<galibert[m]> Oh, not a problem then
<jfng[m]> would it make sense in some cases, to have an implementation before an RFC is merged, just to try it out, in case an issue appears ?
<jfng[m]> or would that just be the motivation for another RFC
<galibert[m]> Is a rfc being merged means it has stopped evolving? I would have thought the freeze point would be a release instead
<jfng[m]> like, i can't find any obvious issue with this draft, but idk how it would hold up in practice, when exercised
<whitequark> jfng: I think that an implementation is expected for any nontrivial change
<whitequark> it is not a strict requirement but it's something people do in order to write good RFCs
<whitequark> galibert: an RFC is a decision point for language design process
<whitequark> it's not a software artefact, like a source tarball; it's a software maintenance artefact, like a little coredump of the developers' minds
<adamgreig[m]> I was wondering if apply() should be two functions, one that creates the signals on a new object and returns it, and another that creates it on an existing object (typically self), and perhaps then the names could be less weird
<galibert[m]> there's a saying in french, coming from a movie I think, that roughly translates into "Only dumbasses never change minds, that's how you recognize them". No dumbasses in here, so I expect that experiementing with the implementation may end up wanting changes w.r.t what was in the rfc
<adamgreig[m]> maybe just __call__() for the "create a new object" and keep "apply" for "create on existing object"
<galibert[m]> s/experiementing/experimenting/
<whitequark> adamgreig: so creating an `object()` is something I think might not even be that common in the end?
<whitequark> usually you want to customize it at least a little
<whitequark> it's very useful for doing quick and dirty work of course
<adamgreig[m]> hmm, I wonder
<adamgreig[m]> I guess given it can recursively make the sub-objects anyway then yea, probably most uses cases are applying to self
<whitequark> this is also something that would benefit from feedback with an existing implementation, of course
<whitequark> yes
<adamgreig[m]> perhaps that's even more reason to have __call__() for "create new object" so that apply() is always given the thing to apply to
<whitequark> the reason I kinda dislike call is that then you end up with BusSignature()() which looks weird.
<adamgreig[m]> it confused me a bit to see it called apply() when the guide-level docs start out by just using it to make a new object
<whitequark> s/call/`__call__`/, s//`/, s//`/
<galibert[m]> I expect both call() and apply() to essentially never be used in normal code. You'll have a .signature that will be transparently instanciated instead
<adamgreig[m]> yea, fthat is a bit weird, you could use create() instead I guess
<whitequark> like, WishboneSignature()() has a huge wtf factor for no good reason
<whitequark> create() was my initial name for it
<whitequark> but then I thought that this doesn't let you use it on existing objects
<galibert[m]> Why would you need a wishbonesignature-derived object that's not itself an Elaboratable?
<whitequark> of course you can always do e.g. WishboneSignature.create(self, WishboneSignature()) but that has repetition instead
<galibert[m]> what's the use case?
<whitequark> I'm just using that as an example
<anuejn> I am a bit concerned if the apply method leads to good types in IDEs
<galibert[m]> Ok, I'll be more precise, I don't see the use-case for anything other than .signature as module prototype at this point
<galibert[m]> since the signature is compositing and nice
<adamgreig[m]> honestly the .signature thing is maybe my least favourite part of the rfc, but I haven't really thought that bit fully through
<adamgreig[m]> but my first point was just whether apply() should be split into two functions for its two different signatures
<whitequark> anuejn: so the reason we have apply instead of just using e.g. field annotations is because we have parametric modules everywhere
<whitequark> meaning you don't know what the actual width is until it's instantiated
<adamgreig[m]> uh, "two different modes of operation" is what I mean by "two different [function] signatures"
<whitequark> if you want IDEs to pick up types, you have to define type hints in addition to .signature
<whitequark> this is regardless of whether you're using .apply or not, I think
<whitequark> unless you want to rely on having self.field = Signal(...) be picked up by pylance as an implied type declaration of self.field (does it do that?)
<anuejn> this is roughly what I want
<whitequark> in general, as elsewhere in Amaranth, my working assumption is that to get good type inference without ridiculous amounts of repetition, it will be necessary to develop a plugin for pylance that picks up e.g. signature method definitions and fills in type hints
<anuejn> it is super usefull to me to have autocompletion of the available signals of a module
<whitequark> I deliberately left in the option of making it possible to have signatures where all the members inside are stuff you define yourself by hand
<whitequark> but I'm not planning on keeping to do that myself, for example
<galibert[m]> Catherine: with the eventually-mandatory .signature and the auto-apply, maybe you want to go back on that option?
<whitequark> no, absolutely not
<galibert[m]> ok then
<whitequark> don't forget that not all important things are Elaboratables
<anuejn> okay yeah I don't want to hold the design process on that, just wanted to say that I am a bit nervous about that as I think it might be a regression
<whitequark> busses are just as important
<whitequark> anuejn: so on autocomplete front, there is a bit of conflict
<galibert[m]> What are busses?
<whitequark> anuejn: remind me, how do you feel about Struct?
<whitequark> galibert: like wishbone or axi
<galibert[m]> (serious question, for me it was just elaboratables connected together)
<whitequark> well a WishboneInterface isn't an elaboratable
<galibert[m]> Of course not, it's a signature (e.g. a type, in my POV)
<whitequark> the bus is the thing you connect. you don't connect together elaboratables, you connect their interfaces
<whitequark> a signature (a bundle of Members and Signatures) describes a bus (a bundle of Signals and objects)
<whitequark> you connect together buses with the connect method
<whitequark> elaboratables own buses, and elaboratable signatures describe it all
<whitequark> (I'm using bus/interface interchangeably above)
<anuejn> whitequark: I feel quite good about it actually (the types are a lie a bit but give good autocompletion?)
<whitequark> (a bus is a kind of interface that is the most obviously useful)
<whitequark> anuejn: so that's what it was developed for
<whitequark> and I'm personally happy to add the same thing for the .signature property, like you inherit from Interface and the only thing it does is it turns annotations into .signature method
<whitequark> but other people complained so much that I've not included it in this RFC
<whitequark> do you see the conundrum I'm in?
<anuejn> I see :D
<whitequark> there is a way to make the annotations proper types, but it requires interacting with the Python type system and I find it really gross
<whitequark> and I don't personally feel like investing time into having it work nicely
<galibert[m]> but the bus (the bundle of Signals and objects) is hosted inside an elaboratable, isn't it?
<anuejn> I can totaly understand that
<whitequark> galibert: sure, but to the interface system, that it's an elaboratable is irrelevant
<whitequark> amaranth.lib.component is about connecting signals together that are hosted by objects with signatures
<whitequark> it actually will ~never connect elaboratables together directly
<anuejn> also I appreachiate that amaranth.lib.component is not special in any way so if I want a different solution (possibly based on that) I can just build it downstream / try to upstream it some time
<tpw_rules> "but to the interface system, that it's an elaboratable is irrelevant" that was definitely not immediately clear to me
<whitequark> anuejn: yes, I chose to make that a priority based on some past frustration people had
<whitequark> it also arguably leads to a better design
<whitequark> tpw_rules: right, another thing missing in the draft
<crzwdjk> galibert[m] elaboratables can, and likely will, have multiple interfaces. Sometimes maybe even multiple instances of interfaces with the same signature.
<galibert[m]> hmmmm. Is a non-flat signature applied to an elaboratable creates sub-objects which are not elaboratables?
<galibert[m]> I guess I see that as "technically a python sub-object, semantically not an amaranth sub-anything"
<whitequark> galibert[m]: yes
<galibert[m]> and yeah, and you need to be able to dynamically create such objects for cases like a memory decoder
<anuejn> Other options might be left/right (in analogy to how one might draw it in a diagram) up/down, upstream/downstream.
<anuejn> Another thing I am not too happy about is the naming of in/out (I just personally find it hard to reason about it).
<anuejn> But I see that each of these have their own problems and don't really want to start that bike-shed here.
<galibert[m]> where you create (at least) one per device you connect to it
<whitequark> left/right is outright evil since some people plainly cannot distinguish one from another (it's like being unable to read analog clocks)
<galibert[m]> "The other left"
<whitequark> up/down doesn't correspond to anything in the actual code so I think it's worse
<whitequark> the naming issue is acknowledged but there's no really good solution I know of
<anuejn> okay fair
<galibert[m]> We already had i_ and o_, it's not fundamentally worse
<whitequark> I did a literature review a while ago and I don't think anyone came up with a really nice solution. some people did come up with really bad ones tho
<whitequark> (ones based on master/slave)
<whitequark> also, what galibert says
* anuejn hiding behind my @UPWARDS and @DOWNWARDS downstream code
<Chips4MakersakaS> If you have two filters that have an input 'i' and output 'o' with both of them flipped versions of the same interface. Can you then do component.connect(m, filt1.o, filt2.i) ? RFC give only example of connect top object with interface.
<galibert[m]> think so yes
<whitequark> Chips4Makers (aka Staf Verhaegen): connect only connects e.g. "i" to "i"
<whitequark> meaning the answer is no
<galibert[m]> (the sub-objects I was talking about)
<tpw_rules> "some people plainly cannot distinguish one from another" *raises hand*
<adamgreig[m]> yea, I had a similar question about connecting sub-interfaces when your module has many different interfaces
<whitequark> Chips4Makers (aka Staf Verhaegen): this (making "null modem" type connections) is something people keep coming up with and asking about, but so far no one has presented a compelling case where you would in practice want that
<adamgreig[m]> if filt1.o is the flip of the signature of filt2.i, can't you connect them? is it a null-modem type?
<whitequark> e.g. would your filters just infinitely chew up the same signal in a mutual feedback? why would anyone want that?
<whitequark> so, let me clarify
<whitequark> you can connect these in two statements, like connect(m, a.i, b.o); connect(m, a.o, b.i)
<whitequark> this works perfectly fine
<whitequark> however you cannot connect them in one statement
<anuejn> also: this RFC is kind of implicitly introducing stream interfaces without specifying semantics. I think it would be good to follow up with an RFC that specifies the semantics to not get incompatible implementations
<whitequark> e.g. you cannot do connect(m, cpu1.uart, cpu2.uart)
<galibert[m]> wait, you said you can only connect "i" to "i". Was that a typo?
<Chips4MakersakaS> So how would you do then a chain filters;
<whitequark> but you can do connect(m, cpu1.uart.tx, cpu2.uart.rx); connect(m, cpu1.uart.rx, cpu2.uart.tx)
<whitequark> tpw_rules: what I mean is that if you `connect(m, x, y)`, and `x.i` exists, `connect` will connect it to `y.i`
<adamgreig[m]> I don't think people are asking about doing a closed loop like that
<galibert[m]> I think Staf was talking about chaining, not null-modem
<adamgreig[m]> but doing the normal "connecting an output of A to input of B, where A and B both have lots of inputs and outputs and so you have to specify which you mean"
<whitequark> ohhhh I misread what Chips4Makers (aka Staf Verhaegen) wrote, sorry everyone
<Chips4MakersakaS> I think connect(filt1.o, filt2.i); connect(filt2.o, filt3.i) seems to be possible and is actually what I want.
<whitequark> Chips4Makers (aka Staf Verhaegen): then the answer is "yes"
<galibert[m]> Phew :-)
<whitequark> explicitly and this is a part of why interfaces are wanted
<adamgreig[m]> the guide-level explanation in the rfc doesn't show any connecting to sub-interfaces, only of top-level objects, so I had the same question
<jfng[m]> i vaguely remember having asked this at a previous meeting, but forgot the answer:
<jfng[m]> how to deal with e.g. a WB initiator implementing an optional signal (e.g. `err` input), and a target that doesn't ?
<jfng[m]> technically, they would be compatible, but afaiu, connect would fail ?
<jfng[m]> ah, assuming the absence of this port is equal to 0
<whitequark> no
<galibert[m]> that's a dangerous assumption
<whitequark> connect never assumes anything about missing ports, it will strictly refuse to connect
<jfng[m]> i did the assumption
<whitequark> connect will, however, connect an input signal to an output constant
<whitequark> so the target can have self.err = Const(0)
<jfng[m]> ahh, right
<jfng[m]> you replied something like that already before, but i forgot
<whitequark> and there could be a method on `WishboneInterface` that creates these "dummy" members on request
<galibert[m]> can we have a cranky connect that bitches if not everything is connected to something?
<whitequark> galibert: that's how it's defined
<whitequark> anything except for "every member on the left matches every member on the right" is a hard error
<whitequark> and there is no include/exclude bullshit that existed in records
<galibert[m]> you seem to be bitching when an input doesn't have a corresponding output, I'm asking for when an output has to input to talk to
<galibert[m]> e.g. a symmetric bitching
<whitequark> hm?
<galibert[m]> or left right, but also right/left, that works too
<whitequark> > 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.
<tpw_rules> yeah it is already defined to whine symmetrically
<galibert[m]> "and there are no other members in any of rest", missed that
<galibert[m]> maximal bitching, perfect
<tpw_rules> (this might be a language thing but i personally do not like the term "bitching" and wouldn't like to see it same as master/slave)
<whitequark> this is another thing that needs feedback from implementation
<whitequark> we'll get there, I'm hoping, very soon
<galibert[m]> (probably a native language thing, as a non-native speaker I feel no link between bitch and bitching, because I use the second often and the first essentially never)
<tpw_rules> notation question: what does full/none mean in the "to be done" section?
<whitequark> tpw_rules: this is just a draft thing for myself
<tpw_rules> ok
<galibert[m]> the sentence in the signature.apply (c) part is broken
<galibert[m]> If the member is a signature, member.signature.apply() for Out members, and member.signature.flip().apply().
<whitequark> ... for In members, yea missing
<galibert[m]> hmmm, I think there's a definitional issue with connect
<galibert[m]> I think you want to have connect(m, o, i1, i2) equivalent to connect(m, o, i1); connect(m, o. i2)
<galibert[m]> (thinking for loop there)
<galibert[m]> s/./,/
<whitequark> it is
<whitequark> (in a subset of cases where it doesn't throw an error)
<galibert[m]> technically you never say what happens when you connect o twice
<galibert[m]> only when you connect it twice in a given connect call
<whitequark> I do
<whitequark> the semantics of connection is defined to be "the same as writing out m.d.comb +=..."
<whitequark> you can actually do a multiplexer like with m.If(cond): connect(m, a, b) with m.Else(): connect(m, a, c)
<whitequark> anyway, the allotted time is coming to an end
<galibert[m]> cute
<whitequark> feedback from tpw_rules: needs an expanded reference level explanation that introduces connect, etc
<whitequark> feedback from anuejn: apply could play poorly with autocomplete
<whitequark> feedback from adamgreig: some reservations about apply (stated above), some reservations about .signature (unstated)
<whitequark> is this all? am I forgetting anything?
<tpw_rules> did you want to talk at all about the In[x] vs In(x) syntax? or are you going to just unconditionally do the latter cause you want kwargs?
<crzwdjk> Will there be an implementation we can try out before the RFC is merged?
<galibert[m]> that we, or at list I, love the thing? :-)
<whitequark> crzwdjk: yes
<whitequark> tpw_rules: I did, actually
<adamgreig[m]> I also wondered about getitem for in/out, and also how "mandatory .signature, applied in init" would work unless users are required to call super().init, but that part's not really in the rfc yet
<adamgreig[m]> * I also wondered about __getitem__ for in/out, and also how "mandatory .signature, applied in init" would work unless users are required to call super().init, but that part's not really in the rfc yet
<adamgreig[m]> but your summary is accurate for what I actually said in the meeting
<crzwdjk> Cool! I will wait for that and may have some feedback from actually trying to use it.
<adamgreig[m]> (and nice, I am also keen to try out the impl once available)
<whitequark> the mandatory .signature would be in Fragment.get, same place we have a warning for "please derive from Elaboratable"
<adamgreig[m]> so it happens at elaborate() not at init?
<whitequark> yes
<adamgreig[m]> that's great
<adamgreig[m]> resolves the concern from last week about modules where the signature is changed by calling additional methods after construction
<galibert[m]> indeed
<jfng[m]> (Element keeps scrolling up 5+ messages, everytime a new one is received, very annoying...)
<tpw_rules> how would connections work then? does the actual connection happen during elaboration too?
<whitequark> correct
<whitequark> it's just m.d.comb += ... but in a function provided in a library
<adamgreig[m]> very quick aside, are you waiting for anything else for the CRC PR? I think I've addressed all the review feedback so far
<galibert[m]> you wouldn't do a m.d.comb += ... earlier than elaboration anyway
<tpw_rules> oh right, cause you do that in your .elaborate func
<tpw_rules> so the auto apply would happen before all those funcs are called
<galibert[m]> yep
<tpw_rules> makes sense
<whitequark> adamgreig: re CRC PR: for me to feel better health wise
<whitequark> I'm just barely able to handle this meeting today...
<galibert[m]> it was a good meeting though
<tpw_rules> yes, thank you
<whitequark> yes, necessary
<adamgreig[m]> that's fine, just checking I hadn't missed anything, thanks
<jfng[m]> i guess we won't have time to discuss amaranth-soc CSRs now, then
<whitequark> no, not really
<whitequark> re In[]/In(), I think that will also go into the next meeting
<whitequark> and maybe implementation makes that clear anyway
<galibert[m]> when is a submodule's elaborate called, when m.submodules += ... happens?
<whitequark> no
<whitequark> in rtlil.convert
<whitequark> or earlier if you call Fragment.get
<whitequark> in practice, when platform.build is called
<galibert[m]> ok, let me rephrase, when will apply() be called on the submodule?
<whitequark> never?
<whitequark> you don't call apply on your submodules...
<galibert[m]> yeah, it's going to be auto-called in Fragment.get()
<whitequark> I don't understand
<galibert[m]> you way apply will be called just before elaborate() in Fragment.get
<whitequark> I can't even parse that sentence
<galibert[m]> s/way/said/
<whitequark> uh
<whitequark> I don't think I did?
<whitequark> where?
<galibert[m]> the mandatory .signature would be in Fragment.get, same place we have a warning for "please derive from Elaboratable"
<whitequark> correct
<whitequark> that doesn't say anything about apply() though
<whitequark> Fragment.get would check if the Elaboratable has a signature, and raise at first a warning, then an error
<whitequark> by the time you reached Fragment.get, apply() won't do anything useful since all the connections have been made already
<whitequark> * you reached that check in Fragment.get, apply()
<galibert[m]> it was in answer to "mandatory .signature, applied in init", so I expected it meant apply was at that time
<galibert[m]> pretty sure that's what adam understood too
<whitequark> .apply is how someone may choose to add fields to the elaboratable, or they may choose any other way they want
<galibert[m]> ok, a couple of says ago we were talking about apply(*self) being automatically called on Elaboratables
<whitequark> well not automatically
<whitequark> I suggested that it could be in Elaboratable.__init__
<galibert[m]> I admit I said automagically
<whitequark> but that is still called manually, via super().__init__()
<galibert[m]> ah, so you did not answer adam's question
<whitequark> ?
<galibert[m]> also how "mandatory .signature, applied in init" would work unless users are required to call super().init
<galibert[m]> so you're now saying they're required to call init
<whitequark> no?
<galibert[m]> (fuck the underscores)
<galibert[m]> to have the apply automatically done
<whitequark> the apply call and the requirement of having .signature are completely independent from each other
<whitequark> the answer to adamgreig's question is that no such thing would work since it wasn't ever planned to work in that way
<whitequark> for Fragment.get, the change would be that an elaborable needs to declare which attributes are ports and which are just random signals
<whitequark> it doesn't care one little bit how you make that declaration, though Elaboratable, coincidentally, offers a way to do this if you call its constructor
<whitequark> * it doesn't care one little bit how you add those ports, though Elaboratable, coincidentally, offers a way to add them if you call its constructor
<whitequark> sorry, typoed the last message
<galibert[m]> that's gonna end up so boilerplate-ry
<whitequark> I don't see how it would
<crzwdjk> Way less boilerplate-y than what we have today though
<galibert[m]> well, you're going to have the mystic knowledge that "you must call super.\_\_init\_\_() at the end of the constructor" propagate
<whitequark> mystic knowledge?!
<whitequark> I'm proposing that pattern because it is basic Python
<galibert[m]> "why at the end? Because apply freezes the signature"
<galibert[m]> basic python is init at the start
<whitequark> yeah
<whitequark> if you're putting a signature into a class property it might as well be frozen at class definition time
<whitequark> so I don't see why you'd want to apply at the end
<whitequark> (also, I'm not a fan of the kind of discussion where sentences are said with assumptions that are unstated and everyone else has to guess what they are)
<galibert[m]> because you may want to change the signature on constructor parameters
<galibert[m]> yeah, we got misunderstanding in there, but hopefully no bad faith
<whitequark> the case where you change the signature based on constructor parameters is actually why there's no Interface class anymore and why a Signature only checks for the presence of members using a single well known property
<whitequark> because if you have stuff change based on constructor parameters, you no longer call super().__init__(), you do it all manually to some extent
Wolfvak has quit [Server closed connection]
<whitequark> we have no way to express that parts of a signature are dependent on constructor parameters (essentially, dependent typing)
<whitequark> and we probably won't have any way to do that
Wolfvak has joined #amaranth-lang
<whitequark> I expect that the usual pattern will be self.signature = Signature(...); self.signature.apply(self)
<whitequark> at the start of the constructor
<galibert[m]> Ah, I see. I guess I was hoping that "apply is automatically called with your elaboratable .signature between construction and elaborate"
<whitequark> that would be actively unhelpful
<whitequark> it also goes against the goal of "lib.component not being special"
<galibert[m]> the warning/eventual error makes it special already
<whitequark> I guess it's inevitably special once we start requiring Elaboratables to have a signature, though you could see that as a backend requirement and not a language requirement (indeed, only the backends care about it, the simulator does not)
<whitequark> it's also an option to not introduce that warning/error.
crzwdjk has quit [Quit: Client closed]
crzwdjk has joined #amaranth-lang
<whitequark> I think it is important to have members be normal attributes you assign in the constructor manually or otherwise
<whitequark> we have enough magic going on (especially after the recent Signal(shape_castable) change) that spending magic budget on stuff like this is wasteful
<whitequark> then you won't play the game of "where does this attribute actually come from?"
<galibert[m]> I guess it's all about what vision you have you a "standard well-written module" for 0.4 and beyond
<galibert[m]> * have you of a "standard
<galibert[m]> s/you/of/
<crzwdjk> I feel like it's find for signatures to be a little special, otherwise you're relying a bit too much on convention and you end up with Perl OO
<galibert[m]> gotta move, need to go buy toothpaste, later
<whitequark> this meeting went much too far past the end point, yes
<whitequark> thanks everyone
<whitequark> crzwdjk: well the OO is provided by Python here
<jfng[m]> thanks to you ! hope you can get some rest
<crzwdjk> Thanks whitequark
<whitequark> crzwdjk: I get your point, but interfaces are a much less complex concept than OO
<whitequark> so they're inherently less prone to that
<crzwdjk> True, true
<crzwdjk> In the end this is all just machinery to automate the stuff that one now has to do verbosely by hand, so yeah, no need for it to be magical
crzwdjk has quit [Quit: Client closed]
<zyp[m]> > and there could be a method on `WishboneInterface` that creates these "dummy" members on request
<zyp[m]> The problem with `apply()` freezing the signature in the elaboratable constructor is that the `WishboneInterface` sub interface's signature would then also be frozen, since freeze happens recursively
<anuejn> whitequark: thanks for the meeting and for the well thought out RFC
mithro has quit [Ping timeout: 240 seconds]
tucanae47 has quit [Read error: Connection reset by peer]
_alice has quit [Read error: Connection reset by peer]
_alice has joined #amaranth-lang
mithro has joined #amaranth-lang
tucanae47 has joined #amaranth-lang
josuah has joined #amaranth-lang
FL4SHK has joined #amaranth-lang
fl4shk[m] has joined #amaranth-lang
<FL4SHK> okay good
FL4SHK has quit [Client Quit]
<fl4shk[m]> I have switched to Matrix
<fl4shk[m]> for my IRC needs
ktemkin has quit [Server closed connection]
ktemkin has joined #amaranth-lang