[ MAJOR BUG ] - NAND is NOT working properly - random glitching / timing
In circuits that are extremely dependent on tick synchronization, this issue becomes a major obstacle, and some situations are simply unsolvable... To me, this game has great potential, but it suffers from a fundamental flaw in one of its core components — the NAND gate. I’m attaching some images. (not only NAND gate but any circuit with some kind of feedback loop). Without this bug being fixed and without a place to share experiences — like a Discord server — this game feels like a complete waste of time for me, honestly. Spending countless hours building a circuit that mysteriously works sometimes and other times doesn’t is just plain frustrating.
- this behaviour is not persistent, sometimes there is one tick glitch sometimes no. Different tick rate settings changes nothing. Sometimes it glitfching every N tick sometimes every every N+1 tick... cant be solved because this randomnes... In one word "frustrating"
FRAME 1:
FRAME 2: same inputs like in frame 3 but different output.
FRAME 3:
I think this is a duplicate of #471, which you made. If this wasn't a duplicate, you should say so, to prevent confusion.
I think this is a duplicate of #471, which you made. If this wasn't a duplicate, you should say so, to prevent confusion.
no its not duplicate. Because Sebastian Lague said its not the problem with the NAND i so folowed that route to explore this major bug, so i posted this fundamental bug as a bug.
PS: do you have something relevant to the topic?
I think this is a duplicate of #471, which you made. If this wasn't a duplicate, you should say so, to prevent confusion.
no its not duplicate. Because Sebastian Lague said its not the problem with the NAND i so folowed that route to explore this major bug, so i posted this fundamental bug as a bug.
PS: do you have something relevant to the topic?
it is a duplicate. it's not a problem with the NAND gate, it's an artifact of how the simulation has to propagate state in loopback circuits. again: read #326
I think this is a duplicate of #471, which you made. If this wasn't a duplicate, you should say so, to prevent confusion.
no its not duplicate. Because Sebastian Lague said its not the problem with the NAND i so folowed that route to explore this major bug, so i posted this fundamental bug as a bug. PS: do you have something relevant to the topic?
it is a duplicate. it's not a problem with the NAND gate, it's an artifact of how the simulation has to propagate state in loopback circuits. again: read #326
like i said. its not a duplicate, because its another case. Original topic was closed. This is explaining what is fundamentaly wrong. I said "not only NAND gate but any circuit with some kind of feedback loop"... read a whole comment. From my point view is that when you cant do perfect feedback loops without glitching is called a bug not "artifact". If digital machine have artifact to not count properly, its not digital machine in basic... Until this bug is fixed, its waste of time to play with, like i said before.
To clarify for others who are confused as to what the difference between this and #326 , the latter is about the non-deterministic state of circuits with loops. This issue is about incorrect behavior of circuits after initialization, though we don't know if it's due to timing or something else.
Does anyone have a minimal circuit demonstrating the error?
I think I have a minimal circuit demonstrating asymmetry in evaluation, and I believe this to be the actual bug in question.
- When !Q is active and I turn on Clock, Q goes live but !Q stays live.
- When I turn off Clock !Q goes dark and Q stays live.
- When Q is active and I turn on the clock, !Q goes live.
- When I then turn off Clock, !Q goes dark again.
Expected behavior is that when Clock is triggered the live outputs should toggle/swap.
Also, this used to be a JK flip-flop which is why those inputs have those labels instead of S/R.
Expected behavior is that when Clock is triggered the live outputs should toggle/swap.
A basic S/R latch wont toggle with both inputs on, only a J/K flip-flop does that. If you look at an S/R latch's truth table, you'll see that is an invalid input.
@smokeflypaper This is actually invalid (see above) since it's not a issue with the simulation. It works just fine and does what is expected. Rather, it's your chips that are "faulty" since they aren't doing what you think they do.
Edited to add labels to the diagram.
First, I'm not the original reporter. I'm another user running into surprises and trying to figure out if it's me or the sim.
Second, my example was flawed as it didn't include the second feedback lines from the front to the back. I have updated it accordingly. Since I'm trying to make my example only use atomic components, the requisite 3-input NAND is expanded into three individual NANDs with one of them acting as a NOT. Given that, the updated example should represent the DLS equivalent of a classic JK flip-flop, which, according to everything I've found online, is expected to toggle on the leading edge of the clock signal if J and K are both up.
My expectation is that when the clock line rises, 4j and 4k will switch to off and on, respectively and simultaneously, THEN their outputs will suppress 1j and 1k, leading 3j and 3k to also turn off, again at the same time, and thus 4j and 4k will stay swapped, rather than both being on.
What is sometimes happens for me is that, when J and K are up and I raise IN (clock) the circuit goes into a loop of alternation. Other times it stops with both Q and -Q up, and when I lowered clock it goes back to what it had been before.
What seems to be happening is that the circuit is processed depth-first instead of breadth-first, so that 4j and 4k aren't flopped before 3j and 3k go down.
My understanding is that the difference in behavior between DLS and all the articles online is that DLS doesn't try to simulate the natural delays in a real-world implementation, and that the real-world implementations only work the way they do because of those delays. Is that the gist of the "won't-fix" response, or is there something else I'm missing too?
Would @SebLague accept a PR to address this? My idea for a solution is to not have signals propagate all the way through in one cycle step. In this scenario, after clicking the "clock" input, the next cycle would raise the wires, the cycle after that would change the states of 3j and 3k, the cycle after that would change the states of the connections between them and 4j and 4k, etc.
To make this efficient I'd add a "dirty" list to track which components need to be updated in the next cycle. Clicking "clock" would add the clock pin to that list. The next cycle would only process the wires connected to it, etc.
I hope I'm helping!
Edited to add labels to the diagram.
First, I'm not the original reporter. I'm another user running into surprises and trying to figure out if it's me or the sim.
Second, my example was flawed as it didn't include the second feedback lines from the front to the back. I have updated it accordingly. Since I'm trying to make my example only use atomic components, the requisite 3-input NAND is expanded into three individual NANDs with one of them acting as a NOT. Given that, the updated example should represent the DLS equivalent of a classic JK flip-flop, which, according to everything I've found online, is expected to toggle on the leading edge of the clock signal if J and K are both up.
My expectation is that when the clock line rises, 4j and 4k will switch to off and on, respectively and simultaneously, THEN their outputs will suppress 1j and 1k, leading 3j and 3k to also turn off, again at the same time, and thus 4j and 4k will stay swapped, rather than both being on.
What is sometimes happens for me is that, when J and K are up and I raise IN (clock) the circuit goes into a loop of alternation. Other times it stops with both Q and -Q up, and when I lowered clock it goes back to what it had been before.
What seems to be happening is that the circuit is processed depth-first instead of breadth-first, so that 4j and 4k aren't flopped before 3j and 3k go down.
My understanding is that the difference in behavior between DLS and all the articles online is that DLS doesn't try to simulate the natural delays in a real-world implementation, and that the real-world implementations only work the way they do because of those delays. Is that the gist of the "won't-fix" response, or is there something else I'm missing too?
Would @SebLague accept a PR to address this? My idea for a solution is to not have signals propagate all the way through in one cycle step. In this scenario, after clicking the "clock" input, the next cycle would raise the wires, the cycle after that would change the states of 3j and 3k, the cycle after that would change the states of the connections between them and 4j and 4k, etc.
To make this efficient I'd add a "dirty" list to track which components need to be updated in the next cycle. Clicking "clock" would add the clock pin to that list. The next cycle would only process the wires connected to it, etc.
I hope I'm helping!
I was talking to the original reporter... but thank you anyway...
What is sometimes happens for me is that, when J and K are up and I raise IN (clock) the circuit goes into a loop of alternation. Other times it stops with both Q and -Q up, and when I lowered clock it goes back to what it had been before.
Yes, this is a bit of an issue. Though, while this works in electronics, technically the logic is working as intended here. You're burning both inputs high at the same time, and when you release the clock, it's up to a race condition that determines which way the latch will settle. To get it to work like an electronic JK, you would need a clock pulse that's shorter than the game's update tick rate. Or, update parts of the circuit slower than the clock pulse. Again, I believe this is what Seb and Rob were talking about here https://github.com/SebLague/Digital-Logic-Sim/issues/326#issuecomment-2819334599.
It is a separate 'issue' that the feedback loops settle differently after each initialization, but I believe that would become a moot point if Seb changes how propagation delay is managed.
In the meantime, I have simply been using a Master-Slave J-K Flip-Flop design for more robustness. It's not perfect, but it fails far less than other latch designs.
[This sets on the falling edge, but you can make it rising edge by inverting where the not gate is on the clock. The issue with that is it can cause problems for resetting, etc. Falling edge detection has been working fine for my designs.]
Edit: To add do this, as my design looks the same as smokeflypaper, I believe he's complaining about both the Q and Q-bar being on at the same time. That can cause some issues as it will cause timers to momentarily count backwards for a tick. I've fond this not to be an issue as long as you keep your step-per-clock high enough so that your system doesn't read the garbage data. I need to keep mine S/C above 2 or 3 to be stable, but this number might be higher in more complex designs.
Realistically real computers have a garbage data settle time as well, so it's not unusual to have to work around this constraint.
latches settling into an indeterminate state after initialization is a non issue. real latches settle into an indeterminate state. that is a known design choice by Sebastian.
#326 is about the assymmetry in the number of steps between set and reset actions of a latch. one will take longer, and which one takes longer is also indeterminate, which makes it impossible to adjust for.
#326 also touches on that Q and Q-bar will sometimes be the same value on the same tick.
@smokeflypaper This is actually invalid (see above) since it's not a issue with the simulation. It works just fine and does what is expected. Rather, it's your chips that are "faulty" since they aren't doing what you think they do.
Okay, if my JK flip‑flops are ‘faulty,’ could you please show me a ‘non‑faulty’ JK flip‑flop built into a simple 4‑bit counter and record—frame by frame—how its outputs behave?
What is sometimes happens for me is that, when J and K are up and I raise IN (clock) the circuit goes into a loop of alternation. Other times it stops with both Q and -Q up, and when I lowered clock it goes back to what it had been before.
Yes, this is a bit of an issue. Though, while this works in electronics, technically the logic is working as intended here. You're burning both inputs high at the same time, and when you release the clock, it's up to a race condition that determines which way the latch will settle. To get it to work like an electronic JK, you would need a clock pulse that's shorter than the game's update tick rate. Or, update parts of the circuit slower than the clock pulse. Again, I believe this is what Seb and Rob were talking about here #326 (comment).
It is a separate 'issue' that the feedback loops settle differently after each initialization, but I believe that would become a moot point if Seb changes how propagation delay is managed.
In the meantime, I have simply been using a Master-Slave J-K Flip-Flop design for more robustness. It's not perfect, but it fails far less than other latch designs.
[This sets on the falling edge, but you can make it rising edge by inverting where the not gate is on the clock. The issue with that is it can cause problems for resetting, etc. Falling edge detection has been working fine for my designs.]
Edit: To add do this, as my design looks the same as smokeflypaper, I believe he's complaining about both the Q and Q-bar being on at the same time. That can cause some issues as it will cause timers to momentarily count backwards for a tick. I've fond this not to be an issue as long as you keep your step-per-clock high enough so that your system doesn't read the garbage data. I need to keep mine S/C above 2 or 3 to be stable, but this number might be higher in more complex designs.
Realistically real computers have a garbage data settle time as well, so it's not unusual to have to work around this constraint.
I tried upgrading the JK flip‑flop with those NAND gates in front. I also experimented with the steps‑per‑clock timing on a simple circuit (just one JK FF). My computer can handle up to 8 192 steps per second for a single JK FF, so I set it to 4 096 steps per clock tick, but I also tried other timings all the way down to the minimum values.
Then I connected your JK FF to a fault‑signal check (just a simple AND comparator that stretches a fault pulse). A video of the test is attached.
https://github.com/user-attachments/assets/6edb9424-6c86-4c87-8003-3992d78435a4
A solution can be to invert the -Q output instead of propagating the signal directly from the bottom NAND gate. This solution works, but JK flip-flops are not typically constructed this way. I'm attaching a video.
PS: Even so, the circuit sometimes behaves unpredictably. If you expand it anywhere in the system— even in a completely separate block that’s only indirectly connected to the flip‑flop—the glitch can still appear and both outputs may pulse HIGH. Whenever that happens, you have to restart the entire game to get stable behaviour again. While this workaround technically works, what I originally meant was a dedicated filter component where you could set a time threshold, and it would only pass signals longer than that threshold. That would be a cleaner and more reliable solution—hopefully it will be added someday.
https://github.com/user-attachments/assets/6213102f-0da5-4c79-9dc8-13291b56773f
I tried upgrading the JK flip‑flop with those NAND gates in front. I also experimented with the steps‑per‑clock timing on a simple circuit (just one JK FF). My computer can handle up to 8 192 steps per second for a single JK FF, so I set it to 4 096 steps per clock tick, but I also tried other timings all the way down to the minimum values.
Then I connected your JK FF to a fault‑signal check (just a simple AND comparator that stretches a fault pulse). A video of the test is attached.
JK-FF-FAULT.CHECK.mp4
I know it seems like I'm beating a dead horse, but you are figuring out the exact same thing that I figured out in #326 .
There is a random selector on which "subtree" (quotes because I don't think Sebastian ever uses the term "subtree") to resolve, which sometimes leads to situations where JK Flip-flops have their Q and /Q outputs be the same for one tick.
https://github.com/SebLague/Digital-Logic-Sim/issues/326#issuecomment-2816532694
https://github.com/SebLague/Digital-Logic-Sim/issues/326#issuecomment-2816849244
again, another (and related) artifact of this random selector mechanism is that the J/K flip flop takes a tick longer to either set or reset and which one takes longer is random (and coincides with the J/K's initial state).
I know that in that thread I go back and forth on whether it's "a Bug" in the strictest sense, or if it's an anticipated, albeit unfavorable, result of the simulation, but that's me wrestling with semantics - the point with underscoring is that it is undesirable (and potentially non-trivial to "fix")
You can see what Sebastian as to say about it here: https://github.com/SebLague/Digital-Logic-Sim/issues/326#issuecomment-2819334599
I did see that you commented on that very thread, but given that you're touching on the very things that I've dissected, I get the impression that you didn't really read it (I can't say I entirely blame you, it's a fairly long and meandering thread). But my point is that this is both a known thing, and that Sebastian has said that he's open to input on it.
In the absence of any further word from Sebastian, it sounds like those of us who want the tree processed breadth-first have no other option than to fork the project. I noticed that it was forked once before but that fork hasn't been active for almost a year. I've got my own fork now on which I'm planning to develop my "process only dirty elements" patch. If anyone wants to join me there, let me know.
In the absence of any further word from Sebastian, it sounds like those of us who want the tree processed breadth-first have no other option than to fork the project. I noticed that it was forked once before but that fork hasn't been active for almost a year. I've got my own fork now on which I'm planning to develop my "process only dirty elements" patch. If anyone wants to join me there, let me know.
What do you mean "in absence of any further word from Sebastian"? He's given his thoughts, a potential solution he's considering, the adverse effects therein, and a call for feedback right here: https://github.com/SebLague/Digital-Logic-Sim/issues/326#issuecomment-2819334599
In the absence of any further word from Sebastian, it sounds like those of us who want the tree processed breadth-first have no other option than to fork the project. I noticed that it was forked once before but that fork hasn't been active for almost a year. I've got my own fork now on which I'm planning to develop my "process only dirty elements" patch. If anyone wants to join me there, let me know.
I think the simplest solution is the one I mentioned in my post (PS: edit) — just to have the opposite of what the pulse component does. Some kind of threshold signal filter that blocks signals shorter than a specified duration. But so far, this idea hasn't been acknowledged...
I tried upgrading the JK flip‑flop with those NAND gates in front. I also experimented with the steps‑per‑clock timing on a simple circuit (just one JK FF). My computer can handle up to 8 192 steps per second for a single JK FF, so I set it to 4 096 steps per clock tick, but I also tried other timings all the way down to the minimum values. Then I connected your JK FF to a fault‑signal check (just a simple AND comparator that stretches a fault pulse). A video of the test is attached. JK-FF-FAULT.CHECK.mp4
I know it seems like I'm beating a dead horse, but you are figuring out the exact same thing that I figured out in #326 .
There is a random selector on which "subtree" (quotes because I don't think Sebastian ever uses the term "subtree") to resolve, which sometimes leads to situations where JK Flip-flops have their Q and /Q outputs be the same for one tick.
again, another (and related) artifact of this random selector mechanism is that the J/K flip flop takes a tick longer to either set or reset and which one takes longer is random (and coincides with the J/K's initial state).
I know that in that thread I go back and forth on whether it's "a Bug" in the strictest sense, or if it's an anticipated, albeit unfavorable, result of the simulation, but that's me wrestling with semantics - the point with underscoring is that it is undesirable (and potentially non-trivial to "fix")
You can see what Sebastian as to say about it here: #326 (comment)
I did see that you commented on that very thread, but given that you're touching on the very things that I've dissected, I get the impression that you didn't really read it (I can't say I entirely blame you, it's a fairly long and meandering thread). But my point is that this is both a known thing, and that Sebastian has said that he's open to input on it.
Of course I read it — which is obvious from the fact that I commented on it some time ago and even proposed this:
"As a possible workaround, I propose the idea of a component that only allows pulses longer than N ticks to pass through. This could act as a filter that removes short, unintended glitches caused by timing anomalies..."
Unfortunately, no one responded to that suggestion, or even commented on whether it's a feasible and simple workaround.
In the absence of any further word from Sebastian, it sounds like those of us who want the tree processed breadth-first have no other option than to fork the project. I noticed that it was forked once before but that fork hasn't been active for almost a year. I've got my own fork now on which I'm planning to develop my "process only dirty elements" patch. If anyone wants to join me there, let me know.
What do you mean "in absence of any further word from Sebastian"? He's given his thoughts, a potential solution he's considering, the adverse effects therein, and a call for feedback right here: #326 (comment)
I meant that Sebastian has stated his vision clearly already and hasn't said anything to contradict it since, nor does he need to. He has had ample opportunity to speak up, and having not done so it's safe to say he isn't going to change his mind no matter how we state our position(s).
We find ourselves in violent agreement :)
Given that, those of us who object to the current behavior must accept it and move on rather than stirring people up on this thread. The beauty of open source is that we can try it our way and if it works better Sebastian can take our patches, and if it doesn't, he doesn't have to waste his time trying it out.
In the absence of any further word from Sebastian, it sounds like those of us who want the tree processed breadth-first have no other option than to fork the project. I noticed that it was forked once before but that fork hasn't been active for almost a year. I've got my own fork now on which I'm planning to develop my "process only dirty elements" patch. If anyone wants to join me there, let me know.
It's been forked many, many more times since then... that version is really old now and made before 2.0. (not your fork, the community edition)
It's been forked many, many more times since then... that version is really old now and made before 2.0. (not your fork, the community edition)
Are there any especially active forks? I don't want to duplicate any effort and I'd rather help someone else than have someone help me if given the choice. And should I take this to Discussions/Dev?
Are there any especially active forks?
Here is your answer. (literally just the list of all forks sorted by last updated)
I don't want to duplicate any effort and I'd rather help someone else than have someone help me if given the choice.
As far as I know, there aren't any forks that attempt to do the same thing, but there are 494 of them, so I definitely haven't seen them all. I've only seen the forks with PRs on the github (see this for a list) so far.
And should I take this to Discussions/Dev?
As far as I know, seb doesn't check the discussions regularly. (despite them existing) Neither do most of the other users here, some discussions have no comments at all. (see mine, for example) Plus, since seb hasn't commented on this issue, it's likely that he doesn't have this as a very high priority right now.