replace most references to pico with RP2040

This commit is contained in:
Brian S. Stephan 2023-11-07 00:26:51 -06:00
parent a8156f5e89
commit fc022452f5
Signed by: bss
GPG Key ID: 3DE06D3180895FCB
5 changed files with 50 additions and 50 deletions

View File

@ -6,7 +6,7 @@ import logging
from google.protobuf.message import Message from google.protobuf.message import Message
from gp2040ce_bintools import core_parser from gp2040ce_bintools import core_parser
from gp2040ce_bintools.pico import get_bootsel_endpoints, read, write from gp2040ce_bintools.rp2040 import get_bootsel_endpoints, read, write
from gp2040ce_bintools.storage import (STORAGE_BINARY_LOCATION, STORAGE_MEMORY_ADDRESS, STORAGE_SIZE, from gp2040ce_bintools.storage import (STORAGE_BINARY_LOCATION, STORAGE_MEMORY_ADDRESS, STORAGE_SIZE,
pad_config_to_storage_size, serialize_config_with_footer) pad_config_to_storage_size, serialize_config_with_footer)
@ -149,7 +149,7 @@ def write_new_config_to_usb(config: Message, endpoint_out: object, endpoint_in:
"""Serialize the provided config to a device over USB, in the proper location for a GP2040-CE board. """Serialize the provided config to a device over USB, in the proper location for a GP2040-CE board.
Args: Args:
config: the Protobuf configuration to write to a Pico board in BOOTSEL mode config: the Protobuf configuration to write to a RP2040 board in BOOTSEL mode
endpoint_out: the USB endpoint to write to endpoint_out: the USB endpoint to write to
endpoint_in: the USB endpoint to read from endpoint_in: the USB endpoint to read from
""" """

View File

@ -17,7 +17,7 @@ from textual.widgets.tree import TreeNode
from gp2040ce_bintools import core_parser, handler from gp2040ce_bintools import core_parser, handler
from gp2040ce_bintools.builder import write_new_config_to_filename, write_new_config_to_usb from gp2040ce_bintools.builder import write_new_config_to_filename, write_new_config_to_usb
from gp2040ce_bintools.pico import get_bootsel_endpoints, read from gp2040ce_bintools.rp2040 import get_bootsel_endpoints, read
from gp2040ce_bintools.storage import (STORAGE_MEMORY_ADDRESS, STORAGE_SIZE, ConfigReadError, get_config, from gp2040ce_bintools.storage import (STORAGE_MEMORY_ADDRESS, STORAGE_SIZE, ConfigReadError, get_config,
get_config_from_file, get_new_config) get_config_from_file, get_new_config)
@ -390,7 +390,7 @@ def edit_config():
parser.add_argument('--new-if-not-found', action='store_true', default=True, parser.add_argument('--new-if-not-found', action='store_true', default=True,
help="if the file/USB device doesn't have a config section, start a new one (default: enabled)") help="if the file/USB device doesn't have a config section, start a new one (default: enabled)")
group = parser.add_mutually_exclusive_group(required=True) group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('--usb', action='store_true', help="retrieve the config from a Pico board connected over USB " group.add_argument('--usb', action='store_true', help="retrieve the config from a RP2040 board connected over USB "
"and in BOOTSEL mode") "and in BOOTSEL mode")
group.add_argument('--filename', help=".bin file of a GP2040-CE board's config + footer or entire storage section, " group.add_argument('--filename', help=".bin file of a GP2040-CE board's config + footer or entire storage section, "
"or of a GP2040-CE's whole board dump if --whole-board is specified") "or of a GP2040-CE's whole board dump if --whole-board is specified")

View File

@ -1,4 +1,4 @@
"""Methods to interact with the Raspberry Pi Pico directly. """Methods to interact with the Raspberry Pi RP2040 directly.
Much of this code is a partial Python implementation of picotool. Much of this code is a partial Python implementation of picotool.
""" """
@ -37,12 +37,12 @@ PICO_COMMANDS = {
################# #################
class PicoAlignmentError(ValueError): class RP2040AlignmentError(ValueError):
"""Exception raised when the address provided for an operation is invalid.""" """Exception raised when the address provided for an operation is invalid."""
def get_bootsel_endpoints() -> tuple[usb.core.Endpoint, usb.core.Endpoint]: def get_bootsel_endpoints() -> tuple[usb.core.Endpoint, usb.core.Endpoint]:
"""Retrieve the USB endpoint for purposes of interacting with a Pico in BOOTSEL mode. """Retrieve the USB endpoint for purposes of interacting with a RP2040 in BOOTSEL mode.
Returns: Returns:
the out and in endpoints for the BOOTSEL interface the out and in endpoints for the BOOTSEL interface
@ -51,7 +51,7 @@ def get_bootsel_endpoints() -> tuple[usb.core.Endpoint, usb.core.Endpoint]:
pico_device = usb.core.find(idVendor=PICO_VENDOR, idProduct=PICO_PRODUCT) pico_device = usb.core.find(idVendor=PICO_VENDOR, idProduct=PICO_PRODUCT)
if not pico_device: if not pico_device:
raise ValueError("Pico board in BOOTSEL mode could not be found!") raise ValueError("RP2040 board in BOOTSEL mode could not be found!")
if pico_device.is_kernel_driver_active(0): if pico_device.is_kernel_driver_active(0):
pico_device.detach_kernel_driver(0) pico_device.detach_kernel_driver(0)
@ -71,7 +71,7 @@ def get_bootsel_endpoints() -> tuple[usb.core.Endpoint, usb.core.Endpoint]:
def exclusive_access(out_end: usb.core.Endpoint, in_end: usb.core.Endpoint, is_exclusive: bool = True) -> None: def exclusive_access(out_end: usb.core.Endpoint, in_end: usb.core.Endpoint, is_exclusive: bool = True) -> None:
"""Enable exclusive access mode on a Pico in BOOTSEL. """Enable exclusive access mode on a RP2040 in BOOTSEL.
Args: Args:
out_endpoint: the out direction USB endpoint to write to out_endpoint: the out direction USB endpoint to write to
@ -91,7 +91,7 @@ def exclusive_access(out_end: usb.core.Endpoint, in_end: usb.core.Endpoint, is_e
def erase(out_end: usb.core.Endpoint, in_end: usb.core.Endpoint, location: int, size: int) -> None: def erase(out_end: usb.core.Endpoint, in_end: usb.core.Endpoint, location: int, size: int) -> None:
"""Erase a section of flash memory on a Pico in BOOTSEL mode. """Erase a section of flash memory on a RP2040 in BOOTSEL mode.
Args: Args:
out_endpoint: the out direction USB endpoint to write to out_endpoint: the out direction USB endpoint to write to
@ -113,7 +113,7 @@ def erase(out_end: usb.core.Endpoint, in_end: usb.core.Endpoint, location: int,
def exit_xip(out_end: usb.core.Endpoint, in_end: usb.core.Endpoint) -> None: def exit_xip(out_end: usb.core.Endpoint, in_end: usb.core.Endpoint) -> None:
"""Exit XIP on a Pico in BOOTSEL. """Exit XIP on a RP2040 in BOOTSEL.
Args: Args:
out_endpoint: the out direction USB endpoint to write to out_endpoint: the out direction USB endpoint to write to
@ -131,7 +131,7 @@ def exit_xip(out_end: usb.core.Endpoint, in_end: usb.core.Endpoint) -> None:
def read(out_end: usb.core.Endpoint, in_end: usb.core.Endpoint, location: int, size: int) -> bytearray: def read(out_end: usb.core.Endpoint, in_end: usb.core.Endpoint, location: int, size: int) -> bytearray:
"""Read a requested number of bytes from a Pico in BOOTSEL, starting from the specified location. """Read a requested number of bytes from a RP2040 in BOOTSEL, starting from the specified location.
This also prepares the USB device for reading, so it expects to be able to grab This also prepares the USB device for reading, so it expects to be able to grab
exclusive access. exclusive access.
@ -172,7 +172,7 @@ def read(out_end: usb.core.Endpoint, in_end: usb.core.Endpoint, location: int, s
def reboot(out_end: usb.core.Endpoint) -> None: def reboot(out_end: usb.core.Endpoint) -> None:
"""Reboot a Pico in BOOTSEL mode.""" """Reboot a RP2040 in BOOTSEL mode."""
# set up the data # set up the data
pico_token = 1 pico_token = 1
command_size = 12 command_size = 12
@ -187,7 +187,7 @@ def reboot(out_end: usb.core.Endpoint) -> None:
def write(out_end: usb.core.Endpoint, in_end: usb.core.Endpoint, location: int, content: bytes) -> None: def write(out_end: usb.core.Endpoint, in_end: usb.core.Endpoint, location: int, content: bytes) -> None:
"""Write content to a Pico in BOOTSEL, starting from the specified location. """Write content to a RP2040 in BOOTSEL, starting from the specified location.
This also prepares the USB device for writing, so it expects to be able to grab This also prepares the USB device for writing, so it expects to be able to grab
exclusive access. exclusive access.
@ -203,8 +203,8 @@ def write(out_end: usb.core.Endpoint, in_end: usb.core.Endpoint, location: int,
write_size = 0 write_size = 0
if (location % chunk_size) != 0: if (location % chunk_size) != 0:
raise PicoAlignmentError(f"writes must start at {chunk_size} byte boundaries, " raise RP2040AlignmentError(f"writes must start at {chunk_size} byte boundaries, "
f"please pad or align as appropriate!") f"please pad or align as appropriate!")
# set up the data # set up the data
command_size = 8 command_size = 8

View File

@ -7,7 +7,7 @@ from google.protobuf.json_format import MessageToJson
from google.protobuf.message import Message from google.protobuf.message import Message
from gp2040ce_bintools import core_parser, get_config_pb2 from gp2040ce_bintools import core_parser, get_config_pb2
from gp2040ce_bintools.pico import get_bootsel_endpoints, read from gp2040ce_bintools.rp2040 import get_bootsel_endpoints, read
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -229,7 +229,7 @@ def visualize():
parser.add_argument('--whole-board', action='store_true', help="indicate the binary file is a whole board dump") parser.add_argument('--whole-board', action='store_true', help="indicate the binary file is a whole board dump")
parser.add_argument('--json', action='store_true', help="print the config out as a JSON document") parser.add_argument('--json', action='store_true', help="print the config out as a JSON document")
group = parser.add_mutually_exclusive_group(required=True) group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('--usb', action='store_true', help="retrieve the config from a Pico board connected over USB " group.add_argument('--usb', action='store_true', help="retrieve the config from a RP2040 board connected over USB "
"and in BOOTSEL mode") "and in BOOTSEL mode")
group.add_argument('--filename', help=".bin file of a GP2040-CE board's storage section, bytes " group.add_argument('--filename', help=".bin file of a GP2040-CE board's storage section, bytes "
"101FC000-10200000, or of a GP2040-CE's whole board dump " "101FC000-10200000, or of a GP2040-CE's whole board dump "

View File

@ -8,7 +8,7 @@ from array import array
import pytest import pytest
from decorator import decorator from decorator import decorator
import gp2040ce_bintools.pico as pico import gp2040ce_bintools.rp2040 as rp2040
HERE = os.path.dirname(os.path.abspath(__file__)) HERE = os.path.dirname(os.path.abspath(__file__))
@ -34,9 +34,9 @@ def test_get_bootsel_endpoints():
mock_interface = mock.MagicMock(name='mock_interface') mock_interface = mock.MagicMock(name='mock_interface')
with mock.patch('usb.core.find', return_value=mock_device) as mock_find: with mock.patch('usb.core.find', return_value=mock_device) as mock_find:
with mock.patch('usb.util.find_descriptor', return_value=mock_interface) as mock_find_descriptor: with mock.patch('usb.util.find_descriptor', return_value=mock_interface) as mock_find_descriptor:
_, _ = pico.get_bootsel_endpoints() _, _ = rp2040.get_bootsel_endpoints()
mock_find.assert_called_with(idVendor=pico.PICO_VENDOR, idProduct=pico.PICO_PRODUCT) mock_find.assert_called_with(idVendor=rp2040.PICO_VENDOR, idProduct=rp2040.PICO_PRODUCT)
mock_device.is_kernel_driver_active.assert_called_with(0) mock_device.is_kernel_driver_active.assert_called_with(0)
mock_device.detach_kernel_driver.assert_not_called() mock_device.detach_kernel_driver.assert_not_called()
mock_device.get_active_configuration.assert_called_once() mock_device.get_active_configuration.assert_called_once()
@ -54,9 +54,9 @@ def test_get_bootsel_endpoints_with_kernel_disconnect():
mock_interface = mock.MagicMock(name='mock_interface') mock_interface = mock.MagicMock(name='mock_interface')
with mock.patch('usb.core.find', return_value=mock_device) as mock_find: with mock.patch('usb.core.find', return_value=mock_device) as mock_find:
with mock.patch('usb.util.find_descriptor', return_value=mock_interface) as mock_find_descriptor: with mock.patch('usb.util.find_descriptor', return_value=mock_interface) as mock_find_descriptor:
_, _ = pico.get_bootsel_endpoints() _, _ = rp2040.get_bootsel_endpoints()
mock_find.assert_called_with(idVendor=pico.PICO_VENDOR, idProduct=pico.PICO_PRODUCT) mock_find.assert_called_with(idVendor=rp2040.PICO_VENDOR, idProduct=rp2040.PICO_PRODUCT)
mock_device.is_kernel_driver_active.assert_called_with(0) mock_device.is_kernel_driver_active.assert_called_with(0)
mock_device.detach_kernel_driver.assert_called_with(0) mock_device.detach_kernel_driver.assert_called_with(0)
mock_device.get_active_configuration.assert_called_once() mock_device.get_active_configuration.assert_called_once()
@ -68,7 +68,7 @@ def test_get_bootsel_endpoints_with_kernel_disconnect():
def test_exclusive_access(): def test_exclusive_access():
"""Test that we can get exclusive access to a BOOTSEL board.""" """Test that we can get exclusive access to a BOOTSEL board."""
end_out, end_in = mock.MagicMock(), mock.MagicMock() end_out, end_in = mock.MagicMock(), mock.MagicMock()
pico.exclusive_access(end_out, end_in) rp2040.exclusive_access(end_out, end_in)
payload = struct.pack('<LLBBxxLL12x', 0x431fd10b, 1, 0x1, 1, 0, 1) payload = struct.pack('<LLBBxxLL12x', 0x431fd10b, 1, 0x1, 1, 0, 1)
end_out.write.assert_called_with(payload) end_out.write.assert_called_with(payload)
@ -76,7 +76,7 @@ def test_exclusive_access():
end_out.reset_mock() end_out.reset_mock()
end_in.reset_mock() end_in.reset_mock()
pico.exclusive_access(end_out, end_in, is_exclusive=False) rp2040.exclusive_access(end_out, end_in, is_exclusive=False)
payload = struct.pack('<LLBBxxLL12x', 0x431fd10b, 1, 0x1, 1, 0, 0) payload = struct.pack('<LLBBxxLL12x', 0x431fd10b, 1, 0x1, 1, 0, 0)
end_out.write.assert_called_with(payload) end_out.write.assert_called_with(payload)
@ -86,7 +86,7 @@ def test_exclusive_access():
def test_exit_xip(): def test_exit_xip():
"""Test that we can exit XIP on a BOOTSEL board.""" """Test that we can exit XIP on a BOOTSEL board."""
end_out, end_in = mock.MagicMock(), mock.MagicMock() end_out, end_in = mock.MagicMock(), mock.MagicMock()
pico.exit_xip(end_out, end_in) rp2040.exit_xip(end_out, end_in)
payload = struct.pack('<LLBBxxL16x', 0x431fd10b, 1, 0x6, 0, 0) payload = struct.pack('<LLBBxxL16x', 0x431fd10b, 1, 0x6, 0, 0)
end_out.write.assert_called_with(payload) end_out.write.assert_called_with(payload)
@ -96,7 +96,7 @@ def test_exit_xip():
def test_erase(): def test_erase():
"""Test that we can send a command to erase a section of memory.""" """Test that we can send a command to erase a section of memory."""
end_out, end_in = mock.MagicMock(), mock.MagicMock() end_out, end_in = mock.MagicMock(), mock.MagicMock()
pico.erase(end_out, end_in, 0x101FC000, 8192) rp2040.erase(end_out, end_in, 0x101FC000, 8192)
payload = struct.pack('<LLBBxxLLL8x', 0x431fd10b, 1, 0x3, 8, 0, 0x101FC000, 8192) payload = struct.pack('<LLBBxxLLL8x', 0x431fd10b, 1, 0x3, 8, 0, 0x101FC000, 8192)
end_out.write.assert_called_with(payload) end_out.write.assert_called_with(payload)
@ -107,7 +107,7 @@ def test_read():
"""Test that we can read a memory of a BOOTSEL board in a variety of conditions.""" """Test that we can read a memory of a BOOTSEL board in a variety of conditions."""
end_out, end_in = mock.MagicMock(), mock.MagicMock() end_out, end_in = mock.MagicMock(), mock.MagicMock()
end_in.read.return_value = array('B', b'\x11' * 256) end_in.read.return_value = array('B', b'\x11' * 256)
content = pico.read(end_out, end_in, 0x101FC000, 256) content = rp2040.read(end_out, end_in, 0x101FC000, 256)
expected_writes = [ expected_writes = [
mock.call(struct.pack('<LLBBxxLL12x', 0x431fd10b, 1, 0x1, 1, 0, 1)), mock.call(struct.pack('<LLBBxxLL12x', 0x431fd10b, 1, 0x1, 1, 0, 1)),
@ -125,7 +125,7 @@ def test_read_shorter_than_chunk():
"""Test that we can read a memory of a BOOTSEL board in a variety of conditions.""" """Test that we can read a memory of a BOOTSEL board in a variety of conditions."""
end_out, end_in = mock.MagicMock(), mock.MagicMock() end_out, end_in = mock.MagicMock(), mock.MagicMock()
end_in.read.return_value = array('B', b'\x11' * 256) end_in.read.return_value = array('B', b'\x11' * 256)
content = pico.read(end_out, end_in, 0x101FC000, 128) content = rp2040.read(end_out, end_in, 0x101FC000, 128)
expected_writes = [ expected_writes = [
mock.call(struct.pack('<LLBBxxLL12x', 0x431fd10b, 1, 0x1, 1, 0, 1)), mock.call(struct.pack('<LLBBxxLL12x', 0x431fd10b, 1, 0x1, 1, 0, 1)),
@ -143,7 +143,7 @@ def test_read_bigger_than_chunk():
"""Test that we can read a memory of a BOOTSEL board in a variety of conditions.""" """Test that we can read a memory of a BOOTSEL board in a variety of conditions."""
end_out, end_in = mock.MagicMock(), mock.MagicMock() end_out, end_in = mock.MagicMock(), mock.MagicMock()
end_in.read.return_value = array('B', b'\x11' * 256) end_in.read.return_value = array('B', b'\x11' * 256)
content = pico.read(end_out, end_in, 0x101FC000, 512) content = rp2040.read(end_out, end_in, 0x101FC000, 512)
expected_writes = [ expected_writes = [
mock.call(struct.pack('<LLBBxxLL12x', 0x431fd10b, 1, 0x1, 1, 0, 1)), mock.call(struct.pack('<LLBBxxLL12x', 0x431fd10b, 1, 0x1, 1, 0, 1)),
@ -163,7 +163,7 @@ def test_read_bigger_than_chunk():
def test_reboot(): def test_reboot():
"""Test that we can reboot a BOOTSEL board.""" """Test that we can reboot a BOOTSEL board."""
end_out = mock.MagicMock() end_out = mock.MagicMock()
pico.reboot(end_out) rp2040.reboot(end_out)
payload = struct.pack('<LLBBxxLLLL4x', 0x431fd10b, 1, 0x2, 12, 0, 0, 0x20042000, 500) payload = struct.pack('<LLBBxxLLLL4x', 0x431fd10b, 1, 0x2, 12, 0, 0, 0x20042000, 500)
end_out.write.assert_called_with(payload) end_out.write.assert_called_with(payload)
@ -172,7 +172,7 @@ def test_reboot():
def test_write(): def test_write():
"""Test that we can write to a board in BOOTSEL mode.""" """Test that we can write to a board in BOOTSEL mode."""
end_out, end_in = mock.MagicMock(), mock.MagicMock() end_out, end_in = mock.MagicMock(), mock.MagicMock()
_ = pico.write(end_out, end_in, 0x101FC000, b'\x00\x01\x02\x03') _ = rp2040.write(end_out, end_in, 0x101FC000, b'\x00\x01\x02\x03')
expected_writes = [ expected_writes = [
mock.call(struct.pack('<LLBBxxLL12x', 0x431fd10b, 1, 0x1, 1, 0, 1)), mock.call(struct.pack('<LLBBxxLL12x', 0x431fd10b, 1, 0x1, 1, 0, 1)),
@ -190,7 +190,7 @@ def test_write_chunked():
"""Test that we can write to a board in BOOTSEL mode.""" """Test that we can write to a board in BOOTSEL mode."""
end_out, end_in = mock.MagicMock(), mock.MagicMock() end_out, end_in = mock.MagicMock(), mock.MagicMock()
payload = bytearray(b'\x00\x01\x02\x03' * 1024) payload = bytearray(b'\x00\x01\x02\x03' * 1024)
_ = pico.write(end_out, end_in, 0x10100000, payload * 2) _ = rp2040.write(end_out, end_in, 0x10100000, payload * 2)
expected_writes = [ expected_writes = [
mock.call(struct.pack('<LLBBxxLL12x', 0x431fd10b, 1, 0x1, 1, 0, 1)), mock.call(struct.pack('<LLBBxxLL12x', 0x431fd10b, 1, 0x1, 1, 0, 1)),
@ -211,27 +211,27 @@ def test_write_chunked():
def test_misaligned_write(): def test_misaligned_write():
"""Test that we can't write to a board at invalid memory addresses.""" """Test that we can't write to a board at invalid memory addresses."""
end_out, end_in = mock.MagicMock(), mock.MagicMock() end_out, end_in = mock.MagicMock(), mock.MagicMock()
with pytest.raises(pico.PicoAlignmentError): with pytest.raises(rp2040.RP2040AlignmentError):
_ = pico.write(end_out, end_in, 0x101FE001, b'\x00\x01\x02\x03') _ = rp2040.write(end_out, end_in, 0x101FE001, b'\x00\x01\x02\x03')
with pytest.raises(pico.PicoAlignmentError): with pytest.raises(rp2040.RP2040AlignmentError):
_ = pico.write(end_out, end_in, 0x101FE008, b'\x00\x01\x02\x03') _ = rp2040.write(end_out, end_in, 0x101FE008, b'\x00\x01\x02\x03')
with pytest.raises(pico.PicoAlignmentError): with pytest.raises(rp2040.RP2040AlignmentError):
_ = pico.write(end_out, end_in, 0x101FE010, b'\x00\x01\x02\x03') _ = rp2040.write(end_out, end_in, 0x101FE010, b'\x00\x01\x02\x03')
with pytest.raises(pico.PicoAlignmentError): with pytest.raises(rp2040.RP2040AlignmentError):
_ = pico.write(end_out, end_in, 0x101FE020, b'\x00\x01\x02\x03') _ = rp2040.write(end_out, end_in, 0x101FE020, b'\x00\x01\x02\x03')
with pytest.raises(pico.PicoAlignmentError): with pytest.raises(rp2040.RP2040AlignmentError):
_ = pico.write(end_out, end_in, 0x101FE040, b'\x00\x01\x02\x03') _ = rp2040.write(end_out, end_in, 0x101FE040, b'\x00\x01\x02\x03')
with pytest.raises(pico.PicoAlignmentError): with pytest.raises(rp2040.RP2040AlignmentError):
_ = pico.write(end_out, end_in, 0x101FE080, b'\x00\x01\x02\x03') _ = rp2040.write(end_out, end_in, 0x101FE080, b'\x00\x01\x02\x03')
with pytest.raises(pico.PicoAlignmentError): with pytest.raises(rp2040.RP2040AlignmentError):
_ = pico.write(end_out, end_in, 0x101FE0FF, b'\x00\x01\x02\x03') _ = rp2040.write(end_out, end_in, 0x101FE0FF, b'\x00\x01\x02\x03')
# 256 byte alignment is what is desired, but see comments around there for # 256 byte alignment is what is desired, but see comments around there for
# why only 4096 seems to work right... # why only 4096 seems to work right...
with pytest.raises(pico.PicoAlignmentError): with pytest.raises(rp2040.RP2040AlignmentError):
_ = pico.write(end_out, end_in, 0x101FE100, b'\x00\x01\x02\x03') _ = rp2040.write(end_out, end_in, 0x101FE100, b'\x00\x01\x02\x03')
_ = pico.write(end_out, end_in, 0x101FF000, b'\x00\x01\x02\x03') _ = rp2040.write(end_out, end_in, 0x101FF000, b'\x00\x01\x02\x03')
expected_writes = [ expected_writes = [
mock.call(struct.pack('<LLBBxxLL12x', 0x431fd10b, 1, 0x1, 1, 0, 1)), mock.call(struct.pack('<LLBBxxLL12x', 0x431fd10b, 1, 0x1, 1, 0, 1)),