Created
October 14, 2021 14:12
-
-
Save enjoy-digital/39fda912c94515fe87488d56057d3f68 to your computer and use it in GitHub Desktop.
KC705 PCA9548 + Si5324 (122.88MHz) I2C configuration
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| from collections import namedtuple | |
| from migen import * | |
| from litex.soc.interconnect import wishbone | |
| # I2C----------------------------------------------------------------------------------------------- | |
| class I2CClockGen(Module): | |
| def __init__(self, width): | |
| self.load = Signal(width) | |
| self.clk2x = Signal() | |
| cnt = Signal.like(self.load) | |
| self.comb += [ | |
| self.clk2x.eq(cnt == 0), | |
| ] | |
| self.sync += [ | |
| If(self.clk2x, | |
| cnt.eq(self.load), | |
| ).Else( | |
| cnt.eq(cnt - 1), | |
| ), | |
| ] | |
| class I2CMasterMachine(Module): | |
| def __init__(self, clock_width): | |
| self.scl_o = Signal(reset=1) | |
| self.sda_o = Signal(reset=1) | |
| self.sda_i = Signal() | |
| self.submodules.cg = CEInserter()(I2CClockGen(clock_width)) | |
| self.idle = Signal() | |
| self.start = Signal() | |
| self.stop = Signal() | |
| self.write = Signal() | |
| self.read = Signal() | |
| self.ack = Signal() | |
| self.data = Signal(8) | |
| ### | |
| busy = Signal() | |
| bits = Signal(4) | |
| fsm = CEInserter()(FSM("IDLE")) | |
| self.submodules += fsm | |
| fsm.act("IDLE", | |
| If(self.start, | |
| NextState("START0"), | |
| ).Elif(self.stop & self.start, | |
| NextState("RESTART0"), | |
| ).Elif(self.stop, | |
| NextState("STOP0"), | |
| ).Elif(self.write, | |
| NextValue(bits, 8), | |
| NextState("WRITE0"), | |
| ).Elif(self.read, | |
| NextValue(bits, 8), | |
| NextState("READ0"), | |
| ) | |
| ) | |
| fsm.act("START0", | |
| NextValue(self.scl_o, 1), | |
| NextState("START1")) | |
| fsm.act("START1", | |
| NextValue(self.sda_o, 0), | |
| NextState("IDLE")) | |
| fsm.act("RESTART0", | |
| NextValue(self.scl_o, 0), | |
| NextState("RESTART1")) | |
| fsm.act("RESTART1", | |
| NextValue(self.sda_o, 1), | |
| NextState("START0")) | |
| fsm.act("STOP0", | |
| NextValue(self.scl_o, 0), | |
| NextState("STOP1")) | |
| fsm.act("STOP1", | |
| NextValue(self.scl_o, 1), | |
| NextValue(self.sda_o, 0), | |
| NextState("STOP2")) | |
| fsm.act("STOP2", | |
| NextValue(self.sda_o, 1), | |
| NextState("IDLE")) | |
| fsm.act("WRITE0", | |
| NextValue(self.scl_o, 0), | |
| If(bits == 0, | |
| NextValue(self.sda_o, 1), | |
| NextState("READACK0"), | |
| ).Else( | |
| NextValue(self.sda_o, self.data[7]), | |
| NextState("WRITE1"), | |
| ) | |
| ) | |
| fsm.act("WRITE1", | |
| NextValue(self.scl_o, 1), | |
| NextValue(self.data[1:], self.data[:-1]), | |
| NextValue(bits, bits - 1), | |
| NextState("WRITE0"), | |
| ) | |
| fsm.act("READACK0", | |
| NextValue(self.scl_o, 1), | |
| NextState("READACK1"), | |
| ) | |
| fsm.act("READACK1", | |
| NextValue(self.ack, ~self.sda_i), | |
| NextState("IDLE") | |
| ) | |
| fsm.act("READ0", | |
| NextValue(self.scl_o, 0), | |
| NextValue(self.sda_o, 1), | |
| NextState("READ1"), | |
| ) | |
| fsm.act("READ1", | |
| NextValue(self.data[0], self.sda_i), | |
| NextValue(self.scl_o, 0), | |
| If(bits == 0, | |
| NextValue(self.sda_o, ~self.ack), | |
| NextState("WRITEACK0"), | |
| ).Else( | |
| NextValue(self.sda_o, 1), | |
| NextState("READ2"), | |
| ) | |
| ) | |
| fsm.act("READ2", | |
| NextValue(self.scl_o, 1), | |
| NextValue(self.data[1:], self.data[:-1]), | |
| NextValue(bits, bits - 1), | |
| NextState("READ1"), | |
| ) | |
| fsm.act("WRITEACK0", | |
| NextValue(self.scl_o, 1), | |
| NextState("IDLE"), | |
| ) | |
| run = Signal() | |
| self.comb += [ | |
| run.eq(self.start | self.stop | self.write | self.read), | |
| self.idle.eq(~run & fsm.ongoing("IDLE")), | |
| self.cg.ce.eq(~self.idle), | |
| fsm.ce.eq(run | self.cg.clk2x), | |
| ] | |
| # Registers: | |
| # config = Record([ | |
| # ("div", 20), | |
| # ]) | |
| # xfer = Record([ | |
| # ("data", 8), | |
| # ("ack", 1), | |
| # ("read", 1), | |
| # ("write", 1), | |
| # ("start", 1), | |
| # ("stop", 1), | |
| # ("idle", 1), | |
| # ]) | |
| class I2CMaster(Module): | |
| def __init__(self, pads, bus=None): | |
| if bus is None: | |
| bus = wishbone.Interface(data_width=32) | |
| self.bus = bus | |
| ### | |
| # Wishbone | |
| self.submodules.i2c = i2c = I2CMasterMachine( | |
| clock_width=20) | |
| self.sync += [ | |
| # read | |
| If(bus.adr[0], | |
| bus.dat_r.eq(i2c.cg.load), | |
| ).Else( | |
| bus.dat_r.eq(Cat(i2c.data, i2c.ack, C(0, 4), i2c.idle)), | |
| ), | |
| # write | |
| i2c.read.eq(0), | |
| i2c.write.eq(0), | |
| i2c.start.eq(0), | |
| i2c.stop.eq(0), | |
| bus.ack.eq(0), | |
| If(bus.cyc & bus.stb & ~bus.ack, | |
| bus.ack.eq(1), | |
| If(bus.we, | |
| If(bus.adr[0], | |
| i2c.cg.load.eq(bus.dat_w), | |
| ).Else( | |
| i2c.data.eq(bus.dat_w[0:8]), | |
| i2c.ack.eq(bus.dat_w[8]), | |
| i2c.read.eq(bus.dat_w[9]), | |
| i2c.write.eq(bus.dat_w[10]), | |
| i2c.start.eq(bus.dat_w[11]), | |
| i2c.stop.eq(bus.dat_w[12]), | |
| ) | |
| ) | |
| ) | |
| ] | |
| # I/O | |
| self.scl_t = TSTriple() | |
| self.specials += self.scl_t.get_tristate(pads.scl) | |
| self.comb += [ | |
| self.scl_t.oe.eq(~i2c.scl_o), | |
| self.scl_t.o.eq(0), | |
| ] | |
| self.sda_t = TSTriple() | |
| self.specials += self.sda_t.get_tristate(pads.sda) | |
| self.comb += [ | |
| self.sda_t.oe.eq(~i2c.sda_o), | |
| self.sda_t.o.eq(0), | |
| i2c.sda_i.eq(self.sda_t.i), | |
| ] | |
| I2C_XFER_ADDR, I2C_CONFIG_ADDR = range(2) | |
| ( | |
| I2C_ACK, | |
| I2C_READ, | |
| I2C_WRITE, | |
| I2C_START, | |
| I2C_STOP, | |
| I2C_IDLE, | |
| ) = (1 << i for i in range(8, 14)) | |
| # Sequencer----------------------------------------------------------------------------------------- | |
| # Instruction set: | |
| # <2> OP <1> ADDRESS <20> DATA_MASK | |
| # | |
| # OP=00: end program, ADDRESS=don't care, DATA_MASK=don't care | |
| # OP=01: write, ADDRESS=address, DATA_MASK=data | |
| # OP=10: wait until masked bits set, ADDRESS=address, DATA_MASK=mask | |
| InstEnd = namedtuple("InstEnd", "") | |
| InstWrite = namedtuple("InstWrite", "address data") | |
| InstWait = namedtuple("InstWait", "address mask") | |
| def encode(inst): | |
| address, data_mask = 0, 0 | |
| if isinstance(inst, InstEnd): | |
| opcode = 0b00 | |
| elif isinstance(inst, InstWrite): | |
| opcode = 0b01 | |
| address = inst.address | |
| data_mask = inst.data | |
| elif isinstance(inst, InstWait): | |
| opcode = 0b10 | |
| address = inst.address | |
| data_mask = inst.mask | |
| else: | |
| raise ValueError | |
| return (opcode << 21) | (address << 20) | data_mask | |
| class Sequencer(Module): | |
| def __init__(self, program, bus=None): | |
| if bus is None: | |
| bus = wishbone.Interface() | |
| self.bus = bus | |
| ### | |
| assert isinstance(program[-1], InstEnd) | |
| program_e = [encode(inst) for inst in program] | |
| mem = Memory(32, len(program), init=program_e) | |
| self.specials += mem | |
| mem_port = mem.get_port() | |
| self.specials += mem_port | |
| fsm = FSM(reset_state="FETCH") | |
| self.submodules += fsm | |
| i_opcode = mem_port.dat_r[21:23] | |
| i_address = mem_port.dat_r[20:21] | |
| i_data_mask = mem_port.dat_r[0:20] | |
| self.sync += [ | |
| self.bus.adr.eq(i_address), | |
| self.bus.sel.eq(1), | |
| self.bus.dat_w.eq(i_data_mask), | |
| ] | |
| fsm.act("FETCH", NextState("DECODE")) | |
| fsm.act("DECODE", | |
| If(i_opcode == 0b00, | |
| NextState("END") | |
| ).Elif(i_opcode == 0b01, | |
| NextState("WRITE") | |
| ).Elif(i_opcode == 0b10, | |
| NextState("WAIT") | |
| ) | |
| ) | |
| fsm.act("WRITE", | |
| self.bus.cyc.eq(1), | |
| self.bus.stb.eq(1), | |
| self.bus.we.eq(1), | |
| If(self.bus.ack, | |
| NextValue(mem_port.adr, mem_port.adr + 1), | |
| NextState("FETCH") | |
| ) | |
| ) | |
| fsm.act("WAIT", | |
| self.bus.cyc.eq(1), | |
| self.bus.stb.eq(1), | |
| If(self.bus.ack & ((self.bus.dat_r & i_data_mask) == i_data_mask), | |
| NextValue(mem_port.adr, mem_port.adr + 1), | |
| NextState("FETCH") | |
| ) | |
| ) | |
| fsm.act("END", NextState("END")) | |
| # SI5324 programs----------------------------------------------------------------------------------- | |
| def i2c_program_kc705(sys_clk_freq): | |
| i2c_sequence = [ | |
| # PCA9548: select channel 7 | |
| [(0x74 << 1), 1 << 7], | |
| # Si5324: configure (122.88MHz free run) | |
| [(0x68 << 1), 0, 0x54], | |
| [(0x68 << 1), 1, 0xe4], | |
| [(0x68 << 1), 2, 0x42], | |
| [(0x68 << 1), 3, 0x15], | |
| [(0x68 << 1), 4, 0x92], | |
| [(0x68 << 1), 5, 0xed], | |
| [(0x68 << 1), 6, 0x1b], | |
| [(0x68 << 1), 7, 0x2a], | |
| [(0x68 << 1), 8, 0x00], | |
| [(0x68 << 1), 9, 0xc0], | |
| [(0x68 << 1), 10, 0x08], | |
| [(0x68 << 1), 11, 0x40], | |
| [(0x68 << 1), 19, 0x29], | |
| [(0x68 << 1), 20, 0x3e], | |
| [(0x68 << 1), 21, 0xff], | |
| [(0x68 << 1), 22, 0xdf], | |
| [(0x68 << 1), 23, 0x1f], | |
| [(0x68 << 1), 24, 0x3f], | |
| [(0x68 << 1), 25, 0xc0], | |
| [(0x68 << 1), 31, 0x00], | |
| [(0x68 << 1), 32, 0x00], | |
| [(0x68 << 1), 33, 0x03], | |
| [(0x68 << 1), 34, 0x00], | |
| [(0x68 << 1), 35, 0x00], | |
| [(0x68 << 1), 36, 0x03], | |
| [(0x68 << 1), 40, 0xc0], | |
| [(0x68 << 1), 41, 0x6b], | |
| [(0x68 << 1), 42, 0xab], | |
| [(0x68 << 1), 43, 0x00], | |
| [(0x68 << 1), 44, 0x1a], | |
| [(0x68 << 1), 45, 0xea], | |
| [(0x68 << 1), 46, 0x00], | |
| [(0x68 << 1), 47, 0x19], | |
| [(0x68 << 1), 48, 0x08], | |
| [(0x68 << 1), 55, 0x00], | |
| [(0x68 << 1), 131, 0x1f], | |
| [(0x68 << 1), 132, 0x02], | |
| [(0x68 << 1), 137, 0x01], | |
| [(0x68 << 1), 138, 0x0f], | |
| [(0x68 << 1), 139, 0xff], | |
| [(0x68 << 1), 142, 0x00], | |
| [(0x68 << 1), 143, 0x00], | |
| [(0x68 << 1), 136, 0x40], | |
| ] | |
| program = [ | |
| InstWrite(I2C_CONFIG_ADDR, int(sys_clk_freq/1e3)), | |
| ] | |
| for subseq in i2c_sequence: | |
| program += [ | |
| InstWrite(I2C_XFER_ADDR, I2C_START), | |
| InstWait(I2C_XFER_ADDR, I2C_IDLE), | |
| ] | |
| for octet in subseq: | |
| program += [ | |
| InstWrite(I2C_XFER_ADDR, I2C_WRITE | octet), | |
| InstWait(I2C_XFER_ADDR, I2C_IDLE), | |
| ] | |
| program += [ | |
| InstWrite(I2C_XFER_ADDR, I2C_STOP), | |
| InstWait(I2C_XFER_ADDR, I2C_IDLE), | |
| ] | |
| program += [ | |
| InstEnd(), | |
| ] | |
| return program | |
| # SI5324-------------------------------------------------------------------------------------------- | |
| class SI5324(Module): | |
| def __init__(self, pads, program, sys_clk_freq): | |
| if hasattr(pads, "cs_n"): | |
| self.comb += pads.cs_n.eq(0) | |
| reset_val = int(sys_clk_freq/20e3) | |
| reset_ctr = Signal(max=reset_val+1, reset=reset_val) | |
| self.sync += \ | |
| If(reset_ctr != 0, | |
| reset_ctr.eq(reset_ctr - 1) | |
| ).Else( | |
| pads.rst_n.eq(1) | |
| ) | |
| i2c_master = I2CMaster(pads) | |
| sequencer = Sequencer(program(sys_clk_freq)) | |
| self.submodules += i2c_master, sequencer | |
| self.comb += sequencer.bus.connect(i2c_master.bus) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment