forked from M-Labs/artiq
Merge branch 'master' into new-py2llvm
This commit is contained in:
commit
fd46d8b11e
|
@ -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.
|
||||
"""
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
|
@ -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()
|
|
@ -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)
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -53,4 +53,3 @@ clean:
|
|||
|
||||
liblwip.a: $(LWIPOBJS)
|
||||
$(AR) clr liblwip.a $(LWIPOBJS)
|
||||
$(RANLIB) liblwip.a
|
||||
|
|
|
@ -26,17 +26,6 @@ SECTIONS
|
|||
_etext = .;
|
||||
} > runtime
|
||||
|
||||
.got :
|
||||
{
|
||||
_GLOBAL_OFFSET_TABLE_ = .;
|
||||
*(.got)
|
||||
} > runtime
|
||||
|
||||
.got.plt :
|
||||
{
|
||||
*(.got.plt)
|
||||
} > runtime
|
||||
|
||||
.rodata :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
#include <stdio.h>
|
||||
#include <generated/csr.h>
|
||||
|
||||
#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;
|
||||
}
|
|
@ -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 */
|
|
@ -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]);
|
||||
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,11 +530,18 @@ void session_poll(void **data, int *len)
|
|||
{
|
||||
int l;
|
||||
|
||||
if((user_kernel_state == USER_KERNEL_RUNNING) && watchdog_expired()) {
|
||||
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();
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
||||
|
|
|
@ -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):
|
||||
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)
|
||||
|
|
Loading…
Reference in New Issue