diff --git a/src/gateware/kasli_soc.py b/src/gateware/kasli_soc.py index 25e9c04..ff1f268 100755 --- a/src/gateware/kasli_soc.py +++ b/src/gateware/kasli_soc.py @@ -26,6 +26,7 @@ import analyzer import acpki import drtio_aux_controller import zynq_clocking +import si549 from config import write_csr_file, write_mem_file, write_rustc_cfg_file eem_iostandard_dict = { @@ -105,7 +106,7 @@ class GTPBootstrapClock(Module): class GenericStandalone(SoCCore): - def __init__(self, description, acpki=False): + def __init__(self, description, acpki=False, with_wrpll=False): self.acpki = acpki clk_freq = description["rtio_frequency"] @@ -205,7 +206,7 @@ class GenericStandalone(SoCCore): class GenericMaster(SoCCore): - def __init__(self, description, acpki=False): + def __init__(self, description, acpki=False, with_wrpll=False): clk_freq = description["rtio_frequency"] has_drtio_over_eem = any(peripheral["type"] == "shuttler" for peripheral in description["peripherals"]) @@ -398,7 +399,7 @@ class GenericMaster(SoCCore): class GenericSatellite(SoCCore): - def __init__(self, description, acpki=False): + def __init__(self, description, acpki=False, with_wrpll=False): clk_freq = description["rtio_frequency"] self.acpki = acpki @@ -551,14 +552,19 @@ class GenericSatellite(SoCCore): self.config["RTIO_FREQUENCY"] = str(clk_freq/1e6) self.config["CLOCK_FREQUENCY"] = int(clk_freq) - self.submodules.siphaser = SiPhaser7Series( - si5324_clkin=platform.request("cdr_clk"), - rx_synchronizer=self.rx_synchronizer, - ultrascale=False, - rtio_clk_freq=self.gt_drtio.rtio_clk_freq) - self.csr_devices.append("siphaser") - self.config["HAS_SI5324"] = None - self.config["SI5324_SOFT_RESET"] = None + if with_wrpll: + self.submodules.main_dcxo = si549.Si549(platform.request("ddmtd_main_dcxo_i2c")) + self.submodules.helper_dcxo = si549.Si549(platform.request("ddmtd_helper_dcxo_i2c")) + self.config["HAS_SI549"] = None + else: + self.submodules.siphaser = SiPhaser7Series( + si5324_clkin=platform.request("cdr_clk"), + rx_synchronizer=self.rx_synchronizer, + ultrascale=False, + rtio_clk_freq=self.gt_drtio.rtio_clk_freq) + self.csr_devices.append("siphaser") + self.config["HAS_SI5324"] = None + self.config["SI5324_SOFT_RESET"] = None gtx0 = self.gt_drtio.gtxs[0] platform.add_false_path_constraints( @@ -588,6 +594,8 @@ def main(): help="build gateware into the specified directory") parser.add_argument("--acpki", default=False, action="store_true", help="enable ACPKI") + parser.add_argument("--with-wrpll", default=False, action="store_true", + help="enable WRPLL") parser.add_argument("description", metavar="DESCRIPTION", help="JSON system description file") args = parser.parse_args() @@ -605,7 +613,7 @@ def main(): else: raise ValueError("Invalid DRTIO role") - soc = cls(description, acpki=args.acpki) + soc = cls(description, acpki=args.acpki, with_wrpll=args.with_wrpll) soc.finalize() if args.r is not None: diff --git a/src/gateware/si549.py b/src/gateware/si549.py new file mode 100644 index 0000000..d98a327 --- /dev/null +++ b/src/gateware/si549.py @@ -0,0 +1,278 @@ +from migen import * +from migen.genlib.fsm import * + +from misoc.interconnect.csr import * + + +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 = Signal(reset=1) + self.sda_o = Signal(reset=1) + self.sda_i = Signal() + + self.submodules.cg = CEInserter()(I2CClockGen(clock_width)) + self.start = Signal() + self.stop = Signal() + self.write = Signal() + self.ack = Signal() + self.data = Signal(8) + self.ready = Signal() + + # # # + + bits = Signal(4) + data = Signal(8) + + fsm = CEInserter()(FSM("IDLE")) + self.submodules += fsm + + fsm.act("IDLE", + self.ready.eq(1), + If(self.start, + NextState("START0"), + ).Elif(self.stop, + NextState("STOP0"), + ).Elif(self.write, + NextValue(bits, 8), + NextValue(data, self.data), + NextState("WRITE0") + ) + ) + + fsm.act("START0", + NextValue(self.scl, 1), + NextState("START1") + ) + fsm.act("START1", + NextValue(self.sda_o, 0), + NextState("IDLE") + ) + + fsm.act("STOP0", + NextValue(self.scl, 0), + NextState("STOP1") + ) + fsm.act("STOP1", + NextValue(self.sda_o, 0), + NextState("STOP2") + ) + fsm.act("STOP2", + NextValue(self.scl, 1), + NextState("STOP3") + ) + fsm.act("STOP3", + NextValue(self.sda_o, 1), + NextState("IDLE") + ) + + fsm.act("WRITE0", + NextValue(self.scl, 0), + NextState("WRITE1") + ) + fsm.act("WRITE1", + If(bits == 0, + NextValue(self.sda_o, 1), + NextState("READACK0"), + ).Else( + NextValue(self.sda_o, data[7]), + NextState("WRITE2"), + ) + ) + fsm.act("WRITE2", + NextValue(self.scl, 1), + NextValue(data[1:], data[:-1]), + NextValue(bits, bits - 1), + NextState("WRITE0"), + ) + fsm.act("READACK0", + NextValue(self.scl, 1), + NextState("READACK1"), + ) + fsm.act("READACK1", + NextValue(self.ack, ~self.sda_i), + NextState("IDLE") + ) + + run = Signal() + idle = Signal() + self.comb += [ + run.eq((self.start | self.stop | self.write) & self.ready), + idle.eq(~run & fsm.ongoing("IDLE")), + self.cg.ce.eq(~idle), + fsm.ce.eq(run | self.cg.clk2x), + ] + + +class ADPLLProgrammer(Module): + def __init__(self): + self.i2c_divider = Signal(16) + self.i2c_address = Signal(7) + + self.adpll = Signal(24) + self.stb = Signal() + self.busy = Signal() + self.nack = Signal() + + self.scl = Signal() + self.sda_i = Signal() + self.sda_o = Signal() + + # # # + + master = I2CMasterMachine(16) + self.submodules += master + + self.comb += [ + master.cg.load.eq(self.i2c_divider), + self.scl.eq(master.scl), + master.sda_i.eq(self.sda_i), + self.sda_o.eq(master.sda_o) + ] + + fsm = FSM() + self.submodules += fsm + + adpll = Signal.like(self.adpll) + + fsm.act("IDLE", + If(self.stb, + NextValue(adpll, self.adpll), + NextState("START") + ) + ) + fsm.act("START", + master.start.eq(1), + If(master.ready, NextState("DEVADDRESS")) + ) + fsm.act("DEVADDRESS", + master.data.eq(self.i2c_address << 1), + master.write.eq(1), + If(master.ready, NextState("REGADRESS")) + ) + fsm.act("REGADRESS", + master.data.eq(231), + master.write.eq(1), + If(master.ready, + If(master.ack, + NextState("DATA0") + ).Else( + self.nack.eq(1), + NextState("STOP") + ) + ) + ) + fsm.act("DATA0", + master.data.eq(adpll[0:8]), + master.write.eq(1), + If(master.ready, + If(master.ack, + NextState("DATA1") + ).Else( + self.nack.eq(1), + NextState("STOP") + ) + ) + ) + fsm.act("DATA1", + master.data.eq(adpll[8:16]), + master.write.eq(1), + If(master.ready, + If(master.ack, + NextState("DATA2") + ).Else( + self.nack.eq(1), + NextState("STOP") + ) + ) + ) + fsm.act("DATA2", + master.data.eq(adpll[16:24]), + master.write.eq(1), + If(master.ready, + If(~master.ack, self.nack.eq(1)), + NextState("STOP") + ) + ) + fsm.act("STOP", + master.stop.eq(1), + If(master.ready, + If(~master.ack, self.nack.eq(1)), + NextState("IDLE") + ) + ) + + self.comb += self.busy.eq(~fsm.ongoing("IDLE")) + + +class Si549(Module, AutoCSR): + def __init__(self, pads): + self.i2c_divider = CSRStorage(16, reset=75) + self.i2c_address = CSRStorage(7) + + self.adpll = CSRStorage(24) + self.adpll_stb = CSRStorage() + self.adpll_busy = CSRStatus() + self.nack = CSRStatus() + + self.bitbang_enable = CSRStorage() + + self.sda_oe = CSRStorage() + self.sda_out = CSRStorage() + self.sda_in = CSRStatus() + self.scl_oe = CSRStorage() + self.scl_out = CSRStorage() + + # # # + + self.submodules.programmer = ADPLLProgrammer() + + self.comb += [ + self.programmer.i2c_divider.eq(self.i2c_divider.storage), + self.programmer.i2c_address.eq(self.i2c_address.storage), + self.programmer.adpll.eq(self.adpll.storage), + self.programmer.stb.eq(self.adpll_stb.storage), + self.adpll_busy.status.eq(self.programmer.busy), + self.nack.status.eq(self.programmer.nack) + ] + + # I2C with bitbang/gateware mode select + sda_t = TSTriple(1) + scl_t = TSTriple(1) + self.specials += [ + sda_t.get_tristate(pads.sda), + scl_t.get_tristate(pads.scl) + ] + + self.comb += [ + If(self.bitbang_enable.storage, + sda_t.oe.eq(self.sda_oe.storage), + sda_t.o.eq(self.sda_out.storage), + self.sda_in.status.eq(sda_t.i), + scl_t.oe.eq(self.scl_oe.storage), + scl_t.o.eq(self.scl_out.storage) + ).Else( + sda_t.oe.eq(~self.programmer.sda_o), + sda_t.o.eq(0), + self.programmer.sda_i.eq(sda_t.i), + scl_t.oe.eq(~self.programmer.scl), + scl_t.o.eq(0), + ) + ] \ No newline at end of file