kivikakk has quit [Quit: Idle timeout reached: 172800s]
Hoernchen_ has joined #amaranth-lang
Hoernche- has joined #amaranth-lang
Hoernchen has quit [Ping timeout: 276 seconds]
Hoernchen_ has quit [Ping timeout: 258 seconds]
skipwich has joined #amaranth-lang
Hoernche- has quit [Ping timeout: 258 seconds]
Hoernchen has joined #amaranth-lang
mabl[m] has quit [Quit: Idle timeout reached: 172800s]
Hoernchen has quit [Ping timeout: 258 seconds]
Hoernchen has joined #amaranth-lang
Hoernchen_ has joined #amaranth-lang
Hoernchen has quit [Ping timeout: 276 seconds]
mcc111[m] has joined #amaranth-lang
<mcc111[m]>
<vegard_e> "if you've got one process that's..." <- okay so wait i'm going back and trying to do this… if i want to "trigger" on the other process's clk signal going high, how would I do that?
<mcc111[m]>
just actually sit there in the "triggered" process and while True: await ctx.get(clk)? (It doesn't look like you can await a get like this…)
<mcc111[m]>
oh, await tick?
<mcc111[m]>
ctx.edge looks like my ticket. tryign this…
zyp[m] has joined #amaranth-lang
<zyp[m]>
tick if it's a named domain, edge if it's an arbitrary signal
<mcc111[m]>
so i used ctx.get(). it said don't use get, use sample. ok.
<mcc111[m]>
Hmm, I don't think I understand this thing the sample() object returns :(
<mcc111[m]>
I'm also a little worried by the implications of "but before propagation of signal values that have been updated by that clock edge"
<mcc111[m]>
Yeah, I dunno, I'm confused :( I understand how ctx.get() works, and ctx.get() is used in the docs in a way that makes sense, but if I try to use get() in a process I'm told it's not allowed in this context. From poring over the docs I think I need to replace, in my code above, the ctx.get with a ctx.tick().sample() (and it should be awaited to get a true value). but i don't want to tick. i want to get the current value of
<mcc111[m]>
top.video_vs, not the value after a tick occurs.
<mcc111[m]>
i thought i could get a tick from saving the return value of until(), but that's not true, until returns a tuple of… something, actually i'm a little unclear from the docs wording what the values in the tuple are, but i'm pretty sure they're not the values i want
<mcc111[m]>
and before anyone asks "why are you using until() and not edge()": because I don't want to wait for the next edge. i want to wait until the condition is true (if the condition is already true i don't want to wait for it to go low and high again)
<mcc111[m]>
(I feel like I just went through about three iterations of asking a question, then answering it, then asking a new question, so apologies if it's… unclear what I'm asking at this point. The short version is "how do I get the value of a signal at a particular point in the simulation, if I am in a process"
skipwich_ has joined #amaranth-lang
Lord_Nightmare2 has joined #amaranth-lang
Lord_Nightmare has quit [Ping timeout: 248 seconds]
tpw_rules has quit [Ping timeout: 248 seconds]
vup has quit [Ping timeout: 248 seconds]
skipwich has quit [Ping timeout: 252 seconds]
Xesxen has quit [Ping timeout: 252 seconds]
vup has joined #amaranth-lang
Xesxen has joined #amaranth-lang
Lord_Nightmare2 is now known as Lord_Nightmare
tpw_rules has joined #amaranth-lang
<whitequark[cis]>
mcc111: you can't do this in a process, intentionally, because that would result in nondeterminism
<_whitenotifier-3>
[amaranth] github-merge-queue[bot] created branch gh-readonly-queue/main/pr-1478-ba1860553cacfbb5d358f9a9e0699fb7efce0451 - https://github.com/amaranth-lang/amaranth
<mcc111[m]>
my read of the documentation is that you don't read values from the context, but you can read them from a domain trigger object. that is fine, but i am unable to figure out how to do this (obtain a domain trigger object and read values from it)
<mcc111[m]>
all i'm doing currently is setting some processes and then saying run() and it runs until the one critical process terminates. should i just be moving my critical process into a while loop that replaces run?
<_whitenotifier-3>
[amaranth-lang/amaranth-lang.github.io] github-merge-queue[bot] 1cf0046 - Deploying to main from @ amaranth-lang/amaranth@1b319d358cd238c64576f0f257b92c7154634482 🚀
<whitequark[cis]>
mcc111[m]: I'm incredibly confused as to what's the core problem here, and why it's taking so long, to be honest. aren't you just trying to toggle a clock or something?
<mcc111[m]>
I am attempting to port simulator drivers designed for 0.4 to 0.5.
<whitequark[cis]>
the return values of await ctx...sample() and await ctx...until() are different
<mcc111[m]>
yeah
<mcc111[m]>
it also appears ctx.tick().sample().until() is correct but ctx.until().sample() is incorrect?
<whitequark[cis]>
i think the latter is an error?
<whitequark[cis]>
sample is defined on TriggerCombination and TickTrigger, until only on TickTrigger
<whitequark[cis]>
and ctx is neither, it's a SimulatorContext
<mcc111[m]>
sorry, i typed that wrong. ctx.tick().until().sample() is an error.
<whitequark[cis]>
oh, yes. until has to be the last one in the chain
<whitequark[cis]>
it's not a combinator
<whitequark[cis]>
it has a loop internally (as the docs explain) so you can't combine it with the rest
<mcc111[m]>
so like, i think?? if I `ctx.tick().sample(SIGNAL).until(CONDITION)` this samples SIGNAL until CONDITION, and returns… two things i don't care about, then CONDITION, then SIGNAL? maybe? but i see `until` being used with `for async`, so,
<mcc111[m]>
what i'm not sure is whether saying `await ctx.tick().sample(SIGNAL).until(CONDITION)` will *repeat the loop once and return the first value* or *repeat the loop until the until fails and then return the final values*
<mcc111[m]>
(it's possible i am speaking nonsense here again.)
<whitequark[cis]>
hm
<whitequark[cis]>
until just gives you the value of SIGNAL at the point where CONDITION is met
<whitequark[cis]>
it's a helper whose point is that you don't have to write the loop and discard the two things you don't care about
<mcc111[m]>
you mean, tick.sample.util gives you this?
<mcc111[m]>
s/util/until/
<whitequark[cis]>
also i don't believe you can use until with an async for loop
<whitequark[cis]>
is the documentation for until misleading somehow? (I am missing it if it is)
<mcc111[m]>
there are several problems i have having with the docs.
<mcc111[m]>
for one thing, a, b, *c, d = function() syntax was kind of surprising to me and i didn't know until i read this it was even allowed. i'm not sure if that's something that can be fixed at your end.
<whitequark[cis]>
oh! that's standard python since i think 2.7?
<mcc111[m]>
well, one problem i'm having is the fact the return value is complex (a variable number of items depending on context and inputs), combined with mildly-complicating additional syntaxes like for async and the fancy destructure, is making it hard for me to tell what the "simple" version of the call looks like
<whitequark[cis]>
ah, I see
<whitequark[cis]>
did the guide section not come in useful?
<whitequark[cis]>
oh i see we don't have until in the guide section
<whitequark[cis]>
okay
<mcc111[m]>
the first code block does not feature "until". The second block features "until", but calls it on a variable "trigger", the meaning of which is not explained
<whitequark[cis]>
I see the issue now
<whitequark[cis]>
for until, there is actually one example of its use, and the other code block explains what you would have to do if until didn't exist
<mcc111[m]>
there is no sample in the second code block, so the return value described immediately above that ("the values sampled") is not clear
<whitequark[cis]>
this is just bad writing on my end
<mcc111[m]>
i think i understand this now but it took me a while to understand it. the text could be improved
<whitequark[cis]>
ok, let me try to explain it in a more reasonable way
<whitequark[cis]>
trigger is any TickTrigger, which may include several .sample() values already (or not, in which case values will be just None I think)
<whitequark[cis]>
if you use until, you don't get the stuff you'd normally get awaiting any TickTrigger at the beginning (clk_edge, rst_active) because it takes care of those internally
<whitequark[cis]>
correct (the reason until does two things is that if you loop you have to massage the return values)
<whitequark[cis]>
there is also the wrinkle that tick and edge, delay, changed form two incompatible chains
<whitequark[cis]>
but they both have sample
<mcc111[m]>
okay
<mcc111[m]>
so also just asking for clarification
<mcc111[m]>
*every time i add a sample*, the chain of things being returned from the previous line of ticks gets longer and longer?
<whitequark[cis]>
correct
<mcc111[m]>
so i have to know exactly what i have, i cannot just get a tick.
<whitequark[cis]>
correct
<mcc111[m]>
i think i know how to proceed from here, but i have a conceptual question
<whitequark[cis]>
(this happens because whenever you await on a tick, the values are sampled some time before your code is entered. this is a core part of the mechanism)
<mcc111[m]>
i want to get a particular edge(), and then i want to sample from it
<whitequark[cis]>
(i.e. by the time your code executes, the values may be different already.)
<whitequark[cis]>
there is no way to "get" an edge
<whitequark[cis]>
that's not a semantic operation in this model
<mcc111[m]>
time doesn't matter
<whitequark[cis]>
you can build up a trigger combination or a tick trigger, and before you await on it, it's an inert object
<whitequark[cis]>
await tells the simulator to act on it
<mcc111[m]>
sampling and then getting the sample on the edge are, after the fact, indistinguishable from getting an edge and then sampling it. i'm trying to describe my after-the-fact desired state.
<mcc111[m]>
my after the fact desired state is i have a SIGNAL and a CONDITION, CONDITION is met and i can read SIGNAL.
<whitequark[cis]>
yeah
<mcc111[m]>
say CONDITION is already high. if I say edge(CONDITION), that will wait for CONDITION to go low, and then wait for it to go low again. correct?
<whitequark[cis]>
edge(CONDITION) is an error
<mcc111[m]>
edge(CONDITION, true) then?
<mcc111[m]>
s/low/high/
<mcc111[m]>
* say CONDITION is already high. if I say edge(CONDITION, true), that will wait for CONDITION to go low, and then wait for it to go high again. correct?
<whitequark[cis]>
edge(CONDITION, True) or posedge(CONDITION) which are the same thing, will only trigger on a low-to-high transition
<whitequark[cis]>
i.e. if it's high now it'll have to become low first
<whitequark[cis]>
so, correct
<mcc111[m]>
ok. is it reasonable of me that "sample on the next low-to-high transition of CONDITION" and "sample on the next tick where CONDITION is true" are so dissimilar, api-interface wise?
<mcc111[m]>
* of me to be struggling that "sample
<whitequark[cis]>
Amaranth is heavily biased towards the use of synchronous logic; edge is present because you sometimes can't avoid asynchrony, but in almost all cases you should be using tick
<whitequark[cis]>
an example of a case where you should be using edge if you are emulating a peripheral like an SPI flash for an SPI controller core in amaranth-stdio
<whitequark[cis]>
if you're just testing your design, and you want to use edge, and it's a single clock domain design, something went wrong
<whitequark[cis]>
the dissimilarity is very intentional
<mcc111[m]>
if i shouldn't be using edge why is it there?
<whitequark[cis]>
I literally just explained?
<whitequark[cis]>
the idea with these kinds of simulations is that both the RTL you wrote (Module, etc) and the testbench you wrote (async def, etc) operate on the same principles). the principles on which Module is based is clock domain. the way async def stuff interacts with clock domains is await ctx.tick()...
<mcc111[m]>
so my issue is, my design has many circumstances where the next thing i want to do is "wait for X to happen"
<whitequark[cis]>
sometimes you need an escape hatch from this, which is why you have `Instance` for example, and `edge`, and `delay` even. these are a non-normal case, you only use them if you can't do what you want with the normal case (i.e. `m.d.<whatever> += x.eq(y)` and `await ctx.tick()`)
<mcc111[m]>
i don't actually care about edge. edge doesn't do what i want
<whitequark[cis]>
right
<mcc111[m]>
i'm trying to compare that edge and delay have such a different return value shape to until
<mcc111[m]>
like, okay, i could MANUALLY do until with just while ctx.tick() if whatever break
<mcc111[m]>
s/do/recreate/
<whitequark[cis]>
edge, delay, and changed are asynchronous and therefore prone to glitches and race conditions
<whitequark[cis]>
it's extremely easy to write unreliable, hard to debug testbenches if you use them
<whitequark[cis]>
they're basically the Verilog mode
<mcc111[m]>
okay
<whitequark[cis]>
you would usually have one or two instances of edge abstracted away somewhere in a library and build the rest around them, in certain restricted cases
<whitequark[cis]>
like the SPI flash emulator I brought up above
<whitequark[cis]>
like the docs say this explicitly:
<whitequark[cis]>
> In most cases, this method should not be used to wait for a status signal to be asserted or deasserted in a testbench because it is likely to introduce a race condition. Whenever a suitable clock domain is available, use await ctx.tick().until(signal == polarity) instead.
<whitequark[cis]>
s/like//
<mcc111[m]>
so, when i was initially reading the docs
<mcc111[m]>
i saw that sentence
<mcc111[m]>
and i went "okay, then i will use until"
<mcc111[m]>
and then until errored out on me
<whitequark[cis]>
errored out how?
<mcc111[m]>
because it is not a drop in replacement for edge, has a completely different return value, and the documentation for it is not as clear as the documentation for edge
<mcc111[m]>
"method not found on tuple", as i remember
<whitequark[cis]>
right
<whitequark[cis]>
yeah, the docs for until should be improved
<mcc111[m]>
anyway i guess i have three problems
<whitequark[cis]>
the reason the documentation says nothing about the return value is that the return value in this particular case can be ignored completely
<whitequark[cis]>
if you don't add .sample() into a combination there's nothing to look at after you run `.until()~
<whitequark[cis]>
the guide is pretty explicit about what processes are for: replacing logic with Python code. are you doing that?
<mcc111[m]>
whitequark[cis]: because i am porting 0.4 code which used add_sync_process.
<whitequark[cis]>
ok then you should be not using processes
<mcc111[m]>
however, a process is nice because processes are composable.
<mcc111[m]>
for example, i have a process which can capture a video frame from the simulation and i have a second process which can capture audio from the simulation.
<whitequark[cis]>
what do you mean by composable?
<mcc111[m]>
i could imagine doing a single simulation where i capture both. by just saying add_process twice.
<whitequark[cis]>
you can do this with testbenches too
<mcc111[m]>
whitequark[cis]: "there can be two of them"
<whitequark[cis]>
by saying add_testbench twice
<mcc111[m]>
Oh.
<mcc111[m]>
then… i guess because i just picked the wrong function to begin with sighs
<whitequark[cis]>
the user visible difference between add_process and add_testbench is basically that add_process functions can't use ctx.get and they run in a nondeterministic order
<mcc111[m]>
okay give me a moment and i'll see if i can recommend a specific docs change that would have prevented me from being confused in this manner
<whitequark[cis]>
this discussion has had at least three, maybe four, suggested docs change at the moment. I don't have the bandwidth to handle that now or probably soon, so it would be best if you put all of the context you imagine I might need in a month or two to fix this and put it in an issue
<mcc111[m]>
i can look at that. would it alternately be helpful if i attempted to put part of it in a pr?
<whitequark[cis]>
it's probably better if you put it as a diff in an issue
<mcc111[m]>
okay
<whitequark[cis]>
I've found that such frustration-driven doc contributions usually have to be rewritten anyway
<mcc111[m]>
i have one thing i still don't understand tho
<whitequark[cis]>
because they often miss important parts of the overall model
<mcc111[m]>
A sentence I feel should be in the doc but don't currently understand is:
<mcc111[m]>
A process is more limited than a testbench, but should be used when __________
<whitequark[cis]>
the guide does say that, I think?
<whitequark[cis]>
> During simulation, it is possible to replace an Amaranth circuit with the equivalent Python code. This can be used to improve simulation performance, or to avoid reimplementing complex Python algorithms in Amaranth if they do not need to be synthesized.
<whitequark[cis]>
> This is done by adding a process to the simulator: an async Python function that runs as an integral part of the simulation, simultaneously with the DUT.
<whitequark[cis]>
* > During simulation, it is possible to replace an Amaranth circuit with the equivalent Python code. This can be used to improve simulation performance, or to avoid reimplementing complex Python algorithms in Amaranth if they do not need to be synthesized.
<whitequark[cis]>
>
<whitequark[cis]>
> This is done by adding a process to the simulator: an async Python function that runs as an integral part of the simulation, simultaneously with the DUT.
<whitequark[cis]>
it tells you what it's for and when it should be used
<mcc111[m]>
i'm looking at the section "Replacing circuits with code"
<whitequark[cis]>
me too
<mcc111[m]>
why can't I do that with a testbench?
<mcc111[m]>
or perhaps, why shouldn't I?
<whitequark[cis]>
it will introduce race conditions
<whitequark[cis]>
all processes run "simultaneously" with each other (i.e. anything you can do in a process that has to do with ctx will return exactly the same value regardless of the order in which processes run)
<whitequark[cis]>
all testbenches run in the order in which they are added, and you can observe this via ctx.get()
<whitequark[cis]>
so imagine you have two processes A, B, with two wires ab, ba (going from A to B and from B to A respectively)
<whitequark[cis]>
anything that works in a process will also work in a testbench in exactly the same way
<whitequark[cis]>
(you can only observe the discrepancies between them using get)
<mcc111[m]>
alright, i guess i get it now
<mcc111[m]>
i'll come back here if i get stuck again
<whitequark[cis]>
there is one more thing
<whitequark[cis]>
every time you run ctx.set() in a testbench, this may run processes
<whitequark[cis]>
this means that you can do ctx.set(), have it go through a comb assignment in a process, and then you can retrieve it via ctx.get() in a testbench
<mcc111[m]>
oh, that is important
<whitequark[cis]>
if you do ctx.set() in a process, it merely records that the value is going to change on the next simulator advance
<whitequark[cis]>
I feel that explaining the details of the simulation engine in the simulator doc is just intractable and it will cause people to quit reading it
<whitequark[cis]>
so instead, I explain it by use case
<whitequark[cis]>
the deprecation warning on add_sync_process is technically correct but perhaps it could more strongly recommend add_testbench, or explain when to use one or the other
<whitequark[cis]>
you could open a separate issue for that I guess
<mcc111[m]>
whitequark[cis]: this would have helped; one problem was i started by just ctrl-Fing for 'add process' and assuming it was equivalent, when this is not the case
<mcc111[m]>
thanks for the help
<whitequark[cis]>
the new simulator docs are written for the "I am writing new Amaranth code and want to understand how to use the simulator" case rather than "I am migrating from old Amaranth code" case
<whitequark[cis]>
this is because it was sufficiently burnout-inducing to write this much in that period of time, really
<whitequark[cis]>
and also because we never had docs for the old simulator
<whitequark[cis]>
or well defined semantics
<mcc111[m]>
yes, and i don't think it makes sense to write a "migrating from old amaranth code" doc
<whitequark[cis]>
so it's not super clear how to write that in first place
<mcc111[m]>
however, as you note, the deprecation warning for add_sync_process has a very specific and limited audience!
<mcc111[m]>
…possibly consisting of me and nobody else.
<whitequark[cis]>
it's probably more people than this
<mcc111[m]>
my wav and png output testbenches are now working!
<whitequark[cis]>
nice! congratulations
<whitequark[cis]>
it's taken a lot longer than I wish it did but at least it's done
<mcc111[m]>
also to be clear you expressed surprise it took this long this is "week 3, working one day a week"