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.