forked from M-Labs/artiq
suservo: lots of gateware/ runtime changes
tested/validated: * servo enable/disable * dds interface, timing, io_update, mask_nu * channel control (en_out, en_iir, profile) * profile configuration (coefficients, delays, offsets, channel) * adc timings and waveforms measured * asf state readback * adc readback individual changes below: suservo: correct rtio readback suservo: example, device_db [wip] suservo: change rtio channel layout suservo: mem ports in rio domain suservo: sck clocked from rio_phy suservo: cleanup, straighten out timing suservo: dds cs polarity suservo: simplify pipeline suservo: drop unused eem names suservo: decouple adc SR from IIR suservo: expand coredevice layer suservo: start the correct stage suservo: actually load ctrl suservo: refactor/tweak adc timing suservo: implement cpld and dds init
This commit is contained in:
parent
01f762a8f5
commit
307cd07b9d
|
@ -0,0 +1,132 @@
|
||||||
|
from artiq.language.core import kernel, delay, portable, now_mu
|
||||||
|
from artiq.language.units import us, ms
|
||||||
|
from artiq.coredevice.rtio import rtio_output, rtio_input_data
|
||||||
|
|
||||||
|
from numpy import int32, int64
|
||||||
|
|
||||||
|
from artiq.coredevice import spi2 as spi
|
||||||
|
from artiq.coredevice import urukul, sampler
|
||||||
|
|
||||||
|
|
||||||
|
COEFF_WIDTH = 18
|
||||||
|
COEFF_DEPTH = 10 + 1
|
||||||
|
WE = 1 << COEFF_DEPTH + 1
|
||||||
|
STATE_SEL = 1 << COEFF_DEPTH
|
||||||
|
CONFIG_SEL = 1 << COEFF_DEPTH - 1
|
||||||
|
CONFIG_ADDR = CONFIG_SEL | STATE_SEL
|
||||||
|
|
||||||
|
|
||||||
|
class SUServo:
|
||||||
|
kernel_invariants = {"channel", "core", "pgia", "cpld0", "cpld1",
|
||||||
|
"dds0", "dds1", "ref_period_mu"}
|
||||||
|
|
||||||
|
def __init__(self, dmgr, channel, pgia_device,
|
||||||
|
cpld0_device, cpld1_device,
|
||||||
|
dds0_device, dds1_device,
|
||||||
|
core_device="core"):
|
||||||
|
|
||||||
|
self.core = dmgr.get(core_device)
|
||||||
|
self.pgia = dmgr.get(pgia_device)
|
||||||
|
self.dds0 = dmgr.get(dds0_device)
|
||||||
|
self.dds1 = dmgr.get(dds1_device)
|
||||||
|
self.cpld0 = dmgr.get(cpld0_device)
|
||||||
|
self.cpld1 = dmgr.get(cpld1_device)
|
||||||
|
self.channel = channel
|
||||||
|
self.gains = 0x0000
|
||||||
|
self.ref_period_mu = self.core.seconds_to_mu(
|
||||||
|
self.core.coarse_ref_period)
|
||||||
|
assert self.ref_period_mu == self.core.ref_multiplier
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def init(self):
|
||||||
|
self.set_config(0)
|
||||||
|
delay(2*us) # pipeline flush
|
||||||
|
|
||||||
|
self.pgia.set_config_mu(
|
||||||
|
sampler.SPI_CONFIG | spi.SPI_END,
|
||||||
|
16, 4, sampler.SPI_CS_PGIA)
|
||||||
|
|
||||||
|
self.cpld0.init(blind=True)
|
||||||
|
cfg0 = self.cpld0.cfg_reg
|
||||||
|
self.cpld0.cfg_write(cfg0 | (0xf << urukul.CFG_MASK_NU))
|
||||||
|
self.dds0.init(blind=True)
|
||||||
|
self.cpld0.cfg_write(cfg0)
|
||||||
|
|
||||||
|
self.cpld1.init(blind=True)
|
||||||
|
cfg1 = self.cpld1.cfg_reg
|
||||||
|
self.cpld1.cfg_write(cfg1 | (0xf << urukul.CFG_MASK_NU))
|
||||||
|
self.dds1.init(blind=True)
|
||||||
|
self.cpld1.cfg_write(cfg1)
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def write(self, addr, value):
|
||||||
|
rtio_output(now_mu(), self.channel, addr | WE, value)
|
||||||
|
delay_mu(self.ref_period_mu)
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def read(self, addr):
|
||||||
|
rtio_output(now_mu(), self.channel, addr, 0)
|
||||||
|
return rtio_input_data(self.channel)
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def set_config(self, start):
|
||||||
|
self.write(CONFIG_ADDR, start)
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def get_status(self):
|
||||||
|
return self.read(CONFIG_ADDR)
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def get_adc_mu(self, adc):
|
||||||
|
return self.read(STATE_SEL | (adc << 1) | (1 << 8))
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def set_gain_mu(self, channel, gain):
|
||||||
|
"""Set instrumentation amplifier gain of a channel.
|
||||||
|
|
||||||
|
The four gain settings (0, 1, 2, 3) corresponds to gains of
|
||||||
|
(1, 10, 100, 1000) respectively.
|
||||||
|
|
||||||
|
:param channel: Channel index
|
||||||
|
:param gain: Gain setting
|
||||||
|
"""
|
||||||
|
gains = self.gains
|
||||||
|
gains &= ~(0b11 << (channel*2))
|
||||||
|
gains |= gain << (channel*2)
|
||||||
|
self.pgia.write(gains << 16)
|
||||||
|
self.gains = gains
|
||||||
|
|
||||||
|
|
||||||
|
class Channel:
|
||||||
|
kernel_invariants = {"channel", "core", "servo", "servo_channel"}
|
||||||
|
|
||||||
|
def __init__(self, dmgr, channel, servo_device,
|
||||||
|
core_device="core"):
|
||||||
|
self.core = dmgr.get(core_device)
|
||||||
|
self.servo = dmgr.get(servo_device)
|
||||||
|
self.channel = channel
|
||||||
|
self.servo_channel = self.channel + 8 - self.servo.channel # FIXME
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def set(self, en_out, en_iir=0, profile=0):
|
||||||
|
rtio_output(now_mu(), self.channel, 0,
|
||||||
|
en_out | (en_iir << 1) | (profile << 2))
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def set_profile_mu(self, profile, ftw, adc, offset,
|
||||||
|
a1, b0, b1, delay, pow=0):
|
||||||
|
base = (self.servo_channel << 8) | (profile << 3)
|
||||||
|
data = [ftw >> 16, b1, pow, adc | (delay << 8), offset, a1, ftw, b0]
|
||||||
|
for i in range(8):
|
||||||
|
self.servo.write(base + i, data[i])
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def get_profile_mu(self, profile, data):
|
||||||
|
base = (self.servo_channel << 8) | (profile << 3)
|
||||||
|
for i in range(8):
|
||||||
|
data[i] = self.servo.read(base + i)
|
||||||
|
delay(2*us)
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def get_asf_mu(self, profile):
|
||||||
|
return self.servo.read(STATE_SEL | (self.servo_channel << 5) | profile)
|
|
@ -1,4 +1,4 @@
|
||||||
core_addr = "lauda.ber.quartiq.de"
|
core_addr = "10.0.16.119"
|
||||||
|
|
||||||
device_db = {
|
device_db = {
|
||||||
"core": {
|
"core": {
|
||||||
|
@ -141,9 +141,11 @@ device_db = {
|
||||||
"class": "SUServo",
|
"class": "SUServo",
|
||||||
"arguments": {
|
"arguments": {
|
||||||
"channel": 24,
|
"channel": 24,
|
||||||
"sampler_pgia_device": "spi_sampler0_pgia",
|
"pgia_device": "spi_sampler0_pgia",
|
||||||
"urukul0_device": "urukul0_cpld",
|
"cpld0_device": "urukul0_cpld",
|
||||||
"urukul1_device": "urukul1_cpld"
|
"cpld1_device": "urukul1_cpld",
|
||||||
|
"dds0_device": "urukul0_dds",
|
||||||
|
"dds1_device": "urukul1_dds"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -215,10 +217,20 @@ device_db = {
|
||||||
"class": "CPLD",
|
"class": "CPLD",
|
||||||
"arguments": {
|
"arguments": {
|
||||||
"spi_device": "spi_urukul0",
|
"spi_device": "spi_urukul0",
|
||||||
"refclk": 125e6,
|
"refclk": 100e6,
|
||||||
"clk_sel": 0
|
"clk_sel": 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"urukul0_dds": {
|
||||||
|
"type": "local",
|
||||||
|
"module": "artiq.coredevice.ad9910",
|
||||||
|
"class": "AD9910",
|
||||||
|
"arguments": {
|
||||||
|
"pll_n": 40,
|
||||||
|
"chip_select": 3,
|
||||||
|
"cpld_device": "urukul0_cpld",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
"spi_urukul1": {
|
"spi_urukul1": {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
|
@ -232,10 +244,20 @@ device_db = {
|
||||||
"class": "CPLD",
|
"class": "CPLD",
|
||||||
"arguments": {
|
"arguments": {
|
||||||
"spi_device": "spi_urukul1",
|
"spi_device": "spi_urukul1",
|
||||||
"refclk": 125e6,
|
"refclk": 100e6,
|
||||||
"clk_sel": 0
|
"clk_sel": 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"urukul1_dds": {
|
||||||
|
"type": "local",
|
||||||
|
"module": "artiq.coredevice.ad9910",
|
||||||
|
"class": "AD9910",
|
||||||
|
"arguments": {
|
||||||
|
"pll_n": 40,
|
||||||
|
"chip_select": 3,
|
||||||
|
"cpld_device": "urukul1_cpld",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
"led0": {
|
"led0": {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
from artiq.experiment import *
|
|
||||||
|
|
||||||
|
|
||||||
class Sampler(EnvExperiment):
|
|
||||||
def build(self):
|
|
||||||
self.setattr_device("core")
|
|
||||||
self.setattr_device("sampler0")
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
self.data = []
|
|
||||||
self.sample()
|
|
||||||
for d in self.data:
|
|
||||||
print(d)
|
|
||||||
|
|
||||||
@kernel
|
|
||||||
def sample(self):
|
|
||||||
self.core.break_realtime()
|
|
||||||
self.sampler0.init()
|
|
||||||
for g in range(4):
|
|
||||||
for ch in range(8):
|
|
||||||
self.sampler0.set_gain_mu(ch, g)
|
|
||||||
self.ret([self.sampler0.get_gains_mu()])
|
|
||||||
delay(10*ms)
|
|
||||||
raw = [0] * 8
|
|
||||||
self.sampler0.sample_mu(raw)
|
|
||||||
self.ret(raw)
|
|
||||||
delay(10*ms)
|
|
||||||
data = [0.] * 8
|
|
||||||
self.sampler0.sample(data)
|
|
||||||
self.ret(data)
|
|
||||||
delay(10*ms)
|
|
||||||
|
|
||||||
@rpc(flags={"async"})
|
|
||||||
def ret(self, data):
|
|
||||||
self.data.append(data)
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
from artiq.experiment import *
|
||||||
|
|
||||||
|
|
||||||
|
class SUServo(EnvExperiment):
|
||||||
|
def build(self):
|
||||||
|
self.setattr_device("core")
|
||||||
|
self.setattr_device("led0")
|
||||||
|
self.setattr_device("suservo0")
|
||||||
|
self.setattr_device("suservo0_ch0")
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
# self.led()
|
||||||
|
self.init()
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def init(self):
|
||||||
|
self.core.break_realtime()
|
||||||
|
self.core.reset()
|
||||||
|
|
||||||
|
self.suservo0.init()
|
||||||
|
self.suservo0.set_config(1)
|
||||||
|
print(self.suservo0.get_status())
|
||||||
|
delay(3*ms)
|
||||||
|
self.suservo0.set_config(0)
|
||||||
|
delay(3*ms)
|
||||||
|
print(self.suservo0.get_status())
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def led(self):
|
||||||
|
self.core.break_realtime()
|
||||||
|
for i in range(10):
|
||||||
|
self.led0.pulse(.1*s)
|
||||||
|
delay(.1*s)
|
|
@ -7,18 +7,19 @@ class RTServoCtrl(Module):
|
||||||
"""Per channel RTIO control interface"""
|
"""Per channel RTIO control interface"""
|
||||||
def __init__(self, ctrl):
|
def __init__(self, ctrl):
|
||||||
self.rtlink = rtlink.Interface(
|
self.rtlink = rtlink.Interface(
|
||||||
rtlink.OInterface(len(ctrl)))
|
rtlink.OInterface(len(ctrl.profile) + 2))
|
||||||
|
|
||||||
# # #
|
# # #
|
||||||
|
|
||||||
self.sync.rio += [
|
|
||||||
If(self.rtlink.o.stb,
|
|
||||||
Cat(ctrl.profile, ctrl.en_out, ctrl.en_iir).eq(
|
|
||||||
self.rtlink.o.data),
|
|
||||||
)
|
|
||||||
]
|
|
||||||
self.comb += [
|
self.comb += [
|
||||||
ctrl.stb.eq(self.rtlink.o.stb)
|
ctrl.stb.eq(self.rtlink.o.stb),
|
||||||
|
self.rtlink.o.busy.eq(0)
|
||||||
|
]
|
||||||
|
self.sync.rio_phy += [
|
||||||
|
If(self.rtlink.o.stb,
|
||||||
|
Cat(ctrl.en_out, ctrl.en_iir, ctrl.profile).eq(
|
||||||
|
self.rtlink.o.data)
|
||||||
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,10 +27,11 @@ class RTServoMem(Module):
|
||||||
"""All-channel all-profile coefficient and state RTIO control
|
"""All-channel all-profile coefficient and state RTIO control
|
||||||
interface."""
|
interface."""
|
||||||
def __init__(self, w, servo):
|
def __init__(self, w, servo):
|
||||||
m_coeff = servo.m_coeff.get_port(write_capable=True,
|
m_coeff = servo.iir.m_coeff.get_port(write_capable=True,
|
||||||
we_granularity=w.coeff)
|
we_granularity=w.coeff, clock_domain="rio")
|
||||||
assert len(m_coeff.we) == 2
|
assert len(m_coeff.we) == 2
|
||||||
m_state = servo.m_state.get_port(write_capable=True)
|
m_state = servo.iir.m_state.get_port(write_capable=True,
|
||||||
|
clock_domain="rio")
|
||||||
self.specials += m_state, m_coeff
|
self.specials += m_state, m_coeff
|
||||||
|
|
||||||
# just expose the w.coeff (18) MSBs of state
|
# just expose the w.coeff (18) MSBs of state
|
||||||
|
@ -52,8 +54,8 @@ class RTServoMem(Module):
|
||||||
|
|
||||||
# # #
|
# # #
|
||||||
|
|
||||||
config = Signal(1, reset=0)
|
config = Signal(w.coeff, reset=0)
|
||||||
status = Signal(2)
|
status = Signal(w.coeff)
|
||||||
self.comb += [
|
self.comb += [
|
||||||
Cat(servo.start).eq(config),
|
Cat(servo.start).eq(config),
|
||||||
status.eq(Cat(servo.start, servo.done))
|
status.eq(Cat(servo.start, servo.done))
|
||||||
|
@ -87,16 +89,18 @@ class RTServoMem(Module):
|
||||||
m_state.we.eq(self.rtlink.o.stb & we & state_sel & ~config_sel),
|
m_state.we.eq(self.rtlink.o.stb & we & state_sel & ~config_sel),
|
||||||
]
|
]
|
||||||
read = Signal()
|
read = Signal()
|
||||||
read_sel = Signal()
|
read_state = Signal()
|
||||||
read_high = Signal()
|
read_high = Signal()
|
||||||
|
read_config = Signal()
|
||||||
self.sync.rio += [
|
self.sync.rio += [
|
||||||
If(read,
|
If(read,
|
||||||
read.eq(0)
|
read.eq(0)
|
||||||
),
|
),
|
||||||
If(self.rtlink.o.stb,
|
If(self.rtlink.o.stb,
|
||||||
read.eq(~we),
|
read.eq(~we),
|
||||||
read_sel.eq(state_sel),
|
read_state.eq(state_sel),
|
||||||
read_high.eq(high_coeff),
|
read_high.eq(high_coeff),
|
||||||
|
read_config.eq(config_sel),
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
self.sync.rio_phy += [
|
self.sync.rio_phy += [
|
||||||
|
@ -107,8 +111,8 @@ class RTServoMem(Module):
|
||||||
self.comb += [
|
self.comb += [
|
||||||
self.rtlink.i.stb.eq(read),
|
self.rtlink.i.stb.eq(read),
|
||||||
self.rtlink.i.data.eq(
|
self.rtlink.i.data.eq(
|
||||||
Mux(state_sel,
|
Mux(read_state,
|
||||||
Mux(config_sel,
|
Mux(read_config,
|
||||||
status,
|
status,
|
||||||
m_state.dat_r[w.state - w.coeff:]),
|
m_state.dat_r[w.state - w.coeff:]),
|
||||||
Mux(read_high,
|
Mux(read_high,
|
||||||
|
|
|
@ -55,13 +55,13 @@ class ADC(Module):
|
||||||
assert 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 - 1, t_read, p.t_rtt + 1) - 1,
|
count = Signal(max=max(p.t_cnvh, p.t_conv, t_read, p.t_rtt),
|
||||||
reset_less=True)
|
reset_less=True)
|
||||||
count_load = Signal.like(count)
|
count_load = Signal.like(count)
|
||||||
count_done = Signal()
|
count_done = Signal()
|
||||||
self.comb += [
|
update = Signal()
|
||||||
count_done.eq(count == 0),
|
|
||||||
]
|
self.comb += count_done.eq(count == 0)
|
||||||
self.sync += [
|
self.sync += [
|
||||||
count.eq(count - 1),
|
count.eq(count - 1),
|
||||||
If(count_done,
|
If(count_done,
|
||||||
|
@ -78,7 +78,7 @@ class ADC(Module):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
fsm.act("CNVH",
|
fsm.act("CNVH",
|
||||||
count_load.eq(p.t_conv - 2), # account for sck ODDR delay
|
count_load.eq(p.t_conv - 1),
|
||||||
pads.cnv.eq(1),
|
pads.cnv.eq(1),
|
||||||
If(count_done,
|
If(count_done,
|
||||||
NextState("CONV")
|
NextState("CONV")
|
||||||
|
@ -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), # again account for sck ODDR delay
|
count_load.eq(p.t_rtt - 1),
|
||||||
pads.sck_en.eq(1),
|
pads.sck_en.eq(1),
|
||||||
If(count_done,
|
If(count_done,
|
||||||
NextState("RTT")
|
NextState("RTT")
|
||||||
|
@ -101,6 +101,7 @@ class ADC(Module):
|
||||||
fsm.act("RTT", # account for sck->clkout round trip time
|
fsm.act("RTT", # account for sck->clkout round trip time
|
||||||
self.reading.eq(1),
|
self.reading.eq(1),
|
||||||
If(count_done,
|
If(count_done,
|
||||||
|
update.eq(1),
|
||||||
NextState("IDLE")
|
NextState("IDLE")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -111,10 +112,7 @@ class ADC(Module):
|
||||||
sck_en_ret = 1
|
sck_en_ret = 1
|
||||||
|
|
||||||
self.clock_domains.cd_ret = ClockDomain("ret", reset_less=True)
|
self.clock_domains.cd_ret = ClockDomain("ret", reset_less=True)
|
||||||
self.comb += [
|
self.comb += self.cd_ret.clk.eq(pads.clkout)
|
||||||
# falling clkout makes two bits available
|
|
||||||
self.cd_ret.clk.eq(pads.clkout)
|
|
||||||
]
|
|
||||||
|
|
||||||
k = p.channels//p.lanes
|
k = p.channels//p.lanes
|
||||||
assert t_read == k*p.width
|
assert t_read == k*p.width
|
||||||
|
@ -126,7 +124,9 @@ class ADC(Module):
|
||||||
sdo_sr[0].eq(sdo),
|
sdo_sr[0].eq(sdo),
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
self.comb += [
|
self.sync += [
|
||||||
Cat(reversed([self.data[i*k + j] for j in range(k)])
|
If(update,
|
||||||
).eq(sdo_sr)
|
Cat(reversed([self.data[i*k + j] for j in range(k)])
|
||||||
|
).eq(sdo_sr)
|
||||||
|
)
|
||||||
]
|
]
|
||||||
|
|
|
@ -36,13 +36,8 @@ class DDS(spi.SPISimple):
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
io_update = pads.io_update
|
|
||||||
# this assumes that the cycle time (1/125 MHz = 8 ns) is >1 SYNC_CLK
|
# this assumes that the cycle time (1/125 MHz = 8 ns) is >1 SYNC_CLK
|
||||||
# cycle (1/250 MHz = 4ns)
|
# cycle (1/250 MHz = 4ns)
|
||||||
done = Signal()
|
done_old = Signal()
|
||||||
self.sync += [
|
self.sync += done_old.eq(self.done)
|
||||||
done.eq(self.done)
|
self.comb += pads.io_update.eq(self.done & ~done_old)
|
||||||
]
|
|
||||||
self.comb += [
|
|
||||||
io_update.eq(self.done & ~done)
|
|
||||||
]
|
|
||||||
|
|
|
@ -3,17 +3,17 @@ from migen.genlib.io import DifferentialOutput, DifferentialInput, DDROutput
|
||||||
|
|
||||||
|
|
||||||
class SamplerPads(Module):
|
class SamplerPads(Module):
|
||||||
def __init__(self, platform, eem0, eem1):
|
def __init__(self, platform, eem):
|
||||||
self.sck_en = Signal()
|
self.sck_en = Signal()
|
||||||
self.cnv = Signal()
|
self.cnv = Signal()
|
||||||
self.clkout = Signal()
|
self.clkout = Signal()
|
||||||
|
|
||||||
spip = platform.request("{}_adc_spi_p".format(eem0))
|
spip = platform.request("{}_adc_spi_p".format(eem))
|
||||||
spin = platform.request("{}_adc_spi_n".format(eem0))
|
spin = platform.request("{}_adc_spi_n".format(eem))
|
||||||
cnv = platform.request("{}_cnv".format(eem0))
|
cnv = platform.request("{}_cnv".format(eem))
|
||||||
sdr = platform.request("{}_sdr".format(eem0))
|
sdr = platform.request("{}_sdr".format(eem))
|
||||||
dp = platform.request("{}_adc_data_p".format(eem0))
|
dp = platform.request("{}_adc_data_p".format(eem))
|
||||||
dn = platform.request("{}_adc_data_n".format(eem0))
|
dn = platform.request("{}_adc_data_n".format(eem))
|
||||||
|
|
||||||
clkout_se = Signal()
|
clkout_se = Signal()
|
||||||
sck = Signal()
|
sck = Signal()
|
||||||
|
@ -21,7 +21,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(self.sck_en, 0, sck),
|
DDROutput(0, self.sck_en, sck, ClockSignal("rio_phy")),
|
||||||
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("BUFR", i_I=clkout_se, o_O=self.clkout)
|
Instance("BUFR", i_I=clkout_se, o_O=self.clkout)
|
||||||
|
@ -52,17 +52,17 @@ class SamplerPads(Module):
|
||||||
|
|
||||||
|
|
||||||
class UrukulPads(Module):
|
class UrukulPads(Module):
|
||||||
def __init__(self, platform, eem00, eem01, eem10, eem11):
|
def __init__(self, platform, eem0, eem1):
|
||||||
spip, spin = [[
|
spip, spin = [[
|
||||||
platform.request("{}_qspi_{}".format(eem, pol), 0)
|
platform.request("{}_qspi_{}".format(eem, pol), 0)
|
||||||
for eem in (eem00, eem10)] for pol in "pn"]
|
for eem in (eem0, eem1)] for pol in "pn"]
|
||||||
ioup = [platform.request("{}_io_update".format(eem), 0)
|
ioup = [platform.request("{}_io_update".format(eem), 0)
|
||||||
for eem in (eem00, eem10)]
|
for eem in (eem0, eem1)]
|
||||||
self.cs_n = Signal()
|
self.cs_n = Signal()
|
||||||
self.clk = Signal()
|
self.clk = Signal()
|
||||||
self.io_update = Signal()
|
self.io_update = Signal()
|
||||||
self.specials += [(
|
self.specials += [(
|
||||||
DifferentialOutput(self.cs_n, spip[i].cs_n, spin[i].cs_n),
|
DifferentialOutput(~self.cs_n, spip[i].cs, spin[i].cs),
|
||||||
DifferentialOutput(self.clk, spip[i].clk, spin[i].clk),
|
DifferentialOutput(self.clk, spip[i].clk, spin[i].clk),
|
||||||
DifferentialOutput(self.io_update, ioup[i].p, ioup[i].n))
|
DifferentialOutput(self.io_update, ioup[i].p, ioup[i].n))
|
||||||
for i in range(2)]
|
for i in range(2)]
|
||||||
|
|
|
@ -24,38 +24,37 @@ class Servo(Module):
|
||||||
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_adc
|
t_restart = t_cycle - t_adc + 1
|
||||||
assert t_restart > 0
|
assert t_restart > 0
|
||||||
cnt = Signal(max=t_restart + 1)
|
cnt = Signal(max=t_restart)
|
||||||
cnt_done = Signal()
|
cnt_done = Signal()
|
||||||
token = Signal(2)
|
active = Signal(3)
|
||||||
self.done = Signal()
|
self.done = Signal()
|
||||||
iir_done = Signal()
|
|
||||||
self.comb += [
|
|
||||||
cnt_done.eq(cnt == 0),
|
|
||||||
iir_done.eq(self.iir.shifting | self.iir.done),
|
|
||||||
self.adc.start.eq(self.start & cnt_done),
|
|
||||||
self.iir.start.eq(token[0] & self.adc.done),
|
|
||||||
self.dds.start.eq(token[1] & iir_done),
|
|
||||||
self.done.eq(self.dds.done),
|
|
||||||
]
|
|
||||||
self.sync += [
|
self.sync += [
|
||||||
If(self.adc.done & ~cnt_done,
|
If(self.dds.done,
|
||||||
cnt.eq(cnt - 1),
|
active[2].eq(0)
|
||||||
),
|
),
|
||||||
If(self.adc.start,
|
If(self.dds.start & self.dds.done,
|
||||||
cnt.eq(t_restart),
|
active[2].eq(1),
|
||||||
|
active[1].eq(0)
|
||||||
),
|
),
|
||||||
If(self.adc.done,
|
If(self.iir.start & self.iir.done,
|
||||||
token[0].eq(0)
|
active[1].eq(1),
|
||||||
|
active[0].eq(0)
|
||||||
),
|
),
|
||||||
If(self.adc.start,
|
If(~cnt_done & self.adc.done,
|
||||||
token[0].eq(1)
|
cnt.eq(cnt - 1)
|
||||||
),
|
),
|
||||||
If(iir_done,
|
If(self.adc.start & self.adc.done,
|
||||||
token[1].eq(0)
|
active[0].eq(1),
|
||||||
),
|
cnt.eq(t_restart - 1)
|
||||||
If(self.iir.start,
|
|
||||||
token[1].eq(1)
|
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
self.comb += [
|
||||||
|
cnt_done.eq(cnt == 0),
|
||||||
|
self.adc.start.eq(self.start & cnt_done),
|
||||||
|
self.iir.start.eq(active[0] & self.adc.done),
|
||||||
|
self.dds.start.eq(active[1] &
|
||||||
|
(self.iir.shifting | self.iir.done)),
|
||||||
|
self.done.eq(self.dds.done),
|
||||||
|
]
|
||||||
|
|
|
@ -37,8 +37,6 @@ class SPISimple(Module):
|
||||||
|
|
||||||
assert p.clk >= 1
|
assert p.clk >= 1
|
||||||
|
|
||||||
cs_n = pads.cs_n
|
|
||||||
clk = pads.clk
|
|
||||||
cnt = Signal(max=max(2, p.clk), reset_less=True)
|
cnt = Signal(max=max(2, p.clk), reset_less=True)
|
||||||
cnt_done = Signal()
|
cnt_done = Signal()
|
||||||
cnt_next = Signal()
|
cnt_next = Signal()
|
||||||
|
@ -54,21 +52,17 @@ class SPISimple(Module):
|
||||||
]
|
]
|
||||||
|
|
||||||
for i, d in enumerate(self.data):
|
for i, d in enumerate(self.data):
|
||||||
self.comb += [
|
self.comb += getattr(pads, "mosi{}".format(i)).eq(d[-1])
|
||||||
getattr(pads, "mosi{}".format(i)).eq(d[-1])
|
|
||||||
]
|
|
||||||
|
|
||||||
bits = Signal(max=p.width + 1, reset_less=True)
|
bits = Signal(max=p.width + 1, reset_less=True)
|
||||||
|
|
||||||
self.submodules.fsm = fsm = CEInserter()(FSM("IDLE"))
|
self.submodules.fsm = fsm = CEInserter()(FSM("IDLE"))
|
||||||
|
|
||||||
self.comb += [
|
self.comb += fsm.ce.eq(cnt_done)
|
||||||
fsm.ce.eq(cnt_done)
|
|
||||||
]
|
|
||||||
|
|
||||||
fsm.act("IDLE",
|
fsm.act("IDLE",
|
||||||
self.done.eq(1),
|
self.done.eq(1),
|
||||||
cs_n.eq(1),
|
pads.cs_n.eq(1),
|
||||||
If(self.start,
|
If(self.start,
|
||||||
cnt_next.eq(1),
|
cnt_next.eq(1),
|
||||||
NextState("SETUP")
|
NextState("SETUP")
|
||||||
|
@ -84,7 +78,7 @@ class SPISimple(Module):
|
||||||
)
|
)
|
||||||
fsm.act("HOLD",
|
fsm.act("HOLD",
|
||||||
cnt_next.eq(1),
|
cnt_next.eq(1),
|
||||||
clk.eq(1),
|
pads.clk.eq(1),
|
||||||
NextState("SETUP")
|
NextState("SETUP")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -224,7 +224,7 @@ def _sampler(eem, eem_aux=None):
|
||||||
Subsignal("sdod", Pins(_eem_pin(eem_aux, 4, "n"))),
|
Subsignal("sdod", Pins(_eem_pin(eem_aux, 4, "n"))),
|
||||||
IOStandard("LVDS_25"),
|
IOStandard("LVDS_25"),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
return ios
|
return ios
|
||||||
|
|
||||||
|
|
||||||
|
@ -362,7 +362,7 @@ def _urukul_qspi(eem0, eem1):
|
||||||
))
|
))
|
||||||
ios += [
|
ios += [
|
||||||
("{}_qspi_p".format(eem0), 0,
|
("{}_qspi_p".format(eem0), 0,
|
||||||
Subsignal("cs_n", Pins(_eem_pin(eem0, 5, "p"))),
|
Subsignal("cs", Pins(_eem_pin(eem0, 5, "p"))),
|
||||||
Subsignal("clk", Pins(_eem_pin(eem0, 2, "p"))),
|
Subsignal("clk", Pins(_eem_pin(eem0, 2, "p"))),
|
||||||
Subsignal("mosi0", Pins(_eem_pin(eem1, 0, "p"))),
|
Subsignal("mosi0", Pins(_eem_pin(eem1, 0, "p"))),
|
||||||
Subsignal("mosi1", Pins(_eem_pin(eem1, 1, "p"))),
|
Subsignal("mosi1", Pins(_eem_pin(eem1, 1, "p"))),
|
||||||
|
@ -371,7 +371,7 @@ def _urukul_qspi(eem0, eem1):
|
||||||
IOStandard("LVDS_25"),
|
IOStandard("LVDS_25"),
|
||||||
),
|
),
|
||||||
("{}_qspi_n".format(eem0), 0,
|
("{}_qspi_n".format(eem0), 0,
|
||||||
Subsignal("cs_n", Pins(_eem_pin(eem0, 5, "n"))),
|
Subsignal("cs", Pins(_eem_pin(eem0, 5, "n"))),
|
||||||
Subsignal("clk", Pins(_eem_pin(eem0, 2, "n"))),
|
Subsignal("clk", Pins(_eem_pin(eem0, 2, "n"))),
|
||||||
Subsignal("mosi0", Pins(_eem_pin(eem1, 0, "n"))),
|
Subsignal("mosi0", Pins(_eem_pin(eem1, 0, "n"))),
|
||||||
Subsignal("mosi1", Pins(_eem_pin(eem1, 1, "n"))),
|
Subsignal("mosi1", Pins(_eem_pin(eem1, 1, "n"))),
|
||||||
|
@ -525,25 +525,25 @@ class SUServo(_StandaloneBase):
|
||||||
rtio_channels.append(rtio.Channel.from_phy(phy))
|
rtio_channels.append(rtio.Channel.from_phy(phy))
|
||||||
|
|
||||||
# EEM3, EEM2: Sampler
|
# EEM3, EEM2: Sampler
|
||||||
sampler_pads = servo_pads.SamplerPads(self.platform, "eem3", "eem2")
|
sampler_pads = servo_pads.SamplerPads(self.platform, "eem3")
|
||||||
# EEM5, EEM4 and EEM7, EEM6: Urukul
|
# EEM5, EEM4 and EEM7, EEM6: Urukul
|
||||||
urukul_pads = servo_pads.UrukulPads(self.platform,
|
urukul_pads = servo_pads.UrukulPads(self.platform,
|
||||||
"eem5", "eem4", "eem7", "eem6")
|
"eem5", "eem7")
|
||||||
adc_p = servo.ADCParams(width=16, channels=8, lanes=4,
|
adc_p = servo.ADCParams(width=16, channels=8, lanes=4, t_cnvh=4,
|
||||||
t_cnvh=4, t_conv=57, t_rtt=4)
|
# account for SCK pipeline latency
|
||||||
iir_p = servo.IIRWidths(state=25, coeff=18, adc=16,
|
t_conv=57 - 4, t_rtt=4 + 4)
|
||||||
asf=14, word=16, accu=48, shift=11,
|
iir_p = servo.IIRWidths(state=25, coeff=18, adc=16, asf=14, word=16,
|
||||||
channel=3, profile=5)
|
accu=48, shift=11, channel=3, profile=5)
|
||||||
dds_p = servo.DDSParams(width=8 + 32 + 16 + 16,
|
dds_p = servo.DDSParams(width=8 + 32 + 16 + 16,
|
||||||
channels=adc_p.channels, clk=1)
|
channels=adc_p.channels, clk=1)
|
||||||
su = servo.Servo(sampler_pads, urukul_pads, adc_p, iir_p, dds_p)
|
su = servo.Servo(sampler_pads, urukul_pads, adc_p, iir_p, dds_p)
|
||||||
su = ClockDomainsRenamer({"sys": "rio_phy"})(su)
|
su = ClockDomainsRenamer("rio_phy")(su)
|
||||||
self.submodules += sampler_pads, urukul_pads, su
|
self.submodules += sampler_pads, urukul_pads, su
|
||||||
|
|
||||||
ctrls = [rtservo.RTServoCtrl(ctrl) for ctrl in su.iir.ctrl]
|
ctrls = [rtservo.RTServoCtrl(ctrl) for ctrl in su.iir.ctrl]
|
||||||
self.submodules += ctrls
|
self.submodules += ctrls
|
||||||
rtio_channels.extend(rtio.Channel.from_phy(ctrl) for ctrl in ctrls)
|
rtio_channels.extend(rtio.Channel.from_phy(ctrl) for ctrl in ctrls)
|
||||||
mem = rtservo.RTServoMem(iir_p, su.iir)
|
mem = rtservo.RTServoMem(iir_p, su)
|
||||||
self.submodules += mem
|
self.submodules += mem
|
||||||
rtio_channels.append(rtio.Channel.from_phy(mem, ififo_depth=4))
|
rtio_channels.append(rtio.Channel.from_phy(mem, ififo_depth=4))
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ class TB(Module):
|
||||||
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(self._dly(sr[-1], -1)),
|
sdo.eq(self._dly(sr[-1], 0)),
|
||||||
If(adc_sck_en,
|
If(adc_sck_en,
|
||||||
sr[1:].eq(sr)
|
sr[1:].eq(sr)
|
||||||
)
|
)
|
||||||
|
@ -52,10 +52,10 @@ class TB(Module):
|
||||||
|
|
||||||
adc_clk_rec = Signal()
|
adc_clk_rec = Signal()
|
||||||
self.comb += [
|
self.comb += [
|
||||||
adc_sck_en.eq(self._dly(self.sck_en, 1)),
|
adc_sck_en.eq(self._dly(self.sck_en, 0)),
|
||||||
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, 0)),
|
||||||
self.clkout.eq(self._dly(adc_clk_rec)),
|
self.clkout.eq(self._dly(adc_clk_rec)),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -11,10 +11,9 @@ from artiq.gateware.suservo import servo
|
||||||
class ServoSim(servo.Servo):
|
class ServoSim(servo.Servo):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
adc_p = servo.ADCParams(width=16, channels=8, lanes=4,
|
adc_p = servo.ADCParams(width=16, channels=8, lanes=4,
|
||||||
t_cnvh=4, t_conv=57, t_rtt=4)
|
t_cnvh=4, t_conv=57 - 4, t_rtt=4 + 4)
|
||||||
iir_p = servo.IIRWidths(state=25, coeff=18, adc=16,
|
iir_p = servo.IIRWidths(state=25, coeff=18, adc=16, asf=14, word=16,
|
||||||
asf=14, word=16, accu=48, shift=11,
|
accu=48, shift=11, channel=3, profile=5)
|
||||||
channel=3, profile=5)
|
|
||||||
dds_p = servo.DDSParams(width=8 + 32 + 16 + 16,
|
dds_p = servo.DDSParams(width=8 + 32 + 16 + 16,
|
||||||
channels=adc_p.channels, clk=1)
|
channels=adc_p.channels, clk=1)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue