Quazim0t0 commited on
Commit
bed47ff
·
verified ·
1 Parent(s): ad5ff64

Verified neural units + tools (neural-ddr)

Browse files
DBI.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2d2b79f7d4faf8495deb2e175dad1dd752b16722bd3e64b2d013d80fe38a49b3
3
+ size 154354
DDR5_units.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0d12c4287f9af7967e266f34b872a7c7e6ae8d88ade42067486c5243f0942976
3
+ size 3077986
LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Dean Byrne (Quazim0t0)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
README.md ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: mit
3
+ library_name: pytorch
4
+ tags:
5
+ - verified-units
6
+ - neural-memory
7
+ - ddr5
8
+ - ecc
9
+ - retrocomputing
10
+ ---
11
+
12
+ # neural-ddr — verified DDR5 memory-logic units
13
+
14
+ Small neural nets that **emulate DDR5 memory logic**, each verified **bit-identical
15
+ to a golden reference over its entire finite input domain (N/N)** — the same
16
+ methodology as [neural-aarch64-units](https://huggingface.co/Quazim0t0/neural-aarch64-units).
17
+
18
+ A dev tool: present DDR5 behaviour (bus encoding, addressing, command decode,
19
+ write-CRC, **on-die ECC**) on a machine that only has DDR3/DDR4. The bytes live in
20
+ the host's real RAM; the neural nets emulate the *logic*.
21
+
22
+ > **Honest by design:** no capacity or speed is invented. This models the
23
+ > data-path logic; it does not turn DDR3 silicon into DDR5.
24
+
25
+ ## Units
26
+
27
+ | unit | role | verification |
28
+ |------|------|--------------|
29
+ | `DBI` | data-bus-inversion encode/decode (DDR4/5) | 256/256 · 512/512 |
30
+ | `ADDR_MAP` | address → bank-group/bank/row/column | 4096/4096 |
31
+ | `CMD_DECODE` | control lines → DDR command | 32/32 |
32
+ | `WR_CRC` | write-CRC bit-slice (rippled) | 512/512 |
33
+ | `ODECC` | DDR5 on-die ECC — corrects a single-bit flip | 256/256 · 3328/3328 |
34
+ | `DDR5Bridge` | composes the units over real host RAM | 2048/2048 faults corrected |
35
+
36
+ ## Use
37
+
38
+ ```bash
39
+ pip install torch
40
+ python step1_train_verify.py # DBI + memory bridge
41
+ python step2to6_train_verify.py # DDR5 units + ECC-correcting bridge
42
+ ```
43
+
44
+ Weights: `DBI.pt`, `DDR5_units.pt`.
45
+
46
+ **Create your own verified unit** (template: `neural_ddr/dbi.py`): write the exact
47
+ golden finite function → enumerate the domain (decompose big/linear ones into
48
+ bit/byte slices, see `crc.py`/`ecc.py`) → `common.train` → `common.verify` must be
49
+ bit-exact on 100% of inputs → compose (`ddr5_bridge.py`).
50
+
51
+ ## Citation
52
+
53
+ ```bibtex
54
+ @misc{byrne2026neuralddr,
55
+ title = {neural-ddr: Verified DDR5 Memory Logic Units},
56
+ author = {Byrne, Dean (Quazim0t0)},
57
+ year = {2026},
58
+ howpublished = {\url{https://huggingface.co/Quazim0t0/neural-ddr}}
59
+ }
60
+ ```
61
+
62
+ **Dean Byrne (Quazim0t0)** · 2026
neural_ddr/__init__.py ADDED
File without changes
neural_ddr/addr_map.py ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Step 2: ADDR_MAP -- physical address -> (rank/bank-group/bank/row/column).
3
+
4
+ DDR performance depends on which address bits pick the bank vs the row/column,
5
+ because interleaving consecutive addresses across banks/bank-groups exposes
6
+ parallelism. That mapping is a fixed bit-permutation -> a finite function we can
7
+ verify N/N, and it is exactly the kind of per-generation policy the bridge swaps.
8
+
9
+ Small but representative geometry (12-bit address, 4096 configs):
10
+ column = 4 bits, bank = 2, bank-group = 2, row = 4
11
+ laid out as an interleave (bank/bg taken from interior bits for parallelism).
12
+ """
13
+ from __future__ import annotations
14
+ import torch
15
+ from .common import bits_of, int_of, pm
16
+
17
+ ADDR_BITS = 12
18
+ # output bit index -> which input address bit feeds it (the addressing scheme)
19
+ PERM = [0, 3, 6, 9, 1, 4, 7, 10, 2, 5, 8, 11] # [col4][bank2][bg2][row4]
20
+ FIELDS = [("col", 4), ("bank", 2), ("bg", 2), ("row", 4)]
21
+
22
+
23
+ def golden_map_bits(addr: int) -> list[int]:
24
+ ib = bits_of(addr, ADDR_BITS)
25
+ return [ib[PERM[o]] for o in range(ADDR_BITS)]
26
+
27
+
28
+ def decode_fields(outbits) -> dict:
29
+ res, i = {}, 0
30
+ for name, w in FIELDS:
31
+ res[name] = int_of(outbits[i:i + w]); i += w
32
+ return res
33
+
34
+
35
+ def domain():
36
+ X = torch.stack([pm(bits_of(a, ADDR_BITS)) for a in range(2 ** ADDR_BITS)])
37
+ Y = torch.tensor([[float(b) for b in golden_map_bits(a)] for a in range(2 ** ADDR_BITS)])
38
+ return X, Y
39
+
40
+
41
+ def run(net, addr: int) -> dict:
42
+ y = (net(pm(bits_of(addr, ADDR_BITS)).unsqueeze(0))[0] > 0).int().tolist()
43
+ return decode_fields(y)
neural_ddr/bridge.py ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ The memory bridge: real host RAM as the storage array, DDR bus LOGIC emulated by
3
+ verified neural units.
4
+
5
+ This is the honest core of the "run it on your old PC" idea. The bytes live in a
6
+ buffer in the machine's actual DDR3/DDR4. Every access is routed through the
7
+ neural DDR units for a CHOSEN generation, so the bridge presents that
8
+ generation's *behavior* over ordinary host memory:
9
+
10
+ * DDR3 -> no DBI: bytes drive the bus as-is.
11
+ * DDR4/5 -> DBI: the verified neural DBI unit encodes each byte onto the
12
+ "bus" (>4 zeros -> invert + flag), so <=4 DQ lines are ever
13
+ LOW; reads decode it back, bit-exact.
14
+
15
+ No capacity or speed is created: this models the data-path logic, it does not
16
+ turn DDR3 into DDR5 silicon.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ from .dbi import NeuralDBIEncode, NeuralDBIDecode
22
+
23
+
24
+ class MemoryBridge:
25
+ def __init__(self, size: int, enc: NeuralDBIEncode, dec: NeuralDBIDecode,
26
+ generation: str = "DDR5"):
27
+ self.size = size
28
+ self.enc = enc
29
+ self.dec = dec
30
+ self.generation = generation
31
+ self.dbi = generation in ("DDR4", "DDR5") # DBI introduced in DDR4
32
+ # storage in real host RAM: the (possibly DBI-encoded) data byte + flag.
33
+ self._data = bytearray(size)
34
+ self._flag = bytearray(size)
35
+ # bus statistics
36
+ self.dq_low_total = 0
37
+ self.transfers = 0
38
+
39
+ def write(self, addr: int, value: int) -> None:
40
+ value &= 0xFF
41
+ if self.dbi:
42
+ enc, flag = self.enc.encode(value) # verified neural unit
43
+ else:
44
+ enc, flag = value, 0
45
+ self._data[addr] = enc
46
+ self._flag[addr] = flag
47
+ self.dq_low_total += 8 - bin(enc).count("1") # DQ lines driven LOW
48
+ self.transfers += 1
49
+
50
+ def read(self, addr: int) -> int:
51
+ enc, flag = self._data[addr], self._flag[addr]
52
+ if self.dbi:
53
+ return self.dec.decode(enc, flag) # verified neural unit
54
+ return enc
55
+
56
+ def avg_dq_low(self) -> float:
57
+ return self.dq_low_total / max(1, self.transfers)
neural_ddr/cmd_decode.py ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Step 3: CMD_DECODE -- control lines -> DDR command.
3
+
4
+ A DDR command is a truth table over the control pins (CS_n, ACT_n, and the
5
+ RAS_n/CAS_n/WE_n multiplexed lines). It is a finite function -> N/N verifiable.
6
+ (DDR5 uses a multi-cycle CA encoding; the table here is a compact, self-consistent
7
+ DDR4-style decode that the same unit shape can be retrained to for DDR5.)
8
+ """
9
+ from __future__ import annotations
10
+ import torch
11
+ from .common import bits_of, int_of, pm
12
+
13
+ IN, OUT = 5, 4
14
+ CMDS = {"DESELECT": 0, "ACTIVATE": 1, "NOP": 2, "READ": 3, "WRITE": 4,
15
+ "PRECHARGE": 5, "REFRESH": 6, "MRS": 7, "ZQ": 8}
16
+ NAME = {v: k for k, v in CMDS.items()}
17
+
18
+
19
+ def golden_cmd(v: int) -> int:
20
+ cs, act, ras, cas, we = bits_of(v, 5) # bit0=CS_n .. bit4=WE_n
21
+ if cs == 1:
22
+ return CMDS["DESELECT"]
23
+ if act == 0:
24
+ return CMDS["ACTIVATE"]
25
+ table = {(1, 1, 1): "NOP", (1, 1, 0): "NOP", (1, 0, 1): "READ", (1, 0, 0): "WRITE",
26
+ (0, 1, 1): "PRECHARGE", (0, 1, 0): "REFRESH", (0, 0, 1): "MRS", (0, 0, 0): "ZQ"}
27
+ return CMDS[table[(ras, cas, we)]]
28
+
29
+
30
+ def domain():
31
+ X = torch.stack([pm(bits_of(v, IN)) for v in range(2 ** IN)])
32
+ Y = torch.tensor([[float(b) for b in bits_of(golden_cmd(v), OUT)] for v in range(2 ** IN)])
33
+ return X, Y
34
+
35
+
36
+ def run(net, v: int) -> int:
37
+ return int_of((net(pm(bits_of(v, IN)).unsqueeze(0))[0] > 0).int().tolist())
neural_ddr/common.py ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Shared helpers: bit<->int, MLP factory, exhaustive verify, trainer."""
2
+ from __future__ import annotations
3
+ import torch
4
+ import torch.nn as nn
5
+
6
+ DEV = "cuda" if torch.cuda.is_available() else "cpu"
7
+
8
+
9
+ def bits_of(v: int, n: int) -> list[int]:
10
+ return [(v >> k) & 1 for k in range(n)] # LSB-first
11
+
12
+
13
+ def int_of(bits) -> int:
14
+ return sum((1 << k) for k, b in enumerate(bits) if b > 0)
15
+
16
+
17
+ def pm(bits) -> torch.Tensor:
18
+ return torch.tensor([1.0 if b else -1.0 for b in bits], dtype=torch.float32)
19
+
20
+
21
+ def mlp(inp: int, out: int, h: int = 256, layers: int = 2) -> nn.Sequential:
22
+ mods = [nn.Linear(inp, h), nn.GELU()]
23
+ for _ in range(layers - 1):
24
+ mods += [nn.Linear(h, h), nn.GELU()]
25
+ mods += [nn.Linear(h, out)]
26
+ return nn.Sequential(*mods)
27
+
28
+
29
+ @torch.no_grad()
30
+ def verify(net, X, Ybits) -> tuple[int, int]:
31
+ net = net.to("cpu")
32
+ pred = (net(X) > 0).int()
33
+ ok = (pred == Ybits.int()).all(dim=1).sum().item()
34
+ return ok, X.shape[0]
35
+
36
+
37
+ def train(net, X, Ybits, steps=8000, lr=2e-3, tag="", report=2000):
38
+ net = net.to(DEV)
39
+ Xd, Yd = X.to(DEV), Ybits.to(DEV)
40
+ opt = torch.optim.Adam(net.parameters(), lr=lr)
41
+ sch = torch.optim.lr_scheduler.CosineAnnealingLR(opt, T_max=steps)
42
+ lossfn = nn.BCEWithLogitsLoss()
43
+ for e in range(steps):
44
+ opt.zero_grad()
45
+ loss = lossfn(net(Xd), Yd)
46
+ loss.backward(); opt.step(); sch.step()
47
+ if e % report == 0 or e == steps - 1:
48
+ ok, tot = verify(net, X, Ybits); net.to(DEV)
49
+ print(f" [{tag}] epoch {e:5d} loss {loss.item():.2e} verified {ok}/{tot}")
50
+ if ok == tot:
51
+ print(f" [{tag}] -> N/N"); break
52
+ return net.to("cpu")
neural_ddr/crc.py ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Step 4: WR_CRC -- DDR4/5 write-CRC integrity.
3
+
4
+ CRC is linear over GF(2), so a full-burst CRC over 64+ bits can never be
5
+ enumerated -- but a single-bit slice can. We verify the CRC BIT-SLICE
6
+ exhaustively (state 8b + data bit -> new state, 512 configs) and ripple it,
7
+ exactly the byte-slice/ripple approach of the neural-aarch64 datapath.
8
+
9
+ Polynomial is configurable; default 0x07. Swap it to match the target JEDEC spec.
10
+ """
11
+ from __future__ import annotations
12
+ import torch
13
+ from .common import bits_of, int_of, pm
14
+
15
+ POLY = 0x07
16
+ IN, OUT = 9, 8
17
+
18
+
19
+ def crc_bit(state: int, databit: int) -> int:
20
+ fb = ((state >> 7) & 1) ^ (databit & 1)
21
+ ns = (state << 1) & 0xFF
22
+ if fb:
23
+ ns ^= POLY
24
+ return ns & 0xFF
25
+
26
+
27
+ def domain():
28
+ xs, ys = [], []
29
+ for s in range(256):
30
+ for d in (0, 1):
31
+ v = (d << 8) | s # bits0-7=state, bit8=databit
32
+ xs.append(pm(bits_of(v, 9)))
33
+ ys.append([float(b) for b in bits_of(crc_bit(s, d), 8)])
34
+ return torch.stack(xs), torch.tensor(ys)
35
+
36
+
37
+ def run_bit(net, state: int, databit: int) -> int:
38
+ v = (databit << 8) | state
39
+ return int_of((net(pm(bits_of(v, 9)).unsqueeze(0))[0] > 0).int().tolist())
40
+
41
+
42
+ def crc_byte(net, state: int, byte: int) -> int:
43
+ for k in range(7, -1, -1): # MSB-first
44
+ state = run_bit(net, state, (byte >> k) & 1)
45
+ return state
46
+
47
+
48
+ def crc_burst(net, data_bytes, init=0) -> int:
49
+ s = init
50
+ for b in data_bytes:
51
+ s = crc_byte(net, s, b)
52
+ return s
53
+
54
+
55
+ def golden_burst(data_bytes, init=0) -> int:
56
+ s = init
57
+ for b in data_bytes:
58
+ for k in range(7, -1, -1):
59
+ s = crc_bit(s, (b >> k) & 1)
60
+ return s
neural_ddr/dbi.py ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Step 1 of neural-DDR: the DBI (Data Bus Inversion) unit, verified N/N.
3
+
4
+ Same methodology as the neural-aarch64 datapath units: DBI is a FIXED finite
5
+ function, so a small MLP can be trained until it is bit-identical to a golden
6
+ reference over its ENTIRE input domain (exhaustive N/N verification).
7
+
8
+ DBI is a real DDR4/DDR5 feature (absent in DDR3) -- so it is exactly the kind of
9
+ per-generation logic the bridge swaps in/out. On the data bus, DDR reads back
10
+ data over DQ lines that are terminated to a pull-up; a line driven LOW burns
11
+ termination power. DBI_dc reduces that: if a byte has more than four 0-bits,
12
+ invert all eight and flag it, so at most four lines are ever LOW.
13
+
14
+ encode(byte) -> 9 bits = [8 encoded data][1 DBI flag]
15
+ inverted = (number_of_zero_bits(byte) > 4)
16
+ data_out = ~byte & 0xFF if inverted else byte # <=4 zeros afterwards
17
+ flag = 1 if inverted else 0
18
+ decode(data_out, flag) -> byte
19
+ byte = ~data_out & 0xFF if flag else data_out
20
+
21
+ Neither storage nor capacity is invented here: this unit only emulates the
22
+ bus-encoding LOGIC. The bytes themselves live in the host's real RAM (bridge).
23
+ """
24
+
25
+ from __future__ import annotations
26
+
27
+ import torch
28
+ import torch.nn as nn
29
+
30
+
31
+ # ---------- golden reference (the finite function we must match) ----------
32
+ def golden_encode(byte: int) -> tuple[int, int]:
33
+ zeros = 8 - bin(byte & 0xFF).count("1")
34
+ if zeros > 4:
35
+ return (~byte) & 0xFF, 1
36
+ return byte & 0xFF, 0
37
+
38
+
39
+ def golden_decode(data_out: int, flag: int) -> int:
40
+ return (~data_out) & 0xFF if flag else data_out & 0xFF
41
+
42
+
43
+ # ---------- bit <-> tensor helpers (LSB-first, +/-1 encoding) ----------
44
+ def byte_bits(v: int, n: int = 8) -> torch.Tensor:
45
+ return torch.tensor([1.0 if (v >> k) & 1 else -1.0 for k in range(n)], dtype=torch.float32)
46
+
47
+
48
+ def bits_to_int(bits) -> int:
49
+ return sum((1 << k) for k, b in enumerate(bits) if b > 0)
50
+
51
+
52
+ # ---------- the neural units ----------
53
+ class NeuralDBIEncode(nn.Module):
54
+ """8 data bits -> 9 bits (8 encoded data + 1 DBI flag)."""
55
+
56
+ def __init__(self):
57
+ super().__init__()
58
+ self.net = nn.Sequential(
59
+ nn.Linear(8, 128), nn.GELU(),
60
+ nn.Linear(128, 128), nn.GELU(),
61
+ nn.Linear(128, 9),
62
+ )
63
+
64
+ def forward(self, x):
65
+ return self.net(x)
66
+
67
+ @torch.no_grad()
68
+ def encode(self, byte: int) -> tuple[int, int]:
69
+ y = (self(byte_bits(byte).unsqueeze(0))[0] > 0).int().tolist()
70
+ return bits_to_int(y[:8]), int(y[8])
71
+
72
+
73
+ class NeuralDBIDecode(nn.Module):
74
+ """9 bits (8 data + flag) -> 8 recovered data bits."""
75
+
76
+ def __init__(self):
77
+ super().__init__()
78
+ self.net = nn.Sequential(
79
+ nn.Linear(9, 128), nn.GELU(),
80
+ nn.Linear(128, 128), nn.GELU(),
81
+ nn.Linear(128, 8),
82
+ )
83
+
84
+ def forward(self, x):
85
+ return self.net(x)
86
+
87
+ @torch.no_grad()
88
+ def decode(self, data_out: int, flag: int) -> int:
89
+ inp = torch.cat([byte_bits(data_out), torch.tensor([1.0 if flag else -1.0])]).unsqueeze(0)
90
+ y = (self(inp)[0] > 0).int().tolist()
91
+ return bits_to_int(y)
92
+
93
+
94
+ # ---------- exhaustive datasets + verification ----------
95
+ def encode_domain():
96
+ X = torch.stack([byte_bits(b) for b in range(256)])
97
+ Y = torch.stack([
98
+ torch.tensor([1.0 if (enc >> k) & 1 else -1.0 for k in range(8)] + [1.0 if fl else -1.0])
99
+ for enc, fl in (golden_encode(b) for b in range(256))
100
+ ])
101
+ return X, Y
102
+
103
+
104
+ def decode_domain():
105
+ X, Y = [], []
106
+ for data_out in range(256):
107
+ for flag in (0, 1):
108
+ X.append(torch.cat([byte_bits(data_out), torch.tensor([1.0 if flag else -1.0])]))
109
+ b = golden_decode(data_out, flag)
110
+ Y.append(torch.tensor([1.0 if (b >> k) & 1 else -1.0 for k in range(8)]))
111
+ return torch.stack(X), torch.stack(Y)
112
+
113
+
114
+ @torch.no_grad()
115
+ def verify_encode(unit: NeuralDBIEncode) -> tuple[int, int]:
116
+ ok = 0
117
+ for b in range(256):
118
+ g_enc, g_fl = golden_encode(b)
119
+ n_enc, n_fl = unit.encode(b)
120
+ ok += (n_enc == g_enc and n_fl == g_fl)
121
+ return ok, 256
122
+
123
+
124
+ @torch.no_grad()
125
+ def verify_decode(unit: NeuralDBIDecode) -> tuple[int, int]:
126
+ ok = 0
127
+ for data_out in range(256):
128
+ for flag in (0, 1):
129
+ ok += (unit.decode(data_out, flag) == golden_decode(data_out, flag))
130
+ return ok, 512
neural_ddr/ddr5_bridge.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Step 6: DDR5Bridge -- compose the verified units into a DDR5 logical interface
3
+ backed by the host's real RAM.
4
+
5
+ Write: byte -> neural ECC parity -> 12-bit codeword stored in host RAM
6
+ Read: 12-bit codeword -> neural ECC decode (corrects a single-bit flip) -> byte
7
+
8
+ `inject_fault` flips a bit in stored memory to emulate a DRAM disturbance; the
9
+ neural ECC unit corrects it on read. That is the DDR5 on-die-ECC guarantee,
10
+ presented on a machine that need not have DDR5 at all.
11
+ """
12
+ from __future__ import annotations
13
+ from . import ecc
14
+
15
+
16
+ class DDR5Bridge:
17
+ def __init__(self, size: int, enc_net, dec_net):
18
+ self.size = size
19
+ self.enc = enc_net
20
+ self.dec = dec_net
21
+ self.mem = [0] * size # host RAM: 12-bit ECC codewords
22
+
23
+ def write(self, addr: int, byte: int) -> None:
24
+ byte &= 0xFF
25
+ par = ecc.enc_run(self.enc, byte) # verified neural parity
26
+ d = [(byte >> k) & 1 for k in range(8)]
27
+ cw = [0] * 12
28
+ for i, pos in enumerate(ecc.DATA_POS):
29
+ cw[pos - 1] = d[i]
30
+ for i, j in enumerate(ecc.PAR_POS):
31
+ cw[j - 1] = par[i]
32
+ self.mem[addr] = sum((1 << k) for k, b in enumerate(cw) if b)
33
+
34
+ def read(self, addr: int) -> int:
35
+ return ecc.dec_run(self.dec, self.mem[addr]) # verified neural correct+decode
36
+
37
+ def inject_fault(self, addr: int, bitpos: int) -> None:
38
+ self.mem[addr] ^= (1 << bitpos) # emulate a single-bit disturbance
neural_ddr/ecc.py ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Step 5: ODECC -- DDR5 on-die ECC (the marquee DDR5 feature).
3
+
4
+ DDR5 adds on-die single-error-correction. We implement a concrete SEC Hamming
5
+ code -- Hamming(12,8): 8 data bits protected by 4 parity bits, correcting any
6
+ single-bit flip in the 12-bit codeword. Real DDR5 protects wider words; the
7
+ logic is identical, just more bits.
8
+
9
+ Two verified units:
10
+ ENC : 8 data bits -> 4 parity bits (domain 256, N/N)
11
+ DEC : 12-bit codeword -> 8 corrected data bits (all no-error + every
12
+ single-bit flip, 256*13 = 3328 inputs, N/N)
13
+
14
+ The DEC unit genuinely CORRECTS a flipped bit -- that is what lets the bridge
15
+ present ECC-protected memory over ordinary host RAM.
16
+ """
17
+ from __future__ import annotations
18
+ import torch
19
+ from .common import bits_of, int_of, pm
20
+
21
+ DATA_POS = [3, 5, 6, 7, 9, 10, 11, 12] # 1-indexed Hamming positions
22
+ PAR_POS = [1, 2, 4, 8]
23
+ ENC_IN, ENC_OUT = 8, 4
24
+ DEC_IN, DEC_OUT = 12, 8
25
+
26
+
27
+ def encode(data8: int) -> list[int]:
28
+ d = bits_of(data8, 8)
29
+ cw = {p: 0 for p in range(1, 13)}
30
+ for i, pos in enumerate(DATA_POS):
31
+ cw[pos] = d[i]
32
+ par = []
33
+ for j in PAR_POS:
34
+ p = 0
35
+ for pos in range(1, 13):
36
+ if pos in PAR_POS:
37
+ continue
38
+ if pos & j:
39
+ p ^= cw[pos]
40
+ par.append(p)
41
+ return par # for positions 1,2,4,8
42
+
43
+
44
+ def codeword_int(data8: int) -> int:
45
+ d = bits_of(data8, 8)
46
+ par = encode(data8)
47
+ cw = [0] * 12 # index k = position k+1
48
+ for i, pos in enumerate(DATA_POS):
49
+ cw[pos - 1] = d[i]
50
+ for i, j in enumerate(PAR_POS):
51
+ cw[j - 1] = par[i]
52
+ return int_of(cw)
53
+
54
+
55
+ def decode(cw_int: int) -> int:
56
+ cw = bits_of(cw_int, 12)
57
+ syn = 0
58
+ for j in PAR_POS:
59
+ s = 0
60
+ for pos in range(1, 13):
61
+ if pos & j:
62
+ s ^= cw[pos - 1]
63
+ if s:
64
+ syn |= j
65
+ if 1 <= syn <= 12:
66
+ cw[syn - 1] ^= 1 # correct the flipped bit
67
+ return int_of([cw[pos - 1] for pos in DATA_POS])
68
+
69
+
70
+ def enc_domain():
71
+ X = torch.stack([pm(bits_of(d, 8)) for d in range(256)])
72
+ Y = torch.tensor([[float(b) for b in encode(d)] for d in range(256)])
73
+ return X, Y
74
+
75
+
76
+ def dec_domain():
77
+ xs, ys = [], []
78
+ for d in range(256):
79
+ base = codeword_int(d)
80
+ for e in range(13): # 0 = no error, 1..12 flip pos e
81
+ v = base if e == 0 else base ^ (1 << (e - 1))
82
+ xs.append(pm(bits_of(v, 12)))
83
+ ys.append([float(b) for b in bits_of(d, 8)])
84
+ return torch.stack(xs), torch.tensor(ys)
85
+
86
+
87
+ def enc_run(net, d: int) -> list[int]:
88
+ return (net(pm(bits_of(d, 8)).unsqueeze(0))[0] > 0).int().tolist()
89
+
90
+
91
+ def dec_run(net, cw_int: int) -> int:
92
+ return int_of((net(pm(bits_of(cw_int, 12)).unsqueeze(0))[0] > 0).int().tolist())
requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ torch
step1_train_verify.py ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Step 1 for neural-DDR: train + exhaustively verify the DBI units, then prove the
3
+ memory bridge round-trips real data through them.
4
+
5
+ (a) encode N/N -> DBI encode matches golden on all 256 bytes
6
+ (b) decode N/N -> DBI decode matches golden on all 512 (data,flag) inputs
7
+ (c) round-trip -> bridge stores every byte at many addresses via the neural
8
+ units and reads it back bit-exact (data integrity)
9
+ (d) DBI works -> DDR4/5 bridge never drives >4 DQ lines LOW; DDR3 does
10
+ (e) generation -> same host RAM, DDR3 vs DDR5 behavior, both correct
11
+ """
12
+
13
+ import torch
14
+ from neural_ddr.dbi import (
15
+ NeuralDBIEncode, NeuralDBIDecode, encode_domain, decode_domain,
16
+ verify_encode, verify_decode,
17
+ )
18
+ from neural_ddr.bridge import MemoryBridge
19
+
20
+ torch.manual_seed(0)
21
+ dev = "cuda" if torch.cuda.is_available() else "cpu"
22
+
23
+
24
+ def train(unit, X, Y, steps=12000, lr=2e-3, tag="", verify=None):
25
+ X, Y = X.to(dev), Y.to(dev)
26
+ unit = unit.to(dev)
27
+ opt = torch.optim.Adam(unit.parameters(), lr=lr)
28
+ sched = torch.optim.lr_scheduler.CosineAnnealingLR(opt, T_max=steps)
29
+ for epoch in range(steps):
30
+ opt.zero_grad()
31
+ loss = (torch.tanh(unit(X)) - torch.tanh(Y * 4)).pow(2).mean()
32
+ loss.backward(); opt.step(); sched.step()
33
+ if epoch % 2000 == 0 or epoch == steps - 1:
34
+ n_ok, n_tot = verify(unit.cpu()); unit.to(dev)
35
+ print(f" [{tag}] epoch {epoch:5d} loss {loss.item():.2e} verified {n_ok}/{n_tot}")
36
+ if n_ok == n_tot:
37
+ print(f" [{tag}] -> full N/N reached")
38
+ break
39
+ return unit.cpu()
40
+
41
+
42
+ if __name__ == "__main__":
43
+ print("=" * 60)
44
+ print("STEP 1 -- neural DBI units + memory bridge")
45
+ print("=" * 60)
46
+
47
+ Xe, Ye = encode_domain()
48
+ Xd, Yd = decode_domain()
49
+ print("training DBI encode (8 -> 9):")
50
+ enc = train(NeuralDBIEncode(), Xe, Ye, tag="enc", verify=verify_encode)
51
+ print("training DBI decode (9 -> 8):")
52
+ dec = train(NeuralDBIDecode(), Xd, Yd, tag="dec", verify=verify_decode)
53
+
54
+ ne, te = verify_encode(enc)
55
+ nd, td = verify_decode(dec)
56
+ print("-" * 60)
57
+ print(f"(a) DBI encode verified {ne}/{te} -> {'PASS' if ne==te else 'FAIL'}")
58
+ print(f"(b) DBI decode verified {nd}/{td} -> {'PASS' if nd==td else 'FAIL'}")
59
+
60
+ # (c) round-trip integrity through the bridge (DDR5 mode)
61
+ N = 4096
62
+ br5 = MemoryBridge(N, enc, dec, generation="DDR5")
63
+ bad = 0
64
+ for a in range(N):
65
+ v = (a * 37 + 11) & 0xFF
66
+ br5.write(a, v)
67
+ for a in range(N):
68
+ v = (a * 37 + 11) & 0xFF
69
+ bad += (br5.read(a) != v)
70
+ print(f"(c) bridge round-trip {N-bad}/{N} bytes exact -> {'PASS' if bad==0 else 'FAIL'}")
71
+
72
+ # (d) DBI bus effect: DDR5 caps DQ-low at 4/byte; DDR3 does not
73
+ br3 = MemoryBridge(256, enc, dec, generation="DDR3")
74
+ worst5 = 0
75
+ for v in range(256):
76
+ br5b = MemoryBridge(1, enc, dec, generation="DDR5"); br5b.write(0, v)
77
+ worst5 = max(worst5, int(br5b.dq_low_total))
78
+ br3.write(v % 256, v)
79
+ print(f"(d) DBI bus effect: max DQ-low/byte DDR5={worst5} (<=4?), "
80
+ f"avg DQ-low DDR3={br3.avg_dq_low():.2f} -> {'PASS' if worst5<=4 else 'FAIL'}")
81
+
82
+ # (e) generation swap over the SAME data, both correct
83
+ okg = True
84
+ for gen in ("DDR3", "DDR4", "DDR5"):
85
+ b = MemoryBridge(256, enc, dec, generation=gen)
86
+ for v in range(256):
87
+ b.write(v, v)
88
+ okg = okg and all(b.read(v) == v for v in range(256))
89
+ print(f"(e) generation swap DDR3/4/5 all round-trip -> {'PASS' if okg else 'FAIL'}")
90
+
91
+ if ne == te and nd == td:
92
+ torch.save({"encode": enc.state_dict(), "decode": dec.state_dict(),
93
+ "meta": {"unit": "DBI", "encode_verified": f"{ne}/{te}",
94
+ "decode_verified": f"{nd}/{td}"}}, "DBI.pt")
95
+ print(" saved verified unit -> DBI.pt")
step2to6_train_verify.py ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Steps 2-6: train + exhaustively verify ADDR_MAP, CMD_DECODE, WR_CRC, ODECC,
3
+ then compose them into a DDR5 bridge that corrects real bit-flips over host RAM.
4
+ """
5
+ import random
6
+ import torch
7
+ from neural_ddr.common import mlp, train, verify
8
+ from neural_ddr import addr_map, cmd_decode, crc, ecc
9
+ from neural_ddr.ddr5_bridge import DDR5Bridge
10
+
11
+ torch.manual_seed(0)
12
+ random.seed(0)
13
+ R = {}
14
+
15
+ print("=" * 62)
16
+ print("STEPS 2-6 -- neural DDR5 units + bridge")
17
+ print("=" * 62)
18
+
19
+ # ---- Step 2: ADDR_MAP ----
20
+ print("Step 2 ADDR_MAP (12 -> 12):")
21
+ Xa, Ya = addr_map.domain()
22
+ net_addr = train(mlp(12, 12, 256, 2), Xa, Ya, steps=8000, tag="addr")
23
+ R["ADDR_MAP"] = verify(net_addr, Xa, Ya)
24
+
25
+ # ---- Step 3: CMD_DECODE ----
26
+ print("Step 3 CMD_DECODE (5 -> 4):")
27
+ Xc, Yc = cmd_decode.domain()
28
+ net_cmd = train(mlp(5, 4, 64, 2), Xc, Yc, steps=6000, tag="cmd")
29
+ R["CMD_DECODE"] = verify(net_cmd, Xc, Yc)
30
+
31
+ # ---- Step 4: WR_CRC (bit-slice) ----
32
+ print("Step 4 WR_CRC bit-slice (9 -> 8):")
33
+ Xr, Yr = crc.domain()
34
+ net_crc = train(mlp(9, 8, 128, 2), Xr, Yr, steps=6000, tag="crc")
35
+ R["WR_CRC"] = verify(net_crc, Xr, Yr)
36
+
37
+ # ---- Step 5: ODECC (encode + decode/correct) ----
38
+ print("Step 5 ODECC encode (8 -> 4):")
39
+ Xe, Ye = ecc.enc_domain()
40
+ net_enc = train(mlp(8, 4, 256, 3), Xe, Ye, steps=15000, tag="ecc-enc")
41
+ R["ODECC_ENC"] = verify(net_enc, Xe, Ye)
42
+ print("Step 5 ODECC decode/correct (12 -> 8):")
43
+ Xd, Yd = ecc.dec_domain()
44
+ net_dec = train(mlp(12, 8, 512, 3), Xd, Yd, steps=20000, tag="ecc-dec")
45
+ R["ODECC_DEC"] = verify(net_dec, Xd, Yd)
46
+
47
+ print("-" * 62)
48
+ for name, (ok, tot) in R.items():
49
+ print(f" {name:12s} verified {ok}/{tot} -> {'PASS' if ok == tot else 'FAIL'}")
50
+
51
+ # ---- behavioural checks ----
52
+ print("-" * 62)
53
+ # CMD_DECODE full table
54
+ cmd_ok = all(cmd_decode.run(net_cmd, v) == cmd_decode.golden_cmd(v) for v in range(32))
55
+ print(f"CMD_DECODE truth table (32/32): {'PASS' if cmd_ok else 'FAIL'} "
56
+ f"e.g. 0b00101 -> {cmd_decode.NAME[cmd_decode.run(net_cmd, 0b00101)]}")
57
+
58
+ # ADDR_MAP sample
59
+ f = addr_map.run(net_addr, 0xABC)
60
+ print(f"ADDR_MAP 0xABC -> col={f['col']} bank={f['bank']} bg={f['bg']} row={f['row']}")
61
+
62
+ # CRC burst vs golden
63
+ bad = 0
64
+ for _ in range(200):
65
+ msg = [random.randint(0, 255) for _ in range(8)]
66
+ bad += (crc.crc_burst(net_crc, msg) != crc.golden_burst(msg))
67
+ print(f"WR_CRC 8-byte bursts (200): {'PASS' if bad == 0 else f'FAIL({bad})'} "
68
+ f"(neural ripple == golden CRC)")
69
+
70
+ # ---- Step 6: DDR5 bridge with real bit-flip correction ----
71
+ N = 2048
72
+ br = DDR5Bridge(N, net_enc, net_dec)
73
+ for a in range(N):
74
+ br.write(a, (a * 89 + 7) & 0xFF)
75
+ # inject one random single-bit fault per location, then read back
76
+ corrected = 0
77
+ for a in range(N):
78
+ br.inject_fault(a, random.randint(0, 11))
79
+ if br.read(a) == ((a * 89 + 7) & 0xFF):
80
+ corrected += 1
81
+ print(f"DDR5Bridge SEC: {corrected}/{N} bytes correct after a random bit-flip each "
82
+ f"-> {'PASS' if corrected == N else 'FAIL'}")
83
+
84
+ allpass = all(ok == tot for ok, tot in R.values()) and cmd_ok and bad == 0 and corrected == N
85
+ print("=" * 62)
86
+ print(f"OVERALL: {'ALL PASS' if allpass else 'some checks failed'}")
87
+
88
+ if all(ok == tot for ok, tot in R.values()):
89
+ torch.save({
90
+ "addr_map": net_addr.state_dict(), "cmd_decode": net_cmd.state_dict(),
91
+ "wr_crc": net_crc.state_dict(), "odecc_enc": net_enc.state_dict(),
92
+ "odecc_dec": net_dec.state_dict(),
93
+ "meta": {k: f"{v[0]}/{v[1]}" for k, v in R.items()},
94
+ }, "DDR5_units.pt")
95
+ print("saved verified units -> DDR5_units.pt")