<johninbaltimore>
where the same identifier refers to the same variable, but the registers are all copied forward through each stage when a later stage attempts to read that identifier
<johninbaltimore>
i.e. a read requires a write in a prior stage, but a write doesn't interact with a prior read or write and doesn't cause the identifier's earlier contents to be forwarded down the pipeline. Reading from and writing to the same identifier is of course sensible, since reads come from an input line and writes go to an output register and these are
<johninbaltimore>
two separate things anyway
<johninbaltimore>
the pipeline just adds implicit x.eq(x) between the stage that wrote x and the later stage that reads it
<johninbaltimore>
(and then there's the obvious implications of nesting a pipeline inside an FSM() State() that may reenter itself as it loads data into the pipeline)
<johninbaltimore>
and the ability to check a pipeline for output
balrog has quit [Quit: Bye]
balrog has joined #amaranth-lang
cr1901 has quit [Quit: Leaving]
cr1901 has joined #amaranth-lang
dyniec has quit [Ping timeout: 255 seconds]
dyniec has joined #amaranth-lang
tarmoo has joined #amaranth-lang
tarmoo has quit [Ping timeout: 255 seconds]
<whitequark[cis]>
<johninbaltimore> "I'm finding great use for the..." <- currently there isn't
<whitequark[cis]>
it's an open question whether there should be one that's implemented the way you're suggesting
<whitequark[cis]>
a design can be proposed by anyone through the RFC process
tarmoo has joined #amaranth-lang
tarmoo has quit [Client Quit]
<crzwdjk>
Couldn't something like that could be implemented without the "with" syntax as a library? That might be a good way to try it out and see if it's a good idea.
<whitequark[cis]>
the aspect where a value can lower into one signal or the other depending on the stage can be potentially difficult to manage
<whitequark[cis]>
also the with: body can only run once (it's not like a closure) so that creates more restrictions on things
jfng[m] has joined #amaranth-lang
<jfng[m]>
@libera_johninbaltimore:catircservices.org @libera_crzwdjk:catircservices.org there has been a previous attempt at a pipeline primitive using context managers:
<whitequark[cis]>
this is a design question about the shape-like range(0, 0). this range doesn't have any elements so a value with this shape can't have any values
<whitequark[cis]>
* this is a design question about the shape-like range(0, 0). this range doesn't have any elements so the set of possible values for an Amaranth value with this shape is empty
<whitequark[cis]>
it's 0-bit, but the discussion linked above implies it's 1-bit. I'm a bit confused by the history there
<whitequark[cis]>
16:10 <Wanda[cis]> it's not a direct equivalent; Signal(unsigned(0)) is equivalent to Signal(range(1))
<Wanda[cis]>
it's 1-value, not 1-bit
<Wanda[cis]>
ie. 0-bit
<Wanda[cis]>
as opposed to 0-value, which is ... (-inf)-bit
<whitequark[cis]>
(-inf)-bit.
<whitequark[cis]>
what ... how does that even work
<Wanda[cis]>
mathematically correctly
<Wanda[cis]>
this is the amount of information in a never type
* whitequark[cis]
headdesk
<whitequark[cis]>
okay, I see the underlying question now, anyway
<whitequark[cis]>
which is basically: when we say Signal(some_shape_like), is the range of values derived from Shape.cast(some_shape_like), or some_shape_like?
zyp[m] has joined #amaranth-lang
<zyp[m]>
the way I see it, range(0) and range(1) would have the same width for the same reason that range(3) and range(4) has the same width, this is RFC 17 over again
<whitequark[cis]>
in the former case, range(0) is 0-bit, and in the latter, range(0) is (-inf)-bit
<whitequark[cis]>
I agree with zyp, I think that unless we start adding assertions that e.g. the value of Signal(range(3)) is never 3 (which we might one day!) we should probably treat range(0,0) and range(0,1) the same
<whitequark[cis]>
I'm open to disagreement though
<Wanda[cis]>
I think adding proper never types is such a pain that it's basically infeasible
<jfng[m]>
more generally, should reset values of Signals whose shape is a `range()` always belong to that range, or can they be valid if they match the rounded-up width ?
<zyp[m]>
I guess there could be cases for generic code where you have e.g. a selector signal and there's nothing to select between, and allowing range(0) reduces the need for special casing
<whitequark[cis]>
jfng[m]: I think we should probably make an exception for the case of `range(0, 0)` where we accept `reset=0`
<Wanda[cis]>
whitequark[cis]: this is my thinking as well
<Wanda[cis]>
because otherwise it's impossible to construct a Signal(range(0))
<Wanda[cis]>
and having those is desirable for some kinds of generic code
<Wanda[cis]>
so ... it's kinda a never type, but IMO not quite
Lord_Nightmare has joined #amaranth-lang
<Wanda[cis]>
in HDL, lots of signals kinda come with an implicit "valid" gate, ie. are effectively "don't care" if some condition isn't met
<Wanda[cis]>
and a never-type signal is perfectly ok to have in your design as long as this implicit gate condition is never true
<whitequark[cis]>
right
<galibert[m]>
How much error detection power are we supposed to lose in favour of iffy generated or generic code? unsigned (0) and empty case are borderline enough already
<zyp[m]>
you're not losing anything if you're only getting rid of false positives
<whitequark[cis]>
the question is, is the workaround that is inspired by seeing the warning making things better, or worse?
<whitequark[cis]>
if you add conditionals around paths like this, you can introduce edge cases instead of addressing them
<whitequark[cis]>
I do think we need to eventually have a story for adding asserts for all values with a range() shape
<whitequark[cis]>
unsigned(0) is perfectly valid on its own by the way, Verilog doesn't have a 0-bit-wide type and that's a massive pain (I think they added it in SV)
<zyp[m]>
I think the simplest workaround for generic code in this case would be something like range(n or 1)
<Wanda[cis]>
(they did not)
<whitequark[cis]>
oh ok. well that's unfortunate
<whitequark[cis]>
it is perfectly reasonable to have a value of unit type in the language, to represent a case where no value is actually required but you want to keep wherever it is you're plugging it into for generic programming reasons
Chips4MakersakaS has joined #amaranth-lang
<Chips4MakersakaS>
Do you expect code generation to specify explicit reset value for signals if they also can generatre a never-value. Otherwise you could error when explicit reset value is specified for a never-value.
<whitequark[cis]>
Chips4Makers (aka Staf Verhaegen): I would expect in most cases an implicit reset value would be used
<Wanda[cis]>
oh yes, that's a reasonable alternative
<Wanda[cis]>
ok on Signal(range(0)), warning on Signal(range(0), reset=0)
<Wanda[cis]>
though I'm a little concerned it could involve a bunch of magic
<Wanda[cis]>
eg. to make Signal.like work correctly
<zyp[m]>
are there any plausible situations where you'd do Signal(range(0), reset=0) by accident?
<galibert[m]>
and what is as_value() supposed to return?
<Wanda[cis]>
as_value() on what?
<Wanda[cis]>
there's no ValueCastable involved here
<galibert[m]>
<whitequark[cis]> "it is perfectly reasonable to..." <- But then the onus should be on the generic code to say explicitely that it can be in this case
<galibert[m]>
On the Signal
<Wanda[cis]>
the Signal already is a Value
<galibert[m]>
Yeah whatever
<galibert[m]>
what's its value?
<Wanda[cis]>
itself? I have no idea what you're asking here
<zyp[m]>
0
<whitequark[cis]>
galibert: `Cat()`
<Wanda[cis]>
it's 0-bit if that's the question
<galibert[m]>
so it's unsigned(range(1)) or unsigned(0) except for reset?
<whitequark[cis]>
i.e. whatever is the value of Signal(unsigned(0)), it must be the same as Cat()
<galibert[m]>
s/unsigned(//, s/)//
<whitequark[cis]>
and you can't remove Signal(unsigned(0)) from the language unless you also remove Cat(), which is what Verilog tried to do
<whitequark[cis]>
(as well as .replicate(0), x[0:0], and a thousand other constructs they couldn't ever fully chase down anyway)
<zyp[m]>
the way I see it, the max value of a n-bit unsigned is 2**n - 1 which for n=0 is 0
<whitequark[cis]>
yeah, that's sensible
<whitequark[cis]>
let's get back to range(0,0)
<zyp[m]>
<whitequark[cis]> "I think we should probably..." <- my vote is for this exception
<galibert[m]>
my vote is hard error
<zyp[m]>
I find a situation where that warning is not a false positive to be very unlikely
<galibert[m]>
they can add "or 1" if it's really, really what they intended
<jfng[m]>
<Wanda[cis]> "ok on Signal(range(0)), warnin..." <- i prefer this option, as it doesn't open the door for reset values that are out of a `range()`
<whitequark[cis]>
Chips4Makers (aka Staf Verhaegen), do you have anything to say?
<zyp[m]>
is it documented valid to pass reset=None?
<galibert[m]>
zyp: My congratulations to you for never typoing. I'm nowhere near that good
<Chips4MakersakaS>
No hard error for range(0,0); happy with both accepting reset=0 and warning if explicitly specified.
<whitequark[cis]>
zyp: we don't yet have documentation for that (I'm working on it)
<zyp[m]>
I'm okay with distinguishing between reset=None and reset=0 iff it's explicitly valid to pass reset=None
<zyp[m]>
(as opposed to being an implementation detail when no reset argument is passed)
<whitequark[cis]>
generally speaking I think we'll have to document reset=None because otherwise making a wrapper around Signal is infeasible
<whitequark[cis]>
well, it is feasible, but you have to mess so much with **kwargs
<zyp[m]>
hmm, I guess it also has to be because of shape castables
<whitequark[cis]>
you can technically leave it undefined and require everyone who wraps it to grab the value from __kwdefaults__ but I think that is not reasonable
<whitequark[cis]>
zyp: can you elaborate?
<zyp[m]>
e.g. StructLayout.const() would expect None or a dict, does it also accept int?
<whitequark[cis]>
Layout.const only accepts None, Mapping, or Sequence
<zyp[m]>
in other words, a shape castable doesn't necessarily accept 0 as a reset value, so from that perspective it makes sense that range(0) also doesn't
<whitequark[cis]>
(where a sequence is treated as a dict with keys 0, 1, 2...)
<whitequark[cis]>
yeah, I agree with @zyp: Signal(range(0), reset=0) should be a hard error
johninbaltimore has quit [Ping timeout: 250 seconds]
<whitequark[cis]>
we are nearing the end of the meeting. based on this discussion I believe that the way forward is: make Signal(range(0), reset=0) a hard error, allow Signal(range(0)[, reset=None]) without a warning
<whitequark[cis]>
thanks everyone who has participated
<Wanda[cis]>
hard error?
<Wanda[cis]>
that's a harder error than Signal(range(3), reset=3), which seems ... inconsistent
<whitequark[cis]>
I think we can make all out-of-range reset values for range() shapes hard erros
<whitequark[cis]>
s//`/, s//`/, s/erros/errors/
<whitequark[cis]>
should we do a deprecation cycle for this?
<jfng[m]>
whitequark[cis]: agreed if feasible
<Wanda[cis]>
... I mean, it already comes with a warning
<zyp[m]>
the warning already exists, deprecating a warning seems unnecessary
<Wanda[cis]>
agreed
<whitequark[cis]>
all right, let's make all out-of-range reset values a hard error then
<_whitenotifier-3>
[amaranth-lang/amaranth-lang.github.io] github-merge-queue[bot] 35b8098 - Deploying to main from @ amaranth-lang/amaranth@8501d9dd7365958397e850816ca3ed7a852d43b7 🚀
tarmoo has joined #amaranth-lang
tarmoo has quit [Remote host closed the connection]
tarmoo has joined #amaranth-lang
<whitequark[cis]>
should it be OK to raise from ShapeCastable.as_shape()? I can't see any reason it shouldn't be
<whitequark[cis]>
I'm wondering if rather than going for fixed.Const(x) we should make it possible to do C(0.5, SQ(16)) or something
<cr1901>
I love that latter idea, FWIW (also hi, sorry for missing meeting. My waking hours are messed up.)
<whitequark[cis]>
in general, I wonder if we should have the concept equivalent to a "typed literal" in most languages (i.e. have Const() remember the original shape passed and returning it from shape(), and also doing the logic of "iterating and calling .as_shape() until you get to a point where you have __call__" inside the constructor)
<zyp[m]>
<whitequark[cis]> "I'm wondering if rather than..." <- I've thought about `C(0.5, SQ(16))` myself, but what it'd replace is `SQ(16).const(0.5)`
<whitequark[cis]>
yeah, it may not be a win
<whitequark[cis]>
I'm writing docs for amaranth.hdl right now and wondering if Const should be aware of RFCs 8, 9
<zyp[m]>
I think it'd help make the syntax more general if Const accepted shape castables
<whitequark[cis]>
that is true
<whitequark[cis]>
it could also maybe enable some use cases later on, like if we kept operators in the AST when both operands are value-castable, without casting them yet
<zyp[m]>
I figure we could have Const(value, shape_castable) forward to shape_castable.const(value), essentially being the equivalent of RFC 15 for Const
<zyp[m]>
because it's inconsistent that you can do C(1, unsigned(16)) but not unsigned(16).const(1) and the other way around for shape castables
<whitequark[cis]>
take a look at what Layout.__call__ does. I think it may have to do the same dance where it iteratively calls .as_shape() and then __call__ on the result if it exists, and it would be great to have that logic outside of lib.data
<whitequark[cis]>
up for an RFC?
<zyp[m]>
not tonight, but I can probably write it at some point if nobody else does it first
<whitequark[cis]>
thank you
notgull has quit [Ping timeout: 255 seconds]
notgull has joined #amaranth-lang
<whitequark[cis]>
Wanda: should we not check that each enum member is constant-castable specifically rather than just value-castable, in ShapeLike's isinstance representation
<Wanda[cis]>
perhaps
<Wanda[cis]>
but there's no ConstLike
<Wanda[cis]>
and hm
<Wanda[cis]>
I think I specifically tried to avoid it for some reason?
<whitequark[cis]>
I vaguely recall that but why?
<Wanda[cis]>
good question
<Wanda[cis]>
I like having normal functional memory
<Wanda[cis]>
anyway
<Wanda[cis]>
is it causing problems?
<Wanda[cis]>
I think part of justification was that any non-const valuelike would blow up quickly when attempting to construct the enum in the first place, so the distinction wouldn't come up in practice?