From 944bfafefabcfd1f0453e0c818ab616dead1bac2 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sun, 28 Jun 2015 21:37:27 +0200 Subject: [PATCH] soc: support QC2 and AD9914 (untested) --- artiq/gateware/{ad9858.py => ad9xxx.py} | 43 ++++++------ artiq/gateware/nist_qc2.py | 5 +- artiq/gateware/rtio/phy/dds.py | 53 ++++++++++---- soc/runtime/dds.c | 2 +- soc/targets/artiq_kc705.py | 91 +++++++++++++++++-------- soc/targets/artiq_pipistrello.py | 2 +- 6 files changed, 130 insertions(+), 66 deletions(-) rename artiq/gateware/{ad9858.py => ad9xxx.py} (81%) diff --git a/artiq/gateware/ad9858.py b/artiq/gateware/ad9xxx.py similarity index 81% rename from artiq/gateware/ad9858.py rename to artiq/gateware/ad9xxx.py index 5b475d628..f774f63ed 100644 --- a/artiq/gateware/ad9858.py +++ b/artiq/gateware/ad9xxx.py @@ -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,7 +72,10 @@ class AD9858(Module): ) fud = Signal() - self.sync += pads.fud_n.eq(~fud) + if hasattr(pads, "fud"): + self.sync += pads.fud.eq(fud) + else: + self.sync += pads.fud_n.eq(~fud) pads.wr_n.reset = 1 pads.rd_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") diff --git a/artiq/gateware/nist_qc2.py b/artiq/gateware/nist_qc2.py index 7f93e4e49..9d6896781 100644 --- a/artiq/gateware/nist_qc2.py +++ b/artiq/gateware/nist_qc2.py @@ -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, diff --git a/artiq/gateware/rtio/phy/dds.py b/artiq/gateware/rtio/phy/dds.py index a0daec62b..e9685223d 100644 --- a/artiq/gateware/rtio/phy/dds.py +++ b/artiq/gateware/rtio/phy/dds.py @@ -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), - current_channel.eq(self.rtlink.o.data)) + 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): - for i in range(4): - self.sync.rio += \ - If(self.rtlink.o.stb & \ - (self.rtlink.o.address == 0x0a+i) & \ - (current_channel == c), - ftw[i*8:(i+1)*8].eq(self.rtlink.o.data) - ) + if flen(pads.d) == 8: + for i in range(4): + self.sync.rio += \ + If(self.rtlink.o.stb & \ + (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) diff --git a/soc/runtime/dds.c b/soc/runtime/dds.c index 9a3790832..326c48845 100644 --- a/soc/runtime/dds.c +++ b/soc/runtime/dds.c @@ -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); diff --git a/soc/targets/artiq_kc705.py b/soc/targets/artiq_kc705.py index 740b68b22..d6631ef87 100644 --- a/soc/targets/artiq_kc705.py +++ b/soc/targets/artiq_kc705.py @@ -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 diff --git a/soc/targets/artiq_pipistrello.py b/soc/targets/artiq_pipistrello.py index 5d6f900c1..6f5e6f632 100644 --- a/soc/targets/artiq_pipistrello.py +++ b/soc/targets/artiq_pipistrello.py @@ -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,