SPI sniffer feature

The problem with marking CS is that you have no symbols reserved for anything except data. For the single-pin version, you’re logging a single bit, so 0 == low and 1 == high. There is no other value.
For the two-pin version, the same basic problem.

You need to log more than two bits per cycle, and then do some post-processing to only extract the data bits.

What do you think about the following PIO program, which logs four bits per cycle?

start:
cs_program_exit:
    ; Ensure final 2k of data is output via the ISR
    ; Since each loop writes 4 bytes, this requires 0x1FF writes.
    ; Since the JMP check is done pre-decrement, start at 0x1FE.
    set Y, 0x1FF ; Need to write 0x1FF times, jmp checks pre-decrement
cs_fill_2k
    set X, 0xFFFFFFFF
    in X, 32
    jmp Y-- cs_fill_2k ; Ensure final 2k DMA trigger
    ; Then drop into the infinite loop, ready to start another trace

infinite_loop:
    jmp infinite_loop

cs_changed_to_high:
    set X, 0xFFFFFFF3 ; logged when CS changed to high
    in X, 32 ; 0b0001 is CS low->high

cs_stayed_high:
    ; set pins, LED_OFF ; indicate tracing stopped
    jmp pin, cs_stayed_high ; while CS stays high, do nothing more

cs_changed_to_low:
    ; set pins, LED_ON ; indicate tracing occuring
    set X, 0xFFFFFFF2 ; ensure flushed buffer!
    in X, 32  ; 0b0010 is CS high->low

.wrap_target
data_in:
    wait 0 pin 2  ; wait for clock to go low
    wait 1 pin 2  ; wait for clock to go high
    in pins, 2       ; two data bits logged
    in null, 2        ; and two zero bits logged
is_cs_low:
    jmp pin, cs_changed_to_high
.wrap

The main data logging takes minimum of five clock cycles instead of four cycles, because of the need to pad with two zero bits.

Technically, you could do this with logging only three bits per cycle, since there are only six symbols to be encoded. However, this will still take five clock cycles, the logic would be much more involved, and I’d have to think about how to encode / find CS transitions at arbitrary bit position in a binary stream.

In contrast, as conceptualized, each nybble can be independently analyzed:

Value Meaning
0bAB00 Logged Data: Data0 is A, Data1 is B
0b1111 Filler … used to ensure ISR is flushed
0b0010 CS high->low
0b0001 CS low → high
other Unused / invalid

To start tracing, use pio_sm_exec() to jump to cs_stayed_high.
To stop tracing, use pio_sm_exec() to jump to cs_program_exit. This ensures a final 2k of filler data is DMA’d, ensuring the last samples get logged to the file (without writing any additional code to handle this edge condition).

Hook up the ISR to a DMA engine, filling 2k pages of data (two, in a ring buffer). Each time a page is filled, write that 2k block of data to the flash memory.

Note: This is entirely conceptual … haven’t even seen if the above compiles.

1 Like