forked from M-Labs/artiq
suservo: use 125 MHz SDR ADC
* easier timing * natural sampling on rising edge * timing, signal robustness * adjust the servo iteration timing
This commit is contained in:
parent
37c186a0fc
commit
b44d6517d1
@ -19,9 +19,9 @@ ADCParams = namedtuple("ADCParams", [
|
|||||||
"t_cnvh", # CNVH duration (minimum)
|
"t_cnvh", # CNVH duration (minimum)
|
||||||
"t_conv", # CONV duration (minimum)
|
"t_conv", # CONV duration (minimum)
|
||||||
"t_rtt", # upper estimate for clock round trip time from
|
"t_rtt", # upper estimate for clock round trip time from
|
||||||
# sck at the FPGA to clkout at the FPGA.
|
# sck at the FPGA to clkout at the FPGA (cycles)
|
||||||
# this avoids having synchronizers and another counter
|
# this avoids having synchronizers and another counter
|
||||||
# to signal end-of transfer (CLKOUT cycles)
|
# to signal end-of transfer
|
||||||
# and it ensures fixed latency early in the pipeline
|
# and it ensures fixed latency early in the pipeline
|
||||||
])
|
])
|
||||||
|
|
||||||
@ -51,11 +51,11 @@ class ADC(Module):
|
|||||||
assert p.lanes == len(sdo)
|
assert p.lanes == len(sdo)
|
||||||
|
|
||||||
# set up counters for the four states CNVH, CONV, READ, RTT
|
# set up counters for the four states CNVH, CONV, READ, RTT
|
||||||
t_read = p.width*p.channels//p.lanes//2 # DDR
|
t_read = p.width*p.channels//p.lanes # SDR
|
||||||
assert 2*p.lanes*t_read == p.width*p.channels
|
assert p.lanes*t_read == p.width*p.channels
|
||||||
assert all(_ > 0 for _ in (p.t_cnvh, p.t_conv, p.t_rtt))
|
assert all(_ > 0 for _ in (p.t_cnvh, p.t_conv, p.t_rtt))
|
||||||
assert p.t_conv > 1
|
assert p.t_conv > 1
|
||||||
count = Signal(max=max(p.t_cnvh, p.t_conv, t_read, p.t_rtt) - 1,
|
count = Signal(max=max(p.t_cnvh, p.t_conv - 1, t_read, p.t_rtt + 1) - 1,
|
||||||
reset_less=True)
|
reset_less=True)
|
||||||
count_load = Signal.like(count)
|
count_load = Signal.like(count)
|
||||||
count_done = Signal()
|
count_done = Signal()
|
||||||
@ -92,7 +92,7 @@ class ADC(Module):
|
|||||||
)
|
)
|
||||||
fsm.act("READ",
|
fsm.act("READ",
|
||||||
self.reading.eq(1),
|
self.reading.eq(1),
|
||||||
count_load.eq(p.t_rtt), # account for sck ODDR delay
|
count_load.eq(p.t_rtt), # again account for sck ODDR delay
|
||||||
pads.sck_en.eq(1),
|
pads.sck_en.eq(1),
|
||||||
If(count_done,
|
If(count_done,
|
||||||
NextState("RTT")
|
NextState("RTT")
|
||||||
@ -113,21 +113,20 @@ class ADC(Module):
|
|||||||
self.clock_domains.cd_ret = ClockDomain("ret", reset_less=True)
|
self.clock_domains.cd_ret = ClockDomain("ret", reset_less=True)
|
||||||
self.comb += [
|
self.comb += [
|
||||||
# falling clkout makes two bits available
|
# falling clkout makes two bits available
|
||||||
self.cd_ret.clk.eq(~pads.clkout)
|
self.cd_ret.clk.eq(pads.clkout)
|
||||||
]
|
]
|
||||||
|
|
||||||
k = p.channels//p.lanes
|
k = p.channels//p.lanes
|
||||||
assert 2*t_read == k*p.width
|
assert t_read == k*p.width
|
||||||
for i, sdo in enumerate(sdo):
|
for i, sdo in enumerate(sdo):
|
||||||
sdo_sr0 = Signal(t_read - 1)
|
sdo_sr = Signal(2*t_read)
|
||||||
sdo_sr1 = Signal(t_read - 1)
|
|
||||||
self.sync.ret += [
|
self.sync.ret += [
|
||||||
If(self.reading & sck_en_ret,
|
If(self.reading & sck_en_ret,
|
||||||
sdo_sr0.eq(Cat(sdo[0], sdo_sr0)),
|
sdo_sr[1:].eq(sdo_sr),
|
||||||
sdo_sr1.eq(Cat(sdo[1], sdo_sr1))
|
sdo_sr[0].eq(sdo),
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
self.comb += [
|
self.comb += [
|
||||||
Cat(reversed([self.data[i*k + j] for j in range(k)])).eq(
|
Cat(reversed([self.data[i*k + j] for j in range(k)])
|
||||||
Cat(sdo, zip(sdo_sr0, sdo_sr1)))
|
).eq(sdo_sr)
|
||||||
]
|
]
|
||||||
|
@ -22,7 +22,7 @@ class SamplerPads(Module):
|
|||||||
self.specials += [
|
self.specials += [
|
||||||
DifferentialOutput(self.cnv, cnv.p, cnv.n),
|
DifferentialOutput(self.cnv, cnv.p, cnv.n),
|
||||||
DifferentialOutput(1, sdr.p, sdr.n),
|
DifferentialOutput(1, sdr.p, sdr.n),
|
||||||
DDROutput(0, self.sck_en, sck),
|
DDROutput(self.sck_en, 0, sck),
|
||||||
DifferentialOutput(sck, spip.clk, spin.clk),
|
DifferentialOutput(sck, spip.clk, spin.clk),
|
||||||
DifferentialInput(dp.clkout, dn.clkout, clkout_se),
|
DifferentialInput(dp.clkout, dn.clkout, clkout_se),
|
||||||
Instance(
|
Instance(
|
||||||
@ -43,8 +43,7 @@ class SamplerPads(Module):
|
|||||||
# platform.add_period_constraint(sampler_pads.clkout_p, 8.)
|
# platform.add_period_constraint(sampler_pads.clkout_p, 8.)
|
||||||
for i in "abcd":
|
for i in "abcd":
|
||||||
sdo_se = Signal()
|
sdo_se = Signal()
|
||||||
sdo_d = Signal()
|
sdo = Signal()
|
||||||
sdo = Signal(2)
|
|
||||||
setattr(self, "sdo{}".format(i), sdo)
|
setattr(self, "sdo{}".format(i), sdo)
|
||||||
sdop = getattr(dp, "sdo{}".format(i))
|
sdop = getattr(dp, "sdo{}".format(i))
|
||||||
sdon = getattr(dn, "sdo{}".format(i))
|
sdon = getattr(dn, "sdo{}".format(i))
|
||||||
@ -53,24 +52,16 @@ class SamplerPads(Module):
|
|||||||
Instance(
|
Instance(
|
||||||
"IDELAYE2",
|
"IDELAYE2",
|
||||||
p_HIGH_PERFORMANCE_MODE="TRUE", p_IDELAY_TYPE="FIXED",
|
p_HIGH_PERFORMANCE_MODE="TRUE", p_IDELAY_TYPE="FIXED",
|
||||||
p_SIGNAL_PATTERN="DATA", p_IDELAY_VALUE=31,
|
p_SIGNAL_PATTERN="DATA", p_IDELAY_VALUE=15,
|
||||||
p_REFCLK_FREQUENCY=200.,
|
p_REFCLK_FREQUENCY=200.,
|
||||||
i_IDATAIN=sdo_se, o_DATAOUT=sdo_d),
|
i_IDATAIN=sdo_se, o_DATAOUT=sdo),
|
||||||
Instance("IDDR",
|
|
||||||
p_DDR_CLK_EDGE="SAME_EDGE",
|
|
||||||
i_C=~self.clkout, i_CE=1, i_S=0, i_R=0,
|
|
||||||
i_D=sdo_d, o_Q1=sdo[0], o_Q2=sdo[1]) # sdo[1] older
|
|
||||||
]
|
]
|
||||||
# 4, -0+1.5 hold (t_HSDO_DDR), -0.2+0.2 skew
|
# 8, -0+1.5 hold (t_HSDO_DDR), -0.5+0.5 skew
|
||||||
platform.add_platform_command(
|
platform.add_platform_command(
|
||||||
"set_input_delay -clock {clk} "
|
"set_input_delay -clock {clk} "
|
||||||
"-max 1.6 [get_ports {port}]\n"
|
"-max 2 [get_ports {port}] -clock_fall\n"
|
||||||
"set_input_delay -clock {clk} "
|
"set_input_delay -clock {clk} "
|
||||||
"-min -0.1 [get_ports {port}]\n"
|
"-min -0.5 [get_ports {port}] -clock_fall",
|
||||||
"set_input_delay -clock {clk} "
|
|
||||||
"-max 1.6 [get_ports {port}] -clock_fall -add_delay\n"
|
|
||||||
"set_input_delay -clock {clk} "
|
|
||||||
"-min -0.1 [get_ports {port}] -clock_fall -add_delay",
|
|
||||||
clk=dp.clkout, port=sdop)
|
clk=dp.clkout, port=sdop)
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,14 +16,15 @@ class Servo(Module):
|
|||||||
self.comb += j.eq(i), l.eq(k)
|
self.comb += j.eq(i), l.eq(k)
|
||||||
|
|
||||||
t_adc = (adc_p.t_cnvh + adc_p.t_conv + adc_p.t_rtt +
|
t_adc = (adc_p.t_cnvh + adc_p.t_conv + adc_p.t_rtt +
|
||||||
adc_p.channels*adc_p.width//2//adc_p.lanes) + 1
|
adc_p.channels*adc_p.width//adc_p.lanes) + 1
|
||||||
t_iir = ((1 + 4 + 1) << iir_p.channel) + 1
|
t_iir = ((1 + 4 + 1) << iir_p.channel) + 1
|
||||||
t_dds = (dds_p.width*2 + 1)*dds_p.clk + 1
|
t_dds = (dds_p.width*2 + 1)*dds_p.clk + 1
|
||||||
|
|
||||||
t_cycle = max(t_adc, t_iir, t_dds)
|
t_cycle = max(t_adc, t_iir, t_dds)
|
||||||
assert t_iir + (2 << iir_p.channel) < t_cycle, "need shifting time"
|
assert t_iir + (2 << iir_p.channel) < t_cycle, "need shifting time"
|
||||||
|
|
||||||
self.start = Signal()
|
self.start = Signal()
|
||||||
t_restart = t_cycle - t_iir - t_adc
|
t_restart = t_cycle - t_adc
|
||||||
assert t_restart > 0
|
assert t_restart > 0
|
||||||
cnt = Signal(max=t_restart)
|
cnt = Signal(max=t_restart)
|
||||||
cnt_done = Signal()
|
cnt_done = Signal()
|
||||||
@ -39,7 +40,7 @@ class Servo(Module):
|
|||||||
self.done.eq(self.dds.done),
|
self.done.eq(self.dds.done),
|
||||||
]
|
]
|
||||||
self.sync += [
|
self.sync += [
|
||||||
If(iir_done & ~cnt_done & ~token[0],
|
If(self.adc.done & ~cnt_done,
|
||||||
cnt.eq(cnt - 1),
|
cnt.eq(cnt - 1),
|
||||||
),
|
),
|
||||||
If(self.adc.start,
|
If(self.adc.start,
|
||||||
|
@ -31,15 +31,15 @@ class TB(Module):
|
|||||||
srs = []
|
srs = []
|
||||||
for i in range(p.lanes):
|
for i in range(p.lanes):
|
||||||
name = "sdo" + string.ascii_lowercase[i]
|
name = "sdo" + string.ascii_lowercase[i]
|
||||||
sdo = Signal(2, name=name, reset_less=True)
|
sdo = Signal(name=name, reset_less=True)
|
||||||
self.sdo.append(sdo)
|
self.sdo.append(sdo)
|
||||||
setattr(self, name, sdo)
|
setattr(self, name, sdo)
|
||||||
sr = Signal(p.width*p.channels//p.lanes, reset_less=True)
|
sr = Signal(p.width*p.channels//p.lanes, reset_less=True)
|
||||||
srs.append(sr)
|
srs.append(sr)
|
||||||
self.sync.adc += [
|
self.sync.adc += [
|
||||||
sdo.eq(Cat(self._dly(sr[-1], 3), self._dly(sr[-2], 3))),
|
sdo.eq(self._dly(sr[-1], -1)),
|
||||||
If(adc_sck_en,
|
If(adc_sck_en,
|
||||||
sr[2:].eq(sr)
|
sr[1:].eq(sr)
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
cnv_old = Signal(reset_less=True)
|
cnv_old = Signal(reset_less=True)
|
||||||
@ -54,6 +54,7 @@ class TB(Module):
|
|||||||
self.comb += [
|
self.comb += [
|
||||||
adc_sck_en.eq(self._dly(self.sck_en, 1)),
|
adc_sck_en.eq(self._dly(self.sck_en, 1)),
|
||||||
self.sck_en_ret.eq(self._dly(adc_sck_en)),
|
self.sck_en_ret.eq(self._dly(adc_sck_en)),
|
||||||
|
|
||||||
adc_clk_rec.eq(self._dly(self.sck, 1)),
|
adc_clk_rec.eq(self._dly(self.sck, 1)),
|
||||||
self.clkout.eq(self._dly(adc_clk_rec)),
|
self.clkout.eq(self._dly(adc_clk_rec)),
|
||||||
]
|
]
|
||||||
|
@ -93,10 +93,6 @@ def main():
|
|||||||
"adc": (8, 0),
|
"adc": (8, 0),
|
||||||
"ret": (8, 0),
|
"ret": (8, 0),
|
||||||
"async": (2, 0),
|
"async": (2, 0),
|
||||||
},
|
|
||||||
special_overrides={
|
|
||||||
io.DDROutput: test_adc.DDROutput,
|
|
||||||
io.DDRInput: test_adc.DDRInput
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user