Skip to content

Commit 638e3c7

Browse files
authored
Merge pull request #38 from OpenIPC/feature/ff-skip
Skip 0xFF sectors and pages in CMD_FLASH_STREAM
2 parents d18b798 + 6858888 commit 638e3c7

2 files changed

Lines changed: 85 additions & 36 deletions

File tree

agent/main.c

Lines changed: 54 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -501,13 +501,39 @@ static void handle_flash_program(const uint8_t *data, uint32_t len) {
501501
* Progress: RSP_DATA [sector_done:2LE] [total:2LE] after each sector.
502502
* Final: ACK_OK or ACK_CRC_ERROR.
503503
*/
504+
/* Check if a 256-byte page is all 0xFF (erased state).
505+
* Uses word-aligned reads for speed (~0.1µs vs 2ms page program). */
506+
static int page_is_ff(const uint8_t *data, uint32_t len) {
507+
const uint32_t *w = (const uint32_t *)data;
508+
for (uint32_t i = 0; i < len / 4; i++)
509+
if (w[i] != 0xFFFFFFFF) return 0;
510+
return 1;
511+
}
512+
513+
/* Erase sector + program non-0xFF pages from buffer */
514+
static void erase_and_program(uint32_t addr, const uint8_t *buf,
515+
uint32_t bytes, uint32_t page_sz,
516+
int drain_fifo) {
517+
flash_erase_sector(addr);
518+
uint32_t offset = 0;
519+
while (offset < bytes) {
520+
uint32_t chunk = bytes - offset;
521+
if (chunk > page_sz) chunk = page_sz;
522+
if (!page_is_ff(&buf[offset], chunk))
523+
flash_write_page(addr + offset, &buf[offset], chunk);
524+
offset += chunk;
525+
if (drain_fifo) proto_drain_fifo();
526+
}
527+
}
528+
504529
static void handle_flash_stream(const uint8_t *data, uint32_t len) {
505-
if (len < 12) { proto_send_ack(ACK_CRC_ERROR); return; }
530+
if (len < 44) { proto_send_ack(ACK_CRC_ERROR); return; }
506531
if (!flash_readable) { proto_send_ack(ACK_FLASH_ERROR); return; }
507532

508533
uint32_t flash_addr = read_le32(&data[0]);
509534
uint32_t size = read_le32(&data[4]);
510535
uint32_t expected_crc = read_le32(&data[8]);
536+
const uint8_t *bitmap = &data[12]; /* 32-byte sector bitmap */
511537

512538
if (size == 0 || flash_addr + size > flash_info.size) {
513539
proto_send_ack(ACK_FLASH_ERROR);
@@ -539,6 +565,29 @@ static void handle_flash_stream(const uint8_t *data, uint32_t len) {
539565
uint32_t sector_bytes = size - sector_offset;
540566
if (sector_bytes > sector_sz) sector_bytes = sector_sz;
541567

568+
/* Check bitmap: bit=0 means sector is all 0xFF, skip it */
569+
if (!(bitmap[s / 8] & (1 << (s % 8)))) {
570+
/* Still process pending data sector if any */
571+
if (pending_buf >= 0) {
572+
erase_and_program(pending_addr, buf[pending_buf],
573+
pending_bytes, page_sz, 1);
574+
pending_buf = -1;
575+
}
576+
577+
/* Erase this sector (ensure 0xFF state) but don't program */
578+
flash_erase_sector(flash_addr + sector_offset);
579+
580+
/* Send progress — no data to receive */
581+
uint8_t progress[4];
582+
progress[0] = ((s + 1) >> 0) & 0xFF;
583+
progress[1] = ((s + 1) >> 8) & 0xFF;
584+
progress[2] = (num_sectors >> 0) & 0xFF;
585+
progress[3] = (num_sectors >> 8) & 0xFF;
586+
proto_send(RSP_DATA, progress, 4);
587+
588+
continue; /* Don't touch rx_buf or set new pending */
589+
}
590+
542591
/* Receive sector data into rx_buf.
543592
* No flash operations during receive — UART stays responsive. */
544593
uint32_t buf_received = 0;
@@ -575,16 +624,8 @@ static void handle_flash_stream(const uint8_t *data, uint32_t len) {
575624
/* Process previous sector if pending (erase + program).
576625
* Host is streaming next sector into the OTHER buffer right now. */
577626
if (pending_buf >= 0) {
578-
flash_erase_sector(pending_addr);
579-
uint32_t offset = 0;
580-
while (offset < pending_bytes) {
581-
uint32_t chunk = pending_bytes - offset;
582-
if (chunk > page_sz) chunk = page_sz;
583-
flash_write_page(pending_addr + offset,
584-
&buf[pending_buf][offset], chunk);
585-
offset += chunk;
586-
proto_drain_fifo();
587-
}
627+
erase_and_program(pending_addr, buf[pending_buf],
628+
pending_bytes, page_sz, 1);
588629
}
589630

590631
/* This sector's buffer becomes the pending one */
@@ -598,15 +639,8 @@ static void handle_flash_stream(const uint8_t *data, uint32_t len) {
598639

599640
/* Process the last sector (no more data to receive) */
600641
if (pending_buf >= 0) {
601-
flash_erase_sector(pending_addr);
602-
uint32_t offset = 0;
603-
while (offset < pending_bytes) {
604-
uint32_t chunk = pending_bytes - offset;
605-
if (chunk > page_sz) chunk = page_sz;
606-
flash_write_page(pending_addr + offset,
607-
&buf[pending_buf][offset], chunk);
608-
offset += chunk;
609-
}
642+
erase_and_program(pending_addr, buf[pending_buf],
643+
pending_bytes, page_sz, 0);
610644
}
611645

612646
proto_send_ack(ACK_OK);

src/defib/agent/client.py

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -448,8 +448,23 @@ async def write_flash(
448448

449449
total = len(data)
450450
expected_crc = zlib.crc32(data) & 0xFFFFFFFF
451+
sector_sz = self._sector_size or 0x10000
452+
num_sectors = (total + sector_sz - 1) // sector_sz
453+
454+
# Build sector bitmap: bit=1 if sector has non-0xFF data
455+
ff_sector = b'\xff' * sector_sz
456+
bitmap = bytearray(32)
457+
for s in range(num_sectors):
458+
sector_data = data[s * sector_sz : (s + 1) * sector_sz]
459+
if sector_data != ff_sector[:len(sector_data)]:
460+
bitmap[s // 8] |= 1 << (s % 8)
461+
462+
skip_count = num_sectors - bin(int.from_bytes(bitmap, 'little')).count('1')
463+
if skip_count > 0:
464+
logger.info("Flash stream: skipping %d/%d sectors (0xFF)",
465+
skip_count, num_sectors)
451466

452-
payload = struct.pack("<III", addr, total, expected_crc)
467+
payload = struct.pack("<III", addr, total, expected_crc) + bytes(bitmap)
453468
await send_packet(self._transport, CMD_FLASH_STREAM, payload)
454469

455470
cmd, resp = await recv_response(self._transport, timeout=10.0)
@@ -459,29 +474,29 @@ async def write_flash(
459474

460475
# Double-buffer pipeline: send sector N, get "received" signal,
461476
# immediately send sector N+1 while agent erases+programs N.
462-
# Agent sends progress RSP_DATA after RECEIVING (not after programming),
463-
# so the host can keep streaming without waiting for flash ops.
464-
sector_sz = self._sector_size or 0x10000
465-
num_sectors = (total + sector_sz - 1) // sector_sz
477+
# Sectors with bitmap bit=0 are skipped (all 0xFF).
466478
offset = 0
467479

468480
for s in range(num_sectors):
469481
sector_bytes = min(sector_sz, total - offset)
470482

471-
# Send one sector of DATA packets
472-
sent = 0
473-
seq = offset // WRITE_CHUNK_SIZE
474-
while sent < sector_bytes:
475-
chunk = min(WRITE_CHUNK_SIZE, sector_bytes - sent)
476-
pkt = struct.pack("<H", seq) + data[offset + sent:offset + sent + chunk]
477-
await send_packet(self._transport, RSP_DATA, pkt)
478-
sent += chunk
479-
seq += 1
483+
if bitmap[s // 8] & (1 << (s % 8)):
484+
# Send one sector of DATA packets
485+
sent = 0
486+
seq = offset // WRITE_CHUNK_SIZE
487+
while sent < sector_bytes:
488+
chunk = min(WRITE_CHUNK_SIZE, sector_bytes - sent)
489+
pkt = struct.pack("<H", seq) + data[offset + sent:offset + sent + chunk]
490+
await send_packet(self._transport, RSP_DATA, pkt)
491+
sent += chunk
492+
seq += 1
493+
494+
# else: sector is all 0xFF — don't send data, agent skips it
480495

481496
offset += sector_bytes
482497

483-
# Wait for "sector received" progress — agent sends this BEFORE
484-
# erase+program, so we can start sending next sector immediately.
498+
# Wait for progress — agent sends RSP_DATA for both data and
499+
# skipped sectors (immediately for skipped, after receive for data).
485500
while True:
486501
cmd, data_pkt = await recv_packet(self._transport, timeout=120.0)
487502
if cmd == RSP_READY:

0 commit comments

Comments
 (0)