I2C/SMbus monitor
I've just delved into I2C transactions in recent weeks. I've now started on making a monitor for observing activity. Yet again, I have to give kudos to Chip for an instruction set with conditional execution ... I've built the layer-2 capturing, in assembly, without any need for state tracker changes being decided via extra branching. The only branching, other than a subroutine, is the state changes! In assembly, that's a huge saving on mental resources to prevent bugs, not to mention it producing efficient code execution.
Of course, the speed of the prop2 means assembly wasn't really necessary at all. I'm comfortable with pasm2 so I took it as a challenge to do a high efficiency version. Sysclock can probably be quite low. Below 20 MHz for 100 kHz SCL.
As of yet, there's no Spin/C/Basic coding done and no layer-3 done. I vaguely intend to make it portable equipment.
First draft (entirely untested):
DAT ORG 0 smb_monitor fltl mbpin fltl sclpin fltl sdapin hubset ##$4000_0060 | boundedtap ' deglitch "filt0", 8 samples over 12.5% of bit-period wrpin m_pinmon, sclpin ' Schmitt-triggered and deglitched wrpin m_pinmon, sdapin wrpin #1, mbpin ' mailbox repository for byte count (buffer index) dirh mbpin wrfast #$100, buffadr ' 16 kByte ring-buffer testb sclpin, #5 wc if_nc sets get_sample, #ina ' if port A if_c sets get_sample, #inb ' if port B bitl sclpin, #5 bitl sdapin, #5 bus_unsynced call #get_sample if_nz jmp #bus_unsynced ' loop while SCL low getct pa addct1 pa, ticks50us ' start 50 us timeout mov oldsam, sample ' latch SDA on SCL rise unsynced_scl_high call #get_sample if_c jct1 #idle_scl_high ' if SDA high and timed-out then synced testb oldsam, sdapin xorc if_nc_and_z jmp #unsynced_scl_high ' loop while SCL high and SDA steady if_nz jmp #bus_unsynced ' ensure SCL high still idle_scl_high testb sample, sdapin wc testbn oldsam, sdapin andc ' if SDA rise if_c wfword #%01 ' indicates a stop bit if_c add bytecount, #1 wxpin bytecount, mbpin .idle_loop call #get_sample if_z jmp #.idle_loop ' ignore SDA until SCL falls if_c jmp #bus_unsynced ' if SDA high then not a start getct pa addct2 pa, ticks25ms ' start 25 ms timeout mov count, #9 ' data bits, including ACK/NAK mov databyte, #1 ' indicates a start bit bit_scl_low jct2 #bus_unsynced ' error on timeout call #get_sample if_nz jmp #bit_scl_low ' loop until SCL rises getct pa addct1 pa, ticks50us ' start 50 us timeout rcl databyte, #1 ' msbit first mov oldsam, sample ' latch SDA on SCL rise bit_scl_high jct1 #bus_unsynced ' error on timeout call #get_sample testb oldsam, sdapin xorc if_c_and_z jmp #bus_unsynced ' error if SDA has changed if_z jmp #bit_scl_high ' loop until SCL falls djnz count, #bit_scl_low ' loop for next bit shl databyte, #2 ' reserved for error/stop wfword databyte ' start + completed byte + ACK add bytecount, #1 char_scl_low jct2 #bus_unsynced ' error on timeout call #get_sample if_z jmp #char_scl_low ' ignore SDA until SCL rises getct pa addct1 pa, ticks50us ' start 50 us timeout rcl databyte, #1 ' possible next byte mov oldsam, sample ' latch SDA on SCL rise char_scl_high jct1 #bus_unsynced ' error on timeout call #get_sample testb oldsam, sdapin xorc if_nc_and_z jmp #char_scl_high ' loop until SCL falls or SDA changes if_c jmp #idle_scl_high ' if SDA changed then stop/restart mov count, #8 ' else next byte has already started mov databyte, #0 jmp #bit_scl_low get_sample mov sample, 0-0 '=== sample pins === testb sample, sdapin wc ' check SDA level _ret_ testb sample, sclpin wz ' check SCL level bytecount long 0 ticks50us long (_clkfreq + 10000) / 20000 ticks25ms long (_clkfreq + 20) / 40 m_pinmon long P_SYNC_IO | P_SCHMITT_A | P_FILT0_AB | P_HIGH_FLOAT | P_LOW_FLOAT m_pinsim long P_SYNC_IO | P_SCHMITT_A | P_FILT0_AB | P_LOW_1MA | P_HIGH_1MA buffadr long 0 sclpin long 0 sdapin long 0 mbpin long 0 sample long 0 oldsam long 0 count long 0 databyte long 0
Comments
I suppose I should ask if anyone has done such a monitor already?
Definitely interested in this Evanh. We're starting to do a whole lot more with i2c
Is it intended to be something along the lines of I2CDriver?
Well, the monitoring part anyway; on second look, it seems that can do some sort of active testing, too.
Either way, yes, tools like that are great to have. 👍
Not a Propeller by the looks. Oh, Forth! That was unexpected. That's one language that bamboozles me.
I don't at all understand how your code works, but here's my take on a i2c monitor.
It's essentially a stripped down version of my P1 slave object, ported to the P2 and with all of the writer pieces removed, converts received bits into an ASCII string.
Thanks Chris. I'll have a read of that.
The monitoring process does not attempt to drive the bus at any stage. It's not really a master nor slave, it passively sits and watches. So the code is less as a result. It should be able to record both sent commands and the responses to those commands. The recording will indicate the various I2C bus states as the recording progresses. Maybe even time-stamped.
Okay, yep, you've got it like that. And the whole test wrapper is nice. Good stuff.
You'll note I've used TESTB, on a "sample" of the pins, rather than TESTP on the live pins. This prevents glitches from creating logic bugs in the state tracking.
There's a few logic differences in the state tracker too. It may not be important. I have yet to do any testing.