optimize CAN send over SPI (#2266)

* profiling

* lil faster

* chunking happens later

* little better

* prealloc is no bueno?

* cleanup

* fix usb tests

---------

Co-authored-by: Comma Device <device@comma.ai>
This commit is contained in:
Adeeb Shihadeh
2025-09-02 19:26:52 -07:00
committed by GitHub
parent 819fa5854e
commit 5c1ff7bfa3
6 changed files with 35 additions and 22 deletions

View File

@@ -32,16 +32,13 @@ def calculate_checksum(data):
res ^= b
return res
def pack_can_buffer(arr, fd=False):
snds = [b'']
def pack_can_buffer(arr, chunk=False, fd=False):
snds = [bytearray(), ]
for address, dat, bus in arr:
assert len(dat) in LEN_TO_DLC
#logger.debug(" W 0x%x: 0x%s", address, dat.hex())
extended = 1 if address >= 0x800 else 0
data_len_code = LEN_TO_DLC[len(dat)]
header = bytearray(CANPACKET_HEAD_SIZE)
word_4b = address << 3 | extended << 2
word_4b = (address << 3) | (extended << 2)
header[0] = (data_len_code << 4) | (bus << 1) | int(fd)
header[1] = word_4b & 0xFF
header[2] = (word_4b >> 8) & 0xFF
@@ -49,9 +46,10 @@ def pack_can_buffer(arr, fd=False):
header[4] = (word_4b >> 24) & 0xFF
header[5] = calculate_checksum(header[:5] + dat)
snds[-1] += header + dat
if len(snds[-1]) > 256: # Limit chunks to 256 bytes
snds.append(b'')
snds[-1].extend(header)
snds[-1].extend(dat)
if chunk and len(snds[-1]) > 256:
snds.append(bytearray())
return snds
@@ -729,7 +727,7 @@ class Panda:
@ensure_can_packet_version
def can_send_many(self, arr, *, fd=False, timeout=CAN_SEND_TIMEOUT_MS):
snds = pack_can_buffer(arr, fd=fd)
snds = pack_can_buffer(arr, chunk=(not self.spi), fd=fd)
for tx in snds:
while len(tx) > 0:
bs = self._handle.bulkWrite(3, tx, timeout=timeout)

View File

@@ -29,7 +29,8 @@ CHECKSUM_START = 0xAB
MIN_ACK_TIMEOUT_MS = 100
MAX_XFER_RETRY_COUNT = 5
XFER_SIZE = 0x40*31
SPI_BUF_SIZE = 4096 # from panda/board/drivers/spi.h
XFER_SIZE = SPI_BUF_SIZE - 0x40 # give some room for SPI protocol overhead
DEV_PATH = "/dev/spidev0.0"
@@ -290,8 +291,9 @@ class PandaSpiHandle(BaseHandle):
return self._transfer(0, struct.pack("<BHHH", request, value, index, length), timeout, max_rx_len=length)
def bulkWrite(self, endpoint: int, data: bytes, timeout: int = TIMEOUT) -> int:
mv = memoryview(data)
for x in range(math.ceil(len(data) / XFER_SIZE)):
self._transfer(endpoint, data[XFER_SIZE*x:XFER_SIZE*(x+1)], timeout)
self._transfer(endpoint, mv[XFER_SIZE*x:XFER_SIZE*(x+1)], timeout)
return len(data)
def bulkRead(self, endpoint: int, length: int, timeout: int = TIMEOUT) -> bytes: