In theory, any piece of software could be built out of discrete pieces of hardware, provided there are enough transistors, passive components, and time available. In general, though, we’re much more likely to reach for a programmable computer or microcontroller for all but the simplest tasks for several reasons: cost, effort, complexity, economics, and sanity. [Igor Brichkov] was working with I2C and decided that he wanted to see just where this line between hardware and software should be by implementing this protocol itself directly with hardware.
One of the keys to “programming” a communications protocol in hardware is getting the timing right, the first part of which is initializing communications between this device and another on the bus. [Igor] is going to be building up the signal in parts and then ORing them together. The first part is a start condition, generated by one oscillator and a counter. This also creates a pause, at which point a second oscillator takes over and sends data out. The first data needed for I2C is an address, which is done with a shift register and a counter pre-set to send the correct bits out on the communications lines.
To build up the rest of the signal, including data from the rotary encoder [Igor] is using for his project, essentially sets of shift registers and counters are paired together to pass data out through the I2C communications lines in sequence. It could be thought of that the main loop of the hardware program is a counter, which steps through all the functions sequentially, sending out data from the shift registers one by one. We saw a similar project over a decade ago, but rather than automating the task of sending data on I2C it allowed the user to key in data manually instead.