gateware: use LTC2195 dco clk as PL only clock source

This commit is contained in:
linuswck 2025-01-10 12:38:02 +08:00
parent d9d44fa9e8
commit 0ca6ac1354
6 changed files with 98 additions and 192 deletions

View File

@ -22,93 +22,8 @@ from migen.genlib.cdc import MultiReg
from misoc.interconnect.csr import AutoCSR, CSRStatus, CSRStorage
from misoc.interconnect.stream import AsyncFIFO
class CRG(Module):
def __init__(self, platform, dco_clk, dco_freq=200e6):
self.clock_domains.cd_dco = ClockDomain()
self.clock_domains.cd_dco2x = ClockDomain()
self.clock_domains.cd_dco2d = ClockDomain()
self.clock_domains.cd_dco2d_45_degree = ClockDomain()
dco_clk_p, dco_clk_n = dco_clk
dco_clk_buf = Signal()
self.specials += Instance(
"IBUFGDS", i_I=dco_clk_p, i_IB=dco_clk_n, o_O=dco_clk_buf
)
# # #
clk_feedback = Signal()
clk_feedback_buf = Signal()
clk_dco = Signal()
clk_dco2x = Signal()
clk_dco2d = Signal()
clk_dco2d_45_degree = Signal()
mmcm_ps_psdone = Signal()
self.locked = Signal()
self.mmcm_rst = Signal()
self.ddr_clk_phase_shift_en = Signal()
self.ddr_clk_phase_incdec = Signal()
platform.add_period_constraint(dco_clk_p, 1e9 / dco_freq)
self.specials += [
Instance(
"MMCME2_ADV",
p_BANDWIDTH="OPTIMIZED",
p_DIVCLK_DIVIDE=1,
p_CLKFBOUT_PHASE=0.0,
p_CLKFBOUT_MULT_F=4, # VCO @ 800 MHz
p_CLKIN1_PERIOD=(1e9 / dco_freq),
p_REF_JITTER1=0.01,
p_STARTUP_WAIT="FALSE",
i_CLKIN1=dco_clk_buf,
i_PWRDWN=0,
i_RST=ResetSignal("sys") | self.mmcm_rst,
i_CLKFBIN=clk_feedback_buf,
o_CLKFBOUT=clk_feedback,
p_CLKOUT0_USE_FINE_PS="TRUE",
p_CLKOUT0_DIVIDE_F=8,
p_CLKOUT0_PHASE=45.0,
p_CLKOUT0_DUTY_CYCLE=0.5,
o_CLKOUT0=clk_dco2d_45_degree, # 100 MHz <- dco_clk / 2 = 200 MHz / 2
o_LOCKED=self.locked,
p_CLKOUT1_DIVIDE=2,
p_CLKOUT1_PHASE=0.0,
p_CLKOUT1_DUTY_CYCLE=0.5,
o_CLKOUT1=clk_dco2x, # 400 MHZ <- 2 * dco_clk = 2*200 MHz
p_CLKOUT2_DIVIDE=8,
p_CLKOUT2_PHASE=0.0,
p_CLKOUT2_DUTY_CYCLE=0.5,
o_CLKOUT2=clk_dco2d, # 100 MHz <- dco_clk / 2 = 200 MHz / 2
p_CLKOUT3_DIVIDE=4,
p_CLKOUT3_PHASE=0.0,
p_CLKOUT3_DUTY_CYCLE=0.5,
o_CLKOUT3=clk_dco, # 200 MHz <- dco_clk
i_PSCLK=ClockSignal(),
i_PSEN=self.ddr_clk_phase_shift_en,
i_PSINCDEC=self.ddr_clk_phase_incdec,
o_PSDONE=mmcm_ps_psdone,
)
]
self.specials += Instance("BUFG", i_I=clk_feedback, o_O=clk_feedback_buf)
self.specials += Instance("BUFG", i_I=clk_dco, o_O=self.cd_dco.clk)
self.specials += Instance("BUFG", i_I=clk_dco2d, o_O=self.cd_dco2d.clk)
self.specials += Instance("BUFG", i_I=clk_dco2d_45_degree, o_O=self.cd_dco2d_45_degree.clk)
self.specials += Instance("BUFG", i_I=clk_dco2x, o_O=self.cd_dco2x.clk)
# Ignore dco2d to mmcm dco_clk path created by SoC's rst.
platform.add_false_path_constraints(self.cd_dco2d.clk, dco_clk_buf)
self.specials += Instance("FD", p_INIT=1, i_D=~self.locked, i_C=self.cd_dco2d.clk, o_Q=self.cd_dco2d.rst)
class ADC(Module, AutoCSR):
def __init__(self, platform, dco_freq=200e6):
def __init__(self, platform):
adc_pads = platform.request("adc")
afe_pads = platform.request("adc_afe")
@ -131,45 +46,27 @@ class ADC(Module, AutoCSR):
ch2_shdn = Signal()
self.data_out = [Signal(16, reset_less=True), Signal(16, reset_less=True)]
self.data_out_cdc = [Signal(16, reset_less=True), Signal(16, reset_less=True)]
self.s_frame = Signal(4)
self.s_frame_cdc = Signal(4)
self.submodules.cdc_fifo = ClockDomainsRenamer({"write": "dco2d", "read": "sys"})(AsyncFIFO([("data", 36)], 4))
self.comb += [
self.cdc_fifo.sink.data.eq(Cat(self.data_out_cdc[0], self.data_out_cdc[1], self.s_frame_cdc)),
self.cdc_fifo.sink.stb.eq(~ResetSignal("dco2d")),
Cat(self.data_out[0], self.data_out[1], self.s_frame).eq(self.cdc_fifo.source.data),
self.cdc_fifo.source.ack.eq(~ResetSignal("sys")),
]
###
# DCO clock coming from LTC2195
# dco_clk = Record([("p", 1), ("n", 1)])
dco_clk =(adc_pads.dco_p, adc_pads.dco_n)
self.comb += [
# dco_clk.p.eq(adc_pads.dco_p),
# dco_clk.n.eq(adc_pads.dco_n),
tap_delay_val.eq(self.tap_delay.storage),
Cat(ch1_gain_x10, ch2_gain_x10, ch1_shdn, ch2_shdn).eq(
self.afe_ctrl.storage[0:4]
),
]
self.submodules.crg = CRG(platform, dco_clk, dco_freq)
self.comb += self.afe_ctrl.storage[4].eq(self.crg.mmcm_rst)
self.comb += self.afe_ctrl.storage[5].eq(self.crg.ddr_clk_phase_shift_en)
self.comb += self.afe_ctrl.storage[6].eq(self.crg.ddr_clk_phase_incdec)
# self.comb += self.afe_ctrl.storage[4].eq(self.crg.mmcm_rst)
# self.comb += self.afe_ctrl.storage[5].eq(self.crg.ddr_clk_phase_shift_en)
# self.comb += self.afe_ctrl.storage[6].eq(self.crg.ddr_clk_phase_incdec)
self.specials += MultiReg(self.bitslip_csr.re, bitslip_re_dco_2d, "dco2d")
self.sync.dco2d += [
self.specials += MultiReg(self.bitslip_csr.re, bitslip_re_dco_2d, "sys")
self.sync.sys += [
bitslip.eq(Mux(bitslip_re_dco_2d, self.bitslip_csr.storage, 0))
]
self.comb += [
self.frame_csr.status[0:4].eq(self.s_frame[0:4]),
self.frame_csr.status[4].eq(self.crg.locked),
# self.frame_csr.status[4].eq(self.crg.locked),
self.data_ch0.status.eq(self.data_out[0]),
self.data_ch1.status.eq(self.data_out[1]),
]
@ -186,10 +83,10 @@ class ADC(Module, AutoCSR):
self.specials += Instance(
"LTC2195",
i_rst_in=ResetSignal("dco2d"),
i_rst_in=ResetSignal("sys"),
i_clk200=ClockSignal("idelay"),
i_DCO=ClockSignal("dco"),
i_DCO_2D=ClockSignal("dco2d"),
i_DCO=ClockSignal("sys_double"),
i_DCO_2D=ClockSignal("sys"),
i_FR_in_p=adc_pads.frame_p,
i_FR_in_n=adc_pads.frame_n,
i_D0_in_p=adc_pads.data0_p,
@ -198,9 +95,9 @@ class ADC(Module, AutoCSR):
i_D1_in_n=adc_pads.data1_n,
i_bitslip=bitslip,
i_delay_val=tap_delay_val,
o_ADC0_out=self.data_out_cdc[1], # LANES swapped on hardware
o_ADC1_out=self.data_out_cdc[0],
o_FR_out=self.s_frame_cdc,
o_ADC0_out=self.data_out[1], # LANES swapped on hardware
o_ADC1_out=self.data_out[0],
o_FR_out=self.s_frame,
o_o_data_from_pins=dummy,
o_idelay_rdy=dummy_idelay_rdy,
)

View File

@ -24,10 +24,13 @@ from misoc.interconnect.stream import AsyncFIFO
class DAC(Module, AutoCSR):
def __init__(self, platform):
def __init__(self, platform, phase_shift_ctrl):
dac_pads = platform.request("dac")
dac_afe_pads = platform.request("dac_afe")
self.dac_ctrl = CSRStorage(3)
phase_shift_en, phase_shift_dir = phase_shift_ctrl
self.dac_ctrl = CSRStorage(5)
self.output_value_ch0 = CSRStorage(14)
self.output_value_ch1 = CSRStorage(14)
@ -41,45 +44,36 @@ class DAC(Module, AutoCSR):
self.data_in = [Signal(14, reset_less=True), Signal(14, reset_less=True)]
self.data_in_csr = [Signal(14, reset_less=True), Signal(14, reset_less=True)]
self.data_in_cdc = [Signal(14, reset_less=True), Signal(14, reset_less=True)]
self.data_in_csr_cdc = [Signal(14, reset_less=True), Signal(14, reset_less=True)]
platform.add_period_constraint(dac_pads.dclkio, 10.0)
self.submodules.cdc_fifo = ClockDomainsRenamer({"write": "sys", "read": "dco2d"})(AsyncFIFO([("data", 56)], 4))
self.comb += [
self.data_in_csr[0].eq(self.output_value_ch0.storage),
self.data_in_csr[1].eq(self.output_value_ch1.storage),
self.cdc_fifo.sink.data.eq(Cat(self.data_in[0], self.data_in[1], self.data_in_csr[0], self.data_in_csr[1])),
self.cdc_fifo.sink.stb.eq(~ResetSignal("sys")),
Cat(self.data_in_cdc[0], self.data_in_cdc[1], self.data_in_csr_cdc[0], self.data_in_csr_cdc[1]).eq(self.cdc_fifo.source.data),
self.cdc_fifo.source.ack.eq(~ResetSignal("dco2d")),
]
self.comb += [
Cat(manual_override, ch0_pd, ch1_pd).eq(self.dac_ctrl.storage),
dac_pads.rst.eq(ResetSignal("dco2d")),
Cat(manual_override, ch0_pd, ch1_pd, phase_shift_en, phase_shift_dir).eq(self.dac_ctrl.storage),
dac_pads.rst.eq(ResetSignal("sys")),
dac_afe_pads.ch1_pd_n.eq(~ch0_pd),
dac_afe_pads.ch2_pd_n.eq(~ch1_pd),
output_data_ch0.eq(
Mux(manual_override, self.data_in_csr_cdc[0], self.data_in_cdc[0])
Mux(manual_override, self.data_in_csr[0], self.data_in[0])
),
output_data_ch1.eq(
Mux(manual_override, self.data_in_csr_cdc[1], self.data_in_cdc[1])
Mux(manual_override, self.data_in_csr[1], self.data_in[1])
),
]
self.specials += [
Instance("ODDR",
i_C=ClockSignal("dco2d"),
i_CE=~ResetSignal("dco2d"),
i_C=ClockSignal("sys"),
i_CE=~ResetSignal("sys"),
i_D1=output_data_ch0[lane], # DDR CLK Rising Edge
i_D2=output_data_ch1[lane], # DDR CLK Falling Edge
o_Q=dac_pads.data[lane],
p_DDR_CLK_EDGE="SAME_EDGE")
for lane in range(14)]
self.specials += Instance("ODDR",
i_C=ClockSignal("dco2d_45_degree"),
i_CE=~ResetSignal("dco2d"),
i_C=ClockSignal("sys_45_degree"),
i_CE=~ResetSignal("sys"),
i_D1=0,
i_D2=1,
o_Q=dac_pads.dclkio,

View File

@ -80,11 +80,13 @@ _io = [
Subsignal("data0_n", Pins("E7 B6 E3 C1"), IOStandard("LVDS_25")),
Subsignal("data1_p", Pins("A2 D5 F2 D7"), IOStandard("LVDS_25")),
Subsignal("data1_n", Pins("A1 C4 F1 D6"), IOStandard("LVDS_25")),
Subsignal("dco_p", Pins("B4"), IOStandard("LVDS_25")),
Subsignal("dco_n", Pins("B3"), IOStandard("LVDS_25")),
Subsignal("frame_p", Pins("B2"), IOStandard("LVDS_25")),
Subsignal("frame_n", Pins("B1"), IOStandard("LVDS_25"))
),
("adc_dco_clk", 0 ,
Subsignal("p", Pins("B4"), IOStandard("LVDS_25")),
Subsignal("n", Pins("B3"), IOStandard("LVDS_25")),
),
# ADC AFE
("adc_afe", 0,
@ -315,21 +317,23 @@ ps7_config_board_preset = {
}
class Platform(XilinxPlatform):
default_clk_name = "clk100"
default_clk_period = 10.0
default_clk_name = "adc_dco_clk_p"
default_clk_period = 5.0
def __init__(self):
XilinxPlatform.__init__(self, "xc7z015-clg485-1", _io, _connector_gpio + _connector_eem, toolchain="vivado")
ps7_config = ps7_config_board_preset
self.ps7_config = ps7_config
self.toolchain.with_phys_opt = True
verilog_sources = os.listdir(verilog_dir)
self.add_sources(verilog_dir, *verilog_sources)
def do_finalize(self, fragment):
try:
XilinxPlatform.do_finalize(self, fragment)
self.add_period_constraint(self.lookup_request(self.default_clk_name, loose=True), self.default_clk_period)
self.add_period_constraint(self.lookup_request(self.default_clk_name), self.default_clk_period)
except ValueError:
pass
except ConstraintError:

View File

@ -31,72 +31,91 @@ from fast_servo.gateware.cores.spi_phy import SpiInterface, SpiPhy
class CRG(Module):
def __init__(self, platform):
def __init__(self, platform, dco_freq=200e6):
self.ps_rst = Signal()
self.locked = Signal()
dco_clk = platform.request("adc_dco_clk")
dco_clk_buf = Signal()
self.specials += Instance(
"IBUFGDS", i_I=dco_clk.p, i_IB=dco_clk.n, o_O=dco_clk_buf
)
self.clock_domains.cd_sys = ClockDomain()
self.clock_domains.cd_sys_45_degree = ClockDomain()
self.clock_domains.cd_sys_double = ClockDomain()
self.clock_domains.cd_idelay = ClockDomain()
# # #
# Clk.
clk100 = platform.request("clk100")
platform.add_period_constraint(clk100, 10.0)
self.clkin = clk100
clk100_buf = Signal()
self.specials += Instance("IBUFG", i_I=clk100, o_O=clk100_buf)
clk_feedback = Signal()
clk_feedback_buf = Signal()
clk_sys = Signal()
clk_sys_45_degree = Signal()
clk_sys_double = Signal()
clk_idelay = Signal()
self.ddr_clk_phase_shift_en = Signal()
self.ddr_clk_phase_incdec = Signal()
self.mmcm_ps_psdone = Signal()
si5340_nlol = platform.request("si5340_nlol")
si5340_nlol_buf = Signal()
self.specials += Instance("IBUF", i_I=si5340_nlol, o_O=si5340_nlol_buf)
platform.add_period_constraint(dco_clk.p, 1e9 / dco_freq)
self.specials += [
Instance(
"PLLE2_BASE",
"MMCME2_ADV",
p_BANDWIDTH="OPTIMIZED",
p_DIVCLK_DIVIDE=1,
p_CLKFBOUT_PHASE=0.0,
p_CLKFBOUT_MULT=10,
p_CLKIN1_PERIOD=10.0,
p_REF_JITTER1=0.01,
p_CLKFBOUT_MULT_F=4, # VCO @ 800 MHz
p_CLKIN1_PERIOD=(1e9 / dco_freq),
p_REF_JITTER1=0.06, # From LTC2195 Datasheet
p_STARTUP_WAIT="FALSE",
i_CLKIN1=clk100_buf,
i_CLKIN1=dco_clk_buf,
i_PWRDWN=0,
i_RST=self.ps_rst | ~si5340_nlol_buf,
i_CLKFBIN=clk_feedback_buf,
o_CLKFBOUT=clk_feedback,
p_CLKOUT0_DIVIDE=10,
p_CLKOUT0_PHASE=0.0,
p_CLKOUT0_USE_FINE_PS="True",
p_CLKOUT0_DIVIDE_F=8,
p_CLKOUT0_PHASE=45.0,
p_CLKOUT0_DUTY_CYCLE=0.5,
o_CLKOUT0=clk_sys, # 100 MHz <- sys_clk
p_CLKOUT1_DIVIDE=5,
o_CLKOUT0=clk_sys_45_degree, # 800MHz / 8 -> 100MHz
o_LOCKED=self.locked,
p_CLKOUT1_DIVIDE=8,
p_CLKOUT1_PHASE=0.0,
p_CLKOUT1_DUTY_CYCLE=0.5,
o_CLKOUT1=clk_idelay, # 200 MHZ <- 2 * sys_clk = 2*100 MHz
p_CLKOUT2_DIVIDE=5,
o_CLKOUT1=clk_sys, # 800MHz / 8 -> 100MHz
p_CLKOUT2_DIVIDE=4,
p_CLKOUT2_PHASE=0.0,
p_CLKOUT2_DUTY_CYCLE=0.5,
o_CLKOUT2=clk_sys_double, # 200 MHZ <- 2 * sys_clk_double = 2*100 MHz
o_LOCKED=self.locked,
o_CLKOUT2=clk_sys_double, # 800MHz / 4 -> 200MHz
p_CLKOUT3_DIVIDE=4,
p_CLKOUT3_PHASE=0.0,
p_CLKOUT3_DUTY_CYCLE=0.5,
o_CLKOUT3=clk_idelay, # 800MHz / 4 -> 200MHz
i_PSCLK=ClockSignal(),
i_PSEN=self.ddr_clk_phase_shift_en,
i_PSINCDEC=self.ddr_clk_phase_incdec,
o_PSDONE=self.mmcm_ps_psdone,
)
]
self.specials += Instance("BUFG", i_I=clk_feedback, o_O=clk_feedback_buf)
self.specials += Instance("BUFG", i_I=clk_sys, o_O=self.cd_sys.clk)
self.specials += Instance("BUFG", i_I=clk_idelay, o_O=self.cd_idelay.clk)
self.specials += Instance("BUFG", i_I=clk_sys_45_degree, o_O=self.cd_sys_45_degree.clk)
self.specials += Instance("BUFG", i_I=clk_sys_double, o_O=self.cd_sys_double.clk)
self.specials += Instance("BUFG", i_I=clk_idelay, o_O=self.cd_idelay.clk)
# Ignore sys_clk to pll clkin path created by SoC's rst.
platform.add_false_path_constraints(self.cd_sys.clk, self.clkin)
platform.add_false_path_constraints(self.cd_sys.clk, dco_clk)
self.specials += Instance("FD", p_INIT=1, i_D=~self.locked, i_C=self.cd_sys.clk, o_Q=self.cd_sys.rst)
@ -126,6 +145,10 @@ class BaseSoC(PS7, AutoCSR):
self.submodules.crg = CRG(platform)
# self.comb += self.afe_ctrl.storage[4].eq(self.crg.mmcm_rst)
# self.comb += self.afe_ctrl.storage[5].eq(self.crg.ddr_clk_phase_shift_en)
# self.comb += self.afe_ctrl.storage[6].eq(self.crg.ddr_clk_phase_incdec)
# # # AXI to system bus bridge
self.submodules.axi2sys = Axi2Sys()
self.submodules.sys2csr = Sys2CSR()
@ -156,10 +179,10 @@ class BaseSoC(PS7, AutoCSR):
# self.add_main_adc(platform)
self.submodules.adc = ADC(platform)
self.csr_devices.append("adc")
platform.add_false_path_constraints(self.crg.cd_sys.clk, self.adc.crg.cd_dco2d.clk)
# platform.add_false_path_constraints(self.crg.cd_sys.clk, self.adc.crg.cd_dco2d.clk)
# self.add_main_dac(platform)
self.submodules.dac = DAC(platform)
self.submodules.dac = DAC(platform, [self.crg.ddr_clk_phase_shift_en, self.crg.ddr_clk_phase_incdec,])
self.csr_devices.append("dac")
# DEBUG

View File

@ -85,26 +85,6 @@ def perform_bitslip():
print(f"No bitslip required; Current frame: 0x{current_frame:02x}")
return
def mmcm_rst():
curr_cfg = read_from_memory(ADC_AFE_CTRL_ADDR, 1)[0] & 0x0F
write_to_memory(ADC_AFE_CTRL_ADDR, 0x10 | curr_cfg) # Reset MMCM
write_to_memory(ADC_AFE_CTRL_ADDR, 0x00 | curr_cfg) # Release MMCM Reset
while not(read_frame() & 0x10):
print(f"Waiting for MMCM to lock")
time.sleep(0.001)
def inc_ddr_clk_phase():
curr_cfg = read_from_memory(ADC_AFE_CTRL_ADDR, 1)[0] & 0x1F
write_to_memory(ADC_AFE_CTRL_ADDR, 0x40 | curr_cfg) # Set MMCM Phase Shift to be INC
write_to_memory(ADC_AFE_CTRL_ADDR, 0x60 | curr_cfg) # Assert MMCM Phase Shift EN High
write_to_memory(ADC_AFE_CTRL_ADDR, curr_cfg) # Deassert MMCM Phase Shift EN High
def dec_ddr_clk_phase():
curr_cfg = read_from_memory(ADC_AFE_CTRL_ADDR, 1)[0] & 0x1F
write_to_memory(ADC_AFE_CTRL_ADDR, 0x00 | curr_cfg) # Set MMCM Phase Shift to be DEC
write_to_memory(ADC_AFE_CTRL_ADDR, 0x20 | curr_cfg) # Assert MMCM Phase Shift EN High
write_to_memory(ADC_AFE_CTRL_ADDR, curr_cfg) # Deassert MMCM Phase Shift EN High
def find_edge():
prev_frame = read_frame()
for tap_delay in range(32):
@ -191,9 +171,6 @@ def configure_ltc2195():
0x04: test_pattern & 0xFF
})
# ADC software reset put its PLL to sleep momentarily. Thus, MMCM needs to be reset as well.
mmcm_rst()
# Performing Word Align
perform_bitslip()
find_edge()

View File

@ -37,6 +37,17 @@ MAIN_DAC_BUS = 2
MAIN_DAC_DEVICE = 0
DAC_VERSION = 0x0A
def inc_ddr_clk_phase():
curr_cfg = read_from_memory(CTRL_ADDR, 1)[0] & 0x07
write_to_memory(CTRL_ADDR, 0x10 | curr_cfg) # Set MMCM Phase Shift to be INC
write_to_memory(CTRL_ADDR, 0x18 | curr_cfg) # Assert MMCM Phase Shift EN High
write_to_memory(CTRL_ADDR, curr_cfg) # Deassert MMCM Phase Shift EN High
def dec_ddr_clk_phase():
curr_cfg = read_from_memory(CTRL_ADDR, 1)[0] & 0x07
write_to_memory(CTRL_ADDR, 0x00 | curr_cfg) # Set MMCM Phase Shift to be DEC
write_to_memory(CTRL_ADDR, 0x08 | curr_cfg) # Assert MMCM Phase Shift EN High
write_to_memory(CTRL_ADDR, curr_cfg) # Deassert MMCM Phase Shift EN High
def spi_write(spi, address, value):
spi.xfer2([address, value])