FA-89116 / Digital logic simulation / Open access
Write-first RAM returns stale data · case 01
In write-first mode a read in the same cycle as a write returns the previous word.
ROOT CAUSE
The read path ignores the configured read-during-write mode.
VERIFIED REPAIR
Return the merged new word in write_first mode and the old word in read_first mode.
Unsuccessful approach: Inverting the mode test breaks read-first memories instead.
Case contract
Input [depth, mode, ops]; memory holds 16-bit words initialised to 0. Each op [we, addr, wdata, be, re]: be bit k enables byte lane k (bits 8k..8k+7). Addresses outside 0..depth-1 ignore writes and read 'x'. A read in the same cycle as a write returns the old word in mode 'read_first' and the merged new word in 'write_first'. re=0 yields None.
Why this case matters
RTL memory models must match the inferred RAM read-during-write and byte-enable behaviour or simulation diverges from silicon.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(*args):
depth, mode, ops = args
mem = [0] * depth
out = []
for we, addr, wdata, be, re in ops:
ok = 0 <= addr < depth
old = mem[addr] if ok else None
new = old
if we and ok:
for lane in range(2):
if be >> lane & 1:
mask = 0xFF << (8 * lane)
new = (new & ~mask) | (wdata & mask)
mem[addr] = new
if re:
if not ok:
out.append('x')
else:
out.append(old)
else:
out.append(None)
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('full write then read-first read', [5, 'read_first', [[1, 1, 43981, 3, 1], [0, 1, 0, 0, 1]]], [0, 43981]), ('write-first partial write', [5, 'write_first', [[1, 2, 4660, 3, 0], [1, 2, 65535, 1, 1], [0, 2, 0, 0, 1]]], [None, 4863, 4863]), ('high lane only', [5, 'read_first', [[1, 3, 23041, 2, 0], [0, 3, 0, 0, 1]]], [None, 23040]), ('address zero', [5, 'write_first', [[1, 0, 3855, 3, 1], [0, 0, 0, 0, 1]]], [3855, 3855]), ('negative address rejected', [5, 'read_first', [[1, -1, 30583, 3, 1], [0, 4, 0, 0, 1]]], ['x', 0]), ('out of range read', [5, 'read_first', [[0, 6, 0, 0, 1], [0, 1, 0, 0, 0]]], ['x', None]), ('read-first returns old data', [5, 'read_first', [[1, 1, 4369, 3, 0], [1, 1, 8738, 3, 1], [0, 1, 0, 0, 1]]], [None, 4369, 8738])], [('full write then read-first read', [4, 'read_first', [[1, 1, 43981, 3, 1], [0, 1, 0, 0, 1]]], [0, 43981]), ('write-first partial write', [4, 'write_first', [[1, 2, 4660, 3, 0], [1, 2, 65535, 1, 1], [0, 2, 0, 0, 1]]], [None, 4863, 4863]), ('high lane only', [4, 'read_first', [[1, 3, 23042, 2, 0], [0, 3, 0, 0, 1]]], [None, 23040]), ('address zero', [4, 'write_first', [[1, 0, 3855, 3, 1], [0, 0, 0, 0, 1]]], [3855, 3855]), ('negative address rejected', [4, 'read_first', [[1, -1, 30583, 3, 1], [0, 3, 0, 0, 1]]], ['x', 0]), ('out of range read', [4, 'read_first', [[0, 6, 0, 0, 1], [0, 1, 0, 0, 0]]], ['x', None]), ('read-first returns old data', [4, 'read_first', [[1, 1, 4369, 3, 0], [1, 1, 17476, 3, 1], [0, 1, 0, 0, 1]]], [None, 4369, 17476])], [('full write then read-first read', [5, 'read_first', [[1, 1, 43981, 3, 1], [0, 1, 0, 0, 1]]], [0, 43981]), ('write-first partial write', [5, 'write_first', [[1, 2, 4660, 3, 0], [1, 2, 65535, 1, 1], [0, 2, 0, 0, 1]]], [None, 4863, 4863]), ('high lane only', [5, 'read_first', [[1, 3, 23043, 2, 0], [0, 3, 0, 0, 1]]], [None, 23040]), ('address zero', [5, 'write_first', [[1, 0, 3855, 3, 1], [0, 0, 0, 0, 1]]], [3855, 3855]), ('negative address rejected', [5, 'read_first', [[1, -1, 30583, 3, 1], [0, 4, 0, 0, 1]]], ['x', 0]), ('out of range read', [5, 'read_first', [[0, 8, 0, 0, 1], [0, 1, 0, 0, 0]]], ['x', None]), ('read-first returns old data', [5, 'read_first', [[1, 1, 4369, 3, 0], [1, 1, 26214, 3, 1], [0, 1, 0, 0, 1]]], [None, 4369, 26214])], [('full write then read-first read', [4, 'read_first', [[1, 1, 43981, 3, 1], [0, 1, 0, 0, 1]]], [0, 43981]), ('write-first partial write', [4, 'write_first', [[1, 2, 4660, 3, 0], [1, 2, 65535, 1, 1], [0, 2, 0, 0, 1]]], [None, 4863, 4863]), ('high lane only', [4, 'read_first', [[1, 3, 23044, 2, 0], [0, 3, 0, 0, 1]]], [None, 23040]), ('address zero', [4, 'write_first', [[1, 0, 3855, 3, 1], [0, 0, 0, 0, 1]]], [3855, 3855]), ('negative address rejected', [4, 'read_first', [[1, -1, 30583, 3, 1], [0, 3, 0, 0, 1]]], ['x', 0]), ('out of range read', [4, 'read_first', [[0, 8, 0, 0, 1], [0, 1, 0, 0, 0]]], ['x', None]), ('read-first returns old data', [4, 'read_first', [[1, 1, 4369, 3, 0], [1, 1, 34952, 3, 1], [0, 1, 0, 0, 1]]], [None, 4369, 34952])], [('full write then read-first read', [5, 'read_first', [[1, 1, 43981, 3, 1], [0, 1, 0, 0, 1]]], [0, 43981]), ('write-first partial write', [5, 'write_first', [[1, 2, 4660, 3, 0], [1, 2, 65535, 1, 1], [0, 2, 0, 0, 1]]], [None, 4863, 4863]), ('high lane only', [5, 'read_first', [[1, 3, 23045, 2, 0], [0, 3, 0, 0, 1]]], [None, 23040]), ('address zero', [5, 'write_first', [[1, 0, 3855, 3, 1], [0, 0, 0, 0, 1]]], [3855, 3855]), ('negative address rejected', [5, 'read_first', [[1, -1, 30583, 3, 1], [0, 4, 0, 0, 1]]], ['x', 0]), ('out of range read', [5, 'read_first', [[0, 10, 0, 0, 1], [0, 1, 0, 0, 0]]], ['x', None]), ('read-first returns old data', [5, 'read_first', [[1, 1, 4369, 3, 0], [1, 1, 43690, 3, 1], [0, 1, 0, 0, 1]]], [None, 4369, 43690])]]
for label, args, expected in fixtures[N-1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| full write then read-first read | [0, 43981] | [0, 43981] | Passed |
| write-first partial write | [None, 4660, 4863] | [None, 4863, 4863] | Failed |
| high lane only | [None, 23040] | [None, 23040] | Passed |
| address zero | [0, 3855] | [3855, 3855] | Failed |
| negative address rejected | ['x', 0] | ['x', 0] | Passed |
| out of range read | ['x', None] | ['x', None] | Passed |
| read-first returns old data | [None, 4369, 8738] | [None, 4369, 8738] | Passed |
SHA-256 / cb9839401836bdeca871fec80c8bb1a94cc52f11c541661ef051ff2a58e242b2
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(*args):
depth, mode, ops = args
mem = [0] * depth
out = []
for we, addr, wdata, be, re in ops:
ok = 0 <= addr < depth
old = mem[addr] if ok else None
new = old
if we and ok:
for lane in range(2):
if be >> lane & 1:
mask = 0xFF << (8 * lane)
new = (new & ~mask) | (wdata & mask)
mem[addr] = new
if re:
if not ok:
out.append('x')
else:
out.append(new if mode == 'read_first' else old)
else:
out.append(None)
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('full write then read-first read', [5, 'read_first', [[1, 1, 43981, 3, 1], [0, 1, 0, 0, 1]]], [0, 43981]), ('write-first partial write', [5, 'write_first', [[1, 2, 4660, 3, 0], [1, 2, 65535, 1, 1], [0, 2, 0, 0, 1]]], [None, 4863, 4863]), ('high lane only', [5, 'read_first', [[1, 3, 23041, 2, 0], [0, 3, 0, 0, 1]]], [None, 23040]), ('address zero', [5, 'write_first', [[1, 0, 3855, 3, 1], [0, 0, 0, 0, 1]]], [3855, 3855]), ('negative address rejected', [5, 'read_first', [[1, -1, 30583, 3, 1], [0, 4, 0, 0, 1]]], ['x', 0]), ('out of range read', [5, 'read_first', [[0, 6, 0, 0, 1], [0, 1, 0, 0, 0]]], ['x', None]), ('read-first returns old data', [5, 'read_first', [[1, 1, 4369, 3, 0], [1, 1, 8738, 3, 1], [0, 1, 0, 0, 1]]], [None, 4369, 8738])], [('full write then read-first read', [4, 'read_first', [[1, 1, 43981, 3, 1], [0, 1, 0, 0, 1]]], [0, 43981]), ('write-first partial write', [4, 'write_first', [[1, 2, 4660, 3, 0], [1, 2, 65535, 1, 1], [0, 2, 0, 0, 1]]], [None, 4863, 4863]), ('high lane only', [4, 'read_first', [[1, 3, 23042, 2, 0], [0, 3, 0, 0, 1]]], [None, 23040]), ('address zero', [4, 'write_first', [[1, 0, 3855, 3, 1], [0, 0, 0, 0, 1]]], [3855, 3855]), ('negative address rejected', [4, 'read_first', [[1, -1, 30583, 3, 1], [0, 3, 0, 0, 1]]], ['x', 0]), ('out of range read', [4, 'read_first', [[0, 6, 0, 0, 1], [0, 1, 0, 0, 0]]], ['x', None]), ('read-first returns old data', [4, 'read_first', [[1, 1, 4369, 3, 0], [1, 1, 17476, 3, 1], [0, 1, 0, 0, 1]]], [None, 4369, 17476])], [('full write then read-first read', [5, 'read_first', [[1, 1, 43981, 3, 1], [0, 1, 0, 0, 1]]], [0, 43981]), ('write-first partial write', [5, 'write_first', [[1, 2, 4660, 3, 0], [1, 2, 65535, 1, 1], [0, 2, 0, 0, 1]]], [None, 4863, 4863]), ('high lane only', [5, 'read_first', [[1, 3, 23043, 2, 0], [0, 3, 0, 0, 1]]], [None, 23040]), ('address zero', [5, 'write_first', [[1, 0, 3855, 3, 1], [0, 0, 0, 0, 1]]], [3855, 3855]), ('negative address rejected', [5, 'read_first', [[1, -1, 30583, 3, 1], [0, 4, 0, 0, 1]]], ['x', 0]), ('out of range read', [5, 'read_first', [[0, 8, 0, 0, 1], [0, 1, 0, 0, 0]]], ['x', None]), ('read-first returns old data', [5, 'read_first', [[1, 1, 4369, 3, 0], [1, 1, 26214, 3, 1], [0, 1, 0, 0, 1]]], [None, 4369, 26214])], [('full write then read-first read', [4, 'read_first', [[1, 1, 43981, 3, 1], [0, 1, 0, 0, 1]]], [0, 43981]), ('write-first partial write', [4, 'write_first', [[1, 2, 4660, 3, 0], [1, 2, 65535, 1, 1], [0, 2, 0, 0, 1]]], [None, 4863, 4863]), ('high lane only', [4, 'read_first', [[1, 3, 23044, 2, 0], [0, 3, 0, 0, 1]]], [None, 23040]), ('address zero', [4, 'write_first', [[1, 0, 3855, 3, 1], [0, 0, 0, 0, 1]]], [3855, 3855]), ('negative address rejected', [4, 'read_first', [[1, -1, 30583, 3, 1], [0, 3, 0, 0, 1]]], ['x', 0]), ('out of range read', [4, 'read_first', [[0, 8, 0, 0, 1], [0, 1, 0, 0, 0]]], ['x', None]), ('read-first returns old data', [4, 'read_first', [[1, 1, 4369, 3, 0], [1, 1, 34952, 3, 1], [0, 1, 0, 0, 1]]], [None, 4369, 34952])], [('full write then read-first read', [5, 'read_first', [[1, 1, 43981, 3, 1], [0, 1, 0, 0, 1]]], [0, 43981]), ('write-first partial write', [5, 'write_first', [[1, 2, 4660, 3, 0], [1, 2, 65535, 1, 1], [0, 2, 0, 0, 1]]], [None, 4863, 4863]), ('high lane only', [5, 'read_first', [[1, 3, 23045, 2, 0], [0, 3, 0, 0, 1]]], [None, 23040]), ('address zero', [5, 'write_first', [[1, 0, 3855, 3, 1], [0, 0, 0, 0, 1]]], [3855, 3855]), ('negative address rejected', [5, 'read_first', [[1, -1, 30583, 3, 1], [0, 4, 0, 0, 1]]], ['x', 0]), ('out of range read', [5, 'read_first', [[0, 10, 0, 0, 1], [0, 1, 0, 0, 0]]], ['x', None]), ('read-first returns old data', [5, 'read_first', [[1, 1, 4369, 3, 0], [1, 1, 43690, 3, 1], [0, 1, 0, 0, 1]]], [None, 4369, 43690])]]
for label, args, expected in fixtures[N-1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| full write then read-first read | [43981, 43981] | [0, 43981] | Failed |
| write-first partial write | [None, 4660, 4863] | [None, 4863, 4863] | Failed |
| high lane only | [None, 23040] | [None, 23040] | Passed |
| address zero | [0, 3855] | [3855, 3855] | Failed |
| negative address rejected | ['x', 0] | ['x', 0] | Passed |
| out of range read | ['x', None] | ['x', None] | Passed |
| read-first returns old data | [None, 8738, 8738] | [None, 4369, 8738] | Failed |
SHA-256 / f4c3c260eeedc2284afc975f933c880f354c6f17058723983afc221b036eaa28
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(*args):
depth, mode, ops = args
mem = [0] * depth
out = []
for we, addr, wdata, be, re in ops:
ok = 0 <= addr < depth
old = mem[addr] if ok else None
new = old
if we and ok:
for lane in range(2):
if be >> lane & 1:
mask = 0xFF << (8 * lane)
new = (new & ~mask) | (wdata & mask)
mem[addr] = new
if re:
if not ok:
out.append('x')
else:
out.append(new if mode == 'write_first' else old)
else:
out.append(None)
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('full write then read-first read', [5, 'read_first', [[1, 1, 43981, 3, 1], [0, 1, 0, 0, 1]]], [0, 43981]), ('write-first partial write', [5, 'write_first', [[1, 2, 4660, 3, 0], [1, 2, 65535, 1, 1], [0, 2, 0, 0, 1]]], [None, 4863, 4863]), ('high lane only', [5, 'read_first', [[1, 3, 23041, 2, 0], [0, 3, 0, 0, 1]]], [None, 23040]), ('address zero', [5, 'write_first', [[1, 0, 3855, 3, 1], [0, 0, 0, 0, 1]]], [3855, 3855]), ('negative address rejected', [5, 'read_first', [[1, -1, 30583, 3, 1], [0, 4, 0, 0, 1]]], ['x', 0]), ('out of range read', [5, 'read_first', [[0, 6, 0, 0, 1], [0, 1, 0, 0, 0]]], ['x', None]), ('read-first returns old data', [5, 'read_first', [[1, 1, 4369, 3, 0], [1, 1, 8738, 3, 1], [0, 1, 0, 0, 1]]], [None, 4369, 8738])], [('full write then read-first read', [4, 'read_first', [[1, 1, 43981, 3, 1], [0, 1, 0, 0, 1]]], [0, 43981]), ('write-first partial write', [4, 'write_first', [[1, 2, 4660, 3, 0], [1, 2, 65535, 1, 1], [0, 2, 0, 0, 1]]], [None, 4863, 4863]), ('high lane only', [4, 'read_first', [[1, 3, 23042, 2, 0], [0, 3, 0, 0, 1]]], [None, 23040]), ('address zero', [4, 'write_first', [[1, 0, 3855, 3, 1], [0, 0, 0, 0, 1]]], [3855, 3855]), ('negative address rejected', [4, 'read_first', [[1, -1, 30583, 3, 1], [0, 3, 0, 0, 1]]], ['x', 0]), ('out of range read', [4, 'read_first', [[0, 6, 0, 0, 1], [0, 1, 0, 0, 0]]], ['x', None]), ('read-first returns old data', [4, 'read_first', [[1, 1, 4369, 3, 0], [1, 1, 17476, 3, 1], [0, 1, 0, 0, 1]]], [None, 4369, 17476])], [('full write then read-first read', [5, 'read_first', [[1, 1, 43981, 3, 1], [0, 1, 0, 0, 1]]], [0, 43981]), ('write-first partial write', [5, 'write_first', [[1, 2, 4660, 3, 0], [1, 2, 65535, 1, 1], [0, 2, 0, 0, 1]]], [None, 4863, 4863]), ('high lane only', [5, 'read_first', [[1, 3, 23043, 2, 0], [0, 3, 0, 0, 1]]], [None, 23040]), ('address zero', [5, 'write_first', [[1, 0, 3855, 3, 1], [0, 0, 0, 0, 1]]], [3855, 3855]), ('negative address rejected', [5, 'read_first', [[1, -1, 30583, 3, 1], [0, 4, 0, 0, 1]]], ['x', 0]), ('out of range read', [5, 'read_first', [[0, 8, 0, 0, 1], [0, 1, 0, 0, 0]]], ['x', None]), ('read-first returns old data', [5, 'read_first', [[1, 1, 4369, 3, 0], [1, 1, 26214, 3, 1], [0, 1, 0, 0, 1]]], [None, 4369, 26214])], [('full write then read-first read', [4, 'read_first', [[1, 1, 43981, 3, 1], [0, 1, 0, 0, 1]]], [0, 43981]), ('write-first partial write', [4, 'write_first', [[1, 2, 4660, 3, 0], [1, 2, 65535, 1, 1], [0, 2, 0, 0, 1]]], [None, 4863, 4863]), ('high lane only', [4, 'read_first', [[1, 3, 23044, 2, 0], [0, 3, 0, 0, 1]]], [None, 23040]), ('address zero', [4, 'write_first', [[1, 0, 3855, 3, 1], [0, 0, 0, 0, 1]]], [3855, 3855]), ('negative address rejected', [4, 'read_first', [[1, -1, 30583, 3, 1], [0, 3, 0, 0, 1]]], ['x', 0]), ('out of range read', [4, 'read_first', [[0, 8, 0, 0, 1], [0, 1, 0, 0, 0]]], ['x', None]), ('read-first returns old data', [4, 'read_first', [[1, 1, 4369, 3, 0], [1, 1, 34952, 3, 1], [0, 1, 0, 0, 1]]], [None, 4369, 34952])], [('full write then read-first read', [5, 'read_first', [[1, 1, 43981, 3, 1], [0, 1, 0, 0, 1]]], [0, 43981]), ('write-first partial write', [5, 'write_first', [[1, 2, 4660, 3, 0], [1, 2, 65535, 1, 1], [0, 2, 0, 0, 1]]], [None, 4863, 4863]), ('high lane only', [5, 'read_first', [[1, 3, 23045, 2, 0], [0, 3, 0, 0, 1]]], [None, 23040]), ('address zero', [5, 'write_first', [[1, 0, 3855, 3, 1], [0, 0, 0, 0, 1]]], [3855, 3855]), ('negative address rejected', [5, 'read_first', [[1, -1, 30583, 3, 1], [0, 4, 0, 0, 1]]], ['x', 0]), ('out of range read', [5, 'read_first', [[0, 10, 0, 0, 1], [0, 1, 0, 0, 0]]], ['x', None]), ('read-first returns old data', [5, 'read_first', [[1, 1, 4369, 3, 0], [1, 1, 43690, 3, 1], [0, 1, 0, 0, 1]]], [None, 4369, 43690])]]
for label, args, expected in fixtures[N-1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| full write then read-first read | [0, 43981] | [0, 43981] | Passed |
| write-first partial write | [None, 4863, 4863] | [None, 4863, 4863] | Passed |
| high lane only | [None, 23040] | [None, 23040] | Passed |
| address zero | [3855, 3855] | [3855, 3855] | Passed |
| negative address rejected | ['x', 0] | ['x', 0] | Passed |
| out of range read | ['x', None] | ['x', None] | Passed |
| read-first returns old data | [None, 4369, 8738] | [None, 4369, 8738] | Passed |
SHA-256 / f0d5972043ded25ba9c633bcf6086565978310e7987deaee77c932edf2717a0f
Verification & scope
A deterministic bounded teaching model of one simulator rule set; the contract is stipulated and is not a claim of conformance to any HDL standard or commercial simulator. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.
Observations recorded using Python 3.12.14 at 2026-09-29T14:51:14.269883+00:00.
Case digest / 09614d58f6a6c2345784dc393c64f11b425d149c3ea16ebe4023c6ec8559214c