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 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