forked from M-Labs/artiq
1
0
Fork 0

Gateware: si549 & WRPLL

ddmtd: add DDMTD and deglitcher
wrpll: add helper clockdomain
wrpll: add frequency counter
wrpll: add skewtester
wrpll: add gtx & main tag collection
wrpll: add gtx & main tag eventmanager for interrupt
si549: add i2c and adpll programmer
This commit is contained in:
morgan 2024-05-24 10:54:13 +08:00 committed by Sébastien Bourdeauducq
parent 57780e36be
commit 1b0586e6a8
4 changed files with 569 additions and 0 deletions

View File

View File

@ -0,0 +1,119 @@
from migen import *
from migen.genlib.cdc import PulseSynchronizer, MultiReg
from misoc.interconnect.csr import *
class DDMTDSampler(Module):
def __init__(self, cd_ref, main_clk_se):
self.ref_beating = Signal()
self.main_beating = Signal()
# # #
ref_clk = Signal()
self.specials +=[
# ISERDESE2 can only be driven from fabric via IDELAYE2 (see UG471)
Instance("IDELAYE2",
p_DELAY_SRC="DATAIN",
p_HIGH_PERFORMANCE_MODE="TRUE",
p_REFCLK_FREQUENCY=208.3, # REFCLK frequency from IDELAYCTRL
p_IDELAY_VALUE=0,
i_DATAIN=cd_ref.clk,
o_DATAOUT=ref_clk
),
Instance("ISERDESE2",
p_IOBDELAY="IFD", # use DDLY as input
p_DATA_RATE="SDR",
p_DATA_WIDTH=2, # min is 2
p_NUM_CE=1,
i_DDLY=ref_clk,
i_CE1=1,
i_CLK=ClockSignal("helper"),
i_CLKDIV=ClockSignal("helper"),
o_Q1=self.ref_beating
),
Instance("ISERDESE2",
p_DATA_RATE="SDR",
p_DATA_WIDTH=2, # min is 2
p_NUM_CE=1,
i_D=main_clk_se,
i_CE1=1,
i_CLK=ClockSignal("helper"),
i_CLKDIV=ClockSignal("helper"),
o_Q1=self.main_beating,
),
]
class DDMTDDeglitcherMedianEdge(Module):
def __init__(self, counter, input_signal, stable_0_period=100, stable_1_period=100):
self.tag = Signal(len(counter))
self.detect = Signal()
stable_0_counter = Signal(reset=stable_0_period - 1, max=stable_0_period)
stable_1_counter = Signal(reset=stable_1_period - 1, max=stable_1_period)
# # #
# Based on CERN's median edge deglitcher FSM
# https://white-rabbit.web.cern.ch/documents/Precise_time_and_frequency_transfer_in_a_White_Rabbit_network.pdf (p.72)
fsm = ClockDomainsRenamer("helper")(FSM(reset_state="WAIT_STABLE_0"))
self.submodules += fsm
fsm.act("WAIT_STABLE_0",
If(stable_0_counter != 0,
NextValue(stable_0_counter, stable_0_counter - 1)
).Else(
NextValue(stable_0_counter, stable_0_period - 1),
NextState("WAIT_EDGE")
),
If(input_signal,
NextValue(stable_0_counter, stable_0_period - 1)
),
)
fsm.act("WAIT_EDGE",
If(input_signal,
NextValue(self.tag, counter),
NextState("GOT_EDGE")
)
)
fsm.act("GOT_EDGE",
If(stable_1_counter != 0,
NextValue(stable_1_counter, stable_1_counter - 1)
).Else(
NextValue(stable_1_counter, stable_1_period - 1),
self.detect.eq(1),
NextState("WAIT_STABLE_0")
),
If(~input_signal,
NextValue(self.tag, self.tag + 1),
NextValue(stable_1_counter, stable_1_period - 1)
),
)
class DDMTD(Module):
def __init__(self, counter, input_signal):
# in helper clock domain
self.h_tag = Signal(len(counter))
self.h_tag_update = Signal()
# # #
deglitcher = DDMTDDeglitcherMedianEdge(counter, input_signal)
self.submodules += deglitcher
self.sync.helper += [
self.h_tag_update.eq(0),
If(deglitcher.detect,
self.h_tag_update.eq(1),
self.h_tag.eq(deglitcher.tag)
)
]

View File

