I2C 4 KB Memory Target¶
The PxI2C4KbMemoryTarget class emulates a byte-addressable memory device on an I2C (or I3C) bus. A controller accesses the device using a 7-bit I2C address plus a 16-bit memory offset (sub-address, MSB first), then reads or writes a contiguous block of bytes.
This target is useful when you want to test controller drivers, EEPROM-style peripherals, or larger linear memory arrays that need more than 8-bit addressing.
Memory model¶
- 4 KB of memory (
0x0000–0x0FFF). - Static I2C address configured at creation time (for example
0x50). - 16-bit sub-address sent as the first two data bytes of a write transaction (MSB first), or written before a read-with-sub-address sequence.
On the bus, a typical write looks like:
A typical read-with-sub-address looks like:
The exerciser updates memory directly during bus transfers. Python can inspect or preload memory through read_mem / write_mem without going through the I2C controller.
Quick start¶
Attach a target to an I2C bus and read/write memory from Python:
from aqpxlib import AqProtocolExerciser
from aqpxlib.i2c import PxI2CBus, PxI2C4KbMemoryTarget
with AqProtocolExerciser.connect(port=60600) as px:
i2c_bus = PxI2CBus(px, scl=0, sda=1)
target = PxI2C4KbMemoryTarget(px, address=0x50)
target.attach_to_bus(i2c_bus)
target.write_mem(0x0100, [0x01, 0x02, 0x03, 0x04])
assert target.read_mem(0x0100, 4) == [0x01, 0x02, 0x03, 0x04]
target.detach_from_bus()
Controller access¶
Use a PxI2CController on the same bus to exercise the target as a real controller would. Pass the 16-bit sub-address as a two-byte list to i2c_write_addr / i2c_read_addr:
from aqpxlib import AqProtocolExerciser
from aqpxlib.i2c import PxI2CBus, PxI2CController, PxI2C4KbMemoryTarget
with AqProtocolExerciser.connect(port=60600) as px:
i2c_bus = PxI2CBus(px, scl=0, sda=1)
target = PxI2C4KbMemoryTarget(px, address=0x50)
target.attach_to_bus(i2c_bus)
i2c_controller = PxI2CController(px)
i2c_controller.attach_to_bus(i2c_bus)
sub_address = 0x0100
data = [0xDE, 0xAD, 0xBE, 0xEF]
i2c_controller.i2c_write_addr(
address=target.address,
sub_address=[(sub_address >> 8) & 0xFF, sub_address & 0xFF],
data=data,
)
read_back = i2c_controller.i2c_read_addr(
address=target.address,
sub_address=[(sub_address >> 8) & 0xFF, sub_address & 0xFF],
count=len(data),
)
assert read_back == data
# Confirm the memory contents match what the controller wrote.
assert target.read_mem(sub_address, len(data)) == data
i2c_controller.detach_from_bus()
target.detach_from_bus()
Bus access events¶
When another device on the bus reads or writes the target, the exerciser emits memory_target_write_event and memory_target_read_event on the target instance. These are shared event types (also used by other memory targets on I3C, SPI, and so on):
| Event | Payload fields | When emitted |
|---|---|---|
memory_target_write_event |
addr, data |
After a completed bus write transfer |
memory_target_read_event |
addr, count |
After a completed bus read transfer |
Subscribe with separate handlers for each event type:
from aqpxlib.event import PxMemoryTargetReadEvent, PxMemoryTargetWriteEvent
@target.on_event(PxMemoryTargetWriteEvent)
def on_memory_target_write(write: PxMemoryTargetWriteEvent):
print(f"Bus write @ 0x{write.addr:04x}: {write.data.hex()}")
@target.on_event(PxMemoryTargetReadEvent)
def on_memory_target_read(read: PxMemoryTargetReadEvent):
print(f"Bus read @ 0x{read.addr:04x}, {read.count} bytes")
Events reflect bus activity only. Calls to read_mem / write_mem from Python do not emit memory target events.
See also: Event Registration and Handler Setup.
I3C bus support¶
PxI2C4KbMemoryTarget may be attached to an I3C bus (valid_buses includes "i3c" and "i2c"). The device behaves as an I2C-style target on the I3C SDA line.
API reference¶
PxI2C4KbMemoryTarget
¶
PxI2C4KbMemoryTarget(
exerciser: AqProtocolExerciser,
address: int = 0,
config: PxI2C4KbMemoryTargetConfig | None = None,
*args,
**kwargs
)
Bases: PxAbstractTarget[PxI2C4KbMemoryTargetConfig, None]
I2C byte-addressable 4 KB memory target.
Emulates a 4 KB memory device on an I2C or I3C bus. Controllers access memory using a 7-bit device address plus a 16-bit sub-address (MSB first).
Use read_mem and write_mem to access memory from Python. Bus reads and
writes from a controller emit memory_target_read_event and
memory_target_write_event notifications.
| METHOD | DESCRIPTION |
|---|---|
add_event_handler |
Add an event handler to the current device instance, by event class or event name. |
remove_event_handler |
Remove an event handler from the current device instance, by event class or event name. |
get_event_handlers |
Retreive handlers registerd on this device. |
on_event |
Add an event handler to the instance by decorator, by event class or event name. |
match |
Return whether a device config describes a device of this class. |
update_additional_data |
Atomically update additional_data with revision-aware retry. |
update_config |
Set the config of the target. |
set_config |
Set the config of the target. |
attach_to_bus |
Attach the target to a bus. |
detach_from_bus |
Detach the device from the bus. |
perform_operation |
Send an operation. |
read_mem |
Read bytes from memory via the Protocol Exerciser API. |
write_mem |
Write bytes to memory via the Protocol Exerciser API. |
| ATTRIBUTE | DESCRIPTION |
|---|---|
available_events_map |
Map each available event name to its id in event topics. |
is_attached |
Check if the device is attached to a bus.
TYPE:
|
state |
Get the state of the device.
TYPE:
|
name |
Get the name of the device.
TYPE:
|
cts_op_name |
Get the name of CTS operation. Currently not available now.
TYPE:
|
additional_data |
Get the opaque additional data blob associated with this device.
TYPE:
|
config |
Get the config of the device.
TYPE:
|
address |
Static address of the device.
TYPE:
|
available_events_map
¶
Map each available event name to its id in event topics.
additional_data
¶
additional_data: bytes
Get the opaque additional data blob associated with this device.
add_event_handler
¶
Add an event handler to the current device instance, by event class or event name.
remove_event_handler
¶
remove_event_handler(
event: str | type[PxAbstractEvent], handler: Callable[[Any], Any] | None = None
) -> None
Remove an event handler from the current device instance, by event class or event name.
get_event_handlers
¶
get_event_handlers(
filter_by: Callable[[str], bool] | None = None,
) -> dict[str, list[Callable[[Any], Any]]]
Retreive handlers registerd on this device.
Handlers are always scoped to this device; an optional filter_by is
applied on friendly event names.
on_event
¶
on_event(
event_type: str | type[PxAbstractEvent],
) -> Callable[[Callable[..., Any]], Callable[..., Any]]
Add an event handler to the instance by decorator, by event class or event name.
match
¶
match(config: PxAbstractDeviceConfig) -> bool
Return whether a device config describes a device of this class.
update_additional_data
¶
Atomically update additional_data with revision-aware retry.
attach_to_bus
¶
Attach the target to a bus.
This method is used to make the device aware of the bus it is connected to. It is used to set the bus attribute of the device.
Note
This method will not automatically create an actual instance on
the Protocol Exerciser until user call the attach_to_bus method.
| PARAMETER | DESCRIPTION |
|---|---|
|
The bus to attach the device to
TYPE:
|
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If the device is already attached to a bus |
detach_from_bus
¶
Detach the device from the bus.
The opposite of the attach_to_bus method.
perform_operation
¶
perform_operation(
operation: PxDeviceOperation, *, timeout: int | None = None
) -> PxDeviceOperation
read_mem
¶
Read bytes from memory via the Protocol Exerciser API.
This does not perform an I2C bus transaction. Use a
PxI2CController to exercise the
target over the bus.
| PARAMETER | DESCRIPTION |
|---|---|
|
Starting 16-bit memory address (
TYPE:
|
|
Number of bytes to read.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
list[int]
|
Bytes read from memory as a list of integers ( |
write_mem
¶
Write bytes to memory via the Protocol Exerciser API.
This does not perform an I2C bus transaction. Use a
PxI2CController to exercise the
target over the bus.
| PARAMETER | DESCRIPTION |
|---|---|
|
Starting 16-bit memory address (
TYPE:
|
|
Bytes to write as a list of integers ( |