Cheap and easy hardware implant for sniffing TWI (I2C) busses
Adobe Portable Document Format - 7.99 MB - 01/14/2017 at 21:19
Time's up for the 1K challenge and the project is still in progress. The compiled object code is nowhere near 1K, even the hex file is only 800 bytes, so size is not an inhibitor for this project. Some glitches in writing to EEPROM still exist, but they will unravel over the weekend.
The project will be finished within a week, because that's when I'll be presenting part of it at the disobey.fi conference. There will be details on the password manager that started this project in the first place too when I post links to slides after the conference on the 15th of January.
I've mentioned before the data read from the bus is about ten times as fast as the data that is written to EEPROM. As the readable data is extremely volatile, reading it must always have highest priority. This is what interrupts are used for, they do actions that have to be done before a specific deadline. In the TWI case the deadline for storing a byte from the bus into RAM is just before the transmission of a new byte starts.
Below we can see the timing of the interrupt routines. The yellow debug signal is pulled low during USI overflow of start condition interrupt handler. The green signal is flashed whenever a new byte is found in the buffer by the main function. A shared buffer is the way the interrupt routines are communicating with the main function.
Transferring the byte from RAM to EEPROM has a deadline much longer in the future. It is sufficient to finish the transfer before the microcontroller is shut down as that's when we lose the contents of the RAM. We can use a data pipeline where data is constantly copied from the buffer to the EEPROM, but this will be progressing a lot more slowly than the sniffing.
Below we see the timing of EEPROM writes. The green signal is pulled low while a byte is being written from the buffer to EEPROM. The yellow signal shows when bytes are received on the TWI bus.
As the oscilloscope captures show, the software architecture is very simple, with interrupt routines producing data to a buffer and the main function consuming that data by storing it in long-term memory.
It's very common to use a serial connection to your laptop for debugging with printf statements. This is usually okay when the stuff that is being done is slower than the serial connection. When working with interrupt driven code and fast signals the serial connection will cause disturbances and bugs that happen due to timing issues. In other words your debbugging code will be working against you.
Flipping pins to blink LEDs is another approach that works well for showing that a certain state in the program has been reached. This causes very little disturbance as it only takes one opcode to switch a pin state. However, when interrupts are happening at 10KHz, the human eye will not be able to perceive those blinks.
This is where it's good to have access to an oscilloscope or a logic analyzer. They can be found in schools, universities, hacklabs, and in Helsinki even in some libraries. Don't be afraid to ask for them, they are very often not in active use. In the scale of oscilloscopes a 100KHz TWI signal is slow, as they are often able to sample at several MHz.
When I'm debugging my TWI signal handler interrupts I'd switch the state of a pin every time an interrupt routine is entered and exited. From a programming point of view this is not different from blinking LEDs. Since I have access to an oscilloscope with many digital inputs I simply use a separate pin for every individual interrupt. This way I can very easily see on my oscilloscope screen when each of my interrupt routines are executing and locate errors. As I'm also showing the data and clock signals on the oscilloscope I can verify that operations happen when they are supposed to. Since I can capture a burst of signals and analyze it statically, my human eyes are well capable of understanding what is happening.
If you've never worked with oscilloscopes, do try it out. Learn how to use triggers and set it up to catch the part that you are debugging. This might be overkill for many projects but for some you just can't do without it.
blue:SDA, red:SCL, green:TWI start condition interrupt, yellow:TWI byte/ack received interrupt