Skip to content

Commit fd46d8b

Browse files
author
whitequark
committedJul 29, 2015
Merge branch 'master' into new-py2llvm
2 parents 244ace1 + c40ae9d commit fd46d8b

24 files changed

+857
-104
lines changed
 

Diff for: ‎artiq/coredevice/ttl.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ def gate_falling(self, duration):
177177
self._set_sensitivity(0)
178178

179179
@kernel
180-
def gate_both_mu(self, duration):
180+
def gate_both(self, duration):
181181
"""Register both rising and falling edge events for the specified
182182
duration (in seconds)."""
183183
self._set_sensitivity(3)
@@ -196,8 +196,8 @@ def count(self):
196196

197197
@kernel
198198
def timestamp_mu(self):
199-
"""Poll the RTIO input and returns an event timestamp, according to
200-
the gating.
199+
"""Poll the RTIO input and returns an event timestamp (in machine
200+
units), according to the gating.
201201
202202
If the gate is permanently closed, returns a negative value.
203203
"""

Diff for: ‎artiq/gateware/rtio/core.py

+13-8
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,10 @@ def __init__(self, interface, counter, fifo_depth, guard_io_cycles):
118118
sequence_error = Signal()
119119
nop = Signal()
120120
self.sync.rsys += [
121-
replace.eq(self.ev.timestamp == buf.timestamp[fine_ts_width:]),
122-
sequence_error.eq(self.ev.timestamp < buf.timestamp[fine_ts_width:])
121+
replace.eq(self.ev.timestamp[fine_ts_width:] \
122+
== buf.timestamp[fine_ts_width:]),
123+
sequence_error.eq(self.ev.timestamp[fine_ts_width:] \
124+
< buf.timestamp[fine_ts_width:])
123125
]
124126
if interface.suppress_nop:
125127
# disable NOP at reset: do not suppress a first write with all 0s
@@ -300,8 +302,7 @@ def __init__(self, chan_sel_width,
300302

301303

302304
class RTIO(Module):
303-
def __init__(self, channels, clk_freq, full_ts_width=63,
304-
guard_io_cycles=20):
305+
def __init__(self, channels, full_ts_width=63, guard_io_cycles=20):
305306
data_width = max(rtlink.get_data_width(c.interface)
306307
for c in channels)
307308
address_width = max(rtlink.get_address_width(c.interface)
@@ -329,11 +330,15 @@ def __init__(self, channels, clk_freq, full_ts_width=63,
329330
self.cd_rsys.rst.eq(self.kcsrs.reset.storage)
330331
]
331332
self.comb += self.cd_rio.clk.eq(ClockSignal("rtio"))
332-
self.specials += AsyncResetSynchronizer(self.cd_rio,
333-
self.kcsrs.reset.storage)
333+
self.specials += AsyncResetSynchronizer(
334+
self.cd_rio,
335+
self.kcsrs.reset.storage | ResetSignal("rtio",
336+
allow_reset_less=True))
334337
self.comb += self.cd_rio_phy.clk.eq(ClockSignal("rtio"))
335-
self.specials += AsyncResetSynchronizer(self.cd_rio_phy,
336-
self.kcsrs.reset_phy.storage)
338+
self.specials += AsyncResetSynchronizer(
339+
self.cd_rio_phy,
340+
self.kcsrs.reset_phy.storage | ResetSignal("rtio",
341+
allow_reset_less=True))
337342

338343
# Managers
339344
self.submodules.counter = _RTIOCounter(full_ts_width - fine_ts_width)

Diff for: ‎artiq/gateware/rtio/phy/ttl_serdes_7series.py

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
from migen.fhdl.std import *
2+
3+
from artiq.gateware.rtio.phy import ttl_serdes_generic
4+
5+
6+
class _OSERDESE2_8X(Module):
7+
def __init__(self, pad):
8+
self.o = Signal(8)
9+
self.t_in = Signal()
10+
self.t_out = Signal()
11+
12+
# # #
13+
14+
o = self.o
15+
self.specials += Instance("OSERDESE2",
16+
p_DATA_RATE_OQ="DDR", p_DATA_RATE_TQ="BUF",
17+
p_DATA_WIDTH=8, p_TRISTATE_WIDTH=1,
18+
o_OQ=pad, o_TQ=self.t_out,
19+
i_CLK=ClockSignal("rtiox4"),
20+
i_CLKDIV=ClockSignal("rio_phy"),
21+
i_D1=o[0], i_D2=o[1], i_D3=o[2], i_D4=o[3],
22+
i_D5=o[4], i_D6=o[5], i_D7=o[6], i_D8=o[7],
23+
i_TCE=1, i_OCE=1, i_RST=0,
24+
i_T1=self.t_in)
25+
26+
27+
class _IOSERDESE2_8X(Module):
28+
def __init__(self, pad):
29+
self.o = Signal(8)
30+
self.i = Signal(8)
31+
self.oe = Signal()
32+
33+
# # #
34+
35+
pad_i = Signal()
36+
pad_o = Signal()
37+
i = self.i
38+
self.specials += Instance("ISERDESE2", p_DATA_RATE="DDR",
39+
p_DATA_WIDTH=8,
40+
p_INTERFACE_TYPE="NETWORKING", p_NUM_CE=1,
41+
o_Q1=i[7], o_Q2=i[6], o_Q3=i[5], o_Q4=i[4],
42+
o_Q5=i[3], o_Q6=i[2], o_Q7=i[1], o_Q8=i[0],
43+
i_D=pad_i,
44+
i_CLK=ClockSignal("rtiox4"),
45+
i_CLKB=~ClockSignal("rtiox4"),
46+
i_CE1=1, i_RST=0,
47+
i_CLKDIV=ClockSignal("rio_phy"))
48+
oserdes = _OSERDESE2_8X(pad_o)
49+
self.submodules += oserdes
50+
self.specials += Instance("IOBUF",
51+
i_I=pad_o, o_O=pad_i, i_T=oserdes.t_out,
52+
io_IO=pad)
53+
self.comb += [
54+
oserdes.t_in.eq(~self.oe),
55+
oserdes.o.eq(self.o)
56+
]
57+
58+
59+
class Output_8X(ttl_serdes_generic.Output):
60+
def __init__(self, pad):
61+
serdes = _OSERDESE2_8X(pad)
62+
self.submodules += serdes
63+
ttl_serdes_generic.Output.__init__(self, serdes)
64+
65+
66+
class Inout_8X(ttl_serdes_generic.Inout):
67+
def __init__(self, pad):
68+
serdes = _IOSERDESE2_8X(pad)
69+
self.submodules += serdes
70+
ttl_serdes_generic.Inout.__init__(self, serdes)

Diff for: ‎artiq/gateware/rtio/phy/ttl_serdes_generic.py

+273
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
from migen.fhdl.std import *
2+
from migen.genlib.coding import PriorityEncoder
3+
4+
from artiq.gateware.rtio import rtlink
5+
6+
7+
def _mk_edges(w, direction):
8+
l = [(1 << i) - 1 for i in range(w)]
9+
if direction == "rising":
10+
l = [2**w - 1 ^ x for x in l]
11+
elif direction == "falling":
12+
pass
13+
else:
14+
raise ValueError
15+
return l
16+
17+
18+
class _SerdesDriver(Module):
19+
def __init__(self, serdes_o, stb, data, fine_ts, override_en, override_o):
20+
previous_data = Signal()
21+
serdes_width = flen(serdes_o)
22+
edges = Array(_mk_edges(serdes_width, "rising"))
23+
edges_n = Array(_mk_edges(serdes_width, "falling"))
24+
self.sync.rio_phy += [
25+
If(stb, previous_data.eq(data)),
26+
If(override_en,
27+
serdes_o.eq(Replicate(override_o, serdes_width))
28+
).Else(
29+
If(stb & ~previous_data & data,
30+
serdes_o.eq(edges[fine_ts]),
31+
).Elif(stb & previous_data & ~data,
32+
serdes_o.eq(edges_n[fine_ts]),
33+
).Else(
34+
serdes_o.eq(Replicate(previous_data, serdes_width)),
35+
)
36+
)
37+
]
38+
39+
40+
class Output(Module):
41+
def __init__(self, serdes):
42+
self.rtlink = rtlink.Interface(
43+
rtlink.OInterface(1, fine_ts_width=log2_int(flen(serdes.o))))
44+
self.probes = [serdes.o[-1]]
45+
override_en = Signal()
46+
override_o = Signal()
47+
self.overrides = [override_en, override_o]
48+
49+
# # #
50+
51+
self.submodules += _SerdesDriver(
52+
serdes.o,
53+
self.rtlink.o.stb, self.rtlink.o.data, self.rtlink.o.fine_ts,
54+
override_en, override_o)
55+
56+
57+
class Inout(Module):
58+
def __init__(self, serdes):
59+
serdes_width = flen(serdes.o)
60+
assert flen(serdes.i) == serdes_width
61+
self.rtlink = rtlink.Interface(
62+
rtlink.OInterface(2, 2, fine_ts_width=log2_int(serdes_width)),
63+
rtlink.IInterface(1, fine_ts_width=log2_int(serdes_width)))
64+
self.probes = [serdes.i[-1], serdes.oe]
65+
override_en = Signal()
66+
override_o = Signal()
67+
override_oe = Signal()
68+
self.overrides = [override_en, override_o, override_oe]
69+
70+
# # #
71+
72+
# Output
73+
self.submodules += _SerdesDriver(
74+
serdes_o=serdes.o,
75+
stb=self.rtlink.o.stb & (self.rtlink.o.address == 0),
76+
data=self.rtlink.o.data[0],
77+
fine_ts=self.rtlink.o.fine_ts,
78+
override_en=override_en, override_o=override_o)
79+
80+
oe_k = Signal()
81+
self.sync.rio_phy += [
82+
If(self.rtlink.o.stb & (self.rtlink.o.address == 1),
83+
oe_k.eq(self.rtlink.o.data[0])),
84+
If(override_en,
85+
serdes.oe.eq(override_oe)
86+
).Else(
87+
serdes.oe.eq(oe_k)
88+
)
89+
]
90+
91+
# Input
92+
sensitivity = Signal(2)
93+
self.sync.rio += If(self.rtlink.o.stb & (self.rtlink.o.address == 2),
94+
sensitivity.eq(self.rtlink.o.data))
95+
96+
i = serdes.i[-1]
97+
i_d = Signal()
98+
self.sync.rio_phy += [
99+
i_d.eq(i),
100+
self.rtlink.i.stb.eq(
101+
(sensitivity[0] & ( i & ~i_d)) |
102+
(sensitivity[1] & (~i & i_d))
103+
),
104+
self.rtlink.i.data.eq(i),
105+
]
106+
107+
pe = PriorityEncoder(serdes_width)
108+
self.submodules += pe
109+
self.comb += pe.i.eq(serdes.i ^ Replicate(i_d, serdes_width))
110+
self.sync.rio_phy += self.rtlink.i.fine_ts.eq(pe.o)
111+
112+
113+
class _FakeSerdes(Module):
114+
def __init__(self):
115+
self.o = Signal(8)
116+
self.i = Signal(8)
117+
self.oe = Signal()
118+
119+
120+
class _OutputTB(Module):
121+
def __init__(self):
122+
serdes = _FakeSerdes()
123+
self.submodules.dut = RenameClockDomains(Output(serdes),
124+
{"rio_phy": "sys"})
125+
126+
def gen_simulation(self, selfp):
127+
selfp.dut.rtlink.o.data = 1
128+
selfp.dut.rtlink.o.fine_ts = 1
129+
selfp.dut.rtlink.o.stb = 1
130+
yield
131+
selfp.dut.rtlink.o.stb = 0
132+
yield
133+
selfp.dut.rtlink.o.data = 0
134+
selfp.dut.rtlink.o.fine_ts = 2
135+
selfp.dut.rtlink.o.stb = 1
136+
yield
137+
yield
138+
selfp.dut.rtlink.o.data = 1
139+
selfp.dut.rtlink.o.fine_ts = 7
140+
selfp.dut.rtlink.o.stb = 1
141+
for _ in range(6):
142+
# note that stb stays active; output should not change
143+
yield
144+
145+
146+
class _InoutTB(Module):
147+
def __init__(self):
148+
self.serdes = _FakeSerdes()
149+
self.submodules.dut = RenameClockDomains(Inout(self.serdes),
150+
{"rio_phy": "sys",
151+
"rio": "sys"})
152+
153+
def check_input(self, selfp, stb, fine_ts=None):
154+
if stb != selfp.dut.rtlink.i.stb:
155+
print("KO rtlink.i.stb should be {} but is {}"
156+
.format(stb, selfp.dut.rtlink.i.stb))
157+
elif fine_ts is not None and fine_ts != selfp.dut.rtlink.i.fine_ts:
158+
print("KO rtlink.i.fine_ts should be {} but is {}"
159+
.format(fine_ts, selfp.dut.rtlink.i.fine_ts))
160+
else:
161+
print("OK")
162+
163+
def check_output(self, selfp, data):
164+
if selfp.serdes.o != data:
165+
print("KO io.o should be {} but is {}".format(data, selfp.serdes.o))
166+
else:
167+
print("OK")
168+
169+
def check_output_enable(self, selfp, oe):
170+
if selfp.serdes.oe != oe:
171+
print("KO io.oe should be {} but is {}".format(oe, selfp.serdes.oe))
172+
else:
173+
print("OK")
174+
175+
def gen_simulation(self, selfp):
176+
selfp.dut.rtlink.o.address = 2
177+
selfp.dut.rtlink.o.data = 0b11
178+
selfp.dut.rtlink.o.stb = 1 # set sensitivity to rising + falling
179+
yield
180+
selfp.dut.rtlink.o.stb = 0
181+
182+
self.check_output_enable(selfp, 0)
183+
yield
184+
185+
selfp.serdes.i = 0b11111110 # rising edge at fine_ts = 1
186+
yield
187+
selfp.serdes.i = 0b11111111
188+
yield
189+
self.check_input(selfp, stb=1, fine_ts=1)
190+
191+
selfp.serdes.i = 0b01111111 # falling edge at fine_ts = 7
192+
yield
193+
selfp.serdes.i = 0b00000000
194+
yield
195+
self.check_input(selfp, stb=1, fine_ts=7)
196+
197+
selfp.serdes.i = 0b11000000 # rising edge at fine_ts = 6
198+
yield
199+
selfp.serdes.i = 0b11111111
200+
yield
201+
self.check_input(selfp, stb=1, fine_ts=6)
202+
203+
selfp.dut.rtlink.o.address = 2
204+
selfp.dut.rtlink.o.data = 0b11
205+
selfp.dut.rtlink.o.stb = 1 # set sensitivity to rising only
206+
yield
207+
selfp.dut.rtlink.o.stb = 0
208+
yield
209+
210+
selfp.serdes.i = 0b00001111 # falling edge at fine_ts = 4
211+
yield
212+
self.check_input(selfp, stb=0) # no strobe, sensitivity is rising edge
213+
214+
selfp.serdes.i = 0b11110000 # rising edge at fine_ts = 4
215+
yield
216+
self.check_input(selfp, stb=1, fine_ts=4)
217+
218+
selfp.dut.rtlink.o.address = 1
219+
selfp.dut.rtlink.o.data = 1
220+
selfp.dut.rtlink.o.stb = 1 # set Output Enable to 1
221+
yield
222+
selfp.dut.rtlink.o.stb = 0
223+
yield
224+
yield
225+
self.check_output_enable(selfp, 1)
226+
227+
selfp.dut.rtlink.o.address = 0
228+
selfp.dut.rtlink.o.data = 1
229+
selfp.dut.rtlink.o.fine_ts = 3
230+
selfp.dut.rtlink.o.stb = 1 # rising edge at fine_ts = 3
231+
yield
232+
selfp.dut.rtlink.o.stb = 0
233+
yield
234+
self.check_output(selfp, data=0b11111000)
235+
236+
yield
237+
self.check_output(selfp, data=0xFF) # stays at 1
238+
239+
selfp.dut.rtlink.o.data = 0
240+
selfp.dut.rtlink.o.fine_ts = 0
241+
selfp.dut.rtlink.o.stb = 1 # falling edge at fine_ts = 0
242+
yield
243+
selfp.dut.rtlink.o.stb = 0
244+
yield
245+
self.check_output(selfp, data=0)
246+
247+
yield
248+
self.check_output(selfp, data=0)
249+
250+
selfp.dut.rtlink.o.data = 1
251+
selfp.dut.rtlink.o.fine_ts = 7
252+
selfp.dut.rtlink.o.stb = 1 # rising edge at fine_ts = 7
253+
yield
254+
selfp.dut.rtlink.o.stb = 0
255+
yield
256+
self.check_output(selfp, data=0b10000000)
257+
258+
259+
if __name__ == "__main__":
260+
import sys
261+
from migen.sim.generic import Simulator, TopLevel
262+
263+
if len(sys.argv) != 2:
264+
print("Incorrect command line")
265+
sys.exit(1)
266+
267+
cls = {
268+
"output": _OutputTB,
269+
"inout": _InoutTB
270+
}[sys.argv[1]]
271+
272+
with Simulator(cls(), TopLevel("top.vcd", clk_period=int(1/0.125))) as s:
273+
s.run()

Diff for: ‎artiq/gateware/rtio/phy/ttl_serdes_spartan6.py

+154
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
from migen.fhdl.std import *
2+
3+
from artiq.gateware.rtio.phy import ttl_serdes_generic
4+
5+
6+
class _OSERDES2_8X(Module):
7+
def __init__(self, pad, stb):
8+
self.o = Signal(8)
9+
self.t_in = Signal()
10+
self.t_out = Signal()
11+
12+
# # #
13+
14+
cascade = Signal(4)
15+
o = self.o
16+
common = dict(p_DATA_RATE_OQ="SDR", p_DATA_RATE_OT="SDR",
17+
p_DATA_WIDTH=8, p_OUTPUT_MODE="SINGLE_ENDED", i_TRAIN=0,
18+
i_CLK0=ClockSignal("rtiox8"), i_CLK1=0,
19+
i_CLKDIV=ClockSignal("rio_phy"),
20+
i_IOCE=stb, i_OCE=1, i_TCE=1, i_RST=0,
21+
i_T4=self.t_in, i_T3=self.t_in,
22+
i_T2=self.t_in, i_T1=self.t_in)
23+
24+
self.specials += [
25+
Instance("OSERDES2", p_SERDES_MODE="MASTER",
26+
i_D4=o[7], i_D3=o[6], i_D2=o[5], i_D1=o[4],
27+
i_SHIFTIN1=1, i_SHIFTIN2=1,
28+
i_SHIFTIN3=cascade[2], i_SHIFTIN4=cascade[3],
29+
o_SHIFTOUT1=cascade[0], o_SHIFTOUT2=cascade[1],
30+
o_OQ=pad, o_TQ=self.t_out, **common),
31+
Instance("OSERDES2", p_SERDES_MODE="SLAVE",
32+
i_D4=o[3], i_D3=o[2], i_D2=o[1], i_D1=o[0],
33+
i_SHIFTIN1=cascade[0], i_SHIFTIN2=cascade[1],
34+
i_SHIFTIN3=1, i_SHIFTIN4=1,
35+
o_SHIFTOUT3=cascade[2], o_SHIFTOUT4=cascade[3],
36+
**common),
37+
]
38+
39+
40+
class _IOSERDES2_8X(Module):
41+
def __init__(self, pad, stb):
42+
self.o = Signal(8)
43+
self.i = Signal(8)
44+
self.oe = Signal()
45+
46+
# # #
47+
48+
pad_i = Signal()
49+
pad_o = Signal()
50+
cascade = Signal()
51+
i = self.i
52+
common = dict(p_BITSLIP_ENABLE="FALSE", p_DATA_RATE="SDR",
53+
p_DATA_WIDTH=8, p_INTERFACE_TYPE="RETIMED",
54+
i_BITSLIP=0, i_CE0=1, i_IOCE=stb,
55+
i_RST=0, i_CLK0=ClockSignal("rtiox8"), i_CLK1=0,
56+
i_CLKDIV=ClockSignal("rio_phy"))
57+
self.specials += [
58+
Instance("ISERDES2", p_SERDES_MODE="MASTER",
59+
o_Q4=i[7], o_Q3=i[6], o_Q2=i[5], o_Q1=i[4],
60+
o_SHIFTOUT=cascade, i_D=pad_i, i_SHIFTIN=0,
61+
**common),
62+
Instance("ISERDES2", p_SERDES_MODE="SLAVE",
63+
o_Q4=i[3], o_Q3=i[2], o_Q2=i[1], o_Q1=i[0],
64+
i_D=0, i_SHIFTIN=cascade, **common),
65+
]
66+
67+
oserdes = _OSERDES2_8X(pad_o, stb)
68+
self.submodules += oserdes
69+
self.specials += Instance("IOBUF",
70+
i_I=pad_o, o_O=pad_i, i_T=oserdes.t_out,
71+
io_IO=pad)
72+
self.comb += [
73+
oserdes.t_in.eq(~self.oe),
74+
oserdes.o.eq(self.o),
75+
]
76+
77+
78+
class Output_8X(ttl_serdes_generic.Output):
79+
def __init__(self, pad, stb):
80+
serdes = _OSERDES2_8X(pad, stb)
81+
self.submodules += serdes
82+
ttl_serdes_generic.Output.__init__(self, serdes)
83+
84+
85+
class Inout_8X(ttl_serdes_generic.Inout):
86+
def __init__(self, pad, stb):
87+
serdes = _IOSERDES2_8X(pad, stb)
88+
self.submodules += serdes
89+
ttl_serdes_generic.Inout.__init__(self, serdes)
90+
91+
92+
class _OSERDES2_4X(Module):
93+
def __init__(self, pad, stb):
94+
self.o = Signal(4)
95+
self.t_in = Signal()
96+
self.t_out = Signal()
97+
98+
# # #
99+
100+
o = self.o
101+
self.specials += Instance("OSERDES2", p_SERDES_MODE="NONE",
102+
p_DATA_RATE_OQ="SDR", p_DATA_RATE_OT="SDR",
103+
p_DATA_WIDTH=4, p_OUTPUT_MODE="SINGLE_ENDED",
104+
i_TRAIN=0, i_CLK0=ClockSignal("rtiox4"),
105+
i_CLK1=0, i_CLKDIV=ClockSignal("rio_phy"),
106+
i_IOCE=stb, i_OCE=1, i_TCE=1, i_RST=0,
107+
i_T4=self.t_in, i_T3=self.t_in,
108+
i_T2=self.t_in, i_T1=self.t_in,
109+
i_D4=o[3], i_D3=o[2], i_D2=o[1], i_D1=o[0],
110+
o_OQ=pad, o_TQ=self.t_out)
111+
112+
113+
class _IOSERDES2_4X(Module):
114+
def __init__(self, pad, stb):
115+
self.o = Signal(4)
116+
self.i = Signal(4)
117+
self.oe = Signal()
118+
119+
# # #
120+
121+
pad_i = Signal()
122+
pad_o = Signal()
123+
i = self.i
124+
self.specials += Instance("ISERDES2", p_SERDES_MODE="NONE",
125+
p_BITSLIP_ENABLE="FALSE", p_DATA_RATE="SDR",
126+
p_DATA_WIDTH=4, p_INTERFACE_TYPE="RETIMED",
127+
i_BITSLIP=0, i_CE0=1, i_IOCE=stb,
128+
i_RST=0, i_CLK0=ClockSignal("rtiox4"),
129+
i_CLK1=0, i_CLKDIV=ClockSignal("rio_phy"),
130+
o_Q4=i[3], o_Q3=i[2], o_Q2=i[1], o_Q1=i[0],
131+
i_D=pad_i, i_SHIFTIN=0)
132+
oserdes = _OSERDES2_4X(pad_o, stb)
133+
self.submodules += oserdes
134+
self.specials += Instance("IOBUF",
135+
i_I=pad_o, o_O=pad_i, i_T=oserdes.t_out,
136+
io_IO=pad)
137+
self.comb += [
138+
oserdes.t_in.eq(~self.oe),
139+
oserdes.o.eq(self.o),
140+
]
141+
142+
143+
class Output_4X(ttl_serdes_generic.Output):
144+
def __init__(self, pad, stb):
145+
serdes = _OSERDES2_4X(pad, stb)
146+
self.submodules += serdes
147+
ttl_serdes_generic.Output.__init__(self, serdes)
148+
149+
150+
class Inout_4X(ttl_serdes_generic.Inout):
151+
def __init__(self, pad, stb):
152+
serdes = _IOSERDES2_4X(pad, stb)
153+
self.submodules += serdes
154+
ttl_serdes_generic.Inout.__init__(self, serdes)

Diff for: ‎artiq/test/coredevice.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ def set_count(self, count):
120120
@kernel
121121
def run(self):
122122
self.ttl_inout.output()
123-
delay(1*us)
123+
delay(5*us)
124124
with parallel:
125125
self.ttl_inout.gate_rising(10*us)
126126
with sequential:

Diff for: ‎doc/manual/installing.rst

+3-5
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ These steps are required to generate bitstream (``.bit``) files, build the MiSoC
147147
::
148148

149149
$ cd ~/artiq-dev
150-
$ svn co https://xc3sprog.svn.sourceforge.net/svnroot/xc3sprog/trunk xc3sprog
150+
$ svn co http://svn.code.sf.net/p/xc3sprog/code/trunk xc3sprog
151151
$ cd xc3sprog
152152
$ cmake . && make
153153
$ sudo make install
@@ -299,16 +299,14 @@ Installing the host-side software
299299
* Install the llvmlite Python bindings: ::
300300

301301
$ cd ~/artiq-dev
302-
$ git clone https://github.com/numba/llvmlite
302+
$ git clone https://github.com/m-labs/llvmlite
303+
$ git checkout backport-3.5
303304
$ cd llvmlite
304305
$ patch -p1 < ~/artiq-dev/artiq/misc/llvmlite-add-all-targets.patch
305306
$ patch -p1 < ~/artiq-dev/artiq/misc/llvmlite-rename.patch
306307
$ patch -p1 < ~/artiq-dev/artiq/misc/llvmlite-build-as-debug-on-windows.patch
307308
$ LLVM_CONFIG=/usr/local/llvm-or1k/bin/llvm-config python3 setup.py install --user
308309

309-
.. note::
310-
llvmlite is in development and its API is not stable yet. Commit ID ``11a8303d02e3d6dd2d1e0e9065701795cd8a979f`` is known to work.
311-
312310
* Install ARTIQ: ::
313311

314312
$ cd ~/artiq-dev

Diff for: ‎examples/master/ddb.pyon

+4-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"type": "local",
1010
"module": "artiq.coredevice.core",
1111
"class": "Core",
12-
"arguments": {}
12+
"arguments": {"ref_period": 1e-9}
1313
},
1414

1515
"pmt0": {
@@ -133,6 +133,9 @@
133133
"command": "lda_controller -p {port} --bind {bind}"
134134
},
135135

136+
"ttl_inout": "pmt0",
137+
"ttl_out": "ttl0",
138+
136139
"pmt": "pmt0",
137140
"bd_dds": "dds0",
138141
"bd_sw": "ttl0",

Diff for: ‎examples/master/repository/tdr.py

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
from artiq import *
2+
3+
4+
class PulseNotReceivedError(Exception):
5+
pass
6+
7+
8+
class TDR(EnvExperiment):
9+
"""Time domain reflectometer.
10+
11+
From ttl2 an impedance matched pulse is send onto a coax
12+
cable with an open end. pmt0 (very short stub, high impedance) also
13+
listens on the transmission line near ttl2.
14+
15+
When the forward propagating pulse passes pmt0, the voltage is half of the
16+
logic voltage and does not register as a rising edge. Once the
17+
rising edge is reflected at an open end (same sign) and passes by pmt0 on
18+
its way back to ttl2, it is detected. Analogously, hysteresis leads to
19+
detection of the falling edge once the reflection reaches pmt0 after
20+
one round trip time.
21+
22+
This works marginally and is just a proof of principle: it relies on
23+
hysteresis at FPGA inputs around half voltage and good impedance steps,
24+
as well as reasonably low loss cable. It does not work well for longer
25+
cables (>100 ns RTT). The default drive strength of 12 mA and 3.3 V would
26+
be ~300 Ω but it seems 40 Ω series impedance at the output matches
27+
the hysteresis of the input.
28+
29+
This is also equivalent to a loopback tester or a delay measurement.
30+
"""
31+
def build(self):
32+
self.attr_device("core")
33+
self.attr_device("pmt0")
34+
self.attr_device("ttl2")
35+
36+
def run(self):
37+
n = 1000 # repetitions
38+
latency = 50e-9 # calibrated latency without a transmission line
39+
pulse = 1e-6 # pulse length, larger than rtt
40+
try:
41+
self.many(n, seconds_to_mu(pulse, self.core))
42+
except PulseNotReceivedError:
43+
print("to few edges: cable too long or wiring bad")
44+
else:
45+
print(self.t)
46+
t_rise = mu_to_seconds(self.t[0], self.core)/n - latency
47+
t_fall = mu_to_seconds(self.t[1], self.core)/n - latency - pulse
48+
print("round trip times:")
49+
print("rising: {:5g} ns, falling {:5g} ns".format(
50+
t_rise/1e-9, t_fall/1e-9))
51+
52+
@kernel
53+
def many(self, n, p):
54+
t = [0 for i in range(2)]
55+
self.core.break_realtime()
56+
for i in range(n):
57+
self.one(t, p)
58+
self.t = t
59+
60+
@kernel
61+
def one(self, t, p):
62+
t0 = now_mu()
63+
with parallel:
64+
self.pmt0.gate_both_mu(2*p)
65+
self.ttl2.pulse_mu(p)
66+
for i in range(len(t)):
67+
ti = self.pmt0.timestamp_mu()
68+
if ti <= 0:
69+
raise PulseNotReceivedError
70+
t[i] += ti - t0
71+
self.pmt0.count() # flush

Diff for: ‎soc/runtime/Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
include $(MSCDIR)/software/common.mak
22

3-
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
3+
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
44
OBJECTS_KSUPPORT := ksupport.o exception_jmp.o exceptions.o mailbox.o bridge.o rtio.o ttl.o dds.o
55

66
CFLAGS += -Ilwip/src/include -Iliblwip

Diff for: ‎soc/runtime/bridge.c

+5-3
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@
55
#include "dds.h"
66
#include "bridge.h"
77

8+
#define TIME_BUFFER (8000 << RTIO_FINE_TS_WIDTH)
9+
810
static void dds_write(int addr, int data)
911
{
1012
rtio_chan_sel_write(RTIO_DDS_CHANNEL);
1113
rtio_o_address_write(addr);
1214
rtio_o_data_write(data);
13-
rtio_o_timestamp_write(rtio_get_counter() + 8000);
15+
rtio_o_timestamp_write(rtio_get_counter() + TIME_BUFFER);
1416
rtio_o_we_write(1);
1517
}
1618

@@ -46,15 +48,15 @@ void bridge_main(void)
4648
struct msg_brg_ttl_out *msg;
4749

4850
msg = (struct msg_brg_ttl_out *)umsg;
49-
ttl_set_oe(rtio_get_counter() + 8000, msg->channel, msg->value);
51+
ttl_set_oe(rtio_get_counter() + TIME_BUFFER, msg->channel, msg->value);
5052
mailbox_acknowledge();
5153
break;
5254
}
5355
case MESSAGE_TYPE_BRG_TTL_O: {
5456
struct msg_brg_ttl_out *msg;
5557

5658
msg = (struct msg_brg_ttl_out *)umsg;
57-
ttl_set_o(rtio_get_counter() + 8000, msg->channel, msg->value);
59+
ttl_set_o(rtio_get_counter() + TIME_BUFFER, msg->channel, msg->value);
5860
mailbox_acknowledge();
5961
break;
6062
}

Diff for: ‎soc/runtime/clock.c

+10
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,16 @@ long long int clock_get_ms(void)
2626
return clock_ms;
2727
}
2828

29+
void busywait_us(long long int us)
30+
{
31+
long long int threshold;
32+
33+
timer0_update_value_write(1);
34+
threshold = timer0_value_read() - us*(long long int)identifier_frequency_read()/1000000LL;
35+
while(timer0_value_read() > threshold)
36+
timer0_update_value_write(1);
37+
}
38+
2939
struct watchdog {
3040
int active;
3141
long long int threshold;

Diff for: ‎soc/runtime/clock.h

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
void clock_init(void);
55
long long int clock_get_ms(void);
6+
void busywait_us(long long us);
67

78
#define MAX_WATCHDOGS 16
89

Diff for: ‎soc/runtime/kloader.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#define __KLOADER_H
33

44
#define KERNELCPU_EXEC_ADDRESS 0x40400000
5-
#define KERNELCPU_PAYLOAD_ADDRESS 0x40404000
5+
#define KERNELCPU_PAYLOAD_ADDRESS 0x40408000
66

77
extern long long int now;
88

Diff for: ‎soc/runtime/ksupport.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ long long int now_init(void)
7979

8080
if(now < 0) {
8181
rtio_init();
82-
now = rtio_get_counter() + 125000;
82+
now = rtio_get_counter() + (125000 << RTIO_FINE_TS_WIDTH);
8383
}
8484

8585
return now;

Diff for: ‎soc/runtime/ksupport.ld

+2-13
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ ENTRY(_start)
44
INCLUDE generated/regions.ld
55

66
/* First 4M of main memory are reserved for runtime code/data
7-
* then comes kernel memory. First 16K of kernel memory are for support code.
7+
* then comes kernel memory. First 32K of kernel memory are for support code.
88
*/
99
MEMORY {
10-
ksupport : ORIGIN = 0x40400000, LENGTH = 0x4000
10+
ksupport : ORIGIN = 0x40400000, LENGTH = 0x8000
1111
}
1212

1313
/* On AMP systems, kernel stack is at the end of main RAM,
@@ -24,17 +24,6 @@ SECTIONS
2424
_etext = .;
2525
} > ksupport
2626

27-
.got :
28-
{
29-
_GLOBAL_OFFSET_TABLE_ = .;
30-
*(.got)
31-
} > ksupport
32-
33-
.got.plt :
34-
{
35-
*(.got.plt)
36-
} > ksupport
37-
3827
.rodata :
3928
{
4029
. = ALIGN(4);

Diff for: ‎soc/runtime/liblwip/Makefile

-1
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,3 @@ clean:
5353

5454
liblwip.a: $(LWIPOBJS)
5555
$(AR) clr liblwip.a $(LWIPOBJS)
56-
$(RANLIB) liblwip.a

Diff for: ‎soc/runtime/linker.ld

-11
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,6 @@ SECTIONS
2626
_etext = .;
2727
} > runtime
2828

29-
.got :
30-
{
31-
_GLOBAL_OFFSET_TABLE_ = .;
32-
*(.got)
33-
} > runtime
34-
35-
.got.plt :
36-
{
37-
*(.got.plt)
38-
} > runtime
39-
4029
.rodata :
4130
{
4231
. = ALIGN(4);

Diff for: ‎soc/runtime/main.c

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "kloader.h"
2525
#include "flash_storage.h"
2626
#include "clock.h"
27+
#include "rtiocrg.h"
2728
#include "test_mode.h"
2829
#include "kserver.h"
2930
#include "session.h"
@@ -250,6 +251,7 @@ int main(void)
250251
puts("ARTIQ runtime built "__DATE__" "__TIME__"\n");
251252

252253
clock_init();
254+
rtiocrg_init();
253255
puts("Press 't' to enter test mode...");
254256
blink_led();
255257

Diff for: ‎soc/runtime/rtiocrg.c

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#include <stdio.h>
2+
#include <generated/csr.h>
3+
4+
#include "clock.h"
5+
#include "flash_storage.h"
6+
#include "rtiocrg.h"
7+
8+
void rtiocrg_init(void)
9+
{
10+
char b;
11+
int clk;
12+
13+
#ifdef CSR_RTIO_CRG_PLL_RESET_ADDR
14+
rtio_crg_pll_reset_write(0);
15+
#endif
16+
b = 'i';
17+
clk = 0;
18+
fs_read("startup_clock", &b, 1, NULL);
19+
if(b == 'i')
20+
printf("Startup RTIO clock: internal\n");
21+
else if(b == 'e') {
22+
printf("Startup RTIO clock: external\n");
23+
clk = 1;
24+
} else
25+
printf("WARNING: unknown startup_clock entry in flash storage\n");
26+
27+
if(!rtiocrg_switch_clock(clk)) {
28+
printf("WARNING: startup RTIO clock failed\n");
29+
printf("WARNING: this may cause the system initialization to fail\n");
30+
printf("WARNING: fix clocking and reset the device\n");
31+
}
32+
}
33+
34+
int rtiocrg_check(void)
35+
{
36+
#ifdef CSR_RTIO_CRG_PLL_RESET_ADDR
37+
return rtio_crg_pll_locked_read();
38+
#else
39+
return 1;
40+
#endif
41+
}
42+
43+
int rtiocrg_switch_clock(int clk)
44+
{
45+
int current_clk;
46+
47+
current_clk = rtio_crg_clock_sel_read();
48+
if(clk == current_clk) {
49+
#ifdef CSR_RTIO_CRG_PLL_RESET_ADDR
50+
busywait_us(150);
51+
if(!rtio_crg_pll_locked_read())
52+
return 0;
53+
#endif
54+
return 1;
55+
}
56+
#ifdef CSR_RTIO_CRG_PLL_RESET_ADDR
57+
rtio_crg_pll_reset_write(1);
58+
#endif
59+
rtio_crg_clock_sel_write(clk);
60+
#ifdef CSR_RTIO_CRG_PLL_RESET_ADDR
61+
rtio_crg_pll_reset_write(0);
62+
busywait_us(150);
63+
if(!rtio_crg_pll_locked_read())
64+
return 0;
65+
#endif
66+
return 1;
67+
}

Diff for: ‎soc/runtime/rtiocrg.h

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#ifndef __RTIOCRG_H
2+
#define __RTIOCRG_H
3+
4+
void rtiocrg_init(void);
5+
int rtiocrg_check(void);
6+
int rtiocrg_switch_clock(int clk);
7+
8+
#endif /* __RTIOCRG_H */

Diff for: ‎soc/runtime/session.c

+16-6
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "kloader.h"
1313
#include "exceptions.h"
1414
#include "flash_storage.h"
15+
#include "rtiocrg.h"
1516
#include "session.h"
1617

1718
#define BUFFER_IN_SIZE (1024*1024)
@@ -154,8 +155,10 @@ static int process_input(void)
154155
submit_output(9);
155156
break;
156157
}
157-
rtio_crg_clock_sel_write(buffer_in[9]);
158-
buffer_out[8] = REMOTEMSG_TYPE_CLOCK_SWITCH_COMPLETED;
158+
if(rtiocrg_switch_clock(buffer_in[9]))
159+
buffer_out[8] = REMOTEMSG_TYPE_CLOCK_SWITCH_COMPLETED;
160+
else
161+
buffer_out[8] = REMOTEMSG_TYPE_CLOCK_SWITCH_FAILED;
159162
submit_output(9);
160163
break;
161164
case REMOTEMSG_TYPE_LOAD_OBJECT:
@@ -527,10 +530,17 @@ void session_poll(void **data, int *len)
527530
{
528531
int l;
529532

530-
if((user_kernel_state == USER_KERNEL_RUNNING) && watchdog_expired()) {
531-
log("Watchdog expired");
532-
*len = -1;
533-
return;
533+
if(user_kernel_state == USER_KERNEL_RUNNING) {
534+
if(watchdog_expired()) {
535+
log("Watchdog expired");
536+
*len = -1;
537+
return;
538+
}
539+
if(!rtiocrg_check()) {
540+
log("RTIO clock failure");
541+
*len = -1;
542+
return;
543+
}
534544
}
535545

536546
l = get_out_packet_len();

Diff for: ‎soc/targets/artiq_kc705.py

+62-14
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
from migen.fhdl.std import *
2+
from migen.genlib.resetsync import AsyncResetSynchronizer
3+
from migen.genlib.cdc import MultiReg
24
from migen.bank.description import *
35
from migen.bank import wbgen
46
from mibuild.generic_platform import *
57
from mibuild.xilinx.vivado import XilinxVivadoToolchain
8+
from mibuild.xilinx.ise import XilinxISEToolchain
69

710
from misoclib.com import gpio
811
from misoclib.soc import mem_decoder
@@ -11,25 +14,63 @@
1114

1215
from artiq.gateware.soc import AMPSoC
1316
from artiq.gateware import rtio, nist_qc1, nist_qc2
14-
from artiq.gateware.rtio.phy import ttl_simple, dds
17+
from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series, dds
1518

1619

1720
class _RTIOCRG(Module, AutoCSR):
1821
def __init__(self, platform, rtio_internal_clk):
1922
self._clock_sel = CSRStorage()
20-
self.clock_domains.cd_rtio = ClockDomain(reset_less=True)
23+
self._pll_reset = CSRStorage(reset=1)
24+
self._pll_locked = CSRStatus()
25+
self.clock_domains.cd_rtio = ClockDomain()
26+
self.clock_domains.cd_rtiox4 = ClockDomain(reset_less=True)
27+
28+
# 10 MHz when using 125MHz input
29+
self.clock_domains.cd_ext_clkout = ClockDomain(reset_less=True)
30+
ext_clkout = platform.request("user_sma_gpio_p")
31+
self.sync.ext_clkout += ext_clkout.eq(~ext_clkout)
32+
2133

2234
rtio_external_clk = Signal()
2335
user_sma_clock = platform.request("user_sma_clock")
2436
platform.add_period_constraint(user_sma_clock.p, 8.0)
2537
self.specials += Instance("IBUFDS",
2638
i_I=user_sma_clock.p, i_IB=user_sma_clock.n,
2739
o_O=rtio_external_clk)
28-
self.specials += Instance("BUFGMUX",
29-
i_I0=rtio_internal_clk,
30-
i_I1=rtio_external_clk,
31-
i_S=self._clock_sel.storage,
32-
o_O=self.cd_rtio.clk)
40+
41+
pll_locked = Signal()
42+
rtio_clk = Signal()
43+
rtiox4_clk = Signal()
44+
ext_clkout_clk = Signal()
45+
self.specials += [
46+
Instance("PLLE2_ADV",
47+
p_STARTUP_WAIT="FALSE", o_LOCKED=pll_locked,
48+
49+
p_REF_JITTER1=0.01,
50+
p_CLKIN1_PERIOD=8.0, p_CLKIN2_PERIOD=8.0,
51+
i_CLKIN1=rtio_internal_clk, i_CLKIN2=rtio_external_clk,
52+
# Warning: CLKINSEL=0 means CLKIN2 is selected
53+
i_CLKINSEL=~self._clock_sel.storage,
54+
55+
# VCO @ 1GHz when using 125MHz input
56+
p_CLKFBOUT_MULT=8, p_DIVCLK_DIVIDE=1,
57+
i_CLKFBIN=self.cd_rtio.clk,
58+
i_RST=self._pll_reset.storage,
59+
60+
o_CLKFBOUT=rtio_clk,
61+
62+
p_CLKOUT0_DIVIDE=2, p_CLKOUT0_PHASE=0.0,
63+
o_CLKOUT0=rtiox4_clk,
64+
65+
p_CLKOUT1_DIVIDE=50, p_CLKOUT1_PHASE=0.0,
66+
o_CLKOUT1=ext_clkout_clk),
67+
Instance("BUFG", i_I=rtio_clk, o_O=self.cd_rtio.clk),
68+
Instance("BUFG", i_I=rtiox4_clk, o_O=self.cd_rtiox4.clk),
69+
Instance("BUFG", i_I=ext_clkout_clk, o_O=self.cd_ext_clkout.clk),
70+
71+
AsyncResetSynchronizer(self.cd_rtio, ~pll_locked),
72+
MultiReg(pll_locked, self._pll_locked.status)
73+
]
3374

3475

3576
class _NIST_QCx(MiniSoC, AMPSoC):
@@ -57,10 +98,10 @@ def __init__(self, platform, cpu_type="or1k", **kwargs):
5798
platform.request("user_led", 1)))
5899

59100
def add_rtio(self, rtio_channels):
60-
self.submodules.rtio_crg = _RTIOCRG(self.platform, self.crg.pll_sys)
61-
self.submodules.rtio = rtio.RTIO(rtio_channels,
62-
clk_freq=125000000)
101+
self.submodules.rtio_crg = _RTIOCRG(self.platform, self.crg.cd_sys.clk)
102+
self.submodules.rtio = rtio.RTIO(rtio_channels)
63103
self.add_constant("RTIO_FINE_TS_WIDTH", self.rtio.fine_ts_width)
104+
assert self.rtio.fine_ts_width <= 3
64105
self.add_constant("DDS_RTIO_CLK_RATIO", 8 >> self.rtio.fine_ts_width)
65106
self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
66107

@@ -71,6 +112,13 @@ def add_rtio(self, rtio_channels):
71112
set_false_path -from [get_clocks rsys_clk] -to [get_clocks rio_clk]
72113
set_false_path -from [get_clocks rio_clk] -to [get_clocks rsys_clk]
73114
""", rsys_clk=self.rtio.cd_rsys.clk, rio_clk=self.rtio.cd_rio.clk)
115+
if isinstance(self.platform.toolchain, XilinxISEToolchain):
116+
self.platform.add_platform_command("""
117+
NET "sys_clk" TNM_NET = "GRPrsys_clk";
118+
NET "{rio_clk}" TNM_NET = "GRPrio_clk";
119+
TIMESPEC "TSfix_cdc1" = FROM "GRPrsys_clk" TO "GRPrio_clk" TIG;
120+
TIMESPEC "TSfix_cdc2" = FROM "GRPrio_clk" TO "GRPrsys_clk" TIG;
121+
""", rio_clk=self.rtio_crg.cd_rtio.clk)
74122

