soc: support QC2 and AD9914 (untested)

This commit is contained in:
Sebastien Bourdeauducq 2015-06-28 21:37:27 +02:00
parent b6310b72db
commit 944bfafefa
6 changed files with 130 additions and 66 deletions

View File

@ -6,15 +6,14 @@ from migen.bus.transactions import *
from migen.sim.generic import run_simulation
class AD9858(Module):
"""Wishbone interface to the AD9858 DDS chip.
class AD9xxx(Module):
"""Wishbone interface to the AD9858 and AD9914 DDS chips.
Addresses 0-63 map the AD9858 registers.
Data is zero-padded.
Addresses 0-2**flen(pads.a)-1 map the AD9xxx registers.
Write to address 64 to pulse the FUD signal.
Address 65 is a GPIO register that controls the sel, p and reset signals.
sel is mapped to the lower bits, followed by p and reset.
Write to address 2**flen(pads.a) to pulse the FUD signal.
Address 2**flen(pads.a)+1 is a GPIO register that controls the
sel and reset signals. sel is mapped to the lower bits, followed by reset.
Write timing:
Address is set one cycle before assertion of we_n.
@ -28,6 +27,7 @@ class AD9858(Module):
Design:
All IO pads are registered.
With QC1 adapter:
LVDS driver/receiver propagation delays are 3.6+4.5 ns max
LVDS state transition delays are 20, 15 ns max
Schmitt trigger delays are 6.4ns max
@ -38,15 +38,15 @@ class AD9858(Module):
read_wait_cycles=10, hiz_wait_cycles=3,
bus=None):
if bus is None:
bus = wishbone.Interface(data_width=8)
bus = wishbone.Interface(data_width=flen(pads.d))
self.bus = bus
# # #
dts = TSTriple(8)
dts = TSTriple(flen(pads.d))
self.specials += dts.get_tristate(pads.d)
hold_address = Signal()
dr = Signal(8)
dr = Signal(flen(pads.d))
rx = Signal()
self.sync += [
If(~hold_address, pads.a.eq(bus.adr)),
@ -55,13 +55,14 @@ class AD9858(Module):
dts.oe.eq(~rx)
]
gpio = Signal(flen(pads.sel) + flen(pads.p) + 1)
gpio = Signal(flen(pads.sel) + 1)
gpio_load = Signal()
self.sync += If(gpio_load, gpio.eq(bus.dat_w))
self.comb += [
Cat(pads.sel, pads.p).eq(gpio),
pads.rst_n.eq(~gpio[-1]),
]
self.comb += pads.sel.eq(gpio),
if hasattr(pads, "rst"):
self.comb += pads.rst.eq(gpio[-1])
else:
self.comb += pads.rst_n.eq(~gpio[-1])
bus_r_gpio = Signal()
self.comb += If(bus_r_gpio,
@ -71,6 +72,9 @@ class AD9858(Module):
)
fud = Signal()
if hasattr(pads, "fud"):
self.sync += pads.fud.eq(fud)
else:
self.sync += pads.fud_n.eq(~fud)
pads.wr_n.reset = 1
@ -87,7 +91,7 @@ class AD9858(Module):
fsm.act("IDLE",
If(bus.cyc & bus.stb,
If(bus.adr[6],
If(bus.adr[flen(pads.a)],
If(bus.adr[0],
NextState("GPIO")
).Else(
@ -168,7 +172,6 @@ class _TestPads:
self.a = Signal(6)
self.d = Signal(8)
self.sel = Signal(5)
self.p = Signal(2)
self.fud_n = Signal()
self.wr_n = Signal()
self.rd_n = Signal()
@ -178,11 +181,11 @@ class _TestPads:
class _TB(Module):
def __init__(self):
pads = _TestPads()
self.submodules.dut = AD9858(pads, drive_fud=True)
self.submodules.dut = AD9xxx(pads, drive_fud=True)
self.submodules.initiator = wishbone.Initiator(_test_gen())
self.submodules.interconnect = wishbone.InterconnectPointToPoint(
self.initiator.bus, self.dut.bus)
if __name__ == "__main__":
run_simulation(_TB(), vcd_name="ad9858.vcd")
run_simulation(_TB(), vcd_name="ad9xxx.vcd")

View File

@ -1,5 +1,6 @@
from mibuild.generic_platform import *
fmc_adapter_io = [
("ttl", 0, Pins("LPC:LA00_CC_P"), IOStandard("LVTTL")),
("ttl", 1, Pins("LPC:LA02_P"), IOStandard("LVTTL")),
@ -28,10 +29,10 @@ fmc_adapter_io = [
Subsignal("sel", Pins("LPC:LA24_N LPC:LA29_P LPC:LA28_P LPC:LA29_N "
"LPC:LA28_N LPC:LA31_P LPC:LA30_P LPC:LA31_N "
"LPC:LA30_N LPC:LA33_P LPC:LA33_N")),
Subsignal("fud_n", Pins("LPC:LA21_N")),
Subsignal("fud", Pins("LPC:LA21_N")),
Subsignal("wr_n", Pins("LPC:LA24_P")),
Subsignal("rd_n", Pins("LPC:LA25_N")),
Subsignal("rst_in", Pins("LPC:LA25_P")),
Subsignal("rst", Pins("LPC:LA25_P")),
IOStandard("LVTTL")),
("i2c", 0,

View File

@ -1,14 +1,14 @@
from migen.fhdl.std import *
from artiq.gateware import ad9858 as ad9858_ll
from artiq.gateware import ad9xxx
from artiq.gateware.rtio.phy.wishbone import RT2WB
class AD9858(Module):
def __init__(self, pads, nchannels=8, **kwargs):
class _AD9xxx(Module):
def __init__(self, ftw_base, pads, nchannels, **kwargs):
self.submodules._ll = RenameClockDomains(
ad9858_ll.AD9858(pads, **kwargs), "rio")
self.submodules._rt2wb = RT2WB(7, self._ll.bus)
ad9xxx.AD9xxx(pads, **kwargs), "rio")
self.submodules._rt2wb = RT2WB(flen(pads.a)+1, self._ll.bus)
self.rtlink = self._rt2wb.rtlink
self.probes = [Signal(32) for i in range(nchannels)]
@ -16,21 +16,44 @@ class AD9858(Module):
# keep track of the currently selected channel
current_channel = Signal(max=nchannels)
self.sync.rio += If(self.rtlink.o.stb & (self.rtlink.o.address == 65),
self.sync.rio += If(self.rtlink.o.stb &
(self.rtlink.o.address == 2**flen(pads.a)+1),
current_channel.eq(self.rtlink.o.data))
# keep track of frequency tuning words, before they are FUDed
ftws = [Signal(32) for i in range(nchannels)]
for c, ftw in enumerate(ftws):
if flen(pads.d) == 8:
for i in range(4):
self.sync.rio += \
If(self.rtlink.o.stb & \
(self.rtlink.o.address == 0x0a+i) & \
(self.rtlink.o.address == ftw_base+i) & \
(current_channel == c),
ftw[i*8:(i+1)*8].eq(self.rtlink.o.data)
)
elif flen(pads.d) == 16:
for i in range(2):
self.sync.rio += \
If(self.rtlink.o.stb & \
(self.rtlink.o.address == ftw_base+2*i) & \
(current_channel == c),
ftw[i*16:(i+1)*16].eq(self.rtlink.o.data)
)
else:
raise NotImplementedError
# FTW to probe on FUD
for c, (probe, ftw) in enumerate(zip(self.probes, ftws)):
fud = self.rtlink.o.stb & (self.rtlink.o.address == 64)
fud = self.rtlink.o.stb & \
(self.rtlink.o.address == 2**flen(pads.a))
self.sync.rio += If(fud & (current_channel == c), probe.eq(ftw))
class AD9858(_AD9xxx):
def __init__(self, pads, nchannels, **kwargs):
_AD9xxx.__init__(self, 0x0a, pads, nchannels, **kwargs)
class AD9914(_AD9xxx):
def __init__(self, pads, nchannels, **kwargs):
_AD9xxx.__init__(self, 0x2d, pads, nchannels, **kwargs)

View File

@ -40,7 +40,7 @@ void dds_init(long long int timestamp, int channel)
now = timestamp - DURATION_INIT;
DDS_WRITE(DDS_GPIO, channel);
DDS_WRITE(DDS_GPIO, channel | (1 << 7));
DDS_WRITE(DDS_GPIO, channel | (1 << 5));
DDS_WRITE(DDS_GPIO, channel);
DDS_WRITE(0x00, 0x78);

View File

@ -10,7 +10,7 @@ from misoclib.mem.sdram.core.minicon import MiniconSettings
from targets.kc705 import MiniSoC
from artiq.gateware.soc import AMPSoC
from artiq.gateware import rtio, nist_qc1
from artiq.gateware import rtio, nist_qc1, nist_qc2
from artiq.gateware.rtio.phy import ttl_simple, dds
@ -32,7 +32,7 @@ class _RTIOCRG(Module, AutoCSR):
o_O=self.cd_rtio.clk)
class NIST_QC1(MiniSoC, AMPSoC):
class _NIST_QCx(MiniSoC, AMPSoC):
csr_map = {
"rtio": None, # mapped on Wishbone instead
"rtio_crg": 13,
@ -52,18 +52,44 @@ class NIST_QC1(MiniSoC, AMPSoC):
sdram_controller_settings=MiniconSettings(l2_size=128*1024),
with_timer=False, **kwargs)
AMPSoC.__init__(self)
platform.add_extension(nist_qc1.fmc_adapter_io)
self.submodules.leds = gpio.GPIOOut(Cat(
platform.request("user_led", 0),
platform.request("user_led", 1)))
def add_rtio(self, rtio_channels):
self.submodules.rtio_crg = _RTIOCRG(self.platform, self.crg.pll_sys)
self.submodules.rtio = rtio.RTIO(rtio_channels,
clk_freq=125000000)
self.add_constant("RTIO_FINE_TS_WIDTH", self.rtio.fine_ts_width)
self.add_constant("DDS_RTIO_CLK_RATIO", 8 >> self.rtio.fine_ts_width)
self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
if isinstance(self.platform.toolchain, XilinxVivadoToolchain):
self.platform.add_platform_command("""
create_clock -name rsys_clk -period 8.0 [get_nets {rsys_clk}]
create_clock -name rio_clk -period 8.0 [get_nets {rio_clk}]
set_false_path -from [get_clocks rsys_clk] -to [get_clocks rio_clk]
set_false_path -from [get_clocks rio_clk] -to [get_clocks rsys_clk]
""", rsys_clk=self.rtio.cd_rsys.clk, rio_clk=self.rtio.cd_rio.clk)
rtio_csrs = self.rtio.get_csrs()
self.submodules.rtiowb = wbgen.Bank(rtio_csrs)
self.kernel_cpu.add_wb_slave(mem_decoder(self.mem_map["rtio"]),
self.rtiowb.bus)
self.add_csr_region("rtio", self.mem_map["rtio"] | 0x80000000, 32,
rtio_csrs)
class NIST_QC1(_NIST_QCx):
def __init__(self, platform, cpu_type="or1k", **kwargs):
_NIST_QCx.__init__(self, platform, cpu_type, **kwargs)
platform.add_extension(nist_qc1.fmc_adapter_io)
self.comb += [
platform.request("ttl_l_tx_en").eq(1),
platform.request("ttl_h_tx_en").eq(1)
]
# RTIO channels
rtio_channels = []
for i in range(2):
phy = ttl_simple.Inout(platform.request("pmt", i))
@ -81,35 +107,46 @@ class NIST_QC1(MiniSoC, AMPSoC):
self.add_constant("RTIO_DDS_CHANNEL", len(rtio_channels))
self.add_constant("DDS_CHANNEL_COUNT", 8)
phy = dds.AD9858(platform.request("dds"))
self.add_constant("DDS_AD9858")
phy = dds.AD9858(platform.request("dds"), 8)
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy,
ofifo_depth=512,
ififo_depth=4))
self.add_rtio(rtio_channels)
# RTIO core
self.submodules.rtio_crg = _RTIOCRG(platform, self.crg.pll_sys)
self.submodules.rtio = rtio.RTIO(rtio_channels,
clk_freq=125000000)
self.add_constant("RTIO_FINE_TS_WIDTH", self.rtio.fine_ts_width)
self.add_constant("DDS_RTIO_CLK_RATIO", 8 >> self.rtio.fine_ts_width)
self.submodules.rtio_moninj = rtio.MonInj(rtio_channels)
if isinstance(platform.toolchain, XilinxVivadoToolchain):
platform.add_platform_command("""
create_clock -name rsys_clk -period 8.0 [get_nets {rsys_clk}]
create_clock -name rio_clk -period 8.0 [get_nets {rio_clk}]
set_false_path -from [get_clocks rsys_clk] -to [get_clocks rio_clk]
set_false_path -from [get_clocks rio_clk] -to [get_clocks rsys_clk]
""", rsys_clk=self.rtio.cd_rsys.clk, rio_clk=self.rtio.cd_rio.clk)
class NIST_QC2(_NIST_QCx):
def __init__(self, platform, cpu_type="or1k", **kwargs):
_NIST_QCx.__init__(self, platform, cpu_type, **kwargs)
platform.add_extension(nist_qc2.fmc_adapter_io)
# CPU connections
rtio_csrs = self.rtio.get_csrs()
self.submodules.rtiowb = wbgen.Bank(rtio_csrs)
self.kernel_cpu.add_wb_slave(mem_decoder(self.mem_map["rtio"]),
self.rtiowb.bus)
self.add_csr_region("rtio", self.mem_map["rtio"] | 0x80000000, 32,
rtio_csrs)
rtio_channels = []
for i in range(16):
if i % 4 == 3:
phy = ttl_simple.Inout(platform.request("ttl", i))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=512))
else:
phy = ttl_simple.Output(platform.request("ttl", i))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
phy = ttl_simple.Output(platform.request("user_led", 2))
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy))
self.add_constant("RTIO_TTL_COUNT", len(rtio_channels))
self.add_constant("RTIO_DDS_CHANNEL", len(rtio_channels))
self.add_constant("DDS_CHANNEL_COUNT", 11)
self.add_constant("DDS_AD9914")
self.add_constant("DDS_ONEHOT_SEL")
phy = dds.AD9914(platform.request("dds"), 11)
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy,
ofifo_depth=512,
ififo_depth=4))
self.add_rtio(rtio_channels)
default_subtarget = NIST_QC1

View File

@ -118,7 +118,7 @@ trce -v 12 -fastpaths -tsi {build_name}.tsi -o {build_name}.twr {build_name}.ncd
self.add_constant("RTIO_DDS_CHANNEL", len(rtio_channels))
self.add_constant("DDS_CHANNEL_COUNT", 8)
phy = dds.AD9858(platform.request("dds"))
phy = dds.AD9858(platform.request("dds"), 8)
self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy,
ofifo_depth=512,