from amaranth import * from i2c import * from amaranth.lib.io import pin_layout from tests import BaseTestClass, provide_testcase_name __all__ = ["i2c_layout", "I2CBusSimulator", "TestHarness", "TestCSROperation"] class TestHarness(Elaboratable): def __init__(self): self.i2c = I2CBusSimulator() self.uut = I2C(10_000_000, 100_000, self.i2c.create_interface()) self.i2c_target = I2CTarget(self.i2c.create_interface()) def elaborate(self, platform): assert platform is None m = Module() m.submodules.i2c = self.i2c m.submodules.uut = self.uut m.submodules.i2c_target = self.i2c_target m.d.comb += self.i2c_target.address.eq(0xAA >> 1) return m class TestCSROperation(BaseTestClass): def setUp(self): self.harness = TestHarness() @provide_testcase_name def test_send_byte(self, test_name): def test(): yield Tick() self._run_test(test, test_name) i2c_layout = [ ("sda", pin_layout(1, "io")), ("scl", pin_layout(1, "io")), ] class I2CBusSimulator(Elaboratable): def __init__(self): self.interfaces = [] self.sda = Signal() self.scl = Signal() def elaborate(self, target): assert target is None, "This bus simulator should never be used in real hardware!" n = len(self.interfaces) m = Module() m.d.comb += self.sda.eq(1) m.d.comb += self.scl.eq(1) # TODO maybe output a bus contention signal? # First interfaces get priority over interfaces added after for i in reversed(range(n)): # Emulate bus drivers with m.If(self.interfaces[i].sda.oe): m.d.comb += self.sda.eq(self.interfaces[i].sda.o) with m.If(self.interfaces[i].scl.oe): m.d.comb += self.scl.eq(self.interfaces[i].scl.o) pass # Connect inputs to bus value m.d.comb += [ self.interfaces[i].sda.i.eq(self.sda), self.interfaces[i].scl.i.eq(self.scl), ] return m def create_interface(self) -> Record: new_interface = Record(i2c_layout) self.interfaces.append(new_interface) return new_interface