I2C fault injection

When writing drivers for a new I2C host controller, it is important to not only get the normal operation working, but to implement robust error handling and recovery. This requires a way to force certain conditions that a “normal” I2C slave device will not exhibit.
There is a I2C fault injection/testing framework for Linux, which runs on devices with a supported I2C slave peripheral:
https://www.kernel.org/doc/html/latest/i2c/gpio-fault-injection.html
This provided some good ideas on what kinds of errors should be tested. However, I had no supported hardware at hand, and I wanted something small that can be easily attached to a board under development.
Looking for suitable I2C slave hardware, I settled on an ATmega88/328. Its I2C block handles the low-level protocol including clock stretching, and leaves enough of the higher-level protocol to the firmware to get decent control about what happens during a complete transaction. It has an address mask register which enables it to respond to multiple I2C addresses – this makes it easy to trigger different behaviours by accessing different target addresses.
The first prototype simply used an ATmega328 breakout board, but I also made a small PCB.
The firmware implements the following functions:
- responds to 16 consecutive addresses, default 0x60..0x6F
- write and read transfers of unlimited length
- write and read transfers of limited length (1..7) to test NACK handling
- bus lockout: force SDA low until a clock is seen on SCL (to test recovery procedures for stuck bus)
- Logging of transfer details to UART port (switchable, UART output slows down bus operation)
Full project details are available at
https://github.com/mschwingen/hardware/tree/master/i2c-slave