75123
rtio_csrs = self.rtio.get_csrs()
76124
self.submodules.rtiowb = wbgen.Bank(rtio_csrs)
@@ -92,11 +140,11 @@ def __init__(self, platform, cpu_type="or1k", **kwargs):
92140

93141
rtio_channels = []
94142
for i in range(2):
95-
phy = ttl_simple.Inout(platform.request("pmt", i))
143+
phy = ttl_serdes_7series.Inout_8X(platform.request("pmt", i))
96144
self.submodules += phy
97145
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512))
98146
for i in range(15):
99-
phy = ttl_simple.Output(platform.request("ttl", i))
147+
phy = ttl_serdes_7series.Output_8X(platform.request("ttl", i))
100148
self.submodules += phy
101149
rtio_channels.append(rtio.Channel.from_phy(phy))
102150

@@ -131,11 +179,11 @@ def __init__(self, platform, cpu_type="or1k", **kwargs):
131179
# TTL14 is for the clock generator
132180
continue
133181
if i % 4 == 3:
134-
phy = ttl_simple.Inout(platform.request("ttl", i))
182+
phy = ttl_serdes_7series.Inout_8X(platform.request("ttl", i))
135183
self.submodules += phy
136184
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512))
137185
else:
138-
phy = ttl_simple.Output(platform.request("ttl", i))
186+
phy = ttl_serdes_7series.Output_8X(platform.request("ttl", i))
139187
self.submodules += phy
140188
rtio_channels.append(rtio.Channel.from_phy(phy))
141189

