forked from M-Labs/artiq
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:
parent
57780e36be
commit
1b0586e6a8
|
@ -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)
|
||||
)
|
||||
]
|
|
@ -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),
|
||||
)
|
||||
]
|
|
@ -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)
|
Loading…
Reference in New Issue