1
0

EEPROM layout

This commit is contained in:
Aiden
2026-05-26 11:35:21 +10:00
parent 1ad03d5692
commit edb8ed78f3
19 changed files with 169583 additions and 8 deletions

View File

@@ -4,6 +4,7 @@ from dataclasses import dataclass, field
X24164_SIZE = 2048
X24164_LOGICAL_SIZE = 4096
X24164_FACTORY_DEFAULT_BASE = 0xC964
X24164_FACTORY_DEFAULT_BYTES = 0x0100
X24164_LOGICAL_PAGE_SIZE = 0x0100
@@ -72,12 +73,30 @@ class X24164TraceEvent:
return " ".join(parts)
@dataclass(frozen=True)
class X24164WriteEvent:
logical_address: int
device: str
device_offset: int
old_value: int
new_value: int
source: str
def line(self) -> str:
return (
f"addr={self.logical_address & 0x0FFF:03X} device={self.device} "
f"offset={self.device_offset & (X24164_SIZE - 1):03X} "
f"{self.old_value & 0xFF:02X}->{self.new_value & 0xFF:02X} source={self.source}"
)
class X24164Bus:
"""Bit-level two-wire bus model for X24164 EEPROMs."""
def __init__(self, devices: list[X24164Device] | None = None) -> None:
self.devices = devices if devices is not None else default_x24164_devices()
self.trace_events: list[X24164TraceEvent] = []
self.write_events: list[X24164WriteEvent] = []
self.active = False
self.phase = "idle"
self.selected: X24164Device | None = None
@@ -197,6 +216,18 @@ class X24164Bus:
)
return True, value
def read_linear_byte(self, address: int) -> tuple[bool, int]:
device = self._device_for_linear_address(address)
if device is None:
self.trace_events.append(
X24164TraceEvent("x24164_linear_read_miss", address=address & 0x0FFF, message="no_mapped_device")
)
return False, 0xFF
offset = address & (X24164_SIZE - 1)
value = device.read(offset)
self.trace_events.append(X24164TraceEvent("x24164_linear_read_byte", device.name, value=value, address=offset))
return True, value
def write_linear_word(self, address: int, value: int) -> bool:
device = self._device_for_linear_address(address)
if device is None:
@@ -205,8 +236,8 @@ class X24164Bus:
)
return False
offset = address & (X24164_SIZE - 1)
device.write(offset, (value >> 8) & 0xFF)
device.write((offset + 1) & (X24164_SIZE - 1), value & 0xFF)
self._write_device_byte(device, offset, (value >> 8) & 0xFF, source="linear_word")
self._write_device_byte(device, (offset + 1) & (X24164_SIZE - 1), value & 0xFF, source="linear_word")
self.trace_events.append(
X24164TraceEvent(
"x24164_linear_write_word",
@@ -218,20 +249,61 @@ class X24164Bus:
)
return True
def write_linear_byte(self, address: int, value: int, *, source: str = "linear_byte") -> bool:
device = self._device_for_linear_address(address)
if device is None:
self.trace_events.append(
X24164TraceEvent("x24164_linear_write_miss", address=address & 0x0FFF, message="no_mapped_device")
)
return False
offset = address & (X24164_SIZE - 1)
self._write_device_byte(device, offset, value, source=source)
self.trace_events.append(X24164TraceEvent("x24164_linear_write_byte", device.name, value=value & 0xFF, address=offset))
return True
def dump_linear(self) -> bytes:
data = bytearray()
for address in range(X24164_LOGICAL_SIZE):
device = self._device_for_linear_address(address)
if device is None:
data.append(0xFF)
else:
data.append(device.read(address & (X24164_SIZE - 1)))
return bytes(data)
def load_linear(self, data: bytes | bytearray, *, fill: int = 0xFF) -> None:
if len(data) > X24164_LOGICAL_SIZE:
raise ValueError(f"EEPROM image is too large: {len(data)} > {X24164_LOGICAL_SIZE}")
padded = bytearray([fill & 0xFF] * X24164_LOGICAL_SIZE)
padded[: len(data)] = data
for address, value in enumerate(padded):
device = self._device_for_linear_address(address)
if device is not None:
device.write(address & (X24164_SIZE - 1), value)
self.clear_write_log()
def clear_write_log(self) -> None:
self.write_events.clear()
def seed_factory_defaults_from_rom(self, rom_bytes: bytes) -> None:
for offset, word in factory_default_words_from_rom(rom_bytes):
for page in range(X24164_LOGICAL_PAGE_COUNT):
self.write_linear_word((page * X24164_LOGICAL_PAGE_SIZE) + offset, word)
for page in range(X24164_LOGICAL_PAGE_COUNT):
for page in range(1, X24164_LOGICAL_PAGE_COUNT):
base = page * X24164_LOGICAL_PAGE_SIZE
for offset in range(0, 8, 2):
self.write_linear_word(base + offset, 0x2020)
self.clear_write_log()
def trace_lines(self, limit: int | None = None) -> list[str]:
events = self.trace_events if limit is None else self.trace_events[-limit:]
return [event.line() for event in events]
def write_log_lines(self, limit: int | None = None) -> list[str]:
events = self.write_events if limit is None else self.write_events[-limit:]
return [event.line() for event in events]
def _scl_rising(self, master_sda: bool, master_sda_output: bool) -> None:
if not self.active:
return
@@ -327,7 +399,7 @@ class X24164Bus:
self._ack_armed_on_current_clock = True
self.phase = "ignore"
return
self.selected.write(self.address, value)
self._write_device_byte(self.selected, self.address, value, source="bit_banged")
self.trace_events.append(
X24164TraceEvent("x24164_write_data", self.selected.name, value=value, address=self.address, ack=True)
)
@@ -365,6 +437,24 @@ class X24164Bus:
return device
return None
def _linear_base_for_device(self, device: X24164Device) -> int:
return 0x0800 if device.control_base == 0xE0 else 0x0000
def _write_device_byte(self, device: X24164Device, offset: int, value: int, *, source: str) -> None:
offset &= X24164_SIZE - 1
old_value = device.read(offset)
device.write(offset, value)
self.write_events.append(
X24164WriteEvent(
logical_address=(self._linear_base_for_device(device) + offset) & 0x0FFF,
device=device.name,
device_offset=offset,
old_value=old_value,
new_value=value & 0xFF,
source=source,
)
)
def _selected_name(self) -> str | None:
return self.selected.name if self.selected is not None else None