Diff for: ‎soc/targets/artiq_pipistrello.py

+89-35
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
from migen.fhdl.std import *
44
from migen.bank.description import *
55
from migen.bank import wbgen
6+
from migen.genlib.resetsync import AsyncResetSynchronizer
7+
from migen.genlib.cdc import MultiReg
68

79
from misoclib.com import gpio
810
from misoclib.soc import mem_decoder
@@ -11,46 +13,84 @@
1113

1214
from artiq.gateware.soc import AMPSoC
1315
from artiq.gateware import rtio, nist_qc1
14-
from artiq.gateware.rtio.phy import ttl_simple, dds
16+
from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_spartan6, dds
1517

1618

1719
class _RTIOCRG(Module, AutoCSR):
1820
def __init__(self, platform, clk_freq):
1921
self._clock_sel = CSRStorage()
20-
self.clock_domains.cd_rtio = ClockDomain(reset_less=True)
22+
self._pll_reset = CSRStorage(reset=1)
23+
self._pll_locked = CSRStatus()
2124

22-
f = Fraction(125*1000*1000, clk_freq)
25+
self.clock_domains.cd_rtio = ClockDomain()
26+
self.clock_domains.cd_rtiox4 = ClockDomain(reset_less=True)
27+
self.clock_domains.cd_rtiox8 = ClockDomain(reset_less=True)
28+
self.rtiox4_stb = Signal()
29+
self.rtiox8_stb = Signal()
30+
31+
rtio_f = 125*1000*1000
32+
f = Fraction(rtio_f, clk_freq)
2333
rtio_internal_clk = Signal()
24-
self.specials += Instance("DCM_CLKGEN",
25-
p_CLKFXDV_DIVIDE=2,
26-
p_CLKFX_DIVIDE=f.denominator,
27-
p_CLKFX_MD_MAX=float(f),
28-
p_CLKFX_MULTIPLY=f.numerator,
29-
p_CLKIN_PERIOD=1e9/clk_freq,
30-
p_SPREAD_SPECTRUM="NONE",
31-
p_STARTUP_WAIT="FALSE",
32-
i_CLKIN=ClockSignal(),
33-
o_CLKFX=rtio_internal_clk,
34-
i_FREEZEDCM=0,
35-
i_RST=ResetSignal())
36-
37-
rtio_external_clk = platform.request("pmt", 2)
38-
# ISE infers constraints for the internal clock
39-
# and propagates them through the BUFGMUX. Adding this:
40-
# platform.add_period_constraint(rtio_external_clk, 8.0)
41-
# seems to confuse it
42-
self.specials += Instance("BUFGMUX",
43-
i_I0=rtio_internal_clk,
44-
i_I1=rtio_external_clk,
45-
i_S=self._clock_sel.storage,
46-
o_O=self.cd_rtio.clk)
34+
rtio_external_clk = Signal()
35+
pmt2 = platform.request("pmt", 2)
36+
dcm_locked = Signal()
37+
rtio_clk = Signal()
38+
pll_locked = Signal()
39+
pll = Signal(3)
40+
pll_fb = Signal()
41+
self.specials += [
42+
Instance("IBUFG", i_I=pmt2, o_O=rtio_external_clk),
43+
Instance("DCM_CLKGEN", p_CLKFXDV_DIVIDE=2,
44+
p_CLKFX_DIVIDE=f.denominator, p_CLKFX_MD_MAX=float(f),
45+
p_CLKFX_MULTIPLY=f.numerator, p_CLKIN_PERIOD=1e9/clk_freq,
46+
p_SPREAD_SPECTRUM="NONE", p_STARTUP_WAIT="FALSE",
47+
i_CLKIN=ClockSignal(), o_CLKFX=rtio_internal_clk,
48+
i_FREEZEDCM=0, i_RST=ResetSignal(), o_LOCKED=dcm_locked),
49+
Instance("BUFGMUX",
50+
i_I0=rtio_internal_clk, i_I1=rtio_external_clk,
51+
i_S=self._clock_sel.storage, o_O=rtio_clk),
52+
Instance("PLL_ADV", p_SIM_DEVICE="SPARTAN6",
53+
p_BANDWIDTH="OPTIMIZED", p_COMPENSATION="INTERNAL",
54+
p_REF_JITTER=.01, p_CLK_FEEDBACK="CLKFBOUT",
55+
i_DADDR=0, i_DCLK=0, i_DEN=0, i_DI=0, i_DWE=0,
56+
i_RST=self._pll_reset.storage | ~dcm_locked, i_REL=0,
57+
p_DIVCLK_DIVIDE=1, p_CLKFBOUT_MULT=8,
58+
p_CLKFBOUT_PHASE=0., i_CLKINSEL=1,
59+
i_CLKIN1=rtio_clk, i_CLKIN2=0,
60+
p_CLKIN1_PERIOD=1e9/rtio_f, p_CLKIN2_PERIOD=0.,
61+
i_CLKFBIN=pll_fb, o_CLKFBOUT=pll_fb, o_LOCKED=pll_locked,
62+
o_CLKOUT0=pll[0], p_CLKOUT0_DUTY_CYCLE=.5,
63+
o_CLKOUT1=pll[1], p_CLKOUT1_DUTY_CYCLE=.5,
64+
o_CLKOUT2=pll[2], p_CLKOUT2_DUTY_CYCLE=.5,
65+
p_CLKOUT0_PHASE=0., p_CLKOUT0_DIVIDE=1,
66+
p_CLKOUT1_PHASE=0., p_CLKOUT1_DIVIDE=2,
67+
p_CLKOUT2_PHASE=0., p_CLKOUT2_DIVIDE=8),
68+
Instance("BUFPLL", p_DIVIDE=8,
69+
i_PLLIN=pll[0], i_GCLK=self.cd_rtio.clk,
70+
i_LOCKED=pll_locked, o_IOCLK=self.cd_rtiox8.clk,
71+
o_SERDESSTROBE=self.rtiox8_stb),
72+
Instance("BUFPLL", p_DIVIDE=4,
73+
i_PLLIN=pll[1], i_GCLK=self.cd_rtio.clk,
74+
i_LOCKED=pll_locked, o_IOCLK=self.cd_rtiox4.clk,
75+
o_SERDESSTROBE=self.rtiox4_stb),
76+
Instance("BUFG", i_I=pll[2], o_O=self.cd_rtio.clk),
77+
AsyncResetSynchronizer(self.cd_rtio, ~pll_locked),
78+
MultiReg(pll_locked, self._pll_locked.status),
79+
]
4780

