diff --git a/artiq/coredevice/ttl.py b/artiq/coredevice/ttl.py index 644e55ff6..866e884e5 100644 --- a/artiq/coredevice/ttl.py +++ b/artiq/coredevice/ttl.py @@ -177,7 +177,7 @@ class TTLInOut: self._set_sensitivity(0) @kernel - def gate_both_mu(self, duration): + def gate_both(self, duration): """Register both rising and falling edge events for the specified duration (in seconds).""" self._set_sensitivity(3) @@ -196,8 +196,8 @@ class TTLInOut: @kernel def timestamp_mu(self): - """Poll the RTIO input and returns an event timestamp, according to - the gating. + """Poll the RTIO input and returns an event timestamp (in machine + units), according to the gating. If the gate is permanently closed, returns a negative value. """ diff --git a/artiq/gateware/rtio/core.py b/artiq/gateware/rtio/core.py index b4fedcf88..08c46188f 100644 --- a/artiq/gateware/rtio/core.py +++ b/artiq/gateware/rtio/core.py @@ -118,8 +118,10 @@ class _OutputManager(Module): sequence_error = Signal() nop = Signal() self.sync.rsys += [ - replace.eq(self.ev.timestamp == buf.timestamp[fine_ts_width:]), - sequence_error.eq(self.ev.timestamp < buf.timestamp[fine_ts_width:]) + replace.eq(self.ev.timestamp[fine_ts_width:] \ + == buf.timestamp[fine_ts_width:]), + sequence_error.eq(self.ev.timestamp[fine_ts_width:] \ + < buf.timestamp[fine_ts_width:]) ] if interface.suppress_nop: # disable NOP at reset: do not suppress a first write with all 0s @@ -300,8 +302,7 @@ class _KernelCSRs(AutoCSR): class RTIO(Module): - def __init__(self, channels, clk_freq, full_ts_width=63, - guard_io_cycles=20): + def __init__(self, channels, full_ts_width=63, guard_io_cycles=20): data_width = max(rtlink.get_data_width(c.interface) for c in channels) address_width = max(rtlink.get_address_width(c.interface) @@ -329,11 +330,15 @@ class RTIO(Module): self.cd_rsys.rst.eq(self.kcsrs.reset.storage) ] self.comb += self.cd_rio.clk.eq(ClockSignal("rtio")) - self.specials += AsyncResetSynchronizer(self.cd_rio, - self.kcsrs.reset.storage) + self.specials += AsyncResetSynchronizer( + self.cd_rio, + self.kcsrs.reset.storage | ResetSignal("rtio", + allow_reset_less=True)) self.comb += self.cd_rio_phy.clk.eq(ClockSignal("rtio")) - self.specials += AsyncResetSynchronizer(self.cd_rio_phy, - self.kcsrs.reset_phy.storage) + self.specials += AsyncResetSynchronizer( + self.cd_rio_phy, + self.kcsrs.reset_phy.storage | ResetSignal("rtio", + allow_reset_less=True)) # Managers self.submodules.counter = _RTIOCounter(full_ts_width - fine_ts_width) diff --git a/artiq/gateware/rtio/phy/ttl_serdes_7series.py b/artiq/gateware/rtio/phy/ttl_serdes_7series.py new file mode 100644 index 000000000..b2322b4f5 --- /dev/null +++ b/artiq/gateware/rtio/phy/ttl_serdes_7series.py @@ -0,0 +1,70 @@ +from migen.fhdl.std import * + +from artiq.gateware.rtio.phy import ttl_serdes_generic + + +class _OSERDESE2_8X(Module): + def __init__(self, pad): + self.o = Signal(8) + self.t_in = Signal() + self.t_out = Signal() + + # # # + + o = self.o + self.specials += Instance("OSERDESE2", + p_DATA_RATE_OQ="DDR", p_DATA_RATE_TQ="BUF", + p_DATA_WIDTH=8, p_TRISTATE_WIDTH=1, + o_OQ=pad, o_TQ=self.t_out, + i_CLK=ClockSignal("rtiox4"), + i_CLKDIV=ClockSignal("rio_phy"), + i_D1=o[0], i_D2=o[1], i_D3=o[2], i_D4=o[3], + i_D5=o[4], i_D6=o[5], i_D7=o[6], i_D8=o[7], + i_TCE=1, i_OCE=1, i_RST=0, + i_T1=self.t_in) + + +class _IOSERDESE2_8X(Module): + def __init__(self, pad): + self.o = Signal(8) + self.i = Signal(8) + self.oe = Signal() + + # # # + + pad_i = Signal() + pad_o = Signal() + i = self.i + self.specials += Instance("ISERDESE2", p_DATA_RATE="DDR", + p_DATA_WIDTH=8, + p_INTERFACE_TYPE="NETWORKING", p_NUM_CE=1, + o_Q1=i[7], o_Q2=i[6], o_Q3=i[5], o_Q4=i[4], + o_Q5=i[3], o_Q6=i[2], o_Q7=i[1], o_Q8=i[0], + i_D=pad_i, + i_CLK=ClockSignal("rtiox4"), + i_CLKB=~ClockSignal("rtiox4"), + i_CE1=1, i_RST=0, + i_CLKDIV=ClockSignal("rio_phy")) + oserdes = _OSERDESE2_8X(pad_o) + self.submodules += oserdes + self.specials += Instance("IOBUF", + i_I=pad_o, o_O=pad_i, i_T=oserdes.t_out, + io_IO=pad) + self.comb += [ + oserdes.t_in.eq(~self.oe), + oserdes.o.eq(self.o) + ] + + +class Output_8X(ttl_serdes_generic.Output): + def __init__(self, pad): + serdes = _OSERDESE2_8X(pad) + self.submodules += serdes + ttl_serdes_generic.Output.__init__(self, serdes) + + +class Inout_8X(ttl_serdes_generic.Inout): + def __init__(self, pad): + serdes = _IOSERDESE2_8X(pad) + self.submodules += serdes + ttl_serdes_generic.Inout.__init__(self, serdes) diff --git a/artiq/gateware/rtio/phy/ttl_serdes_generic.py b/artiq/gateware/rtio/phy/ttl_serdes_generic.py new file mode 100644 index 000000000..2ba010226 --- /dev/null +++ b/artiq/gateware/rtio/phy/ttl_serdes_generic.py @@ -0,0 +1,273 @@ +from migen.fhdl.std import * +from migen.genlib.coding import PriorityEncoder + +from artiq.gateware.rtio import rtlink + + +def _mk_edges(w, direction): + l = [(1 << i) - 1 for i in range(w)] + if direction == "rising": + l = [2**w - 1 ^ x for x in l] + elif direction == "falling": + pass + else: + raise ValueError + return l + + +class _SerdesDriver(Module): + def __init__(self, serdes_o, stb, data, fine_ts, override_en, override_o): + previous_data = Signal() + serdes_width = flen(serdes_o) + edges = Array(_mk_edges(serdes_width, "rising")) + edges_n = Array(_mk_edges(serdes_width, "falling")) + self.sync.rio_phy += [ + If(stb, previous_data.eq(data)), + If(override_en, + serdes_o.eq(Replicate(override_o, serdes_width)) + ).Else( + If(stb & ~previous_data & data, + serdes_o.eq(edges[fine_ts]), + ).Elif(stb & previous_data & ~data, + serdes_o.eq(edges_n[fine_ts]), + ).Else( + serdes_o.eq(Replicate(previous_data, serdes_width)), + ) + ) + ] + + +class Output(Module): + def __init__(self, serdes): + self.rtlink = rtlink.Interface( + rtlink.OInterface(1, fine_ts_width=log2_int(flen(serdes.o)))) + self.probes = [serdes.o[-1]] + override_en = Signal() + override_o = Signal() + self.overrides = [override_en, override_o] + + # # # + + self.submodules += _SerdesDriver( + serdes.o, + self.rtlink.o.stb, self.rtlink.o.data, self.rtlink.o.fine_ts, + override_en, override_o) + + +class Inout(Module): + def __init__(self, serdes): + serdes_width = flen(serdes.o) + assert flen(serdes.i) == serdes_width + self.rtlink = rtlink.Interface( + rtlink.OInterface(2, 2, fine_ts_width=log2_int(serdes_width)), + rtlink.IInterface(1, fine_ts_width=log2_int(serdes_width))) + self.probes = [serdes.i[-1], serdes.oe] + override_en = Signal() + override_o = Signal() + override_oe = Signal() + self.overrides = [override_en, override_o, override_oe] + + # # # + + # Output + self.submodules += _SerdesDriver( + serdes_o=serdes.o, + stb=self.rtlink.o.stb & (self.rtlink.o.address == 0), + data=self.rtlink.o.data[0], + fine_ts=self.rtlink.o.fine_ts, + override_en=override_en, override_o=override_o) + + oe_k = Signal() + self.sync.rio_phy += [ + If(self.rtlink.o.stb & (self.rtlink.o.address == 1), + oe_k.eq(self.rtlink.o.data[0])), + If(override_en, + serdes.oe.eq(override_oe) + ).Else( + serdes.oe.eq(oe_k) + ) + ] + + # Input + sensitivity = Signal(2) + self.sync.rio += If(self.rtlink.o.stb & (self.rtlink.o.address == 2), + sensitivity.eq(self.rtlink.o.data)) + + i = serdes.i[-1] + i_d = Signal() + self.sync.rio_phy += [ + i_d.eq(i), + self.rtlink.i.stb.eq( + (sensitivity[0] & ( i & ~i_d)) | + (sensitivity[1] & (~i & i_d)) + ), + self.rtlink.i.data.eq(i), + ] + + pe = PriorityEncoder(serdes_width) + self.submodules += pe + self.comb += pe.i.eq(serdes.i ^ Replicate(i_d, serdes_width)) + self.sync.rio_phy += self.rtlink.i.fine_ts.eq(pe.o) + + +class _FakeSerdes(Module): + def __init__(self): + self.o = Signal(8) + self.i = Signal(8) + self.oe = Signal() + + +class _OutputTB(Module): + def __init__(self): + serdes = _FakeSerdes() + self.submodules.dut = RenameClockDomains(Output(serdes), + {"rio_phy": "sys"}) + + def gen_simulation(self, selfp): + selfp.dut.rtlink.o.data = 1 + selfp.dut.rtlink.o.fine_ts = 1 + selfp.dut.rtlink.o.stb = 1 + yield + selfp.dut.rtlink.o.stb = 0 + yield + selfp.dut.rtlink.o.data = 0 + selfp.dut.rtlink.o.fine_ts = 2 + selfp.dut.rtlink.o.stb = 1 + yield + yield + selfp.dut.rtlink.o.data = 1 + selfp.dut.rtlink.o.fine_ts = 7 + selfp.dut.rtlink.o.stb = 1 + for _ in range(6): + # note that stb stays active; output should not change + yield + + +class _InoutTB(Module): + def __init__(self): + self.serdes = _FakeSerdes() + self.submodules.dut = RenameClockDomains(Inout(self.serdes), + {"rio_phy": "sys", + "rio": "sys"}) + + def check_input(self, selfp, stb, fine_ts=None): + if stb != selfp.dut.rtlink.i.stb: + print("KO rtlink.i.stb should be {} but is {}" + .format(stb, selfp.dut.rtlink.i.stb)) + elif fine_ts is not None and fine_ts != selfp.dut.rtlink.i.fine_ts: + print("KO rtlink.i.fine_ts should be {} but is {}" + .format(fine_ts, selfp.dut.rtlink.i.fine_ts)) + else: + print("OK") + + def check_output(self, selfp, data): + if selfp.serdes.o != data: + print("KO io.o should be {} but is {}".format(data, selfp.serdes.o)) + else: + print("OK") + + def check_output_enable(self, selfp, oe): + if selfp.serdes.oe != oe: + print("KO io.oe should be {} but is {}".format(oe, selfp.serdes.oe)) + else: + print("OK") + + def gen_simulation(self, selfp): + selfp.dut.rtlink.o.address = 2 + selfp.dut.rtlink.o.data = 0b11 + selfp.dut.rtlink.o.stb = 1 # set sensitivity to rising + falling + yield + selfp.dut.rtlink.o.stb = 0 + + self.check_output_enable(selfp, 0) + yield + + selfp.serdes.i = 0b11111110 # rising edge at fine_ts = 1 + yield + selfp.serdes.i = 0b11111111 + yield + self.check_input(selfp, stb=1, fine_ts=1) + + selfp.serdes.i = 0b01111111 # falling edge at fine_ts = 7 + yield + selfp.serdes.i = 0b00000000 + yield + self.check_input(selfp, stb=1, fine_ts=7) + + selfp.serdes.i = 0b11000000 # rising edge at fine_ts = 6 + yield + selfp.serdes.i = 0b11111111 + yield + self.check_input(selfp, stb=1, fine_ts=6) + + selfp.dut.rtlink.o.address = 2 + selfp.dut.rtlink.o.data = 0b11 + selfp.dut.rtlink.o.stb = 1 # set sensitivity to rising only + yield + selfp.dut.rtlink.o.stb = 0 + yield + + selfp.serdes.i = 0b00001111 # falling edge at fine_ts = 4 + yield + self.check_input(selfp, stb=0) # no strobe, sensitivity is rising edge + + selfp.serdes.i = 0b11110000 # rising edge at fine_ts = 4 + yield + self.check_input(selfp, stb=1, fine_ts=4) + + selfp.dut.rtlink.o.address = 1 + selfp.dut.rtlink.o.data = 1 + selfp.dut.rtlink.o.stb = 1 # set Output Enable to 1 + yield + selfp.dut.rtlink.o.stb = 0 + yield + yield + self.check_output_enable(selfp, 1) + + selfp.dut.rtlink.o.address = 0 + selfp.dut.rtlink.o.data = 1 + selfp.dut.rtlink.o.fine_ts = 3 + selfp.dut.rtlink.o.stb = 1 # rising edge at fine_ts = 3 + yield + selfp.dut.rtlink.o.stb = 0 + yield + self.check_output(selfp, data=0b11111000) + + yield + self.check_output(selfp, data=0xFF) # stays at 1 + + selfp.dut.rtlink.o.data = 0 + selfp.dut.rtlink.o.fine_ts = 0 + selfp.dut.rtlink.o.stb = 1 # falling edge at fine_ts = 0 + yield + selfp.dut.rtlink.o.stb = 0 + yield + self.check_output(selfp, data=0) + + yield + self.check_output(selfp, data=0) + + selfp.dut.rtlink.o.data = 1 + selfp.dut.rtlink.o.fine_ts = 7 + selfp.dut.rtlink.o.stb = 1 # rising edge at fine_ts = 7 + yield + selfp.dut.rtlink.o.stb = 0 + yield + self.check_output(selfp, data=0b10000000) + + +if __name__ == "__main__": + import sys + from migen.sim.generic import Simulator, TopLevel + + if len(sys.argv) != 2: + print("Incorrect command line") + sys.exit(1) + + cls = { + "output": _OutputTB, + "inout": _InoutTB + }[sys.argv[1]] + + with Simulator(cls(), TopLevel("top.vcd", clk_period=int(1/0.125))) as s: + s.run() diff --git a/artiq/gateware/rtio/phy/ttl_serdes_spartan6.py b/artiq/gateware/rtio/phy/ttl_serdes_spartan6.py new file mode 100644 index 000000000..cf31449c1 --- /dev/null +++ b/artiq/gateware/rtio/phy/ttl_serdes_spartan6.py @@ -0,0 +1,154 @@ +from migen.fhdl.std import * + +from artiq.gateware.rtio.phy import ttl_serdes_generic + + +class _OSERDES2_8X(Module): + def __init__(self, pad, stb): + self.o = Signal(8) + self.t_in = Signal() + self.t_out = Signal() + + # # # + + cascade = Signal(4) + o = self.o + common = dict(p_DATA_RATE_OQ="SDR", p_DATA_RATE_OT="SDR", + p_DATA_WIDTH=8, p_OUTPUT_MODE="SINGLE_ENDED", i_TRAIN=0, + i_CLK0=ClockSignal("rtiox8"), i_CLK1=0, + i_CLKDIV=ClockSignal("rio_phy"), + i_IOCE=stb, i_OCE=1, i_TCE=1, i_RST=0, + i_T4=self.t_in, i_T3=self.t_in, + i_T2=self.t_in, i_T1=self.t_in) + + self.specials += [ + Instance("OSERDES2", p_SERDES_MODE="MASTER", + i_D4=o[7], i_D3=o[6], i_D2=o[5], i_D1=o[4], + i_SHIFTIN1=1, i_SHIFTIN2=1, + i_SHIFTIN3=cascade[2], i_SHIFTIN4=cascade[3], + o_SHIFTOUT1=cascade[0], o_SHIFTOUT2=cascade[1], + o_OQ=pad, o_TQ=self.t_out, **common), + Instance("OSERDES2", p_SERDES_MODE="SLAVE", + i_D4=o[3], i_D3=o[2], i_D2=o[1], i_D1=o[0], + i_SHIFTIN1=cascade[0], i_SHIFTIN2=cascade[1], + i_SHIFTIN3=1, i_SHIFTIN4=1, + o_SHIFTOUT3=cascade[2], o_SHIFTOUT4=cascade[3], + **common), + ] + + +class _IOSERDES2_8X(Module): + def __init__(self, pad, stb): + self.o = Signal(8) + self.i = Signal(8) + self.oe = Signal() + + # # # + + pad_i = Signal() + pad_o = Signal() + cascade = Signal() + i = self.i + common = dict(p_BITSLIP_ENABLE="FALSE", p_DATA_RATE="SDR", + p_DATA_WIDTH=8, p_INTERFACE_TYPE="RETIMED", + i_BITSLIP=0, i_CE0=1, i_IOCE=stb, + i_RST=0, i_CLK0=ClockSignal("rtiox8"), i_CLK1=0, + i_CLKDIV=ClockSignal("rio_phy")) + self.specials += [ + Instance("ISERDES2", p_SERDES_MODE="MASTER", + o_Q4=i[7], o_Q3=i[6], o_Q2=i[5], o_Q1=i[4], + o_SHIFTOUT=cascade, i_D=pad_i, i_SHIFTIN=0, + **common), + Instance("ISERDES2", p_SERDES_MODE="SLAVE", + o_Q4=i[3], o_Q3=i[2], o_Q2=i[1], o_Q1=i[0], + i_D=0, i_SHIFTIN=cascade, **common), + ] + + oserdes = _OSERDES2_8X(pad_o, stb) + self.submodules += oserdes + self.specials += Instance("IOBUF", + i_I=pad_o, o_O=pad_i, i_T=oserdes.t_out, + io_IO=pad) + self.comb += [ + oserdes.t_in.eq(~self.oe), + oserdes.o.eq(self.o), + ] + + +class Output_8X(ttl_serdes_generic.Output): + def __init__(self, pad, stb): + serdes = _OSERDES2_8X(pad, stb) + self.submodules += serdes + ttl_serdes_generic.Output.__init__(self, serdes) + + +class Inout_8X(ttl_serdes_generic.Inout): + def __init__(self, pad, stb): + serdes = _IOSERDES2_8X(pad, stb) + self.submodules += serdes + ttl_serdes_generic.Inout.__init__(self, serdes) + + +class _OSERDES2_4X(Module): + def __init__(self, pad, stb): + self.o = Signal(4) + self.t_in = Signal() + self.t_out = Signal() + + # # # + + o = self.o + self.specials += Instance("OSERDES2", p_SERDES_MODE="NONE", + p_DATA_RATE_OQ="SDR", p_DATA_RATE_OT="SDR", + p_DATA_WIDTH=4, p_OUTPUT_MODE="SINGLE_ENDED", + i_TRAIN=0, i_CLK0=ClockSignal("rtiox4"), + i_CLK1=0, i_CLKDIV=ClockSignal("rio_phy"), + i_IOCE=stb, i_OCE=1, i_TCE=1, i_RST=0, + i_T4=self.t_in, i_T3=self.t_in, + i_T2=self.t_in, i_T1=self.t_in, + i_D4=o[3], i_D3=o[2], i_D2=o[1], i_D1=o[0], + o_OQ=pad, o_TQ=self.t_out) + + +class _IOSERDES2_4X(Module): + def __init__(self, pad, stb): + self.o = Signal(4) + self.i = Signal(4) + self.oe = Signal() + + # # # + + pad_i = Signal() + pad_o = Signal() + i = self.i + self.specials += Instance("ISERDES2", p_SERDES_MODE="NONE", + p_BITSLIP_ENABLE="FALSE", p_DATA_RATE="SDR", + p_DATA_WIDTH=4, p_INTERFACE_TYPE="RETIMED", + i_BITSLIP=0, i_CE0=1, i_IOCE=stb, + i_RST=0, i_CLK0=ClockSignal("rtiox4"), + i_CLK1=0, i_CLKDIV=ClockSignal("rio_phy"), + o_Q4=i[3], o_Q3=i[2], o_Q2=i[1], o_Q1=i[0], + i_D=pad_i, i_SHIFTIN=0) + oserdes = _OSERDES2_4X(pad_o, stb) + self.submodules += oserdes + self.specials += Instance("IOBUF", + i_I=pad_o, o_O=pad_i, i_T=oserdes.t_out, + io_IO=pad) + self.comb += [ + oserdes.t_in.eq(~self.oe), + oserdes.o.eq(self.o), + ] + + +class Output_4X(ttl_serdes_generic.Output): + def __init__(self, pad, stb): + serdes = _OSERDES2_4X(pad, stb) + self.submodules += serdes + ttl_serdes_generic.Output.__init__(self, serdes) + + +class Inout_4X(ttl_serdes_generic.Inout): + def __init__(self, pad, stb): + serdes = _IOSERDES2_4X(pad, stb) + self.submodules += serdes + ttl_serdes_generic.Inout.__init__(self, serdes) diff --git a/artiq/test/coredevice.py b/artiq/test/coredevice.py index 95e8573b9..e4d9375ba 100644 --- a/artiq/test/coredevice.py +++ b/artiq/test/coredevice.py @@ -120,7 +120,7 @@ class LoopbackCount(EnvExperiment): @kernel def run(self): self.ttl_inout.output() - delay(1*us) + delay(5*us) with parallel: self.ttl_inout.gate_rising(10*us) with sequential: diff --git a/doc/manual/installing.rst b/doc/manual/installing.rst index ff74515ba..5ade4cb7e 100644 --- a/doc/manual/installing.rst +++ b/doc/manual/installing.rst @@ -147,7 +147,7 @@ These steps are required to generate bitstream (``.bit``) files, build the MiSoC :: $ cd ~/artiq-dev - $ svn co https://xc3sprog.svn.sourceforge.net/svnroot/xc3sprog/trunk xc3sprog + $ svn co http://svn.code.sf.net/p/xc3sprog/code/trunk xc3sprog $ cd xc3sprog $ cmake . && make $ sudo make install @@ -299,16 +299,14 @@ Installing the host-side software * Install the llvmlite Python bindings: :: $ cd ~/artiq-dev - $ git clone https://github.com/numba/llvmlite + $ git clone https://github.com/m-labs/llvmlite + $ git checkout backport-3.5 $ cd llvmlite $ patch -p1 < ~/artiq-dev/artiq/misc/llvmlite-add-all-targets.patch $ patch -p1 < ~/artiq-dev/artiq/misc/llvmlite-rename.patch $ patch -p1 < ~/artiq-dev/artiq/misc/llvmlite-build-as-debug-on-windows.patch $ LLVM_CONFIG=/usr/local/llvm-or1k/bin/llvm-config python3 setup.py install --user -.. note:: - llvmlite is in development and its API is not stable yet. Commit ID ``11a8303d02e3d6dd2d1e0e9065701795cd8a979f`` is known to work. - * Install ARTIQ: :: $ cd ~/artiq-dev diff --git a/examples/master/ddb.pyon b/examples/master/ddb.pyon index e853675c5..a1ce5680a 100644 --- a/examples/master/ddb.pyon +++ b/examples/master/ddb.pyon @@ -9,7 +9,7 @@ "type": "local", "module": "artiq.coredevice.core", "class": "Core", - "arguments": {} + "arguments": {"ref_period": 1e-9} }, "pmt0": { @@ -133,6 +133,9 @@ "command": "lda_controller -p {port} --bind {bind}" }, + "ttl_inout": "pmt0", + "ttl_out": "ttl0", + "pmt": "pmt0", "bd_dds": "dds0", "bd_sw": "ttl0", diff --git a/examples/master/repository/tdr.py b/examples/master/repository/tdr.py new file mode 100644 index 000000000..9a07d5753 --- /dev/null +++ b/examples/master/repository/tdr.py @@ -0,0 +1,71 @@ +from artiq import * + + +class PulseNotReceivedError(Exception): + pass + + +class TDR(EnvExperiment): + """Time domain reflectometer. + + From ttl2 an impedance matched pulse is send onto a coax + cable with an open end. pmt0 (very short stub, high impedance) also + listens on the transmission line near ttl2. + + When the forward propagating pulse passes pmt0, the voltage is half of the + logic voltage and does not register as a rising edge. Once the + rising edge is reflected at an open end (same sign) and passes by pmt0 on + its way back to ttl2, it is detected. Analogously, hysteresis leads to + detection of the falling edge once the reflection reaches pmt0 after + one round trip time. + + This works marginally and is just a proof of principle: it relies on + hysteresis at FPGA inputs around half voltage and good impedance steps, + as well as reasonably low loss cable. It does not work well for longer + cables (>100 ns RTT). The default drive strength of 12 mA and 3.3 V would + be ~300 Ω but it seems 40 Ω series impedance at the output matches + the hysteresis of the input. + + This is also equivalent to a loopback tester or a delay measurement. + """ + def build(self): + self.attr_device("core") + self.attr_device("pmt0") + self.attr_device("ttl2") + + def run(self): + n = 1000 # repetitions + latency = 50e-9 # calibrated latency without a transmission line + pulse = 1e-6 # pulse length, larger than rtt + try: + self.many(n, seconds_to_mu(pulse, self.core)) + except PulseNotReceivedError: + print("to few edges: cable too long or wiring bad") + else: + print(self.t) + t_rise = mu_to_seconds(self.t[0], self.core)/n - latency + t_fall = mu_to_seconds(self.t[1], self.core)/n - latency - pulse + print("round trip times:") + print("rising: {:5g} ns, falling {:5g} ns".format( + t_rise/1e-9, t_fall/1e-9)) + + @kernel + def many(self, n, p): + t = [0 for i in range(2)] + self.core.break_realtime() + for i in range(n): + self.one(t, p) + self.t = t + + @kernel + def one(self, t, p): + t0 = now_mu() + with parallel: + self.pmt0.gate_both_mu(2*p) + self.ttl2.pulse_mu(p) + for i in range(len(t)): + ti = self.pmt0.timestamp_mu() + if ti <= 0: + raise PulseNotReceivedError + t[i] += ti - t0 + self.pmt0.count() # flush diff --git a/soc/runtime/Makefile b/soc/runtime/Makefile index 241448f37..673235928 100644 --- a/soc/runtime/Makefile +++ b/soc/runtime/Makefile @@ -1,6 +1,6 @@ include $(MSCDIR)/software/common.mak -OBJECTS := isr.o flash_storage.o clock.o elf_loader.o services.o session.o log.o test_mode.o kloader.o bridge_ctl.o mailbox.o ksupport_data.o kserver.o moninj.o main.o +OBJECTS := isr.o flash_storage.o clock.o rtiocrg.o elf_loader.o services.o session.o log.o test_mode.o kloader.o bridge_ctl.o mailbox.o ksupport_data.o kserver.o moninj.o main.o OBJECTS_KSUPPORT := ksupport.o exception_jmp.o exceptions.o mailbox.o bridge.o rtio.o ttl.o dds.o CFLAGS += -Ilwip/src/include -Iliblwip diff --git a/soc/runtime/bridge.c b/soc/runtime/bridge.c index eb16587de..78083a16f 100644 --- a/soc/runtime/bridge.c +++ b/soc/runtime/bridge.c @@ -5,12 +5,14 @@ #include "dds.h" #include "bridge.h" +#define TIME_BUFFER (8000 << RTIO_FINE_TS_WIDTH) + static void dds_write(int addr, int data) { rtio_chan_sel_write(RTIO_DDS_CHANNEL); rtio_o_address_write(addr); rtio_o_data_write(data); - rtio_o_timestamp_write(rtio_get_counter() + 8000); + rtio_o_timestamp_write(rtio_get_counter() + TIME_BUFFER); rtio_o_we_write(1); } @@ -46,7 +48,7 @@ void bridge_main(void) struct msg_brg_ttl_out *msg; msg = (struct msg_brg_ttl_out *)umsg; - ttl_set_oe(rtio_get_counter() + 8000, msg->channel, msg->value); + ttl_set_oe(rtio_get_counter() + TIME_BUFFER, msg->channel, msg->value); mailbox_acknowledge(); break; } @@ -54,7 +56,7 @@ void bridge_main(void) struct msg_brg_ttl_out *msg; msg = (struct msg_brg_ttl_out *)umsg; - ttl_set_o(rtio_get_counter() + 8000, msg->channel, msg->value); + ttl_set_o(rtio_get_counter() + TIME_BUFFER, msg->channel, msg->value); mailbox_acknowledge(); break; } diff --git a/soc/runtime/clock.c b/soc/runtime/clock.c index 6b3946790..171fb6f30 100644 --- a/soc/runtime/clock.c +++ b/soc/runtime/clock.c @@ -26,6 +26,16 @@ long long int clock_get_ms(void) return clock_ms; } +void busywait_us(long long int us) +{ + long long int threshold; + + timer0_update_value_write(1); + threshold = timer0_value_read() - us*(long long int)identifier_frequency_read()/1000000LL; + while(timer0_value_read() > threshold) + timer0_update_value_write(1); +} + struct watchdog { int active; long long int threshold; diff --git a/soc/runtime/clock.h b/soc/runtime/clock.h index 70de5e5b7..4f4895443 100644 --- a/soc/runtime/clock.h +++ b/soc/runtime/clock.h @@ -3,6 +3,7 @@ void clock_init(void); long long int clock_get_ms(void); +void busywait_us(long long us); #define MAX_WATCHDOGS 16 diff --git a/soc/runtime/kloader.h b/soc/runtime/kloader.h index 098a740b7..ceefbc89d 100644 --- a/soc/runtime/kloader.h +++ b/soc/runtime/kloader.h @@ -2,7 +2,7 @@ #define __KLOADER_H #define KERNELCPU_EXEC_ADDRESS 0x40400000 -#define KERNELCPU_PAYLOAD_ADDRESS 0x40404000 +#define KERNELCPU_PAYLOAD_ADDRESS 0x40408000 extern long long int now; diff --git a/soc/runtime/ksupport.c b/soc/runtime/ksupport.c index 032fe88ec..eb7a784b7 100644 --- a/soc/runtime/ksupport.c +++ b/soc/runtime/ksupport.c @@ -79,7 +79,7 @@ long long int now_init(void) if(now < 0) { rtio_init(); - now = rtio_get_counter() + 125000; + now = rtio_get_counter() + (125000 << RTIO_FINE_TS_WIDTH); } return now; diff --git a/soc/runtime/ksupport.ld b/soc/runtime/ksupport.ld index 4126b4a7b..3cc585399 100644 --- a/soc/runtime/ksupport.ld +++ b/soc/runtime/ksupport.ld @@ -4,10 +4,10 @@ ENTRY(_start) INCLUDE generated/regions.ld /* First 4M of main memory are reserved for runtime code/data - * then comes kernel memory. First 16K of kernel memory are for support code. + * then comes kernel memory. First 32K of kernel memory are for support code. */ MEMORY { - ksupport : ORIGIN = 0x40400000, LENGTH = 0x4000 + ksupport : ORIGIN = 0x40400000, LENGTH = 0x8000 } /* On AMP systems, kernel stack is at the end of main RAM, @@ -24,17 +24,6 @@ SECTIONS _etext = .; } > ksupport - .got : - { - _GLOBAL_OFFSET_TABLE_ = .; - *(.got) - } > ksupport - - .got.plt : - { - *(.got.plt) - } > ksupport - .rodata : { . = ALIGN(4); diff --git a/soc/runtime/liblwip/Makefile b/soc/runtime/liblwip/Makefile index 5dc22bcf3..0aaabd954 100644 --- a/soc/runtime/liblwip/Makefile +++ b/soc/runtime/liblwip/Makefile @@ -53,4 +53,3 @@ clean: liblwip.a: $(LWIPOBJS) $(AR) clr liblwip.a $(LWIPOBJS) - $(RANLIB) liblwip.a diff --git a/soc/runtime/linker.ld b/soc/runtime/linker.ld index d0c3a6558..4a4217f1e 100644 --- a/soc/runtime/linker.ld +++ b/soc/runtime/linker.ld @@ -26,17 +26,6 @@ SECTIONS _etext = .; } > runtime - .got : - { - _GLOBAL_OFFSET_TABLE_ = .; - *(.got) - } > runtime - - .got.plt : - { - *(.got.plt) - } > runtime - .rodata : { . = ALIGN(4); diff --git a/soc/runtime/main.c b/soc/runtime/main.c index 306f26b20..74df38aea 100644 --- a/soc/runtime/main.c +++ b/soc/runtime/main.c @@ -24,6 +24,7 @@ #include "kloader.h" #include "flash_storage.h" #include "clock.h" +#include "rtiocrg.h" #include "test_mode.h" #include "kserver.h" #include "session.h" @@ -250,6 +251,7 @@ int main(void) puts("ARTIQ runtime built "__DATE__" "__TIME__"\n"); clock_init(); + rtiocrg_init(); puts("Press 't' to enter test mode..."); blink_led(); diff --git a/soc/runtime/rtiocrg.c b/soc/runtime/rtiocrg.c new file mode 100644 index 000000000..255f2a308 --- /dev/null +++ b/soc/runtime/rtiocrg.c @@ -0,0 +1,67 @@ +#include +#include + +#include "clock.h" +#include "flash_storage.h" +#include "rtiocrg.h" + +void rtiocrg_init(void) +{ + char b; + int clk; + +#ifdef CSR_RTIO_CRG_PLL_RESET_ADDR + rtio_crg_pll_reset_write(0); +#endif + b = 'i'; + clk = 0; + fs_read("startup_clock", &b, 1, NULL); + if(b == 'i') + printf("Startup RTIO clock: internal\n"); + else if(b == 'e') { + printf("Startup RTIO clock: external\n"); + clk = 1; + } else + printf("WARNING: unknown startup_clock entry in flash storage\n"); + + if(!rtiocrg_switch_clock(clk)) { + printf("WARNING: startup RTIO clock failed\n"); + printf("WARNING: this may cause the system initialization to fail\n"); + printf("WARNING: fix clocking and reset the device\n"); + } +} + +int rtiocrg_check(void) +{ +#ifdef CSR_RTIO_CRG_PLL_RESET_ADDR + return rtio_crg_pll_locked_read(); +#else + return 1; +#endif +} + +int rtiocrg_switch_clock(int clk) +{ + int current_clk; + + current_clk = rtio_crg_clock_sel_read(); + if(clk == current_clk) { +#ifdef CSR_RTIO_CRG_PLL_RESET_ADDR + busywait_us(150); + if(!rtio_crg_pll_locked_read()) + return 0; +#endif + return 1; + } +#ifdef CSR_RTIO_CRG_PLL_RESET_ADDR + rtio_crg_pll_reset_write(1); +#endif + rtio_crg_clock_sel_write(clk); +#ifdef CSR_RTIO_CRG_PLL_RESET_ADDR + rtio_crg_pll_reset_write(0); + busywait_us(150); + if(!rtio_crg_pll_locked_read()) + return 0; +#endif + return 1; +} diff --git a/soc/runtime/rtiocrg.h b/soc/runtime/rtiocrg.h new file mode 100644 index 000000000..df498f617 --- /dev/null +++ b/soc/runtime/rtiocrg.h @@ -0,0 +1,8 @@ +#ifndef __RTIOCRG_H +#define __RTIOCRG_H + +void rtiocrg_init(void); +int rtiocrg_check(void); +int rtiocrg_switch_clock(int clk); + +#endif /* __RTIOCRG_H */ diff --git a/soc/runtime/session.c b/soc/runtime/session.c index 9b3591e30..c0908639c 100644 --- a/soc/runtime/session.c +++ b/soc/runtime/session.c @@ -12,6 +12,7 @@ #include "kloader.h" #include "exceptions.h" #include "flash_storage.h" +#include "rtiocrg.h" #include "session.h" #define BUFFER_IN_SIZE (1024*1024) @@ -154,8 +155,10 @@ static int process_input(void) submit_output(9); break; } - rtio_crg_clock_sel_write(buffer_in[9]); - buffer_out[8] = REMOTEMSG_TYPE_CLOCK_SWITCH_COMPLETED; + if(rtiocrg_switch_clock(buffer_in[9])) + buffer_out[8] = REMOTEMSG_TYPE_CLOCK_SWITCH_COMPLETED; + else + buffer_out[8] = REMOTEMSG_TYPE_CLOCK_SWITCH_FAILED; submit_output(9); break; case REMOTEMSG_TYPE_LOAD_OBJECT: @@ -527,10 +530,17 @@ void session_poll(void **data, int *len) { int l; - if((user_kernel_state == USER_KERNEL_RUNNING) && watchdog_expired()) { - log("Watchdog expired"); - *len = -1; - return; + if(user_kernel_state == USER_KERNEL_RUNNING) { + if(watchdog_expired()) { + log("Watchdog expired"); + *len = -1; + return; + } + if(!rtiocrg_check()) { + log("RTIO clock failure"); + *len = -1; + return; + } } l = get_out_packet_len(); diff --git a/soc/targets/artiq_kc705.py b/soc/targets/artiq_kc705.py index cfc9774f9..a768e2f07 100644 --- a/soc/targets/artiq_kc705.py +++ b/soc/targets/artiq_kc705.py @@ -1,8 +1,11 @@ from migen.fhdl.std import * +from migen.genlib.resetsync import AsyncResetSynchronizer +from migen.genlib.cdc import MultiReg from migen.bank.description import * from migen.bank import wbgen from mibuild.generic_platform import * from mibuild.xilinx.vivado import XilinxVivadoToolchain +from mibuild.xilinx.ise import XilinxISEToolchain from misoclib.com import gpio from misoclib.soc import mem_decoder @@ -11,13 +14,22 @@ from targets.kc705 import MiniSoC from artiq.gateware.soc import AMPSoC from artiq.gateware import rtio, nist_qc1, nist_qc2 -from artiq.gateware.rtio.phy import ttl_simple, dds +from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series, dds class _RTIOCRG(Module, AutoCSR): def __init__(self, platform, rtio_internal_clk): self._clock_sel = CSRStorage() - self.clock_domains.cd_rtio = ClockDomain(reset_less=True) + self._pll_reset = CSRStorage(reset=1) + self._pll_locked = CSRStatus() + self.clock_domains.cd_rtio = ClockDomain() + self.clock_domains.cd_rtiox4 = ClockDomain(reset_less=True) + + # 10 MHz when using 125MHz input + self.clock_domains.cd_ext_clkout = ClockDomain(reset_less=True) + ext_clkout = platform.request("user_sma_gpio_p") + self.sync.ext_clkout += ext_clkout.eq(~ext_clkout) + rtio_external_clk = Signal() user_sma_clock = platform.request("user_sma_clock") @@ -25,11 +37,40 @@ class _RTIOCRG(Module, AutoCSR): self.specials += Instance("IBUFDS", i_I=user_sma_clock.p, i_IB=user_sma_clock.n, o_O=rtio_external_clk) - self.specials += Instance("BUFGMUX", - i_I0=rtio_internal_clk, - i_I1=rtio_external_clk, - i_S=self._clock_sel.storage, - o_O=self.cd_rtio.clk) + + pll_locked = Signal() + rtio_clk = Signal() + rtiox4_clk = Signal() + ext_clkout_clk = Signal() + self.specials += [ + Instance("PLLE2_ADV", + p_STARTUP_WAIT="FALSE", o_LOCKED=pll_locked, + + p_REF_JITTER1=0.01, + p_CLKIN1_PERIOD=8.0, p_CLKIN2_PERIOD=8.0, + i_CLKIN1=rtio_internal_clk, i_CLKIN2=rtio_external_clk, + # Warning: CLKINSEL=0 means CLKIN2 is selected + i_CLKINSEL=~self._clock_sel.storage, + + # VCO @ 1GHz when using 125MHz input + p_CLKFBOUT_MULT=8, p_DIVCLK_DIVIDE=1, + i_CLKFBIN=self.cd_rtio.clk, + i_RST=self._pll_reset.storage, + + o_CLKFBOUT=rtio_clk, + + p_CLKOUT0_DIVIDE=2, p_CLKOUT0_PHASE=0.0, + o_CLKOUT0=rtiox4_clk, + + p_CLKOUT1_DIVIDE=50, p_CLKOUT1_PHASE=0.0, + o_CLKOUT1=ext_clkout_clk), + Instance("BUFG", i_I=rtio_clk, o_O=self.cd_rtio.clk), + Instance("BUFG", i_I=rtiox4_clk, o_O=self.cd_rtiox4.clk), + Instance("BUFG", i_I=ext_clkout_clk, o_O=self.cd_ext_clkout.clk), + + AsyncResetSynchronizer(self.cd_rtio, ~pll_locked), + MultiReg(pll_locked, self._pll_locked.status) + ] class _NIST_QCx(MiniSoC, AMPSoC): @@ -57,10 +98,10 @@ class _NIST_QCx(MiniSoC, AMPSoC): platform.request("user_led", 1))) def add_rtio(self, rtio_channels): - self.submodules.rtio_crg = _RTIOCRG(self.platform, self.crg.pll_sys) - self.submodules.rtio = rtio.RTIO(rtio_channels, - clk_freq=125000000) + self.submodules.rtio_crg = _RTIOCRG(self.platform, self.crg.cd_sys.clk) + self.submodules.rtio = rtio.RTIO(rtio_channels) self.add_constant("RTIO_FINE_TS_WIDTH", self.rtio.fine_ts_width) + assert self.rtio.fine_ts_width <= 3 self.add_constant("DDS_RTIO_CLK_RATIO", 8 >> self.rtio.fine_ts_width) self.submodules.rtio_moninj = rtio.MonInj(rtio_channels) @@ -71,6 +112,13 @@ create_clock -name rio_clk -period 8.0 [get_nets {rio_clk}] set_false_path -from [get_clocks rsys_clk] -to [get_clocks rio_clk] set_false_path -from [get_clocks rio_clk] -to [get_clocks rsys_clk] """, rsys_clk=self.rtio.cd_rsys.clk, rio_clk=self.rtio.cd_rio.clk) + if isinstance(self.platform.toolchain, XilinxISEToolchain): + self.platform.add_platform_command(""" +NET "sys_clk" TNM_NET = "GRPrsys_clk"; +NET "{rio_clk}" TNM_NET = "GRPrio_clk"; +TIMESPEC "TSfix_cdc1" = FROM "GRPrsys_clk" TO "GRPrio_clk" TIG; +TIMESPEC "TSfix_cdc2" = FROM "GRPrio_clk" TO "GRPrsys_clk" TIG; +""", rio_clk=self.rtio_crg.cd_rtio.clk) rtio_csrs = self.rtio.get_csrs() self.submodules.rtiowb = wbgen.Bank(rtio_csrs) @@ -92,11 +140,11 @@ class NIST_QC1(_NIST_QCx): rtio_channels = [] for i in range(2): - phy = ttl_simple.Inout(platform.request("pmt", i)) + phy = ttl_serdes_7series.Inout_8X(platform.request("pmt", i)) self.submodules += phy rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512)) for i in range(15): - phy = ttl_simple.Output(platform.request("ttl", i)) + phy = ttl_serdes_7series.Output_8X(platform.request("ttl", i)) self.submodules += phy rtio_channels.append(rtio.Channel.from_phy(phy)) @@ -131,11 +179,11 @@ class NIST_QC2(_NIST_QCx): # TTL14 is for the clock generator continue if i % 4 == 3: - phy = ttl_simple.Inout(platform.request("ttl", i)) + phy = ttl_serdes_7series.Inout_8X(platform.request("ttl", i)) self.submodules += phy rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512)) else: - phy = ttl_simple.Output(platform.request("ttl", i)) + phy = ttl_serdes_7series.Output_8X(platform.request("ttl", i)) self.submodules += phy rtio_channels.append(rtio.Channel.from_phy(phy)) diff --git a/soc/targets/artiq_pipistrello.py b/soc/targets/artiq_pipistrello.py index 23c50bc3d..7a16cdcfe 100644 --- a/soc/targets/artiq_pipistrello.py +++ b/soc/targets/artiq_pipistrello.py @@ -3,6 +3,8 @@ from fractions import Fraction from migen.fhdl.std import * from migen.bank.description import * from migen.bank import wbgen +from migen.genlib.resetsync import AsyncResetSynchronizer +from migen.genlib.cdc import MultiReg from misoclib.com import gpio from misoclib.soc import mem_decoder @@ -11,46 +13,84 @@ from targets.pipistrello import BaseSoC from artiq.gateware.soc import AMPSoC from artiq.gateware import rtio, nist_qc1 -from artiq.gateware.rtio.phy import ttl_simple, dds +from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_spartan6, dds class _RTIOCRG(Module, AutoCSR): def __init__(self, platform, clk_freq): self._clock_sel = CSRStorage() - self.clock_domains.cd_rtio = ClockDomain(reset_less=True) + self._pll_reset = CSRStorage(reset=1) + self._pll_locked = CSRStatus() - f = Fraction(125*1000*1000, clk_freq) + self.clock_domains.cd_rtio = ClockDomain() + self.clock_domains.cd_rtiox4 = ClockDomain(reset_less=True) + self.clock_domains.cd_rtiox8 = ClockDomain(reset_less=True) + self.rtiox4_stb = Signal() + self.rtiox8_stb = Signal() + + rtio_f = 125*1000*1000 + f = Fraction(rtio_f, clk_freq) rtio_internal_clk = Signal() - self.specials += Instance("DCM_CLKGEN", - p_CLKFXDV_DIVIDE=2, - p_CLKFX_DIVIDE=f.denominator, - p_CLKFX_MD_MAX=float(f), - p_CLKFX_MULTIPLY=f.numerator, - p_CLKIN_PERIOD=1e9/clk_freq, - p_SPREAD_SPECTRUM="NONE", - p_STARTUP_WAIT="FALSE", - i_CLKIN=ClockSignal(), - o_CLKFX=rtio_internal_clk, - i_FREEZEDCM=0, - i_RST=ResetSignal()) - - rtio_external_clk = platform.request("pmt", 2) - # ISE infers constraints for the internal clock - # and propagates them through the BUFGMUX. Adding this: - # platform.add_period_constraint(rtio_external_clk, 8.0) - # seems to confuse it - self.specials += Instance("BUFGMUX", - i_I0=rtio_internal_clk, - i_I1=rtio_external_clk, - i_S=self._clock_sel.storage, - o_O=self.cd_rtio.clk) + rtio_external_clk = Signal() + pmt2 = platform.request("pmt", 2) + dcm_locked = Signal() + rtio_clk = Signal() + pll_locked = Signal() + pll = Signal(3) + pll_fb = Signal() + self.specials += [ + Instance("IBUFG", i_I=pmt2, o_O=rtio_external_clk), + Instance("DCM_CLKGEN", p_CLKFXDV_DIVIDE=2, + p_CLKFX_DIVIDE=f.denominator, p_CLKFX_MD_MAX=float(f), + p_CLKFX_MULTIPLY=f.numerator, p_CLKIN_PERIOD=1e9/clk_freq, + p_SPREAD_SPECTRUM="NONE", p_STARTUP_WAIT="FALSE", + i_CLKIN=ClockSignal(), o_CLKFX=rtio_internal_clk, + i_FREEZEDCM=0, i_RST=ResetSignal(), o_LOCKED=dcm_locked), + Instance("BUFGMUX", + i_I0=rtio_internal_clk, i_I1=rtio_external_clk, + i_S=self._clock_sel.storage, o_O=rtio_clk), + Instance("PLL_ADV", p_SIM_DEVICE="SPARTAN6", + p_BANDWIDTH="OPTIMIZED", p_COMPENSATION="INTERNAL", + p_REF_JITTER=.01, p_CLK_FEEDBACK="CLKFBOUT", + i_DADDR=0, i_DCLK=0, i_DEN=0, i_DI=0, i_DWE=0, + i_RST=self._pll_reset.storage | ~dcm_locked, i_REL=0, + p_DIVCLK_DIVIDE=1, p_CLKFBOUT_MULT=8, + p_CLKFBOUT_PHASE=0., i_CLKINSEL=1, + i_CLKIN1=rtio_clk, i_CLKIN2=0, + p_CLKIN1_PERIOD=1e9/rtio_f, p_CLKIN2_PERIOD=0., + i_CLKFBIN=pll_fb, o_CLKFBOUT=pll_fb, o_LOCKED=pll_locked, + o_CLKOUT0=pll[0], p_CLKOUT0_DUTY_CYCLE=.5, + o_CLKOUT1=pll[1], p_CLKOUT1_DUTY_CYCLE=.5, + o_CLKOUT2=pll[2], p_CLKOUT2_DUTY_CYCLE=.5, + p_CLKOUT0_PHASE=0., p_CLKOUT0_DIVIDE=1, + p_CLKOUT1_PHASE=0., p_CLKOUT1_DIVIDE=2, + p_CLKOUT2_PHASE=0., p_CLKOUT2_DIVIDE=8), + Instance("BUFPLL", p_DIVIDE=8, + i_PLLIN=pll[0], i_GCLK=self.cd_rtio.clk, + i_LOCKED=pll_locked, o_IOCLK=self.cd_rtiox8.clk, + o_SERDESSTROBE=self.rtiox8_stb), + Instance("BUFPLL", p_DIVIDE=4, + i_PLLIN=pll[1], i_GCLK=self.cd_rtio.clk, + i_LOCKED=pll_locked, o_IOCLK=self.cd_rtiox4.clk, + o_SERDESSTROBE=self.rtiox4_stb), + Instance("BUFG", i_I=pll[2], o_O=self.cd_rtio.clk), + AsyncResetSynchronizer(self.cd_rtio, ~pll_locked), + MultiReg(pll_locked, self._pll_locked.status), + ] + # ISE infers correct period constraints for cd_rtio.clk from + # the internal clock. The first two TIGs target just the BUFGMUX. platform.add_platform_command(""" -NET "{int_clk}" TNM_NET = "GRPint_clk"; NET "sys_clk" TNM_NET = "GRPsys_clk"; -TIMESPEC "TSfix_ise1" = FROM "GRPint_clk" TO "GRPsys_clk" TIG; +NET "{ext_clk}" TNM_NET = "GRPext_clk"; +TIMESPEC "TSfix_ise1" = FROM "GRPsys_clk" TO "GRPext_clk" TIG; +NET "{int_clk}" TNM_NET = "GRPint_clk"; TIMESPEC "TSfix_ise2" = FROM "GRPsys_clk" TO "GRPint_clk" TIG; -""", int_clk=rtio_internal_clk) +NET "{rtio_clk}" TNM_NET = "GRPrtio_clk"; +TIMESPEC "TSfix_ise3" = FROM "GRPrtio_clk" TO "GRPsys_clk" TIG; +TIMESPEC "TSfix_ise4" = FROM "GRPsys_clk" TO "GRPrtio_clk" TIG; +""", ext_clk=rtio_external_clk, int_clk=rtio_internal_clk, + rtio_clk=self.cd_rtio.clk) class NIST_QC1(BaseSoC, AMPSoC): @@ -73,6 +113,7 @@ class NIST_QC1(BaseSoC, AMPSoC): sdram_controller_settings=MiniconSettings(l2_size=64*1024), with_timer=False, **kwargs) AMPSoC.__init__(self) + platform.toolchain.bitgen_opt = "-g Binary:Yes -w" platform.toolchain.ise_commands += """ trce -v 12 -fastpaths -tsi {build_name}.tsi -o {build_name}.twr {build_name}.ncd {build_name}.pcf """ @@ -90,16 +131,29 @@ trce -v 12 -fastpaths -tsi {build_name}.tsi -o {build_name}.twr {build_name}.ncd platform.request("ttl_h_tx_en").eq(1) ] + self.submodules.rtio_crg = _RTIOCRG(platform, self.clk_freq) + # RTIO channels rtio_channels = [] + # pmt1 can run on a 8x serdes if pmt0 is not used for i in range(2): - phy = ttl_simple.Inout(platform.request("pmt", i)) + phy = ttl_serdes_spartan6.Inout_4X(platform.request("pmt", i), + self.rtio_crg.rtiox4_stb) self.submodules += phy rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512, ofifo_depth=4)) + # ttl2 can run on a 8x serdes if xtrig is not used for i in range(15): - phy = ttl_simple.Output(platform.request("ttl", i)) + if i in (0, 1): + phy = ttl_serdes_spartan6.Output_4X(platform.request("ttl", i), + self.rtio_crg.rtiox4_stb) + elif i in (2,): + phy = ttl_serdes_spartan6.Output_8X(platform.request("ttl", i), + self.rtio_crg.rtiox8_stb) + else: + phy = ttl_simple.Output(platform.request("ttl", i)) + self.submodules += phy rtio_channels.append(rtio.Channel.from_phy(phy, ofifo_depth=256)) @@ -120,16 +174,16 @@ trce -v 12 -fastpaths -tsi {build_name}.tsi -o {build_name}.twr {build_name}.ncd self.add_constant("RTIO_DDS_CHANNEL", len(rtio_channels)) self.add_constant("DDS_CHANNEL_COUNT", 8) self.add_constant("DDS_AD9858") - phy = dds.AD9858(platform.request("dds"), 8) + dds_pins = platform.request("dds") + self.comb += dds_pins.p.eq(0) + phy = dds.AD9858(dds_pins, 8) self.submodules += phy rtio_channels.append(rtio.Channel.from_phy(phy, ofifo_depth=512, ififo_depth=4)) # RTIO core - self.submodules.rtio_crg = _RTIOCRG(platform, self.clk_freq) - self.submodules.rtio = rtio.RTIO(rtio_channels, - clk_freq=125000000) + self.submodules.rtio = rtio.RTIO(rtio_channels) self.add_constant("RTIO_FINE_TS_WIDTH", self.rtio.fine_ts_width) self.add_constant("DDS_RTIO_CLK_RATIO", 8 >> self.rtio.fine_ts_width) self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)