mirror of https://github.com/m-labs/artiq.git
shuttler: support pre-DAC gain & offset
This commit is contained in:
parent
c02a14ba37
commit
1bb7e9ceef
|
@ -100,11 +100,16 @@ class Dac(Module):
|
||||||
data (Signal[16]): Output value to be send to the DAC.
|
data (Signal[16]): Output value to be send to the DAC.
|
||||||
clear (Signal): Clear accumulated phase offset when loading a new
|
clear (Signal): Clear accumulated phase offset when loading a new
|
||||||
waveform. Input.
|
waveform. Input.
|
||||||
|
gain (Signal[16]): Output value gain. The gain signal represents the
|
||||||
|
decimal part os the gain in 2's complement.
|
||||||
|
offset (Signal[16]): Output value offset.
|
||||||
i (Endpoint[]): Coefficients of the output lines.
|
i (Endpoint[]): Coefficients of the output lines.
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.clear = Signal()
|
self.clear = Signal()
|
||||||
self.data = Signal(16)
|
self.data = Signal(16)
|
||||||
|
self.gain = Signal(16)
|
||||||
|
self.offset = Signal(16)
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|
||||||
|
@ -113,8 +118,14 @@ class Dac(Module):
|
||||||
Dds(self.clear),
|
Dds(self.clear),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Infer signed multiplication
|
||||||
|
data_raw = Signal((14, True))
|
||||||
|
data_buf = Signal(14)
|
||||||
self.sync.rio += [
|
self.sync.rio += [
|
||||||
self.data.eq(reduce(add, [sub.data for sub in subs])),
|
data_raw.eq(reduce(add, [sub.data for sub in subs])),
|
||||||
|
# Extra buffer for better DSP timing
|
||||||
|
data_buf.eq(((data_raw * Cat(self.gain, ~self.gain[-1])) + (self.offset << 16))[16:]),
|
||||||
|
self.data.eq(data_buf),
|
||||||
]
|
]
|
||||||
|
|
||||||
self.i = [ sub.i for sub in subs ]
|
self.i = [ sub.i for sub in subs ]
|
||||||
|
@ -229,11 +240,43 @@ class Dds(Module):
|
||||||
class Config(Module):
|
class Config(Module):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.clr = Signal(16, reset=0xFFFF)
|
self.clr = Signal(16, reset=0xFFFF)
|
||||||
self.i = Endpoint([("data", 16)])
|
self.gain = [ Signal(16) for _ in range(16) ]
|
||||||
|
self.offset = [ Signal(16) for _ in range(16) ]
|
||||||
|
|
||||||
|
reg_file = Array(self.gain + self.offset + [self.clr])
|
||||||
|
self.i = Endpoint([
|
||||||
|
("data", 16),
|
||||||
|
("addr", 7),
|
||||||
|
])
|
||||||
|
self.o = Endpoint([
|
||||||
|
("data", 16),
|
||||||
|
])
|
||||||
|
|
||||||
# This introduces 1 extra latency to everything in config
|
# This introduces 1 extra latency to everything in config
|
||||||
# See the latency/delay attributes in Volt & DDS Endpoints/rtlinks
|
# See the latency/delay attributes in Volt & DDS Endpoints/rtlinks
|
||||||
self.sync.rio += If(self.i.stb, self.clr.eq(self.i.data))
|
#
|
||||||
|
# Gain & offsets are intended for initial calibration only, latency
|
||||||
|
# is NOT adjusted to match outputs to the DAC interface
|
||||||
|
#
|
||||||
|
# Interface address bits mapping:
|
||||||
|
# 6: Read bit. Assert to read, deassert to write.
|
||||||
|
# 5: Clear bit. Assert to write clr. clr is write-only.
|
||||||
|
# 4: Gain/Offset. (De)Assert to access (Gain)Offset registers.
|
||||||
|
# 0-3: Channel selection for the Gain & Offset registers.
|
||||||
|
#
|
||||||
|
# Reading Gain / Offset register generates an RTIOInput event
|
||||||
|
self.sync.rio += [
|
||||||
|
self.o.stb.eq(0),
|
||||||
|
If(self.i.stb,
|
||||||
|
If(~self.i.addr[6],
|
||||||
|
reg_file[self.i.addr[:6]].eq(self.i.data),
|
||||||
|
).Else(
|
||||||
|
# clr register is unreadable, as an optimization
|
||||||
|
self.o.data.eq(reg_file[self.i.addr[:5]]),
|
||||||
|
self.o.stb.eq(1),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
Phy = namedtuple("Phy", "rtlink probes overrides")
|
Phy = namedtuple("Phy", "rtlink probes overrides")
|
||||||
|
@ -258,13 +301,23 @@ class Shuttler(Module, AutoCSR):
|
||||||
self.phys = []
|
self.phys = []
|
||||||
|
|
||||||
self.submodules.cfg = Config()
|
self.submodules.cfg = Config()
|
||||||
cfg_rtl_iface = rtlink.Interface(rtlink.OInterface(
|
cfg_rtl_iface = rtlink.Interface(
|
||||||
|
rtlink.OInterface(
|
||||||
data_width=len(self.cfg.i.data),
|
data_width=len(self.cfg.i.data),
|
||||||
enable_replace=False))
|
address_width=len(self.cfg.i.addr),
|
||||||
|
enable_replace=False,
|
||||||
|
),
|
||||||
|
rtlink.IInterface(
|
||||||
|
data_width=len(self.cfg.o.data),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
self.comb += [
|
self.comb += [
|
||||||
self.cfg.i.stb.eq(cfg_rtl_iface.o.stb),
|
self.cfg.i.stb.eq(cfg_rtl_iface.o.stb),
|
||||||
|
self.cfg.i.addr.eq(cfg_rtl_iface.o.address),
|
||||||
self.cfg.i.data.eq(cfg_rtl_iface.o.data),
|
self.cfg.i.data.eq(cfg_rtl_iface.o.data),
|
||||||
|
cfg_rtl_iface.i.stb.eq(self.cfg.o.stb),
|
||||||
|
cfg_rtl_iface.i.data.eq(self.cfg.o.data),
|
||||||
]
|
]
|
||||||
self.phys.append(Phy(cfg_rtl_iface, [], []))
|
self.phys.append(Phy(cfg_rtl_iface, [], []))
|
||||||
|
|
||||||
|
@ -277,6 +330,8 @@ class Shuttler(Module, AutoCSR):
|
||||||
dac = Dac()
|
dac = Dac()
|
||||||
self.comb += [
|
self.comb += [
|
||||||
dac.clear.eq(self.cfg.clr[idx]),
|
dac.clear.eq(self.cfg.clr[idx]),
|
||||||
|
dac.gain.eq(self.cfg.gain[idx]),
|
||||||
|
dac.offset.eq(self.cfg.offset[idx]),
|
||||||
self.dac_interface.data[idx // 2][idx % 2].eq(dac.data)
|
self.dac_interface.data[idx // 2][idx % 2].eq(dac.data)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ from artiq.gateware.amp import AMPSoC
|
||||||
from artiq.gateware import rtio
|
from artiq.gateware import rtio
|
||||||
from artiq.gateware.rtio.xilinx_clocking import fix_serdes_timing_path
|
from artiq.gateware.rtio.xilinx_clocking import fix_serdes_timing_path
|
||||||
from artiq.gateware.rtio.phy import ttl_simple
|
from artiq.gateware.rtio.phy import ttl_simple
|
||||||
|
from artiq.gateware.rtio.phy import spi2 as rtio_spi
|
||||||
from artiq.gateware.drtio.transceiver import eem_serdes
|
from artiq.gateware.drtio.transceiver import eem_serdes
|
||||||
from artiq.gateware.drtio.rx_synchronizer import NoRXSynchronizer
|
from artiq.gateware.drtio.rx_synchronizer import NoRXSynchronizer
|
||||||
from artiq.gateware.drtio import *
|
from artiq.gateware.drtio import *
|
||||||
|
@ -145,6 +146,20 @@ class Satellite(BaseSoC, AMPSoC):
|
||||||
Subsignal('data', Pins('fmc0:HB13_N fmc0:HB12_N fmc0:HB13_P fmc0:HB12_P fmc0:HB15_N fmc0:HB15_P fmc0:HB11_N fmc0:HB09_N fmc0:HB09_P fmc0:HB14_N fmc0:HB14_P fmc0:HB10_N fmc0:HB10_P fmc0:HB11_P')),
|
Subsignal('data', Pins('fmc0:HB13_N fmc0:HB12_N fmc0:HB13_P fmc0:HB12_P fmc0:HB15_N fmc0:HB15_P fmc0:HB11_N fmc0:HB09_N fmc0:HB09_P fmc0:HB14_N fmc0:HB14_P fmc0:HB10_N fmc0:HB10_P fmc0:HB11_P')),
|
||||||
Subsignal('clk', Pins('fmc0:HB06_CC_P')),
|
Subsignal('clk', Pins('fmc0:HB06_CC_P')),
|
||||||
IOStandard('LVCMOS18')),
|
IOStandard('LVCMOS18')),
|
||||||
|
('afe_ctrl_dir', 0, Pins('fmc0:LA26_N fmc0:HB00_CC_N fmc0:HB17_CC_P'), IOStandard("LVCMOS18")),
|
||||||
|
('afe_ctrl_oe_n', 0, Pins('fmc0:HB19_N'), IOStandard("LVCMOS18")),
|
||||||
|
('afe_relay', 0,
|
||||||
|
Subsignal('clk', Pins('fmc0:LA02_N')),
|
||||||
|
Subsignal('mosi', Pins('fmc0:LA00_CC_N')),
|
||||||
|
Subsignal('cs_n', Pins('fmc0:LA02_P fmc0:LA01_CC_N')),
|
||||||
|
IOStandard("LVCMOS18")),
|
||||||
|
('afe_adc_spi', 0,
|
||||||
|
Subsignal('clk', Pins('fmc0:LA29_P')),
|
||||||
|
Subsignal('mosi', Pins('fmc0:LA29_N')),
|
||||||
|
Subsignal('miso', Pins('fmc0:LA30_N')),
|
||||||
|
Subsignal('cs_n', Pins('fmc0:LA28_P')),
|
||||||
|
IOStandard("LVCMOS18")),
|
||||||
|
('afe_adc_error_n', 0, Pins('fmc0:LA28_N'), IOStandard("LVCMOS18")),
|
||||||
]
|
]
|
||||||
|
|
||||||
platform.add_extension(shuttler_io)
|
platform.add_extension(shuttler_io)
|
||||||
|
@ -167,6 +182,25 @@ class Satellite(BaseSoC, AMPSoC):
|
||||||
self.csr_devices.append("shuttler")
|
self.csr_devices.append("shuttler")
|
||||||
self.rtio_channels.extend(rtio.Channel.from_phy(phy) for phy in self.shuttler.phys)
|
self.rtio_channels.extend(rtio.Channel.from_phy(phy) for phy in self.shuttler.phys)
|
||||||
|
|
||||||
|
afe_dir = platform.request("afe_ctrl_dir")
|
||||||
|
self.comb += afe_dir.eq(0b011)
|
||||||
|
|
||||||
|
afe_oe = platform.request("afe_ctrl_oe_n")
|
||||||
|
self.comb += afe_oe.eq(0)
|
||||||
|
|
||||||
|
relay_led_phy = rtio_spi.SPIMaster(self.platform.request("afe_relay"))
|
||||||
|
self.submodules += relay_led_phy
|
||||||
|
print("SHUTTLER RELAY at RTIO channel 0x{:06x}".format(len(self.rtio_channels)))
|
||||||
|
self.rtio_channels.append(rtio.Channel.from_phy(relay_led_phy))
|
||||||
|
|
||||||
|
adc_error_n = platform.request("afe_adc_error_n")
|
||||||
|
self.comb += adc_error_n.eq(1)
|
||||||
|
|
||||||
|
adc_spi = rtio_spi.SPIMaster(self.platform.request("afe_adc_spi"))
|
||||||
|
self.submodules += adc_spi
|
||||||
|
print("SHUTTLER ADC at RTIO channel 0x{:06x}".format(len(self.rtio_channels)))
|
||||||
|
self.rtio_channels.append(rtio.Channel.from_phy(adc_spi))
|
||||||
|
|
||||||
self.config["HAS_RTIO_LOG"] = None
|
self.config["HAS_RTIO_LOG"] = None
|
||||||
self.config["RTIO_LOG_CHANNEL"] = len(self.rtio_channels)
|
self.config["RTIO_LOG_CHANNEL"] = len(self.rtio_channels)
|
||||||
self.rtio_channels.append(rtio.LogChannel())
|
self.rtio_channels.append(rtio.LogChannel())
|
||||||
|
|
Loading…
Reference in New Issue