diff --git a/lambdalib/cores/i2c/stream.py b/lambdalib/cores/i2c/stream.py index cd196bd..5da1325 100644 --- a/lambdalib/cores/i2c/stream.py +++ b/lambdalib/cores/i2c/stream.py @@ -230,11 +230,12 @@ def elaborate(self, platform): class I2CRegStream(Elaboratable): - """Converts addr/val stream into an I2C stream. - Currently only supports 8bit reg addresses and values.""" + """Converts addr/val stream into an I2C stream.""" def __init__(self, i2c_addr, addr_width=8, val_width=8): - assert addr_width == 8 - assert val_width == 8 + if addr_width % 8 != 0 and addr_width > 0: + raise ValueError("addr_width must be a multiple of 8 bits") + if val_width % 8 != 0 and val_width > 0: + raise ValueError("val_width must be a multiple of 8 bits") self._i2c_addr = i2c_addr self._addr_width = addr_width @@ -252,8 +253,9 @@ def elaborate(self, platform): m = Module() # Latch addr/val - addr_d = Signal(self._addr_width) - val_d = Signal(self._val_width) + addr_latch = Signal(self._addr_width) + val_latch = Signal(self._val_width) + byte_pos = Signal(range(max(self._addr_width // 8, self._val_width // 8))) with m.FSM(): with m.State("IDLE"): @@ -262,30 +264,49 @@ def elaborate(self, platform): m.d.sync += [ self.source.valid.eq(1), self.source.data.eq(self._i2c_addr << 1), - addr_d.eq(self.sink.addr), - val_d.eq(self.sink.val), + addr_latch.eq(self.sink.addr), + val_latch.eq(self.sink.val), + byte_pos.eq(self._addr_width // 8 - 1), ] m.next = "PUSH-SLAVE-ADDR" with m.State("PUSH-SLAVE-ADDR"): with m.If(self.source.ready): - m.d.sync += self.source.data.eq(addr_d) + m.d.sync += [ + self.source.data.eq(addr_latch.word_select(byte_pos.as_unsigned(), 8)), + ] m.next = "PUSH-REG-ADDR" with m.State("PUSH-REG-ADDR"): with m.If(self.source.ready): - m.d.sync += [ - self.source.data.eq(val_d), - self.source.last.eq(1), - ] - m.next = "PUSH-REG-VAL" + with m.If(byte_pos > 0): + m.d.sync += [ + self.source.data.eq(addr_latch.word_select((byte_pos - 1).as_unsigned(), 8)), + byte_pos.eq(byte_pos - 1), + ] + with m.Else(): + m.d.sync += [ + self.source.data.eq(val_latch[-8:]), + byte_pos.eq(self._val_width // 8 - 1), + ] + if self._val_width == 8: + m.d.sync += self.source.last.eq(1) + m.next = "PUSH-REG-VAL" with m.State("PUSH-REG-VAL"): with m.If(self.source.ready): - m.d.sync += [ - self.source.valid.eq(0), - self.source.last.eq(0), - ] - m.next = "IDLE" + with m.If(byte_pos == 0): + m.d.sync += [ + self.source.valid.eq(0), + self.source.last.eq(0), + ] + m.next = "IDLE" + with m.Else(): + m.d.sync += [ + self.source.data.eq(val_latch.word_select((byte_pos - 1).as_unsigned(), 8)), + byte_pos.eq(byte_pos - 1), + ] + with m.If(byte_pos == 1): + m.d.sync += self.source.last.eq(1) return m diff --git a/lambdalib/tests/test_cores_i2c.py b/lambdalib/tests/test_cores_i2c.py index 88aaed3..4aee245 100644 --- a/lambdalib/tests/test_cores_i2c.py +++ b/lambdalib/tests/test_cores_i2c.py @@ -103,7 +103,7 @@ def test_i2c_proto(): sim.run() -def test_i2c_reg_stream(): +def test_i2c_reg_stream_8bit(): dut = I2CRegStream(0x2C, 8, 8) sim = Simulator(dut) @@ -130,7 +130,44 @@ def test_i2c_reg_stream(): sim.add_clock(1e-6) sim.add_sync_process(sender.sync_process) sim.add_sync_process(receiver.sync_process) - with sim.write_vcd("tests/test_i2c_reg_command_generator.vcd"): + with sim.write_vcd("tests/test_i2c_reg_stream_8bit.vcd"): + sim.run() + + assert receiver.data["data"] == out_stream + + +def test_i2c_reg_stream_16bit(): + dut = I2CRegStream(0x2C, 16, 16) + sim = Simulator(dut) + + datas = { + "addr": [0xABCD, 0xEF01], + "val": [0x5A10, 0xA5BF], + } + + out_stream = [ + 0x2C << 1, + 0xAB, + 0xCD, + 0x5A, + 0x10, + + 0x2C << 1, + 0xEF, + 0x01, + 0xA5, + 0xBF, + ] + + sender = StreamSimSender(dut.sink, datas, speed=0.9, + verbose=True, strname="sender") + receiver = StreamSimReceiver(dut.source, length=len(out_stream), speed=1, + verbose=True, strname="receiver") + + sim.add_clock(1e-6) + sim.add_sync_process(sender.sync_process) + sim.add_sync_process(receiver.sync_process) + with sim.write_vcd("tests/test_i2c_reg_stream_16bit.vcd"): sim.run() assert receiver.data["data"] == out_stream @@ -140,4 +177,5 @@ def test_i2c_reg_stream(): test_i2c_stream_writer() test_i2c_stream() test_i2c_proto() - test_i2c_reg_stream() + test_i2c_reg_stream_8bit() + test_i2c_reg_stream_16bit()