EEPROM layout
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user