81+
# ISE infers correct period constraints for cd_rtio.clk from
82+
# the internal clock. The first two TIGs target just the BUFGMUX.
4883
platform.add_platform_command("""
49-
NET "{int_clk}" TNM_NET = "GRPint_clk";
5084
NET "sys_clk" TNM_NET = "GRPsys_clk";
51-
TIMESPEC "TSfix_ise1" = FROM "GRPint_clk" TO "GRPsys_clk" TIG;
85+
NET "{ext_clk}" TNM_NET = "GRPext_clk";
86+
TIMESPEC "TSfix_ise1" = FROM "GRPsys_clk" TO "GRPext_clk" TIG;
87+
NET "{int_clk}" TNM_NET = "GRPint_clk";
5288
TIMESPEC "TSfix_ise2" = FROM "GRPsys_clk" TO "GRPint_clk" TIG;
53-
""", int_clk=rtio_internal_clk)
89+
NET "{rtio_clk}" TNM_NET = "GRPrtio_clk";
90+
TIMESPEC "TSfix_ise3" = FROM "GRPrtio_clk" TO "GRPsys_clk" TIG;
91+
TIMESPEC "TSfix_ise4" = FROM "GRPsys_clk" TO "GRPrtio_clk" TIG;
92+
""", ext_clk=rtio_external_clk, int_clk=rtio_internal_clk,
93+
rtio_clk=self.cd_rtio.clk)
5494

5595

5696
class NIST_QC1(BaseSoC, AMPSoC):
@@ -73,6 +113,7 @@ def __init__(self, platform, cpu_type="or1k", **kwargs):
73113
sdram_controller_settings=MiniconSettings(l2_size=64*1024),
74114
with_timer=False, **kwargs)
75115
AMPSoC.__init__(self)
116+
platform.toolchain.bitgen_opt = "-g Binary:Yes -w"
76117
platform.toolchain.ise_commands += """
77118
trce -v 12 -fastpaths -tsi {build_name}.tsi -o {build_name}.twr {build_name}.ncd {build_name}.pcf
78119
"""
@@ -90,16 +131,29 @@ def __init__(self, platform, cpu_type="or1k", **kwargs):
90131
platform.request("ttl_h_tx_en").eq(1)
91132
]
92133

134+
self.submodules.rtio_crg = _RTIOCRG(platform, self.clk_freq)
135+
93136
# RTIO channels
94137
rtio_channels = []
138+
# pmt1 can run on a 8x serdes if pmt0 is not used
95139
for i in range(2):
96-
phy = ttl_simple.Inout(platform.request("pmt", i))
140+
phy = ttl_serdes_spartan6.Inout_4X(platform.request("pmt", i),
141+
self.rtio_crg.rtiox4_stb)
97142
self.submodules += phy
98143
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512,
99144
ofifo_depth=4))
100145

146+
# ttl2 can run on a 8x serdes if xtrig is not used
101147
for i in range(15):
102-
phy = ttl_simple.Output(platform.request("ttl", i))
148+
if i in (0, 1):
149+
phy = ttl_serdes_spartan6.Output_4X(platform.request("ttl", i),
150+
self.rtio_crg.rtiox4_stb)
151+
elif i in (2,):
152+
phy = ttl_serdes_spartan6.Output_8X(platform.request("ttl", i),
153+
self.rtio_crg.rtiox8_stb)
154+
else:
155+
phy = ttl_simple.Output(platform.request("ttl", i))
156+
103157
self.submodules += phy
104158
rtio_channels.append(rtio.Channel.from_phy(phy, ofifo_depth=256))
105159

@@ -120,16 +174,16 @@ def __init__(self, platform, cpu_type="or1k", **kwargs):
120174
self.add_constant("RTIO_DDS_CHANNEL", len(rtio_channels))
121175
self.add_constant("DDS_CHANNEL_COUNT", 8)
122176
self.add_constant("DDS_AD9858")
123-
phy = dds.AD9858(platform.request("dds"), 8)
177+
dds_pins = platform.request("dds")
178+
self.comb += dds_pins.p.eq(0)
179+
phy = dds.AD9858(dds_pins, 8)
124180
self.submodules += phy
125181
rtio_channels.append(rtio.Channel.from_phy(phy,
126182
ofifo_depth=512,
127183
ififo_depth=4))
128184

129185
# RTIO core
130-
self.submodules.rtio_crg = _RTIOCRG(platform, self.clk_freq)
131-
self.submodules.rtio = rtio.RTIO(rtio_channels,
132-
clk_freq=125000000)
186+
self.submodules.rtio = rtio.RTIO(rtio_channels)
133187
self.add_constant("RTIO_FINE_TS_WIDTH", self.rtio.fine_ts_width)
134188
self.add_constant("DDS_RTIO_CLK_RATIO", 8 >> self.rtio.fine_ts_width)
135189
self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)

0 commit comments

Comments
 (0)
Please sign in to comment.