diff --git a/artiq/gateware/suservo/adc_ser.py b/artiq/gateware/suservo/adc_ser.py index f0006d927..865017e18 100644 --- a/artiq/gateware/suservo/adc_ser.py +++ b/artiq/gateware/suservo/adc_ser.py @@ -5,8 +5,6 @@ from collections import namedtuple from migen import * from migen.genlib import io -from .tools import DiffMixin - logger = logging.getLogger(__name__) @@ -28,7 +26,7 @@ ADCParams = namedtuple("ADCParams", [ ]) -class ADC(Module, DiffMixin): +class ADC(Module): """Multi-lane, multi-channel, triggered, source-synchronous, serial ADC interface. @@ -49,7 +47,7 @@ class ADC(Module, DiffMixin): # collect sdo lines sdo = [] for i in string.ascii_lowercase[:p.lanes]: - sdo.append(self._diff(pads, "sdo" + i)) + sdo.append(getattr(pads, "sdo" + i)) assert p.lanes == len(sdo) # set up counters for the four states CNVH, CONV, READ, RTT @@ -71,13 +69,6 @@ class ADC(Module, DiffMixin): ) ] - sck_en = Signal() - if hasattr(pads, "sck_en"): - self.sync += pads.sck_en.eq(sck_en) # ODDR delay - self.specials += io.DDROutput(0, sck_en, - self._diff(pads, "sck", output=True)) - cnv_b = Signal() - self.comb += self._diff(pads, "cnv_b", output=True).eq(cnv_b) self.submodules.fsm = fsm = FSM("IDLE") fsm.act("IDLE", self.done.eq(1), @@ -88,7 +79,7 @@ class ADC(Module, DiffMixin): ) fsm.act("CNVH", count_load.eq(p.t_conv - 2), # account for sck ODDR delay - cnv_b.eq(1), + pads.cnv.eq(1), If(count_done, NextState("CONV") ) @@ -102,7 +93,7 @@ class ADC(Module, DiffMixin): fsm.act("READ", self.reading.eq(1), count_load.eq(p.t_rtt), # account for sck ODDR delay - sck_en.eq(1), + pads.sck_en.eq(1), If(count_done, NextState("RTT") ) @@ -118,22 +109,25 @@ class ADC(Module, DiffMixin): sck_en_ret = pads.sck_en_ret except AttributeError: sck_en_ret = 1 - self.clkout_io = Signal() + + self.clock_domains.cd_ret = ClockDomain("ret", reset_less=True) + self.comb += [ + # falling clkout makes two bits available + self.cd_ret.clk.eq(~pads.clkout) + ] + k = p.channels//p.lanes assert 2*t_read == k*p.width for i, sdo in enumerate(sdo): sdo_sr0 = Signal(t_read - 1) sdo_sr1 = Signal(t_read - 1) - sdo_ddr = Signal(2) - self.specials += io.DDRInput(sdo, sdo_ddr[1], sdo_ddr[0], - ~self.clkout_io) self.sync.ret += [ If(self.reading & sck_en_ret, - sdo_sr0.eq(Cat(sdo_ddr[0], sdo_sr0)), - sdo_sr1.eq(Cat(sdo_ddr[1], sdo_sr1)) + sdo_sr0.eq(Cat(sdo[0], sdo_sr0)), + sdo_sr1.eq(Cat(sdo[1], sdo_sr1)) ) ] self.comb += [ Cat(reversed([self.data[i*k + j] for j in range(k)])).eq( - Cat(sdo_ddr, zip(sdo_sr0, sdo_sr1))) + Cat(sdo, zip(sdo_sr0, sdo_sr1))) ] diff --git a/artiq/gateware/suservo/dds_ser.py b/artiq/gateware/suservo/dds_ser.py index 4f6d1f1f2..7ce0021fb 100644 --- a/artiq/gateware/suservo/dds_ser.py +++ b/artiq/gateware/suservo/dds_ser.py @@ -36,7 +36,7 @@ class DDS(spi.SPISimple): ) ] - io_update = self._diff(pads, "io_update", output=True) + io_update = pads.io_update # this assumes that the cycle time (1/125 MHz = 8 ns) is >1 SYNC_CLK # cycle (1/250 MHz = 4ns) done = Signal() diff --git a/artiq/gateware/suservo/pads.py b/artiq/gateware/suservo/pads.py index 34e9924be..91dfb3783 100644 --- a/artiq/gateware/suservo/pads.py +++ b/artiq/gateware/suservo/pads.py @@ -1,24 +1,77 @@ from migen import * -from migen.genlib.io import DifferentialOutput +from migen.genlib.io import DifferentialOutput, DifferentialInput, DDROutput class SamplerPads(Module): def __init__(self, platform, eem0, eem1): - self.sck_p, self.sck_n = [ - platform.request("{}_adc_spi_{}".format(eem0, pol), 0).clk - for pol in "pn"] - pads = platform.request("{}_cnv".format(eem0), 0) - self.cnv_b_p, self.cnv_b_n = pads.p, pads.n - pads = platform.request("{}_sdr".format(eem0), 0) - self.specials += DifferentialOutput(0, pads.p, pads.n) - dp, dn = [ - platform.request("{}_adc_data_{}".format(eem0, pol), 0) - for pol in "pn"] - self.clkout_p, self.clkout_n = dp.clkout, dn.clkout - self.sdoa_p, self.sdoa_n = dp.sdoa, dn.sdoa - self.sdob_p, self.sdob_n = dp.sdob, dn.sdob - self.sdoc_p, self.sdoc_n = dp.sdoc, dn.sdoc - self.sdod_p, self.sdod_n = dp.sdod, dn.sdod + self.sck_en = Signal() + self.cnv = Signal() + self.clkout = Signal() + + spip = platform.request("{}_adc_spi_p".format(eem0)) + spin = platform.request("{}_adc_spi_n".format(eem0)) + cnv = platform.request("{}_cnv".format(eem0)) + sdr = platform.request("{}_sdr".format(eem0)) + dp = platform.request("{}_adc_data_p".format(eem0)) + dn = platform.request("{}_adc_data_n".format(eem0)) + + clkout_se = Signal() + clkout_d = Signal() + sck = Signal() + + self.specials += [ + DifferentialOutput(self.cnv, cnv.p, cnv.n), + DifferentialOutput(1, sdr.p, sdr.n), + DDROutput(0, self.sck_en, sck), + DifferentialOutput(sck, spip.clk, spin.clk), + DifferentialInput(dp.clkout, dn.clkout, clkout_se), + Instance( + "IDELAYE2", + p_HIGH_PERFORMANCE_MODE="TRUE", p_IDELAY_TYPE="FIXED", + p_SIGNAL_PATTERN="CLOCK", p_IDELAY_VALUE=0, + p_REFCLK_FREQUENCY=200., + i_IDATAIN=clkout_se, o_DATAOUT=clkout_d), + Instance("BUFR", i_I=clkout_d, o_O=self.clkout) + ] + + # here to be early before the input delays below to have the clock + # available + self.clkout_p = dp.clkout # availabel for false paths + platform.add_platform_command( + "create_clock -name {clk} -period 8 [get_nets {clk}]", + clk=dp.clkout) + # platform.add_period_constraint(sampler_pads.clkout_p, 8.) + for i in "abcd": + sdo_se = Signal() + sdo_d = Signal() + sdo = Signal(2) + setattr(self, "sdo{}".format(i), sdo) + sdop = getattr(dp, "sdo{}".format(i)) + sdon = getattr(dn, "sdo{}".format(i)) + self.specials += [ + DifferentialInput(sdop, sdon, sdo_se), + Instance( + "IDELAYE2", + p_HIGH_PERFORMANCE_MODE="TRUE", p_IDELAY_TYPE="FIXED", + p_SIGNAL_PATTERN="DATA", p_IDELAY_VALUE=31, + p_REFCLK_FREQUENCY=200., + i_IDATAIN=sdo_se, o_DATAOUT=sdo_d), + Instance("IDDR", + p_DDR_CLK_EDGE="SAME_EDGE", + i_C=~self.clkout, i_CE=1, i_S=0, i_R=0, + i_D=sdo_d, o_Q1=sdo[0], o_Q2=sdo[1]) # sdo[1] older + ] + # 4, -0+1.5 hold (t_HSDO_DDR), -0.2+0.2 skew + platform.add_platform_command( + "set_input_delay -clock {clk} " + "-max 1.6 [get_ports {port}]\n" + "set_input_delay -clock {clk} " + "-min -0.1 [get_ports {port}]\n" + "set_input_delay -clock {clk} " + "-max 1.6 [get_ports {port}] -clock_fall -add_delay\n" + "set_input_delay -clock {clk} " + "-min -0.1 [get_ports {port}] -clock_fall -add_delay", + clk=dp.clkout, port=sdop) class UrukulPads(Module): @@ -37,7 +90,10 @@ class UrukulPads(Module): DifferentialOutput(self.io_update, ioup[i].p, ioup[i].n)) for i in range(2)] for i in range(8): - setattr(self, "mosi{}_p".format(i), - getattr(spip[i // 4], "mosi{}".format(i % 4))) - setattr(self, "mosi{}_n".format(i), + mosi = Signal() + setattr(self, "mosi{}".format(i), mosi) + self.specials += [ + DifferentialOutput(mosi, + getattr(spip[i // 4], "mosi{}".format(i % 4)), getattr(spin[i // 4], "mosi{}".format(i % 4))) + ] diff --git a/artiq/gateware/suservo/spi.py b/artiq/gateware/suservo/spi.py index 791486589..d1d129aa9 100644 --- a/artiq/gateware/suservo/spi.py +++ b/artiq/gateware/suservo/spi.py @@ -5,8 +5,6 @@ from migen import * from migen.genlib.fsm import FSM, NextState from migen.genlib import io -from .tools import DiffMixin - logger = logging.getLogger(__name__) @@ -19,7 +17,7 @@ SPIParams = namedtuple("SPIParams", [ ]) -class SPISimple(Module, DiffMixin): +class SPISimple(Module): """Simple reduced SPI interface. * Multiple MOSI lines @@ -39,9 +37,8 @@ class SPISimple(Module, DiffMixin): assert p.clk >= 1 - cs_n = self._diff(pads, "cs_n", output=True) - - clk = self._diff(pads, "clk", output=True) + cs_n = pads.cs_n + clk = pads.clk cnt = Signal(max=max(2, p.clk), reset_less=True) cnt_done = Signal() cnt_next = Signal() @@ -58,7 +55,7 @@ class SPISimple(Module, DiffMixin): for i, d in enumerate(self.data): self.comb += [ - self._diff(pads, "mosi{}".format(i), output=True).eq(d[-1]) + getattr(pads, "mosi{}".format(i)).eq(d[-1]) ] bits = Signal(max=p.width + 1, reset_less=True) diff --git a/artiq/gateware/suservo/tools.py b/artiq/gateware/suservo/tools.py deleted file mode 100644 index 7a4feac1f..000000000 --- a/artiq/gateware/suservo/tools.py +++ /dev/null @@ -1,19 +0,0 @@ -from migen import * -from migen.genlib import io - - -class DiffMixin: - def _diff(self, pads, name, output=False): - """Retrieve the single-ended ``Signal()`` ``name`` from - ``pads`` and in its absence retrieve the differential signal with the - pin pairs ``name_p`` and ``name_n``. Do so as an output if ``output``, - otherwise make a differential input.""" - if hasattr(pads, name): - return getattr(pads, name) - sig = Signal() - p, n = (getattr(pads, name + "_" + s) for s in "pn") - if output: - self.specials += io.DifferentialOutput(sig, p, n) - else: - self.specials += io.DifferentialInput(p, n, sig) - return sig diff --git a/artiq/gateware/targets/kasli.py b/artiq/gateware/targets/kasli.py index 2619a8656..6bd39081f 100755 --- a/artiq/gateware/targets/kasli.py +++ b/artiq/gateware/targets/kasli.py @@ -540,20 +540,6 @@ class SUServo(_StandaloneBase): su = ClockDomainsRenamer({"sys": "rio_phy"})(su) self.submodules += sampler_pads, urukul_pads, su - self.clock_domains.cd_ret = ClockDomain("ret", reset_less=True) - clkout = Signal() - clkout_fabric = Signal() - self.specials += [ - DifferentialInput( - sampler_pads.clkout_p, sampler_pads.clkout_n, clkout), - Instance("BUFH", i_I=clkout, o_O=clkout_fabric), - Instance("BUFIO", i_I=clkout, o_O=su.adc.clkout_io) - ] - self.comb += [ - # falling clkout makes two bits available - self.cd_ret.clk.eq(~clkout_fabric) - ] - ctrls = [rtservo.RTServoCtrl(ctrl) for ctrl in su.iir.ctrl] self.submodules += ctrls rtio_channels.extend(rtio.Channel.from_phy(ctrl) for ctrl in ctrls) @@ -603,29 +589,12 @@ class SUServo(_StandaloneBase): self.add_rtio(rtio_channels) - platform.add_period_constraint(sampler_pads.clkout_p, 8.) platform.add_false_path_constraints( sampler_pads.clkout_p, self.rtio_crg.cd_rtio.clk) platform.add_false_path_constraints( sampler_pads.clkout_p, self.crg.cd_sys.clk) - for i in "abcd": - port = getattr(sampler_pads, "sdo{}_p".format(i)) - platform.add_platform_command( - "set_input_delay -clock [get_clocks " - "-include_generated_clocks -of [get_nets {clk}]] " - "-max 6 [get_ports {port}]\n" - "set_input_delay -clock [get_clocks " - "-include_generated_clocks -of [get_nets {clk}]] " - "-min 3.5 [get_ports {port}]\n" - "set_input_delay -clock [get_clocks " - "-include_generated_clocks -of [get_nets {clk}]] " - "-max 6 [get_ports {port}] -clock_fall -add_delay\n" - "set_input_delay -clock [get_clocks " - "-include_generated_clocks -of [get_nets {clk}]] " - "-min 3.5 [get_ports {port}] -clock_fall -add_delay", - clk=sampler_pads.clkout_p, port=port) class SYSU(_StandaloneBase): diff --git a/artiq/gateware/test/suservo/test_adc.py b/artiq/gateware/test/suservo/test_adc.py index 3fc11352f..6f74c19ea 100644 --- a/artiq/gateware/test/suservo/test_adc.py +++ b/artiq/gateware/test/suservo/test_adc.py @@ -8,57 +8,6 @@ from migen.genlib import io from artiq.gateware.suservo.adc_ser import ADC, ADCParams -class DDROutputImpl(Module): - def __init__(self, i1, i2, o, clk): - do_clk0 = Signal(reset_less=True) - do_j1 = Signal(reset_less=True) - do_j2 = Signal(reset_less=True) - do_j3 = Signal(reset_less=True) - self.sync.async += [ - do_clk0.eq(clk), - do_j1.eq(i1), - do_j2.eq(i2), - If(Cat(do_clk0, clk) == 0b10, - o.eq(do_j1), - do_j3.eq(do_j2), - ).Elif(Cat(do_clk0, clk) == 0b01, - o.eq(do_j3), - ) - ] - - -class DDROutput: - @staticmethod - def lower(dr): - return DDROutputImpl(dr.i1, dr.i2, dr.o, dr.clk) - - -class DDRInputImpl(Module): - def __init__(self, i, o1, o2, clk): - di_clk0 = Signal(reset_less=True) - # SAME_EDGE_PIPELINED is effectively one register for o1 - # (during rising clock) - di_j1 = Signal(reset_less=True) - di_j2 = Signal(reset_less=True) - di_j3 = Signal(reset_less=True) - self.sync.async += [ - di_clk0.eq(clk), - di_j1.eq(i), - If(Cat(di_clk0, clk) == 0b10, - di_j3.eq(di_j1), - o1.eq(di_j3), - o2.eq(di_j2) - ).Elif(Cat(di_clk0, clk) == 0b01, - di_j2.eq(di_j1) - ) - ] - - -class DDRInput: - @staticmethod - def lower(dr): - return DDRInputImpl(dr.i, dr.o1, dr.o2, dr.clk) - class TB(Module): def __init__(self, params): @@ -66,7 +15,7 @@ class TB(Module): self.sck = Signal() self.clkout = Signal(reset_less=True) - self.cnv_b = Signal() + self.cnv = Signal() self.sck_en = Signal() self.sck_en_ret = Signal() @@ -75,12 +24,6 @@ class TB(Module): cd_adc = ClockDomain("adc", reset_less=True) self.clock_domains += cd_adc - self.clock_domains.cd_ret = ClockDomain("ret", reset_less=True) - self.comb += [ - # falling clkout makes two bits available - self.cd_ret.clk.eq(~self.clkout) - ] - self.sdo = [] self.data = [Signal((p.width, True), reset_less=True) for i in range(p.channels)] @@ -88,23 +31,21 @@ class TB(Module): srs = [] for i in range(p.lanes): name = "sdo" + string.ascii_lowercase[i] - sdo = Signal(name=name, reset_less=True) + sdo = Signal(2, name=name, reset_less=True) self.sdo.append(sdo) setattr(self, name, sdo) sr = Signal(p.width*p.channels//p.lanes, reset_less=True) srs.append(sr) - self.specials += io.DDROutput( - # one for async - self._dly(sr[-1], -1), self._dly(sr[-2], -1), sdo) self.sync.adc += [ + sdo.eq(Cat(self._dly(sr[-1], 3), self._dly(sr[-2], 3))), If(adc_sck_en, sr[2:].eq(sr) ) ] - cnv_b_old = Signal(reset_less=True) + cnv_old = Signal(reset_less=True) self.sync.async += [ - cnv_b_old.eq(self.cnv_b), - If(Cat(cnv_b_old, self.cnv_b) == 0b10, + cnv_old.eq(self.cnv), + If(Cat(cnv_old, self.cnv) == 0b10, sr.eq(Cat(reversed(self.data[2*i:2*i + 2]))), ) ] @@ -130,7 +71,6 @@ def main(): tb = TB(params) adc = ADC(tb, params) tb.submodules += adc - tb.comb += adc.clkout_io.eq(tb.clkout) def run(tb): dut = adc @@ -156,10 +96,7 @@ def main(): "ret": (8, 0), "async": (2, 0), }, - special_overrides={ - io.DDROutput: DDROutput, - io.DDRInput: DDRInput - }) + ) class ADCTest(unittest.TestCase): diff --git a/artiq/gateware/test/suservo/test_servo.py b/artiq/gateware/test/suservo/test_servo.py index a33c379a7..c8b43349e 100644 --- a/artiq/gateware/test/suservo/test_servo.py +++ b/artiq/gateware/test/suservo/test_servo.py @@ -23,7 +23,6 @@ class ServoSim(servo.Servo): servo.Servo.__init__(self, self.adc_tb, self.dds_tb, adc_p, iir_p, dds_p) - self.adc_tb.comb += self.adc.clkout_io.eq(self.adc_tb.clkout) def test(self): assert (yield self.done)