Open kosarev opened 2 years ago
Changing the code to update all nodes on every round leads to more failures on xf and yf even without round-level shuffling.
Because updating rounds essentially quantify propagation of signals, thus making the simulation behave more like real hardware, updating all nodes effectively works as cross-round shuffling. The failures we see with that quantification eliminated should probably suggest that all or at least many instructions updating flags 3 and 5 depend on particular propagation timings, technically meaning race condition, with scf/ccf
being the case where the balance between possible outcomes is fragile enough to be visible when using actual hardware, as people on the Discord server report.
diff --git a/tests/z80sim/z80sim.py b/tests/z80sim/z80sim.py
index 1333e46..de76120 100755
--- a/tests/z80sim/z80sim.py
+++ b/tests/z80sim/z80sim.py
@@ -1419,7 +1419,7 @@ class Z80Simulator(object):
def __update_nodes(self, nodes, *, shuffle=True):
# TODO: Does always updating all nodes lead to any failures?
- # nodes = list(self.__nodes.values())
+ nodes = list(self.__nodes.values())
shuffle &= (SEED is not None)
nodes = list(nodes)
@@ -2141,7 +2141,8 @@ def test_node(instrs, n, at_start, at_end, before, after):
f' expected: {x}',
f' diff: {a ^ x}'))
print('\n'.join(lines), file=sys.stderr, flush=True)
- raise TestFailure()
+ # raise TestFailure()
+ return CheckToken()
phase = len(instrs)
instr = instrs[-1]
$ pypy3 z80sim.py --single-thread --no-before-after-expected
FAILED: inc/dec {b, c, d, e, h, l, a} reg_f3 (xf)
FAILED: inc/dec {b, c, d, e, h, l, a} reg_f5 (yf)
FAILED: inc/dec {b, c, d, e, h, l, a} reg_ff3 (xf)
FAILED: inc/dec {b, c, d, e, h, l, a} reg_ff5 (yf)
13:17:59 1/51 inc/dec {b, c, d, e, h, l, a}
FAILED: in/out r, (c) reg_f3 (xf)
FAILED: in/out r, (c) reg_f5 (yf)
FAILED: in/out r, (c) reg_ff3 (xf)
FAILED: in/out r, (c) reg_ff5 (yf)
13:18:00 2/51 in/out r, (c)
FAILED: <alu> {b, c, d, e, h, l, a} reg_f3 (xf)
FAILED: <alu> {b, c, d, e, h, l, a} reg_f5 (yf)
FAILED: <alu> {b, c, d, e, h, l, a} reg_ff3 (xf)
FAILED: <alu> {b, c, d, e, h, l, a} reg_ff5 (yf)
13:18:03 3/51 <alu> {b, c, d, e, h, l, a}
FAILED: adc/sbc hl, <rp> reg_f3 (xf)
FAILED: adc/sbc hl, <rp> reg_f5 (yf)
FAILED: adc/sbc hl, <rp> reg_ff3 (xf)
FAILED: adc/sbc hl, <rp> reg_ff5 (yf)
13:18:05 4/51 adc/sbc hl, <rp>
FAILED: add hl, <rp> reg_f3 (xf)
FAILED: add hl, <rp> reg_f5 (yf)
FAILED: add hl, <rp> reg_ff3 (xf)
FAILED: add hl, <rp> reg_ff5 (yf)
13:18:06 5/51 add hl, <rp>
FAILED: <alu> n reg_f3 (xf)
FAILED: <alu> n reg_f5 (yf)
FAILED: <alu> n reg_ff3 (xf)
FAILED: <alu> n reg_ff5 (yf)
13:18:06 6/51 <alu> n
FAILED: <alu> (hl) reg_f3 (xf)
FAILED: <alu> (hl) reg_f5 (yf)
FAILED: <alu> (hl) reg_ff3 (xf)
FAILED: <alu> (hl) reg_ff5 (yf)
13:18:07 7/51 <alu> (hl)
FAILED: bit (hl) reg_f3 (xf)
FAILED: bit (hl) reg_f5 (yf)
FAILED: bit (hl) reg_ff3 (xf)
FAILED: bit (hl) reg_ff5 (yf)
13:18:08 8/51 bit (hl)
FAILED: rot/bit/res/set {b, c, d, e, h, l, a} reg_f3 (xf)
FAILED: rot/bit/res/set {b, c, d, e, h, l, a} reg_f5 (yf)
FAILED: rot/bit/res/set {b, c, d, e, h, l, a} reg_ff3 (xf)
FAILED: rot/bit/res/set {b, c, d, e, h, l, a} reg_ff5 (yf)
13:18:22 9/51 rot/bit/res/set {b, c, d, e, h, l, a}
13:18:25 10/51 rot/res/set (hl)
13:18:26 11/51 ret cc
13:18:26 12/51 djnz d
13:18:27 13/51 jr cc, d
13:18:28 14/51 xnop
13:18:28 15/51 rrd/rld
13:18:28 16/51 rst n
FAILED: scf/ccf reg_f3 (xf)
FAILED: scf/ccf reg_f5 (yf)
FAILED: scf/ccf reg_ff3 (xf)
FAILED: scf/ccf reg_ff5 (yf)
13:18:29 17/51 scf/ccf
13:18:29 18/51 rlca/rrca/rla/rra
13:18:30 19/51 reti/retn/xretn
13:18:30 20/51 pop <rp2>
13:18:30 21/51 ret
13:18:31 22/51 push <rp2>
13:18:31 23/51 ld {i, r}, a/ld a, {i, r}
13:18:32 24/51 neg/xneg
13:18:32 25/51 ld {b, c, d, e, h, l, a}, {b, c, d, e, h, l, a}
13:18:33 26/51 nop
13:18:33 27/51 ld hl, (nn)/ld (nn), hl
13:18:34 28/51 ld {b, c, d, e, h, l, a}, n
13:18:34 29/51 ld <rp>, (nn)/ld (nn), <rp>
13:18:34 30/51 ld {b, c, d, e, h, l, a}, (hl)
13:18:35 31/51 ld a, (nn)/ld (nn), a
13:18:35 32/51 jr d
13:18:36 33/51 ld <rp>, nn
13:18:36 34/51 ld sp, hl
13:18:36 35/51 ld (hl), n
13:18:37 36/51 ld (hl), {b, c, d, e, h, l, a}
13:18:37 37/51 ld (<rp>), a/ld a, (<rp>)
13:18:38 38/51 jp nn
13:18:38 39/51 jp hl
13:18:38 40/51 inc/dec <rp>
13:18:39 41/51 inc/dec (hl)
13:18:39 42/51 daa
13:18:39 43/51 cpl
13:18:40 44/51 call nn
13:18:40 45/51 ex (sp), hl
13:18:41 46/51 in a, (n)/out (n), a
13:18:41 47/51 ei/di
13:18:41 48/51 im/xim n
13:18:42 49/51 exx
13:18:42 50/51 ex de, hl
13:18:42 51/51 ex af, af'
scf/ccf
seems to be susceptible to the order nodes are getting updated in during simulation.Can be reproduced on https://github.com/kosarev/z80/commit/ec19a48df0cd9c022eeab8f8626e459090578b14, with seemingly any seed, though one time I observed all tests passing with the shuffling enabled on an early version of the patch that didn't support seeding yet, so presumably not all seeds will do.
Feels like this may have something to do with
rlca
& Co. not having the expected effect onscf/ccf
in our simulation as mentioned in https://github.com/kosarev/z80/issues/42#issuecomment-1259959562.The task is to try to minimise the reproducer and determine the specific conditions that trigger the behaviour change.
xref: https://discord.com/channels/654774470652723220/689220116801650811/1031181913878183966