ra4king / CircuitSim

Basic Circuit Simulator
https://ra4king.github.io/CircuitSim
BSD 3-Clause "New" or "Revised" License
76 stars 27 forks source link

Bug: using multiple clocks in a circuit leads to non-deterministic behavior #59

Closed ra4king closed 1 year ago

ra4king commented 4 years ago

I haven't been able to repro this, but I'll dig into this. Internally, there's only 1 global clock so no matter which clock component is used, the same clock signal gets propagated.

Pear0 commented 4 years ago

I ran into similar issues this semester while writing a Verilog to CircuitSim synthesizer, but finally got around to making a test case. I won't go into how the circuit elaboration works, but I avoided this when generating registers by using a mux instead of the enable bit.

Testcase

Testcase: https://drive.google.com/open?id=1JCGIsZEnFjT7cnMf8XtWFUHGAEE1zNT0 The first circuit "Simple" is four logically equivalent oscillators but with different behavior. The one on the left never oscillates. The one on the right always oscillates. The bottom-middle one seems like it always oscillates but you may be able to catch it not oscillating. The top middle one is completely non-deterministic. It will either oscillate consistently or won't oscillate at all, until the file is reloaded or a change is made to the circuit.

Video: In the video I show the Simple circuits, but I also show an oscillator variant that uses two different clock sources, well chosen gate propagation counts (and luck) to create an oscillator that can be enabled or disabled with a button even though both sources of the mux are logically equivalent so the button shouldn't change anything.

video

What I think the problem is

I've read some of the CircuitSim source, but not all of it so my understanding of how CircuitSim's evaluation works is not perfect. In the Register class, the new value is clocked in if ENABLE=1 and CLK rises from 0 to 1. The issue seems to be that the exact order these updates are evaluated by valueChanged() is non-deterministic and the update will be missed if CLK rises first, then ENABLE is updated from 0 to 1.

ra4king commented 4 years ago

This behavior sounds like it's working as intended: the input is copied to the output only when the register is enabled and the clock rises from 0 to 1. If you build a register manually using logic gates, you'll reproduce this exact behavior. Technically this is not non-deterministic, since you just have to design your system such that the enable signal reaches the register before the rising edge of the clock, which is usually how a system is designed: 1) clock rises -> all signals reach a steady state to enable the required registers 2) clock falls 3) clock rises -> the inputs are immediately copied to the outputs in the previously enabled registers and a new set of registers should be enabled

Never should a physical system be designed such that the enable and clock signals rise at the same time, otherwise you get actual non-deterministic behavior based on how many gates there are and the lengths of the wires. You're lucky you don't have to worry about the lengths of the wires in CircuitSim :)