@ -0,0 +1,277 @@
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
fsm.act("IDLE",
If(self.stb,
NextValue(self.nack, 0),
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(
NextValue(self.nack, 1),
NextState("STOP")
)
)
)
fsm.act("DATA0",
master.data.eq(self.adpll[0:8]),
master.write.eq(1),
If(master.ready,
If(master.ack,
NextState("DATA1")
).Else(
NextValue(self.nack, 1),
NextState("STOP")
)
)
)
fsm.act("DATA1",
master.data.eq(self.adpll[8:16]),
master.write.eq(1),
If(master.ready,
If(master.ack,
NextState("DATA2")
).Else(
NextValue(self.nack, 1),
NextState("STOP")
)
)
)
fsm.act("DATA2",
master.data.eq(self.adpll[16:24]),
master.write.eq(1),
If(master.ready,
If(~master.ack, NextValue(self.nack, 1)),
NextState("STOP")
)
)
fsm.act("STOP",
master.stop.eq(1),
If(master.ready,
If(~master.ack, NextValue(self.nack, 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 = CSR()
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.sync += self.programmer.stb.eq(self.adpll_stb.re)
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.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),
)
]

View File

@ -0,0 +1,173 @@
from migen import *
from migen.genlib.cdc import MultiReg, AsyncResetSynchronizer, PulseSynchronizer
from misoc.interconnect.csr import *
from misoc.interconnect.csr_eventmanager import *
from artiq.gateware.wrpll.ddmtd import DDMTDSampler, DDMTD
from artiq.gateware.wrpll.si549 import Si549
class FrequencyCounter(Module, AutoCSR):
def __init__(self, domains, counter_width=24):
self.update = CSR()
self.busy = CSRStatus()
counter_reset = Signal()
counter_stb = Signal()
timer = Signal(counter_width)
# # #
fsm = FSM()
self.submodules += fsm
fsm.act("IDLE",
counter_reset.eq(1),
If(self.update.re,
NextValue(timer, 2**counter_width - 1),
NextState("COUNTING")
)
)
fsm.act("COUNTING",
self.busy.status.eq(1),
If(timer != 0,
NextValue(timer, timer - 1)
).Else(
counter_stb.eq(1),
NextState("IDLE")
)
)
for domain in domains:
name = "counter_" + domain
counter_csr = CSRStatus(counter_width, name=name)
setattr(self, name, counter_csr)
divider = Signal(2)
divided = Signal()
divided_sys = Signal()
divided_sys_r = Signal()
divided_tick = Signal()
counter = Signal(counter_width)
# # #
sync_domain = getattr(self.sync, domain)
sync_domain +=[
divider.eq(divider + 1),
divided.eq(divider[-1])
]
self.specials += MultiReg(divided, divided_sys)
self.sync += divided_sys_r.eq(divided_sys)
self.comb += divided_tick.eq(divided_sys & ~divided_sys_r)
self.sync += [
If(counter_stb, counter_csr.status.eq(counter)),
If(divided_tick, counter.eq(counter + 1)),
If(counter_reset, counter.eq(0))
]
class SkewTester(Module, AutoCSR):
def __init__(self, rx_synchronizer):
self.error = CSR()
# # #
# The RX synchronizer is tested for setup/hold violations by feeding it a
# toggling pattern and checking that the same toggling pattern comes out.
toggle_in = Signal()
self.sync.rtio_rx0 += toggle_in.eq(~toggle_in)
toggle_out = rx_synchronizer.resync(toggle_in)
toggle_out_expected = Signal()
self.sync += toggle_out_expected.eq(~toggle_out)
error = Signal()
self.sync += [
If(toggle_out != toggle_out_expected, error.eq(1)),
If(self.error.re, error.eq(0))
]
self.specials += MultiReg(error, self.error.w)
class WRPLL(Module, AutoCSR):
def __init__(self, platform, cd_ref, main_clk_se, COUNTER_BIT=32):
self.helper_reset = CSRStorage(reset=1)
self.ref_tag = CSRStatus(COUNTER_BIT)
self.main_tag = CSRStatus(COUNTER_BIT)
ddmtd_counter = Signal(COUNTER_BIT)
ref_tag_sys = Signal(COUNTER_BIT)
main_tag_sys = Signal(COUNTER_BIT)
ref_tag_stb_sys = Signal()
main_tag_stb_sys = Signal()
# # #
self.submodules.main_dcxo = Si549(platform.request("ddmtd_main_dcxo_i2c"))
self.submodules.helper_dcxo = Si549(platform.request("ddmtd_helper_dcxo_i2c"))
helper_dcxo_pads = platform.request("ddmtd_helper_clk")
self.clock_domains.cd_helper = ClockDomain()
self.specials += [
Instance("IBUFGDS",
i_I=helper_dcxo_pads.p, i_IB=helper_dcxo_pads.n,
o_O=self.cd_helper.clk),
AsyncResetSynchronizer(self.cd_helper, self.helper_reset.storage)
]
self.submodules.frequency_counter = FrequencyCounter(["sys", cd_ref.name])
self.submodules.ddmtd_sampler = DDMTDSampler(cd_ref, main_clk_se)
self.sync.helper += ddmtd_counter.eq(ddmtd_counter + 1)
self.submodules.ddmtd_ref = DDMTD(ddmtd_counter, self.ddmtd_sampler.ref_beating)
self.submodules.ddmtd_main = DDMTD(ddmtd_counter, self.ddmtd_sampler.main_beating)
# DDMTD tags collection
self.specials += [
MultiReg(self.ddmtd_ref.h_tag, ref_tag_sys),
MultiReg(self.ddmtd_main.h_tag, main_tag_sys)
]
ref_tag_stb_ps = PulseSynchronizer("helper", "sys")
main_tag_stb_ps = PulseSynchronizer("helper", "sys")
self.submodules += [
ref_tag_stb_ps,
main_tag_stb_ps
]
self.sync.helper += [
ref_tag_stb_ps.i.eq(self.ddmtd_ref.h_tag_update),
main_tag_stb_ps.i.eq(self.ddmtd_main.h_tag_update)
]
self.sync += [
ref_tag_stb_sys.eq(ref_tag_stb_ps.o),
main_tag_stb_sys.eq(main_tag_stb_ps.o)
]
self.sync += [
If(ref_tag_stb_sys,
self.ref_tag.status.eq(ref_tag_sys),
),
If(main_tag_stb_sys,
self.main_tag.status.eq(main_tag_sys)
)
]
# EventMangers for firmware interrupt
self.submodules.ref_tag_ev = EventManager()
self.ref_tag_ev.stb = EventSourcePulse()
self.ref_tag_ev.finalize()
self.submodules.main_tag_ev = EventManager()
self.main_tag_ev.stb = EventSourcePulse()
self.main_tag_ev.finalize()
self.sync += [
self.ref_tag_ev.stb.trigger.eq(ref_tag_stb_sys),
self.main_tag_ev.stb.trigger.eq(main_tag_stb_sys)
]
self.submodules.ev = SharedIRQ(self.ref_tag_ev, self.main_tag_ev)